Python音视频剪辑处理:基于Python和FFmpeg的音视频剪辑命令行工具

Python音视频剪辑处理:基于Python和FFmpeg的音视频剪辑命令行工具

通过简单的命令就能实现剪切、合并、提取音频/视频和调整速度等常用操作。

```python

import os

import subprocess

import sys

from pathlib import Path

def check_ffmpeg():

"""检查系统是否安装FFmpeg"""

try:

subprocess.run(['ffmpeg', '-version'], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)

return True

except FileNotFoundError:

print("错误: 未找到FFmpeg,请先安装FFmpeg并添加到系统PATH。")

print("下载地址: https://ffmpeg.org/download.html")

return False

def get_duration(file_path):

"""获取媒体文件时长(秒)"""

cmd = [

'ffprobe', '-v', 'error', '-show_entries', 'format=duration',

'-of', 'default=noprint_wrappers=1:nokey=1', file_path

]

try:

result = subprocess.run(cmd, capture_output=True, text=True, check=True)

return float(result.stdout.strip())

except Exception as e:

print(f"获取文件时长失败: {e}")

return None

def clip_media(input_file, output_file, start_time, end_time=None, duration=None):

"""

剪辑音视频片段

参数:

input_file: 输入文件路径

output_file: 输出文件路径

start_time: 开始时间 (格式: HH:MM:SS 或秒数)

end_time: 结束时间 (格式: HH:MM:SS 或秒数)

duration: 持续时长 (秒)

"""

if not Path(input_file).exists():

print(f"错误: 输入文件 '{input_file}' 不存在")

return False

# 构建ffmpeg命令

cmd = ['ffmpeg', '-i', input_file, '-ss', start_time]

if end_time and duration:

print("警告: 同时指定了end_time和duration,将使用duration")

if duration:

cmd.extend(['-t', str(duration)])

elif end_time:

# 计算持续时间

try:

# 尝试将时间转换为秒

def time_to_seconds(t):

parts = t.split(':')

if len(parts) == 3:

return int(parts[0]) * 3600 + int(parts[1]) * 60 + float(parts[2])

elif len(parts) == 2:

return int(parts[0]) * 60 + float(parts[1])

else:

return float(t)

start_sec = time_to_seconds(start_time)

end_sec = time_to_seconds(end_time)

if end_sec <= start_sec:

print("错误: 结束时间必须大于开始时间")

return False

cmd.extend(['-t', str(end_sec - start_sec)])

except Exception as e:

print(f"时间解析错误: {e}")

return False

# 根据文件扩展名推断编码格式

output_ext = Path(output_file).suffix.lower()

if output_ext in ['.mp4']:

cmd.extend(['-c:v', 'libx264', '-c:a', 'aac'])

elif output_ext in ['.mkv']:

cmd.extend(['-c:v', 'libx264', '-c:a', 'aac'])

elif output_ext in ['.avi']:

cmd.extend(['-c:v', 'libxvid', '-c:a', 'mp3'])

elif output_ext in ['.mov']:

cmd.extend(['-c:v', 'libx264', '-c:a', 'aac'])

elif output_ext in ['.mp3']:

cmd.extend(['-vn', '-c:a', 'libmp3lame', '-q:a', '2'])

elif output_ext in ['.aac']:

cmd.extend(['-vn', '-c:a', 'aac'])

elif output_ext in ['.wav']:

cmd.extend(['-vn', '-c:a', 'pcm_s16le'])

else:

# 默认复制编码(适用于相同格式的剪辑)

cmd.extend(['-c', 'copy'])

# 覆盖输出文件(如果有)

cmd.extend(['-y'])

print(f"执行命令: {' '.join(cmd)}")

try:

subprocess.run(cmd, check=True, capture_output=True, text=True)

print(f"剪辑成功! 输出文件: {output_file}")

return True

except subprocess.CalledProcessError as e:

print(f"剪辑失败: {e.stderr}")

return False

def merge_media(input_files, output_file):

"""合并多个音视频文件"""

if len(input_files) < 2:

print("错误: 至少需要两个文件才能合并")

return False

for f in input_files:

if not Path(f).exists():

print(f"错误: 文件 '{f}' 不存在")

return False

# 创建临时文件列表

list_file = 'filelist.txt'

with open(list_file, 'w', encoding='utf-8') as f:

for file in input_files:

f.write(f"file '{Path(file).absolute()}'\n")

# 构建命令

cmd = ['ffmpeg', '-f', 'concat', '-safe', '0', '-i', list_file]

# 根据输出格式设置编码

output_ext = Path(output_file).suffix.lower()

if output_ext in ['.mp4', '.mkv']:

cmd.extend(['-c:v', 'libx264', '-c:a', 'aac'])

elif output_ext in ['.mp3']:

cmd.extend(['-vn', '-c:a', 'libmp3lame', '-q:a', '2'])

else:

cmd.extend(['-c', 'copy'])

cmd.extend(['-y', output_file])

print(f"执行命令: {' '.join(cmd)}")

try:

subprocess.run(cmd, check=True, capture_output=True, text=True)

print(f"合并成功! 输出文件: {output_file}")

os.remove(list_file)

return True

except subprocess.CalledProcessError as e:

print(f"合并失败: {e.stderr}")

os.remove(list_file)

return False

def extract_audio(input_file, output_file, bitrate='192k'):

"""提取音频"""

if not Path(input_file).exists():

print(f"错误: 输入文件 '{input_file}' 不存在")

return False

cmd = ['ffmpeg', '-i', input_file, '-vn', '-c:a', 'libmp3lame', '-b:a', bitrate, '-y', output_file]

print(f"执行命令: {' '.join(cmd)}")

try:

subprocess.run(cmd, check=True, capture_output=True, text=True)

print(f"音频提取成功! 输出文件: {output_file}")

return True

except subprocess.CalledProcessError as e:

print(f"提取失败: {e.stderr}")

return False

def extract_video(input_file, output_file):

"""提取视频(无声)"""

if not Path(input_file).exists():

print(f"错误: 输入文件 '{input_file}' 不存在")

return False

cmd = ['ffmpeg', '-i', input_file, '-an', '-c:v', 'libx264', '-y', output_file]

print(f"执行命令: {' '.join(cmd)}")

try:

subprocess.run(cmd, check=True, capture_output=True, text=True)

print(f"视频提取成功! 输出文件: {output_file}")

return True

except subprocess.CalledProcessError as e:

print(f"提取失败: {e.stderr}")

return False

def change_speed(input_file, output_file, speed=1.0):

"""调整播放速度"""

if not Path(input_file).exists():

print(f"错误: 输入文件 '{input_file}' 不存在")

return False

if speed <= 0:

print("错误: 速度必须大于0")

return False

# 判断是音频还是视频

output_ext = Path(output_file).suffix.lower()

if output_ext in ['.mp3', '.aac', '.wav']:

# 纯音频处理

cmd = ['ffmpeg', '-i', input_file, '-filter:a', f'atempo={speed}', '-y', output_file]

else:

# 视频处理(音视频一起变速)

cmd = [

'ffmpeg', '-i', input_file,

'-filter_complex', f'[0:v]setpts={1/speed}*PTS[v];[0:a]atempo={speed}[a]',

'-map', '[v]', '-map', '[a]', '-y', output_file

]

print(f"执行命令: {' '.join(cmd)}")

try:

subprocess.run(cmd, check=True, capture_output=True, text=True)

print(f"变速成功! 速度: {speed}x, 输出文件: {output_file}")

return True

except subprocess.CalledProcessError as e:

print(f"变速失败: {e.stderr}")

return False

def main():

"""命令行交互主函数"""

print("=" * 50)

print(" 音视频剪辑工具 (基于FFmpeg)")

print("=" * 50)

if not check_ffmpeg():

return

while True:

print("\n请选择操作:")

print("1. 剪辑片段 (剪切)")

print("2. 合并多个文件")

print("3. 提取音频")

print("4. 提取视频 (无声)")

print("5. 调整播放速度")

print("6. 查看文件信息")

print("0. 退出")

choice = input("\n请输入数字选择: ").strip()

if choice == '0':

print("感谢使用,再见!")

break

elif choice == '1':

input_file = input("输入文件路径: ").strip()

if not Path(input_file).exists():

print("文件不存在!")

continue

# 获取时长

duration = get_duration(input_file)

if duration:

print(f"文件总时长: {duration:.2f} 秒")

start = input("开始时间 (格式: HH:MM:SS 或秒数): ").strip()

if not start:

print("开始时间不能为空!")

continue

end = input("结束时间 (格式: HH:MM:SS 或秒数, 留空则使用时长): ").strip()

dur = input("持续时长 (秒, 优先级高于结束时间, 留空则自动计算): ").strip()

output_file = input("输出文件路径: ").strip()

if not output_file:

print("输出路径不能为空!")

continue

if dur:

try:

clip_media(input_file, output_file, start, duration=float(dur))

except ValueError:

print("持续时间必须是数字!")

else:

clip_media(input_file, output_file, start, end_time=end if end else None)

elif choice == '2':

files = []

print("输入要合并的文件路径 (每行一个,输入空行结束):")

while True:

f = input().strip()

if not f:

break

if Path(f).exists():

files.append(f)

else:

print(f"文件 '{f}' 不存在,已跳过")

if len(files) < 2:

print("至少需要2个有效文件!")

continue

output_file = input("输出文件路径: ").strip()

if not output_file:

print("输出路径不能为空!")

continue

merge_media(files, output_file)

elif choice == '3':

input_file = input("输入文件路径: ").strip()

output_file = input("输出音频路径 (如 output.mp3): ").strip()

if not input_file or not output_file:

print("路径不能为空!")

continue

bitrate = input("比特率 (默认192k, 如 128k, 320k): ").strip() or '192k'

extract_audio(input_file, output_file, bitrate)

elif choice == '4':

input_file = input("输入文件路径: ").strip()

output_file = input("输出视频路径 (如 output.mp4): ").strip()

if not input_file or not output_file:

print("路径不能为空!")

continue

extract_video(input_file, output_file)

elif choice == '5':

input_file = input("输入文件路径: ").strip()

if not Path(input_file).exists():

print("文件不存在!")

continue

try:

speed = float(input("速度倍数 (如 0.5 慢放, 2.0 快放): ").strip())

except ValueError:

print("请输入有效数字!")

continue

output_file = input("输出文件路径: ").strip()

if not output_file:

print("输出路径不能为空!")

continue

change_speed(input_file, output_file, speed)

elif choice == '6':

input_file = input("输入文件路径: ").strip()

if not Path(input_file).exists():

print("文件不存在!")

continue

duration = get_duration(input_file)

if duration:

print(f"文件时长: {duration:.2f} 秒 ({duration/60:.2f} 分钟)")

else:

print("无法获取文件信息")

else:

print("无效选择,请重新输入")

if __name__ == "__main__":

main()

```

剪辑功能与操作说明

您可以通过命令行交互完成多种剪辑任务,所有操作都基于FFmpeg实现。

· 核心剪辑功能:支持按时间点剪切片段(可指定开始/结束时间或持续时长)、合并多个文件、提取音频或视频轨道,以及调整播放速度(0.5倍慢放至2倍快放)。

· 交互式操作流程:运行程序后,您会看到清晰的功能菜单。选择对应数字后,程序会逐步引导您输入文件路径、时间参数等必要信息,并实时显示执行的FFmpeg命令,方便您了解底层操作。

· 智能格式处理:程序会根据输出文件的扩展名(如.mp4、.mp3)自动选择合理的编码参数。对于常见的剪辑操作(如相同格式的剪切),会优先使用-c copy模式,实现无损且快速的剪辑。