Added more filters and a setup file
This commit is contained in:
parent
d832d4da80
commit
d12a628c3b
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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))
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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}))
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
14
setup.py
Normal file → Executable 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
120
test.py
|
|
@ -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()
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue