Hello community, here is the log from the commit of package python-imageio-ffmpeg for openSUSE:Factory checked in at 2020-04-15 20:06:31 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-imageio-ffmpeg (Old) and /work/SRC/openSUSE:Factory/.python-imageio-ffmpeg.new.2738 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-imageio-ffmpeg" Wed Apr 15 20:06:31 2020 rev:2 rq:792769 version:0.4.1 Changes: -------- --- /work/SRC/openSUSE:Factory/python-imageio-ffmpeg/python-imageio-ffmpeg.changes 2019-05-22 11:08:41.690613817 +0200 +++ /work/SRC/openSUSE:Factory/.python-imageio-ffmpeg.new.2738/python-imageio-ffmpeg.changes 2020-04-15 20:08:04.670169743 +0200 @@ -1,0 +2,11 @@ +Thu Apr 9 14:12:14 UTC 2020 - Marketa Calabkova <mcalabk...@suse.com> + +- Update to 0.4.1 + * The default value for ffmpeg_timeout is now zero, e.g. just wait for ffmpeg. + * Added bits_per_pixel parameter to read_frames. + * Official support for Python 3.8. + * Improved handling of interrupts during reading/writing/closing-down. + * Allow the ffmpeg_timeout arg in write_frames() to be None. + * Don't prevent sigint propagation for ffmpeg call in count_frames_and_secs(). + +------------------------------------------------------------------- Old: ---- imageio-ffmpeg-0.3.0.tar.gz New: ---- imageio-ffmpeg-0.4.1.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-imageio-ffmpeg.spec ++++++ --- /var/tmp/diff_new_pack.kLrLPH/_old 2020-04-15 20:08:05.186170095 +0200 +++ /var/tmp/diff_new_pack.kLrLPH/_new 2020-04-15 20:08:05.186170095 +0200 @@ -1,7 +1,7 @@ # # spec file for package python-imageio-ffmpeg # -# Copyright (c) 2019 SUSE LINUX GmbH, Nuernberg, Germany. +# Copyright (c) 2020 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -12,18 +12,19 @@ # license that conforms to the Open Source Definition (Version 1.9) # published by the Open Source Initiative. -# Please submit bugfixes or comments via http://bugs.opensuse.org/ +# Please submit bugfixes or comments via https://bugs.opensuse.org/ +# %{?!python_module:%define python_module() python-%{**} python3-%{**}} %define skip_python2 1 Name: python-imageio-ffmpeg -Version: 0.3.0 +Version: 0.4.1 Release: 0 -License: BSD-2-Clause Summary: FFMPEG wrapper for Python -Url: https://github.com/imageio/imageio-ffmpeg +License: BSD-2-Clause Group: Development/Languages/Python +URL: https://github.com/imageio/imageio-ffmpeg Source: https://files.pythonhosted.org/packages/source/i/imageio-ffmpeg/imageio-ffmpeg-%{version}.tar.gz BuildRequires: %{python_module pip > 19} BuildRequires: %{python_module setuptools} @@ -32,7 +33,6 @@ BuildRequires: python-rpm-macros Requires: ffmpeg-4 BuildArch: noarch - %python_subpackages %description ++++++ imageio-ffmpeg-0.3.0.tar.gz -> imageio-ffmpeg-0.4.1.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/imageio-ffmpeg-0.3.0/PKG-INFO new/imageio-ffmpeg-0.4.1/PKG-INFO --- old/imageio-ffmpeg-0.3.0/PKG-INFO 2019-04-12 11:08:07.000000000 +0200 +++ new/imageio-ffmpeg-0.4.1/PKG-INFO 2020-02-24 15:11:03.000000000 +0100 @@ -1,11 +1,11 @@ -Metadata-Version: 1.1 +Metadata-Version: 1.2 Name: imageio-ffmpeg -Version: 0.3.0 +Version: 0.4.1 Summary: FFMPEG wrapper for Python Home-page: https://github.com/imageio/imageio-ffmpeg Author: imageio contributors Author-email: almar.kl...@gmail.com -License: (new) BSD +License: BSD-2-Clause Download-URL: http://pypi.python.org/pypi/imageio-ffmpeg Description: FFMPEG wrapper for Python. @@ -33,4 +33,6 @@ Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 Provides: imageio_ffmpeg +Requires-Python: >=3.4 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/imageio-ffmpeg-0.3.0/README.md new/imageio-ffmpeg-0.4.1/README.md --- old/imageio-ffmpeg-0.3.0/README.md 2019-04-12 11:07:51.000000000 +0200 +++ new/imageio-ffmpeg-0.4.1/README.md 2020-02-24 14:13:52.000000000 +0100 @@ -1,6 +1,7 @@ # imageio-ffmpeg [![Build Status](https://travis-ci.org/imageio/imageio-ffmpeg.svg?branch=master)](https://travis-ci.org/imageio/imageio-ffmpeg) +[![PyPI Version](https://img.shields.io/pypi/v/imageio-ffmpeg.svg)](https://pypi.python.org/pypi/imageio-ffmpeg/) FFMPEG wrapper for Python @@ -45,7 +46,7 @@ `IMAGEIO_FFMPEG_EXE` environment variable if needed. -## Usage +## Example usage The `imageio_ffmpeg` library provides low level functionality to read and write video data, using Python generators: @@ -67,6 +68,8 @@ writer.close() # don't forget this ``` +(Also see the API section further down.) + ## How it works @@ -77,20 +80,45 @@ the code itself too. In contrast, [PyAV](https://github.com/mikeboers/PyAV) wraps ffmpeg at the C level. +Note that because of how `imageio-ffmpeg` works, `read_frames()` and +`write_frames()` only accept file names, and not file (like) objects. + + + +## imageio-ffmpeg for enterprise + +Available as part of the Tidelift Subscription + +The maintainers of imageio-ffmpeg and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. [Learn more.](https://tidelift.com/subscription/pkg/pypi-imageio-ffmpeg?utm_source=pypi-imageio-ffmpeg&utm_medium=referral&utm_campaign=enterprise&utm_term=repo) + + +## Security contact information + +To report a security vulnerability, please use the +[Tidelift security contact](https://tidelift.com/security). +Tidelift will coordinate the fix and disclosure. + ## API ```py -def read_frames(path, pix_fmt="rgb24", bpp=3, input_params=None, output_params=None): +def read_frames( + path, + pix_fmt="rgb24", + bpp=None, + input_params=None, + output_params=None, + bits_per_pixel=None, +): """ Create a generator to iterate over the frames in a video file. It first yields a small metadata dictionary that contains: - * ffmpeg_version: the ffmpeg version is use (as a string). - * codec: a hint about the codec used to encode the video, e.g. "h264" - * source_size: the width and height of the encoded video frames - * size: the width and height of the frames that will be produced + * ffmpeg_version: the ffmpeg version in use (as a string). + * codec: a hint about the codec used to encode the video, e.g. "h264". + * source_size: the width and height of the encoded video frames. + * size: the width and height of the frames that will be produced. * fps: the frames per second. Can be zero if it could not be detected. * duration: duration in seconds. Can be zero if it could not be detected. @@ -113,13 +141,16 @@ print(len(frame)) Parameters: - path (str): the file to write to. + path (str): the filename of the file to read from. pix_fmt (str): the pixel format of the frames to be read. The default is "rgb24" (frames are uint8 RGB images). - bpp (int): The number of bytes per pixel in the output frames. - This depends on the given pix_fmt. Default is 3 (RGB). input_params (list): Additional ffmpeg input command line parameters. output_params (list): Additional ffmpeg output command line parameters. + bits_per_pixel (int): The number of bits per pixel in the output frames. + This depends on the given pix_fmt. Default is 24 (RGB) + bpp (int): DEPRECATED, USE bits_per_pixel INSTEAD. The number of bytes per pixel in the output frames. + This depends on the given pix_fmt. Some pixel formats like yuv420p have 12 bits per pixel + and cannot be set in bytes as integer. For this reason the bpp argument is deprecated. """ ``` @@ -135,6 +166,7 @@ codec=None, macro_block_size=16, ffmpeg_log_level="warning", + ffmpeg_timeout=0, input_params=None, output_params=None, ): @@ -154,7 +186,7 @@ gen.close() # don't forget this Parameters: - path (str): the file to write to. + path (str): the filename to write to. size (tuple): the width and height of the frames. pix_fmt_in (str): the pixel format of incoming frames. E.g. "gray", "gray8a", "rgb24", or "rgba". Default "rgb24". @@ -167,7 +199,10 @@ macro_block_size (int): You probably want to align the size of frames to this value to avoid image resizing. Default 16. Can be set to 1 to avoid block alignment, though this is not recommended. - ffmpeg_log_level (str): The ffmpeg logging level. + ffmpeg_log_level (str): The ffmpeg logging level. Default "warning". + ffmpeg_timeout (float): Timeout in seconds to wait for ffmpeg process + to finish. Value of 0 will wait forever (default). The time that + ffmpeg needs depends on CPU speed, compression, and frame size. input_params (list): Additional ffmpeg input command line parameters. output_params (list): Additional ffmpeg output command line parameters. """ @@ -188,7 +223,7 @@ ```py def get_ffmpeg_exe(): """ - Get the ffmpeg executable file. This can be the binary defined by + Get the ffmpeg executable file. This can be the binary defined by the IMAGEIO_FFMPEG_EXE environment variable, the binary distributed with imageio-ffmpeg, an ffmpeg binary installed with conda, or the system ffmpeg (in that order). A RuntimeError is raised if no valid diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/imageio-ffmpeg-0.3.0/imageio_ffmpeg/_definitions.py new/imageio-ffmpeg-0.4.1/imageio_ffmpeg/_definitions.py --- old/imageio-ffmpeg-0.3.0/imageio_ffmpeg/_definitions.py 2019-04-12 11:07:51.000000000 +0200 +++ new/imageio-ffmpeg-0.4.1/imageio_ffmpeg/_definitions.py 2020-02-24 15:10:39.000000000 +0100 @@ -1,7 +1,7 @@ import sys import struct -__version__ = "0.3.0" +__version__ = "0.4.1" def get_platform(): @@ -10,6 +10,8 @@ return "linux{}".format(bits) elif sys.platform.startswith("win"): return "win{}".format(bits) + elif sys.platform.startswith("cygwin"): + return "win{}".format(bits) elif sys.platform.startswith("darwin"): return "osx{}".format(bits) else: # pragma: no cover diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/imageio-ffmpeg-0.3.0/imageio_ffmpeg/_io.py new/imageio-ffmpeg-0.4.1/imageio_ffmpeg/_io.py --- old/imageio-ffmpeg-0.3.0/imageio_ffmpeg/_io.py 2019-04-12 11:07:51.000000000 +0200 +++ new/imageio-ffmpeg-0.4.1/imageio_ffmpeg/_io.py 2020-02-24 15:10:39.000000000 +0100 @@ -1,9 +1,9 @@ import sys import time -import signal +import pathlib import subprocess -from ._utils import get_ffmpeg_exe, logger +from ._utils import get_ffmpeg_exe, _popen_kwargs, logger from ._parsing import LogCatcher, parse_ffmpeg_header, cvsecs @@ -31,11 +31,14 @@ """ # https://stackoverflow.com/questions/2017843/fetch-frame-count-with-ffmpeg - assert isinstance(path, str), "Video path must be a string" + if isinstance(path, pathlib.PurePath): + path = str(path) + if not isinstance(path, str): + raise TypeError("Video path must be a string or pathlib.Path.") cmd = [_get_exe(), "-i", path, "-map", "0:v:0", "-c", "copy", "-f", "null", "-"] try: - out = subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=ISWIN) + out = subprocess.check_output(cmd, stderr=subprocess.STDOUT, **_popen_kwargs()) except subprocess.CalledProcessError as err: out = err.output.decode(errors="ignore") raise RuntimeError("FFMEG call failed with {}:\n{}".format(err.returncode, out)) @@ -60,16 +63,23 @@ raise RuntimeError("Could not get number of frames") # pragma: no cover -def read_frames(path, pix_fmt="rgb24", bpp=3, input_params=None, output_params=None): +def read_frames( + path, + pix_fmt="rgb24", + bpp=None, + input_params=None, + output_params=None, + bits_per_pixel=None, +): """ Create a generator to iterate over the frames in a video file. It first yields a small metadata dictionary that contains: - * ffmpeg_version: the ffmpeg version is use (as a string). - * codec: a hint about the codec used to encode the video, e.g. "h264" - * source_size: the width and height of the encoded video frames - * size: the width and height of the frames that will be produced + * ffmpeg_version: the ffmpeg version in use (as a string). + * codec: a hint about the codec used to encode the video, e.g. "h264". + * source_size: the width and height of the encoded video frames. + * size: the width and height of the frames that will be produced. * fps: the frames per second. Can be zero if it could not be detected. * duration: duration in seconds. Can be zero if it could not be detected. @@ -92,27 +102,34 @@ print(len(frame)) Parameters: - path (str): the file to write to. + path (str): the filename of the file to read from. pix_fmt (str): the pixel format of the frames to be read. The default is "rgb24" (frames are uint8 RGB images). - bpp (int): The number of bytes per pixel in the output frames. - This depends on the given pix_fmt. Default is 3 (RGB). input_params (list): Additional ffmpeg input command line parameters. output_params (list): Additional ffmpeg output command line parameters. + bits_per_pixel (int): The number of bits per pixel in the output frames. + This depends on the given pix_fmt. Default is 24 (RGB) + bpp (int): DEPRECATED, USE bits_per_pixel INSTEAD. The number of bytes per pixel in the output frames. + This depends on the given pix_fmt. Some pixel formats like yuv420p have 12 bits per pixel + and cannot be set in bytes as integer. For this reason the bpp argument is deprecated. """ # ----- Input args - assert isinstance(path, str), "Video path must be a string" + if isinstance(path, pathlib.PurePath): + path = str(path) + if not isinstance(path, str): + raise TypeError("Video path must be a string or pathlib.Path.") # Note: Dont check whether it exists. The source could be e.g. a camera. pix_fmt = pix_fmt or "rgb24" bpp = bpp or 3 + bits_per_pixel = bits_per_pixel or bpp * 8 input_params = input_params or [] output_params = output_params or [] assert isinstance(pix_fmt, str), "pix_fmt must be a string" - assert isinstance(bpp, int), "bpp must be an int" + assert isinstance(bits_per_pixel, int), "bpp and bits_per_pixel must be an int" assert isinstance(input_params, list), "input_params must be a list" assert isinstance(output_params, list), "output_params must be a list" @@ -129,18 +146,25 @@ stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, - shell=ISWIN, + **_popen_kwargs(prevent_sigint=True) ) log_catcher = LogCatcher(p.stderr) + # Init policy by which to terminate ffmpeg. May be set to "kill" later. + stop_policy = "timeout" # not wait; ffmpeg should be able to quit quickly + + # Enter try block directly after opening the process. + # We terminate ffmpeg in the finally clause. + # Generators are automatically closed when they get deleted, + # so the finally block is guaranteed to run. try: # ----- Load meta data # Wait for the log catcher to get the meta information etime = time.time() + 10.0 - while (not log_catcher.header) and time.time() < etime: + while log_catcher.is_alive() and not log_catcher.header and time.time() < etime: time.sleep(0.01) # Check whether we have the information @@ -157,15 +181,20 @@ # ----- Read frames w, h = meta["size"] - framesize = w * h * bpp + framesize_bits = w * h * bits_per_pixel + framesize_bytes = framesize_bits / 8 + assert ( + framesize_bytes.is_integer() + ), "incorrect bits_per_pixel, framesize in bytes must be an int" + framesize_bytes = int(framesize_bytes) framenr = 0 while True: framenr += 1 try: bb = bytes() - while len(bb) < framesize: - extra_bytes = p.stdout.read(framesize - len(bb)) + while len(bb) < framesize_bytes: + extra_bytes = p.stdout.read(framesize_bytes - len(bb)) if not extra_bytes: if len(bb) == 0: return @@ -181,31 +210,55 @@ fmt = "Could not read frame {}:\n{}\n=== stderr ===\n{}" raise RuntimeError(fmt.format(framenr, err1, err2)) + except GeneratorExit: + # Note that GeneratorExit does not inherit from Exception but BaseException + pass + + except Exception: + # Normal exceptions fall through + raise + + except BaseException: + # Detect KeyboardInterrupt / SystemExit: don't wait for ffmpeg to quit + stop_policy = "kill" + raise + finally: - # Generators are automatically closed when they get deleted, - # so this code is almost guaranteed to run. + # Make sure that ffmpeg is terminated. if p.poll() is None: # Ask ffmpeg to quit try: - if True: - p.communicate(b"q") - else: # pragma: no cover - # I read somewhere that modern ffmpeg on Linux prefers a - # "ctrl-c", but tests so far suggests sending q is better. - p.send_signal(signal.SIGINT) + # I read somewhere that modern ffmpeg on Linux prefers a + # "ctrl-c", but tests so far suggests sending q is more robust. + # > p.send_signal(signal.SIGINT) + # Sending q via communicate works, but can hang (see #17) + # > p.communicate(b"q") + # So let's do similar to what communicate does, but without + # reading stdout (which may block). It looks like only closing + # stdout is enough (tried Windows+Linux), but let's play safe. + # Found that writing to stdin can cause "Invalid argument" on + # Windows # and "Broken Pipe" on Unix. + # p.stdin.write(b"q") # commented out in v0.4.1 + p.stdout.close() + p.stdin.close() except Exception as err: # pragma: no cover - logger.warning("Error while attempting stop ffmpeg: " + str(err)) + logger.warning("Error while attempting stop ffmpeg (r): " + str(err)) - # Wait for it to stop - etime = time.time() + 1.5 - while time.time() < etime and p.poll() is None: - time.sleep(0.01) - - # Grr, we have to kill it - if p.poll() is None: # pragma: no cover - logger.warning("We had to kill ffmpeg to stop it.") + if stop_policy == "timeout": + # Wait until timeout, produce a warning and kill if it still exists + try: + etime = time.time() + 1.5 + while time.time() < etime and p.poll() is None: + time.sleep(0.01) + finally: + if p.poll() is None: # pragma: no cover + logger.warning("We had to kill ffmpeg to stop it.") + p.kill() + + else: # stop_policy == "kill" + # Just kill it p.kill() @@ -220,7 +273,7 @@ codec=None, macro_block_size=16, ffmpeg_log_level="warning", - ffmpeg_timeout=20.0, + ffmpeg_timeout=None, input_params=None, output_params=None, ): @@ -240,7 +293,7 @@ gen.close() # don't forget this Parameters: - path (str): the file to write to. + path (str): the filename to write to. size (tuple): the width and height of the frames. pix_fmt_in (str): the pixel format of incoming frames. E.g. "gray", "gray8a", "rgb24", or "rgba". Default "rgb24". @@ -255,15 +308,18 @@ to 1 to avoid block alignment, though this is not recommended. ffmpeg_log_level (str): The ffmpeg logging level. Default "warning". ffmpeg_timeout (float): Timeout in seconds to wait for ffmpeg process - to finish. Value of 0 will wait forever. The time that ffmpeg needs - depends on CPU speed, compression, and frame size. Default 20.0. + to finish. Value of 0 or None will wait forever (default). The time that + ffmpeg needs depends on CPU speed, compression, and frame size. input_params (list): Additional ffmpeg input command line parameters. output_params (list): Additional ffmpeg output command line parameters. """ # ----- Input args - assert isinstance(path, str), "Video path must be a string" + if isinstance(path, pathlib.PurePath): + path = str(path) + if not isinstance(path, str): + raise TypeError("Video path must be a string or pathlib.Path.") # The pix_fmt_out yuv420p is the best for the outpur to work in # QuickTime and most other players. These players only support @@ -281,6 +337,7 @@ ffmpeg_log_level = ffmpeg_log_level or "warning" input_params = input_params or [] output_params = output_params or [] + ffmpeg_timeout = ffmpeg_timeout or 0 floatish = float, int if isinstance(size, (tuple, list)): @@ -376,20 +433,31 @@ # Launch process p = subprocess.Popen( - cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=None, shell=ISWIN + cmd, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=None, + **_popen_kwargs(prevent_sigint=True) ) - # For Windows, set `shell=True` in sp.Popen to prevent popup - # of a command line window in frozen applications. # Note that directing stderr to a pipe on windows will cause ffmpeg # to hang if the buffer is not periodically cleared using # StreamCatcher or other means. # Setting bufsize to 0 or a small value does not seem to have much effect - # (at least on Windows). I suspect that ffmpeg buffers # multiple frames - # (before encoding in a batch). + # (tried on Windows and Linux). I suspect that ffmpeg buffers + # multiple frames (before encoding in a batch). + + # Init policy by which to terminate ffmpeg. May be set to "kill" later. + stop_policy = "timeout" + if not ffmpeg_timeout: + stop_policy = "wait" # ----- Write frames + # Enter try block directly after opening the process. + # We terminate ffmpeg in the finally clause. + # Generators are automatically closed when they get deleted, + # so the finally block is guaranteed to run. try: # Just keep going until the generator.close() is called (raises GeneratorExit). @@ -398,7 +466,7 @@ while True: # Get frame - bb = (yield) + bb = yield # framesize = size[0] * size[1] * depth * bpp # assert isinstance(bb, bytes), "Frame must be send as bytes" @@ -420,29 +488,55 @@ nframes += 1 except GeneratorExit: + # Note that GeneratorExit does not inherit from Exception but BaseException + # Detect premature closing if nframes == 0: logger.warning("No frames have been written; the written video is invalid.") + + except Exception: + # Normal exceptions fall through + raise + + except BaseException: + # Detect KeyboardInterrupt / SystemExit: don't wait for ffmpeg to quit + stop_policy = "kill" + raise + finally: + # Make sure that ffmpeg is terminated. if p.poll() is None: - # Ask ffmpeg to quit - and wait for it to finish writing the file. - # Depending on the frame size and encoding this can take a few - # seconds (sometimes 10-20). Since a user may get bored and hit - # Ctrl-C, we wrap this in a try-except. - waited = False + # Tell ffmpeg that we're done try: + p.stdin.close() + except Exception as err: # pragma: no cover + logger.warning("Error while attempting stop ffmpeg (w): " + str(err)) + + if stop_policy == "timeout": + # Wait until timeout, produce a warning and kill if it still exists + try: + etime = time.time() + ffmpeg_timeout + while (time.time() < etime) and p.poll() is None: + time.sleep(0.01) + finally: + if p.poll() is None: # pragma: no cover + logger.warning( + "We had to kill ffmpeg to stop it. " + + "Consider increasing ffmpeg_timeout, " + + "or setting it to zero (no timeout)." + ) + p.kill() + + elif stop_policy == "wait": + # Wait forever, kill if it if we're interrupted try: - p.stdin.close() - except Exception: # pragma: no cover - pass - etime = time.time() + ffmpeg_timeout - while (not ffmpeg_timeout or time.time() < etime) and p.poll() is None: - time.sleep(0.01) - waited = True - finally: - # Grr, we have to kill it - if p.poll() is None: # pragma: no cover - more = " Consider increasing ffmpeg_timeout." if waited else "" - logger.warning("We had to kill ffmpeg to stop it." + more) - p.kill() + while p.poll() is None: + time.sleep(0.01) + finally: # the above can raise e.g. by ctrl-c or systemexit + if p.poll() is None: # pragma: no cover + p.kill() + + else: # stop_policy == "kill": + # Just kill it + p.kill() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/imageio-ffmpeg-0.3.0/imageio_ffmpeg/_utils.py new/imageio-ffmpeg-0.4.1/imageio_ffmpeg/_utils.py --- old/imageio-ffmpeg-0.3.0/imageio_ffmpeg/_utils.py 2019-04-12 11:07:51.000000000 +0200 +++ new/imageio-ffmpeg-0.4.1/imageio_ffmpeg/_utils.py 2020-02-24 15:10:39.000000000 +0100 @@ -1,8 +1,8 @@ import os -from pkg_resources import resource_filename import sys -import subprocess import logging +import subprocess +from pkg_resources import resource_filename from ._definitions import get_platform, FNAME_PER_PLATFORM @@ -11,7 +11,7 @@ def get_ffmpeg_exe(): """ - Get the ffmpeg executable file. This can be the binary defined by + Get the ffmpeg executable file. This can be the binary defined by the IMAGEIO_FFMPEG_EXE environment variable, the binary distributed with imageio-ffmpeg, an ffmpeg binary installed with conda, or the system ffmpeg (in that order). A RuntimeError is raised if no valid @@ -52,11 +52,35 @@ ) +def _popen_kwargs(prevent_sigint=False): + startupinfo = None + preexec_fn = None + creationflags = 0 + if sys.platform.startswith("win"): + # Stops executable from flashing on Windows (see #22) + startupinfo = subprocess.STARTUPINFO() + startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW + if prevent_sigint: + # Prevent propagation of sigint (see #4) + # https://stackoverflow.com/questions/5045771 + if sys.platform.startswith("win"): + creationflags = 0x00000200 + else: + preexec_fn = os.setpgrp # the _pre_exec does not seem to work + return { + "startupinfo": startupinfo, + "creationflags": creationflags, + "preexec_fn": preexec_fn, + } + + def _is_valid_exe(exe): cmd = [exe, "-version"] try: with open(os.devnull, "w") as null: - subprocess.check_call(cmd, stdout=null, stderr=subprocess.STDOUT) + subprocess.check_call( + cmd, stdout=null, stderr=subprocess.STDOUT, **_popen_kwargs() + ) return True except (OSError, ValueError, subprocess.CalledProcessError): return False @@ -67,7 +91,9 @@ Get the version of the used ffmpeg executable (as a string). """ exe = get_ffmpeg_exe() - line = subprocess.check_output([exe, "-version"]).split(b"\n", 1)[0] + line = subprocess.check_output([exe, "-version"], **_popen_kwargs()).split( + b"\n", 1 + )[0] line = line.decode(errors="ignore").strip() version = line.split("version", 1)[-1].lstrip().split(" ", 1)[0].strip() return version diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/imageio-ffmpeg-0.3.0/imageio_ffmpeg.egg-info/PKG-INFO new/imageio-ffmpeg-0.4.1/imageio_ffmpeg.egg-info/PKG-INFO --- old/imageio-ffmpeg-0.3.0/imageio_ffmpeg.egg-info/PKG-INFO 2019-04-12 11:08:07.000000000 +0200 +++ new/imageio-ffmpeg-0.4.1/imageio_ffmpeg.egg-info/PKG-INFO 2020-02-24 15:11:03.000000000 +0100 @@ -1,11 +1,11 @@ -Metadata-Version: 1.1 +Metadata-Version: 1.2 Name: imageio-ffmpeg -Version: 0.3.0 +Version: 0.4.1 Summary: FFMPEG wrapper for Python Home-page: https://github.com/imageio/imageio-ffmpeg Author: imageio contributors Author-email: almar.kl...@gmail.com -License: (new) BSD +License: BSD-2-Clause Download-URL: http://pypi.python.org/pypi/imageio-ffmpeg Description: FFMPEG wrapper for Python. @@ -33,4 +33,6 @@ Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 Provides: imageio_ffmpeg +Requires-Python: >=3.4 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/imageio-ffmpeg-0.3.0/setup.cfg new/imageio-ffmpeg-0.4.1/setup.cfg --- old/imageio-ffmpeg-0.3.0/setup.cfg 2019-04-12 11:08:07.000000000 +0200 +++ new/imageio-ffmpeg-0.4.1/setup.cfg 2020-02-24 15:11:03.000000000 +0100 @@ -1,5 +1,4 @@ [egg_info] -tag_date = 0 tag_build = -tag_svn_revision = 0 +tag_date = 0 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/imageio-ffmpeg-0.3.0/setup.py new/imageio-ffmpeg-0.4.1/setup.py --- old/imageio-ffmpeg-0.3.0/setup.py 2019-04-12 11:07:51.000000000 +0200 +++ new/imageio-ffmpeg-0.4.1/setup.py 2020-02-24 14:13:52.000000000 +0100 @@ -48,7 +48,7 @@ version=__version__, author="imageio contributors", author_email="almar.kl...@gmail.com", - license="(new) BSD", + license="BSD-2-Clause", url="https://github.com/imageio/imageio-ffmpeg", download_url="http://pypi.python.org/pypi/imageio-ffmpeg", keywords="video ffmpeg", @@ -58,7 +58,7 @@ provides=["imageio_ffmpeg"], python_requires=">=3.4", setup_requires=["pip>19"], - install_requires=[], # todo: maybe numpy + install_requires=[], packages=["imageio_ffmpeg"], package_dir={"imageio_ffmpeg": "imageio_ffmpeg"}, package_data={"imageio_ffmpeg": ["binaries/*.*"]}, @@ -79,5 +79,6 @@ "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", ], )