如何用Python的OpenCV拼接含原音频的多视频?遇错误求助
问题分析与解决方案
核心问题
- OpenCV不支持音频处理:
cv2.VideoCapture只能读取视频帧,无法获取音频数据;cv2.VideoWriter也仅能写入视频流,不支持音频写入。你之前试图通过video.read()获取音频帧的逻辑完全错误,反而导致跳帧,最终输出的视频文件结构损坏,触发moov atom not found错误。 - 跳帧导致文件损坏:循环中连续调用两次
video.read(),每次调用都会读取下一帧,导致实际写入的视频帧丢失一半,文件元数据混乱,进而出现上述错误。
错误修复(先恢复正常视频拼接)
移除代码中错误的音频处理部分,让视频拼接功能回到正常状态:
import os import cv2 import time import sys def merge_videos_no_audio(directory): video_files = [] for file in os.listdir(directory): if file.endswith(".mp4"): video_files.append(os.path.join(directory, file)) video_data = [] for file_path in video_files: file_name = os.path.basename(file_path) date_time = file_name.split("_")[0] video_data.append({"file_path": file_path, "date_time": date_time}) sorted_video_data = sorted(video_data, key=lambda x: x["date_time"]) frame_size = None fps = None video_writer = None total_frames = 0 current_frame = 0 start_time = time.time() for data in sorted_video_data: video = cv2.VideoCapture(data["file_path"]) if frame_size is None: frame_size = (int(video.get(cv2.CAP_PROP_FRAME_WIDTH)), int(video.get(cv2.CAP_PROP_FRAME_HEIGHT))) if fps is None: fps = int(video.get(cv2.CAP_PROP_FPS)) total_frames += int(video.get(cv2.CAP_PROP_FRAME_COUNT)) if video_writer is None: output_path = os.path.join(directory, "merged_videos_no_audio.mp4") video_fourcc = cv2.VideoWriter_fourcc(*"mp4v") video_writer = cv2.VideoWriter(output_path, video_fourcc, fps, frame_size, True) while video.isOpened(): ret, frame = video.read() if ret: video_writer.write(frame) current_frame += 1 progress = current_frame / total_frames bar_length = 50 filled_length = int(round(bar_length * progress)) remaining_time = (time.time() - start_time) * (total_frames / current_frame - 1) eta = time.strftime("%H:%M:%S", time.gmtime(remaining_time)) bar = "#" * filled_length + "-" * (bar_length - filled_length) sys.stdout.write(f"\r|{bar}| {current_frame}/{total_frames} [{progress:.2%}] ETA: {eta}") sys.stdout.flush() else: break video.release() video_writer.release()
这段代码会生成结构正常的无音频视频,解决moov atom not found错误。
带音频的视频拼接方案(无需手动调用ffmpeg)
因为OpenCV无法处理音频,推荐使用moviepy库——它是基于ffmpeg的Python封装,无需手动执行命令,可直接整合到你的脚本中,完美支持带音频的视频拼接。
步骤1:安装依赖
pip install moviepy
步骤2:完整拼接代码
import os import time import sys from moviepy.editor import VideoFileClip, concatenate_videoclips def merge_videos_with_audio(directory): # 获取目录下所有MP4文件 video_files = [] for file in os.listdir(directory): if file.endswith(".mp4"): video_files.append(os.path.join(directory, file)) # 提取文件名中的日期时间并排序 video_data = [] for file_path in video_files: file_name = os.path.basename(file_path) date_time = file_name.split("_")[0] video_data.append({"file_path": file_path, "date_time": date_time}) sorted_video_data = sorted(video_data, key=lambda x: x["date_time"]) sorted_clips = [VideoFileClip(data["file_path"]) for data in sorted_video_data] # 计算总时长(用于进度显示) total_duration = sum(clip.duration for clip in sorted_clips) start_time = time.time() # 拼接视频 merged_clip = concatenate_videoclips(sorted_clips, method="compose") # 写入文件,同时显示进度 output_path = os.path.join(directory, "merged_videos_with_audio.mp4") merged_clip.write_videofile( output_path, codec="libx264", audio_codec="aac", progress_bar=False, verbose=False ) # 手动实现进度显示 elapsed_time = time.time() - start_time bar_length = 50 bar = "#" * bar_length sys.stdout.write(f"\r|{bar}| 100.00% 耗时: {time.strftime('%H:%M:%S', time.gmtime(elapsed_time))}") sys.stdout.flush() print() # 释放资源 for clip in sorted_clips: clip.close() merged_clip.close() if __name__ == "__main__": if len(sys.argv) != 2: print("用法: python script.py <视频目录>") sys.exit(1) merge_videos_with_audio(sys.argv[1])
代码说明
concatenate_videoclips会自动处理视频的分辨率、帧率对齐(通过method="compose"),同时保留所有原始音频。- 沿用你原来的文件名日期排序逻辑,无需修改。
- 支持命令行调用,和原脚本使用方式一致。
- 后续若要添加视频拆分功能,可使用moviepy的
subclip方法轻松实现。
关键提示
- OpenCV定位是计算机视觉处理,而非多媒体文件编辑,因此不适合处理音频相关需求。
moviepy是Python多媒体处理的常用库,覆盖视频拼接、拆分、剪辑、音频处理等功能,完全满足你后续扩展需求。
内容的提问来源于stack exchange,提问作者K Dauda
相关产品推荐
相关产品推荐

