Source code for moviepy.video.io.ffmpeg_tools

"""Miscellaneous bindings to ffmpeg."""

import os
import re
import subprocess

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


[docs]@convert_path_to_string(("inputfile", "outputfile")) @convert_parameter_to_seconds(("start_time", "end_time")) def ffmpeg_extract_subclip( inputfile, start_time, end_time, outputfile=None, logger="bar" ): """Makes a new video file playing video file between two times. Parameters ---------- inputfile : str Path to the file from which the subclip will be extracted. start_time : float Moment of the input clip that marks the start of the produced subclip. end_time : float Moment of the input clip that marks the end of the produced subclip. outputfile : str, optional Path to the output file. Defaults to ``<inputfile_name>SUB<start_time>_<end_time><ext>``. """ if not outputfile: name, ext = os.path.splitext(inputfile) t1, t2 = [int(1000 * t) for t in [start_time, end_time]] outputfile = "%sSUB%d_%d%s" % (name, t1, t2, ext) cmd = [ FFMPEG_BINARY, "-y", "-ss", "%0.2f" % start_time, "-i", ffmpeg_escape_filename(inputfile), "-t", "%0.2f" % (end_time - start_time), "-map", "0", "-vcodec", "copy", "-acodec", "copy", "-copyts", ffmpeg_escape_filename(outputfile), ] subprocess_call(cmd, logger=logger)
[docs]@convert_path_to_string(("videofile", "audiofile", "outputfile")) def ffmpeg_merge_video_audio( videofile, audiofile, outputfile, video_codec="copy", audio_codec="copy", logger="bar", ): """Merges video file and audio file into one movie file. Parameters ---------- videofile : str Path to the video file used in the merge. audiofile : str Path to the audio file used in the merge. outputfile : str Path to the output file. video_codec : str, optional Video codec used by FFmpeg in the merge. audio_codec : str, optional Audio codec used by FFmpeg in the merge. """ cmd = [ FFMPEG_BINARY, "-y", "-i", ffmpeg_escape_filename(audiofile), "-i", ffmpeg_escape_filename(videofile), "-vcodec", video_codec, "-acodec", audio_codec, ffmpeg_escape_filename(outputfile), ] subprocess_call(cmd, logger=logger)
[docs]@convert_path_to_string(("inputfile", "outputfile")) def ffmpeg_extract_audio(inputfile, outputfile, bitrate=3000, fps=44100, logger="bar"): """Extract the sound from a video file and save it in ``outputfile``. Parameters ---------- inputfile : str The path to the file from which the audio will be extracted. outputfile : str The path to the file to which the audio will be stored. bitrate : int, optional Bitrate for the new audio file. fps : int, optional Frame rate for the new audio file. """ cmd = [ FFMPEG_BINARY, "-y", "-i", ffmpeg_escape_filename(inputfile), "-ab", "%dk" % bitrate, "-ar", "%d" % fps, ffmpeg_escape_filename(outputfile), ] subprocess_call(cmd, logger=logger)
[docs]@convert_path_to_string(("inputfile", "outputfile")) def ffmpeg_resize(inputfile, outputfile, size, logger="bar"): """Resizes a file to new size and write the result in another. Parameters ---------- inputfile : str Path to the file to be resized. outputfile : str Path to the output file. size : list or tuple New size in format ``[width, height]`` for the output file. """ cmd = [ FFMPEG_BINARY, "-i", ffmpeg_escape_filename(inputfile), "-vf", "scale=%d:%d" % (size[0], size[1]), ffmpeg_escape_filename(outputfile), ] subprocess_call(cmd, logger=logger)
[docs]@convert_path_to_string(("inputfile", "outputfile", "output_dir")) def ffmpeg_stabilize_video( inputfile, outputfile=None, output_dir="", overwrite_file=True, logger="bar" ): """ Stabilizes ``filename`` and write the result to ``output``. Parameters ---------- inputfile : str The name of the shaky video. outputfile : str, optional The name of new stabilized video. Defaults to appending '_stabilized' to the input file name. output_dir : str, optional The directory to place the output video in. Defaults to the current working directory. overwrite_file : bool, optional If ``outputfile`` already exists in ``output_dir``, then overwrite ``outputfile`` Defaults to True. """ if not outputfile: without_dir = os.path.basename(inputfile) name, ext = os.path.splitext(without_dir) outputfile = f"{name}_stabilized{ext}" outputfile = os.path.join(output_dir, outputfile) cmd = [ FFMPEG_BINARY, "-i", ffmpeg_escape_filename(inputfile), "-vf", "deshake", ffmpeg_escape_filename(outputfile), ] if overwrite_file: cmd.append("-y") subprocess_call(cmd, logger=logger)
[docs]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)
[docs]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)