diff --git a/docs/.buildinfo b/docs/.buildinfo new file mode 100644 index 00000000..d8363d71 --- /dev/null +++ b/docs/.buildinfo @@ -0,0 +1,4 @@ +# Sphinx build info version 1 +# This file records the configuration used when building these files. When it is not found, a full rebuild will be done. +config: fd025ad979da4ca6d9dd1de108d893b1 +tags: 645f666f9bcd5a90fca523b33c5a78b7 diff --git a/docs/_images/options-1.png b/docs/_images/options-1.png new file mode 100644 index 00000000..ce6f3393 Binary files /dev/null and b/docs/_images/options-1.png differ diff --git a/docs/_images/options-2.png b/docs/_images/options-2.png new file mode 100644 index 00000000..1e65da84 Binary files /dev/null and b/docs/_images/options-2.png differ diff --git a/docs/_images/options-3.png b/docs/_images/options-3.png new file mode 100644 index 00000000..d7a11369 Binary files /dev/null and b/docs/_images/options-3.png differ diff --git a/docs/_images/quick-1.png b/docs/_images/quick-1.png new file mode 100644 index 00000000..ce6f3393 Binary files /dev/null and b/docs/_images/quick-1.png differ diff --git a/docs/_images/quick-2.png b/docs/_images/quick-2.png new file mode 100644 index 00000000..ce6f3393 Binary files /dev/null and b/docs/_images/quick-2.png differ diff --git a/docs/_images/quick-3.png b/docs/_images/quick-3.png new file mode 100644 index 00000000..927d28eb Binary files /dev/null and b/docs/_images/quick-3.png differ diff --git a/docs/_images/quick-4.png b/docs/_images/quick-4.png new file mode 100644 index 00000000..927d28eb Binary files /dev/null and b/docs/_images/quick-4.png differ diff --git a/docs/_sources/adv-args.rst.txt b/docs/_sources/adv-args.rst.txt new file mode 100644 index 00000000..f1351cd9 --- /dev/null +++ b/docs/_sources/adv-args.rst.txt @@ -0,0 +1,112 @@ +.. _adv_args: + +Specification of FFmpeg Argument dict :code:`ffmpeg_args` +========================================================= + +FFmpeg command can be invoked directly with :py:func:`ffmpegio.ffmpegprocess.run` or +:py:class:`ffmpegio.ffmpegprocess.Popen` (see :ref:`the reference page ` +for the details). Both of them fully support the FFmpeg command line option +arguments, which can be specified via as :py:mod:`subprocess` via :code:`ffmpeg_args` +argument, which may be supplied as a string or a list of strings to be compatible +with :py:mod:`subprocess` in a plain dict object. + +The FFmpeg command line options structure: + +.. code-block:: bash + + ffmpeg [global_options] {[input_file_options] -i input_url} ... \ + {[output_file_options] output_url} ... + +All the options and urls are mapped to :code:`ffmpeg_args` by: + +.. code-block:: python + + ffmpeg_args = { + "inputs": [(input_url, input_file_options), ...], + "outputs": [(output_url, output_file_options), ...], + "global_options": global_options, + } + +Any Python sequence types may be used in place of the tuples are lists in the above definition. + +:code:`input_file_options`, :code:`output_file_options`, and :code:`global_options` are optional. If +URL does not require any options, set its options to :code:`None`. If no global options, the +:code:`"global_options"` dict entry may be omitted or set to :code:`None`. + +To specify options, each set of options is a dict with option keys as the dict keys **without** the +leading dash (-). For stream-specific options, the key shall include the full stream specifiers. For +example, use :code:`"b:v"` as the dict key to specify the video bitrate. + +Option values may be given as any Python type, so long as it can be converted to :code:`str` at the +time of the subprocess invocation. If an option does not take any values, then use :code:`None`. For +any option which can be defined multiple times (e.g., :code:`map`), specify its value as a sequence +with each of its elements defining a value for each FFmpeg option. Another exception are the filters +(:code:`vf`, :code:`af`, and :code:`filter_complex`) which values may be given with special option +value structure (to be covered later). + +All defined options are passed unchecked to FFmpeg. + +Examples +-------- + +First, here are how to set up some of the examples in `FFmpeg Documentation `__ +for the :py:mod:`ffmpegio`: + +.. code-block:: python + + # To set the video bitrate of the output file to 64 kbit/s: + # ffmpeg -i input.avi -b:v 64k -bufsize 64k output.avi + ffmpeg_args = { + "inputs": [("input.avi", None)], + "outputs": [("output.avi", {"b:v": "64k", "bufsize": "64k"})], + } + + # To force the frame rate of the input file (valid for raw formats only) to 1 fps and + # the frame rate of the output file to 24 fps: + # ffmpeg -r 1 -i input.m2v -r 24 output.avi + ffmpeg_args = { + "inputs": [("input.avi", {"r": 1})], + "outputs": [("output.avi", {"r": 24})], + } + + # automatic stream selection + # ffmpeg -i A.avi -i B.mp4 out1.mkv out2.wav -map 1:a -c:a copy out3.mov + ffmpeg_args = { + "inputs": [("A.avi", None), ("B.mp4", None)], + "outputs": [ + ("out1.mkv", None), + ("out2.wav", None), + ("out3.mov", {"map": "1:a", "c:a": "copy"}), + ], + } + + # unlabeled filtergraph outputs + # ffmpeg -i A.avi -i C.mkv -i B.mp4 -filter_complex "overlay" out1.mp4 out2.srt + ffmpeg_args = { + "inputs": [("A.avi", None), ("C.mkv", None), ("B.mp4", None)], + "outputs": [ + ("out1.mp4", None), + ("out2.srt", None), + ], + "global_options": {"filter_complex": "overlay"} + } + + # labeled filtergraph outputs + # ffmpeg -i A.avi -i B.mp4 -i C.mkv -filter_complex "[1:v]hue=s=0[outv];overlay;aresample" \ + # -map '[outv]' -an out1.mp4 \ + # out2.mkv \ + # -map '[outv]' -map 1:a:0 out3.mkv + ffmpeg_args = { + "inputs": [("A.avi", None), ("B.mp4", None), ("C.mkv", None)], + "outputs": [ + ("out1.mp4", {"map": "[outv]", "an": None}), + ("out2.mkv", None), + ("out3.mkv", {"map": ("[outv]", "1:a:0")}), + ], + "global_options": {"filter_complex": "[1:v]hue=s=0[outv];overlay;aresample"} + } + +FFmpeg FilterGraph Class Specification +-------------------------------------- + +TBD diff --git a/docs/_sources/adv-ffmpeg.rst.txt b/docs/_sources/adv-ffmpeg.rst.txt new file mode 100644 index 00000000..b499f5f5 --- /dev/null +++ b/docs/_sources/adv-ffmpeg.rst.txt @@ -0,0 +1,34 @@ +.. highlight:: python +.. _adv_ffmpeg: + +:py:mod:`ffmpegio.ffmpegprocess`: Direct invocation of FFmpeg subprocess +======================================================================== + +Instead of indirectly calling FFmpeg with :py:mod:`ffmpegio`'s :ref:`Basic I/O Functions `, +you can directly invoke a FFmpeg subprocess with :py:mod:`ffmpegio.ffmpegprocess` module, +which mocks Python's builtin :py:mod:`subprocess` module. + +.. autosummary:: + :nosignatures: + :recursive: + + ffmpegio.ffmpegprocess.run + ffmpegio.ffmpegprocess.run_two_pass + ffmpegio.ffmpegprocess.Popen + +While both :py:func:`ffmpegio.ffmpegprocess.run` and :py:class:`ffmpegio.ffmpegprocess.Popen` +constructor accepts the :code:`args` argument of Python's :py:func:`subprocess.run` and +:py:class:`subprocess.Popen` constructor, the FFmpeg command argument can also be specified +with a Python dict: see :ref:`its specification page ` for the details. + +:py:func:`ffmpegio.ffmpegprocess.run_two_pass` runs FFmpeg twice to perform two-pass video +encoding. The audio encoding is automatically disabled during the first pass by default. It +also offers a finer control of which options to turn on/off during the first pass. + +:py:mod:`ffmpegio.ffmpegprocess` Module Reference +------------------------------------------------- + +.. autofunction:: ffmpegio.ffmpegprocess.run +.. autofunction:: ffmpegio.ffmpegprocess.run_two_pass +.. autoclass:: ffmpegio.ffmpegprocess.Popen + :members: diff --git a/docs/_sources/analysis.rst.txt b/docs/_sources/analysis.rst.txt new file mode 100644 index 00000000..122b345b --- /dev/null +++ b/docs/_sources/analysis.rst.txt @@ -0,0 +1,122 @@ +.. py:currentmodule:: ffmpegio.analyze +.. highlight:: python +.. _analyze: + +*********************************************************** +:py:mod:`ffmpegio.analyze`: Frame Metadata Analysis Module +*********************************************************** + +There are a number of `FFmpeg filters `_ which analyze video +and audio streams and inject per-frame results into frame metadata to be used in a later stage of +a filtergraph. :py:mod:`ffmpegio.analyze.run` retrieves the injected metadata by appending ``metadata`` +and ``ametadata`` filters and logs the frame metadata outputs. You can use either the supplied Python +classes or a custom class, which conforms to :py:class:`MetadataLogger` interface to specify the FFmpeg +filter and to log its output. + +--------------- +Simple examples +--------------- + +The following example detects intervals of pure black frames within the first 30 seconds of the video: + +>>> from ffmpegio import analyze as ffa +>>> logger, *_ = ffa.run("input.mp4", ffa.BlackDetect(pix_th=0.0), t=30) +>>> print(logger.output) +Black(interval=[[0.0, 0.166667]]) + +* Assign options (e.g., ``pix_th``) of the underlying FFmpeg analysis filter (e.g., ``blackdetect``) as + keyword options of its logger object (e.g., ``BlackDetect``) +* FFmpeg input options (e.g., ``t``) can be assigned as the keyword arguments of :py:func:`run`. +* The logger output is a namedtuple. + +Next example analyzes the audio stream and plot its spectral entropy of the first channel: + +>>> logger,*_ = ffa.run("input.mp4", ffa.ASpectralStats(measure='entropy')) +>>> plt.plot(logger.output.time, logger.output.entropy[0]) + +Finally, multiple loggers can run simultaneously: + +>>> loggers = [ +... ffa.AStats(), # time domain statistics of audio channels +... ffa.BBox(), # bounding box of video frames +... ffa.BlackDetect()] # detect black frame intervals +... +>>> ffa.run("input.mp4", *loggers, t=10) +>>> print(loggers[0].output) +>>> print(loggers[1].output) +>>> print(loggers[2].output) + +------------------------ +Available filter loggers +------------------------ + +Following loggers are currently available as a part of the :py:mod:`analyze` module + +===== ========================== ================= === +Type Python class FFmpeg filter Description +===== ========================== ================= === +audio :py:class:`APhaseMeter` `aphasemeter`_ Measures phase of input audio +\ :py:class:`ASpectralStats` `aspectralstats`_ Frequency domain statistical information +\ :py:class:`AStats` `astats`_ Time domain statistical information +\ :py:class:`SilenceDetect` `silencedetect`_ Detect silence +video :py:class:`BBox` `bbox`_ Compute the bounding box +\ :py:class:`BlackDetect` `blackdetect`_ Detect intervals of black frames +\ :py:class:`BlackFrame` `blackframe`_ Detect black frames +\ :py:class:`BlurDetect` `blurdetect`_ Detect blurriness of frames +\ :py:class:`FreezeDetect` `freezedetect`_ Detect frozen video +.. \ :py:class:`PSNR` `psnr`_ Compute peak signal to noise ratio +\ :py:class:`ScDet` `scdet`_ Detect video scene change +===== ========================== ================= === + +.. _aphasemeter: https://ffmpeg.org/ffmpeg-filters.html#aphasemeter +.. _aspectralstats: https://ffmpeg.org/ffmpeg-filters.html#aspectralstats +.. _astats: https://ffmpeg.org/ffmpeg-filters.html#astats-1 +.. _silencedetect: https://ffmpeg.org/ffmpeg-filters.html#silencedetect +.. _bbox: https://ffmpeg.org/ffmpeg-filters.html#bbox +.. _blackdetect: https://ffmpeg.org/ffmpeg-filters.html#blackdetect +.. _blackframe: https://ffmpeg.org/ffmpeg-filters.html#blackframe +.. _blurdetect: https://ffmpeg.org/ffmpeg-filters.html#blurdetect-1 +.. _freezedetect: https://ffmpeg.org/ffmpeg-filters.html#freezedetect +.. _psnr: https://ffmpeg.org/ffmpeg-filters.html#psnr +.. _scdet: https://ffmpeg.org/ffmpeg-filters.html#scdet-1 + +--------------------- +Analyze API Reference +--------------------- + +.. autosummary:: + :nosignatures: + :recursive: + + ffmpegio.analyze.run + ffmpegio.video.detect + ffmpegio.audio.detect + ffmpegio.analyze.MetadataLogger + +.. autofunction:: ffmpegio.analyze.run +.. autofunction:: ffmpegio.video.detect +.. autofunction:: ffmpegio.audio.detect +.. autoclass:: MetadataLogger + :members: +.. autoclass:: APhaseMeter + :members: +.. autoclass:: ASpectralStats + :members: +.. autoclass:: AStats + :members: +.. autoclass:: SilenceDetect + :members: +.. autoclass:: BBox + :members: +.. autoclass:: BlackDetect + :members: +.. autoclass:: BlackFrame + :members: +.. autoclass:: BlurDetect + :members: +.. autoclass:: FreezeDetect + :members: +.. .. autoclass:: PSNR +.. :members: +.. autoclass:: ScDet + :members: diff --git a/docs/_sources/basicio.rst.txt b/docs/_sources/basicio.rst.txt new file mode 100644 index 00000000..aa854bc8 --- /dev/null +++ b/docs/_sources/basicio.rst.txt @@ -0,0 +1,50 @@ +.. highlight:: python +.. _basicio: + +Basic I/O Function References +============================= + +Basic Functions +--------------- + +.. autosummary:: + :nosignatures: + :recursive: + + ffmpegio.ffmpeg_info + ffmpegio.set_path + ffmpegio.get_path + ffmpegio.is_ready + ffmpegio.video.create + ffmpegio.video.read + ffmpegio.video.write + ffmpegio.video.filter + ffmpegio.image.create + ffmpegio.image.read + ffmpegio.image.filter + ffmpegio.image.write + ffmpegio.audio.create + ffmpegio.audio.read + ffmpegio.audio.write + ffmpegio.audio.filter + ffmpegio.open + ffmpegio.transcode + +.. autofunction:: ffmpegio.ffmpeg_info +.. autofunction:: ffmpegio.get_path +.. autofunction:: ffmpegio.set_path +.. autofunction:: ffmpegio.is_ready +.. autofunction:: ffmpegio.video.create +.. autofunction:: ffmpegio.video.read +.. autofunction:: ffmpegio.video.write +.. autofunction:: ffmpegio.video.filter +.. autofunction:: ffmpegio.image.create +.. autofunction:: ffmpegio.image.read +.. autofunction:: ffmpegio.image.write +.. autofunction:: ffmpegio.image.filter +.. autofunction:: ffmpegio.audio.create +.. autofunction:: ffmpegio.audio.read +.. autofunction:: ffmpegio.audio.write +.. autofunction:: ffmpegio.audio.filter +.. autofunction:: ffmpegio.open +.. autofunction:: ffmpegio.transcode diff --git a/docs/_sources/caps.rst.txt b/docs/_sources/caps.rst.txt new file mode 100644 index 00000000..b592678c --- /dev/null +++ b/docs/_sources/caps.rst.txt @@ -0,0 +1,84 @@ +.. _caps: + +FFmpeg Capabilities References +============================== + +:py:mod:`ffmpegio.caps` module contains a set of functions to wrap ffmpeg's +help/show commands to check the capabilities of the ffmpeg executable that +the :py:mod:`ffmpegio` is employing. + +.. todo:: + + Parsing the additional command options that are specific to the containers, + codecs, and filters. The :code:`options` fields are currently returned as + unparsed :code:`str` + + +List of Functions +----------------- + +.. autosummary:: + :nosignatures: + :recursive: + + ffmpegio.caps.options + ffmpegio.caps.pix_fmts + ffmpegio.caps.sample_fmts + ffmpegio.caps.layouts + ffmpegio.caps.colors + ffmpegio.caps.filters + ffmpegio.caps.filter_info + ffmpegio.caps.codecs + ffmpegio.caps.encoders + ffmpegio.caps.encoder_info + ffmpegio.caps.decoders + ffmpegio.caps.decoder_info + ffmpegio.caps.formats + ffmpegio.caps.muxers + ffmpegio.caps.muxer_info + ffmpegio.caps.demuxers + ffmpegio.caps.demuxer_info + ffmpegio.caps.bsfilters + ffmpegio.caps.bsfilter_info + ffmpegio.caps.devices + ffmpegio.caps.protocols + +.. todo:: + + Remaining commands to be wrapped: sources, sinks, h protocol, dispositions + + +List of Constants +----------------- + +.. autosummary:: + :nosignatures: + :recursive: + + ffmpegio.caps.video_size_presets + ffmpegio.caps.frame_rate_presets + +Function References +------------------- + +.. autofunction:: ffmpegio.caps.options +.. autofunction:: ffmpegio.caps.pix_fmts +.. autofunction:: ffmpegio.caps.sample_fmts +.. autofunction:: ffmpegio.caps.layouts +.. autofunction:: ffmpegio.caps.colors +.. autofunction:: ffmpegio.caps.filters +.. autofunction:: ffmpegio.caps.filter_info +.. autofunction:: ffmpegio.caps.codecs +.. autofunction:: ffmpegio.caps.encoders +.. autofunction:: ffmpegio.caps.encoder_info +.. autofunction:: ffmpegio.caps.decoders +.. autofunction:: ffmpegio.caps.decoder_info +.. autofunction:: ffmpegio.caps.formats +.. autofunction:: ffmpegio.caps.muxers +.. autofunction:: ffmpegio.caps.muxer_info +.. autofunction:: ffmpegio.caps.demuxers +.. autofunction:: ffmpegio.caps.demuxer_info +.. autofunction:: ffmpegio.caps.bsfilters +.. autofunction:: ffmpegio.caps.bsfilter_info +.. autofunction:: ffmpegio.caps.devices +.. autofunction:: ffmpegio.caps.protocols diff --git a/docs/_sources/concat.rst.txt b/docs/_sources/concat.rst.txt new file mode 100644 index 00000000..a8f3ed9c --- /dev/null +++ b/docs/_sources/concat.rst.txt @@ -0,0 +1,15 @@ +.. highlight:: python +.. _concat: + +`FFConcat` Class: Concatenating Media Files +=========================================== + +FFmpeg supports different approaches to concatenate media files as described on +`their Wiki Page `__. If many files +are concatenated, any of these approaches results in lengthy command (or a +ffconcat listing file). The :py:class:`ffmpegio.FFConcat` class primarily focus on +the concat demuxer and abstracts the ffconcat listing file when running `ffmpegio` +commands. + +.. autoclass:: ffmpegio.FFConcat + :members: diff --git a/docs/_sources/devices.rst.txt b/docs/_sources/devices.rst.txt new file mode 100644 index 00000000..358d3446 --- /dev/null +++ b/docs/_sources/devices.rst.txt @@ -0,0 +1,109 @@ +.. highlight:: python +.. _devices: + +Hardware I/O Device Enumeration +=============================== + +FFmpeg supports `a number of hardware I/O devices `__, +from which video or audio data are read (sources) and to which data are written (sinks). +For each device, which is specified via ``-f`` option, some of device hardware name +must be obtained via FFmpeg commands: + +.. code-block:: bash + + ffmpeg -sources + ffmpeg -sinks + +If devices do not support these newer interfaces, via device-specific listing commands such as + +.. code-block:: bash + + ffmpeg -f dshow -list_devices true -i dummy + ffmpeg -f avfoundation -list_devices true -i "" + +Moreover, some devices provide a query interface for the capability of individual hardware: + +.. code-block:: bash + + ffmpeg -list_options true -f dshow -i video="Camera" + ffmpeg -f video4linux2 -list_formats all /dev/video0 + +For multi-hardware use, the hardware configuration must be scanned and chosen for each computer +even within a same OS. :py:mod:`ffmpegio.devices` module is intended to abstract the hardware +selection process via unified naming scheme following the stream specifiers. Device supports +are implemented via plugin module, so user can implement interface for unsupported devices. + +.. note:: + + Currently, only Windows DirectShow source device (``-f dshow``) is supported. Developing + device plugins, especially those on MacOS, requires user feedback and involvement. If + you want a specific device to be supported, please post + `an issue on GitHub `__ + to initiate the process. + +How to Use +---------- + +By default, :py:mod:`ffmpegio` does not scan the system for supported devices. User must +initialize the enumeration: + +.. code-block:: python + + import ffmpegio + + ffmpegio.devices.scan() + +Once the system is scanned, the lists of sources and sinks can be obtained: + +.. code-block:: python + + sources = ffmpegio.devices.list_sources() + +The returned variable is a dict: + +.. code-block:: python + + {('dshow', 'a:0'): 'Microphone (Realtek High Definition Audio)', + ('dshow', 'v:0'): 'WebCam SC-10HDP12B24N'} + +Given the enumeration, the enumerated device can be used as the ``url`` in any +:py:mod:`ffmpegio` functions interacting with FFmpeg. For example: + +.. code-block:: python + + # capture 10 seconds of audio + fs, x = ffmpegio.audio.read('a:0', f_in='dshow', t_in=10) + + # stream webcam video feed for + with ffmpegio.open('v:0', 'vr', f_in='dshow') as dev: + for i, frame in enumerate(dev): + print(f'Frame {i}: {frame.shape}') + + # save video and audio to mp4 file + # - if a device support multiple streams, specify their enums separated by '|' + ffmpegio.transcode('v:0|a:0', 'captured.mp4', f_in='dshow', t_in=10) + + + +References +---------- + +.. autosummary:: + :nosignatures: + :recursive: + + ffmpegio.devices.scan + ffmpegio.devices.list_sources + ffmpegio.devices.list_sinks + ffmpegio.devices.list_source_options + ffmpegio.devices.list_sink_options + ffmpegio.devices.resolve_source + ffmpegio.devices.resolve_sink + +.. autofunction:: ffmpegio.devices.scan +.. autofunction:: ffmpegio.devices.list_sources +.. autofunction:: ffmpegio.devices.list_sinks +.. autofunction:: ffmpegio.devices.list_source_options +.. autofunction:: ffmpegio.devices.list_sink_options +.. autofunction:: ffmpegio.devices.resolve_source +.. autofunction:: ffmpegio.devices.resolve_sink diff --git a/docs/_sources/filtergraph.rst.txt b/docs/_sources/filtergraph.rst.txt new file mode 100644 index 00000000..4982211c --- /dev/null +++ b/docs/_sources/filtergraph.rst.txt @@ -0,0 +1,554 @@ +.. highlight:: python +.. py:currentmodule:: ffmpegio.filtergraph +.. _filtergraph: + +***************************** +Filtergraph Builder Reference +***************************** + +One of the great feature of FFmpeg is the plethora of filters to manipulate video and audio data. +See `the official FFmpeg Filters Documentation `_ and +`FFmpeg Wiki articles on Filtering `_. + +All the media I/O operations in :py:mod:`ffmpegio` support FFmpeg filtering via per-stream +``filter``, ``vf``, ``af``, and ``filter_script`` output options as well as the ``filter_complex`` and +``filter_complex_script`` global option. These options are typically specified by filtergraph +expression strings. For example, ``'scale=iw/2:-1'`` to reduce the video frame size by half. Multiple +operations can be performed in sequence by chaining the filters, e.g., ``'afade=t=in:d=1,afade=t=out:st=9:d=1'`` +adds fade-in and fade-out effect to an audio stream. More complex filtergraph with multiple chains +can also be specified, but as the complexity increases the expression length also increases. +The :py:mod:`ffmpegio.filtergraph` submodule is designed to assist building complex filtergraphs. The +module serves 3 primary functions: + +* :ref:`access` +* :ref:`build` +* :ref:`script` + +These functions are served by three classes: + +.. autosummary:: + :nosignatures: + :recursive: + + ffmpegio.filtergraph.Filter + ffmpegio.filtergraph.Chain + ffmpegio.filtergraph.Graph + +See :ref:`api` section below for the full documentation of these classes +and other helper functions. + +All filtergraph classes can be instantiated with a valid filtergraph description string and yield +filtergraph descriptions when converted to :py:class:`str`. + +.. repl:: + + import ffmpegio.filtergraph as fgb + + # for a simple chain, use either Chain or Graph + fgb.Chain('afade=t=in:d=1,afade=t=out:st=9:d=1') + fgb.Graph('afade=t=in:d=1,afade=t=out:st=9:d=1') + + # construct the chain from filters + fgb.Filter('afade=t=in:d=1') + fgb.Filter('afade=t=out:st=9:d=1') + + +All :py:mod:`ffmpegio` functions that take filter options accept these objects as input arguments +and convert to :py:class:`str` internally: + +>>> fs, x = ffmpegio.audio.read('input.mp3', af=fg) +>>> # x contains the audio samples with the fading effects + +.. note:: + + The simplified examples on this pages are for illustration purpose only. If a filtergraph is + simple and does not require programmatic construction, use plain :py:class`str` expressions to + improve the runtime speed. + +.. _access: + +====================================== +Accessing filter information on FFmpeg +====================================== + +Filters can be instantiated in a several different ways: + +* :py:class:`fgb.Filter` constructor with option values as arguments +* :py:class:`fgb.Filter` constructor with a single-filter filtergraph description +* ``fgb.`` dynamic function (where ``>`` is the + name of a FFmpeg filter) + +For example, a crop filter ``crop=in_w-100:in_h-100:x=100:y=100`` can be created +by any of the following 3 lines: + +.. repl:: + + fgb.Filter('crop', 'in_w-100', 'in_h-100', x=100, y=100) + fgb.Filter('crop=in_w-100:in_h-100:x=100:y=100') + fgb.crop('in_w-100', 'in_h-100', x=100, y=100) + +The :py:func`fgb.crop` function is dynamically created when user call it for the +first time. If the function name fails to resolve an FFmpeg filter, an +:py:exc:`AttributeError` will be raised. + +In addition, these dynamic functions get FFmpeg filter help text as their docstrings: + +.. repl:: + + help(fgb.crop) + +Use :py:func:`ffmpegio.caps.filters` to get the full list of filters supported by the installed +FFmpeg and :py:func:`ffmpegio.caps.filter_info` to get a parsed version of the filter help text. + +.. _build: + +========================= +Constructing filtergraphs +========================= + +A complex filtergraph can be authored using a combination of :py:class:`Filter`, :py:class:`Chain`, +and :py:class:`Graph`. The following 4 operators are defined: + +======== =========== +Operator Description +======== =========== + ``|`` Stack sections (no linking) + ``* n`` Create ``n`` copies of itself and stack them + ``+`` Join filtergraph sections + ``>>`` Point-to-point connection and pad labeling +======== =========== + +Other useful filtergraph manipulation class methods are: + +.. autosummary:: + :nosignatures: + :recursive: + + ffmpegio.filtergraph.Filter.apply + ffmpegio.filtergraph.Chain.append + ffmpegio.filtergraph.Chain.extend + ffmpegio.filtergraph.Graph.link + ffmpegio.filtergraph.Graph.add_label + ffmpegio.filtergraph.Graph.stack + ffmpegio.filtergraph.Graph.connect + ffmpegio.filtergraph.Graph.join + ffmpegio.filtergraph.Graph.attach + ffmpegio.filtergraph.Graph.rattach + +This section mainly describes the operators, leaving the details of the class methods to the API +reference section later on this page. + +``|``: filtegraph stacking +-------------------------- + +Stacking operation creates a new :py:class:`Graph` object from two filtergraph objects, orienting +them in parallel without making any connections. The left and right sides do not need to be of the +same class, and they can be mixed and matched. + +.. repl:: + + # 1. given 2 filters + fgb.trim(30, 60) | fgb.trim(90, 120) + + # 2. given 2 chains + fgb.Chain('trim=30:60,scale=200:-1') | fgb.Chain('atrim=30:60,afade=t=in') + + # 3. given 2 graphs + fgb.Graph('[0:v]trim=30:60,scale=200:-1[out]') | fgb.Graph('[0:a]atrim=30:60,afade=t=in[out]') + + +.. note:: + + Duplicate link labels are automatically renamed with a trailing counter. + + +``* n``: filtergraph self-stacking +---------------------------------- + +Like Python lists and tuples, multipling any :py:mod:`filtergraph` object by an integer creates a +:py:class:`Graph` object containing ``n`` copies of the object and stack them (i.e., create parallel +chains). + +.. repl:: + + # multiplying filters + fgb.crop(100,100) * 3 + + # multiplying chains + fgb.Chain('fps=30,format=yuv420p') * 2 + + # multiplying graphs + fgb.Graph('color,[0]overlay[vout]') * 2 + +.. note:: + + Multiplied link labels receive unique labels with trailing counter. + +``+``: filtergraph joining +-------------------------- + +Join operation connects two :py:mod:`filtergraph` objects by auto-linking the available output +pads of the left side and the available input pads of the right side. The output object type depends +on the input types. + +Joining a single-output object to a single-input object connection is trivial. If both are of either +:py:class:`Filter` or :py:class:`Chain` classes, they are joined in series, resulting in +:py:class:`Chain` object. If :py:class:`Graph` is involved, the joining chain is extended with the +other object. + +.. repl:: + + # 1. joining 2 filters: + fgb.trim(60,120) + fgb.Chain('crop=100:100:12:34,fps=30') + + # 2. joining 2 graphs: + fgb.Graph('[0]fps=30[v0];[v0]overlay') + fgb.Graph('split[v0][v1];[v1]hflip') + +Joining multiple-output :py:class:`Graph` object with multiple-input :py:class:`Graph` object yields +a :py:class:`Graph` object. The number of exposed filter pads must match on both sides. The pad +pairing is automatically performed in one of the two possible ways: + +1. pairs the first unused output filter pad of each chain of the left filtergraph and the + first unused input filter pad of each chain of the right filtergraph (per-chain) +2. pairs all the unused filter pads of the left and right filtergraphs (all) + +Both pairing types require the two sides to have the matching number of unused pads. If no match is +attained per chain, then the all unused pads are paired. This mechanism allows the ``+`` operator to +support two important usecases involving branching and merging filters such as ``overlay``, +``concat``, ``split``, and ``asplit``. The following examples demonstrate these cases: + +.. repl:: + + # case 1: attaching a chain of one side to one of the multiple pads of the other + fgb.hflip() + fgb.hstack() + + # case 2: connecting all the chains (one unused pad each) of one side to a filter with + # matching number of pads on the other side + (fgb.hflip() | fgb.vflip()) + fgb.hstack() + +.. note:: + If joining results in a multi-chain filtergraph, inter-chain links are *unnamed*, and when + converted to :py:class:``str`` the unnamed links uses ``L#`` link names. + +.. note:: + Be aware of `the operator precedence `_. + That is, ``*`` precedes ``+``, and ``+`` precedes ``|``. + +When joining filtergraph objects with multiple inputs and outputs, ``+`` + +:py:obj:`>>` filtergraph labeling / filtergraph p2p linking +----------------------------------------------------------- + +The :py:obj:`>>` is a multi-purpose operator to label a filter pad and to stack two filtergraphs +with a single link between them. It also accepts optionally explicit filter pad id's to override the +default selection policty of the first unused filter pad. + +Simple usecases are: + +.. repl:: + + # label input and output pads to a SISO filtergraph + '[in]' >> fgb.scale(100,-2) >> '[out]' + + # connect 2 filtergraphs with the first available filter pads + fgb.hflip() >> fgb.concat() + +Filter pad labeling +^^^^^^^^^^^^^^^^^^^ + +To label a filter pad, the label string must be fully specified with the square brackets: + +.. code-block:: python + + # valid label strings + '[in]' >> fg # valid FFmpeg link label (alphanumeric characters + '_' inside '[]') + '[0:v]' >> fg # valid FFmpeg stream specifier (the first video stream of the first input url) + + # incorrect label strings + 'in' >> fg # create an "in" Filter object (not a valid FFmpeg filter) + '0:v' >> fg # fails to parse the string as a filtergraph + +To label multiple pads at once, provide a sequence of labels: + +.. repl:: + + ['[0:v]','[1:v]'] >> fgb.Chain('overlay,split') >> ['[vout1]','[vout2]'] + +The pads do not need to be of the same filter: + +.. repl:: + + ['[0:v]','[1:v]'] >> fgb.Graph('pad=640:480[v1];scale=100:100[v2];[v1][v2]overlay') + +Filtergraph linking +^^^^^^^^^^^^^^^^^^^ + +Functionally, :py:obj:`>>` and :py:obj:`+` are the same if both sides of the operator expose only +one pad. So, they can be used interchangeably. + +.. repl:: + + # following two operations produce the same filtergraph + fgb.hflip() >> fgb.vflip() + fgb.hflip() + fgb.vflip() + +The :py:obj:`>>` operator is primarily designed to attach a filter or a filterchain to a larger +filtergraph with multiple pads. + +.. repl:: + + # a 4-input graph with the first one connected to an input stream + fg = fgb.Graph('[0:v]hstack[h1];hstack[h2];[h1][h2]vstack') + + # add the zoomed version as the second input + fgb.Graph('[0:v]crop,scale') >> fg + # -> [0:v]crop,scale[L1];[0:v][L1]hstack[h1];hstack[h2];[h1][h2]vstack + +Filter pad indexing +^^^^^^^^^^^^^^^^^^^ + +In some cases linking of the filter pads may not happen in a top-down order. It is also possible to +specify which filter pad to label or to connect. + +First, here is the the automatic pad selection rules: + +- Unused filter pad is searched on filterchains in sequence +- On the selected filterchain on the left side of :py:obj:`>>` + + * The first filter with an unused input pad is selected + * The first unused input pad on the selected filter is selected + +- On the selected filterchain on the right side of :py:obj:`>>` + + * The last filter with an unused output pad is selected + * The first unused output pad on the selected filter is selected + +These rules apply to both labeling and linking. Here are a couple examples to illustrate +the order of pad selection: + +.. repl:: + + ["[in1]", "[in2]", "[in3]", "[in4]"] >> fgb.Graph("overlay,overlay;hflip") + + fgb.Chain("split,split") >> "[label1]" >> "[label2]" >> "[label3]" + + +To specify the connecting pads, accompany the label or attaching filtergraph with +the filter pad index: + +.. repl:: + + ("[in]", (0,1,1)) >> fgb.Graph("overlay,overlay;hflip") + + fgb.Chain("split,split") >> ((0,-1,1), "[label]") + +The filter pad index is given by a three-element :py:obj:`tuple`: + +.. code-block:: python + + # filter pad index (tuple of 3 ints) + + (i, j, k) + # i -> chain index, selecting the (i+1)st chain + # j -> filter index on the (i+1)st chain + # k -> (input or output) pad index of the (j+1)st filter + +So, the first example ``(0,1,1)`` selects the 1st chain's 2nd filter (``overlay``) +and label its 2nd input pad ``[in3]``. Negative indices (as used for Python +sequences) are supported. The second example ``(0,-1,1)`` selects +the 1st chain's last filter and labels its 2nd output as ``[label3]``. + +Alternatively, an existing label could be used to specify the connecting pad: + +.. repl:: + + fg_overlay = fgb.Chain("scale=240:-2,format=gray") + fg1 = fgb.Graph("[in1][in2]overlay,[in3]overlay;[in4]hflip") + + (fg_overlay,'in2') >> fg1 + +The label name for indexing may optionally omit the square brackets as done in this example. + +:py:func:`Graph.link` - within-filtergraph linking +-------------------------------------------------- + +To create a link within a filtergraph, use :py:func`link`. An example in which an intra-graph linking +is with ``scale2ref``. Its 2 outputs (scaled and passthrough reference streams) may not be used in +the output pad order. Suppose we want the output video to show the first input on top of the scaled +version of the second input, the desired filtergraph expression is + +.. code-block:: + + [1:v][0:v]scale2ref[v1_scaled][v0];[v0][v1_scaled]vstack + +Neither joining nor linking operation cannot produce the desired outcome: + +.. repl:: + + #INCORRECT: only one link which is incorrect + fgb.Graph('[1:v][0:v]scale2ref[v1_scaled][v0]') + fgb.vstack() + + #INCORRECT: correct first link but only one link + fgb.Graph('[1:v][0:v]scale2ref[v1_scaled][v0]') >> ('v0', fgb.vstack()) + +To make the explicit link. Use the :py:func:`Graph.link` method to create out-of-order links: + +.. repl:: + + # first stack 2 filters + fg = fgb.Graph("[1:v][0:v]scale2ref[v1_scaled][v0]") | fgb.vstack() + # then make the connections (returns the link label) + fg.link((-1, 0, 0), "v0") # (-1, 0, 0) <- [v0] + fg.link((-1, 0, 1), "v1_scaled") # (-1, 0, 1) <- [v1_scaled] + fg + +This method modifies the filtergraph. + + +Examples +-------- + +Simple example +^^^^^^^^^^^^^^ + +Borrowing `the example from ffmpeg-python package `_: + +.. code-block:: bash + + [0]trim=start_frame=10:end_frame=20[v0]; \ + [0]trim=start_frame=30:end_frame=40[v1]; \ + [1]hflip[v2]; \ + [v0][v1]concat=n=2[v3]; \ + [v3][v2]overlay=eof_action=repeat, drawbox=50:50:120:120:red:t=5[v5] + +This filtergraph can be built in the following steps: + +.. repl:: + + v0 = "[0]" >> fgb.trim(start_frame=10, end_frame=20) + v1 = "[0]" >> fgb.trim(start_frame=30, end_frame=40) + v3 = "[1]" >> fgb.hflip() + v2 = (v0 | v1) + fgb.concat(2) + v5 = (v2|v3) + fgb.overlay(eof_action='repeat') + fgb.drawbox(50, 50, 120, 120, 'red', t=5) + v5 + + +Concat with preprocessing stage +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The ``concat`` filter can be finicky, requiring all the streams to have the same attributes. To combine +mismatched streams, they need to be preprocessed by other filters. Video streams must have the same +frame size, frame rate, and pixel format. Meanwhile, the audio streams need to have the same sampling +rate, channel format, and sample format. + +To build the filtergraph to concatenate mismatched video files, we start by defining the filters + +.. repl:: + + audio_filter = fgb.aformat(sample_fmts='flt', # 32-bit floating point format + sample_rates=48000, # 48 kS/s sampling rate + channel_layouts='stereo') # 2 channels in stereo layout + video_filters = [ + fgb.scale(1280, 720, + force_original_aspect_ratio='decrease'), # scale at least one dimension to 720p + fgb.pad(1280, 720, -1, -1), # if not 16:9, pad to fill the frame + fgb.setsar(1), # make sure pixels are square + fgb.fps(30), # set framerate to 30 (dupe or drop frames) + fgb.format('yuv420p') # use yuv420p pixel format + ] + +We need multiple video filters while the ``aformat`` filter takes care of the audio stream format. +To combine the video filters, we can use the built-in :py:func:`sum` with an empty :py:class:``Filter``. +as the initial value. Then, stack video and audio filters to finalize the preprocessor filtergraph +for an input file. + +.. repl:: + + preproc = sum(video_filters, fgb.Chain()) | audio_filter + preproc + +Suppose that we have 3 video files, we need 3 copies of the preprocessor filtergraph. The preprocessor +filtergraph can be multiplied 3 times and assign the input stream specs: + +.. repl:: + + inputs = [f'[{file_id}:{media_type}]' for file_id in range(3) for media_type in ('v', 'a')] + inputs + prestage = inputs >> (preproc * 3) + prestage + +Finally, feed the outputs of the prestage filtergraph to the ``concat`` filter and assign the output +labels: + +.. repl:: + + fg = prestage + fgb.concat(n=3, v=1, a=1) >> ['[vout]','[aout]'] + fg + +Note that the output pads of the ``concat`` filter are listed as "available" because they are +technically not (yet) connected to anything. You can use this filter graph with :py:func:`ffmpegio.transcode` +to concatenate 3 input MP4 files: + +>>> ffmpegio.transcode(['input1.mp4','input2.mp4','input3.mp4'], 'output.mp4', +... filter_complex=fg, map=['[vout]','[aout]']) + + +.. _script: + +============================================================ +Generating filtergraph script for extremely long filtergraph +============================================================ + +Extremely long filtergraph description may hit the limit of the subprocess argument length (~30 kB +for Windows and ~100 kB for Posix). In such case, the filtergraph description needs to be passed to +FFmpeg by the `filter_script` FFmpeg output option or the `filter_complex_script` global option +with a filtergraph script file. + +A preferred way to pass a long filtergraph description is to pipe it directly. If ``stdin`` is +available, use the ``input`` argument of :py:func:`subprocess.Popen`: + +.. code-block:: python + + # assume `fg` is a SISO video Graph object + + ffmpegio.ffmpegprocess.run( + { + 'inputs': [('input.mp4', None)] + 'outputs': [('output.mp4', {'filter_script:v': 'pipe:0'})] + }, + input=str(fg)) + +Note that ``pipe:0`` must be used and not the shorthand ``'-'`` unlike +the input url. + +If ``stdin`` is not available, :py:func:`Graph.as_script_file` provides a convenient way to create a +temporary script file. The previous example can also run as follows: + +.. code-block:: python + + with fg.as_script_file() as script_path: + ffmpegio.ffmpegprocess.run( + { + 'inputs': [('input.mp4', None)] + 'outputs': [('output.mp4', {'filter_script:v': script_path})] + }) + + +.. _api: + +========================= +Filtergraph API Reference +========================= + +.. autofunction:: ffmpegio.filtergraph.as_filter +.. autofunction:: ffmpegio.filtergraph.as_filterchain +.. autofunction:: ffmpegio.filtergraph.as_filtergraph +.. autofunction:: ffmpegio.filtergraph.as_filtergraph_object +.. autoclass:: ffmpegio.filtergraph.Filter + :members: + :inherited-members: +.. autoclass:: ffmpegio.filtergraph.Chain + :members: + :inherited-members: +.. autoclass:: ffmpegio.filtergraph.Graph + :members: + :inherited-members: diff --git a/docs/_sources/finder_ffdl.rst.txt b/docs/_sources/finder_ffdl.rst.txt new file mode 100644 index 00000000..4aeb25d0 --- /dev/null +++ b/docs/_sources/finder_ffdl.rst.txt @@ -0,0 +1,52 @@ +`ffmpegio-plugin-downloader`: An `ffmpegio` plugin to download latest FFmpeg release binaries +============================================================================================= + +|pypi| |pypi-status| |pypi-pyvers| |github-license| |github-status| + +.. |pypi| image:: https://img.shields.io/pypi/v/ffmpegio-plugin-downloader + :alt: PyPI +.. |pypi-status| image:: https://img.shields.io/pypi/status/ffmpegio-plugin-downloader + :alt: PyPI - Status +.. |pypi-pyvers| image:: https://img.shields.io/pypi/pyversions/ffmpegio-plugin-downloader + :alt: PyPI - Python Version +.. |github-license| image:: https://img.shields.io/github/license/python-ffmpegio/python-ffmpegio-plugin-downloader + :alt: GitHub License +.. |github-status| image:: https://img.shields.io/github/workflow/status/python-ffmpegio/python-ffmpegio-plugin-downloader/Run%20Tests + :alt: GitHub Workflow Status + +`Python ffmpegio `__ package aims to bring +the full capability of `FFmpeg `__ to read, write, and manipulate multimedia +data to Python. FFmpeg is an open-source cross-platform multimedia framework, which can handle +most of the multimedia formats available today. + +One caveat of FFmpeg is that there is no official program installer for Windows and MacOS (although +`homebrew` could be used for the latter). `ffmpegio-plugin-downloader` adds a capability to download +the latest release build of FFmpeg and enables the `ffmpegio` package to detect the paths of `ffmpeg` +and `ffprobe` automatically. This mechanism is supported by `ffmpeg-downloader `__ +package. Downloading of the release build must be performed interactively from the terminal screen, +outside of Python. + +Use +=== + +Install the package (which also installs `ffmpeg-downloader` package). Then, run `ffmpeg_downloader` to +download and install the latest release: + +.. code-block:: bash + + pip install ffmpegio-core ffmpegio-plugin-downloader + + python -m ffmpeg_downloader # downloads and installs the latest release + +Once the plugin and the FFmpeg executables are installed, `ffmpegio` will automatically +detect the downloaded executables. + +At a later date, the installed FFmpeg can be updated to the latest release + +.. code-block:: bash + + python -m ffmpeg_downloader -U # downloads and updates to the latest release + +.. note:: + `ffmpegio-plugin-downloader` will *not* be activated if `ffmpeg` and `ffprobe` are + already available on the system PATH. diff --git a/docs/_sources/index.rst.txt b/docs/_sources/index.rst.txt new file mode 100644 index 00000000..967e3ea1 --- /dev/null +++ b/docs/_sources/index.rst.txt @@ -0,0 +1,332 @@ +`ffmpegio-core`: Media I/O with FFmpeg in Python +=================================================== + +|pypi| |pypi-status| |pypi-pyvers| |github-license| |github-status| + +.. |pypi| image:: https://img.shields.io/pypi/v/ffmpegio + :alt: PyPI +.. |pypi-status| image:: https://img.shields.io/pypi/status/ffmpegio + :alt: PyPI - Status +.. |pypi-pyvers| image:: https://img.shields.io/pypi/pyversions/ffmpegio + :alt: PyPI - Python Version +.. |github-license| image:: https://img.shields.io/github/license/python-ffmpegio/python-ffmpegio + :alt: GitHub License +.. |github-status| image:: https://img.shields.io/github/actions/workflow/status/python-ffmpegio/python-ffmpegio/test_n_pub.yml?branch=main + :alt: GitHub Workflow Status + +Python `ffmpegio` package aims to bring the full capability of `FFmpeg `__ +to read, write, probe, and manipulate multimedia data to Python. FFmpeg is an open-source cross-platform +multimedia framework, which can handle most of the multimedia formats available today. + +Main Features +------------- + +* Pure-Python light-weight package interacting with FFmpeg executable found in + your system +* Read, write, filter, and create functions for audio, image, and video data +* Context-managing `ffmpegio.open` to perform stream read/write operations of video and audio +* Media readers can output the data in a Numpy array (if Numpy is installed) or a plain :code:`bytes` + objects in a :code:`dict`. The mode of operation can be switched with :code:`ffmpegio.use` function. +* Media writers can write a new media file from either data given in a Numpy array or :code:`bytes` + objects in a :code:`dict`. +* Write Matplotlib figures to images or to a video (a simpler interface than Matplotlib's Animation writers). +* Probe media file information +* Accepts all FFmpeg options including filter graphs +* Transcode a media file to another in Python +* Supports a user callback whenever FFmpeg updates its progress information file + (see `-progress` FFmpeg option) +* `ffconcat` scripter to make the use of `-f concat` demuxer easier +* I/O device enumeration to eliminate the need to look up device names. (currently supports only: Windows DirectShow) +* More features to follow + +Installation +------------ + +Install the full `ffmpegio` package via ``pip``: + +.. code-block:: bash + + pip install ffmpegio + +Following optional external packages are required to enable the :code:`ffmpegio` features that interact +with them. + +.. table:: + :class: tight-table + + ========================== ======================================================================== ===================================== + Distro package name :code:`ffmpegio` features Deprecated plugin names + ========================== ======================================================================== ===================================== + :code:`numpy` Support Numpy array inputs and outputs intead of bytes :code:`ffmpegio` + :code:`matplotlib` Support generation of images or videos from Matplotlib figure :code:`ffmpegio-plugin-mpl` + :code:`ffmepeg-downloader` Support the FFmpeg binaries installed by the :code:`ffdl` command :code:`ffmpegio-plugin-downloader` + :code:`static-ffmpeg` Support the FFmpeg binaries installed by :code:`static-ffmpeg` :code:`ffmpegio-plugin-static-ffmpeg` + ========================== ======================================================================== ===================================== + +These features are automatically enabled if the external packages are installed along along side with `ffmpegio`. +:code:`ffmpegio` is imported + +.. note:: + + Prior to v0.11.0, these features were only enabled via installing separate plugin packages (listed in the table above). + :code:`ffmpegio` v0.11 and :code:`ffmpegio-core` v0.11 are identical, and :code:`ffmpegio-core` will no longer receive + the updates. + +Documentation +------------- + +Visit our `GitHub page here `__ + +Examples +-------- + +To import `ffmpegio` + +.. code-block:: python + + >>> import ffmpegio + +- `Transcoding `_ +- `Read Audio Files `_ +- `Read Image Files / Capture Video Frames `_ +- `Read Video Files `_ +- `Read Multiple Files or Streams `_ +- `Write Audio, Image, & Video Files `_ +- `Filter Audio, Image, & Video Data `_ +- `Stream I/O `_ +- `Device I/O Enumeration `_ +- `Progress Callback `_ +- `Filtergraph Builder`_ +- `Run FFmpeg and FFprobe Directly `_ + +Transcoding +^^^^^^^^^^^ + +.. code-block:: python + + >>> # transcode, overwrite output file if exists, showing the FFmpeg log + >>> ffmpegio.transcode('input.avi', 'output.mp4', overwrite=True, show_log=True) + + >>> # 1-pass H.264 transcoding + >>> ffmpegio.transcode('input.avi', 'output.mkv', vcodec='libx264', show_log=True, + >>> preset='slow', crf=22, acodec='copy') + + >>> # 2-pass H.264 transcoding + >>> ffmpegio.transcode('input.avi', 'output.mkv', two_pass=True, show_log=True, + >>> **{'c:v':'libx264', 'b:v':'2600k', 'c:a':'aac', 'b:a':'128k'}) + + >>> # concatenate videos using concat demuxer + >>> files = ['/video/video1.mkv','/video/video2.mkv'] + >>> ffconcat = ffmpegio.FFConcat() + >>> ffconcat.add_files(files) + >>> with ffconcat: # generates temporary ffconcat file + >>> ffmpegio.transcode(ffconcat, 'output.mkv', f_in='concat', codec='copy', safe_in=0) + +Read Audio Files +^^^^^^^^^^^^^^^^ + +.. code-block:: python + + >>> # read audio samples in its native sample format and return all channels + >>> fs, x = ffmpegio.audio.read('myaudio.wav') + >>> # fs: sampling rate in samples/second, x: [nsamples x nchannels] numpy array + + >>> # read audio samples from 24.15 seconds to 63.2 seconds, pre-convert to mono in float data type + >>> fs, x = ffmpegio.audio.read('myaudio.flac', ss=24.15, to=63.2, sample_fmt='dbl', ac=1) + + >>> # read filtered audio samples first 10 seconds + >>> # filter: equalizer which attenuate 10 dB at 1 kHz with a bandwidth of 200 Hz + >>> fs, x = ffmpegio.audio.read('myaudio.mp3', t=10.0, af='equalizer=f=1000:t=h:width=200:g=-10') + +Read Image Files / Capture Video Frames +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: python + + >>> # list supported image extensions + >>> ffmpegio.caps.muxer_info('image2')['extensions'] + ['bmp', 'dpx', 'exr', 'jls', 'jpeg', 'jpg', 'ljpg', 'pam', 'pbm', 'pcx', 'pfm', 'pgm', 'pgmyuv', + 'png', 'ppm', 'sgi', 'tga', 'tif', 'tiff', 'jp2', 'j2c', 'j2k', 'xwd', 'sun', 'ras', 'rs', 'im1', + 'im8', 'im24', 'sunras', 'xbm', 'xface', 'pix', 'y'] + + >>> # read BMP image with auto-detected pixel format (rgb24, gray, rgba, or ya8) + >>> I = ffmpegio.image.read('myimage.bmp') # I: [height x width x ncomp] numpy array + + >>> # read JPEG image, then convert to grayscale and proportionally scale so the width is 480 pixels + >>> I = ffmpegio.image.read('myimage.jpg', pix_fmt='grayscale', s='480x-1') + + >>> # read PNG image with transparency, convert it to plain RGB by filling transparent pixels orange + >>> I = ffmpegio.image.read('myimage.png', pix_fmt='rgb24', fill_color='orange') + + >>> # capture video frame at timestamp=4:25.3 and convert non-square pixels to square + >>> I = ffmpegio.image.read('myvideo.mpg', ss='4:25.3', square_pixels='upscale') + + >>> # capture 5 video frames and tile them on 3x2 grid with 7px between them, and 2px of initial margin + >>> I = ffmpegio.image.read('myvideo.mp4', vf='tile=3x2:nb_frames=5:padding=7:margin=2') + + >>> # create spectrogram of the audio input (must specify pix_fmt if input is audio) + >>> I = ffmpegio.image.read('myaudio.mp3', filter_complex='showspectrumpic=s=960x540', pix_fmt='rgb24') + + +Read Video Files +^^^^^^^^^^^^^^^^ + +.. code-block:: python + + >>> # read 50 video frames at t=00:32:40 then convert to grayscale + >>> fs, F = ffmpegio.video.read('myvideo.mp4', ss='00:32:40', vframes=50, pix_fmt='gray') + >>> # fs: frame rate in frames/second, F: [nframes x height x width x ncomp] numpy array + + >>> # get running spectrogram of audio input (must specify pix_fmt if input is audio) + >>> fs, F = ffmpegio.video.read('myvideo.mp4', pix_fmt='rgb24', filter_complex='showspectrum=s=1280x480') + + +Read Multiple Files or Streams +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: python + + >>> # read both video and audio streams (1 ea) + >>> rates, data = ffmpegio.media.read('mymedia.mp4') + >>> # rates: dict of frame rate and sampling rate: keys="v:0" and "a:0" + >>> # data: dict of video frame array and audio sample array: keys="v:0" and "a:0" + + >>> # combine video and audio files + >>> rates, data = ffmpegio.media.read('myvideo.mp4','myaudio.mp3') + + >>> # get output of complex filtergraph (can take multiple inputs) + >>> expr = "[v:0]split=2[out0][l1];[l1]edgedetect[out1]" + >>> rates, data = ffmpegio.media.read('myvideo.mp4',filter_complex=expr,map=['[out0]','[out1]']) + >>> # rates: dict of frame rates: keys="v:0" and "v:1" + >>> # data: dict of video frame arrays: keys="v:0" and "v:1" + +Write Audio, Image, & Video Files +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: python + + >>> # create a video file from a numpy array + >>> ffmpegio.video.write('myvideo.mp4', rate, F) + + >>> # create an image file from a numpy array + >>> ffmpegio.image.write('myimage.png', F) + + >>> # create an audio file from a numpy array + >>> ffmpegio.audio.write('myaudio.mp3', rate, x) + +Filter Audio, Image, & Video Data +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: python + + >>> # Add fade-in and fade-out effects to audio data + >>> fs_out, y = ffmpegio.audio.filter('afade=t=in:ss=0:d=15,afade=t=out:st=875:d=25', fs_in, x) + + >>> # Apply mirror effect to an image + >>> I_out = ffmpegio.image.filter('crop=iw/2:ih:0:0,split[left][tmp];[tmp]hflip[right];[left][right] hstack', I_in) + + >>> # Add text at the center of the video frame + >>> filter = "drawtext=fontsize=30:fontfile=FreeSerif.ttf:text='hello world':x=(w-text_w)/2:y=(h-text_h)/2" + >>> fs_out, F_out = ffmpegio.video.filter(filter, fs_in, F_in) + +Stream I/O +^^^^^^^^^^ + +.. code-block:: python + + >>> # process video 100 frames at a time and save output as a new video + >>> # with the same frame rate + >>> with ffmpegio.open('myvideo.mp4', 'rv', blocksize=100) as fin, + >>> ffmpegio.open('myoutput.mp4', 'wv', rate=fin.rate) as fout: + >>> for frames in fin: + >>> fout.write(myprocess(frames)) + +Filtergraph Builder +^^^^^^^^^^^^^^^^^^^ + +.. code-block:: python + + >>> # build complex filtergraph + >>> from ffmpegio import filtergraph as fgb + >>> + >>> v0 = "[0]" >> fgb.trim(start_frame=10, end_frame=20) + >>> v1 = "[0]" >> fgb.trim(start_frame=30, end_frame=40) + >>> v3 = "[1]" >> fgb.hflip() + >>> v2 = (v0 | v1) + fgb.concat(2) + >>> v5 = (v2|v3) + fgb.overlay(eof_action='repeat') + fgb.drawbox(50, 50, 120, 120, 'red', t=5) + >>> v5 + + FFmpeg expression: "[0]trim=start_frame=10:end_frame=20[L0];[0]trim=start_frame=30:end_frame=40[L1];[L0][L1]concat=2[L2];[1]hflip[L3];[L2][L3]overlay=eof_action=repeat,drawbox=50:50:120:120:red:t=5" + Number of chains: 5 + chain[0]: [0]trim=start_frame=10:end_frame=20[L0]; + chain[1]: [0]trim=start_frame=30:end_frame=40[L1]; + chain[2]: [L0][L1]concat=2[L2]; + chain[3]: [1]hflip[L3]; + chain[4]: [L2][L3]overlay=eof_action=repeat,drawbox=50:50:120:120:red:t=5[UNC0] + Available input pads (0): + Available output pads: (1): (4, 1, 0) + +Device I/O Enumeration +^^^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: python + + >>> # record 5 minutes of audio from Windows microphone + >>> fs, x = ffmpegio.audio.read('a:0', f_in='dshow', sample_fmt='dbl', t=300) + + >>> # capture Windows' webcam frame + >>> with ffmpegio.open('v:0', 'rv', f_in='dshow') as webcam, + >>> for frame in webcam: + >>> process_frame(frame) + +Progress Callback +^^^^^^^^^^^^^^^^^ + +.. code-block:: python + + >>> import pprint + + >>> # progress callback + >>> def progress(info, done): + >>> pprint(info) # bunch of stats + >>> if done: + >>> print('video decoding completed') + >>> else: + >>> return check_cancel_command(): # return True to kill immediately + + >>> # can be used in any butch processing + >>> rate, F = ffmpegio.video.read('myvideo.mp4', progress=progress) + + >>> # as well as for stream processing + >>> with ffmpegio.open('myvideo.mp4', 'rv', blocksize=100, progress=progress) as fin: + >>> for frames in fin: + >>> myprocess(frames) + +Run FFmpeg and FFprobe Directly +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: python + + >>> from ffmpegio import ffmpeg, FFprobe, ffmpegprocess + >>> from subprocess import PIPE + + >>> # call with options as a long string + >>> ffmpeg('-i input.avi -b:v 64k -bufsize 64k output.avi') + + >>> # or call with list of options + >>> ffmpeg(['-i', 'input.avi' ,'-r', '24', 'output.avi']) + + >>> # the same for ffprobe + >>> ffprobe('ffprobe -show_streams -select_streams a INPUT') + + >>> # specify subprocess arguments to capture stdout + >>> out = ffprobe('ffprobe -of json -show_frames INPUT', + stdout=PIPE, universal_newlines=True).stdout + + >>> # use ffmpegprocess to take advantage of ffmpegio's default behaviors + >>> out = ffmpegprocess.run({"inputs": [("input.avi", None)], + "outputs": [("out1.mp4", None), + ("-", {"f": "rawvideo", "vframes": 1, "pix_fmt": "gray", "an": None}) + }, capture_log=True) + >>> print(out.stderr) # print the captured FFmpeg logs (banner text omitted) + >>> b = out.stdout # width*height bytes of the first frame diff --git a/docs/_sources/install.rst.txt b/docs/_sources/install.rst.txt new file mode 100644 index 00000000..308865ea --- /dev/null +++ b/docs/_sources/install.rst.txt @@ -0,0 +1,80 @@ +.. highlight:: bash +.. _install: + +Installation +============ + +To use :py:mod:`ffmpegio`, the package must be installed on Python as well as +having the FFmpeg binary files at a location :py:mod:`ffmpegio` can find. In addition, +optional external packages can be installed to enable the :code:`ffmpegio` features that interact +with them. + +Install the :py:mod:`ffmpegio` package via :code:`pip`. + +.. code-block:: + + pip install ffmpegio + +Install FFmpeg program +^^^^^^^^^^^^^^^^^^^^^^ + +There are two platform independent approaches to install FFmpeg for the use in Python: + +::code::`ffmpeg-downloader` +""""""""""""""""""""""""""" + +.. code-block:: + pip install ffmpeg-downloader + ffdl install -U # grabs the latest version + + # optionally + ffdl install -U --add-path to have it on the system path in Windows or MacOS + +::code::`static-ffmpeg` +""""""""""""""""""""""" + +.. code-block:: + pip install static-ffmpeg + static_ffmpeg_paths + +The installation of FFmpeg is platform dependent. For Ubuntu/Debian Linux, + +.. code-block:: + + sudo apt install ffmpeg + +and for MacOS, + +.. code-block:: + + brew install ffmpeg + +no other actions are needed as these commands will place the FFmpeg executables +on the system path. + +For Windows, it is a bit more complicated. + +1. Download pre-built packages from the links available on the `FFmpeg's Download page + `__. +2. Unzip the content and place the files in one of the following directories: + + ================================== =============================================== + Auto-detectable FFmpeg folder path Example + ================================== =============================================== + ``%PROGRAMFILES%\ffmpeg`` ``C:\Program Files\ffmpeg`` + ``%PROGRAMFILES(X86)%\ffmpeg`` ``C:\Program Files (x86)\ffmpeg`` + ``%USERPROFILE%\ffmpeg`` ``C:\Users\john\ffmpeg`` + ``%APPDATA%\ffmpeg`` ``C:\Users\john\AppData\Roaming\ffmpeg`` + ``%APPDATA%\programs\ffmpeg`` ``C:\Users\john\AppData\Roaming\programs\ffmpeg`` + ``%LOCALAPPDATA%\ffmpeg`` ``C:\Users\john\AppData\Local\ffmpeg`` + ``%LOCALAPPDATA%\programs\ffmpeg`` ``C:\Users\john\AppData\Local\programs\ffmpeg`` + ================================== =============================================== + + Keep the internal structure intact, i.e., the executables must be found at + ``ffmpeg\bin\ffmpeg.exe`` and ``ffmpeg\bin\ffprobe.exe``. + + There are two other alternative. First, the FFmpeg binaries could be placed on the + Python's current working directory (i.e., :code:`os.getcwd()`). Second, they could + be placed in an arbitrary location and use :py:func:`ffmpegio.set_path` to + specify the location. The latter feature is especially useful when `ffmpegio` is + bundled in a package (e.g., PyInstaller). diff --git a/docs/_sources/links.rst.txt b/docs/_sources/links.rst.txt new file mode 100644 index 00000000..8c9be76e --- /dev/null +++ b/docs/_sources/links.rst.txt @@ -0,0 +1,13 @@ +.. _links: + +External Links +============== + +.. toctree:: + + GitHub Repository + GitHub Discussion Board + FFmpeg Documentation + FFprobe Documentation + FFmpeg Filters Documentation + PyPi Project Page diff --git a/docs/_sources/mpl-writer.rst.txt b/docs/_sources/mpl-writer.rst.txt new file mode 100644 index 00000000..36bb61e7 --- /dev/null +++ b/docs/_sources/mpl-writer.rst.txt @@ -0,0 +1,79 @@ +.. highlight:: python +.. _options: + +Creating Videos from Matplotlib figure +====================================== + +While Matplotlib supports video creation via its +`animation module `__, +its interface is a bit cranky because its primary role is to animate the figure on screen +rather than outputting figures to a video file. You must create an animation object first before +saving it as a video. + +:code:`ffmpegio` provides a direct method to write Matplotlib figure to a video write stream with +the same streaming interface as feeding the RGB frame data to FFmpeg. + +Example +------- + +Create an MP4 video of `Matplotlib's animation example `__. + +.. code-block:: python + + import ffmpegio as ff + from matplotlib import pyplot as plt + import numpy as np + + + fig, ax = plt.subplots() + + x = np.arange(0, 2*np.pi, 0.01) + line, = ax.plot(x, np.sin(x)) + + interval=20 # delay in milliseconds + save_count=50 # number of frames + + def animate(i): + line.set_ydata(np.sin(x + i / 50)) # update the data. + return line + + + with ff.open( + "output.mp4", # output file name + "wv", # open file in write-video mode + 1e3/interval, # framerate in frames/second + pix_fmt="yuv420p", # specify the pixel format (default is yuv444p) + # add other ffmpeg options as keywod argument as needed + ) as f: + for n in range(save_count): + animate(n) # update figure + f.write(fig) # write new video frame + +Any video format can be chosen with this interface and any FFmpeg options can be specified here. +For instance, an GIF animation of the above example can be created with optimized color pallette. +To do this, we use `palettegen `__ and +`paletteuse `__` filters and construct a video filtergraph: + +.. code-block:: + + split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse + +This filtergraph string could be provided directly to :code:`ff.open` as a `vf` keyword argument, +but let's use :code:`ffmpegio.filtergraph` submodule to construct it instead: + +.. code-block:: python + + import ffmpegio.filtergraph as fgb + + vf = fgb.split() + fgb.palettegen() + fgb.paletteuse() + + with ff.open( + "output.gif", # output file name + "wv", # open file in write-video mode + 1e3/interval, # framerate in frames/second + vf = vf # optimize the GIF palette + ) as f: + for n in range(save_count): + animate(n) # update figure + f.write(fig) # write new video frame + diff --git a/docs/_sources/options.rst.txt b/docs/_sources/options.rst.txt new file mode 100644 index 00000000..ed85e732 --- /dev/null +++ b/docs/_sources/options.rst.txt @@ -0,0 +1,225 @@ +.. highlight:: python +.. _options: + +FFmpeg Option References +======================== + +All open/read/write/filter functions in :py:mod:`ffmpegio` accepts any +`FFmpeg options `__ as their keyword arguments. Two rules +apply to construct Python function argument: + +(1) Drop the ``-`` from FFmpeg option name, e.g., enter ``-ss 50`` as ``(..., ss=50, ...)``; and +(2) All the options are assumed output options by default. To specify input options, append ``_in`` + to the option name. To apply ``-ss 50`` to input url, enter ``(..., ss_in=50, ...)``. Global + options are automatically identified. + +The option values can be specified in any data type, but it must have a ``__str__`` function defined +to convert Python data to correct FFmpeg string expression. + +Common FFmpeg Options +--------------------- + +========== ========= = = = = ============================================================ +Name type V A I O Description +========== ========= = = = = ============================================================ +ss float X X X X Start time in seconds +t float X X X X Duration in seconds +to float X X X X End time in seconds (ignored if both `ss`` and `t` are set) +r numeric X X X Video frame rate in frames/second +ar numeric X X X Audio sampling rate in samples/second +s (int,int) X X X Video frame size (width, height). Alt. str expression: `wxh` +pix_fmt str X X X Video frame pixel format, defaults to auto-detect +vf str X X Video filtergraph (leave output pad unlabeled) +ac int X X X Number of audio channels, defaults to auto-detect +sample_fmt int X X X Audio sample format, defaults to None (same as input) +af str X X Audio filtergraph (leave output pad unlabeled) +crf int X X H.264 video encoding constant quality factor (0-51) +========== ========= = = = = ============================================================ + +`s` output option +^^^^^^^^^^^^^^^^^ + +FFmpeg's :code:`-s` output option sets the output video frame size by using the scale video filter. However, +it does not allow non-positive values for width and height which the scale filter accepts. +:py:mod:`ffmpegio` alters this behavior by checking the :code:`s` argument for <=0 width or height +and convert to :code:`vf` argument. + +============ ============================================================ +width/height Description +============ ============================================================ +n (n>0) Specifying the output size to be n pixels +0 Use the input size for the output +-n Scale the dimension proportional to the other dimension then + make sure that the calculated dimension is divisible by n + and adjust the value if necessary. Only one of width or + height can be negative valued. +============ ============================================================ + +Note that passing both :code:`s` with a non-positive value and :code:`vf` +will raise an exception. + +`map` output options +^^^^^^^^^^^^^^^^^^^^ + +The output option `-map` is the (only?) FFmpeg option, which could be specified multiple times +in command line input. This goes against :py:mod:`ffmpegio`'s FFmpeg dict structure, and so `map` +argument is handled differently from the others. First, `map` argument must be a non-`str` sequence, +and each of its element is converted to `-map` option. Furthermore, each element could be a str or +else a sequence which items are then stringified and joined together with `':'`. + + +Video Pixel Formats :code:`pix_fmt` +----------------------------------- + +There are many video pixel formats that FFmpeg support, which you can obtain with +:py:func:`caps.pix_fmts()` function. For the I/O purpose, :py:mod:`ffmpegio` video/image +functions operate strictly with RGB or grayscale formats listed below. + +===== ===== ========= =================================== +ncomp dtype pix_fmt Description +===== ===== ========= =================================== + 1 \|u8 gray grayscale + 1 `__), +:py:mod:`ffmpegio`'s video and image routines adds several convenience +video options to perform simple video maninpulations without the need of setting +up a filtergraph. + + +.. list-table:: Options to manipulate video frames + :widths: auto + :header-rows: 1 + :class: tight-table + + * - name + - value + - FFmpeg filter + - Description + * - :code:`crop` + - seq(int[, int[, int[, int]]]) + - `crop `__ + - video frame cropping/padding, values representing the number of pixels to crop from [left top right bottom]. + If positive, the video frame is cropped from the respective edge. If negative, the video frame is padded on + the respective edge. If right or bottom is missing, uses the same value as left or top, respectively. If top + is missing, it defaults to 0. + * - :code:`flip` + - {:code:`'horizontal'`, :code:`'vertical'`, :code:`'both'`} + - `hflip `__ or `vflip `__ + - flip the video frames horizontally, vertically, or both. + * - :code:`transpose` + - int + - `transpose `__ + - tarnspose the video frames. Its value specifies the mode of operation. Use 0 for the conventional transpose operation. + For the others, see the FFmpeg documentation. + * - :code:`square_pixels` + - {:code:`'upscale'`, :code:`'downscale'`, :code:`'upscale_even'`, + :code:`'downscale_even'`} + - `scale `__ and `setsar `__ + - Resize video frames so that their pixels are square (i.e., SAR=1:1). + :code:`'upscale'` stretches the short side + of the pixels while :code:`'downscale'` compresses the long side. + :code:`'even'` makes sure that the resulting frame size is even (required by some codecs). + * - :code:`remove_alpha` + - bool + - `overlay `__ and `color `__ + - Fill transparent background with :code:`fill_color` color. This filter is automatically + inserted if input :code:`'pix_fmt'` has alpha but not the output. + * - :code:`fill_color` + - str + - n/a + - This option is used for the auto-conversion of an image with transparency to + opaque by setting the output option :code:`pix_fmt`. The option value + specifies a color according to + `FFmpeg Color Specifications `__. + Default color is :code:`'white'`. + +Note that the these operations are pre-wired to perform in a specific order: + +.. blockdiag:: + :caption: Video Manipulation Order + + blockdiag { + square_pixels -> crop -> flip -> transpose; + crop -> flip [folded] + } + +Be aware of this ordering as these filters are non-commutative (i.e., a change in the +order of operation alters the outcome). If your desired order of filters differs or +need to use additional filters, use the :code:`vf` option to specify your own filtergraph. + +.. list-table:: Examples of manipulated images + :class: tight-table + + * - .. plot:: + + IM = ffmpegio.image.read('ffmpeg-logo.png') + plt.figure(figsize=(IM.shape[1]/96, IM.shape[0]/96), dpi=96) + plt.imshow(IM) + plt.gca().set_position((0, 0, 1, 1)) + plt.axis('off') + + .. code-block:: python + + ffmpegio.image.read('ffmpeg-logo.png') + + * - .. plot:: + + IM = ffmpegio.image.read('ffmpeg-logo.png', crop=(100,100,0,0), transpose=0) + plt.figure(figsize=(IM.shape[1]/96, IM.shape[0]/96), dpi=96) + plt.imshow(IM) + plt.gca().set_position((0, 0, 1, 1)) + plt.axis('off') + + .. code-block:: python + + ffmpegio.image.read('ffmpeg-logo.png', crop=(100,100,0,0), transpose=0) + + * - .. plot:: + + IM = ffmpegio.image.read('ffmpeg-logo.png', crop=(100,100,0,0), flip='both', s=(200,50)) + plt.figure(figsize=(IM.shape[1]/96, IM.shape[0]/96), dpi=96) + plt.imshow(IM) + plt.gca().set_position((0, 0, 1, 1)) + plt.axis('off') + + .. code-block:: python + + ffmpegio.image.read('ffmpeg-logo.png', crop=(100,100,0,0), flip='both', size=(200,-1)) diff --git a/docs/_sources/probe.rst.txt b/docs/_sources/probe.rst.txt new file mode 100644 index 00000000..f033c3fc --- /dev/null +++ b/docs/_sources/probe.rst.txt @@ -0,0 +1,39 @@ +.. _probe: + +Media Probe Function References +=============================== + +:py:mod:`ffmpegio.probe` module contains a full-featured ffprobe wrapper function +:py:func:`ffmpegio.probe.full_details` and its derivative functions, which are +tailored to retrieve specific type of information from a media file or stream. + +List of Functions +----------------- + +.. autosummary:: + :nosignatures: + :recursive: + + ffmpegio.probe.format_basic + ffmpegio.probe.streams_basic + ffmpegio.probe.video_streams_basic + ffmpegio.probe.audio_streams_basic + ffmpegio.probe.full_details + ffmpegio.probe.query + ffmpegio.probe.frames + +Argument Type References +------------------------ + +.. autoclass:: ffmpegio.probe.IntervalSpec + +Function References +------------------- + +.. autofunction:: ffmpegio.probe.format_basic +.. autofunction:: ffmpegio.probe.streams_basic +.. autofunction:: ffmpegio.probe.video_streams_basic +.. autofunction:: ffmpegio.probe.audio_streams_basic +.. autofunction:: ffmpegio.probe.full_details +.. autofunction:: ffmpegio.probe.query +.. autofunction:: ffmpegio.probe.frames diff --git a/docs/_sources/quick.rst.txt b/docs/_sources/quick.rst.txt new file mode 100644 index 00000000..7beff41e --- /dev/null +++ b/docs/_sources/quick.rst.txt @@ -0,0 +1,438 @@ +.. highlight:: python +.. _quick: + +Quick Start Guide +================= + +Install +------- + +To use :py:mod:`ffmpegio`, the package must be installed on Python as well as +having the FFmpeg binary files at a location :py:mod:`ffmpegio` can find. + +Install the full :py:mod:`ffmpegio` package via ``pip``: + +.. code-block:: bash + + pip install ffmpegio + +If `numpy.ndarray` data I/O is not needed, instead use + +.. code-block:: bash + + pip install ffmpegio-core + + +If FFmpeg is not installed on your system, please follow the instructions on +:ref:`Installation page ` + +Features +-------- + +FFmpeg can read/write virtually any multimedia file out there, and :code:`ffmpegio` uses +the FFmpeg's prowess to perform media I/O (and other) operations in Python. It offers two +basic modes of operation: block read/write and stream read/write. For the read operations, +it can output data either in a Numpy array or in a plain :code:`bytes`. The Numpy mode is +enabled by default if Numpy is available in the system. Another feature of +:code:`ffmpegio` is to report the properties of the media files, using FFprobe. + +Media Probe +----------- + +To process a media file, you first need to know what's in it. Within FFmpeg +ecosystem, this task is handled by `ffprobe `__. +:code:`ffmpegio`'s :ref:`ffmpegio:probe` module wraps ffprobe with 5 +basic functions: + +.. code-block:: python + + >>> import ffmpegio + >>> from pprint import pprint + + >>> url = 'mytestvideo.mpg' + >>> format_info = ffmpegio.probe.format_basic(url) + >>> pprint(format_info) + {'duration': 66.403256, + 'filename': 'mytestvideo.mpg', + 'format_name': 'mpegts', + 'nb_streams': 2, + 'start_time': 0.0} + + >>> stream_info = ffmpegio.probe.streams_basic(url) + >>> pprint(stream_info) + [{'codec_name': 'mp2', 'codec_type': 'audio', 'index': 0}, + {'codec_name': 'h264', 'codec_type': 'video', 'index': 1}] + + >>> vst_info = ffmpegio.probe.video_streams_basic(url) + >>> pprint(vst_info) + [{'codec_name': 'h264', + 'display_aspect_ratio': Fraction(22, 15), + 'duration': 66.39972222222222, + 'frame_rate': Fraction(15000, 1001), + 'height': 240, + 'index': 1, + 'pix_fmt': 'yuv420p', + 'sample_aspect_ratio': Fraction(1, 1), + 'start_time': 0.0, + 'width': 352}] + + >>> ast_info = ffmpegio.probe.audio_streams_basic(url) + >>> pprint(ast_info) + [{'channel_layout': 'stereo', + 'channels': 2, + 'codec_name': 'mp2', + 'duration': 66.40325555555556, + 'index': 0, + 'nb_samples': 2928384, + 'sample_fmt': 'fltp', + 'sample_rate': 44100, + 'start_time': 0.0}] + +To obtain the complete ffprobe output, use :py:func:`ffmpegio.probe.full_details`, +and to obtain specific format or stream fields, use :py:func:`ffmpegio.probe.query`. +For more information on :py:mod:`probe`, see :ref:`probe`. + +Block Read/Write +---------------- + +Suppose you need to analyze short audio data in :code:`mytestfile.mp3`, you can +read all its samples by + +.. code-block:: python + + >>> fs, x = ffmpegio.audio.read('mytestfile.wav') + +It returns the sampling rate :code:`fs` and :py:class:`numpy.ndarray` :code:`x`. +The audio data is always represetned by a 2-D array, each of which column represents +an audio channel. So, a 2-second stereo recording at 8000 samples/second yields +:code:`x.shape` to be :code:`(16000,2)`. Also, the sample format is preserved: If +the samples in the wav file is 16-bit, :code:`x` is of :code:`numpy.int16` dtype. + +Now, you've processed this audio data and produced the 8000-sample 1-D array :code:`y` +at reduced sampling rate at 4000-samples/second. You want to save this new audio +data as FLAC file. To do so, you run: + +.. code-block:: python + + >>> ffmpegio.audio.write('myoutput.flac', 4000, y) + +There are video counterparts to these two functions: + +.. code-block:: python + + >>> fs, F = ffmpegio.video.read('mytestvideo.mp4') + >>> ffmpegio.video.write('myoutput.avi', fs, F) + +Let's suppose :code:`mytestvideo.mp4` is 10 seconds long, containing a +:code:`yuv420p`-encoded color video stream with the frame size of 640x480 pixels, +and the frame rate of 29.97 (30000/1001) frames/second. Then, the :py:func:`video.read` +returns a 2-element tuple: the first element :code:`fs` is the frame rate in +:py:class:`fractions.Fraction` and the second element :code:`F` contains all the frames +of the video in :py:class:`numpy.ndarray` with shape :code:`(299, 480, 640, 3)`. +Because the video is in color, each pixel is represented in 24-bit RGB, thus +:code:`F.dtype` is :code:`numpy.uint8`. The video write is the reciprocal of +the read operation. + +For image (or single video frame) I/O, there is a pair of functions as well: + +.. code-block:: python + + >>> I = ffmpegio.image.read('myimage.png') + >>> ffmpegio.image.write('myoutput.bmp', I) + +The image data :code:`I` is like the video frame data, but without the leading +dimension. + +.. _quick-streamio: + + +Stream Read/Write +----------------- + +Block read/write is simple and convenient for a short file, but it quickly +becomes slow and inefficient as the data size grows; this is especially true +for video. To enable on-demand data retrieval, :code:`ffmpegio` offers stream +read/write operation. It mimics the familiar Python's file I/O with +:py:func:`ffmpegio.open()`: + +.. code-block:: python + + >>> with ffmpegio.open('mytestvideo.mp4', 'rv') as f: # opens the first video stream + >>> print(f.rate) # frame rate fraction in frames/second + >>> F = f.read() # read the first frame + >>> F = f.read(5) # read the next 5 frames at once + +Another example, which uses read and write streams simultaneously: + +.. code-block:: python + + >>> with ffmpegio.open('mytestvideo.mp4', 'rv', blocksize=100) as f, + >>> ffmpegio.open('myoutput.avi', 'wv', f.rate) as g: + >>> for frames in f: # iterates over all frames, 100 frames at a time + >>> output = my_processor(frames) # function to process data + >>> g.write(output) # send the processed frames to 'myoutput.avi' + +By default, :code:`ffmpegio.open()` opens the first media stream available to read. +However, the operation mode can be specified via the :code:`mode` second argument. +The above example, opens :code:`mytestvideo.mp4` file in :code:`'rv'` or "read +video" mode and :code:`myoutput.avi` in :code:`'wv'` or "write video" mode. The +file reader object :code:`f` is an Iterable object, which returns the next set of +frames (the number set by the :code:`blocksize` argument). For more, +see :py:func:`ffmpegio.open`. + +Specify Read Time Range +----------------------- + +For both block and stream read operations, you can specify the time range to read +data from. There are four options available: + +.. table:: Read Timing Options + :class: tight-table + + ============= ======================================================================== + Name Description + ============= ======================================================================== + :code:`ss` Start time in seconds + :code:`t` Duration in seconds + :code:`to` End time in seconds (ignored if :code:`t_in` is also specified) + ============= ======================================================================== + +Note it is also possible to specify these timing options for the input (i.e., using the +options :code:`ss_in`, :code:`t_in`, and :code:`to_in`). The input options, especially +:code:`ss_in`, may run faster but potentially less accurate. See `FFmpeg documentation +`__ for the explanation. + +.. code-block:: python + + >>> url = 'myvideo.mp4' + + >>> #read only the first 1 seconds + >>> fs, F = ffmpegio.video.read(url, t=1.0) + + >>> #read from 1.2 second mark to 2.5 second mark + >>> fs, F = ffmpegio.video.read(url, t=1.2, to=2.5) + +To specify by the frame numbers for video and sample numbers for audio, user must +convert the units to seconds using :py:func:`probe`. For example: + +.. code-block:: python + + >>> # get frame rate of the (first) video stream + >>> info = ffmpegio.probe.video_streams_basic('myvideo.mp4') + >>> fs = info[0]['frame_rate'] + + >>> #read 30 frame from the 11th frame (remember Python uses 0-based index) + >>> with ffmpegio.open('myvideo.mp4', 'rv', t=10/fs, t=30/fs) as f: + >>> frame = f.read() + >>> # do your thing with the frame data + +Likewise, for an audio input stream: + +.. code-block:: python + + >>> # get sampling rate of the (first) audio stream + >>> info = ffmpegio.probe.audio_streams_basic('myaudio.wav') + >>> fs = info[0]['sample_rate'] + + >>> #read first 10000 audio samples + >>> fs, x = ffmpegio.audio.read('myaudio.wav', t=10000/fs) + +Specify Output Frame/Sample Size +-------------------------------- + +FFmpeg let you change video size or the number of audio channels via output +options :code:`s` and :code:`ac`, respectively, without setting up a +filtergraph. For example, + +.. code-block:: python + + >>> # auto-scale video frame + >>> fs, F = ffmpegio.video.read('myvideo.mp4', t=1.0) # natively 320x240 + >>> F.shape + (30, 240, 320, 3) + + >>> # halve the size + >>> width = 160 + >>> height = 120 + >>> _, G = ffmpegio.video.read('myvideo.mp4', t=1.0, s=(width,height)) + >>> G.shape + (29, 120, 160, 3) + + >>> # auto-convert to mono + >>> fs, x = ffmpegio.audio.read('myaudio.wav') # natively stereo + >>> _, y = ffmpegio.audio.read('myaudio.wav', ac=1) # to mono + >>> x.shape + (44100, 2) + >>> y.shape + (44100, 1) + +To customize the conversion configuration, use :code:`vf` output option +with with :code:`scale` filter or :code:`af` output option with +:code:`channelmap` or :code:`pan` or other channel mixing filter + +Specify Sample Formats +---------------------- + +FFmpeg can also convert the formats of video pixels and sound samples on the fly. +This feature is enabled in :py:mod:`ffmpegio` via output options :code:`pix_fmt` +for video and :code:`sample_fmt` for audio. + + .. table:: Video :code:`pix_fmt` Option Values + :class: tight-table + + =============== ======================================== + :code:`pix_fmt` Description + =============== ======================================== + :code:`gray` grayscale + :code:`ya8` grayscale with transparent alpha channel + :code:`rgb24` RGB + :code:`rgba` RGB with alpha transparent alpha channel + =============== ======================================== + + .. table:: Audio :code:`sample_fmt` Option Values + :class: tight-table + + ================== =============================== =========== ========== + :code:`sample_fmt` Description min max + ================== =============================== =========== ========== + :code:`u8` unsigned 8-bit integer 0 255 + :code:`s16` signed 16-bit integer -32768 32767 + :code:`s32` signed 32-bit integer -2147483648 2147483647 + :code:`flt` single-precision floating point -1.0 1.0 + :code:`dbl` double-precision floating point -1.0 1.0 + ================== =============================== =========== ========== + +.. highlight:: python + +For example, + +.. code-block:: python + + >>> # auto-convert video frames to grayscale + >>> fs, RGB = ffmpegio.video.read('myvideo.mp4', t=1.0) # natively rgb24 + >>> _, GRAY = ffmpegio.video.read('myvideo.mp4', t=1.0, pix_fmt='gray') + >>> RGB.shape + (29, 640, 480, 3) + >>> GRAY.shape + (29, 640, 480, 1) + + >>> # auto-convert PNG image to remove transparency with white background + >>> RGBA = ffmpegio.image.read('myimage.png') # natively rgba with transparency + .. >>> RGB = ffmpegio.image.read('myimage.png', pix_fmt='rgb24', fill_color='white') + >>> RGB.shape + (100, 396, 4) + >>> RGB.shape + (100, 396, 3) + + >>> # auto-convert to audio samples to double precision + >>> fs, x = ffmpegio.audio.read('myaudio.wav') # natively s16 + >>> _, y = ffmpegio.audio.read('myaudio.wav', sample_fmt='dbl') + >>> x.max() + 2324 + >>> y.max() + 0.0709228515625 + +Note when converting from an image with alpha channel (FFmpeg does not support +alpha channel in video input) the background color may be specified with +:code:`fill_color` option (which defaults to ``'white'``). +See `the FFmpeg color specification `__ +for the list of predefined color names. + + +.. list-table:: Examples of changing image format + :class: tight-table + + * - :code:`'rgba'` (original) + - .. plot:: + + IM = ffmpegio.image.read('ffmpeg-logo.png') + plt.figure(figsize=(IM.shape[1]/96, IM.shape[0]/96), dpi=96) + plt.imshow(IM) + plt.gca().set_position((0, 0, 1, 1)) + plt.axis('off') + + .. code-block:: python + + ffmpegio.image.read('ffmpeg-logo.png') + + * - :code:`'rgb24'` with 'Linen' background + - .. plot:: + + IM = ffmpegio.image.read('ffmpeg-logo.png') + plt.figure(figsize=(IM.shape[1]/96, IM.shape[0]/96), dpi=96) + plt.imshow(IM) + plt.gca().set_position((0, 0, 1, 1)) + plt.axis('off') + + .. code-block:: python + + ffmpegio.image.read('ffmpeg-logo.png', pix_fmt='rgb24', fill_color='linen') + + * - :code:`'ya8'` + - .. plot:: + + IM = ffmpegio.image.read('ffmpeg-logo.png', pix_fmt='ya8') + plt.figure(figsize=(IM.shape[1]/96, IM.shape[0]/96), dpi=96) + plt.imshow(IM[...,0], alpha=IM[...,1]/255, cmap='gray') + plt.gca().set_position((0, 0, 1, 1)) + plt.axis('off') + + .. code-block:: python + + ffmpegio.image.read('ffmpeg-logo.png', pix_fmt='ya8') + + * - :code:`'gray'` with light gray background + - .. plot:: + + IM = ffmpegio.image.read('ffmpeg-logo.png', pix_fmt='gray', fill_color='#F0F0F0') + plt.figure(figsize=(IM.shape[1]/96, IM.shape[0]/96), dpi=96) + plt.imshow(IM, cmap='gray') + plt.gca().set_position((0, 0, 1, 1)) + plt.axis('off') + + .. code-block:: python + + ffmpegio.image.read('ffmpeg-logo.png', pix_fmt='gray', + fill_color='#F0F0F0') + +.. _quick-callback: + +Progress Callback +----------------- + +FFmpeg has :code:`-progress` option, which sends program-friendly progress +information to url. :py:mod:`ffmpegio` takes advantage of this option to +let user monitor the transcoding progress with a callback, which could be +set with :code:`progress` argument of all media operations. The callback +function must have the following signature: + +.. code-block:: python + + progress_callback(status:dict, done:bool) -> None|bool + +The :code:`status` dict containing the information similar to what FFmpeg +displays on console. The second argument :code:`done` is only :code:`True` +on the last progress call. Here is an example of :code:`status` dict: + +.. code-block:: python + + {'bitrate': '61.9kbits/s', + 'drop_frames': 0, + 'dup_frames': 0, + 'fps': 336.18, + 'frame': 1014, + 'out_time': '00:00:33.877914', + 'out_time_ms': 33877914, + 'out_time_us': 33877914, + 'speed': '11.2x', + 'stream_0_0_q': 29.0, + 'total_size': 262192} + +While FFmpeg does not report percent progress, it is possible to compute it from +:code:`frame` or :code:`out_time` if you know the total number of output frames +or the output duration, respectively. + +If an FFmpeg media stream object is invoked by :py:func:`ffmpegio.open` +with :code:`progress` callback argument, the callback function can terminate +the FFmpeg execution by returning :code:`True`. This feature is useful for GUI +programming. diff --git a/docs/_sources/rawdata_numpy.rst.txt b/docs/_sources/rawdata_numpy.rst.txt new file mode 100644 index 00000000..f25aec61 --- /dev/null +++ b/docs/_sources/rawdata_numpy.rst.txt @@ -0,0 +1,308 @@ +`ffmpegio`: Media I/O with FFmpeg in Python (with NumPy Array Plugin) +===================================================================== + +|pypi| |pypi-status| |pypi-pyvers| |github-license| |github-status| + +.. |pypi| image:: https://img.shields.io/pypi/v/ffmpegio + :alt: PyPI +.. |pypi-status| image:: https://img.shields.io/pypi/status/ffmpegio + :alt: PyPI - Status +.. |pypi-pyvers| image:: https://img.shields.io/pypi/pyversions/ffmpegio + :alt: PyPI - Python Version +.. |github-license| image:: https://img.shields.io/github/license/python-ffmpegio/python-ffmpegio + :alt: GitHub License +.. |github-status| image:: https://img.shields.io/github/workflow/status/python-ffmpegio/python-ffmpegio/Run%20Tests + :alt: GitHub Workflow Status + +Python `ffmpegio` package aims to bring the full capability of `FFmpeg `__ +to read, write, probe, and manipulate multimedia data to Python. FFmpeg is an open-source cross-platform +multimedia framework, which can handle most of the multimedia formats available today. + +.. note:: + + Since v0.3.0, `ffmpegio` Python distribution package has been split into `ffmpegio-core` and `ffmpegio` to allow + Numpy-independent installation. + +Install the full `ffmpegio` package via ``pip``: + +.. code-block:: bash + + pip install ffmpegio + +If `numpy.ndarray` data I/O is not needed, instead use + +.. code-block:: bash + + pip install ffmpegio-core + +Main Features +------------- + +* Pure-Python light-weight package interacting with FFmpeg executable found in + the system +* Transcode a media file to another in Python +* Read, write, filter, and create functions for audio, image, and video data +* Context-managing `ffmpegio.open` to perform stream read/write operations of video and audio +* Automatically detect and convert audio & video formats to and from `numpy.ndarray` properties +* Probe media file information +* Accepts all FFmpeg options including filter graphs +* Supports a user callback whenever FFmpeg updates its progress information file + (see `-progress` FFmpeg option) +* `ffconcat` scripter to make the use of `-f concat` demuxer easier +* I/O device enumeration to eliminate the need to look up device names. (currently supports only: Windows DirectShow) +* More features to follow + +Documentation +------------- + +Visit our `GitHub page here `__ + +Examples +-------- + +To import `ffmpegio` + +.. code-block:: python + + >>> import ffmpegio + +- `Transcoding `__ +- `Read Audio Files `__ +- `Read Image Files / Capture Video Frames `__ +- `Read Video Files `__ +- `Read Multiple Files or Streams `__ +- `Write Audio, Image, & Video Files `__ +- `Filter Audio, Image, & Video Data `__ +- `Stream I/O `__ +- `Device I/O Enumeration `__ +- `Progress Callback `__ +- `Run FFmpeg and FFprobe Directly `__ + +.. _ex_trancode: + +Transcoding +^^^^^^^^^^^ + +.. code-block:: python + + >>> # transcode, overwrite output file if exists, showing the FFmpeg log + >>> ffmpegio.transcode('input.avi', 'output.mp4', overwrite=True, show_log=True) + + >>> # 1-pass H.264 transcoding + >>> ffmpegio.transcode('input.avi', 'output.mkv', vcodec='libx264', show_log=True, + >>> preset='slow', crf=22, acodec='copy') + + >>> # 2-pass H.264 transcoding + >>> ffmpegio.transcode('input.avi', 'output.mkv', two_pass=True, show_log=True, + >>> **{'c:v':'libx264', 'b:v':'2600k', 'c:a':'aac', 'b:a':'128k'}) + + >>> # concatenate videos using concat demuxer + >>> files = ['/video/video1.mkv','/video/video2.mkv'] + >>> ffconcat = ffmpegio.FFConcat() + >>> ffconcat.add_files(files) + >>> with ffconcat: # generates temporary ffconcat file + >>> ffmpegio.transcode(ffconcat, 'output.mkv', f_in='concat', codec='copy', safe_in=0) + +.. _ex_read_audio: + +Read Audio Files +^^^^^^^^^^^^^^^^ + +.. code-block:: python + + >>> # read audio samples in its native sample format and return all channels + >>> fs, x = ffmpegio.audio.read('myaudio.wav') + >>> # fs: sampling rate in samples/second, x: [nsamples x nchannels] numpy array + + >>> # read audio samples from 24.15 seconds to 63.2 seconds, pre-convert to mono in float data type + >>> fs, x = ffmpegio.audio.read('myaudio.flac', ss=24.15, to=63.2, sample_fmt='dbl', ac=1) + + >>> # read filtered audio samples first 10 seconds + >>> # filter: equalizer which attenuate 10 dB at 1 kHz with a bandwidth of 200 Hz + >>> fs, x = ffmpegio.audio.read('myaudio.mp3', t=10.0, af='equalizer=f=1000:t=h:width=200:g=-10') + +.. _ex_read_image: + +Read Image Files / Capture Video Frames +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: python + + >>> # list supported image extensions + >>> ffmpegio.caps.muxer_info('image2')['extensions'] + ['bmp', 'dpx', 'exr', 'jls', 'jpeg', 'jpg', 'ljpg', 'pam', 'pbm', 'pcx', 'pfm', 'pgm', 'pgmyuv', + 'png', 'ppm', 'sgi', 'tga', 'tif', 'tiff', 'jp2', 'j2c', 'j2k', 'xwd', 'sun', 'ras', 'rs', 'im1', + 'im8', 'im24', 'sunras', 'xbm', 'xface', 'pix', 'y'] + + >>> # read BMP image with auto-detected pixel format (rgb24, gray, rgba, or ya8) + >>> I = ffmpegio.image.read('myimage.bmp') # I: [height x width x ncomp] numpy array + + >>> # read JPEG image, then convert to grayscale and proportionally scale so the width is 480 pixels + >>> I = ffmpegio.image.read('myimage.jpg', pix_fmt='grayscale', s='480x-1') + + >>> # read PNG image with transparency, convert it to plain RGB by filling transparent pixels orange + >>> I = ffmpegio.image.read('myimage.png', pix_fmt='rgb24', fill_color='orange') + + >>> # capture video frame at timestamp=4:25.3 and convert non-square pixels to square + >>> I = ffmpegio.image.read('myvideo.mpg', ss='4:25.3', square_pixels='upscale') + + >>> # capture 5 video frames and tile them on 3x2 grid with 7px between them, and 2px of initial margin + >>> I = ffmpegio.image.read('myvideo.mp4', vf='tile=3x2:nb_frames=5:padding=7:margin=2') + + >>> # create spectrogram of the audio input (must specify pix_fmt if input is audio) + >>> I = ffmpegio.image.read('myaudio.mp3', filter_complex='showspectrumpic=s=960x540', pix_fmt='rgb24') + + +.. _ex_read_video: + +Read Video Files +^^^^^^^^^^^^^^^^ + +.. code-block:: python + + >>> # read 50 video frames at t=00:32:40 then convert to grayscale + >>> fs, F = ffmpegio.video.read('myvideo.mp4', ss='00:32:40', vframes=50, pix_fmt='gray') + >>> # fs: frame rate in frames/second, F: [nframes x height x width x ncomp] numpy array + + >>> # get running spectrogram of audio input (must specify pix_fmt if input is audio) + >>> fs, F = ffmpegio.video.read('myvideo.mp4', pix_fmt='rgb24', filter_complex='showspectrum=s=1280x480') + + +.. _ex_read_media: + +Read Multiple Files or Streams +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: python + + >>> # read both video and audio streams (1 ea) + >>> rates, data = ffmpegio.media.read('mymedia.mp4') + >>> # rates: dict of frame rate and sampling rate: keys="v:0" and "a:0" + >>> # data: dict of video frame array and audio sample array: keys="v:0" and "a:0" + + >>> # combine video and audio files + >>> rates, data = ffmpegio.media.read('myvideo.mp4','myaudio.mp3') + + >>> # get output of complex filtergraph (can take multiple inputs) + >>> expr = "[v:0]split=2[out0][l1];[l1]edgedetect[out1]" + >>> rates, data = ffmpegio.media.read('myvideo.mp4',filter_complex=expr,map=['[out0]','[out1]']) + >>> # rates: dict of frame rates: keys="v:0" and "v:1" + >>> # data: dict of video frame arrays: keys="v:0" and "v:1" + +.. _ex_write: + +Write Audio, Image, & Video Files +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: python + + >>> # create a video file from a numpy array + >>> ffmpegio.video.write('myvideo.mp4', rate, F) + + >>> # create an image file from a numpy array + >>> ffmpegio.image.write('myimage.png', F) + + >>> # create an audio file from a numpy array + >>> ffmpegio.audio.write('myaudio.mp3', rate, x) + +.. _ex_filter: + +Filter Audio, Image, & Video Data +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: python + + >>> # Add fade-in and fade-out effects to audio data + >>> fs_out, y = ffmpegio.audio.filter('afade=t=in:ss=0:d=15,afade=t=out:st=875:d=25', fs_in, x) + + >>> # Apply mirror effect to an image + >>> I_out = ffmpegio.image.filter('crop=iw/2:ih:0:0,split[left][tmp];[tmp]hflip[right];[left][right] hstack', I_in) + + >>> # Add text at the center of the video frame + >>> filter = "drawtext=fontsize=30:fontfile=FreeSerif.ttf:text='hello world':x=(w-text_w)/2:y=(h-text_h)/2" + >>> fs_out, F_out = ffmpegio.video.filter(filter, fs_in, F_in) + +.. _ex_stream: + +Stream I/O +^^^^^^^^^^ + +.. code-block:: python + + >>> # process video 100 frames at a time and save output as a new video + >>> # with the same frame rate + >>> with ffmpegio.open('myvideo.mp4', 'rv', blocksize=100) as fin, + >>> ffmpegio.open('myoutput.mp4', 'wv', rate=fin.frame_rate) as fout: + >>> for frames in fin: + >>> fout.write(myprocess(frames)) + +.. _ex_devices: + +Device I/O Enumeration +^^^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: python + + >>> # record 5 minutes of audio from Windows microphone + >>> fs, x = ffmpegio.audio.read('a:0', f_in='dshow', sample_fmt='dbl', t=300) + + >>> # capture Windows' webcam frame + >>> with ffmpegio.open('v:0', 'rv', f_in='dshow') as webcam, + >>> for frame in webcam: + >>> process_frame(frame) + +.. _ex_progress: + +Progress Callback +^^^^^^^^^^^^^^^^^ + +.. code-block:: python + + >>> import pprint + + >>> # progress callback + >>> def progress(info, done): + >>> pprint(info) # bunch of stats + >>> if done: + >>> print('video decoding completed') + >>> else: + >>> return check_cancel_command(): # return True to kill immediately + + >>> # can be used in any butch processing + >>> rate, F = ffmpegio.video.read('myvideo.mp4', progress=progress) + + >>> # as well as for stream processing + >>> with ffmpegio.open('myvideo.mp4', 'rv', blocksize=100, progress=progress) as fin: + >>> for frames in fin: + >>> myprocess(frames) + +.. _ex_direct: + +Run FFmpeg and FFprobe Directly +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: python + + >>> from ffmpegio import ffmpeg, FFprobe, ffmpegprocess + >>> from subprocess import PIPE + + >>> # call with options as a long string + >>> ffmpeg('-i input.avi -b:v 64k -bufsize 64k output.avi') + + >>> # or call with list of options + >>> ffmpeg(['-i', 'input.avi' ,'-r', '24', 'output.avi']) + + >>> # the same for ffprobe + >>> ffprobe('ffprobe -show_streams -select_streams a INPUT') + + >>> # specify subprocess arguments to capture stdout + >>> out = ffprobe('ffprobe -of json -show_frames INPUT', + stdout=PIPE, universal_newlines=True).stdout + + >>> # use ffmpegprocess to take advantage of ffmpegio's default behaviors + >>> out = ffmpegprocess.run({"inputs": [("input.avi", None)], + "outputs": [("out1.mp4", None), + ("-", {"f": "rawvideo", "vframes": 1, "pix_fmt": "gray", "an": None}) + }, capture_log=True) + >>> print(out.stderr) # print the captured FFmpeg logs (banner text omitted) + >>> b = out.stdout # width*height bytes of the first frame diff --git a/docs/_static/_sphinx_javascript_frameworks_compat.js b/docs/_static/_sphinx_javascript_frameworks_compat.js new file mode 100644 index 00000000..81415803 --- /dev/null +++ b/docs/_static/_sphinx_javascript_frameworks_compat.js @@ -0,0 +1,123 @@ +/* Compatability shim for jQuery and underscores.js. + * + * Copyright Sphinx contributors + * Released under the two clause BSD licence + */ + +/** + * small helper function to urldecode strings + * + * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURIComponent#Decoding_query_parameters_from_a_URL + */ +jQuery.urldecode = function(x) { + if (!x) { + return x + } + return decodeURIComponent(x.replace(/\+/g, ' ')); +}; + +/** + * small helper function to urlencode strings + */ +jQuery.urlencode = encodeURIComponent; + +/** + * This function returns the parsed url parameters of the + * current request. Multiple values per key are supported, + * it will always return arrays of strings for the value parts. + */ +jQuery.getQueryParameters = function(s) { + if (typeof s === 'undefined') + s = document.location.search; + var parts = s.substr(s.indexOf('?') + 1).split('&'); + var result = {}; + for (var i = 0; i < parts.length; i++) { + var tmp = parts[i].split('=', 2); + var key = jQuery.urldecode(tmp[0]); + var value = jQuery.urldecode(tmp[1]); + if (key in result) + result[key].push(value); + else + result[key] = [value]; + } + return result; +}; + +/** + * highlight a given string on a jquery object by wrapping it in + * span elements with the given class name. + */ +jQuery.fn.highlightText = function(text, className) { + function highlight(node, addItems) { + if (node.nodeType === 3) { + var val = node.nodeValue; + var pos = val.toLowerCase().indexOf(text); + if (pos >= 0 && + !jQuery(node.parentNode).hasClass(className) && + !jQuery(node.parentNode).hasClass("nohighlight")) { + var span; + var isInSVG = jQuery(node).closest("body, svg, foreignObject").is("svg"); + if (isInSVG) { + span = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); + } else { + span = document.createElement("span"); + span.className = className; + } + span.appendChild(document.createTextNode(val.substr(pos, text.length))); + node.parentNode.insertBefore(span, node.parentNode.insertBefore( + document.createTextNode(val.substr(pos + text.length)), + node.nextSibling)); + node.nodeValue = val.substr(0, pos); + if (isInSVG) { + var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect"); + var bbox = node.parentElement.getBBox(); + rect.x.baseVal.value = bbox.x; + rect.y.baseVal.value = bbox.y; + rect.width.baseVal.value = bbox.width; + rect.height.baseVal.value = bbox.height; + rect.setAttribute('class', className); + addItems.push({ + "parent": node.parentNode, + "target": rect}); + } + } + } + else if (!jQuery(node).is("button, select, textarea")) { + jQuery.each(node.childNodes, function() { + highlight(this, addItems); + }); + } + } + var addItems = []; + var result = this.each(function() { + highlight(this, addItems); + }); + for (var i = 0; i < addItems.length; ++i) { + jQuery(addItems[i].parent).before(addItems[i].target); + } + return result; +}; + +/* + * backward compatibility for jQuery.browser + * This will be supported until firefox bug is fixed. + */ +if (!jQuery.browser) { + jQuery.uaMatch = function(ua) { + ua = ua.toLowerCase(); + + var match = /(chrome)[ \/]([\w.]+)/.exec(ua) || + /(webkit)[ \/]([\w.]+)/.exec(ua) || + /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) || + /(msie) ([\w.]+)/.exec(ua) || + ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) || + []; + + return { + browser: match[ 1 ] || "", + version: match[ 2 ] || "0" + }; + }; + jQuery.browser = {}; + jQuery.browser[jQuery.uaMatch(navigator.userAgent).browser] = true; +} diff --git a/docs/_static/basic.css b/docs/_static/basic.css new file mode 100644 index 00000000..7ebbd6d0 --- /dev/null +++ b/docs/_static/basic.css @@ -0,0 +1,914 @@ +/* + * Sphinx stylesheet -- basic theme. + */ + +/* -- main layout ----------------------------------------------------------- */ + +div.clearer { + clear: both; +} + +div.section::after { + display: block; + content: ''; + clear: left; +} + +/* -- relbar ---------------------------------------------------------------- */ + +div.related { + width: 100%; + font-size: 90%; +} + +div.related h3 { + display: none; +} + +div.related ul { + margin: 0; + padding: 0 0 0 10px; + list-style: none; +} + +div.related li { + display: inline; +} + +div.related li.right { + float: right; + margin-right: 5px; +} + +/* -- sidebar --------------------------------------------------------------- */ + +div.sphinxsidebarwrapper { + padding: 10px 5px 0 10px; +} + +div.sphinxsidebar { + float: left; + width: 230px; + margin-left: -100%; + font-size: 90%; + word-wrap: break-word; + overflow-wrap : break-word; +} + +div.sphinxsidebar ul { + list-style: none; +} + +div.sphinxsidebar ul ul, +div.sphinxsidebar ul.want-points { + margin-left: 20px; + list-style: square; +} + +div.sphinxsidebar ul ul { + margin-top: 0; + margin-bottom: 0; +} + +div.sphinxsidebar form { + margin-top: 10px; +} + +div.sphinxsidebar input { + border: 1px solid #98dbcc; + font-family: sans-serif; + font-size: 1em; +} + +div.sphinxsidebar #searchbox form.search { + overflow: hidden; +} + +div.sphinxsidebar #searchbox input[type="text"] { + float: left; + width: 80%; + padding: 0.25em; + box-sizing: border-box; +} + +div.sphinxsidebar #searchbox input[type="submit"] { + float: left; + width: 20%; + border-left: none; + padding: 0.25em; + box-sizing: border-box; +} + + +img { + border: 0; + max-width: 100%; +} + +/* -- search page ----------------------------------------------------------- */ + +ul.search { + margin-top: 10px; +} + +ul.search li { + padding: 5px 0; +} + +ul.search li a { + font-weight: bold; +} + +ul.search li p.context { + color: #888; + margin: 2px 0 0 30px; + text-align: left; +} + +ul.keywordmatches li.goodmatch a { + font-weight: bold; +} + +/* -- index page ------------------------------------------------------------ */ + +table.contentstable { + width: 90%; + margin-left: auto; + margin-right: auto; +} + +table.contentstable p.biglink { + line-height: 150%; +} + +a.biglink { + font-size: 1.3em; +} + +span.linkdescr { + font-style: italic; + padding-top: 5px; + font-size: 90%; +} + +/* -- general index --------------------------------------------------------- */ + +table.indextable { + width: 100%; +} + +table.indextable td { + text-align: left; + vertical-align: top; +} + +table.indextable ul { + margin-top: 0; + margin-bottom: 0; + list-style-type: none; +} + +table.indextable > tbody > tr > td > ul { + padding-left: 0em; +} + +table.indextable tr.pcap { + height: 10px; +} + +table.indextable tr.cap { + margin-top: 10px; + background-color: #f2f2f2; +} + +img.toggler { + margin-right: 3px; + margin-top: 3px; + cursor: pointer; +} + +div.modindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +div.genindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +/* -- domain module index --------------------------------------------------- */ + +table.modindextable td { + padding: 2px; + border-collapse: collapse; +} + +/* -- general body styles --------------------------------------------------- */ + +div.body { + min-width: 360px; + max-width: 800px; +} + +div.body p, div.body dd, div.body li, div.body blockquote { + -moz-hyphens: auto; + -ms-hyphens: auto; + -webkit-hyphens: auto; + hyphens: auto; +} + +a.headerlink { + visibility: hidden; +} + +a:visited { + color: #551A8B; +} + +h1:hover > a.headerlink, +h2:hover > a.headerlink, +h3:hover > a.headerlink, +h4:hover > a.headerlink, +h5:hover > a.headerlink, +h6:hover > a.headerlink, +dt:hover > a.headerlink, +caption:hover > a.headerlink, +p.caption:hover > a.headerlink, +div.code-block-caption:hover > a.headerlink { + visibility: visible; +} + +div.body p.caption { + text-align: inherit; +} + +div.body td { + text-align: left; +} + +.first { + margin-top: 0 !important; +} + +p.rubric { + margin-top: 30px; + font-weight: bold; +} + +img.align-left, figure.align-left, .figure.align-left, object.align-left { + clear: left; + float: left; + margin-right: 1em; +} + +img.align-right, figure.align-right, .figure.align-right, object.align-right { + clear: right; + float: right; + margin-left: 1em; +} + +img.align-center, figure.align-center, .figure.align-center, object.align-center { + display: block; + margin-left: auto; + margin-right: auto; +} + +img.align-default, figure.align-default, .figure.align-default { + display: block; + margin-left: auto; + margin-right: auto; +} + +.align-left { + text-align: left; +} + +.align-center { + text-align: center; +} + +.align-default { + text-align: center; +} + +.align-right { + text-align: right; +} + +/* -- sidebars -------------------------------------------------------------- */ + +div.sidebar, +aside.sidebar { + margin: 0 0 0.5em 1em; + border: 1px solid #ddb; + padding: 7px; + background-color: #ffe; + width: 40%; + float: right; + clear: right; + overflow-x: auto; +} + +p.sidebar-title { + font-weight: bold; +} + +nav.contents, +aside.topic, +div.admonition, div.topic, blockquote { + clear: left; +} + +/* -- topics ---------------------------------------------------------------- */ + +nav.contents, +aside.topic, +div.topic { + border: 1px solid #ccc; + padding: 7px; + margin: 10px 0 10px 0; +} + +p.topic-title { + font-size: 1.1em; + font-weight: bold; + margin-top: 10px; +} + +/* -- admonitions ----------------------------------------------------------- */ + +div.admonition { + margin-top: 10px; + margin-bottom: 10px; + padding: 7px; +} + +div.admonition dt { + font-weight: bold; +} + +p.admonition-title { + margin: 0px 10px 5px 0px; + font-weight: bold; +} + +div.body p.centered { + text-align: center; + margin-top: 25px; +} + +/* -- content of sidebars/topics/admonitions -------------------------------- */ + +div.sidebar > :last-child, +aside.sidebar > :last-child, +nav.contents > :last-child, +aside.topic > :last-child, +div.topic > :last-child, +div.admonition > :last-child { + margin-bottom: 0; +} + +div.sidebar::after, +aside.sidebar::after, +nav.contents::after, +aside.topic::after, +div.topic::after, +div.admonition::after, +blockquote::after { + display: block; + content: ''; + clear: both; +} + +/* -- tables ---------------------------------------------------------------- */ + +table.docutils { + margin-top: 10px; + margin-bottom: 10px; + border: 0; + border-collapse: collapse; +} + +table.align-center { + margin-left: auto; + margin-right: auto; +} + +table.align-default { + margin-left: auto; + margin-right: auto; +} + +table caption span.caption-number { + font-style: italic; +} + +table caption span.caption-text { +} + +table.docutils td, table.docutils th { + padding: 1px 8px 1px 5px; + border-top: 0; + border-left: 0; + border-right: 0; + border-bottom: 1px solid #aaa; +} + +th { + text-align: left; + padding-right: 5px; +} + +table.citation { + border-left: solid 1px gray; + margin-left: 1px; +} + +table.citation td { + border-bottom: none; +} + +th > :first-child, +td > :first-child { + margin-top: 0px; +} + +th > :last-child, +td > :last-child { + margin-bottom: 0px; +} + +/* -- figures --------------------------------------------------------------- */ + +div.figure, figure { + margin: 0.5em; + padding: 0.5em; +} + +div.figure p.caption, figcaption { + padding: 0.3em; +} + +div.figure p.caption span.caption-number, +figcaption span.caption-number { + font-style: italic; +} + +div.figure p.caption span.caption-text, +figcaption span.caption-text { +} + +/* -- field list styles ----------------------------------------------------- */ + +table.field-list td, table.field-list th { + border: 0 !important; +} + +.field-list ul { + margin: 0; + padding-left: 1em; +} + +.field-list p { + margin: 0; +} + +.field-name { + -moz-hyphens: manual; + -ms-hyphens: manual; + -webkit-hyphens: manual; + hyphens: manual; +} + +/* -- hlist styles ---------------------------------------------------------- */ + +table.hlist { + margin: 1em 0; +} + +table.hlist td { + vertical-align: top; +} + +/* -- object description styles --------------------------------------------- */ + +.sig { + font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; +} + +.sig-name, code.descname { + background-color: transparent; + font-weight: bold; +} + +.sig-name { + font-size: 1.1em; +} + +code.descname { + font-size: 1.2em; +} + +.sig-prename, code.descclassname { + background-color: transparent; +} + +.optional { + font-size: 1.3em; +} + +.sig-paren { + font-size: larger; +} + +.sig-param.n { + font-style: italic; +} + +/* C++ specific styling */ + +.sig-inline.c-texpr, +.sig-inline.cpp-texpr { + font-family: unset; +} + +.sig.c .k, .sig.c .kt, +.sig.cpp .k, .sig.cpp .kt { + color: #0033B3; +} + +.sig.c .m, +.sig.cpp .m { + color: #1750EB; +} + +.sig.c .s, .sig.c .sc, +.sig.cpp .s, .sig.cpp .sc { + color: #067D17; +} + + +/* -- other body styles ----------------------------------------------------- */ + +ol.arabic { + list-style: decimal; +} + +ol.loweralpha { + list-style: lower-alpha; +} + +ol.upperalpha { + list-style: upper-alpha; +} + +ol.lowerroman { + list-style: lower-roman; +} + +ol.upperroman { + list-style: upper-roman; +} + +:not(li) > ol > li:first-child > :first-child, +:not(li) > ul > li:first-child > :first-child { + margin-top: 0px; +} + +:not(li) > ol > li:last-child > :last-child, +:not(li) > ul > li:last-child > :last-child { + margin-bottom: 0px; +} + +ol.simple ol p, +ol.simple ul p, +ul.simple ol p, +ul.simple ul p { + margin-top: 0; +} + +ol.simple > li:not(:first-child) > p, +ul.simple > li:not(:first-child) > p { + margin-top: 0; +} + +ol.simple p, +ul.simple p { + margin-bottom: 0; +} + +aside.footnote > span, +div.citation > span { + float: left; +} +aside.footnote > span:last-of-type, +div.citation > span:last-of-type { + padding-right: 0.5em; +} +aside.footnote > p { + margin-left: 2em; +} +div.citation > p { + margin-left: 4em; +} +aside.footnote > p:last-of-type, +div.citation > p:last-of-type { + margin-bottom: 0em; +} +aside.footnote > p:last-of-type:after, +div.citation > p:last-of-type:after { + content: ""; + clear: both; +} + +dl.field-list { + display: grid; + grid-template-columns: fit-content(30%) auto; +} + +dl.field-list > dt { + font-weight: bold; + word-break: break-word; + padding-left: 0.5em; + padding-right: 5px; +} + +dl.field-list > dd { + padding-left: 0.5em; + margin-top: 0em; + margin-left: 0em; + margin-bottom: 0em; +} + +dl { + margin-bottom: 15px; +} + +dd > :first-child { + margin-top: 0px; +} + +dd ul, dd table { + margin-bottom: 10px; +} + +dd { + margin-top: 3px; + margin-bottom: 10px; + margin-left: 30px; +} + +.sig dd { + margin-top: 0px; + margin-bottom: 0px; +} + +.sig dl { + margin-top: 0px; + margin-bottom: 0px; +} + +dl > dd:last-child, +dl > dd:last-child > :last-child { + margin-bottom: 0; +} + +dt:target, span.highlighted { + background-color: #fbe54e; +} + +rect.highlighted { + fill: #fbe54e; +} + +dl.glossary dt { + font-weight: bold; + font-size: 1.1em; +} + +.versionmodified { + font-style: italic; +} + +.system-message { + background-color: #fda; + padding: 5px; + border: 3px solid red; +} + +.footnote:target { + background-color: #ffa; +} + +.line-block { + display: block; + margin-top: 1em; + margin-bottom: 1em; +} + +.line-block .line-block { + margin-top: 0; + margin-bottom: 0; + margin-left: 1.5em; +} + +.guilabel, .menuselection { + font-family: sans-serif; +} + +.accelerator { + text-decoration: underline; +} + +.classifier { + font-style: oblique; +} + +.classifier:before { + font-style: normal; + margin: 0 0.5em; + content: ":"; + display: inline-block; +} + +abbr, acronym { + border-bottom: dotted 1px; + cursor: help; +} + +.translated { + background-color: rgba(207, 255, 207, 0.2) +} + +.untranslated { + background-color: rgba(255, 207, 207, 0.2) +} + +/* -- code displays --------------------------------------------------------- */ + +pre { + overflow: auto; + overflow-y: hidden; /* fixes display issues on Chrome browsers */ +} + +pre, div[class*="highlight-"] { + clear: both; +} + +span.pre { + -moz-hyphens: none; + -ms-hyphens: none; + -webkit-hyphens: none; + hyphens: none; + white-space: nowrap; +} + +div[class*="highlight-"] { + margin: 1em 0; +} + +td.linenos pre { + border: 0; + background-color: transparent; + color: #aaa; +} + +table.highlighttable { + display: block; +} + +table.highlighttable tbody { + display: block; +} + +table.highlighttable tr { + display: flex; +} + +table.highlighttable td { + margin: 0; + padding: 0; +} + +table.highlighttable td.linenos { + padding-right: 0.5em; +} + +table.highlighttable td.code { + flex: 1; + overflow: hidden; +} + +.highlight .hll { + display: block; +} + +div.highlight pre, +table.highlighttable pre { + margin: 0; +} + +div.code-block-caption + div { + margin-top: 0; +} + +div.code-block-caption { + margin-top: 1em; + padding: 2px 5px; + font-size: small; +} + +div.code-block-caption code { + background-color: transparent; +} + +table.highlighttable td.linenos, +span.linenos, +div.highlight span.gp { /* gp: Generic.Prompt */ + user-select: none; + -webkit-user-select: text; /* Safari fallback only */ + -webkit-user-select: none; /* Chrome/Safari */ + -moz-user-select: none; /* Firefox */ + -ms-user-select: none; /* IE10+ */ +} + +div.code-block-caption span.caption-number { + padding: 0.1em 0.3em; + font-style: italic; +} + +div.code-block-caption span.caption-text { +} + +div.literal-block-wrapper { + margin: 1em 0; +} + +code.xref, a code { + background-color: transparent; + font-weight: bold; +} + +h1 code, h2 code, h3 code, h4 code, h5 code, h6 code { + background-color: transparent; +} + +.viewcode-link { + float: right; +} + +.viewcode-back { + float: right; + font-family: sans-serif; +} + +div.viewcode-block:target { + margin: -1px -10px; + padding: 0 10px; +} + +/* -- math display ---------------------------------------------------------- */ + +img.math { + vertical-align: middle; +} + +div.body div.math p { + text-align: center; +} + +span.eqno { + float: right; +} + +span.eqno a.headerlink { + position: absolute; + z-index: 1; +} + +div.math:hover a.headerlink { + visibility: visible; +} + +/* -- printout stylesheet --------------------------------------------------- */ + +@media print { + div.document, + div.documentwrapper, + div.bodywrapper { + margin: 0 !important; + width: 100%; + } + + div.sphinxsidebar, + div.related, + div.footer, + #top-link { + display: none; + } +} \ No newline at end of file diff --git a/docs/_static/css/badge_only.css b/docs/_static/css/badge_only.css new file mode 100644 index 00000000..88ba55b9 --- /dev/null +++ b/docs/_static/css/badge_only.css @@ -0,0 +1 @@ +.clearfix{*zoom:1}.clearfix:after,.clearfix:before{display:table;content:""}.clearfix:after{clear:both}@font-face{font-family:FontAwesome;font-style:normal;font-weight:400;src:url(fonts/fontawesome-webfont.eot?674f50d287a8c48dc19ba404d20fe713?#iefix) format("embedded-opentype"),url(fonts/fontawesome-webfont.woff2?af7ae505a9eed503f8b8e6982036873e) format("woff2"),url(fonts/fontawesome-webfont.woff?fee66e712a8a08eef5805a46892932ad) format("woff"),url(fonts/fontawesome-webfont.ttf?b06871f281fee6b241d60582ae9369b9) format("truetype"),url(fonts/fontawesome-webfont.svg?912ec66d7572ff821749319396470bde#FontAwesome) format("svg")}.fa:before{font-family:FontAwesome;font-style:normal;font-weight:400;line-height:1}.fa:before,a .fa{text-decoration:inherit}.fa:before,a .fa,li .fa{display:inline-block}li .fa-large:before{width:1.875em}ul.fas{list-style-type:none;margin-left:2em;text-indent:-.8em}ul.fas li .fa{width:.8em}ul.fas li .fa-large:before{vertical-align:baseline}.fa-book:before,.icon-book:before{content:"\f02d"}.fa-caret-down:before,.icon-caret-down:before{content:"\f0d7"}.fa-caret-up:before,.icon-caret-up:before{content:"\f0d8"}.fa-caret-left:before,.icon-caret-left:before{content:"\f0d9"}.fa-caret-right:before,.icon-caret-right:before{content:"\f0da"}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;z-index:400}.rst-versions a{color:#2980b9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27ae60}.rst-versions .rst-current-version:after{clear:both;content:"";display:block}.rst-versions .rst-current-version .fa{color:#fcfcfc}.rst-versions .rst-current-version .fa-book,.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#e74c3c;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#f1c40f;color:#000}.rst-versions.shift-up{height:auto;max-height:100%;overflow-y:scroll}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:grey;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:1px solid #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions .rst-other-versions .rtd-current-item{font-weight:700}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px;max-height:90%}.rst-versions.rst-badge .fa-book,.rst-versions.rst-badge .icon-book{float:none;line-height:30px}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book,.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge>.rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width:768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}}#flyout-search-form{padding:6px} \ No newline at end of file diff --git a/docs/_static/css/custom.css b/docs/_static/css/custom.css new file mode 100644 index 00000000..284e8063 --- /dev/null +++ b/docs/_static/css/custom.css @@ -0,0 +1,13 @@ +table.tight-table { + width: 100%; + table-layout: auto; +} + +.tight-table td { + white-space: normal !important; +} + +.wy-table-responsive table td, +.wy-table-responsive table th { + white-space: normal; +} \ No newline at end of file diff --git a/docs/_static/css/fonts/Roboto-Slab-Bold.woff b/docs/_static/css/fonts/Roboto-Slab-Bold.woff new file mode 100644 index 00000000..6cb60000 Binary files /dev/null and b/docs/_static/css/fonts/Roboto-Slab-Bold.woff differ diff --git a/docs/_static/css/fonts/Roboto-Slab-Bold.woff2 b/docs/_static/css/fonts/Roboto-Slab-Bold.woff2 new file mode 100644 index 00000000..7059e231 Binary files /dev/null and b/docs/_static/css/fonts/Roboto-Slab-Bold.woff2 differ diff --git a/docs/_static/css/fonts/Roboto-Slab-Regular.woff b/docs/_static/css/fonts/Roboto-Slab-Regular.woff new file mode 100644 index 00000000..f815f63f Binary files /dev/null and b/docs/_static/css/fonts/Roboto-Slab-Regular.woff differ diff --git a/docs/_static/css/fonts/Roboto-Slab-Regular.woff2 b/docs/_static/css/fonts/Roboto-Slab-Regular.woff2 new file mode 100644 index 00000000..f2c76e5b Binary files /dev/null and b/docs/_static/css/fonts/Roboto-Slab-Regular.woff2 differ diff --git a/docs/_static/css/fonts/fontawesome-webfont.eot b/docs/_static/css/fonts/fontawesome-webfont.eot new file mode 100644 index 00000000..e9f60ca9 Binary files /dev/null and b/docs/_static/css/fonts/fontawesome-webfont.eot differ diff --git a/docs/_static/css/fonts/fontawesome-webfont.svg b/docs/_static/css/fonts/fontawesome-webfont.svg new file mode 100644 index 00000000..855c845e --- /dev/null +++ b/docs/_static/css/fonts/fontawesome-webfont.svg @@ -0,0 +1,2671 @@ + + + + +Created by FontForge 20120731 at Mon Oct 24 17:37:40 2016 + By ,,, +Copyright Dave Gandy 2016. All rights reserved. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/_static/css/fonts/fontawesome-webfont.ttf b/docs/_static/css/fonts/fontawesome-webfont.ttf new file mode 100644 index 00000000..35acda2f Binary files /dev/null and b/docs/_static/css/fonts/fontawesome-webfont.ttf differ diff --git a/docs/_static/css/fonts/fontawesome-webfont.woff b/docs/_static/css/fonts/fontawesome-webfont.woff new file mode 100644 index 00000000..400014a4 Binary files /dev/null and b/docs/_static/css/fonts/fontawesome-webfont.woff differ diff --git a/docs/_static/css/fonts/fontawesome-webfont.woff2 b/docs/_static/css/fonts/fontawesome-webfont.woff2 new file mode 100644 index 00000000..4d13fc60 Binary files /dev/null and b/docs/_static/css/fonts/fontawesome-webfont.woff2 differ diff --git a/docs/_static/css/fonts/lato-bold-italic.woff b/docs/_static/css/fonts/lato-bold-italic.woff new file mode 100644 index 00000000..88ad05b9 Binary files /dev/null and b/docs/_static/css/fonts/lato-bold-italic.woff differ diff --git a/docs/_static/css/fonts/lato-bold-italic.woff2 b/docs/_static/css/fonts/lato-bold-italic.woff2 new file mode 100644 index 00000000..c4e3d804 Binary files /dev/null and b/docs/_static/css/fonts/lato-bold-italic.woff2 differ diff --git a/docs/_static/css/fonts/lato-bold.woff b/docs/_static/css/fonts/lato-bold.woff new file mode 100644 index 00000000..c6dff51f Binary files /dev/null and b/docs/_static/css/fonts/lato-bold.woff differ diff --git a/docs/_static/css/fonts/lato-bold.woff2 b/docs/_static/css/fonts/lato-bold.woff2 new file mode 100644 index 00000000..bb195043 Binary files /dev/null and b/docs/_static/css/fonts/lato-bold.woff2 differ diff --git a/docs/_static/css/fonts/lato-normal-italic.woff b/docs/_static/css/fonts/lato-normal-italic.woff new file mode 100644 index 00000000..76114bc0 Binary files /dev/null and b/docs/_static/css/fonts/lato-normal-italic.woff differ diff --git a/docs/_static/css/fonts/lato-normal-italic.woff2 b/docs/_static/css/fonts/lato-normal-italic.woff2 new file mode 100644 index 00000000..3404f37e Binary files /dev/null and b/docs/_static/css/fonts/lato-normal-italic.woff2 differ diff --git a/docs/_static/css/fonts/lato-normal.woff b/docs/_static/css/fonts/lato-normal.woff new file mode 100644 index 00000000..ae1307ff Binary files /dev/null and b/docs/_static/css/fonts/lato-normal.woff differ diff --git a/docs/_static/css/fonts/lato-normal.woff2 b/docs/_static/css/fonts/lato-normal.woff2 new file mode 100644 index 00000000..3bf98433 Binary files /dev/null and b/docs/_static/css/fonts/lato-normal.woff2 differ diff --git a/docs/_static/css/theme.css b/docs/_static/css/theme.css new file mode 100644 index 00000000..0f14f106 --- /dev/null +++ b/docs/_static/css/theme.css @@ -0,0 +1,4 @@ +html{box-sizing:border-box}*,:after,:before{box-sizing:inherit}article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block}audio,canvas,video{display:inline-block;*display:inline;*zoom:1}[hidden],audio:not([controls]){display:none}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}blockquote{margin:0}dfn{font-style:italic}ins{background:#ff9;text-decoration:none}ins,mark{color:#000}mark{background:#ff0;font-style:italic;font-weight:700}.rst-content code,.rst-content tt,code,kbd,pre,samp{font-family:monospace,serif;_font-family:courier new,monospace;font-size:1em}pre{white-space:pre}q{quotes:none}q:after,q:before{content:"";content:none}small{font-size:85%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}dl,ol,ul{margin:0;padding:0;list-style:none;list-style-image:none}li{list-style:none}dd{margin:0}img{border:0;-ms-interpolation-mode:bicubic;vertical-align:middle;max-width:100%}svg:not(:root){overflow:hidden}figure,form{margin:0}label{cursor:pointer}button,input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}button,input{line-height:normal}button,input[type=button],input[type=reset],input[type=submit]{cursor:pointer;-webkit-appearance:button;*overflow:visible}button[disabled],input[disabled]{cursor:default}input[type=search]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}textarea{resize:vertical}table{border-collapse:collapse;border-spacing:0}td{vertical-align:top}.chromeframe{margin:.2em 0;background:#ccc;color:#000;padding:.2em 0}.ir{display:block;border:0;text-indent:-999em;overflow:hidden;background-color:transparent;background-repeat:no-repeat;text-align:left;direction:ltr;*line-height:0}.ir br{display:none}.hidden{display:none!important;visibility:hidden}.visuallyhidden{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.visuallyhidden.focusable:active,.visuallyhidden.focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}.invisible{visibility:hidden}.relative{position:relative}big,small{font-size:100%}@media print{body,html,section{background:none!important}*{box-shadow:none!important;text-shadow:none!important;filter:none!important;-ms-filter:none!important}a,a:visited{text-decoration:underline}.ir a:after,a[href^="#"]:after,a[href^="javascript:"]:after{content:""}blockquote,pre{page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}img{max-width:100%!important}@page{margin:.5cm}.rst-content .toctree-wrapper>p.caption,h2,h3,p{orphans:3;widows:3}.rst-content .toctree-wrapper>p.caption,h2,h3{page-break-after:avoid}}.btn,.fa:before,.icon:before,.rst-content .admonition,.rst-content .admonition-title:before,.rst-content .admonition-todo,.rst-content .attention,.rst-content .caution,.rst-content .code-block-caption .headerlink:before,.rst-content .danger,.rst-content .eqno .headerlink:before,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .note,.rst-content .seealso,.rst-content .tip,.rst-content .warning,.rst-content code.download span:first-child:before,.rst-content dl dt .headerlink:before,.rst-content h1 .headerlink:before,.rst-content h2 .headerlink:before,.rst-content h3 .headerlink:before,.rst-content h4 .headerlink:before,.rst-content h5 .headerlink:before,.rst-content h6 .headerlink:before,.rst-content p.caption .headerlink:before,.rst-content p .headerlink:before,.rst-content table>caption .headerlink:before,.rst-content tt.download span:first-child:before,.wy-alert,.wy-dropdown .caret:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-menu-vertical li.current>a button.toctree-expand:before,.wy-menu-vertical li.on a button.toctree-expand:before,.wy-menu-vertical li button.toctree-expand:before,input[type=color],input[type=date],input[type=datetime-local],input[type=datetime],input[type=email],input[type=month],input[type=number],input[type=password],input[type=search],input[type=tel],input[type=text],input[type=time],input[type=url],input[type=week],select,textarea{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1}.clearfix:after,.clearfix:before{display:table;content:""}.clearfix:after{clear:both}/*! + * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome + * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) + */@font-face{font-family:FontAwesome;src:url(fonts/fontawesome-webfont.eot?674f50d287a8c48dc19ba404d20fe713);src:url(fonts/fontawesome-webfont.eot?674f50d287a8c48dc19ba404d20fe713?#iefix&v=4.7.0) format("embedded-opentype"),url(fonts/fontawesome-webfont.woff2?af7ae505a9eed503f8b8e6982036873e) format("woff2"),url(fonts/fontawesome-webfont.woff?fee66e712a8a08eef5805a46892932ad) format("woff"),url(fonts/fontawesome-webfont.ttf?b06871f281fee6b241d60582ae9369b9) format("truetype"),url(fonts/fontawesome-webfont.svg?912ec66d7572ff821749319396470bde#fontawesomeregular) format("svg");font-weight:400;font-style:normal}.fa,.icon,.rst-content .admonition-title,.rst-content .code-block-caption .headerlink,.rst-content .eqno .headerlink,.rst-content code.download span:first-child,.rst-content dl dt .headerlink,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content p.caption .headerlink,.rst-content p .headerlink,.rst-content table>caption .headerlink,.rst-content tt.download span:first-child,.wy-menu-vertical li.current>a button.toctree-expand,.wy-menu-vertical li.on a button.toctree-expand,.wy-menu-vertical li button.toctree-expand{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14286em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14286em;width:2.14286em;top:.14286em;text-align:center}.fa-li.fa-lg{left:-1.85714em}.fa-border{padding:.2em .25em .15em;border:.08em solid #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa-pull-left.icon,.fa.fa-pull-left,.rst-content .code-block-caption .fa-pull-left.headerlink,.rst-content .eqno .fa-pull-left.headerlink,.rst-content .fa-pull-left.admonition-title,.rst-content code.download span.fa-pull-left:first-child,.rst-content dl dt .fa-pull-left.headerlink,.rst-content h1 .fa-pull-left.headerlink,.rst-content h2 .fa-pull-left.headerlink,.rst-content h3 .fa-pull-left.headerlink,.rst-content h4 .fa-pull-left.headerlink,.rst-content h5 .fa-pull-left.headerlink,.rst-content h6 .fa-pull-left.headerlink,.rst-content p .fa-pull-left.headerlink,.rst-content table>caption .fa-pull-left.headerlink,.rst-content tt.download span.fa-pull-left:first-child,.wy-menu-vertical li.current>a button.fa-pull-left.toctree-expand,.wy-menu-vertical li.on a button.fa-pull-left.toctree-expand,.wy-menu-vertical li button.fa-pull-left.toctree-expand{margin-right:.3em}.fa-pull-right.icon,.fa.fa-pull-right,.rst-content .code-block-caption .fa-pull-right.headerlink,.rst-content .eqno .fa-pull-right.headerlink,.rst-content .fa-pull-right.admonition-title,.rst-content code.download span.fa-pull-right:first-child,.rst-content dl dt .fa-pull-right.headerlink,.rst-content h1 .fa-pull-right.headerlink,.rst-content h2 .fa-pull-right.headerlink,.rst-content h3 .fa-pull-right.headerlink,.rst-content h4 .fa-pull-right.headerlink,.rst-content h5 .fa-pull-right.headerlink,.rst-content h6 .fa-pull-right.headerlink,.rst-content p .fa-pull-right.headerlink,.rst-content table>caption .fa-pull-right.headerlink,.rst-content tt.download span.fa-pull-right:first-child,.wy-menu-vertical li.current>a button.fa-pull-right.toctree-expand,.wy-menu-vertical li.on a button.fa-pull-right.toctree-expand,.wy-menu-vertical li button.fa-pull-right.toctree-expand{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left,.pull-left.icon,.rst-content .code-block-caption .pull-left.headerlink,.rst-content .eqno .pull-left.headerlink,.rst-content .pull-left.admonition-title,.rst-content code.download span.pull-left:first-child,.rst-content dl dt .pull-left.headerlink,.rst-content h1 .pull-left.headerlink,.rst-content h2 .pull-left.headerlink,.rst-content h3 .pull-left.headerlink,.rst-content h4 .pull-left.headerlink,.rst-content h5 .pull-left.headerlink,.rst-content h6 .pull-left.headerlink,.rst-content p .pull-left.headerlink,.rst-content table>caption .pull-left.headerlink,.rst-content tt.download span.pull-left:first-child,.wy-menu-vertical li.current>a button.pull-left.toctree-expand,.wy-menu-vertical li.on a button.pull-left.toctree-expand,.wy-menu-vertical li button.pull-left.toctree-expand{margin-right:.3em}.fa.pull-right,.pull-right.icon,.rst-content .code-block-caption .pull-right.headerlink,.rst-content .eqno .pull-right.headerlink,.rst-content .pull-right.admonition-title,.rst-content code.download span.pull-right:first-child,.rst-content dl dt .pull-right.headerlink,.rst-content h1 .pull-right.headerlink,.rst-content h2 .pull-right.headerlink,.rst-content h3 .pull-right.headerlink,.rst-content h4 .pull-right.headerlink,.rst-content h5 .pull-right.headerlink,.rst-content h6 .pull-right.headerlink,.rst-content p .pull-right.headerlink,.rst-content table>caption .pull-right.headerlink,.rst-content tt.download span.pull-right:first-child,.wy-menu-vertical li.current>a button.pull-right.toctree-expand,.wy-menu-vertical li.on a button.pull-right.toctree-expand,.wy-menu-vertical li button.pull-right.toctree-expand{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s linear infinite;animation:fa-spin 2s linear infinite}.fa-pulse{-webkit-animation:fa-spin 1s steps(8) infinite;animation:fa-spin 1s steps(8) infinite}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scaleX(-1);-ms-transform:scaleX(-1);transform:scaleX(-1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scaleY(-1);-ms-transform:scaleY(-1);transform:scaleY(-1)}:root .fa-flip-horizontal,:root .fa-flip-vertical,:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:""}.fa-music:before{content:""}.fa-search:before,.icon-search:before{content:""}.fa-envelope-o:before{content:""}.fa-heart:before{content:""}.fa-star:before{content:""}.fa-star-o:before{content:""}.fa-user:before{content:""}.fa-film:before{content:""}.fa-th-large:before{content:""}.fa-th:before{content:""}.fa-th-list:before{content:""}.fa-check:before{content:""}.fa-close:before,.fa-remove:before,.fa-times:before{content:""}.fa-search-plus:before{content:""}.fa-search-minus:before{content:""}.fa-power-off:before{content:""}.fa-signal:before{content:""}.fa-cog:before,.fa-gear:before{content:""}.fa-trash-o:before{content:""}.fa-home:before,.icon-home:before{content:""}.fa-file-o:before{content:""}.fa-clock-o:before{content:""}.fa-road:before{content:""}.fa-download:before,.rst-content code.download span:first-child:before,.rst-content tt.download span:first-child:before{content:""}.fa-arrow-circle-o-down:before{content:""}.fa-arrow-circle-o-up:before{content:""}.fa-inbox:before{content:""}.fa-play-circle-o:before{content:""}.fa-repeat:before,.fa-rotate-right:before{content:""}.fa-refresh:before{content:""}.fa-list-alt:before{content:""}.fa-lock:before{content:""}.fa-flag:before{content:""}.fa-headphones:before{content:""}.fa-volume-off:before{content:""}.fa-volume-down:before{content:""}.fa-volume-up:before{content:""}.fa-qrcode:before{content:""}.fa-barcode:before{content:""}.fa-tag:before{content:""}.fa-tags:before{content:""}.fa-book:before,.icon-book:before{content:""}.fa-bookmark:before{content:""}.fa-print:before{content:""}.fa-camera:before{content:""}.fa-font:before{content:""}.fa-bold:before{content:""}.fa-italic:before{content:""}.fa-text-height:before{content:""}.fa-text-width:before{content:""}.fa-align-left:before{content:""}.fa-align-center:before{content:""}.fa-align-right:before{content:""}.fa-align-justify:before{content:""}.fa-list:before{content:""}.fa-dedent:before,.fa-outdent:before{content:""}.fa-indent:before{content:""}.fa-video-camera:before{content:""}.fa-image:before,.fa-photo:before,.fa-picture-o:before{content:""}.fa-pencil:before{content:""}.fa-map-marker:before{content:""}.fa-adjust:before{content:""}.fa-tint:before{content:""}.fa-edit:before,.fa-pencil-square-o:before{content:""}.fa-share-square-o:before{content:""}.fa-check-square-o:before{content:""}.fa-arrows:before{content:""}.fa-step-backward:before{content:""}.fa-fast-backward:before{content:""}.fa-backward:before{content:""}.fa-play:before{content:""}.fa-pause:before{content:""}.fa-stop:before{content:""}.fa-forward:before{content:""}.fa-fast-forward:before{content:""}.fa-step-forward:before{content:""}.fa-eject:before{content:""}.fa-chevron-left:before{content:""}.fa-chevron-right:before{content:""}.fa-plus-circle:before{content:""}.fa-minus-circle:before{content:""}.fa-times-circle:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before{content:""}.fa-check-circle:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before{content:""}.fa-question-circle:before{content:""}.fa-info-circle:before{content:""}.fa-crosshairs:before{content:""}.fa-times-circle-o:before{content:""}.fa-check-circle-o:before{content:""}.fa-ban:before{content:""}.fa-arrow-left:before{content:""}.fa-arrow-right:before{content:""}.fa-arrow-up:before{content:""}.fa-arrow-down:before{content:""}.fa-mail-forward:before,.fa-share:before{content:""}.fa-expand:before{content:""}.fa-compress:before{content:""}.fa-plus:before{content:""}.fa-minus:before{content:""}.fa-asterisk:before{content:""}.fa-exclamation-circle:before,.rst-content .admonition-title:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before{content:""}.fa-gift:before{content:""}.fa-leaf:before{content:""}.fa-fire:before,.icon-fire:before{content:""}.fa-eye:before{content:""}.fa-eye-slash:before{content:""}.fa-exclamation-triangle:before,.fa-warning:before{content:""}.fa-plane:before{content:""}.fa-calendar:before{content:""}.fa-random:before{content:""}.fa-comment:before{content:""}.fa-magnet:before{content:""}.fa-chevron-up:before{content:""}.fa-chevron-down:before{content:""}.fa-retweet:before{content:""}.fa-shopping-cart:before{content:""}.fa-folder:before{content:""}.fa-folder-open:before{content:""}.fa-arrows-v:before{content:""}.fa-arrows-h:before{content:""}.fa-bar-chart-o:before,.fa-bar-chart:before{content:""}.fa-twitter-square:before{content:""}.fa-facebook-square:before{content:""}.fa-camera-retro:before{content:""}.fa-key:before{content:""}.fa-cogs:before,.fa-gears:before{content:""}.fa-comments:before{content:""}.fa-thumbs-o-up:before{content:""}.fa-thumbs-o-down:before{content:""}.fa-star-half:before{content:""}.fa-heart-o:before{content:""}.fa-sign-out:before{content:""}.fa-linkedin-square:before{content:""}.fa-thumb-tack:before{content:""}.fa-external-link:before{content:""}.fa-sign-in:before{content:""}.fa-trophy:before{content:""}.fa-github-square:before{content:""}.fa-upload:before{content:""}.fa-lemon-o:before{content:""}.fa-phone:before{content:""}.fa-square-o:before{content:""}.fa-bookmark-o:before{content:""}.fa-phone-square:before{content:""}.fa-twitter:before{content:""}.fa-facebook-f:before,.fa-facebook:before{content:""}.fa-github:before,.icon-github:before{content:""}.fa-unlock:before{content:""}.fa-credit-card:before{content:""}.fa-feed:before,.fa-rss:before{content:""}.fa-hdd-o:before{content:""}.fa-bullhorn:before{content:""}.fa-bell:before{content:""}.fa-certificate:before{content:""}.fa-hand-o-right:before{content:""}.fa-hand-o-left:before{content:""}.fa-hand-o-up:before{content:""}.fa-hand-o-down:before{content:""}.fa-arrow-circle-left:before,.icon-circle-arrow-left:before{content:""}.fa-arrow-circle-right:before,.icon-circle-arrow-right:before{content:""}.fa-arrow-circle-up:before{content:""}.fa-arrow-circle-down:before{content:""}.fa-globe:before{content:""}.fa-wrench:before{content:""}.fa-tasks:before{content:""}.fa-filter:before{content:""}.fa-briefcase:before{content:""}.fa-arrows-alt:before{content:""}.fa-group:before,.fa-users:before{content:""}.fa-chain:before,.fa-link:before,.icon-link:before{content:""}.fa-cloud:before{content:""}.fa-flask:before{content:""}.fa-cut:before,.fa-scissors:before{content:""}.fa-copy:before,.fa-files-o:before{content:""}.fa-paperclip:before{content:""}.fa-floppy-o:before,.fa-save:before{content:""}.fa-square:before{content:""}.fa-bars:before,.fa-navicon:before,.fa-reorder:before{content:""}.fa-list-ul:before{content:""}.fa-list-ol:before{content:""}.fa-strikethrough:before{content:""}.fa-underline:before{content:""}.fa-table:before{content:""}.fa-magic:before{content:""}.fa-truck:before{content:""}.fa-pinterest:before{content:""}.fa-pinterest-square:before{content:""}.fa-google-plus-square:before{content:""}.fa-google-plus:before{content:""}.fa-money:before{content:""}.fa-caret-down:before,.icon-caret-down:before,.wy-dropdown .caret:before{content:""}.fa-caret-up:before{content:""}.fa-caret-left:before{content:""}.fa-caret-right:before{content:""}.fa-columns:before{content:""}.fa-sort:before,.fa-unsorted:before{content:""}.fa-sort-desc:before,.fa-sort-down:before{content:""}.fa-sort-asc:before,.fa-sort-up:before{content:""}.fa-envelope:before{content:""}.fa-linkedin:before{content:""}.fa-rotate-left:before,.fa-undo:before{content:""}.fa-gavel:before,.fa-legal:before{content:""}.fa-dashboard:before,.fa-tachometer:before{content:""}.fa-comment-o:before{content:""}.fa-comments-o:before{content:""}.fa-bolt:before,.fa-flash:before{content:""}.fa-sitemap:before{content:""}.fa-umbrella:before{content:""}.fa-clipboard:before,.fa-paste:before{content:""}.fa-lightbulb-o:before{content:""}.fa-exchange:before{content:""}.fa-cloud-download:before{content:""}.fa-cloud-upload:before{content:""}.fa-user-md:before{content:""}.fa-stethoscope:before{content:""}.fa-suitcase:before{content:""}.fa-bell-o:before{content:""}.fa-coffee:before{content:""}.fa-cutlery:before{content:""}.fa-file-text-o:before{content:""}.fa-building-o:before{content:""}.fa-hospital-o:before{content:""}.fa-ambulance:before{content:""}.fa-medkit:before{content:""}.fa-fighter-jet:before{content:""}.fa-beer:before{content:""}.fa-h-square:before{content:""}.fa-plus-square:before{content:""}.fa-angle-double-left:before{content:""}.fa-angle-double-right:before{content:""}.fa-angle-double-up:before{content:""}.fa-angle-double-down:before{content:""}.fa-angle-left:before{content:""}.fa-angle-right:before{content:""}.fa-angle-up:before{content:""}.fa-angle-down:before{content:""}.fa-desktop:before{content:""}.fa-laptop:before{content:""}.fa-tablet:before{content:""}.fa-mobile-phone:before,.fa-mobile:before{content:""}.fa-circle-o:before{content:""}.fa-quote-left:before{content:""}.fa-quote-right:before{content:""}.fa-spinner:before{content:""}.fa-circle:before{content:""}.fa-mail-reply:before,.fa-reply:before{content:""}.fa-github-alt:before{content:""}.fa-folder-o:before{content:""}.fa-folder-open-o:before{content:""}.fa-smile-o:before{content:""}.fa-frown-o:before{content:""}.fa-meh-o:before{content:""}.fa-gamepad:before{content:""}.fa-keyboard-o:before{content:""}.fa-flag-o:before{content:""}.fa-flag-checkered:before{content:""}.fa-terminal:before{content:""}.fa-code:before{content:""}.fa-mail-reply-all:before,.fa-reply-all:before{content:""}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:""}.fa-location-arrow:before{content:""}.fa-crop:before{content:""}.fa-code-fork:before{content:""}.fa-chain-broken:before,.fa-unlink:before{content:""}.fa-question:before{content:""}.fa-info:before{content:""}.fa-exclamation:before{content:""}.fa-superscript:before{content:""}.fa-subscript:before{content:""}.fa-eraser:before{content:""}.fa-puzzle-piece:before{content:""}.fa-microphone:before{content:""}.fa-microphone-slash:before{content:""}.fa-shield:before{content:""}.fa-calendar-o:before{content:""}.fa-fire-extinguisher:before{content:""}.fa-rocket:before{content:""}.fa-maxcdn:before{content:""}.fa-chevron-circle-left:before{content:""}.fa-chevron-circle-right:before{content:""}.fa-chevron-circle-up:before{content:""}.fa-chevron-circle-down:before{content:""}.fa-html5:before{content:""}.fa-css3:before{content:""}.fa-anchor:before{content:""}.fa-unlock-alt:before{content:""}.fa-bullseye:before{content:""}.fa-ellipsis-h:before{content:""}.fa-ellipsis-v:before{content:""}.fa-rss-square:before{content:""}.fa-play-circle:before{content:""}.fa-ticket:before{content:""}.fa-minus-square:before{content:""}.fa-minus-square-o:before,.wy-menu-vertical li.current>a button.toctree-expand:before,.wy-menu-vertical li.on a button.toctree-expand:before{content:""}.fa-level-up:before{content:""}.fa-level-down:before{content:""}.fa-check-square:before{content:""}.fa-pencil-square:before{content:""}.fa-external-link-square:before{content:""}.fa-share-square:before{content:""}.fa-compass:before{content:""}.fa-caret-square-o-down:before,.fa-toggle-down:before{content:""}.fa-caret-square-o-up:before,.fa-toggle-up:before{content:""}.fa-caret-square-o-right:before,.fa-toggle-right:before{content:""}.fa-eur:before,.fa-euro:before{content:""}.fa-gbp:before{content:""}.fa-dollar:before,.fa-usd:before{content:""}.fa-inr:before,.fa-rupee:before{content:""}.fa-cny:before,.fa-jpy:before,.fa-rmb:before,.fa-yen:before{content:""}.fa-rouble:before,.fa-rub:before,.fa-ruble:before{content:""}.fa-krw:before,.fa-won:before{content:""}.fa-bitcoin:before,.fa-btc:before{content:""}.fa-file:before{content:""}.fa-file-text:before{content:""}.fa-sort-alpha-asc:before{content:""}.fa-sort-alpha-desc:before{content:""}.fa-sort-amount-asc:before{content:""}.fa-sort-amount-desc:before{content:""}.fa-sort-numeric-asc:before{content:""}.fa-sort-numeric-desc:before{content:""}.fa-thumbs-up:before{content:""}.fa-thumbs-down:before{content:""}.fa-youtube-square:before{content:""}.fa-youtube:before{content:""}.fa-xing:before{content:""}.fa-xing-square:before{content:""}.fa-youtube-play:before{content:""}.fa-dropbox:before{content:""}.fa-stack-overflow:before{content:""}.fa-instagram:before{content:""}.fa-flickr:before{content:""}.fa-adn:before{content:""}.fa-bitbucket:before,.icon-bitbucket:before{content:""}.fa-bitbucket-square:before{content:""}.fa-tumblr:before{content:""}.fa-tumblr-square:before{content:""}.fa-long-arrow-down:before{content:""}.fa-long-arrow-up:before{content:""}.fa-long-arrow-left:before{content:""}.fa-long-arrow-right:before{content:""}.fa-apple:before{content:""}.fa-windows:before{content:""}.fa-android:before{content:""}.fa-linux:before{content:""}.fa-dribbble:before{content:""}.fa-skype:before{content:""}.fa-foursquare:before{content:""}.fa-trello:before{content:""}.fa-female:before{content:""}.fa-male:before{content:""}.fa-gittip:before,.fa-gratipay:before{content:""}.fa-sun-o:before{content:""}.fa-moon-o:before{content:""}.fa-archive:before{content:""}.fa-bug:before{content:""}.fa-vk:before{content:""}.fa-weibo:before{content:""}.fa-renren:before{content:""}.fa-pagelines:before{content:""}.fa-stack-exchange:before{content:""}.fa-arrow-circle-o-right:before{content:""}.fa-arrow-circle-o-left:before{content:""}.fa-caret-square-o-left:before,.fa-toggle-left:before{content:""}.fa-dot-circle-o:before{content:""}.fa-wheelchair:before{content:""}.fa-vimeo-square:before{content:""}.fa-try:before,.fa-turkish-lira:before{content:""}.fa-plus-square-o:before,.wy-menu-vertical li button.toctree-expand:before{content:""}.fa-space-shuttle:before{content:""}.fa-slack:before{content:""}.fa-envelope-square:before{content:""}.fa-wordpress:before{content:""}.fa-openid:before{content:""}.fa-bank:before,.fa-institution:before,.fa-university:before{content:""}.fa-graduation-cap:before,.fa-mortar-board:before{content:""}.fa-yahoo:before{content:""}.fa-google:before{content:""}.fa-reddit:before{content:""}.fa-reddit-square:before{content:""}.fa-stumbleupon-circle:before{content:""}.fa-stumbleupon:before{content:""}.fa-delicious:before{content:""}.fa-digg:before{content:""}.fa-pied-piper-pp:before{content:""}.fa-pied-piper-alt:before{content:""}.fa-drupal:before{content:""}.fa-joomla:before{content:""}.fa-language:before{content:""}.fa-fax:before{content:""}.fa-building:before{content:""}.fa-child:before{content:""}.fa-paw:before{content:""}.fa-spoon:before{content:""}.fa-cube:before{content:""}.fa-cubes:before{content:""}.fa-behance:before{content:""}.fa-behance-square:before{content:""}.fa-steam:before{content:""}.fa-steam-square:before{content:""}.fa-recycle:before{content:""}.fa-automobile:before,.fa-car:before{content:""}.fa-cab:before,.fa-taxi:before{content:""}.fa-tree:before{content:""}.fa-spotify:before{content:""}.fa-deviantart:before{content:""}.fa-soundcloud:before{content:""}.fa-database:before{content:""}.fa-file-pdf-o:before{content:""}.fa-file-word-o:before{content:""}.fa-file-excel-o:before{content:""}.fa-file-powerpoint-o:before{content:""}.fa-file-image-o:before,.fa-file-photo-o:before,.fa-file-picture-o:before{content:""}.fa-file-archive-o:before,.fa-file-zip-o:before{content:""}.fa-file-audio-o:before,.fa-file-sound-o:before{content:""}.fa-file-movie-o:before,.fa-file-video-o:before{content:""}.fa-file-code-o:before{content:""}.fa-vine:before{content:""}.fa-codepen:before{content:""}.fa-jsfiddle:before{content:""}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-ring:before,.fa-life-saver:before,.fa-support:before{content:""}.fa-circle-o-notch:before{content:""}.fa-ra:before,.fa-rebel:before,.fa-resistance:before{content:""}.fa-empire:before,.fa-ge:before{content:""}.fa-git-square:before{content:""}.fa-git:before{content:""}.fa-hacker-news:before,.fa-y-combinator-square:before,.fa-yc-square:before{content:""}.fa-tencent-weibo:before{content:""}.fa-qq:before{content:""}.fa-wechat:before,.fa-weixin:before{content:""}.fa-paper-plane:before,.fa-send:before{content:""}.fa-paper-plane-o:before,.fa-send-o:before{content:""}.fa-history:before{content:""}.fa-circle-thin:before{content:""}.fa-header:before{content:""}.fa-paragraph:before{content:""}.fa-sliders:before{content:""}.fa-share-alt:before{content:""}.fa-share-alt-square:before{content:""}.fa-bomb:before{content:""}.fa-futbol-o:before,.fa-soccer-ball-o:before{content:""}.fa-tty:before{content:""}.fa-binoculars:before{content:""}.fa-plug:before{content:""}.fa-slideshare:before{content:""}.fa-twitch:before{content:""}.fa-yelp:before{content:""}.fa-newspaper-o:before{content:""}.fa-wifi:before{content:""}.fa-calculator:before{content:""}.fa-paypal:before{content:""}.fa-google-wallet:before{content:""}.fa-cc-visa:before{content:""}.fa-cc-mastercard:before{content:""}.fa-cc-discover:before{content:""}.fa-cc-amex:before{content:""}.fa-cc-paypal:before{content:""}.fa-cc-stripe:before{content:""}.fa-bell-slash:before{content:""}.fa-bell-slash-o:before{content:""}.fa-trash:before{content:""}.fa-copyright:before{content:""}.fa-at:before{content:""}.fa-eyedropper:before{content:""}.fa-paint-brush:before{content:""}.fa-birthday-cake:before{content:""}.fa-area-chart:before{content:""}.fa-pie-chart:before{content:""}.fa-line-chart:before{content:""}.fa-lastfm:before{content:""}.fa-lastfm-square:before{content:""}.fa-toggle-off:before{content:""}.fa-toggle-on:before{content:""}.fa-bicycle:before{content:""}.fa-bus:before{content:""}.fa-ioxhost:before{content:""}.fa-angellist:before{content:""}.fa-cc:before{content:""}.fa-ils:before,.fa-shekel:before,.fa-sheqel:before{content:""}.fa-meanpath:before{content:""}.fa-buysellads:before{content:""}.fa-connectdevelop:before{content:""}.fa-dashcube:before{content:""}.fa-forumbee:before{content:""}.fa-leanpub:before{content:""}.fa-sellsy:before{content:""}.fa-shirtsinbulk:before{content:""}.fa-simplybuilt:before{content:""}.fa-skyatlas:before{content:""}.fa-cart-plus:before{content:""}.fa-cart-arrow-down:before{content:""}.fa-diamond:before{content:""}.fa-ship:before{content:""}.fa-user-secret:before{content:""}.fa-motorcycle:before{content:""}.fa-street-view:before{content:""}.fa-heartbeat:before{content:""}.fa-venus:before{content:""}.fa-mars:before{content:""}.fa-mercury:before{content:""}.fa-intersex:before,.fa-transgender:before{content:""}.fa-transgender-alt:before{content:""}.fa-venus-double:before{content:""}.fa-mars-double:before{content:""}.fa-venus-mars:before{content:""}.fa-mars-stroke:before{content:""}.fa-mars-stroke-v:before{content:""}.fa-mars-stroke-h:before{content:""}.fa-neuter:before{content:""}.fa-genderless:before{content:""}.fa-facebook-official:before{content:""}.fa-pinterest-p:before{content:""}.fa-whatsapp:before{content:""}.fa-server:before{content:""}.fa-user-plus:before{content:""}.fa-user-times:before{content:""}.fa-bed:before,.fa-hotel:before{content:""}.fa-viacoin:before{content:""}.fa-train:before{content:""}.fa-subway:before{content:""}.fa-medium:before{content:""}.fa-y-combinator:before,.fa-yc:before{content:""}.fa-optin-monster:before{content:""}.fa-opencart:before{content:""}.fa-expeditedssl:before{content:""}.fa-battery-4:before,.fa-battery-full:before,.fa-battery:before{content:""}.fa-battery-3:before,.fa-battery-three-quarters:before{content:""}.fa-battery-2:before,.fa-battery-half:before{content:""}.fa-battery-1:before,.fa-battery-quarter:before{content:""}.fa-battery-0:before,.fa-battery-empty:before{content:""}.fa-mouse-pointer:before{content:""}.fa-i-cursor:before{content:""}.fa-object-group:before{content:""}.fa-object-ungroup:before{content:""}.fa-sticky-note:before{content:""}.fa-sticky-note-o:before{content:""}.fa-cc-jcb:before{content:""}.fa-cc-diners-club:before{content:""}.fa-clone:before{content:""}.fa-balance-scale:before{content:""}.fa-hourglass-o:before{content:""}.fa-hourglass-1:before,.fa-hourglass-start:before{content:""}.fa-hourglass-2:before,.fa-hourglass-half:before{content:""}.fa-hourglass-3:before,.fa-hourglass-end:before{content:""}.fa-hourglass:before{content:""}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:""}.fa-hand-paper-o:before,.fa-hand-stop-o:before{content:""}.fa-hand-scissors-o:before{content:""}.fa-hand-lizard-o:before{content:""}.fa-hand-spock-o:before{content:""}.fa-hand-pointer-o:before{content:""}.fa-hand-peace-o:before{content:""}.fa-trademark:before{content:""}.fa-registered:before{content:""}.fa-creative-commons:before{content:""}.fa-gg:before{content:""}.fa-gg-circle:before{content:""}.fa-tripadvisor:before{content:""}.fa-odnoklassniki:before{content:""}.fa-odnoklassniki-square:before{content:""}.fa-get-pocket:before{content:""}.fa-wikipedia-w:before{content:""}.fa-safari:before{content:""}.fa-chrome:before{content:""}.fa-firefox:before{content:""}.fa-opera:before{content:""}.fa-internet-explorer:before{content:""}.fa-television:before,.fa-tv:before{content:""}.fa-contao:before{content:""}.fa-500px:before{content:""}.fa-amazon:before{content:""}.fa-calendar-plus-o:before{content:""}.fa-calendar-minus-o:before{content:""}.fa-calendar-times-o:before{content:""}.fa-calendar-check-o:before{content:""}.fa-industry:before{content:""}.fa-map-pin:before{content:""}.fa-map-signs:before{content:""}.fa-map-o:before{content:""}.fa-map:before{content:""}.fa-commenting:before{content:""}.fa-commenting-o:before{content:""}.fa-houzz:before{content:""}.fa-vimeo:before{content:""}.fa-black-tie:before{content:""}.fa-fonticons:before{content:""}.fa-reddit-alien:before{content:""}.fa-edge:before{content:""}.fa-credit-card-alt:before{content:""}.fa-codiepie:before{content:""}.fa-modx:before{content:""}.fa-fort-awesome:before{content:""}.fa-usb:before{content:""}.fa-product-hunt:before{content:""}.fa-mixcloud:before{content:""}.fa-scribd:before{content:""}.fa-pause-circle:before{content:""}.fa-pause-circle-o:before{content:""}.fa-stop-circle:before{content:""}.fa-stop-circle-o:before{content:""}.fa-shopping-bag:before{content:""}.fa-shopping-basket:before{content:""}.fa-hashtag:before{content:""}.fa-bluetooth:before{content:""}.fa-bluetooth-b:before{content:""}.fa-percent:before{content:""}.fa-gitlab:before,.icon-gitlab:before{content:""}.fa-wpbeginner:before{content:""}.fa-wpforms:before{content:""}.fa-envira:before{content:""}.fa-universal-access:before{content:""}.fa-wheelchair-alt:before{content:""}.fa-question-circle-o:before{content:""}.fa-blind:before{content:""}.fa-audio-description:before{content:""}.fa-volume-control-phone:before{content:""}.fa-braille:before{content:""}.fa-assistive-listening-systems:before{content:""}.fa-american-sign-language-interpreting:before,.fa-asl-interpreting:before{content:""}.fa-deaf:before,.fa-deafness:before,.fa-hard-of-hearing:before{content:""}.fa-glide:before{content:""}.fa-glide-g:before{content:""}.fa-sign-language:before,.fa-signing:before{content:""}.fa-low-vision:before{content:""}.fa-viadeo:before{content:""}.fa-viadeo-square:before{content:""}.fa-snapchat:before{content:""}.fa-snapchat-ghost:before{content:""}.fa-snapchat-square:before{content:""}.fa-pied-piper:before{content:""}.fa-first-order:before{content:""}.fa-yoast:before{content:""}.fa-themeisle:before{content:""}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:""}.fa-fa:before,.fa-font-awesome:before{content:""}.fa-handshake-o:before{content:""}.fa-envelope-open:before{content:""}.fa-envelope-open-o:before{content:""}.fa-linode:before{content:""}.fa-address-book:before{content:""}.fa-address-book-o:before{content:""}.fa-address-card:before,.fa-vcard:before{content:""}.fa-address-card-o:before,.fa-vcard-o:before{content:""}.fa-user-circle:before{content:""}.fa-user-circle-o:before{content:""}.fa-user-o:before{content:""}.fa-id-badge:before{content:""}.fa-drivers-license:before,.fa-id-card:before{content:""}.fa-drivers-license-o:before,.fa-id-card-o:before{content:""}.fa-quora:before{content:""}.fa-free-code-camp:before{content:""}.fa-telegram:before{content:""}.fa-thermometer-4:before,.fa-thermometer-full:before,.fa-thermometer:before{content:""}.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:""}.fa-thermometer-2:before,.fa-thermometer-half:before{content:""}.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:""}.fa-thermometer-0:before,.fa-thermometer-empty:before{content:""}.fa-shower:before{content:""}.fa-bath:before,.fa-bathtub:before,.fa-s15:before{content:""}.fa-podcast:before{content:""}.fa-window-maximize:before{content:""}.fa-window-minimize:before{content:""}.fa-window-restore:before{content:""}.fa-times-rectangle:before,.fa-window-close:before{content:""}.fa-times-rectangle-o:before,.fa-window-close-o:before{content:""}.fa-bandcamp:before{content:""}.fa-grav:before{content:""}.fa-etsy:before{content:""}.fa-imdb:before{content:""}.fa-ravelry:before{content:""}.fa-eercast:before{content:""}.fa-microchip:before{content:""}.fa-snowflake-o:before{content:""}.fa-superpowers:before{content:""}.fa-wpexplorer:before{content:""}.fa-meetup:before{content:""}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}.fa,.icon,.rst-content .admonition-title,.rst-content .code-block-caption .headerlink,.rst-content .eqno .headerlink,.rst-content code.download span:first-child,.rst-content dl dt .headerlink,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content p.caption .headerlink,.rst-content p .headerlink,.rst-content table>caption .headerlink,.rst-content tt.download span:first-child,.wy-dropdown .caret,.wy-inline-validate.wy-inline-validate-danger .wy-input-context,.wy-inline-validate.wy-inline-validate-info .wy-input-context,.wy-inline-validate.wy-inline-validate-success .wy-input-context,.wy-inline-validate.wy-inline-validate-warning .wy-input-context,.wy-menu-vertical li.current>a button.toctree-expand,.wy-menu-vertical li.on a button.toctree-expand,.wy-menu-vertical li button.toctree-expand{font-family:inherit}.fa:before,.icon:before,.rst-content .admonition-title:before,.rst-content .code-block-caption .headerlink:before,.rst-content .eqno .headerlink:before,.rst-content code.download span:first-child:before,.rst-content dl dt .headerlink:before,.rst-content h1 .headerlink:before,.rst-content h2 .headerlink:before,.rst-content h3 .headerlink:before,.rst-content h4 .headerlink:before,.rst-content h5 .headerlink:before,.rst-content h6 .headerlink:before,.rst-content p.caption .headerlink:before,.rst-content p .headerlink:before,.rst-content table>caption .headerlink:before,.rst-content tt.download span:first-child:before,.wy-dropdown .caret:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-menu-vertical li.current>a button.toctree-expand:before,.wy-menu-vertical li.on a button.toctree-expand:before,.wy-menu-vertical li button.toctree-expand:before{font-family:FontAwesome;display:inline-block;font-style:normal;font-weight:400;line-height:1;text-decoration:inherit}.rst-content .code-block-caption a .headerlink,.rst-content .eqno a .headerlink,.rst-content a .admonition-title,.rst-content code.download a span:first-child,.rst-content dl dt a .headerlink,.rst-content h1 a .headerlink,.rst-content h2 a .headerlink,.rst-content h3 a .headerlink,.rst-content h4 a .headerlink,.rst-content h5 a .headerlink,.rst-content h6 a .headerlink,.rst-content p.caption a .headerlink,.rst-content p a .headerlink,.rst-content table>caption a .headerlink,.rst-content tt.download a span:first-child,.wy-menu-vertical li.current>a button.toctree-expand,.wy-menu-vertical li.on a button.toctree-expand,.wy-menu-vertical li a button.toctree-expand,a .fa,a .icon,a .rst-content .admonition-title,a .rst-content .code-block-caption .headerlink,a .rst-content .eqno .headerlink,a .rst-content code.download span:first-child,a .rst-content dl dt .headerlink,a .rst-content h1 .headerlink,a .rst-content h2 .headerlink,a .rst-content h3 .headerlink,a .rst-content h4 .headerlink,a .rst-content h5 .headerlink,a .rst-content h6 .headerlink,a .rst-content p.caption .headerlink,a .rst-content p .headerlink,a .rst-content table>caption .headerlink,a .rst-content tt.download span:first-child,a .wy-menu-vertical li button.toctree-expand{display:inline-block;text-decoration:inherit}.btn .fa,.btn .icon,.btn .rst-content .admonition-title,.btn .rst-content .code-block-caption .headerlink,.btn .rst-content .eqno .headerlink,.btn .rst-content code.download span:first-child,.btn .rst-content dl dt .headerlink,.btn .rst-content h1 .headerlink,.btn .rst-content h2 .headerlink,.btn .rst-content h3 .headerlink,.btn .rst-content h4 .headerlink,.btn .rst-content h5 .headerlink,.btn .rst-content h6 .headerlink,.btn .rst-content p .headerlink,.btn .rst-content table>caption .headerlink,.btn .rst-content tt.download span:first-child,.btn .wy-menu-vertical li.current>a button.toctree-expand,.btn .wy-menu-vertical li.on a button.toctree-expand,.btn .wy-menu-vertical li button.toctree-expand,.nav .fa,.nav .icon,.nav .rst-content .admonition-title,.nav .rst-content .code-block-caption .headerlink,.nav .rst-content .eqno .headerlink,.nav .rst-content code.download span:first-child,.nav .rst-content dl dt .headerlink,.nav .rst-content h1 .headerlink,.nav .rst-content h2 .headerlink,.nav .rst-content h3 .headerlink,.nav .rst-content h4 .headerlink,.nav .rst-content h5 .headerlink,.nav .rst-content h6 .headerlink,.nav .rst-content p .headerlink,.nav .rst-content table>caption .headerlink,.nav .rst-content tt.download span:first-child,.nav .wy-menu-vertical li.current>a button.toctree-expand,.nav .wy-menu-vertical li.on a button.toctree-expand,.nav .wy-menu-vertical li button.toctree-expand,.rst-content .btn .admonition-title,.rst-content .code-block-caption .btn .headerlink,.rst-content .code-block-caption .nav .headerlink,.rst-content .eqno .btn .headerlink,.rst-content .eqno .nav .headerlink,.rst-content .nav .admonition-title,.rst-content code.download .btn span:first-child,.rst-content code.download .nav span:first-child,.rst-content dl dt .btn .headerlink,.rst-content dl dt .nav .headerlink,.rst-content h1 .btn .headerlink,.rst-content h1 .nav .headerlink,.rst-content h2 .btn .headerlink,.rst-content h2 .nav .headerlink,.rst-content h3 .btn .headerlink,.rst-content h3 .nav .headerlink,.rst-content h4 .btn .headerlink,.rst-content h4 .nav .headerlink,.rst-content h5 .btn .headerlink,.rst-content h5 .nav .headerlink,.rst-content h6 .btn .headerlink,.rst-content h6 .nav .headerlink,.rst-content p .btn .headerlink,.rst-content p .nav .headerlink,.rst-content table>caption .btn .headerlink,.rst-content table>caption .nav .headerlink,.rst-content tt.download .btn span:first-child,.rst-content tt.download .nav span:first-child,.wy-menu-vertical li .btn button.toctree-expand,.wy-menu-vertical li.current>a .btn button.toctree-expand,.wy-menu-vertical li.current>a .nav button.toctree-expand,.wy-menu-vertical li .nav button.toctree-expand,.wy-menu-vertical li.on a .btn button.toctree-expand,.wy-menu-vertical li.on a .nav button.toctree-expand{display:inline}.btn .fa-large.icon,.btn .fa.fa-large,.btn .rst-content .code-block-caption .fa-large.headerlink,.btn .rst-content .eqno .fa-large.headerlink,.btn .rst-content .fa-large.admonition-title,.btn .rst-content code.download span.fa-large:first-child,.btn .rst-content dl dt .fa-large.headerlink,.btn .rst-content h1 .fa-large.headerlink,.btn .rst-content h2 .fa-large.headerlink,.btn .rst-content h3 .fa-large.headerlink,.btn .rst-content h4 .fa-large.headerlink,.btn .rst-content h5 .fa-large.headerlink,.btn .rst-content h6 .fa-large.headerlink,.btn .rst-content p .fa-large.headerlink,.btn .rst-content table>caption .fa-large.headerlink,.btn .rst-content tt.download span.fa-large:first-child,.btn .wy-menu-vertical li button.fa-large.toctree-expand,.nav .fa-large.icon,.nav .fa.fa-large,.nav .rst-content .code-block-caption .fa-large.headerlink,.nav .rst-content .eqno .fa-large.headerlink,.nav .rst-content .fa-large.admonition-title,.nav .rst-content code.download span.fa-large:first-child,.nav .rst-content dl dt .fa-large.headerlink,.nav .rst-content h1 .fa-large.headerlink,.nav .rst-content h2 .fa-large.headerlink,.nav .rst-content h3 .fa-large.headerlink,.nav .rst-content h4 .fa-large.headerlink,.nav .rst-content h5 .fa-large.headerlink,.nav .rst-content h6 .fa-large.headerlink,.nav .rst-content p .fa-large.headerlink,.nav .rst-content table>caption .fa-large.headerlink,.nav .rst-content tt.download span.fa-large:first-child,.nav .wy-menu-vertical li button.fa-large.toctree-expand,.rst-content .btn .fa-large.admonition-title,.rst-content .code-block-caption .btn .fa-large.headerlink,.rst-content .code-block-caption .nav .fa-large.headerlink,.rst-content .eqno .btn .fa-large.headerlink,.rst-content .eqno .nav .fa-large.headerlink,.rst-content .nav .fa-large.admonition-title,.rst-content code.download .btn span.fa-large:first-child,.rst-content code.download .nav span.fa-large:first-child,.rst-content dl dt .btn .fa-large.headerlink,.rst-content dl dt .nav .fa-large.headerlink,.rst-content h1 .btn .fa-large.headerlink,.rst-content h1 .nav .fa-large.headerlink,.rst-content h2 .btn .fa-large.headerlink,.rst-content h2 .nav .fa-large.headerlink,.rst-content h3 .btn .fa-large.headerlink,.rst-content h3 .nav .fa-large.headerlink,.rst-content h4 .btn .fa-large.headerlink,.rst-content h4 .nav .fa-large.headerlink,.rst-content h5 .btn .fa-large.headerlink,.rst-content h5 .nav .fa-large.headerlink,.rst-content h6 .btn .fa-large.headerlink,.rst-content h6 .nav .fa-large.headerlink,.rst-content p .btn .fa-large.headerlink,.rst-content p .nav .fa-large.headerlink,.rst-content table>caption .btn .fa-large.headerlink,.rst-content table>caption .nav .fa-large.headerlink,.rst-content tt.download .btn span.fa-large:first-child,.rst-content tt.download .nav span.fa-large:first-child,.wy-menu-vertical li .btn button.fa-large.toctree-expand,.wy-menu-vertical li .nav button.fa-large.toctree-expand{line-height:.9em}.btn .fa-spin.icon,.btn .fa.fa-spin,.btn .rst-content .code-block-caption .fa-spin.headerlink,.btn .rst-content .eqno .fa-spin.headerlink,.btn .rst-content .fa-spin.admonition-title,.btn .rst-content code.download span.fa-spin:first-child,.btn .rst-content dl dt .fa-spin.headerlink,.btn .rst-content h1 .fa-spin.headerlink,.btn .rst-content h2 .fa-spin.headerlink,.btn .rst-content h3 .fa-spin.headerlink,.btn .rst-content h4 .fa-spin.headerlink,.btn .rst-content h5 .fa-spin.headerlink,.btn .rst-content h6 .fa-spin.headerlink,.btn .rst-content p .fa-spin.headerlink,.btn .rst-content table>caption .fa-spin.headerlink,.btn .rst-content tt.download span.fa-spin:first-child,.btn .wy-menu-vertical li button.fa-spin.toctree-expand,.nav .fa-spin.icon,.nav .fa.fa-spin,.nav .rst-content .code-block-caption .fa-spin.headerlink,.nav .rst-content .eqno .fa-spin.headerlink,.nav .rst-content .fa-spin.admonition-title,.nav .rst-content code.download span.fa-spin:first-child,.nav .rst-content dl dt .fa-spin.headerlink,.nav .rst-content h1 .fa-spin.headerlink,.nav .rst-content h2 .fa-spin.headerlink,.nav .rst-content h3 .fa-spin.headerlink,.nav .rst-content h4 .fa-spin.headerlink,.nav .rst-content h5 .fa-spin.headerlink,.nav .rst-content h6 .fa-spin.headerlink,.nav .rst-content p .fa-spin.headerlink,.nav .rst-content table>caption .fa-spin.headerlink,.nav .rst-content tt.download span.fa-spin:first-child,.nav .wy-menu-vertical li button.fa-spin.toctree-expand,.rst-content .btn .fa-spin.admonition-title,.rst-content .code-block-caption .btn .fa-spin.headerlink,.rst-content .code-block-caption .nav .fa-spin.headerlink,.rst-content .eqno .btn .fa-spin.headerlink,.rst-content .eqno .nav .fa-spin.headerlink,.rst-content .nav .fa-spin.admonition-title,.rst-content code.download .btn span.fa-spin:first-child,.rst-content code.download .nav span.fa-spin:first-child,.rst-content dl dt .btn .fa-spin.headerlink,.rst-content dl dt .nav .fa-spin.headerlink,.rst-content h1 .btn .fa-spin.headerlink,.rst-content h1 .nav .fa-spin.headerlink,.rst-content h2 .btn .fa-spin.headerlink,.rst-content h2 .nav .fa-spin.headerlink,.rst-content h3 .btn .fa-spin.headerlink,.rst-content h3 .nav .fa-spin.headerlink,.rst-content h4 .btn .fa-spin.headerlink,.rst-content h4 .nav .fa-spin.headerlink,.rst-content h5 .btn .fa-spin.headerlink,.rst-content h5 .nav .fa-spin.headerlink,.rst-content h6 .btn .fa-spin.headerlink,.rst-content h6 .nav .fa-spin.headerlink,.rst-content p .btn .fa-spin.headerlink,.rst-content p .nav .fa-spin.headerlink,.rst-content table>caption .btn .fa-spin.headerlink,.rst-content table>caption .nav .fa-spin.headerlink,.rst-content tt.download .btn span.fa-spin:first-child,.rst-content tt.download .nav span.fa-spin:first-child,.wy-menu-vertical li .btn button.fa-spin.toctree-expand,.wy-menu-vertical li .nav button.fa-spin.toctree-expand{display:inline-block}.btn.fa:before,.btn.icon:before,.rst-content .btn.admonition-title:before,.rst-content .code-block-caption .btn.headerlink:before,.rst-content .eqno .btn.headerlink:before,.rst-content code.download span.btn:first-child:before,.rst-content dl dt .btn.headerlink:before,.rst-content h1 .btn.headerlink:before,.rst-content h2 .btn.headerlink:before,.rst-content h3 .btn.headerlink:before,.rst-content h4 .btn.headerlink:before,.rst-content h5 .btn.headerlink:before,.rst-content h6 .btn.headerlink:before,.rst-content p .btn.headerlink:before,.rst-content table>caption .btn.headerlink:before,.rst-content tt.download span.btn:first-child:before,.wy-menu-vertical li button.btn.toctree-expand:before{opacity:.5;-webkit-transition:opacity .05s ease-in;-moz-transition:opacity .05s ease-in;transition:opacity .05s ease-in}.btn.fa:hover:before,.btn.icon:hover:before,.rst-content .btn.admonition-title:hover:before,.rst-content .code-block-caption .btn.headerlink:hover:before,.rst-content .eqno .btn.headerlink:hover:before,.rst-content code.download span.btn:first-child:hover:before,.rst-content dl dt .btn.headerlink:hover:before,.rst-content h1 .btn.headerlink:hover:before,.rst-content h2 .btn.headerlink:hover:before,.rst-content h3 .btn.headerlink:hover:before,.rst-content h4 .btn.headerlink:hover:before,.rst-content h5 .btn.headerlink:hover:before,.rst-content h6 .btn.headerlink:hover:before,.rst-content p .btn.headerlink:hover:before,.rst-content table>caption .btn.headerlink:hover:before,.rst-content tt.download span.btn:first-child:hover:before,.wy-menu-vertical li button.btn.toctree-expand:hover:before{opacity:1}.btn-mini .fa:before,.btn-mini .icon:before,.btn-mini .rst-content .admonition-title:before,.btn-mini .rst-content .code-block-caption .headerlink:before,.btn-mini .rst-content .eqno .headerlink:before,.btn-mini .rst-content code.download span:first-child:before,.btn-mini .rst-content dl dt .headerlink:before,.btn-mini .rst-content h1 .headerlink:before,.btn-mini .rst-content h2 .headerlink:before,.btn-mini .rst-content h3 .headerlink:before,.btn-mini .rst-content h4 .headerlink:before,.btn-mini .rst-content h5 .headerlink:before,.btn-mini .rst-content h6 .headerlink:before,.btn-mini .rst-content p .headerlink:before,.btn-mini .rst-content table>caption .headerlink:before,.btn-mini .rst-content tt.download span:first-child:before,.btn-mini .wy-menu-vertical li button.toctree-expand:before,.rst-content .btn-mini .admonition-title:before,.rst-content .code-block-caption .btn-mini .headerlink:before,.rst-content .eqno .btn-mini .headerlink:before,.rst-content code.download .btn-mini span:first-child:before,.rst-content dl dt .btn-mini .headerlink:before,.rst-content h1 .btn-mini .headerlink:before,.rst-content h2 .btn-mini .headerlink:before,.rst-content h3 .btn-mini .headerlink:before,.rst-content h4 .btn-mini .headerlink:before,.rst-content h5 .btn-mini .headerlink:before,.rst-content h6 .btn-mini .headerlink:before,.rst-content p .btn-mini .headerlink:before,.rst-content table>caption .btn-mini .headerlink:before,.rst-content tt.download .btn-mini span:first-child:before,.wy-menu-vertical li .btn-mini button.toctree-expand:before{font-size:14px;vertical-align:-15%}.rst-content .admonition,.rst-content .admonition-todo,.rst-content .attention,.rst-content .caution,.rst-content .danger,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .note,.rst-content .seealso,.rst-content .tip,.rst-content .warning,.wy-alert{padding:12px;line-height:24px;margin-bottom:24px;background:#e7f2fa}.rst-content .admonition-title,.wy-alert-title{font-weight:700;display:block;color:#fff;background:#6ab0de;padding:6px 12px;margin:-12px -12px 12px}.rst-content .danger,.rst-content .error,.rst-content .wy-alert-danger.admonition,.rst-content .wy-alert-danger.admonition-todo,.rst-content .wy-alert-danger.attention,.rst-content .wy-alert-danger.caution,.rst-content .wy-alert-danger.hint,.rst-content .wy-alert-danger.important,.rst-content .wy-alert-danger.note,.rst-content .wy-alert-danger.seealso,.rst-content .wy-alert-danger.tip,.rst-content .wy-alert-danger.warning,.wy-alert.wy-alert-danger{background:#fdf3f2}.rst-content .danger .admonition-title,.rst-content .danger .wy-alert-title,.rst-content .error .admonition-title,.rst-content .error .wy-alert-title,.rst-content .wy-alert-danger.admonition-todo .admonition-title,.rst-content .wy-alert-danger.admonition-todo .wy-alert-title,.rst-content .wy-alert-danger.admonition .admonition-title,.rst-content .wy-alert-danger.admonition .wy-alert-title,.rst-content .wy-alert-danger.attention .admonition-title,.rst-content .wy-alert-danger.attention .wy-alert-title,.rst-content .wy-alert-danger.caution .admonition-title,.rst-content .wy-alert-danger.caution .wy-alert-title,.rst-content .wy-alert-danger.hint .admonition-title,.rst-content .wy-alert-danger.hint .wy-alert-title,.rst-content .wy-alert-danger.important .admonition-title,.rst-content .wy-alert-danger.important .wy-alert-title,.rst-content .wy-alert-danger.note .admonition-title,.rst-content .wy-alert-danger.note .wy-alert-title,.rst-content .wy-alert-danger.seealso .admonition-title,.rst-content .wy-alert-danger.seealso .wy-alert-title,.rst-content .wy-alert-danger.tip .admonition-title,.rst-content .wy-alert-danger.tip .wy-alert-title,.rst-content .wy-alert-danger.warning .admonition-title,.rst-content .wy-alert-danger.warning .wy-alert-title,.rst-content .wy-alert.wy-alert-danger .admonition-title,.wy-alert.wy-alert-danger .rst-content .admonition-title,.wy-alert.wy-alert-danger .wy-alert-title{background:#f29f97}.rst-content .admonition-todo,.rst-content .attention,.rst-content .caution,.rst-content .warning,.rst-content .wy-alert-warning.admonition,.rst-content .wy-alert-warning.danger,.rst-content .wy-alert-warning.error,.rst-content .wy-alert-warning.hint,.rst-content .wy-alert-warning.important,.rst-content .wy-alert-warning.note,.rst-content .wy-alert-warning.seealso,.rst-content .wy-alert-warning.tip,.wy-alert.wy-alert-warning{background:#ffedcc}.rst-content .admonition-todo .admonition-title,.rst-content .admonition-todo .wy-alert-title,.rst-content .attention .admonition-title,.rst-content .attention .wy-alert-title,.rst-content .caution .admonition-title,.rst-content .caution .wy-alert-title,.rst-content .warning .admonition-title,.rst-content .warning .wy-alert-title,.rst-content .wy-alert-warning.admonition .admonition-title,.rst-content .wy-alert-warning.admonition .wy-alert-title,.rst-content .wy-alert-warning.danger .admonition-title,.rst-content .wy-alert-warning.danger .wy-alert-title,.rst-content .wy-alert-warning.error .admonition-title,.rst-content .wy-alert-warning.error .wy-alert-title,.rst-content .wy-alert-warning.hint .admonition-title,.rst-content .wy-alert-warning.hint .wy-alert-title,.rst-content .wy-alert-warning.important .admonition-title,.rst-content .wy-alert-warning.important .wy-alert-title,.rst-content .wy-alert-warning.note .admonition-title,.rst-content .wy-alert-warning.note .wy-alert-title,.rst-content .wy-alert-warning.seealso .admonition-title,.rst-content .wy-alert-warning.seealso .wy-alert-title,.rst-content .wy-alert-warning.tip .admonition-title,.rst-content .wy-alert-warning.tip .wy-alert-title,.rst-content .wy-alert.wy-alert-warning .admonition-title,.wy-alert.wy-alert-warning .rst-content .admonition-title,.wy-alert.wy-alert-warning .wy-alert-title{background:#f0b37e}.rst-content .note,.rst-content .seealso,.rst-content .wy-alert-info.admonition,.rst-content .wy-alert-info.admonition-todo,.rst-content .wy-alert-info.attention,.rst-content .wy-alert-info.caution,.rst-content .wy-alert-info.danger,.rst-content .wy-alert-info.error,.rst-content .wy-alert-info.hint,.rst-content .wy-alert-info.important,.rst-content .wy-alert-info.tip,.rst-content .wy-alert-info.warning,.wy-alert.wy-alert-info{background:#e7f2fa}.rst-content .note .admonition-title,.rst-content .note .wy-alert-title,.rst-content .seealso .admonition-title,.rst-content .seealso .wy-alert-title,.rst-content .wy-alert-info.admonition-todo .admonition-title,.rst-content .wy-alert-info.admonition-todo .wy-alert-title,.rst-content .wy-alert-info.admonition .admonition-title,.rst-content .wy-alert-info.admonition .wy-alert-title,.rst-content .wy-alert-info.attention .admonition-title,.rst-content .wy-alert-info.attention .wy-alert-title,.rst-content .wy-alert-info.caution .admonition-title,.rst-content .wy-alert-info.caution .wy-alert-title,.rst-content .wy-alert-info.danger .admonition-title,.rst-content .wy-alert-info.danger .wy-alert-title,.rst-content .wy-alert-info.error .admonition-title,.rst-content .wy-alert-info.error .wy-alert-title,.rst-content .wy-alert-info.hint .admonition-title,.rst-content .wy-alert-info.hint .wy-alert-title,.rst-content .wy-alert-info.important .admonition-title,.rst-content .wy-alert-info.important .wy-alert-title,.rst-content .wy-alert-info.tip .admonition-title,.rst-content .wy-alert-info.tip .wy-alert-title,.rst-content .wy-alert-info.warning .admonition-title,.rst-content .wy-alert-info.warning .wy-alert-title,.rst-content .wy-alert.wy-alert-info .admonition-title,.wy-alert.wy-alert-info .rst-content .admonition-title,.wy-alert.wy-alert-info .wy-alert-title{background:#6ab0de}.rst-content .hint,.rst-content .important,.rst-content .tip,.rst-content .wy-alert-success.admonition,.rst-content .wy-alert-success.admonition-todo,.rst-content .wy-alert-success.attention,.rst-content .wy-alert-success.caution,.rst-content .wy-alert-success.danger,.rst-content .wy-alert-success.error,.rst-content .wy-alert-success.note,.rst-content .wy-alert-success.seealso,.rst-content .wy-alert-success.warning,.wy-alert.wy-alert-success{background:#dbfaf4}.rst-content .hint .admonition-title,.rst-content .hint .wy-alert-title,.rst-content .important .admonition-title,.rst-content .important .wy-alert-title,.rst-content .tip .admonition-title,.rst-content .tip .wy-alert-title,.rst-content .wy-alert-success.admonition-todo .admonition-title,.rst-content .wy-alert-success.admonition-todo .wy-alert-title,.rst-content .wy-alert-success.admonition .admonition-title,.rst-content .wy-alert-success.admonition .wy-alert-title,.rst-content .wy-alert-success.attention .admonition-title,.rst-content .wy-alert-success.attention .wy-alert-title,.rst-content .wy-alert-success.caution .admonition-title,.rst-content .wy-alert-success.caution .wy-alert-title,.rst-content .wy-alert-success.danger .admonition-title,.rst-content .wy-alert-success.danger .wy-alert-title,.rst-content .wy-alert-success.error .admonition-title,.rst-content .wy-alert-success.error .wy-alert-title,.rst-content .wy-alert-success.note .admonition-title,.rst-content .wy-alert-success.note .wy-alert-title,.rst-content .wy-alert-success.seealso .admonition-title,.rst-content .wy-alert-success.seealso .wy-alert-title,.rst-content .wy-alert-success.warning .admonition-title,.rst-content .wy-alert-success.warning .wy-alert-title,.rst-content .wy-alert.wy-alert-success .admonition-title,.wy-alert.wy-alert-success .rst-content .admonition-title,.wy-alert.wy-alert-success .wy-alert-title{background:#1abc9c}.rst-content .wy-alert-neutral.admonition,.rst-content .wy-alert-neutral.admonition-todo,.rst-content .wy-alert-neutral.attention,.rst-content .wy-alert-neutral.caution,.rst-content .wy-alert-neutral.danger,.rst-content .wy-alert-neutral.error,.rst-content .wy-alert-neutral.hint,.rst-content .wy-alert-neutral.important,.rst-content .wy-alert-neutral.note,.rst-content .wy-alert-neutral.seealso,.rst-content .wy-alert-neutral.tip,.rst-content .wy-alert-neutral.warning,.wy-alert.wy-alert-neutral{background:#f3f6f6}.rst-content .wy-alert-neutral.admonition-todo .admonition-title,.rst-content .wy-alert-neutral.admonition-todo .wy-alert-title,.rst-content .wy-alert-neutral.admonition .admonition-title,.rst-content .wy-alert-neutral.admonition .wy-alert-title,.rst-content .wy-alert-neutral.attention .admonition-title,.rst-content .wy-alert-neutral.attention .wy-alert-title,.rst-content .wy-alert-neutral.caution .admonition-title,.rst-content .wy-alert-neutral.caution .wy-alert-title,.rst-content .wy-alert-neutral.danger .admonition-title,.rst-content .wy-alert-neutral.danger .wy-alert-title,.rst-content .wy-alert-neutral.error .admonition-title,.rst-content .wy-alert-neutral.error .wy-alert-title,.rst-content .wy-alert-neutral.hint .admonition-title,.rst-content .wy-alert-neutral.hint .wy-alert-title,.rst-content .wy-alert-neutral.important .admonition-title,.rst-content .wy-alert-neutral.important .wy-alert-title,.rst-content .wy-alert-neutral.note .admonition-title,.rst-content .wy-alert-neutral.note .wy-alert-title,.rst-content .wy-alert-neutral.seealso .admonition-title,.rst-content .wy-alert-neutral.seealso .wy-alert-title,.rst-content .wy-alert-neutral.tip .admonition-title,.rst-content .wy-alert-neutral.tip .wy-alert-title,.rst-content .wy-alert-neutral.warning .admonition-title,.rst-content .wy-alert-neutral.warning .wy-alert-title,.rst-content .wy-alert.wy-alert-neutral .admonition-title,.wy-alert.wy-alert-neutral .rst-content .admonition-title,.wy-alert.wy-alert-neutral .wy-alert-title{color:#404040;background:#e1e4e5}.rst-content .wy-alert-neutral.admonition-todo a,.rst-content .wy-alert-neutral.admonition a,.rst-content .wy-alert-neutral.attention a,.rst-content .wy-alert-neutral.caution a,.rst-content .wy-alert-neutral.danger a,.rst-content .wy-alert-neutral.error a,.rst-content .wy-alert-neutral.hint a,.rst-content .wy-alert-neutral.important a,.rst-content .wy-alert-neutral.note a,.rst-content .wy-alert-neutral.seealso a,.rst-content .wy-alert-neutral.tip a,.rst-content .wy-alert-neutral.warning a,.wy-alert.wy-alert-neutral a{color:#2980b9}.rst-content .admonition-todo p:last-child,.rst-content .admonition p:last-child,.rst-content .attention p:last-child,.rst-content .caution p:last-child,.rst-content .danger p:last-child,.rst-content .error p:last-child,.rst-content .hint p:last-child,.rst-content .important p:last-child,.rst-content .note p:last-child,.rst-content .seealso p:last-child,.rst-content .tip p:last-child,.rst-content .warning p:last-child,.wy-alert p:last-child{margin-bottom:0}.wy-tray-container{position:fixed;bottom:0;left:0;z-index:600}.wy-tray-container li{display:block;width:300px;background:transparent;color:#fff;text-align:center;box-shadow:0 5px 5px 0 rgba(0,0,0,.1);padding:0 24px;min-width:20%;opacity:0;height:0;line-height:56px;overflow:hidden;-webkit-transition:all .3s ease-in;-moz-transition:all .3s ease-in;transition:all .3s ease-in}.wy-tray-container li.wy-tray-item-success{background:#27ae60}.wy-tray-container li.wy-tray-item-info{background:#2980b9}.wy-tray-container li.wy-tray-item-warning{background:#e67e22}.wy-tray-container li.wy-tray-item-danger{background:#e74c3c}.wy-tray-container li.on{opacity:1;height:56px}@media screen and (max-width:768px){.wy-tray-container{bottom:auto;top:0;width:100%}.wy-tray-container li{width:100%}}button{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle;cursor:pointer;line-height:normal;-webkit-appearance:button;*overflow:visible}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}button[disabled]{cursor:default}.btn{display:inline-block;border-radius:2px;line-height:normal;white-space:nowrap;text-align:center;cursor:pointer;font-size:100%;padding:6px 12px 8px;color:#fff;border:1px solid rgba(0,0,0,.1);background-color:#27ae60;text-decoration:none;font-weight:400;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;box-shadow:inset 0 1px 2px -1px hsla(0,0%,100%,.5),inset 0 -2px 0 0 rgba(0,0,0,.1);outline-none:false;vertical-align:middle;*display:inline;zoom:1;-webkit-user-drag:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-transition:all .1s linear;-moz-transition:all .1s linear;transition:all .1s linear}.btn-hover{background:#2e8ece;color:#fff}.btn:hover{background:#2cc36b;color:#fff}.btn:focus{background:#2cc36b;outline:0}.btn:active{box-shadow:inset 0 -1px 0 0 rgba(0,0,0,.05),inset 0 2px 0 0 rgba(0,0,0,.1);padding:8px 12px 6px}.btn:visited{color:#fff}.btn-disabled,.btn-disabled:active,.btn-disabled:focus,.btn-disabled:hover,.btn:disabled{background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=40);opacity:.4;cursor:not-allowed;box-shadow:none}.btn::-moz-focus-inner{padding:0;border:0}.btn-small{font-size:80%}.btn-info{background-color:#2980b9!important}.btn-info:hover{background-color:#2e8ece!important}.btn-neutral{background-color:#f3f6f6!important;color:#404040!important}.btn-neutral:hover{background-color:#e5ebeb!important;color:#404040}.btn-neutral:visited{color:#404040!important}.btn-success{background-color:#27ae60!important}.btn-success:hover{background-color:#295!important}.btn-danger{background-color:#e74c3c!important}.btn-danger:hover{background-color:#ea6153!important}.btn-warning{background-color:#e67e22!important}.btn-warning:hover{background-color:#e98b39!important}.btn-invert{background-color:#222}.btn-invert:hover{background-color:#2f2f2f!important}.btn-link{background-color:transparent!important;color:#2980b9;box-shadow:none;border-color:transparent!important}.btn-link:active,.btn-link:hover{background-color:transparent!important;color:#409ad5!important;box-shadow:none}.btn-link:visited{color:#9b59b6}.wy-btn-group .btn,.wy-control .btn{vertical-align:middle}.wy-btn-group{margin-bottom:24px;*zoom:1}.wy-btn-group:after,.wy-btn-group:before{display:table;content:""}.wy-btn-group:after{clear:both}.wy-dropdown{position:relative;display:inline-block}.wy-dropdown-active .wy-dropdown-menu{display:block}.wy-dropdown-menu{position:absolute;left:0;display:none;float:left;top:100%;min-width:100%;background:#fcfcfc;z-index:100;border:1px solid #cfd7dd;box-shadow:0 2px 2px 0 rgba(0,0,0,.1);padding:12px}.wy-dropdown-menu>dd>a{display:block;clear:both;color:#404040;white-space:nowrap;font-size:90%;padding:0 12px;cursor:pointer}.wy-dropdown-menu>dd>a:hover{background:#2980b9;color:#fff}.wy-dropdown-menu>dd.divider{border-top:1px solid #cfd7dd;margin:6px 0}.wy-dropdown-menu>dd.search{padding-bottom:12px}.wy-dropdown-menu>dd.search input[type=search]{width:100%}.wy-dropdown-menu>dd.call-to-action{background:#e3e3e3;text-transform:uppercase;font-weight:500;font-size:80%}.wy-dropdown-menu>dd.call-to-action:hover{background:#e3e3e3}.wy-dropdown-menu>dd.call-to-action .btn{color:#fff}.wy-dropdown.wy-dropdown-up .wy-dropdown-menu{bottom:100%;top:auto;left:auto;right:0}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu{background:#fcfcfc;margin-top:2px}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a{padding:6px 12px}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a:hover{background:#2980b9;color:#fff}.wy-dropdown.wy-dropdown-left .wy-dropdown-menu{right:0;left:auto;text-align:right}.wy-dropdown-arrow:before{content:" ";border-bottom:5px solid #f5f5f5;border-left:5px solid transparent;border-right:5px solid transparent;position:absolute;display:block;top:-4px;left:50%;margin-left:-3px}.wy-dropdown-arrow.wy-dropdown-arrow-left:before{left:11px}.wy-form-stacked select{display:block}.wy-form-aligned .wy-help-inline,.wy-form-aligned input,.wy-form-aligned label,.wy-form-aligned select,.wy-form-aligned textarea{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.wy-form-aligned .wy-control-group>label{display:inline-block;vertical-align:middle;width:10em;margin:6px 12px 0 0;float:left}.wy-form-aligned .wy-control{float:left}.wy-form-aligned .wy-control label{display:block}.wy-form-aligned .wy-control select{margin-top:6px}fieldset{margin:0}fieldset,legend{border:0;padding:0}legend{width:100%;white-space:normal;margin-bottom:24px;font-size:150%;*margin-left:-7px}label,legend{display:block}label{margin:0 0 .3125em;color:#333;font-size:90%}input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}.wy-control-group{margin-bottom:24px;max-width:1200px;margin-left:auto;margin-right:auto;*zoom:1}.wy-control-group:after,.wy-control-group:before{display:table;content:""}.wy-control-group:after{clear:both}.wy-control-group.wy-control-group-required>label:after{content:" *";color:#e74c3c}.wy-control-group .wy-form-full,.wy-control-group .wy-form-halves,.wy-control-group .wy-form-thirds{padding-bottom:12px}.wy-control-group .wy-form-full input[type=color],.wy-control-group .wy-form-full input[type=date],.wy-control-group .wy-form-full input[type=datetime-local],.wy-control-group .wy-form-full input[type=datetime],.wy-control-group .wy-form-full input[type=email],.wy-control-group .wy-form-full input[type=month],.wy-control-group .wy-form-full input[type=number],.wy-control-group .wy-form-full input[type=password],.wy-control-group .wy-form-full input[type=search],.wy-control-group .wy-form-full input[type=tel],.wy-control-group .wy-form-full input[type=text],.wy-control-group .wy-form-full input[type=time],.wy-control-group .wy-form-full input[type=url],.wy-control-group .wy-form-full input[type=week],.wy-control-group .wy-form-full select,.wy-control-group .wy-form-halves input[type=color],.wy-control-group .wy-form-halves input[type=date],.wy-control-group .wy-form-halves input[type=datetime-local],.wy-control-group .wy-form-halves input[type=datetime],.wy-control-group .wy-form-halves input[type=email],.wy-control-group .wy-form-halves input[type=month],.wy-control-group .wy-form-halves input[type=number],.wy-control-group .wy-form-halves input[type=password],.wy-control-group .wy-form-halves input[type=search],.wy-control-group .wy-form-halves input[type=tel],.wy-control-group .wy-form-halves input[type=text],.wy-control-group .wy-form-halves input[type=time],.wy-control-group .wy-form-halves input[type=url],.wy-control-group .wy-form-halves input[type=week],.wy-control-group .wy-form-halves select,.wy-control-group .wy-form-thirds input[type=color],.wy-control-group .wy-form-thirds input[type=date],.wy-control-group .wy-form-thirds input[type=datetime-local],.wy-control-group .wy-form-thirds input[type=datetime],.wy-control-group .wy-form-thirds input[type=email],.wy-control-group .wy-form-thirds input[type=month],.wy-control-group .wy-form-thirds input[type=number],.wy-control-group .wy-form-thirds input[type=password],.wy-control-group .wy-form-thirds input[type=search],.wy-control-group .wy-form-thirds input[type=tel],.wy-control-group .wy-form-thirds input[type=text],.wy-control-group .wy-form-thirds input[type=time],.wy-control-group .wy-form-thirds input[type=url],.wy-control-group .wy-form-thirds input[type=week],.wy-control-group .wy-form-thirds select{width:100%}.wy-control-group .wy-form-full{float:left;display:block;width:100%;margin-right:0}.wy-control-group .wy-form-full:last-child{margin-right:0}.wy-control-group .wy-form-halves{float:left;display:block;margin-right:2.35765%;width:48.82117%}.wy-control-group .wy-form-halves:last-child,.wy-control-group .wy-form-halves:nth-of-type(2n){margin-right:0}.wy-control-group .wy-form-halves:nth-of-type(odd){clear:left}.wy-control-group .wy-form-thirds{float:left;display:block;margin-right:2.35765%;width:31.76157%}.wy-control-group .wy-form-thirds:last-child,.wy-control-group .wy-form-thirds:nth-of-type(3n){margin-right:0}.wy-control-group .wy-form-thirds:nth-of-type(3n+1){clear:left}.wy-control-group.wy-control-group-no-input .wy-control,.wy-control-no-input{margin:6px 0 0;font-size:90%}.wy-control-no-input{display:inline-block}.wy-control-group.fluid-input input[type=color],.wy-control-group.fluid-input input[type=date],.wy-control-group.fluid-input input[type=datetime-local],.wy-control-group.fluid-input input[type=datetime],.wy-control-group.fluid-input input[type=email],.wy-control-group.fluid-input input[type=month],.wy-control-group.fluid-input input[type=number],.wy-control-group.fluid-input input[type=password],.wy-control-group.fluid-input input[type=search],.wy-control-group.fluid-input input[type=tel],.wy-control-group.fluid-input input[type=text],.wy-control-group.fluid-input input[type=time],.wy-control-group.fluid-input input[type=url],.wy-control-group.fluid-input input[type=week]{width:100%}.wy-form-message-inline{padding-left:.3em;color:#666;font-size:90%}.wy-form-message{display:block;color:#999;font-size:70%;margin-top:.3125em;font-style:italic}.wy-form-message p{font-size:inherit;font-style:italic;margin-bottom:6px}.wy-form-message p:last-child{margin-bottom:0}input{line-height:normal}input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;*overflow:visible}input[type=color],input[type=date],input[type=datetime-local],input[type=datetime],input[type=email],input[type=month],input[type=number],input[type=password],input[type=search],input[type=tel],input[type=text],input[type=time],input[type=url],input[type=week]{-webkit-appearance:none;padding:6px;display:inline-block;border:1px solid #ccc;font-size:80%;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;box-shadow:inset 0 1px 3px #ddd;border-radius:0;-webkit-transition:border .3s linear;-moz-transition:border .3s linear;transition:border .3s linear}input[type=datetime-local]{padding:.34375em .625em}input[disabled]{cursor:default}input[type=checkbox],input[type=radio]{padding:0;margin-right:.3125em;*height:13px;*width:13px}input[type=checkbox],input[type=radio],input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}input[type=color]:focus,input[type=date]:focus,input[type=datetime-local]:focus,input[type=datetime]:focus,input[type=email]:focus,input[type=month]:focus,input[type=number]:focus,input[type=password]:focus,input[type=search]:focus,input[type=tel]:focus,input[type=text]:focus,input[type=time]:focus,input[type=url]:focus,input[type=week]:focus{outline:0;outline:thin dotted\9;border-color:#333}input.no-focus:focus{border-color:#ccc!important}input[type=checkbox]:focus,input[type=file]:focus,input[type=radio]:focus{outline:thin dotted #333;outline:1px auto #129fea}input[type=color][disabled],input[type=date][disabled],input[type=datetime-local][disabled],input[type=datetime][disabled],input[type=email][disabled],input[type=month][disabled],input[type=number][disabled],input[type=password][disabled],input[type=search][disabled],input[type=tel][disabled],input[type=text][disabled],input[type=time][disabled],input[type=url][disabled],input[type=week][disabled]{cursor:not-allowed;background-color:#fafafa}input:focus:invalid,select:focus:invalid,textarea:focus:invalid{color:#e74c3c;border:1px solid #e74c3c}input:focus:invalid:focus,select:focus:invalid:focus,textarea:focus:invalid:focus{border-color:#e74c3c}input[type=checkbox]:focus:invalid:focus,input[type=file]:focus:invalid:focus,input[type=radio]:focus:invalid:focus{outline-color:#e74c3c}input.wy-input-large{padding:12px;font-size:100%}textarea{overflow:auto;vertical-align:top;width:100%;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif}select,textarea{padding:.5em .625em;display:inline-block;border:1px solid #ccc;font-size:80%;box-shadow:inset 0 1px 3px #ddd;-webkit-transition:border .3s linear;-moz-transition:border .3s linear;transition:border .3s linear}select{border:1px solid #ccc;background-color:#fff}select[multiple]{height:auto}select:focus,textarea:focus{outline:0}input[readonly],select[disabled],select[readonly],textarea[disabled],textarea[readonly]{cursor:not-allowed;background-color:#fafafa}input[type=checkbox][disabled],input[type=radio][disabled]{cursor:not-allowed}.wy-checkbox,.wy-radio{margin:6px 0;color:#404040;display:block}.wy-checkbox input,.wy-radio input{vertical-align:baseline}.wy-form-message-inline{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.wy-input-prefix,.wy-input-suffix{white-space:nowrap;padding:6px}.wy-input-prefix .wy-input-context,.wy-input-suffix .wy-input-context{line-height:27px;padding:0 8px;display:inline-block;font-size:80%;background-color:#f3f6f6;border:1px solid #ccc;color:#999}.wy-input-suffix .wy-input-context{border-left:0}.wy-input-prefix .wy-input-context{border-right:0}.wy-switch{position:relative;display:block;height:24px;margin-top:12px;cursor:pointer}.wy-switch:before{left:0;top:0;width:36px;height:12px;background:#ccc}.wy-switch:after,.wy-switch:before{position:absolute;content:"";display:block;border-radius:4px;-webkit-transition:all .2s ease-in-out;-moz-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.wy-switch:after{width:18px;height:18px;background:#999;left:-3px;top:-3px}.wy-switch span{position:absolute;left:48px;display:block;font-size:12px;color:#ccc;line-height:1}.wy-switch.active:before{background:#1e8449}.wy-switch.active:after{left:24px;background:#27ae60}.wy-switch.disabled{cursor:not-allowed;opacity:.8}.wy-control-group.wy-control-group-error .wy-form-message,.wy-control-group.wy-control-group-error>label{color:#e74c3c}.wy-control-group.wy-control-group-error input[type=color],.wy-control-group.wy-control-group-error input[type=date],.wy-control-group.wy-control-group-error input[type=datetime-local],.wy-control-group.wy-control-group-error input[type=datetime],.wy-control-group.wy-control-group-error input[type=email],.wy-control-group.wy-control-group-error input[type=month],.wy-control-group.wy-control-group-error input[type=number],.wy-control-group.wy-control-group-error input[type=password],.wy-control-group.wy-control-group-error input[type=search],.wy-control-group.wy-control-group-error input[type=tel],.wy-control-group.wy-control-group-error input[type=text],.wy-control-group.wy-control-group-error input[type=time],.wy-control-group.wy-control-group-error input[type=url],.wy-control-group.wy-control-group-error input[type=week],.wy-control-group.wy-control-group-error textarea{border:1px solid #e74c3c}.wy-inline-validate{white-space:nowrap}.wy-inline-validate .wy-input-context{padding:.5em .625em;display:inline-block;font-size:80%}.wy-inline-validate.wy-inline-validate-success .wy-input-context{color:#27ae60}.wy-inline-validate.wy-inline-validate-danger .wy-input-context{color:#e74c3c}.wy-inline-validate.wy-inline-validate-warning .wy-input-context{color:#e67e22}.wy-inline-validate.wy-inline-validate-info .wy-input-context{color:#2980b9}.rotate-90{-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.rotate-180{-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg)}.rotate-270{-webkit-transform:rotate(270deg);-moz-transform:rotate(270deg);-ms-transform:rotate(270deg);-o-transform:rotate(270deg);transform:rotate(270deg)}.mirror{-webkit-transform:scaleX(-1);-moz-transform:scaleX(-1);-ms-transform:scaleX(-1);-o-transform:scaleX(-1);transform:scaleX(-1)}.mirror.rotate-90{-webkit-transform:scaleX(-1) rotate(90deg);-moz-transform:scaleX(-1) rotate(90deg);-ms-transform:scaleX(-1) rotate(90deg);-o-transform:scaleX(-1) rotate(90deg);transform:scaleX(-1) rotate(90deg)}.mirror.rotate-180{-webkit-transform:scaleX(-1) rotate(180deg);-moz-transform:scaleX(-1) rotate(180deg);-ms-transform:scaleX(-1) rotate(180deg);-o-transform:scaleX(-1) rotate(180deg);transform:scaleX(-1) rotate(180deg)}.mirror.rotate-270{-webkit-transform:scaleX(-1) rotate(270deg);-moz-transform:scaleX(-1) rotate(270deg);-ms-transform:scaleX(-1) rotate(270deg);-o-transform:scaleX(-1) rotate(270deg);transform:scaleX(-1) rotate(270deg)}@media only screen and (max-width:480px){.wy-form button[type=submit]{margin:.7em 0 0}.wy-form input[type=color],.wy-form input[type=date],.wy-form input[type=datetime-local],.wy-form input[type=datetime],.wy-form input[type=email],.wy-form input[type=month],.wy-form input[type=number],.wy-form input[type=password],.wy-form input[type=search],.wy-form input[type=tel],.wy-form input[type=text],.wy-form input[type=time],.wy-form input[type=url],.wy-form input[type=week],.wy-form label{margin-bottom:.3em;display:block}.wy-form input[type=color],.wy-form input[type=date],.wy-form input[type=datetime-local],.wy-form input[type=datetime],.wy-form input[type=email],.wy-form input[type=month],.wy-form input[type=number],.wy-form input[type=password],.wy-form input[type=search],.wy-form input[type=tel],.wy-form input[type=time],.wy-form input[type=url],.wy-form input[type=week]{margin-bottom:0}.wy-form-aligned .wy-control-group label{margin-bottom:.3em;text-align:left;display:block;width:100%}.wy-form-aligned .wy-control{margin:1.5em 0 0}.wy-form-message,.wy-form-message-inline,.wy-form .wy-help-inline{display:block;font-size:80%;padding:6px 0}}@media screen and (max-width:768px){.tablet-hide{display:none}}@media screen and (max-width:480px){.mobile-hide{display:none}}.float-left{float:left}.float-right{float:right}.full-width{width:100%}.rst-content table.docutils,.rst-content table.field-list,.wy-table{border-collapse:collapse;border-spacing:0;empty-cells:show;margin-bottom:24px}.rst-content table.docutils caption,.rst-content table.field-list caption,.wy-table caption{color:#000;font:italic 85%/1 arial,sans-serif;padding:1em 0;text-align:center}.rst-content table.docutils td,.rst-content table.docutils th,.rst-content table.field-list td,.rst-content table.field-list th,.wy-table td,.wy-table th{font-size:90%;margin:0;overflow:visible;padding:8px 16px}.rst-content table.docutils td:first-child,.rst-content table.docutils th:first-child,.rst-content table.field-list td:first-child,.rst-content table.field-list th:first-child,.wy-table td:first-child,.wy-table th:first-child{border-left-width:0}.rst-content table.docutils thead,.rst-content table.field-list thead,.wy-table thead{color:#000;text-align:left;vertical-align:bottom;white-space:nowrap}.rst-content table.docutils thead th,.rst-content table.field-list thead th,.wy-table thead th{font-weight:700;border-bottom:2px solid #e1e4e5}.rst-content table.docutils td,.rst-content table.field-list td,.wy-table td{background-color:transparent;vertical-align:middle}.rst-content table.docutils td p,.rst-content table.field-list td p,.wy-table td p{line-height:18px}.rst-content table.docutils td p:last-child,.rst-content table.field-list td p:last-child,.wy-table td p:last-child{margin-bottom:0}.rst-content table.docutils .wy-table-cell-min,.rst-content table.field-list .wy-table-cell-min,.wy-table .wy-table-cell-min{width:1%;padding-right:0}.rst-content table.docutils .wy-table-cell-min input[type=checkbox],.rst-content table.field-list .wy-table-cell-min input[type=checkbox],.wy-table .wy-table-cell-min input[type=checkbox]{margin:0}.wy-table-secondary{color:grey;font-size:90%}.wy-table-tertiary{color:grey;font-size:80%}.rst-content table.docutils:not(.field-list) tr:nth-child(2n-1) td,.wy-table-backed,.wy-table-odd td,.wy-table-striped tr:nth-child(2n-1) td{background-color:#f3f6f6}.rst-content table.docutils,.wy-table-bordered-all{border:1px solid #e1e4e5}.rst-content table.docutils td,.wy-table-bordered-all td{border-bottom:1px solid #e1e4e5;border-left:1px solid #e1e4e5}.rst-content table.docutils tbody>tr:last-child td,.wy-table-bordered-all tbody>tr:last-child td{border-bottom-width:0}.wy-table-bordered{border:1px solid #e1e4e5}.wy-table-bordered-rows td{border-bottom:1px solid #e1e4e5}.wy-table-bordered-rows tbody>tr:last-child td{border-bottom-width:0}.wy-table-horizontal td,.wy-table-horizontal th{border-width:0 0 1px;border-bottom:1px solid #e1e4e5}.wy-table-horizontal tbody>tr:last-child td{border-bottom-width:0}.wy-table-responsive{margin-bottom:24px;max-width:100%;overflow:auto}.wy-table-responsive table{margin-bottom:0!important}.wy-table-responsive table td,.wy-table-responsive table th{white-space:nowrap}a{color:#2980b9;text-decoration:none;cursor:pointer}a:hover{color:#3091d1}a:visited{color:#9b59b6}html{height:100%}body,html{overflow-x:hidden}body{font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;font-weight:400;color:#404040;min-height:100%;background:#edf0f2}.wy-text-left{text-align:left}.wy-text-center{text-align:center}.wy-text-right{text-align:right}.wy-text-large{font-size:120%}.wy-text-normal{font-size:100%}.wy-text-small,small{font-size:80%}.wy-text-strike{text-decoration:line-through}.wy-text-warning{color:#e67e22!important}a.wy-text-warning:hover{color:#eb9950!important}.wy-text-info{color:#2980b9!important}a.wy-text-info:hover{color:#409ad5!important}.wy-text-success{color:#27ae60!important}a.wy-text-success:hover{color:#36d278!important}.wy-text-danger{color:#e74c3c!important}a.wy-text-danger:hover{color:#ed7669!important}.wy-text-neutral{color:#404040!important}a.wy-text-neutral:hover{color:#595959!important}.rst-content .toctree-wrapper>p.caption,h1,h2,h3,h4,h5,h6,legend{margin-top:0;font-weight:700;font-family:Roboto Slab,ff-tisa-web-pro,Georgia,Arial,sans-serif}p{line-height:24px;font-size:16px;margin:0 0 24px}h1{font-size:175%}.rst-content .toctree-wrapper>p.caption,h2{font-size:150%}h3{font-size:125%}h4{font-size:115%}h5{font-size:110%}h6{font-size:100%}hr{display:block;height:1px;border:0;border-top:1px solid #e1e4e5;margin:24px 0;padding:0}.rst-content code,.rst-content tt,code{white-space:nowrap;max-width:100%;background:#fff;border:1px solid #e1e4e5;font-size:75%;padding:0 5px;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;color:#e74c3c;overflow-x:auto}.rst-content tt.code-large,code.code-large{font-size:90%}.rst-content .section ul,.rst-content .toctree-wrapper ul,.rst-content section ul,.wy-plain-list-disc,article ul{list-style:disc;line-height:24px;margin-bottom:24px}.rst-content .section ul li,.rst-content .toctree-wrapper ul li,.rst-content section ul li,.wy-plain-list-disc li,article ul li{list-style:disc;margin-left:24px}.rst-content .section ul li p:last-child,.rst-content .section ul li ul,.rst-content .toctree-wrapper ul li p:last-child,.rst-content .toctree-wrapper ul li ul,.rst-content section ul li p:last-child,.rst-content section ul li ul,.wy-plain-list-disc li p:last-child,.wy-plain-list-disc li ul,article ul li p:last-child,article ul li ul{margin-bottom:0}.rst-content .section ul li li,.rst-content .toctree-wrapper ul li li,.rst-content section ul li li,.wy-plain-list-disc li li,article ul li li{list-style:circle}.rst-content .section ul li li li,.rst-content .toctree-wrapper ul li li li,.rst-content section ul li li li,.wy-plain-list-disc li li li,article ul li li li{list-style:square}.rst-content .section ul li ol li,.rst-content .toctree-wrapper ul li ol li,.rst-content section ul li ol li,.wy-plain-list-disc li ol li,article ul li ol li{list-style:decimal}.rst-content .section ol,.rst-content .section ol.arabic,.rst-content .toctree-wrapper ol,.rst-content .toctree-wrapper ol.arabic,.rst-content section ol,.rst-content section ol.arabic,.wy-plain-list-decimal,article ol{list-style:decimal;line-height:24px;margin-bottom:24px}.rst-content .section ol.arabic li,.rst-content .section ol li,.rst-content .toctree-wrapper ol.arabic li,.rst-content .toctree-wrapper ol li,.rst-content section ol.arabic li,.rst-content section ol li,.wy-plain-list-decimal li,article ol li{list-style:decimal;margin-left:24px}.rst-content .section ol.arabic li ul,.rst-content .section ol li p:last-child,.rst-content .section ol li ul,.rst-content .toctree-wrapper ol.arabic li ul,.rst-content .toctree-wrapper ol li p:last-child,.rst-content .toctree-wrapper ol li ul,.rst-content section ol.arabic li ul,.rst-content section ol li p:last-child,.rst-content section ol li ul,.wy-plain-list-decimal li p:last-child,.wy-plain-list-decimal li ul,article ol li p:last-child,article ol li ul{margin-bottom:0}.rst-content .section ol.arabic li ul li,.rst-content .section ol li ul li,.rst-content .toctree-wrapper ol.arabic li ul li,.rst-content .toctree-wrapper ol li ul li,.rst-content section ol.arabic li ul li,.rst-content section ol li ul li,.wy-plain-list-decimal li ul li,article ol li ul li{list-style:disc}.wy-breadcrumbs{*zoom:1}.wy-breadcrumbs:after,.wy-breadcrumbs:before{display:table;content:""}.wy-breadcrumbs:after{clear:both}.wy-breadcrumbs>li{display:inline-block;padding-top:5px}.wy-breadcrumbs>li.wy-breadcrumbs-aside{float:right}.rst-content .wy-breadcrumbs>li code,.rst-content .wy-breadcrumbs>li tt,.wy-breadcrumbs>li .rst-content tt,.wy-breadcrumbs>li code{all:inherit;color:inherit}.breadcrumb-item:before{content:"/";color:#bbb;font-size:13px;padding:0 6px 0 3px}.wy-breadcrumbs-extra{margin-bottom:0;color:#b3b3b3;font-size:80%;display:inline-block}@media screen and (max-width:480px){.wy-breadcrumbs-extra,.wy-breadcrumbs li.wy-breadcrumbs-aside{display:none}}@media print{.wy-breadcrumbs li.wy-breadcrumbs-aside{display:none}}html{font-size:16px}.wy-affix{position:fixed;top:1.618em}.wy-menu a:hover{text-decoration:none}.wy-menu-horiz{*zoom:1}.wy-menu-horiz:after,.wy-menu-horiz:before{display:table;content:""}.wy-menu-horiz:after{clear:both}.wy-menu-horiz li,.wy-menu-horiz ul{display:inline-block}.wy-menu-horiz li:hover{background:hsla(0,0%,100%,.1)}.wy-menu-horiz li.divide-left{border-left:1px solid #404040}.wy-menu-horiz li.divide-right{border-right:1px solid #404040}.wy-menu-horiz a{height:32px;display:inline-block;line-height:32px;padding:0 16px}.wy-menu-vertical{width:300px}.wy-menu-vertical header,.wy-menu-vertical p.caption{color:#55a5d9;height:32px;line-height:32px;padding:0 1.618em;margin:12px 0 0;display:block;font-weight:700;text-transform:uppercase;font-size:85%;white-space:nowrap}.wy-menu-vertical ul{margin-bottom:0}.wy-menu-vertical li.divide-top{border-top:1px solid #404040}.wy-menu-vertical li.divide-bottom{border-bottom:1px solid #404040}.wy-menu-vertical li.current{background:#e3e3e3}.wy-menu-vertical li.current a{color:grey;border-right:1px solid #c9c9c9;padding:.4045em 2.427em}.wy-menu-vertical li.current a:hover{background:#d6d6d6}.rst-content .wy-menu-vertical li tt,.wy-menu-vertical li .rst-content tt,.wy-menu-vertical li code{border:none;background:inherit;color:inherit;padding-left:0;padding-right:0}.wy-menu-vertical li button.toctree-expand{display:block;float:left;margin-left:-1.2em;line-height:18px;color:#4d4d4d;border:none;background:none;padding:0}.wy-menu-vertical li.current>a,.wy-menu-vertical li.on a{color:#404040;font-weight:700;position:relative;background:#fcfcfc;border:none;padding:.4045em 1.618em}.wy-menu-vertical li.current>a:hover,.wy-menu-vertical li.on a:hover{background:#fcfcfc}.wy-menu-vertical li.current>a:hover button.toctree-expand,.wy-menu-vertical li.on a:hover button.toctree-expand{color:grey}.wy-menu-vertical li.current>a button.toctree-expand,.wy-menu-vertical li.on a button.toctree-expand{display:block;line-height:18px;color:#333}.wy-menu-vertical li.toctree-l1.current>a{border-bottom:1px solid #c9c9c9;border-top:1px solid #c9c9c9}.wy-menu-vertical .toctree-l1.current .toctree-l2>ul,.wy-menu-vertical .toctree-l2.current .toctree-l3>ul,.wy-menu-vertical .toctree-l3.current .toctree-l4>ul,.wy-menu-vertical .toctree-l4.current .toctree-l5>ul,.wy-menu-vertical .toctree-l5.current .toctree-l6>ul,.wy-menu-vertical .toctree-l6.current .toctree-l7>ul,.wy-menu-vertical .toctree-l7.current .toctree-l8>ul,.wy-menu-vertical .toctree-l8.current .toctree-l9>ul,.wy-menu-vertical .toctree-l9.current .toctree-l10>ul,.wy-menu-vertical .toctree-l10.current .toctree-l11>ul{display:none}.wy-menu-vertical .toctree-l1.current .current.toctree-l2>ul,.wy-menu-vertical .toctree-l2.current .current.toctree-l3>ul,.wy-menu-vertical .toctree-l3.current .current.toctree-l4>ul,.wy-menu-vertical .toctree-l4.current .current.toctree-l5>ul,.wy-menu-vertical .toctree-l5.current .current.toctree-l6>ul,.wy-menu-vertical .toctree-l6.current .current.toctree-l7>ul,.wy-menu-vertical .toctree-l7.current .current.toctree-l8>ul,.wy-menu-vertical .toctree-l8.current .current.toctree-l9>ul,.wy-menu-vertical .toctree-l9.current .current.toctree-l10>ul,.wy-menu-vertical .toctree-l10.current .current.toctree-l11>ul{display:block}.wy-menu-vertical li.toctree-l3,.wy-menu-vertical li.toctree-l4{font-size:.9em}.wy-menu-vertical li.toctree-l2 a,.wy-menu-vertical li.toctree-l3 a,.wy-menu-vertical li.toctree-l4 a,.wy-menu-vertical li.toctree-l5 a,.wy-menu-vertical li.toctree-l6 a,.wy-menu-vertical li.toctree-l7 a,.wy-menu-vertical li.toctree-l8 a,.wy-menu-vertical li.toctree-l9 a,.wy-menu-vertical li.toctree-l10 a{color:#404040}.wy-menu-vertical li.toctree-l2 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l3 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l4 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l5 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l6 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l7 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l8 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l9 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l10 a:hover button.toctree-expand{color:grey}.wy-menu-vertical li.toctree-l2.current li.toctree-l3>a,.wy-menu-vertical li.toctree-l3.current li.toctree-l4>a,.wy-menu-vertical li.toctree-l4.current li.toctree-l5>a,.wy-menu-vertical li.toctree-l5.current li.toctree-l6>a,.wy-menu-vertical li.toctree-l6.current li.toctree-l7>a,.wy-menu-vertical li.toctree-l7.current li.toctree-l8>a,.wy-menu-vertical li.toctree-l8.current li.toctree-l9>a,.wy-menu-vertical li.toctree-l9.current li.toctree-l10>a,.wy-menu-vertical li.toctree-l10.current li.toctree-l11>a{display:block}.wy-menu-vertical li.toctree-l2.current>a{padding:.4045em 2.427em}.wy-menu-vertical li.toctree-l2.current li.toctree-l3>a{padding:.4045em 1.618em .4045em 4.045em}.wy-menu-vertical li.toctree-l3.current>a{padding:.4045em 4.045em}.wy-menu-vertical li.toctree-l3.current li.toctree-l4>a{padding:.4045em 1.618em .4045em 5.663em}.wy-menu-vertical li.toctree-l4.current>a{padding:.4045em 5.663em}.wy-menu-vertical li.toctree-l4.current li.toctree-l5>a{padding:.4045em 1.618em .4045em 7.281em}.wy-menu-vertical li.toctree-l5.current>a{padding:.4045em 7.281em}.wy-menu-vertical li.toctree-l5.current li.toctree-l6>a{padding:.4045em 1.618em .4045em 8.899em}.wy-menu-vertical li.toctree-l6.current>a{padding:.4045em 8.899em}.wy-menu-vertical li.toctree-l6.current li.toctree-l7>a{padding:.4045em 1.618em .4045em 10.517em}.wy-menu-vertical li.toctree-l7.current>a{padding:.4045em 10.517em}.wy-menu-vertical li.toctree-l7.current li.toctree-l8>a{padding:.4045em 1.618em .4045em 12.135em}.wy-menu-vertical li.toctree-l8.current>a{padding:.4045em 12.135em}.wy-menu-vertical li.toctree-l8.current li.toctree-l9>a{padding:.4045em 1.618em .4045em 13.753em}.wy-menu-vertical li.toctree-l9.current>a{padding:.4045em 13.753em}.wy-menu-vertical li.toctree-l9.current li.toctree-l10>a{padding:.4045em 1.618em .4045em 15.371em}.wy-menu-vertical li.toctree-l10.current>a{padding:.4045em 15.371em}.wy-menu-vertical li.toctree-l10.current li.toctree-l11>a{padding:.4045em 1.618em .4045em 16.989em}.wy-menu-vertical li.toctree-l2.current>a,.wy-menu-vertical li.toctree-l2.current li.toctree-l3>a{background:#c9c9c9}.wy-menu-vertical li.toctree-l2 button.toctree-expand{color:#a3a3a3}.wy-menu-vertical li.toctree-l3.current>a,.wy-menu-vertical li.toctree-l3.current li.toctree-l4>a{background:#bdbdbd}.wy-menu-vertical li.toctree-l3 button.toctree-expand{color:#969696}.wy-menu-vertical li.current ul{display:block}.wy-menu-vertical li ul{margin-bottom:0;display:none}.wy-menu-vertical li ul li a{margin-bottom:0;color:#d9d9d9;font-weight:400}.wy-menu-vertical a{line-height:18px;padding:.4045em 1.618em;display:block;position:relative;font-size:90%;color:#d9d9d9}.wy-menu-vertical a:hover{background-color:#4e4a4a;cursor:pointer}.wy-menu-vertical a:hover button.toctree-expand{color:#d9d9d9}.wy-menu-vertical a:active{background-color:#2980b9;cursor:pointer;color:#fff}.wy-menu-vertical a:active button.toctree-expand{color:#fff}.wy-side-nav-search{display:block;width:300px;padding:.809em;margin-bottom:.809em;z-index:200;background-color:#2980b9;text-align:center;color:#fcfcfc}.wy-side-nav-search input[type=text]{width:100%;border-radius:50px;padding:6px 12px;border-color:#2472a4}.wy-side-nav-search img{display:block;margin:auto auto .809em;height:45px;width:45px;background-color:#2980b9;padding:5px;border-radius:100%}.wy-side-nav-search .wy-dropdown>a,.wy-side-nav-search>a{color:#fcfcfc;font-size:100%;font-weight:700;display:inline-block;padding:4px 6px;margin-bottom:.809em;max-width:100%}.wy-side-nav-search .wy-dropdown>a:hover,.wy-side-nav-search .wy-dropdown>aactive,.wy-side-nav-search .wy-dropdown>afocus,.wy-side-nav-search>a:hover,.wy-side-nav-search>aactive,.wy-side-nav-search>afocus{background:hsla(0,0%,100%,.1)}.wy-side-nav-search .wy-dropdown>a img.logo,.wy-side-nav-search>a img.logo{display:block;margin:0 auto;height:auto;width:auto;border-radius:0;max-width:100%;background:transparent}.wy-side-nav-search .wy-dropdown>a.icon,.wy-side-nav-search>a.icon{display:block}.wy-side-nav-search .wy-dropdown>a.icon img.logo,.wy-side-nav-search>a.icon img.logo{margin-top:.85em}.wy-side-nav-search>div.switch-menus{position:relative;display:block;margin-top:-.4045em;margin-bottom:.809em;font-weight:400;color:hsla(0,0%,100%,.3)}.wy-side-nav-search>div.switch-menus>div.language-switch,.wy-side-nav-search>div.switch-menus>div.version-switch{display:inline-block;padding:.2em}.wy-side-nav-search>div.switch-menus>div.language-switch select,.wy-side-nav-search>div.switch-menus>div.version-switch select{display:inline-block;margin-right:-2rem;padding-right:2rem;max-width:240px;text-align-last:center;background:none;border:none;border-radius:0;box-shadow:none;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;font-size:1em;font-weight:400;color:hsla(0,0%,100%,.3);cursor:pointer;appearance:none;-webkit-appearance:none;-moz-appearance:none}.wy-side-nav-search>div.switch-menus>div.language-switch select:active,.wy-side-nav-search>div.switch-menus>div.language-switch select:focus,.wy-side-nav-search>div.switch-menus>div.language-switch select:hover,.wy-side-nav-search>div.switch-menus>div.version-switch select:active,.wy-side-nav-search>div.switch-menus>div.version-switch select:focus,.wy-side-nav-search>div.switch-menus>div.version-switch select:hover{background:hsla(0,0%,100%,.1);color:hsla(0,0%,100%,.5)}.wy-side-nav-search>div.switch-menus>div.language-switch select option,.wy-side-nav-search>div.switch-menus>div.version-switch select option{color:#000}.wy-side-nav-search>div.switch-menus>div.language-switch:has(>select):after,.wy-side-nav-search>div.switch-menus>div.version-switch:has(>select):after{display:inline-block;width:1.5em;height:100%;padding:.1em;content:"\f0d7";font-size:1em;line-height:1.2em;font-family:FontAwesome;text-align:center;pointer-events:none;box-sizing:border-box}.wy-nav .wy-menu-vertical header{color:#2980b9}.wy-nav .wy-menu-vertical a{color:#b3b3b3}.wy-nav .wy-menu-vertical a:hover{background-color:#2980b9;color:#fff}[data-menu-wrap]{-webkit-transition:all .2s ease-in;-moz-transition:all .2s ease-in;transition:all .2s ease-in;position:absolute;opacity:1;width:100%;opacity:0}[data-menu-wrap].move-center{left:0;right:auto;opacity:1}[data-menu-wrap].move-left{right:auto;left:-100%;opacity:0}[data-menu-wrap].move-right{right:-100%;left:auto;opacity:0}.wy-body-for-nav{background:#fcfcfc}.wy-grid-for-nav{position:absolute;width:100%;height:100%}.wy-nav-side{position:fixed;top:0;bottom:0;left:0;padding-bottom:2em;width:300px;overflow-x:hidden;overflow-y:hidden;min-height:100%;color:#9b9b9b;background:#343131;z-index:200}.wy-side-scroll{width:320px;position:relative;overflow-x:hidden;overflow-y:scroll;height:100%}.wy-nav-top{display:none;background:#2980b9;color:#fff;padding:.4045em .809em;position:relative;line-height:50px;text-align:center;font-size:100%;*zoom:1}.wy-nav-top:after,.wy-nav-top:before{display:table;content:""}.wy-nav-top:after{clear:both}.wy-nav-top a{color:#fff;font-weight:700}.wy-nav-top img{margin-right:12px;height:45px;width:45px;background-color:#2980b9;padding:5px;border-radius:100%}.wy-nav-top i{font-size:30px;float:left;cursor:pointer;padding-top:inherit}.wy-nav-content-wrap{margin-left:300px;background:#fcfcfc;min-height:100%}.wy-nav-content{padding:1.618em 3.236em;height:100%;max-width:800px;margin:auto}.wy-body-mask{position:fixed;width:100%;height:100%;background:rgba(0,0,0,.2);display:none;z-index:499}.wy-body-mask.on{display:block}footer{color:grey}footer p{margin-bottom:12px}.rst-content footer span.commit tt,footer span.commit .rst-content tt,footer span.commit code{padding:0;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;font-size:1em;background:none;border:none;color:grey}.rst-footer-buttons{*zoom:1}.rst-footer-buttons:after,.rst-footer-buttons:before{width:100%;display:table;content:""}.rst-footer-buttons:after{clear:both}.rst-breadcrumbs-buttons{margin-top:12px;*zoom:1}.rst-breadcrumbs-buttons:after,.rst-breadcrumbs-buttons:before{display:table;content:""}.rst-breadcrumbs-buttons:after{clear:both}#search-results .search li{margin-bottom:24px;border-bottom:1px solid #e1e4e5;padding-bottom:24px}#search-results .search li:first-child{border-top:1px solid #e1e4e5;padding-top:24px}#search-results .search li a{font-size:120%;margin-bottom:12px;display:inline-block}#search-results .context{color:grey;font-size:90%}.genindextable li>ul{margin-left:24px}@media screen and (max-width:768px){.wy-body-for-nav{background:#fcfcfc}.wy-nav-top{display:block}.wy-nav-side{left:-300px}.wy-nav-side.shift{width:85%;left:0}.wy-menu.wy-menu-vertical,.wy-side-nav-search,.wy-side-scroll{width:auto}.wy-nav-content-wrap{margin-left:0}.wy-nav-content-wrap .wy-nav-content{padding:1.618em}.wy-nav-content-wrap.shift{position:fixed;min-width:100%;left:85%;top:0;height:100%;overflow:hidden}}@media screen and (min-width:1100px){.wy-nav-content-wrap{background:rgba(0,0,0,.05)}.wy-nav-content{margin:0;background:#fcfcfc}}@media print{.rst-versions,.wy-nav-side,footer{display:none}.wy-nav-content-wrap{margin-left:0}}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;z-index:400}.rst-versions a{color:#2980b9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27ae60;*zoom:1}.rst-versions .rst-current-version:after,.rst-versions .rst-current-version:before{display:table;content:""}.rst-versions .rst-current-version:after{clear:both}.rst-content .code-block-caption .rst-versions .rst-current-version .headerlink,.rst-content .eqno .rst-versions .rst-current-version .headerlink,.rst-content .rst-versions .rst-current-version .admonition-title,.rst-content code.download .rst-versions .rst-current-version span:first-child,.rst-content dl dt .rst-versions .rst-current-version .headerlink,.rst-content h1 .rst-versions .rst-current-version .headerlink,.rst-content h2 .rst-versions .rst-current-version .headerlink,.rst-content h3 .rst-versions .rst-current-version .headerlink,.rst-content h4 .rst-versions .rst-current-version .headerlink,.rst-content h5 .rst-versions .rst-current-version .headerlink,.rst-content h6 .rst-versions .rst-current-version .headerlink,.rst-content p .rst-versions .rst-current-version .headerlink,.rst-content table>caption .rst-versions .rst-current-version .headerlink,.rst-content tt.download .rst-versions .rst-current-version span:first-child,.rst-versions .rst-current-version .fa,.rst-versions .rst-current-version .icon,.rst-versions .rst-current-version .rst-content .admonition-title,.rst-versions .rst-current-version .rst-content .code-block-caption .headerlink,.rst-versions .rst-current-version .rst-content .eqno .headerlink,.rst-versions .rst-current-version .rst-content code.download span:first-child,.rst-versions .rst-current-version .rst-content dl dt .headerlink,.rst-versions .rst-current-version .rst-content h1 .headerlink,.rst-versions .rst-current-version .rst-content h2 .headerlink,.rst-versions .rst-current-version .rst-content h3 .headerlink,.rst-versions .rst-current-version .rst-content h4 .headerlink,.rst-versions .rst-current-version .rst-content h5 .headerlink,.rst-versions .rst-current-version .rst-content h6 .headerlink,.rst-versions .rst-current-version .rst-content p .headerlink,.rst-versions .rst-current-version .rst-content table>caption .headerlink,.rst-versions .rst-current-version .rst-content tt.download span:first-child,.rst-versions .rst-current-version .wy-menu-vertical li button.toctree-expand,.wy-menu-vertical li .rst-versions .rst-current-version button.toctree-expand{color:#fcfcfc}.rst-versions .rst-current-version .fa-book,.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#e74c3c;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#f1c40f;color:#000}.rst-versions.shift-up{height:auto;max-height:100%;overflow-y:scroll}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:grey;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:1px solid #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions .rst-other-versions .rtd-current-item{font-weight:700}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px;max-height:90%}.rst-versions.rst-badge .fa-book,.rst-versions.rst-badge .icon-book{float:none;line-height:30px}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book,.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge>.rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width:768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}}#flyout-search-form{padding:6px}.rst-content .toctree-wrapper>p.caption,.rst-content h1,.rst-content h2,.rst-content h3,.rst-content h4,.rst-content h5,.rst-content h6{margin-bottom:24px}.rst-content img{max-width:100%;height:auto}.rst-content div.figure,.rst-content figure{margin-bottom:24px}.rst-content div.figure .caption-text,.rst-content figure .caption-text{font-style:italic}.rst-content div.figure p:last-child.caption,.rst-content figure p:last-child.caption{margin-bottom:0}.rst-content div.figure.align-center,.rst-content figure.align-center{text-align:center}.rst-content .section>a>img,.rst-content .section>img,.rst-content section>a>img,.rst-content section>img{margin-bottom:24px}.rst-content abbr[title]{text-decoration:none}.rst-content.style-external-links a.reference.external:after{font-family:FontAwesome;content:"\f08e";color:#b3b3b3;vertical-align:super;font-size:60%;margin:0 .2em}.rst-content blockquote{margin-left:24px;line-height:24px;margin-bottom:24px}.rst-content pre.literal-block{white-space:pre;margin:0;padding:12px;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;display:block;overflow:auto}.rst-content div[class^=highlight],.rst-content pre.literal-block{border:1px solid #e1e4e5;overflow-x:auto;margin:1px 0 24px}.rst-content div[class^=highlight] div[class^=highlight],.rst-content pre.literal-block div[class^=highlight]{padding:0;border:none;margin:0}.rst-content div[class^=highlight] td.code{width:100%}.rst-content .linenodiv pre{border-right:1px solid #e6e9ea;margin:0;padding:12px;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;user-select:none;pointer-events:none}.rst-content div[class^=highlight] pre{white-space:pre;margin:0;padding:12px;display:block;overflow:auto}.rst-content div[class^=highlight] pre .hll{display:block;margin:0 -12px;padding:0 12px}.rst-content .linenodiv pre,.rst-content div[class^=highlight] pre,.rst-content pre.literal-block{font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;font-size:12px;line-height:1.4}.rst-content div.highlight .gp,.rst-content div.highlight span.linenos{user-select:none;pointer-events:none}.rst-content div.highlight span.linenos{display:inline-block;padding-left:0;padding-right:12px;margin-right:12px;border-right:1px solid #e6e9ea}.rst-content .code-block-caption{font-style:italic;font-size:85%;line-height:1;padding:1em 0;text-align:center}@media print{.rst-content .codeblock,.rst-content div[class^=highlight],.rst-content div[class^=highlight] pre{white-space:pre-wrap}}.rst-content .admonition,.rst-content .admonition-todo,.rst-content .attention,.rst-content .caution,.rst-content .danger,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .note,.rst-content .seealso,.rst-content .tip,.rst-content .warning{clear:both}.rst-content .admonition-todo .last,.rst-content .admonition-todo>:last-child,.rst-content .admonition .last,.rst-content .admonition>:last-child,.rst-content .attention .last,.rst-content .attention>:last-child,.rst-content .caution .last,.rst-content .caution>:last-child,.rst-content .danger .last,.rst-content .danger>:last-child,.rst-content .error .last,.rst-content .error>:last-child,.rst-content .hint .last,.rst-content .hint>:last-child,.rst-content .important .last,.rst-content .important>:last-child,.rst-content .note .last,.rst-content .note>:last-child,.rst-content .seealso .last,.rst-content .seealso>:last-child,.rst-content .tip .last,.rst-content .tip>:last-child,.rst-content .warning .last,.rst-content .warning>:last-child{margin-bottom:0}.rst-content .admonition-title:before{margin-right:4px}.rst-content .admonition table{border-color:rgba(0,0,0,.1)}.rst-content .admonition table td,.rst-content .admonition table th{background:transparent!important;border-color:rgba(0,0,0,.1)!important}.rst-content .section ol.loweralpha,.rst-content .section ol.loweralpha>li,.rst-content .toctree-wrapper ol.loweralpha,.rst-content .toctree-wrapper ol.loweralpha>li,.rst-content section ol.loweralpha,.rst-content section ol.loweralpha>li{list-style:lower-alpha}.rst-content .section ol.upperalpha,.rst-content .section ol.upperalpha>li,.rst-content .toctree-wrapper ol.upperalpha,.rst-content .toctree-wrapper ol.upperalpha>li,.rst-content section ol.upperalpha,.rst-content section ol.upperalpha>li{list-style:upper-alpha}.rst-content .section ol li>*,.rst-content .section ul li>*,.rst-content .toctree-wrapper ol li>*,.rst-content .toctree-wrapper ul li>*,.rst-content section ol li>*,.rst-content section ul li>*{margin-top:12px;margin-bottom:12px}.rst-content .section ol li>:first-child,.rst-content .section ul li>:first-child,.rst-content .toctree-wrapper ol li>:first-child,.rst-content .toctree-wrapper ul li>:first-child,.rst-content section ol li>:first-child,.rst-content section ul li>:first-child{margin-top:0}.rst-content .section ol li>p,.rst-content .section ol li>p:last-child,.rst-content .section ul li>p,.rst-content .section ul li>p:last-child,.rst-content .toctree-wrapper ol li>p,.rst-content .toctree-wrapper ol li>p:last-child,.rst-content .toctree-wrapper ul li>p,.rst-content .toctree-wrapper ul li>p:last-child,.rst-content section ol li>p,.rst-content section ol li>p:last-child,.rst-content section ul li>p,.rst-content section ul li>p:last-child{margin-bottom:12px}.rst-content .section ol li>p:only-child,.rst-content .section ol li>p:only-child:last-child,.rst-content .section ul li>p:only-child,.rst-content .section ul li>p:only-child:last-child,.rst-content .toctree-wrapper ol li>p:only-child,.rst-content .toctree-wrapper ol li>p:only-child:last-child,.rst-content .toctree-wrapper ul li>p:only-child,.rst-content .toctree-wrapper ul li>p:only-child:last-child,.rst-content section ol li>p:only-child,.rst-content section ol li>p:only-child:last-child,.rst-content section ul li>p:only-child,.rst-content section ul li>p:only-child:last-child{margin-bottom:0}.rst-content .section ol li>ol,.rst-content .section ol li>ul,.rst-content .section ul li>ol,.rst-content .section ul li>ul,.rst-content .toctree-wrapper ol li>ol,.rst-content .toctree-wrapper ol li>ul,.rst-content .toctree-wrapper ul li>ol,.rst-content .toctree-wrapper ul li>ul,.rst-content section ol li>ol,.rst-content section ol li>ul,.rst-content section ul li>ol,.rst-content section ul li>ul{margin-bottom:12px}.rst-content .section ol.simple li>*,.rst-content .section ol.simple li ol,.rst-content .section ol.simple li ul,.rst-content .section ul.simple li>*,.rst-content .section ul.simple li ol,.rst-content .section ul.simple li ul,.rst-content .toctree-wrapper ol.simple li>*,.rst-content .toctree-wrapper ol.simple li ol,.rst-content .toctree-wrapper ol.simple li ul,.rst-content .toctree-wrapper ul.simple li>*,.rst-content .toctree-wrapper ul.simple li ol,.rst-content .toctree-wrapper ul.simple li ul,.rst-content section ol.simple li>*,.rst-content section ol.simple li ol,.rst-content section ol.simple li ul,.rst-content section ul.simple li>*,.rst-content section ul.simple li ol,.rst-content section ul.simple li ul{margin-top:0;margin-bottom:0}.rst-content .line-block{margin-left:0;margin-bottom:24px;line-height:24px}.rst-content .line-block .line-block{margin-left:24px;margin-bottom:0}.rst-content .topic-title{font-weight:700;margin-bottom:12px}.rst-content .toc-backref{color:#404040}.rst-content .align-right{float:right;margin:0 0 24px 24px}.rst-content .align-left{float:left;margin:0 24px 24px 0}.rst-content .align-center{margin:auto}.rst-content .align-center:not(table){display:block}.rst-content .code-block-caption .headerlink,.rst-content .eqno .headerlink,.rst-content .toctree-wrapper>p.caption .headerlink,.rst-content dl dt .headerlink,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content p.caption .headerlink,.rst-content p .headerlink,.rst-content table>caption .headerlink{opacity:0;font-size:14px;font-family:FontAwesome;margin-left:.5em}.rst-content .code-block-caption .headerlink:focus,.rst-content .code-block-caption:hover .headerlink,.rst-content .eqno .headerlink:focus,.rst-content .eqno:hover .headerlink,.rst-content .toctree-wrapper>p.caption .headerlink:focus,.rst-content .toctree-wrapper>p.caption:hover .headerlink,.rst-content dl dt .headerlink:focus,.rst-content dl dt:hover .headerlink,.rst-content h1 .headerlink:focus,.rst-content h1:hover .headerlink,.rst-content h2 .headerlink:focus,.rst-content h2:hover .headerlink,.rst-content h3 .headerlink:focus,.rst-content h3:hover .headerlink,.rst-content h4 .headerlink:focus,.rst-content h4:hover .headerlink,.rst-content h5 .headerlink:focus,.rst-content h5:hover .headerlink,.rst-content h6 .headerlink:focus,.rst-content h6:hover .headerlink,.rst-content p.caption .headerlink:focus,.rst-content p.caption:hover .headerlink,.rst-content p .headerlink:focus,.rst-content p:hover .headerlink,.rst-content table>caption .headerlink:focus,.rst-content table>caption:hover .headerlink{opacity:1}.rst-content p a{overflow-wrap:anywhere}.rst-content .wy-table td p,.rst-content .wy-table td ul,.rst-content .wy-table th p,.rst-content .wy-table th ul,.rst-content table.docutils td p,.rst-content table.docutils td ul,.rst-content table.docutils th p,.rst-content table.docutils th ul,.rst-content table.field-list td p,.rst-content table.field-list td ul,.rst-content table.field-list th p,.rst-content table.field-list th ul{font-size:inherit}.rst-content .btn:focus{outline:2px solid}.rst-content table>caption .headerlink:after{font-size:12px}.rst-content .centered{text-align:center}.rst-content .sidebar{float:right;width:40%;display:block;margin:0 0 24px 24px;padding:24px;background:#f3f6f6;border:1px solid #e1e4e5}.rst-content .sidebar dl,.rst-content .sidebar p,.rst-content .sidebar ul{font-size:90%}.rst-content .sidebar .last,.rst-content .sidebar>:last-child{margin-bottom:0}.rst-content .sidebar .sidebar-title{display:block;font-family:Roboto Slab,ff-tisa-web-pro,Georgia,Arial,sans-serif;font-weight:700;background:#e1e4e5;padding:6px 12px;margin:-24px -24px 24px;font-size:100%}.rst-content .highlighted{background:#f1c40f;box-shadow:0 0 0 2px #f1c40f;display:inline;font-weight:700}.rst-content .citation-reference,.rst-content .footnote-reference{vertical-align:baseline;position:relative;top:-.4em;line-height:0;font-size:90%}.rst-content .citation-reference>span.fn-bracket,.rst-content .footnote-reference>span.fn-bracket{display:none}.rst-content .hlist{width:100%}.rst-content dl dt span.classifier:before{content:" : "}.rst-content dl dt span.classifier-delimiter{display:none!important}html.writer-html4 .rst-content table.docutils.citation,html.writer-html4 .rst-content table.docutils.footnote{background:none;border:none}html.writer-html4 .rst-content table.docutils.citation td,html.writer-html4 .rst-content table.docutils.citation tr,html.writer-html4 .rst-content table.docutils.footnote td,html.writer-html4 .rst-content table.docutils.footnote tr{border:none;background-color:transparent!important;white-space:normal}html.writer-html4 .rst-content table.docutils.citation td.label,html.writer-html4 .rst-content table.docutils.footnote td.label{padding-left:0;padding-right:0;vertical-align:top}html.writer-html5 .rst-content dl.citation,html.writer-html5 .rst-content dl.field-list,html.writer-html5 .rst-content dl.footnote{display:grid;grid-template-columns:auto minmax(80%,95%)}html.writer-html5 .rst-content dl.citation>dt,html.writer-html5 .rst-content dl.field-list>dt,html.writer-html5 .rst-content dl.footnote>dt{display:inline-grid;grid-template-columns:max-content auto}html.writer-html5 .rst-content aside.citation,html.writer-html5 .rst-content aside.footnote,html.writer-html5 .rst-content div.citation{display:grid;grid-template-columns:auto auto minmax(.65rem,auto) minmax(40%,95%)}html.writer-html5 .rst-content aside.citation>span.label,html.writer-html5 .rst-content aside.footnote>span.label,html.writer-html5 .rst-content div.citation>span.label{grid-column-start:1;grid-column-end:2}html.writer-html5 .rst-content aside.citation>span.backrefs,html.writer-html5 .rst-content aside.footnote>span.backrefs,html.writer-html5 .rst-content div.citation>span.backrefs{grid-column-start:2;grid-column-end:3;grid-row-start:1;grid-row-end:3}html.writer-html5 .rst-content aside.citation>p,html.writer-html5 .rst-content aside.footnote>p,html.writer-html5 .rst-content div.citation>p{grid-column-start:4;grid-column-end:5}html.writer-html5 .rst-content dl.citation,html.writer-html5 .rst-content dl.field-list,html.writer-html5 .rst-content dl.footnote{margin-bottom:24px}html.writer-html5 .rst-content dl.citation>dt,html.writer-html5 .rst-content dl.field-list>dt,html.writer-html5 .rst-content dl.footnote>dt{padding-left:1rem}html.writer-html5 .rst-content dl.citation>dd,html.writer-html5 .rst-content dl.citation>dt,html.writer-html5 .rst-content dl.field-list>dd,html.writer-html5 .rst-content dl.field-list>dt,html.writer-html5 .rst-content dl.footnote>dd,html.writer-html5 .rst-content dl.footnote>dt{margin-bottom:0}html.writer-html5 .rst-content dl.citation,html.writer-html5 .rst-content dl.footnote{font-size:.9rem}html.writer-html5 .rst-content dl.citation>dt,html.writer-html5 .rst-content dl.footnote>dt{margin:0 .5rem .5rem 0;line-height:1.2rem;word-break:break-all;font-weight:400}html.writer-html5 .rst-content dl.citation>dt>span.brackets:before,html.writer-html5 .rst-content dl.footnote>dt>span.brackets:before{content:"["}html.writer-html5 .rst-content dl.citation>dt>span.brackets:after,html.writer-html5 .rst-content dl.footnote>dt>span.brackets:after{content:"]"}html.writer-html5 .rst-content dl.citation>dt>span.fn-backref,html.writer-html5 .rst-content dl.footnote>dt>span.fn-backref{text-align:left;font-style:italic;margin-left:.65rem;word-break:break-word;word-spacing:-.1rem;max-width:5rem}html.writer-html5 .rst-content dl.citation>dt>span.fn-backref>a,html.writer-html5 .rst-content dl.footnote>dt>span.fn-backref>a{word-break:keep-all}html.writer-html5 .rst-content dl.citation>dt>span.fn-backref>a:not(:first-child):before,html.writer-html5 .rst-content dl.footnote>dt>span.fn-backref>a:not(:first-child):before{content:" "}html.writer-html5 .rst-content dl.citation>dd,html.writer-html5 .rst-content dl.footnote>dd{margin:0 0 .5rem;line-height:1.2rem}html.writer-html5 .rst-content dl.citation>dd p,html.writer-html5 .rst-content dl.footnote>dd p{font-size:.9rem}html.writer-html5 .rst-content aside.citation,html.writer-html5 .rst-content aside.footnote,html.writer-html5 .rst-content div.citation{padding-left:1rem;padding-right:1rem;font-size:.9rem;line-height:1.2rem}html.writer-html5 .rst-content aside.citation p,html.writer-html5 .rst-content aside.footnote p,html.writer-html5 .rst-content div.citation p{font-size:.9rem;line-height:1.2rem;margin-bottom:12px}html.writer-html5 .rst-content aside.citation span.backrefs,html.writer-html5 .rst-content aside.footnote span.backrefs,html.writer-html5 .rst-content div.citation span.backrefs{text-align:left;font-style:italic;margin-left:.65rem;word-break:break-word;word-spacing:-.1rem;max-width:5rem}html.writer-html5 .rst-content aside.citation span.backrefs>a,html.writer-html5 .rst-content aside.footnote span.backrefs>a,html.writer-html5 .rst-content div.citation span.backrefs>a{word-break:keep-all}html.writer-html5 .rst-content aside.citation span.backrefs>a:not(:first-child):before,html.writer-html5 .rst-content aside.footnote span.backrefs>a:not(:first-child):before,html.writer-html5 .rst-content div.citation span.backrefs>a:not(:first-child):before{content:" "}html.writer-html5 .rst-content aside.citation span.label,html.writer-html5 .rst-content aside.footnote span.label,html.writer-html5 .rst-content div.citation span.label{line-height:1.2rem}html.writer-html5 .rst-content aside.citation-list,html.writer-html5 .rst-content aside.footnote-list,html.writer-html5 .rst-content div.citation-list{margin-bottom:24px}html.writer-html5 .rst-content dl.option-list kbd{font-size:.9rem}.rst-content table.docutils.footnote,html.writer-html4 .rst-content table.docutils.citation,html.writer-html5 .rst-content aside.footnote,html.writer-html5 .rst-content aside.footnote-list aside.footnote,html.writer-html5 .rst-content div.citation-list>div.citation,html.writer-html5 .rst-content dl.citation,html.writer-html5 .rst-content dl.footnote{color:grey}.rst-content table.docutils.footnote code,.rst-content table.docutils.footnote tt,html.writer-html4 .rst-content table.docutils.citation code,html.writer-html4 .rst-content table.docutils.citation tt,html.writer-html5 .rst-content aside.footnote-list aside.footnote code,html.writer-html5 .rst-content aside.footnote-list aside.footnote tt,html.writer-html5 .rst-content aside.footnote code,html.writer-html5 .rst-content aside.footnote tt,html.writer-html5 .rst-content div.citation-list>div.citation code,html.writer-html5 .rst-content div.citation-list>div.citation tt,html.writer-html5 .rst-content dl.citation code,html.writer-html5 .rst-content dl.citation tt,html.writer-html5 .rst-content dl.footnote code,html.writer-html5 .rst-content dl.footnote tt{color:#555}.rst-content .wy-table-responsive.citation,.rst-content .wy-table-responsive.footnote{margin-bottom:0}.rst-content .wy-table-responsive.citation+:not(.citation),.rst-content .wy-table-responsive.footnote+:not(.footnote){margin-top:24px}.rst-content .wy-table-responsive.citation:last-child,.rst-content .wy-table-responsive.footnote:last-child{margin-bottom:24px}.rst-content table.docutils th{border-color:#e1e4e5}html.writer-html5 .rst-content table.docutils th{border:1px solid #e1e4e5}html.writer-html5 .rst-content table.docutils td>p,html.writer-html5 .rst-content table.docutils th>p{line-height:1rem;margin-bottom:0;font-size:.9rem}.rst-content table.docutils td .last,.rst-content table.docutils td .last>:last-child{margin-bottom:0}.rst-content table.field-list,.rst-content table.field-list td{border:none}.rst-content table.field-list td p{line-height:inherit}.rst-content table.field-list td>strong{display:inline-block}.rst-content table.field-list .field-name{padding-right:10px;text-align:left;white-space:nowrap}.rst-content table.field-list .field-body{text-align:left}.rst-content code,.rst-content tt{color:#000;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;padding:2px 5px}.rst-content code big,.rst-content code em,.rst-content tt big,.rst-content tt em{font-size:100%!important;line-height:normal}.rst-content code.literal,.rst-content tt.literal{color:#e74c3c;white-space:normal}.rst-content code.xref,.rst-content tt.xref,a .rst-content code,a .rst-content tt{font-weight:700;color:#404040;overflow-wrap:normal}.rst-content kbd,.rst-content pre,.rst-content samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace}.rst-content a code,.rst-content a tt{color:#2980b9}.rst-content dl{margin-bottom:24px}.rst-content dl dt{font-weight:700;margin-bottom:12px}.rst-content dl ol,.rst-content dl p,.rst-content dl table,.rst-content dl ul{margin-bottom:12px}.rst-content dl dd{margin:0 0 12px 24px;line-height:24px}.rst-content dl dd>ol:last-child,.rst-content dl dd>p:last-child,.rst-content dl dd>table:last-child,.rst-content dl dd>ul:last-child{margin-bottom:0}html.writer-html4 .rst-content dl:not(.docutils),html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple){margin-bottom:24px}html.writer-html4 .rst-content dl:not(.docutils)>dt,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt{display:table;margin:6px 0;font-size:90%;line-height:normal;background:#e7f2fa;color:#2980b9;border-top:3px solid #6ab0de;padding:6px;position:relative}html.writer-html4 .rst-content dl:not(.docutils)>dt:before,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt:before{color:#6ab0de}html.writer-html4 .rst-content dl:not(.docutils)>dt .headerlink,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt .headerlink{color:#404040;font-size:100%!important}html.writer-html4 .rst-content dl:not(.docutils) dl:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) dl:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt{margin-bottom:6px;border:none;border-left:3px solid #ccc;background:#f0f0f0;color:#555}html.writer-html4 .rst-content dl:not(.docutils) dl:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt .headerlink,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) dl:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt .headerlink{color:#404040;font-size:100%!important}html.writer-html4 .rst-content dl:not(.docutils)>dt:first-child,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt:first-child{margin-top:0}html.writer-html4 .rst-content dl:not(.docutils) code.descclassname,html.writer-html4 .rst-content dl:not(.docutils) code.descname,html.writer-html4 .rst-content dl:not(.docutils) tt.descclassname,html.writer-html4 .rst-content dl:not(.docutils) tt.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) code.descclassname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) code.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) tt.descclassname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) tt.descname{background-color:transparent;border:none;padding:0;font-size:100%!important}html.writer-html4 .rst-content dl:not(.docutils) code.descname,html.writer-html4 .rst-content dl:not(.docutils) tt.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) code.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) tt.descname{font-weight:700}html.writer-html4 .rst-content dl:not(.docutils) .optional,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .optional{display:inline-block;padding:0 4px;color:#000;font-weight:700}html.writer-html4 .rst-content dl:not(.docutils) .property,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .property{display:inline-block;padding-right:8px;max-width:100%}html.writer-html4 .rst-content dl:not(.docutils) .k,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .k{font-style:italic}html.writer-html4 .rst-content dl:not(.docutils) .descclassname,html.writer-html4 .rst-content dl:not(.docutils) .descname,html.writer-html4 .rst-content dl:not(.docutils) .sig-name,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .descclassname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .sig-name{font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;color:#000}.rst-content .viewcode-back,.rst-content .viewcode-link{display:inline-block;color:#27ae60;font-size:80%;padding-left:24px}.rst-content .viewcode-back{display:block;float:right}.rst-content p.rubric{margin-bottom:12px;font-weight:700}.rst-content code.download,.rst-content tt.download{background:inherit;padding:inherit;font-weight:400;font-family:inherit;font-size:inherit;color:inherit;border:inherit;white-space:inherit}.rst-content code.download span:first-child,.rst-content tt.download span:first-child{-webkit-font-smoothing:subpixel-antialiased}.rst-content code.download span:first-child:before,.rst-content tt.download span:first-child:before{margin-right:4px}.rst-content .guilabel,.rst-content .menuselection{font-size:80%;font-weight:700;border-radius:4px;padding:2.4px 6px;margin:auto 2px}.rst-content .guilabel,.rst-content .menuselection{border:1px solid #7fbbe3;background:#e7f2fa}.rst-content :not(dl.option-list)>:not(dt):not(kbd):not(.kbd)>.kbd,.rst-content :not(dl.option-list)>:not(dt):not(kbd):not(.kbd)>kbd{color:inherit;font-size:80%;background-color:#fff;border:1px solid #a6a6a6;border-radius:4px;box-shadow:0 2px grey;padding:2.4px 6px;margin:auto 0}.rst-content .versionmodified{font-style:italic}@media screen and (max-width:480px){.rst-content .sidebar{width:100%}}span[id*=MathJax-Span]{color:#404040}.math{text-align:center}@font-face{font-family:Lato;src:url(fonts/lato-normal.woff2?bd03a2cc277bbbc338d464e679fe9942) format("woff2"),url(fonts/lato-normal.woff?27bd77b9162d388cb8d4c4217c7c5e2a) format("woff");font-weight:400;font-style:normal;font-display:block}@font-face{font-family:Lato;src:url(fonts/lato-bold.woff2?cccb897485813c7c256901dbca54ecf2) format("woff2"),url(fonts/lato-bold.woff?d878b6c29b10beca227e9eef4246111b) format("woff");font-weight:700;font-style:normal;font-display:block}@font-face{font-family:Lato;src:url(fonts/lato-bold-italic.woff2?0b6bb6725576b072c5d0b02ecdd1900d) format("woff2"),url(fonts/lato-bold-italic.woff?9c7e4e9eb485b4a121c760e61bc3707c) format("woff");font-weight:700;font-style:italic;font-display:block}@font-face{font-family:Lato;src:url(fonts/lato-normal-italic.woff2?4eb103b4d12be57cb1d040ed5e162e9d) format("woff2"),url(fonts/lato-normal-italic.woff?f28f2d6482446544ef1ea1ccc6dd5892) format("woff");font-weight:400;font-style:italic;font-display:block}@font-face{font-family:Roboto Slab;font-style:normal;font-weight:400;src:url(fonts/Roboto-Slab-Regular.woff2?7abf5b8d04d26a2cafea937019bca958) format("woff2"),url(fonts/Roboto-Slab-Regular.woff?c1be9284088d487c5e3ff0a10a92e58c) format("woff");font-display:block}@font-face{font-family:Roboto Slab;font-style:normal;font-weight:700;src:url(fonts/Roboto-Slab-Bold.woff2?9984f4a9bda09be08e83f2506954adbe) format("woff2"),url(fonts/Roboto-Slab-Bold.woff?bed5564a116b05148e3b3bea6fb1162a) format("woff");font-display:block} \ No newline at end of file diff --git a/docs/_static/doctools.js b/docs/_static/doctools.js new file mode 100644 index 00000000..0398ebb9 --- /dev/null +++ b/docs/_static/doctools.js @@ -0,0 +1,149 @@ +/* + * Base JavaScript utilities for all Sphinx HTML documentation. + */ +"use strict"; + +const BLACKLISTED_KEY_CONTROL_ELEMENTS = new Set([ + "TEXTAREA", + "INPUT", + "SELECT", + "BUTTON", +]); + +const _ready = (callback) => { + if (document.readyState !== "loading") { + callback(); + } else { + document.addEventListener("DOMContentLoaded", callback); + } +}; + +/** + * Small JavaScript module for the documentation. + */ +const Documentation = { + init: () => { + Documentation.initDomainIndexTable(); + Documentation.initOnKeyListeners(); + }, + + /** + * i18n support + */ + TRANSLATIONS: {}, + PLURAL_EXPR: (n) => (n === 1 ? 0 : 1), + LOCALE: "unknown", + + // gettext and ngettext don't access this so that the functions + // can safely bound to a different name (_ = Documentation.gettext) + gettext: (string) => { + const translated = Documentation.TRANSLATIONS[string]; + switch (typeof translated) { + case "undefined": + return string; // no translation + case "string": + return translated; // translation exists + default: + return translated[0]; // (singular, plural) translation tuple exists + } + }, + + ngettext: (singular, plural, n) => { + const translated = Documentation.TRANSLATIONS[singular]; + if (typeof translated !== "undefined") + return translated[Documentation.PLURAL_EXPR(n)]; + return n === 1 ? singular : plural; + }, + + addTranslations: (catalog) => { + Object.assign(Documentation.TRANSLATIONS, catalog.messages); + Documentation.PLURAL_EXPR = new Function( + "n", + `return (${catalog.plural_expr})` + ); + Documentation.LOCALE = catalog.locale; + }, + + /** + * helper function to focus on search bar + */ + focusSearchBar: () => { + document.querySelectorAll("input[name=q]")[0]?.focus(); + }, + + /** + * Initialise the domain index toggle buttons + */ + initDomainIndexTable: () => { + const toggler = (el) => { + const idNumber = el.id.substr(7); + const toggledRows = document.querySelectorAll(`tr.cg-${idNumber}`); + if (el.src.substr(-9) === "minus.png") { + el.src = `${el.src.substr(0, el.src.length - 9)}plus.png`; + toggledRows.forEach((el) => (el.style.display = "none")); + } else { + el.src = `${el.src.substr(0, el.src.length - 8)}minus.png`; + toggledRows.forEach((el) => (el.style.display = "")); + } + }; + + const togglerElements = document.querySelectorAll("img.toggler"); + togglerElements.forEach((el) => + el.addEventListener("click", (event) => toggler(event.currentTarget)) + ); + togglerElements.forEach((el) => (el.style.display = "")); + if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) togglerElements.forEach(toggler); + }, + + initOnKeyListeners: () => { + // only install a listener if it is really needed + if ( + !DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS && + !DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS + ) + return; + + document.addEventListener("keydown", (event) => { + // bail for input elements + if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return; + // bail with special keys + if (event.altKey || event.ctrlKey || event.metaKey) return; + + if (!event.shiftKey) { + switch (event.key) { + case "ArrowLeft": + if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; + + const prevLink = document.querySelector('link[rel="prev"]'); + if (prevLink && prevLink.href) { + window.location.href = prevLink.href; + event.preventDefault(); + } + break; + case "ArrowRight": + if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; + + const nextLink = document.querySelector('link[rel="next"]'); + if (nextLink && nextLink.href) { + window.location.href = nextLink.href; + event.preventDefault(); + } + break; + } + } + + // some keyboard layouts may need Shift to get / + switch (event.key) { + case "/": + if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) break; + Documentation.focusSearchBar(); + event.preventDefault(); + } + }); + }, +}; + +// quick alias for translations +const _ = Documentation.gettext; + +_ready(Documentation.init); diff --git a/docs/_static/documentation_options.js b/docs/_static/documentation_options.js new file mode 100644 index 00000000..52789742 --- /dev/null +++ b/docs/_static/documentation_options.js @@ -0,0 +1,13 @@ +const DOCUMENTATION_OPTIONS = { + VERSION: '0.11.0', + LANGUAGE: 'en', + COLLAPSE_INDEX: false, + BUILDER: 'html', + FILE_SUFFIX: '.html', + LINK_SUFFIX: '.html', + HAS_SOURCE: true, + SOURCELINK_SUFFIX: '.txt', + NAVIGATION_WITH_KEYS: false, + SHOW_SEARCH_SUMMARY: true, + ENABLE_SEARCH_SHORTCUTS: true, +}; \ No newline at end of file diff --git a/docs/_static/file.png b/docs/_static/file.png new file mode 100644 index 00000000..a858a410 Binary files /dev/null and b/docs/_static/file.png differ diff --git a/docs/_static/fonts/Lato/lato-bold.eot b/docs/_static/fonts/Lato/lato-bold.eot new file mode 100644 index 00000000..3361183a Binary files /dev/null and b/docs/_static/fonts/Lato/lato-bold.eot differ diff --git a/docs/_static/fonts/Lato/lato-bold.ttf b/docs/_static/fonts/Lato/lato-bold.ttf new file mode 100644 index 00000000..29f691d5 Binary files /dev/null and b/docs/_static/fonts/Lato/lato-bold.ttf differ diff --git a/docs/_static/fonts/Lato/lato-bold.woff b/docs/_static/fonts/Lato/lato-bold.woff new file mode 100644 index 00000000..c6dff51f Binary files /dev/null and b/docs/_static/fonts/Lato/lato-bold.woff differ diff --git a/docs/_static/fonts/Lato/lato-bold.woff2 b/docs/_static/fonts/Lato/lato-bold.woff2 new file mode 100644 index 00000000..bb195043 Binary files /dev/null and b/docs/_static/fonts/Lato/lato-bold.woff2 differ diff --git a/docs/_static/fonts/Lato/lato-bolditalic.eot b/docs/_static/fonts/Lato/lato-bolditalic.eot new file mode 100644 index 00000000..3d415493 Binary files /dev/null and b/docs/_static/fonts/Lato/lato-bolditalic.eot differ diff --git a/docs/_static/fonts/Lato/lato-bolditalic.ttf b/docs/_static/fonts/Lato/lato-bolditalic.ttf new file mode 100644 index 00000000..f402040b Binary files /dev/null and b/docs/_static/fonts/Lato/lato-bolditalic.ttf differ diff --git a/docs/_static/fonts/Lato/lato-bolditalic.woff b/docs/_static/fonts/Lato/lato-bolditalic.woff new file mode 100644 index 00000000..88ad05b9 Binary files /dev/null and b/docs/_static/fonts/Lato/lato-bolditalic.woff differ diff --git a/docs/_static/fonts/Lato/lato-bolditalic.woff2 b/docs/_static/fonts/Lato/lato-bolditalic.woff2 new file mode 100644 index 00000000..c4e3d804 Binary files /dev/null and b/docs/_static/fonts/Lato/lato-bolditalic.woff2 differ diff --git a/docs/_static/fonts/Lato/lato-italic.eot b/docs/_static/fonts/Lato/lato-italic.eot new file mode 100644 index 00000000..3f826421 Binary files /dev/null and b/docs/_static/fonts/Lato/lato-italic.eot differ diff --git a/docs/_static/fonts/Lato/lato-italic.ttf b/docs/_static/fonts/Lato/lato-italic.ttf new file mode 100644 index 00000000..b4bfc9b2 Binary files /dev/null and b/docs/_static/fonts/Lato/lato-italic.ttf differ diff --git a/docs/_static/fonts/Lato/lato-italic.woff b/docs/_static/fonts/Lato/lato-italic.woff new file mode 100644 index 00000000..76114bc0 Binary files /dev/null and b/docs/_static/fonts/Lato/lato-italic.woff differ diff --git a/docs/_static/fonts/Lato/lato-italic.woff2 b/docs/_static/fonts/Lato/lato-italic.woff2 new file mode 100644 index 00000000..3404f37e Binary files /dev/null and b/docs/_static/fonts/Lato/lato-italic.woff2 differ diff --git a/docs/_static/fonts/Lato/lato-regular.eot b/docs/_static/fonts/Lato/lato-regular.eot new file mode 100644 index 00000000..11e3f2a5 Binary files /dev/null and b/docs/_static/fonts/Lato/lato-regular.eot differ diff --git a/docs/_static/fonts/Lato/lato-regular.ttf b/docs/_static/fonts/Lato/lato-regular.ttf new file mode 100644 index 00000000..74decd9e Binary files /dev/null and b/docs/_static/fonts/Lato/lato-regular.ttf differ diff --git a/docs/_static/fonts/Lato/lato-regular.woff b/docs/_static/fonts/Lato/lato-regular.woff new file mode 100644 index 00000000..ae1307ff Binary files /dev/null and b/docs/_static/fonts/Lato/lato-regular.woff differ diff --git a/docs/_static/fonts/Lato/lato-regular.woff2 b/docs/_static/fonts/Lato/lato-regular.woff2 new file mode 100644 index 00000000..3bf98433 Binary files /dev/null and b/docs/_static/fonts/Lato/lato-regular.woff2 differ diff --git a/docs/_static/fonts/RobotoSlab/roboto-slab-v7-bold.eot b/docs/_static/fonts/RobotoSlab/roboto-slab-v7-bold.eot new file mode 100644 index 00000000..79dc8efe Binary files /dev/null and b/docs/_static/fonts/RobotoSlab/roboto-slab-v7-bold.eot differ diff --git a/docs/_static/fonts/RobotoSlab/roboto-slab-v7-bold.ttf b/docs/_static/fonts/RobotoSlab/roboto-slab-v7-bold.ttf new file mode 100644 index 00000000..df5d1df2 Binary files /dev/null and b/docs/_static/fonts/RobotoSlab/roboto-slab-v7-bold.ttf differ diff --git a/docs/_static/fonts/RobotoSlab/roboto-slab-v7-bold.woff b/docs/_static/fonts/RobotoSlab/roboto-slab-v7-bold.woff new file mode 100644 index 00000000..6cb60000 Binary files /dev/null and b/docs/_static/fonts/RobotoSlab/roboto-slab-v7-bold.woff differ diff --git a/docs/_static/fonts/RobotoSlab/roboto-slab-v7-bold.woff2 b/docs/_static/fonts/RobotoSlab/roboto-slab-v7-bold.woff2 new file mode 100644 index 00000000..7059e231 Binary files /dev/null and b/docs/_static/fonts/RobotoSlab/roboto-slab-v7-bold.woff2 differ diff --git a/docs/_static/fonts/RobotoSlab/roboto-slab-v7-regular.eot b/docs/_static/fonts/RobotoSlab/roboto-slab-v7-regular.eot new file mode 100644 index 00000000..2f7ca78a Binary files /dev/null and b/docs/_static/fonts/RobotoSlab/roboto-slab-v7-regular.eot differ diff --git a/docs/_static/fonts/RobotoSlab/roboto-slab-v7-regular.ttf b/docs/_static/fonts/RobotoSlab/roboto-slab-v7-regular.ttf new file mode 100644 index 00000000..eb52a790 Binary files /dev/null and b/docs/_static/fonts/RobotoSlab/roboto-slab-v7-regular.ttf differ diff --git a/docs/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff b/docs/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff new file mode 100644 index 00000000..f815f63f Binary files /dev/null and b/docs/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff differ diff --git a/docs/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff2 b/docs/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff2 new file mode 100644 index 00000000..f2c76e5b Binary files /dev/null and b/docs/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff2 differ diff --git a/docs/_static/jquery.js b/docs/_static/jquery.js new file mode 100644 index 00000000..c4c6022f --- /dev/null +++ b/docs/_static/jquery.js @@ -0,0 +1,2 @@ +/*! jQuery v3.6.0 | (c) OpenJS Foundation and other contributors | jquery.org/license */ +!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],r=Object.getPrototypeOf,s=t.slice,g=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType&&"function"!=typeof e.item},x=function(e){return null!=e&&e===e.window},E=C.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.6.0",S=function(e,t){return new S.fn.init(e,t)};function p(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ye(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace($,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e&&e.namespaceURI,n=e&&(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||v.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||v.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||v.push(".#.+[+~]"),e.querySelectorAll("\\\f"),v.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},j=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&y(p,e)?-1:t==C||t.ownerDocument==p&&y(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||D,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,D=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),y.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="",y.option=!!ce.lastChild;var ge={thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n",""]);var me=/<|&#?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\s*$/g;function je(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function De(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function qe(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Le(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var _t,zt=[],Ut=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=zt.pop()||S.expando+"_"+wt.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Ut.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Ut.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Ut,"$1"+r):!1!==e.jsonp&&(e.url+=(Tt.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,zt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((_t=E.implementation.createHTMLDocument("").body).innerHTML="
",2===_t.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=Fe(y.pixelPosition,function(e,t){if(t)return t=We(e,n),Pe.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return $(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 0"),n("table.docutils.footnote").wrap("
"),n("table.docutils.citation").wrap("
"),n(".wy-menu-vertical ul").not(".simple").siblings("a").each((function(){var t=n(this);expand=n(''),expand.on("click",(function(n){return e.toggleCurrent(t),n.stopPropagation(),!1})),t.prepend(expand)}))},reset:function(){var n=encodeURI(window.location.hash)||"#";try{var e=$(".wy-menu-vertical"),t=e.find('[href="'+n+'"]');if(0===t.length){var i=$('.document [id="'+n.substring(1)+'"]').closest("div.section");0===(t=e.find('[href="#'+i.attr("id")+'"]')).length&&(t=e.find('[href="#"]'))}if(t.length>0){$(".wy-menu-vertical .current").removeClass("current").attr("aria-expanded","false"),t.addClass("current").attr("aria-expanded","true"),t.closest("li.toctree-l1").parent().addClass("current").attr("aria-expanded","true");for(let n=1;n<=10;n++)t.closest("li.toctree-l"+n).addClass("current").attr("aria-expanded","true");t[0].scrollIntoView()}}catch(n){console.log("Error expanding nav for anchor",n)}},onScroll:function(){this.winScroll=!1;var n=this.win.scrollTop(),e=n+this.winHeight,t=this.navBar.scrollTop()+(n-this.winPosition);n<0||e>this.docHeight||(this.navBar.scrollTop(t),this.winPosition=n)},onResize:function(){this.winResize=!1,this.winHeight=this.win.height(),this.docHeight=$(document).height()},hashChange:function(){this.linkScroll=!0,this.win.one("hashchange",(function(){this.linkScroll=!1}))},toggleCurrent:function(n){var e=n.closest("li");e.siblings("li.current").removeClass("current").attr("aria-expanded","false"),e.siblings().find("li.current").removeClass("current").attr("aria-expanded","false");var t=e.find("> ul li");t.length&&(t.removeClass("current").attr("aria-expanded","false"),e.toggleClass("current").attr("aria-expanded",(function(n,e){return"true"==e?"false":"true"})))}},"undefined"!=typeof window&&(window.SphinxRtdTheme={Navigation:n.exports.ThemeNav,StickyNav:n.exports.ThemeNav}),function(){for(var n=0,e=["ms","moz","webkit","o"],t=0;t a.language.name.localeCompare(b.language.name)); + + const languagesHTML = ` +
+
Languages
+ ${languages + .map( + (translation) => ` +
+ ${translation.language.code} +
+ `, + ) + .join("\n")} +
+ `; + return languagesHTML; + } + + function renderVersions(config) { + if (!config.versions.active.length) { + return ""; + } + const versionsHTML = ` +
+
Versions
+ ${config.versions.active + .map( + (version) => ` +
+ ${version.slug} +
+ `, + ) + .join("\n")} +
+ `; + return versionsHTML; + } + + function renderDownloads(config) { + if (!Object.keys(config.versions.current.downloads).length) { + return ""; + } + const downloadsNameDisplay = { + pdf: "PDF", + epub: "Epub", + htmlzip: "HTML", + }; + + const downloadsHTML = ` +
+
Downloads
+ ${Object.entries(config.versions.current.downloads) + .map( + ([name, url]) => ` +
+ ${downloadsNameDisplay[name]} +
+ `, + ) + .join("\n")} +
+ `; + return downloadsHTML; + } + + document.addEventListener("readthedocs-addons-data-ready", function (event) { + const config = event.detail.data(); + + const flyout = ` +
+ + Read the Docs + v: ${config.versions.current.slug} + + +
+
+ ${renderLanguages(config)} + ${renderVersions(config)} + ${renderDownloads(config)} +
+
On Read the Docs
+
+ Project Home +
+
+ Builds +
+
+ Downloads +
+
+
+
Search
+
+
+ +
+
+
+
+ + Hosted by Read the Docs + +
+
+ `; + + // Inject the generated flyout into the body HTML element. + document.body.insertAdjacentHTML("beforeend", flyout); + + // Trigger the Read the Docs Addons Search modal when clicking on the "Search docs" input from inside the flyout. + document + .querySelector("#flyout-search-form") + .addEventListener("focusin", () => { + const event = new CustomEvent("readthedocs-search-show"); + document.dispatchEvent(event); + }); + }) +} + +if (themeLanguageSelector || themeVersionSelector) { + function onSelectorSwitch(event) { + const option = event.target.selectedIndex; + const item = event.target.options[option]; + window.location.href = item.dataset.url; + } + + document.addEventListener("readthedocs-addons-data-ready", function (event) { + const config = event.detail.data(); + + const versionSwitch = document.querySelector( + "div.switch-menus > div.version-switch", + ); + if (themeVersionSelector) { + let versions = config.versions.active; + if (config.versions.current.hidden || config.versions.current.type === "external") { + versions.unshift(config.versions.current); + } + const versionSelect = ` + + `; + + versionSwitch.innerHTML = versionSelect; + versionSwitch.firstElementChild.addEventListener("change", onSelectorSwitch); + } + + const languageSwitch = document.querySelector( + "div.switch-menus > div.language-switch", + ); + + if (themeLanguageSelector) { + if (config.projects.translations.length) { + // Add the current language to the options on the selector + let languages = config.projects.translations.concat( + config.projects.current, + ); + languages = languages.sort((a, b) => + a.language.name.localeCompare(b.language.name), + ); + + const languageSelect = ` + + `; + + languageSwitch.innerHTML = languageSelect; + languageSwitch.firstElementChild.addEventListener("change", onSelectorSwitch); + } + else { + languageSwitch.remove(); + } + } + }); +} + +document.addEventListener("readthedocs-addons-data-ready", function (event) { + // Trigger the Read the Docs Addons Search modal when clicking on "Search docs" input from the topnav. + document + .querySelector("[role='search'] input") + .addEventListener("focusin", () => { + const event = new CustomEvent("readthedocs-search-show"); + document.dispatchEvent(event); + }); +}); \ No newline at end of file diff --git a/docs/_static/language_data.js b/docs/_static/language_data.js new file mode 100644 index 00000000..c7fe6c6f --- /dev/null +++ b/docs/_static/language_data.js @@ -0,0 +1,192 @@ +/* + * This script contains the language-specific data used by searchtools.js, + * namely the list of stopwords, stemmer, scorer and splitter. + */ + +var stopwords = ["a", "and", "are", "as", "at", "be", "but", "by", "for", "if", "in", "into", "is", "it", "near", "no", "not", "of", "on", "or", "such", "that", "the", "their", "then", "there", "these", "they", "this", "to", "was", "will", "with"]; + + +/* Non-minified version is copied as a separate JS file, if available */ + +/** + * Porter Stemmer + */ +var Stemmer = function() { + + var step2list = { + ational: 'ate', + tional: 'tion', + enci: 'ence', + anci: 'ance', + izer: 'ize', + bli: 'ble', + alli: 'al', + entli: 'ent', + eli: 'e', + ousli: 'ous', + ization: 'ize', + ation: 'ate', + ator: 'ate', + alism: 'al', + iveness: 'ive', + fulness: 'ful', + ousness: 'ous', + aliti: 'al', + iviti: 'ive', + biliti: 'ble', + logi: 'log' + }; + + var step3list = { + icate: 'ic', + ative: '', + alize: 'al', + iciti: 'ic', + ical: 'ic', + ful: '', + ness: '' + }; + + var c = "[^aeiou]"; // consonant + var v = "[aeiouy]"; // vowel + var C = c + "[^aeiouy]*"; // consonant sequence + var V = v + "[aeiou]*"; // vowel sequence + + var mgr0 = "^(" + C + ")?" + V + C; // [C]VC... is m>0 + var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1 + var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1 + var s_v = "^(" + C + ")?" + v; // vowel in stem + + this.stemWord = function (w) { + var stem; + var suffix; + var firstch; + var origword = w; + + if (w.length < 3) + return w; + + var re; + var re2; + var re3; + var re4; + + firstch = w.substr(0,1); + if (firstch == "y") + w = firstch.toUpperCase() + w.substr(1); + + // Step 1a + re = /^(.+?)(ss|i)es$/; + re2 = /^(.+?)([^s])s$/; + + if (re.test(w)) + w = w.replace(re,"$1$2"); + else if (re2.test(w)) + w = w.replace(re2,"$1$2"); + + // Step 1b + re = /^(.+?)eed$/; + re2 = /^(.+?)(ed|ing)$/; + if (re.test(w)) { + var fp = re.exec(w); + re = new RegExp(mgr0); + if (re.test(fp[1])) { + re = /.$/; + w = w.replace(re,""); + } + } + else if (re2.test(w)) { + var fp = re2.exec(w); + stem = fp[1]; + re2 = new RegExp(s_v); + if (re2.test(stem)) { + w = stem; + re2 = /(at|bl|iz)$/; + re3 = new RegExp("([^aeiouylsz])\\1$"); + re4 = new RegExp("^" + C + v + "[^aeiouwxy]$"); + if (re2.test(w)) + w = w + "e"; + else if (re3.test(w)) { + re = /.$/; + w = w.replace(re,""); + } + else if (re4.test(w)) + w = w + "e"; + } + } + + // Step 1c + re = /^(.+?)y$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = new RegExp(s_v); + if (re.test(stem)) + w = stem + "i"; + } + + // Step 2 + re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + suffix = fp[2]; + re = new RegExp(mgr0); + if (re.test(stem)) + w = stem + step2list[suffix]; + } + + // Step 3 + re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + suffix = fp[2]; + re = new RegExp(mgr0); + if (re.test(stem)) + w = stem + step3list[suffix]; + } + + // Step 4 + re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/; + re2 = /^(.+?)(s|t)(ion)$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = new RegExp(mgr1); + if (re.test(stem)) + w = stem; + } + else if (re2.test(w)) { + var fp = re2.exec(w); + stem = fp[1] + fp[2]; + re2 = new RegExp(mgr1); + if (re2.test(stem)) + w = stem; + } + + // Step 5 + re = /^(.+?)e$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = new RegExp(mgr1); + re2 = new RegExp(meq1); + re3 = new RegExp("^" + C + v + "[^aeiouwxy]$"); + if (re.test(stem) || (re2.test(stem) && !(re3.test(stem)))) + w = stem; + } + re = /ll$/; + re2 = new RegExp(mgr1); + if (re.test(w) && re2.test(w)) { + re = /.$/; + w = w.replace(re,""); + } + + // and turn initial Y back to y + if (firstch == "y") + w = firstch.toLowerCase() + w.substr(1); + return w; + } +} + diff --git a/docs/_static/minus.png b/docs/_static/minus.png new file mode 100644 index 00000000..d96755fd Binary files /dev/null and b/docs/_static/minus.png differ diff --git a/docs/_static/plot_directive.css b/docs/_static/plot_directive.css new file mode 100644 index 00000000..d45593c9 --- /dev/null +++ b/docs/_static/plot_directive.css @@ -0,0 +1,16 @@ +/* + * plot_directive.css + * ~~~~~~~~~~~~ + * + * Stylesheet controlling images created using the `plot` directive within + * Sphinx. + * + * :copyright: Copyright 2020-* by the Matplotlib development team. + * :license: Matplotlib, see LICENSE for details. + * + */ + +img.plot-directive { + border: 0; + max-width: 100%; +} diff --git a/docs/_static/plus.png b/docs/_static/plus.png new file mode 100644 index 00000000..7107cec9 Binary files /dev/null and b/docs/_static/plus.png differ diff --git a/docs/_static/pygments.css b/docs/_static/pygments.css new file mode 100644 index 00000000..84ab3030 --- /dev/null +++ b/docs/_static/pygments.css @@ -0,0 +1,75 @@ +pre { line-height: 125%; } +td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } +span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } +td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +.highlight .hll { background-color: #ffffcc } +.highlight { background: #f8f8f8; } +.highlight .c { color: #3D7B7B; font-style: italic } /* Comment */ +.highlight .err { border: 1px solid #FF0000 } /* Error */ +.highlight .k { color: #008000; font-weight: bold } /* Keyword */ +.highlight .o { color: #666666 } /* Operator */ +.highlight .ch { color: #3D7B7B; font-style: italic } /* Comment.Hashbang */ +.highlight .cm { color: #3D7B7B; font-style: italic } /* Comment.Multiline */ +.highlight .cp { color: #9C6500 } /* Comment.Preproc */ +.highlight .cpf { color: #3D7B7B; font-style: italic } /* Comment.PreprocFile */ +.highlight .c1 { color: #3D7B7B; font-style: italic } /* Comment.Single */ +.highlight .cs { color: #3D7B7B; font-style: italic } /* Comment.Special */ +.highlight .gd { color: #A00000 } /* Generic.Deleted */ +.highlight .ge { font-style: italic } /* Generic.Emph */ +.highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */ +.highlight .gr { color: #E40000 } /* Generic.Error */ +.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ +.highlight .gi { color: #008400 } /* Generic.Inserted */ +.highlight .go { color: #717171 } /* Generic.Output */ +.highlight .gp { color: #000080; font-weight: bold } /* Generic.Prompt */ +.highlight .gs { font-weight: bold } /* Generic.Strong */ +.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ +.highlight .gt { color: #0044DD } /* Generic.Traceback */ +.highlight .kc { color: #008000; font-weight: bold } /* Keyword.Constant */ +.highlight .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */ +.highlight .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */ +.highlight .kp { color: #008000 } /* Keyword.Pseudo */ +.highlight .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */ +.highlight .kt { color: #B00040 } /* Keyword.Type */ +.highlight .m { color: #666666 } /* Literal.Number */ +.highlight .s { color: #BA2121 } /* Literal.String */ +.highlight .na { color: #687822 } /* Name.Attribute */ +.highlight .nb { color: #008000 } /* Name.Builtin */ +.highlight .nc { color: #0000FF; font-weight: bold } /* Name.Class */ +.highlight .no { color: #880000 } /* Name.Constant */ +.highlight .nd { color: #AA22FF } /* Name.Decorator */ +.highlight .ni { color: #717171; font-weight: bold } /* Name.Entity */ +.highlight .ne { color: #CB3F38; font-weight: bold } /* Name.Exception */ +.highlight .nf { color: #0000FF } /* Name.Function */ +.highlight .nl { color: #767600 } /* Name.Label */ +.highlight .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */ +.highlight .nt { color: #008000; font-weight: bold } /* Name.Tag */ +.highlight .nv { color: #19177C } /* Name.Variable */ +.highlight .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */ +.highlight .w { color: #bbbbbb } /* Text.Whitespace */ +.highlight .mb { color: #666666 } /* Literal.Number.Bin */ +.highlight .mf { color: #666666 } /* Literal.Number.Float */ +.highlight .mh { color: #666666 } /* Literal.Number.Hex */ +.highlight .mi { color: #666666 } /* Literal.Number.Integer */ +.highlight .mo { color: #666666 } /* Literal.Number.Oct */ +.highlight .sa { color: #BA2121 } /* Literal.String.Affix */ +.highlight .sb { color: #BA2121 } /* Literal.String.Backtick */ +.highlight .sc { color: #BA2121 } /* Literal.String.Char */ +.highlight .dl { color: #BA2121 } /* Literal.String.Delimiter */ +.highlight .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */ +.highlight .s2 { color: #BA2121 } /* Literal.String.Double */ +.highlight .se { color: #AA5D1F; font-weight: bold } /* Literal.String.Escape */ +.highlight .sh { color: #BA2121 } /* Literal.String.Heredoc */ +.highlight .si { color: #A45A77; font-weight: bold } /* Literal.String.Interpol */ +.highlight .sx { color: #008000 } /* Literal.String.Other */ +.highlight .sr { color: #A45A77 } /* Literal.String.Regex */ +.highlight .s1 { color: #BA2121 } /* Literal.String.Single */ +.highlight .ss { color: #19177C } /* Literal.String.Symbol */ +.highlight .bp { color: #008000 } /* Name.Builtin.Pseudo */ +.highlight .fm { color: #0000FF } /* Name.Function.Magic */ +.highlight .vc { color: #19177C } /* Name.Variable.Class */ +.highlight .vg { color: #19177C } /* Name.Variable.Global */ +.highlight .vi { color: #19177C } /* Name.Variable.Instance */ +.highlight .vm { color: #19177C } /* Name.Variable.Magic */ +.highlight .il { color: #666666 } /* Literal.Number.Integer.Long */ \ No newline at end of file diff --git a/docs/_static/searchtools.js b/docs/_static/searchtools.js new file mode 100644 index 00000000..2c774d17 --- /dev/null +++ b/docs/_static/searchtools.js @@ -0,0 +1,632 @@ +/* + * Sphinx JavaScript utilities for the full-text search. + */ +"use strict"; + +/** + * Simple result scoring code. + */ +if (typeof Scorer === "undefined") { + var Scorer = { + // Implement the following function to further tweak the score for each result + // The function takes a result array [docname, title, anchor, descr, score, filename] + // and returns the new score. + /* + score: result => { + const [docname, title, anchor, descr, score, filename, kind] = result + return score + }, + */ + + // query matches the full name of an object + objNameMatch: 11, + // or matches in the last dotted part of the object name + objPartialMatch: 6, + // Additive scores depending on the priority of the object + objPrio: { + 0: 15, // used to be importantResults + 1: 5, // used to be objectResults + 2: -5, // used to be unimportantResults + }, + // Used when the priority is not in the mapping. + objPrioDefault: 0, + + // query found in title + title: 15, + partialTitle: 7, + // query found in terms + term: 5, + partialTerm: 2, + }; +} + +// Global search result kind enum, used by themes to style search results. +class SearchResultKind { + static get index() { return "index"; } + static get object() { return "object"; } + static get text() { return "text"; } + static get title() { return "title"; } +} + +const _removeChildren = (element) => { + while (element && element.lastChild) element.removeChild(element.lastChild); +}; + +/** + * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping + */ +const _escapeRegExp = (string) => + string.replace(/[.*+\-?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string + +const _displayItem = (item, searchTerms, highlightTerms) => { + const docBuilder = DOCUMENTATION_OPTIONS.BUILDER; + const docFileSuffix = DOCUMENTATION_OPTIONS.FILE_SUFFIX; + const docLinkSuffix = DOCUMENTATION_OPTIONS.LINK_SUFFIX; + const showSearchSummary = DOCUMENTATION_OPTIONS.SHOW_SEARCH_SUMMARY; + const contentRoot = document.documentElement.dataset.content_root; + + const [docName, title, anchor, descr, score, _filename, kind] = item; + + let listItem = document.createElement("li"); + // Add a class representing the item's type: + // can be used by a theme's CSS selector for styling + // See SearchResultKind for the class names. + listItem.classList.add(`kind-${kind}`); + let requestUrl; + let linkUrl; + if (docBuilder === "dirhtml") { + // dirhtml builder + let dirname = docName + "/"; + if (dirname.match(/\/index\/$/)) + dirname = dirname.substring(0, dirname.length - 6); + else if (dirname === "index/") dirname = ""; + requestUrl = contentRoot + dirname; + linkUrl = requestUrl; + } else { + // normal html builders + requestUrl = contentRoot + docName + docFileSuffix; + linkUrl = docName + docLinkSuffix; + } + let linkEl = listItem.appendChild(document.createElement("a")); + linkEl.href = linkUrl + anchor; + linkEl.dataset.score = score; + linkEl.innerHTML = title; + if (descr) { + listItem.appendChild(document.createElement("span")).innerHTML = + " (" + descr + ")"; + // highlight search terms in the description + if (SPHINX_HIGHLIGHT_ENABLED) // set in sphinx_highlight.js + highlightTerms.forEach((term) => _highlightText(listItem, term, "highlighted")); + } + else if (showSearchSummary) + fetch(requestUrl) + .then((responseData) => responseData.text()) + .then((data) => { + if (data) + listItem.appendChild( + Search.makeSearchSummary(data, searchTerms, anchor) + ); + // highlight search terms in the summary + if (SPHINX_HIGHLIGHT_ENABLED) // set in sphinx_highlight.js + highlightTerms.forEach((term) => _highlightText(listItem, term, "highlighted")); + }); + Search.output.appendChild(listItem); +}; +const _finishSearch = (resultCount) => { + Search.stopPulse(); + Search.title.innerText = _("Search Results"); + if (!resultCount) + Search.status.innerText = Documentation.gettext( + "Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories." + ); + else + Search.status.innerText = Documentation.ngettext( + "Search finished, found one page matching the search query.", + "Search finished, found ${resultCount} pages matching the search query.", + resultCount, + ).replace('${resultCount}', resultCount); +}; +const _displayNextItem = ( + results, + resultCount, + searchTerms, + highlightTerms, +) => { + // results left, load the summary and display it + // this is intended to be dynamic (don't sub resultsCount) + if (results.length) { + _displayItem(results.pop(), searchTerms, highlightTerms); + setTimeout( + () => _displayNextItem(results, resultCount, searchTerms, highlightTerms), + 5 + ); + } + // search finished, update title and status message + else _finishSearch(resultCount); +}; +// Helper function used by query() to order search results. +// Each input is an array of [docname, title, anchor, descr, score, filename, kind]. +// Order the results by score (in opposite order of appearance, since the +// `_displayNextItem` function uses pop() to retrieve items) and then alphabetically. +const _orderResultsByScoreThenName = (a, b) => { + const leftScore = a[4]; + const rightScore = b[4]; + if (leftScore === rightScore) { + // same score: sort alphabetically + const leftTitle = a[1].toLowerCase(); + const rightTitle = b[1].toLowerCase(); + if (leftTitle === rightTitle) return 0; + return leftTitle > rightTitle ? -1 : 1; // inverted is intentional + } + return leftScore > rightScore ? 1 : -1; +}; + +/** + * Default splitQuery function. Can be overridden in ``sphinx.search`` with a + * custom function per language. + * + * The regular expression works by splitting the string on consecutive characters + * that are not Unicode letters, numbers, underscores, or emoji characters. + * This is the same as ``\W+`` in Python, preserving the surrogate pair area. + */ +if (typeof splitQuery === "undefined") { + var splitQuery = (query) => query + .split(/[^\p{Letter}\p{Number}_\p{Emoji_Presentation}]+/gu) + .filter(term => term) // remove remaining empty strings +} + +/** + * Search Module + */ +const Search = { + _index: null, + _queued_query: null, + _pulse_status: -1, + + htmlToText: (htmlString, anchor) => { + const htmlElement = new DOMParser().parseFromString(htmlString, 'text/html'); + for (const removalQuery of [".headerlink", "script", "style"]) { + htmlElement.querySelectorAll(removalQuery).forEach((el) => { el.remove() }); + } + if (anchor) { + const anchorContent = htmlElement.querySelector(`[role="main"] ${anchor}`); + if (anchorContent) return anchorContent.textContent; + + console.warn( + `Anchored content block not found. Sphinx search tries to obtain it via DOM query '[role=main] ${anchor}'. Check your theme or template.` + ); + } + + // if anchor not specified or not found, fall back to main content + const docContent = htmlElement.querySelector('[role="main"]'); + if (docContent) return docContent.textContent; + + console.warn( + "Content block not found. Sphinx search tries to obtain it via DOM query '[role=main]'. Check your theme or template." + ); + return ""; + }, + + init: () => { + const query = new URLSearchParams(window.location.search).get("q"); + document + .querySelectorAll('input[name="q"]') + .forEach((el) => (el.value = query)); + if (query) Search.performSearch(query); + }, + + loadIndex: (url) => + (document.body.appendChild(document.createElement("script")).src = url), + + setIndex: (index) => { + Search._index = index; + if (Search._queued_query !== null) { + const query = Search._queued_query; + Search._queued_query = null; + Search.query(query); + } + }, + + hasIndex: () => Search._index !== null, + + deferQuery: (query) => (Search._queued_query = query), + + stopPulse: () => (Search._pulse_status = -1), + + startPulse: () => { + if (Search._pulse_status >= 0) return; + + const pulse = () => { + Search._pulse_status = (Search._pulse_status + 1) % 4; + Search.dots.innerText = ".".repeat(Search._pulse_status); + if (Search._pulse_status >= 0) window.setTimeout(pulse, 500); + }; + pulse(); + }, + + /** + * perform a search for something (or wait until index is loaded) + */ + performSearch: (query) => { + // create the required interface elements + const searchText = document.createElement("h2"); + searchText.textContent = _("Searching"); + const searchSummary = document.createElement("p"); + searchSummary.classList.add("search-summary"); + searchSummary.innerText = ""; + const searchList = document.createElement("ul"); + searchList.setAttribute("role", "list"); + searchList.classList.add("search"); + + const out = document.getElementById("search-results"); + Search.title = out.appendChild(searchText); + Search.dots = Search.title.appendChild(document.createElement("span")); + Search.status = out.appendChild(searchSummary); + Search.output = out.appendChild(searchList); + + const searchProgress = document.getElementById("search-progress"); + // Some themes don't use the search progress node + if (searchProgress) { + searchProgress.innerText = _("Preparing search..."); + } + Search.startPulse(); + + // index already loaded, the browser was quick! + if (Search.hasIndex()) Search.query(query); + else Search.deferQuery(query); + }, + + _parseQuery: (query) => { + // stem the search terms and add them to the correct list + const stemmer = new Stemmer(); + const searchTerms = new Set(); + const excludedTerms = new Set(); + const highlightTerms = new Set(); + const objectTerms = new Set(splitQuery(query.toLowerCase().trim())); + splitQuery(query.trim()).forEach((queryTerm) => { + const queryTermLower = queryTerm.toLowerCase(); + + // maybe skip this "word" + // stopwords array is from language_data.js + if ( + stopwords.indexOf(queryTermLower) !== -1 || + queryTerm.match(/^\d+$/) + ) + return; + + // stem the word + let word = stemmer.stemWord(queryTermLower); + // select the correct list + if (word[0] === "-") excludedTerms.add(word.substr(1)); + else { + searchTerms.add(word); + highlightTerms.add(queryTermLower); + } + }); + + if (SPHINX_HIGHLIGHT_ENABLED) { // set in sphinx_highlight.js + localStorage.setItem("sphinx_highlight_terms", [...highlightTerms].join(" ")) + } + + // console.debug("SEARCH: searching for:"); + // console.info("required: ", [...searchTerms]); + // console.info("excluded: ", [...excludedTerms]); + + return [query, searchTerms, excludedTerms, highlightTerms, objectTerms]; + }, + + /** + * execute search (requires search index to be loaded) + */ + _performSearch: (query, searchTerms, excludedTerms, highlightTerms, objectTerms) => { + const filenames = Search._index.filenames; + const docNames = Search._index.docnames; + const titles = Search._index.titles; + const allTitles = Search._index.alltitles; + const indexEntries = Search._index.indexentries; + + // Collect multiple result groups to be sorted separately and then ordered. + // Each is an array of [docname, title, anchor, descr, score, filename, kind]. + const normalResults = []; + const nonMainIndexResults = []; + + _removeChildren(document.getElementById("search-progress")); + + const queryLower = query.toLowerCase().trim(); + for (const [title, foundTitles] of Object.entries(allTitles)) { + if (title.toLowerCase().trim().includes(queryLower) && (queryLower.length >= title.length/2)) { + for (const [file, id] of foundTitles) { + const score = Math.round(Scorer.title * queryLower.length / title.length); + const boost = titles[file] === title ? 1 : 0; // add a boost for document titles + normalResults.push([ + docNames[file], + titles[file] !== title ? `${titles[file]} > ${title}` : title, + id !== null ? "#" + id : "", + null, + score + boost, + filenames[file], + SearchResultKind.title, + ]); + } + } + } + + // search for explicit entries in index directives + for (const [entry, foundEntries] of Object.entries(indexEntries)) { + if (entry.includes(queryLower) && (queryLower.length >= entry.length/2)) { + for (const [file, id, isMain] of foundEntries) { + const score = Math.round(100 * queryLower.length / entry.length); + const result = [ + docNames[file], + titles[file], + id ? "#" + id : "", + null, + score, + filenames[file], + SearchResultKind.index, + ]; + if (isMain) { + normalResults.push(result); + } else { + nonMainIndexResults.push(result); + } + } + } + } + + // lookup as object + objectTerms.forEach((term) => + normalResults.push(...Search.performObjectSearch(term, objectTerms)) + ); + + // lookup as search terms in fulltext + normalResults.push(...Search.performTermsSearch(searchTerms, excludedTerms)); + + // let the scorer override scores with a custom scoring function + if (Scorer.score) { + normalResults.forEach((item) => (item[4] = Scorer.score(item))); + nonMainIndexResults.forEach((item) => (item[4] = Scorer.score(item))); + } + + // Sort each group of results by score and then alphabetically by name. + normalResults.sort(_orderResultsByScoreThenName); + nonMainIndexResults.sort(_orderResultsByScoreThenName); + + // Combine the result groups in (reverse) order. + // Non-main index entries are typically arbitrary cross-references, + // so display them after other results. + let results = [...nonMainIndexResults, ...normalResults]; + + // remove duplicate search results + // note the reversing of results, so that in the case of duplicates, the highest-scoring entry is kept + let seen = new Set(); + results = results.reverse().reduce((acc, result) => { + let resultStr = result.slice(0, 4).concat([result[5]]).map(v => String(v)).join(','); + if (!seen.has(resultStr)) { + acc.push(result); + seen.add(resultStr); + } + return acc; + }, []); + + return results.reverse(); + }, + + query: (query) => { + const [searchQuery, searchTerms, excludedTerms, highlightTerms, objectTerms] = Search._parseQuery(query); + const results = Search._performSearch(searchQuery, searchTerms, excludedTerms, highlightTerms, objectTerms); + + // for debugging + //Search.lastresults = results.slice(); // a copy + // console.info("search results:", Search.lastresults); + + // print the results + _displayNextItem(results, results.length, searchTerms, highlightTerms); + }, + + /** + * search for object names + */ + performObjectSearch: (object, objectTerms) => { + const filenames = Search._index.filenames; + const docNames = Search._index.docnames; + const objects = Search._index.objects; + const objNames = Search._index.objnames; + const titles = Search._index.titles; + + const results = []; + + const objectSearchCallback = (prefix, match) => { + const name = match[4] + const fullname = (prefix ? prefix + "." : "") + name; + const fullnameLower = fullname.toLowerCase(); + if (fullnameLower.indexOf(object) < 0) return; + + let score = 0; + const parts = fullnameLower.split("."); + + // check for different match types: exact matches of full name or + // "last name" (i.e. last dotted part) + if (fullnameLower === object || parts.slice(-1)[0] === object) + score += Scorer.objNameMatch; + else if (parts.slice(-1)[0].indexOf(object) > -1) + score += Scorer.objPartialMatch; // matches in last name + + const objName = objNames[match[1]][2]; + const title = titles[match[0]]; + + // If more than one term searched for, we require other words to be + // found in the name/title/description + const otherTerms = new Set(objectTerms); + otherTerms.delete(object); + if (otherTerms.size > 0) { + const haystack = `${prefix} ${name} ${objName} ${title}`.toLowerCase(); + if ( + [...otherTerms].some((otherTerm) => haystack.indexOf(otherTerm) < 0) + ) + return; + } + + let anchor = match[3]; + if (anchor === "") anchor = fullname; + else if (anchor === "-") anchor = objNames[match[1]][1] + "-" + fullname; + + const descr = objName + _(", in ") + title; + + // add custom score for some objects according to scorer + if (Scorer.objPrio.hasOwnProperty(match[2])) + score += Scorer.objPrio[match[2]]; + else score += Scorer.objPrioDefault; + + results.push([ + docNames[match[0]], + fullname, + "#" + anchor, + descr, + score, + filenames[match[0]], + SearchResultKind.object, + ]); + }; + Object.keys(objects).forEach((prefix) => + objects[prefix].forEach((array) => + objectSearchCallback(prefix, array) + ) + ); + return results; + }, + + /** + * search for full-text terms in the index + */ + performTermsSearch: (searchTerms, excludedTerms) => { + // prepare search + const terms = Search._index.terms; + const titleTerms = Search._index.titleterms; + const filenames = Search._index.filenames; + const docNames = Search._index.docnames; + const titles = Search._index.titles; + + const scoreMap = new Map(); + const fileMap = new Map(); + + // perform the search on the required terms + searchTerms.forEach((word) => { + const files = []; + const arr = [ + { files: terms[word], score: Scorer.term }, + { files: titleTerms[word], score: Scorer.title }, + ]; + // add support for partial matches + if (word.length > 2) { + const escapedWord = _escapeRegExp(word); + if (!terms.hasOwnProperty(word)) { + Object.keys(terms).forEach((term) => { + if (term.match(escapedWord)) + arr.push({ files: terms[term], score: Scorer.partialTerm }); + }); + } + if (!titleTerms.hasOwnProperty(word)) { + Object.keys(titleTerms).forEach((term) => { + if (term.match(escapedWord)) + arr.push({ files: titleTerms[term], score: Scorer.partialTitle }); + }); + } + } + + // no match but word was a required one + if (arr.every((record) => record.files === undefined)) return; + + // found search word in contents + arr.forEach((record) => { + if (record.files === undefined) return; + + let recordFiles = record.files; + if (recordFiles.length === undefined) recordFiles = [recordFiles]; + files.push(...recordFiles); + + // set score for the word in each file + recordFiles.forEach((file) => { + if (!scoreMap.has(file)) scoreMap.set(file, {}); + scoreMap.get(file)[word] = record.score; + }); + }); + + // create the mapping + files.forEach((file) => { + if (!fileMap.has(file)) fileMap.set(file, [word]); + else if (fileMap.get(file).indexOf(word) === -1) fileMap.get(file).push(word); + }); + }); + + // now check if the files don't contain excluded terms + const results = []; + for (const [file, wordList] of fileMap) { + // check if all requirements are matched + + // as search terms with length < 3 are discarded + const filteredTermCount = [...searchTerms].filter( + (term) => term.length > 2 + ).length; + if ( + wordList.length !== searchTerms.size && + wordList.length !== filteredTermCount + ) + continue; + + // ensure that none of the excluded terms is in the search result + if ( + [...excludedTerms].some( + (term) => + terms[term] === file || + titleTerms[term] === file || + (terms[term] || []).includes(file) || + (titleTerms[term] || []).includes(file) + ) + ) + break; + + // select one (max) score for the file. + const score = Math.max(...wordList.map((w) => scoreMap.get(file)[w])); + // add result to the result list + results.push([ + docNames[file], + titles[file], + "", + null, + score, + filenames[file], + SearchResultKind.text, + ]); + } + return results; + }, + + /** + * helper function to return a node containing the + * search summary for a given text. keywords is a list + * of stemmed words. + */ + makeSearchSummary: (htmlText, keywords, anchor) => { + const text = Search.htmlToText(htmlText, anchor); + if (text === "") return null; + + const textLower = text.toLowerCase(); + const actualStartPosition = [...keywords] + .map((k) => textLower.indexOf(k.toLowerCase())) + .filter((i) => i > -1) + .slice(-1)[0]; + const startWithContext = Math.max(actualStartPosition - 120, 0); + + const top = startWithContext === 0 ? "" : "..."; + const tail = startWithContext + 240 < text.length ? "..." : ""; + + let summary = document.createElement("p"); + summary.classList.add("context"); + summary.textContent = top + text.substr(startWithContext, 240).trim() + tail; + + return summary; + }, +}; + +_ready(Search.init); diff --git a/docs/_static/sphinx_highlight.js b/docs/_static/sphinx_highlight.js new file mode 100644 index 00000000..8a96c69a --- /dev/null +++ b/docs/_static/sphinx_highlight.js @@ -0,0 +1,154 @@ +/* Highlighting utilities for Sphinx HTML documentation. */ +"use strict"; + +const SPHINX_HIGHLIGHT_ENABLED = true + +/** + * highlight a given string on a node by wrapping it in + * span elements with the given class name. + */ +const _highlight = (node, addItems, text, className) => { + if (node.nodeType === Node.TEXT_NODE) { + const val = node.nodeValue; + const parent = node.parentNode; + const pos = val.toLowerCase().indexOf(text); + if ( + pos >= 0 && + !parent.classList.contains(className) && + !parent.classList.contains("nohighlight") + ) { + let span; + + const closestNode = parent.closest("body, svg, foreignObject"); + const isInSVG = closestNode && closestNode.matches("svg"); + if (isInSVG) { + span = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); + } else { + span = document.createElement("span"); + span.classList.add(className); + } + + span.appendChild(document.createTextNode(val.substr(pos, text.length))); + const rest = document.createTextNode(val.substr(pos + text.length)); + parent.insertBefore( + span, + parent.insertBefore( + rest, + node.nextSibling + ) + ); + node.nodeValue = val.substr(0, pos); + /* There may be more occurrences of search term in this node. So call this + * function recursively on the remaining fragment. + */ + _highlight(rest, addItems, text, className); + + if (isInSVG) { + const rect = document.createElementNS( + "http://www.w3.org/2000/svg", + "rect" + ); + const bbox = parent.getBBox(); + rect.x.baseVal.value = bbox.x; + rect.y.baseVal.value = bbox.y; + rect.width.baseVal.value = bbox.width; + rect.height.baseVal.value = bbox.height; + rect.setAttribute("class", className); + addItems.push({ parent: parent, target: rect }); + } + } + } else if (node.matches && !node.matches("button, select, textarea")) { + node.childNodes.forEach((el) => _highlight(el, addItems, text, className)); + } +}; +const _highlightText = (thisNode, text, className) => { + let addItems = []; + _highlight(thisNode, addItems, text, className); + addItems.forEach((obj) => + obj.parent.insertAdjacentElement("beforebegin", obj.target) + ); +}; + +/** + * Small JavaScript module for the documentation. + */ +const SphinxHighlight = { + + /** + * highlight the search words provided in localstorage in the text + */ + highlightSearchWords: () => { + if (!SPHINX_HIGHLIGHT_ENABLED) return; // bail if no highlight + + // get and clear terms from localstorage + const url = new URL(window.location); + const highlight = + localStorage.getItem("sphinx_highlight_terms") + || url.searchParams.get("highlight") + || ""; + localStorage.removeItem("sphinx_highlight_terms") + url.searchParams.delete("highlight"); + window.history.replaceState({}, "", url); + + // get individual terms from highlight string + const terms = highlight.toLowerCase().split(/\s+/).filter(x => x); + if (terms.length === 0) return; // nothing to do + + // There should never be more than one element matching "div.body" + const divBody = document.querySelectorAll("div.body"); + const body = divBody.length ? divBody[0] : document.querySelector("body"); + window.setTimeout(() => { + terms.forEach((term) => _highlightText(body, term, "highlighted")); + }, 10); + + const searchBox = document.getElementById("searchbox"); + if (searchBox === null) return; + searchBox.appendChild( + document + .createRange() + .createContextualFragment( + '" + ) + ); + }, + + /** + * helper function to hide the search marks again + */ + hideSearchWords: () => { + document + .querySelectorAll("#searchbox .highlight-link") + .forEach((el) => el.remove()); + document + .querySelectorAll("span.highlighted") + .forEach((el) => el.classList.remove("highlighted")); + localStorage.removeItem("sphinx_highlight_terms") + }, + + initEscapeListener: () => { + // only install a listener if it is really needed + if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) return; + + document.addEventListener("keydown", (event) => { + // bail for input elements + if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return; + // bail with special keys + if (event.shiftKey || event.altKey || event.ctrlKey || event.metaKey) return; + if (DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS && (event.key === "Escape")) { + SphinxHighlight.hideSearchWords(); + event.preventDefault(); + } + }); + }, +}; + +_ready(() => { + /* Do not call highlightSearchWords() when we are on the search page. + * It will highlight words from the *previous* search query. + */ + if (typeof Search === "undefined") SphinxHighlight.highlightSearchWords(); + SphinxHighlight.initEscapeListener(); +}); diff --git a/docs/adv-args.html b/docs/adv-args.html new file mode 100644 index 00000000..72d707bf --- /dev/null +++ b/docs/adv-args.html @@ -0,0 +1,205 @@ + + + + + + + + + Specification of FFmpeg Argument dict ffmpeg_args — python-ffmpegio 0.11.0 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Specification of FFmpeg Argument dict ffmpeg_args

+

FFmpeg command can be invoked directly with ffmpegio.ffmpegprocess.run() or +ffmpegio.ffmpegprocess.Popen (see the reference page +for the details). Both of them fully support the FFmpeg command line option +arguments, which can be specified via as subprocess via ffmpeg_args +argument, which may be supplied as a string or a list of strings to be compatible +with subprocess in a plain dict object.

+

The FFmpeg command line options structure:

+
ffmpeg [global_options] {[input_file_options] -i input_url} ... \
+    {[output_file_options] output_url} ...
+
+
+

All the options and urls are mapped to ffmpeg_args by:

+
ffmpeg_args = {
+    "inputs": [(input_url, input_file_options), ...],
+    "outputs": [(output_url, output_file_options), ...],
+    "global_options": global_options,
+}
+
+
+

Any Python sequence types may be used in place of the tuples are lists in the above definition.

+

input_file_options, output_file_options, and global_options are optional. If +URL does not require any options, set its options to None. If no global options, the +"global_options" dict entry may be omitted or set to None.

+

To specify options, each set of options is a dict with option keys as the dict keys without the +leading dash (-). For stream-specific options, the key shall include the full stream specifiers. For +example, use "b:v" as the dict key to specify the video bitrate.

+

Option values may be given as any Python type, so long as it can be converted to str at the +time of the subprocess invocation. If an option does not take any values, then use None. For +any option which can be defined multiple times (e.g., map), specify its value as a sequence +with each of its elements defining a value for each FFmpeg option. Another exception are the filters +(vf, af, and filter_complex) which values may be given with special option +value structure (to be covered later).

+

All defined options are passed unchecked to FFmpeg.

+
+

Examples

+

First, here are how to set up some of the examples in FFmpeg Documentation +for the ffmpegio:

+
# To set the video bitrate of the output file to 64 kbit/s:
+#   ffmpeg -i input.avi -b:v 64k -bufsize 64k output.avi
+ffmpeg_args = {
+    "inputs": [("input.avi", None)],
+    "outputs": [("output.avi", {"b:v": "64k", "bufsize": "64k"})],
+}
+
+# To force the frame rate of the input file (valid for raw formats only) to 1 fps and
+# the frame rate of the output file to 24 fps:
+#   ffmpeg -r 1 -i input.m2v -r 24 output.avi
+ffmpeg_args = {
+    "inputs": [("input.avi", {"r": 1})],
+    "outputs": [("output.avi", {"r": 24})],
+}
+
+# automatic stream selection
+#   ffmpeg -i A.avi -i B.mp4 out1.mkv out2.wav -map 1:a -c:a copy out3.mov
+ffmpeg_args = {
+    "inputs": [("A.avi", None), ("B.mp4", None)],
+    "outputs": [
+        ("out1.mkv", None),
+        ("out2.wav", None),
+        ("out3.mov", {"map": "1:a", "c:a": "copy"}),
+    ],
+}
+
+# unlabeled filtergraph outputs
+#   ffmpeg -i A.avi -i C.mkv -i B.mp4 -filter_complex "overlay" out1.mp4 out2.srt
+ffmpeg_args = {
+    "inputs": [("A.avi", None), ("C.mkv", None), ("B.mp4", None)],
+    "outputs": [
+        ("out1.mp4", None),
+        ("out2.srt", None),
+    ],
+    "global_options": {"filter_complex": "overlay"}
+}
+
+# labeled filtergraph outputs
+#   ffmpeg -i A.avi -i B.mp4 -i C.mkv -filter_complex "[1:v]hue=s=0[outv];overlay;aresample" \
+#      -map '[outv]' -an        out1.mp4 \
+#                               out2.mkv \
+#      -map '[outv]' -map 1:a:0 out3.mkv
+ffmpeg_args = {
+    "inputs": [("A.avi", None), ("B.mp4", None), ("C.mkv", None)],
+    "outputs": [
+        ("out1.mp4", {"map": "[outv]", "an": None}),
+        ("out2.mkv", None),
+        ("out3.mkv", {"map": ("[outv]", "1:a:0")}),
+    ],
+    "global_options": {"filter_complex": "[1:v]hue=s=0[outv];overlay;aresample"}
+}
+
+
+
+
+

FFmpeg FilterGraph Class Specification

+

TBD

+
+
+ + +
+
+
+ +
+ +
+

© Copyright 2021-2022, Takeshi (Kesh) Ikuma, Louisiana State University Health Sciences Center.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/adv-ffmpeg.html b/docs/adv-ffmpeg.html new file mode 100644 index 00000000..731f8bf0 --- /dev/null +++ b/docs/adv-ffmpeg.html @@ -0,0 +1,313 @@ + + + + + + + + + ffmpegio.ffmpegprocess: Direct invocation of FFmpeg subprocess — python-ffmpegio 0.11.0 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + +
  • + View page source +
  • +
+
+
+
+
+ +
+

ffmpegio.ffmpegprocess: Direct invocation of FFmpeg subprocess

+

Instead of indirectly calling FFmpeg with ffmpegio’s Basic I/O Functions, +you can directly invoke a FFmpeg subprocess with ffmpegio.ffmpegprocess module, +which mocks Python’s builtin subprocess module.

+ + + + + + + + + + + + +

ffmpegio.ffmpegprocess.run

run FFmpeg subprocess with standard pipes with a single transaction

ffmpegio.ffmpegprocess.run_two_pass

run FFmpeg subprocess with standard pipes with a single transaction twice for 2-pass encoding

ffmpegio.ffmpegprocess.Popen

Execute FFmpeg in a new process.

+

While both ffmpegio.ffmpegprocess.run() and ffmpegio.ffmpegprocess.Popen +constructor accepts the args argument of Python’s subprocess.run() and +subprocess.Popen constructor, the FFmpeg command argument can also be specified +with a Python dict: see its specification page for the details.

+

ffmpegio.ffmpegprocess.run_two_pass() runs FFmpeg twice to perform two-pass video +encoding. The audio encoding is automatically disabled during the first pass by default. It +also offers a finer control of which options to turn on/off during the first pass.

+
+

ffmpegio.ffmpegprocess Module Reference

+
+
+ffmpegio.ffmpegprocess.run(ffmpeg_args, *, hide_banner=True, progress=None, overwrite=None, capture_log=None, stdin=None, stdout=None, stderr=None, input=None, **other_popen_kwargs)
+

run FFmpeg subprocess with standard pipes with a single transaction

+
+
Parameters:
+
    +
  • ffmpeg_args (dict) – FFmpeg argument options

  • +
  • hide_banner (bool, optional) – False to output ffmpeg banner in stderr, defaults to True

  • +
  • progress (callable object, optional) –

    progress callback function, defaults to None. This function +takes two arguments:

    +
    +

    progress(data:dict, done:bool) -> None

    +
    +

  • +
  • overwrite (bool, optional) – True to overwrite if output url exists, defaults to None +(auto-select)

  • +
  • capture_log (bool, optional) – True to capture log messages on stderr, False to send +logs to console, defaults to None (no show/capture)

  • +
  • stdin (readable file-like object, optional) – source file object, defaults to None

  • +
  • stdout (writable file-like object, optional) – sink file object, defaults to None

  • +
  • stderr (writable file-like object, optional) – file to log ffmpeg messages, defaults to None

  • +
  • input (bytes-convertible object, optional) – input data buffer must be given if FFmpeg is configured to receive +data stream from Python. It must be bytes convertible to bytes.

  • +
  • **other_popen_kwargs (dict, optional) – other keyword arguments of Popen, defaults to {}

  • +
+
+
Rparam:
+

completed process

+
+
Return type:
+

subprocess.CompleteProcess

+
+
+
+ +
+
+ffmpegio.ffmpegprocess.run_two_pass(ffmpeg_args, pass1_omits=None, pass1_extras=None, overwrite=None, stdin=None, **other_run_kwargs)
+

run FFmpeg subprocess with standard pipes with a single transaction twice for 2-pass encoding

+
+
Parameters:
+
    +
  • ffmpeg_args (dict) – FFmpeg argument options

  • +
  • pass1_omits (seq(str) or seq(seq(str)) or dict(int:seq(str)) optional) – per-file list of output arguments to ignore in pass 1. If not applicable to every +output file, use a nested dict with int keys to specify which output, +defaults to None (remove ‘c:a’ or ‘acodec’).

  • +
  • pass1_extras (dict(str) or seq(dict(str)) or dict(int:dict(str)), optional) – per-file list of additional output arguments to include in pass 1. If it does +not apply to every output files, use a nested dict with int keys to specify +which output, defaults to None (add ‘an’ if pass1_omits also None)

  • +
  • hide_banner (bool, optional) – False to output ffmpeg banner in stderr, defaults to True

  • +
  • progress (callable object, optional) –

    progress callback function, defaults to None. This function +takes two arguments:

    +
    +

    progress(data:dict, done:bool) -> None

    +
    +

  • +
  • overwrite (bool, optional) – True to overwrite if output url exists, defaults to None +(auto-select)

  • +
  • capture_log (bool, optional) – True to capture log messages on stderr, False to send +logs to console, defaults to None (no show/capture)

  • +
  • stdin (readable file-like object, optional) – source file object, defaults to None

  • +
  • stderr (writable file-like object, optional) – file to log ffmpeg messages, defaults to None

  • +
  • input (bytes-convertible object, optional) – input data buffer must be given if FFmpeg is configured to receive +data stream from Python. It must be bytes convertible to bytes.

  • +
  • **other_popen_kwargs (dict, optional) – other keyword arguments of Popen, defaults to {}

  • +
+
+
Rparam:
+

completed process

+
+
Return type:
+

subprocess.CompleteProcess

+
+
+
+ +
+
+class ffmpegio.ffmpegprocess.Popen(ffmpeg_args, *, hide_banner=True, progress=None, overwrite=None, capture_log=None, stdin=None, stdout=None, stderr=None, on_exit=None, **other_popen_args)
+

Execute FFmpeg in a new process.

+
+
Parameters:
+
    +
  • ffmpeg_args (dict) – FFmpeg arguments

  • +
  • hide_banner (bool, optional) – False to output ffmpeg banner in stderr, defaults to True

  • +
  • progress (Callable, optional) –

    progress callback function, defaults to None. This function +takes two arguments and may return True to terminate execution:

    +
    progress(data:dict, done:bool) -> bool|None
    +
    +
    +

  • +
  • overwrite (bool, optional) – True to overwrite if output url exists, defaults to None +(auto-select)

  • +
  • capture_log (bool, optional) – True to capture log messages on stderr, False to send +logs to console, defaults to None (no show/capture)

  • +
  • stdin (readable file object, optional) – source file object, defaults to None

  • +
  • stdout (writable file object, optional) – sink file object, defaults to None

  • +
  • stderr (writable file object, optional) – file to log ffmpeg messages, defaults to None

  • +
  • on_exit (Callable or seq(Callable), optional) – function(s) to execute when FFmpeg process terminates, defaults to None

  • +
  • **other_popen_args (dict, optional) – other keyword arguments to subprocess.Popen

  • +
+
+
+

If ffmpeg_args calls for input or output to be piped (e.g., url=”-”) then Popen +automatically sets stdin=PIPE or stdout=PIPE. Alternately, a file-stream object could be +specified in the argument for each of stdin, stdout, and stderr +to redirect pipes to existing file streams. If files aren’t already open in Python, +specify their urls in ffmpeg_args instead of using the pipes.

+
+
+ffmpeg_args
+

The FFmpeg args argument as it was passed to Popen

+
+
Type:
+

dict

+
+
+
+ +
+
+kill()
+

Kill the FFmpeg process

+
+ +
+
+send_signal(sig=None, kill_monitor=False)
+

Sends the signal signal to the FFmpeg process

+
+
Parameters:
+
    +
  • sig (int, optional) – signal id, default SIGINT (POSIX) / CTRL_C_EVENT (Windows)

  • +
  • kill_monitor (bool, optional) – True to kill the monitor thread, default False

  • +
+
+
+

Without any argument, send_signal() will perform control-C to initiate +soft-terminate FFmpeg. FFmpeg may output additional frames before exits.

+

Note: Setting kill_monitor=True will block the caller thread until the +FFmpeg terminates.

+
+ +
+
+terminate()
+

Terminate the FFmpeg process

+
+ +
+
+wait(timeout=None)
+

Wait for FFmpeg process to terminate; returns self.returncode

+
+
Parameters:
+

timeout (float, optional) – optional timeout in seconds, defaults to None

+
+
+

For FFmpeg to terminate autonomously, its stdin PIPE must be closed.

+

If the process does not terminate after timeout seconds, raise a TimeoutExpired exception. +It is safe to catch this exception and retry the wait.

+
+ +
+ +
+
+ + +
+
+
+ +
+ +
+

© Copyright 2021-2022, Takeshi (Kesh) Ikuma, Louisiana State University Health Sciences Center.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/analysis.html b/docs/analysis.html new file mode 100644 index 00000000..a306e9b9 --- /dev/null +++ b/docs/analysis.html @@ -0,0 +1,2041 @@ + + + + + + + + + ffmpegio.analyze: Frame Metadata Analysis Module — python-ffmpegio 0.11.0 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

ffmpegio.analyze: Frame Metadata Analysis Module

+

There are a number of FFmpeg filters which analyze video +and audio streams and inject per-frame results into frame metadata to be used in a later stage of +a filtergraph. ffmpegio.analyze.run retrieves the injected metadata by appending metadata +and ametadata filters and logs the frame metadata outputs. You can use either the supplied Python +classes or a custom class, which conforms to MetadataLogger interface to specify the FFmpeg +filter and to log its output.

+
+

Simple examples

+

The following example detects intervals of pure black frames within the first 30 seconds of the video:

+
>>> from ffmpegio import analyze as ffa
+>>> logger, *_ = ffa.run("input.mp4", ffa.BlackDetect(pix_th=0.0), t=30)
+>>> print(logger.output)
+Black(interval=[[0.0, 0.166667]])
+
+
+
    +
  • Assign options (e.g., pix_th) of the underlying FFmpeg analysis filter (e.g., blackdetect) as +keyword options of its logger object (e.g., BlackDetect)

  • +
  • FFmpeg input options (e.g., t) can be assigned as the keyword arguments of run().

  • +
  • The logger output is a namedtuple.

  • +
+

Next example analyzes the audio stream and plot its spectral entropy of the first channel:

+
>>> logger,*_ = ffa.run("input.mp4", ffa.ASpectralStats(measure='entropy'))
+>>> plt.plot(logger.output.time, logger.output.entropy[0])
+
+
+

Finally, multiple loggers can run simultaneously:

+
>>> loggers = [
+...   ffa.AStats(),       # time domain statistics of audio channels
+...   ffa.BBox(),         # bounding box of video frames
+...   ffa.BlackDetect()]  # detect black frame intervals
+...
+>>> ffa.run("input.mp4", *loggers, t=10)
+>>> print(loggers[0].output)
+>>> print(loggers[1].output)
+>>> print(loggers[2].output)
+
+
+
+
+

Available filter loggers

+

Following loggers are currently available as a part of the analyze module

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Type

Python class

FFmpeg filter

Description

audio

APhaseMeter

aphasemeter

Measures phase of input audio

ASpectralStats

aspectralstats

Frequency domain statistical information

AStats

astats

Time domain statistical information

SilenceDetect

silencedetect

Detect silence

video

BBox

bbox

Compute the bounding box

BlackDetect

blackdetect

Detect intervals of black frames

BlackFrame

blackframe

Detect black frames

BlurDetect

blurdetect

Detect blurriness of frames

FreezeDetect

freezedetect

Detect frozen video

PSNR

psnr

Compute peak signal to noise ratio

ScDet

scdet

Detect video scene change

+
+
+

Analyze API Reference

+ + + + + + + + + + + + + + + +

ffmpegio.analyze.run

analyze media streams' frames with FFmpeg filters

ffmpegio.video.detect

detect video frame features

ffmpegio.audio.detect

detect audio stream features

ffmpegio.analyze.MetadataLogger

Abstract class for analyze.run() frame metadata loggers

+
+
+ffmpegio.analyze.run(url, *loggers, references=None, time_units=None, start_at_zero=False, progress=None, show_log=None, **input_options)
+

analyze media streams’ frames with FFmpeg filters

+
+
Parameters:
+
    +
  • url (str) – video file url

  • +
  • *loggers (tuple[MetadataLogger]) – class object with the metadata logging interface

  • +
  • references (seq of str or seq of (str, dict), optional) – reference input urls or pairs of url and input option +dict, defaults to None

  • +
  • ss (int, float, str, optional) – start time to process, defaults to None

  • +
  • t (int, float, str, optional) – duration of data to process, defaults to None

  • +
  • to (int, float, str, optional) – stop processing at this time (ignored if t is also specified), defaults to None

  • +
  • time_units ('seconds', 'frames', 'pts', optional) – units of detected time stamps (not for ss, t, or to), defaults to None (‘seconds’)

  • +
  • start_at_zero (bool, optional) – ignore start time, defaults to False

  • +
  • progress (callable object, optional) – progress callback function, defaults to None

  • +
  • show_log (bool, optional) – True to show FFmpeg log messages on the console, +defaults to None (no show/capture)

  • +
  • **options (dict, optional) – FFmpeg (primary) input options.

  • +
+
+
Returns:
+

logger objects passed in

+
+
Return type:
+

tuple[MetadataLogger]

+
+
+
+ +
+
+ffmpegio.video.detect(url, *features, ss=None, t=None, to=None, start_at_zero=False, time_units=None, progress=None, show_log=None, scene_all_scores=False, **options)
+

detect video frame features

+
+
Parameters:
+
    +
  • url (str) – video file url

  • +
  • *features (tuple, a subset of ('scene', 'black', 'blackframe', 'freeze'), optional) –

    specify frame features to detect:

    + + + + + + + + + + + + + + + + + + + + + + + + + +

    feature

    FFmpeg filter

    description

    ’scene’

    scdet

    Detect video scene change

    ’black’

    blackdetect

    Detect video intervals that are (almost) completely black

    ’blackframe’

    blackframe

    Detect frames that are (almost) completely black

    ’freeze’

    freezedetect

    Detect frozen video

    +

    defaults to include all the features

    +

  • +
  • ss (int, float, str, optional) – start time to process, defaults to None

  • +
  • t (int, float, str, optional) – duration of data to process, defaults to None

  • +
  • to (int, float, str, optional) – stop processing at this time (ignored if t is also specified), defaults to None

  • +
  • start_at_zero (bool, optional) – ignore start time, defaults to False

  • +
  • time_units ('seconds', 'frames', 'pts', optional) – units of detected time stamps (not for ss, t, or to), defaults to None (‘seconds’)

  • +
  • progress (callable object, optional) – progress callback function, defaults to None

  • +
  • show_log (bool, optional) – True to show FFmpeg log messages on the console, +defaults to None (no show/capture)

  • +
  • scene_all_scores (bool, optional) – (only for ‘scene’ feature) True to return scores for all frames, defaults to False

  • +
  • **options (dict, optional) – FFmpeg detector filter options. For a single-feature call, the FFmpeg filter options +of the specified feature can be specified directly as keyword arguments. For a multiple-feature call, +options for each individual FFmpeg filter can be specified with <feature>_options dict keyword argument. +Any other arguments are treated as a common option to all FFmpeg filters. For the available options +for each filter, follow the link on the feature table above to the FFmpeg documentation.

  • +
+
+
Returns:
+

detection outcomes. A namedtuple is returned for each feature in the order specified. +All namedtuple fields contain a list with the element specified as below:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

feature

named tuple field

element type

description

’scene’

’time’

numeric

Timestamp of the frame

’change’

bool

True if scene change detected (only present if scene_all_scores=True)

’score’

’float’

Absolute difference of MAFD of current and previous frame

’mafd’

float

Mean absolute frame difference. See this commentary for detailed discussion of the MAFD.

’black’

’interval’

(numeric, numeric)

Interval of black frames

’blackframe’

’time’

numeric

Timestamp of a black frame

’pblack’

int

Percentage of black pixels

’freeze’

’interval’

(numeric, numeric)

Interval of frozen frames

+

+
+
Return type:
+

tuple of namedtuples

+
+
+

Examples

+
+ +
+
+ffmpegio.audio.detect(url, *features, ss=None, t=None, to=None, start_at_zero=False, time_units=None, progress=None, show_log=None, **options)
+

detect audio stream features

+
+
Parameters:
+
    +
  • url (str) – audio file url

  • +
  • *features (tuple, a subset of ('silence',), optional) –

    specify features to detect:

    + + + + + + + + + + + + + +

    feature

    FFmpeg filter

    description

    ’silence’

    silencedetect

    Detect silence in an audio stream

    +

    defaults to include all the features

    +

  • +
  • ss (int, float, str, optional) – start time to process, defaults to None

  • +
  • t (int, float, str, optional) – duration of data to process, defaults to None

  • +
  • to (int, float, str, optional) – stop processing at this time (ignored if t is also specified), defaults to None

  • +
  • start_at_zero (bool, optional) – ignore start time, defaults to False

  • +
  • time_units ('seconds', 'frames', 'pts', optional) – units of detected time stamps (not for ss, t, or to), defaults to None (‘seconds’)

  • +
  • progress (callable object, optional) – progress callback function, defaults to None

  • +
  • show_log (bool, optional) – True to show FFmpeg log messages on the console, +defaults to None (no show/capture)

  • +
  • **options (dict, optional) – FFmpeg detector filter options. For a single-feature call, the FFmpeg filter options +of the specified feature can be specified directly as keyword arguments. For a multiple-feature call, +options for each individual FFmpeg filter can be specified with <feature>_options dict keyword argument. +Any other arguments are treated as a common option to all FFmpeg filters. For the available options +for each filter, follow the link on the feature table above to the FFmpeg documentation.

  • +
+
+
Returns:
+

detection outcomes. A namedtuple is returned for each feature in the order specified. +All namedtuple fields contain a list with the element specified as below:

+ + + + + + + + + + + + + + + + + + + + +

feature

named tuple field

element type

description

’silence’

’interval’

(numeric, numeric)

(only if mono=False) Silent interval

’chX’

(numeric, numeric)

(only if mono=True) Silent interval of channel X (multiple)

+

+
+
Return type:
+

tuple of namedtuples

+
+
+

Examples

+
+ +
+
+class ffmpegio.analyze.MetadataLogger
+

Abstract class for analyze.run() frame metadata loggers

+
+
+property filter: Filter
+

filter specification expression to be used in FilterGraph

+
+ +
+
+filter_name: str
+

(static) name of the FFmpeg filter to use

+
+ +
+
+log(t, name, key, value)
+

log the metadata

+
+
Parameters:
+
    +
  • t (float|int) – timestamps in seconds, frames, or pts

  • +
  • name (str) – one of the class’ meta_names

  • +
  • key (str | None) – secondary metadata key if found

  • +
  • value (str) – metadata value

  • +
+
+
+

This method is called by analyze.run() if a metadata line begins +with one of the class’ meta_names entry. The log method shall store +the metadata info in a private storage property of the class so they can be +returned later by the output property.

+
+ +
+
+media_type: Literal['video', 'audio']
+

(static) target stream media type

+
+ +
+
+meta_names: Tuple[str]
+

(static) metadata names to be logged

+
+ +
+
+options: dict[str, Any]
+

FFmpeg filter options (value must be stringifiable)

+
+ +
+
+property output: NamedTuple
+

log output as a namedtuple

+
+ +
+
+property ref_in: str | None
+

None)

+
+
Type:
+

stream specifier for reference input url only if applicable (default

+
+
+
+ +
+ +
+
+class ffmpegio.analyze.APhaseMeter(**options)
+

Logger for FFmpeg aphasemeter filter to measure stereo audio phase differences

+
+
Parameters:
+

**options (dict[str, any]) – FFmpeg filter options (see below)

+
+
+
+

FFmpeg aphasemeter filter options

+ + + + + + + + + + + + + + + + + + + + + + + + + +

name

type

description

phasing

bool

mono and out-of-phase detection output (default false)

tolerance

float

phase tolerance for mono detection (from 0 to 1) (default 0). Alias param name: t

angle

float

angle threshold for out-of-phase detection (from 90 to 180) (default 170). Alias param name: a

duration

duration

minimum mono or out-of-phase duration in seconds (default 2). Alias param name: d

+
+
+class Phase(time, value, mono_interval, out_phase_interval)
+

output log namedtuple subclass

+
+
Parameters:
+
    +
  • time (List[float | int])

  • +
  • value (List[float])

  • +
  • mono_interval (List[float | int | None, float | int | None])

  • +
  • out_phase_interval (List[float | int | None, float | int | None])

  • +
+
+
+
+
+mono_interval: List[float | int | None, float | int | None]
+

intervals in which stereo stream is in-phase

+
+ +
+
+out_phase_interval: List[float | int | None, float | int | None]
+

intervals in which stereo stream is out-of-phase

+
+ +
+
+time: List[float | int]
+

timestamps in seconds, frames, or pts

+
+ +
+
+value: List[float]
+

detected phases

+
+ +
+ +
+
+property filter
+

filter specification expression to be used in FilterGraph

+
+ +
+
+filter_name: Literal['aphasemeter'] = 'aphasemeter'
+

(static) name of the FFmpeg filter to use

+
+ +
+
+log(t, name, key, value)
+

log the metadata

+
+
Parameters:
+
    +
  • t (float|int) – timestamps in seconds, frames, or pts

  • +
  • name (str) – one of the class’ meta_names

  • +
  • key (str | None) – secondary metadata key if found

  • +
  • value (str) – metadata value

  • +
+
+
+

This method is called by analyze.run() if a metadata line begins +with one of the class’ meta_names entry. The log method shall store +the metadata info in a private storage property of the class so they can be +returned later by the output property.

+
+ +
+
+media_type: Literal['audio'] = 'audio'
+

(static) target stream media type

+
+ +
+
+meta_names: Tuple[Literal['aphasemeter']] = ('aphasemeter',)
+

(static) metadata names to be logged

+
+ +
+
+property output: Phase
+

log output

+
+ +
+
+ +
+
+class ffmpegio.analyze.ASpectralStats(**options)
+

Logger for FFmpeg aspectralstats filter to measure frequency domain statistics about audio frames

+
+
Parameters:
+

**options (dict[str, any]) – FFmpeg filter options (see below)

+
+
+
+

FFmpeg aspectralstats filter options

+ + + + + + + + + + + + + + + + + + + + + +

name

type

description

win_size

int

set the window size (from 32 to 65536) (default 2048)

win_func

str|int

set window function (see below for the accepted values) (default hann)

overlap

float

set window overlap (from 0 to 1) (default 0.5)

+
+
+

Supported win_func option values

+

The win_func option can be set to any of the following window function by its name or +id:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

name

id

desc

bartlett

4

Bartlett

bhann

11

Bartlett-Hann

bharris

7

Blackman-Harris

blackman

3

Blackman

bnuttall

8

Blackman-Nuttall

bohman

19

Bohman

cauchy

16

Cauchy

dolph

15

Dolph-Chebyshev

flattop

6

Flat-top

gauss

13

Gauss

hamming

2

Hamming

hann

1

Hann

hanning

1

Hanning

lanczos

12

Lanczos

nuttall

10

Nuttall

parzen

17

Parzen

poisson

18

Poisson

rect

0

Rectangular

sine

9

Sine

tukey

14

Tukey

welch

5

Welch

+
+
+filter_name: Literal['aspectralstats'] = 'aspectralstats'
+

(static) name of the FFmpeg filter to use

+
+ +
+
+log(t, name, key, value)
+

log the metadata

+
+
Parameters:
+
    +
  • t (float|int) – timestamps in seconds, frames, or pts

  • +
  • name (str) – one of the class’ meta_names

  • +
  • key (str | None) – secondary metadata key if found

  • +
  • value (str) – metadata value

  • +
+
+
+

This method is called by analyze.run() if a metadata line begins +with one of the class’ meta_names entry. The log method shall store +the metadata info in a private storage property of the class so they can be +returned later by the output property.

+
+ +
+
+media_type: Literal['audio'] = 'audio'
+

(static) target stream media type

+
+ +
+
+meta_names: Tuple[Literal['aspectralstats']] = ('aspectralstats',)
+

(static) metadata names to be logged

+
+ +
+
+property output
+

log output

+

ASpectalStats’ log output is a dynamically composed namedtuple. Each +statistic is stored in its own named field as a dict of +per-channel list of measurements. The dict is +keyed by the audio channel ids (positive int). +One exception is the time field, which is a plain list +of the starting timestamps of analysis windows.

+

Here is the full list of possible fields for FFmpeg v5:

+
    +
  • time

  • +
  • mean

  • +
  • variance

  • +
  • centroid

  • +
  • spread

  • +
  • skewness

  • +
  • kurtosis

  • +
  • entropy

  • +
  • flatness

  • +
  • crest

  • +
  • flux

  • +
  • slope

  • +
  • decrease

  • +
  • rolloff

  • +
+

All the stats are computed in the linear scale (not in dB).

+
+ +
+
+ +
+
+class ffmpegio.analyze.AStats(**options)
+

Logger for FFmpeg astats filter to measure time domain statistics per audio frames

+
+
Parameters:
+

**options (dict[str, any]) – FFmpeg filter options (see below)

+
+
+
+

FFmpeg astats filter options

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

name

type

description

length

float

window length (from 0 to 10) (default 0.05)

metadata

bool

true to inject metadata in the filtergraph (default false)

reset

int

number of frames over which cumulative stats are calculated before being reset (from 0 to INT_MAX) (default 0)

measure_perchannel

str

parameters to measure per channel (default “all”) “none” to disable

measure_overall

str

parameters to measure overall (default “all”) “none” to disable

+
+
+

Measurement parameters

+

Following parameters can be used for measure_perchannel and measure_overall. To specify +multiple parameters, combine them with + (plus) signs. E.g., “DC_offset+Min_level”.

+
    +
  • DC_offset

  • +
  • Min_level

  • +
  • Max_level

  • +
  • Min_difference

  • +
  • Max_difference

  • +
  • Mean_difference

  • +
  • RMS_differenc

  • +
  • Peak_level

  • +
  • RMS_level

  • +
  • RMS_peak

  • +
  • RMS_trough

  • +
  • Crest_factor

  • +
  • Flat_factor

  • +
  • Peak_count

  • +
  • Bit_depth

  • +
  • Dynamic_range

  • +
  • Zero_crossings

  • +
  • Zero_crossings_rate

  • +
  • Noise_floor

  • +
  • Noise_floor_count

  • +
  • Entropy

  • +
  • Number_of_samples

  • +
  • Number_of_NaNs

  • +
  • Number_of_Infs

  • +
  • Number_of_denormals

  • +
+
+
+property filter
+

filter specification expression to be used in FilterGraph

+
+ +
+
+filter_name: Literal['astats'] = 'astats'
+

(static) name of the FFmpeg filter to use

+
+ +
+
+log(t, name, key, value)
+

log the metadata

+
+
Parameters:
+
    +
  • t (float|int) – timestamps in seconds, frames, or pts

  • +
  • name (str) – one of the class’ meta_names

  • +
  • key (str | None) – secondary metadata key if found

  • +
  • value (str) – metadata value

  • +
+
+
+

This method is called by analyze.run() if a metadata line begins +with one of the class’ meta_names entry. The log method shall store +the metadata info in a private storage property of the class so they can be +returned later by the output property.

+

lavfi.astats.1.Number of Infs=0.000000 +lavfi.astats.2.Number of denormals=0.000000 +lavfi.astats.Overall.DC_offset=-0.000003 +lavfi.astats.Overall.Min_level=-0.092316 +lavfi.astats.Overall.Max_level=0.100442

+
+ +
+
+media_type: Literal['audio'] = 'audio'
+

(static) target stream media type

+
+ +
+
+meta_names: Tuple[Literal['astats']] = ('astats',)
+

(static) metadata names to be logged

+
+ +
+
+property output: NamedTuple
+

log output

+

AStats’ log output is a dynamically composed namedtuple. Every field +contains lists of statistics. Except for the time field, which is +a plain list, the fields are a dict, each of which item +keyed by the channel number in int (1, 2, …) or literal +"overall" and contains a list of the statistics +computed at each analysis window. The full list of possible fields +for FFmpeg v5 and its individual stat datatype is shown below:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

field name

datatype

description

time

float|int

timestamps in seconds, frames, or pts

dc_offset

float

DC offset

min_level

float

Min level

max_level

float

Max level

min_difference

float

Min difference

max_difference

float

Max difference

mean_difference

float

Mean difference

rms_difference

float

RMS difference

peak_level

float

Peak level dB

rms_level

float

RMS level dB

rms_peak

float

RMS peak dB

rms_trough

float

RMS trough dB

crest_factor

float

Crest factor

flat_factor

float

Flat factor

peak_count

int

Peak count

noise_floor

float

Noise floor dB

noise_floor_count

int

Noise floor count

entropy

float

Entropy

bit_depth

int

Bit depth (available)

bit_depth2

int

Bit depth (used)

dynamic_range

float

Dynamic range

zero_crossings

float

Zero crossings

zero_crossings_rate

float

Zero crossings rate

number_of_nans

int

Number of NaNs

number_of_infs

int

Number of Infs

number_of_denormals

int

Number of denormals

+
+ +
+
+ +
+
+class ffmpegio.analyze.SilenceDetect(**options)
+

Logger for FFmpeg silencedetect filter to detect silent audio intervals

+
+
Parameters:
+

**options (dict[str, any]) – FFmpeg filter options (see below)

+
+
+
+

FFmpeg silencedetect filter options

+ + + + + + + + + + + + + + + + + + + + + +

name

type

description

noise

double

noise tolerance (from 0 to DBL_MAX) (default 0.001). Alias param name: n

duration

duration

minimum duration in seconds (default 2). Alias param name: d

mono

bool

check each channel separately (default false). Alias param name: m

+
+
+class Silent(interval)
+

output log namedtuple subclass for mono=False (default)

+
+
Parameters:
+

interval (List[float | int | None, float | int | None])

+
+
+
+
+interval: List[float | int | None, float | int | None]
+

pairs of start and end timestamps of frozen frame intervals

+
+ +
+ +
+
+filter_name: Literal['silencedetect'] = 'silencedetect'
+

(static) name of the FFmpeg filter to use

+
+ +
+
+log(t, name, ch, value)
+

log the metadata

+
+
Parameters:
+
    +
  • t (float|int) – timestamps in seconds, frames, or pts

  • +
  • name (str) – one of the class’ meta_names

  • +
  • ch (str | None) – audio channel key

  • +
  • value (str) – metadata value

  • +
+
+
+

This method is called by analyze.run() if a metadata line begins +with one of the class’ meta_names entry. The log method shall store +the metadata info in a private storage property of the class so they can be +returned later by the output property.

+
+ +
+
+media_type: Literal['audio'] = 'audio'
+

(static) target stream media type

+
+ +
+
+meta_names: Tuple[Literal['silence_start', 'silence_end']] = ('silence_start', 'silence_end')
+

(static) metadata names to be logged

+
+ +
+
+property output: Silent | NamedTuple
+

log output

+

If the silentdetect filter is configured with mono=False (default), the returned log is +a SilenceDetect.Silent object.

+

If mono=True, the returned log is a dynamically formed namedtuple of the name SilentPerCh, +each of which field is named ch# (where # is an integer) and contains a list of the +silent intevals of the specified audio channel.

+
+ +
+
+ +
+
+class ffmpegio.analyze.BBox(**options)
+

Logger for FFmpeg bbox filter to compute bounding box for each frame

+
+
Parameters:
+

**options (dict[str, any]) – FFmpeg filter options (see below)

+
+
+
+

FFmpeg bbox filter options

+ + + + + + + + + + + + + + + + + +

name

type

description

min_val

int

minimum luminance value for bounding box (from 0 to 65535) (default 16)

enable

str

support for timeline. See FFmpeg documentation.

+
+
+class BBox(time, position)
+

output log namedtuple subclass

+
+
Parameters:
+
    +
  • time (List[float | int])

  • +
  • position (List[List[int, int, int, int]])

  • +
+
+
+
+
+position: List[List[int, int, int, int]]
+

bbox positions [x0,x1,w,h]

+
+ +
+
+time: List[float | int]
+

timestamps in seconds, frames, or pts

+
+ +
+ +
+
+filter_name: Literal['bbox'] = 'bbox'
+

(static) name of the FFmpeg filter to use

+
+ +
+
+log(t, name, key, value)
+

log the metadata

+
+
Parameters:
+
    +
  • t (float|int) – timestamps in seconds, frames, or pts

  • +
  • name (str) – one of the class’ meta_names

  • +
  • key (str | None) – secondary metadata key if found

  • +
  • value (str) – metadata value

  • +
+
+
+

This method is called by analyze.run() if a metadata line begins +with one of the class’ meta_names entry. The log method shall store +the metadata info in a private storage property of the class so they can be +returned later by the output property.

+
+ +
+
+media_type: Literal['video'] = 'video'
+

(static) target stream media type

+
+ +
+
+meta_names: Tuple[Literal['bbox']] = ('bbox',)
+

(static) metadata names to be logged

+
+ +
+
+property output: BBox
+

log output

+
+ +
+
+ +
+
+class ffmpegio.analyze.BlackDetect(**options)
+

Logger for FFmpeg blackdetect filter to detect video intervals that are (almost) black

+
+
Parameters:
+

**options (dict[str, any]) – FFmpeg filter options (see below)

+
+
+
+

FFmpeg blackdetect filter options

+ + + + + + + + + + + + + + + + + + + + + +

name

type

description

black_min_duration

float

set minimum detected black duration in seconds (from 0 to DBL_MAX) (default 2)

picture_black_ratio_th

float

set the picture black ratio threshold (from 0 to 1) (default 0.98). Alias param name: pic_th

pixel_black_th

float

set the pixel black threshold (from 0 to 1) (default 0.1). Alias param name: pix_th

+
+
+class Black(interval)
+

output log namedtuple subclass

+
+
Parameters:
+

interval (List[float | int | None, float | int | None])

+
+
+
+
+interval: List[float | int | None, float | int | None]
+

pairs of start and end timestamps of black intervals

+
+ +
+ +
+
+filter_name: str = 'blackdetect'
+

(static) name of the FFmpeg filter to use

+
+ +
+
+log(t, name, *_)
+

log the metadata

+
+
Parameters:
+
    +
  • t (float|int) – timestamps in seconds, frames, or pts

  • +
  • name (str) – metadata key

  • +
+
+
+

This method is called by analyze.run() if a metadata line begins +with one of the class’ meta_names entry. The log method shall store +the metadata info in a private storage property of the class so they can be +returned later by the output property.

+
+ +
+
+media_type: Literal['video', 'audio'] = 'video'
+

(static) target stream media type

+
+ +
+
+meta_names: Tuple[str] = ('black_start', 'black_end')
+

(static) metadata names to be logged

+
+ +
+
+property output: Black
+

log output

+
+ +
+
+ +
+
+class ffmpegio.analyze.BlackFrame(**options)
+

Logger for FFmpeg blackframe filter to detect frames that are (almost) black

+
+
Parameters:
+

**options (dict[str, any]) – FFmpeg filter options (see below)

+
+
+
+

FFmpeg blackframe filter options

+ + + + + + + + + + + + + + + + + +

name

type

description

amount

int

percentage of the pixels that have to be below the threshold for the frame to be considered black (from 0 to 100) (default 98)

threshold

int

threshold below which a pixel value is considered black (from 0 to 255) (default 32). Alias param name: thresh

+
+
+class BlackFrames(time, pblack)
+

output log namedtuple subclass

+
+
Parameters:
+
    +
  • time (List[float | int])

  • +
  • pblack (List[int])

  • +
+
+
+
+
+pblack: List[int]
+

percentage of black pixels

+
+ +
+
+time: List[float | int]
+

timestamps in seconds, frames, or pts

+
+ +
+ +
+
+filter_name: str = 'blackframe'
+

(static) name of the FFmpeg filter to use

+
+ +
+
+log(t, name, key, value)
+

log the metadata

+
+
Parameters:
+
    +
  • t (float|int) – timestamps in seconds, frames, or pts

  • +
  • name (str) – one of the class’ meta_names

  • +
  • key (str | None) – secondary metadata key if found

  • +
  • value (str) – metadata value

  • +
+
+
+

This method is called by analyze.run() if a metadata line begins +with one of the class’ meta_names entry. The log method shall store +the metadata info in a private storage property of the class so they can be +returned later by the output property.

+
+ +
+
+media_type: Literal['video', 'audio'] = 'video'
+

(static) target stream media type

+
+ +
+
+meta_names: Tuple[str] = 'blackframe'
+

(static) metadata names to be logged

+
+ +
+
+property output: BlackFrames
+

log output

+
+ +
+
+ +
+
+class ffmpegio.analyze.BlurDetect(**options)
+

Logger for FFmpeg blurdetect filter to detect video frames that are blurry

+
+
Parameters:
+

**options (dict[str, any]) – FFmpeg filter options (see below)

+
+
+
+

FFmpeg blurdetect filter options

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

name

type

description

high

float

high threshold (from 0 to 1) (default 0.117647)

low

float

low threshold (from 0 to 1) (default 0.0588235)

radius

int

search radius for maxima detection (from 1 to 100) (default 50)

block_pct

int

block pooling threshold when calculating blurriness (from 1 to 100) (default 80)

block_width

int

block width for block-based abbreviation of blurriness (from -1 to INT_MAX) (default -1)

block_height

int

block height for block-based abbreviation of blurriness (from -1 to INT_MAX) (default -1)

planes

int

set planes to filter (from 0 to 15) (default 1)

+
+
+class Blur(time, blur)
+

output log namedtuple subclass

+
+
Parameters:
+
    +
  • time (List[float | int])

  • +
  • blur (List[float])

  • +
+
+
+
+
+blur: List[float]
+

blurness score

+
+ +
+
+time: List[float | int]
+

timestamps in seconds, frames, or pts

+
+ +
+ +
+
+filter_name: Literal['blurdetect'] = 'blurdetect'
+

(static) name of the FFmpeg filter to use

+
+ +
+
+log(t, name, key, value)
+

log the metadata

+
+
Parameters:
+
    +
  • t (float|int) – timestamps in seconds, frames, or pts

  • +
  • name (str) – one of the class’ meta_names

  • +
  • key (str | None) – secondary metadata key if found

  • +
  • value (str) – metadata value

  • +
+
+
+

This method is called by analyze.run() if a metadata line begins +with one of the class’ meta_names entry. The log method shall store +the metadata info in a private storage property of the class so they can be +returned later by the output property.

+
+ +
+
+media_type: Literal['video'] = 'video'
+

(static) target stream media type

+
+ +
+
+meta_names: Tuple[Literal['blur']] = ('blur',)
+

(static) metadata names to be logged

+
+ +
+
+property output: Blur
+

log output

+
+ +
+
+ +
+
+class ffmpegio.analyze.FreezeDetect(**options)
+

Logger for FFmpeg freezedetect filter to detect frozen video input

+
+
Parameters:
+

**options (dict[str, any]) – FFmpeg filter options (see below)

+
+
+
+

FFmpeg freezedetect filter options

+ + + + + + + + + + + + + + + + + +

name

type

description

noise

float

noise tolerance (from 0 to 1) (default 0.001). Alias param name: n

duration

duration

minimum duration in seconds (default 2). Alias param name: d

+
+
+class Frozen(interval)
+

output log namedtuple subclass

+
+
Parameters:
+

interval (List[float | int | None, float | int | None])

+
+
+
+
+interval: List[float | int | None, float | int | None]
+

pairs of start and end timestamps of frozen frame intervals

+
+ +
+ +
+
+filter_name: Literal['freezedetect'] = 'freezedetect'
+

(static) name of the FFmpeg filter to use

+
+ +
+
+log(t, name, key, value)
+

log the metadata

+
+
Parameters:
+
    +
  • t (float|int) – timestamps in seconds, frames, or pts

  • +
  • name (str) – one of the class’ meta_names

  • +
  • key (str | None) – secondary metadata key if found

  • +
  • value (str) – metadata value

  • +
+
+
+

This method is called by analyze.run() if a metadata line begins +with one of the class’ meta_names entry. The log method shall store +the metadata info in a private storage property of the class so they can be +returned later by the output property.

+
+ +
+
+media_type: Literal['video'] = 'video'
+

(static) target stream media type

+
+ +
+
+meta_names: Tuple[Literal['freeze']] = ('freeze',)
+

(static) metadata names to be logged

+
+ +
+
+property output: Frozen
+

log output

+
+ +
+
+ +
+
+class ffmpegio.analyze.ScDet(all_scores=False, **options)
+

Logger for FFmpeg scdet filter to detect video scene change

+
+
Parameters:
+
    +
  • all_scores (bool, optional) – True to return scene scores on all the frames, defaults to False

  • +
  • **options (dict[str, any]) – FFmpeg filter options (see below)

  • +
+
+
+
+

FFmpeg scdet filter options

+ + + + + + + + + + + + + + + + + +

name

type

description

threshold

float

Set the scene change detection threshold as a percentage of maximum change. +Good values are in the [8.0, 14.0] range. The range for threshold is [0., 100.]. +Defaults to 10. Alias param name: t

sc_pass

int

Set the flag to pass scene change frames to the next filter. Default value is +0 You can enable it if you want to get snapshot of scene change frames only. +Alias param name: s

+
+
+class AllScenes(time, changed, score, mafd)
+

Output namedtuple subclass for all_scores=True

+
+
Parameters:
+
    +
  • time (Tuple[float | int])

  • +
  • changed (Tuple[bool])

  • +
  • score (Tuple[float])

  • +
  • mafd (Tuple[float])

  • +
+
+
+
+
+changed: Tuple[bool]
+

scene change flags

+
+ +
+
+mafd: Tuple[float]
+

mafd scores

+
+ +
+
+score: Tuple[float]
+

scene change scores

+
+ +
+
+time: Tuple[float | int]
+

log times

+
+ +
+ +
+
+class Scenes(time, score, mafd)
+

Default output namedtuple subclass

+
+
Parameters:
+
    +
  • time (Tuple[float | int])

  • +
  • score (Tuple[float])

  • +
  • mafd (Tuple[float])

  • +
+
+
+
+
+mafd: Tuple[float]
+

mafd scores

+
+ +
+
+score: Tuple[float]
+

scene change scores

+
+ +
+
+time: Tuple[float | int]
+

log times

+
+ +
+ +
+
+filter_name: str = 'scdet'
+

(static) name of the FFmpeg filter to use

+
+ +
+
+log(t, name, key, value)
+

log the metadata

+
+
Parameters:
+
    +
  • t (float|int) – timestamps in seconds, frames, or pts

  • +
  • name (str) – one of the class’ meta_names

  • +
  • key (str | None) – secondary metadata key if found

  • +
  • value (str) – metadata value

  • +
+
+
+

This method is called by analyze.run() if a metadata line begins +with one of the class’ meta_names entry. The log method shall store +the metadata info in a private storage property of the class so they can be +returned later by the output property.

+
+ +
+
+media_type: Literal['video', 'audio'] = 'video'
+

(static) target stream media type

+
+ +
+
+meta_names: Tuple[str] = ('scd',)
+

(static) metadata names to be logged

+
+ +
+
+options: dict[str, Any]
+

FFmpeg filter options (value must be stringifiable)

+
+ +
+
+property output: Scenes | AllScenes
+

log output. Scenes if all_scores==True else AllScenes

+
+ +
+
+ +
+
+ + +
+
+
+ +
+ +
+

© Copyright 2021-2022, Takeshi (Kesh) Ikuma, Louisiana State University Health Sciences Center.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/basicio.html b/docs/basicio.html new file mode 100644 index 00000000..c78af740 --- /dev/null +++ b/docs/basicio.html @@ -0,0 +1,954 @@ + + + + + + + + + Basic I/O Function References — python-ffmpegio 0.11.0 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Basic I/O Function References

+
+

Basic Functions

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

ffmpegio.ffmpeg_info

Get FFmpeg version and configuration information

ffmpegio.set_path

Set FFmpeg and FFprobe executables

ffmpegio.get_path

Get the path to FFmpeg/FFprobe executable

ffmpegio.is_ready

True if ffmpeg and ffprobe binaries are located

ffmpegio.video.create

Create a video using a source video filter

ffmpegio.video.read

Read video frames

ffmpegio.video.write

Write Numpy array to a video file

ffmpegio.video.filter

Filter video frames.

ffmpegio.image.create

Create an image using a source video filter

ffmpegio.image.read

Read an image file or a snapshot of a video frame

ffmpegio.image.filter

Filter image pixels.

ffmpegio.image.write

Write a NumPy array to an image file.

ffmpegio.audio.create

Create audio data using an audio source filter

ffmpegio.audio.read

Read audio samples.

ffmpegio.audio.write

Write a NumPy array to an audio file.

ffmpegio.audio.filter

Filter audio samples.

ffmpegio.open

Open a multimedia file/stream for read/write

ffmpegio.transcode

Transcode media files to another format/encoding

+
+
+ffmpegio.ffmpeg_info()
+

Get FFmpeg version and configuration information

+
+
Returns:
+

versions of ffmpeg and its av libraries as well as build configuration

+
+
Return type:
+

dict

+
+
+ + + + + + + + + + + + + + + + + + + + + +

key

type

description

‘version’

str

FFmpeg version

‘configuration’

list

list of build configuration options

‘library_versions’

dict

version numbers of dependent av libraries

+
+ +
+
+ffmpegio.get_path(probe=False)
+

Get the path to FFmpeg/FFprobe executable

+
+
Parameters:
+

probe (bool, optional) – True to return FFprobe path instead, defaults to False

+
+
Returns:
+

Path to FFmpeg/FFprobe exectutable

+
+
Return type:
+

str or None

+
+
+
+ +
+
+ffmpegio.set_path(ffmpeg_path=None, ffprobe_path=None)
+

Set FFmpeg and FFprobe executables

+
+
Parameters:
+
    +
  • ffmpeg_path (str, optional) – Full path to either the ffmpeg executable file or +to the folder housing both ffmpeg and ffprobe, defaults to None

  • +
  • ffprobe_path (str, optional) – Full path to the ffprobe executable file, defaults to None

  • +
+
+
Returns:
+

ffmpeg path, ffprobe path, and ffmpeg version

+
+
Return type:
+

Tuple[str,str,str]

+
+
+

If ffmpeg_path specifies a directory, the names of the executables are +auto-set to ffmpeg and ffprobe.

+

If the file locations are specified, the presence of the files will be +tested and an exception will be raised if both ffmpeg and ffprobe are not +valid executables.

+

If no argument is specified, the executables are auto-detected in the following orders.

+
    +
  1. ffmpeg and ffprobe commands, i.e., the path to the parent directory +is included in the system PATH environmental variable.

  2. +
  3. Run the finder plugin functions in the LIFO order and use the first valid +paths. There are two plugins currently offered: ffmpegio-plugin-downloader +and ffmpegio-plugin-static-ffmpeg.

  4. +
  5. In Windows, additional locations are searched (e.g., C:Program Filesffmpeg). +See the documentation for the full list.

  6. +
+
+ +
+
+ffmpegio.is_ready()
+

True if ffmpeg and ffprobe binaries are located

+
+
Returns:
+

True if both ffmpeg and ffprobe are found

+
+
Return type:
+

bool

+
+
+
+ +
+
+ffmpegio.video.create(expr, *args, progress=None, show_log=None, sp_kwargs=None, **options)
+

Create a video using a source video filter

+
+
Parameters:
+
    +
  • name (str) – name of the source filter

  • +
  • *args (seq, optional) – sequential filter option arguments. Only valid for +a single-filter expr, and they will overwrite the +options set by expr.

  • +
  • progress (callable object, optional) – progress callback function, defaults to None

  • +
  • show_log (bool, optional) – True to show FFmpeg log messages on the console, +defaults to None (no show/capture) +Ignored if stream format must be retrieved automatically.

  • +
  • sp_kwargs (dict, optional) – dictionary with keywords passed to subprocess.run() or +subprocess.Popen() call used to run the FFmpeg, defaults +to None

  • +
  • **options (dict, optional) – Named filter options or FFmpeg options. Items are +only considered as the filter options if expr is a +single-filter graph, and take the precedents over +general FFmpeg options. Append ‘_in’ for input +option names (see FFmpeg Option References), and ‘_out’ for +output option names if they conflict with the filter +options.

  • +
+
+
Returns:
+

frame rate and video data, created by bytes_to_video plugin hook

+
+
Return type:
+

tuple[Fraction,object]

+
+
+
+
…seealso::

https://ffmpeg.org/ffmpeg-filters.html#Video-Sources for available +video source filters

+
+
+
+ +
+
+ffmpegio.video.read(url, progress=None, show_log=None, sp_kwargs=None, **options)
+

Read video frames

+
+
Parameters:
+
    +
  • url (str) – URL of the video file to read.

  • +
  • progress (callable object, optional) – progress callback function, defaults to None

  • +
  • show_log (bool, optional) – True to show FFmpeg log messages on the console, +defaults to None (no show/capture) +Ignored if stream format must be retrieved automatically.

  • +
  • sp_kwargs (dict, optional) – dictionary with keywords passed to subprocess.run() or +subprocess.Popen() call used to run the FFmpeg, defaults +to None

  • +
  • **options (dict, optional) – FFmpeg options, append ‘_in’ for input option names (see FFmpeg Option References)

  • +
+
+
Returns:
+

frame rate and video frame data, created by bytes_to_video plugin hook

+
+
Return type:
+

(fractions.Fraction, object)

+
+
+
+ +
+
+ffmpegio.video.write(url, rate_in, data, progress=None, overwrite=None, show_log=None, two_pass=False, pass1_omits=None, pass1_extras=None, extra_inputs=None, sp_kwargs=None, **options)
+

Write Numpy array to a video file

+
+
Parameters:
+
    +
  • url (str) – URL of the video file to write.

  • +
  • rate_in (float, int, or fractions.Fraction) – frame rate in frames/second

  • +
  • data (object) – video frame data object, accessed by video_info and video_bytes plugin hooks

  • +
  • progress (callable object, optional) – progress callback function, defaults to None

  • +
  • overwrite (bool, optional) – True to overwrite if output url exists, defaults to None +(auto-select)

  • +
  • show_log (bool, optional) – True to show FFmpeg log messages on the console, +defaults to None (no show/capture)

  • +
  • two_pass – True to encode in 2-pass

  • +
  • pass1_omits (seq(str), optional) – list of output arguments to ignore in pass 1, defaults to None

  • +
  • pass1_extras (dict(int:dict(str)), optional) – list of additional output arguments to include in pass 1, defaults to None

  • +
  • extra_inputs (seq(str|(str,dict))) – list of additional input sources, defaults to None. Each source may be url +string or a pair of a url string and an option dict.

  • +
  • sp_kwargs (dict, optional) – dictionary with keywords passed to subprocess.run() or +subprocess.Popen() call used to run the FFmpeg, defaults +to None

  • +
  • **options (dict, optional) – FFmpeg options, append ‘_in’ for input option names (see FFmpeg Option References)

  • +
+
+
+
+ +
+
+ffmpegio.video.filter(expr, rate, input, progress=None, show_log=None, sp_kwargs=None, **options)
+

Filter video frames.

+
+
Parameters:
+
    +
  • expr (str, None) – SISO filter graph or None if implicit filtering via output options.

  • +
  • rate (float, int, or fractions.Fraction) – input frame rate in frames/second

  • +
  • input (object) – input video frame data object, accessed by video_info and video_bytes plugin hooks

  • +
  • progress (callable object, optional) – progress callback function, defaults to None

  • +
  • show_log (bool, optional) – True to show FFmpeg log messages on the console, +defaults to None (no show/capture)

  • +
  • sp_kwargs (dict, optional) – dictionary with keywords passed to subprocess.run() or +subprocess.Popen() call used to run the FFmpeg, defaults +to None

  • +
  • **options (dict, optional) – FFmpeg options, append ‘_in’ for input option names (see FFmpeg Option References)

  • +
+
+
Returns:
+

output frame rate and video frame data, created by bytes_to_video plugin hook

+
+
Return type:
+

object

+
+
+
+ +
+
+ffmpegio.image.create(expr, *args, show_log=None, sp_kwargs=None, **options)
+

Create an image using a source video filter

+
+
Parameters:
+
    +
  • name (str) – name of the source filter

  • +
  • *args (seq, optional) – sequential filter option arguments. Only valid for +a single-filter expr, and they will overwrite the +options set by expr.

  • +
  • show_log (bool, optional) – True to show FFmpeg log messages on the console, +defaults to None (no show/capture) +Ignored if stream format must be retrieved automatically.

  • +
  • sp_kwargs (dict, optional) – dictionary with keywords passed to subprocess.run() or +subprocess.Popen() call used to run the FFmpeg, defaults +to None

  • +
  • **options (dict, optional) – Named filter options or FFmpeg options. Items are +only considered as the filter options if expr is a +single-filter graph, and take the precedents over +general FFmpeg options. Append ‘_in’ for input +option names (see FFmpeg Option References), and ‘_out’ for +output option names if they conflict with the filter +options.

  • +
+
+
Returns:
+

image data, created by bytes_to_video plugin hook

+
+
Return type:
+

object

+
+
+
+

See also

+

See https://ffmpeg.org/ffmpeg-filters.html#Video-Sources for +available video source filters

+
+
+ +
+
+ffmpegio.image.read(url, show_log=None, sp_kwargs=None, **options)
+

Read an image file or a snapshot of a video frame

+
+
Parameters:
+
    +
  • url (str) – URL of the image or video file to read.

  • +
  • show_log (bool, optional) – True to show FFmpeg log messages on the console, +defaults to None (no show/capture) +Ignored if stream format must be retrieved automatically.

  • +
  • sp_kwargs (dict, optional) – dictionary with keywords passed to subprocess.run() or +subprocess.Popen() call used to run the FFmpeg, defaults +to None

  • +
  • **options (dict, optional) – FFmpeg options, append ‘_in’ for input option names (see FFmpeg Option References)

  • +
+
+
Returns:
+

image data, created by bytes_to_video plugin hook

+
+
Return type:
+

object

+
+
+

Note on **options: To specify the video frame capture time, use time +option which is an alias of start standard option.

+
+ +
+
+ffmpegio.image.write(url, data, overwrite=None, show_log=None, extra_inputs=None, sp_kwargs=None, **options)
+

Write a NumPy array to an image file.

+
+
Parameters:
+
    +
  • url (str) – URL of the image file to write.

  • +
  • data (object) – image data, accessed by video_info() and video_bytes() plugin hooks

  • +
  • overwrite (bool, optional) – True to overwrite if output url exists, defaults to None +(auto-select)

  • +
  • show_log (bool, optional) – True to show FFmpeg log messages on the console, +defaults to None (no show/capture)

  • +
  • sp_kwargs (dict, optional) – dictionary with keywords passed to subprocess.run() or +subprocess.Popen() call used to run the FFmpeg, defaults +to None

  • +
  • extra_inputs (seq(str|(str,dict))) – list of additional input sources, defaults to None. Each source may be url +string or a pair of a url string and an option dict.

  • +
  • **options (dict, optional) – FFmpeg options, append ‘_in’ for input option names (see FFmpeg Option References)

  • +
+
+
+
+ +
+
+ffmpegio.image.filter(expr, input, show_log=None, sp_kwargs=None, **options)
+

Filter image pixels.

+
+
Parameters:
+
    +
  • expr (str, None) – SISO filter graph or None if implicit filtering via output options.

  • +
  • input (object) – input image data, accessed by video_info and video_bytes plugin hooks

  • +
  • show_log (bool, optional) – True to show FFmpeg log messages on the console, +defaults to None (no show/capture)

  • +
  • sp_kwargs (dict, optional) – dictionary with keywords passed to subprocess.run() or +subprocess.Popen() call used to run the FFmpeg, defaults +to None

  • +
  • **options (dict, optional) – FFmpeg options, append ‘_in’ for input option names (see FFmpeg Option References)

  • +
+
+
Returns:
+

output sampling rate and data, created by bytes_to_video plugin hook

+
+
Return type:
+

(int, object)

+
+
+
+ +
+
+ffmpegio.audio.create(expr, *args, progress=None, show_log=None, sp_kwargs=None, **options)
+

Create audio data using an audio source filter

+
+
Parameters:
+
    +
  • expr (str) – name of the source filter or full filter expression

  • +
  • *args (seq, optional) – sequential filter option arguments. Only valid for +a single-filter expr, and they will overwrite the +options set by expr.

  • +
  • progress (callable object, optional) – progress callback function, defaults to None

  • +
  • show_log (bool, optional) – True to show FFmpeg log messages on the console, +defaults to None (no show/capture) +Ignored if stream format must be retrieved automatically.

  • +
  • sp_kwargs (dict, optional) – dictionary with keywords passed to subprocess.run() or +subprocess.Popen() call used to run the FFmpeg, defaults +to None

  • +
  • **options (dict, optional) – Named filter options or FFmpeg options. Items are +only considered as the filter options if expr is a +single-filter graph, and take the precedents over +general FFmpeg options. Append ‘_in’ for input +option names (see FFmpeg Option References), and ‘_out’ for +output option names if they conflict with the filter +options.

  • +
+
+
Returns:
+

sampling rate and audio data (a plugin may change this behavior +with the bytes_to_audio hook.)

+
+
Return type:
+

tuple[int, object]

+
+
+
+

See also

+

https://ffmpeg.org/ffmpeg-filters.html#Audio-Sources for available +audio source filters

+
+
+

Warning

+

Nearly all the source filters by default continue outputting +indefinitely. Set its duration option or FFmpeg’s t (duration) +or to (end time) input/output options to make sure the function +returns properly.

+
+
+

Note

+

output data object is determined by the selected hook

+
+
+ +
+
+ffmpegio.audio.read(url, progress=None, show_log=None, sp_kwargs=None, **options)
+

Read audio samples.

+
+
Parameters:
+
    +
  • url (str) – URL of the audio file to read.

  • +
  • progress (callable object, optional) – progress callback function, defaults to None

  • +
  • show_log (bool, optional) – True to show FFmpeg log messages on the console, +defaults to None (no show/capture) +Ignored if stream format must be retrieved automatically.

  • +
  • sp_kwargs (dict, optional) – dictionary with keywords passed to subprocess.run() or +subprocess.Popen() call used to run the FFmpeg, defaults +to None

  • +
  • **options (dict, optional) – FFmpeg options, append ‘_in’ for input option names (see FFmpeg Option References)

  • +
+
+
Returns:
+

sample rate in samples/second and audio data object specified by bytes_to_audio plugin hook

+
+
Return type:
+

tuple(float, object)

+
+
+
+

Note

+

Even if start_time option is set, all the prior samples will be read. +The retrieved data will be truncated before returning it to the caller. +This is to ensure the timing accuracy. As such, do not use this function +to perform block-wise processing. Instead use the streaming solution, +see open().

+
+
+ +
+
+ffmpegio.audio.write(url, rate_in, data, progress=None, overwrite=None, show_log=None, extra_inputs=None, sp_kwargs=None, **options)
+

Write a NumPy array to an audio file.

+
+
Parameters:
+
    +
  • url (str) – URL of the audio file to write.

  • +
  • rate_in (int) – The sample rate in samples/second.

  • +
  • data (object) – input audio data object, converted to bytes by audio_bytes plugin hook .

  • +
  • progress (callable object, optional) – progress callback function, defaults to None

  • +
  • overwrite (bool, optional) – True to overwrite if output url exists, defaults to None +(auto-select)

  • +
  • show_log (bool, optional) – True to show FFmpeg log messages on the console, +defaults to None (no show/capture)

  • +
  • extra_inputs (seq(str|(str,dict))) – list of additional input sources, defaults to None. Each source may be url +string or a pair of a url string and an option dict.

  • +
  • sp_kwargs (dict, optional) – dictionary with keywords passed to subprocess.run() or +subprocess.Popen() call used to run the FFmpeg, defaults +to None

  • +
  • **options (dict, optional) – FFmpeg options, append ‘_in’ for input option names (see FFmpeg Option References)

  • +
+
+
+
+ +
+
+ffmpegio.audio.filter(expr, input_rate, input, sample_fmt=None, progress=None, show_log=None, sp_kwargs=None, **options)
+

Filter audio samples.

+
+
Parameters:
+
    +
  • expr (str, None) – SISO filter graph or None if implicit filtering via output options.

  • +
  • input_rate (int) – Input sample rate in samples/second

  • +
  • input (object) – input audio data, accessed by audio_info() and audio_bytes() plugin hooks.

  • +
  • progress (callable object, optional) – progress callback function, defaults to None

  • +
  • show_log (bool, optional) – True to show FFmpeg log messages on the console, +defaults to None (no show/capture)

  • +
  • sp_kwargs (dict, optional) – dictionary with keywords passed to subprocess.run() or +subprocess.Popen() call used to run the FFmpeg, defaults +to None

  • +
  • **options (dict, optional) – FFmpeg options, append ‘_in’ for input option names (see FFmpeg Option References)

  • +
+
+
Returns:
+

output sampling rate and audio data object, created by bytes_to_audio plugin hook

+
+
Return type:
+

tuple(int, object)

+
+
+
+ +
+
+ffmpegio.open(url_fg, mode, rate_in=None, shape_in=None, dtype_in=None, rate=None, shape=None, **kwds)
+

Open a multimedia file/stream for read/write

+
+
Parameters:
+
    +
  • url_fg (str or seq(str)) – URL of the media source/destination for file read/write or filtergraph definition +for filter operation.

  • +
  • mode (str) – specifies the mode in which the FFmpeg is used, see below

  • +
  • rate_in (Fraction, float, int, optional) – (write and filter only, required) input frame rate (video) or sampling rate +(audio), defaults to None

  • +
  • shape_in (seq of int, optional) – (write and filter only) input video frame size (height x width [x ncomponents]), +or audio sample size (channels,), defaults to None

  • +
  • dtype_in (str, optional) – (write and filter only) input data type, defaults to None

  • +
  • rate (Fraction, float, int, optional) – (filter only, required) output frame rate (video write) or sample rate (audio +write), defaults to None

  • +
  • dtype (str, optional) – (read and filter specific) output data type, defaults to None

  • +
  • shape (seq of int, optional) – (read and filter specific) output video frame size (height x width [x ncomponents]), +or audio sample size (channels,), defaults to None

  • +
  • show_log (bool, optional) – True to echo the ffmpeg log to stdout, default to False

  • +
  • progress (Callable, optional) – progress callback function (see Progress Callback)

  • +
  • blocksize (int, optional) – (read and filter only) Number of frames to read by read() method, default to None (auto)

  • +
  • extra_inputs (List[Tuple[str,dict]], optional) – (write only) List of additional (non-pipe) inputs to pass onto FFmpeg. Each +input is defined by a tuple of its url or a dict of input options, default to None

  • +
  • default_timeout (float, optional) – (filter only) default filter timeout in seconds, defaults to None (10 ms)

  • +
  • sp_kwargs (dict, optional) – Keyword arguments for FFmpeg process (see ffmpegio.ffmpegprocess.Popen), default to None

  • +
  • **options (dict, optional) – FFmpeg options, append ‘_in’ for input option names (see FFmpeg Option References)

  • +
+
+
Returns:
+

ffmpegio stream object

+
+
+

Start FFmpeg and open I/O link to it to perform read/write/filter operation and return +a corresponding stream object. If the file cannot be opened, an error is raised. +See Stream Read/Write for more examples of how to use this function.

+

Just like built-in open(), it is good practice to use the with keyword when dealing with +ffmpegio stream objects. The advantage is that the ffmpeg process and associated threads are +properly closed after ffmpeg terminates, even if an exception is raised at some point. +Using with is also much shorter than writing equivalent try-finally blocks.

+
+
Examples:
+

+
Parameters:
+
    +
  • url_fg (str)

  • +
  • mode (str)

  • +
  • rate_in (float | None)

  • +
  • shape_in (Tuple[int, ...] | None)

  • +
  • dtype_in (str | None)

  • +
  • rate (float | None)

  • +
  • shape (Tuple[int, ...] | None)

  • +
+
+
+

Open an MP4 file and process all the frames:

+
with ffmpegio.open('video_source.mp4', 'rv') as f:
+    frame = f.read()
+    while frame:
+        # process the captured frame data
+        frame = f.read()
+
+
+

Read an audio stream of MP4 file and write it to a FLAC file as samples +are decoded:

+
with ffmpegio.open('video_source.mp4','ra') as rd:
+    fs = rd.sample_rate
+    with ffmpegio.open('video_dst.flac','wa',rate_in=fs) as wr:
+        frame = rd.read()
+        while frame:
+            wr.write(frame)
+            frame = rd.read()
+
+
+
+
Additional Notes:
+

+
Parameters:
+
    +
  • url_fg (str)

  • +
  • mode (str)

  • +
  • rate_in (float | None)

  • +
  • shape_in (Tuple[int, ...] | None)

  • +
  • dtype_in (str | None)

  • +
  • rate (float | None)

  • +
  • shape (Tuple[int, ...] | None)

  • +
+
+
+

url_fg can be a string specifying either the pathname (absolute or relative to the current +working directory) of the media target (file or streaming media) to be opened or a string describing +the filtergraph to be implemented. Its interpretation depends on the mode argument.

+

mode is an optional string that specifies the mode in which the FFmpeg is opened.

+ + + + + + + + + + + + + + + + + + + + + + + +

Mode

Description

‘r’

read from url

‘w’

write to url

‘f’

filter data defined by fg

‘v’

operate on video stream, ‘vv’ if multi-video reader

‘a’

operate on audio stream, ‘aa’ if multi-audio reader

+

rate and rate_in: Video frame rates shall be given in frames/second and +may be given as a number, string, or fractions.Fraction. Audio sample rate in +samples/second (per channel) and shall be given as an integer or string.

+

Optional shape or shape_in for video defines the video frame size and +number of components with a 2 or 3 element sequence: (width, height[, ncomp]). +The number of components and other optional dtype (or dtype_in) implicitly +define the pixel format (FFmpeg pix_fmt option):

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

ncomp

dtype

pix_fmt

Description

1

|u8

gray

grayscale

1

<u2

gray10le

10-bit grayscale

1

<u2

gray12le

12-bit grayscale

1

<u2

gray14le

14-bit grayscale

1

<u2

gray16le

16-bit grayscale (default <u2 choice)

1

<f4

grayf32le

floating-point grayscale

2

|u1

ya8

grayscale with alpha channel

2

<u2

ya16le

16-bit grayscale with alpha channel

3

|u1

rgb24

RGB

3

<u2

rgb48le

16-bit RGB

4

|u1

rgba

RGB with alpha transparency channel

4

<u2

rgba64le

16-bit RGB with alpha channel

+

For audio stream, single-element seq argument, shape or shape_in, +specifies the number of channels while dtype and dtype_in determines +the sample format (FFmpeg sample_fmt option):

+ + + + + + + + + + + + + + + + + + + + + + + +

dtype

sample_fmt

|u1

u8

<i2

s16

<i4

s32

<f4

flt

<f8

dbl

+

If dtypes and shapes are not specified at the time of opening, they will +be set during the first write/filter operation using the input data.

+

In addition, open() accepts the standard FFmpeg option keyword arguments.

+
+ +
+
+ffmpegio.transcode(inputs, outputs, progress=None, overwrite=None, show_log=None, two_pass=False, pass1_omits=None, pass1_extras=None, sp_kwargs=None, **options)
+

Transcode media files to another format/encoding

+
+
Parameters:
+
    +
  • inputs (str or a list of str or a sequence of (str,dict)) – url/path of the input media file or a sequence of tuples, each +containing an input url and its options dict

  • +
  • outputs (str or sequence of (str, dict)) – url/path of the output media file or a sequence of tuples, each +containing an output url and its options dict

  • +
  • progress (callable object, optional) – progress callback function, defaults to None

  • +
  • overwrite (bool, optional) – True to overwrite if output url exists, defaults to None +(auto-select)

  • +
  • show_log (bool, optional) – True to show FFmpeg log messages on the console, +defaults to None (no show/capture) +Ignored if stream format must be retrieved automatically.

  • +
  • two_pass – True to encode in 2-pass

  • +
  • pass1_omits (seq(str), or seq(seq(str)) optional) – list of output arguments to ignore in pass 1, defaults to +None (removes ‘c:a’ or ‘acodec’). For multiple outputs, +specify use list of the list of arguments, matching the +length of outputs, for per-output omission.

  • +
  • pass1_extras (dict(int:dict(str)), optional) – list of additional output arguments to include in pass 1, +defaults to None (add ‘an’ if pass1_omits also None)

  • +
  • sp_kwargs (dict, optional) – dictionary with keywords passed to subprocess.run() or +subprocess.Popen() call used to run the FFmpeg, defaults +to None

  • +
  • **options (dict, optional) –

    FFmpeg options. For output and global options, use FFmpeg +option names as is. For input options, append “_in” to the +option name. For example, r_in=2000 to force the input frame +rate to 2000 frames/s (see FFmpeg Option References).

    +

    If multiple inputs or outputs are specified, these input +or output options specified here are treated as common +options, and the url-specific duplicate options in the +inputs or outputs sequence will overwrite those +specified here.

    +

  • +
+
+
Returns:
+

if any of the outputs is stdout, returns output bytes

+
+
Return type:
+

bytes | None

+
+
+
+ +
+
+ + +
+
+
+ +
+ +
+

© Copyright 2021-2022, Takeshi (Kesh) Ikuma, Louisiana State University Health Sciences Center.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/caps.html b/docs/caps.html new file mode 100644 index 00000000..35d260ea --- /dev/null +++ b/docs/caps.html @@ -0,0 +1,1154 @@ + + + + + + + + + FFmpeg Capabilities References — python-ffmpegio 0.11.0 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

FFmpeg Capabilities References

+

ffmpegio.caps module contains a set of functions to wrap ffmpeg’s +help/show commands to check the capabilities of the ffmpeg executable that +the ffmpegio is employing.

+
+

Todo

+

Parsing the additional command options that are specific to the containers, +codecs, and filters. The options fields are currently returned as +unparsed str

+
+
+

List of Functions

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

ffmpegio.caps.options

get FFmpeg command options

ffmpegio.caps.pix_fmts

get supported pixel formats

ffmpegio.caps.sample_fmts

get supported audio sample formats

ffmpegio.caps.layouts

get supported audio channel layouts

ffmpegio.caps.colors

get recognized color names

ffmpegio.caps.filters

get FFmpeg filters

ffmpegio.caps.filter_info

get detailed info of a filter

ffmpegio.caps.codecs

get FFmpeg codecs

ffmpegio.caps.encoders

get summary of FFmpeg encoders

ffmpegio.caps.encoder_info

get detailed info of an encoder

ffmpegio.caps.decoders

get summary of FFmpeg decoders

ffmpegio.caps.decoder_info

get detailed info of a decoder

ffmpegio.caps.formats

get FFmpeg formats

ffmpegio.caps.muxers

get FFmpeg muxers

ffmpegio.caps.muxer_info

get detailed info of a media muxer

ffmpegio.caps.demuxers

get FFmpeg demuxers

ffmpegio.caps.demuxer_info

get detailed info of a media demuxer

ffmpegio.caps.bsfilters

get list of FFmpeg bitstream filters

ffmpegio.caps.bsfilter_info

get detailed info of a bitstream filter

ffmpegio.caps.devices

get FFmpeg devices

ffmpegio.caps.protocols

get list of supported protocols

+
+

Todo

+

Remaining commands to be wrapped: sources, sinks, h protocol, dispositions

+
+
+
+

List of Constants

+ + + + + + + + + +

ffmpegio.caps.video_size_presets

list of video size presets with their sizes

ffmpegio.caps.frame_rate_presets

list of video frame rate presets with their rates

+
+
+

Function References

+
+
+ffmpegio.caps.options(type=None, name_only=False, return_desc=False)
+

get FFmpeg command options

+
+
Parameters:
+
    +
  • type ("per-file"|"video"|"audio"|"subtitle"|"general"|"global"|None, optional) – specify option type to return, defaults to None

  • +
  • name_only (bool, optional) – True to only return option names, defaults to False

  • +
  • return_desc (bool, optional) – True to also return option description, defaults to False

  • +
+
+
Returns:
+

dict of types of options

+
+
Return type:
+

dict(dict or tuple) if type not specified

+
+
+
+ +
+
+ffmpegio.caps.pix_fmts()
+

get supported pixel formats

+
+
Returns:
+

list of supported pixel formats

+
+
Return type:
+

dict

+
+
+

Each key of the returned dict is a name of a pix_fmt and its value is a dict +with the following items:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Key

type

description

nb_components

int

Number of color components

bits_per_pixel

int

Number of bits per pixel

input

bool

True if can be used as an input option

output

bool

True if can be used as an output option

hw_accel

bool

True if supported by hardware accelerators

paletted

bool

True if uses paletted colors

bitstream

bool

True if can be used with bistreams

+
+ +
+
+ffmpegio.caps.sample_fmts()
+

get supported audio sample formats

+
+
Returns:
+

list of supported audio sample formats

+
+
Return type:
+

dict

+
+
+

Each key of the returned dict is a name of a sample_fmt and its value +is the number of bits per sample.

+
+ +
+
+ffmpegio.caps.layouts()
+

get supported audio channel layouts

+
+
Returns:
+

list of supported audio channel layouts

+
+
Return type:
+

dict

+
+
+

Returned dict has two keys “channels” and “layouts”. The value of “channels” +is a dict of possible channel names as keys and their descriptions as values. +The value of “layouts” is also a dict, which keys specifies the names and +their value strs indicate the combinations of channels (their names are +“+”ed).

+
+ +
+
+ffmpegio.caps.colors()
+

get recognized color names

+
+
Returns:
+

list of color names

+
+
Return type:
+

dict

+
+
+

The keys of the returned dict are the name of the colors and their values +are the RGB hex strs.

+
+ +
+
+ffmpegio.caps.filters(type=None)
+

get FFmpeg filters

+
+
Parameters:
+

type ('audio'|'video'|'dynamic', optional) – specify input or output stream type, defaults to None

+
+
Returns:
+

dict of summary of the filters

+
+
Return type:
+

dict(key,FilterSummary)

+
+
+

Each key of the returned dict is a name of a filter and its value is a +FilterSummary namedtuple with the following items:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Key

type

description

description

str

Short description of the filter

input

str

Input stream type: ‘audio’|’video’|’dynamic’

num_inputs

int|None

Number of inputs or None if ‘dynamic’

output

str

Output stream type: ‘audio’|’video’|’dynamic’

num_outputs

int|None

Number of outputs or None if ‘dynamic’

timeline_support

bool

True if supports timeline editing

slice_threading

bool

True if supports threading

command_support

bool

True if supports command input from stdin

+
+ +
+
+ffmpegio.caps.filter_info(name)
+

get detailed info of a filter

+
+
Returns:
+

list of features

+
+
Return type:
+

FilterInfo (namedtuple)

+
+
+

The returned FilterInfo named tuple has following entries:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Key

type

description

name

str

Name

description

str

Description

threading

list(str)

List of threading capabilities

inputs

list(dict)|str

List of input pads or ‘dynamic’ if variable

outputs

list(dict)|str

List of output pads or ‘dynamic’ if variable

options

list(FilterOption)

List of filter options

extra_options

dict(str,list(FilterOption))

Extra options co-listed

timeline_support

bool

True if enable timeline option is supported

+

‘inputs’ and ‘outputs’ entries has two keys: ‘name’ and ‘type’ +defining the pad name and pad stream type (‘audio’ or ‘video’)

+

FilterOption is a namedtuple with the following entries:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Key

type

description

name

str

Name

alias

str

Alias name

type

str

Data type

help

str

Help text

ranges

list(tuple(any,any))|None

List of ranges of values

constants

dict(str:any)

List of defined constant/enum values

default

any

Default value

video

bool

True if option for video stream

audio

bool

True if option for audio stream

runtime

bool

True if modifiable during runtime

+
+ +
+
+ffmpegio.caps.codecs(type=None, stream_type=None)
+

get FFmpeg codecs

+
+
Parameters:
+
    +
  • type ('decoder'|'encoder', optional) – Specify to list only decoder or encoder, defaults to None

  • +
  • stream_type ('audio'|'video'|'subtitle', optional) – Specify to stream type, defaults to None

  • +
+
+
Returns:
+

summary of FFmpeg codecs

+
+
Return type:
+

dict

+
+
+

Each key of the returned dict is a name of a codec and its value is a dict +with the following items:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Key

type

description

type

str

Stream type: ‘audio’|’video’|’subtitle’

description

str

Short description of the codec

can_decode

bool

True if FFmpeg can decode

decoders

list(str)

List of compatible decoders

can_encode

bool

True if FFmpeg can encode

encoders

list(str)

List of compatible encoders

intra_frame_only

bool

True if codec only uses intra-frame coding

is_lossy

bool

True if codec can do lossy compression

is_lossless

bool

True if codec can do lossless compression

+
+ +
+
+ffmpegio.caps.encoders(type=None)
+

get summary of FFmpeg encoders

+
+
Parameters:
+

type ('audio'|'video'|'subtitle', optional) – specify stream type, defaults to None

+
+
Returns:
+

list of encoders

+
+
Return type:
+

dict

+
+
+

Each key of the returned dict is a name of a decoder or encoder and its +value is a dict with the following items:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Key

type

description

type

str

Stream type: ‘audio’|’video’|’subtitle’

description

str

Short description of the coder

frame_mt

bool

True if employs frame-level multithreading

slice_mt

bool

True if employs slice-level multithreading

experimental

bool

True if experimental encoder

draw_horiz_band

bool

True if supports draw_horiz_band

directRendering

bool

True if supports direct encoding method 1

+
+ +
+
+ffmpegio.caps.encoder_info(name)
+

get detailed info of an encoder

+
+
Returns:
+

list of features

+
+
Return type:
+

dict

+
+
+

The returned dict has following entries:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Key

type

description

name

list(str)

Short names

long_name

str

Long name

capabilities

list(str)

List of supported capabilities

threading

list(str)

List of threading capabilities

supported_hwdevices

list(str)

List of supported hardware accelerators

supported_framerates

list(Fraction)

List of supported video frame rates

supported_pix_fmts

list(str)

List of supported video pixel formats

supported_sample_rates

list(int)

List of supported audio sample rates

supported_sample_fmts

list(str)

List of supported audio sample formats

supported_layouts

list(str)

List of supported audio channel layouts

options

str

Unparsed string, listing supported options

+
+ +
+
+ffmpegio.caps.decoders(type=None)
+

get summary of FFmpeg decoders

+
+
Parameters:
+

stream_type ('audio'|'video'|'subtitle', optional) – specify stream type, defaults to None

+
+
Returns:
+

list of decoders or encoders

+
+
Return type:
+

dict

+
+
+

Each key of the returned dict is a name of a decoder and its +value is a dict with the following items:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Key

type

description

type

str

Stream type: ‘audio’|’video’|’subtitle’

description

str

Short description of the coder

frame_mt

bool

True if employs frame-level multithreading

slice_mt

bool

True if employs slice-level multithreading

experimental

bool

True if experimental encoder

draw_horiz_band

bool

True if supports draw_horiz_band

directRendering

bool

True if supports direct encoding method 1

+
+ +
+
+ffmpegio.caps.decoder_info(name)
+

get detailed info of a decoder

+
+
Returns:
+

list of features

+
+
Return type:
+

dict

+
+
+

The returned dict has following entries:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Key

type

description

name

list(str)

Short names

long_name

str

Long name

capabilities

list(str)

List of supported capabilities

threading

list(str)

List of threading capabilities

supported_hwdevices

list(str)

List of supported hardware accelerators

supported_framerates

list(Fraction)

List of supported video frame rates

supported_pix_fmts

list(str)

List of supported video pixel formats

supported_sample_rates

list(int)

List of supported audio sample rates

supported_sample_fmts

list(str)

List of supported audio sample formats

supported_layouts

list(str)

List of supported audio channel layouts

options

str

Unparsed string, listing supported options

+
+ +
+
+ffmpegio.caps.formats()
+

get FFmpeg formats

+
+
Returns:
+

list of formats

+
+
Return type:
+

dict

+
+
+

Each key of the returned dict is a name of a format and its value is a dict +with the following items:

+ + + + + + + + + + + + + + + + + + + + + +

Key

type

description

description

str

Short description of the format

can_demux

bool

True if supports inputs of this format

can_mux

bool

True if support outputs of this format

+
+ +
+
+ffmpegio.caps.muxers()
+

get FFmpeg muxers

+
+
Returns:
+

list of muxers

+
+
Return type:
+

dict

+
+
+

Each key of the returned dict is a name of a muxer and its value is a dict +with the following items:

+ + + + + + + + + + + + + +

Key

type

description

description

str

Short description of the muxer

+
+ +
+
+ffmpegio.caps.muxer_info(name)
+

get detailed info of a media muxer

+
+
Returns:
+

list of features

+
+
Return type:
+

dict

+
+
+

The returned dict has following entries:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Key

type

description

names

list(str)

List of compatible short names

long_name

str

Common long name

extensions

list(str)

List of associated common extensions (w/out ‘.’)

mime_types

list(str)

List of associated MIME types

video_codecs

list(str)

List of supported video codecs

audio_codecs

list(str)

List of supported audio codecs

subtitle_codecs

list(str)

List of supported subtitle codecs

options

str

Unparsed string, listing supported options

+
+ +
+
+ffmpegio.caps.demuxers()
+

get FFmpeg demuxers

+
+
Returns:
+

list of demuxers

+
+
Return type:
+

dict

+
+
+

Each key of the returned dict is a name of a demuxer and its value is a dict +with the following items:

+ + + + + + + + + + + + + +

Key

type

description

description

str

Short description of the demuxer

+
+ +
+
+ffmpegio.caps.demuxer_info(name)
+

get detailed info of a media demuxer

+
+
Returns:
+

list of features

+
+
Return type:
+

dict

+
+
+

The returned dict has following entries:

+ + + + + + + + + + + + + + + + + + + + + + + + + +

Key

type

description

names

list(str)

List of compatible short names

long_name

str

Common long name

extensions

list(str)

List of associated common extensions (w/out ‘.’)

options

str

Unparsed string, listing supported options

+
+ +
+
+ffmpegio.caps.bsfilters()
+

get list of FFmpeg bitstream filters

+
+
Returns:
+

list of bistream filters

+
+
Return type:
+

list(str)

+
+
+
+ +
+
+ffmpegio.caps.bsfilter_info(name)
+

get detailed info of a bitstream filter

+
+
Returns:
+

list of features

+
+
Return type:
+

dict

+
+
+

The returned dict has following entries:

+ + + + + + + + + + + + + + + + + + + + + +

Key

type

description

name

str

Name

supported_codecs

str

List of supported codecs

options

str

Unparsed string, listing supported options

+
+ +
+
+ffmpegio.caps.devices(type=None)
+

get FFmpeg devices

+
+
Parameters:
+

type ('source'|'sink', optional) – specify source or sink type, defaults to None

+
+
Returns:
+

list of devices

+
+
Return type:
+

dict

+
+
+

Each key of the returned dict is a name of a device and its value is a dict +with the following items:

+ + + + + + + + + + + + + + + + + + + + + +

Key

type

description

description

str

Short description of the device

can_demux

bool

True if supports inputs of this format

can_mux

bool

True if support outputs of this format

+
+ +
+
+ffmpegio.caps.protocols()
+

get list of supported protocols

+
+
Returns:
+

list of protocols

+
+
Return type:
+

dict

+
+
+

Returned dict has ‘input’ and ‘output’ keys and each contains a list of +supported protocol names.

+
+ +
+
+ + +
+
+
+ +
+ +
+

© Copyright 2021-2022, Takeshi (Kesh) Ikuma, Louisiana State University Health Sciences Center.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/concat.html b/docs/concat.html new file mode 100644 index 00000000..e7d8e213 --- /dev/null +++ b/docs/concat.html @@ -0,0 +1,647 @@ + + + + + + + + + FFConcat Class: Concatenating Media Files — python-ffmpegio 0.11.0 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

FFConcat Class: Concatenating Media Files

+

FFmpeg supports different approaches to concatenate media files as described on +their Wiki Page. If many files +are concatenated, any of these approaches results in lengthy command (or a +ffconcat listing file). The ffmpegio.FFConcat class primarily focus on +the concat demuxer and abstracts the ffconcat listing file when running ffmpegio +commands.

+
+
+class ffmpegio.FFConcat(script=None, pipe_url=None, ffconcat_url=None)
+

Create FFmpeg concat demuxer source generator

+
+
Parameters:
+
    +
  • script (str, optional) – concat script to parse, defaults to None (empty script)

  • +
  • pipe_url (bool, optional) – stdin pipe or None to use a temp file, defaults to None

  • +
+
+
+

FFConcat instance is intended to be used as an input url object when invoking ffmpegprocess.run +or ffmpegprocess.Popen. The FFmpeg command parser stringify the ConatDemuxer instance to either the +temp file path or the pipe name, depending on the chosen operation mode. The temporary listing is +automatically generated within the FFConcat context. If the listing is send in via pipe, the +listing data can be obtained via ffconcat.input.

+

The listing can be populated either by parsing a valid ffconcat script via the constructor or +ffconcat.parse(). Or an individual item (file, stream, option, or chapter) can be added by +ffconcat.add_file(), ffconcat.add_stream(), ffconcat.add_option(), or +ffconcat.add_chapter(). Files can also be added in batch by ffconcat.add_files().

+

Aside from the intended operations with ffmpegprocess, a listing file can be explicitly created by +calling ffconcat.compose() with a valid writable text file object.

+

Alternately, the files in the listing can be used for a concat filtergraph use with as_filter().

+

Examples

+
    +
  1. Concatenate mkv files with a temp listing file

    +
    files = ['/video/video1.mkv','/video/video2.mkv']
    +ffconcat = ffmpegio.FFConcat()
    +ffconcat.add_files(files)
    +with ffconcat: # generates temporary ffconcat file
    +    ffmpegio.transcode(ffconcat, 'output.mkv', f_in='concat', safe_in=0)
    +
    +
    +

    Note that the files in an ffconcat listing file are defined relative to +to the location of the ffconcat file. As such, both video files must be +defined with absolute paths because the temporary ffconcat file is in a +tempdir. Because the absolute paths are given, safe_in=0 option must +be specified.

    +
  2. +
  3. Save generated ffconcat file in a same folder as the source video files

    +
    files = ['video1.mkv','video2.mkv']
    +ffconcat = ffmpegio.FFConcat(ffconcat_url='/video/concat.txt')
    +ffconcat.add_files(files)
    +with ffconcat: # generates ffconcat file at ffconcat_url
    +    ffmpegio.transcode(ffconcat, 'output.mkv', f_in='concat')
    +
    +
    +

    By creating the ffconcat listing file in the directory where the video files +are, the files in the listing can be defined relatively (i.e., just filenames). +FFConcat will overwrite the file if exists, and the generated ffconcat file +will not be deleted.

    +
  4. +
  5. Concatenate mkv files with listing piped to stdin

    +
    files = ['file:video1.mkv','file:video2.mkv']
    +ffconcat = ffmpegio.FFConcat(pipe_url='-')
    +ffconcat.add_files(files)
    +ffmpegio.transcode(ffconcat, 'output.mkv', f_in='concat',
    +                   protocol_whitelist_in="pipe,file", safe_in=0)
    +
    +
    +

    Because of files are specified by data passed in via pipe (protocol) +the files in the FFConcat must specify the protocol: file:. Also, +additional input options are necessary: protocol_whitelist_in=”pipe,file” +and safe_in=0.

    +
  6. +
  7. The concat script may be populated/altered inside the with statement, +but update() must be called to update the prepared script:

    +
    files = ['video1.mkv','video2.mkv']
    +with ffmpegio.FFConcat(ffconcat_url='/video/concat.txt') as ffconcat:
    +    ffconcat.add_files(files)
    +    ffconcat.update() # must call update() before transcode
    +    ffmpegio.transcode(ffconcat,'output.mkv', f_in='concat')
    +
    +
    +
  8. +
+
    +
  1. Rather than using demuxer, it can be used to compose concat filter command:

    +
    inputs,fg = ffconcat.as_filter(v=1, a=1)
    +
    +ffmpegio.ffmpeg(
    +    {
    +        "inputs": inputs,
    +        "outputs": [("output.mkv", None)],
    +        "global_options": {"filter_complex": fg},
    +    }
    +)
    +
    +
    +
  2. +
+
+
+class FileItem(filepath, duration=None, inpoint=None, outpoint=None, metadata=None, options=None)
+

File listing item

+
+
Parameters:
+
    +
  • filepath (str) – url of the file to be included

  • +
  • duration (str or numeric, optional) – duration of the file, defaults to None

  • +
  • inpoint (str or numeric, optional) – in point of the file, defaults to None

  • +
  • outpoint (str or numeric, optional) – out point of the file, defaults to None

  • +
  • metadata (dict, optional) – Metadata of the packets of the file, defaults to None

  • +
  • options (dict, optional) – Option to access, open and probe the file, defaults to None

  • +
+
+
+
+
+duration
+

duration of the file, optional

+
+
Type:
+

str or numeric or None

+
+
+
+ +
+
+inpoint
+

start time of the file, optional

+
+
Type:
+

str or numeric or None

+
+
+
+ +
+
+property lines
+

ffconcat lines of the file

+
+
Type:
+

List[str]

+
+
+
+ +
+
+metadata
+

metadata of the packets of the file, optional

+
+
Type:
+

dict or None

+
+
+
+ +
+
+options
+

option key-value pairs to be included

+
+
Type:
+

dict[str,Any]

+
+
+
+ +
+
+outpoint
+

end time of the file, optional

+
+
Type:
+

str or numeric or None

+
+
+
+ +
+
+path
+

url of the file

+
+
Type:
+

str

+
+
+
+ +
+ +
+
+class StreamItem(id=None, codec=None, metadata=None, extradata=None)
+

Stream listing item

+
+
Parameters:
+
    +
  • id (str, optional) – ID of the stream, defaults to None

  • +
  • codec (str, optional) – Codec for the stream, defaults to None

  • +
  • metadata (dict, optional) – Metadata for the stream, defaults to None

  • +
  • extradata (str or bytes-like, optional) – Extradata for the stream in hexadecimal, defaults to None

  • +
+
+
+
+
+codec
+

codec of the stream, optional

+
+
Type:
+

str or None

+
+
+
+ +
+
+extradata
+

extra data of the stream, optional

+
+
Type:
+

bytes or str or None

+
+
+
+ +
+
+id
+

id of the stream, optional

+
+
Type:
+

str or None

+
+
+
+ +
+
+property lines
+

ffconcat lines of the stream

+
+
Type:
+

List[str]

+
+
+
+ +
+
+metadata
+

of the stream, optional

+
+
Type:
+

dict or None

+
+
+
+ +
+ +
+
+add_chapter(id, start, end)
+

add a chapter

+
+
Parameters:
+
    +
  • id (str) – chapter ID

  • +
  • start (numeric or str) – start time

  • +
  • end (numeric or str) – end time

  • +
+
+
+
+ +
+
+add_file(filepath, duration=None, inpoint=None, outpoint=None, metadata=None, options=None)
+

append a file to the list

+
+
Parameters:
+
    +
  • filepath (str) – url of the file to be included

  • +
  • duration (str or numeric, optional) – duration of the file, defaults to None

  • +
  • inpoint (str or numeric, optional) – in point of the file, defaults to None

  • +
  • outpoint (str or numeric, optional) – out point of the file, defaults to None

  • +
  • metadata (dict, optional) – Metadata of the packets of the file, defaults to None

  • +
  • options (dict, optional) – Option to access, open and probe the file, defaults to None

  • +
+
+
+
+ +
+
+add_files(files)
+

append files to the list

+
+
Parameters:
+

files (Sequence[str]) – list of files

+
+
+
+ +
+
+add_glob(expr, root_dir=None, recursive=False)
+

append files with glob expression

+
+
Parameters:
+
    +
  • expr (str) – glob expression

  • +
  • root_dir (str, optional) – the root directory for searching, defaults to None (uses the current directory)

  • +
  • recursive (bool, optional) – True to use the pattern “**” to match any files and zero or more directories, defaults to False

  • +
+
+
+
+ +
+
+add_stream(id=None, codec=None, metadata=None, extradata=None)
+

append a stream specification to the list

+
+
Parameters:
+
    +
  • id (str, optional) – ID of the stream, defaults to None

  • +
  • codec (str, optional) – Codec for the stream, defaults to None

  • +
  • metadata (dict, optional) – Metadata for the stream, defaults to None

  • +
  • extradata (str or bytes-like, optional) – Extradata for the stream in hexadecimal, defaults to None

  • +
+
+
+
+ +
+
+as_filter(v=1, a=0, file_offset=0)
+

convert to concat filter commands

+
+
Parameters:
+
    +
  • v (int, optional) – number of video streams in each file, default to 1

  • +
  • a (int, optional) – number of audio streams in each file, default to 0

  • +
  • file_offset (int, optional) – id of the first file used in the filtergraph input labels

  • +
+
+
Returns:
+

inputs list and concat filtergraph string

+
+
Return type:
+

tuple[list[tuple[str,dict]], str]

+
+
+
+ +
+
+chapters
+

chapter id-(start,end) pairs to be included

+
+
Type:
+

dict[str,tuple]

+
+
+
+ +
+
+compose(f=None)
+

compose ffconcat file

+
+
Parameters:
+

f (File-like object, optional) – writable file-like object, defaults to None, outputting to a +StringIO object.

+
+
Returns:
+

passes through f or the created StringIO object

+
+
Return type:
+

File-like object

+
+
+
+ +
+
+ffconcat_url
+

specify url to save generated ffconcat file instead of a temp file

+
+
Type:
+

str|None

+
+
+
+ +
+
+property input
+

script as bytes

+
+
Type:
+

bytes

+
+
+
+ +
+
+property last_file
+

Last added file item

+
+
Type:
+

FFConcat.FileItem

+
+
+
+ +
+
+property last_stream
+

Last added stream item

+
+
Type:
+

FFConcat.StreamItem

+
+
+
+ +
+
+parse(script, append=False)
+

parse ffconcat script

+
+
Parameters:
+
    +
  • script (str) – ffconcat script

  • +
  • append (bool, optional) – True to append to the existing listing, False to clear +existing and start new, defaults to False

  • +
+
+
+
+ +
+
+pipe_url
+

specify pipe url if concat script to be loaded via stdin; None via a temp file

+
+
Type:
+

str|None

+
+
+
+ +
+
+property script
+

composed concat listing script

+
+
Type:
+

str

+
+
+
+ +
+
+streams
+

list of streams to be included in the order of appearance

+
+
Type:
+

ListConcatDemuxer.StreamItem]

+
+
+
+ +
+
+update()
+

Update the prepared script for the context

+
+ +
+
+property url
+

url to use as FFmpeg -i option

+
+
Type:
+

str

+
+
+
+ +
+ +
+ + +
+
+
+ +
+ +
+

© Copyright 2021-2022, Takeshi (Kesh) Ikuma, Louisiana State University Health Sciences Center.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/devices.html b/docs/devices.html new file mode 100644 index 00000000..03bfd5ac --- /dev/null +++ b/docs/devices.html @@ -0,0 +1,364 @@ + + + + + + + + + Hardware I/O Device Enumeration — python-ffmpegio 0.11.0 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Hardware I/O Device Enumeration

+

FFmpeg supports a number of hardware I/O devices, +from which video or audio data are read (sources) and to which data are written (sinks). +For each device, which is specified via -f option, some of device hardware name +must be obtained via FFmpeg commands:

+
ffmpeg -sources
+ffmpeg -sinks
+
+
+

If devices do not support these newer interfaces, via device-specific listing commands such as

+
ffmpeg -f dshow -list_devices true -i dummy
+ffmpeg -f avfoundation -list_devices true -i ""
+
+
+

Moreover, some devices provide a query interface for the capability of individual hardware:

+
ffmpeg -list_options true -f dshow -i video="Camera"
+ffmpeg -f video4linux2 -list_formats all /dev/video0
+
+
+

For multi-hardware use, the hardware configuration must be scanned and chosen for each computer +even within a same OS. ffmpegio.devices module is intended to abstract the hardware +selection process via unified naming scheme following the stream specifiers. Device supports +are implemented via plugin module, so user can implement interface for unsupported devices.

+
+

Note

+

Currently, only Windows DirectShow source device (-f dshow) is supported. Developing +device plugins, especially those on MacOS, requires user feedback and involvement. If +you want a specific device to be supported, please post +an issue on GitHub +to initiate the process.

+
+
+

How to Use

+

By default, ffmpegio does not scan the system for supported devices. User must +initialize the enumeration:

+
import ffmpegio
+
+ffmpegio.devices.scan()
+
+
+

Once the system is scanned, the lists of sources and sinks can be obtained:

+
sources = ffmpegio.devices.list_sources()
+
+
+

The returned variable is a dict:

+
{('dshow', 'a:0'): 'Microphone (Realtek High Definition Audio)',
+ ('dshow', 'v:0'): 'WebCam SC-10HDP12B24N'}
+
+
+

Given the enumeration, the enumerated device can be used as the url in any +ffmpegio functions interacting with FFmpeg. For example:

+
# capture 10 seconds of audio
+fs, x = ffmpegio.audio.read('a:0', f_in='dshow', t_in=10)
+
+# stream webcam video feed for
+with ffmpegio.open('v:0', 'vr', f_in='dshow') as dev:
+    for i, frame in enumerate(dev):
+        print(f'Frame {i}: {frame.shape}')
+
+# save video and audio to mp4 file
+# - if a device support multiple streams, specify their enums separated by '|'
+ffmpegio.transcode('v:0|a:0', 'captured.mp4', f_in='dshow', t_in=10)
+
+
+
+
+

References

+ + + + + + + + + + + + + + + + + + + + + + + + +

ffmpegio.devices.scan

scans the system for input/output hardware

ffmpegio.devices.list_sources

list enumerated source hardware devices

ffmpegio.devices.list_sinks

list enumerated sink hardware devices

ffmpegio.devices.list_source_options

list supported options of enumerated source hardware

ffmpegio.devices.list_sink_options

list supported options of enumerated sink hardware

ffmpegio.devices.resolve_source

resolve source enumeration

ffmpegio.devices.resolve_sink

resolve sink enumeration

+
+
+ffmpegio.devices.scan()
+

scans the system for input/output hardware

+

This function must be called by user to enable device enumeration in +ffmpegio. Also, none of functions in ffmpegio.devices module will return +meaningful outputs until scan is called. Likewise, scan() must +run again after a change in hardware to reflect the change.

+

The devices are enumerated according to the outputs of outputs +ffmpeg -sources and ffmpeg -sinks calls for the devices supporting +this fairly new FFmpeg interface. Additional hardware configurations +are detected by registered plugins with hooks device_source_api or +device_sink_api.

+
+

Currently Supported Devices

+

Windows: dshow +Mac: tbd +Linux: tbd

+
+
+ +
+
+ffmpegio.devices.list_sources(dev=None, mtype=None, return_nested=False)
+

list enumerated source hardware devices

+
+
Parameters:
+
    +
  • dev ("video", "audio", optional) – ffmpeg device name, defaults to None

  • +
  • mtype – media type, defaults to None

  • +
  • return_nested (bool, optional) – True to return results in nested dict, defaults to False

  • +
+
+
Returns:
+

dict of names of supported hardware, keyed by a tuple of the device name and enumeration, +or nested dicts. If dev is specified, dict of enumerated hardware devices and their names

+
+
Return type:
+

dict(tuple(str,str),str) or dict(str,dict(str,str)) or dict(str,str)

+
+
+
+ +
+
+ffmpegio.devices.list_sinks(dev=None, mtype=None, return_nested=False)
+

list enumerated sink hardware devices

+
+
Parameters:
+
    +
  • dev ("video", "audio", optional) – ffmpeg device name, default to None

  • +
  • mtype – media type, default to None

  • +
  • return_nested (bool, optional) – True to return results in nested dict, defaults to False

  • +
+
+
Returns:
+

dict of names of supported hardware, keyed by a tuple of the device name and enumeration, +or nested dicts. If dev is specified, dict of enumerated hardware devices and their names

+
+
Return type:
+

dict(tuple(str,str),str) or dict(str,dict(str,str)) or dict(str,str)

+
+
+
+ +
+
+ffmpegio.devices.list_source_options(device, enum)
+

list supported options of enumerated source hardware

+
+
Parameters:
+
    +
  • device (str) – device name

  • +
  • enum (str) – hardware specifier, e.g., v:0, a:0

  • +
+
+
Returns:
+

list of supported option combinations. If option values are tuple +it indicates the min and max range of the option value.

+
+
Return type:
+

list[dict]

+
+
+
+ +
+
+ffmpegio.devices.list_sink_options(device, enum)
+

list supported options of enumerated sink hardware

+
+
Parameters:
+
    +
  • device (str) – device name

  • +
  • enum (str) – hardware specifier, e.g., v:0, a:0

  • +
+
+
Returns:
+

list of supported option combinations. If option values are tuple +it indicates the min and max range of the option value.

+
+
Return type:
+

list[dict]

+
+
+
+ +
+
+ffmpegio.devices.resolve_source(url, opts)
+

resolve source enumeration

+
+
Parameters:
+
    +
  • url (str) – input url, possibly device enum

  • +
  • opts (dict) – input options

  • +
+
+
Returns:
+

possibly modified url and opts

+
+
Return type:
+

tuple[str,dict]

+
+
+

This function is called by ffmpeg.compose() to convert +device enumeration back to url expected by ffmpeg

+

The device name (-f) could be provided via opts[‘f’] or encoded as a +part of enumeration

+
+ +
+
+ffmpegio.devices.resolve_sink(url, opts)
+

resolve sink enumeration

+
+
Parameters:
+
    +
  • url (str) – output url, possibly device enum

  • +
  • opts (dict) – output options

  • +
+
+
Returns:
+

possibly modified url and opts

+
+
Return type:
+

tuple[str,dict]

+
+
+

This function is called by ffmpeg.compose() to convert +device enumeration back to url expected by ffmpeg

+
+ +
+
+ + +
+
+
+ +
+ +
+

© Copyright 2021-2022, Takeshi (Kesh) Ikuma, Louisiana State University Health Sciences Center.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/filtergraph.html b/docs/filtergraph.html new file mode 100644 index 00000000..405f7b4e --- /dev/null +++ b/docs/filtergraph.html @@ -0,0 +1,3698 @@ + + + + + + + + + Filtergraph Builder Reference — python-ffmpegio 0.11.0 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Filtergraph Builder Reference

+

One of the great feature of FFmpeg is the plethora of filters to manipulate video and audio data. +See the official FFmpeg Filters Documentation and +FFmpeg Wiki articles on Filtering.

+

All the media I/O operations in ffmpegio support FFmpeg filtering via per-stream +filter, vf, af, and filter_script output options as well as the filter_complex and +filter_complex_script global option. These options are typically specified by filtergraph +expression strings. For example, 'scale=iw/2:-1' to reduce the video frame size by half. Multiple +operations can be performed in sequence by chaining the filters, e.g., 'afade=t=in:d=1,afade=t=out:st=9:d=1' +adds fade-in and fade-out effect to an audio stream. More complex filtergraph with multiple chains +can also be specified, but as the complexity increases the expression length also increases. +The ffmpegio.filtergraph submodule is designed to assist building complex filtergraphs. The +module serves 3 primary functions:

+ +

These functions are served by three classes:

+ + + + + + + + + + + + +

ffmpegio.filtergraph.Filter

FFmpeg filter definition immutable class

ffmpegio.filtergraph.Chain

List of FFmpeg filters, connected in series

ffmpegio.filtergraph.Graph

List of FFmpeg filterchains in parallel with interchain link specifications

+

See Filtergraph API Reference section below for the full documentation of these classes +and other helper functions.

+

All filtergraph classes can be instantiated with a valid filtergraph description string and yield +filtergraph descriptions when converted to str.

+
>>> import ffmpegio.filtergraph as fgb
+>>> 
+>>> # for a simple chain, use either Chain or Graph
+>>> fgb.Chain('afade=t=in:d=1,afade=t=out:st=9:d=1')
+<ffmpegio.filtergraph.Chain.Chain object at 0x7f95df033110>
+    FFmpeg expression: "[UNC0]afade=t=in:d=1[UNC2];[UNC1]afade=t=out:st=9:d=1[UNC3]"
+    Number of filters: 2
+    Input pads (1): (0, 0)
+    Output pads: (1): (1, 0)
+
+>>> fgb.Graph('afade=t=in:d=1,afade=t=out:st=9:d=1')
+<ffmpegio.filtergraph.Graph.Graph object at 0x7f95df031970>
+    FFmpeg expression: "afade=t=in:d=1,afade=t=out:st=9:d=1"
+    Number of chains: 1
+      chain[0]: [UNC0]afade=t=in:d=1,afade=t=out:st=9:d=1[UNC1]      
+    Available input pads (1): (0, 0, 0)
+    Available output pads: (1): (0, 1, 0)
+
+>>> 
+>>> # construct the chain from filters
+>>> fgb.Filter('afade=t=in:d=1') + fgb.Filter('afade=t=out:st=9:d=1')
+<ffmpegio.filtergraph.Chain.Chain object at 0x7f95cfe9e3f0>
+    FFmpeg expression: "[UNC0]afade=t=in:d=1[UNC2];[UNC1]afade=t=out:st=9:d=1[UNC3]"
+    Number of filters: 2
+    Input pads (1): (0, 0)
+    Output pads: (1): (1, 0)
+
+
+

All ffmpegio functions that take filter options accept these objects as input arguments +and convert to str internally:

+
>>> fs, x = ffmpegio.audio.read('input.mp3', af=fg)
+>>> # x contains the audio samples with the fading effects
+
+
+
+

Note

+

The simplified examples on this pages are for illustration purpose only. If a filtergraph is +simple and does not require programmatic construction, use plain :py:class`str` expressions to +improve the runtime speed.

+
+
+

Accessing filter information on FFmpeg

+

Filters can be instantiated in a several different ways:

+
    +
  • fgb.Filter constructor with option values as arguments

  • +
  • fgb.Filter constructor with a single-filter filtergraph description

  • +
  • fgb.<filter_name> dynamic function (where <filter_name>> is the +name of a FFmpeg filter)

  • +
+

For example, a crop filter crop=in_w-100:in_h-100:x=100:y=100 can be created +by any of the following 3 lines:

+
>>> fgb.Filter('crop', 'in_w-100', 'in_h-100', x=100, y=100)
+Traceback (most recent call last):
+  File "<stdin>", line 1, in <module>
+  File "/home/runner/work/python-ffmpegio/python-ffmpegio/src/ffmpegio/filtergraph/Filter.py", line 164, in __repr__
+    FFmpeg expression: \"{self.compose(True,True)}\"
+                          ^^^^^^^^^^^^^^^^^^^^^^^
+  File "/home/runner/work/python-ffmpegio/python-ffmpegio/src/ffmpegio/filtergraph/Filter.py", line 154, in compose
+    fgb.Graph(self.data).compose(
+              ^^^^^^^^^
+AttributeError: 'Filter' object has no attribute 'data'
+>>> fgb.Filter('crop=in_w-100:in_h-100:x=100:y=100')
+Traceback (most recent call last):
+  File "<stdin>", line 1, in <module>
+  File "/home/runner/work/python-ffmpegio/python-ffmpegio/src/ffmpegio/filtergraph/Filter.py", line 164, in __repr__
+    FFmpeg expression: \"{self.compose(True,True)}\"
+                          ^^^^^^^^^^^^^^^^^^^^^^^
+  File "/home/runner/work/python-ffmpegio/python-ffmpegio/src/ffmpegio/filtergraph/Filter.py", line 154, in compose
+    fgb.Graph(self.data).compose(
+              ^^^^^^^^^
+AttributeError: 'Filter' object has no attribute 'data'
+>>> fgb.crop('in_w-100', 'in_h-100', x=100, y=100)
+Traceback (most recent call last):
+  File "<stdin>", line 1, in <module>
+  File "/home/runner/work/python-ffmpegio/python-ffmpegio/src/ffmpegio/filtergraph/Filter.py", line 164, in __repr__
+    FFmpeg expression: \"{self.compose(True,True)}\"
+                          ^^^^^^^^^^^^^^^^^^^^^^^
+  File "/home/runner/work/python-ffmpegio/python-ffmpegio/src/ffmpegio/filtergraph/Filter.py", line 154, in compose
+    fgb.Graph(self.data).compose(
+              ^^^^^^^^^
+AttributeError: 'Filter' object has no attribute 'data'
+
+
+

The :py:func`fgb.crop` function is dynamically created when user call it for the +first time. If the function name fails to resolve an FFmpeg filter, an +AttributeError will be raised.

+

In addition, these dynamic functions get FFmpeg filter help text as their docstrings:

+
>>> help(fgb.crop)
+Help on function crop in module ffmpegio.filtergraph:
+
+crop(*args, filter_id=None, **kwargs)
+    Filter crop
+      Crop the input video.
+        Inputs:
+           #0: default (video)
+        Outputs:
+           #0: default (video)
+    crop AVOptions:
+       out_w             <string>     ..FV.....T. set the width crop area expression (default "iw")
+       w                 <string>     ..FV.....T. set the width crop area expression (default "iw")
+       out_h             <string>     ..FV.....T. set the height crop area expression (default "ih")
+       h                 <string>     ..FV.....T. set the height crop area expression (default "ih")
+       x                 <string>     ..FV.....T. set the x crop area expression (default "(in_w-out_w)/2")
+       y                 <string>     ..FV.....T. set the y crop area expression (default "(in_h-out_h)/2")
+       keep_aspect       <boolean>    ..FV....... keep aspect ratio (default false)
+       exact             <boolean>    ..FV....... do exact cropping (default false)
+
+
+

Use ffmpegio.caps.filters() to get the full list of filters supported by the installed +FFmpeg and ffmpegio.caps.filter_info() to get a parsed version of the filter help text.

+
+
+

Constructing filtergraphs

+

A complex filtergraph can be authored using a combination of Filter, Chain, +and Graph. The following 4 operators are defined:

+ + + + + + + + + + + + + + + + + + + + +

Operator

Description

|

Stack sections (no linking)

* n

Create n copies of itself and stack them

+

Join filtergraph sections

>>

Point-to-point connection and pad labeling

+

Other useful filtergraph manipulation class methods are:

+ + + +
+

This section mainly describes the operators, leaving the details of the class methods to the API +reference section later on this page.

+
+

|: filtegraph stacking

+

Stacking operation creates a new Graph object from two filtergraph objects, orienting +them in parallel without making any connections. The left and right sides do not need to be of the +same class, and they can be mixed and matched.

+
>>> # 1. given 2 filters
+>>> fgb.trim(30, 60) | fgb.trim(90, 120)
+<ffmpegio.filtergraph.Graph.Graph object at 0x7f95cfcd2060>
+    FFmpeg expression: "trim=30:60;trim=90:120"
+    Number of chains: 2
+      chain[0]: [UNC0]trim=30:60[UNC2];
+      chain[1]: [UNC1]trim=90:120[UNC3]      
+    Available input pads (2): (0, 0, 0), (1, 0, 0)
+    Available output pads: (2): (0, 0, 0), (1, 0, 0)
+
+>>> 
+>>> # 2. given 2 chains
+>>> fgb.Chain('trim=30:60,scale=200:-1') | fgb.Chain('atrim=30:60,afade=t=in')
+<ffmpegio.filtergraph.Graph.Graph object at 0x7f95df0319a0>
+    FFmpeg expression: "trim=30:60,scale=200:-1;atrim=30:60,afade=t=in"
+    Number of chains: 2
+      chain[0]: [UNC0]trim=30:60,scale=200:-1[UNC2];
+      chain[1]: [UNC1]atrim=30:60,afade=t=in[UNC3]      
+    Available input pads (2): (0, 0, 0), (1, 0, 0)
+    Available output pads: (2): (0, 1, 0), (1, 1, 0)
+
+>>> 
+>>> # 3. given 2 graphs
+>>> fgb.Graph('[0:v]trim=30:60,scale=200:-1[out]') | fgb.Graph('[0:a]atrim=30:60,afade=t=in[out]')
+Traceback (most recent call last):
+  File "/home/runner/work/python-ffmpegio/python-ffmpegio/src/ffmpegio/filtergraph/Graph.py", line 835, in _stack
+    fg._links.update(
+  File "/home/runner/work/python-ffmpegio/python-ffmpegio/src/ffmpegio/filtergraph/GraphLinks.py", line 820, in update
+    fglinks.create_label(l, i, o, force)
+  File "/home/runner/work/python-ffmpegio/python-ffmpegio/src/ffmpegio/filtergraph/GraphLinks.py", line 656, in create_label
+    label = self._resolve_label(label, force=force, check_stream_spec=False)
+            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+  File "/home/runner/work/python-ffmpegio/python-ffmpegio/src/ffmpegio/filtergraph/GraphLinks.py", line 337, in _resolve_label
+    raise GraphLinks.Error(f"{label=} is already in use.")
+ffmpegio.filtergraph.GraphLinks.GraphLinks.Error: label='out' is already in use.
+
+The above exception was the direct cause of the following exception:
+
+Traceback (most recent call last):
+  File "<stdin>", line 1, in <module>
+  File "/home/runner/work/python-ffmpegio/python-ffmpegio/src/ffmpegio/filtergraph/abc.py", line 653, in __or__
+    return fgb.stack(self, other)
+           ^^^^^^^^^^^^^^^^^^^^^^
+  File "/home/runner/work/python-ffmpegio/python-ffmpegio/src/ffmpegio/filtergraph/build.py", line 347, in stack
+    fg = fg._stack(fgb.as_filtergraph_object(other), auto_link, replace_sws_flags)
+         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+  File "/home/runner/work/python-ffmpegio/python-ffmpegio/src/ffmpegio/filtergraph/Graph.py", line 842, in _stack
+    raise Graph.Error(e) from e
+ffmpegio.filtergraph.Graph.Graph.Error: label='out' is already in use.
+
+
+
+

Note

+

Duplicate link labels are automatically renamed with a trailing counter.

+
+
+
+

* n: filtergraph self-stacking

+

Like Python lists and tuples, multipling any filtergraph object by an integer creates a +Graph object containing n copies of the object and stack them (i.e., create parallel +chains).

+
>>> # multiplying filters
+>>> fgb.crop(100,100) * 3
+<ffmpegio.filtergraph.Graph.Graph object at 0x7f95cfcd2360>
+    FFmpeg expression: "crop=100:100;crop=100:100;crop=100:100"
+    Number of chains: 3
+      chain[0]: [UNC0]crop=100:100[UNC3];
+      chain[1]: [UNC1]crop=100:100[UNC4];
+      chain[2]: [UNC2]crop=100:100[UNC5]      
+    Available input pads (3): (0, 0, 0), (1, 0, 0), (2, 0, 0)
+    Available output pads: (3): (0, 0, 0), (1, 0, 0), (2, 0, 0)
+
+>>> 
+>>> # multiplying chains
+>>> fgb.Chain('fps=30,format=yuv420p') * 2
+<ffmpegio.filtergraph.Graph.Graph object at 0x7f95cfcd25a0>
+    FFmpeg expression: "fps=30,format=yuv420p;fps=30,format=yuv420p"
+    Number of chains: 2
+      chain[0]: [UNC0]fps=30,format=yuv420p[UNC2];
+      chain[1]: [UNC1]fps=30,format=yuv420p[UNC3]      
+    Available input pads (2): (0, 0, 0), (1, 0, 0)
+    Available output pads: (2): (0, 1, 0), (1, 1, 0)
+
+>>> 
+>>> # multiplying graphs
+>>> fgb.Graph('color,[0]overlay[vout]') * 2
+Traceback (most recent call last):
+  File "/home/runner/work/python-ffmpegio/python-ffmpegio/src/ffmpegio/filtergraph/Graph.py", line 835, in _stack
+    fg._links.update(
+  File "/home/runner/work/python-ffmpegio/python-ffmpegio/src/ffmpegio/filtergraph/GraphLinks.py", line 820, in update
+    fglinks.create_label(l, i, o, force)
+  File "/home/runner/work/python-ffmpegio/python-ffmpegio/src/ffmpegio/filtergraph/GraphLinks.py", line 656, in create_label
+    label = self._resolve_label(label, force=force, check_stream_spec=False)
+            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+  File "/home/runner/work/python-ffmpegio/python-ffmpegio/src/ffmpegio/filtergraph/GraphLinks.py", line 337, in _resolve_label
+    raise GraphLinks.Error(f"{label=} is already in use.")
+ffmpegio.filtergraph.GraphLinks.GraphLinks.Error: label='vout' is already in use.
+
+The above exception was the direct cause of the following exception:
+
+Traceback (most recent call last):
+  File "<stdin>", line 1, in <module>
+  File "/home/runner/work/python-ffmpegio/python-ffmpegio/src/ffmpegio/filtergraph/abc.py", line 643, in __mul__
+    return fgb.stack(*((self,) * __n))
+           ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+  File "/home/runner/work/python-ffmpegio/python-ffmpegio/src/ffmpegio/filtergraph/build.py", line 347, in stack
+    fg = fg._stack(fgb.as_filtergraph_object(other), auto_link, replace_sws_flags)
+         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+  File "/home/runner/work/python-ffmpegio/python-ffmpegio/src/ffmpegio/filtergraph/Graph.py", line 842, in _stack
+    raise Graph.Error(e) from e
+ffmpegio.filtergraph.Graph.Graph.Error: label='vout' is already in use.
+
+
+
+

Note

+

Multiplied link labels receive unique labels with trailing counter.

+
+
+
+

+: filtergraph joining

+

Join operation connects two filtergraph objects by auto-linking the available output +pads of the left side and the available input pads of the right side. The output object type depends +on the input types.

+

Joining a single-output object to a single-input object connection is trivial. If both are of either +Filter or Chain classes, they are joined in series, resulting in +Chain object. If Graph is involved, the joining chain is extended with the +other object.

+
>>> # 1. joining 2 filters:
+>>> fgb.trim(60,120) + fgb.Chain('crop=100:100:12:34,fps=30')
+<ffmpegio.filtergraph.Chain.Chain object at 0x7f95cfcd20f0>
+    FFmpeg expression: "[UNC0]trim=60:120[UNC3];[UNC1]crop=100:100:12:34[UNC4];[UNC2]fps=30[UNC5]"
+    Number of filters: 3
+    Input pads (1): (0, 0)
+    Output pads: (1): (2, 0)
+
+>>> 
+>>> # 2. joining 2 graphs:
+>>> fgb.Graph('[0]fps=30[v0];[v0]overlay') + fgb.Graph('split[v0][v1];[v1]hflip')
+<ffmpegio.filtergraph.Graph.Graph object at 0x7f95cfcd2270>
+    FFmpeg expression: "[0]fps=30[v0];[v0]overlay[L0];[L0]split[v1];[v1]hflip"
+    Number of chains: 4
+      chain[0]: [0]fps=30[v0];
+      chain[1]: [v0][UNC0]overlay[L0];
+      chain[2]: [L0]split[UNC1][v1];
+      chain[3]: [v1]hflip[UNC2]      
+    Available input pads (1): (1, 0, 1)
+    Available output pads: (2): (2, 0, 0), (3, 0, 0)
+
+
+

Joining multiple-output Graph object with multiple-input Graph object yields +a Graph object. The number of exposed filter pads must match on both sides. The pad +pairing is automatically performed in one of the two possible ways:

+
    +
  1. +
    pairs the first unused output filter pad of each chain of the left filtergraph and the

    first unused input filter pad of each chain of the right filtergraph (per-chain)

    +
    +
    +
  2. +
  3. pairs all the unused filter pads of the left and right filtergraphs (all)

  4. +
+

Both pairing types require the two sides to have the matching number of unused pads. If no match is +attained per chain, then the all unused pads are paired. This mechanism allows the + operator to +support two important usecases involving branching and merging filters such as overlay, +concat, split, and asplit. The following examples demonstrate these cases:

+
>>> # case 1: attaching a chain of one side to one of the multiple pads of the other
+>>> fgb.hflip() + fgb.hstack()
+<ffmpegio.filtergraph.Graph.Graph object at 0x7f95cfcd27b0>
+    FFmpeg expression: "hflip[L0];[L0]hstack"
+    Number of chains: 2
+      chain[0]: [UNC0]hflip[L0];
+      chain[1]: [L0][UNC1]hstack[UNC2]      
+    Available input pads (2): (0, 0, 0), (1, 0, 1)
+    Available output pads: (1): (1, 0, 0)
+
+>>> 
+>>> # case 2: connecting all the chains (one unused pad each) of one side to a filter with
+>>> #         matching number of pads on the other side
+>>> (fgb.hflip() | fgb.vflip()) + fgb.hstack()
+<ffmpegio.filtergraph.Graph.Graph object at 0x7f95cfcd2960>
+    FFmpeg expression: "hflip[L0];vflip[L1];[L0][L1]hstack"
+    Number of chains: 3
+      chain[0]: [UNC0]hflip[L0];
+      chain[1]: [UNC1]vflip[L1];
+      chain[2]: [L0][L1]hstack[UNC2]      
+    Available input pads (2): (0, 0, 0), (1, 0, 0)
+    Available output pads: (1): (2, 0, 0)
+
+
+
+

Note

+

If joining results in a multi-chain filtergraph, inter-chain links are unnamed, and when +converted to :py:class:str the unnamed links uses L# link names.

+
+
+

Note

+

Be aware of the operator precedence. +That is, * precedes +, and + precedes |.

+
+

When joining filtergraph objects with multiple inputs and outputs, +

+
+
+

>> filtergraph labeling / filtergraph p2p linking

+

The >> is a multi-purpose operator to label a filter pad and to stack two filtergraphs +with a single link between them. It also accepts optionally explicit filter pad id’s to override the +default selection policty of the first unused filter pad.

+

Simple usecases are:

+
>>> # label input and output pads to a SISO filtergraph
+>>> '[in]' >> fgb.scale(100,-2) >> '[out]'
+<ffmpegio.filtergraph.Graph.Graph object at 0x7f95cfcd23f0>
+    FFmpeg expression: "[in]scale=100:-2[out]"
+    Number of chains: 1
+      chain[0]: [in]scale=100:-2[out]      
+    Available input pads (1): (0, 0, 0)
+    Available output pads: (1): (0, 0, 0)
+
+>>> 
+>>> # connect 2 filtergraphs with the first available filter pads
+>>> fgb.hflip() >> fgb.concat()
+<ffmpegio.filtergraph.Graph.Graph object at 0x7f95cfcd21b0>
+    FFmpeg expression: "hflip[L0];[L0]concat"
+    Number of chains: 2
+      chain[0]: [UNC0]hflip[L0];
+      chain[1]: [L0][UNC1]concat[UNC2]      
+    Available input pads (2): (0, 0, 0), (1, 0, 1)
+    Available output pads: (1): (1, 0, 0)
+
+
+
+

Filter pad labeling

+

To label a filter pad, the label string must be fully specified with the square brackets:

+
# valid label strings
+'[in]' >> fg   # valid FFmpeg link label (alphanumeric characters + '_' inside '[]')
+'[0:v]' >> fg  # valid FFmpeg stream specifier (the first video stream of the first input url)
+
+# incorrect label strings
+'in' >> fg  # create an "in" Filter object (not a valid FFmpeg filter)
+'0:v' >> fg # fails to parse the string as a filtergraph
+
+
+

To label multiple pads at once, provide a sequence of labels:

+
>>> ['[0:v]','[1:v]'] >> fgb.Chain('overlay,split') >> ['[vout1]','[vout2]']
+<ffmpegio.filtergraph.Graph.Graph object at 0x7f95cfcd2ea0>
+    FFmpeg expression: "[0:v][1:v]overlay,split[vout1][vout2]"
+    Number of chains: 1
+      chain[0]: [0:v][1:v]overlay,split[vout1][vout2]      
+    Available input pads (0): 
+    Available output pads: (2): (0, 1, 0), (0, 1, 1)
+
+
+

The pads do not need to be of the same filter:

+
>>> ['[0:v]','[1:v]'] >> fgb.Graph('pad=640:480[v1];scale=100:100[v2];[v1][v2]overlay')
+<ffmpegio.filtergraph.Graph.Graph object at 0x7f95cfcd30b0>
+    FFmpeg expression: "[0:v]pad=640:480[v1];[1:v]scale=100:100[v2];[v1][v2]overlay"
+    Number of chains: 3
+      chain[0]: [0:v]pad=640:480[v1];
+      chain[1]: [1:v]scale=100:100[v2];
+      chain[2]: [v1][v2]overlay[UNC0]      
+    Available input pads (0): 
+    Available output pads: (1): (2, 0, 0)
+
+
+
+
+

Filtergraph linking

+

Functionally, >> and + are the same if both sides of the operator expose only +one pad. So, they can be used interchangeably.

+
>>> # following two operations produce the same filtergraph
+>>> fgb.hflip() >> fgb.vflip()
+<ffmpegio.filtergraph.Chain.Chain object at 0x7f95cfcd2fc0>
+    FFmpeg expression: "[UNC0]hflip[UNC2];[UNC1]vflip[UNC3]"
+    Number of filters: 2
+    Input pads (1): (0, 0)
+    Output pads: (1): (1, 0)
+
+>>> fgb.hflip() + fgb.vflip()
+<ffmpegio.filtergraph.Chain.Chain object at 0x7f95cfcd30b0>
+    FFmpeg expression: "[UNC0]hflip[UNC2];[UNC1]vflip[UNC3]"
+    Number of filters: 2
+    Input pads (1): (0, 0)
+    Output pads: (1): (1, 0)
+
+
+

The >> operator is primarily designed to attach a filter or a filterchain to a larger +filtergraph with multiple pads.

+
>>> # a 4-input graph with the first one connected to an input stream
+>>> fg = fgb.Graph('[0:v]hstack[h1];hstack[h2];[h1][h2]vstack')
+>>> 
+>>> # add the zoomed version as the second input
+>>> fgb.Graph('[0:v]crop,scale') >> fg
+Traceback (most recent call last):
+  File "<stdin>", line 1, in <module>
+  File "/home/runner/work/python-ffmpegio/python-ffmpegio/src/ffmpegio/filtergraph/abc.py", line 708, in __rshift__
+    return fgb.attach(self, right, left_on, right_on)
+           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+  File "/home/runner/work/python-ffmpegio/python-ffmpegio/src/ffmpegio/filtergraph/build.py", line 252, in attach
+    raise ValueError(
+ValueError: Cannot determine which side is attaching. One of left or right argument must be a Filter or Chain object.
+>>> # -> [0:v]crop,scale[L1];[0:v][L1]hstack[h1];hstack[h2];[h1][h2]vstack
+
+
+
+
+

Filter pad indexing

+

In some cases linking of the filter pads may not happen in a top-down order. It is also possible to +specify which filter pad to label or to connect.

+

First, here is the the automatic pad selection rules:

+
    +
  • Unused filter pad is searched on filterchains in sequence

  • +
  • On the selected filterchain on the left side of >>

    +
      +
    • The first filter with an unused input pad is selected

    • +
    • The first unused input pad on the selected filter is selected

    • +
    +
  • +
  • On the selected filterchain on the right side of >>

    +
      +
    • The last filter with an unused output pad is selected

    • +
    • The first unused output pad on the selected filter is selected

    • +
    +
  • +
+

These rules apply to both labeling and linking. Here are a couple examples to illustrate +the order of pad selection:

+
>>> ["[in1]", "[in2]", "[in3]", "[in4]"] >> fgb.Graph("overlay,overlay;hflip")
+<ffmpegio.filtergraph.Graph.Graph object at 0x7f95cfcd3110>
+    FFmpeg expression: "[in1][in2]overlay,[in3]overlay;[in4]hflip"
+    Number of chains: 2
+      chain[0]: [in1][in2]overlay,[in3]overlay[UNC0];
+      chain[1]: [in4]hflip[UNC1]      
+    Available input pads (4): (0, 0, 0), (0, 0, 1), (0, 1, 0), (1, 0, 0)
+    Available output pads: (2): (0, 1, 0), (1, 0, 0)
+
+>>> 
+>>> fgb.Chain("split,split") >> "[label1]" >> "[label2]" >> "[label3]"
+Traceback (most recent call last):
+  File "<stdin>", line 1, in <module>
+  File "/home/runner/work/python-ffmpegio/python-ffmpegio/src/ffmpegio/filtergraph/abc.py", line 708, in __rshift__
+    return fgb.attach(self, right, left_on, right_on)
+           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+  File "/home/runner/work/python-ffmpegio/python-ffmpegio/src/ffmpegio/filtergraph/build.py", line 302, in attach
+    return left_objs_labels._attach(right_objs_labels, left_on, right_on)
+           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+  File "/home/runner/work/python-ffmpegio/python-ffmpegio/src/ffmpegio/filtergraph/abc.py", line 1020, in _attach
+    fg.add_label(r, outpad=l_idx)
+  File "/home/runner/work/python-ffmpegio/python-ffmpegio/src/ffmpegio/filtergraph/Graph.py", line 729, in add_label
+    self._links.create_label(label, inpad, outpad, force)
+  File "/home/runner/work/python-ffmpegio/python-ffmpegio/src/ffmpegio/filtergraph/GraphLinks.py", line 699, in create_label
+    raise GraphLinks.Error(
+ffmpegio.filtergraph.GraphLinks.GraphLinks.Error: pad_in_use='label1' is already using the specified pad: (0, 0, 0)
+
+
+

To specify the connecting pads, accompany the label or attaching filtergraph with +the filter pad index:

+
>>> ("[in]", (0,1,1)) >> fgb.Graph("overlay,overlay;hflip")
+Traceback (most recent call last):
+  File "<stdin>", line 1, in <module>
+  File "/home/runner/work/python-ffmpegio/python-ffmpegio/src/ffmpegio/filtergraph/abc.py", line 758, in __rrshift__
+    return fgb.attach(left, self, left_on, right_on)
+           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+  File "/home/runner/work/python-ffmpegio/python-ffmpegio/src/ffmpegio/filtergraph/build.py", line 297, in attach
+    right_on, left_on = resolve_indices(
+                        ^^^^^^^^^^^^^^^^
+  File "/home/runner/work/python-ffmpegio/python-ffmpegio/src/ffmpegio/filtergraph/build.py", line 271, in resolve_indices
+    base_indices = base.resolve_pad_indices(base_indices, is_input=base_is_input)
+                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+  File "/home/runner/work/python-ffmpegio/python-ffmpegio/src/ffmpegio/filtergraph/abc.py", line 914, in resolve_pad_indices
+    self.resolve_pad_index(
+  File "/home/runner/work/python-ffmpegio/python-ffmpegio/src/ffmpegio/filtergraph/Graph.py", line 200, in resolve_pad_index
+    return super().resolve_pad_index(
+           ^^^^^^^^^^^^^^^^^^^^^^^^^^
+  File "/home/runner/work/python-ffmpegio/python-ffmpegio/src/ffmpegio/filtergraph/abc.py", line 882, in resolve_pad_index
+    raise FiltergraphPadNotFoundError(
+ffmpegio.filtergraph.exceptions.FiltergraphPadNotFoundError: index_or_label=(0, 1, 1) is either already connected or invalid input pad.
+>>> 
+>>> fgb.Chain("split,split") >> ((0,-1,1), "[label]")
+Traceback (most recent call last):
+  File "<stdin>", line 1, in <module>
+  File "/home/runner/work/python-ffmpegio/python-ffmpegio/src/ffmpegio/filtergraph/abc.py", line 708, in __rshift__
+    return fgb.attach(self, right, left_on, right_on)
+           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+  File "/home/runner/work/python-ffmpegio/python-ffmpegio/src/ffmpegio/filtergraph/build.py", line 293, in attach
+    left_on, right_on = resolve_indices(
+                        ^^^^^^^^^^^^^^^^
+  File "/home/runner/work/python-ffmpegio/python-ffmpegio/src/ffmpegio/filtergraph/build.py", line 271, in resolve_indices
+    base_indices = base.resolve_pad_indices(base_indices, is_input=base_is_input)
+                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+  File "/home/runner/work/python-ffmpegio/python-ffmpegio/src/ffmpegio/filtergraph/abc.py", line 914, in resolve_pad_indices
+    self.resolve_pad_index(
+  File "/home/runner/work/python-ffmpegio/python-ffmpegio/src/ffmpegio/filtergraph/abc.py", line 882, in resolve_pad_index
+    raise FiltergraphPadNotFoundError(
+ffmpegio.filtergraph.exceptions.FiltergraphPadNotFoundError: index_or_label=(0, -1, 1) is either already connected or invalid output pad.
+
+
+

The filter pad index is given by a three-element tuple:

+
# filter pad index (tuple of 3 ints)
+
+(i, j, k)
+# i -> chain index, selecting the (i+1)st chain
+# j -> filter index on the (i+1)st chain
+# k -> (input or output) pad index of the (j+1)st filter
+
+
+

So, the first example (0,1,1) selects the 1st chain’s 2nd filter (overlay) +and label its 2nd input pad [in3]. Negative indices (as used for Python +sequences) are supported. The second example (0,-1,1) selects +the 1st chain’s last filter and labels its 2nd output as [label3].

+

Alternatively, an existing label could be used to specify the connecting pad:

+
>>> fg_overlay = fgb.Chain("scale=240:-2,format=gray")
+>>> fg1 = fgb.Graph("[in1][in2]overlay,[in3]overlay;[in4]hflip")
+>>> 
+>>> (fg_overlay,'in2') >> fg1
+<ffmpegio.filtergraph.Graph.Graph object at 0x7f95cfcd33e0>
+    FFmpeg expression: "scale=240:-2,format=gray[L0];[in1][L0]overlay,[in3]overlay;[in4]hflip"
+    Number of chains: 3
+      chain[0]: [UNC0]scale=240:-2,format=gray[L0];
+      chain[1]: [in1][L0]overlay,[in3]overlay[UNC1];
+      chain[2]: [in4]hflip[UNC2]      
+    Available input pads (4): (0, 0, 0), (1, 0, 0), (1, 1, 0), (2, 0, 0)
+    Available output pads: (2): (1, 1, 0), (2, 0, 0)
+
+
+

The label name for indexing may optionally omit the square brackets as done in this example.

+
+
+ +
+

Examples

+
+

Simple example

+

Borrowing the example from ffmpeg-python package:

+
[0]trim=start_frame=10:end_frame=20[v0]; \
+[0]trim=start_frame=30:end_frame=40[v1]; \
+[1]hflip[v2]; \
+[v0][v1]concat=n=2[v3]; \
+[v3][v2]overlay=eof_action=repeat, drawbox=50:50:120:120:red:t=5[v5]
+
+
+

This filtergraph can be built in the following steps:

+
>>> v0 = "[0]" >> fgb.trim(start_frame=10, end_frame=20)
+>>> v1 = "[0]" >> fgb.trim(start_frame=30, end_frame=40)
+>>> v3 = "[1]" >> fgb.hflip()
+>>> v2 = (v0 | v1) + fgb.concat(2)
+>>> v5 = (v2|v3) + fgb.overlay(eof_action='repeat') + fgb.drawbox(50, 50, 120, 120, 'red', t=5)
+>>> v5
+<ffmpegio.filtergraph.Graph.Graph object at 0x7f95cfcd3920>
+    FFmpeg expression: "[0]trim=start_frame=10:end_frame=20[L0];[0]trim=start_frame=30:end_frame=40[L1];[L0][L1]concat=2[L2];[1]hflip[L3];[L2][L3]overlay=eof_action=repeat,drawbox=50:50:120:120:red:t=5"
+    Number of chains: 5
+      chain[0]: [0]trim=start_frame=10:end_frame=20[L0];
+      chain[1]: [0]trim=start_frame=30:end_frame=40[L1];
+      chain[2]: [L0][L1]concat=2[L2];
+      chain[3]: [1]hflip[L3];
+      chain[4]: [L2][L3]overlay=eof_action=repeat,drawbox=50:50:120:120:red:t=5[UNC0]      
+    Available input pads (0): 
+    Available output pads: (1): (4, 1, 0)
+
+
+
+
+

Concat with preprocessing stage

+

The concat filter can be finicky, requiring all the streams to have the same attributes. To combine +mismatched streams, they need to be preprocessed by other filters. Video streams must have the same +frame size, frame rate, and pixel format. Meanwhile, the audio streams need to have the same sampling +rate, channel format, and sample format.

+

To build the filtergraph to concatenate mismatched video files, we start by defining the filters

+
>>> audio_filter = fgb.aformat(sample_fmts='flt',        # 32-bit floating point format
+...                            sample_rates=48000,       # 48 kS/s sampling rate
+...                            channel_layouts='stereo') # 2 channels in stereo layout
+>>> video_filters = [
+...    fgb.scale(1280, 720,
+...              force_original_aspect_ratio='decrease'), # scale at least one dimension to 720p
+...    fgb.pad(1280, 720, -1, -1),                        # if not 16:9, pad to fill the frame
+...    fgb.setsar(1),                                     # make sure pixels are square
+...    fgb.fps(30),                                       # set framerate to 30 (dupe or drop frames)
+...    fgb.format('yuv420p')                              # use yuv420p pixel format
+... ]
+
+
+

We need multiple video filters while the aformat filter takes care of the audio stream format. +To combine the video filters, we can use the built-in sum() with an empty :py:class:Filter. +as the initial value. Then, stack video and audio filters to finalize the preprocessor filtergraph +for an input file.

+
>>> preproc = sum(video_filters, fgb.Chain()) | audio_filter
+>>> preproc
+<ffmpegio.filtergraph.Graph.Graph object at 0x7f95cfcd3740>
+    FFmpeg expression: "scale=1280:720:force_original_aspect_ratio=decrease,pad=1280:720:-1:-1,setsar=1,fps=30,format=yuv420p;aformat=sample_fmts=flt:sample_rates=48000:channel_layouts=stereo"
+    Number of chains: 2
+      chain[0]: [UNC0]scale=1280:720:force_original_aspect_ratio=decrease,pad=1280:720:-1:-1,setsar=1,fps=30,format=yuv420p[UNC2];
+      chain[1]: [UNC1]aformat=sample_fmts=flt:sample_rates=48000:channel_layouts=stereo[UNC3]      
+    Available input pads (2): (0, 0, 0), (1, 0, 0)
+    Available output pads: (2): (0, 4, 0), (1, 0, 0)
+
+
+

Suppose that we have 3 video files, we need 3 copies of the preprocessor filtergraph. The preprocessor +filtergraph can be multiplied 3 times and assign the input stream specs:

+
>>> inputs = [f'[{file_id}:{media_type}]' for file_id in range(3) for media_type in ('v', 'a')]
+>>> inputs
+['[0:v]', '[0:a]', '[1:v]', '[1:a]', '[2:v]', '[2:a]']
+>>> prestage = inputs >> (preproc * 3)
+>>> prestage
+<ffmpegio.filtergraph.Graph.Graph object at 0x7f95cfcd3020>
+    FFmpeg expression: "[0:v]scale=1280:720:force_original_aspect_ratio=decrease,pad=1280:720:-1:-1,setsar=1,fps=30,format=yuv420p;[0:a]aformat=sample_fmts=flt:sample_rates=48000:channel_layouts=stereo;[1:v]scale=1280:720:force_original_aspect_ratio=decrease,pad=1280:720:-1:-1,setsar=1,fps=30,format=yuv420p;[1:a]aformat=sample_fmts=flt:sample_rates=48000:channel_layouts=stereo;[2:v]scale=1280:720:force_original_aspect_ratio=decrease,pad=1280:720:-1:-1,setsar=1,fps=30,format=yuv420p;[2:a]aformat=sample_fmts=flt:sample_rates=48000:channel_layouts=stereo"
+    Number of chains: 6
+      chain[0]: [0:v]scale=1280:720:force_original_aspect_ratio=decrease,pad=1280:720:-1:-1,setsar=1,fps=30,format=yuv420p[UNC0];
+      chain[1]: [0:a]aformat=sample_fmts=flt:sample_rates=48000:channel_layouts=stereo[UNC1];
+      chain[2]: [1:v]scale=1280:720:force_original_aspect_ratio=decrease,pad=1280:720:-1:-1,setsar=1,fps=30,format=yuv420p[UNC2];
+      chain[3]: [1:a]aformat=sample_fmts=flt:sample_rates=48000:channel_layouts=stereo[UNC3];
+      chain[4]: [2:v]scale=1280:720:force_original_aspect_ratio=decrease,pad=1280:720:-1:-1,setsar=1,fps=30,format=yuv420p[UNC4];
+      chain[5]: [2:a]aformat=sample_fmts=flt:sample_rates=48000:channel_layouts=stereo[UNC5]      
+    Available input pads (0): 
+    Available output pads: (6): (0, 4, 0), (1, 0, 0), (2, 4, 0), (3, 0, 0), (4, 4, 0), (5, 0, 0)
+
+
+

Finally, feed the outputs of the prestage filtergraph to the concat filter and assign the output +labels:

+
>>> fg = prestage + fgb.concat(n=3, v=1, a=1) >> ['[vout]','[aout]']
+>>> fg
+<ffmpegio.filtergraph.Graph.Graph object at 0x7f95cfd04050>
+    FFmpeg expression: "[0:v]scale=1280:720:force_original_aspect_ratio=decrease,pad=1280:720:-1:-1,setsar=1,fps=30,format=yuv420p[L0];[0:a]aformat=sample_fmts=flt:sample_rates=48000:channel_layouts=stereo[L1];[1:v]scale=1280:720:force_original_aspect_ratio=decrease,pad=1280:720:-1:-1,setsar=1,fps=30,format=yuv420p[L2];[1:a]aformat=sample_fmts=flt:sample_rates=48000:channel_layouts=stereo[L3];[2:v]scale=1280:720:force_original_aspect_ratio=decrease,pad=1280:720:-1:-1,setsar=1,fps=30,format=yuv420p[L4];[2:a]aformat=sample_fmts=flt:sample_rates=48000:channel_layouts=stereo[L5];[L0][L1][L2][L3][L4][L5]concat=n=3:v=1:a=1[vout][aout]"
+    Number of chains: 7
+      chain[0]: [0:v]scale=1280:720:force_original_aspect_ratio=decrease,pad=1280:720:-1:-1,setsar=1,fps=30,format=yuv420p[L0];
+      chain[1]: [0:a]aformat=sample_fmts=flt:sample_rates=48000:channel_layouts=stereo[L1];
+      chain[2]: [1:v]scale=1280:720:force_original_aspect_ratio=decrease,pad=1280:720:-1:-1,setsar=1,fps=30,format=yuv420p[L2];
+      chain[3]: [1:a]aformat=sample_fmts=flt:sample_rates=48000:channel_layouts=stereo[L3];
+      chain[4]: [2:v]scale=1280:720:force_original_aspect_ratio=decrease,pad=1280:720:-1:-1,setsar=1,fps=30,format=yuv420p[L4];
+      chain[5]: [2:a]aformat=sample_fmts=flt:sample_rates=48000:channel_layouts=stereo[L5];
+      chain[6]: [L0][L1][L2][L3][L4][L5]concat=n=3:v=1:a=1[vout][aout]      
+    Available input pads (0): 
+    Available output pads: (2): (6, 0, 0), (6, 0, 1)
+
+
+

Note that the output pads of the concat filter are listed as “available” because they are +technically not (yet) connected to anything. You can use this filter graph with ffmpegio.transcode() +to concatenate 3 input MP4 files:

+
>>> ffmpegio.transcode(['input1.mp4','input2.mp4','input3.mp4'], 'output.mp4',
+...                    filter_complex=fg, map=['[vout]','[aout]'])
+
+
+
+
+
+
+

Generating filtergraph script for extremely long filtergraph

+

Extremely long filtergraph description may hit the limit of the subprocess argument length (~30 kB +for Windows and ~100 kB for Posix). In such case, the filtergraph description needs to be passed to +FFmpeg by the filter_script FFmpeg output option or the filter_complex_script global option +with a filtergraph script file.

+

A preferred way to pass a long filtergraph description is to pipe it directly. If stdin is +available, use the input argument of subprocess.Popen():

+
# assume `fg` is a SISO video Graph object
+
+ffmpegio.ffmpegprocess.run(
+   {
+      'inputs':  [('input.mp4', None)]
+      'outputs': [('output.mp4', {'filter_script:v': 'pipe:0'})]
+   },
+   input=str(fg))
+
+
+

Note that pipe:0 must be used and not the shorthand '-' unlike +the input url.

+

If stdin is not available, Graph.as_script_file() provides a convenient way to create a +temporary script file. The previous example can also run as follows:

+
with fg.as_script_file() as script_path:
+   ffmpegio.ffmpegprocess.run(
+      {
+            'inputs':  [('input.mp4', None)]
+            'outputs': [('output.mp4', {'filter_script:v': script_path})]
+      })
+
+
+
+
+

Filtergraph API Reference

+
+
+ffmpegio.filtergraph.as_filter(filter_spec, copy=False)
+

convert the input to a filter

+
+
Parameters:
+
    +
  • filter_spec (str | FilterGraphObject) – filtergraph expression or object.

  • +
  • copy (bool) – True to copy even if the input is a Filter object.

  • +
+
+
Returns:
+

Filter object interpretation of filter_spec. No copy is performed if the input is +already a Filter and copy=False.

+
+
Return type:
+

Filter

+
+
+

If the input is a Chain or Graph object with more than one filter element, this function +will raise a FiltergraphConversionError exception.

+

If the input expression could not be parsed, FiltergraphInvalidExpression will be raised.

+
+ +
+
+ffmpegio.filtergraph.as_filterchain(filter_specs, copy=False)
+

Convert the input to a filter chain

+
+
Parameters:
+
    +
  • filter_spec – filtergraph expression or object.

  • +
  • copy (bool) – True to copy even if the input is a Filter object.

  • +
  • filter_specs (str | FilterGraphObject)

  • +
+
+
Returns:
+

Chain object interpretation of filter_spec. No copy is performed if the input is +already a Chain and copy=False.

+
+
Return type:
+

Chain

+
+
+

If the input is a Graph object with more than one filter chain, this function +will raise a FiltergraphConversionError exception.

+

If the input expression could not be parsed, FiltergraphInvalidExpression will be raised.

+
+ +
+
+ffmpegio.filtergraph.as_filtergraph(filter_specs, copy=False)
+

Convert the input to a filter graph

+
+
Parameters:
+
    +
  • filter_spec – filtergraph expression or object.

  • +
  • copy (bool) – True to copy even if the input is a Filter object.

  • +
  • filter_specs (str | FilterGraphObject)

  • +
+
+
Returns:
+

Graph object interpretation of filter_spec. No copy is performed if the input is +already a Graph and copy=False.

+
+
Return type:
+

Graph

+
+
+

If the input expression could not be parsed, FiltergraphInvalidExpression will be raised.

+
+ +
+
+ffmpegio.filtergraph.as_filtergraph_object(filter_specs, copy=False)
+

Convert the input to a filter graph object

+
+
Parameters:
+
    +
  • filter_spec – filtergraph expression or object.

  • +
  • copy (bool) – True to copy even if the input is a Filter object.

  • +
  • filter_specs (str | FilterGraphObject)

  • +
+
+
Returns:
+

Depending on the complexity of the filter_spec, Filter, +Chain, or Graph object interpretation of filter_spec. +No copy is performed if the input is already a Graph and copy=False.

+
+
Return type:
+

FilterGraphObject

+
+
+
+ +
+
+class ffmpegio.filtergraph.Filter(filter_spec, *args, filter_id=None, **kwargs)
+

FFmpeg filter definition immutable class

+
+
Parameters:
+
    +
  • filter_spec (_type_) – _description_

  • +
  • filter_id (_type_, optional) – _description_, defaults to None

  • +
  • *opts (dict, optional) – filter option values assigned in the order options are +declared

  • +
  • **kwopts (dict, optional) – filter options in key=value pairs

  • +
+
+
+
+
+exception Error
+
+
+add_note()
+

Exception.add_note(note) – +add a note to the exception

+
+ +
+
+with_traceback()
+

Exception.with_traceback(tb) – +set self.__traceback__ to tb and return self.

+
+ +
+ +
+
+exception InvalidName(name)
+
+
+add_note()
+

Exception.add_note(note) – +add a note to the exception

+
+ +
+
+with_traceback()
+

Exception.with_traceback(tb) – +set self.__traceback__ to tb and return self.

+
+ +
+ +
+
+exception InvalidOption
+
+
+add_note()
+

Exception.add_note(note) – +add a note to the exception

+
+ +
+
+with_traceback()
+

Exception.with_traceback(tb) – +set self.__traceback__ to tb and return self.

+
+ +
+ +
+
+exception Unsupported(name, feature)
+
+
Return type:
+

None

+
+
+
+
+add_note()
+

Exception.add_note(note) – +add a note to the exception

+
+ +
+
+with_traceback()
+

Exception.with_traceback(tb) – +set self.__traceback__ to tb and return self.

+
+ +
+ +
+
+add_label(label, inpad=None, outpad=None, force=None)
+

label a filter pad

+
+
Parameters:
+
    +
  • label (str) – name of the new label. Square brackets are optional.

  • +
  • inpad (Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | Sequence[Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int]) – input filter pad index or a sequence of pads, defaults to None

  • +
  • outpad (Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int) – output filter pad index, defaults to None

  • +
  • force (bool) – True to delete existing labels, defaults to None

  • +
+
+
Returns:
+

actual label name

+
+
Return type:
+

Graph

+
+
+

Only one of inpad and outpad argument must be given.

+

If given label already exists, no new label will be created.

+

If inpad indices are given, the label must be an input stream specifier.

+

If label has a trailing number, the number will be dropped and replaced with an +internally assigned label number.

+
+ +
+
+apply(options, filter_id=None)
+

apply new filter options

+
+
Parameters:
+
    +
  • options (dict) – new option key-value pairs. For ordered option, use positional index (0 +corresponds to the first option). Set value as None to drop the option. +Ordered options can only be dropped in contiguous fashion, including the +last ordered option.

  • +
  • filter_id (str, optional) – new filter id, defaults to None

  • +
+
+
Returns:
+

new filter with modified options

+
+
Return type:
+

Filter

+
+
+
+

Note

+

To add new ordered options, int-keyed options item must be presented in +the increasing key order so the option can be expanded one at a time.

+
+
+ +
+
+attach(right, left_on=None, right_on=None)
+

attach filter(s), chain(s), or label(s) to a filtergraph object

+
+
Parameters:
+
    +
  • right (FilterGraphObject | str | list[FilterGraphObject | str]) – output filterchain, filtergraph expression, or label, or list thereof

  • +
  • left_on (Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str | list[Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str | None] | None) – pad_index, specify the pad on left, default to None (first available)

  • +
  • right_on (Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str | list[Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str | None] | None) – pad index, specifies which pad on the right graph, defaults to None (first available)

  • +
+
+
Returns:
+

new filtergraph object

+
+
Return type:
+

Graph

+
+
+

One and only one of left or right may be a list or a label.

+

If pad indices are not specified, only the first available output/input pad is linked. If the +primary filtergraph object is Filter or Chain, the chainable pad (i.e., the last pad) will be +chosen.

+
+ +
+
+compose(show_unconnected_inputs=False, show_unconnected_outputs=False)
+

compose filtergraph

+
+
Parameters:
+
    +
  • show_unconnected_inputs (bool) – display [UNC#] on all unconnected input pads, defaults to True

  • +
  • show_unconnected_outputs (bool) – display [UNC#] on all unconnected output pads, defaults to True

  • +
+
+
+
+ +
+
+connect(right, from_left, to_right, from_right=None, to_left=None, chain_siso=True, replace_sws_flags=None)
+

append another filtergraph object and make downstream connections

+
+
Parameters:
+
    +
  • right (FilterGraphObject | str) – receiving filtergraph object

  • +
  • from_left (Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str | list[Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str]) – output pad ids or labels of left fg

  • +
  • to_right (Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str | list[Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str]) – input pad ids or labels of the right fg

  • +
  • from_right (Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str | list[Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str] | None) – output pad ids or labels of the right fg

  • +
  • to_left (Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str | list[Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str] | None) – input pad ids or labels of this left fg

  • +
  • chain_siso (bool) – True to chain the single-input single-output connection, default: True

  • +
  • replace_sws_flags (bool | None) – True to use right sws_flags if present, +False to drop right sws_flags, +None to throw an exception (default)

  • +
+
+
Returns:
+

new filtergraph object

+
+
Return type:
+

Graph | Chain

+
+
+
    +
  • link labels may be auto-renamed if there is a conflict

  • +
+
+ +
+
+count(value, /)
+

Return number of occurrences of value.

+
+ +
+
+get_input_pad(index_or_label)
+

resolve (unconnected) input pad from pad index or label

+
+
Parameters:
+
    +
  • index – pad index or link label

  • +
  • index_or_label (Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str)

  • +
+
+
Returns:
+

filter input pad index and its link label (None if not assigned)

+
+
Return type:
+

tuple[Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int, str | None]

+
+
+

Raises error if specified label does not resolve uniquely to an input pad

+
+ +
+
+get_label(input=True, index=None, inpad=None, outpad=None)
+

get the label string of the specified filter input or output pad

+
+
Parameters:
+
    +
  • input (bool) – True to get label of input pad, False to get label of output pad, defaults to True

  • +
  • index (Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | None) – 3-element tuple to specify the (chain, filter, pad) indices, defaults to None

  • +
  • inpad (Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | None) – alternate argument to specify an input pad index, defaults to None

  • +
  • outpad (Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | None) – alternate argument to specify an output pad index, defaults to None

  • +
+
+
Returns:
+

the label of the specified pad or None if no label is assigned.

+
+
Return type:
+

str | None

+
+
+

If the pad index is invalid, the method raises FiltergraphInvalidIndex.

+
+ +
+
+get_num_chains()
+

get the number of chains

+
+
Return type:
+

int

+
+
+
+ +
+
+get_num_filters(chain)
+

get the number of filters of the specfied chain

+
+
Parameters:
+

chain (int) – id of the chain

+
+
Return type:
+

int

+
+
+
+ +
+
+get_num_inputs()
+

get the number of input pads of the filter +:return: number of input pads +:rtype: int

+
+ +
+
+get_num_outputs()
+

get the number of output pads of the filter +:return: number of output pads +:rtype: int

+
+ +
+
+get_num_pads(input)
+

get the number of available pads at input or output

+
+
Parameters:
+

input (bool) – True to get the input count, False for the output count.

+
+
Return type:
+

int

+
+
+
+ +
+
+get_output_pad(index_or_label)
+

resolve (unconnected) output filter pad from pad index or labels

+
+
Parameters:
+
    +
  • index (tuple(int,int,int) or str) – pad index or link label

  • +
  • index_or_label (Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str)

  • +
+
+
Returns:
+

filter output pad index and its link labels

+
+
Return type:
+

tuple(int,int,int), list(str)

+
+
+

Raises error if specified index does not resolve uniquely to an output pad

+
+ +
+
+index(value, start=0, stop=9223372036854775807, /)
+

Return first index of value.

+

Raises ValueError if the value is not present.

+
+ +
+
+iter_chains(skip_if_no_input=False, skip_if_no_output=False, chainable_only=False)
+

iterate over chains of the filtergraphobject

+
+
Parameters:
+
    +
  • skip_if_no_input (bool) – True to skip chains without available input pads, defaults to False

  • +
  • skip_if_no_output (bool) – True to skip chains without available output pads, defaults to False

  • +
  • chainable_only (bool) – True to further restrict skip_if_no_input and skip_if_no_input +arguments to require chainable input or output, defaults to False to +allow any input/output

  • +
+
+
Yield:
+

chain id and chain object

+
+
Return type:
+

Generator[tuple[int, Chain]]

+
+
+
+ +
+
+iter_input_labels(exclude_stream_specs=False)
+

iterate over the dangling labeled input pads of the filtergraph object

+
+
Parameters:
+

exclude_stream_specs (bool) – True to not include input streams

+
+
Yield:
+

a tuple of 3-tuple pad index and the pad index of the connected output pad if connected

+
+
Return type:
+

Generator[tuple[str, Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int]]

+
+
+
+ +
+
+iter_input_pads(pad=None, filter=None, chain=None, *, exclude_chainable=False, chainable_first=False, include_connected=False, unlabeled_only=False, chainable_only=False, full_pad_index=False)
+

Iterate over input pads of the filter

+
+
Parameters:
+
    +
  • pad (int | None) – pad id, defaults to None

  • +
  • filter (Literal[0] | None) – filter index, defaults to None

  • +
  • chain (Literal[0] | None) – chain index, defaults to None

  • +
  • exclude_chainable (bool) – True to leave out the last input pads, defaults to False (all avail pads)

  • +
  • chainable_first (bool) – True to yield the last input first then the rest, defaults to False

  • +
  • include_connected (bool) – True to include pads connected to input streams, defaults to False

  • +
  • unlabeled_only (bool) – True to leave out named inputs, defaults to False to return all inputs

  • +
  • chainable_only (bool) – True to only iterate chainable pads, defaults to False to return all inputs

  • +
  • full_pad_index (bool) – True to return 3-element index

  • +
+
+
Yield:
+

filter pad index, link label, filter object, output pad index of connected filter if connected

+
+
Return type:
+

Generator[tuple[PAD_INDEX, Filter, None]]

+
+
+
+ +
+
+iter_output_labels()
+

iterate over the dangling labeled output pads of the filtergraph object

+
+
Yield:
+

a tuple of 3-tuple pad index and the pad index of the connected input pad if connected

+
+
Return type:
+

Generator[tuple[str, Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int]]

+
+
+
+ +
+
+iter_output_pads(pad=None, filter=None, chain=None, *, exclude_chainable=False, chainable_first=False, include_connected=False, unlabeled_only=False, chainable_only=False, full_pad_index=False)
+

Iterate over output pads of the filter

+
+
Parameters:
+
    +
  • pad (int | None) – pad id, defaults to None

  • +
  • filter (Literal[0] | None) – filter index, defaults to None

  • +
  • chain (Literal[0] | None) – chain index, defaults to None

  • +
  • exclude_chainable (bool) – True to leave out the last output pads, defaults to False (all avail pads)

  • +
  • chainable_first (bool) – True to yield the last output first then the rest, defaults to False

  • +
  • include_connected (bool) – True to include pads connected to output streams, defaults to False

  • +
  • unlabeled_only (bool) – True to leave out named outputs, defaults to False to return only all outputs

  • +
  • chainable_only (bool) – True to only iterate chainable pads, defaults to False to return all outputs

  • +
  • full_pad_index (bool) – True to return 3-element index

  • +
+
+
Yield:
+

filter pad index, link label, filter object, output pad index of connected filter if connected

+
+
Return type:
+

Generator[tuple[PAD_INDEX, Filter, PAD_INDEX | None]]

+
+
+
+ +
+
+join(right, how='per_chain', n_links='all', strict=False, unlabeled_only=False, chain_siso=True, replace_sws_flags=None)
+

filtergraph auto-connector

+
+
Parameters:
+
    +
  • right (FilterGraphObject | str) – receiving filtergraph object

  • +
  • how (Literal['chainable', 'per_chain', 'all', 'auto']) –

    method on how to mate input and output, defaults to "per_chain".

    +
      +
    • 'chainable': joins only chainable input pads and output pads.

    • +
    • +
      'per_chain': joins one pair of first available input pad and output pad of each

      mating chains. Source and sink chains are ignored.

      +
      +
      +
    • +
    • 'all': joins all input pads and output pads

    • +
    • 'auto': tries 'per_chain' first, if fails, then tries 'all'.

    • +
    +

  • +
  • n_links (int | Literal['all']) – number of left output pads to be connected to the right input pads, default: 0 +(all matching links). If how=='per_chain', n_links connections are made +per chain.

  • +
  • strict (bool) – True to raise exception if numbers of available pads do not match, default: False

  • +
  • unlabeled_only (bool) – True to ignore labeled unconnected pads, defaults to False

  • +
  • chain_siso (bool) – True to chain the single-input single-output connection, default: True

  • +
  • replace_sws_flags (bool) – True to use other’s sws_flags if present, +False to ignore other’s sws_flags, +None to throw an exception (default)

  • +
+
+
Returns:
+

Graph with the appended filter chains or None if inplace=True.

+
+
Return type:
+

Graph | None

+
+
+
+ +
+
+next_input_pad(pad=None, filter=None, chain=None, chainable_first=False, unlabeled_only=False, chainable_only=False, full_pad_index=False, exclude_indices=None)
+

get next available input pad

+
+
Parameters:
+
    +
  • pad (int | None) – pad id, defaults to None

  • +
  • filter (int | None) – filter index, defaults to None

  • +
  • chain (int | None) – chain index, defaults to None

  • +
  • chainable_first (bool) – True to retrieve the last pad first, then the rest sequentially, defaults to False

  • +
  • unlabeled_only (bool) – True to retrieve only unlabeled pad, defaults to False

  • +
  • chainable_only (bool) – True to only iterate chainable pads, defaults to False to return all inputs

  • +
  • full_pad_index (bool) – True to return 3-element index, defaults to False

  • +
  • exclude_indices (Sequence[Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int] | None) – List pad indices to skip, defaults to None to allow all

  • +
+
+
Returns:
+

The index of the pad or None if no pad found

+
+
Return type:
+

Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | None

+
+
+
+ +
+
+next_output_pad(pad=None, filter=None, chain=None, chainable_first=False, unlabeled_only=False, chainable_only=False, full_pad_index=False, exclude_indices=None)
+

get next available output pad

+
+
Parameters:
+
    +
  • pad (int | None) – pad id, defaults to None

  • +
  • filter (int | None) – filter index, defaults to None

  • +
  • chain (int | None) – chain index, defaults to None

  • +
  • chainable_first (bool) – True to retrieve the last pad first, then the rest sequentially, defaults to False

  • +
  • unlabeled_only (bool) – True to retrieve only unlabeled pad, defaults to False

  • +
  • chainable_only (bool) – True to only iterate chainable pads, defaults to False to return all inputs

  • +
  • full_pad_index (bool) – True to return 3-element index, defaults to False

  • +
  • exclude_indices (Sequence[Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int] | None) – List pad indices to skip, defaults to None to allow all

  • +
+
+
Returns:
+

The index of the pad or None if no pad found

+
+
Return type:
+

Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | None

+
+
+
+ +
+
+rattach(left, left_on=None, right_on=None)
+

attach filter(s), chain(s), or label(s) to a filtergraph object

+
+
Parameters:
+
    +
  • left (FilterGraphObject | str | list[FilterGraphObject | str]) – input filtergraph object, filtergraph expression, or label, or list thereof

  • +
  • right – output filterchain, filtergraph expression, or label, or list thereof

  • +
  • left_on (Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str | list[Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str | None] | None) – pad_index, specify the pad on left, default to None (first available)

  • +
  • right_on (Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str | list[Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str | None] | None) – pad index, specifies which pad on the right graph, defaults to None (first available)

  • +
+
+
Returns:
+

new filtergraph object

+
+
Return type:
+

Graph

+
+
+

One and only one of left or right may be a list or a label.

+

If pad indices are not specified, only the first available output/input pad is linked. If the +primary filtergraph object is Filter or Chain, the chainable pad (i.e., the last pad) will be +chosen.

+
+ +
+
+rconnect(left, from_left, to_right, from_right=None, to_left=None, chain_siso=True, replace_sws_flags=None)
+

append another filtergraph object and make upstream connections

+
+
Parameters:
+
    +
  • left (FilterGraphObject | str) – transmitting filtergraph object

  • +
  • right – receiving filtergraph object

  • +
  • from_left (Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str | list[Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str]) – output pad ids or labels of left fg

  • +
  • to_right (Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str | list[Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str]) – input pad ids or labels of the right fg

  • +
  • from_right (Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str | list[Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str] | None) – output pad ids or labels of the right fg

  • +
  • to_left (Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str | list[Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str] | None) – input pad ids or labels of this left fg

  • +
  • chain_siso (bool) – True to chain the single-input single-output connection, default: True

  • +
  • replace_sws_flags (bool | None) – True to use right sws_flags if present, +False to drop right sws_flags, +None to throw an exception (default)

  • +
+
+
Returns:
+

new filtergraph object

+
+
Return type:
+

Graph | Chain

+
+
+
    +
  • link labels may be auto-renamed if there is a conflict

  • +
+
+ +
+
+resolve_pad_index(index_or_label, *, is_input=True, chain_id_omittable=False, filter_id_omittable=False, pad_id_omittable=False, resolve_omitted=True, chain_fill_value=None, filter_fill_value=None, pad_fill_value=None, chainable_first=False, chainable_only=False)
+

Resolve unconnected label or pad index to full 3-element pad index

+
+
Parameters:
+
    +
  • index_or_label (Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str | None) – pad index set or pad label or None to auto-select

  • +
  • is_input (bool) – True to resolve an input pad, else an output pad, defaults to True

  • +
  • chain_id_omittable (bool) – True to allow None chain index, defaults to False

  • +
  • filter_id_omittable (bool) – True to allow None filter index, defaults to False

  • +
  • pad_id_omittable (bool) – True to allow None pad index, defaults to False

  • +
  • resolve_omitted (bool) – True to fill each omitted value with the prescribed fill value.

  • +
  • chain_fill_value (int | None) – if chain_id_omittable=True and chain index is either not +given or None, this value will be returned, defaults to None, +which returns the first available pad.

  • +
  • filter_fill_value (int | None)

  • +
  • pad_fill_value (int | None)

  • +
  • chainable_first (bool)

  • +
  • chainable_only (bool)

  • +
+
+
Return type:
+

Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int

+
+
+
+
:param filter_fill_value:if filter_id_omittable=True and filter index is either not

given or None, this value will be returned, defaults to None, +which returns the first available pad.

+
+
+
+
Parameters:
+
    +
  • pad_fill_value (int | None) – if pad_id_omittable=True and either index is None or +pad index is None, this value will be returned, defaults to None, +which returns the first available pad.

  • +
  • chainable_first (bool) – if True, chainable pad is selected first, defaults to False

  • +
  • chainable_only (bool) – True to only iterate chainable pads, defaults to False to return all pads

  • +
  • index_or_label (Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str | None)

  • +
  • is_input (bool)

  • +
  • chain_id_omittable (bool)

  • +
  • filter_id_omittable (bool)

  • +
  • pad_id_omittable (bool)

  • +
  • resolve_omitted (bool)

  • +
  • chain_fill_value (int | None)

  • +
  • filter_fill_value (int | None)

  • +
+
+
Return type:
+

Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int

+
+
+

One and only one of index and label must be specified. If the given index +or label is invalid, it raises FiltergraphPadNotFoundError.

+
+ +
+
+resolve_pad_indices(indices_or_labels, *, is_input=True, resolve_omitted=True, chainable_first=False, unlabeled_only=False, chainable_only=False)
+

Resolve unconnected labels or pad indices to full 3-element pad indices

+
+
Parameters:
+
    +
  • indices_or_labels (Sequence[Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str | None]) – a list of pad indices or pad labels or None to auto-select

  • +
  • is_input (bool) – True to resolve an input pad, else an output pad, defaults to True

  • +
  • chainable_first (bool) – if True, chainable pad is selected first, defaults to False

  • +
  • unlabeled_only (bool) – True to retrieve only unlabeled pad, defaults to False

  • +
  • chainable_only (bool) – True to only iterate chainable pads, defaults to False to return all pads

  • +
  • resolve_omitted (bool)

  • +
+
+
Return type:
+

list[Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int]

+
+
+

One and only one of index and label must be specified. If the given index +or label is invalid, it raises FiltergraphPadNotFoundError.

+

Omitted pads

+
+ +
+
+rjoin(left, how='per_chain', n_links='all', strict=False, unlabeled_only=False, chain_siso=True, replace_sws_flags=None)
+

filtergraph auto-connector

+
+
Parameters:
+
    +
  • left (FilterGraphObject | str) – transmitting filtergraph object

  • +
  • how (Literal['chainable', 'per_chain', 'all', 'auto']) –

    method on how to mate input and output, defaults to "per_chain".

    +
      +
    • 'chainable': joins only chainable input pads and output pads.

    • +
    • +
      'per_chain': joins one pair of first available input pad and output pad of each

      mating chains. Source and sink chains are ignored.

      +
      +
      +
    • +
    • 'all': joins all input pads and output pads

    • +
    • 'auto': tries 'per_chain' first, if fails, then tries 'all'.

    • +
    +

  • +
  • n_links (int | Literal['all']) – number of left output pads to be connected to the right input pads, default: 0 +(all matching links). If how=='per_chain', n_links connections are made +per chain.

  • +
  • strict (bool) – True to raise exception if numbers of available pads do not match, default: False

  • +
  • unlabeled_only (bool) – True to ignore labeled unconnected pads, defaults to False

  • +
  • chain_siso (bool) – True to chain the single-input single-output connection, default: True

  • +
  • replace_sws_flags (bool) – True to use other’s sws_flags if present, +False to ignore other’s sws_flags, +None to throw an exception (default)

  • +
+
+
Returns:
+

Graph with the appended filter chains or None if inplace=True.

+
+
Return type:
+

Graph | None

+
+
+
+ +
+
+stack(other, auto_link=False, replace_sws_flags=None)
+

stack another Graph to this Graph

+
+
Parameters:
+
    +
  • other (FilterGraphObject | str) – other filtergraph

  • +
  • auto_link (bool) – True to connect matched I/O labels, defaults to None

  • +
  • replace_sws_flags (bool | None) – True to use other’s sws_flags if present, +False to ignore other’s sws_flags, +None to throw an exception (default)

  • +
+
+
Returns:
+

new filtergraph object

+
+
Return type:
+

Graph

+
+
+
+

Remarks

+
    +
  • extend() and import links

  • +
  • If auto-link=False, common labels may be renamed.

  • +
  • For more explicit linking rather than the auto-linking, use connect() instead.

  • +
+

TO-CHECK/TO-DO: what happens if common link labels are already linked

+
+
+ +
+ +
+
+class ffmpegio.filtergraph.Chain(filter_specs=None)
+

List of FFmpeg filters, connected in series

+

Chain() to instantiate empty Graph object

+

Chain(obj) to copy-instantiate Graph object from another

+

Chain(’…’) to parse an FFmpeg filtergraph expression

+
+
Parameters:
+

filter_specs (str or seq(Filter), optional) – single-in-single-out filtergraph description without +labels, defaults to None

+
+
+
+
+exception Error
+
+
+add_note()
+

Exception.add_note(note) – +add a note to the exception

+
+ +
+
+with_traceback()
+

Exception.with_traceback(tb) – +set self.__traceback__ to tb and return self.

+
+ +
+ +
+
+add_label(label, inpad=None, outpad=None, force=None)
+

label a filter pad

+
+
Parameters:
+
    +
  • label (str) – name of the new label. Square brackets are optional.

  • +
  • inpad (Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int) – input filter pad index or a sequence of pads, defaults to None

  • +
  • outpad (Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int) – output filter pad index, defaults to None

  • +
  • force (bool) – True to delete existing labels, defaults to None

  • +
+
+
Returns:
+

actual label name

+
+
Return type:
+

Graph

+
+
+

Only one of inpad and outpad argument must be given.

+

If given label already exists, no new label will be created.

+

If label has a trailing number, the number will be dropped and replaced with an +internally assigned label number.

+
+ +
+
+append(item)
+

S.append(value) – append value to the end of the sequence

+
+ +
+
+attach(right, left_on=None, right_on=None)
+

attach filter(s), chain(s), or label(s) to a filtergraph object

+
+
Parameters:
+
    +
  • right (FilterGraphObject | str | list[FilterGraphObject | str]) – output filterchain, filtergraph expression, or label, or list thereof

  • +
  • left_on (Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str | list[Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str | None] | None) – pad_index, specify the pad on left, default to None (first available)

  • +
  • right_on (Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str | list[Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str | None] | None) – pad index, specifies which pad on the right graph, defaults to None (first available)

  • +
+
+
Returns:
+

new filtergraph object

+
+
Return type:
+

Graph

+
+
+

One and only one of left or right may be a list or a label.

+

If pad indices are not specified, only the first available output/input pad is linked. If the +primary filtergraph object is Filter or Chain, the chainable pad (i.e., the last pad) will be +chosen.

+
+ +
+
+clear() None -- remove all items from S
+
+ +
+
+compose(show_unconnected_inputs=False, show_unconnected_outputs=False)
+

compose filtergraph

+
+
Parameters:
+
    +
  • show_unconnected_inputs (bool) – display [UNC#] on all unconnected input pads, defaults to True

  • +
  • show_unconnected_outputs (bool) – display [UNC#] on all unconnected output pads, defaults to True

  • +
+
+
+
+ +
+
+connect(right, from_left, to_right, from_right=None, to_left=None, chain_siso=True, replace_sws_flags=None)
+

append another filtergraph object and make downstream connections

+
+
Parameters:
+
    +
  • right (FilterGraphObject | str) – receiving filtergraph object

  • +
  • from_left (Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str | list[Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str]) – output pad ids or labels of left fg

  • +
  • to_right (Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str | list[Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str]) – input pad ids or labels of the right fg

  • +
  • from_right (Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str | list[Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str] | None) – output pad ids or labels of the right fg

  • +
  • to_left (Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str | list[Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str] | None) – input pad ids or labels of this left fg

  • +
  • chain_siso (bool) – True to chain the single-input single-output connection, default: True

  • +
  • replace_sws_flags (bool | None) – True to use right sws_flags if present, +False to drop right sws_flags, +None to throw an exception (default)

  • +
+
+
Returns:
+

new filtergraph object

+
+
Return type:
+

Graph | Chain

+
+
+
    +
  • link labels may be auto-renamed if there is a conflict

  • +
+
+ +
+
+count(value) integer -- return number of occurrences of value
+
+ +
+
+extend(other)
+

S.extend(iterable) – extend sequence by appending elements from the iterable

+
+
Parameters:
+

other (Chain | Sequence[Filter | str])

+
+
+
+ +
+
+get_input_pad(index_or_label)
+

resolve (unconnected) input pad from pad index or label

+
+
Parameters:
+
    +
  • index – pad index or link label

  • +
  • index_or_label (Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str)

  • +
+
+
Returns:
+

filter input pad index and its link label (None if not assigned)

+
+
Return type:
+

tuple[Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int, str | None]

+
+
+

Raises error if specified label does not resolve uniquely to an input pad

+
+ +
+
+get_label(input=True, index=None, inpad=None, outpad=None)
+

get the label string of the specified filter input or output pad

+
+
Parameters:
+
    +
  • input (bool) – True to get label of input pad, False to get label of output pad, defaults to True

  • +
  • index (Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | None) – 3-element tuple to specify the (chain, filter, pad) indices, defaults to None

  • +
  • inpad (Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | None) – alternate argument to specify an input pad index, defaults to None

  • +
  • outpad (Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | None) – alternate argument to specify an output pad index, defaults to None

  • +
+
+
Returns:
+

the label of the specified pad or None if no label is assigned.

+
+
Return type:
+

str | None

+
+
+

If the pad index is invalid, the method raises FiltergraphInvalidIndex.

+
+ +
+
+get_num_chains()
+

get the number of chains

+
+
Return type:
+

int

+
+
+
+ +
+
+get_num_filters(chain)
+

get the number of filters of the specfied chain

+
+
Parameters:
+

chain (int) – id of the chain

+
+
Return type:
+

int

+
+
+
+ +
+
+get_num_inputs()
+

get the number of input pads of the filter +:return: number of input pads

+
+
Return type:
+

int

+
+
+
+ +
+
+get_num_outputs()
+

get the number of output pads of the filter +:return: number of output pads

+
+
Return type:
+

int

+
+
+
+ +
+
+get_num_pads(input)
+

get the number of available pads at input or output

+
+
Parameters:
+

input (bool) – True to get the input count, False for the output count.

+
+
Return type:
+

int

+
+
+
+ +
+
+get_output_pad(index_or_label)
+

resolve (unconnected) output filter pad from pad index or labels

+
+
Parameters:
+
    +
  • index (tuple(int,int,int) or str) – pad index or link label

  • +
  • index_or_label (Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str)

  • +
+
+
Returns:
+

filter output pad index and its link labels

+
+
Return type:
+

tuple(int,int,int), list(str)

+
+
+

Raises error if specified index does not resolve uniquely to an output pad

+
+ +
+
+index(value[, start[, stop]]) integer -- return first index of value.
+

Raises ValueError if the value is not present.

+

Supporting start and stop arguments is optional, but +recommended.

+
+ +
+
+insert(i, item)
+

S.insert(index, value) – insert value before index

+
+ +
+
+is_last_filter(filter_id)
+

Returns True if the given id is the last filter of the chain

+
+
Parameters:
+

filter_id (int)

+
+
Return type:
+

bool

+
+
+
+ +
+
+iter_chains(skip_if_no_input=False, skip_if_no_output=False, chainable_only=False)
+

iterate over chains of the filtergraphobject

+
+
Parameters:
+
    +
  • skip_if_no_input (bool) – True to skip chains without available input pads, defaults to False

  • +
  • skip_if_no_output (bool) – True to skip chains without available output pads, defaults to False

  • +
  • chainable_only (bool) – True to further restrict skip_if_no_input and skip_if_no_input +arguments to require chainable input or output, defaults to False to +allow any input/output

  • +
+
+
Yield:
+

chain id and chain object

+
+
Return type:
+

Generator[tuple[int, Chain]]

+
+
+
+ +
+
+iter_input_labels(exclude_stream_specs=False)
+

iterate over the dangling labeled input pads of the filtergraph object

+
+
Parameters:
+

exclude_stream_specs (bool) – True to not include input streams

+
+
Yield:
+

a tuple of 3-tuple pad index and the pad index of the connected output pad if connected

+
+
Return type:
+

Generator[tuple[str, Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int]]

+
+
+
+ +
+
+iter_input_pads(pad=None, filter=None, chain=None, *, exclude_chainable=False, chainable_first=False, include_connected=False, unlabeled_only=False, chainable_only=False, full_pad_index=False)
+

Iterate over input pads of the filters on the filterchain

+
+
Parameters:
+
    +
  • pad (int | None) – pad id, defaults to None

  • +
  • filter (int | None) – filter index, defaults to None

  • +
  • chain (Literal[0] | None) – chain index, defaults to None

  • +
  • exclude_chainable (bool) – True to leave out the last input pads, defaults to False (all avail pads)

  • +
  • chainable_first (bool) – True to yield the last input first then the rest, defaults to False

  • +
  • include_connected (bool) – True to include pads connected to input streams, defaults to False

  • +
  • unlabeled_only (bool) – True to leave out named inputs, defaults to False to return all inputs

  • +
  • chainable_only (bool) – True to only iterate chainable pads, defaults to False to return all inputs

  • +
  • full_pad_index (bool) – True to return 3-element index

  • +
+
+
Yield:
+

filter pad index, link label, filter object, output pad index of connected filter if connected

+
+
Return type:
+

Generator[tuple[Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int, Filter, Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | None]]

+
+
+
+ +
+
+iter_output_labels()
+

iterate over the dangling labeled output pads of the filtergraph object

+
+
Yield:
+

a tuple of 3-tuple pad index and the pad index of the connected input pad if connected

+
+
Return type:
+

Generator[tuple[str, Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int]]

+
+
+
+ +
+
+iter_output_pads(pad=None, filter=None, chain=None, *, exclude_chainable=False, chainable_first=False, include_connected=False, unlabeled_only=False, chainable_only=False, full_pad_index=False)
+

Iterate over output pads of the filters on the filterchain

+
+
Parameters:
+
    +
  • pad (int | None) – pad id, defaults to None

  • +
  • filter (int | None) – filter index, defaults to None

  • +
  • chain (int | None) – chain index, defaults to None

  • +
  • exclude_chainable (bool) – True to leave out the last output pads, defaults to False (all avail pads)

  • +
  • chainable_first (bool) – True to yield the last output first then the rest, defaults to False

  • +
  • include_connected (bool) – True to include pads connected to output streams, defaults to False

  • +
  • unlabeled_only (bool) – True to leave out named outputs, defaults to False to return all outputs

  • +
  • chainable_only (bool) – True to only iterate chainable pads, defaults to False to return all outputs

  • +
  • full_pad_index (bool) – True to return 3-element index

  • +
+
+
Yield:
+

filter pad index, link label, filter object, output pad index of connected filter if connected

+
+
Return type:
+

Generator[tuple[Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int, Filter, Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | None]]

+
+
+
+ +
+
+join(right, how='per_chain', n_links='all', strict=False, unlabeled_only=False, chain_siso=True, replace_sws_flags=None)
+

filtergraph auto-connector

+
+
Parameters:
+
    +
  • right (FilterGraphObject | str) – receiving filtergraph object

  • +
  • how (Literal['chainable', 'per_chain', 'all', 'auto']) –

    method on how to mate input and output, defaults to "per_chain".

    +
      +
    • 'chainable': joins only chainable input pads and output pads.

    • +
    • +
      'per_chain': joins one pair of first available input pad and output pad of each

      mating chains. Source and sink chains are ignored.

      +
      +
      +
    • +
    • 'all': joins all input pads and output pads

    • +
    • 'auto': tries 'per_chain' first, if fails, then tries 'all'.

    • +
    +

  • +
  • n_links (int | Literal['all']) – number of left output pads to be connected to the right input pads, default: 0 +(all matching links). If how=='per_chain', n_links connections are made +per chain.

  • +
  • strict (bool) – True to raise exception if numbers of available pads do not match, default: False

  • +
  • unlabeled_only (bool) – True to ignore labeled unconnected pads, defaults to False

  • +
  • chain_siso (bool) – True to chain the single-input single-output connection, default: True

  • +
  • replace_sws_flags (bool) – True to use other’s sws_flags if present, +False to ignore other’s sws_flags, +None to throw an exception (default)

  • +
+
+
Returns:
+

Graph with the appended filter chains or None if inplace=True.

+
+
Return type:
+

Graph | None

+
+
+
+ +
+
+next_input_pad(pad=None, filter=None, chain=None, chainable_first=False, unlabeled_only=False, chainable_only=False, full_pad_index=False, exclude_indices=None)
+

get next available input pad

+
+
Parameters:
+
    +
  • pad (int | None) – pad id, defaults to None

  • +
  • filter (int | None) – filter index, defaults to None

  • +
  • chain (int | None) – chain index, defaults to None

  • +
  • chainable_first (bool) – True to retrieve the last pad first, then the rest sequentially, defaults to False

  • +
  • unlabeled_only (bool) – True to retrieve only unlabeled pad, defaults to False

  • +
  • chainable_only (bool) – True to only iterate chainable pads, defaults to False to return all inputs

  • +
  • full_pad_index (bool) – True to return 3-element index, defaults to False

  • +
  • exclude_indices (Sequence[Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int] | None) – List pad indices to skip, defaults to None to allow all

  • +
+
+
Returns:
+

The index of the pad or None if no pad found

+
+
Return type:
+

Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | None

+
+
+
+ +
+
+next_output_pad(pad=None, filter=None, chain=None, chainable_first=False, unlabeled_only=False, chainable_only=False, full_pad_index=False, exclude_indices=None)
+

get next available output pad

+
+
Parameters:
+
    +
  • pad (int | None) – pad id, defaults to None

  • +
  • filter (int | None) – filter index, defaults to None

  • +
  • chain (int | None) – chain index, defaults to None

  • +
  • chainable_first (bool) – True to retrieve the last pad first, then the rest sequentially, defaults to False

  • +
  • unlabeled_only (bool) – True to retrieve only unlabeled pad, defaults to False

  • +
  • chainable_only (bool) – True to only iterate chainable pads, defaults to False to return all inputs

  • +
  • full_pad_index (bool) – True to return 3-element index, defaults to False

  • +
  • exclude_indices (Sequence[Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int] | None) – List pad indices to skip, defaults to None to allow all

  • +
+
+
Returns:
+

The index of the pad or None if no pad found

+
+
Return type:
+

Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | None

+
+
+
+ +
+
+pop([index]) item -- remove and return item at index (default last).
+

Raise IndexError if list is empty or index is out of range.

+
+ +
+
+rattach(left, left_on=None, right_on=None)
+

attach filter(s), chain(s), or label(s) to a filtergraph object

+
+
Parameters:
+
    +
  • left (FilterGraphObject | str | list[FilterGraphObject | str]) – input filtergraph object, filtergraph expression, or label, or list thereof

  • +
  • right – output filterchain, filtergraph expression, or label, or list thereof

  • +
  • left_on (Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str | list[Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str | None] | None) – pad_index, specify the pad on left, default to None (first available)

  • +
  • right_on (Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str | list[Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str | None] | None) – pad index, specifies which pad on the right graph, defaults to None (first available)

  • +
+
+
Returns:
+

new filtergraph object

+
+
Return type:
+

Graph

+
+
+

One and only one of left or right may be a list or a label.

+

If pad indices are not specified, only the first available output/input pad is linked. If the +primary filtergraph object is Filter or Chain, the chainable pad (i.e., the last pad) will be +chosen.

+
+ +
+
+rconnect(left, from_left, to_right, from_right=None, to_left=None, chain_siso=True, replace_sws_flags=None)
+

append another filtergraph object and make upstream connections

+
+
Parameters:
+
    +
  • left (FilterGraphObject | str) – transmitting filtergraph object

  • +
  • right – receiving filtergraph object

  • +
  • from_left (Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str | list[Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str]) – output pad ids or labels of left fg

  • +
  • to_right (Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str | list[Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str]) – input pad ids or labels of the right fg

  • +
  • from_right (Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str | list[Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str] | None) – output pad ids or labels of the right fg

  • +
  • to_left (Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str | list[Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str] | None) – input pad ids or labels of this left fg

  • +
  • chain_siso (bool) – True to chain the single-input single-output connection, default: True

  • +
  • replace_sws_flags (bool | None) – True to use right sws_flags if present, +False to drop right sws_flags, +None to throw an exception (default)

  • +
+
+
Returns:
+

new filtergraph object

+
+
Return type:
+

Graph | Chain

+
+
+
    +
  • link labels may be auto-renamed if there is a conflict

  • +
+
+ +
+
+remove(item)
+

S.remove(value) – remove first occurrence of value. +Raise ValueError if the value is not present.

+
+ +
+
+resolve_pad_index(index_or_label, *, is_input=True, chain_id_omittable=False, filter_id_omittable=False, pad_id_omittable=False, resolve_omitted=True, chain_fill_value=None, filter_fill_value=None, pad_fill_value=None, chainable_first=False, chainable_only=False)
+

Resolve unconnected label or pad index to full 3-element pad index

+
+
Parameters:
+
    +
  • index_or_label (Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str | None) – pad index set or pad label or None to auto-select

  • +
  • is_input (bool) – True to resolve an input pad, else an output pad, defaults to True

  • +
  • chain_id_omittable (bool) – True to allow None chain index, defaults to False

  • +
  • filter_id_omittable (bool) – True to allow None filter index, defaults to False

  • +
  • pad_id_omittable (bool) – True to allow None pad index, defaults to False

  • +
  • resolve_omitted (bool) – True to fill each omitted value with the prescribed fill value.

  • +
  • chain_fill_value (int | None) – if chain_id_omittable=True and chain index is either not +given or None, this value will be returned, defaults to None, +which returns the first available pad.

  • +
  • filter_fill_value (int | None)

  • +
  • pad_fill_value (int | None)

  • +
  • chainable_first (bool)

  • +
  • chainable_only (bool)

  • +
+
+
Return type:
+

Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int

+
+
+
+
:param filter_fill_value:if filter_id_omittable=True and filter index is either not

given or None, this value will be returned, defaults to None, +which returns the first available pad.

+
+
+
+
Parameters:
+
    +
  • pad_fill_value (int | None) – if pad_id_omittable=True and either index is None or +pad index is None, this value will be returned, defaults to None, +which returns the first available pad.

  • +
  • chainable_first (bool) – if True, chainable pad is selected first, defaults to False

  • +
  • chainable_only (bool) – True to only iterate chainable pads, defaults to False to return all pads

  • +
  • index_or_label (Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str | None)

  • +
  • is_input (bool)

  • +
  • chain_id_omittable (bool)

  • +
  • filter_id_omittable (bool)

  • +
  • pad_id_omittable (bool)

  • +
  • resolve_omitted (bool)

  • +
  • chain_fill_value (int | None)

  • +
  • filter_fill_value (int | None)

  • +
+
+
Return type:
+

Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int

+
+
+

One and only one of index and label must be specified. If the given index +or label is invalid, it raises FiltergraphPadNotFoundError.

+
+ +
+
+resolve_pad_indices(indices_or_labels, *, is_input=True, resolve_omitted=True, chainable_first=False, unlabeled_only=False, chainable_only=False)
+

Resolve unconnected labels or pad indices to full 3-element pad indices

+
+
Parameters:
+
    +
  • indices_or_labels (Sequence[Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str | None]) – a list of pad indices or pad labels or None to auto-select

  • +
  • is_input (bool) – True to resolve an input pad, else an output pad, defaults to True

  • +
  • chainable_first (bool) – if True, chainable pad is selected first, defaults to False

  • +
  • unlabeled_only (bool) – True to retrieve only unlabeled pad, defaults to False

  • +
  • chainable_only (bool) – True to only iterate chainable pads, defaults to False to return all pads

  • +
  • resolve_omitted (bool)

  • +
+
+
Return type:
+

list[Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int]

+
+
+

One and only one of index and label must be specified. If the given index +or label is invalid, it raises FiltergraphPadNotFoundError.

+

Omitted pads

+
+ +
+
+reverse()
+

S.reverse() – reverse IN PLACE

+
+ +
+
+rjoin(left, how='per_chain', n_links='all', strict=False, unlabeled_only=False, chain_siso=True, replace_sws_flags=None)
+

filtergraph auto-connector

+
+
Parameters:
+
    +
  • left (FilterGraphObject | str) – transmitting filtergraph object

  • +
  • how (Literal['chainable', 'per_chain', 'all', 'auto']) –

    method on how to mate input and output, defaults to "per_chain".

    +
      +
    • 'chainable': joins only chainable input pads and output pads.

    • +
    • +
      'per_chain': joins one pair of first available input pad and output pad of each

      mating chains. Source and sink chains are ignored.

      +
      +
      +
    • +
    • 'all': joins all input pads and output pads

    • +
    • 'auto': tries 'per_chain' first, if fails, then tries 'all'.

    • +
    +

  • +
  • n_links (int | Literal['all']) – number of left output pads to be connected to the right input pads, default: 0 +(all matching links). If how=='per_chain', n_links connections are made +per chain.

  • +
  • strict (bool) – True to raise exception if numbers of available pads do not match, default: False

  • +
  • unlabeled_only (bool) – True to ignore labeled unconnected pads, defaults to False

  • +
  • chain_siso (bool) – True to chain the single-input single-output connection, default: True

  • +
  • replace_sws_flags (bool) – True to use other’s sws_flags if present, +False to ignore other’s sws_flags, +None to throw an exception (default)

  • +
+
+
Returns:
+

Graph with the appended filter chains or None if inplace=True.

+
+
Return type:
+

Graph | None

+
+
+
+ +
+
+stack(other, auto_link=False, replace_sws_flags=None)
+

stack another Graph to this Graph

+
+
Parameters:
+
    +
  • other (FilterGraphObject | str) – other filtergraph

  • +
  • auto_link (bool) – True to connect matched I/O labels, defaults to None

  • +
  • replace_sws_flags (bool | None) – True to use other’s sws_flags if present, +False to ignore other’s sws_flags, +None to throw an exception (default)

  • +
+
+
Returns:
+

new filtergraph object

+
+
Return type:
+

Graph

+
+
+
+

Remarks

+
    +
  • extend() and import links

  • +
  • If auto-link=False, common labels may be renamed.

  • +
  • For more explicit linking rather than the auto-linking, use connect() instead.

  • +
+

TO-CHECK/TO-DO: what happens if common link labels are already linked

+
+
+ +
+ +
+
+class ffmpegio.filtergraph.Graph(filter_specs=None, links=None, sws_flags=None)
+

List of FFmpeg filterchains in parallel with interchain link specifications

+

Graph() to instantiate empty Graph object

+

Graph(obj) to copy-instantiate Graph object from another

+

Graph(’…’) to parse an FFmpeg filtergraph expression

+

Graph(filter_specs, links, sws_flags) +to specify the compose_graph(…) arguments

+
+
Parameters:
+
    +
  • filter_specs (Graph, str, or seq(seq(filter_args))) – either an existing Graph instance to copy, an FFmpeg +filtergraph expression, or a nested sequence of argument +sequences to compose_filter() to define a filtergraph. +For the latter option, The last element of each filter argument +sequence may be a dict, defining its keyword arguments, +defaults to None

  • +
  • links (dict, optional) – specifies filter links

  • +
  • sws_flags (seq of stringifyable elements with optional dict as the last +element for the keyword flags, optional) – specify swscale flags for those automatically inserted +scalers, defaults to None

  • +
+
+
+
+
+exception Error
+
+
+add_note()
+

Exception.add_note(note) – +add a note to the exception

+
+ +
+
+with_traceback()
+

Exception.with_traceback(tb) – +set self.__traceback__ to tb and return self.

+
+ +
+ +
+
+exception FilterPadMediaTypeMismatch(in_name, in_pad, in_type, out_name, out_pad, out_type)
+
+
+add_note()
+

Exception.add_note(note) – +add a note to the exception

+
+ +
+
+with_traceback()
+

Exception.with_traceback(tb) – +set self.__traceback__ to tb and return self.

+
+ +
+ +
+
+exception InvalidFilterPadId(type, index)
+
+
+add_note()
+

Exception.add_note(note) – +add a note to the exception

+
+ +
+
+with_traceback()
+

Exception.with_traceback(tb) – +set self.__traceback__ to tb and return self.

+
+ +
+ +
+
+add_label(label, inpad=None, outpad=None, force=None)
+

label a filter pad

+
+
Parameters:
+
    +
  • label (str) – name of the new label. Square brackets are optional.

  • +
  • inpad (tuple(int,int,int) | seq(tuple(int,int,int)), optional) – input filter pad index or a sequence of pads, defaults to None

  • +
  • outpad (tuple(int,int,int), optional) – output filter pad index, defaults to None

  • +
  • force (bool, optional) – True to delete existing labels, defaults to None

  • +
+
+
Returns:
+

actual label name

+
+
Return type:
+

str

+
+
+

Only one of inpad and outpad argument must be given.

+

If given label already exists, no new label will be created.

+

If label has a trailing number, the number will be dropped and replaced with an +internally assigned label number.

+
+ +
+
+append(item)
+

S.append(value) – append value to the end of the sequence

+
+
Parameters:
+

item (Chain | str)

+
+
+
+ +
+
+are_linked(inpad, outpad, check_input_stream=False)
+

True if given pads are linked

+
+
Parameters:
+
    +
  • inpad (Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | None) – input pad index, default to None to check if outpad is connected to any +input pad.

  • +
  • outpad (Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | None) – output pad index, defaults to None to check if inpad is connected to any +output pad or an input stream.

  • +
  • check_input_stream (bool | str) – True to check inpad is connected to an input stream, or a stream +specifier string to check the connection to a specific stream, defaults +to False.

  • +
+
+
Return type:
+

bool

+
+
+

ValueError will be raised if both inpad and outpad None or +if include_input_stream!=False and outpad is None.

+
+ +
+
+as_script_file()
+

return script file containing the filtergraph description

+
+
Yield:
+

path of a temporary text file with filtergraph description

+
+
Return type:
+

str

+
+
+

This method is intended to work with the filter_script and +filter_complex_script FFmpeg options, by creating a temporary text file +containing the filtergraph description.

+
+

Note

+

Only use this function when the filtergraph description is too long for +OS to handle it. Presenting the filtergraph with a filter_complex or +filter option to FFmpeg is always a faster solution.

+

Moreover, if stdin is available, i.e., not for a write or filter +operation, it is more performant to pass the long filtergraph object +to the subprocess’ input argument instead of using this method.

+
+

Use this method with a with statement. How to incorporate its output +with ffmpegprocess depends on the as_file_obj argument.

+
+
Example:
+

The following example illustrates a usecase for a video SISO filtergraph:

+
# assume `fg` is a SISO video filter Graph object
+
+with fg.as_script_file() as script_path:
+    ffmpegio.ffmpegprocess.run(
+        {
+            'inputs':  [('input.mp4', None)]
+            'outputs': [('output.mp4', {'filter_script:v': script_path})]
+        })
+
+
+

As noted above, a performant alternative is to use an input pipe and +feed the filtergraph description directly:

+
ffmpegio.ffmpegprocess.run(
+    {
+        'inputs':  [('input.mp4', None)]
+        'outputs': [('output.mp4', {'filter_script:v': 'pipe:0'})]
+    },
+    input=str(fg))
+
+
+

Note that pipe:0 must be used and not the shorthand '-' unlike +the input url.

+
+
+
+ +
+
+attach(right, left_on=None, right_on=None)
+

attach filter(s), chain(s), or label(s) to a filtergraph object

+
+
Parameters:
+
    +
  • right (FilterGraphObject | str | list[FilterGraphObject | str]) – output filterchain, filtergraph expression, or label, or list thereof

  • +
  • left_on (Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str | list[Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str | None] | None) – pad_index, specify the pad on left, default to None (first available)

  • +
  • right_on (Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str | list[Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str | None] | None) – pad index, specifies which pad on the right graph, defaults to None (first available)

  • +
+
+
Returns:
+

new filtergraph object

+
+
Return type:
+

Graph

+
+
+

One and only one of left or right may be a list or a label.

+

If pad indices are not specified, only the first available output/input pad is linked. If the +primary filtergraph object is Filter or Chain, the chainable pad (i.e., the last pad) will be +chosen.

+
+ +
+
+clear() None -- remove all items from S
+
+ +
+
+compose(show_unconnected_inputs=True, show_unconnected_outputs=True)
+

compose filtergraph

+
+
Parameters:
+
    +
  • show_unconnected_inputs (bool) – display [UNC#] on all unconnected input pads, defaults to True

  • +
  • show_unconnected_outputs (bool) – display [UNC#] on all unconnected output pads, defaults to True

  • +
+
+
+
+ +
+
+connect(right, from_left, to_right, from_right=None, to_left=None, chain_siso=True, replace_sws_flags=None)
+

append another filtergraph object and make downstream connections

+
+
Parameters:
+
    +
  • right (FilterGraphObject | str) – receiving filtergraph object

  • +
  • from_left (Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str | list[Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str]) – output pad ids or labels of left fg

  • +
  • to_right (Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str | list[Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str]) – input pad ids or labels of the right fg

  • +
  • from_right (Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str | list[Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str] | None) – output pad ids or labels of the right fg

  • +
  • to_left (Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str | list[Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str] | None) – input pad ids or labels of this left fg

  • +
  • chain_siso (bool) – True to chain the single-input single-output connection, default: True

  • +
  • replace_sws_flags (bool | None) – True to use right sws_flags if present, +False to drop right sws_flags, +None to throw an exception (default)

  • +
+
+
Returns:
+

new filtergraph object

+
+
Return type:
+

Graph | Chain

+
+
+
    +
  • link labels may be auto-renamed if there is a conflict

  • +
+
+ +
+
+count(value) integer -- return number of occurrences of value
+
+ +
+
+extend(other, auto_link=False, force_link=False)
+

S.extend(iterable) – extend sequence by appending elements from the iterable

+
+
Parameters:
+
    +
  • other (Sequence[fgb.Chain | str] | fgb.FilterGraph)

  • +
  • auto_link (bool)

  • +
  • force_link (bool)

  • +
+
+
+
+ +
+
+get_input_pad(index_or_label)
+

resolve (unconnected) input pad from pad index or label

+
+
Parameters:
+
    +
  • index – pad index or link label

  • +
  • index_or_label (Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str)

  • +
+
+
Returns:
+

filter input pad index and its link label (None if not assigned)

+
+
Return type:
+

tuple[Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int, str | None]

+
+
+

Raises error if specified label does not resolve uniquely to an input pad

+
+ +
+
+get_label(input=True, index=None, inpad=None, outpad=None)
+

get the label string of the specified filter input or output pad

+
+
Parameters:
+
    +
  • input (bool) – True to get label of input pad, False to get label of output pad, defaults to True

  • +
  • index (Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | None) – 3-element tuple to specify the (chain, filter, pad) indices, defaults to None

  • +
  • inpad (Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | None) – alternate argument to specify an input pad index, defaults to None

  • +
  • outpad (Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | None) – alternate argument to specify an output pad index, defaults to None

  • +
+
+
Returns:
+

the label of the specified pad or None if no label is assigned.

+
+
Return type:
+

str | None

+
+
+

If the pad index is invalid, the method raises FiltergraphInvalidIndex.

+
+ +
+
+get_num_chains()
+

get the number of hains

+
+
Return type:
+

int

+
+
+
+ +
+
+get_num_filters(chain)
+

get the number of filters of the specfied chain

+
+
Parameters:
+

chain (int) – id of the chain

+
+
Return type:
+

int

+
+
+
+ +
+
+get_num_inputs(chainable_only=False)
+

get the number of input pads of the filter +:return: number of input pads

+
+ +
+
+get_num_outputs(chainable_only=False)
+

get the number of output pads of the filter +:return: number of output pads

+
+ +
+
+get_num_pads(input)
+

get the number of available pads at input or output

+
+
Parameters:
+

input (bool) – True to get the input count, False for the output count.

+
+
Return type:
+

int

+
+
+
+ +
+
+get_output_pad(index_or_label)
+

resolve (unconnected) output filter pad from pad index or labels

+
+
Parameters:
+
    +
  • index (tuple(int,int,int) or str) – pad index or link label

  • +
  • index_or_label (Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str)

  • +
+
+
Returns:
+

filter output pad index and its link labels

+
+
Return type:
+

tuple(int,int,int), list(str)

+
+
+

Raises error if specified index does not resolve uniquely to an output pad

+
+ +
+
+index(value[, start[, stop]]) integer -- return first index of value.
+

Raises ValueError if the value is not present.

+

Supporting start and stop arguments is optional, but +recommended.

+
+ +
+
+insert(i, item)
+

S.insert(index, value) – insert value before index

+
+
Parameters:
+
    +
  • i (int)

  • +
  • item (Chain | str)

  • +
+
+
+
+ +
+
+is_chain_siso(chain_id, check_input=True, check_output=True, check_link=False)
+

True if specified filter chain is single-input and single-output

+
+
Parameters:
+
    +
  • chain_id (int) – chain id

  • +
  • check_input (bool) – False to check only for single-output, defaults to True

  • +
  • check_output (bool) – False to check only for single-input, defaults to True

  • +
  • check_link (bool) – True to return True if and only if the chain has no active connection, defaults to True

  • +
+
+
Return type:
+

bool

+
+
+
+ +
+
+iter_chains(skip_if_no_input=False, skip_if_no_output=False, chainable_only=False)
+

iterate over chains of the filtergraphobject

+
+
Parameters:
+
    +
  • skip_if_no_input (bool) – True to skip chains without available input pads, defaults to False

  • +
  • skip_if_no_output (bool) – True to skip chains without available output pads, defaults to False

  • +
  • chainable_only (bool) – True to further restrict skip_if_no_input and skip_if_no_input +arguments to require chainable input or output, defaults to False to +allow any input/output

  • +
+
+
Yield:
+

chain id and chain object

+
+
Return type:
+

Generator[tuple[int, Chain]]

+
+
+
+ +
+
+iter_input_labels(exclude_stream_specs=False)
+

iterate over the dangling labeled input pads of the filtergraph object

+
+
Parameters:
+

exclude_stream_specs (bool) – True to not include input streams

+
+
Yield:
+

a tuple of 3-tuple pad index and the pad index of the connected output pad if connected

+
+
Return type:
+

Generator[tuple[str, Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int]]

+
+
+
+ +
+
+iter_input_pads(pad=None, filter=None, chain=None, *, exclude_chainable=False, chainable_first=False, include_connected=False, unlabeled_only=False, chainable_only=False, full_pad_index=False)
+

Iterate over input pads of the filters on the filtergraph

+
+
Parameters:
+
    +
  • pad (int | None) – pad id, defaults to None

  • +
  • filter (int | None) – filter index, defaults to None

  • +
  • chain (int | None) – chain index, defaults to None

  • +
  • exclude_chainable (bool) – True to leave out the last input pads, defaults to False (all avail pads)

  • +
  • chainable_first (bool) – True to yield the last input first then the rest, defaults to False

  • +
  • include_connected (bool) – True to include pads connected to input streams, defaults to False

  • +
  • unlabeled_only (bool) – True to leave out named inputs, defaults to False to return all inputs

  • +
  • chainable_only (bool) – True to only iterate chainable pads, defaults to False to return all inputs

  • +
  • full_pad_index (bool) – True to return 3-element index

  • +
+
+
Yield:
+

filter pad index, link label, filter object, output pad index of connected filter if connected

+
+
Return type:
+

Generator[tuple[Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int, Filter, Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str | None]]

+
+
+
+ +
+
+iter_output_labels()
+

iterate over the dangling labeled output pads of the filtergraph object

+
+
Yield:
+

a tuple of 3-tuple pad index and the pad index of the connected input pad if connected

+
+
Return type:
+

Generator[tuple[str, Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int]]

+
+
+
+ +
+
+iter_output_pads(pad=None, filter=None, chain=None, *, exclude_chainable=False, chainable_first=False, include_connected=False, unlabeled_only=False, chainable_only=False, full_pad_index=False)
+

Iterate over output pads of the filter

+
+
Parameters:
+
    +
  • pad – pad id, defaults to None

  • +
  • filter – filter index, defaults to None

  • +
  • chain – chain index, defaults to None

  • +
  • exclude_chainable (bool) – True to leave out the last output pads, defaults to False (all avail pads)

  • +
  • chainable_first (bool) – True to yield the last output first then the rest, defaults to False

  • +
  • include_connected (bool) – True to include pads connected to output streams, defaults to False

  • +
  • unlabeled_only (bool) – True to leave out named outputs, defaults to False to return all outputs

  • +
  • chainable_only (bool) – True to only iterate chainable pads, defaults to False to return all outputs

  • +
  • full_pad_index (bool) – True to return 3-element index

  • +
+
+
Yield:
+

filter pad index, link label, filter object, output pad index of connected filter if connected

+
+
Return type:
+

Generator[tuple[Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int, Filter, Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str | None]]

+
+
+
+ +
+
+join(right, how='per_chain', n_links='all', strict=False, unlabeled_only=False, chain_siso=True, replace_sws_flags=None)
+

filtergraph auto-connector

+
+
Parameters:
+
    +
  • right (FilterGraphObject | str) – receiving filtergraph object

  • +
  • how (Literal['chainable', 'per_chain', 'all', 'auto']) –

    method on how to mate input and output, defaults to "per_chain".

    +
      +
    • 'chainable': joins only chainable input pads and output pads.

    • +
    • +
      'per_chain': joins one pair of first available input pad and output pad of each

      mating chains. Source and sink chains are ignored.

      +
      +
      +
    • +
    • 'all': joins all input pads and output pads

    • +
    • 'auto': tries 'per_chain' first, if fails, then tries 'all'.

    • +
    +

  • +
  • n_links (int | Literal['all']) – number of left output pads to be connected to the right input pads, default: 0 +(all matching links). If how=='per_chain', n_links connections are made +per chain.

  • +
  • strict (bool) – True to raise exception if numbers of available pads do not match, default: False

  • +
  • unlabeled_only (bool) – True to ignore labeled unconnected pads, defaults to False

  • +
  • chain_siso (bool) – True to chain the single-input single-output connection, default: True

  • +
  • replace_sws_flags (bool) – True to use other’s sws_flags if present, +False to ignore other’s sws_flags, +None to throw an exception (default)

  • +
+
+
Returns:
+

Graph with the appended filter chains or None if inplace=True.

+
+
Return type:
+

Graph | None

+
+
+
+ +
+ +

set a filtergraph link

+
+
Parameters:
+
    +
  • inpad (PAD_INDEX) – input pad ids

  • +
  • outpad (PAD_INDEX) – output pad index

  • +
  • label (str | None) – desired label name, defaults to None (=reuse inpad/outpad label or unnamed link)

  • +
  • preserve_label (Literal[False, 'input', 'output']) – False to remove the labels of the input and output pads (default) or +‘input’ to prefer the input label or ‘output’ to prefer the output +label.

  • +
  • force (bool) – True to drop conflicting existing link, defaults to False

  • +
+
+
Returns:
+

assigned label of the created link. Unnamed links gets a +unique integer value assigned to it.

+
+
Return type:
+

str | int

+
+
+

..notes:

+
+
    +
  • Unless force=True, inpad pad must not be already connected

  • +
  • User-supplied label name is a suggested name, and the function could +modify the name to maintain integrity.

  • +
  • If inpad or outpad were previously named, their names will be dropped +unless one matches the user-supplied label.

  • +
  • No guarantee on consistency of the link label (both named and unnamed) +during the life of the object

  • +
+
+
+ +
+
+next_input_pad(pad=None, filter=None, chain=None, chainable_first=False, unlabeled_only=False, chainable_only=False, full_pad_index=False, exclude_indices=None)
+

get next available input pad

+
+
Parameters:
+
    +
  • pad (int | None) – pad id, defaults to None

  • +
  • filter (int | None) – filter index, defaults to None

  • +
  • chain (int | None) – chain index, defaults to None

  • +
  • chainable_first (bool) – True to retrieve the last pad first, then the rest sequentially, defaults to False

  • +
  • unlabeled_only (bool) – True to retrieve only unlabeled pad, defaults to False

  • +
  • chainable_only (bool) – True to only iterate chainable pads, defaults to False to return all inputs

  • +
  • full_pad_index (bool) – True to return 3-element index, defaults to False

  • +
  • exclude_indices (Sequence[Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int] | None) – List pad indices to skip, defaults to None to allow all

  • +
+
+
Returns:
+

The index of the pad or None if no pad found

+
+
Return type:
+

Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | None

+
+
+
+ +
+
+next_output_pad(pad=None, filter=None, chain=None, chainable_first=False, unlabeled_only=False, chainable_only=False, full_pad_index=False, exclude_indices=None)
+

get next available output pad

+
+
Parameters:
+
    +
  • pad (int | None) – pad id, defaults to None

  • +
  • filter (int | None) – filter index, defaults to None

  • +
  • chain (int | None) – chain index, defaults to None

  • +
  • chainable_first (bool) – True to retrieve the last pad first, then the rest sequentially, defaults to False

  • +
  • unlabeled_only (bool) – True to retrieve only unlabeled pad, defaults to False

  • +
  • chainable_only (bool) – True to only iterate chainable pads, defaults to False to return all inputs

  • +
  • full_pad_index (bool) – True to return 3-element index, defaults to False

  • +
  • exclude_indices (Sequence[Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int] | None) – List pad indices to skip, defaults to None to allow all

  • +
+
+
Returns:
+

The index of the pad or None if no pad found

+
+
Return type:
+

Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | None

+
+
+
+ +
+
+pop([index]) item -- remove and return item at index (default last).
+

Raise IndexError if list is empty or index is out of range.

+
+ +
+
+rattach(left, left_on=None, right_on=None)
+

attach filter(s), chain(s), or label(s) to a filtergraph object

+
+
Parameters:
+
    +
  • left (FilterGraphObject | str | list[FilterGraphObject | str]) – input filtergraph object, filtergraph expression, or label, or list thereof

  • +
  • right – output filterchain, filtergraph expression, or label, or list thereof

  • +
  • left_on (Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str | list[Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str | None] | None) – pad_index, specify the pad on left, default to None (first available)

  • +
  • right_on (Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str | list[Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str | None] | None) – pad index, specifies which pad on the right graph, defaults to None (first available)

  • +
+
+
Returns:
+

new filtergraph object

+
+
Return type:
+

Graph

+
+
+

One and only one of left or right may be a list or a label.

+

If pad indices are not specified, only the first available output/input pad is linked. If the +primary filtergraph object is Filter or Chain, the chainable pad (i.e., the last pad) will be +chosen.

+
+ +
+
+rconnect(left, from_left, to_right, from_right=None, to_left=None, chain_siso=True, replace_sws_flags=None)
+

append another filtergraph object and make upstream connections

+
+
Parameters:
+
    +
  • left (FilterGraphObject | str) – transmitting filtergraph object

  • +
  • right – receiving filtergraph object

  • +
  • from_left (Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str | list[Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str]) – output pad ids or labels of left fg

  • +
  • to_right (Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str | list[Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str]) – input pad ids or labels of the right fg

  • +
  • from_right (Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str | list[Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str] | None) – output pad ids or labels of the right fg

  • +
  • to_left (Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str | list[Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str] | None) – input pad ids or labels of this left fg

  • +
  • chain_siso (bool) – True to chain the single-input single-output connection, default: True

  • +
  • replace_sws_flags (bool | None) – True to use right sws_flags if present, +False to drop right sws_flags, +None to throw an exception (default)

  • +
+
+
Returns:
+

new filtergraph object

+
+
Return type:
+

Graph | Chain

+
+
+
    +
  • link labels may be auto-renamed if there is a conflict

  • +
+
+ +
+
+remove(item)
+

S.remove(value) – remove first occurrence of value. +Raise ValueError if the value is not present.

+
+ +
+
+remove_label(label)
+

remove an input/output label

+
+
Parameters:
+

label (str) – linkn label

+
+
+
+ +
+
+rename_label(old_label, new_label)
+

rename an existing link label

+
+
Parameters:
+
    +
  • old_label (str) – existing label named

  • +
  • new_label (str) – new desired label name or None to make it unnamed label

  • +
+
+
Returns:
+

actual label name or None if unnamed

+
+
Return type:
+

str | None

+
+
+

Note:

+
    +
  • new_label is not guaranteed, and actual label depends on existing labels

  • +
+
+ +
+
+resolve_pad_index(index_or_label, *, is_input=True, chain_id_omittable=False, filter_id_omittable=False, pad_id_omittable=False, resolve_omitted=True, chain_fill_value=None, filter_fill_value=None, pad_fill_value=None, chainable_first=False)
+

Resolve unconnected label or pad index to full 3-element pad index

+
+
Parameters:
+
    +
  • index_or_label (Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str | None) – pad index set or pad label or None to auto-select

  • +
  • is_input (bool) – True to resolve an input pad, else an output pad, defaults to True

  • +
  • chain_id_omittable (bool) – True to allow None chain index, defaults to False

  • +
  • filter_id_omittable (bool) – True to allow None filter index, defaults to False

  • +
  • pad_id_omittable (bool) – True to allow None pad index, defaults to False

  • +
  • resolve_omitted (bool) – True to fill each omitted value with the prescribed fill value.

  • +
  • chain_fill_value (int | None) – if chain_id_omittable=True and chain index is either not +given or None, this value will be returned, defaults to None, +which returns the first available pad.

  • +
  • filter_fill_value (int | None)

  • +
  • pad_fill_value (int | None)

  • +
  • chainable_first (bool)

  • +
+
+
Return type:
+

Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int

+
+
+
+
:param filter_fill_value:if filter_id_omittable=True and filter index is either not

given or None, this value will be returned, defaults to None, +which returns the first available pad.

+
+
+
+
Parameters:
+
    +
  • pad_fill_value (int | None) – if pad_id_omittable=True and either index is None or +pad index is None, this value will be returned, defaults to None, +which returns the first available pad.

  • +
  • chainable_first (bool) – if True, chainable pad is selected first, defaults to False

  • +
  • index_or_label (Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str | None)

  • +
  • is_input (bool)

  • +
  • chain_id_omittable (bool)

  • +
  • filter_id_omittable (bool)

  • +
  • pad_id_omittable (bool)

  • +
  • resolve_omitted (bool)

  • +
  • chain_fill_value (int | None)

  • +
  • filter_fill_value (int | None)

  • +
+
+
Return type:
+

Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int

+
+
+

One and only one of index and label must be specified. If the given index +or label is invalid, it raises FiltergraphPadNotFoundError.

+
+ +
+
+resolve_pad_indices(indices_or_labels, *, is_input=True, resolve_omitted=True, chainable_first=False, unlabeled_only=False, chainable_only=False)
+

Resolve unconnected labels or pad indices to full 3-element pad indices

+
+
Parameters:
+
    +
  • indices_or_labels (Sequence[Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int | str | None]) – a list of pad indices or pad labels or None to auto-select

  • +
  • is_input (bool) – True to resolve an input pad, else an output pad, defaults to True

  • +
  • chainable_first (bool) – if True, chainable pad is selected first, defaults to False

  • +
  • unlabeled_only (bool) – True to retrieve only unlabeled pad, defaults to False

  • +
  • chainable_only (bool) – True to only iterate chainable pads, defaults to False to return all pads

  • +
  • resolve_omitted (bool)

  • +
+
+
Return type:
+

list[Tuple[int | None, int | None, int] | Tuple[int | None, int | None] | Tuple[int | None] | int]

+
+
+

One and only one of index and label must be specified. If the given index +or label is invalid, it raises FiltergraphPadNotFoundError.

+

Omitted pads

+
+ +
+
+reverse()
+

S.reverse() – reverse IN PLACE

+
+ +
+
+rjoin(left, how='per_chain', n_links='all', strict=False, unlabeled_only=False, chain_siso=True, replace_sws_flags=None)
+

filtergraph auto-connector

+
+
Parameters:
+
    +
  • left (FilterGraphObject | str) – transmitting filtergraph object

  • +
  • how (Literal['chainable', 'per_chain', 'all', 'auto']) –

    method on how to mate input and output, defaults to "per_chain".

    +
      +
    • 'chainable': joins only chainable input pads and output pads.

    • +
    • +
      'per_chain': joins one pair of first available input pad and output pad of each

      mating chains. Source and sink chains are ignored.

      +
      +
      +
    • +
    • 'all': joins all input pads and output pads

    • +
    • 'auto': tries 'per_chain' first, if fails, then tries 'all'.

    • +
    +

  • +
  • n_links (int | Literal['all']) – number of left output pads to be connected to the right input pads, default: 0 +(all matching links). If how=='per_chain', n_links connections are made +per chain.

  • +
  • strict (bool) – True to raise exception if numbers of available pads do not match, default: False

  • +
  • unlabeled_only (bool) – True to ignore labeled unconnected pads, defaults to False

  • +
  • chain_siso (bool) – True to chain the single-input single-output connection, default: True

  • +
  • replace_sws_flags (bool) – True to use other’s sws_flags if present, +False to ignore other’s sws_flags, +None to throw an exception (default)

  • +
+
+
Returns:
+

Graph with the appended filter chains or None if inplace=True.

+
+
Return type:
+

Graph | None

+
+
+
+ +
+
+stack(other, auto_link=False, replace_sws_flags=None)
+

stack another Graph to this Graph

+
+
Parameters:
+
    +
  • other (FilterGraphObject | str) – other filtergraph

  • +
  • auto_link (bool) – True to connect matched I/O labels, defaults to None

  • +
  • replace_sws_flags (bool | None) – True to use other’s sws_flags if present, +False to ignore other’s sws_flags, +None to throw an exception (default)

  • +
+
+
Returns:
+

new filtergraph object

+
+
Return type:
+

Graph

+
+
+
+

Remarks

+
    +
  • extend() and import links

  • +
  • If auto-link=False, common labels may be renamed.

  • +
  • For more explicit linking rather than the auto-linking, use connect() instead.

  • +
+

TO-CHECK/TO-DO: what happens if common link labels are already linked

+
+
+ +
+
+sws_flags
+

swscale flags for automatically inserted scalers

+
+
Type:
+

Filter|None

+
+
+
+ +
+ +

unlink specified links

+
+
Parameters:
+
    +
  • label (str|int, optional) – specify all the links with this label, defaults to None

  • +
  • inpad (tuple(int,int,int), optional) – specify the link with this inpad pad, defaults to None

  • +
  • outpad (tuple(int,int,int), optional) – specify all the links with this outpad pad, defaults to None

  • +
+
+
+
+ +
+ +
+
+ + +
+
+
+ +
+ +
+

© Copyright 2021-2022, Takeshi (Kesh) Ikuma, Louisiana State University Health Sciences Center.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/finder_ffdl.html b/docs/finder_ffdl.html new file mode 100644 index 00000000..f3283d76 --- /dev/null +++ b/docs/finder_ffdl.html @@ -0,0 +1,139 @@ + + + + + + + + + ffmpegio-plugin-downloader: An ffmpegio plugin to download latest FFmpeg release binaries — python-ffmpegio 0.11.0 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + +
  • + View page source +
  • +
+
+
+
+
+ +
+

ffmpegio-plugin-downloader: An ffmpegio plugin to download latest FFmpeg release binaries

+

PyPI PyPI - Status PyPI - Python Version GitHub License GitHub Workflow Status

+

Python ffmpegio package aims to bring +the full capability of FFmpeg to read, write, and manipulate multimedia +data to Python. FFmpeg is an open-source cross-platform multimedia framework, which can handle +most of the multimedia formats available today.

+

One caveat of FFmpeg is that there is no official program installer for Windows and MacOS (although +homebrew could be used for the latter). ffmpegio-plugin-downloader adds a capability to download +the latest release build of FFmpeg and enables the ffmpegio package to detect the paths of ffmpeg +and ffprobe automatically. This mechanism is supported by ffmpeg-downloader +package. Downloading of the release build must be performed interactively from the terminal screen, +outside of Python.

+
+
+

Use

+

Install the package (which also installs ffmpeg-downloader package). Then, run ffmpeg_downloader to +download and install the latest release:

+
pip install ffmpegio-core ffmpegio-plugin-downloader
+
+python -m ffmpeg_downloader # downloads and installs the latest release
+
+
+

Once the plugin and the FFmpeg executables are installed, ffmpegio will automatically +detect the downloaded executables.

+

At a later date, the installed FFmpeg can be updated to the latest release

+
python -m ffmpeg_downloader -U # downloads and updates to the latest release
+
+
+
+

Note

+

ffmpegio-plugin-downloader will not be activated if ffmpeg and ffprobe are +already available on the system PATH.

+
+
+ + +
+
+
+ +
+ +
+

© Copyright 2021-2022, Takeshi (Kesh) Ikuma, Louisiana State University Health Sciences Center.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/genindex.html b/docs/genindex.html new file mode 100644 index 00000000..2b39ebbd --- /dev/null +++ b/docs/genindex.html @@ -0,0 +1,1084 @@ + + + + + + + + Index — python-ffmpegio 0.11.0 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + +
  • +
  • +
+
+
+
+
+ + +

Index

+ +
+ A + | B + | C + | D + | E + | F + | G + | I + | J + | K + | L + | M + | N + | O + | P + | Q + | R + | S + | T + | U + | V + | W + +
+

A

+ + + +
+ +

B

+ + + +
+ +

C

+ + + +
+ +

D

+ + + +
+ +

E

+ + + +
+ +

F

+ + + +
+ +

G

+ + + +
+ +

I

+ + + +
+ +

J

+ + +
+ +

K

+ + +
+ +

L

+ + + +
+ +

M

+ + + +
+ +

N

+ + + +
+ +

O

+ + +
+ +

P

+ + + +
+ +

Q

+ + +
+ +

R

+ + + +
+ +

S

+ + + +
+ +

T

+ + + +
+ +

U

+ + + +
+ +

V

+ + + +
+ +

W

+ + + +
+ + + +
+
+
+ +
+ +
+

© Copyright 2021-2022, Takeshi (Kesh) Ikuma, Louisiana State University Health Sciences Center.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/index.html b/docs/index.html new file mode 100644 index 00000000..03a83a5f --- /dev/null +++ b/docs/index.html @@ -0,0 +1,438 @@ + + + + + + + + + ffmpegio-core: Media I/O with FFmpeg in Python — python-ffmpegio 0.11.0 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

ffmpegio-core: Media I/O with FFmpeg in Python

+

PyPI PyPI - Status PyPI - Python Version GitHub License GitHub Workflow Status

+

Python ffmpegio package aims to bring the full capability of FFmpeg +to read, write, probe, and manipulate multimedia data to Python. FFmpeg is an open-source cross-platform +multimedia framework, which can handle most of the multimedia formats available today.

+
+

Main Features

+
    +
  • Pure-Python light-weight package interacting with FFmpeg executable found in +your system

  • +
  • Read, write, filter, and create functions for audio, image, and video data

  • +
  • Context-managing ffmpegio.open to perform stream read/write operations of video and audio

  • +
  • Media readers can output the data in a Numpy array (if Numpy is installed) or a plain bytes +objects in a dict. The mode of operation can be switched with ffmpegio.use function.

  • +
  • Media writers can write a new media file from either data given in a Numpy array or bytes +objects in a dict.

  • +
  • Write Matplotlib figures to images or to a video (a simpler interface than Matplotlib’s Animation writers).

  • +
  • Probe media file information

  • +
  • Accepts all FFmpeg options including filter graphs

  • +
  • Transcode a media file to another in Python

  • +
  • Supports a user callback whenever FFmpeg updates its progress information file +(see -progress FFmpeg option)

  • +
  • ffconcat scripter to make the use of -f concat demuxer easier

  • +
  • I/O device enumeration to eliminate the need to look up device names. (currently supports only: Windows DirectShow)

  • +
  • More features to follow

  • +
+
+
+

Installation

+

Install the full ffmpegio package via pip:

+
pip install ffmpegio
+
+
+

Following optional external packages are required to enable the ffmpegio features that interact +with them.

+ + + + + + + + + + + + + + + + + + + + + + + + + +

Distro package name

ffmpegio features

Deprecated plugin names

numpy

Support Numpy array inputs and outputs intead of bytes

ffmpegio

matplotlib

Support generation of images or videos from Matplotlib figure

ffmpegio-plugin-mpl

ffmepeg-downloader

Support the FFmpeg binaries installed by the ffdl command

ffmpegio-plugin-downloader

static-ffmpeg

Support the FFmpeg binaries installed by static-ffmpeg

ffmpegio-plugin-static-ffmpeg

+

These features are automatically enabled if the external packages are installed along along side with ffmpegio. +ffmpegio is imported

+
+

Note

+

Prior to v0.11.0, these features were only enabled via installing separate plugin packages (listed in the table above). +ffmpegio v0.11 and ffmpegio-core v0.11 are identical, and ffmpegio-core will no longer receive +the updates.

+
+
+
+

Documentation

+

Visit our GitHub page here

+
+
+

Examples

+

To import ffmpegio

+
>>> import ffmpegio
+
+
+ +
+

Transcoding

+
>>> # transcode, overwrite output file if exists, showing the FFmpeg log
+>>> ffmpegio.transcode('input.avi', 'output.mp4', overwrite=True, show_log=True)
+
+>>> # 1-pass H.264 transcoding
+>>> ffmpegio.transcode('input.avi', 'output.mkv', vcodec='libx264', show_log=True,
+>>>                    preset='slow', crf=22, acodec='copy')
+
+>>> # 2-pass H.264 transcoding
+>>> ffmpegio.transcode('input.avi', 'output.mkv', two_pass=True, show_log=True,
+>>>                    **{'c:v':'libx264', 'b:v':'2600k', 'c:a':'aac', 'b:a':'128k'})
+
+>>> # concatenate videos using concat demuxer
+>>> files = ['/video/video1.mkv','/video/video2.mkv']
+>>> ffconcat = ffmpegio.FFConcat()
+>>> ffconcat.add_files(files)
+>>> with ffconcat: # generates temporary ffconcat file
+>>>     ffmpegio.transcode(ffconcat, 'output.mkv', f_in='concat', codec='copy', safe_in=0)
+
+
+
+
+

Read Audio Files

+
>>> # read audio samples in its native sample format and return all channels
+>>> fs, x = ffmpegio.audio.read('myaudio.wav')
+>>> # fs: sampling rate in samples/second, x: [nsamples x nchannels] numpy array
+
+>>> # read audio samples from 24.15 seconds to 63.2 seconds, pre-convert to mono in float data type
+>>> fs, x = ffmpegio.audio.read('myaudio.flac', ss=24.15, to=63.2, sample_fmt='dbl', ac=1)
+
+>>> # read filtered audio samples first 10 seconds
+>>> #   filter: equalizer which attenuate 10 dB at 1 kHz with a bandwidth of 200 Hz
+>>> fs, x = ffmpegio.audio.read('myaudio.mp3', t=10.0, af='equalizer=f=1000:t=h:width=200:g=-10')
+
+
+
+
+

Read Image Files / Capture Video Frames

+
>>> # list supported image extensions
+>>> ffmpegio.caps.muxer_info('image2')['extensions']
+['bmp', 'dpx', 'exr', 'jls', 'jpeg', 'jpg', 'ljpg', 'pam', 'pbm', 'pcx', 'pfm', 'pgm', 'pgmyuv',
+ 'png', 'ppm', 'sgi', 'tga', 'tif', 'tiff', 'jp2', 'j2c', 'j2k', 'xwd', 'sun', 'ras', 'rs', 'im1',
+ 'im8', 'im24', 'sunras', 'xbm', 'xface', 'pix', 'y']
+
+>>> # read BMP image with auto-detected pixel format (rgb24, gray, rgba, or ya8)
+>>> I = ffmpegio.image.read('myimage.bmp') # I: [height x width x ncomp] numpy array
+
+>>> # read JPEG image, then convert to grayscale and proportionally scale so the width is 480 pixels
+>>> I = ffmpegio.image.read('myimage.jpg', pix_fmt='grayscale', s='480x-1')
+
+>>> # read PNG image with transparency, convert it to plain RGB by filling transparent pixels orange
+>>> I = ffmpegio.image.read('myimage.png', pix_fmt='rgb24', fill_color='orange')
+
+>>> # capture video frame at timestamp=4:25.3 and convert non-square pixels to square
+>>> I = ffmpegio.image.read('myvideo.mpg', ss='4:25.3', square_pixels='upscale')
+
+>>> # capture 5 video frames and tile them on 3x2 grid with 7px between them, and 2px of initial margin
+>>> I = ffmpegio.image.read('myvideo.mp4', vf='tile=3x2:nb_frames=5:padding=7:margin=2')
+
+>>> # create spectrogram of the audio input (must specify pix_fmt if input is audio)
+>>> I = ffmpegio.image.read('myaudio.mp3', filter_complex='showspectrumpic=s=960x540', pix_fmt='rgb24')
+
+
+
+
+

Read Video Files

+
>>> # read 50 video frames at t=00:32:40 then convert to grayscale
+>>> fs, F = ffmpegio.video.read('myvideo.mp4', ss='00:32:40', vframes=50, pix_fmt='gray')
+>>> #  fs: frame rate in frames/second, F: [nframes x height x width x ncomp] numpy array
+
+>>> # get running spectrogram of audio input (must specify pix_fmt if input is audio)
+>>> fs, F = ffmpegio.video.read('myvideo.mp4', pix_fmt='rgb24', filter_complex='showspectrum=s=1280x480')
+
+
+
+
+

Read Multiple Files or Streams

+
>>> # read both video and audio streams (1 ea)
+>>> rates, data = ffmpegio.media.read('mymedia.mp4')
+>>> #  rates: dict of frame rate and sampling rate: keys="v:0" and "a:0"
+>>> #  data: dict of video frame array and audio sample array: keys="v:0" and "a:0"
+
+>>> # combine video and audio files
+>>> rates, data = ffmpegio.media.read('myvideo.mp4','myaudio.mp3')
+
+>>> # get output of complex filtergraph (can take multiple inputs)
+>>> expr = "[v:0]split=2[out0][l1];[l1]edgedetect[out1]"
+>>> rates, data = ffmpegio.media.read('myvideo.mp4',filter_complex=expr,map=['[out0]','[out1]'])
+>>> #  rates: dict of frame rates: keys="v:0" and "v:1"
+>>> #  data: dict of video frame arrays: keys="v:0" and "v:1"
+
+
+
+
+

Write Audio, Image, & Video Files

+
>>> # create a video file from a numpy array
+>>> ffmpegio.video.write('myvideo.mp4', rate, F)
+
+>>> # create an image file from a numpy array
+>>> ffmpegio.image.write('myimage.png', F)
+
+>>> # create an audio file from a numpy array
+>>> ffmpegio.audio.write('myaudio.mp3', rate, x)
+
+
+
+
+

Filter Audio, Image, & Video Data

+
>>> # Add fade-in and fade-out effects to audio data
+>>> fs_out, y = ffmpegio.audio.filter('afade=t=in:ss=0:d=15,afade=t=out:st=875:d=25', fs_in, x)
+
+>>> # Apply mirror effect to an image
+>>> I_out = ffmpegio.image.filter('crop=iw/2:ih:0:0,split[left][tmp];[tmp]hflip[right];[left][right] hstack', I_in)
+
+>>> # Add text at the center of the video frame
+>>> filter = "drawtext=fontsize=30:fontfile=FreeSerif.ttf:text='hello world':x=(w-text_w)/2:y=(h-text_h)/2"
+>>> fs_out, F_out = ffmpegio.video.filter(filter, fs_in, F_in)
+
+
+
+
+

Stream I/O

+
>>> # process video 100 frames at a time and save output as a new video
+>>> # with the same frame rate
+>>> with ffmpegio.open('myvideo.mp4', 'rv', blocksize=100) as fin,
+>>>      ffmpegio.open('myoutput.mp4', 'wv', rate=fin.rate) as fout:
+>>>     for frames in fin:
+>>>         fout.write(myprocess(frames))
+
+
+
+
+

Filtergraph Builder

+
>>> # build complex filtergraph
+>>> from ffmpegio import filtergraph as fgb
+>>>
+>>> v0 = "[0]" >> fgb.trim(start_frame=10, end_frame=20)
+>>> v1 = "[0]" >> fgb.trim(start_frame=30, end_frame=40)
+>>> v3 = "[1]" >> fgb.hflip()
+>>> v2 = (v0 | v1) + fgb.concat(2)
+>>> v5 = (v2|v3) + fgb.overlay(eof_action='repeat') + fgb.drawbox(50, 50, 120, 120, 'red', t=5)
+>>> v5
+<ffmpegio.filtergraph.Graph.Graph object at 0x2a4ef084bd0>
+    FFmpeg expression: "[0]trim=start_frame=10:end_frame=20[L0];[0]trim=start_frame=30:end_frame=40[L1];[L0][L1]concat=2[L2];[1]hflip[L3];[L2][L3]overlay=eof_action=repeat,drawbox=50:50:120:120:red:t=5"
+    Number of chains: 5
+      chain[0]: [0]trim=start_frame=10:end_frame=20[L0];
+      chain[1]: [0]trim=start_frame=30:end_frame=40[L1];
+      chain[2]: [L0][L1]concat=2[L2];
+      chain[3]: [1]hflip[L3];
+      chain[4]: [L2][L3]overlay=eof_action=repeat,drawbox=50:50:120:120:red:t=5[UNC0]
+    Available input pads (0):
+    Available output pads: (1): (4, 1, 0)
+
+
+
+
+

Device I/O Enumeration

+
>>> # record 5 minutes of audio from Windows microphone
+>>> fs, x = ffmpegio.audio.read('a:0', f_in='dshow', sample_fmt='dbl', t=300)
+
+>>> # capture Windows' webcam frame
+>>> with ffmpegio.open('v:0', 'rv', f_in='dshow') as webcam,
+>>>     for frame in webcam:
+>>>         process_frame(frame)
+
+
+
+
+

Progress Callback

+
>>> import pprint
+
+>>> # progress callback
+>>> def progress(info, done):
+>>>     pprint(info) # bunch of stats
+>>>     if done:
+>>>        print('video decoding completed')
+>>>     else:
+>>>        return check_cancel_command(): # return True to kill immediately
+
+>>> # can be used in any butch processing
+>>> rate, F = ffmpegio.video.read('myvideo.mp4', progress=progress)
+
+>>> # as well as for stream processing
+>>> with ffmpegio.open('myvideo.mp4', 'rv', blocksize=100, progress=progress) as fin:
+>>>     for frames in fin:
+>>>         myprocess(frames)
+
+
+
+
+

Run FFmpeg and FFprobe Directly

+
>>> from ffmpegio import ffmpeg, FFprobe, ffmpegprocess
+>>> from subprocess import PIPE
+
+>>> # call with options as a long string
+>>> ffmpeg('-i input.avi -b:v 64k -bufsize 64k output.avi')
+
+>>> # or call with list of options
+>>> ffmpeg(['-i', 'input.avi' ,'-r', '24', 'output.avi'])
+
+>>> # the same for ffprobe
+>>> ffprobe('ffprobe -show_streams -select_streams a INPUT')
+
+>>> # specify subprocess arguments to capture stdout
+>>> out = ffprobe('ffprobe -of json -show_frames INPUT',
+                  stdout=PIPE, universal_newlines=True).stdout
+
+>>> # use ffmpegprocess to take advantage of ffmpegio's default behaviors
+>>> out = ffmpegprocess.run({"inputs": [("input.avi", None)],
+                             "outputs": [("out1.mp4", None),
+                                         ("-", {"f": "rawvideo", "vframes": 1, "pix_fmt": "gray", "an": None})
+                            }, capture_log=True)
+>>> print(out.stderr) # print the captured FFmpeg logs (banner text omitted)
+>>> b = out.stdout # width*height bytes of the first frame
+
+
+
+
+
+ + +
+
+
+ +
+ +
+

© Copyright 2021-2022, Takeshi (Kesh) Ikuma, Louisiana State University Health Sciences Center.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/install.html b/docs/install.html new file mode 100644 index 00000000..4222e2f4 --- /dev/null +++ b/docs/install.html @@ -0,0 +1,184 @@ + + + + + + + + + Installation — python-ffmpegio 0.11.0 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Installation

+

To use ffmpegio, the package must be installed on Python as well as +having the FFmpeg binary files at a location ffmpegio can find. In addition, +optional external packages can be installed to enable the ffmpegio features that interact +with them.

+

Install the ffmpegio package via pip.

+
pip install ffmpegio
+
+
+
+

Install FFmpeg program

+

There are two platform independent approaches to install FFmpeg for the use in Python:

+
+

::code::ffmpeg-downloader

+
+
+

::code::static-ffmpeg

+

The installation of FFmpeg is platform dependent. For Ubuntu/Debian Linux,

+
sudo apt install ffmpeg
+
+
+

and for MacOS,

+
brew install ffmpeg
+
+
+

no other actions are needed as these commands will place the FFmpeg executables +on the system path.

+

For Windows, it is a bit more complicated.

+
    +
  1. Download pre-built packages from the links available on the FFmpeg’s Download page.

  2. +
  3. Unzip the content and place the files in one of the following directories:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    Auto-detectable FFmpeg folder path

    Example

    %PROGRAMFILES%\ffmpeg

    C:\Program Files\ffmpeg

    %PROGRAMFILES(X86)%\ffmpeg

    C:\Program Files (x86)\ffmpeg

    %USERPROFILE%\ffmpeg

    C:\Users\john\ffmpeg

    %APPDATA%\ffmpeg

    C:\Users\john\AppData\Roaming\ffmpeg

    %APPDATA%\programs\ffmpeg

    C:\Users\john\AppData\Roaming\programs\ffmpeg

    %LOCALAPPDATA%\ffmpeg

    C:\Users\john\AppData\Local\ffmpeg

    %LOCALAPPDATA%\programs\ffmpeg

    C:\Users\john\AppData\Local\programs\ffmpeg

    +

    Keep the internal structure intact, i.e., the executables must be found at +ffmpeg\bin\ffmpeg.exe and ffmpeg\bin\ffprobe.exe.

    +

    There are two other alternative. First, the FFmpeg binaries could be placed on the +Python’s current working directory (i.e., os.getcwd()). Second, they could +be placed in an arbitrary location and use ffmpegio.set_path() to +specify the location. The latter feature is especially useful when ffmpegio is +bundled in a package (e.g., PyInstaller).

    +
  4. +
+
+
+
+ + +
+
+
+ +
+ +
+

© Copyright 2021-2022, Takeshi (Kesh) Ikuma, Louisiana State University Health Sciences Center.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/links.html b/docs/links.html new file mode 100644 index 00000000..66d9d271 --- /dev/null +++ b/docs/links.html @@ -0,0 +1,116 @@ + + + + + + + + + External Links — python-ffmpegio 0.11.0 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+ +
+ +
+ +
+

© Copyright 2021-2022, Takeshi (Kesh) Ikuma, Louisiana State University Health Sciences Center.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/mpl-writer.html b/docs/mpl-writer.html new file mode 100644 index 00000000..64102d63 --- /dev/null +++ b/docs/mpl-writer.html @@ -0,0 +1,174 @@ + + + + + + + + + Creating Videos from Matplotlib figure — python-ffmpegio 0.11.0 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Creating Videos from Matplotlib figure

+

While Matplotlib supports video creation via its +animation module, +its interface is a bit cranky because its primary role is to animate the figure on screen +rather than outputting figures to a video file. You must create an animation object first before +saving it as a video.

+

ffmpegio provides a direct method to write Matplotlib figure to a video write stream with +the same streaming interface as feeding the RGB frame data to FFmpeg.

+
+

Example

+

Create an MP4 video of Matplotlib’s animation example.

+
import ffmpegio as ff
+from matplotlib import pyplot as plt
+import numpy as np
+
+
+fig, ax = plt.subplots()
+
+x = np.arange(0, 2*np.pi, 0.01)
+line, = ax.plot(x, np.sin(x))
+
+interval=20 # delay in milliseconds
+save_count=50 # number of frames
+
+def animate(i):
+    line.set_ydata(np.sin(x + i / 50))  # update the data.
+    return line
+
+
+with ff.open(
+  "output.mp4", # output file name
+  "wv", # open file in write-video mode
+  1e3/interval, # framerate in frames/second
+  pix_fmt="yuv420p", # specify the pixel format (default is yuv444p)
+  # add other ffmpeg options as keywod argument as needed
+) as f:
+    for n in range(save_count):
+        animate(n) # update figure
+        f.write(fig) # write new video frame
+
+
+

Any video format can be chosen with this interface and any FFmpeg options can be specified here. +For instance, an GIF animation of the above example can be created with optimized color pallette. +To do this, we use palettegen and +paletteuse <https://ffmpeg.org/ffmpeg-filters.html#paletteuse>`__ filters and construct a video filtergraph:

+
split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse
+
+
+

This filtergraph string could be provided directly to ff.open as a vf keyword argument, +but let’s use ffmpegio.filtergraph submodule to construct it instead:

+
import ffmpegio.filtergraph as fgb
+
+vf = fgb.split() + fgb.palettegen() + fgb.paletteuse()
+
+with ff.open(
+  "output.gif", # output file name
+  "wv", # open file in write-video mode
+  1e3/interval, # framerate in frames/second
+  vf = vf # optimize the GIF palette
+) as f:
+    for n in range(save_count):
+        animate(n) # update figure
+        f.write(fig) # write new video frame
+
+
+
+
+ + +
+
+
+ +
+ +
+

© Copyright 2021-2022, Takeshi (Kesh) Ikuma, Louisiana State University Health Sciences Center.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/objects.inv b/docs/objects.inv new file mode 100644 index 00000000..c5ae9a69 Binary files /dev/null and b/docs/objects.inv differ diff --git a/docs/options.html b/docs/options.html new file mode 100644 index 00000000..635eba65 --- /dev/null +++ b/docs/options.html @@ -0,0 +1,519 @@ + + + + + + + + + FFmpeg Option References — python-ffmpegio 0.11.0 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

FFmpeg Option References

+

All open/read/write/filter functions in ffmpegio accepts any +FFmpeg options as their keyword arguments. Two rules +apply to construct Python function argument:

+
    +
  1. Drop the - from FFmpeg option name, e.g., enter -ss 50 as (..., ss=50, ...); and

  2. +
  3. All the options are assumed output options by default. To specify input options, append _in +to the option name. To apply -ss 50 to input url, enter (..., ss_in=50, ...). Global +options are automatically identified.

  4. +
+

The option values can be specified in any data type, but it must have a __str__ function defined +to convert Python data to correct FFmpeg string expression.

+
+

Common FFmpeg Options

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Name

type

V

A

I

O

Description

ss

float

X

X

X

X

Start time in seconds

t

float

X

X

X

X

Duration in seconds

to

float

X

X

X

X

End time in seconds (ignored if both ss` and t are set)

r

numeric

X

X

X

Video frame rate in frames/second

ar

numeric

X

X

X

Audio sampling rate in samples/second

s

(int,int)

X

X

X

Video frame size (width, height). Alt. str expression: wxh

pix_fmt

str

X

X

X

Video frame pixel format, defaults to auto-detect

vf

str

X

X

Video filtergraph (leave output pad unlabeled)

ac

int

X

X

X

Number of audio channels, defaults to auto-detect

sample_fmt

int

X

X

X

Audio sample format, defaults to None (same as input)

af

str

X

X

Audio filtergraph (leave output pad unlabeled)

crf

int

X

X

H.264 video encoding constant quality factor (0-51)

+
+

s output option

+

FFmpeg’s -s output option sets the output video frame size by using the scale video filter. However, +it does not allow non-positive values for width and height which the scale filter accepts. +ffmpegio alters this behavior by checking the s argument for <=0 width or height +and convert to vf argument.

+ + + + + + + + + + + + + + + + + +

width/height

Description

n (n>0)

Specifying the output size to be n pixels

0

Use the input size for the output

-n

Scale the dimension proportional to the other dimension then +make sure that the calculated dimension is divisible by n +and adjust the value if necessary. Only one of width or +height can be negative valued.

+

Note that passing both s with a non-positive value and vf +will raise an exception.

+
+
+

map output options

+

The output option -map is the (only?) FFmpeg option, which could be specified multiple times +in command line input. This goes against ffmpegio’s FFmpeg dict structure, and so map +argument is handled differently from the others. First, map argument must be a non-str sequence, +and each of its element is converted to -map option. Furthermore, each element could be a str or +else a sequence which items are then stringified and joined together with ‘:’.

+
+
+
+

Video Pixel Formats pix_fmt

+

There are many video pixel formats that FFmpeg support, which you can obtain with +caps.pix_fmts() function. For the I/O purpose, ffmpegio video/image +functions operate strictly with RGB or grayscale formats listed below.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

ncomp

dtype

pix_fmt

Description

1

|u8

gray

grayscale

1

<u2

gray10le

10-bit grayscale

1

<u2

gray12le

12-bit grayscale

1

<u2

gray14le

14-bit grayscale

1

<u2

gray16le

16-bit grayscale (default for <u2)

1

<f4

grayf32le

floating-point grayscale

2

|u1

ya8

grayscale with alpha channel

2

<u2

ya16le

16-bit grayscale with alpha channel

3

|u1

rgb24

RGB

3

<u2

rgb48le

16-bit RGB

4

|u1

rgba

RGB with alpha transparency channel

4

<u2

rgba64le

16-bit RGB with alpha channel

+

Note that each video pixel format has a specific dtype (or dtype_in) str argument, which +follows the NumPy array data type convention.

+
+
+

Audio Sample Formats sample_fmt

+

FFmpeg offers its audio channels in both interleaved and planar sample formats (sample_fmt, +run caps.sample_fmts() to list available formats). For the I/O purpose, +ffmpegio audio functions always use the interleaved formats:

+ + + + + + + + + + + + + + + + + + + + + + + +

dtype

sample_fmt

|u1

u8

<i2

s16

<i4

s32

<f4

flt

<f8

dbl

+

Like pix_fmt, sample_fmt also has concrete relationship to the dtype option

+
+
+

Built-in Video Manipulation Options

+

While the use of the vf or filter_complex option enables the full spectrum +of FFmpeg’s filtering capability (FFmpeg Documentation), +ffmpegio’s video and image routines adds several convenience +video options to perform simple video maninpulations without the need of setting +up a filtergraph.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Options to manipulate video frames

name

value

FFmpeg filter

Description

crop

seq(int[, int[, int[, int]]])

crop

video frame cropping/padding, values representing the number of pixels to crop from [left top right bottom]. +If positive, the video frame is cropped from the respective edge. If negative, the video frame is padded on +the respective edge. If right or bottom is missing, uses the same value as left or top, respectively. If top +is missing, it defaults to 0.

flip

{'horizontal', 'vertical', 'both'}

hflip or vflip

flip the video frames horizontally, vertically, or both.

transpose

int

transpose

tarnspose the video frames. Its value specifies the mode of operation. Use 0 for the conventional transpose operation. +For the others, see the FFmpeg documentation.

square_pixels

{'upscale', 'downscale', 'upscale_even', +'downscale_even'}

scale and setsar

Resize video frames so that their pixels are square (i.e., SAR=1:1). +'upscale' stretches the short side +of the pixels while 'downscale' compresses the long side. +'even' makes sure that the resulting frame size is even (required by some codecs).

remove_alpha

bool

overlay and color

Fill transparent background with fill_color color. This filter is automatically +inserted if input 'pix_fmt' has alpha but not the output.

fill_color

str

n/a

This option is used for the auto-conversion of an image with transparency to +opaque by setting the output option pix_fmt. The option value +specifies a color according to +FFmpeg Color Specifications. +Default color is 'white'.

+

Note that the these operations are pre-wired to perform in a specific order:

+
+
+ + + + + + blockdiag + + + + + + + square_pixels + + crop + + flip + + transpose + + + + + + + + + +
+
+

Video Manipulation Order

+
+
+

Be aware of this ordering as these filters are non-commutative (i.e., a change in the +order of operation alters the outcome). If your desired order of filters differs or +need to use additional filters, use the vf option to specify your own filtergraph.

+ + + + + + + + + + +
Examples of manipulated images
+_images/options-1.png +
+
ffmpegio.image.read('ffmpeg-logo.png')
+
+
+
+_images/options-2.png +
+
ffmpegio.image.read('ffmpeg-logo.png', crop=(100,100,0,0), transpose=0)
+
+
+
+_images/options-3.png +
+
ffmpegio.image.read('ffmpeg-logo.png', crop=(100,100,0,0), flip='both', size=(200,-1))
+
+
+
+
+
+ + +
+
+
+ +
+ +
+

© Copyright 2021-2022, Takeshi (Kesh) Ikuma, Louisiana State University Health Sciences Center.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/probe.html b/docs/probe.html new file mode 100644 index 00000000..aab58006 --- /dev/null +++ b/docs/probe.html @@ -0,0 +1,524 @@ + + + + + + + + + Media Probe Function References — python-ffmpegio 0.11.0 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Media Probe Function References

+

ffmpegio.probe module contains a full-featured ffprobe wrapper function +ffmpegio.probe.full_details() and its derivative functions, which are +tailored to retrieve specific type of information from a media file or stream.

+
+

List of Functions

+ + + + + + + + + + + + + + + + + + + + + + + + +

ffmpegio.probe.format_basic

Retrieve basic media format info

ffmpegio.probe.streams_basic

Retrieve basic info of media streams

ffmpegio.probe.video_streams_basic

Retrieve basic info of video streams

ffmpegio.probe.audio_streams_basic

Retrieve basic info of audio streams

ffmpegio.probe.full_details

Retrieve full details of a media file or stream

ffmpegio.probe.query

Query specific fields of media format or stream

ffmpegio.probe.frames

get frame information

+
+
+

Argument Type References

+
+
+ffmpegio.probe.IntervalSpec
+

Union type to specify the FFprobe read_intervals option

+

FFprobe will seek to the interval starting point and will continue reading from that. +An IntervalSpec argument can be specified in multiple ways to form the FFprobe read_intervals option:

+
    +
  1. str - pass through the argument as-is to ffprobe

  2. +
  3. int - read this numbers of packets to read from the beginning of the file

  4. +
  5. float - read packets over this duration in seconds from the beginning of the file

  6. +
  7. +
    tuple[str|float, str|int|float] - sets (start, end) points
      +
    • start: str = as-is, float = starting time in seconds

    • +
    • end: str = as-is, int = offset in # of packets, float = offset in seconds

    • +
    +
    +
    +
  8. +
  9. +
    dict - specifies start and end points with the following keys:
      +
    • 'start' - (str|float) start time

    • +
    • 'start_offset' - (str|float) start time offset from the previous read. Ignored if 'start' is present.

    • +
    • 'end' - (str|float) end time

    • +
    • 'end_offset' - (str|float|int) end time offset from the start time. Ignored if 'end' is present.

    • +
    +
    +
    +
  10. +
+
+ +
+
+

Function References

+
+
+ffmpegio.probe.format_basic(url, entries=None, keep_optional_fields=None, keep_str_values=False, cache_output=False, sp_kwargs=None)
+

Retrieve basic media format info

+
+
Parameters:
+
    +
  • url (str or seekable file-like object or bytes-like object) – URL of the media file/stream

  • +
  • entries (seq of str) – specify to narrow which information entries to retrieve. Default to None, to return all entries

  • +
  • keep_optional_fields (bool, optional) – True to return a missing optional field in the +returned dict with None or “N/A” (if keep_str_values +is True) as its value

  • +
  • keep_str_values (bool, optional) – True to keep all field values as str, +defaults to False to convert numeric values

  • +
  • cache_output (bool, optional) – True to cache FFprobe output, defaults to False

  • +
  • sp_kwargs (dict[str, Any], optional) – Additional keyword arguments for subprocess.run(), +default to None

  • +
+
+
Returns:
+

set of media format information.

+
+
Return type:
+

dict

+
+
+

Media Format Information Entries

+ + + + + + + + + + + + + + + + + + + + + + + +

name

type

filename

int

nb_streams

str

format_name

str

start_time

float

duration

float

+
+ +
+
+ffmpegio.probe.streams_basic(url, entries=None, keep_optional_fields=None, keep_str_values=False, cache_output=False, sp_kwargs=None)
+

Retrieve basic info of media streams

+
+
Parameters:
+
    +
  • url (str or seekable file-like object or bytes-like object) – URL of the media file/stream

  • +
  • entries (seq of str, optional) – specify to narrow which stream entries to retrieve. Default to None, returning all entries

  • +
  • keep_optional_fields (bool, optional) – True to return a missing optional field in the +returned dict with None or “N/A” (if keep_str_values +is True) as its value

  • +
  • keep_str_values (bool, optional) – True to keep all field values as str, +defaults to False to convert numeric values

  • +
  • cache_output (bool, optional) – True to cache FFprobe output, defaults to False

  • +
  • sp_kwargs (dict[str, Any], optional) – Additional keyword arguments for subprocess.run(), +default to None

  • +
+
+
Returns:
+

List of media stream information.

+
+
Return type:
+

list of dict

+
+
+

Media Stream Information dict Entries

+ + + + + + + + + + + + + + + + + +

name

type

index

int

codec_name

str

codec_type

str

+
+ +
+
+ffmpegio.probe.video_streams_basic(url, index=None, entries=None, keep_optional_fields=None, keep_str_values=False, cache_output=False, sp_kwargs=None)
+

Retrieve basic info of video streams

+
+
Parameters:
+
    +
  • url (str or seekable file-like object or bytes-like object) – URL of the media file/stream

  • +
  • index (int, optional) – video stream index. 0=first video stream. Defaults to None, which returns info of all video streams

  • +
  • entries (seq of str) – specify to narrow which information entries to retrieve. Default to None, to return all entries

  • +
  • keep_optional_fields (bool, optional) – True to return a missing optional field in the +returned dict with None or “N/A” (if keep_str_values +is True) as its value

  • +
  • keep_str_values (bool, optional) – True to keep all field values as str, +defaults to False to convert numeric values

  • +
  • cache_output (bool, optional) – True to cache FFprobe output, defaults to False

  • +
  • sp_kwargs (dict[str, Any], optional) – Additional keyword arguments for subprocess.run(), +default to None

  • +
+
+
Returns:
+

List of video stream information.

+
+
Return type:
+

list of dict

+
+
+

Video Stream Information Entries

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

name

type

index

int

codec_name

str

width

int

height

int

sample_aspect_ratio

Fractions

display_aspect_ratio

Fractions

pix_fmt

str

start_time

float

duration

float

frame_rate

Fractions

nb_frames

int

+
+ +
+
+ffmpegio.probe.audio_streams_basic(url, index=None, entries=None, keep_optional_fields=None, keep_str_values=False, cache_output=False, sp_kwargs=None)
+

Retrieve basic info of audio streams

+
+
Parameters:
+
    +
  • url (str or seekable file-like object or bytes-like object) – URL of the media file/stream

  • +
  • index (int, optional) – audio stream index. 0=first audio stream. Defaults to None, which returns info of all audio streams

  • +
  • entries (seq of str) – specify to narrow which information entries to retrieve. Default to None, to return all entries

  • +
  • keep_optional_fields (bool, optional) – True to return a missing optional field in the +returned dict with None or “N/A” (if keep_str_values +is True) as its value

  • +
  • keep_str_values (bool, optional) – True to keep all field values as str, +defaults to False to convert numeric values

  • +
  • cache_output (bool, optional) – True to cache FFprobe output, defaults to False

  • +
  • sp_kwargs (dict[str, Any], optional) – Additional keyword arguments for subprocess.run(), +default to None

  • +
+
+
Returns:
+

List of audio stream information.

+
+
Return type:
+

list of dict

+
+
+

Audio Stream Information Entries

+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

name

type

index

int

codec_name

str

sample_fmt

str

sample_rate

int

channels

int

channel_layout

str

start_time

float

duration

float

nb_samples

int

+
+
+ +
+
+ffmpegio.probe.full_details(url, show_format=True, show_streams=True, show_programs=False, show_chapters=False, select_streams=None, keep_str_values=False, cache_output=False, sp_kwargs=None)
+

Retrieve full details of a media file or stream

+
+
Parameters:
+
    +
  • url (str or seekable file-like object or bytes-like object) – URL of the media file/stream

  • +
  • show_format (bool, optional) – True to return format info, defaults to True

  • +
  • show_streams (bool, optional) – True to return stream info, defaults to True

  • +
  • show_programs (bool, optional) – True to return program info, defaults to False

  • +
  • show_chapters (bool, optional) – True to return chapter info, defaults to False

  • +
  • select_streams (str, int, optional) – Stream specifier of the streams to get info of, defaults to None to retrieve all

  • +
  • keep_str_values (bool, optional) – True to keep all field values as str, +defaults to False to convert numeric values

  • +
  • cache_output (bool, optional) – True to cache FFprobe output, defaults to False

  • +
  • sp_kwargs (dict[str, Any], optional) – Additional keyword arguments for subprocess.run(), +default to None

  • +
+
+
Returns:
+

media file information

+
+
Return type:
+

dict[str, str|Number|Fraction]

+
+
+
+ +
+
+ffmpegio.probe.query(url, streams=None, fields=None, keep_optional_fields=None, keep_str_values=False, cache_output=False, sp_kwargs=None)
+

Query specific fields of media format or stream

+
+
Parameters:
+
    +
  • url (str or seekable file-like object or bytes-like object) – URL of the media file/stream

  • +
  • streams (str, int, bool, optional) – stream specifier, defaults to None to get format

  • +
  • fields (sequence of str, optional) – list of format/stream fields to retrieve, defaults to None (all fields)

  • +
  • keep_optional_fields (bool, optional) – True to return a missing optional field in the +returned dict with None or “N/A” (if keep_str_values +is True) as its value

  • +
  • keep_str_values (bool, optional) – True to keep all field values as str, +defaults to False to convert numeric values

  • +
  • cache_output (bool, optional) – True to cache FFprobe output, defaults to False

  • +
  • sp_kwargs (dict[str, Any], optional) – Additional keyword arguments for subprocess.run(), +default to None

  • +
+
+
Returns:
+

field name-value dict. If streams argument is given but does not specify +index, a list of dict is returned instead

+
+
Return type:
+

dict or list or dict

+
+
+
+
Note: Unlike video_stream_basic() and audio_stream_basic(),

query() does not process ffprobe output except for the conversion +from str to float/int.

+
+
+
+ +
+
+ffmpegio.probe.frames(url, entries=None, streams=None, intervals=None, accurate_time=False, sp_kwargs=None)
+

get frame information

+
+
Parameters:
+
    +
  • url (str or seekable file-like object or bytes-like object) – URL of the media file/stream

  • +
  • entries (str or seq[str], optional) – names of frame attributes, defaults to None (get all attributes)

  • +
  • stream (str or int, optional) – stream specifier of the stream to retrieve the data of, defaults to None to get all streams

  • +
  • intervals (IntervalSpec or Sequence[IntervalSpec], optional) – time intervals to retrieve the data, see below for the details, defaults to None (get all)

  • +
  • accurate_time (bool | None) – True to return all ‘*_time’ attributes to be computed from associated timestamps and +stream timebase, defaults to False (= us accuracy)

  • +
  • accurate_time – bool, optional

  • +
  • sp_kwargs (dict[str, Any], optional) – Additional keyword arguments for subprocess.run(), +default to None

  • +
  • streams (str | int | None)

  • +
+
+
Returns:
+

frame information. list of dictionary if entries is None or a sequence; list of the selected entry +if entries is str (i.e., a single entry)

+
+
Return type:
+

list[dict] or list[str|int|float]

+
+
+
+ +
+
+ + +
+
+
+ +
+ +
+

© Copyright 2021-2022, Takeshi (Kesh) Ikuma, Louisiana State University Health Sciences Center.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/quick.html b/docs/quick.html new file mode 100644 index 00000000..384be5d5 --- /dev/null +++ b/docs/quick.html @@ -0,0 +1,524 @@ + + + + + + + + + Quick Start Guide — python-ffmpegio 0.11.0 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Quick Start Guide

+
+

Install

+

To use ffmpegio, the package must be installed on Python as well as +having the FFmpeg binary files at a location ffmpegio can find.

+

Install the full ffmpegio package via pip:

+
pip install ffmpegio
+
+
+

If numpy.ndarray data I/O is not needed, instead use

+
pip install ffmpegio-core
+
+
+

If FFmpeg is not installed on your system, please follow the instructions on +Installation page

+
+
+

Features

+

FFmpeg can read/write virtually any multimedia file out there, and ffmpegio uses +the FFmpeg’s prowess to perform media I/O (and other) operations in Python. It offers two +basic modes of operation: block read/write and stream read/write. For the read operations, +it can output data either in a Numpy array or in a plain bytes. The Numpy mode is +enabled by default if Numpy is available in the system. Another feature of +ffmpegio is to report the properties of the media files, using FFprobe.

+
+
+

Media Probe

+

To process a media file, you first need to know what’s in it. Within FFmpeg +ecosystem, this task is handled by ffprobe. +ffmpegio’s ffmpegio:probe module wraps ffprobe with 5 +basic functions:

+
>>> import ffmpegio
+>>> from pprint import pprint
+
+>>> url = 'mytestvideo.mpg'
+>>> format_info = ffmpegio.probe.format_basic(url)
+>>> pprint(format_info)
+{'duration': 66.403256,
+'filename': 'mytestvideo.mpg',
+'format_name': 'mpegts',
+'nb_streams': 2,
+'start_time': 0.0}
+
+>>> stream_info = ffmpegio.probe.streams_basic(url)
+>>> pprint(stream_info)
+[{'codec_name': 'mp2', 'codec_type': 'audio', 'index': 0},
+{'codec_name': 'h264', 'codec_type': 'video', 'index': 1}]
+
+>>> vst_info = ffmpegio.probe.video_streams_basic(url)
+>>> pprint(vst_info)
+[{'codec_name': 'h264',
+'display_aspect_ratio': Fraction(22, 15),
+'duration': 66.39972222222222,
+'frame_rate': Fraction(15000, 1001),
+'height': 240,
+'index': 1,
+'pix_fmt': 'yuv420p',
+'sample_aspect_ratio': Fraction(1, 1),
+'start_time': 0.0,
+'width': 352}]
+
+>>> ast_info = ffmpegio.probe.audio_streams_basic(url)
+>>> pprint(ast_info)
+[{'channel_layout': 'stereo',
+'channels': 2,
+'codec_name': 'mp2',
+'duration': 66.40325555555556,
+'index': 0,
+'nb_samples': 2928384,
+'sample_fmt': 'fltp',
+'sample_rate': 44100,
+'start_time': 0.0}]
+
+
+

To obtain the complete ffprobe output, use ffmpegio.probe.full_details(), +and to obtain specific format or stream fields, use ffmpegio.probe.query(). +For more information on probe, see Media Probe Function References.

+
+
+

Block Read/Write

+

Suppose you need to analyze short audio data in mytestfile.mp3, you can +read all its samples by

+
>>> fs, x = ffmpegio.audio.read('mytestfile.wav')
+
+
+

It returns the sampling rate fs and numpy.ndarray x. +The audio data is always represetned by a 2-D array, each of which column represents +an audio channel. So, a 2-second stereo recording at 8000 samples/second yields +x.shape to be (16000,2). Also, the sample format is preserved: If +the samples in the wav file is 16-bit, x is of numpy.int16 dtype.

+

Now, you’ve processed this audio data and produced the 8000-sample 1-D array y +at reduced sampling rate at 4000-samples/second. You want to save this new audio +data as FLAC file. To do so, you run:

+
>>> ffmpegio.audio.write('myoutput.flac', 4000, y)
+
+
+

There are video counterparts to these two functions:

+
>>> fs, F = ffmpegio.video.read('mytestvideo.mp4')
+>>> ffmpegio.video.write('myoutput.avi', fs, F)
+
+
+

Let’s suppose mytestvideo.mp4 is 10 seconds long, containing a +yuv420p-encoded color video stream with the frame size of 640x480 pixels, +and the frame rate of 29.97 (30000/1001) frames/second. Then, the video.read() +returns a 2-element tuple: the first element fs is the frame rate in +fractions.Fraction and the second element F contains all the frames +of the video in numpy.ndarray with shape (299, 480, 640, 3). +Because the video is in color, each pixel is represented in 24-bit RGB, thus +F.dtype is numpy.uint8. The video write is the reciprocal of +the read operation.

+

For image (or single video frame) I/O, there is a pair of functions as well:

+
>>> I = ffmpegio.image.read('myimage.png')
+>>> ffmpegio.image.write('myoutput.bmp', I)
+
+
+

The image data I is like the video frame data, but without the leading +dimension.

+
+
+

Stream Read/Write

+

Block read/write is simple and convenient for a short file, but it quickly +becomes slow and inefficient as the data size grows; this is especially true +for video. To enable on-demand data retrieval, ffmpegio offers stream +read/write operation. It mimics the familiar Python’s file I/O with +ffmpegio.open():

+
>>> with ffmpegio.open('mytestvideo.mp4', 'rv') as f: # opens the first video stream
+>>>     print(f.rate) # frame rate fraction in frames/second
+>>>     F = f.read() # read the first frame
+>>>     F = f.read(5) # read the next 5 frames at once
+
+
+

Another example, which uses read and write streams simultaneously:

+
>>> with ffmpegio.open('mytestvideo.mp4', 'rv', blocksize=100) as f,
+>>>      ffmpegio.open('myoutput.avi', 'wv', f.rate) as g:
+>>>         for frames in f: # iterates over all frames, 100 frames at a time
+>>>             output = my_processor(frames) # function to process data
+>>>             g.write(output) # send the processed frames to 'myoutput.avi'
+
+
+

By default, ffmpegio.open() opens the first media stream available to read. +However, the operation mode can be specified via the mode second argument. +The above example, opens mytestvideo.mp4 file in 'rv' or “read +video” mode and myoutput.avi in 'wv' or “write video” mode. The +file reader object f is an Iterable object, which returns the next set of +frames (the number set by the blocksize argument). For more, +see ffmpegio.open().

+
+
+

Specify Read Time Range

+

For both block and stream read operations, you can specify the time range to read +data from. There are four options available:

+ + + + + + + + + + + + + + + + + + +
Read Timing Options

Name

Description

ss

Start time in seconds

t

Duration in seconds

to

End time in seconds (ignored if t_in is also specified)

+

Note it is also possible to specify these timing options for the input (i.e., using the +options ss_in, t_in, and to_in). The input options, especially +ss_in, may run faster but potentially less accurate. See FFmpeg documentation for the explanation.

+
>>> url = 'myvideo.mp4'
+
+>>> #read only the first 1 seconds
+>>> fs, F = ffmpegio.video.read(url, t=1.0)
+
+>>> #read from 1.2 second mark to 2.5 second mark
+>>> fs, F = ffmpegio.video.read(url, t=1.2, to=2.5)
+
+
+

To specify by the frame numbers for video and sample numbers for audio, user must +convert the units to seconds using probe(). For example:

+
>>> # get frame rate of the (first) video stream
+>>> info = ffmpegio.probe.video_streams_basic('myvideo.mp4')
+>>> fs = info[0]['frame_rate']
+
+>>> #read 30 frame from the 11th frame (remember Python uses 0-based index)
+>>> with ffmpegio.open('myvideo.mp4', 'rv', t=10/fs, t=30/fs) as f:
+>>>     frame = f.read()
+>>>     # do your thing with the frame data
+
+
+

Likewise, for an audio input stream:

+
>>> # get sampling rate of the (first) audio stream
+>>> info = ffmpegio.probe.audio_streams_basic('myaudio.wav')
+>>> fs = info[0]['sample_rate']
+
+>>> #read first 10000 audio samples
+>>> fs, x = ffmpegio.audio.read('myaudio.wav', t=10000/fs)
+
+
+
+
+

Specify Output Frame/Sample Size

+

FFmpeg let you change video size or the number of audio channels via output +options s and ac, respectively, without setting up a +filtergraph. For example,

+
>>> # auto-scale video frame
+>>> fs, F = ffmpegio.video.read('myvideo.mp4', t=1.0) # natively 320x240
+>>> F.shape
+(30, 240, 320, 3)
+
+>>> # halve the size
+>>> width = 160
+>>> height = 120
+>>> _, G = ffmpegio.video.read('myvideo.mp4', t=1.0, s=(width,height))
+>>> G.shape
+(29, 120, 160, 3)
+
+>>> # auto-convert to mono
+>>> fs, x = ffmpegio.audio.read('myaudio.wav') # natively stereo
+>>> _, y = ffmpegio.audio.read('myaudio.wav', ac=1) # to mono
+>>> x.shape
+(44100, 2)
+>>> y.shape
+(44100, 1)
+
+
+

To customize the conversion configuration, use vf output option +with with scale filter or af output option with +channelmap or pan or other channel mixing filter

+
+
+

Specify Sample Formats

+

FFmpeg can also convert the formats of video pixels and sound samples on the fly. +This feature is enabled in ffmpegio via output options pix_fmt +for video and sample_fmt for audio.

+
+
+ + + + + + + + + + + + + + + + + + + + +
Video pix_fmt Option Values

pix_fmt

Description

gray

grayscale

ya8

grayscale with transparent alpha channel

rgb24

RGB

rgba

RGB with alpha transparent alpha channel

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Audio sample_fmt Option Values

sample_fmt

Description

min

max

u8

unsigned 8-bit integer

0

255

s16

signed 16-bit integer

-32768

32767

s32

signed 32-bit integer

-2147483648

2147483647

flt

single-precision floating point

-1.0

1.0

dbl

double-precision floating point

-1.0

1.0

+
+

For example,

+
>>> # auto-convert video frames to grayscale
+>>> fs, RGB = ffmpegio.video.read('myvideo.mp4', t=1.0) # natively rgb24
+>>> _, GRAY = ffmpegio.video.read('myvideo.mp4', t=1.0, pix_fmt='gray')
+>>> RGB.shape
+(29, 640, 480, 3)
+>>> GRAY.shape
+(29, 640, 480, 1)
+
+>>> # auto-convert PNG image to remove transparency with white background
+>>> RGBA = ffmpegio.image.read('myimage.png') # natively rgba with transparency
+.. >>> RGB = ffmpegio.image.read('myimage.png', pix_fmt='rgb24', fill_color='white')
+>>> RGB.shape
+(100, 396, 4)
+>>> RGB.shape
+(100, 396, 3)
+
+>>> # auto-convert to audio samples to double precision
+>>> fs, x = ffmpegio.audio.read('myaudio.wav') # natively s16
+>>> _, y = ffmpegio.audio.read('myaudio.wav', sample_fmt='dbl')
+>>> x.max()
+2324
+>>> y.max()
+0.0709228515625
+
+
+

Note when converting from an image with alpha channel (FFmpeg does not support +alpha channel in video input) the background color may be specified with +fill_color option (which defaults to 'white'). +See the FFmpeg color specification +for the list of predefined color names.

+ + + + + + + + + + + + + + + + +
Examples of changing image format

'rgba' (original)

+_images/quick-1.png +
+
ffmpegio.image.read('ffmpeg-logo.png')
+
+
+

'rgb24' with ‘Linen’ background

+_images/quick-2.png +
+
ffmpegio.image.read('ffmpeg-logo.png', pix_fmt='rgb24', fill_color='linen')
+
+
+

'ya8'

+_images/quick-3.png +
+
ffmpegio.image.read('ffmpeg-logo.png', pix_fmt='ya8')
+
+
+

'gray' with light gray background

+_images/quick-4.png +
+
ffmpegio.image.read('ffmpeg-logo.png', pix_fmt='gray',
+    fill_color='#F0F0F0')
+
+
+
+
+
+

Progress Callback

+

FFmpeg has -progress option, which sends program-friendly progress +information to url. ffmpegio takes advantage of this option to +let user monitor the transcoding progress with a callback, which could be +set with progress argument of all media operations. The callback +function must have the following signature:

+
progress_callback(status:dict, done:bool) -> None|bool
+
+
+

The status dict containing the information similar to what FFmpeg +displays on console. The second argument done is only True +on the last progress call. Here is an example of status dict:

+
{'bitrate': '61.9kbits/s',
+'drop_frames': 0,
+'dup_frames': 0,
+'fps': 336.18,
+'frame': 1014,
+'out_time': '00:00:33.877914',
+'out_time_ms': 33877914,
+'out_time_us': 33877914,
+'speed': '11.2x',
+'stream_0_0_q': 29.0,
+'total_size': 262192}
+
+
+

While FFmpeg does not report percent progress, it is possible to compute it from +frame or out_time if you know the total number of output frames +or the output duration, respectively.

+

If an FFmpeg media stream object is invoked by ffmpegio.open() +with progress callback argument, the callback function can terminate +the FFmpeg execution by returning True. This feature is useful for GUI +programming.

+
+
+ + +
+
+
+ +
+ +
+

© Copyright 2021-2022, Takeshi (Kesh) Ikuma, Louisiana State University Health Sciences Center.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/rawdata_numpy.html b/docs/rawdata_numpy.html new file mode 100644 index 00000000..0c42e696 --- /dev/null +++ b/docs/rawdata_numpy.html @@ -0,0 +1,377 @@ + + + + + + + + + ffmpegio: Media I/O with FFmpeg in Python (with NumPy Array Plugin) — python-ffmpegio 0.11.0 documentation + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + +
  • + View page source +
  • +
+
+
+
+
+ +
+

ffmpegio: Media I/O with FFmpeg in Python (with NumPy Array Plugin)

+

PyPI PyPI - Status PyPI - Python Version GitHub License GitHub Workflow Status

+

Python ffmpegio package aims to bring the full capability of FFmpeg +to read, write, probe, and manipulate multimedia data to Python. FFmpeg is an open-source cross-platform +multimedia framework, which can handle most of the multimedia formats available today.

+
+

Note

+

Since v0.3.0, ffmpegio Python distribution package has been split into ffmpegio-core and ffmpegio to allow +Numpy-independent installation.

+
+

Install the full ffmpegio package via pip:

+
pip install ffmpegio
+
+
+

If numpy.ndarray data I/O is not needed, instead use

+
pip install ffmpegio-core
+
+
+
+

Main Features

+
    +
  • Pure-Python light-weight package interacting with FFmpeg executable found in +the system

  • +
  • Transcode a media file to another in Python

  • +
  • Read, write, filter, and create functions for audio, image, and video data

  • +
  • Context-managing ffmpegio.open to perform stream read/write operations of video and audio

  • +
  • Automatically detect and convert audio & video formats to and from numpy.ndarray properties

  • +
  • Probe media file information

  • +
  • Accepts all FFmpeg options including filter graphs

  • +
  • Supports a user callback whenever FFmpeg updates its progress information file +(see -progress FFmpeg option)

  • +
  • ffconcat scripter to make the use of -f concat demuxer easier

  • +
  • I/O device enumeration to eliminate the need to look up device names. (currently supports only: Windows DirectShow)

  • +
  • More features to follow

  • +
+
+
+

Documentation

+

Visit our GitHub page here

+
+
+

Examples

+

To import ffmpegio

+
>>> import ffmpegio
+
+
+ +
+

Transcoding

+
>>> # transcode, overwrite output file if exists, showing the FFmpeg log
+>>> ffmpegio.transcode('input.avi', 'output.mp4', overwrite=True, show_log=True)
+
+>>> # 1-pass H.264 transcoding
+>>> ffmpegio.transcode('input.avi', 'output.mkv', vcodec='libx264', show_log=True,
+>>>                    preset='slow', crf=22, acodec='copy')
+
+>>> # 2-pass H.264 transcoding
+>>> ffmpegio.transcode('input.avi', 'output.mkv', two_pass=True, show_log=True,
+>>>                    **{'c:v':'libx264', 'b:v':'2600k', 'c:a':'aac', 'b:a':'128k'})
+
+>>> # concatenate videos using concat demuxer
+>>> files = ['/video/video1.mkv','/video/video2.mkv']
+>>> ffconcat = ffmpegio.FFConcat()
+>>> ffconcat.add_files(files)
+>>> with ffconcat: # generates temporary ffconcat file
+>>>     ffmpegio.transcode(ffconcat, 'output.mkv', f_in='concat', codec='copy', safe_in=0)
+
+
+
+
+

Read Audio Files

+
>>> # read audio samples in its native sample format and return all channels
+>>> fs, x = ffmpegio.audio.read('myaudio.wav')
+>>> # fs: sampling rate in samples/second, x: [nsamples x nchannels] numpy array
+
+>>> # read audio samples from 24.15 seconds to 63.2 seconds, pre-convert to mono in float data type
+>>> fs, x = ffmpegio.audio.read('myaudio.flac', ss=24.15, to=63.2, sample_fmt='dbl', ac=1)
+
+>>> # read filtered audio samples first 10 seconds
+>>> #   filter: equalizer which attenuate 10 dB at 1 kHz with a bandwidth of 200 Hz
+>>> fs, x = ffmpegio.audio.read('myaudio.mp3', t=10.0, af='equalizer=f=1000:t=h:width=200:g=-10')
+
+
+
+
+

Read Image Files / Capture Video Frames

+
>>> # list supported image extensions
+>>> ffmpegio.caps.muxer_info('image2')['extensions']
+['bmp', 'dpx', 'exr', 'jls', 'jpeg', 'jpg', 'ljpg', 'pam', 'pbm', 'pcx', 'pfm', 'pgm', 'pgmyuv',
+ 'png', 'ppm', 'sgi', 'tga', 'tif', 'tiff', 'jp2', 'j2c', 'j2k', 'xwd', 'sun', 'ras', 'rs', 'im1',
+ 'im8', 'im24', 'sunras', 'xbm', 'xface', 'pix', 'y']
+
+>>> # read BMP image with auto-detected pixel format (rgb24, gray, rgba, or ya8)
+>>> I = ffmpegio.image.read('myimage.bmp') # I: [height x width x ncomp] numpy array
+
+>>> # read JPEG image, then convert to grayscale and proportionally scale so the width is 480 pixels
+>>> I = ffmpegio.image.read('myimage.jpg', pix_fmt='grayscale', s='480x-1')
+
+>>> # read PNG image with transparency, convert it to plain RGB by filling transparent pixels orange
+>>> I = ffmpegio.image.read('myimage.png', pix_fmt='rgb24', fill_color='orange')
+
+>>> # capture video frame at timestamp=4:25.3 and convert non-square pixels to square
+>>> I = ffmpegio.image.read('myvideo.mpg', ss='4:25.3', square_pixels='upscale')
+
+>>> # capture 5 video frames and tile them on 3x2 grid with 7px between them, and 2px of initial margin
+>>> I = ffmpegio.image.read('myvideo.mp4', vf='tile=3x2:nb_frames=5:padding=7:margin=2')
+
+>>> # create spectrogram of the audio input (must specify pix_fmt if input is audio)
+>>> I = ffmpegio.image.read('myaudio.mp3', filter_complex='showspectrumpic=s=960x540', pix_fmt='rgb24')
+
+
+
+
+

Read Video Files

+
>>> # read 50 video frames at t=00:32:40 then convert to grayscale
+>>> fs, F = ffmpegio.video.read('myvideo.mp4', ss='00:32:40', vframes=50, pix_fmt='gray')
+>>> #  fs: frame rate in frames/second, F: [nframes x height x width x ncomp] numpy array
+
+>>> # get running spectrogram of audio input (must specify pix_fmt if input is audio)
+>>> fs, F = ffmpegio.video.read('myvideo.mp4', pix_fmt='rgb24', filter_complex='showspectrum=s=1280x480')
+
+
+
+
+

Read Multiple Files or Streams

+
>>> # read both video and audio streams (1 ea)
+>>> rates, data = ffmpegio.media.read('mymedia.mp4')
+>>> #  rates: dict of frame rate and sampling rate: keys="v:0" and "a:0"
+>>> #  data: dict of video frame array and audio sample array: keys="v:0" and "a:0"
+
+>>> # combine video and audio files
+>>> rates, data = ffmpegio.media.read('myvideo.mp4','myaudio.mp3')
+
+>>> # get output of complex filtergraph (can take multiple inputs)
+>>> expr = "[v:0]split=2[out0][l1];[l1]edgedetect[out1]"
+>>> rates, data = ffmpegio.media.read('myvideo.mp4',filter_complex=expr,map=['[out0]','[out1]'])
+>>> #  rates: dict of frame rates: keys="v:0" and "v:1"
+>>> #  data: dict of video frame arrays: keys="v:0" and "v:1"
+
+
+
+
+

Write Audio, Image, & Video Files

+
>>> # create a video file from a numpy array
+>>> ffmpegio.video.write('myvideo.mp4', rate, F)
+
+>>> # create an image file from a numpy array
+>>> ffmpegio.image.write('myimage.png', F)
+
+>>> # create an audio file from a numpy array
+>>> ffmpegio.audio.write('myaudio.mp3', rate, x)
+
+
+
+
+

Filter Audio, Image, & Video Data

+
>>> # Add fade-in and fade-out effects to audio data
+>>> fs_out, y = ffmpegio.audio.filter('afade=t=in:ss=0:d=15,afade=t=out:st=875:d=25', fs_in, x)
+
+>>> # Apply mirror effect to an image
+>>> I_out = ffmpegio.image.filter('crop=iw/2:ih:0:0,split[left][tmp];[tmp]hflip[right];[left][right] hstack', I_in)
+
+>>> # Add text at the center of the video frame
+>>> filter = "drawtext=fontsize=30:fontfile=FreeSerif.ttf:text='hello world':x=(w-text_w)/2:y=(h-text_h)/2"
+>>> fs_out, F_out = ffmpegio.video.filter(filter, fs_in, F_in)
+
+
+
+
+

Stream I/O

+
>>> # process video 100 frames at a time and save output as a new video
+>>> # with the same frame rate
+>>> with ffmpegio.open('myvideo.mp4', 'rv', blocksize=100) as fin,
+>>>      ffmpegio.open('myoutput.mp4', 'wv', rate=fin.frame_rate) as fout:
+>>>     for frames in fin:
+>>>         fout.write(myprocess(frames))
+
+
+
+
+

Device I/O Enumeration

+
>>> # record 5 minutes of audio from Windows microphone
+>>> fs, x = ffmpegio.audio.read('a:0', f_in='dshow', sample_fmt='dbl', t=300)
+
+>>> # capture Windows' webcam frame
+>>> with ffmpegio.open('v:0', 'rv', f_in='dshow') as webcam,
+>>>     for frame in webcam:
+>>>         process_frame(frame)
+
+
+
+
+

Progress Callback

+
>>> import pprint
+
+>>> # progress callback
+>>> def progress(info, done):
+>>>     pprint(info) # bunch of stats
+>>>     if done:
+>>>        print('video decoding completed')
+>>>     else:
+>>>        return check_cancel_command(): # return True to kill immediately
+
+>>> # can be used in any butch processing
+>>> rate, F = ffmpegio.video.read('myvideo.mp4', progress=progress)
+
+>>> # as well as for stream processing
+>>> with ffmpegio.open('myvideo.mp4', 'rv', blocksize=100, progress=progress) as fin:
+>>>     for frames in fin:
+>>>         myprocess(frames)
+
+
+
+
+

Run FFmpeg and FFprobe Directly

+
>>> from ffmpegio import ffmpeg, FFprobe, ffmpegprocess
+>>> from subprocess import PIPE
+
+>>> # call with options as a long string
+>>> ffmpeg('-i input.avi -b:v 64k -bufsize 64k output.avi')
+
+>>> # or call with list of options
+>>> ffmpeg(['-i', 'input.avi' ,'-r', '24', 'output.avi'])
+
+>>> # the same for ffprobe
+>>> ffprobe('ffprobe -show_streams -select_streams a INPUT')
+
+>>> # specify subprocess arguments to capture stdout
+>>> out = ffprobe('ffprobe -of json -show_frames INPUT',
+                  stdout=PIPE, universal_newlines=True).stdout
+
+>>> # use ffmpegprocess to take advantage of ffmpegio's default behaviors
+>>> out = ffmpegprocess.run({"inputs": [("input.avi", None)],
+                             "outputs": [("out1.mp4", None),
+                                         ("-", {"f": "rawvideo", "vframes": 1, "pix_fmt": "gray", "an": None})
+                            }, capture_log=True)
+>>> print(out.stderr) # print the captured FFmpeg logs (banner text omitted)
+ >>> b = out.stdout # width*height bytes of the first frame
+
+
+
+
+
+ + +
+
+
+ +
+ +
+

© Copyright 2021-2022, Takeshi (Kesh) Ikuma, Louisiana State University Health Sciences Center.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/search.html b/docs/search.html new file mode 100644 index 00000000..6369773f --- /dev/null +++ b/docs/search.html @@ -0,0 +1,119 @@ + + + + + + + + Search — python-ffmpegio 0.11.0 documentation + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + +
  • +
  • +
+
+
+
+
+ + + + +
+ +
+ +
+
+
+ +
+ +
+

© Copyright 2021-2022, Takeshi (Kesh) Ikuma, Louisiana State University Health Sciences Center.

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/docs/searchindex.js b/docs/searchindex.js new file mode 100644 index 00000000..b0733d04 --- /dev/null +++ b/docs/searchindex.js @@ -0,0 +1 @@ +Search.setIndex({"alltitles": {"* n: filtergraph self-stacking": [[7, "n-filtergraph-self-stacking"]], "+: filtergraph joining": [[7, "filtergraph-joining"]], "::code::ffmpeg-downloader": [[10, "code-ffmpeg-downloader"]], "::code::static-ffmpeg": [[10, "code-static-ffmpeg"]], ">> filtergraph labeling / filtergraph p2p linking": [[7, "filtergraph-labeling-filtergraph-p2p-linking"]], "Accessing filter information on FFmpeg": [[7, "accessing-filter-information-on-ffmpeg"]], "Analyze API Reference": [[2, "analyze-api-reference"]], "Argument Type References": [[14, "argument-type-references"]], "Audio Sample Formats sample_fmt": [[13, "audio-sample-formats-sample-fmt"]], "Audio sample_fmt Option Values": [[15, "id3"]], "Available filter loggers": [[2, "available-filter-loggers"]], "Basic Functions": [[3, "basic-functions"]], "Basic I/O Function References": [[3, null]], "Block Read/Write": [[15, "block-read-write"]], "Built-in Video Manipulation Options": [[13, "built-in-video-manipulation-options"]], "Common FFmpeg Options": [[13, "common-ffmpeg-options"]], "Concat with preprocessing stage": [[7, "concat-with-preprocessing-stage"]], "Constructing filtergraphs": [[7, "constructing-filtergraphs"]], "Creating Videos from Matplotlib figure": [[12, null]], "Currently Supported Devices": [[6, "currently-supported-devices"]], "Device I/O Enumeration": [[9, "device-i-o-enumeration"], [16, "device-i-o-enumeration"]], "Documentation": [[9, "documentation"], [16, "documentation"]], "Example": [[12, "example"]], "Examples": [[0, "examples"], [7, "examples"], [9, "examples"], [16, "examples"]], "Examples of changing image format": [[15, "id4"]], "Examples of manipulated images": [[13, "id3"]], "External Links": [[11, null]], "FFConcat Class: Concatenating Media Files": [[5, null]], "FFmpeg Capabilities References": [[4, null]], "FFmpeg FilterGraph Class Specification": [[0, "ffmpeg-filtergraph-class-specification"]], "FFmpeg Option References": [[13, null]], "FFmpeg aphasemeter filter options": [[2, "ffmpeg-aphasemeter-filter-options"]], "FFmpeg aspectralstats filter options": [[2, "ffmpeg-aspectralstats-filter-options"]], "FFmpeg astats filter options": [[2, "ffmpeg-astats-filter-options"]], "FFmpeg bbox filter options": [[2, "ffmpeg-bbox-filter-options"]], "FFmpeg blackdetect filter options": [[2, "ffmpeg-blackdetect-filter-options"]], "FFmpeg blackframe filter options": [[2, "ffmpeg-blackframe-filter-options"]], "FFmpeg blurdetect filter options": [[2, "ffmpeg-blurdetect-filter-options"]], "FFmpeg freezedetect filter options": [[2, "ffmpeg-freezedetect-filter-options"]], "FFmpeg scdet filter options": [[2, "ffmpeg-scdet-filter-options"]], "FFmpeg silencedetect filter options": [[2, "ffmpeg-silencedetect-filter-options"]], "Features": [[15, "features"]], "Filter Audio, Image, & Video Data": [[9, "filter-audio-image-video-data"], [16, "filter-audio-image-video-data"]], "Filter pad indexing": [[7, "filter-pad-indexing"]], "Filter pad labeling": [[7, "filter-pad-labeling"]], "Filtergraph API Reference": [[7, "filtergraph-api-reference"]], "Filtergraph Builder": [[9, "filtergraph-builder"]], "Filtergraph Builder Reference": [[7, null]], "Filtergraph linking": [[7, "filtergraph-linking"]], "Function References": [[4, "function-references"], [14, "function-references"]], "Generating filtergraph script for extremely long filtergraph": [[7, "generating-filtergraph-script-for-extremely-long-filtergraph"]], "Graph.link() - within-filtergraph linking": [[7, "graph-link-within-filtergraph-linking"]], "Hardware I/O Device Enumeration": [[6, null]], "How to Use": [[6, "how-to-use"]], "Install": [[15, "install"]], "Install FFmpeg program": [[10, "install-ffmpeg-program"]], "Installation": [[9, "installation"], [10, null]], "List of Constants": [[4, "list-of-constants"]], "List of Functions": [[4, "list-of-functions"], [14, "list-of-functions"]], "Main Features": [[9, "main-features"], [16, "main-features"]], "Measurement parameters": [[2, "measurement-parameters"]], "Media Probe": [[15, "media-probe"]], "Media Probe Function References": [[14, null]], "Options to manipulate video frames": [[13, "id1"]], "Progress Callback": [[9, "progress-callback"], [15, "progress-callback"], [16, "progress-callback"]], "Quick Start Guide": [[15, null]], "Read Audio Files": [[9, "read-audio-files"], [16, "read-audio-files"]], "Read Image Files / Capture Video Frames": [[9, "read-image-files-capture-video-frames"], [16, "read-image-files-capture-video-frames"]], "Read Multiple Files or Streams": [[9, "read-multiple-files-or-streams"], [16, "read-multiple-files-or-streams"]], "Read Timing Options": [[15, "id1"]], "Read Video Files": [[9, "read-video-files"], [16, "read-video-files"]], "References": [[6, "references"]], "Remarks": [[7, "remarks"], [7, "id1"], [7, "id2"]], "Run FFmpeg and FFprobe Directly": [[9, "run-ffmpeg-and-ffprobe-directly"], [16, "run-ffmpeg-and-ffprobe-directly"]], "Simple example": [[7, "simple-example"]], "Simple examples": [[2, "simple-examples"]], "Specification of FFmpeg Argument dict ffmpeg_args": [[0, null]], "Specify Output Frame/Sample Size": [[15, "specify-output-frame-sample-size"]], "Specify Read Time Range": [[15, "specify-read-time-range"]], "Specify Sample Formats": [[15, "specify-sample-formats"]], "Stream I/O": [[9, "stream-i-o"], [16, "stream-i-o"]], "Stream Read/Write": [[15, "stream-read-write"]], "Supported win_func option values": [[2, "supported-win-func-option-values"]], "Todo": [[4, "id1"], [4, "id2"]], "Transcoding": [[9, "transcoding"], [16, "transcoding"]], "Use": [[8, "use"]], "Video Pixel Formats pix_fmt": [[13, "video-pixel-formats-pix-fmt"]], "Video pix_fmt Option Values": [[15, "id2"]], "Write Audio, Image, & Video Files": [[9, "write-audio-image-video-files"], [16, "write-audio-image-video-files"]], "ffmpegio-core: Media I/O with FFmpeg in Python": [[9, null]], "ffmpegio-plugin-downloader: An ffmpegio plugin to download latest FFmpeg release binaries": [[8, null]], "ffmpegio.analyze: Frame Metadata Analysis Module": [[2, null]], "ffmpegio.ffmpegprocess Module Reference": [[1, "ffmpegio-ffmpegprocess-module-reference"]], "ffmpegio.ffmpegprocess: Direct invocation of FFmpeg subprocess": [[1, null]], "ffmpegio: Media I/O with FFmpeg in Python (with NumPy Array Plugin)": [[16, null]], "map output options": [[13, "map-output-options"]], "s output option": [[13, "s-output-option"]], "|: filtegraph stacking": [[7, "filtegraph-stacking"]]}, "docnames": ["adv-args", "adv-ffmpeg", "analysis", "basicio", "caps", "concat", "devices", "filtergraph", "finder_ffdl", "index", "install", "links", "mpl-writer", "options", "probe", "quick", "rawdata_numpy"], "envversion": {"sphinx": 64, "sphinx.domains.c": 3, "sphinx.domains.changeset": 1, "sphinx.domains.citation": 1, "sphinx.domains.cpp": 9, "sphinx.domains.index": 1, "sphinx.domains.javascript": 3, "sphinx.domains.math": 2, "sphinx.domains.python": 4, "sphinx.domains.rst": 2, "sphinx.domains.std": 2, "sphinx.ext.intersphinx": 1, "sphinx.ext.todo": 2}, "filenames": ["adv-args.rst", "adv-ffmpeg.rst", "analysis.rst", "basicio.rst", "caps.rst", "concat.rst", "devices.rst", "filtergraph.rst", "finder_ffdl.rst", "index.rst", "install.rst", "links.rst", "mpl-writer.rst", "options.rst", "probe.rst", "quick.rst", "rawdata_numpy.rst"], "indexentries": {"add_chapter() (ffmpegio.ffconcat method)": [[5, "ffmpegio.FFConcat.add_chapter", false]], "add_file() (ffmpegio.ffconcat method)": [[5, "ffmpegio.FFConcat.add_file", false]], "add_files() (ffmpegio.ffconcat method)": [[5, "ffmpegio.FFConcat.add_files", false]], "add_glob() (ffmpegio.ffconcat method)": [[5, "ffmpegio.FFConcat.add_glob", false]], "add_label() (ffmpegio.filtergraph.chain method)": [[7, "ffmpegio.filtergraph.Chain.add_label", false]], "add_label() (ffmpegio.filtergraph.filter method)": [[7, "ffmpegio.filtergraph.Filter.add_label", false]], "add_label() (ffmpegio.filtergraph.graph method)": [[7, "ffmpegio.filtergraph.Graph.add_label", false]], "add_note() (ffmpegio.filtergraph.chain.error method)": [[7, "ffmpegio.filtergraph.Chain.Error.add_note", false]], "add_note() (ffmpegio.filtergraph.filter.error method)": [[7, "ffmpegio.filtergraph.Filter.Error.add_note", false]], "add_note() (ffmpegio.filtergraph.filter.invalidname method)": [[7, "ffmpegio.filtergraph.Filter.InvalidName.add_note", false]], "add_note() (ffmpegio.filtergraph.filter.invalidoption method)": [[7, "ffmpegio.filtergraph.Filter.InvalidOption.add_note", false]], "add_note() (ffmpegio.filtergraph.filter.unsupported method)": [[7, "ffmpegio.filtergraph.Filter.Unsupported.add_note", false]], "add_note() (ffmpegio.filtergraph.graph.error method)": [[7, "ffmpegio.filtergraph.Graph.Error.add_note", false]], "add_note() (ffmpegio.filtergraph.graph.filterpadmediatypemismatch method)": [[7, "ffmpegio.filtergraph.Graph.FilterPadMediaTypeMismatch.add_note", false]], "add_note() (ffmpegio.filtergraph.graph.invalidfilterpadid method)": [[7, "ffmpegio.filtergraph.Graph.InvalidFilterPadId.add_note", false]], "add_stream() (ffmpegio.ffconcat method)": [[5, "ffmpegio.FFConcat.add_stream", false]], "aphasemeter (class in ffmpegio.analyze)": [[2, "ffmpegio.analyze.APhaseMeter", false]], "aphasemeter.phase (class in ffmpegio.analyze)": [[2, "ffmpegio.analyze.APhaseMeter.Phase", false]], "append() (ffmpegio.filtergraph.chain method)": [[7, "ffmpegio.filtergraph.Chain.append", false]], "append() (ffmpegio.filtergraph.graph method)": [[7, "ffmpegio.filtergraph.Graph.append", false]], "apply() (ffmpegio.filtergraph.filter method)": [[7, "ffmpegio.filtergraph.Filter.apply", false]], "are_linked() (ffmpegio.filtergraph.graph method)": [[7, "ffmpegio.filtergraph.Graph.are_linked", false]], "as_filter() (ffmpegio.ffconcat method)": [[5, "ffmpegio.FFConcat.as_filter", false]], "as_filter() (in module ffmpegio.filtergraph)": [[7, "ffmpegio.filtergraph.as_filter", false]], "as_filterchain() (in module ffmpegio.filtergraph)": [[7, "ffmpegio.filtergraph.as_filterchain", false]], "as_filtergraph() (in module ffmpegio.filtergraph)": [[7, "ffmpegio.filtergraph.as_filtergraph", false]], "as_filtergraph_object() (in module ffmpegio.filtergraph)": [[7, "ffmpegio.filtergraph.as_filtergraph_object", false]], "as_script_file() (ffmpegio.filtergraph.graph method)": [[7, "ffmpegio.filtergraph.Graph.as_script_file", false]], "aspectralstats (class in ffmpegio.analyze)": [[2, "ffmpegio.analyze.ASpectralStats", false]], "astats (class in ffmpegio.analyze)": [[2, "ffmpegio.analyze.AStats", false]], "attach() (ffmpegio.filtergraph.chain method)": [[7, "ffmpegio.filtergraph.Chain.attach", false]], "attach() (ffmpegio.filtergraph.filter method)": [[7, "ffmpegio.filtergraph.Filter.attach", false]], "attach() (ffmpegio.filtergraph.graph method)": [[7, "ffmpegio.filtergraph.Graph.attach", false]], "audio_streams_basic() (in module ffmpegio.probe)": [[14, "ffmpegio.probe.audio_streams_basic", false]], "bbox (class in ffmpegio.analyze)": [[2, "ffmpegio.analyze.BBox", false]], "bbox.bbox (class in ffmpegio.analyze)": [[2, "ffmpegio.analyze.BBox.BBox", false]], "blackdetect (class in ffmpegio.analyze)": [[2, "ffmpegio.analyze.BlackDetect", false]], "blackdetect.black (class in ffmpegio.analyze)": [[2, "ffmpegio.analyze.BlackDetect.Black", false]], "blackframe (class in ffmpegio.analyze)": [[2, "ffmpegio.analyze.BlackFrame", false]], "blackframe.blackframes (class in ffmpegio.analyze)": [[2, "ffmpegio.analyze.BlackFrame.BlackFrames", false]], "blur (ffmpegio.analyze.blurdetect.blur attribute)": [[2, "ffmpegio.analyze.BlurDetect.Blur.blur", false]], "blurdetect (class in ffmpegio.analyze)": [[2, "ffmpegio.analyze.BlurDetect", false]], "blurdetect.blur (class in ffmpegio.analyze)": [[2, "ffmpegio.analyze.BlurDetect.Blur", false]], "bsfilter_info() (in module ffmpegio.caps)": [[4, "ffmpegio.caps.bsfilter_info", false]], "bsfilters() (in module ffmpegio.caps)": [[4, "ffmpegio.caps.bsfilters", false]], "chain (class in ffmpegio.filtergraph)": [[7, "ffmpegio.filtergraph.Chain", false]], "chain.error": [[7, "ffmpegio.filtergraph.Chain.Error", false]], "changed (ffmpegio.analyze.scdet.allscenes attribute)": [[2, "ffmpegio.analyze.ScDet.AllScenes.changed", false]], "chapters (ffmpegio.ffconcat attribute)": [[5, "ffmpegio.FFConcat.chapters", false]], "clear() (ffmpegio.filtergraph.chain method)": [[7, "ffmpegio.filtergraph.Chain.clear", false]], "clear() (ffmpegio.filtergraph.graph method)": [[7, "ffmpegio.filtergraph.Graph.clear", false]], "codec (ffmpegio.ffconcat.streamitem attribute)": [[5, "ffmpegio.FFConcat.StreamItem.codec", false]], "codecs() (in module ffmpegio.caps)": [[4, "ffmpegio.caps.codecs", false]], "colors() (in module ffmpegio.caps)": [[4, "ffmpegio.caps.colors", false]], "compose() (ffmpegio.ffconcat method)": [[5, "ffmpegio.FFConcat.compose", false]], "compose() (ffmpegio.filtergraph.chain method)": [[7, "ffmpegio.filtergraph.Chain.compose", false]], "compose() (ffmpegio.filtergraph.filter method)": [[7, "ffmpegio.filtergraph.Filter.compose", false]], "compose() (ffmpegio.filtergraph.graph method)": [[7, "ffmpegio.filtergraph.Graph.compose", false]], "connect() (ffmpegio.filtergraph.chain method)": [[7, "ffmpegio.filtergraph.Chain.connect", false]], "connect() (ffmpegio.filtergraph.filter method)": [[7, "ffmpegio.filtergraph.Filter.connect", false]], "connect() (ffmpegio.filtergraph.graph method)": [[7, "ffmpegio.filtergraph.Graph.connect", false]], "count() (ffmpegio.filtergraph.chain method)": [[7, "ffmpegio.filtergraph.Chain.count", false]], "count() (ffmpegio.filtergraph.filter method)": [[7, "ffmpegio.filtergraph.Filter.count", false]], "count() (ffmpegio.filtergraph.graph method)": [[7, "ffmpegio.filtergraph.Graph.count", false]], "create() (in module ffmpegio.audio)": [[3, "ffmpegio.audio.create", false]], "create() (in module ffmpegio.image)": [[3, "ffmpegio.image.create", false]], "create() (in module ffmpegio.video)": [[3, "ffmpegio.video.create", false]], "decoder_info() (in module ffmpegio.caps)": [[4, "ffmpegio.caps.decoder_info", false]], "decoders() (in module ffmpegio.caps)": [[4, "ffmpegio.caps.decoders", false]], "demuxer_info() (in module ffmpegio.caps)": [[4, "ffmpegio.caps.demuxer_info", false]], "demuxers() (in module ffmpegio.caps)": [[4, "ffmpegio.caps.demuxers", false]], "detect() (in module ffmpegio.audio)": [[2, "ffmpegio.audio.detect", false]], "detect() (in module ffmpegio.video)": [[2, "ffmpegio.video.detect", false]], "devices() (in module ffmpegio.caps)": [[4, "ffmpegio.caps.devices", false]], "duration (ffmpegio.ffconcat.fileitem attribute)": [[5, "ffmpegio.FFConcat.FileItem.duration", false]], "encoder_info() (in module ffmpegio.caps)": [[4, "ffmpegio.caps.encoder_info", false]], "encoders() (in module ffmpegio.caps)": [[4, "ffmpegio.caps.encoders", false]], "extend() (ffmpegio.filtergraph.chain method)": [[7, "ffmpegio.filtergraph.Chain.extend", false]], "extend() (ffmpegio.filtergraph.graph method)": [[7, "ffmpegio.filtergraph.Graph.extend", false]], "extradata (ffmpegio.ffconcat.streamitem attribute)": [[5, "ffmpegio.FFConcat.StreamItem.extradata", false]], "ffconcat (class in ffmpegio)": [[5, "ffmpegio.FFConcat", false]], "ffconcat.fileitem (class in ffmpegio)": [[5, "ffmpegio.FFConcat.FileItem", false]], "ffconcat.streamitem (class in ffmpegio)": [[5, "ffmpegio.FFConcat.StreamItem", false]], "ffconcat_url (ffmpegio.ffconcat attribute)": [[5, "ffmpegio.FFConcat.ffconcat_url", false]], "ffmpeg_args (ffmpegio.ffmpegprocess.popen attribute)": [[1, "ffmpegio.ffmpegprocess.Popen.ffmpeg_args", false]], "ffmpeg_info() (in module ffmpegio)": [[3, "ffmpegio.ffmpeg_info", false]], "filter (class in ffmpegio.filtergraph)": [[7, "ffmpegio.filtergraph.Filter", false]], "filter (ffmpegio.analyze.aphasemeter property)": [[2, "ffmpegio.analyze.APhaseMeter.filter", false]], "filter (ffmpegio.analyze.astats property)": [[2, "ffmpegio.analyze.AStats.filter", false]], "filter (ffmpegio.analyze.metadatalogger property)": [[2, "ffmpegio.analyze.MetadataLogger.filter", false]], "filter() (in module ffmpegio.audio)": [[3, "ffmpegio.audio.filter", false]], "filter() (in module ffmpegio.image)": [[3, "ffmpegio.image.filter", false]], "filter() (in module ffmpegio.video)": [[3, "ffmpegio.video.filter", false]], "filter.error": [[7, "ffmpegio.filtergraph.Filter.Error", false]], "filter.invalidname": [[7, "ffmpegio.filtergraph.Filter.InvalidName", false]], "filter.invalidoption": [[7, "ffmpegio.filtergraph.Filter.InvalidOption", false]], "filter.unsupported": [[7, "ffmpegio.filtergraph.Filter.Unsupported", false]], "filter_info() (in module ffmpegio.caps)": [[4, "ffmpegio.caps.filter_info", false]], "filter_name (ffmpegio.analyze.aphasemeter attribute)": [[2, "ffmpegio.analyze.APhaseMeter.filter_name", false]], "filter_name (ffmpegio.analyze.aspectralstats attribute)": [[2, "ffmpegio.analyze.ASpectralStats.filter_name", false]], "filter_name (ffmpegio.analyze.astats attribute)": [[2, "ffmpegio.analyze.AStats.filter_name", false]], "filter_name (ffmpegio.analyze.bbox attribute)": [[2, "ffmpegio.analyze.BBox.filter_name", false]], "filter_name (ffmpegio.analyze.blackdetect attribute)": [[2, "ffmpegio.analyze.BlackDetect.filter_name", false]], "filter_name (ffmpegio.analyze.blackframe attribute)": [[2, "ffmpegio.analyze.BlackFrame.filter_name", false]], "filter_name (ffmpegio.analyze.blurdetect attribute)": [[2, "ffmpegio.analyze.BlurDetect.filter_name", false]], "filter_name (ffmpegio.analyze.freezedetect attribute)": [[2, "ffmpegio.analyze.FreezeDetect.filter_name", false]], "filter_name (ffmpegio.analyze.metadatalogger attribute)": [[2, "ffmpegio.analyze.MetadataLogger.filter_name", false]], "filter_name (ffmpegio.analyze.scdet attribute)": [[2, "ffmpegio.analyze.ScDet.filter_name", false]], "filter_name (ffmpegio.analyze.silencedetect attribute)": [[2, "ffmpegio.analyze.SilenceDetect.filter_name", false]], "filters() (in module ffmpegio.caps)": [[4, "ffmpegio.caps.filters", false]], "format_basic() (in module ffmpegio.probe)": [[14, "ffmpegio.probe.format_basic", false]], "formats() (in module ffmpegio.caps)": [[4, "ffmpegio.caps.formats", false]], "frames() (in module ffmpegio.probe)": [[14, "ffmpegio.probe.frames", false]], "freezedetect (class in ffmpegio.analyze)": [[2, "ffmpegio.analyze.FreezeDetect", false]], "freezedetect.frozen (class in ffmpegio.analyze)": [[2, "ffmpegio.analyze.FreezeDetect.Frozen", false]], "full_details() (in module ffmpegio.probe)": [[14, "ffmpegio.probe.full_details", false]], "get_input_pad() (ffmpegio.filtergraph.chain method)": [[7, "ffmpegio.filtergraph.Chain.get_input_pad", false]], "get_input_pad() (ffmpegio.filtergraph.filter method)": [[7, "ffmpegio.filtergraph.Filter.get_input_pad", false]], "get_input_pad() (ffmpegio.filtergraph.graph method)": [[7, "ffmpegio.filtergraph.Graph.get_input_pad", false]], "get_label() (ffmpegio.filtergraph.chain method)": [[7, "ffmpegio.filtergraph.Chain.get_label", false]], "get_label() (ffmpegio.filtergraph.filter method)": [[7, "ffmpegio.filtergraph.Filter.get_label", false]], "get_label() (ffmpegio.filtergraph.graph method)": [[7, "ffmpegio.filtergraph.Graph.get_label", false]], "get_num_chains() (ffmpegio.filtergraph.chain method)": [[7, "ffmpegio.filtergraph.Chain.get_num_chains", false]], "get_num_chains() (ffmpegio.filtergraph.filter method)": [[7, "ffmpegio.filtergraph.Filter.get_num_chains", false]], "get_num_chains() (ffmpegio.filtergraph.graph method)": [[7, "ffmpegio.filtergraph.Graph.get_num_chains", false]], "get_num_filters() (ffmpegio.filtergraph.chain method)": [[7, "ffmpegio.filtergraph.Chain.get_num_filters", false]], "get_num_filters() (ffmpegio.filtergraph.filter method)": [[7, "ffmpegio.filtergraph.Filter.get_num_filters", false]], "get_num_filters() (ffmpegio.filtergraph.graph method)": [[7, "ffmpegio.filtergraph.Graph.get_num_filters", false]], "get_num_inputs() (ffmpegio.filtergraph.chain method)": [[7, "ffmpegio.filtergraph.Chain.get_num_inputs", false]], "get_num_inputs() (ffmpegio.filtergraph.filter method)": [[7, "ffmpegio.filtergraph.Filter.get_num_inputs", false]], "get_num_inputs() (ffmpegio.filtergraph.graph method)": [[7, "ffmpegio.filtergraph.Graph.get_num_inputs", false]], "get_num_outputs() (ffmpegio.filtergraph.chain method)": [[7, "ffmpegio.filtergraph.Chain.get_num_outputs", false]], "get_num_outputs() (ffmpegio.filtergraph.filter method)": [[7, "ffmpegio.filtergraph.Filter.get_num_outputs", false]], "get_num_outputs() (ffmpegio.filtergraph.graph method)": [[7, "ffmpegio.filtergraph.Graph.get_num_outputs", false]], "get_num_pads() (ffmpegio.filtergraph.chain method)": [[7, "ffmpegio.filtergraph.Chain.get_num_pads", false]], "get_num_pads() (ffmpegio.filtergraph.filter method)": [[7, "ffmpegio.filtergraph.Filter.get_num_pads", false]], "get_num_pads() (ffmpegio.filtergraph.graph method)": [[7, "ffmpegio.filtergraph.Graph.get_num_pads", false]], "get_output_pad() (ffmpegio.filtergraph.chain method)": [[7, "ffmpegio.filtergraph.Chain.get_output_pad", false]], "get_output_pad() (ffmpegio.filtergraph.filter method)": [[7, "ffmpegio.filtergraph.Filter.get_output_pad", false]], "get_output_pad() (ffmpegio.filtergraph.graph method)": [[7, "ffmpegio.filtergraph.Graph.get_output_pad", false]], "get_path() (in module ffmpegio)": [[3, "ffmpegio.get_path", false]], "graph (class in ffmpegio.filtergraph)": [[7, "ffmpegio.filtergraph.Graph", false]], "graph.error": [[7, "ffmpegio.filtergraph.Graph.Error", false]], "graph.filterpadmediatypemismatch": [[7, "ffmpegio.filtergraph.Graph.FilterPadMediaTypeMismatch", false]], "graph.invalidfilterpadid": [[7, "ffmpegio.filtergraph.Graph.InvalidFilterPadId", false]], "id (ffmpegio.ffconcat.streamitem attribute)": [[5, "ffmpegio.FFConcat.StreamItem.id", false]], "index() (ffmpegio.filtergraph.chain method)": [[7, "ffmpegio.filtergraph.Chain.index", false]], "index() (ffmpegio.filtergraph.filter method)": [[7, "ffmpegio.filtergraph.Filter.index", false]], "index() (ffmpegio.filtergraph.graph method)": [[7, "ffmpegio.filtergraph.Graph.index", false]], "inpoint (ffmpegio.ffconcat.fileitem attribute)": [[5, "ffmpegio.FFConcat.FileItem.inpoint", false]], "input (ffmpegio.ffconcat property)": [[5, "ffmpegio.FFConcat.input", false]], "insert() (ffmpegio.filtergraph.chain method)": [[7, "ffmpegio.filtergraph.Chain.insert", false]], "insert() (ffmpegio.filtergraph.graph method)": [[7, "ffmpegio.filtergraph.Graph.insert", false]], "interval (ffmpegio.analyze.blackdetect.black attribute)": [[2, "ffmpegio.analyze.BlackDetect.Black.interval", false]], "interval (ffmpegio.analyze.freezedetect.frozen attribute)": [[2, "ffmpegio.analyze.FreezeDetect.Frozen.interval", false]], "interval (ffmpegio.analyze.silencedetect.silent attribute)": [[2, "ffmpegio.analyze.SilenceDetect.Silent.interval", false]], "intervalspec (in module ffmpegio.probe)": [[14, "ffmpegio.probe.IntervalSpec", false]], "is_chain_siso() (ffmpegio.filtergraph.graph method)": [[7, "ffmpegio.filtergraph.Graph.is_chain_siso", false]], "is_last_filter() (ffmpegio.filtergraph.chain method)": [[7, "ffmpegio.filtergraph.Chain.is_last_filter", false]], "is_ready() (in module ffmpegio)": [[3, "ffmpegio.is_ready", false]], "iter_chains() (ffmpegio.filtergraph.chain method)": [[7, "ffmpegio.filtergraph.Chain.iter_chains", false]], "iter_chains() (ffmpegio.filtergraph.filter method)": [[7, "ffmpegio.filtergraph.Filter.iter_chains", false]], "iter_chains() (ffmpegio.filtergraph.graph method)": [[7, "ffmpegio.filtergraph.Graph.iter_chains", false]], "iter_input_labels() (ffmpegio.filtergraph.chain method)": [[7, "ffmpegio.filtergraph.Chain.iter_input_labels", false]], "iter_input_labels() (ffmpegio.filtergraph.filter method)": [[7, "ffmpegio.filtergraph.Filter.iter_input_labels", false]], "iter_input_labels() (ffmpegio.filtergraph.graph method)": [[7, "ffmpegio.filtergraph.Graph.iter_input_labels", false]], "iter_input_pads() (ffmpegio.filtergraph.chain method)": [[7, "ffmpegio.filtergraph.Chain.iter_input_pads", false]], "iter_input_pads() (ffmpegio.filtergraph.filter method)": [[7, "ffmpegio.filtergraph.Filter.iter_input_pads", false]], "iter_input_pads() (ffmpegio.filtergraph.graph method)": [[7, "ffmpegio.filtergraph.Graph.iter_input_pads", false]], "iter_output_labels() (ffmpegio.filtergraph.chain method)": [[7, "ffmpegio.filtergraph.Chain.iter_output_labels", false]], "iter_output_labels() (ffmpegio.filtergraph.filter method)": [[7, "ffmpegio.filtergraph.Filter.iter_output_labels", false]], "iter_output_labels() (ffmpegio.filtergraph.graph method)": [[7, "ffmpegio.filtergraph.Graph.iter_output_labels", false]], "iter_output_pads() (ffmpegio.filtergraph.chain method)": [[7, "ffmpegio.filtergraph.Chain.iter_output_pads", false]], "iter_output_pads() (ffmpegio.filtergraph.filter method)": [[7, "ffmpegio.filtergraph.Filter.iter_output_pads", false]], "iter_output_pads() (ffmpegio.filtergraph.graph method)": [[7, "ffmpegio.filtergraph.Graph.iter_output_pads", false]], "join() (ffmpegio.filtergraph.chain method)": [[7, "ffmpegio.filtergraph.Chain.join", false]], "join() (ffmpegio.filtergraph.filter method)": [[7, "ffmpegio.filtergraph.Filter.join", false]], "join() (ffmpegio.filtergraph.graph method)": [[7, "ffmpegio.filtergraph.Graph.join", false]], "kill() (ffmpegio.ffmpegprocess.popen method)": [[1, "ffmpegio.ffmpegprocess.Popen.kill", false]], "last_file (ffmpegio.ffconcat property)": [[5, "ffmpegio.FFConcat.last_file", false]], "last_stream (ffmpegio.ffconcat property)": [[5, "ffmpegio.FFConcat.last_stream", false]], "layouts() (in module ffmpegio.caps)": [[4, "ffmpegio.caps.layouts", false]], "lines (ffmpegio.ffconcat.fileitem property)": [[5, "ffmpegio.FFConcat.FileItem.lines", false]], "lines (ffmpegio.ffconcat.streamitem property)": [[5, "ffmpegio.FFConcat.StreamItem.lines", false]], "link() (ffmpegio.filtergraph.graph method)": [[7, "ffmpegio.filtergraph.Graph.link", false]], "list_sink_options() (in module ffmpegio.devices)": [[6, "ffmpegio.devices.list_sink_options", false]], "list_sinks() (in module ffmpegio.devices)": [[6, "ffmpegio.devices.list_sinks", false]], "list_source_options() (in module ffmpegio.devices)": [[6, "ffmpegio.devices.list_source_options", false]], "list_sources() (in module ffmpegio.devices)": [[6, "ffmpegio.devices.list_sources", false]], "log() (ffmpegio.analyze.aphasemeter method)": [[2, "ffmpegio.analyze.APhaseMeter.log", false]], "log() (ffmpegio.analyze.aspectralstats method)": [[2, "ffmpegio.analyze.ASpectralStats.log", false]], "log() (ffmpegio.analyze.astats method)": [[2, "ffmpegio.analyze.AStats.log", false]], "log() (ffmpegio.analyze.bbox method)": [[2, "ffmpegio.analyze.BBox.log", false]], "log() (ffmpegio.analyze.blackdetect method)": [[2, "ffmpegio.analyze.BlackDetect.log", false]], "log() (ffmpegio.analyze.blackframe method)": [[2, "ffmpegio.analyze.BlackFrame.log", false]], "log() (ffmpegio.analyze.blurdetect method)": [[2, "ffmpegio.analyze.BlurDetect.log", false]], "log() (ffmpegio.analyze.freezedetect method)": [[2, "ffmpegio.analyze.FreezeDetect.log", false]], "log() (ffmpegio.analyze.metadatalogger method)": [[2, "ffmpegio.analyze.MetadataLogger.log", false]], "log() (ffmpegio.analyze.scdet method)": [[2, "ffmpegio.analyze.ScDet.log", false]], "log() (ffmpegio.analyze.silencedetect method)": [[2, "ffmpegio.analyze.SilenceDetect.log", false]], "mafd (ffmpegio.analyze.scdet.allscenes attribute)": [[2, "ffmpegio.analyze.ScDet.AllScenes.mafd", false]], "mafd (ffmpegio.analyze.scdet.scenes attribute)": [[2, "ffmpegio.analyze.ScDet.Scenes.mafd", false]], "media_type (ffmpegio.analyze.aphasemeter attribute)": [[2, "ffmpegio.analyze.APhaseMeter.media_type", false]], "media_type (ffmpegio.analyze.aspectralstats attribute)": [[2, "ffmpegio.analyze.ASpectralStats.media_type", false]], "media_type (ffmpegio.analyze.astats attribute)": [[2, "ffmpegio.analyze.AStats.media_type", false]], "media_type (ffmpegio.analyze.bbox attribute)": [[2, "ffmpegio.analyze.BBox.media_type", false]], "media_type (ffmpegio.analyze.blackdetect attribute)": [[2, "ffmpegio.analyze.BlackDetect.media_type", false]], "media_type (ffmpegio.analyze.blackframe attribute)": [[2, "ffmpegio.analyze.BlackFrame.media_type", false]], "media_type (ffmpegio.analyze.blurdetect attribute)": [[2, "ffmpegio.analyze.BlurDetect.media_type", false]], "media_type (ffmpegio.analyze.freezedetect attribute)": [[2, "ffmpegio.analyze.FreezeDetect.media_type", false]], "media_type (ffmpegio.analyze.metadatalogger attribute)": [[2, "ffmpegio.analyze.MetadataLogger.media_type", false]], "media_type (ffmpegio.analyze.scdet attribute)": [[2, "ffmpegio.analyze.ScDet.media_type", false]], "media_type (ffmpegio.analyze.silencedetect attribute)": [[2, "ffmpegio.analyze.SilenceDetect.media_type", false]], "meta_names (ffmpegio.analyze.aphasemeter attribute)": [[2, "ffmpegio.analyze.APhaseMeter.meta_names", false]], "meta_names (ffmpegio.analyze.aspectralstats attribute)": [[2, "ffmpegio.analyze.ASpectralStats.meta_names", false]], "meta_names (ffmpegio.analyze.astats attribute)": [[2, "ffmpegio.analyze.AStats.meta_names", false]], "meta_names (ffmpegio.analyze.bbox attribute)": [[2, "ffmpegio.analyze.BBox.meta_names", false]], "meta_names (ffmpegio.analyze.blackdetect attribute)": [[2, "ffmpegio.analyze.BlackDetect.meta_names", false]], "meta_names (ffmpegio.analyze.blackframe attribute)": [[2, "ffmpegio.analyze.BlackFrame.meta_names", false]], "meta_names (ffmpegio.analyze.blurdetect attribute)": [[2, "ffmpegio.analyze.BlurDetect.meta_names", false]], "meta_names (ffmpegio.analyze.freezedetect attribute)": [[2, "ffmpegio.analyze.FreezeDetect.meta_names", false]], "meta_names (ffmpegio.analyze.metadatalogger attribute)": [[2, "ffmpegio.analyze.MetadataLogger.meta_names", false]], "meta_names (ffmpegio.analyze.scdet attribute)": [[2, "ffmpegio.analyze.ScDet.meta_names", false]], "meta_names (ffmpegio.analyze.silencedetect attribute)": [[2, "ffmpegio.analyze.SilenceDetect.meta_names", false]], "metadata (ffmpegio.ffconcat.fileitem attribute)": [[5, "ffmpegio.FFConcat.FileItem.metadata", false]], "metadata (ffmpegio.ffconcat.streamitem attribute)": [[5, "ffmpegio.FFConcat.StreamItem.metadata", false]], "metadatalogger (class in ffmpegio.analyze)": [[2, "ffmpegio.analyze.MetadataLogger", false]], "mono_interval (ffmpegio.analyze.aphasemeter.phase attribute)": [[2, "ffmpegio.analyze.APhaseMeter.Phase.mono_interval", false]], "muxer_info() (in module ffmpegio.caps)": [[4, "ffmpegio.caps.muxer_info", false]], "muxers() (in module ffmpegio.caps)": [[4, "ffmpegio.caps.muxers", false]], "next_input_pad() (ffmpegio.filtergraph.chain method)": [[7, "ffmpegio.filtergraph.Chain.next_input_pad", false]], "next_input_pad() (ffmpegio.filtergraph.filter method)": [[7, "ffmpegio.filtergraph.Filter.next_input_pad", false]], "next_input_pad() (ffmpegio.filtergraph.graph method)": [[7, "ffmpegio.filtergraph.Graph.next_input_pad", false]], "next_output_pad() (ffmpegio.filtergraph.chain method)": [[7, "ffmpegio.filtergraph.Chain.next_output_pad", false]], "next_output_pad() (ffmpegio.filtergraph.filter method)": [[7, "ffmpegio.filtergraph.Filter.next_output_pad", false]], "next_output_pad() (ffmpegio.filtergraph.graph method)": [[7, "ffmpegio.filtergraph.Graph.next_output_pad", false]], "open() (in module ffmpegio)": [[3, "ffmpegio.open", false]], "options (ffmpegio.analyze.metadatalogger attribute)": [[2, "ffmpegio.analyze.MetadataLogger.options", false]], "options (ffmpegio.analyze.scdet attribute)": [[2, "ffmpegio.analyze.ScDet.options", false]], "options (ffmpegio.ffconcat.fileitem attribute)": [[5, "ffmpegio.FFConcat.FileItem.options", false]], "options() (in module ffmpegio.caps)": [[4, "ffmpegio.caps.options", false]], "out_phase_interval (ffmpegio.analyze.aphasemeter.phase attribute)": [[2, "ffmpegio.analyze.APhaseMeter.Phase.out_phase_interval", false]], "outpoint (ffmpegio.ffconcat.fileitem attribute)": [[5, "ffmpegio.FFConcat.FileItem.outpoint", false]], "output (ffmpegio.analyze.aphasemeter property)": [[2, "ffmpegio.analyze.APhaseMeter.output", false]], "output (ffmpegio.analyze.aspectralstats property)": [[2, "ffmpegio.analyze.ASpectralStats.output", false]], "output (ffmpegio.analyze.astats property)": [[2, "ffmpegio.analyze.AStats.output", false]], "output (ffmpegio.analyze.bbox property)": [[2, "ffmpegio.analyze.BBox.output", false]], "output (ffmpegio.analyze.blackdetect property)": [[2, "ffmpegio.analyze.BlackDetect.output", false]], "output (ffmpegio.analyze.blackframe property)": [[2, "ffmpegio.analyze.BlackFrame.output", false]], "output (ffmpegio.analyze.blurdetect property)": [[2, "ffmpegio.analyze.BlurDetect.output", false]], "output (ffmpegio.analyze.freezedetect property)": [[2, "ffmpegio.analyze.FreezeDetect.output", false]], "output (ffmpegio.analyze.metadatalogger property)": [[2, "ffmpegio.analyze.MetadataLogger.output", false]], "output (ffmpegio.analyze.scdet property)": [[2, "ffmpegio.analyze.ScDet.output", false]], "output (ffmpegio.analyze.silencedetect property)": [[2, "ffmpegio.analyze.SilenceDetect.output", false]], "parse() (ffmpegio.ffconcat method)": [[5, "ffmpegio.FFConcat.parse", false]], "path (ffmpegio.ffconcat.fileitem attribute)": [[5, "ffmpegio.FFConcat.FileItem.path", false]], "pblack (ffmpegio.analyze.blackframe.blackframes attribute)": [[2, "ffmpegio.analyze.BlackFrame.BlackFrames.pblack", false]], "pipe_url (ffmpegio.ffconcat attribute)": [[5, "ffmpegio.FFConcat.pipe_url", false]], "pix_fmts() (in module ffmpegio.caps)": [[4, "ffmpegio.caps.pix_fmts", false]], "pop() (ffmpegio.filtergraph.chain method)": [[7, "ffmpegio.filtergraph.Chain.pop", false]], "pop() (ffmpegio.filtergraph.graph method)": [[7, "ffmpegio.filtergraph.Graph.pop", false]], "popen (class in ffmpegio.ffmpegprocess)": [[1, "ffmpegio.ffmpegprocess.Popen", false]], "position (ffmpegio.analyze.bbox.bbox attribute)": [[2, "ffmpegio.analyze.BBox.BBox.position", false]], "protocols() (in module ffmpegio.caps)": [[4, "ffmpegio.caps.protocols", false]], "query() (in module ffmpegio.probe)": [[14, "ffmpegio.probe.query", false]], "rattach() (ffmpegio.filtergraph.chain method)": [[7, "ffmpegio.filtergraph.Chain.rattach", false]], "rattach() (ffmpegio.filtergraph.filter method)": [[7, "ffmpegio.filtergraph.Filter.rattach", false]], "rattach() (ffmpegio.filtergraph.graph method)": [[7, "ffmpegio.filtergraph.Graph.rattach", false]], "rconnect() (ffmpegio.filtergraph.chain method)": [[7, "ffmpegio.filtergraph.Chain.rconnect", false]], "rconnect() (ffmpegio.filtergraph.filter method)": [[7, "ffmpegio.filtergraph.Filter.rconnect", false]], "rconnect() (ffmpegio.filtergraph.graph method)": [[7, "ffmpegio.filtergraph.Graph.rconnect", false]], "read() (in module ffmpegio.audio)": [[3, "ffmpegio.audio.read", false]], "read() (in module ffmpegio.image)": [[3, "ffmpegio.image.read", false]], "read() (in module ffmpegio.video)": [[3, "ffmpegio.video.read", false]], "ref_in (ffmpegio.analyze.metadatalogger property)": [[2, "ffmpegio.analyze.MetadataLogger.ref_in", false]], "remove() (ffmpegio.filtergraph.chain method)": [[7, "ffmpegio.filtergraph.Chain.remove", false]], "remove() (ffmpegio.filtergraph.graph method)": [[7, "ffmpegio.filtergraph.Graph.remove", false]], "remove_label() (ffmpegio.filtergraph.graph method)": [[7, "ffmpegio.filtergraph.Graph.remove_label", false]], "rename_label() (ffmpegio.filtergraph.graph method)": [[7, "ffmpegio.filtergraph.Graph.rename_label", false]], "resolve_pad_index() (ffmpegio.filtergraph.chain method)": [[7, "ffmpegio.filtergraph.Chain.resolve_pad_index", false]], "resolve_pad_index() (ffmpegio.filtergraph.filter method)": [[7, "ffmpegio.filtergraph.Filter.resolve_pad_index", false]], "resolve_pad_index() (ffmpegio.filtergraph.graph method)": [[7, "ffmpegio.filtergraph.Graph.resolve_pad_index", false]], "resolve_pad_indices() (ffmpegio.filtergraph.chain method)": [[7, "ffmpegio.filtergraph.Chain.resolve_pad_indices", false]], "resolve_pad_indices() (ffmpegio.filtergraph.filter method)": [[7, "ffmpegio.filtergraph.Filter.resolve_pad_indices", false]], "resolve_pad_indices() (ffmpegio.filtergraph.graph method)": [[7, "ffmpegio.filtergraph.Graph.resolve_pad_indices", false]], "resolve_sink() (in module ffmpegio.devices)": [[6, "ffmpegio.devices.resolve_sink", false]], "resolve_source() (in module ffmpegio.devices)": [[6, "ffmpegio.devices.resolve_source", false]], "reverse() (ffmpegio.filtergraph.chain method)": [[7, "ffmpegio.filtergraph.Chain.reverse", false]], "reverse() (ffmpegio.filtergraph.graph method)": [[7, "ffmpegio.filtergraph.Graph.reverse", false]], "rjoin() (ffmpegio.filtergraph.chain method)": [[7, "ffmpegio.filtergraph.Chain.rjoin", false]], "rjoin() (ffmpegio.filtergraph.filter method)": [[7, "ffmpegio.filtergraph.Filter.rjoin", false]], "rjoin() (ffmpegio.filtergraph.graph method)": [[7, "ffmpegio.filtergraph.Graph.rjoin", false]], "run() (in module ffmpegio.analyze)": [[2, "ffmpegio.analyze.run", false]], "run() (in module ffmpegio.ffmpegprocess)": [[1, "ffmpegio.ffmpegprocess.run", false]], "run_two_pass() (in module ffmpegio.ffmpegprocess)": [[1, "ffmpegio.ffmpegprocess.run_two_pass", false]], "sample_fmts() (in module ffmpegio.caps)": [[4, "ffmpegio.caps.sample_fmts", false]], "scan() (in module ffmpegio.devices)": [[6, "ffmpegio.devices.scan", false]], "scdet (class in ffmpegio.analyze)": [[2, "ffmpegio.analyze.ScDet", false]], "scdet.allscenes (class in ffmpegio.analyze)": [[2, "ffmpegio.analyze.ScDet.AllScenes", false]], "scdet.scenes (class in ffmpegio.analyze)": [[2, "ffmpegio.analyze.ScDet.Scenes", false]], "score (ffmpegio.analyze.scdet.allscenes attribute)": [[2, "ffmpegio.analyze.ScDet.AllScenes.score", false]], "score (ffmpegio.analyze.scdet.scenes attribute)": [[2, "ffmpegio.analyze.ScDet.Scenes.score", false]], "script (ffmpegio.ffconcat property)": [[5, "ffmpegio.FFConcat.script", false]], "send_signal() (ffmpegio.ffmpegprocess.popen method)": [[1, "ffmpegio.ffmpegprocess.Popen.send_signal", false]], "set_path() (in module ffmpegio)": [[3, "ffmpegio.set_path", false]], "silencedetect (class in ffmpegio.analyze)": [[2, "ffmpegio.analyze.SilenceDetect", false]], "silencedetect.silent (class in ffmpegio.analyze)": [[2, "ffmpegio.analyze.SilenceDetect.Silent", false]], "stack() (ffmpegio.filtergraph.chain method)": [[7, "ffmpegio.filtergraph.Chain.stack", false]], "stack() (ffmpegio.filtergraph.filter method)": [[7, "ffmpegio.filtergraph.Filter.stack", false]], "stack() (ffmpegio.filtergraph.graph method)": [[7, "ffmpegio.filtergraph.Graph.stack", false]], "streams (ffmpegio.ffconcat attribute)": [[5, "ffmpegio.FFConcat.streams", false]], "streams_basic() (in module ffmpegio.probe)": [[14, "ffmpegio.probe.streams_basic", false]], "sws_flags (ffmpegio.filtergraph.graph attribute)": [[7, "ffmpegio.filtergraph.Graph.sws_flags", false]], "terminate() (ffmpegio.ffmpegprocess.popen method)": [[1, "ffmpegio.ffmpegprocess.Popen.terminate", false]], "time (ffmpegio.analyze.aphasemeter.phase attribute)": [[2, "ffmpegio.analyze.APhaseMeter.Phase.time", false]], "time (ffmpegio.analyze.bbox.bbox attribute)": [[2, "ffmpegio.analyze.BBox.BBox.time", false]], "time (ffmpegio.analyze.blackframe.blackframes attribute)": [[2, "ffmpegio.analyze.BlackFrame.BlackFrames.time", false]], "time (ffmpegio.analyze.blurdetect.blur attribute)": [[2, "ffmpegio.analyze.BlurDetect.Blur.time", false]], "time (ffmpegio.analyze.scdet.allscenes attribute)": [[2, "ffmpegio.analyze.ScDet.AllScenes.time", false]], "time (ffmpegio.analyze.scdet.scenes attribute)": [[2, "ffmpegio.analyze.ScDet.Scenes.time", false]], "transcode() (in module ffmpegio)": [[3, "ffmpegio.transcode", false]], "unlink() (ffmpegio.filtergraph.graph method)": [[7, "ffmpegio.filtergraph.Graph.unlink", false]], "update() (ffmpegio.ffconcat method)": [[5, "ffmpegio.FFConcat.update", false]], "url (ffmpegio.ffconcat property)": [[5, "ffmpegio.FFConcat.url", false]], "value (ffmpegio.analyze.aphasemeter.phase attribute)": [[2, "ffmpegio.analyze.APhaseMeter.Phase.value", false]], "video_streams_basic() (in module ffmpegio.probe)": [[14, "ffmpegio.probe.video_streams_basic", false]], "wait() (ffmpegio.ffmpegprocess.popen method)": [[1, "ffmpegio.ffmpegprocess.Popen.wait", false]], "with_traceback() (ffmpegio.filtergraph.chain.error method)": [[7, "ffmpegio.filtergraph.Chain.Error.with_traceback", false]], "with_traceback() (ffmpegio.filtergraph.filter.error method)": [[7, "ffmpegio.filtergraph.Filter.Error.with_traceback", false]], "with_traceback() (ffmpegio.filtergraph.filter.invalidname method)": [[7, "ffmpegio.filtergraph.Filter.InvalidName.with_traceback", false]], "with_traceback() (ffmpegio.filtergraph.filter.invalidoption method)": [[7, "ffmpegio.filtergraph.Filter.InvalidOption.with_traceback", false]], "with_traceback() (ffmpegio.filtergraph.filter.unsupported method)": [[7, "ffmpegio.filtergraph.Filter.Unsupported.with_traceback", false]], "with_traceback() (ffmpegio.filtergraph.graph.error method)": [[7, "ffmpegio.filtergraph.Graph.Error.with_traceback", false]], "with_traceback() (ffmpegio.filtergraph.graph.filterpadmediatypemismatch method)": [[7, "ffmpegio.filtergraph.Graph.FilterPadMediaTypeMismatch.with_traceback", false]], "with_traceback() (ffmpegio.filtergraph.graph.invalidfilterpadid method)": [[7, "ffmpegio.filtergraph.Graph.InvalidFilterPadId.with_traceback", false]], "write() (in module ffmpegio.audio)": [[3, "ffmpegio.audio.write", false]], "write() (in module ffmpegio.image)": [[3, "ffmpegio.image.write", false]], "write() (in module ffmpegio.video)": [[3, "ffmpegio.video.write", false]]}, "objects": {"ffmpegio": [[5, 0, 1, "", "FFConcat"], [3, 4, 1, "", "ffmpeg_info"], [3, 4, 1, "", "get_path"], [3, 4, 1, "", "is_ready"], [3, 4, 1, "", "open"], [3, 4, 1, "", "set_path"], [3, 4, 1, "", "transcode"]], "ffmpegio.FFConcat": [[5, 0, 1, "", "FileItem"], [5, 0, 1, "", "StreamItem"], [5, 3, 1, "", "add_chapter"], [5, 3, 1, "", "add_file"], [5, 3, 1, "", "add_files"], [5, 3, 1, "", "add_glob"], [5, 3, 1, "", "add_stream"], [5, 3, 1, "", "as_filter"], [5, 1, 1, "", "chapters"], [5, 3, 1, "", "compose"], [5, 1, 1, "", "ffconcat_url"], [5, 2, 1, "", "input"], [5, 2, 1, "", "last_file"], [5, 2, 1, "", "last_stream"], [5, 3, 1, "", "parse"], [5, 1, 1, "", "pipe_url"], [5, 2, 1, "", "script"], [5, 1, 1, "", "streams"], [5, 3, 1, "", "update"], [5, 2, 1, "", "url"]], "ffmpegio.FFConcat.FileItem": [[5, 1, 1, "", "duration"], [5, 1, 1, "", "inpoint"], [5, 2, 1, "", "lines"], [5, 1, 1, "", "metadata"], [5, 1, 1, "", "options"], [5, 1, 1, "", "outpoint"], [5, 1, 1, "", "path"]], "ffmpegio.FFConcat.StreamItem": [[5, 1, 1, "", "codec"], [5, 1, 1, "", "extradata"], [5, 1, 1, "", "id"], [5, 2, 1, "", "lines"], [5, 1, 1, "", "metadata"]], "ffmpegio.analyze": [[2, 0, 1, "", "APhaseMeter"], [2, 0, 1, "", "ASpectralStats"], [2, 0, 1, "", "AStats"], [2, 0, 1, "", "BBox"], [2, 0, 1, "", "BlackDetect"], [2, 0, 1, "", "BlackFrame"], [2, 0, 1, "", "BlurDetect"], [2, 0, 1, "", "FreezeDetect"], [2, 0, 1, "", "MetadataLogger"], [2, 0, 1, "", "ScDet"], [2, 0, 1, "", "SilenceDetect"], [2, 4, 1, "", "run"]], "ffmpegio.analyze.APhaseMeter": [[2, 0, 1, "", "Phase"], [2, 2, 1, "", "filter"], [2, 1, 1, "", "filter_name"], [2, 3, 1, "", "log"], [2, 1, 1, "", "media_type"], [2, 1, 1, "", "meta_names"], [2, 2, 1, "", "output"]], "ffmpegio.analyze.APhaseMeter.Phase": [[2, 1, 1, "", "mono_interval"], [2, 1, 1, "", "out_phase_interval"], [2, 1, 1, "", "time"], [2, 1, 1, "", "value"]], "ffmpegio.analyze.ASpectralStats": [[2, 1, 1, "", "filter_name"], [2, 3, 1, "", "log"], [2, 1, 1, "", "media_type"], [2, 1, 1, "", "meta_names"], [2, 2, 1, "", "output"]], "ffmpegio.analyze.AStats": [[2, 2, 1, "", "filter"], [2, 1, 1, "", "filter_name"], [2, 3, 1, "", "log"], [2, 1, 1, "", "media_type"], [2, 1, 1, "", "meta_names"], [2, 2, 1, "", "output"]], "ffmpegio.analyze.BBox": [[2, 0, 1, "", "BBox"], [2, 1, 1, "", "filter_name"], [2, 3, 1, "", "log"], [2, 1, 1, "", "media_type"], [2, 1, 1, "", "meta_names"], [2, 2, 1, "", "output"]], "ffmpegio.analyze.BBox.BBox": [[2, 1, 1, "", "position"], [2, 1, 1, "", "time"]], "ffmpegio.analyze.BlackDetect": [[2, 0, 1, "", "Black"], [2, 1, 1, "", "filter_name"], [2, 3, 1, "", "log"], [2, 1, 1, "", "media_type"], [2, 1, 1, "", "meta_names"], [2, 2, 1, "", "output"]], "ffmpegio.analyze.BlackDetect.Black": [[2, 1, 1, "", "interval"]], "ffmpegio.analyze.BlackFrame": [[2, 0, 1, "", "BlackFrames"], [2, 1, 1, "", "filter_name"], [2, 3, 1, "", "log"], [2, 1, 1, "", "media_type"], [2, 1, 1, "", "meta_names"], [2, 2, 1, "", "output"]], "ffmpegio.analyze.BlackFrame.BlackFrames": [[2, 1, 1, "", "pblack"], [2, 1, 1, "", "time"]], "ffmpegio.analyze.BlurDetect": [[2, 0, 1, "", "Blur"], [2, 1, 1, "", "filter_name"], [2, 3, 1, "", "log"], [2, 1, 1, "", "media_type"], [2, 1, 1, "", "meta_names"], [2, 2, 1, "", "output"]], "ffmpegio.analyze.BlurDetect.Blur": [[2, 1, 1, "", "blur"], [2, 1, 1, "", "time"]], "ffmpegio.analyze.FreezeDetect": [[2, 0, 1, "", "Frozen"], [2, 1, 1, "", "filter_name"], [2, 3, 1, "", "log"], [2, 1, 1, "", "media_type"], [2, 1, 1, "", "meta_names"], [2, 2, 1, "", "output"]], "ffmpegio.analyze.FreezeDetect.Frozen": [[2, 1, 1, "", "interval"]], "ffmpegio.analyze.MetadataLogger": [[2, 2, 1, "", "filter"], [2, 1, 1, "", "filter_name"], [2, 3, 1, "", "log"], [2, 1, 1, "", "media_type"], [2, 1, 1, "", "meta_names"], [2, 1, 1, "", "options"], [2, 2, 1, "", "output"], [2, 2, 1, "", "ref_in"]], "ffmpegio.analyze.ScDet": [[2, 0, 1, "", "AllScenes"], [2, 0, 1, "", "Scenes"], [2, 1, 1, "", "filter_name"], [2, 3, 1, "", "log"], [2, 1, 1, "", "media_type"], [2, 1, 1, "", "meta_names"], [2, 1, 1, "", "options"], [2, 2, 1, "", "output"]], "ffmpegio.analyze.ScDet.AllScenes": [[2, 1, 1, "", "changed"], [2, 1, 1, "", "mafd"], [2, 1, 1, "", "score"], [2, 1, 1, "", "time"]], "ffmpegio.analyze.ScDet.Scenes": [[2, 1, 1, "", "mafd"], [2, 1, 1, "", "score"], [2, 1, 1, "", "time"]], "ffmpegio.analyze.SilenceDetect": [[2, 0, 1, "", "Silent"], [2, 1, 1, "", "filter_name"], [2, 3, 1, "", "log"], [2, 1, 1, "", "media_type"], [2, 1, 1, "", "meta_names"], [2, 2, 1, "", "output"]], "ffmpegio.analyze.SilenceDetect.Silent": [[2, 1, 1, "", "interval"]], "ffmpegio.audio": [[3, 4, 1, "", "create"], [2, 4, 1, "", "detect"], [3, 4, 1, "", "filter"], [3, 4, 1, "", "read"], [3, 4, 1, "", "write"]], "ffmpegio.caps": [[4, 4, 1, "", "bsfilter_info"], [4, 4, 1, "", "bsfilters"], [4, 4, 1, "", "codecs"], [4, 4, 1, "", "colors"], [4, 4, 1, "", "decoder_info"], [4, 4, 1, "", "decoders"], [4, 4, 1, "", "demuxer_info"], [4, 4, 1, "", "demuxers"], [4, 4, 1, "", "devices"], [4, 4, 1, "", "encoder_info"], [4, 4, 1, "", "encoders"], [4, 4, 1, "", "filter_info"], [4, 4, 1, "", "filters"], [4, 4, 1, "", "formats"], [4, 4, 1, "", "layouts"], [4, 4, 1, "", "muxer_info"], [4, 4, 1, "", "muxers"], [4, 4, 1, "", "options"], [4, 4, 1, "", "pix_fmts"], [4, 4, 1, "", "protocols"], [4, 4, 1, "", "sample_fmts"]], "ffmpegio.devices": [[6, 4, 1, "", "list_sink_options"], [6, 4, 1, "", "list_sinks"], [6, 4, 1, "", "list_source_options"], [6, 4, 1, "", "list_sources"], [6, 4, 1, "", "resolve_sink"], [6, 4, 1, "", "resolve_source"], [6, 4, 1, "", "scan"]], "ffmpegio.ffmpegprocess": [[1, 0, 1, "", "Popen"], [1, 4, 1, "", "run"], [1, 4, 1, "", "run_two_pass"]], "ffmpegio.ffmpegprocess.Popen": [[1, 1, 1, "", "ffmpeg_args"], [1, 3, 1, "", "kill"], [1, 3, 1, "", "send_signal"], [1, 3, 1, "", "terminate"], [1, 3, 1, "", "wait"]], "ffmpegio.filtergraph": [[7, 0, 1, "", "Chain"], [7, 0, 1, "", "Filter"], [7, 0, 1, "", "Graph"], [7, 4, 1, "", "as_filter"], [7, 4, 1, "", "as_filterchain"], [7, 4, 1, "", "as_filtergraph"], [7, 4, 1, "", "as_filtergraph_object"]], "ffmpegio.filtergraph.Chain": [[7, 5, 1, "", "Error"], [7, 3, 1, "", "add_label"], [7, 3, 1, "", "append"], [7, 3, 1, "", "attach"], [7, 3, 1, "", "clear"], [7, 3, 1, "", "compose"], [7, 3, 1, "", "connect"], [7, 3, 1, "", "count"], [7, 3, 1, "", "extend"], [7, 3, 1, "", "get_input_pad"], [7, 3, 1, "", "get_label"], [7, 3, 1, "", "get_num_chains"], [7, 3, 1, "", "get_num_filters"], [7, 3, 1, "", "get_num_inputs"], [7, 3, 1, "", "get_num_outputs"], [7, 3, 1, "", "get_num_pads"], [7, 3, 1, "", "get_output_pad"], [7, 3, 1, "", "index"], [7, 3, 1, "", "insert"], [7, 3, 1, "", "is_last_filter"], [7, 3, 1, "", "iter_chains"], [7, 3, 1, "", "iter_input_labels"], [7, 3, 1, "", "iter_input_pads"], [7, 3, 1, "", "iter_output_labels"], [7, 3, 1, "", "iter_output_pads"], [7, 3, 1, "", "join"], [7, 3, 1, "", "next_input_pad"], [7, 3, 1, "", "next_output_pad"], [7, 3, 1, "", "pop"], [7, 3, 1, "", "rattach"], [7, 3, 1, "", "rconnect"], [7, 3, 1, "", "remove"], [7, 3, 1, "", "resolve_pad_index"], [7, 3, 1, "", "resolve_pad_indices"], [7, 3, 1, "", "reverse"], [7, 3, 1, "", "rjoin"], [7, 3, 1, "", "stack"]], "ffmpegio.filtergraph.Chain.Error": [[7, 3, 1, "", "add_note"], [7, 3, 1, "", "with_traceback"]], "ffmpegio.filtergraph.Filter": [[7, 5, 1, "", "Error"], [7, 5, 1, "", "InvalidName"], [7, 5, 1, "", "InvalidOption"], [7, 5, 1, "", "Unsupported"], [7, 3, 1, "", "add_label"], [7, 3, 1, "", "apply"], [7, 3, 1, "", "attach"], [7, 3, 1, "", "compose"], [7, 3, 1, "", "connect"], [7, 3, 1, "", "count"], [7, 3, 1, "", "get_input_pad"], [7, 3, 1, "", "get_label"], [7, 3, 1, "", "get_num_chains"], [7, 3, 1, "", "get_num_filters"], [7, 3, 1, "", "get_num_inputs"], [7, 3, 1, "", "get_num_outputs"], [7, 3, 1, "", "get_num_pads"], [7, 3, 1, "", "get_output_pad"], [7, 3, 1, "", "index"], [7, 3, 1, "", "iter_chains"], [7, 3, 1, "", "iter_input_labels"], [7, 3, 1, "", "iter_input_pads"], [7, 3, 1, "", "iter_output_labels"], [7, 3, 1, "", "iter_output_pads"], [7, 3, 1, "", "join"], [7, 3, 1, "", "next_input_pad"], [7, 3, 1, "", "next_output_pad"], [7, 3, 1, "", "rattach"], [7, 3, 1, "", "rconnect"], [7, 3, 1, "", "resolve_pad_index"], [7, 3, 1, "", "resolve_pad_indices"], [7, 3, 1, "", "rjoin"], [7, 3, 1, "", "stack"]], "ffmpegio.filtergraph.Filter.Error": [[7, 3, 1, "", "add_note"], [7, 3, 1, "", "with_traceback"]], "ffmpegio.filtergraph.Filter.InvalidName": [[7, 3, 1, "", "add_note"], [7, 3, 1, "", "with_traceback"]], "ffmpegio.filtergraph.Filter.InvalidOption": [[7, 3, 1, "", "add_note"], [7, 3, 1, "", "with_traceback"]], "ffmpegio.filtergraph.Filter.Unsupported": [[7, 3, 1, "", "add_note"], [7, 3, 1, "", "with_traceback"]], "ffmpegio.filtergraph.Graph": [[7, 5, 1, "", "Error"], [7, 5, 1, "", "FilterPadMediaTypeMismatch"], [7, 5, 1, "", "InvalidFilterPadId"], [7, 3, 1, "", "add_label"], [7, 3, 1, "", "append"], [7, 3, 1, "", "are_linked"], [7, 3, 1, "", "as_script_file"], [7, 3, 1, "", "attach"], [7, 3, 1, "", "clear"], [7, 3, 1, "", "compose"], [7, 3, 1, "", "connect"], [7, 3, 1, "", "count"], [7, 3, 1, "", "extend"], [7, 3, 1, "", "get_input_pad"], [7, 3, 1, "", "get_label"], [7, 3, 1, "", "get_num_chains"], [7, 3, 1, "", "get_num_filters"], [7, 3, 1, "", "get_num_inputs"], [7, 3, 1, "", "get_num_outputs"], [7, 3, 1, "", "get_num_pads"], [7, 3, 1, "", "get_output_pad"], [7, 3, 1, "", "index"], [7, 3, 1, "", "insert"], [7, 3, 1, "", "is_chain_siso"], [7, 3, 1, "", "iter_chains"], [7, 3, 1, "", "iter_input_labels"], [7, 3, 1, "", "iter_input_pads"], [7, 3, 1, "", "iter_output_labels"], [7, 3, 1, "", "iter_output_pads"], [7, 3, 1, "", "join"], [7, 3, 1, "", "link"], [7, 3, 1, "", "next_input_pad"], [7, 3, 1, "", "next_output_pad"], [7, 3, 1, "", "pop"], [7, 3, 1, "", "rattach"], [7, 3, 1, "", "rconnect"], [7, 3, 1, "", "remove"], [7, 3, 1, "", "remove_label"], [7, 3, 1, "", "rename_label"], [7, 3, 1, "", "resolve_pad_index"], [7, 3, 1, "", "resolve_pad_indices"], [7, 3, 1, "", "reverse"], [7, 3, 1, "", "rjoin"], [7, 3, 1, "", "stack"], [7, 1, 1, "", "sws_flags"], [7, 3, 1, "", "unlink"]], "ffmpegio.filtergraph.Graph.Error": [[7, 3, 1, "", "add_note"], [7, 3, 1, "", "with_traceback"]], "ffmpegio.filtergraph.Graph.FilterPadMediaTypeMismatch": [[7, 3, 1, "", "add_note"], [7, 3, 1, "", "with_traceback"]], "ffmpegio.filtergraph.Graph.InvalidFilterPadId": [[7, 3, 1, "", "add_note"], [7, 3, 1, "", "with_traceback"]], "ffmpegio.image": [[3, 4, 1, "", "create"], [3, 4, 1, "", "filter"], [3, 4, 1, "", "read"], [3, 4, 1, "", "write"]], "ffmpegio.probe": [[14, 1, 1, "", "IntervalSpec"], [14, 4, 1, "", "audio_streams_basic"], [14, 4, 1, "", "format_basic"], [14, 4, 1, "", "frames"], [14, 4, 1, "", "full_details"], [14, 4, 1, "", "query"], [14, 4, 1, "", "streams_basic"], [14, 4, 1, "", "video_streams_basic"]], "ffmpegio.video": [[3, 4, 1, "", "create"], [2, 4, 1, "", "detect"], [3, 4, 1, "", "filter"], [3, 4, 1, "", "read"], [3, 4, 1, "", "write"]]}, "objnames": {"0": ["py", "class", "Python class"], "1": ["py", "attribute", "Python attribute"], "2": ["py", "property", "Python property"], "3": ["py", "method", "Python method"], "4": ["py", "function", "Python function"], "5": ["py", "exception", "Python exception"]}, "objtypes": {"0": "py:class", "1": "py:attribute", "2": "py:property", "3": "py:method", "4": "py:function", "5": "py:exception"}, "terms": {"": [0, 1, 2, 3, 4, 7, 9, 10, 12, 15, 16], "0": [0, 2, 5, 6, 7, 9, 12, 13, 14, 15, 16], "00": [9, 15, 16], "000000": 2, "000003": 2, "001": 2, "01": 12, "05": 2, "0588235": 2, "0709228515625": 15, "092316": 2, "0x2a4ef084bd0": 9, "0x7f95cfcd2060": 7, "0x7f95cfcd20f0": 7, "0x7f95cfcd21b0": 7, "0x7f95cfcd2270": 7, "0x7f95cfcd2360": 7, "0x7f95cfcd23f0": 7, "0x7f95cfcd25a0": 7, "0x7f95cfcd27b0": 7, "0x7f95cfcd2960": 7, "0x7f95cfcd2ba0": 7, "0x7f95cfcd2ea0": 7, "0x7f95cfcd2fc0": 7, "0x7f95cfcd3020": 7, "0x7f95cfcd30b0": 7, "0x7f95cfcd3110": 7, "0x7f95cfcd32f0": 7, "0x7f95cfcd3350": 7, "0x7f95cfcd33e0": 7, "0x7f95cfcd3740": 7, "0x7f95cfcd3920": 7, "0x7f95cfd04050": 7, "0x7f95cfe9e3f0": 7, "0x7f95df031970": 7, "0x7f95df0319a0": 7, "0x7f95df033110": 7, "1": [0, 1, 2, 3, 4, 5, 7, 9, 13, 15, 16], "10": [2, 3, 6, 7, 9, 13, 15, 16], "100": [2, 7, 9, 13, 15, 16], "1000": [9, 16], "10000": 15, "1001": 15, "100442": 2, "1014": 15, "1020": 7, "10hdp12b24n": 6, "11": [2, 9, 15], "117647": 2, "11th": 15, "12": [2, 3, 7, 13], "120": [7, 9, 15], "1280": 7, "1280x480": [9, 16], "128k": [9, 16], "13": 2, "14": [2, 3, 13], "15": [2, 9, 15, 16], "15000": 15, "154": 7, "16": [2, 3, 7, 13, 15], "160": 15, "16000": 15, "164": 7, "166667": 2, "17": 2, "170": 2, "18": [2, 15], "180": 2, "19": 2, "1e3": 12, "1st": 7, "2": [1, 2, 3, 7, 9, 12, 13, 15, 16], "20": [7, 9, 12], "200": [7, 9, 13, 16], "2000": 3, "2048": 2, "2147483647": 15, "2147483648": 15, "22": [9, 15, 16], "228": 7, "2324": 15, "24": [0, 9, 15, 16], "240": [7, 15], "25": [9, 16], "252": 7, "255": [2, 15], "2600k": [9, 16], "262192": 15, "264": [9, 13, 16], "271": 7, "29": 15, "2928384": 15, "293": 7, "297": 7, "299": 15, "2nd": 7, "2px": [9, 16], "2x": 15, "3": [2, 3, 7, 9, 13, 15, 16], "30": [2, 7, 9, 15, 16], "300": [9, 16], "30000": 15, "302": 7, "32": [2, 7, 9, 15, 16], "320": 15, "320x240": 15, "32767": 15, "32768": 15, "33": 15, "336": 15, "337": 7, "33877914": 15, "34": 7, "347": 7, "352": 15, "396": 15, "39972222222222": 15, "3x2": [9, 16], "4": [2, 3, 7, 9, 13, 15, 16], "40": [7, 9, 16], "4000": 15, "40325555555556": 15, "403256": 15, "44100": 15, "48": 7, "480": [7, 9, 15, 16], "48000": 7, "480x": [9, 16], "5": [2, 7, 9, 15, 16], "50": [2, 7, 9, 12, 13, 16], "51": 13, "6": [2, 7], "60": 7, "61": 15, "63": [9, 16], "64": 0, "640": [7, 15], "640x480": 15, "643": 7, "64k": [0, 9, 16], "653": 7, "65535": 2, "65536": 2, "656": 7, "66": 15, "673": 7, "699": 7, "7": [2, 7, 9, 16], "708": 7, "720": 7, "720p": 7, "729": 7, "758": 7, "7px": [9, 16], "8": [2, 15], "80": 2, "8000": 15, "820": 7, "835": 7, "842": 7, "875": [9, 16], "877914": 15, "882": 7, "9": [2, 7], "90": [2, 7], "914": 7, "9223372036854775807": 7, "93": 7, "960x540": [9, 16], "97": 15, "98": 2, "9kbit": 15, "A": [0, 2, 7, 13, 14], "As": [3, 5, 7], "At": 8, "Be": [7, 13], "By": [5, 6, 15], "For": [0, 1, 2, 3, 6, 7, 10, 12, 13, 15], "IN": 7, "If": [0, 1, 2, 3, 5, 6, 7, 13, 14, 15, 16], "In": [3, 7, 10], "It": [1, 7, 15], "Its": [3, 7, 13], "No": 7, "On": 7, "One": [2, 7, 8], "Or": 5, "TO": 7, "That": 7, "The": [0, 1, 2, 3, 4, 5, 6, 7, 9, 10, 13, 15], "Then": [7, 8, 15], "There": [2, 3, 10, 13, 15], "These": [7, 9], "To": [0, 2, 3, 7, 9, 10, 12, 13, 15, 16], "_": [2, 7, 15], "__": 12, "__mul__": 7, "__n": 7, "__or__": 7, "__repr__": 7, "__rrshift__": 7, "__rshift__": 7, "__str__": 13, "__traceback__": 7, "_attach": 7, "_description_": 7, "_in": [3, 13], "_link": 7, "_option": 2, "_out": 3, "_resolve_label": 7, "_stack": 7, "_time": 14, "_type_": 7, "aa": 3, "aac": [9, 16], "abbrevi": 2, "abc": 7, "about": 2, "abov": [0, 2, 7, 9, 12, 15], "absolut": [2, 3, 5], "abstract": [2, 5, 6], "ac": [9, 13, 15, 16], "acceler": 4, "accept": [1, 2, 3, 7, 9, 13, 16], "access": [3, 5], "accompani": 7, "accord": [6, 13], "accur": 15, "accuraci": [3, 14], "accurate_tim": 14, "acodec": [1, 3, 9, 16], "action": 10, "activ": [7, 8], "actual": 7, "ad": 5, "add": [1, 3, 5, 7, 8, 9, 12, 13, 16], "add_chapt": 5, "add_fil": [5, 9, 16], "add_glob": 5, "add_label": 7, "add_not": 7, "add_opt": 5, "add_stream": 5, "addit": [1, 3, 4, 5, 6, 7, 10, 13, 14], "adjust": 13, "advantag": [3, 9, 15, 16], "af": [0, 7, 9, 13, 15, 16], "afad": [7, 9, 16], "aformat": 7, "after": [1, 3, 6], "again": 6, "against": 13, "aim": [8, 9, 16], "alia": [2, 3, 4], "all": [0, 2, 3, 6, 7, 9, 13, 14, 15, 16], "all_scor": 2, "allow": [7, 13, 16], "allscen": 2, "almost": 2, "along": 9, "alpha": [3, 13, 15], "alphanumer": 7, "alreadi": [1, 7, 8], "also": [1, 2, 3, 4, 5, 6, 7, 8, 13, 15], "alt": 13, "alter": [5, 13], "altern": [1, 5, 7, 10], "although": 8, "alwai": [7, 13, 15], "ametadata": 2, "amount": 2, "an": [0, 1, 2, 3, 4, 5, 6, 7, 9, 10, 12, 13, 14, 15, 16], "analyz": 15, "angl": 2, "ani": [0, 1, 2, 3, 4, 5, 6, 7, 9, 12, 13, 14, 15, 16], "anim": [9, 12], "anoth": [0, 3, 7, 9, 15, 16], "anyth": 7, "aout": 7, "appdata": 10, "appear": 5, "append": [2, 3, 5, 7, 13], "appli": [1, 7, 9, 13, 16], "applic": [1, 2], "approach": [5, 10], "apt": 10, "ar": [0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 13, 14, 15], "arang": 12, "arbitrari": 10, "are_link": 7, "area": 7, "aren": 1, "aresampl": 0, "arg": [1, 3, 7], "argument": [1, 2, 3, 7, 9, 12, 13, 15, 16], "arrai": [3, 9, 13, 15], "articl": 7, "as_file_obj": 7, "as_filt": [5, 7], "as_filterchain": 7, "as_filtergraph": 7, "as_filtergraph_object": 7, "as_script_fil": 7, "asid": 5, "aspect": 7, "aspectalstat": 2, "asplit": 7, "assign": [2, 7], "assist": 7, "associ": [3, 4, 14], "assum": [7, 13], "ast_info": 15, "atrim": 7, "attach": 7, "attain": 7, "attenu": [9, 16], "attribut": [7, 14], "attributeerror": 7, "audio": [1, 2, 3, 4, 5, 6, 7, 14], "audio_byt": 3, "audio_codec": 4, "audio_filt": 7, "audio_info": 3, "audio_stream_bas": 14, "audio_streams_bas": [14, 15], "author": 7, "auto": [1, 3, 7, 9, 10, 13, 15, 16], "auto_link": 7, "automat": [0, 1, 3, 5, 7, 8, 9, 13, 16], "autonom": 1, "av": 3, "avail": [3, 7, 8, 9, 10, 13, 15, 16], "avfound": 6, "avi": [0, 9, 15, 16], "avopt": 7, "awar": [7, 13], "ax": 12, "b": [0, 9, 16], "back": 6, "background": [13, 15], "bandwidth": [9, 16], "banner": [1, 9, 16], "bartlett": 2, "base": [2, 7, 15], "base_indic": 7, "base_is_input": 7, "basic": [1, 14, 15], "batch": 5, "becaus": [5, 7, 12, 15], "becom": 15, "been": 16, "befor": [1, 2, 3, 5, 7, 12], "begin": [2, 14], "behavior": [3, 9, 13, 16], "being": 2, "below": [2, 3, 7, 13, 14], "between": [7, 9, 16], "bhann": 2, "bharri": 2, "bin": 10, "binari": [3, 9, 10, 15], "bistream": 4, "bit": [2, 3, 4, 7, 10, 12, 13, 15], "bit_depth": 2, "bit_depth2": 2, "bitrat": [0, 15], "bits_per_pixel": 4, "bitstream": 4, "black": 2, "black_end": 2, "black_min_dur": 2, "black_start": 2, "blackman": 2, "block": [1, 2, 3], "block_height": 2, "block_pct": 2, "block_width": 2, "blocksiz": [3, 9, 15, 16], "blur": 2, "blurri": 2, "bmp": [9, 15, 16], "bnuttal": 2, "board": 11, "bohman": 2, "bool": [1, 2, 3, 4, 5, 6, 7, 13, 14, 15], "boolean": 7, "borrow": 7, "both": [0, 1, 3, 5, 7, 9, 13, 15, 16], "bottom": 13, "bound": 2, "box": 2, "bracket": 7, "branch": 7, "brew": 10, "bring": [8, 9, 16], "bsfilter": 4, "bsfilter_info": 4, "buffer": 1, "bufsiz": [0, 9, 16], "build": [3, 7, 8, 9], "built": [3, 7, 10], "builtin": 1, "bunch": [9, 16], "bundl": 10, "butch": [9, 16], "byte": [1, 3, 5, 9, 14, 15, 16], "bytes_to_audio": 3, "bytes_to_video": 3, "c": [0, 1, 3, 9, 10, 16], "cach": 14, "cache_output": 14, "calcul": [2, 13], "call": [1, 2, 3, 5, 6, 7, 9, 15, 16], "callabl": [1, 2, 3], "callback": [1, 2, 3], "caller": [1, 3], "camera": 6, "can": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 13, 14, 15, 16], "can_decod": 4, "can_demux": 4, "can_encod": 4, "can_mux": 4, "cannot": [3, 7], "cap": [4, 7, 9, 13, 16], "capabl": [6, 8, 9, 13, 16], "captur": [1, 2, 3, 6], "capture_log": [1, 9, 16], "care": 7, "case": 7, "catch": 1, "cauchi": 2, "caus": 7, "caveat": 8, "center": [9, 16], "centroid": 2, "ch": 2, "chain": [7, 9], "chain_fill_valu": 7, "chain_id": 7, "chain_id_omitt": 7, "chain_siso": 7, "chainabl": 7, "chainable_first": 7, "chainable_onli": 7, "chang": [2, 3, 6, 13], "channel": [2, 3, 4, 7, 9, 13, 14, 15, 16], "channel_layout": [7, 14, 15], "channelmap": 15, "chapter": [5, 14], "charact": 7, "chebyshev": 2, "check": [2, 4, 7, 13], "check_cancel_command": [9, 16], "check_input": 7, "check_input_stream": 7, "check_link": 7, "check_output": 7, "check_stream_spec": 7, "choic": 3, "chosen": [5, 6, 7, 12], "chx": 2, "class": [1, 2, 7], "clear": [5, 7], "close": [1, 3], "co": 4, "code": 4, "codec": [4, 5, 9, 13, 16], "codec_nam": [14, 15], "codec_typ": [14, 15], "coder": 4, "color": [4, 7, 12, 13, 15], "column": 15, "combin": [2, 4, 6, 7, 9, 16], "command": [0, 1, 3, 4, 5, 6, 9, 10, 13], "command_support": 4, "commentari": 2, "common": [2, 3, 4, 7], "commut": 13, "compat": [0, 4], "complet": [1, 2, 9, 15, 16], "completeprocess": 1, "complex": [7, 9, 16], "complic": 10, "compon": [3, 4], "compos": [2, 5, 6, 7], "compose_filt": 7, "compose_graph": 7, "compress": [4, 13], "comput": [2, 6, 14, 15], "conatdemux": 5, "concat": [5, 9, 16], "concaten": [7, 9, 16], "concret": 13, "configur": [1, 2, 3, 6, 15], "conflict": [3, 7], "conform": 2, "connect": 7, "connector": 7, "consid": [2, 3], "consist": 7, "consol": [1, 2, 3, 15], "constant": 13, "construct": [12, 13], "constructor": [1, 5, 7], "contain": [2, 3, 4, 7, 14, 15], "content": 10, "context": [5, 9, 16], "contigu": 7, "continu": [3, 14], "control": 1, "conveni": [7, 13, 15], "convent": 13, "convers": [13, 14, 15], "convert": [0, 1, 3, 5, 6, 7, 9, 13, 14, 15, 16], "copi": [0, 7, 9, 16], "core": [8, 15, 16], "correct": [7, 13], "correspond": [3, 7], "could": [1, 6, 7, 8, 10, 12, 13, 15], "count": [2, 7], "counter": 7, "counterpart": 15, "coupl": 7, "cover": 0, "cranki": 12, "creat": [3, 5, 7, 9, 16], "create_label": 7, "creation": 12, "crest": 2, "crest_factor": 2, "crf": [9, 13, 16], "crop": [7, 9, 13, 16], "cross": [2, 8, 9, 16], "ctrl_c_event": 1, "cumul": 2, "current": [2, 3, 4, 5, 9, 10, 16], "custom": [2, 15], "d": [2, 7, 9, 15, 16], "dangl": 7, "dash": 0, "data": [1, 2, 3, 4, 5, 6, 7, 8, 12, 13, 14, 15], "datatyp": 2, "date": 8, "db": [2, 9, 16], "dbl": [3, 9, 13, 15, 16], "dbl_max": 2, "dc": 2, "dc_offset": 2, "deal": 3, "debian": 10, "declar": 7, "decod": [3, 4, 9, 16], "decoder_info": 4, "decreas": [2, 7], "def": [9, 12, 16], "default": [1, 2, 3, 4, 5, 6, 7, 9, 12, 13, 14, 15, 16], "default_timeout": 3, "defin": [0, 3, 4, 5, 7, 13], "definit": [0, 3, 6, 7], "delai": 12, "delet": [5, 7], "demand": 15, "demonstr": 7, "demux": [4, 5, 9, 16], "demuxer_info": 4, "denorm": 2, "depend": [3, 5, 7, 10], "deprec": 9, "depth": 2, "deriv": 14, "desc": 2, "describ": [3, 5, 7], "descript": [2, 3, 4, 7, 13, 15], "design": 7, "desir": [7, 13], "destin": 3, "detail": [0, 1, 2, 4, 7, 14], "detect": [2, 3, 6, 8, 9, 10, 13, 16], "detector": 2, "determin": [3, 7], "dev": 6, "develop": 6, "devic": 4, "device_sink_api": 6, "device_source_api": 6, "dict": [1, 2, 3, 4, 5, 6, 7, 9, 13, 14, 15, 16], "dictionari": [3, 14], "differ": [2, 5, 7, 13], "dimens": [7, 13, 15], "direct": [4, 7, 12], "directli": [0, 1, 2, 7, 12], "directori": [3, 5, 10], "directrend": 4, "directshow": [6, 9, 16], "disabl": [1, 2], "discuss": [2, 11], "displai": [7, 15], "display_aspect_ratio": [14, 15], "disposit": 4, "distribut": 16, "distro": 9, "divis": 13, "do": [3, 4, 6, 7, 12, 15], "docstr": 7, "document": [0, 2, 3, 7, 11, 13, 15], "doe": [0, 1, 6, 7, 13, 14, 15], "dolph": 2, "domain": 2, "done": [1, 7, 9, 15, 16], "doubl": [2, 15], "down": 7, "download": [3, 9], "downscal": 13, "downscale_even": 13, "downstream": 7, "dpx": [9, 16], "draw_horiz_band": 4, "drawbox": [7, 9], "drawtext": [9, 16], "drop": [7, 13], "drop_fram": 15, "dshow": [6, 9, 16], "dtype": [3, 13, 15], "dtype_in": [3, 13], "dummi": 6, "dup_fram": 15, "dupe": 7, "duplic": [3, 7], "durat": [2, 3, 5, 13, 14, 15], "dure": [1, 3, 4, 7], "dynam": [2, 4, 7], "dynamic_rang": 2, "e": [0, 1, 2, 3, 5, 6, 7, 10, 13, 14, 15], "ea": [9, 16], "each": [0, 1, 2, 3, 4, 5, 6, 7, 13, 15], "easier": [9, 16], "echo": 3, "ecosystem": 15, "ed": 4, "edg": 13, "edgedetect": [9, 16], "edit": 4, "effect": [7, 9, 16], "either": [2, 3, 5, 7, 9, 15], "element": [0, 2, 3, 7, 13, 15], "elimin": [9, 16], "els": [2, 7, 9, 13, 16], "emploi": 4, "empti": [5, 7], "enabl": [2, 4, 6, 8, 9, 10, 13, 15], "encod": [1, 3, 4, 6, 13, 15], "encoder_info": 4, "end": [2, 3, 5, 7, 13, 14, 15], "end_fram": [7, 9], "end_offset": 14, "ensur": 3, "enter": 13, "entri": [0, 2, 4, 14], "entropi": 2, "enum": [4, 6], "environment": 3, "eof_act": [7, 9], "equal": [9, 16], "equival": 3, "error": [3, 7], "especi": [6, 10, 15], "even": [3, 6, 7, 13], "everi": [1, 2], "ex": 10, "exact": 7, "exampl": [3, 5, 6, 10], "except": [0, 1, 2, 3, 7, 13, 14], "exclude_chain": 7, "exclude_indic": 7, "exclude_stream_spec": 7, "exectut": 3, "execut": [1, 3, 4, 8, 9, 10, 15, 16], "exist": [1, 3, 5, 7, 9, 16], "exit": 1, "expand": 7, "expect": 6, "experiment": 4, "explan": 15, "explicit": 7, "explicitli": 5, "expos": 7, "expr": [3, 5, 9, 16], "express": [2, 3, 5, 7, 9, 13], "exr": [9, 16], "extend": 7, "extens": [4, 9, 16], "extern": [9, 10], "extra": [4, 5], "extra_input": 3, "extra_opt": 4, "extradata": 5, "f": [3, 5, 6, 7, 9, 12, 15, 16], "f0f0f0": 15, "f4": [3, 13], "f8": [3, 13], "f_in": [5, 6, 9, 16], "f_out": [9, 16], "factor": [2, 13], "fade": [7, 9, 16], "fail": 7, "fairli": 6, "fals": [1, 2, 3, 4, 5, 6, 7, 14], "familiar": 15, "fashion": 7, "faster": [7, 15], "featur": [2, 4, 7, 10, 14], "feed": [6, 7, 12], "feedback": 6, "ff": 12, "ffa": 2, "ffconcat": [9, 16], "ffconcat_url": 5, "ffdl": 9, "ffmepeg": 9, "ffmpeg": [3, 5, 6, 11, 12, 15], "ffmpeg_arg": 1, "ffmpeg_download": 8, "ffmpeg_info": 3, "ffmpeg_path": 3, "ffmpegio": [0, 3, 4, 5, 6, 7, 10, 12, 13, 14, 15], "ffmpegprocess": [0, 3, 5, 7, 9, 16], "ffprobe": [3, 8, 10, 11, 14, 15], "ffprobe_path": 3, "fg": [3, 5, 7], "fg1": 7, "fg_overlai": 7, "fgb": [7, 9, 12], "fglink": 7, "field": [2, 4, 14, 15], "fig": 12, "figur": 9, "file": [0, 1, 2, 3, 4, 6, 7, 10, 12, 14, 15], "file_id": 7, "file_offset": 5, "fileitem": 5, "filenam": [5, 14, 15], "filepath": 5, "filesffmpeg": 3, "fill": [7, 9, 13, 16], "fill_color": [9, 13, 15, 16], "filter": [0, 3, 4, 5, 11, 12, 13, 15], "filter_arg": 7, "filter_complex": [0, 5, 7, 9, 13, 16], "filter_complex_script": 7, "filter_fill_valu": 7, "filter_id": 7, "filter_id_omitt": 7, "filter_info": [4, 7], "filter_nam": [2, 7], "filter_script": 7, "filter_spec": 7, "filterchain": 7, "filtergraph": [2, 3, 5, 12, 13, 15, 16], "filtergraphconversionerror": 7, "filtergraphinvalidexpress": 7, "filtergraphinvalidindex": 7, "filtergraphobject": 7, "filtergraphpadnotfounderror": 7, "filterinfo": 4, "filteropt": 4, "filterpadmediatypemismatch": 7, "filtersummari": 4, "fin": [9, 16], "final": [2, 3, 7], "find": [10, 15], "finder": 3, "finer": 1, "finicki": 7, "first": [0, 1, 2, 3, 5, 7, 9, 10, 12, 13, 14, 15, 16], "flac": [3, 9, 15, 16], "flag": [2, 7], "flat": 2, "flat_factor": 2, "flattop": 2, "flip": 13, "float": [1, 2, 3, 7, 9, 13, 14, 15, 16], "floor": 2, "flt": [3, 7, 13, 15], "fltp": 15, "flux": 2, "fly": 15, "focu": 5, "folder": [3, 5, 10], "follow": [2, 3, 4, 6, 7, 9, 10, 13, 14, 15, 16], "fontfil": [9, 16], "fontsiz": [9, 16], "forc": [0, 3, 7], "force_link": 7, "force_original_aspect_ratio": 7, "form": [2, 14], "format": [0, 3, 4, 7, 8, 9, 12, 14, 16], "format_bas": [14, 15], "format_info": 15, "format_nam": [14, 15], "found": [2, 3, 7, 9, 10, 16], "four": 15, "fout": [9, 16], "fp": [0, 7, 15], "fraction": [3, 4, 14, 15], "frame": [0, 1, 3, 4, 6, 7, 12, 14], "frame_mt": 4, "frame_r": [14, 15, 16], "framer": [7, 12], "framework": [8, 9, 16], "freeserif": [9, 16], "freez": 2, "frequenc": 2, "friendli": 15, "from": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 13, 14, 15, 16], "from_left": 7, "from_right": 7, "frozen": 2, "fs_in": [9, 16], "fs_out": [9, 16], "full": [0, 2, 3, 7, 8, 9, 13, 14, 15, 16], "full_detail": [14, 15], "full_pad_index": 7, "fulli": [0, 7], "func": 7, "function": [1, 2, 6, 7, 9, 13, 15, 16], "further": 7, "furthermor": 13, "fv": 7, "g": [0, 1, 2, 3, 6, 7, 9, 10, 13, 15, 16], "gauss": 2, "gener": [3, 4, 5, 9, 16], "get": [2, 3, 4, 7, 9, 14, 15, 16], "get_input_pad": 7, "get_label": 7, "get_num_chain": 7, "get_num_filt": 7, "get_num_input": 7, "get_num_output": 7, "get_num_pad": 7, "get_output_pad": 7, "get_path": 3, "getcwd": 10, "gif": 12, "github": [6, 9, 11, 16], "given": [0, 1, 3, 5, 6, 7, 9, 14], "glob": 5, "global": [0, 3, 4, 7, 13], "global_opt": [0, 5], "goe": 13, "good": [2, 3], "grai": [3, 7, 9, 13, 15, 16], "graph": [3, 9, 16], "graphlink": 7, "gray10l": [3, 13], "gray12l": [3, 13], "gray14l": [3, 13], "gray16l": [3, 13], "grayf32l": [3, 13], "grayscal": [3, 9, 13, 15, 16], "great": 7, "grid": [9, 16], "grow": 15, "guarante": 7, "gui": 15, "h": [2, 4, 7, 9, 13, 16], "h1": 7, "h2": 7, "h264": 15, "ha": [4, 7, 13, 15, 16], "hain": 7, "half": 7, "halv": 15, "ham": 2, "han": 2, "handl": [7, 8, 9, 13, 15, 16], "hann": 2, "happen": 7, "hardwar": 4, "harri": 2, "have": [2, 7, 10, 13, 15], "height": [2, 3, 7, 9, 13, 14, 15, 16], "hello": [9, 16], "help": [4, 7], "helper": 7, "here": [0, 2, 3, 7, 9, 12, 15, 16], "hex": 4, "hexadecim": 5, "hflip": [7, 9, 13, 16], "hide_bann": 1, "high": [2, 6], "hit": 7, "home": 7, "homebrew": 8, "hook": [3, 6], "horizont": 13, "hous": 3, "how": [0, 3, 7], "howev": [13, 15], "hstack": [7, 9, 16], "html": [3, 12], "http": [3, 12], "hue": 0, "hw_accel": 4, "hz": [9, 16], "i": [0, 1, 2, 4, 5, 7, 8, 10, 12, 13, 14, 15], "i2": [3, 13], "i4": [3, 13], "i_in": [9, 16], "i_out": [9, 16], "id": [1, 2, 5, 7], "ident": 9, "identifi": 13, "ignor": [1, 2, 3, 7, 13, 14, 15], "ih": [7, 9, 16], "illustr": 7, "im1": [9, 16], "im24": [9, 16], "im8": [9, 16], "imag": 3, "image2": [9, 16], "immedi": [9, 16], "immut": 7, "implement": [3, 6], "implicit": 3, "implicitli": 3, "import": [2, 6, 7, 9, 12, 15, 16], "improv": 7, "in1": 7, "in2": 7, "in3": 7, "in4": 7, "in_h": 7, "in_nam": 7, "in_pad": 7, "in_typ": 7, "in_w": 7, "includ": [0, 1, 2, 3, 5, 7, 9, 16], "include_connect": 7, "include_input_stream": 7, "incorpor": 7, "incorrect": 7, "increas": 7, "indefinit": 3, "independ": [10, 16], "index": [14, 15], "index_or_label": 7, "indexerror": 7, "indic": [4, 6, 7], "indices_or_label": 7, "indirectli": 1, "individu": [2, 5, 6], "ineffici": 15, "inf": 2, "info": [2, 4, 9, 14, 15, 16], "inform": [2, 3, 9, 14, 15, 16], "initi": [1, 6, 7, 9, 16], "inject": 2, "inpad": 7, "inplac": 7, "inpoint": 5, "input": [0, 1, 2, 3, 4, 5, 6, 7, 9, 13, 15, 16], "input1": 7, "input2": 7, "input3": 7, "input_file_opt": 0, "input_opt": 2, "input_r": 3, "input_url": 0, "insert": [7, 13], "insid": [5, 7], "instal": [7, 8, 16], "instanc": [5, 7, 12], "instanti": 7, "instead": [1, 3, 5, 7, 12, 14, 15, 16], "instruct": 15, "int": [1, 2, 3, 4, 5, 7, 13, 14], "int16": 15, "int_max": 2, "intact": 10, "intead": 9, "integ": [2, 3, 7, 15], "integr": 7, "intend": [5, 6, 7], "inter": 7, "interact": [6, 8, 9, 10, 16], "interchain": 7, "interchang": 7, "interfac": [2, 6, 9, 12], "interleav": 13, "intern": [7, 10], "interpret": [3, 7], "interv": [2, 12, 14], "intervalspec": 14, "intev": 2, "intra": [4, 7], "intra_frame_onli": 4, "invalid": 7, "invalidfilterpadid": 7, "invalidnam": 7, "invalidopt": 7, "invoc": 0, "invok": [0, 1, 5, 15], "involv": [6, 7], "is_chain_siso": 7, "is_input": 7, "is_last_filt": 7, "is_lossi": 4, "is_lossless": 4, "is_readi": 3, "issu": 6, "item": [2, 3, 4, 5, 7, 13], "iter": [7, 15], "iter_chain": 7, "iter_input_label": 7, "iter_input_pad": 7, "iter_output_label": 7, "iter_output_pad": 7, "its": [0, 1, 2, 3, 4, 7, 9, 12, 13, 14, 15, 16], "itself": 7, "iw": [7, 9, 16], "j": 7, "j2c": [9, 16], "j2k": [9, 16], "jl": [9, 16], "john": 10, "join": 13, "jp2": [9, 16], "jpeg": [9, 16], "jpg": [9, 16], "json": [9, 16], "just": [3, 5], "k": 7, "kb": 7, "kbit": 0, "keep": [7, 10, 14], "keep_aspect": 7, "keep_optional_field": 14, "keep_str_valu": 14, "kei": [0, 1, 2, 3, 4, 5, 6, 7, 9, 14, 16], "keywod": 12, "keyword": [1, 2, 3, 7, 12, 13, 14], "khz": [9, 16], "kill": [1, 9, 16], "kill_monitor": 1, "know": 15, "kurtosi": 2, "kwarg": 7, "kwd": 3, "kwopt": 7, "l": 7, "l0": [7, 9], "l1": [7, 9, 16], "l2": [7, 9], "l3": [7, 9], "l4": 7, "l5": 7, "l_idx": 7, "label": [0, 5], "label1": 7, "label2": 7, "label3": 7, "lanczo": 2, "larger": 7, "last": [5, 7, 15], "last_fil": 5, "last_stream": 5, "later": [0, 2, 7, 8], "latter": [7, 8, 10], "lavfi": 2, "layout": [4, 7], "lead": [0, 15], "least": 7, "leav": [7, 13], "left": [7, 9, 13, 16], "left_objs_label": 7, "left_on": 7, "length": [2, 3, 7], "lengthi": 5, "less": 15, "let": [12, 15], "level": [2, 4], "librari": 3, "library_vers": 3, "libx264": [9, 16], "life": 7, "lifo": 3, "light": [9, 15, 16], "like": [1, 3, 5, 7, 13, 14, 15], "likewis": [6, 15], "limit": 7, "line": [0, 2, 5, 7, 12, 13], "linear": 2, "linen": 15, "link": [2, 3, 10], "linkn": 7, "linux": [6, 10], "list": [0, 1, 2, 3, 5, 6, 7, 9, 13, 15, 16], "list_devic": 6, "list_format": 6, "list_opt": 6, "list_sink": 6, "list_sink_opt": 6, "list_sourc": 6, "list_source_opt": 6, "listconcatdemux": 5, "liter": [2, 7], "ljpg": [9, 16], "load": 5, "local": 10, "localappdata": 10, "locat": [3, 5, 10, 15], "log": [1, 2, 3, 9, 16], "logo": [13, 15], "long": [0, 4, 9, 13, 15, 16], "long_nam": 4, "longer": 9, "look": [9, 16], "lossi": 4, "lossless": 4, "low": 2, "lumin": 2, "m": [2, 3, 8], "m2v": 0, "mac": 6, "maco": [6, 8, 10], "made": 7, "mafd": 2, "mai": [0, 1, 3, 5, 7, 15], "mainli": 7, "maintain": 7, "make": [3, 7, 9, 13, 16], "manag": [9, 16], "mani": [5, 13], "maninpul": 13, "manipul": [7, 8, 9, 16], "map": [0, 7, 9, 16], "margin": [9, 16], "mark": 15, "match": [3, 5, 7], "mate": 7, "matplotlib": 9, "max": [2, 6, 15], "max_differ": 2, "max_level": 2, "maxima": 2, "maximum": 2, "mean": 2, "mean_differ": 2, "meaning": 6, "meanwhil": 7, "measure_overal": 2, "measure_perchannel": 2, "mechan": [7, 8], "media": [2, 3, 4, 6, 7], "media_typ": [2, 7], "merg": 7, "messag": [1, 2, 3], "meta_nam": 2, "metadata": 5, "metadatalogg": 2, "method": [2, 3, 4, 7, 12], "microphon": [6, 9, 16], "millisecond": 12, "mime": 4, "mime_typ": 4, "mimic": 15, "min": [2, 6, 15], "min_differ": 2, "min_level": 2, "min_val": 2, "minimum": 2, "minut": [9, 16], "mirror": [9, 16], "mismatch": 7, "miss": [13, 14], "mix": [7, 15], "mkv": [0, 5, 9, 16], "mock": 1, "mode": [3, 5, 9, 12, 13, 15], "modifi": [4, 6, 7], "modul": [4, 6, 7, 12, 14, 15], "monitor": [1, 15], "mono": [2, 9, 15, 16], "mono_interv": 2, "more": [3, 5, 7, 9, 10, 15, 16], "moreov": [6, 7], "most": [7, 8, 9, 16], "mov": 0, "mp2": 15, "mp3": [7, 9, 15, 16], "mp4": [0, 2, 3, 6, 7, 9, 12, 15, 16], "mpegt": 15, "mpg": [9, 15, 16], "mpl": 9, "mtype": 6, "much": 3, "multi": [3, 6, 7], "multimedia": [3, 8, 9, 15, 16], "multipl": [0, 2, 3, 6, 7, 13, 14], "multipli": 7, "multithread": 4, "must": [1, 2, 3, 5, 6, 7, 8, 9, 10, 12, 13, 15, 16], "muxer": 4, "muxer_info": [4, 9, 16], "my_processor": 15, "myaudio": [9, 15, 16], "myimag": [9, 15, 16], "mymedia": [9, 16], "myoutput": [9, 15, 16], "myprocess": [9, 16], "mytestfil": 15, "mytestvideo": 15, "myvideo": [9, 15, 16], "n": [2, 12, 13, 14], "n_link": 7, "name": [2, 3, 4, 5, 6, 7, 9, 12, 13, 14, 15, 16], "name_onli": 4, "namedtupl": [2, 4], "nan": 2, "narrow": 14, "nativ": [9, 15, 16], "nb_compon": 4, "nb_frame": [9, 14, 16], "nb_sampl": [14, 15], "nb_stream": [14, 15], "nchannel": [9, 16], "ncomp": [3, 9, 13, 16], "ncompon": 3, "ndarrai": [15, 16], "nearli": 3, "necessari": [5, 13], "need": [7, 9, 10, 12, 13, 15, 16], "neg": [7, 13], "neither": 7, "nest": [1, 6, 7], "new": [1, 5, 6, 7, 9, 12, 15, 16], "new_label": 7, "newer": 6, "next": [2, 7, 15], "next_input_pad": 7, "next_output_pad": 7, "nframe": [9, 16], "nois": 2, "noise_floor": 2, "noise_floor_count": 2, "non": [3, 9, 13, 16], "none": [0, 1, 2, 3, 4, 5, 6, 7, 9, 13, 14, 15, 16], "none_ok": 7, "nor": 7, "note": [1, 3, 5, 7, 13, 14, 15], "now": 15, "np": 12, "nsampl": [9, 16], "num_input": 4, "num_output": 4, "number": [2, 3, 4, 5, 6, 7, 9, 12, 13, 14, 15], "number_of_denorm": 2, "number_of_inf": 2, "number_of_nan": 2, "number_of_sampl": 2, "numer": [2, 5, 13, 14], "numpi": [3, 9, 12, 13, 15], "nuttal": 2, "o": [1, 7, 10, 13, 15], "obj": 7, "object": [0, 1, 2, 3, 5, 7, 9, 12, 14, 15], "obtain": [5, 6, 13, 15], "occurr": 7, "off": 1, "offer": [1, 3, 13, 15], "offici": [7, 8], "offset": [2, 14], "old_label": 7, "omiss": 3, "omit": [0, 7, 9, 16], "on_exit": 1, "onc": [6, 7, 8, 15], "one": [2, 7, 10, 13], "onli": [0, 2, 3, 4, 6, 7, 9, 13, 15, 16], "onto": 3, "opaqu": 13, "open": [1, 3, 5, 6, 8, 9, 12, 13, 15, 16], "oper": [3, 5, 7, 9, 13, 15, 16], "opt": [6, 7], "optim": 12, "option": [0, 1, 3, 4, 5, 6, 7, 9, 10, 12, 14, 16], "orang": [9, 16], "order": [2, 3, 5, 7, 13], "org": [3, 12], "orient": 7, "origin": 15, "other": [1, 2, 3, 7, 10, 12, 13, 15], "other_popen_arg": 1, "other_popen_kwarg": 1, "other_run_kwarg": 1, "our": [9, 16], "out": [2, 4, 5, 7, 9, 15, 16], "out0": [9, 16], "out1": [0, 9, 16], "out2": 0, "out3": 0, "out_h": 7, "out_nam": 7, "out_pad": 7, "out_phase_interv": 2, "out_tim": 15, "out_time_m": 15, "out_time_u": 15, "out_typ": 7, "out_w": 7, "outcom": [2, 7, 13], "outpad": 7, "outpoint": 5, "output": [0, 1, 2, 3, 4, 5, 6, 7, 9, 12, 14, 16], "output_file_opt": 0, "output_url": 0, "outsid": 8, "outv": 0, "over": [2, 3, 7, 14, 15], "overal": 2, "overlai": [0, 7, 9, 13], "overlap": 2, "overrid": 7, "overwrit": [1, 3, 5, 9, 16], "own": [2, 13], "p": 12, "packag": [7, 8, 9, 10, 15, 16], "packet": [5, 14], "pad": [4, 9, 13, 16], "pad_fill_valu": 7, "pad_id_omitt": 7, "pad_in_us": 7, "pad_index": 7, "page": [0, 1, 5, 7, 9, 10, 11, 15, 16], "pair": [2, 3, 5, 7, 15], "palet": 4, "palett": 12, "palettegen": 12, "paletteus": 12, "pallett": 12, "pam": [9, 16], "pan": 15, "parallel": 7, "param": [2, 7], "paramet": [1, 3, 4, 5, 6, 7, 14], "parent": 3, "pars": [4, 5, 7], "parser": 5, "part": [2, 6], "parzen": 2, "pass": [0, 1, 2, 3, 5, 7, 9, 13, 14, 16], "pass1_extra": [1, 3], "pass1_omit": [1, 3], "passthrough": 7, "path": [3, 5, 7, 8, 10], "pathnam": 3, "pattern": 5, "pblack": 2, "pbm": [9, 16], "pcx": [9, 16], "peak": 2, "peak_count": 2, "peak_level": 2, "per": [1, 2, 3, 4, 7], "per_chain": 7, "percent": 15, "percentag": 2, "perform": [1, 3, 7, 8, 9, 13, 15, 16], "pfm": [9, 16], "pgm": [9, 16], "pgmyuv": [9, 16], "phase": 2, "pi": 12, "pic_th": 2, "pictur": 2, "picture_black_ratio_th": 2, "pip": [8, 9, 10, 15, 16], "pipe": [1, 3, 5, 7, 9, 16], "pipe_url": 5, "pix": [9, 16], "pix_fmt": [3, 4, 9, 12, 14, 16], "pix_th": 2, "pixel": [2, 3, 4, 7, 9, 12, 15, 16], "pixel_black_th": 2, "place": [0, 7, 10], "plain": [0, 2, 7, 9, 15, 16], "planar": 13, "plane": 2, "platform": [8, 9, 10, 16], "pleas": [6, 15], "plethora": 7, "plot": [2, 12], "plt": [2, 12], "plu": 2, "plugin": [3, 6, 9], "png": [9, 13, 15, 16], "point": [3, 5, 7, 13, 14, 15], "poisson": 2, "policti": 7, "pool": 2, "pop": 7, "popen": [0, 1, 3, 5, 7], "popul": 5, "posit": [2, 7, 13], "posix": [1, 7], "possibl": [2, 4, 7, 15], "possibli": 6, "post": 6, "potenti": 15, "ppm": [9, 16], "pprint": [9, 15, 16], "practic": 3, "pre": [9, 10, 13, 16], "preced": [3, 7], "precis": 15, "predefin": 15, "prefer": 7, "prepar": 5, "preproc": 7, "preprocessor": 7, "prescrib": 7, "presenc": 3, "present": [2, 7, 14], "preserv": 15, "preserve_label": 7, "preset": [9, 16], "prestag": 7, "previou": [2, 7, 14], "previous": 7, "primari": [2, 7, 12], "primarili": [5, 7], "print": [2, 6, 9, 15, 16], "prior": [3, 9], "privat": 2, "probe": [3, 5, 9, 16], "process": [1, 2, 3, 6, 9, 14, 15, 16], "process_fram": [9, 16], "produc": [7, 15], "program": [3, 8, 14, 15], "programfil": 10, "programmat": 7, "progress": [1, 2, 3], "progress_callback": 15, "project": 11, "properli": 3, "properti": [2, 5, 15, 16], "proport": 13, "proportion": [9, 16], "protocol": [4, 5], "protocol_whitelist_in": 5, "provid": [6, 7, 12], "prowess": 15, "psnr": 2, "pt": 2, "pure": [2, 9, 16], "purpos": [7, 13], "py": 7, "pyinstal": 10, "pypi": 11, "pyplot": 12, "python": [0, 1, 2, 7, 8, 10, 13, 15], "qualiti": 13, "queri": [6, 14, 15], "quickli": 15, "r": [0, 3, 7, 9, 13, 16], "r_in": 3, "ra": [3, 9, 16], "radiu": 2, "rais": [1, 3, 7, 13], "rang": [2, 4, 6, 7, 12], "rate": [0, 2, 3, 4, 7, 9, 13, 15, 16], "rate_in": 3, "rather": [5, 7, 12], "ratio": [2, 7], "rattach": 7, "raw": 0, "rawvideo": [9, 16], "rconnect": 7, "rd": 3, "read": [3, 6, 7, 8, 13, 14], "read_interv": 14, "readabl": 1, "reader": [3, 9, 15], "realtek": 6, "receiv": [1, 7, 9], "recent": 7, "reciproc": 15, "recogn": 4, "recommend": 7, "record": [9, 15, 16], "rect": 2, "rectangular": 2, "recurs": 5, "red": [7, 9], "redirect": 1, "reduc": [7, 15], "ref_in": 2, "refer": [0, 15], "reflect": 6, "regist": 6, "rel": [3, 5], "relationship": 13, "remain": 4, "rememb": 15, "remov": [1, 3, 7, 15], "remove_alpha": 13, "remove_label": 7, "renam": 7, "rename_label": 7, "repeat": [7, 9], "replac": 7, "replace_sws_flag": 7, "report": 15, "repositori": 11, "repres": [13, 15], "represetn": 15, "requir": [0, 3, 6, 7, 9, 13], "reset": 2, "resiz": 13, "resolv": [6, 7], "resolve_indic": 7, "resolve_omit": 7, "resolve_pad_index": 7, "resolve_pad_indic": 7, "resolve_sink": 6, "resolve_sourc": 6, "respect": [13, 15], "rest": 7, "restrict": 7, "result": [2, 5, 6, 7, 13], "retri": 1, "retriev": [2, 3, 7, 14, 15], "return": [1, 2, 3, 4, 5, 6, 7, 9, 12, 14, 15, 16], "return_desc": 4, "return_nest": 6, "returncod": 1, "reus": 7, "revers": 7, "rgb": [3, 4, 9, 12, 13, 15, 16], "rgb24": [3, 9, 13, 15, 16], "rgb48le": [3, 13], "rgba": [3, 9, 13, 15, 16], "rgba64l": [3, 13], "right": [7, 9, 13, 16], "right_objs_label": 7, "right_on": 7, "rjoin": 7, "rm": 2, "rms_differ": 2, "rms_differenc": 2, "rms_level": 2, "rms_peak": 2, "rms_trough": 2, "roam": 10, "role": 12, "rolloff": 2, "root": 5, "root_dir": 5, "routin": 13, "rparam": 1, "rtype": 7, "rule": [7, 13], "run": [0, 1, 2, 3, 5, 6, 7, 8, 13, 14, 15], "run_two_pass": 1, "runner": 7, "runtim": [4, 7], "rv": [3, 9, 15, 16], "s0": 12, "s1": 12, "s16": [3, 13, 15], "s32": [3, 13, 15], "safe": 1, "safe_in": [5, 9, 16], "same": [5, 6, 7, 9, 12, 13, 16], "sampl": [3, 4, 7, 9, 16], "sample_aspect_ratio": [14, 15], "sample_fmt": [3, 4, 7, 9, 14, 16], "sample_r": [3, 7, 14, 15], "sar": 13, "save": [5, 6, 9, 12, 15, 16], "save_count": 12, "sc": 6, "sc_pass": 2, "scale": [2, 7, 9, 13, 15, 16], "scale2ref": 7, "scaler": 7, "scan": 6, "scd": 2, "scene": 2, "scene_all_scor": 2, "scheme": 6, "score": 2, "screen": [8, 12], "script": 5, "script_path": 7, "scripter": [9, 16], "search": [2, 3, 5, 7], "second": [1, 2, 3, 6, 7, 9, 10, 12, 13, 14, 15, 16], "secondari": 2, "section": 7, "see": [0, 1, 2, 3, 7, 9, 13, 14, 15, 16], "seealso": 3, "seek": 14, "seekabl": 14, "select": [0, 1, 3, 6, 7, 14], "select_stream": [9, 14, 16], "self": 1, "send": [1, 5, 15], "send_sign": 1, "separ": [2, 6, 9], "seq": [1, 2, 3, 7, 13, 14], "sequenc": [0, 3, 5, 7, 13, 14], "sequenti": [3, 7], "seri": 7, "serv": 7, "set": [0, 1, 2, 3, 4, 7, 13, 14, 15], "set_path": [3, 10], "set_ydata": 12, "setsar": [7, 13], "sever": [7, 13], "sgi": [9, 16], "shall": [0, 2, 3], "shape": [3, 6, 15], "shape_in": 3, "short": [4, 13, 15], "shorter": 3, "shorthand": 7, "show": [1, 2, 3, 4, 7, 9, 16], "show_chapt": 14, "show_format": 14, "show_fram": [9, 16], "show_log": [2, 3, 9, 16], "show_program": 14, "show_stream": [9, 14, 16], "show_unconnected_input": 7, "show_unconnected_output": 7, "shown": 2, "showspectrum": [9, 16], "showspectrump": [9, 16], "side": [7, 9, 13], "sig": 1, "sigint": 1, "sign": [2, 15], "signal": [1, 2], "signatur": 15, "silenc": 2, "silence_end": 2, "silence_start": 2, "silent": 2, "silentdetect": 2, "silentperch": 2, "similar": 15, "simpl": [13, 15], "simpler": 9, "simplifi": 7, "simultan": [2, 15], "sin": 12, "sinc": 16, "sine": 2, "singl": [1, 2, 3, 7, 14, 15], "sink": [1, 4, 6, 7], "siso": [3, 7], "size": [2, 3, 7, 13], "skew": 2, "skip": 7, "skip_if_no_input": 7, "skip_if_no_output": 7, "slice": 4, "slice_mt": 4, "slice_thread": 4, "slope": 2, "slow": [9, 15, 16], "snapshot": [2, 3], "so": [0, 2, 6, 7, 9, 13, 15, 16], "soft": 1, "solut": [3, 7], "some": [0, 3, 6, 7, 13], "sound": 15, "sourc": [1, 3, 4, 5, 6, 7, 8, 9, 16], "sp_kwarg": [3, 14], "spec": 7, "specfi": 7, "special": 0, "specif": [1, 2, 3, 4, 5, 6, 7, 13, 14, 15], "specifi": [0, 1, 2, 3, 4, 5, 6, 7, 9, 10, 12, 13, 14, 16], "spectral": 2, "spectrogram": [9, 16], "spectrum": 13, "speed": [7, 15], "split": [7, 9, 12, 16], "spread": 2, "squar": [7, 9, 13, 16], "square_pixel": [9, 13, 16], "src": 7, "srt": 0, "ss": [2, 9, 13, 15, 16], "ss_in": [13, 15], "st": [7, 9, 16], "stage": 2, "stamp": 2, "standard": [1, 3], "start": [2, 3, 5, 7, 13, 14], "start_at_zero": 2, "start_fram": [7, 9], "start_offset": 14, "start_tim": [3, 14, 15], "stat": [2, 9, 16], "statement": [5, 7], "static": [2, 3, 9], "statist": 2, "statu": 15, "stderr": [1, 9, 16], "stdin": [1, 4, 5, 7], "stdout": [1, 3, 9, 16], "step": 7, "stereo": [2, 7, 15], "stop": [2, 7], "storag": 2, "store": 2, "str": [0, 1, 2, 3, 4, 5, 6, 7, 13, 14], "stream": [0, 1, 2, 3, 4, 5, 6, 7, 12, 14], "stream_0_0_q": 15, "stream_info": 15, "stream_typ": 4, "streamitem": 5, "streams_bas": [14, 15], "stretch": 13, "strict": 7, "strictli": 13, "string": [0, 3, 4, 5, 7, 9, 12, 13, 16], "stringifi": [2, 5, 13], "stringify": 7, "stringio": 5, "structur": [0, 10, 13], "subclass": 2, "submodul": [7, 12], "subplot": 12, "subprocess": [0, 3, 7, 9, 14, 16], "subset": 2, "subtitl": 4, "subtitle_codec": 4, "sudo": 10, "suggest": 7, "sum": 7, "summari": 4, "sun": [9, 16], "sunra": [9, 16], "super": 7, "suppli": [0, 2, 7], "support": [0, 4, 5, 7, 8, 9, 12, 13, 15, 16], "supported_codec": 4, "supported_framer": 4, "supported_hwdevic": 4, "supported_layout": 4, "supported_pix_fmt": 4, "supported_sample_fmt": 4, "supported_sample_r": 4, "suppos": [7, 15], "sure": [3, 7, 13], "switch": 9, "sws_flag": 7, "swscale": 7, "system": [3, 6, 8, 9, 10, 15, 16], "t": [1, 2, 3, 7, 9, 13, 15, 16], "t_in": [6, 15], "tabl": [2, 9], "tailor": 14, "take": [0, 1, 3, 7, 9, 15, 16], "target": [2, 3], "tarnspos": 13, "task": 15, "tb": 7, "tbd": [0, 6], "technic": 7, "temp": 5, "tempdir": 5, "temporari": [5, 7, 9, 16], "termin": [1, 3, 8, 15], "test": 3, "text": [4, 5, 7, 9, 16], "text_h": [9, 16], "text_w": [9, 16], "tga": [9, 16], "than": [3, 5, 7, 9, 12], "thei": [2, 3, 7, 10], "them": [0, 2, 7, 9, 10, 16], "thereof": 7, "thi": [1, 2, 3, 4, 6, 7, 8, 12, 13, 14, 15], "thing": 15, "those": [3, 6, 7], "thread": [1, 3, 4], "three": 7, "thresh": 2, "threshold": 2, "through": [5, 14], "throw": 7, "thu": 15, "tif": [9, 16], "tiff": [9, 16], "tile": [9, 16], "time": [0, 2, 3, 5, 7, 9, 13, 14, 16], "time_unit": 2, "timebas": 14, "timelin": [2, 4], "timeline_support": 4, "timeout": [1, 3], "timeoutexpir": 1, "timestamp": [2, 9, 14, 16], "tmp": [9, 16], "to_in": 15, "to_left": 7, "to_right": 7, "todai": [8, 9, 16], "togeth": 13, "toler": 2, "too": 7, "top": [2, 7, 13], "total": 15, "total_s": 15, "traceback": 7, "trail": 7, "transact": 1, "transcod": [3, 5, 6, 7, 15], "transmit": 7, "transpar": [3, 9, 13, 15, 16], "transpos": 13, "treat": [2, 3], "tri": 7, "trim": [7, 9], "trivial": 7, "trough": 2, "true": [1, 2, 3, 4, 5, 6, 7, 9, 14, 15, 16], "truncat": 3, "try": 3, "ttf": [9, 16], "tukei": 2, "tupl": [0, 2, 3, 4, 5, 6, 7, 14, 15], "turn": 1, "twice": 1, "two": [1, 3, 4, 7, 10, 13, 15], "two_pass": [3, 9, 16], "txt": 5, "type": [0, 1, 2, 3, 4, 5, 6, 7, 9, 13, 16], "typic": 7, "u": [8, 14], "u1": [3, 13], "u2": [3, 13], "u8": [3, 13, 15], "ubuntu": 10, "uint8": 15, "unc": 7, "unc0": [7, 9], "unc1": 7, "unc2": 7, "unc3": 7, "unc4": 7, "unc5": 7, "uncheck": 0, "unconnect": 7, "underli": 2, "unifi": 6, "union": 14, "uniqu": 7, "unit": [2, 15], "universal_newlin": [9, 16], "unlabel": [0, 7, 13], "unlabeled_onli": 7, "unless": 7, "unlik": [7, 14], "unlink": 7, "unnam": 7, "unpars": 4, "unsign": 15, "unsupport": [6, 7], "until": [1, 6], "unus": 7, "unzip": 10, "up": [0, 9, 13, 15, 16], "updat": [5, 7, 8, 9, 12, 16], "upscal": [9, 13, 16], "upscale_even": 13, "upstream": 7, "url": [0, 1, 2, 3, 5, 6, 7, 13, 14, 15], "url_fg": 3, "us": [0, 1, 2, 3, 4, 5, 7, 9, 10, 12, 13, 15, 16], "usecas": 7, "user": [6, 7, 9, 10, 15, 16], "userprofil": 10, "v": [0, 3, 5, 6, 7, 9, 13, 16], "v0": [7, 9, 16], "v1": [7, 9], "v1_scale": 7, "v2": [7, 9], "v3": [7, 9], "v5": [2, 7, 9], "valid": [0, 3, 5, 7], "validate_pad_idx": 7, "valu": [0, 4, 5, 6, 7, 13, 14], "valueerror": 7, "variabl": [3, 4, 6], "varianc": 2, "vcodec": [9, 16], "ve": 15, "version": [3, 7], "vertic": 13, "vf": [0, 7, 9, 12, 13, 15, 16], "vflip": [7, 13], "vframe": [9, 16], "via": [0, 3, 5, 6, 7, 9, 10, 12, 15, 16], "video": [0, 1, 2, 3, 4, 5, 6, 7, 14], "video0": 6, "video1": [5, 9, 16], "video2": [5, 9, 16], "video4linux2": 6, "video_byt": 3, "video_codec": 4, "video_dst": 3, "video_filt": 7, "video_info": 3, "video_sourc": 3, "video_stream_bas": 14, "video_streams_bas": [14, 15], "virtual": 15, "visit": [9, 16], "vout": 7, "vout1": 7, "vout2": 7, "vr": 6, "vst_info": 15, "vstack": 7, "vv": 3, "w": [2, 3, 4, 7, 9, 16], "wa": [1, 3, 7], "wai": [7, 14], "wait": 1, "want": [2, 6, 7, 15], "wav": [0, 9, 15, 16], "we": [7, 12], "webcam": [6, 9, 16], "weight": [9, 16], "welch": 2, "well": [3, 7, 9, 10, 15, 16], "were": [7, 9], "what": [7, 15], "when": [1, 2, 3, 5, 7, 10, 15], "whenev": [9, 16], "where": [2, 5, 7], "which": [0, 1, 2, 3, 4, 6, 7, 8, 9, 13, 14, 15, 16], "while": [1, 3, 7, 12, 13, 15], "white": [13, 15], "width": [2, 3, 7, 9, 13, 14, 15, 16], "wiki": [5, 7], "win_siz": 2, "window": [1, 2, 3, 6, 7, 8, 9, 10, 16], "wire": 13, "wise": 3, "with_traceback": 7, "within": [2, 5, 6, 15], "without": [0, 1, 7, 13, 15], "work": [3, 7, 10], "world": [9, 16], "wr": 3, "wrap": [4, 15], "wrapper": 14, "writabl": [1, 5], "write": [3, 7, 8, 12, 13], "writer": 9, "written": 6, "wv": [9, 12, 15, 16], "wxh": 13, "x": [2, 3, 6, 7, 9, 12, 13, 15, 16], "x0": 2, "x1": 2, "x86": 10, "xbm": [9, 16], "xface": [9, 16], "xwd": [9, 16], "y": [7, 9, 15, 16], "ya16l": [3, 13], "ya8": [3, 9, 13, 15, 16], "yet": 7, "yield": [7, 15], "you": [1, 2, 6, 7, 12, 13, 15], "your": [9, 13, 15], "yuv420p": [7, 12, 15], "yuv444p": 12, "zero": [2, 5], "zero_cross": 2, "zero_crossings_r": 2, "zoom": 7}, "titles": ["Specification of FFmpeg Argument dict ffmpeg_args", "ffmpegio.ffmpegprocess: Direct invocation of FFmpeg subprocess", "ffmpegio.analyze: Frame Metadata Analysis Module", "Basic I/O Function References", "FFmpeg Capabilities References", "FFConcat Class: Concatenating Media Files", "Hardware I/O Device Enumeration", "Filtergraph Builder Reference", "ffmpegio-plugin-downloader: An ffmpegio plugin to download latest FFmpeg release binaries", "ffmpegio-core: Media I/O with FFmpeg in Python", "Installation", "External Links", "Creating Videos from Matplotlib figure", "FFmpeg Option References", "Media Probe Function References", "Quick Start Guide", "ffmpegio: Media I/O with FFmpeg in Python (with NumPy Array Plugin)"], "titleterms": {"": 13, "access": 7, "an": 8, "analysi": 2, "analyz": 2, "aphasemet": 2, "api": [2, 7], "argument": [0, 14], "arrai": 16, "aspectralstat": 2, "astat": 2, "audio": [9, 13, 15, 16], "avail": 2, "basic": 3, "bbox": 2, "binari": 8, "blackdetect": 2, "blackfram": 2, "block": 15, "blurdetect": 2, "builder": [7, 9], "built": 13, "callback": [9, 15, 16], "capabl": 4, "captur": [9, 16], "chang": 15, "class": [0, 5], "code": 10, "common": 13, "concat": 7, "concaten": 5, "constant": 4, "construct": 7, "core": 9, "creat": 12, "current": 6, "data": [9, 16], "devic": [6, 9, 16], "dict": 0, "direct": 1, "directli": [9, 16], "document": [9, 16], "download": [8, 10], "enumer": [6, 9, 16], "exampl": [0, 2, 7, 9, 12, 13, 15, 16], "extern": 11, "extrem": 7, "featur": [9, 15, 16], "ffconcat": 5, "ffmpeg": [0, 1, 2, 4, 7, 8, 9, 10, 13, 16], "ffmpeg_arg": 0, "ffmpegio": [1, 2, 8, 9, 16], "ffmpegprocess": 1, "ffprobe": [9, 16], "figur": 12, "file": [5, 9, 16], "filtegraph": 7, "filter": [2, 7, 9, 16], "filtergraph": [0, 7, 9], "format": [13, 15], "frame": [2, 9, 13, 15, 16], "freezedetect": 2, "from": 12, "function": [3, 4, 14], "gener": 7, "graph": 7, "guid": 15, "hardwar": 6, "how": 6, "i": [3, 6, 9, 16], "imag": [9, 13, 15, 16], "index": 7, "inform": 7, "instal": [9, 10, 15], "invoc": 1, "join": 7, "label": 7, "latest": 8, "link": [7, 11], "list": [4, 14], "logger": 2, "long": 7, "main": [9, 16], "manipul": 13, "map": 13, "matplotlib": 12, "measur": 2, "media": [5, 9, 14, 15, 16], "metadata": 2, "modul": [1, 2], "multipl": [9, 16], "n": 7, "numpi": 16, "o": [3, 6, 9, 16], "option": [2, 13, 15], "output": [13, 15], "p2p": 7, "pad": 7, "paramet": 2, "pix_fmt": [13, 15], "pixel": 13, "plugin": [8, 16], "preprocess": 7, "probe": [14, 15], "program": 10, "progress": [9, 15, 16], "python": [9, 16], "quick": 15, "rang": 15, "read": [9, 15, 16], "refer": [1, 2, 3, 4, 6, 7, 13, 14], "releas": 8, "remark": 7, "run": [9, 16], "sampl": [13, 15], "sample_fmt": [13, 15], "scdet": 2, "script": 7, "self": 7, "silencedetect": 2, "simpl": [2, 7], "size": 15, "specif": 0, "specifi": 15, "stack": 7, "stage": 7, "start": 15, "static": 10, "stream": [9, 15, 16], "subprocess": 1, "support": [2, 6], "time": 15, "todo": 4, "transcod": [9, 16], "type": 14, "us": [6, 8], "valu": [2, 15], "video": [9, 12, 13, 15, 16], "win_func": 2, "within": 7, "write": [9, 15, 16]}}) \ No newline at end of file