cv2.videocapture 读取视频时长不准,因其不解析容器元数据而依赖解码器边读边估,导致 cap_prop_frame_count 和 cap_prop_pos_msec 常返回 -1 或错误值,尤其对 mp4、webm 等格式;可靠时长须从容器层获取,而非帧数×帧率推算。

cv2.VideoCapture 读取视频时长不准,为什么?
OpenCV 的 cv2.VideoCapture 本身不直接解析容器元数据,而是靠解码器“边读边估”,所以 cv2.CAP_PROP_FRAME_COUNT 和 cv2.CAP_PROP_POS_MSEC 常返回 -1 或错误值,尤其对 MP4(H.264/H.265)、WebM 等封装格式。
- 真正可靠的时长必须从容器层获取,不是靠逐帧跳转或帧数 × 帧率推算
-
cv2.CAP_PROP_FPS通常能读对,但若视频无明确 FPS 字段(如某些 VFR 视频),它可能返回 0 或默认值 25 - 用
cap.get(cv2.CAP_PROP_POS_FRAMES)查当前帧号没问题,但不能反推总帧数
moviepy 用 VideoFileClip.duration 比 cv2 更准,但要注意加载开销
moviepy 底层调用 ffmpeg 解析容器头,VideoFileClip.duration 返回的是真实时长(秒),精度到毫秒级;VideoFileClip.fps 是从流中提取的平均帧率,对 CFR 视频可靠,VFR 下是估算值。
- 首次访问
.duration或.fps会触发一次轻量 ffmpeg 调用(不加载全部帧),但对象初始化本身有 IO 和解析延迟 - 如果只想要元数据,别调
.get_frame(0)或.subclip(),否则会触发完整解码准备 - 路径含中文或空格时,确保传入的是 raw string 或已正确转义的
str
想零依赖又准?用 ffprobe 命令行 + json 解析最稳
ffmpeg 自带的 ffprobe 是事实标准,它不依赖 Python 包,也不启动解码器,纯读包头和索引。配合 subprocess 调用,比 moviepy 更轻、比 cv2 更准。
- 命令:
ffprobe -v quiet -print_format json -show_entries format=duration:stream=avg_frame_rate,r_frame_rate -of default=nw=1 "video.mp4" -
format.duration是总时长(秒),stream.avg_frame_rate是平均帧率(如 "30/1"),需用eval()或正则转 float - 注意:不同流(视频/音频)可能有多个
stream条目,要过滤codec_type == "video" - Windows 上若报
'ffprobe' is not recognized,说明没加 ffmpeg 到 PATH,别硬改 Python 路径,直接用绝对路径调用更可控
cv2 和 moviepy 混用时,帧率单位和时长单位容易对不上
cv2 的 cv2.CAP_PROP_FPS 是 float(单位:帧/秒),moviepy 的 .fps 也是 float,但两者来源不同;而时长上,cv2 给不出可靠值,moviepy 的 .duration 是秒,ffprobe 的 duration 也是秒 —— 表面一致,实际含义可能错位。
立即学习“Python免费学习笔记(深入)”;
- 不要用
int(cap.get(cv2.CAP_PROP_FPS) * clip.duration)算总帧数,这在 VFR 视频里必然错 - moviepy 的
.write_videofile()默认按输入 clip 的.fps写出,但如果原始视频是 VFR,这个 fps 只是参考值,输出仍是 CFR - 真要精确逐帧处理,优先用
cv2.VideoCapture+ 手动计数,时长用 ffprobe 单独查,别交叉混用元数据
最麻烦的其实是 VFR 视频——没有单一帧率,时长也不能简单用帧数除帧率。这时候别纠结“哪个库更方便”,老实用 ffprobe 查 duration,用 cv2 一帧帧读,中间不做任何帧率假设。










