使用libavcodec+VAAPI实现H.264硬件编码报错求助
解决libavcodec VAAPI编码器初始化AVERROR(EINVAL)的问题
我看了你遇到的VAAPI硬件编码初始化失败的问题——命令行FFmpeg能正常跑VAAPI编码,但自己的代码调用avcodec_open2()返回-22(EINVAL),还报Mismatching AVCodecContext.pix_fmt and AVHWFramesContext.format错误,问题确实出在setupEncoder()的硬件初始化部分,你对VAAPI上下文的配置逻辑有几个关键错误,我来帮你修正并解释:
核心错误点分析
你的代码里对VAAPI的hw_device_ctx和hw_frames_ctx的处理完全搞反了,而且没有正确配置帧上下文的必要参数:
av_hwframe_ctx_alloc()是用来创建帧上下文(AVHWFramesContext)的,不是设备上下文- 你直接把设备上下文的引用赋值给
hw_frames_ctx,导致帧上下文没有正确的格式、宽高配置,和AVCodecContext.pix_fmt(VAAPI)不匹配 - 没有初始化
AVHWFramesContext的核心参数,比如格式、宽高、帧池大小
修改后的setupEncoder()硬件部分代码
把你原有的硬件初始化逻辑替换成下面这段:
if(m_hardwareAcceleration) { m_encoder->pix_fmt = AV_PIX_FMT_VAAPI; m_encoder->get_format = get_vaapi_format; // 1. 创建VAAPI设备上下文 assert(av_hwdevice_ctx_create(&m_device, AV_HWDEVICE_TYPE_VAAPI, "/dev/dri/renderD128", nullptr, 0) == 0); AVHWDeviceContext* deviceCtx = (AVHWDeviceContext*) m_device->data; assert(deviceCtx->type == AV_HWDEVICE_TYPE_VAAPI); // 2. 基于设备上下文创建帧上下文 m_encoder->hw_frames_ctx = av_hwframe_ctx_alloc(m_device); assert(m_encoder->hw_frames_ctx != nullptr); // 3. 配置帧上下文参数 AVHWFramesContext* framesCtx = (AVHWFramesContext*)m_encoder->hw_frames_ctx->data; framesCtx->format = AV_PIX_FMT_VAAPI; // 硬件帧格式,和编码器pix_fmt保持一致 framesCtx->sw_format = AV_PIX_FMT_NV12; // 软件输入格式(和你传入的frame格式匹配) framesCtx->width = s_width; framesCtx->height = s_height; framesCtx->initial_pool_size = 10; // 帧池大小,可按需调整 // 4. 初始化帧上下文(必须调用,否则参数不生效) assert(av_hwframe_ctx_init(m_encoder->hw_frames_ctx) == 0); // 5. 基于帧上下文分配硬件帧 m_hwFrame = av_frame_alloc(); assert(av_hwframe_get_buffer(m_encoder->hw_frames_ctx, m_hwFrame, 0) == 0); }
关键修改说明
- 上下文角色区分:
hw_device_ctx是全局的VAAPI设备上下文,你已经正确创建,但不需要直接赋值给编码器,编码器通过hw_frames_ctx关联设备hw_frames_ctx是绑定到编码器的帧上下文,必须基于设备上下文创建,并配置和输入/输出匹配的格式参数
- 格式匹配:
framesCtx->format设为AV_PIX_FMT_VAAPI,和m_encoder->pix_fmt严格一致,解决报错的格式不匹配问题framesCtx->sw_format设为AV_PIX_FMT_NV12,对应你传入的软件帧格式,确保av_hwframe_transfer_data能正确完成软件帧到硬件帧的转换
- 帧上下文初始化:
- 必须调用
av_hwframe_ctx_init()完成帧上下文的初始化,否则之前配置的参数不会生效
- 必须调用
- 硬件帧分配:
- 基于
hw_frames_ctx分配硬件帧,而不是原来的hw_device_ctx,这样分配的帧才属于编码器对应的帧池,能被正确使用
- 基于
额外注意事项
- 你当前的
addFrame()里,av_hwframe_transfer_data从软件帧(NV12)转到硬件帧(VAAPI)的方向是对的,但要确保硬件帧的宽高和软件帧完全一致 - 记得在析构函数里补充资源释放逻辑:
av_frame_free(&m_hwFrame)、av_buffer_unref(&m_device)、av_buffer_unref(&m_encoder->hw_frames_ctx)、avcodec_free_context(&m_encoder)等,避免内存泄漏
修改后,avcodec_open2()应该能正常初始化VAAPI编码器,硬件编码流程就能和命令行FFmpeg的表现一致了。
内容的提问来源于stack exchange,提问作者gretel99




