|
|
|
@ -1,3 +1,6 @@
@@ -1,3 +1,6 @@
|
|
|
|
|
import threading |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def filter_escape(s): |
|
|
|
|
return (str(s) |
|
|
|
|
.replace(':', '\\:') |
|
|
|
@ -5,12 +8,27 @@ def filter_escape(s):
@@ -5,12 +8,27 @@ def filter_escape(s):
|
|
|
|
|
.replace(';', '\\;') |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
class FFmpegInput: |
|
|
|
|
def __init__(self, name, **options): |
|
|
|
|
self.name = name |
|
|
|
|
self.options = options |
|
|
|
|
|
|
|
|
|
def format(self): |
|
|
|
|
args = [] |
|
|
|
|
for (k, v) in self.options.items(): |
|
|
|
|
args.append('-' + k) |
|
|
|
|
if v is not None: |
|
|
|
|
args.append(str(v)) |
|
|
|
|
args.append('-i') |
|
|
|
|
args.append(self.name) |
|
|
|
|
return args |
|
|
|
|
|
|
|
|
|
class FFmpegFilter: |
|
|
|
|
def __init__(self, name, *, ins=[], outs=[], **args): |
|
|
|
|
def __init__(self, name, *, ins=None, outs=None, **args): |
|
|
|
|
self.name = name |
|
|
|
|
self.args = args |
|
|
|
|
self.ins = ins |
|
|
|
|
self.outs = outs |
|
|
|
|
self.ins = ins or [] |
|
|
|
|
self.outs = outs or [] |
|
|
|
|
|
|
|
|
|
def __repr__(self): |
|
|
|
|
return '{}({}, {}, ins={!r}, outs={!r})'.format( |
|
|
|
@ -31,8 +49,10 @@ class FFmpegFilter:
@@ -31,8 +49,10 @@ class FFmpegFilter:
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
class FFmpegChain: |
|
|
|
|
def __init__(self, *filters): |
|
|
|
|
def __init__(self, *filters, inputs=None, runners=None): |
|
|
|
|
self.filters = list(filters) |
|
|
|
|
self.inputs = inputs or [] |
|
|
|
|
self.runners = runners or [] |
|
|
|
|
|
|
|
|
|
def append(self, f): |
|
|
|
|
self.filters.append(f) |
|
|
|
@ -51,9 +71,20 @@ class FFmpegChain:
@@ -51,9 +71,20 @@ class FFmpegChain:
|
|
|
|
|
def __repr__(self): |
|
|
|
|
return '{}({})'.format(self.__class__.__name__, ', '.join(repr(f) for f in self.filters)) |
|
|
|
|
|
|
|
|
|
def __str__(self): |
|
|
|
|
def format_inputs(self): |
|
|
|
|
args = [] |
|
|
|
|
for input in self.inputs: |
|
|
|
|
args.extend(input.format()) |
|
|
|
|
return args |
|
|
|
|
|
|
|
|
|
def format_filters(self): |
|
|
|
|
return ', '.join(str(f) for f in self.filters) |
|
|
|
|
|
|
|
|
|
def run(self): |
|
|
|
|
for r in self.runners: |
|
|
|
|
t = threading.Thread(target=r, daemon=True) |
|
|
|
|
t.start() |
|
|
|
|
|
|
|
|
|
class FFmpegGraph: |
|
|
|
|
def __init__(self, *chains): |
|
|
|
|
self.chains = list(chains) |
|
|
|
@ -75,5 +106,20 @@ class FFmpegGraph:
@@ -75,5 +106,20 @@ class FFmpegGraph:
|
|
|
|
|
def __repr__(self): |
|
|
|
|
return '{}({})'.format(self.__class__.__name__, ', '.join(repr(c) for c in self.chains)) |
|
|
|
|
|
|
|
|
|
def __str__(self): |
|
|
|
|
return '; '.join(str(c) for c in self.chains) |
|
|
|
|
def format(self): |
|
|
|
|
args = [] |
|
|
|
|
|
|
|
|
|
in_no = 0 |
|
|
|
|
for c in self.chains: |
|
|
|
|
args.extend(c.format_inputs()) |
|
|
|
|
for i in range(len(c.inputs)): |
|
|
|
|
c.filters[0].ins.append(str(in_no)) |
|
|
|
|
in_no += 1 |
|
|
|
|
|
|
|
|
|
args.append('-filter_complex') |
|
|
|
|
args.append('; '.join(c.format_filters() for c in self.chains)) |
|
|
|
|
return args |
|
|
|
|
|
|
|
|
|
def run(self): |
|
|
|
|
for c in self.chains: |
|
|
|
|
c.run() |
|
|
|
|