本文详细讲解基于 OpenCV 实现实时视频录制的完整方法,涵盖 VideoWriter 初始化、逐帧写入、资源释放等核心步骤,并适配 Flask Web 应用场景。
在 Python 中通过 OpenCV 录制视频,核心思路是协同操作:利用 cv2.VideoCapture 捕获每一帧图像,同时借助 cv2.VideoWriter 将帧写入磁盘文件。与仅显示实时画面的流不同,录制需要逐帧持久化保存,最终生成标准的视频文件(如 AVI 或 MP4)。以下是一套可直接投入使用的完整方案。
✅ 正确初始化 VideoWriter
cv2.VideoWriter 需要四个关键参数:输出文件路径、编码器(FourCC)、帧率(FPS)和帧尺寸(宽度×高度)。这里需要特别注意:帧尺寸必须与采集到的实际帧完全一致,否则写入会失败且程序不报错,这是一个常见陷阱。常用的编码器包括:
| 编码器 | 说明 | 兼容性 |
|---|---|---|
| 'XVID' | 跨平台通用,生成 .avi 文件 | ✅ Windows/Linux/macOS |
| 'mp4v' | MP4 容器推荐,生成 .mp4 文件 | ✅(需 OpenCV ≥ 3.0,部分系统需 FFmpeg 支持) |
| 'avc1' | H.264 编码(需系统支持) | ⚠️ 依赖底层编解码器 |
假设摄像头分辨率为 640×480,初始化代码可以这样实现:
self.fourcc = cv2.VideoWriter_fourcc(*'XVID')
self.out = cv2.VideoWriter('output.avi', self.fourcc, 20.0, (640, 480))
补充建议:使用 self.video.get(cv2.CAP_PROP_FRAME_WIDTH) 和 self.video.get(cv2.CAP_PROP_FRAME_HEIGHT) 动态获取实际分辨率,能提升代码的健壮性。
✅ 修改 get_frame() 实现边预览边录制
每读取一帧后,先写入视频文件,再编码为 JPEG 供 Web 流传输。逻辑清晰直观:
def get_frame(self):
ret, frame = self.video.read()
if ret:
# 确保尺寸匹配(关键步骤!)
if frame.shape[1] != 640 or frame.shape[0] != 480:
frame = cv2.resize(frame, (640, 480)) # 统一分辨率
self.out.write(frame) # 写入视频文件
ret, jpeg = cv2.imencode('.jpg', frame)
return jpeg.tobytes()
return None
注意:如果帧尺寸不匹配,直接使用 resize 进行调整。切勿跳过此步骤,否则可能导致写入失败且无错误提示。
✅ 安全释放资源(避免文件损坏)
在对象销毁时务必调用 release(),否则视频文件可能不完整甚至无法播放——这是很多开发者容易忽视的细节:
def __del__(self):
self.video.release()
self.out.release() # 必须显式调用!
⚠️ 注意事项与最佳实践
- 避免在多线程或多请求中复用同一个 VideoWriter 实例。在 Flask 的多 worker 场景下,每个录制会话应独占一个
VideoCamera实例,或通过全局锁控制写入,防止数据混乱。 - 文件命名需保持唯一。如果用户多次访问
/Record,建议使用时间戳动态生成文件名,避免覆盖:import datetime timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S") self.out = cv2.VideoWriter(f'output_{timestamp}.avi', self.fourcc, 20.0, (640, 480)) - 提前确认 MP4 支持情况。若使用 'mp4v' 却生成空文件,请检查 OpenCV 是否链接了 FFmpeg(执行
cv2.getBuildInformation()查看 ffmpeg 项是否为 YES)。若不支持,建议降级使用 'XVID' 搭配 .avi 格式,确保稳定性。 - 关注性能瓶颈。高分辨率(如 1080p)配合高帧率(30 FPS)容易导致磁盘 I/O 成为瓶颈。推荐在 SSD 环境下运行,或适当降低帧率和分辨率。
完成上述修改后,重启 Flask 应用,访问 /video_feed 即可实时预览并同步录制到本地的 output.avi 文件。录制结束后,使用 VLC、PotPlayer 或系统默认播放器验证,正常情况下视频文件应能正常播放。
