#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
视频合并程序
将多个mp4视频文件按照名称顺序合并成一个视频文件
支持两种方法:moviepy 和 ffmpeg
"""
import os
import glob
import subprocess
import sys
# 尝试导入moviepy,如果失败则使用ffmpeg方法
try:
from moviepy.editor import VideoFileClip, concatenate_videoclips
MOVIEPY_AVAILABLE = True
print("使用 MoviePy 进行视频合并")
except ImportError:
MOVIEPY_AVAILABLE = False
print("MoviePy 未安装,将使用 FFmpeg 方法")
def check_ffmpeg():
"""检查系统是否安装了ffmpeg"""
try:
result = subprocess.run(['ffmpeg', '-version'], capture_output=True, text=True)
return result.returncode == 0
except FileNotFoundError:
return False
def merge_videos_ffmpeg(video_files, output_filename="merged_video.mp4"):
"""使用FFmpeg合并视频"""
# 创建文件列表
list_filename = "video_list.txt"
try:
with open(list_filename, 'w') as f:
for video_file in video_files:
f.write(f"file '{video_file}'\n")
# 使用ffmpeg合并
cmd = [
'ffmpeg', '-f', 'concat', '-safe', '0',
'-i', list_filename, '-c', 'copy', output_filename, '-y'
]
print("正在使用FFmpeg合并视频...")
result = subprocess.run(cmd, capture_output=True, text=True)
if result.returncode == 0:
print(f"✓ 视频合并完成!")
print(f"输出文件: {output_filename}")
else:
print(f"FFmpeg错误: {result.stderr}")
return False
except Exception as e:
print(f"错误: {str(e)}")
return False
finally:
# 清理临时文件
if os.path.exists(list_filename):
os.remove(list_filename)
return True
def merge_videos_moviepy(video_files, output_filename="merged_video.mp4"):
"""使用MoviePy合并视频"""
try:
clips = []
for i, video_file in enumerate(video_files):
print(f"正在加载视频 {i + 1}/{len(video_files)}: {video_file}")
clip = VideoFileClip(video_file)
clips.append(clip)
print("正在合并视频...")
final_clip = concatenate_videoclips(clips, method="compose")
print(f"正在保存合并后的视频: {output_filename}")
final_clip.write_videofile(
output_filename,
codec='libx264',
audio_codec='aac',
temp_audiofile='temp-audio.m4a',
remove_temp=True
)
# 清理内存
for clip in clips:
clip.close()
final_clip.close()
print(f"✓ 视频合并完成!")
print(f"输出文件: {output_filename}")
return True
except Exception as e:
print(f"错误: 视频合并失败 - {str(e)}")
try:
for clip in clips:
clip.close()
except:
pass
return False
def merge_videos():
"""
合并视频文件的主函数
"""
# 定义视频文件列表(按照你提供的文件名顺序)
# video_files = [
# "0014_new-a-open2_segment_000_composite.mp4",
# "0014_new-a-open2_segment_001_composite.mp4",
# "0014_new-a-open2_segment_002_composite.mp4",
# "0014_new-a-open2_segment_003_composite.mp4",
# "0014_new-a-open2_segment_004_composite.mp4",
# "0014_new-a-open2_segment_005_composite.mp4",
# "0014_new-a-open2_segment_006_composite.mp4",
# "0014_new-a-open2_segment_007_composite.mp4",
# "0014_new-a-open2_segment_008_composite.mp4",
# "0014_new-a-open2_segment_009_composite.mp4"
# ]
video_files = [
"merged_video_1.mp4",
"merged_video_2.mp4"
]
# 检查文件是否存在
existing_files = []
missing_files = []
for video_file in video_files:
if os.path.exists(video_file):
existing_files.append(video_file)
print(f"✓ 找到文件: {video_file}")
else:
missing_files.append(video_file)
print(f"✗ 文件未找到: {video_file}")
if missing_files:
print(f"\n警告: 有 {len(missing_files)} 个文件未找到")
choice = input("是否继续合并已存在的文件? (y/n): ")
if choice.lower() != 'y':
print("操作已取消")
return
if not existing_files:
print("错误: 没有找到任何视频文件")
return
print(f"\n开始合并 {len(existing_files)} 个视频文件...")
# 选择合并方法
if MOVIEPY_AVAILABLE:
success = merge_videos_moviepy(existing_files)
elif check_ffmpeg():
success = merge_videos_ffmpeg(existing_files)
else:
print("错误: 既没有安装MoviePy也没有安装FFmpeg")
print("请安装其中一个:")
print("1. 安装MoviePy: pip install moviepy")
print("2. 安装FFmpeg: https://ffmpeg.org/download.html")
return
if success:
print(f"合并了 {len(existing_files)} 个视频文件")
def merge_videos_auto_detect():
"""
自动检测当前目录下的视频文件并按名称排序合并
"""
# 查找所有mp4文件
video_files = glob.glob("0014_new-a-open_segment_*_composite.mp4")
if not video_files:
print("错误: 在当前目录下没有找到匹配的视频文件")
print("请确保视频文件在当前目录下,且文件名格式为: 0014_new-a-open_segment_XXX_composite.mp4")
return
# 按名称排序
video_files.sort()
print(f"找到 {len(video_files)} 个视频文件:")
for i, file in enumerate(video_files):
print(f"{i + 1}. {file}")
choice = input("\n是否继续合并这些文件? (y/n): ")
if choice.lower() != 'y':
print("操作已取消")
return
print("\n开始合并视频...")
# 选择合并方法
if MOVIEPY_AVAILABLE:
success = merge_videos_moviepy(video_files)
elif check_ffmpeg():
success = merge_videos_ffmpeg(video_files)
else:
print("错误: 既没有安装MoviePy也没有安装FFmpeg")
print("请安装其中一个:")
print("1. 安装MoviePy: pip install moviepy")
print("2. 安装FFmpeg: https://ffmpeg.org/download.html")
return
if success:
print(f"合并了 {len(video_files)} 个视频文件")
if __name__ == "__main__":
print("视频合并程序")
print("=" * 50)
# 检查可用的工具
if MOVIEPY_AVAILABLE:
print("✓ MoviePy 可用")
else:
print("✗ MoviePy 不可用")
if check_ffmpeg():
print("✓ FFmpeg 可用")
else:
print("✗ FFmpeg 不可用")
if not MOVIEPY_AVAILABLE and not check_ffmpeg():
print("\n错误: 没有可用的视频处理工具!")
print("请安装以下工具之一:")
print("1. MoviePy: pip install moviepy")
print("2. FFmpeg: https://ffmpeg.org/download.html")
sys.exit(1)
print("\n选择合并模式:")
print("1. 使用预定义的文件列表合并")
print("2. 自动检测当前目录下的视频文件")
choice = input("\n请选择模式 (1/2): ")
if choice == "1":
merge_videos()
elif choice == "2":
merge_videos_auto_detect()
else:
print("无效选择,使用默认模式...")
merge_videos()
- 目的:将多个MP4视频文件按顺序合并成一个输出视频文件(默认名为merged_video.mp4)。
- 支持的方法:
- MoviePy:一个Python库,适合处理视频编辑任务,代码中通过加载视频片段并拼接实现合并。
- FFmpeg:一个强大的多媒体处理工具,通过命令行调用实现快速合并。
- 主要特点:
- 自动检测系统中是否安装了MoviePy或FFmpeg,优先使用MoviePy,如果不可用则fallback到FFmpeg。
- 提供两种合并模式:
- 预定义文件列表:用户在代码中手动指定要合并的视频文件。
- 自动检测:扫描当前目录下符合特定命名规则的MP4文件并按名称排序后合并。
- 检查输入文件是否存在,允许用户选择是否继续合并部分存在的文件。
- 清理临时文件(如FFmpeg的输入列表文件)以保持目录整洁。
- 将两个MP4文件合并的实现方式
代码中已经预定义了一个文件列表,用于合并两个MP4文件:
python
video_files = [
"merged_video_1.mp4",
"merged_video_2.mp4"
]
要合并这两个文件,可以通过以下步骤实现:
步骤 1:准备环境
- 确保系统中安装了以下任一工具:
- MoviePy:运行pip install moviepy安装。
- FFmpeg:从FFmpeg官网下载并配置环境变量,确保命令行能运行ffmpeg -version。
- 将两个MP4文件(merged_video_1.mp4和merged_video_2.mp4)放在代码运行的同一目录下。
步骤 2:运行代码
- 运行方式:
- 保存代码为Python文件(例如merge_videos.py)。
- 在命令行中运行:python merge_videos.py。
- 选择模式:
- 在程序启动时,选择模式1(使用预定义文件列表合并)。
- 程序会检查merged_video_1.mp4和merged_video_2.mp4是否存在。
- 如果文件存在,程序将根据可用工具(MoviePy或FFmpeg)执行合并。
步骤 3:合并逻辑
代码会根据工具执行以下操作:
-
使用MoviePy合并(merge_videos_moviepy函数):
- 加载两个视频文件为VideoFileClip对象。
- 使用concatenate_videoclips将两个片段拼接。
- 保存结果到merged_video.mp4,使用libx264视频编码和aac音频编码。
- 清理内存,关闭所有视频片段。
-
使用FFmpeg合并(merge_videos_ffmpeg函数):
-
创建一个临时文本文件video_list.txt,列出两个视频文件的路径(格式为file ‘filename’)。
-
运行FFmpeg命令:
bash
ffmpeg -f concat -safe 0 -i video_list.txt -c copy merged_video.mp4 -y
- -f concat:使用FFmpeg的concat demuxer合并文件。
- -safe 0:允许非标准文件路径。
- -c copy:直接复制视频和音频流,无需重新编码,速度快且无损。
- -y:自动覆盖输出文件。
-
删除临时文件video_list.txt。
-
步骤 4:输出
-
合并成功后,输出文件为merged_video.mp4,位于当前目录。
-
程序会打印日志,如:
✓ 视频合并完成! 输出文件: merged_video.mp4
- 关键代码片段解读
以下是与合并两个MP4文件直接相关的核心部分:
预定义文件列表
python
video_files = [
"merged_video_1.mp4",
"merged_video_2.mp4"
]
- 定义了两个要合并的文件,程序会按列表顺序拼接(先播放merged_video_1.mp4,再播放merged_video_2.mp4)。
文件存在性检查
python
existing_files = []
missing_files = []
for video_file in video_files:
if os.path.exists(video_file):
existing_files.append(video_file)
print(f"✓ 找到文件: {video_file}")
else:
missing_files.append(video_file)
print(f"✗ 文件未找到: {video_file}")
- 确保merged_video_1.mp4和merged_video_2.mp4存在。
- 如果有文件缺失,询问用户是否继续合并已有文件。
MoviePy合并逻辑
python
clips = []
for video_file in video_files:
clip = VideoFileClip(video_file)
clips.append(clip)
final_clip = concatenate_videoclips(clips, method="compose")
final_clip.write_videofile(output_filename, codec='libx264', audio_codec='aac')
- 逐个加载视频,拼接后保存。
FFmpeg合并逻辑
python
with open(list_filename, 'w') as f:
for video_file in video_files:
f.write(f"file '{video_file}'\n")
cmd = ['ffmpeg', '-f', 'concat', '-safe', '0', '-i', list_filename, '-c', 'copy', output_filename, '-y']
subprocess.run(cmd, capture_output=True, text=True)
- 创建文件列表,调用FFmpeg的无损合并。
- 注意事项
- 文件格式兼容性:
- 两个MP4文件的视频和音频编码格式需一致(如都使用H.264视频和AAC音频),否则FFmpeg的-c copy可能失败。
- MoviePy会重新编码输出文件,兼容性较高,但速度较慢且可能有质量损失。
- 性能对比:
- FFmpeg的-c copy方式速度快、无损,适合格式一致的视频。
- MoviePy适合需要额外编辑(如裁剪、添加效果)的场景,但耗时较长。
- 错误处理:
- 如果文件缺失,程序会提示用户选择是否继续。
- 如果工具未安装,程序会引导用户安装MoviePy或FFmpeg。
- 路径问题:
- 确保merged_video_1.mp4和merged_video_2.mp4在当前工作目录下。
- 如果路径包含非ASCII字符,FFmpeg的-safe 0参数可避免路径解析错误。
- 如何修改代码以只合并两个特定文件
如果只需要合并merged_video_1.mp4和merged_video_2.mp4,可以简化代码,直接调用merge_videos函数:
python
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import os
import subprocess
try:
from moviepy.editor import VideoFileClip, concatenate_videoclips
MOVIEPY_AVAILABLE = True
except ImportError:
MOVIEPY_AVAILABLE = False
def check_ffmpeg():
try:
result = subprocess.run(['ffmpeg', '-version'], capture_output=True, text=True)
return result.returncode == 0
except FileNotFoundError:
return False
def merge_videos_ffmpeg(video_files, output_filename="merged_video.mp4"):
list_filename = "video_list.txt"
with open(list_filename, 'w') as f:
for video_file in video_files:
f.write(f"file '{video_file}'\n")
cmd = ['ffmpeg', '-f', 'concat', '-safe', '0', '-i', list_filename, '-c', 'copy', output_filename, '-y']
result = subprocess.run(cmd, capture_output=True, text=True)
os.remove(list_filename)
return result.returncode == 0
def merge_videos_moviepy(video_files, output_filename="merged_video.mp4"):
clips = [VideoFileClip(video_file) for video_file in video_files]
final_clip = concatenate_videoclips(clips, method="compose")
final_clip.write_videofile(output_filename, codec='libx264', audio_codec='aac')
for clip in clips:
clip.close()
final_clip.close()
return True
if __name__ == "__main__":
video_files = ["merged_video_1.mp4", "merged_video_2.mp4"]
existing_files = [f for f in video_files if os.path.exists(f)]
if len(existing_files) != 2:
print("错误: 请确保两个视频文件都存在")
exit(1)
if MOVIEPY_AVAILABLE:
merge_videos_moviepy(existing_files)
elif check_ffmpeg():
merge_videos_ffmpeg(existing_files)
else:
print("错误: 请安装MoviePy或FFmpeg")
exit(1)
print("合并完成,输出文件:merged_video.mp4")
- 运行示例
假设目录结构如下:
/project
├── merge_videos.py
├── merged_video_1.mp4
├── merged_video_2.mp4
运行命令:
bash
python merge_videos.py