1. 理解avcodec_send_frame()与-22错误
当你用FFmpeg处理视频编码时,avcodec_send_frame()是个关键函数。它负责把准备好的视频帧(AVFrame)喂给编码器。但很多开发者都遇到过它突然返回-22的情况,这时候控制台可能会打印出AVERROR(EINVAL)的错误提示。这个错误代码的字面意思是"无效参数",但实际上它更像编码器在说:"我现在没法工作!"
我第一次遇到这个问题时也很懵——明明参数都检查过了,为什么还说无效?后来发现这个错误码其实是个"万能筐",可能隐藏着多种底层问题。就像你按下电灯开关灯不亮,可能是灯泡坏了、没通电,或者开关本身故障。我们需要用排除法来定位真实原因。
2. 编码器未初始化的排查与修复
2.1 检查编码器打开状态
最常见的原因就像你忘记打开水龙头却纳闷为什么没水——编码器根本没启动。在FFmpeg中,avcodec_open2()就是那个"开关"。我见过不少案例都是开发者调用了avcodec_alloc_context3()创建上下文后,直接跳过了打开步骤。
验证方法很简单,在调用avcodec_send_frame()前加个检查:
if (!avctx->codec || !avcodec_is_open(avctx)) { fprintf(stderr, "编码器未初始化!请检查avcodec_open2调用\n"); return; }2.2 编码器参数配置要点
即使调用了avcodec_open2(),参数配置不当也会导致初始化失败。去年我帮一个团队调试时发现,他们设置了AV_CODEC_FLAG_GLOBAL_HEADER却忘记配置extradata,导致编码器半瘫痪状态。建议检查:
- 分辨率是否超出编码器限制(比如某些硬件编码器要求16对齐)
- 像素格式是否被支持(用
avcodec_get_supported_framerate()验证) - 比特率/帧率设置是否合理
3. 编码器类型错误的典型场景
3.1 编码器与解码器混淆
新手最容易踩的坑就是把avcodec_find_encoder()错写成avcodec_find_decoder()。这种错误在音频处理时特别隐蔽——因为有些编解码器同时支持双向操作,但视频编码器通常区分严格。
建议在代码里显式验证:
if (!av_codec_is_encoder(codec)) { fprintf(stderr, "错误:%s不是编码器!\n", codec->name); return AVERROR(EINVAL); }3.2 硬件编码器的特殊要求
如果你在使用VAAPI、NVENC等硬件编码器,还需要注意:
- 设备初始化是否成功(检查
hw_device_ctx) - 像素格式是否匹配(比如NVENC通常要求NV12)
- 是否有足够的显存
有个真实案例:某直播应用在Windows平台正常,但在Linux上报-22错误,最后发现是缺少libva驱动。这种跨平台问题建议用av_hwdevice_iterate_types()枚举支持的设备类型。
4. 帧数据与编码状态检查
4.1 帧参数匹配性验证
即使编码器正常,帧数据不匹配也会触发EINVAL。我曾遇到一个4:2:2格式的帧试图喂给只支持4:2:0的编码器的情况。关键检查点:
if (frame->format != avctx->pix_fmt) { fprintf(stderr, "像素格式不匹配!帧:%d,编码器:%d\n", frame->format, avctx->pix_fmt); } if (frame->width != avctx->width || frame->height != avctx->height) { fprintf(stderr, "分辨率不匹配!帧:%dx%d,编码器:%dx%d\n", frame->width, frame->height, avctx->width, avctx->height); }4.2 编码器内部状态机
FFmpeg编码器内部有复杂的状态管理。当出现EAGAIN错误后立即重试可能触发EINVAL。建议的处理流程:
- 收到EAGAIN时调用
avcodec_receive_packet()清空缓冲区 - 检查
avci->draining状态 - 对于视频编码,确保PTS是严格递增的
5. 高级调试技巧与工具
5.1 启用FFmpeg调试日志
在调用avcodec_open2()前设置:
avctx->debug = FF_DEBUG_PICT_INFO | FF_DEBUG_RC; av_log_set_level(AV_LOG_DEBUG);这样可以看到编码器初始化的详细过程,有时能发现配置参数被自动修改的情况。
5.2 使用GDB/LLDB断点调试
对于偶发问题,可以在avcodec_send_frame()内部设断点。重点关注:
av_codec_is_encoder()的返回值avcodec_is_open()的判定路径encode_send_frame_internal()的预处理逻辑
6. 跨版本兼容性处理
FFmpeg不同版本的行为可能有差异。比如在4.3版本中,某些编码器对NULL帧的处理更严格。建议:
- 用
avcodec_version()检查运行时版本 - 对于关键应用,固定链接特定版本的库
- 注意
AVCodecContext结构体字段的变化
最近就遇到一个案例:某项目升级FFmpeg后出现-22错误,原来是thread_count默认值从0变成了auto,导致某些编码器异常。
7. 实战问题排查流程图
遇到-22错误时,可以按以下步骤排查:
- [编码器类型] 确认使用的是编码器而非解码器
- [初始化] 检查
avcodec_open2()是否成功调用 - [参数匹配] 验证帧参数与编码器设置一致
- [硬件支持] 如果是硬件编码,检查设备初始化
- [状态机] 确认没有处在draining或异常状态
- [版本检查] 对比FFmpeg版本差异
把这个流程图打印贴在工位上,能节省大量调试时间——这是我从三个通宵的调试经历中得出的经验。