Metadata-Version: 2.4
Name: asyncffmpeg
Version: 1.4.0
Summary: Supports async / await pattern for FFmpeg operations.
Author-email: Yukihiko Shinoda <yuk.hik.future@gmail.com>
Maintainer-email: Yukihiko Shinoda <yuk.hik.future@gmail.com>
License: MIT License
        
        Copyright (c) 2021 Yukihiko Shinoda
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
        
Project-URL: homepage, https://github.com/yukihiko-shinoda/asyncffmpeg
Project-URL: repository, https://github.com/yukihiko-shinoda/asyncffmpeg
Keywords: asyncffmpeg,ffmpeg,ffmpeg-python,asyncio,asynchronous,video,audio,image,capture,conversion,streaming,processing
Classifier: Development Status :: 5 - Production/Stable
Classifier: Framework :: AsyncIO
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Information Technology
Classifier: Intended Audience :: Science/Research
Classifier: Intended Audience :: Telecommunications Industry
Classifier: License :: OSI Approved :: MIT License
Classifier: Natural Language :: English
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Multimedia
Classifier: Topic :: Multimedia :: Graphics
Classifier: Topic :: Multimedia :: Graphics :: Capture
Classifier: Topic :: Multimedia :: Graphics :: Graphics Conversion
Classifier: Topic :: Multimedia :: Sound/Audio
Classifier: Topic :: Multimedia :: Sound/Audio :: Capture/Recording
Classifier: Topic :: Multimedia :: Sound/Audio :: Conversion
Classifier: Topic :: Multimedia :: Video
Classifier: Topic :: Multimedia :: Video :: Capture
Classifier: Topic :: Multimedia :: Video :: Conversion
Classifier: Topic :: Scientific/Engineering :: Image Processing
Classifier: Topic :: Scientific/Engineering :: Interface Engine/Protocol Translator
Classifier: Topic :: Software Development
Classifier: Topic :: Software Development :: Libraries
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: System
Classifier: Topic :: System :: Archiving
Classifier: Topic :: System :: Archiving :: Compression
Classifier: Topic :: System :: Archiving :: Packaging
Classifier: Typing :: Typed
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: ffmpeg-python
Requires-Dist: livesubprocess
Requires-Dist: pywin32; sys_platform == "win32"
Dynamic: license-file

# Asynchronous FFmpeg

[![Test](https://github.com/yukihiko-shinoda/asyncffmpeg/workflows/Test/badge.svg)](https://github.com/yukihiko-shinoda/asyncffmpeg/actions?query=workflow%3ATest)
[![CodeQL](https://github.com/yukihiko-shinoda/asyncffmpeg/workflows/CodeQL/badge.svg)](https://github.com/yukihiko-shinoda/asyncffmpeg/actions?query=workflow%3ACodeQL)
[![Code Coverage](https://qlty.sh/gh/yukihiko-shinoda/projects/asyncffmpeg/coverage.svg)](https://qlty.sh/gh/yukihiko-shinoda/projects/asyncffmpeg)
[![Maintainability](https://qlty.sh/gh/yukihiko-shinoda/projects/asyncffmpeg/maintainability.svg)](https://qlty.sh/gh/yukihiko-shinoda/projects/asyncffmpeg)
[![Dependabot](https://flat.badgen.net/github/dependabot/yukihiko-shinoda/asyncffmpeg?icon=dependabot)](https://github.com/yukihiko-shinoda/asyncffmpeg/security/dependabot)
[![Python versions](https://img.shields.io/pypi/pyversions/asyncffmpeg.svg)](https://pypi.org/project/asyncffmpeg)
[![X URL](https://img.shields.io/twitter/url?style=social&url=https%3A%2F%2Fgithub.com%2Fyukihiko-shinoda%2Fasyncffmpeg)](https://x.com/intent/post?text=Asynchronous%20FFmpeg&url=https%3A%2F%2Fpypi.org%2Fproject%2Fasyncffmpeg%2F&hashtags=python)

Supports async / await pattern for FFmpeg operations.

## Advantage

1. Support async / await pattern for FFmpeg operations
2. Support Ctrl + C

### 1. Support async / await pattern for FFmpeg operations

This package supports FFmpeg asynchronously invoke with async / await pattern
wrapping [`ffmpeg.run_async()`] of [ffmpeg-python] and returned [`subprocess.Popen`].

The async / await syntax makes asynchronous code as:

- Simple
- Readable

### 2. Support Ctrl + C

User can stop FFmpeg process gracefully by Ctrl + C.
This works as same as sending `q` key to running FFmpeg.
This action is guaranteed by pytest.

## Quickstart

### 1. Install

```console
pip install asyncffmpeg
```

### 2. Implement

`asyncffmpeg.FFmpegCoroutine` class has asynchronous method: `execute()`.
To run concurrently, it requires not multi threading but multi processing
since FFmpeg process is CPU-bound operation.
The package [`asynccpu`] is helpful to simple implement.

Ex:

```python
import ffmpeg
from asynccpu import ProcessTaskPoolExecutor
from asyncffmpeg import FFmpegCoroutineFactory, StreamSpec


async def create_stream_spec_copy() -> StreamSpec:
    stream = ffmpeg.input("input.mp4")
    return ffmpeg.output(stream, "output1.mp4", c="copy")


async def create_stream_spec_filter() -> StreamSpec:
    stream = ffmpeg.input("input.mp4")
    stream = ffmpeg.filter(stream, "scale", 768, -1)
    return ffmpeg.output(stream, "output2.mp4")


async def main() -> None:
    ffmpeg_coroutine = FFmpegCoroutineFactory.create()

    with ProcessTaskPoolExecutor(max_workers=3, cancel_tasks_when_shutdown=True) as executor:
        awaitables = (
            executor.create_process_task(ffmpeg_coroutine.execute, create_stream_spec)
            for create_stream_spec in [create_stream_spec_copy, create_stream_spec_filter]
        )
        await asyncio.gather(*awaitables)


if __name__ == "__main__":
    asyncio.run(main())
```

#### Why not [`asyncio`] but [`asynccpu`] ?

Unfortunately High-level APIs of [`asyncio`] doesn't support CPU-bound operations
since it works based on not [`ProcessPoolExecutor`] but [`ThreadPoolExecutor`].
When we want to run CPU-bound operations concurrently with [`asyncio`],
we need to use Low-level APIs which need finer control over the event loop behavior.

### Note

The argument of [`Coroutine`] requires not "raw [`Coroutine`] object" but "[`Coroutine`] function"
since raw [`Coroutine`] object is not picklable.

This specification is depend on the one of Python [`multiprocessing`] package:

[multiprocessing — Process-based parallelism]

> Note When an object is put on a queue, the object is pickled
> and a background thread later flushes the pickled data to an underlying pipe.

<!-- markdownlint-disable-next-line no-inline-html -->
See: [Answer: Python multiprocessing PicklingError: Can't pickle <type 'function'> - Stack Overflow]

## API

### FFmpegCoroutineFactory

```python
class FFmpegCoroutineFactory:
    @staticmethod
    def create(
        *,
        time_to_force_termination: int = 8
    ) -> FFmpegCoroutine:
```

#### time_to_force_termination: int = 8

The time limit (second) to wait stopping FFmpeg process gracefully
when send Ctrl + C.
At first, subprocess will try to send `q` key to FFmpeg process.
In case when FFmpeg process doesn't stop gracefully by time limit,
subprocess will terminate process.

### FFmpegCoroutine

```python
class FFmpegCoroutine:
    async def execute(
        self,
        create_stream_spec: Callable[[], Awaitable[StreamSpec]],
        *,
        after_start: Optional[Callable[[FFmpegProcess], Awaitable]] = None
    ) -> None:
```

#### create_stream_spec: Callable[[], Awaitable[StreamSpec]]

[`Coroutine`] function to create [stream spec] for FFmpeg process.
Created [stream spec] will be set the first argument of [`ffmpeg.run_async()`] of [ffmpeg-python] inside of `FFmpegCoroutine`.
[stream spec] is a Stream, list of Streams, or label-to-Stream dictionary mapping
in [ffmpeg-python].

#### after_start: Optional[Callable[[FFmpegProcess], Awaitable]] = None

[`Coroutine`] function to execute after start FFmpeg process.

## Credits

This package was created with [Cookiecutter] and the [yukihiko-shinoda/cookiecutter-pypackage] project template.

[`ffmpeg.run_async()`]: https://kkroening.github.io/ffmpeg-python/#ffmpeg.run_async
[ffmpeg-python]: https://pypi.org/project/ffmpeg-python/
[`subprocess.Popen`]: https://docs.python.org/3/library/subprocess.html#popen-objects
[`asyncio`]: https://docs.python.org/3/library/asyncio.html
[`ProcessPoolExecutor`]: https://docs.python.org/3/library/concurrent.futures.html#processpoolexecutor
[`ThreadPoolExecutor`]: https://docs.python.org/3/library/concurrent.futures.html#threadpoolexecutor
[`asynccpu`]: https://pypi.org/project/asynccpu/
[`Coroutine`]: https://docs.python.org/3/library/asyncio-task.html#coroutines
[`multiprocessing`]: https://docs.python.org/3/library/multiprocessing.html
[multiprocessing — Process-based parallelism]: https://docs.python.org/3/library/multiprocessing.html
<!-- markdownlint-disable-next-line no-inline-html -->
[Answer: Python multiprocessing PicklingError: Can't pickle <type 'function'> - Stack Overflow]: https://stackoverflow.com/a/8805244/12721873
[stream spec]: https://ffmpeg.org/ffmpeg.html#Stream-specifiers-1
[Cookiecutter]: https://github.com/audreyr/cookiecutter
[yukihiko-shinoda/cookiecutter-pypackage]: https://github.com/audreyr/cookiecutter-pypackage
