Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/yukihiko-shinoda/asyncffmpeg
Supports async / await pattern for FFmpeg operations.
https://github.com/yukihiko-shinoda/asyncffmpeg
asyncio ffmpeg multiprocessing python python3
Last synced: about 2 months ago
JSON representation
Supports async / await pattern for FFmpeg operations.
- Host: GitHub
- URL: https://github.com/yukihiko-shinoda/asyncffmpeg
- Owner: yukihiko-shinoda
- License: mit
- Created: 2021-03-13T13:40:19.000Z (almost 4 years ago)
- Default Branch: main
- Last Pushed: 2021-08-16T08:04:20.000Z (over 3 years ago)
- Last Synced: 2024-04-29T17:49:49.230Z (9 months ago)
- Topics: asyncio, ffmpeg, multiprocessing, python, python3
- Language: Python
- Homepage:
- Size: 328 KB
- Stars: 14
- Watchers: 1
- Forks: 2
- Open Issues: 5
-
Metadata Files:
- Readme: README.md
- Contributing: docs/CONTRIBUTING.md
- License: LICENSE
Awesome Lists containing this project
README
# 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, StreamSpecasync 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.See: [Answer: Python multiprocessing PicklingError: Can't pickle - 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[Answer: Python multiprocessing PicklingError: Can't pickle - 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