禁用解码器后avformat_find_stream_info无法获取视频宽高的问询
问题原因
你编译FFmpeg时禁用了所有解码器,而avformat_find_stream_info()在无法解码视频帧的情况下,没法从首帧中提取宽高信息——部分容器的视频宽高不会直接存在文件头的元数据里,必须解码首帧才能获取。
可行解决方案
1. 启用对应视频编码的解码器
根据你实际处理的视频编码,在编译FFmpeg时添加对应解码器的启用参数,比如:
- 处理H.264视频:添加
--enable-decoder=h264 - 处理H.265视频:添加
--enable-decoder=hevc - 处理MPEG-4视频:添加
--enable-decoder=mpeg4
启用解码器后,avformat_find_stream_info()就能正常解码首帧并填充宽高到codecpar->width和codecpar->height中。
2. 解析容器元数据或码流扩展数据(无需解码器)
如果不想启用解码器,可以通过解析容器的元数据结构或视频码流的扩展数据(extradata)来提取宽高:
方法A:使用av_codec_get_dimensions()函数
FFmpeg提供的av_codec_get_dimensions()可以尝试从AVCodecParameters的extradata中解析宽高,这个过程不需要解码器,只要FFmpeg支持该编码格式的基础解析能力(需启用对应编码的parser)。
示例代码:
int width = 0, height = 0; AVCodecParameters *codecpar = format_context->streams[0]->codecpar; // 尝试从extradata解析宽高 int ret = av_codec_get_dimensions(codecpar, &width, &height); if (ret == 0 && width > 0 && height > 0) { // 成功获取到宽高 }
编译时需确保启用对应编码的parser,比如处理H.264时添加--enable-parser=h264。
方法B:手动解析码流扩展数据
以H.264为例,可以直接解析SPS(序列参数集)中的宽高信息,这部分只需要解析NAL单元结构,不需要解码器:
示例代码:
AVCodecParameters *codecpar = format_context->streams[0]->codecpar; if (codecpar->codec_id == AV_CODEC_ID_H264 && codecpar->extradata_size > 4) { // 跳过AVCC格式的extradata头部(前4字节) uint8_t *sps_buf = codecpar->extradata + 4; int sps_len = codecpar->extradata_size - 4; // 初始化H.264 parser AVCodecParserContext *parser = av_parser_init(AV_CODEC_ID_H264); if (!parser) { // 初始化失败处理 return; } AVCodecContext *tmp_ctx = avcodec_alloc_context3(avcodec_find_decoder(AV_CODEC_ID_H264)); if (tmp_ctx) { // 解析SPS数据 int parsed_len = av_parser_parse2(parser, tmp_ctx, &sps_buf, &sps_len, sps_buf, sps_len, AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0); if (parsed_len > 0) { int width = tmp_ctx->width; int height = tmp_ctx->height; // 这里拿到宽高 } avcodec_free_context(&tmp_ctx); } av_parser_close(parser); }
方法C:解析容器原生结构
不同容器的元数据存储位置不同,比如:
- MOV/MP4:可以遍历
AVFormatContext的metadata字典,用av_dict_get()查找width、height相关字段;或者直接解析moov原子中的tkhd(轨道头部)结构。 - Matroska:直接读取
AVStream的codecpar中存储的轨道信息,部分Matroska文件会直接在轨道元数据中写入宽高。
3. 调整avformat_find_stream_info()的参数
可以给avformat_find_stream_info()传入一个包含少量解码参数的AVDictionary,比如设置更大的probesize,让FFmpeg读取更多文件数据来尝试从元数据中提取宽高,不过这个方法的兼容性取决于容器是否在文件头存储了宽高信息:
示例代码:
AVDictionary *opts = nullptr; av_dict_set(&opts, "probesize", "1000000", 0); // 设置更大的探测大小 int ret = avformat_find_stream_info(format_context, &opts); av_dict_free(&opts);
内容的提问来源于stack exchange,提问作者Raghavendra Ganiga

