Libavcodec(FFMPEG) D3D11硬件加速无法输出硬件帧问题排查
问题描述
我想让FFmpeg直接输出D3D11 2D纹理格式的帧,实现GPU端直接解码YUV格式并显示,无需回传CPU,但解码器始终输出软件YUV420J格式,而非预期的硬件格式。我参考hw_decode.c示例,手动配置AV_CODEC_ID_H264解码器,复用已初始化的D3DDevice和Context,使用av_hwdevice_ctx_init(也尝试过av_hwdevice_ctx_create,结果一致),且不想通过av_hwframe_transfer_data转帧,希望保留GPU纹理直接显示。以下是相关代码及编译配置,请求排查初始化或配置问题:
初始化代码
AVHWDeviceType type = AV_HWDEVICE_TYPE_D3D11VA; hw_pix_fmt = AV_PIX_FMT_D3D11; _codec = avcodec_find_decoder(AV_CODEC_ID_H264); // 不确定是否为硬件支持的解码器 int i; for (i = 0;; i++) { const AVCodecHWConfig* config = avcodec_get_hw_config(_codec, i); if (!config) { fprintf(stderr, "Decoder %s does not support device type %s.\n", _codec->name, av_hwdevice_get_type_name(type)); return; } if (config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX && config->device_type == type) { hw_pix_fmt = config->pix_fmt; break; } } if (!(_codecCtx = avcodec_alloc_context3(_codec))) { // 错误处理 return; } _hw_device_ctx = av_hwdevice_ctx_alloc(type); _device_ctx = reinterpret_cast<AVHWDeviceContext*>(_hw_device_ctx->data); _d3d11va_device_ctx = reinterpret_cast<AVD3D11VADeviceContext*>(_device_ctx->hwctx); _d3d11va_device_ctx->device = dev; _codecCtx->hw_device_ctx = av_buffer_ref(_hw_device_ctx); if (av_hwdevice_ctx_init(_codecCtx->hw_device_ctx) < 0) { return; } if (avcodec_open2(_codecCtx, _codec, NULL) < 0) { return; } _parser = av_parser_init(_codec->id); _packet = av_packet_alloc();
解码代码
void Decoder::Load(std::vector<std::byte> data) { int data_size = data.size(); int bytesRead = 0; int bytesParsed = 0; while (data_size > 0) { bytesParsed = av_parser_parse2( _parser, _codecCtx, &_packet->data, &_packet->size, (uint8_t*)&data[bytesRead], data.size() - bytesRead, AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0); bytesRead += bytesParsed; data_size -= bytesParsed; if (bytesParsed < 0) { //std::cout << "Error while parsing packet" << std::endl; return; } if (_packet->size) { int ret = avcodec_send_packet(_codecCtx, _packet); if (ret < 0) { //std::cout << "Error Sending Packet for Decoding" << std::endl; return; } while (ret >= 0) { AVFrame* frame = av_frame_alloc(); ret = avcodec_receive_frame(_codecCtx, frame); if (frame->format != AV_PIX_FMT_D3D11) { // <--- 此处永远为真,始终返回YUV420J格式而非硬件格式 break; } if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { break; } else if (ret < 0) { //std::cout << "Error During Decoding" << std::endl; return; } _frameQueue.push(frame); } } } }
FFmpeg编译配置
./../../sources/ffmpeg/configure \ --prefix=./../../installed \ --toolchain=msvc \ --arch=x86_64 \ --enable-yasm \ --enable-asm \ --enable-shared \ --enable-dxva2 \ --enable-d3d11va \ --disable-static \ --enable-gpl \ --extra-ldflags="-LIBPATH:./../../installed/lib/" \ --extra-cflags="-I./../../installed/include/"
排查要点
解码器匹配问题
- 直接使用
avcodec_find_decoder(AV_CODEC_ID_H264)可能拿到通用软件解码器,建议替换为avcodec_find_decoder_by_name("h264_d3d11va"),明确指定D3D11VA硬件解码器。 - 代码中判断帧格式时硬编码了
AV_PIX_FMT_D3D11,但D3D11VA解码的实际输出格式是AV_PIX_FMT_D3D11VA_VLD,应改为使用循环中获取的hw_pix_fmt进行判断。
- 直接使用
设备上下文初始化问题
- 复用外部D3D设备时,需确保设备创建时包含
D3D11_CREATE_DEVICE_VIDEO_SUPPORT标志,否则FFmpeg无法利用其进行硬件加速。 - 检查
av_hwdevice_ctx_init的返回值,若失败需打印错误信息,排查设备兼容性。
- 复用外部D3D设备时,需确保设备创建时包含
解码流程逻辑错误
- 当前代码先判断帧格式再处理
ret状态,顺序错误。正确流程应为:先判断avcodec_receive_frame的返回值,确认帧有效后再检查格式,避免处理无效帧。同时格式不匹配时不应直接break,应释放帧并继续循环,避免丢弃后续有效硬件帧。修正后的核心逻辑示例:ret = avcodec_receive_frame(_codecCtx, frame); if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { av_frame_free(&frame); break; } else if (ret < 0) { av_frame_free(&frame); // 错误处理 return; } if (frame->format == hw_pix_fmt) { _frameQueue.push(frame); } else { av_frame_free(&frame); }
- 当前代码先判断帧格式再处理
编译配置验证
- 执行
ffmpeg -decoders命令,确认输出中存在h264_d3d11va解码器,若不存在说明编译时--enable-d3d11va未生效,需重新编译。
- 执行
内容的提问来源于stack exchange,提问作者luckybroma
相关产品推荐
相关产品推荐

