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
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
resulting command.
Codec, Filters and other ParameterContainer to the FFmpeg class and then
run the resulting command.
>>> from ffmpegwrapper import FFmpeg, Input, Output, VideoCodec, VideoFilter
>>> 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.
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.
An Parameter is a namedtuple which represents parameters of the FFmpeg command.
Containers combines them. :class:`VideoFilter` is for example a subclassed
:class:`ParameterContainer` which can contain some filters.
API
---
@ -68,14 +65,47 @@ Filters
:members:
:undoc-members:
Stores and Options
~~~~~~~~~~~~~~~~~~
Container and Parameter
~~~~~~~~~~~~~~~~~~~~
.. autoclass:: ffmpegwrapper.options.Option
.. autoclass:: ffmpegwrapper.parameters.Parameter
:members:
.. autoclass:: ffmpegwrapper.options.OptionStore
.. autoclass:: ffmpegwrapper.parameters.ParameterContainer
:members:
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 .options import OptionStore
from .parameters import ParameterContainer
NO_AUDIO = ['-an']
NO_VIDEO = ['-vn']
NO_AUDIO = ('-an',)
NO_VIDEO = ('-vn',)
class Codec(OptionStore):
class Codec(ParameterContainer):
""" Container for Codecs-Settings"""
def __init__(self, name, *args):
self.name = name
OptionStore.__init__(self, *args)
ParameterContainer.__init__(self, *args)
class VideoCodec(Codec):
"""This represent an video codec.
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):
self.add_option('-b', str(bitrate))
self.add_formatparam('-b', str(bitrate))
return self
def frames(self, number):
self.add_option('-vframes', str(number))
self.add_formatparam('-vframes', str(number))
return self
def fps(self, fps):
self.add_option('-r', str(fps))
self.add_formatparam('-r', str(fps))
return self
def size(self, x, y):
filter = "{x}x{y}".format(x, y)
self.add_option('-s', filter)
self.add_formatparam('-s', filter)
return self
def aspect(self, x, y):
self.add_parameter('-aspect', x, y)
self.add_formatparam('-aspect', x, y)
return self
def bitrate_tolerance(self, tolerance):
self.add_option('-bt', str(tolerance))
self.add_formatparam('-bt', str(tolerance))
return self
def max_bitrate(self, rate):
self.add_option('-maxrate', str(rate))
self.add_formatparam('-maxrate', str(rate))
return self
def min_bitrate(self, rate):
self.add_option('-minrate', str(rate))
self.add_formatparam('-minrate', str(rate))
return self
def buffer_size(self, size):
self.add_option('-bufsize', str(size))
self.add_formatparam('-bufsize', str(size))
return self
def pass_number(self, number):
self.add_option('-pass', str(number))
self.add_formatparam('-pass', str(number))
return self
def language(self, lang):
self.add_option('-vlang', str(lang))
self.add_formatparam('-vlang', str(lang))
return self
def same_quality(self):
self.add_option('-sameq', None)
self.add_formatparam('-sameq', None)
return self
def preset(self, preset):
self.add_option('-vpre', str(preset))
self.add_formatparam('-vpre', str(preset))
return self
def __iter__(self):
@ -91,37 +92,38 @@ class VideoCodec(Codec):
class AudioCodec(Codec):
"""This represent an audio codec.
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):
self.add_option('-aframes', str(number))
self.add_formatparam('-aframes', str(number))
return self
def frequence(self, freq):
self.add_option('-ar', str(freq))
self.add_formatparam('-ar', str(freq))
return self
def bitrate(self, rate):
self.add_option('-ab', str(rate))
self.add_formatparam('-ab', str(rate))
return self
def quality(self, number):
self.add_option('-aq', str(number))
self.add_formatparam('-aq', str(number))
return self
def channels(self, number):
self.add_option('-ac', str(number))
self.add_formatparam('-ac', str(number))
return self
def language(self, lang):
self.add_option('-alang', str(lang))
self.add_formatparam('-alang', str(lang))
return self
def preset(self, preset):
"""Load default presets from a preset file"""
self.add_option('-apre', str(preset))
self.add_formatparam('-apre', str(preset))
return self
def __iter__(self):

View file

@ -19,42 +19,43 @@ try:
except ImportError:
from queue import Queue, Empty
from .options import OptionStore, Option
from .parameters import ParameterContainer, Parameter
class Input(OptionStore):
"""Store for a input file.
class Input(ParameterContainer):
"""Container for a input file.
:param file: Path to the input file
:param args: A list of Stores that should be appended
:param file_path: Path to the input file
:param args: A list of Containers that should be appended
"""
def __init__(self, file, *args):
self.file = file
OptionStore.__init__(self, *args)
def __init__(self, file_path, *args):
self.file_path = file_path
ParameterContainer.__init__(self, *args)
def __iter__(self):
return chain(OptionStore.__iter__(self), ['-i', self.file])
return chain(ParameterContainer.__iter__(self),
['-i', self.file_path])
class Output(OptionStore):
"""Store for a output file.
class Output(ParameterContainer):
"""Container for a output file.
:param file: Path in which the file should be saved
:param args: A list of Stores that should be appended
:param file_path: Path in which the file should be saved
:param args: A list of Containers that should be appended
"""
def __init__(self, file, *args):
self.file = file
OptionStore.__init__(self, *args)
def __init__(self, file_path, *args):
self.file_path = file_path
ParameterContainer.__init__(self, *args)
def overwrite(self):
"""Overwrite the file if it already exist"""
self.add_option('-y', None)
self.add_parameter('-y', None)
return self
def __iter__(self):
return chain(OptionStore.__iter__(self), [self.file])
return chain(ParameterContainer.__iter__(self), [self.file_path])
class FFmpegProcess(object):
@ -65,7 +66,7 @@ class FFmpegProcess(object):
def __init__(self, command):
self.command = list(command)
self.queue = Queue()
self.queue = Queue(maxsize=2000)
self.process = None
def _queue_output(self, out, queue):
@ -127,26 +128,25 @@ class FFmpegProcess(object):
return self.readlines()
class FFmpeg(OptionStore):
class FFmpeg(ParameterContainer):
"""This class represent the FFmpeg command.
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
for the command are in the Option classes. They can be appended directly
or through one or more Stores.
for the command are in the Parameter classes. They can be appended
directly or through one or more Containers.
: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):
self.binary = binary
self.process = None
OptionStore.__init__(self, *args)
ParameterContainer.__init__(self, *args)
def add_option(self, key, value):
self.container_list.insert(0, Option(key, value))
def add_parameter(self, key, value):
self.container_list.insert(0, Parameter(key, value))
def run(self):
"""Executes the command of this object. Return a
@ -167,4 +167,4 @@ class FFmpeg(OptionStore):
self.process = None
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 .options import OptionStore
from .parameters import ParameterContainer
class FilterStore(OptionStore):
class FilterContainer(ParameterContainer):
"""Contains filters which you can append to an output file."""
def __str__(self):
return ",".join(FilterStore.__iter__(self))
return ",".join(FilterContainer.__iter__(self))
def __iter__(self):
for key, value in OptionStore.iteritems(self):
for key, value in ParameterContainer.iteritems(self):
if value is not None:
yield "=".join([key, str(value)])
else:
yield key
class VideoFilter(FilterStore):
"""FilterStore for Videofilters.
class VideoFilter(FilterContainer):
"""FilterContainer for Videofilters.
.. seealso::
@ -40,142 +40,142 @@ class VideoFilter(FilterStore):
"""
def blackframe(self, amount, threshold):
self.add_parameter('blackframe', amount, threshold)
self.add_formatparam('blackframe', amount, threshold)
return self
def copy(self):
self.add_option('copy', None)
self.add_parameter('copy', None)
return self
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
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
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
def drawtext(self, **kwargs):
self.add_parameter('drawtext', **kwargs)
self.add_formatparam('drawtext', **kwargs)
return self
def fade(self, type, start, number):
self.add_parameter('fade', type, start, number)
self.add_formatparam('fade', type, start, number)
return self
def fieldorder(self, type):
if str(type) not in ['0', '1', 'bff', 'tff']:
raise ValueError('Invalid Option for fieldorder. '
raise ValueError('Invalid Parameter for fieldorder. '
'Read FFmpeg manual!')
self.add_parameter('fieldorder', type)
self.add_formatparam('fieldorder', type)
return self
def fifo(self):
self.add_option('fifo', None)
self.add_parameter('fifo', None)
return self
def format(self, *args):
self.add_parameter('format', *args)
self.add_formatparam('format', *args)
return self
def freior(self, name, *args):
self.add_parameter('frei0r', name, *args)
self.add_formatparam('frei0r', name, *args)
return self
def gradfun(self, strength='', radius=''):
self.add_parameter('gradfun', strength, radius)
self.add_formatparam('gradfun', strength, radius)
return self
def hflip(self):
self.add_option('hflip', None)
self.add_parameter('hflip', None)
return self
def hqdn3d(self, luma_sp=None, chroma_sp=None,
luma_tmp=None, chroma_tmp=None):
self.add_parameter('hqdn3d',
self.add_formatparam('hqdn3d',
luma_sp, chroma_sp, luma_tmp, chroma_tmp)
return self
def mp(self, **kwargs):
self.add_parameter('mp', **kwargs)
self.add_formatparam('mp', **kwargs)
return self
def negate(self):
self.add_option('negate', 1)
self.add_parameter('negate', 1)
return self
def noformat(self, *args):
self.add_parameter('noformat', *args)
self.add_formatparam('noformat', *args)
return self
def null(self):
self.add_option('null', None)
self.add_parameter('null', None)
return self
def overlay(self, x, y):
self.add_parameter('overlay', x, y)
self.add_formatparam('overlay', x, y)
return self
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
def scale(self, width=-1, height=-1):
self.add_parameter('scale', width, height)
self.add_formatparam('scale', width, height)
return self
def select(self, expression):
self.add_parameter('select', expression)
self.add_formatparam('select', expression)
return self
def setdar(self, x, y):
self.add_parameter('setdar', x, y)
self.add_formatparam('setdar', x, y)
return self
def setpts(self, expression):
self.add_parameter('setpts', expression)
self.add_formatparam('setpts', expression)
return self
def setsar(self, x, y):
self.add_parameter('setsar', x, y)
self.add_formatparam('setsar', x, y)
return self
def slicify(self, height=16):
self.add_parameter('slicify', height)
self.add_formatparam('slicify', height)
return self
def transpose(self, type):
if str(type) not in ['0', '1','2', '3']:
raise ValueError('Invalid Option for transpose. '
if str(type) not in ['0', '1', '2', '3']:
raise ValueError('Invalid Parameter for transpose. '
'Read FFmpeg manual')
self.add_parameter('transpose', type)
self.add_formatparam('transpose', type)
return self
def unsharp(self, *args):
if len(args) > 6:
message = 'unsharp() takes exactly 6 positional arguments'
raise TypeError(message)
self.add_parameter('unsharp', *args)
self.add_formatparam('unsharp', *args)
return self
def vflip(self):
self.add_option('vflip', None)
self.add_parameter('vflip', None)
return self
def yadif(self, mode=0, parity=-1):
self.add_parameter('yadif', mode, parity)
self.add_formatparam('yadif', mode, parity)
return self
def __iter__(self):
return chain(['-vf', FilterStore.__str__(self)])
return chain(['-vf', FilterContainer.__str__(self)])
class AudioFilter(FilterStore):
"""FilterStore for Audifilters.
class AudioFilter(FilterContainer):
"""FilterContainer for Audifilters.
.. seealso::
@ -188,8 +188,8 @@ class AudioFilter(FilterStore):
def null(self):
"""does nothing"""
self.add_option('null', None)
self.add_parameter('null', None)
return 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
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.
>>> from ffmpegwrapper import FFmpeg, Input, Output, VideoCodec, VideoFilter
@ -18,10 +18,8 @@ resulting command.
"""
import sys
from setuptools import setup
setup(
name="ffmpegwrapper",
version="0.1-dev",

26
test.py
View file

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