Added more filters and a setup file

This commit is contained in:
Mathias Koehler 2011-10-17 14:39:49 +02:00
parent d832d4da80
commit d12a628c3b
7 changed files with 247 additions and 19 deletions

View file

@ -1,3 +1,5 @@
# -*- coding: utf8 -*- # -*- coding: utf8 -*-
from ffmpegwrapper.ffmpeg import FFmpeg, Input, Output from .ffmpeg import FFmpeg, Input, Output
from .codec import VideoCodec, AudioCodec, NO_AUDIO, NO_VIDEO
from .filter import VideoFilter, AudioFilter

View file

@ -5,6 +5,10 @@ from itertools import chain
from .options import CombinedOptions from .options import CombinedOptions
NO_AUDIO = ['-an']
NO_VIDEO = ['-vn']
class Codec(CombinedOptions): class Codec(CombinedOptions):
def __init__(self, name, *args): def __init__(self, name, *args):
@ -14,11 +18,95 @@ class Codec(CombinedOptions):
class VideoCodec(Codec): class VideoCodec(Codec):
def bitrate(self, bitrate):
self.add_option('-b', str(bitrate))
return self
def frames(self, number):
self.add_option('-vframes', str(number))
return self
def fps(self, fps):
self.add_option('-r', str(fps))
return self
def size(self, x, y):
filter = "{x}x{y}".format(x, y)
self.add_option('-s', filter)
return self
def aspect(self, x, y):
filter = self._format_parameter(x, y)
self.add_option('-aspect', filter)
return self
def bitrate_tolerance(self, tolerance):
self.add_option('-bt', str(tolerance))
return self
def max_bitrate(self, rate):
self.add_option('-maxrate', str(rate))
return self
def min_bitrate(self, rate):
self.add_option('-minrate', str(rate))
return self
def buffer_size(self, size):
self.add_option('-bufsize', str(size))
return self
def pass_number(self, number):
self.add_option('-pass', str(number))
return self
def language(self, lang):
self.add_option('-vlang', str(lang))
return self
def same_quality(self):
self.add_option('-sameq', None)
return self
def preset(self, preset):
self.add_option('-vpre', str(preset))
return self
def __iter__(self): def __iter__(self):
return chain(['-vcodec', self.name], Codec.__iter__(self)) return chain(['-vcodec', self.name], Codec.__iter__(self))
class AudioCodec(Codec): class AudioCodec(Codec):
def frames(self, number):
self.add_option('-aframes', str(number))
return self
def frequence(self, freq):
self.add_option('-ar', str(freq))
return self
def bitrate(self, rate):
self.add_option('-ab', str(rate))
return self
def quality(self, number):
self.add_option('-aq', str(number))
return self
def channels(self, number):
self.add_option('-ac', str(number))
return self
def language(self, lang):
self.add_option('-alang', str(lang))
return self
def preset(self, preset):
self.add_option('-apre', str(preset))
return self
def __iter__(self): def __iter__(self):
return chain(['-acodec', self.name], Codec.__iter__(self)) return chain(['-acodec', self.name], Codec.__iter__(self))

View file

@ -8,14 +8,19 @@
:license: BSD, see LICENSE for more details. :license: BSD, see LICENSE for more details.
""" """
from subprocess import Popen, PIPE
import os
from fcntl import fcntl, F_SETFL, F_GETFL
from select import select
from subprocess import Popen, PIPE, STDOUT
from itertools import chain from itertools import chain
from .options import CombinedOptions, Options from .options import CombinedOptions, Options
class Input(CombinedOptions): class Input(CombinedOptions):
def __init__(self, file, *args): def __init__(self, file, *args):
self.file = file self.file = file
CombinedOptions.__init__(self, *args) CombinedOptions.__init__(self, *args)
@ -25,11 +30,15 @@ class Input(CombinedOptions):
class Output(CombinedOptions): class Output(CombinedOptions):
def __init__(self, file, *args): def __init__(self, file, *args):
self.file = file self.file = file
CombinedOptions.__init__(self, *args) CombinedOptions.__init__(self, *args)
def overwrite(self):
self.add_option('-y', None)
return self
def __iter__(self): def __iter__(self):
return chain(CombinedOptions.__iter__(self), [self.file]) return chain(CombinedOptions.__iter__(self), [self.file])
@ -41,8 +50,17 @@ class FFmpeg(CombinedOptions):
CombinedOptions.__init__(self, *args) CombinedOptions.__init__(self, *args)
def run(self): def run(self):
return Popen(self, executable=self.binary, stdin=PIPE, self.pipe = Popen(self, executable=self.binary,
stdout=PIPE, stderr=PIPE) stderr=PIPE)
fcntl(self.pipe.stderr.fileno(), F_SETFL,
fcntl(self.pipe.stderr.fileno(), F_GETFL) | os.O_NONBLOCK)
return self
def wait_for_data(self):
while True:
ready = select([self.pipe.stderr.fileno()], [], [])[0]
if ready:
return True
def add_option(self, key, value): def add_option(self, key, value):
self._list.insert(0, Options({key: value})) self._list.insert(0, Options({key: value}))

View file

@ -13,7 +13,7 @@ class CombinedFilter(CombinedOptions):
def __iter__(self): def __iter__(self):
for key, value in CombinedOptions.iteritems(self): for key, value in CombinedOptions.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
@ -160,7 +160,7 @@ class VideoFilter(CombinedFilter):
filter = self._format_parameter(*args) filter = self._format_parameter(*args)
self.add_option('unsharp', filter) self.add_option('unsharp', filter)
return self return self
def vflip(self): def vflip(self):
self.add_option('vflip', None) self.add_option('vflip', None)
return self return self

View file

@ -2,7 +2,7 @@
class Options(dict): class Options(dict):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
dict.__init__(self, *args, **kwargs) dict.__init__(self, *args, **kwargs)
@ -35,7 +35,7 @@ class CombinedOptions(object):
def append(self, item): def append(self, item):
self._list.append(item) self._list.append(item)
def insert(self, item): def insert(self, item):
self._list.insert(item) self._list.insert(item)
@ -67,7 +67,7 @@ class CombinedOptions(object):
for option in self._list: for option in self._list:
for item in option.iteritems(): for item in option.iteritems():
yield item yield item
def _format_parameter(self, *args): def _format_parameter(self, *args):
parameter = filter(lambda x: x is not None, args) parameter = filter(lambda x: x is not None, args)
return ':'.join(map(str, parameter)) return ':'.join(map(str, parameter))

14
setup.py Normal file → Executable file
View file

@ -0,0 +1,14 @@
#!/usr/bin/env python
# -*- coding: utf8 -*-
try:
from setuptools import setup
except ImportError:
from distutils.core import setup
setup(
name="ffmpegwrapper",
version="0.1-dev",
packages=['ffmpegwrapper']
)

120
test.py
View file

@ -2,9 +2,8 @@
import unittest import unittest
from ffmpegwrapper import FFmpeg, Input, Output from ffmpegwrapper import FFmpeg, Input, Output, \
from ffmpegwrapper.codec import VideoCodec, AudioCodec VideoCodec, AudioCodec, VideoFilter
from ffmpegwrapper.filter import VideoFilter
from ffmpegwrapper.options import Options from ffmpegwrapper.options import Options
@ -46,12 +45,11 @@ class FFmpegTestCase(unittest.TestCase):
def test_filter_interface(self): def test_filter_interface(self):
filter = VideoFilter() filter = VideoFilter()
filter.blackframe(1, 2).crop(792) filter.blackframe(1, 2).crop(792)
self.assertEqual(list(filter), ['-vf', self.assertEqual(list(filter), ['-vf', 'blackframe=1:2,crop=792'])
'blackframe=1:2,crop=792'])
output = Output('/new', filter) output = Output('/new', filter)
self.assertEqual(list(output), ['-vf', self.assertEqual(
'blackframe=1:2,crop=792', '/new']) list(output), ['-vf', 'blackframe=1:2,crop=792', '/new'])
def test_ffmpeg_interface(self): def test_ffmpeg_interface(self):
input = Input('/old') input = Input('/old')
@ -210,5 +208,113 @@ class VideoFilterTestCase(unittest.TestCase):
self.prefix('yadif=0:-1')) self.prefix('yadif=0:-1'))
class VideoCodecTestCase(unittest.TestCase):
def setUp(self):
self.codec = VideoCodec('libx264')
def prefix(self, *args):
return ['-vcodec', 'libx264'] + list(args)
def test_bitrate(self):
self.codec.bitrate('300k')
self.assertEqual(list(self.codec),
self.prefix('-b', '300k'))
def test_frames(self):
self.codec.frames(100)
self.assertEqual(list(self.codec),
self.prefix('-vframes', '100'))
def test_fps(self):
self.codec.fps(24)
self.assertEqual(list(self.codec),
self.prefix('-r', '24'))
def test_aspect(self):
self.codec.aspect(16, 9)
self.assertEqual(list(self.codec),
self.prefix('-aspect', '16:9'))
def test_bitrate_tolerance(self):
self.codec.bitrate_tolerance(10)
self.assertEqual(list(self.codec),
self.prefix('-bt', '10'))
def test_max_bitrate(self):
self.codec.max_bitrate('100k')
self.assertEqual(list(self.codec),
self.prefix('-maxrate', '100k'))
def test_min_bitrate(self):
self.codec.min_bitrate('50k')
self.assertEqual(list(self.codec),
self.prefix('-minrate', '50k'))
def test_buffer_size(self):
self.codec.buffer_size('20k')
self.assertEqual(list(self.codec),
self.prefix('-bufsize', '20k'))
def test_pass_number(self):
self.codec.pass_number(2)
self.assertEqual(list(self.codec),
self.prefix('-pass', '2'))
def test_language(self):
self.codec.language('DEU')
self.assertEqual(list(self.codec),
self.prefix('-vlang', 'DEU'))
def test_same_quality(self):
self.codec.same_quality()
self.assertEqual(list(self.codec),
self.prefix('-sameq'))
def test_preset(self):
self.codec.preset('max')
self.assertEqual(list(self.codec),
self.prefix('-vpre', 'max'))
class AudioTestCase(unittest.TestCase):
def setUp(self):
self.codec = AudioCodec('AC3')
def prefix(self, *args):
return ['-acodec', 'AC3'] + list(args)
def test_frames(self):
self.codec.frames(100)
self.assertEqual(list(self.codec),
self.prefix('-aframes', '100'))
def test_frequence(self):
self.codec.frequence(48000)
self.assertEqual(list(self.codec),
self.prefix('-ar', '48000'))
def test_bitrate(self):
self.codec.bitrate('320k')
self.assertEqual(list(self.codec),
self.prefix('-ab', '320k'))
def test_quality(self):
self.codec.quality(8)
self.assertEqual(list(self.codec),
self.prefix('-aq', '8'))
def test_language(self):
self.codec.language('DEU')
self.assertEqual(list(self.codec),
self.prefix('-alang', 'DEU'))
def test_preset(self):
self.codec.preset('max')
self.assertEqual(list(self.codec),
self.prefix('-apre', 'max'))
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()