Implement SourceNode

This commit is contained in:
Davide Depau 2018-01-25 12:25:20 +01:00
parent 19f316e9c5
commit 1cb5abaa25
No known key found for this signature in database
GPG Key ID: C7D999B6A55EFE86
3 changed files with 68 additions and 20 deletions

View File

@ -7,7 +7,7 @@ from .nodes import (
MergeOutputsNode,
OutputNode,
output_operator,
)
SourceNode)
def input(filename, **kwargs):
@ -24,6 +24,30 @@ def input(filename, **kwargs):
return InputNode(input.__name__, kwargs=kwargs).stream()
def source_multi_output(filter_name, *args, **kwargs):
"""Apply custom filter with one or more outputs.
This is the same as ``filter_`` except that the filter can produce more than one output.
To reference an output stream, use either the ``.stream`` operator or bracket shorthand:
Example:
```
split = ffmpeg.input('in.mp4').filter_multi_output('split')
split0 = split.stream(0)
split1 = split[1]
ffmpeg.concat(split0, split1).output('out.mp4').run()
```
"""
return SourceNode(filter_name, args=args, kwargs=kwargs)
def source(filter_name, *args, **kwargs):
return source_multi_output(filter_name, *args, **kwargs).stream()
@output_operator()
def overwrite_output(stream):
"""Overwrite output files without asking (ffmpeg ``-y`` option)
@ -58,6 +82,8 @@ def output(stream, filename, **kwargs):
__all__ = [
'input',
'source_multi_output',
'source',
'merge_outputs',
'output',
'overwrite_output',

View File

@ -19,7 +19,7 @@ from .nodes import (
InputNode,
OutputNode,
output_operator,
)
SourceNode)
def _get_stream_name(name):
@ -118,10 +118,11 @@ def get_args(stream_spec, overwrite_output=False):
input_nodes = [node for node in sorted_nodes if isinstance(node, InputNode)]
output_nodes = [node for node in sorted_nodes if isinstance(node, OutputNode)]
global_nodes = [node for node in sorted_nodes if isinstance(node, GlobalNode)]
filter_nodes = [node for node in sorted_nodes if isinstance(node, FilterNode)]
filter_nodes = [node for node in sorted_nodes if isinstance(node, (FilterNode, SourceNode))]
stream_name_map = {(node, None): _get_stream_name(i) for i, node in enumerate(input_nodes)}
filter_arg = _get_filter_arg(filter_nodes, outgoing_edge_maps, stream_name_map)
args += reduce(operator.add, [_get_input_args(node) for node in input_nodes])
if len(input_nodes) > 0:
args += reduce(operator.add, [_get_input_args(node) for node in input_nodes])
if filter_arg:
args += ['-filter_complex', filter_arg]
args += reduce(operator.add, [_get_output_args(node, stream_name_map) for node in output_nodes])

View File

@ -21,6 +21,7 @@ def _get_types_str(types):
class Stream(object):
"""Represents the outgoing edge of an upstream node; may be used to create more downstream nodes."""
def __init__(self, upstream_node, upstream_label, node_types):
if not _is_of_types(upstream_node, node_types):
raise TypeError('Expected upstream node to be of one of the following type(s): {}; got {}'.format(
@ -68,6 +69,7 @@ def get_stream_spec_nodes(stream_spec):
class Node(KwargReprNode):
"""Node base"""
@classmethod
def __check_input_len(cls, stream_map, min_inputs, max_inputs):
if min_inputs is not None and len(stream_map) < min_inputs:
@ -80,7 +82,7 @@ class Node(KwargReprNode):
for stream in list(stream_map.values()):
if not _is_of_types(stream, incoming_stream_types):
raise TypeError('Expected incoming stream(s) to be of one of the following types: {}; got {}'
.format(_get_types_str(incoming_stream_types), type(stream)))
.format(_get_types_str(incoming_stream_types), type(stream)))
@classmethod
def __get_incoming_edge_map(cls, stream_map):
@ -90,7 +92,7 @@ class Node(KwargReprNode):
return incoming_edge_map
def __init__(self, stream_spec, name, incoming_stream_types, outgoing_stream_type, min_inputs, max_inputs, args=[],
kwargs={}):
kwargs={}):
stream_map = get_stream_map(stream_spec)
self.__check_input_len(stream_map, min_inputs, max_inputs)
self.__check_input_types(stream_map, incoming_stream_types)
@ -113,11 +115,12 @@ class Node(KwargReprNode):
class FilterableStream(Stream):
def __init__(self, upstream_node, upstream_label):
super(FilterableStream, self).__init__(upstream_node, upstream_label, {InputNode, FilterNode})
super(FilterableStream, self).__init__(upstream_node, upstream_label, {InputNode, FilterNode, SourceNode})
class InputNode(Node):
"""InputNode type"""
def __init__(self, name, args=[], kwargs={}):
super(InputNode, self).__init__(
stream_spec=None,
@ -135,20 +138,9 @@ class InputNode(Node):
return os.path.basename(self.kwargs['filename'])
class FilterNode(Node):
def __init__(self, stream_spec, name, max_inputs=1, args=[], kwargs={}):
super(FilterNode, self).__init__(
stream_spec=stream_spec,
name=name,
incoming_stream_types={FilterableStream},
outgoing_stream_type=FilterableStream,
min_inputs=1,
max_inputs=max_inputs,
args=args,
kwargs=kwargs
)
class FilterableNode(Node):
"""FilterableNode"""
"""FilterNode"""
def _get_filter(self, outgoing_edges):
args = self.args
kwargs = self.kwargs
@ -173,6 +165,34 @@ class FilterNode(Node):
return escape_chars(params_text, '\\\'[],;')
class FilterNode(FilterableNode):
def __init__(self, stream_spec, name, max_inputs=1, args=[], kwargs={}):
super(FilterNode, self).__init__(
stream_spec=stream_spec,
name=name,
incoming_stream_types={FilterableStream},
outgoing_stream_type=FilterableStream,
min_inputs=1,
max_inputs=max_inputs,
args=args,
kwargs=kwargs
)
class SourceNode(FilterableNode):
def __init__(self, name, args=[], kwargs={}):
super(SourceNode, self).__init__(
stream_spec=None,
name=name,
incoming_stream_types={},
outgoing_stream_type=FilterableStream,
min_inputs=0,
max_inputs=0,
args=args,
kwargs=kwargs
)
class OutputNode(Node):
def __init__(self, stream, name, args=[], kwargs={}):
super(OutputNode, self).__init__(
@ -227,6 +247,7 @@ def stream_operator(stream_classes={Stream}, name=None):
func_name = name or func.__name__
[setattr(stream_class, func_name, func) for stream_class in stream_classes]
return func
return decorator