You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.04 18:09:03