diff --git a/ffmpeg/_run.py b/ffmpeg/_run.py index f820c1c..85391c2 100644 --- a/ffmpeg/_run.py +++ b/ffmpeg/_run.py @@ -7,6 +7,7 @@ import collections import copy import operator import subprocess +import asyncio from ._ffmpeg import ( input, @@ -296,10 +297,44 @@ def run( return out, err +@output_operator() +async def run_asyncio( + stream_spec, cmd='ffmpeg', pipe_stdin=False, pipe_stdout=False, pipe_stderr=False, + quiet=False, overwrite_output=False): + """Asynchronously invoke ffmpeg in asyncio sync/await style and return coroutine. + Have the same possibilities as `run_async` call. + + Args: + pipe_stdin: if True, connect pipe to subprocess stdin (to be + used with ``pipe:`` ffmpeg inputs). + pipe_stdout: if True, connect pipe to subprocess stdout (to be + used with ``pipe:`` ffmpeg outputs). + pipe_stderr: if True, connect pipe to subprocess stderr. + quiet: shorthand for setting ``capture_stdout`` and + ``capture_stderr``. + + Returns: + A Process instance as a coroutine + """ + + args = compile(stream_spec, cmd, overwrite_output=overwrite_output) + stdin_stream = asyncio.subprocess.PIPE if pipe_stdin else None + stdout_stream = asyncio.subprocess.PIPE if pipe_stdout or quiet else None + stderr_stream = asyncio.subprocess.PIPE if pipe_stderr or quiet else None + + return await asyncio.create_subprocess_exec( + *args, + stdin=stdin_stream, + stdout=stdout_stream, + stderr=stderr_stream + ) + + __all__ = [ 'compile', 'Error', 'get_args', 'run', 'run_async', + 'run_asyncio', ] diff --git a/ffmpeg/tests/test_ffmpeg.py b/ffmpeg/tests/test_ffmpeg.py index b9e5d3a..c7b8242 100644 --- a/ffmpeg/tests/test_ffmpeg.py +++ b/ffmpeg/tests/test_ffmpeg.py @@ -1,4 +1,7 @@ from __future__ import unicode_literals + +import asyncio + from builtins import bytes from builtins import range from builtins import str @@ -655,3 +658,31 @@ def test__probe__exception(): def test__probe__extra_args(): data = ffmpeg.probe(TEST_INPUT_FILE1, show_frames=None) assert set(data.keys()) == {'format', 'streams', 'frames'} + + +def test_run_asyncio(): + async def test_async(): + process = await ( + ffmpeg + .input(TEST_INPUT_FILE1) + .output('pipe:', format='rawvideo', pix_fmt='rgb24')['v'] + .run_asyncio(pipe_stdout=True, quiet=False) + ) + + video_frame_size = 320 * 240 * 3 # Note: RGB24 == 3 bytes per pixel. 320x240 - video size + + total_bytes = 0 + + while True: + frame_bytes = await process.stdout.read(video_frame_size) + if len(frame_bytes) == 0: + break + else: + total_bytes += len(frame_bytes) + + await process.wait() + + assert total_bytes == 48153600, 'Incorrect size of the output frames' + + loop = asyncio.get_event_loop() + loop.run_until_complete(test_async())