改造版:moviepy使用ffmpeg按照长度分割mp4,根据源文件命名,及时关闭文件避免异常

发布时间 2023-08-26 12:56:55作者: Augustone
import os  # 导入 os 模块,用于处理文件和目录操作
import subprocess # 导入 subprocess 模块,用于在新的进程中执行子程序
import time # 导入 time 模块,用于处理时间相关操作
import random
from moviepy.editor import VideoFileClip # 从 moviepy.editor 模块导入 VideoFileClip 类,用于处理视频文件



def is_float(string): # 定义一个函数,判断给定字符串是否可以转换为浮点数
try:
float(string) # 尝试将字符串转换为浮点数
return True # 如果成功,返回 True
except ValueError: # 如果抛出 ValueError 异常
return False # 返回 False

def get_keyframe_timestamps(input_path): # 定义一个函数,获取输入视频文件的关键帧时间戳列表
command = [ # 定义 ffprobe 命令,用于提取关键帧时间戳
"ffprobe",
"-v", "quiet",
"-select_streams", "v",
"-skip_frame", "nokey",
"-show_entries", "frame=pkt_pts_time",
"-of", "csv=print_section=0",
input_path
]
result = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) # 运行 ffprobe 命令,并捕获标准输出和标准错误
keyframe_timestamps = [float(ts) for ts in result.stdout.decode().splitlines() if is_float(ts)] # 将输出结果转换为关键帧时间戳列表
return keyframe_timestamps # 返回关键帧时间戳列表

def split_video_ffmpeg(input_path, output_path, num_parts): # 定义一个函数,使用 FFmpeg 将输入视频分割成指定数量的部分
if not os.path.exists(output_path): # 如果输出目录不存在
os.makedirs(output_path) # 创建输出目录

video = VideoFileClip(input_path) # 读取输入视频文件
duration = video.duration # 获取视频时长

print(duration)
audio = video.audio
target_part_duration = duration / num_parts
# 计算每部分的目标时长

keyframe_timestamps = get_keyframe_timestamps(input_path) # 获取关键帧时间戳列表

part_start_times = [keyframe_timestamps[0]] # 初始化每部分的起始时间列表
for _ in range(1, num_parts): # 对于剩余部分
target_start_time = part_start_times[-1] + target_part_duration # 计算目标起始时间
closest_keyframe_time = min(keyframe_timestamps, key=lambda ts: abs(ts - target_start_time)) # 寻找最接近的关键帧时间
part_start_times.append(closest_keyframe_time) # 将找到的关键帧时间添加到起始时间列表中

start_time = time.time() # 记录开始时间
out_filename=input_path.split('\\')[-1].split('.')[0]


for i in range(num_parts): # 遍历每个部分
current_time = time.time() # 获取当前时间
elapsed_time = current_time - start_time # 计算已经过去的时间
part_start = part_start_times[i] # 获取当前部分的起始时间
part_end = part_start_times[i + 1] if i + 1 < num_parts else duration # 获取当前部分的结束时间,如果是最后一个部分,则使用视频总时长
output_file_path = os.path.join(output_path, out_filename+f"_{i + 1}.mp4") # 生成当前部分的输出文件路径

progress = (i + 1) / num_parts # 计算当前的进度百分比
time_per_part = elapsed_time / (i + 1) # 计算每个部分的平均处理时间
remaining_time = time_per_part * (num_parts - i - 1) # 估计剩余时间

command = [ # 定义 ffmpeg 命令,用于分割视频
"ffmpeg",
"-ss", str(part_start),
"-i", input_path,
"-t", str(part_end - part_start + random.randint(-10, 5)),
"-c", "copy",
output_file_path
]
subprocess.run(command) # 运行 ffmpeg 命令,分割视频

print(f"\rProcessing part {i + 1}/{num_parts}: {output_file_path}", end="") # 打印当前部分的进度信息
print(f"\rProgress: {progress:.1%}, remaining time: {remaining_time:.0f} seconds", end="") # 打印进度百分比和剩余时间估计

print("\nVideo splitting completed.") # 当所有部分处理完毕后,打印完成信息
video.close()

if __name__ == "__main__":
input_video_path = "E:\cutmp4\planet.mp4" # 输入视频文件的路径
output_videos_path = "E:\cutmp4\output" # 输出分割后的视频文件的目录
videoLength = VideoFileClip(input_video_path)
number_of_parts = int(videoLength.duration/30)-1 # 要将视频分割成的部分数量
videoLength.close()
split_video_ffmpeg(input_video_path, output_videos_path, number_of_parts) # 调用函数,将视频分割成指定数量的部分