FFMPEG使用自定义AVIOContext时调用avformat_find_stream_info报错
问题排查与解决方案
核心问题原因
- 你的自定义AVIO未实现seek回调:你遇到问题的MP4文件属于
moov盒位于文件尾部的类型,FFmpeg解析MP4格式时需要向后seek读取末尾的moov元数据盒来获取完整的编码参数(包括像素格式、编码等级等),缺失seek回调导致FFmpeg无法读取moov内容,自然解析不到完整的流参数。 - 原
vidBuf结构设计缺陷:你直接在读取回调中修改缓冲区指针和剩余大小,没有保留整个缓冲区的基地址和总长度,即便要实现seek也无法复位到历史偏移位置。
修复步骤
1. 重构vidBuf结构
保留缓冲区完整信息,新增当前偏移量字段:
struct vidBuf { const std::byte* base_ptr; // 整个缓冲区的起始地址 int total_size; // 缓冲区总大小 int cur_offset; // 当前读取偏移 };
2. 实现seek回调+改造read回调
// 初始化vidBuf vidBuf tmpVidBuf = { videoBuffer.data(), static_cast<int>(videoBuffer.size()), 0 }; std::uint32_t bufSize = 4096; AVIOContext *avioContext = avio_alloc_context( reinterpret_cast<std::uint8_t*>(av_malloc(bufSize)), bufSize, 0, reinterpret_cast<void*>(&tmpVidBuf), // read回调 [](void *opaque, std::uint8_t *buf, int bufSize) -> int { auto &me = *reinterpret_cast<vidBuf*>(opaque); int read_size = std::min(bufSize, me.total_size - me.cur_offset); if (read_size <= 0) return AVERROR_EOF; std::copy_n(me.base_ptr + me.cur_offset, read_size, reinterpret_cast<std::byte*>(buf)); me.cur_offset += read_size; return read_size; }, nullptr, // 新增seek回调 [](void *opaque, int64_t offset, int whence) -> int64_t { auto &me = *reinterpret_cast<vidBuf*>(opaque); switch (whence) { case SEEK_SET: me.cur_offset = static_cast<int>(offset); break; case SEEK_CUR: me.cur_offset += static_cast<int>(offset); break; case SEEK_END: me.cur_offset = me.total_size + static_cast<int>(offset); break; case AVSEEK_SIZE: // FFmpeg请求获取文件总大小,直接返回总长度 return me.total_size; default: return AVERROR(EINVAL); } // 偏移合法性修正 me.cur_offset = std::clamp(me.cur_offset, 0, me.total_size); return me.cur_offset; } );
3. 补充参数配置与错误检查
放开之前注释的探测参数配置,同时补充avformat_open_input的错误检查:
auto avFormatPtr = avformat_alloc_context(); avFormatPtr->pb = avioContext; avFormatPtr->flags |= AVFMT_FLAG_CUSTOM_IO; // 配置足够大的探测参数,确保能读取完整元数据 avFormatPtr->probesize = tmpVidBuf.total_size; avFormatPtr->max_analyze_duration = 10 * AV_TIME_BASE; // 最多分析10秒内容 // 补充打开输入的错误检查 int ret = avformat_open_input(&avFormatPtr, nullptr, nullptr, nullptr); if (ret < 0) { logerror << "Could not open input: " << makeAVError(ret) << '\n'; // 这里要加资源释放逻辑,避免内存泄漏 } if(auto ret = avformat_find_stream_info(avFormatPtr, nullptr); ret < 0) { logerror << "Could not find stream info: " << makeAVError(ret) << '\n'; }
额外注意事项
tmpVidBuf的生命周期必须覆盖avformat_open_input、avformat_find_stream_info以及后续的解封装操作,不能提前释放。- 所有FFmpeg资源用完后要记得按顺序释放:
avio_context_free、avformat_close_input、avformat_free_context,避免内存泄漏。
内容的提问来源于stack exchange,提问作者ryan
相关产品推荐
相关产品推荐

