Rename Option to Parameter

This commit is contained in:
Mathias Koehler 2012-03-16 23:10:09 +01:00
parent 9cb5d8efe2
commit 83950f8d8f
7 changed files with 163 additions and 206 deletions

View file

@ -4,8 +4,8 @@ FFmpegWrapper
.. module:: ffmpegwrapper .. module:: ffmpegwrapper
FFmpegWrapper is a small wrapper for the ffmpeg encoder. You can append FFmpegWrapper is a small wrapper for the ffmpeg encoder. You can append
Codec, Filters and other OptionStores to the FFmpeg class and then run the Codec, Filters and other ParameterContainer to the FFmpeg class and then
resulting command. run the resulting command.
>>> from ffmpegwrapper import FFmpeg, Input, Output, VideoCodec, VideoFilter >>> from ffmpegwrapper import FFmpeg, Input, Output, VideoCodec, VideoFilter
>>> videofilter = VideoFilter().crop(300, 200) >>> videofilter = VideoFilter().crop(300, 200)
@ -17,16 +17,13 @@ resulting command.
Stores and Options Parameter and Containers
------------------ ---------------------
There are two kinds of classes in FFmpegwrapper. The Stores and Options. An Parameter is a namedtuple which represents parameters of the FFmpeg command.
A Store is a object where you can append an Option or even Stores which
then have their own Options. A Option represent one or more arguments for
FFmpeg. A Store is more likea bigger block of Options for a specific need.
For example: Everything that describe the Output can be appended to the
Output class.
Containers combines them. :class:`VideoFilter` is for example a subclassed
:class:`ParameterContainer` which can contain some filters.
API API
--- ---
@ -68,14 +65,47 @@ Filters
:members: :members:
:undoc-members: :undoc-members:
Stores and Options Container and Parameter
~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~
.. autoclass:: ffmpegwrapper.options.Option .. autoclass:: ffmpegwrapper.parameters.Parameter
:members: :members:
.. autoclass:: ffmpegwrapper.options.OptionStore .. autoclass:: ffmpegwrapper.parameters.ParameterContainer
:members: :members:
License License
------- -------
Copyright (c) 2012 by Mathias Koehler.
Some rights reserved.
Redistribution and use in source and binary forms of the software as well
as documentation, 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 of the contributors may not be used to endorse or
promote products derived from this software without specific
prior written permission.
THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY THE COPYRIGHT HOLDERS AND
CONTRIBUTORS "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 OWNER
OR CONTRIBUTORS 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 AND DOCUMENTATION, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.

View file

@ -11,78 +11,79 @@
""" """
from itertools import chain from itertools import chain
from .parameters import ParameterContainer
from .options import OptionStore
NO_AUDIO = ['-an'] NO_AUDIO = ('-an',)
NO_VIDEO = ['-vn'] NO_VIDEO = ('-vn',)
class Codec(OptionStore): class Codec(ParameterContainer):
""" Container for Codecs-Settings"""
def __init__(self, name, *args): def __init__(self, name, *args):
self.name = name self.name = name
OptionStore.__init__(self, *args) ParameterContainer.__init__(self, *args)
class VideoCodec(Codec): class VideoCodec(Codec):
"""This represent an video codec. """This represent an video codec.
You can append this class to an :class:`Output` object to tell You can append this class to an :class:`Output` object to tell
which FFmpeg which codec you want.""" which FFmpeg which codec you want.
"""
def bitrate(self, bitrate): def bitrate(self, bitrate):
self.add_option('-b', str(bitrate)) self.add_formatparam('-b', str(bitrate))
return self return self
def frames(self, number): def frames(self, number):
self.add_option('-vframes', str(number)) self.add_formatparam('-vframes', str(number))
return self return self
def fps(self, fps): def fps(self, fps):
self.add_option('-r', str(fps)) self.add_formatparam('-r', str(fps))
return self return self
def size(self, x, y): def size(self, x, y):
filter = "{x}x{y}".format(x, y) filter = "{x}x{y}".format(x, y)
self.add_option('-s', filter) self.add_formatparam('-s', filter)
return self return self
def aspect(self, x, y): def aspect(self, x, y):
self.add_parameter('-aspect', x, y) self.add_formatparam('-aspect', x, y)
return self return self
def bitrate_tolerance(self, tolerance): def bitrate_tolerance(self, tolerance):
self.add_option('-bt', str(tolerance)) self.add_formatparam('-bt', str(tolerance))
return self return self
def max_bitrate(self, rate): def max_bitrate(self, rate):
self.add_option('-maxrate', str(rate)) self.add_formatparam('-maxrate', str(rate))
return self return self
def min_bitrate(self, rate): def min_bitrate(self, rate):
self.add_option('-minrate', str(rate)) self.add_formatparam('-minrate', str(rate))
return self return self
def buffer_size(self, size): def buffer_size(self, size):
self.add_option('-bufsize', str(size)) self.add_formatparam('-bufsize', str(size))
return self return self
def pass_number(self, number): def pass_number(self, number):
self.add_option('-pass', str(number)) self.add_formatparam('-pass', str(number))
return self return self
def language(self, lang): def language(self, lang):
self.add_option('-vlang', str(lang)) self.add_formatparam('-vlang', str(lang))
return self return self
def same_quality(self): def same_quality(self):
self.add_option('-sameq', None) self.add_formatparam('-sameq', None)
return self return self
def preset(self, preset): def preset(self, preset):
self.add_option('-vpre', str(preset)) self.add_formatparam('-vpre', str(preset))
return self return self
def __iter__(self): def __iter__(self):
@ -93,35 +94,36 @@ class AudioCodec(Codec):
"""This represent an audio codec. """This represent an audio codec.
You can append this class to an :class:`Output` object to tell You can append this class to an :class:`Output` object to tell
which FFmpeg which codec you want.""" which FFmpeg which codec you want.
"""
def frames(self, number): def frames(self, number):
self.add_option('-aframes', str(number)) self.add_formatparam('-aframes', str(number))
return self return self
def frequence(self, freq): def frequence(self, freq):
self.add_option('-ar', str(freq)) self.add_formatparam('-ar', str(freq))
return self return self
def bitrate(self, rate): def bitrate(self, rate):
self.add_option('-ab', str(rate)) self.add_formatparam('-ab', str(rate))
return self return self
def quality(self, number): def quality(self, number):
self.add_option('-aq', str(number)) self.add_formatparam('-aq', str(number))
return self return self
def channels(self, number): def channels(self, number):
self.add_option('-ac', str(number)) self.add_formatparam('-ac', str(number))
return self return self
def language(self, lang): def language(self, lang):
self.add_option('-alang', str(lang)) self.add_formatparam('-alang', str(lang))
return self return self
def preset(self, preset): def preset(self, preset):
"""Load default presets from a preset file""" """Load default presets from a preset file"""
self.add_option('-apre', str(preset)) self.add_formatparam('-apre', str(preset))
return self return self
def __iter__(self): def __iter__(self):

View file

@ -19,42 +19,43 @@ try:
except ImportError: except ImportError:
from queue import Queue, Empty from queue import Queue, Empty
from .options import OptionStore, Option from .parameters import ParameterContainer, Parameter
class Input(OptionStore): class Input(ParameterContainer):
"""Store for a input file. """Container for a input file.
:param file: Path to the input file :param file_path: Path to the input file
:param args: A list of Stores that should be appended :param args: A list of Containers that should be appended
""" """
def __init__(self, file, *args): def __init__(self, file_path, *args):
self.file = file self.file_path = file_path
OptionStore.__init__(self, *args) ParameterContainer.__init__(self, *args)
def __iter__(self): def __iter__(self):
return chain(OptionStore.__iter__(self), ['-i', self.file]) return chain(ParameterContainer.__iter__(self),
['-i', self.file_path])
class Output(OptionStore): class Output(ParameterContainer):
"""Store for a output file. """Container for a output file.
:param file: Path in which the file should be saved :param file_path: Path in which the file should be saved
:param args: A list of Stores that should be appended :param args: A list of Containers that should be appended
""" """
def __init__(self, file, *args): def __init__(self, file_path, *args):
self.file = file self.file_path = file_path
OptionStore.__init__(self, *args) ParameterContainer.__init__(self, *args)
def overwrite(self): def overwrite(self):
"""Overwrite the file if it already exist""" """Overwrite the file if it already exist"""
self.add_option('-y', None) self.add_parameter('-y', None)
return self return self
def __iter__(self): def __iter__(self):
return chain(OptionStore.__iter__(self), [self.file]) return chain(ParameterContainer.__iter__(self), [self.file_path])
class FFmpegProcess(object): class FFmpegProcess(object):
@ -65,7 +66,7 @@ class FFmpegProcess(object):
def __init__(self, command): def __init__(self, command):
self.command = list(command) self.command = list(command)
self.queue = Queue() self.queue = Queue(maxsize=2000)
self.process = None self.process = None
def _queue_output(self, out, queue): def _queue_output(self, out, queue):
@ -127,26 +128,25 @@ class FFmpegProcess(object):
return self.readlines() return self.readlines()
class FFmpeg(ParameterContainer):
class FFmpeg(OptionStore):
"""This class represent the FFmpeg command. """This class represent the FFmpeg command.
It behaves like a list. If you iterate over the object it will yield It behaves like a list. If you iterate over the object it will yield
small parts from the ffmpeg command with it arguments. The arguments small parts from the ffmpeg command with it arguments. The arguments
for the command are in the Option classes. They can be appended directly for the command are in the Parameter classes. They can be appended
or through one or more Stores. directly or through one or more Containers.
:param binary: The binary subprocess should execute at the :meth:`run` :param binary: The binary subprocess should execute at the :meth:`run`
:param args: A list of Stores that should be appended :param args: A list of Containers that should be appended
""" """
def __init__(self, binary="ffmpeg", *args): def __init__(self, binary="ffmpeg", *args):
self.binary = binary self.binary = binary
self.process = None self.process = None
OptionStore.__init__(self, *args) ParameterContainer.__init__(self, *args)
def add_option(self, key, value): def add_parameter(self, key, value):
self.container_list.insert(0, Option(key, value)) self.container_list.insert(0, Parameter(key, value))
def run(self): def run(self):
"""Executes the command of this object. Return a """Executes the command of this object. Return a
@ -167,4 +167,4 @@ class FFmpeg(OptionStore):
self.process = None self.process = None
def __iter__(self): def __iter__(self):
return chain([self.binary], OptionStore.__iter__(self)) return chain([self.binary], ParameterContainer.__iter__(self))

View file

@ -10,25 +10,25 @@
""" """
from itertools import chain from itertools import chain
from .parameters import ParameterContainer
from .options import OptionStore
class FilterStore(OptionStore): class FilterContainer(ParameterContainer):
"""Contains filters which you can append to an output file."""
def __str__(self): def __str__(self):
return ",".join(FilterStore.__iter__(self)) return ",".join(FilterContainer.__iter__(self))
def __iter__(self): def __iter__(self):
for key, value in OptionStore.iteritems(self): for key, value in ParameterContainer.iteritems(self):
if value is not None: if value is not None:
yield "=".join([key, str(value)]) yield "=".join([key, str(value)])
else: else:
yield key yield key
class VideoFilter(FilterStore): class VideoFilter(FilterContainer):
"""FilterStore for Videofilters. """FilterContainer for Videofilters.
.. seealso:: .. seealso::
@ -40,142 +40,142 @@ class VideoFilter(FilterStore):
""" """
def blackframe(self, amount, threshold): def blackframe(self, amount, threshold):
self.add_parameter('blackframe', amount, threshold) self.add_formatparam('blackframe', amount, threshold)
return self return self
def copy(self): def copy(self):
self.add_option('copy', None) self.add_parameter('copy', None)
return self return self
def crop(self, out_w, out_h=None, x=None, y=None): def crop(self, out_w, out_h=None, x=None, y=None):
self.add_parameter('crop', out_w, out_h, x, y) self.add_formatparam('crop', out_w, out_h, x, y)
return self return self
def cropdetect(self, limit=None, round=None, reset=None): def cropdetect(self, limit=None, round=None, reset=None):
self.add_parameter('cropdetect', limit, round, reset) self.add_formatparam('cropdetect', limit, round, reset)
return self return self
def drawbox(self, x, y, width, height, color): def drawbox(self, x, y, width, height, color):
self.add_parameter('drawbox', x, y, width, height, color) self.add_formatparam('drawbox', x, y, width, height, color)
return self return self
def drawtext(self, **kwargs): def drawtext(self, **kwargs):
self.add_parameter('drawtext', **kwargs) self.add_formatparam('drawtext', **kwargs)
return self return self
def fade(self, type, start, number): def fade(self, type, start, number):
self.add_parameter('fade', type, start, number) self.add_formatparam('fade', type, start, number)
return self return self
def fieldorder(self, type): def fieldorder(self, type):
if str(type) not in ['0', '1', 'bff', 'tff']: if str(type) not in ['0', '1', 'bff', 'tff']:
raise ValueError('Invalid Option for fieldorder. ' raise ValueError('Invalid Parameter for fieldorder. '
'Read FFmpeg manual!') 'Read FFmpeg manual!')
self.add_parameter('fieldorder', type) self.add_formatparam('fieldorder', type)
return self return self
def fifo(self): def fifo(self):
self.add_option('fifo', None) self.add_parameter('fifo', None)
return self return self
def format(self, *args): def format(self, *args):
self.add_parameter('format', *args) self.add_formatparam('format', *args)
return self return self
def freior(self, name, *args): def freior(self, name, *args):
self.add_parameter('frei0r', name, *args) self.add_formatparam('frei0r', name, *args)
return self return self
def gradfun(self, strength='', radius=''): def gradfun(self, strength='', radius=''):
self.add_parameter('gradfun', strength, radius) self.add_formatparam('gradfun', strength, radius)
return self return self
def hflip(self): def hflip(self):
self.add_option('hflip', None) self.add_parameter('hflip', None)
return self return self
def hqdn3d(self, luma_sp=None, chroma_sp=None, def hqdn3d(self, luma_sp=None, chroma_sp=None,
luma_tmp=None, chroma_tmp=None): luma_tmp=None, chroma_tmp=None):
self.add_parameter('hqdn3d', self.add_formatparam('hqdn3d',
luma_sp, chroma_sp, luma_tmp, chroma_tmp) luma_sp, chroma_sp, luma_tmp, chroma_tmp)
return self return self
def mp(self, **kwargs): def mp(self, **kwargs):
self.add_parameter('mp', **kwargs) self.add_formatparam('mp', **kwargs)
return self return self
def negate(self): def negate(self):
self.add_option('negate', 1) self.add_parameter('negate', 1)
return self return self
def noformat(self, *args): def noformat(self, *args):
self.add_parameter('noformat', *args) self.add_formatparam('noformat', *args)
return self return self
def null(self): def null(self):
self.add_option('null', None) self.add_parameter('null', None)
return self return self
def overlay(self, x, y): def overlay(self, x, y):
self.add_parameter('overlay', x, y) self.add_formatparam('overlay', x, y)
return self return self
def pad(self, width, height, x, y, color): def pad(self, width, height, x, y, color):
self.add_parameter('pad', width, height, x, y, color) self.add_formatparam('pad', width, height, x, y, color)
return self return self
def scale(self, width=-1, height=-1): def scale(self, width=-1, height=-1):
self.add_parameter('scale', width, height) self.add_formatparam('scale', width, height)
return self return self
def select(self, expression): def select(self, expression):
self.add_parameter('select', expression) self.add_formatparam('select', expression)
return self return self
def setdar(self, x, y): def setdar(self, x, y):
self.add_parameter('setdar', x, y) self.add_formatparam('setdar', x, y)
return self return self
def setpts(self, expression): def setpts(self, expression):
self.add_parameter('setpts', expression) self.add_formatparam('setpts', expression)
return self return self
def setsar(self, x, y): def setsar(self, x, y):
self.add_parameter('setsar', x, y) self.add_formatparam('setsar', x, y)
return self return self
def slicify(self, height=16): def slicify(self, height=16):
self.add_parameter('slicify', height) self.add_formatparam('slicify', height)
return self return self
def transpose(self, type): def transpose(self, type):
if str(type) not in ['0', '1','2', '3']: if str(type) not in ['0', '1', '2', '3']:
raise ValueError('Invalid Option for transpose. ' raise ValueError('Invalid Parameter for transpose. '
'Read FFmpeg manual') 'Read FFmpeg manual')
self.add_parameter('transpose', type) self.add_formatparam('transpose', type)
return self return self
def unsharp(self, *args): def unsharp(self, *args):
if len(args) > 6: if len(args) > 6:
message = 'unsharp() takes exactly 6 positional arguments' message = 'unsharp() takes exactly 6 positional arguments'
raise TypeError(message) raise TypeError(message)
self.add_parameter('unsharp', *args) self.add_formatparam('unsharp', *args)
return self return self
def vflip(self): def vflip(self):
self.add_option('vflip', None) self.add_parameter('vflip', None)
return self return self
def yadif(self, mode=0, parity=-1): def yadif(self, mode=0, parity=-1):
self.add_parameter('yadif', mode, parity) self.add_formatparam('yadif', mode, parity)
return self return self
def __iter__(self): def __iter__(self):
return chain(['-vf', FilterStore.__str__(self)]) return chain(['-vf', FilterContainer.__str__(self)])
class AudioFilter(FilterStore): class AudioFilter(FilterContainer):
"""FilterStore for Audifilters. """FilterContainer for Audifilters.
.. seealso:: .. seealso::
@ -188,8 +188,8 @@ class AudioFilter(FilterStore):
def null(self): def null(self):
"""does nothing""" """does nothing"""
self.add_option('null', None) self.add_parameter('null', None)
return self return self
def __iter__(self): def __iter__(self):
return chain(['-af', FilterStore.__str__(self)]) return chain(['-af', FilterContainer.__str__(self)])

View file

@ -1,73 +0,0 @@
# -*- coding: utf-8 -*-
from itertools import chain
from collections import MutableSequence, namedtuple
try:
from itertools import ifilter
except ImportError:
ifilter = filter
def format_parameter(*args, **kwargs):
parameter_list = []
for key, value in kwargs.items():
try:
if not value:
parameter_list.append(key)
else:
parameter_list.append("=".join([key, value]))
except TypeError:
values = ':'.join(kwargs[key])
parameter_list.append("=".join([key, values]))
for value in args:
if value is not None:
parameter_list.append(str(value))
result = ':'.join(parameter_list)
if kwargs:
return '"%s"' % result
return result
Option = namedtuple('Option', ['name', 'value'])
class OptionStore(MutableSequence):
def __init__(self, *containers):
self.container_list = list(containers)
def add_option(self, key, value):
self.container_list.append(Option(key, value))
def add_parameter(self, name, *args, **kwargs):
parameter = format_parameter(*args, **kwargs)
self.add_option(name, parameter)
def insert(self, index, value):
self.container_list.insert(index, value)
def iteritems(self):
return iter(self.container_list)
def __iter__(self):
return ifilter(None, chain.from_iterable(self.container_list))
def __len__(self):
return self.container_list.__len__()
def __setitem__(self, index, value):
self.container_list.__setitem__(index, value)
def __getitem__(self, index):
return self.container_list.__getitem__(index)
def __delitem__(self, index):
self.container_list.__delitem__(index)
def __contains__(self, other):
return self.container_list.__contains__(other)
def __repr__(self):
return "<{cls} {opts}>".format(
opts=list(self), cls=self.__class__.__name__)

View file

@ -5,7 +5,7 @@ FFmpegwrapper
~~~~~~~~~~~~~ ~~~~~~~~~~~~~
FFmpegWrapper is a small wrapper for the ffmpeg encoder. You can append FFmpegWrapper is a small wrapper for the ffmpeg encoder. You can append
Codec, Filters and other OptionStores to the FFmpeg class and then run the Codec, Filters and other ParameterStores to the FFmpeg class and then run the
resulting command. resulting command.
>>> from ffmpegwrapper import FFmpeg, Input, Output, VideoCodec, VideoFilter >>> from ffmpegwrapper import FFmpeg, Input, Output, VideoCodec, VideoFilter
@ -18,10 +18,8 @@ resulting command.
""" """
import sys
from setuptools import setup from setuptools import setup
setup( setup(
name="ffmpegwrapper", name="ffmpegwrapper",
version="0.1-dev", version="0.1-dev",

26
test.py
View file

@ -6,7 +6,7 @@ from mock import patch
from ffmpegwrapper import FFmpeg, Input, Output, \ from ffmpegwrapper import FFmpeg, Input, Output, \
VideoCodec, AudioCodec, VideoFilter VideoCodec, AudioCodec, VideoFilter
from ffmpegwrapper.options import Option from ffmpegwrapper.parameters import Parameter
class FFmpegTestCase(unittest.TestCase): class FFmpegTestCase(unittest.TestCase):
@ -31,28 +31,28 @@ class FFmpegTestCase(unittest.TestCase):
def test_input_interface(self): def test_input_interface(self):
input = Input('/old') input = Input('/old')
self.assertEqual(list(input), ['-i', '/old']) self.assertEqual(list(input), ['-i', '/old'])
self.assertEqual(input.file, '/old') self.assertEqual(input.file_path, '/old')
option = Option('-vf', 'x11grab') parameter = Parameter('-vf', 'x11grab')
input.append(option) input.append(parameter)
self.assertEqual(list(input), ['-vf', 'x11grab', '-i', '/old']) self.assertEqual(list(input), ['-vf', 'x11grab', '-i', '/old'])
self.assertEqual(input.pop(), option) self.assertEqual(input.pop(), parameter)
input.add_option('-vf', 'x11grab') input.add_formatparam('-vf', 'x11grab')
self.assertEqual(input.container_list, [option]) self.assertEqual(input.container_list, [parameter])
def test_output_interface(self): def test_output_interface(self):
output = Output('/new') output = Output('/new')
self.assertEqual(list(output), ['/new']) self.assertEqual(list(output), ['/new'])
self.assertEqual(output.file, '/new') self.assertEqual(output.file_path, '/new')
option = Option('-vcodec', 'libx264') parameter = Parameter('-vcodec', 'libx264')
output.append(option) output.append(parameter)
self.assertEqual(list(output), ['-vcodec', 'libx264', '/new']) self.assertEqual(list(output), ['-vcodec', 'libx264', '/new'])
self.assertEqual(output.pop(), option) self.assertEqual(output.pop(), parameter)
output.add_option('-vcodec', 'libx264') output.add_formatparam('-vcodec', 'libx264')
self.assertEqual(output.container_list, [option]) self.assertEqual(output.container_list, [parameter])
def test_codec_interface(self): def test_codec_interface(self):
codec = VideoCodec('libx264') codec = VideoCodec('libx264')