Metadata-Version: 2.1
Name: asyncffmpeg
Version: 1.2.0
Summary: Supports async / await pattern for FFmpeg operations.
Home-page: https://github.com/yukihiko-shinoda/asyncffmpeg
Author: Yukihiko Shinoda
Author-email: yuk.hik.future@gmail.com
License: UNKNOWN
Keywords: asyncffmpeg
Platform: UNKNOWN
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: 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

# Asynchronous FFmpeg

[![Test](https://github.com/yukihiko-shinoda/asyncffmpeg/workflows/Test/badge.svg)](https://github.com/yukihiko-shinoda/asyncffmpeg/actions?query=workflow%3ATest)
[![Test Coverage](https://api.codeclimate.com/v1/badges/d0715bdfc5dd7725e0a2/test_coverage)](https://codeclimate.com/github/yukihiko-shinoda/asyncffmpeg/test_coverage)
[![Maintainability](https://api.codeclimate.com/v1/badges/d0715bdfc5dd7725e0a2/maintainability)](https://codeclimate.com/github/yukihiko-shinoda/asyncffmpeg/maintainability)
[![Code Climate technical debt](https://img.shields.io/codeclimate/tech-debt/yukihiko-shinoda/asyncffmpeg)](https://codeclimate.com/github/yukihiko-shinoda/asyncffmpeg)
[![Updates](https://pyup.io/repos/github/yukihiko-shinoda/asyncffmpeg/shield.svg)](https://pyup.io/repos/github/yukihiko-shinoda/asyncffmpeg/)
[![Python versions](https://img.shields.io/pypi/pyversions/asyncffmpeg.svg)](https://pypi.org/project/asyncffmpeg)
[![Twitter URL](https://img.shields.io/twitter/url?style=social&url=https%3A%2F%2Fgithub.com%2Fyukihiko-shinoda%2Fasyncffmpeg)](http://twitter.com/share?text=Asynchronous%20FFmpeg&url=https://pypi.org/project/asyncffmpeg/&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


