Re-apply Black formatting, and wrap docstrings at ~88 columns. (#639)

This commit is contained in:
Karl Kroening 2022-03-06 13:24:40 -08:00 committed by GitHub
parent f3079726fa
commit fd1da13f11
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 339 additions and 222 deletions

View File

@ -34,8 +34,7 @@ def input(filename, **kwargs):
@output_operator()
def global_args(stream, *args):
"""Add extra global command-line argument(s), e.g. ``-progress``.
"""
"""Add extra global command-line argument(s), e.g. ``-progress``."""
return GlobalNode(stream, global_args.__name__, args).stream()
@ -50,8 +49,7 @@ def overwrite_output(stream):
@output_operator()
def merge_outputs(*streams):
"""Include all given outputs in one ffmpeg command line
"""
"""Include all given outputs in one ffmpeg command line"""
return MergeOutputsNode(streams, merge_outputs.__name__).stream()

View File

@ -8,9 +8,11 @@ from ._utils import escape_chars
def filter_multi_output(stream_spec, 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.
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:
To reference an output stream, use either the ``.stream`` operator or bracket
shorthand:
Example:
@ -30,9 +32,10 @@ def filter_multi_output(stream_spec, filter_name, *args, **kwargs):
def filter(stream_spec, filter_name, *args, **kwargs):
"""Apply custom filter.
``filter_`` is normally used by higher-level filter functions such as ``hflip``, but if a filter implementation
is missing from ``ffmpeg-python``, you can call ``filter_`` directly to have ``ffmpeg-python`` pass the filter name
and arguments to ffmpeg verbatim.
``filter_`` is normally used by higher-level filter functions such as ``hflip``,
but if a filter implementation is missing from ``ffmpeg-python``, you can call
``filter_`` directly to have ``ffmpeg-python`` pass the filter name and arguments
to ffmpeg verbatim.
Args:
stream_spec: a Stream, list of Streams, or label-to-Stream dictionary mapping
@ -40,7 +43,8 @@ def filter(stream_spec, filter_name, *args, **kwargs):
*args: list of args to pass to ffmpeg verbatim
**kwargs: list of keyword-args to pass to ffmpeg verbatim
The function name is suffixed with ``_`` in order avoid confusion with the standard python ``filter`` function.
The function name is suffixed with ``_`` in order avoid confusion with the standard
python ``filter`` function.
Example:
@ -72,7 +76,8 @@ def setpts(stream, expr):
"""Change the PTS (presentation timestamp) of the input frames.
Args:
expr: The expression which is evaluated for each frame to construct its timestamp.
expr: The expression which is evaluated for each frame to construct its
timestamp.
Official documentation: `setpts, asetpts <https://ffmpeg.org/ffmpeg-filters.html#setpts_002c-asetpts>`__
"""
@ -84,14 +89,15 @@ def trim(stream, **kwargs):
"""Trim the input so that the output contains one continuous subpart of the input.
Args:
start: Specify the time of the start of the kept section, i.e. the frame with the timestamp start will be the
first frame in the output.
end: Specify the time of the first frame that will be dropped, i.e. the frame immediately preceding the one
with the timestamp end will be the last frame in the output.
start_pts: This is the same as start, except this option sets the start timestamp in timebase units instead of
seconds.
end_pts: This is the same as end, except this option sets the end timestamp in timebase units instead of
seconds.
start: Specify the time of the start of the kept section, i.e. the frame with
the timestamp start will be the first frame in the output.
end: Specify the time of the first frame that will be dropped, i.e. the frame
immediately preceding the one with the timestamp end will be the last frame
in the output.
start_pts: This is the same as start, except this option sets the start
timestamp in timebase units instead of seconds.
end_pts: This is the same as end, except this option sets the end timestamp in
timebase units instead of seconds.
duration: The maximum duration of the output in seconds.
start_frame: The number of the first frame that should be passed to the output.
end_frame: The number of the first frame that should be dropped.
@ -106,14 +112,16 @@ def overlay(main_parent_node, overlay_parent_node, eof_action='repeat', **kwargs
"""Overlay one video on top of another.
Args:
x: Set the expression for the x coordinates of the overlaid video on the main video. Default value is 0. In
case the expression is invalid, it is set to a huge value (meaning that the overlay will not be displayed
within the output visible area).
y: Set the expression for the y coordinates of the overlaid video on the main video. Default value is 0. In
case the expression is invalid, it is set to a huge value (meaning that the overlay will not be displayed
within the output visible area).
eof_action: The action to take when EOF is encountered on the secondary input; it accepts one of the following
values:
x: Set the expression for the x coordinates of the overlaid video on the main
video. Default value is 0. In case the expression is invalid, it is set to
a huge value (meaning that the overlay will not be displayed within the
output visible area).
y: Set the expression for the y coordinates of the overlaid video on the main
video. Default value is 0. In case the expression is invalid, it is set to
a huge value (meaning that the overlay will not be displayed within the
output visible area).
eof_action: The action to take when EOF is encountered on the secondary input;
it accepts one of the following values:
* ``repeat``: Repeat the last frame (the default).
* ``endall``: End both streams.
@ -122,12 +130,13 @@ def overlay(main_parent_node, overlay_parent_node, eof_action='repeat', **kwargs
eval: Set when the expressions for x, and y are evaluated.
It accepts the following values:
* ``init``: only evaluate expressions once during the filter initialization or when a command is
processed
* ``init``: only evaluate expressions once during the filter initialization
or when a command is processed
* ``frame``: evaluate expressions for each incoming frame
Default value is ``frame``.
shortest: If set to 1, force the output to terminate when the shortest input terminates. Default value is 0.
shortest: If set to 1, force the output to terminate when the shortest input
terminates. Default value is 0.
format: Set the format for the output video.
It accepts the following values:
@ -138,10 +147,12 @@ def overlay(main_parent_node, overlay_parent_node, eof_action='repeat', **kwargs
* ``gbrp``: force planar RGB output
Default value is ``yuv420``.
rgb (deprecated): If set to 1, force the filter to accept inputs in the RGB color space. Default value is 0.
This option is deprecated, use format instead.
repeatlast: If set to 1, force the filter to draw the last overlay frame over the main input until the end of
the stream. A value of 0 disables this behavior. Default value is 1.
rgb (deprecated): If set to 1, force the filter to accept inputs in the RGB
color space. Default value is 0. This option is deprecated, use format
instead.
repeatlast: If set to 1, force the filter to draw the last overlay frame over
the main input until the end of the stream. A value of 0 disables this
behavior. Default value is 1.
Official documentation: `overlay <https://ffmpeg.org/ffmpeg-filters.html#overlay-1>`__
"""
@ -196,14 +207,20 @@ def drawbox(stream, x, y, width, height, color, thickness=None, **kwargs):
"""Draw a colored box on the input image.
Args:
x: The expression which specifies the top left corner x coordinate of the box. It defaults to 0.
y: The expression which specifies the top left corner y coordinate of the box. It defaults to 0.
width: Specify the width of the box; if 0 interpreted as the input width. It defaults to 0.
heigth: Specify the height of the box; if 0 interpreted as the input height. It defaults to 0.
color: Specify the color of the box to write. For the general syntax of this option, check the "Color" section
in the ffmpeg-utils manual. If the special value invert is used, the box edge color is the same as the
video with inverted luma.
thickness: The expression which sets the thickness of the box edge. Default value is 3.
x: The expression which specifies the top left corner x coordinate of the box.
It defaults to 0.
y: The expression which specifies the top left corner y coordinate of the box.
It defaults to 0.
width: Specify the width of the box; if 0 interpreted as the input width. It
defaults to 0.
heigth: Specify the height of the box; if 0 interpreted as the input height. It
defaults to 0.
color: Specify the color of the box to write. For the general syntax of this
option, check the "Color" section in the ffmpeg-utils manual. If the
special value invert is used, the box edge color is the same as the video
with inverted luma.
thickness: The expression which sets the thickness of the box edge. Default
value is 3.
w: Alias for ``width``.
h: Alias for ``height``.
c: Alias for ``color``.
@ -220,46 +237,57 @@ def drawbox(stream, x, y, width, height, color, thickness=None, **kwargs):
@filter_operator()
def drawtext(stream, text=None, x=0, y=0, escape_text=True, **kwargs):
"""Draw a text string or text from a specified file on top of a video, using the libfreetype library.
"""Draw a text string or text from a specified file on top of a video, using the
libfreetype library.
To enable compilation of this filter, you need to configure FFmpeg with ``--enable-libfreetype``. To enable default
font fallback and the font option you need to configure FFmpeg with ``--enable-libfontconfig``. To enable the
To enable compilation of this filter, you need to configure FFmpeg with
``--enable-libfreetype``. To enable default font fallback and the font option you
need to configure FFmpeg with ``--enable-libfontconfig``. To enable the
text_shaping option, you need to configure FFmpeg with ``--enable-libfribidi``.
Args:
box: Used to draw a box around text using the background color. The value must be either 1 (enable) or 0
(disable). The default value of box is 0.
boxborderw: Set the width of the border to be drawn around the box using boxcolor. The default value of
boxborderw is 0.
boxcolor: The color to be used for drawing box around text. For the syntax of this option, check the "Color"
section in the ffmpeg-utils manual. The default value of boxcolor is "white".
line_spacing: Set the line spacing in pixels of the border to be drawn around the box using box. The default
value of line_spacing is 0.
borderw: Set the width of the border to be drawn around the text using bordercolor. The default value of
borderw is 0.
bordercolor: Set the color to be used for drawing border around text. For the syntax of this option, check the
"Color" section in the ffmpeg-utils manual. The default value of bordercolor is "black".
expansion: Select how the text is expanded. Can be either none, strftime (deprecated) or normal (default). See
the Text expansion section below for details.
basetime: Set a start time for the count. Value is in microseconds. Only applied in the deprecated strftime
expansion mode. To emulate in normal expansion mode use the pts function, supplying the start time (in
seconds) as the second argument.
box: Used to draw a box around text using the background color. The value must
be either 1 (enable) or 0 (disable). The default value of box is 0.
boxborderw: Set the width of the border to be drawn around the box using
boxcolor. The default value of boxborderw is 0.
boxcolor: The color to be used for drawing box around text. For the syntax of
this option, check the "Color" section in the ffmpeg-utils manual. The
default value of boxcolor is "white".
line_spacing: Set the line spacing in pixels of the border to be drawn around
the box using box. The default value of line_spacing is 0.
borderw: Set the width of the border to be drawn around the text using
bordercolor. The default value of borderw is 0.
bordercolor: Set the color to be used for drawing border around text. For the
syntax of this option, check the "Color" section in the ffmpeg-utils
manual. The default value of bordercolor is "black".
expansion: Select how the text is expanded. Can be either none, strftime
(deprecated) or normal (default). See the Text expansion section below for
details.
basetime: Set a start time for the count. Value is in microseconds. Only
applied in the deprecated strftime expansion mode. To emulate in normal
expansion mode use the pts function, supplying the start time (in seconds)
as the second argument.
fix_bounds: If true, check and fix text coords to avoid clipping.
fontcolor: The color to be used for drawing fonts. For the syntax of this option, check the "Color" section in
the ffmpeg-utils manual. The default value of fontcolor is "black".
fontcolor_expr: String which is expanded the same way as text to obtain dynamic fontcolor value. By default
this option has empty value and is not processed. When this option is set, it overrides fontcolor option.
fontcolor: The color to be used for drawing fonts. For the syntax of this
option, check the "Color" section in the ffmpeg-utils manual. The default
value of fontcolor is "black".
fontcolor_expr: String which is expanded the same way as text to obtain dynamic
fontcolor value. By default this option has empty value and is not
processed. When this option is set, it overrides fontcolor option.
font: The font family to be used for drawing text. By default Sans.
fontfile: The font file to be used for drawing text. The path must be included. This parameter is mandatory if
the fontconfig support is disabled.
alpha: Draw the text applying alpha blending. The value can be a number between 0.0 and 1.0. The expression
accepts the same variables x, y as well. The default value is 1. Please see fontcolor_expr.
fontsize: The font size to be used for drawing text. The default value of fontsize is 16.
text_shaping: If set to 1, attempt to shape the text (for example, reverse the order of right-to-left text and
join Arabic characters) before drawing it. Otherwise, just draw the text exactly as given. By default 1 (if
supported).
ft_load_flags: The flags to be used for loading the fonts. The flags map the corresponding flags supported by
libfreetype, and are a combination of the following values:
fontfile: The font file to be used for drawing text. The path must be included.
This parameter is mandatory if the fontconfig support is disabled.
alpha: Draw the text applying alpha blending. The value can be a number between
0.0 and 1.0. The expression accepts the same variables x, y as well. The
default value is 1. Please see fontcolor_expr.
fontsize: The font size to be used for drawing text. The default value of
fontsize is 16.
text_shaping: If set to 1, attempt to shape the text (for example, reverse the
order of right-to-left text and join Arabic characters) before drawing it.
Otherwise, just draw the text exactly as given. By default 1 (if supported).
ft_load_flags: The flags to be used for loading the fonts. The flags map the
corresponding flags supported by libfreetype, and are a combination of the
following values:
* ``default``
* ``no_scale``
@ -277,75 +305,89 @@ def drawtext(stream, text=None, x=0, y=0, escape_text=True, **kwargs):
* ``linear_design``
* ``no_autohint``
Default value is "default". For more information consult the documentation for the FT_LOAD_* libfreetype
flags.
shadowcolor: The color to be used for drawing a shadow behind the drawn text. For the syntax of this option,
check the "Color" section in the ffmpeg-utils manual. The default value of shadowcolor is "black".
shadowx: The x offset for the text shadow position with respect to the position of the text. It can be either
positive or negative values. The default value is "0".
shadowy: The y offset for the text shadow position with respect to the position of the text. It can be either
positive or negative values. The default value is "0".
start_number: The starting frame number for the n/frame_num variable. The default value is "0".
tabsize: The size in number of spaces to use for rendering the tab. Default value is 4.
timecode: Set the initial timecode representation in "hh:mm:ss[:;.]ff" format. It can be used with or without
text parameter. timecode_rate option must be specified.
Default value is "default". For more information consult the documentation
for the FT_LOAD_* libfreetype flags.
shadowcolor: The color to be used for drawing a shadow behind the drawn text.
For the syntax of this option, check the "Color" section in the ffmpeg-utils
manual. The default value of shadowcolor is "black".
shadowx: The x offset for the text shadow position with respect to the position
of the text. It can be either positive or negative values. The default value
is "0".
shadowy: The y offset for the text shadow position with respect to the position
of the text. It can be either positive or negative values. The default value
is "0".
start_number: The starting frame number for the n/frame_num variable. The
default value is "0".
tabsize: The size in number of spaces to use for rendering the tab. Default
value is 4.
timecode: Set the initial timecode representation in "hh:mm:ss[:;.]ff" format.
It can be used with or without text parameter. timecode_rate option must be
specified.
rate: Set the timecode frame rate (timecode only).
timecode_rate: Alias for ``rate``.
r: Alias for ``rate``.
tc24hmax: If set to 1, the output of the timecode option will wrap around at 24 hours. Default is 0 (disabled).
text: The text string to be drawn. The text must be a sequence of UTF-8 encoded characters. This parameter is
mandatory if no file is specified with the parameter textfile.
textfile: A text file containing text to be drawn. The text must be a sequence of UTF-8 encoded characters.
This parameter is mandatory if no text string is specified with the parameter text. If both text and
textfile are specified, an error is thrown.
reload: If set to 1, the textfile will be reloaded before each frame. Be sure to update it atomically, or it
may be read partially, or even fail.
x: The expression which specifies the offset where text will be drawn within the video frame. It is relative to
the left border of the output image. The default value is "0".
y: The expression which specifies the offset where text will be drawn within the video frame. It is relative to
the top border of the output image. The default value is "0". See below for the list of accepted constants
and functions.
tc24hmax: If set to 1, the output of the timecode option will wrap around at 24
hours. Default is 0 (disabled).
text: The text string to be drawn. The text must be a sequence of UTF-8 encoded
characters. This parameter is mandatory if no file is specified with the
parameter textfile.
textfile: A text file containing text to be drawn. The text must be a sequence
of UTF-8 encoded characters. This parameter is mandatory if no text string
is specified with the parameter text. If both text and textfile are
specified, an error is thrown.
reload: If set to 1, the textfile will be reloaded before each frame. Be sure
to update it atomically, or it may be read partially, or even fail.
x: The expression which specifies the offset where text will be drawn within
the video frame. It is relative to the left border of the output image. The
default value is "0".
y: The expression which specifies the offset where text will be drawn within
the video frame. It is relative to the top border of the output image. The
default value is "0". See below for the list of accepted constants and
functions.
Expression constants:
The parameters for x and y are expressions containing the following constants and functions:
- dar: input display aspect ratio, it is the same as ``(w / h) * sar``
- hsub: horizontal chroma subsample values. For example for the pixel format "yuv422p" hsub is 2 and vsub
is 1.
- vsub: vertical chroma subsample values. For example for the pixel format "yuv422p" hsub is 2 and vsub
is 1.
- line_h: the height of each text line
- lh: Alias for ``line_h``.
- main_h: the input height
- h: Alias for ``main_h``.
- H: Alias for ``main_h``.
- main_w: the input width
- w: Alias for ``main_w``.
- W: Alias for ``main_w``.
- ascent: the maximum distance from the baseline to the highest/upper grid coordinate used to place a glyph
outline point, for all the rendered glyphs. It is a positive value, due to the grid's orientation with the Y
axis upwards.
- max_glyph_a: Alias for ``ascent``.
- descent: the maximum distance from the baseline to the lowest grid coordinate used to place a glyph outline
point, for all the rendered glyphs. This is a negative value, due to the grid's orientation, with the Y axis
upwards.
- max_glyph_d: Alias for ``descent``.
- max_glyph_h: maximum glyph height, that is the maximum height for all the glyphs contained in the rendered
text, it is equivalent to ascent - descent.
- max_glyph_w: maximum glyph width, that is the maximum width for all the glyphs contained in the rendered
text.
- n: the number of input frame, starting from 0
- rand(min, max): return a random number included between min and max
- sar: The input sample aspect ratio.
- t: timestamp expressed in seconds, NAN if the input timestamp is unknown
- text_h: the height of the rendered text
- th: Alias for ``text_h``.
- text_w: the width of the rendered text
- tw: Alias for ``text_w``.
- x: the x offset coordinates where the text is drawn.
- y: the y offset coordinates where the text is drawn.
The parameters for x and y are expressions containing the following constants
and functions:
- dar: input display aspect ratio, it is the same as ``(w / h) * sar``
- hsub: horizontal chroma subsample values. For example for the pixel format
"yuv422p" hsub is 2 and vsub is 1.
- vsub: vertical chroma subsample values. For example for the pixel format
"yuv422p" hsub is 2 and vsub is 1.
- line_h: the height of each text line
- lh: Alias for ``line_h``.
- main_h: the input height
- h: Alias for ``main_h``.
- H: Alias for ``main_h``.
- main_w: the input width
- w: Alias for ``main_w``.
- W: Alias for ``main_w``.
- ascent: the maximum distance from the baseline to the highest/upper grid
coordinate used to place a glyph outline point, for all the rendered glyphs.
It is a positive value, due to the grid's orientation with the Y axis
upwards.
- max_glyph_a: Alias for ``ascent``.
- descent: the maximum distance from the baseline to the lowest grid
coordinate used to place a glyph outline
point, for all the rendered glyphs. This is a negative value, due to the
grid's orientation, with the Y axis upwards.
- max_glyph_d: Alias for ``descent``.
- max_glyph_h: maximum glyph height, that is the maximum height for all the
glyphs contained in the rendered text, it is equivalent to ascent - descent.
- max_glyph_w: maximum glyph width, that is the maximum width for all the
glyphs contained in the rendered text.
- n: the number of input frame, starting from 0
- rand(min, max): return a random number included between min and max
- sar: The input sample aspect ratio.
- t: timestamp expressed in seconds, NAN if the input timestamp is unknown
- text_h: the height of the rendered text
- th: Alias for ``text_h``.
- text_w: the width of the rendered text
- tw: Alias for ``text_w``.
- x: the x offset coordinates where the text is drawn.
- y: the y offset coordinates where the text is drawn.
These parameters allow the x and y expressions to refer each other, so you can for example specify
``y=x/dar``.
These parameters allow the x and y expressions to refer each other, so you can
for example specify ``y=x/dar``.
Official documentation: `drawtext <https://ffmpeg.org/ffmpeg-filters.html#drawtext>`__
"""
@ -364,25 +406,28 @@ def drawtext(stream, text=None, x=0, y=0, escape_text=True, **kwargs):
def concat(*streams, **kwargs):
"""Concatenate audio and video streams, joining them together one after the other.
The filter works on segments of synchronized video and audio streams. All segments must have the same number of
streams of each type, and that will also be the number of streams at output.
The filter works on segments of synchronized video and audio streams. All segments
must have the same number of streams of each type, and that will also be the number
of streams at output.
Args:
unsafe: Activate unsafe mode: do not fail if segments have a different format.
Related streams do not always have exactly the same duration, for various reasons including codec frame size or
sloppy authoring. For that reason, related synchronized streams (e.g. a video and its audio track) should be
concatenated at once. The concat filter will use the duration of the longest stream in each segment (except the
last one), and if necessary pad shorter audio streams with silence.
Related streams do not always have exactly the same duration, for various reasons
including codec frame size or sloppy authoring. For that reason, related
synchronized streams (e.g. a video and its audio track) should be concatenated at
once. The concat filter will use the duration of the longest stream in each segment
(except the last one), and if necessary pad shorter audio streams with silence.
For this filter to work correctly, all segments must start at timestamp 0.
All corresponding streams must have the same parameters in all segments; the filtering system will automatically
select a common pixel format for video streams, and a common sample format, sample rate and channel layout for
audio streams, but other settings, such as resolution, must be converted explicitly by the user.
All corresponding streams must have the same parameters in all segments; the
filtering system will automatically select a common pixel format for video streams,
and a common sample format, sample rate and channel layout for audio streams, but
other settings, such as resolution, must be converted explicitly by the user.
Different frame rates are acceptable but will result in variable frame rate at output; be sure to configure the
output file to handle it.
Different frame rates are acceptable but will result in variable frame rate at
output; be sure to configure the output file to handle it.
Official documentation: `concat <https://ffmpeg.org/ffmpeg-filters.html#concat>`__
"""
@ -407,8 +452,8 @@ def zoompan(stream, **kwargs):
zoom: Set the zoom expression. Default is 1.
x: Set the x expression. Default is 0.
y: Set the y expression. Default is 0.
d: Set the duration expression in number of frames. This sets for how many number of frames effect will last
for single input image.
d: Set the duration expression in number of frames. This sets for how many
number of frames effect will last for single input image.
s: Set the output image size, default is ``hd720``.
fps: Set the output frame rate, default is 25.
z: Alias for ``zoom``.
@ -423,10 +468,14 @@ def hue(stream, **kwargs):
"""Modify the hue and/or the saturation of the input.
Args:
h: Specify the hue angle as a number of degrees. It accepts an expression, and defaults to "0".
s: Specify the saturation in the [-10,10] range. It accepts an expression and defaults to "1".
H: Specify the hue angle as a number of radians. It accepts an expression, and defaults to "0".
b: Specify the brightness in the [-10,10] range. It accepts an expression and defaults to "0".
h: Specify the hue angle as a number of degrees. It accepts an expression, and
defaults to "0".
s: Specify the saturation in the [-10,10] range. It accepts an expression and
defaults to "1".
H: Specify the hue angle as a number of radians. It accepts an expression, and
defaults to "0".
b: Specify the brightness in the [-10,10] range. It accepts an expression and
defaults to "0".
Official documentation: `hue <https://ffmpeg.org/ffmpeg-filters.html#hue>`__
"""

View File

@ -88,8 +88,8 @@ def _allocate_filter_stream_names(filter_nodes, outgoing_edge_maps, stream_name_
if len(downstreams) > 1:
# TODO: automatically insert `splits` ahead of time via graph transformation.
raise ValueError(
'Encountered {} with multiple outgoing edges with same upstream label {!r}; a '
'`split` filter is probably required'.format(
'Encountered {} with multiple outgoing edges with same upstream '
'label {!r}; a `split` filter is probably required'.format(
upstream_node, upstream_label
)
)
@ -199,7 +199,7 @@ def run_async(
pipe_stderr=False,
quiet=False,
overwrite_output=False,
cwd=None
cwd=None,
):
"""Asynchronously invoke ffmpeg for the supplied node graph.
@ -286,8 +286,11 @@ def run_async(
stderr_stream = subprocess.STDOUT
stdout_stream = subprocess.DEVNULL
return subprocess.Popen(
args, stdin=stdin_stream, stdout=stdout_stream, stderr=stderr_stream,
cwd=cwd
args,
stdin=stdin_stream,
stdout=stdout_stream,
stderr=stderr_stream,
cwd=cwd,
)
@ -300,7 +303,7 @@ def run(
input=None,
quiet=False,
overwrite_output=False,
cwd=None
cwd=None,
):
"""Invoke ffmpeg for the supplied node graph.
@ -324,7 +327,7 @@ def run(
pipe_stderr=capture_stderr,
quiet=quiet,
overwrite_output=overwrite_output,
cwd=cwd
cwd=cwd,
)
out, err = process.communicate(input)
retcode = process.poll()
@ -333,4 +336,10 @@ def run(
return out, err
__all__ = ['compile', 'Error', 'get_args', 'run', 'run_async']
__all__ = [
'compile',
'Error',
'get_args',
'run',
'run_async',
]

View File

@ -49,8 +49,8 @@ else:
def _recursive_repr(item):
"""Hack around python `repr` to deterministically represent dictionaries.
This is able to represent more things than json.dumps, since it does not require things to be JSON serializable
(e.g. datetimes).
This is able to represent more things than json.dumps, since it does not require
things to be JSON serializable (e.g. datetimes).
"""
if isinstance(item, basestring):
result = str(item)

View File

@ -35,8 +35,8 @@ def view(stream_spec, detail=False, filename=None, pipe=False, **kwargs):
import graphviz
except ImportError:
raise ImportError(
'failed to import graphviz; please make sure graphviz is installed (e.g. `pip install '
'graphviz`)'
'failed to import graphviz; please make sure graphviz is installed (e.g. '
'`pip install graphviz`)'
)
show_labels = kwargs.pop('show_labels', True)

View File

@ -9,38 +9,45 @@ class DagNode(object):
"""Node in a directed-acyclic graph (DAG).
Edges:
DagNodes are connected by edges. An edge connects two nodes with a label for each side:
DagNodes are connected by edges. An edge connects two nodes with a label for
each side:
- ``upstream_node``: upstream/parent node
- ``upstream_label``: label on the outgoing side of the upstream node
- ``downstream_node``: downstream/child node
- ``downstream_label``: label on the incoming side of the downstream node
For example, DagNode A may be connected to DagNode B with an edge labelled "foo" on A's side, and "bar" on B's
side:
For example, DagNode A may be connected to DagNode B with an edge labelled
"foo" on A's side, and "bar" on B's side:
_____ _____
| | | |
| A >[foo]---[bar]> B |
|_____| |_____|
Edge labels may be integers or strings, and nodes cannot have more than one incoming edge with the same label.
Edge labels may be integers or strings, and nodes cannot have more than one
incoming edge with the same label.
DagNodes may have any number of incoming edges and any number of outgoing edges. DagNodes keep track only of
their incoming edges, but the entire graph structure can be inferred by looking at the furthest downstream
nodes and working backwards.
DagNodes may have any number of incoming edges and any number of outgoing
edges. DagNodes keep track only of their incoming edges, but the entire graph
structure can be inferred by looking at the furthest downstream nodes and
working backwards.
Hashing:
DagNodes must be hashable, and two nodes are considered to be equivalent if they have the same hash value.
DagNodes must be hashable, and two nodes are considered to be equivalent if
they have the same hash value.
Nodes are immutable, and the hash should remain constant as a result. If a node with new contents is required,
create a new node and throw the old one away.
Nodes are immutable, and the hash should remain constant as a result. If a
node with new contents is required, create a new node and throw the old one
away.
String representation:
In order for graph visualization tools to show useful information, nodes must be representable as strings. The
``repr`` operator should provide a more or less "full" representation of the node, and the ``short_repr``
property should be a shortened, concise representation.
In order for graph visualization tools to show useful information, nodes must
be representable as strings. The ``repr`` operator should provide a more or
less "full" representation of the node, and the ``short_repr`` property should
be a shortened, concise representation.
Again, because nodes are immutable, the string representations should remain constant.
Again, because nodes are immutable, the string representations should remain
constant.
"""
def __hash__(self):
@ -48,7 +55,9 @@ class DagNode(object):
raise NotImplementedError()
def __eq__(self, other):
"""Compare two nodes; implementations should return True if (and only if) hashes match."""
"""Compare two nodes; implementations should return True if (and only if)
hashes match.
"""
raise NotImplementedError()
def __repr__(self, other):
@ -64,8 +73,9 @@ class DagNode(object):
def incoming_edge_map(self):
"""Provides information about all incoming edges that connect to this node.
The edge map is a dictionary that maps an ``incoming_label`` to ``(outgoing_node, outgoing_label)``. Note that
implicity, ``incoming_node`` is ``self``. See "Edges" section above.
The edge map is a dictionary that maps an ``incoming_label`` to
``(outgoing_node, outgoing_label)``. Note that implicity, ``incoming_node`` is
``self``. See "Edges" section above.
"""
raise NotImplementedError()
@ -116,8 +126,7 @@ def get_outgoing_edges(upstream_node, outgoing_edge_map):
class KwargReprNode(DagNode):
"""A DagNode that can be represented as a set of args+kwargs.
"""
"""A DagNode that can be represented as a set of args+kwargs."""
@property
def __upstream_hashes(self):

View File

@ -21,7 +21,9 @@ def _get_types_str(types):
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.
"""
def __init__(
self, upstream_node, upstream_label, node_types, upstream_selector=None
@ -214,9 +216,10 @@ class Node(KwargReprNode):
return self.__outgoing_stream_type(self, label, upstream_selector=selector)
def __getitem__(self, item):
"""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:'a']`` returns a stream with label 0 and
selector ``'a'``, which is the same as ``node.stream(label=0, selector='a')``.
"""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:'a']`` returns a stream with label 0 and selector ``'a'``, which is
the same as ``node.stream(label=0, selector='a')``.
Example:
Process the audio and video portions of a stream independently::

View File

@ -116,9 +116,20 @@ def test_stream_repr():
dummy_out.label, dummy_out.node.short_hash
)
def test_repeated_args():
out_file = ffmpeg.input('dummy.mp4').output('dummy2.mp4', streamid=['0:0x101', '1:0x102'])
assert out_file.get_args() == ['-i', 'dummy.mp4', '-streamid', '0:0x101', '-streamid', '1:0x102', 'dummy2.mp4']
out_file = ffmpeg.input('dummy.mp4').output(
'dummy2.mp4', streamid=['0:0x101', '1:0x102']
)
assert out_file.get_args() == [
'-i',
'dummy.mp4',
'-streamid',
'0:0x101',
'-streamid',
'1:0x102',
'dummy2.mp4',
]
def test__get_args__simple():
@ -332,8 +343,13 @@ def test_filter_asplit():
'-i',
TEST_INPUT_FILE1,
'-filter_complex',
'[0]vflip[s0];[s0]asplit=2[s1][s2];[s1]atrim=end=20:start=10[s3];[s2]atrim=end=40:start=30[s4];[s3]'
'[s4]concat=n=2[s5]',
(
'[0]vflip[s0];'
'[s0]asplit=2[s1][s2];'
'[s1]atrim=end=20:start=10[s3];'
'[s2]atrim=end=40:start=30[s4];'
'[s3][s4]concat=n=2[s5]'
),
'-map',
'[s5]',
TEST_OUTPUT_FILE1,
@ -357,10 +373,14 @@ def test__output__video_size(video_size):
def test_filter_normal_arg_escape():
"""Test string escaping of normal filter args (e.g. ``font`` param of ``drawtext`` filter)."""
"""Test string escaping of normal filter args (e.g. ``font`` param of ``drawtext``
filter).
"""
def _get_drawtext_font_repr(font):
"""Build a command-line arg using drawtext ``font`` param and extract the ``-filter_complex`` arg."""
"""Build a command-line arg using drawtext ``font`` param and extract the
``-filter_complex`` arg.
"""
args = (
ffmpeg.input('in')
.drawtext('test', font='a{}b'.format(font))
@ -370,7 +390,9 @@ def test_filter_normal_arg_escape():
assert args[:3] == ['-i', 'in', '-filter_complex']
assert args[4:] == ['-map', '[s0]', 'out']
match = re.match(
r'\[0\]drawtext=font=a((.|\n)*)b:text=test\[s0\]', args[3], re.MULTILINE
r'\[0\]drawtext=font=a((.|\n)*)b:text=test\[s0\]',
args[3],
re.MULTILINE,
)
assert match is not None, 'Invalid -filter_complex arg: {!r}'.format(args[3])
return match.group(1)
@ -394,10 +416,14 @@ def test_filter_normal_arg_escape():
def test_filter_text_arg_str_escape():
"""Test string escaping of normal filter args (e.g. ``text`` param of ``drawtext`` filter)."""
"""Test string escaping of normal filter args (e.g. ``text`` param of ``drawtext``
filter).
"""
def _get_drawtext_text_repr(text):
"""Build a command-line arg using drawtext ``text`` param and extract the ``-filter_complex`` arg."""
"""Build a command-line arg using drawtext ``text`` param and extract the
``-filter_complex`` arg.
"""
args = ffmpeg.input('in').drawtext('a{}b'.format(text)).output('out').get_args()
assert args[:3] == ['-i', 'in', '-filter_complex']
assert args[4:] == ['-map', '[s0]', 'out']
@ -447,8 +473,11 @@ def test__run_async(mocker, pipe_stdin, pipe_stdout, pipe_stderr, cwd):
popen__mock = mocker.patch.object(subprocess, 'Popen', return_value=process__mock)
stream = _get_simple_example()
process = ffmpeg.run_async(
stream, pipe_stdin=pipe_stdin, pipe_stdout=pipe_stdout,
pipe_stderr=pipe_stderr, cwd=cwd
stream,
pipe_stdin=pipe_stdin,
pipe_stdout=pipe_stdout,
pipe_stderr=pipe_stderr,
cwd=cwd,
)
assert process is process__mock
@ -458,8 +487,10 @@ def test__run_async(mocker, pipe_stdin, pipe_stdout, pipe_stderr, cwd):
(args,), kwargs = popen__mock.call_args
assert args == ffmpeg.compile(stream)
assert kwargs == dict(
stdin=expected_stdin, stdout=expected_stdout, stderr=expected_stderr,
cwd=cwd
stdin=expected_stdin,
stdout=expected_stdout,
stderr=expected_stderr,
cwd=cwd,
)
@ -695,7 +726,10 @@ def test_pipe():
cmd = ['ffmpeg'] + args
p = subprocess.Popen(
cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE
cmd,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
in_data = bytes(
@ -715,10 +749,10 @@ def test__probe():
assert data['format']['duration'] == '7.036000'
@pytest.mark.skipif(sys.version_info < (3, 3), reason="requires python3.3 or higher")
@pytest.mark.skipif(sys.version_info < (3, 3), reason='requires python3.3 or higher')
def test__probe_timeout():
with pytest.raises(subprocess.TimeoutExpired) as excinfo:
data = ffmpeg.probe(TEST_INPUT_FILE1, timeout=0)
ffmpeg.probe(TEST_INPUT_FILE1, timeout=0)
assert 'timed out after 0 seconds' in str(excinfo.value)
@ -751,24 +785,24 @@ def get_filter_complex_outputs(flt, name):
def test__get_filter_complex_input():
assert get_filter_complex_input("", "scale") is None
assert get_filter_complex_input("scale", "scale") is None
assert get_filter_complex_input("scale[s3][s4];etc", "scale") is None
assert get_filter_complex_input("[s2]scale", "scale") == "s2"
assert get_filter_complex_input("[s2]scale;etc", "scale") == "s2"
assert get_filter_complex_input("[s2]scale[s3][s4];etc", "scale") == "s2"
assert get_filter_complex_input('', 'scale') is None
assert get_filter_complex_input('scale', 'scale') is None
assert get_filter_complex_input('scale[s3][s4];etc', 'scale') is None
assert get_filter_complex_input('[s2]scale', 'scale') == 's2'
assert get_filter_complex_input('[s2]scale;etc', 'scale') == 's2'
assert get_filter_complex_input('[s2]scale[s3][s4];etc', 'scale') == 's2'
def test__get_filter_complex_outputs():
assert get_filter_complex_outputs("", "scale") is None
assert get_filter_complex_outputs("scale", "scale") is None
assert get_filter_complex_outputs("scalex[s0][s1]", "scale") is None
assert get_filter_complex_outputs("scale[s0][s1]", "scale") == ['s0', 's1']
assert get_filter_complex_outputs("[s5]scale[s0][s1]", "scale") == ['s0', 's1']
assert get_filter_complex_outputs("[s5]scale[s1][s0]", "scale") == ['s1', 's0']
assert get_filter_complex_outputs("[s5]scale[s1]", "scale") == ['s1']
assert get_filter_complex_outputs("[s5]scale[s1];x", "scale") == ['s1']
assert get_filter_complex_outputs("y;[s5]scale[s1];x", "scale") == ['s1']
assert get_filter_complex_outputs('', 'scale') is None
assert get_filter_complex_outputs('scale', 'scale') is None
assert get_filter_complex_outputs('scalex[s0][s1]', 'scale') is None
assert get_filter_complex_outputs('scale[s0][s1]', 'scale') == ['s0', 's1']
assert get_filter_complex_outputs('[s5]scale[s0][s1]', 'scale') == ['s0', 's1']
assert get_filter_complex_outputs('[s5]scale[s1][s0]', 'scale') == ['s1', 's0']
assert get_filter_complex_outputs('[s5]scale[s1]', 'scale') == ['s1']
assert get_filter_complex_outputs('[s5]scale[s1];x', 'scale') == ['s1']
assert get_filter_complex_outputs('y;[s5]scale[s1];x', 'scale') == ['s1']
def test__multi_output_edge_label_order():

15
pyproject.toml Normal file
View File

@ -0,0 +1,15 @@
[tool.black]
skip-string-normalization = true
target_version = ['py27'] # TODO: drop Python 2 support (... "Soon").
include = '\.pyi?$'
exclude = '''
(
/(
\.eggs
| \.git
| \.tox
| \venv
| dist
)/
)
'''