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

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的返回值,若失败需打印错误信息,排查设备兼容性。
  • 解码流程逻辑错误

    • 当前代码先判断帧格式再处理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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 23:51:36