无ID3标签MP3:ffprobe与FFmpeg C API获取时长结果不一致
问题:缺失ID3标签的MP3文件,FFmpeg C API获取时长错误但ffprobe正常
开发基于FFmpeg C API的应用时遇到问题:有一个缺失ID3标签(以0xFFFB帧数据开头)的MP3文件,用ffprobe能正确识别时长,但自己的C++代码调用FFmpeg API得到的时长严重错误。已尝试设置scan_all_pmts参数,目前仅能通过累加AVPacket的时长得到正确结果,想了解ffprobe处理该文件的差异点。
ffprobe输出日志
[AVFormatContext @ 0000015509158a00] Opening 'OhYeah.mp3' for reading [file @ 0000015509158fc0] Setting default whitelist 'file,crypto,data' Probing mp3 score:1 size:2048 Probing mp3 score:25 size:4096 Probing mp3 score:51 size:8192 [mp3 @ 0000015509158a00] Format mp3 probed with size=8192 and score=51 [mp3 @ 0000015509158a00] pad 576 1418 [mp3 @ 0000015509158a00] Skipping 0 bytes of junk at 835. [mp3 @ 0000015509158a00] Before avformat_find_stream_info() pos: 835 bytes read:97968 seeks:1 nb_streams:1 [mp3 @ 0000015509158a00] demuxer injecting skip 1105 / discard 0 [mp3float @ 0000015509162000] skip 1105 / discard 0 samples due to side data [mp3float @ 0000015509162000] skip 1105/1152 samples [mp3 @ 0000015509158a00] All info found [mp3 @ 0000015509158a00] stream 0: start_time: 0.0250567 duration: 2.011429 [mp3 @ 0000015509158a00] format: start_time: 0.025057 duration: 2.011429 (estimate from stream) bitrate=259 kb/s [mp3 @ 0000015509158a00] After avformat_find_stream_info() pos: 42819 bytes read:130400 seeks:1 frames:50 Input #0, mp3, from 'OhYeah.mp3': Duration: 00:00:02.01, start: 0.025057, bitrate: 259 kb/s Stream #0:0, 50, 1/14112000: Audio: mp3 (mp3float), 44100 Hz, stereo, fltp, 256 kb/s Metadata: encoder : LAME3.100 [AVIOContext @ 0000015509161a80] Statistics: 130400 bytes read, 1 seeks
自定义C++代码输出日志
[AVFormatContext @ 0x236c2f0] Opening '/tmp/OhYeah.mp3' for reading [file @ 0x236c6b0] Setting default whitelist 'file,crypto,data' Probing mp3 score:1 size:2048 Probing mp3 score:25 size:4096 Probing mp3 score:51 size:8192 [mp3 @ 0x236c2f0] Format mp3 probed with size=8192 and score=51 [mp3 @ 0x236c2f0] invalid concatenated file detected - using bitrate for duration [mp3 @ 0x236c2f0] pad 576 1418 [mp3 @ 0x236c2f0] Skipping 0 bytes of junk at 835. [mp3 @ 0x236c2f0] Before avformat_find_stream_info() pos: 835 bytes read:65536 seeks:2 nb_streams:1 [mp3 @ 0x236c2f0] demuxer injecting skip 1105 / discard 0 [mp3float @ 0x2374cd0] skip 1105 / discard 0 samples due to side data [mp3float @ 0x2374cd0] skip 1105/1152 samples [mp3 @ 0x236c2f0] All info found [mp3 @ 0x236c2f0] Estimating duration from bitrate, this may be inaccurate [mp3 @ 0x236c2f0] stream 0: start_time: 0.0250567 duration: 53521.548438 [mp3 @ 0x236c2f0] format: start_time: 0.025057 duration: 53521.548438 (estimate from bit rate) bitrate=256 kb/s [mp3 @ 0x236c2f0] format: start_time: 0.025057 duration: 53521.548438 (estimate from bit rate) bitrate=256 kb/s [mp3 @ 0x236c2f0] After avformat_find_stream_info() pos: 42819 bytes read:97968 seeks:2 frames:50 Input #0, mp3, from '/tmp/OhYeah.mp3': Duration: 14:52:01.55, start: 0.025057, bitrate: 256 kb/s Stream #0:0, 50, 1/14112000: Audio: mp3 (mp3float), 44100 Hz, stereo, fltp, 256 kb/s Metadata: encoder : LAME3.100 [AVIOContext @ 0x2374780] Statistics: 97968 bytes read, 2 seeks
自定义C++代码
av_log_set_level(AV_LOG_TRACE); AVDictionary *options = nullptr; av_dict_set(&options, "scan_all_pmts", "1", AV_DICT_MATCH_CASE); AVFormatContext *format_context = nullptr; int ret = avformat_open_input(&format_context, path.c_str(), nullptr, &options); if (ret < 0) { char strError[AV_ERROR_MAX_STRING_SIZE]; av_strerror(ret, strError, sizeof(strError)); LOG(1, "Error opening input file: %s, error %s\n", path.c_str(), strError); retObj.set("status", "error"); retObj.set("error", strError); return retObj; } ret = avformat_find_stream_info(format_context, nullptr); if (ret < 0) { char strError[AV_ERROR_MAX_STRING_SIZE]; av_strerror(ret, strError, sizeof(strError)); LOG(1, "Error retrieving stream information: %s, error %s\n", path.c_str(), strError); avformat_close_input(&format_context); retObj.set("status", "error"); retObj.set("error", strError); return retObj; } av_dump_format(format_context, 0, path.c_str(), 0);
当前获取正确时长的方法
// 遍历包计算时长 float duration = 0; AVPacket pkg; av_init_packet(&pkg); pkg.data = nullptr; pkg.size = 0; while (av_read_frame(format_context, &pkg) >= 0) { if (pkg.stream_index == audioStreamIndex) { duration += av_q2d(format_context->streams[audioStreamIndex]->time_base) * pkg.duration; } av_packet_unref(&pkg); } LOG(3, "duration: %f\n", duration);
差异分析与解决建议
核心差异点
时长估算逻辑:
- ffprobe通过解析到的实际流帧信息估算时长(日志显示
estimate from stream),结果准确。 - 自定义代码中FFmpeg误判文件为无效拼接文件,转而用文件总大小/比特率估算时长,导致结果严重错误(日志显示
invalid concatenated file detected - using bitrate for duration)。
- ffprobe通过解析到的实际流帧信息估算时长(日志显示
探测阶段的读取行为:
- ffprobe在
avformat_find_stream_info()前读取了97968字节,仅1次seek;自定义代码仅读取65536字节,2次seek。更多的读取量让ffprobe能更完整地识别MP3帧结构,避免误判。
- ffprobe在
参数有效性:
scan_all_pmts是MPEG-TS流的专用参数,对MP3文件完全无效。ffprobe默认启用了针对音频文件的优化参数(如更大的analyzeduration和probesize),确保足够的数据分析。
解决建议
- 替换无效参数,设置音频专用分析参数:
将scan_all_pmts替换为analyzeduration和probesize,让FFmpeg读取更多数据来分析流信息:AVDictionary *options = nullptr; // 设置分析时长为1秒(单位:微秒) av_dict_set(&options, "analyzeduration", "1000000", 0); // 设置探测缓冲区大小为5MB(单位:字节) av_dict_set(&options, "probesize", "5000000", 0); - 保留累加AVPacket的方法:如果参数调整后仍有问题,遍历所有帧累加时长是最可靠的方案,虽然需要读取整个文件,但结果完全准确。
内容的提问来源于stack exchange,提问作者Spider
相关产品推荐
相关产品推荐

