diff --git a/examples/get_video_thumbnail.py b/examples/get_video_thumbnail.py index d1e608a..0a006d5 100755 --- a/examples/get_video_thumbnail.py +++ b/examples/get_video_thumbnail.py @@ -22,10 +22,12 @@ def generate_thumbnail(in_filename, out_filename, time, width): .input(in_filename, ss=time) .filter_('scale', width, -1) .output(out_filename, vframes=1) - .run(capture_stdout=True, capture_stderr=True, overwrite_output=True) + .overwrite_output() + .run(capture_stdout=True, capture_stderr=True) ) except ffmpeg.Error as e: print(e.stderr.decode(), file=sys.stderr) + sys.exit(1) if __name__ == '__main__': diff --git a/examples/requirements.txt b/examples/requirements.txt index d5f10fc..ca8ae60 100644 --- a/examples/requirements.txt +++ b/examples/requirements.txt @@ -1,2 +1,4 @@ ffmpeg-python +gevent google-cloud-speech +tqdm diff --git a/examples/show_progress.py b/examples/show_progress.py new file mode 100755 index 0000000..dd0253a --- /dev/null +++ b/examples/show_progress.py @@ -0,0 +1,130 @@ +#!/usr/bin/env python +from __future__ import unicode_literals, print_function +from tqdm import tqdm +import argparse +import contextlib +import ffmpeg +import gevent +import gevent.monkey; gevent.monkey.patch_all(thread=False) +import os +import shutil +import socket +import sys +import tempfile +import textwrap + + +parser = argparse.ArgumentParser(description=textwrap.dedent('''\ + Process video and report and show progress bar. + + This is an example of using the ffmpeg `-progress` option with a + unix-domain socket to report progress in the form of a progress + bar. + + The video processing simply consists of converting the video to + sepia colors, but the same pattern can be applied to other use + cases. +''')) + +parser.add_argument('in_filename', help='Input filename') +parser.add_argument('out_filename', help='Output filename') + + +@contextlib.contextmanager +def _tmpdir_scope(): + tmpdir = tempfile.mkdtemp() + try: + yield tmpdir + finally: + shutil.rmtree(tmpdir) + + +def _do_watch_progress(filename, sock, handler): + """Function to run in a separate gevent greenlet to read progress + events from a unix-domain socket.""" + connection, client_address = sock.accept() + data = b'' + try: + while True: + more_data = connection.recv(16) + if not more_data: + break + data += more_data + lines = data.split(b'\n') + for line in lines[:-1]: + line = line.decode() + parts = line.split('=') + key = parts[0] if len(parts) > 0 else None + value = parts[1] if len(parts) > 1 else None + handler(key, value) + data = lines[-1] + finally: + connection.close() + + +@contextlib.contextmanager +def _watch_progress(handler): + """Context manager for creating a unix-domain socket and listen for + ffmpeg progress events. + + The socket filename is yielded from the context manager and the + socket is closed when the context manager is exited. + + Args: + handler: a function to be called when progress events are + received; receives a ``key`` argument and ``value`` + argument. (The example ``show_progress`` below uses tqdm) + + Yields: + socket_filename: the name of the socket file. + """ + with _tmpdir_scope() as tmpdir: + socket_filename = os.path.join(tmpdir, 'sock') + sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + with contextlib.closing(sock): + sock.bind(socket_filename) + sock.listen(1) + child = gevent.spawn(_do_watch_progress, socket_filename, sock, handler) + try: + yield socket_filename + except: + gevent.kill(child) + raise + + + +@contextlib.contextmanager +def show_progress(total_duration): + """Create a unix-domain socket to watch progress and render tqdm + progress bar.""" + with tqdm(total=round(total_duration, 2)) as bar: + def handler(key, value): + if key == 'out_time_ms': + time = round(float(value) / 1000000., 2) + bar.update(time - bar.n) + elif key == 'progress' and value == 'end': + bar.update(bar.total - bar.n) + with _watch_progress(handler) as socket_filename: + yield socket_filename + + +if __name__ == '__main__': + args = parser.parse_args() + total_duration = float(ffmpeg.probe(args.in_filename)['format']['duration']) + + with show_progress(total_duration) as socket_filename: + # See https://ffmpeg.org/ffmpeg-filters.html#Examples-44 + sepia_values = [.393, .769, .189, 0, .349, .686, .168, 0, .272, .534, .131] + try: + (ffmpeg + .input(args.in_filename) + .colorchannelmixer(*sepia_values) + .output(args.out_filename) + .global_args('-progress', 'unix://{}'.format(socket_filename)) + .overwrite_output() + .run(capture_stdout=True, capture_stderr=True) + ) + except ffmpeg.Error as e: + print(e.stderr, file=sys.stderr) + sys.exit(1) + diff --git a/examples/transcribe.py b/examples/transcribe.py index fb484df..0b7200c 100755 --- a/examples/transcribe.py +++ b/examples/transcribe.py @@ -1,13 +1,11 @@ #!/usr/bin/env python -from __future__ import unicode_literals - +from __future__ import unicode_literals, print_function from google.cloud import speech from google.cloud.speech import enums from google.cloud.speech import types import argparse import ffmpeg import logging -import subprocess import sys @@ -21,21 +19,17 @@ parser.add_argument('in_filename', help='Input filename (`-` for stdin)') def decode_audio(in_filename, **input_kwargs): - p = subprocess.Popen( - (ffmpeg + try: + out, err = (ffmpeg .input(in_filename, **input_kwargs) .output('-', format='s16le', acodec='pcm_s16le', ac=1, ar='16k') .overwrite_output() - .compile() - ), - stdout=subprocess.PIPE, - stderr=subprocess.PIPE - ) - out = p.communicate() - if p.returncode != 0: - sys.stderr.write(out[1]) + .run(capture_stdout=True, capture_stderr=True) + ) + except ffmpeg.Error as e: + print(e.stderr, file=sys.stderr) sys.exit(1) - return out[0] + return out def get_transcripts(audio_data): diff --git a/examples/video_info.py b/examples/video_info.py index 0446504..df9c992 100755 --- a/examples/video_info.py +++ b/examples/video_info.py @@ -11,15 +11,21 @@ parser.add_argument('in_filename', help='Input filename') if __name__ == '__main__': args = parser.parse_args() - probe = ffmpeg.probe(args.in_filename) - video_info = next((stream for stream in probe['streams'] if stream['codec_type'] == 'video'), None) - if video_info is None: + + try: + probe = ffmpeg.probe(args.in_filename) + except ffmpeg.Error as e: + print(e.stderr, file=sys.stderr) + sys.exit(1) + + video_stream = next((stream for stream in probe['streams'] if stream['codec_type'] == 'video'), None) + if video_stream is None: print('No video stream found', file=sys.stderr) sys.exit(1) - width = int(video_info['width']) - height = int(video_info['height']) - num_frames = int(video_info['nb_frames']) + width = int(video_stream['width']) + height = int(video_stream['height']) + num_frames = int(video_stream['nb_frames']) print('width: {}'.format(width)) print('height: {}'.format(height)) print('num_frames: {}'.format(num_frames))