| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -3,4 +3,5 @@ | |||
| 3 | 3 | from ._filters import * | |
| 4 | 4 | from ._ffmpeg import * | |
| 5 | 5 | from ._run import * | |
| 6 | - __all__ = _filters.__all__ + _ffmpeg.__all__ + _run.__all__ | ||
| 6 | + from ._view import * | ||
| 7 | + __all__ = _filters.__all__ + _ffmpeg.__all__ + _run.__all__ + _view.__all__ | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,10 +1,11 @@ | |||
| 1 | 1 | from __future__ import unicode_literals | |
| 2 | 2 | from .nodes import ( | |
| 3 | - FilterNode, | ||
| 3 | + filter_operator, | ||
| 4 | 4 | GlobalNode, | |
| 5 | 5 | InputNode, | |
| 6 | - operator, | ||
| 6 | + MergeOutputsNode, | ||
| 7 | 7 | OutputNode, | |
| 8 | + output_operator, | ||
| 8 | 9 | ) | |
| 9 | 10 | ||
| 10 | 11 | ||
@@ -19,27 +20,27 @@ def input(filename, **kwargs): | |||
| 19 | 20 | if 'format' in kwargs: | |
| 20 | 21 | raise ValueError("Can't specify both `format` and `f` kwargs") | |
| 21 | 22 | kwargs['format'] = fmt | |
| 22 | - return InputNode(input.__name__, **kwargs) | ||
| 23 | + return InputNode(input.__name__, kwargs=kwargs).stream() | ||
| 23 | 24 | ||
| 24 | 25 | ||
| 25 | - @operator(node_classes={OutputNode, GlobalNode}) | ||
| 26 | - def overwrite_output(parent_node): | ||
| 26 | + @output_operator() | ||
| 27 | + def overwrite_output(stream): | ||
| 27 | 28 | """Overwrite output files without asking (ffmpeg ``-y`` option) | |
| 28 | 29 | ||
| 29 | 30 | Official documentation: `Main options <https://ffmpeg.org/ffmpeg.html#Main-options>`__ | |
| 30 | 31 | """ | |
| 31 | - return GlobalNode(parent_node, overwrite_output.__name__) | ||
| 32 | + return GlobalNode(stream, overwrite_output.__name__).stream() | ||
| 32 | 33 | ||
| 33 | 34 | ||
| 34 | - @operator(node_classes={OutputNode}) | ||
| 35 | - def merge_outputs(*parent_nodes): | ||
| 35 | + @output_operator() | ||
| 36 | + def merge_outputs(*streams): | ||
| 36 | 37 | """Include all given outputs in one ffmpeg command line | |
| 37 | 38 | """ | |
| 38 | - return OutputNode(parent_nodes, merge_outputs.__name__) | ||
| 39 | + return MergeOutputsNode(streams, merge_outputs.__name__).stream() | ||
| 39 | 40 | ||
| 40 | 41 | ||
| 41 | - @operator(node_classes={InputNode, FilterNode}) | ||
| 42 | - def output(parent_node, filename, **kwargs): | ||
| 42 | + @filter_operator() | ||
| 43 | + def output(stream, filename, **kwargs): | ||
| 43 | 44 | """Output file URL | |
| 44 | 45 | ||
| 45 | 46 | Official documentation: `Synopsis <https://ffmpeg.org/ffmpeg.html#Synopsis>`__ | |
@@ -50,7 +51,7 @@ def output(parent_node, filename, **kwargs): | |||
| 50 | 51 | if 'format' in kwargs: | |
| 51 | 52 | raise ValueError("Can't specify both `format` and `f` kwargs") | |
| 52 | 53 | kwargs['format'] = fmt | |
| 53 | - return OutputNode([parent_node], output.__name__, **kwargs) | ||
| 54 | + return OutputNode(stream, output.__name__, kwargs=kwargs).stream() | ||
| 54 | 55 | ||
| 55 | 56 | ||
| 56 | 57 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,76 +1,67 @@ | |||
| 1 | 1 | from __future__ import unicode_literals | |
| 2 | 2 | from .nodes import ( | |
| 3 | 3 | FilterNode, | |
| 4 | - operator, | ||
| 4 | + filter_operator, | ||
| 5 | 5 | ) | |
| 6 | 6 | ||
| 7 | 7 | ||
| 8 | - @operator() | ||
| 9 | - def filter_(parent_node, filter_name, *args, **kwargs): | ||
| 10 | - """Apply custom single-source filter. | ||
| 8 | + @filter_operator() | ||
| 9 | + def filter_multi_output(stream_spec, filter_name, *args, **kwargs): | ||
| 10 | + """Apply custom filter with one or more outputs. | ||
| 11 | 11 | ||
| 12 | - ``filter_`` is normally used by higher-level filter functions such as ``hflip``, but if a filter implementation | ||
| 13 | - is missing from ``fmpeg-python``, you can call ``filter_`` directly to have ``fmpeg-python`` pass the filter name | ||
| 14 | - and arguments to ffmpeg verbatim. | ||
| 15 | - | ||
| 16 | - Args: | ||
| 17 | - parent_node: Source stream to apply filter to. | ||
| 18 | - filter_name: ffmpeg filter name, e.g. `colorchannelmixer` | ||
| 19 | - *args: list of args to pass to ffmpeg verbatim | ||
| 20 | - **kwargs: list of keyword-args to pass to ffmpeg verbatim | ||
| 12 | + This is the same as ``filter_`` except that the filter can produce more than one output. | ||
| 21 | 13 | ||
| 22 | - This function is used internally by all of the other single-source filters (e.g. ``hflip``, ``crop``, etc.). | ||
| 23 | - For custom multi-source filters, see ``filter_multi`` instead. | ||
| 24 | - | ||
| 25 | - The function name is suffixed with ``_`` in order avoid confusion with the standard python ``filter`` function. | ||
| 14 | + To reference an output stream, use either the ``.stream`` operator or bracket shorthand: | ||
| 26 | 15 | ||
| 27 | 16 | Example: | |
| 28 | 17 | ||
| 29 | - ``ffmpeg.input('in.mp4').filter_('hflip').output('out.mp4').run()`` | ||
| 18 | + ``` | ||
| 19 | + split = ffmpeg.input('in.mp4').filter_multi_output('split') | ||
| 20 | + split0 = split.stream(0) | ||
| 21 | + split1 = split[1] | ||
| 22 | + ffmpeg.concat(split0, split1).output('out.mp4').run() | ||
| 23 | + ``` | ||
| 30 | 24 | """ | |
| 31 | - return FilterNode([parent_node], filter_name, *args, **kwargs) | ||
| 32 | - | ||
| 25 | + return FilterNode(stream_spec, filter_name, args=args, kwargs=kwargs, max_inputs=None) | ||
| 33 | 26 | ||
| 34 | - def filter_multi(parent_nodes, filter_name, *args, **kwargs): | ||
| 35 | - """Apply custom multi-source filter. | ||
| 36 | 27 | ||
| 37 | - This is nearly identical to the ``filter`` function except that it allows filters to be applied to multiple | ||
| 38 | - streams. It's normally used by higher-level filter functions such as ``concat``, but if a filter implementation | ||
| 39 | - is missing from ``fmpeg-python``, you can call ``filter_multi`` directly. | ||
| 28 | + @filter_operator() | ||
| 29 | + def filter_(stream_spec, filter_name, *args, **kwargs): | ||
| 30 | + """Apply custom filter. | ||
| 40 | 31 | ||
| 41 | - Note that because it applies to multiple streams, it can't be used as an operator, unlike the ``filter`` function | ||
| 42 | - (e.g. ``ffmpeg.input('in.mp4').filter_('hflip')``) | ||
| 32 | + ``filter_`` is normally used by higher-level filter functions such as ``hflip``, but if a filter implementation | ||
| 33 | + is missing from ``fmpeg-python``, you can call ``filter_`` directly to have ``fmpeg-python`` pass the filter name | ||
| 34 | + and arguments to ffmpeg verbatim. | ||
| 43 | 35 | ||
| 44 | 36 | Args: | |
| 45 | - parent_nodes: List of source streams to apply filter to. | ||
| 46 | - filter_name: ffmpeg filter name, e.g. `concat` | ||
| 37 | + stream_spec: a Stream, list of Streams, or label-to-Stream dictionary mapping | ||
| 38 | + filter_name: ffmpeg filter name, e.g. `colorchannelmixer` | ||
| 47 | 39 | *args: list of args to pass to ffmpeg verbatim | |
| 48 | 40 | **kwargs: list of keyword-args to pass to ffmpeg verbatim | |
| 49 | 41 | ||
| 50 | - For custom single-source filters, see ``filter_multi`` instead. | ||
| 42 | + The function name is suffixed with ``_`` in order avoid confusion with the standard python ``filter`` function. | ||
| 51 | 43 | ||
| 52 | 44 | Example: | |
| 53 | 45 | ||
| 54 | - ``ffmpeg.filter_multi(ffmpeg.input('in1.mp4'), ffmpeg.input('in2.mp4'), 'concat', n=2).output('out.mp4').run()`` | ||
| 46 | + ``ffmpeg.input('in.mp4').filter_('hflip').output('out.mp4').run()`` | ||
| 55 | 47 | """ | |
| 56 | - return FilterNode(parent_nodes, filter_name, *args, **kwargs) | ||
| 57 | - | ||
| 48 | + return filter_multi_output(stream_spec, filter_name, *args, **kwargs).stream() | ||
| 58 | 49 | ||
| 59 | 50 | ||
| 60 | - @operator() | ||
| 61 | - def setpts(parent_node, expr): | ||
| 51 | + @filter_operator() | ||
| 52 | + def setpts(stream, expr): | ||
| 62 | 53 | """Change the PTS (presentation timestamp) of the input frames. | |
| 63 | 54 | ||
| 64 | 55 | Args: | |
| 65 | 56 | expr: The expression which is evaluated for each frame to construct its timestamp. | |
| 66 | 57 | ||
| 67 | 58 | Official documentation: `setpts, asetpts <https://ffmpeg.org/ffmpeg-filters.html#setpts_002c-asetpts>`__ | |
| 68 | 59 | """ | |
| 69 | - return filter_(parent_node, setpts.__name__, expr) | ||
| 60 | + return FilterNode(stream, setpts.__name__, args=[expr]).stream() | ||
| 70 | 61 | ||
| 71 | 62 | ||
| 72 | - @operator() | ||
| 73 | - def trim(parent_node, **kwargs): | ||
| 63 | + @filter_operator() | ||
| 64 | + def trim(stream, **kwargs): | ||
| 74 | 65 | """Trim the input so that the output contains one continuous subpart of the input. | |
| 75 | 66 | ||
| 76 | 67 | Args: | |
@@ -88,10 +79,10 @@ def trim(parent_node, **kwargs): | |||
| 88 | 79 | ||
| 89 | 80 | Official documentation: `trim <https://ffmpeg.org/ffmpeg-filters.html#trim>`__ | |
| 90 | 81 | """ | |
| 91 | - return filter_(parent_node, trim.__name__, **kwargs) | ||
| 82 | + return FilterNode(stream, trim.__name__, kwargs=kwargs).stream() | ||
| 92 | 83 | ||
| 93 | 84 | ||
| 94 | - @operator() | ||
| 85 | + @filter_operator() | ||
| 95 | 86 | def overlay(main_parent_node, overlay_parent_node, eof_action='repeat', **kwargs): | |
| 96 | 87 | """Overlay one video on top of another. | |
| 97 | 88 | ||
@@ -136,29 +127,29 @@ def overlay(main_parent_node, overlay_parent_node, eof_action='repeat', **kwargs | |||
| 136 | 127 | Official documentation: `overlay <https://ffmpeg.org/ffmpeg-filters.html#overlay-1>`__ | |
| 137 | 128 | """ | |
| 138 | 129 | kwargs['eof_action'] = eof_action | |
| 139 | - return filter_multi([main_parent_node, overlay_parent_node], overlay.__name__, **kwargs) | ||
| 130 | + return FilterNode([main_parent_node, overlay_parent_node], overlay.__name__, kwargs=kwargs, max_inputs=2).stream() | ||
| 140 | 131 | ||
| 141 | 132 | ||
| 142 | - @operator() | ||
| 143 | - def hflip(parent_node): | ||
| 133 | + @filter_operator() | ||
| 134 | + def hflip(stream): | ||
| 144 | 135 | """Flip the input video horizontally. | |
| 145 | 136 | ||
| 146 | 137 | Official documentation: `hflip <https://ffmpeg.org/ffmpeg-filters.html#hflip>`__ | |
| 147 | 138 | """ | |
| 148 | - return filter_(parent_node, hflip.__name__) | ||
| 139 | + return FilterNode(stream, hflip.__name__).stream() | ||
| 149 | 140 | ||
| 150 | 141 | ||
| 151 | - @operator() | ||
| 152 | - def vflip(parent_node): | ||
| 142 | + @filter_operator() | ||
| 143 | + def vflip(stream): | ||
| 153 | 144 | """Flip the input video vertically. | |
| 154 | 145 | ||
| 155 | 146 | Official documentation: `vflip <https://ffmpeg.org/ffmpeg-filters.html#vflip>`__ | |
| 156 | 147 | """ | |
| 157 | - return filter_(parent_node, vflip.__name__) | ||
| 148 | + return FilterNode(stream, vflip.__name__).stream() | ||
| 158 | 149 | ||
| 159 | 150 | ||
| 160 | - @operator() | ||
| 161 | - def drawbox(parent_node, x, y, width, height, color, thickness=None, **kwargs): | ||
| 151 | + @filter_operator() | ||
| 152 | + def drawbox(stream, x, y, width, height, color, thickness=None, **kwargs): | ||
| 162 | 153 | """Draw a colored box on the input image. | |
| 163 | 154 | ||
| 164 | 155 | Args: | |
@@ -179,11 +170,11 @@ def drawbox(parent_node, x, y, width, height, color, thickness=None, **kwargs): | |||
| 179 | 170 | """ | |
| 180 | 171 | if thickness: | |
| 181 | 172 | kwargs['t'] = thickness | |
| 182 | - return filter_(parent_node, drawbox.__name__, x, y, width, height, color, **kwargs) | ||
| 173 | + return FilterNode(stream, drawbox.__name__, args=[x, y, width, height, color], kwargs=kwargs).stream() | ||
| 183 | 174 | ||
| 184 | 175 | ||
| 185 | - @operator() | ||
| 186 | - def concat(*parent_nodes, **kwargs): | ||
| 176 | + @filter_operator() | ||
| 177 | + def concat(*streams, **kwargs): | ||
| 187 | 178 | """Concatenate audio and video streams, joining them together one after the other. | |
| 188 | 179 | ||
| 189 | 180 | The filter works on segments of synchronized video and audio streams. All segments must have the same number of | |
@@ -208,12 +199,12 @@ def concat(*parent_nodes, **kwargs): | |||
| 208 | 199 | ||
| 209 | 200 | Official documentation: `concat <https://ffmpeg.org/ffmpeg-filters.html#concat>`__ | |
| 210 | 201 | """ | |
| 211 | - kwargs['n'] = len(parent_nodes) | ||
| 212 | - return filter_multi(parent_nodes, concat.__name__, **kwargs) | ||
| 202 | + kwargs['n'] = len(streams) | ||
| 203 | + return FilterNode(streams, concat.__name__, kwargs=kwargs, max_inputs=None).stream() | ||
| 213 | 204 | ||
| 214 | 205 | ||
| 215 | - @operator() | ||
| 216 | - def zoompan(parent_node, **kwargs): | ||
| 206 | + @filter_operator() | ||
| 207 | + def zoompan(stream, **kwargs): | ||
| 217 | 208 | """Apply Zoom & Pan effect. | |
| 218 | 209 | ||
| 219 | 210 | Args: | |
@@ -228,11 +219,11 @@ def zoompan(parent_node, **kwargs): | |||
| 228 | 219 | ||
| 229 | 220 | Official documentation: `zoompan <https://ffmpeg.org/ffmpeg-filters.html#zoompan>`__ | |
| 230 | 221 | """ | |
| 231 | - return filter_(parent_node, zoompan.__name__, **kwargs) | ||
| 222 | + return FilterNode(stream, zoompan.__name__, kwargs=kwargs).stream() | ||
| 232 | 223 | ||
| 233 | 224 | ||
| 234 | - @operator() | ||
| 235 | - def hue(parent_node, **kwargs): | ||
| 225 | + @filter_operator() | ||
| 226 | + def hue(stream, **kwargs): | ||
| 236 | 227 | """Modify the hue and/or the saturation of the input. | |
| 237 | 228 | ||
| 238 | 229 | Args: | |
@@ -243,24 +234,23 @@ def hue(parent_node, **kwargs): | |||
| 243 | 234 | ||
| 244 | 235 | Official documentation: `hue <https://ffmpeg.org/ffmpeg-filters.html#hue>`__ | |
| 245 | 236 | """ | |
| 246 | - return filter_(parent_node, hue.__name__, **kwargs) | ||
| 237 | + return FilterNode(stream, hue.__name__, kwargs=kwargs).stream() | ||
| 247 | 238 | ||
| 248 | 239 | ||
| 249 | - @operator() | ||
| 250 | - def colorchannelmixer(parent_node, *args, **kwargs): | ||
| 240 | + @filter_operator() | ||
| 241 | + def colorchannelmixer(stream, *args, **kwargs): | ||
| 251 | 242 | """Adjust video input frames by re-mixing color channels. | |
| 252 | 243 | ||
| 253 | 244 | Official documentation: `colorchannelmixer <https://ffmpeg.org/ffmpeg-filters.html#colorchannelmixer>`__ | |
| 254 | 245 | """ | |
| 255 | - return filter_(parent_node, colorchannelmixer.__name__, **kwargs) | ||
| 246 | + return FilterNode(stream, colorchannelmixer.__name__, kwargs=kwargs).stream() | ||
| 256 | 247 | ||
| 257 | 248 | ||
| 258 | 249 | __all__ = [ | |
| 259 | 250 | 'colorchannelmixer', | |
| 260 | 251 | 'concat', | |
| 261 | 252 | 'drawbox', | |
| 262 | 253 | 'filter_', | |
| 263 | - 'filter_multi', | ||
| 264 | 254 | 'hflip', | |
| 265 | 255 | 'hue', | |
| 266 | 256 | 'overlay', | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -4,22 +4,23 @@ | |||
| 4 | 4 | from functools import reduce | |
| 5 | 5 | from past.builtins import basestring | |
| 6 | 6 | import copy | |
| 7 | - import operator as _operator | ||
| 7 | + import operator | ||
| 8 | 8 | import subprocess as _subprocess | |
| 9 | 9 | ||
| 10 | 10 | from ._ffmpeg import ( | |
| 11 | 11 | input, | |
| 12 | - merge_outputs, | ||
| 13 | 12 | output, | |
| 14 | 13 | overwrite_output, | |
| 15 | 14 | ) | |
| 16 | 15 | from .nodes import ( | |
| 17 | 16 | GlobalNode, | |
| 18 | 17 | InputNode, | |
| 19 | - operator, | ||
| 20 | 18 | OutputNode, | |
| 19 | + output_operator, | ||
| 20 | + Stream, | ||
| 21 | 21 | ) | |
| 22 | 22 | ||
| 23 | + | ||
| 23 | 24 | def _get_stream_name(name): | |
| 24 | 25 | return '[{}]'.format(name) | |
| 25 | 26 | ||
@@ -73,48 +74,47 @@ def _get_global_args(node): | |||
| 73 | 74 | ||
| 74 | 75 | ||
| 75 | 76 | def _get_output_args(node, stream_name_map): | |
| 77 | + if node.name != output.__name__: | ||
| 78 | + raise ValueError('Unsupported output node: {}'.format(node)) | ||
| 76 | 79 | args = [] | |
| 77 | - if node.name != merge_outputs.__name__: | ||
| 78 | - assert len(node.incoming_edges) == 1 | ||
| 79 | - stream_name = stream_name_map[node.incoming_edges[0].upstream_node] | ||
| 80 | - if stream_name != '[0]': | ||
| 81 | - args += ['-map', stream_name] | ||
| 82 | - if node.name == output.__name__: | ||
| 83 | - kwargs = copy.copy(node.kwargs) | ||
| 84 | - filename = kwargs.pop('filename') | ||
| 85 | - fmt = kwargs.pop('format', None) | ||
| 86 | - if fmt: | ||
| 87 | - args += ['-f', fmt] | ||
| 88 | - args += _convert_kwargs_to_cmd_line_args(kwargs) | ||
| 89 | - args += [filename] | ||
| 90 | - else: | ||
| 91 | - raise ValueError('Unsupported output node: {}'.format(node)) | ||
| 80 | + assert len(node.incoming_edges) == 1 | ||
| 81 | + stream_name = stream_name_map[node.incoming_edges[0].upstream_node] | ||
| 82 | + if stream_name != '[0]': | ||
| 83 | + args += ['-map', stream_name] | ||
| 84 | + kwargs = copy.copy(node.kwargs) | ||
| 85 | + filename = kwargs.pop('filename') | ||
| 86 | + fmt = kwargs.pop('format', None) | ||
| 87 | + if fmt: | ||
| 88 | + args += ['-f', fmt] | ||
| 89 | + args += _convert_kwargs_to_cmd_line_args(kwargs) | ||
| 90 | + args += [filename] | ||
| 92 | 91 | return args | |
| 93 | 92 | ||
| 94 | 93 | ||
| 95 | - @operator(node_classes={OutputNode, GlobalNode}) | ||
| 96 | - def get_args(node): | ||
| 94 | + @output_operator() | ||
| 95 | + def get_args(stream): | ||
| 97 | 96 | """Get command-line arguments for ffmpeg.""" | |
| 97 | + if not isinstance(stream, Stream): | ||
| 98 | + raise TypeError('Expected Stream; got {}'.format(type(stream))) | ||
| 98 | 99 | args = [] | |
| 99 | 100 | # TODO: group nodes together, e.g. `-i somefile -r somerate`. | |
| 100 | - sorted_nodes, outgoing_edge_maps = topo_sort([node]) | ||
| 101 | - del(node) | ||
| 101 | + sorted_nodes, outgoing_edge_maps = topo_sort([stream.node]) | ||
| 102 | 102 | input_nodes = [node for node in sorted_nodes if isinstance(node, InputNode)] | |
| 103 | 103 | output_nodes = [node for node in sorted_nodes if isinstance(node, OutputNode) and not | |
| 104 | 104 | isinstance(node, GlobalNode)] | |
| 105 | 105 | global_nodes = [node for node in sorted_nodes if isinstance(node, GlobalNode)] | |
| 106 | 106 | filter_nodes = [node for node in sorted_nodes if node not in (input_nodes + output_nodes + global_nodes)] | |
| 107 | 107 | stream_name_map = {node: _get_stream_name(i) for i, node in enumerate(input_nodes)} | |
| 108 | 108 | filter_arg = _get_filter_arg(filter_nodes, stream_name_map) | |
| 109 | - args += reduce(_operator.add, [_get_input_args(node) for node in input_nodes]) | ||
| 109 | + args += reduce(operator.add, [_get_input_args(node) for node in input_nodes]) | ||
| 110 | 110 | if filter_arg: | |
| 111 | 111 | args += ['-filter_complex', filter_arg] | |
| 112 | - args += reduce(_operator.add, [_get_output_args(node, stream_name_map) for node in output_nodes]) | ||
| 113 | - args += reduce(_operator.add, [_get_global_args(node) for node in global_nodes], []) | ||
| 112 | + args += reduce(operator.add, [_get_output_args(node, stream_name_map) for node in output_nodes]) | ||
| 113 | + args += reduce(operator.add, [_get_global_args(node) for node in global_nodes], []) | ||
| 114 | 114 | return args | |
| 115 | 115 | ||
| 116 | 116 | ||
| 117 | - @operator(node_classes={OutputNode, GlobalNode}) | ||
| 117 | + @output_operator() | ||
| 118 | 118 | def run(node, cmd='ffmpeg'): | |
| 119 | 119 | """Run ffmpeg on node graph.""" | |
| 120 | 120 | if isinstance(cmd, basestring): | |
| Back | FazBrowse Home | New Git URL |
0 commit comments