diff --git a/CHANGELOG.md b/CHANGELOG.md index bb3d1c79e..25cc0398b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Improve perfs of decorator by pre-computing arguments - Fix textclip being cut or of impredictable height (see issues #2325, #2260 and #2268) - Fix TimeMirror and TimeSymmetrize cutting last second of clip +- Fix audiopreview not working with ffplay >= 7.0.0 ## [v2.1.2](https://github.com/zulko/moviepy/tree/master) diff --git a/moviepy/audio/io/ffplay_audiopreviewer.py b/moviepy/audio/io/ffplay_audiopreviewer.py index e1217ab56..fbfcfcacc 100644 --- a/moviepy/audio/io/ffplay_audiopreviewer.py +++ b/moviepy/audio/io/ffplay_audiopreviewer.py @@ -5,7 +5,7 @@ from moviepy.config import FFPLAY_BINARY from moviepy.decorators import requires_duration from moviepy.tools import cross_platform_popen_params - +from moviepy.video.io import ffmpeg_tools class FFPLAY_AudioPreviewer: """ @@ -24,7 +24,6 @@ class FFPLAY_AudioPreviewer: nchannels: Number of audio channels in the clip. Default to 2 channels. - """ def __init__( @@ -42,8 +41,22 @@ def __init__( "s%dle" % (8 * nbytes), "-ar", "%d" % fps_input, - "-ac", - "%d" % nchannels, + ] + + # Adapt number of channels argument to ffplay version + ffplay_version = ffmpeg_tools.ffplay_version()[1] + if int(ffplay_version.split('.')[0]) >= 7: + cmd += [ + "-ch_layout", + "stereo" if nchannels == 2 else "mono", + ] + else : + cmd += [ + "-ac", + "%d" % nchannels, + ] + + cmd += [ "-i", "-", ] @@ -62,12 +75,7 @@ def write_frames(self, frames_array): _, ffplay_error = self.proc.communicate() if ffplay_error is not None: ffplay_error = ffplay_error.decode() - else: - # The error was redirected to a logfile with `write_logfile=True`, - # so read the error from that file instead - self.logfile.seek(0) - ffplay_error = self.logfile.read() - + error = ( f"{err}\n\nMoviePy error: FFPLAY encountered the following error while " f":\n\n {ffplay_error}" diff --git a/moviepy/version.py b/moviepy/version.py index 58039f505..4eabd0b3f 100644 --- a/moviepy/version.py +++ b/moviepy/version.py @@ -1 +1 @@ -__version__ = "2.1.1" +__version__ = "2.1.2" diff --git a/moviepy/video/io/ffmpeg_tools.py b/moviepy/video/io/ffmpeg_tools.py index e9716ffbb..0da6921a0 100644 --- a/moviepy/video/io/ffmpeg_tools.py +++ b/moviepy/video/io/ffmpeg_tools.py @@ -2,7 +2,11 @@ import os -from moviepy.config import FFMPEG_BINARY +import subprocess + +import re + +from moviepy.config import FFMPEG_BINARY, FFPLAY_BINARY from moviepy.decorators import convert_parameter_to_seconds, convert_path_to_string from moviepy.tools import ffmpeg_escape_filename, subprocess_call @@ -207,3 +211,84 @@ def ffmpeg_stabilize_video( cmd.append("-y") subprocess_call(cmd, logger=logger) + + +def ffmpeg_version(): + """ + Retrieve the FFmpeg version. + + This function retrieves both the full and numeric version of FFmpeg + by executing the `ffmpeg -version` command. The full version includes + additional details like build information, while the numeric version + contains only the version numbers (e.g., '7.0.2'). + + Return + ------ + tuple + A tuple containing: + - `full_version` (str): The complete version string (e.g., '7.0.2-static'). + - `numeric_version` (str): The numeric version string (e.g., '7.0.2'). + + Example + ------- + >>> ffmpeg_version() + ('7.0.2-static', '7.0.2') + + Raises + ------ + subprocess.CalledProcessError + If the FFmpeg command fails to execute properly. + """ + cmd = [ + FFMPEG_BINARY, + "-version", + "-v" + "quiet", + ] + + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + + # Extract the version number from the first line of output + full_version = result.stdout.splitlines()[0].split()[2] + numeric_version = re.match(r'^[0-9.]+', full_version).group(0) + return (full_version, numeric_version) + + +def ffplay_version(): + """ + Retrieve the FFplay version. + + This function retrieves both the full and numeric version of FFplay + by executing the `ffplay -version` command. The full version includes + additional details like build information, while the numeric version + contains only the version numbers (e.g., '6.0.1'). + + Return + ------ + tuple + A tuple containing: + - `full_version` (str): The complete version string (e.g., '6.0.1-static'). + - `numeric_version` (str): The numeric version string (e.g., '6.0.1'). + + Example + ------- + >>> ffplay_version() + ('6.0.1-static', '6.0.1') + + Raises + ------ + subprocess.CalledProcessError + If the FFplay command fails to execute properly. + """ + cmd = [ + FFPLAY_BINARY, + "-version", + ] + + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + # Extract the version number from the first line of output + full_version = result.stdout.splitlines()[0].split()[2] + numeric_version = re.match(r'^[0-9.]+', full_version).group(0) + return (full_version, numeric_version) + +