mirror of
https://github.com/kkroening/ffmpeg-python.git
synced 2025-04-06 04:15:44 +08:00
Change selector syntax from [:a]
to [a]
; remove map
operator (for now)
This commit is contained in:
parent
6169b89321
commit
57abf6e86e
@ -9,7 +9,7 @@ from .nodes import (
|
|||||||
MergeOutputsNode,
|
MergeOutputsNode,
|
||||||
OutputNode,
|
OutputNode,
|
||||||
output_operator,
|
output_operator,
|
||||||
OutputStream)
|
)
|
||||||
|
|
||||||
|
|
||||||
def input(filename, **kwargs):
|
def input(filename, **kwargs):
|
||||||
@ -68,26 +68,9 @@ def output(*streams_and_filename, **kwargs):
|
|||||||
return OutputNode(streams, output.__name__, kwargs=kwargs).stream()
|
return OutputNode(streams, output.__name__, kwargs=kwargs).stream()
|
||||||
|
|
||||||
|
|
||||||
@output_operator()
|
|
||||||
def map(*streams):
|
|
||||||
"""Map multiple streams to the same output
|
|
||||||
"""
|
|
||||||
head = streams[0]
|
|
||||||
tail = streams[1:]
|
|
||||||
|
|
||||||
if not isinstance(head, OutputStream):
|
|
||||||
raise ValueError('First argument must be an output stream')
|
|
||||||
|
|
||||||
if not tail:
|
|
||||||
return head
|
|
||||||
|
|
||||||
return OutputNode(head.node, tail).stream()
|
|
||||||
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'input',
|
'input',
|
||||||
'merge_outputs',
|
'merge_outputs',
|
||||||
'output',
|
'output',
|
||||||
'map',
|
|
||||||
'overwrite_output',
|
'overwrite_output',
|
||||||
]
|
]
|
||||||
|
@ -76,10 +76,7 @@ DagEdge = namedtuple('DagEdge', ['downstream_node', 'downstream_label', 'upstrea
|
|||||||
def get_incoming_edges(downstream_node, incoming_edge_map):
|
def get_incoming_edges(downstream_node, incoming_edge_map):
|
||||||
edges = []
|
edges = []
|
||||||
for downstream_label, upstream_info in incoming_edge_map.items():
|
for downstream_label, upstream_info in incoming_edge_map.items():
|
||||||
# `upstream_info` may contain the upstream_selector. [:2] trims it away
|
upstream_node, upstream_label, upstream_selector = upstream_info
|
||||||
upstream_node, upstream_label = upstream_info[:2]
|
|
||||||
# Take into account the stream selector if it's present (i.e. len(upstream_info) >= 3)
|
|
||||||
upstream_selector = None if len(upstream_info) < 3 else upstream_info[2]
|
|
||||||
edges += [DagEdge(downstream_node, downstream_label, upstream_node, upstream_label, upstream_selector)]
|
edges += [DagEdge(downstream_node, downstream_label, upstream_node, upstream_label, upstream_selector)]
|
||||||
return edges
|
return edges
|
||||||
|
|
||||||
@ -88,10 +85,7 @@ def get_outgoing_edges(upstream_node, outgoing_edge_map):
|
|||||||
edges = []
|
edges = []
|
||||||
for upstream_label, downstream_infos in list(outgoing_edge_map.items()):
|
for upstream_label, downstream_infos in list(outgoing_edge_map.items()):
|
||||||
for downstream_info in downstream_infos:
|
for downstream_info in downstream_infos:
|
||||||
# `downstream_info` may contain the downstream_selector. [:2] trims it away
|
downstream_node, downstream_label, downstream_selector = downstream_info
|
||||||
downstream_node, downstream_label = downstream_info[:2]
|
|
||||||
# Take into account the stream selector if it's present
|
|
||||||
downstream_selector = None if len(downstream_info) < 3 else downstream_info[2]
|
|
||||||
edges += [DagEdge(downstream_node, downstream_label, upstream_node, upstream_label, downstream_selector)]
|
edges += [DagEdge(downstream_node, downstream_label, upstream_node, upstream_label, downstream_selector)]
|
||||||
return edges
|
return edges
|
||||||
|
|
||||||
@ -104,10 +98,8 @@ class KwargReprNode(DagNode):
|
|||||||
def __upstream_hashes(self):
|
def __upstream_hashes(self):
|
||||||
hashes = []
|
hashes = []
|
||||||
for downstream_label, upstream_info in self.incoming_edge_map.items():
|
for downstream_label, upstream_info in self.incoming_edge_map.items():
|
||||||
# `upstream_info` may contain the upstream_selector. [:2] trims it away
|
upstream_node, upstream_label, upstream_selector = upstream_info
|
||||||
upstream_node, upstream_label = upstream_info[:2]
|
hashes += [hash(x) for x in [downstream_label, upstream_node, upstream_label, upstream_selector]]
|
||||||
# The stream selector is discarded when calculating the hash: the stream "as a whole" is still the same
|
|
||||||
hashes += [hash(x) for x in [downstream_label, upstream_node, upstream_label]]
|
|
||||||
return hashes
|
return hashes
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
162
ffmpeg/nodes.py
162
ffmpeg/nodes.py
@ -1,9 +1,9 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from .dag import KwargReprNode
|
from .dag import KwargReprNode
|
||||||
from ._utils import escape_chars, get_hash_int
|
from ._utils import escape_chars, get_hash_int
|
||||||
from builtins import object
|
from builtins import object
|
||||||
import os, sys
|
import os
|
||||||
import inspect
|
|
||||||
|
|
||||||
|
|
||||||
def _is_of_types(obj, types):
|
def _is_of_types(obj, types):
|
||||||
@ -19,13 +19,6 @@ def _get_types_str(types):
|
|||||||
return ', '.join(['{}.{}'.format(x.__module__, x.__name__) for x in types])
|
return ', '.join(['{}.{}'.format(x.__module__, x.__name__) for x in types])
|
||||||
|
|
||||||
|
|
||||||
def _get_arg_count(callable):
|
|
||||||
if sys.version_info.major >= 3:
|
|
||||||
return len(inspect.getfullargspec(callable).args)
|
|
||||||
else:
|
|
||||||
return len(inspect.getargspec(callable).args)
|
|
||||||
|
|
||||||
|
|
||||||
class Stream(object):
|
class Stream(object):
|
||||||
"""Represents the outgoing edge of an upstream node; may be used to create more downstream nodes."""
|
"""Represents the outgoing edge of an upstream node; may be used to create more downstream nodes."""
|
||||||
|
|
||||||
@ -37,7 +30,6 @@ class Stream(object):
|
|||||||
self.label = upstream_label
|
self.label = upstream_label
|
||||||
self.selector = upstream_selector
|
self.selector = upstream_selector
|
||||||
|
|
||||||
|
|
||||||
def __hash__(self):
|
def __hash__(self):
|
||||||
return get_hash_int([hash(self.node), hash(self.label)])
|
return get_hash_int([hash(self.node), hash(self.label)])
|
||||||
|
|
||||||
@ -54,14 +46,22 @@ class Stream(object):
|
|||||||
|
|
||||||
def __getitem__(self, index):
|
def __getitem__(self, index):
|
||||||
"""
|
"""
|
||||||
Select a component of the stream. `stream[:X]` is analogous to `stream.node.stream(select=X)`.
|
Select a component (audio, video) of the stream.
|
||||||
Please note that this can only be used to select a substream that already exist. If you want to split
|
|
||||||
the stream, use the `split` filter.
|
|
||||||
"""
|
|
||||||
if not isinstance(index, slice) or index.start is not None:
|
|
||||||
raise ValueError("Invalid syntax. Use `stream[:\'something\']`, not `stream[\'something\']`.")
|
|
||||||
|
|
||||||
return self.node.stream(select=index.stop)
|
Example:
|
||||||
|
|
||||||
|
Process the audio and video portions of a stream independently::
|
||||||
|
|
||||||
|
in = ffmpeg.input('in.mp4')
|
||||||
|
audio = in['a'].filter_("aecho", 0.8, 0.9, 1000, 0.3)
|
||||||
|
video = in['v'].hflip()
|
||||||
|
ffmpeg.output(audio, video, 'out.mp4')
|
||||||
|
"""
|
||||||
|
if self.selector is not None:
|
||||||
|
raise ValueError('Stream already has a selector: {}'.format(self))
|
||||||
|
elif not isinstance(index, basestring):
|
||||||
|
raise TypeError("Expected string index (e.g. 'v'); got {!r}".format(index))
|
||||||
|
return self.node.stream(label=self.label, selector=index)
|
||||||
|
|
||||||
|
|
||||||
def get_stream_map(stream_spec):
|
def get_stream_map(stream_spec):
|
||||||
@ -93,22 +93,6 @@ def get_stream_spec_nodes(stream_spec):
|
|||||||
class Node(KwargReprNode):
|
class Node(KwargReprNode):
|
||||||
"""Node base"""
|
"""Node base"""
|
||||||
|
|
||||||
@property
|
|
||||||
def min_inputs(self):
|
|
||||||
return self.__min_inputs
|
|
||||||
|
|
||||||
@property
|
|
||||||
def max_inputs(self):
|
|
||||||
return self.__max_inputs
|
|
||||||
|
|
||||||
@property
|
|
||||||
def incoming_stream_types(self):
|
|
||||||
return self.__incoming_stream_types
|
|
||||||
|
|
||||||
@property
|
|
||||||
def outgoing_stream_type(self):
|
|
||||||
return self.__outgoing_stream_type
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def __check_input_len(cls, stream_map, min_inputs, max_inputs):
|
def __check_input_len(cls, stream_map, min_inputs, max_inputs):
|
||||||
if min_inputs is not None and len(stream_map) < min_inputs:
|
if min_inputs is not None and len(stream_map) < min_inputs:
|
||||||
@ -130,9 +114,8 @@ class Node(KwargReprNode):
|
|||||||
incoming_edge_map[downstream_label] = (upstream.node, upstream.label, upstream.selector)
|
incoming_edge_map[downstream_label] = (upstream.node, upstream.label, upstream.selector)
|
||||||
return incoming_edge_map
|
return incoming_edge_map
|
||||||
|
|
||||||
def __init_fromscratch__(self, stream_spec, name, incoming_stream_types, outgoing_stream_type, min_inputs,
|
def __init__(self, stream_spec, name, incoming_stream_types, outgoing_stream_type, min_inputs,
|
||||||
max_inputs, args=[],
|
max_inputs, args=[], kwargs={}):
|
||||||
kwargs={}):
|
|
||||||
stream_map = get_stream_map(stream_spec)
|
stream_map = get_stream_map(stream_spec)
|
||||||
self.__check_input_len(stream_map, min_inputs, max_inputs)
|
self.__check_input_len(stream_map, min_inputs, max_inputs)
|
||||||
self.__check_input_types(stream_map, incoming_stream_types)
|
self.__check_input_types(stream_map, incoming_stream_types)
|
||||||
@ -141,92 +124,21 @@ class Node(KwargReprNode):
|
|||||||
super(Node, self).__init__(incoming_edge_map, name, args, kwargs)
|
super(Node, self).__init__(incoming_edge_map, name, args, kwargs)
|
||||||
self.__outgoing_stream_type = outgoing_stream_type
|
self.__outgoing_stream_type = outgoing_stream_type
|
||||||
self.__incoming_stream_types = incoming_stream_types
|
self.__incoming_stream_types = incoming_stream_types
|
||||||
self.__min_inputs = min_inputs
|
|
||||||
self.__max_inputs = max_inputs
|
|
||||||
|
|
||||||
def __init_fromnode__(self, old_node, stream_spec):
|
def stream(self, label=None, selector=None):
|
||||||
# Make sure old node and new node are of the same type
|
|
||||||
if type(self) != type(old_node):
|
|
||||||
raise TypeError('`old_node` should be of type {}'.format(self.__class__.__name__))
|
|
||||||
|
|
||||||
# Copy needed data from old node
|
|
||||||
name = old_node.name
|
|
||||||
incoming_stream_types = old_node.incoming_stream_types
|
|
||||||
outgoing_stream_type = old_node.outgoing_stream_type
|
|
||||||
min_inputs = old_node.min_inputs
|
|
||||||
max_inputs = old_node.max_inputs
|
|
||||||
prev_edges = old_node.incoming_edge_map.values()
|
|
||||||
args = old_node.args
|
|
||||||
kwargs = old_node.kwargs
|
|
||||||
|
|
||||||
# Check new stream spec - the old spec should have already been checked
|
|
||||||
new_stream_map = get_stream_map(stream_spec)
|
|
||||||
self.__check_input_types(new_stream_map, incoming_stream_types)
|
|
||||||
|
|
||||||
# Generate new edge map
|
|
||||||
new_inc_edge_map = self.__get_incoming_edge_map(new_stream_map)
|
|
||||||
new_edges = new_inc_edge_map.values()
|
|
||||||
|
|
||||||
# Rename all edges
|
|
||||||
new_edge_map = dict(enumerate(list(prev_edges) + list(new_edges)))
|
|
||||||
|
|
||||||
# Check new length
|
|
||||||
self.__check_input_len(new_edge_map, min_inputs, max_inputs)
|
|
||||||
|
|
||||||
super(Node, self).__init__(new_edge_map, name, args, kwargs)
|
|
||||||
self.__outgoing_stream_type = outgoing_stream_type
|
|
||||||
self.__incoming_stream_types = incoming_stream_types
|
|
||||||
self.__min_inputs = min_inputs
|
|
||||||
self.__max_inputs = max_inputs
|
|
||||||
|
|
||||||
# noinspection PyMissingConstructor
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
"""
|
|
||||||
If called with the following arguments, the new Node is created from scratch:
|
|
||||||
- stream_spec, name, incoming_stream_types, outgoing_stream_type, min_inputs, max_inputs, args=[], kwargs={}
|
|
||||||
|
|
||||||
If called with the following arguments, the new node is a copy of `old_node` that includes the additional
|
|
||||||
`stream_spec`:
|
|
||||||
- old_node, stream_spec
|
|
||||||
"""
|
|
||||||
# Python doesn't support constructor overloading. This hacky code detects how we want to construct the object
|
|
||||||
# based on the number of arguments and the type of the first argument, then calls the appropriate constructor
|
|
||||||
# helper method
|
|
||||||
|
|
||||||
# "1+" is for `self`
|
|
||||||
argc = 1 + len(args) + len(kwargs)
|
|
||||||
|
|
||||||
first_arg = None
|
|
||||||
if 'old_node' in kwargs:
|
|
||||||
first_arg = kwargs['old_node']
|
|
||||||
elif len(args) > 0:
|
|
||||||
first_arg = args[0]
|
|
||||||
|
|
||||||
if argc == _get_arg_count(self.__init_fromnode__) and type(first_arg) == type(self):
|
|
||||||
self.__init_fromnode__(*args, **kwargs)
|
|
||||||
else:
|
|
||||||
if isinstance(first_arg, Node):
|
|
||||||
raise ValueError(
|
|
||||||
'{}.__init__() received an instance of {} as the first argument. If you want to create a '
|
|
||||||
'copy of an existing node, the types must match and you must provide an additional stream_spec.'
|
|
||||||
.format(self.__class__.__name__, first_arg.__class__.__name__)
|
|
||||||
)
|
|
||||||
self.__init_fromscratch__(*args, **kwargs)
|
|
||||||
|
|
||||||
def stream(self, label=None, select=None):
|
|
||||||
"""Create an outgoing stream originating from this node.
|
"""Create an outgoing stream originating from this node.
|
||||||
|
|
||||||
More nodes may be attached onto the outgoing stream.
|
More nodes may be attached onto the outgoing stream.
|
||||||
"""
|
"""
|
||||||
return self.__outgoing_stream_type(self, label, upstream_selector=select)
|
return self.__outgoing_stream_type(self, label, upstream_selector=selector)
|
||||||
|
|
||||||
def __getitem__(self, item):
|
def __getitem__(self, item):
|
||||||
"""Create an outgoing stream originating from this node; syntactic sugar for ``self.stream(label)``.
|
"""Create an outgoing stream originating from this node; syntactic sugar for ``self.stream(label)``.
|
||||||
It can also be used to apply a selector: e.g. ``node[0:'audio']`` returns a stream with label 0 and
|
It can also be used to apply a selector: e.g. ``node[0:'audio']`` returns a stream with label 0 and
|
||||||
selector ``'audio'``, which is the same as ``node.stream(label=0, select='audio')``.
|
selector ``'audio'``, which is the same as ``node.stream(label=0, selector='audio')``.
|
||||||
"""
|
"""
|
||||||
if isinstance(item, slice):
|
if isinstance(item, slice):
|
||||||
return self.stream(label=item.start, select=item.stop)
|
return self.stream(label=item.start, selector=item.stop)
|
||||||
else:
|
else:
|
||||||
return self.stream(label=item)
|
return self.stream(label=item)
|
||||||
|
|
||||||
@ -241,8 +153,8 @@ class FilterableStream(Stream):
|
|||||||
class InputNode(Node):
|
class InputNode(Node):
|
||||||
"""InputNode type"""
|
"""InputNode type"""
|
||||||
|
|
||||||
def __init_fromscratch__(self, name, args=[], kwargs={}):
|
def __init__(self, name, args=[], kwargs={}):
|
||||||
super(InputNode, self).__init_fromscratch__(
|
super(InputNode, self).__init__(
|
||||||
stream_spec=None,
|
stream_spec=None,
|
||||||
name=name,
|
name=name,
|
||||||
incoming_stream_types={},
|
incoming_stream_types={},
|
||||||
@ -253,9 +165,6 @@ class InputNode(Node):
|
|||||||
kwargs=kwargs
|
kwargs=kwargs
|
||||||
)
|
)
|
||||||
|
|
||||||
def __init_fromnode__(self, old_node, stream_spec):
|
|
||||||
raise TypeError("{} can't be constructed from an existing node".format(self.__class__.__name__))
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def short_repr(self):
|
def short_repr(self):
|
||||||
return os.path.basename(self.kwargs['filename'])
|
return os.path.basename(self.kwargs['filename'])
|
||||||
@ -263,8 +172,8 @@ class InputNode(Node):
|
|||||||
|
|
||||||
# noinspection PyMethodOverriding
|
# noinspection PyMethodOverriding
|
||||||
class FilterNode(Node):
|
class FilterNode(Node):
|
||||||
def __init_fromscratch__(self, stream_spec, name, max_inputs=1, args=[], kwargs={}):
|
def __init__(self, stream_spec, name, max_inputs=1, args=[], kwargs={}):
|
||||||
super(FilterNode, self).__init_fromscratch__(
|
super(FilterNode, self).__init__(
|
||||||
stream_spec=stream_spec,
|
stream_spec=stream_spec,
|
||||||
name=name,
|
name=name,
|
||||||
incoming_stream_types={FilterableStream},
|
incoming_stream_types={FilterableStream},
|
||||||
@ -303,13 +212,13 @@ class FilterNode(Node):
|
|||||||
|
|
||||||
# noinspection PyMethodOverriding
|
# noinspection PyMethodOverriding
|
||||||
class OutputNode(Node):
|
class OutputNode(Node):
|
||||||
def __init_fromscratch__(self, stream, name, args=[], kwargs={}):
|
def __init__(self, stream, name, args=[], kwargs={}):
|
||||||
super(OutputNode, self).__init_fromscratch__(
|
super(OutputNode, self).__init__(
|
||||||
stream_spec=stream,
|
stream_spec=stream,
|
||||||
name=name,
|
name=name,
|
||||||
incoming_stream_types={FilterableStream},
|
incoming_stream_types={FilterableStream},
|
||||||
outgoing_stream_type=OutputStream,
|
outgoing_stream_type=OutputStream,
|
||||||
min_inputs=0, # Allow streams to be mapped afterwards
|
min_inputs=1,
|
||||||
max_inputs=None,
|
max_inputs=None,
|
||||||
args=args,
|
args=args,
|
||||||
kwargs=kwargs
|
kwargs=kwargs
|
||||||
@ -328,8 +237,8 @@ class OutputStream(Stream):
|
|||||||
|
|
||||||
# noinspection PyMethodOverriding
|
# noinspection PyMethodOverriding
|
||||||
class MergeOutputsNode(Node):
|
class MergeOutputsNode(Node):
|
||||||
def __init_fromscratch__(self, streams, name):
|
def __init__(self, streams, name):
|
||||||
super(MergeOutputsNode, self).__init_fromscratch__(
|
super(MergeOutputsNode, self).__init__(
|
||||||
stream_spec=streams,
|
stream_spec=streams,
|
||||||
name=name,
|
name=name,
|
||||||
incoming_stream_types={OutputStream},
|
incoming_stream_types={OutputStream},
|
||||||
@ -341,8 +250,8 @@ class MergeOutputsNode(Node):
|
|||||||
|
|
||||||
# noinspection PyMethodOverriding
|
# noinspection PyMethodOverriding
|
||||||
class GlobalNode(Node):
|
class GlobalNode(Node):
|
||||||
def __init_fromscratch__(self, stream, name, args=[], kwargs={}):
|
def __init__(self, stream, name, args=[], kwargs={}):
|
||||||
super(GlobalNode, self).__init_fromscratch__(
|
super(GlobalNode, self).__init__(
|
||||||
stream_spec=stream,
|
stream_spec=stream,
|
||||||
name=name,
|
name=name,
|
||||||
incoming_stream_types={OutputStream},
|
incoming_stream_types={OutputStream},
|
||||||
@ -353,9 +262,6 @@ class GlobalNode(Node):
|
|||||||
kwargs=kwargs
|
kwargs=kwargs
|
||||||
)
|
)
|
||||||
|
|
||||||
def __init_fromnode__(self, old_node, stream_spec):
|
|
||||||
raise TypeError("{} can't be constructed from an existing node".format(self.__class__.__name__))
|
|
||||||
|
|
||||||
|
|
||||||
def stream_operator(stream_classes={Stream}, name=None):
|
def stream_operator(stream_classes={Stream}, name=None):
|
||||||
def decorator(func):
|
def decorator(func):
|
||||||
|
@ -147,16 +147,25 @@ def test_get_args_complex_filter():
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
def _get_filter_with_select_example():
|
def test_combined_output():
|
||||||
|
i1 = ffmpeg.input(TEST_INPUT_FILE1)
|
||||||
|
i2 = ffmpeg.input(TEST_OVERLAY_FILE)
|
||||||
|
out = ffmpeg.output(i1, i2, TEST_OUTPUT_FILE1)
|
||||||
|
assert out.get_args() == [
|
||||||
|
'-i', TEST_INPUT_FILE1,
|
||||||
|
'-i', TEST_OVERLAY_FILE,
|
||||||
|
'-map', '[0]',
|
||||||
|
'-map', '[1]',
|
||||||
|
TEST_OUTPUT_FILE1
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def test_filter_with_selector():
|
||||||
i = ffmpeg.input(TEST_INPUT_FILE1)
|
i = ffmpeg.input(TEST_INPUT_FILE1)
|
||||||
v1 = i[:'v'].hflip()
|
v1 = i['v'].hflip()
|
||||||
a1 = i[:'a'].filter_('aecho', 0.8, 0.9, 1000, 0.3)
|
a1 = i['a'].filter_('aecho', 0.8, 0.9, 1000, 0.3)
|
||||||
|
out = ffmpeg.output(a1, v1, TEST_OUTPUT_FILE1)
|
||||||
return ffmpeg.output(a1, v1, TEST_OUTPUT_FILE1)
|
assert out.get_args() == [
|
||||||
|
|
||||||
|
|
||||||
def test_filter_with_select():
|
|
||||||
assert _get_filter_with_select_example().get_args() == [
|
|
||||||
'-i', TEST_INPUT_FILE1,
|
'-i', TEST_INPUT_FILE1,
|
||||||
'-filter_complex',
|
'-filter_complex',
|
||||||
'[0:a]aecho=0.8:0.9:1000:0.3[s0];' \
|
'[0:a]aecho=0.8:0.9:1000:0.3[s0];' \
|
||||||
@ -166,26 +175,6 @@ def test_filter_with_select():
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
def test_map_same_effect_as_output():
|
|
||||||
i1 = ffmpeg.input(TEST_INPUT_FILE1)
|
|
||||||
i2 = ffmpeg.input(TEST_OVERLAY_FILE)
|
|
||||||
|
|
||||||
_o_map = i1.output(TEST_OUTPUT_FILE1)
|
|
||||||
o_map = _o_map.map(i2)
|
|
||||||
|
|
||||||
o_nomap = ffmpeg.output(i1, i2, TEST_OUTPUT_FILE1)
|
|
||||||
|
|
||||||
assert id(o_map) != id(_o_map) # Checks immutability
|
|
||||||
assert o_map.node.incoming_edge_map == o_nomap.node.incoming_edge_map
|
|
||||||
assert o_map.get_args() == o_nomap.get_args() == [
|
|
||||||
'-i', TEST_INPUT_FILE1,
|
|
||||||
'-i', TEST_OVERLAY_FILE,
|
|
||||||
'-map', '[0]',
|
|
||||||
'-map', '[1]',
|
|
||||||
TEST_OUTPUT_FILE1
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
def _get_complex_filter_asplit_example():
|
def _get_complex_filter_asplit_example():
|
||||||
split = (ffmpeg
|
split = (ffmpeg
|
||||||
.input(TEST_INPUT_FILE1)
|
.input(TEST_INPUT_FILE1)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user