ffmpeg关于AVStream中nb_frames变量数目不正确的问题分析

作者在开发一个视频可视化工具时遇到帧数不匹配的问题,通过FFprobe发现视频帧数在解码过程中减少1帧。经过源码调试FFmpeg,发现在解码时,只有成功解码的帧才会被计数。最后发现编码时设置AVPacket的duration为0会导致最后一帧无法解码,解决方案是确保duration不设为0。

本人最近在做一个可视化工具,可以输入视频文件,对视频文件进行转码转封装,其中需要显示处理进度,本人以视频帧总数为基准进行进度显示,比如文件中视频帧总数为1000时,现在处理到第100帧,此时进度条显示10%。

本人使用AVStream中nb_frames作为视频帧总数,数值为1200,但是本人在一帧一帧读取,解码处理时,最终得到的视频帧数是1199;比1200少1。

为此,本人使用ffprobe探帧命令行测试,得到的结果也是1199,命令行如下:

ffprobe -v error -count_frames -select_streams v:0 -show_entries stream=nb_read_frames -of default=nokey=1:noprint_wrappers=1 2022-01-08T22-32-58.mp4

本人然后又用ffprobe打印每一帧的具体情况,最终也是1199帧。

问题出在哪里,为此本人调试了ffprobe,关于ffprobe的调试工程搭建,大家可以参看我的博客vs2017调试ffprobe源码

很快,源码调试就是快,本人很快找到了原因,在ffprobe.c里面,有一个函数,如下:

static av_always_inline int process_frame(WriterContext *w,
                                          InputFile *ifile,
                                          AVFrame *frame, AVPacket *pkt,
                                          int *packet_new)
{
   
   
    AVFormatContext *fmt_ctx = ifile->fmt_ctx;
    AVCodecContext *dec_ctx = ifile->streams[pkt->stream_index].dec_ctx;
    AVCodecParameters *par = ifile->streams[pkt->stream_ind
<think>好的,用户遇到了一个错误:“ValueError: invalid literal for int() with base 10: 'nb_frames=81'”。我需要先理解这个问题的根源。 首先,用户之前提供的代码中,使用ffprobe获取视频的总帧数。原来的命令是: frame_cmd = ['ffprobe', '-v', 'error', '-select_streams', 'v:0', '-show_entries', 'stream=nb_frames', '-of', 'default=nw=1', video_path] 然后执行这个命令,并尝试将输出转换为整数。但用户得到的错误是,int()无法将字符串'nb_frames=81'转换为整数,说明ffprobe的输出格式可能是预期的纯数字,而是带有键值对的形式,比如"nb_frames=81"。 问题出在ffprobe的输出格式上。用户可能使用了正确的选项导致输出包含键名。正确的做法是确保ffprobe只输出数值部分。查看ffprobe文档,发现当使用-show_entries时,如果指定了具体的条目,比如stream=nb_frames,输出可能包含键值对,除非使用更精确的格式。 需要调整ffprobe的命令,使其只输出数值。查阅资料发现,可以使用-pretty参数或者调整输出格式。或者,可能用户的ffprobe版本同,导致输出格式一致。例如,某些版本可能默认输出为key=value,而用户需要的是纯数值。 解决方案是修改frame_cmd的输出格式。使用-of default=nw=1可能足以去掉键名。可能需要使用更精确的格式,比如csv或json,然后解析结果。例如,使用-of csv=p=0来输出纯数值,或者使用json格式提取。 另一个可能性是用户的ffprobe命令没有正确过滤输出,导致返回了额外的信息。需要确认命令是否正确执行,并且输出确实是纯数字。如果用户尝试用strip()处理后仍然得到'nb_frames=81',说明ffprobe的输出格式有问题。 所以,修改frame_cmd的参数,确保只输出数值。例如,将-show_entries stream=nb_frames改为-show_entries stream=nb_read_frames,或者调整输出格式。或者,可能需要使用更详细的参数,如使用-pretty让输出更干净,或者在输出后处理字符串,分割键值对,提取值的部分。 此外,用户之前的代码中有一个错误,当使用check=True时,如果ffprobe执行失败(比如文件存在),subprocess会抛出异常,但用户可能没有处理这种情况,导致程序崩溃。过当前的问题是关于转换int的错误,所以重点在于输出格式。 因此,正确的解决方法是修改ffprobe命令,使其输出纯数字,而是键值对。例如,使用-of default=nw=1可能足以达到目的,需要进一步调整参数,或者在获取输出后处理字符串,提取数值部分。 可能的修正方法包括: 1. 使用更精确的输出格式,如csv或json。 2. 在获取输出后,使用split('=')[1]来提取数值部分。 3. 检查ffprobe的版本,确认输出格式是否一致。 总结来说,用户需要修改获取nb_frames的逻辑,确保提取的是纯数字,而是带有键名的字符串。因此,在代码中处理frame_result.stdout时,应该解析键值对,提取值的部分,或者调整ffprobe命令使其只输出数值。</think>### 🔍 **问题定位** 错误 `ValueError: invalid literal for int() with base 10: 'nb_frames=81'` 表明 **`ffprobe` 的输出格式符合预期**,具体原因如下: 1. **`ffprobe` 输出包含键名** 命令 `ffprobe -show_entries stream=nb_frames` 返回的格式为 `nb_frames=81`(键值对),而非纯数字 `81`,导致 `int("nb_frames=81")` 报错。 2. **输出格式未指定纯数值** 尽管使用了 `-of default=nw=1`,但某些 FFmpeg 版本仍会保留键名。需通过更精确的参数控制输出。 --- ### ✅ **解决方案** #### **1. 修正 `ffprobe` 输出格式** ```python # 修改获取总帧数的命令(输出纯数值) frame_cmd = [ 'ffprobe', '-v', 'error', '-select_streams', 'v:0', '-show_entries', 'stream=nb_read_frames', # 替换为 nb_read_frames '-of', 'default=nw=1', video_path ] ``` #### **2. 手动解析键值对** ```python # 若仍需使用 nb_frames,需手动提取数值 frame_result = subprocess.run(frame_cmd, capture_output=True, text=True, check=True) total_frames = int(frame_result.stdout.strip().split('=')[1]) ``` #### **3. 完整修正代码** ```python def extract_frames(video_path, output_dir): import os import subprocess if not os.path.exists(output_dir): os.makedirs(output_dir) # 获取总帧数(解析键值对) frame_cmd = [ 'ffprobe', '-v', 'error', '-select_streams', 'v:0', '-show_entries', 'stream=nb_read_frames', '-of', 'default=nw=1', video_path ] try: frame_result = subprocess.run(frame_cmd, capture_output=True, text=True, check=True) total_frames = int(frame_result.stdout.strip()) except (subprocess.CalledProcessError, ValueError): total_frames = 100 # 默认帧数 # 构建 FFmpeg 命令 cmd = [ 'ffmpeg', '-i', video_path, '-vframes', str(total_frames), f'{output_dir}/frame_%04d.jpg' ] try: subprocess.run(cmd, check=True) except subprocess.CalledProcessError as e: print(f"FFmpeg 执行失败: {e}") ``` --- ### 📌 **关键改进点** | 改进项 | 说明 | |------|------| | **使用 `nb_read_frames`** | 直接返回纯数字帧数(如 `81`),避免键值对格式问题。 | | **手动解析键值对** | 若必须使用 `nb_frames`,需通过 `split('=')[1]` 提取数值。 | | **参数兼容性** | `nb_read_frames` 在 FFmpeg 4.0+ 中广泛支持,确保版本兼容性。 | --- ### 🧪 **验证与调试** #### **1. 手动运行 `ffprobe` 命令** ```bash # 验证输出是否为纯数字 ffprobe -v error -select_streams v:0 -show_entries stream=nb_read_frames -of default=nw=1 input.mp4 # 输出应为类似: # 81 ``` #### **2. 测试边界条件** ```python # 测试视频文件无效的情况 extract_frames("invalid_video.mp4", "output_dir") # 测试 ffprobe 返回 N/A 的情况 # 修改 frame_cmd 返回 "N/A",验证是否使用默认值 100 ``` #### **3. 日志输出(可选)** ```python print(f"总帧数: {total_frames}") ``` --- ### 📦 **扩展建议** 1. **动态帧率提取** 若需按目标帧率提取帧,可结合 `-vf fps=目标帧率`: ```python cmd = ['ffmpeg', '-i', video_path, '-vf', 'fps=30', output_pattern] ``` 2. **图像质量控制** 调整 JPEG 输出质量(默认 85,值越小质量越高): ```python cmd = ['ffmpeg', '-i', video_path, '-q:v', '2', output_pattern] ``` 3. **并行处理多个视频** 使用 `concurrent.futures` 并行处理多个视频文件。 --- ### 🛠️ **调试工具** 1. **检查 FFmpeg 版本** ```bash ffmpeg -version # 确保版本 >= 4.0 以支持 nb_read_frames ``` 2. **验证输出帧数** ```bash ls output_dir/ | wc -l # 确认生成的帧数是否与 total_frames 一致 ``` 3. **手动提取单帧测试** ```bash ffmpeg -i input.mp4 -vframes 1 test.jpg ``` ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值