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模式,实现无损且快速的剪辑。