diff --git a/ffmpeg/_run.py b/ffmpeg/_run.py index c9cbb7c..700a494 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, output from .nodes import ( @@ -326,4 +327,37 @@ def run( return out, err -__all__ = ['compile', 'Error', 'get_args', 'run', 'run_async'] +@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 51ee258..83c79d9 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 @@ -782,3 +785,31 @@ def test__multi_output_edge_label_order(): out1, out2 = get_filter_complex_outputs(flt_cmpl, 'scale2ref') assert out1 == get_filter_complex_input(flt_cmpl, 'scale') assert out2 == get_filter_complex_input(flt_cmpl, 'hflip') + + +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())