基于FFmpeg的Dxva2硬解码及Direct3D显示

Posted huluwa508

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于FFmpeg的Dxva2硬解码及Direct3D显示相关的知识,希望对你有一定的参考价值。

配置FFmpeg硬解码

  1. 设置解码输出格式回调
static AVPixelFormat GetHwFormat(AVCodecContext * pCodecCtx, const AVPixelFormat * pPixFmt)
{
    // 因为采用的是DXVA2,所以这里直接写死了
    return AV_PIX_FMT_DXVA2_VLD;    
}

m_pDecoderCtx->get_format = GetHwFormat;
  1. 设置解码数据回调
// 个人理解就是将LPDIRECT3DSURFACE9转为(uint8_t *),同时得保证内存不会立即被释放 
static int GetBufferCallBack(AVCodecContext * pCodecCtx, AVFrame * pFrame, int flags)
{
    // 单路视频时 pFrame 地址为两个固定地址切换,类似于一个前台表面,一个后台表面
    if (pFrame->format != AV_PIX_FMT_DXVA2_VLD)
    {
        return -1;
    }

    // 获取解码后的数据,不可直接访问
    LPDIRECT3DSURFACE9 surface = ((LPDIRECT3DSURFACE9*)(pCodecCtx->opaque))[0];

    // 将LPDIRECT3DSURFACE9转为AVBuffer,并返回AVBufferRef
    // 类似于智能指针,增加对buffer的引用计数,默认使用av_buffer_default_free释放
    pFrame->buf[0] = av_buffer_create((uint8_t*)surface, 0, nullptr, nullptr, AV_BUFFER_FLAG_READONLY);
    if (!pFrame->buf[0])
    {
        return AVERROR(ENOMEM);
    }
    
    // 这一步拿到最终可以显示的数据,必须是data[3]
    pFrame->data[3] = (uint8_t *)surface;

    return 0;
}

m_pDecoderCtx->get_buffer2 = GetBufferCallBack;
  1. 设置解码上下文额外信息
// m_pSurface 在上一篇博文介绍了
pCodecCtx->opaque = m_pSurface; 
  1. 配置硬解加速上下文(上一篇博文的内容都是为这一步服务的)
// 为解码器上下文申请硬件加速内存
pCodecCtx->hwaccel_context = av_mallocz(sizeof(struct dxva_context));
if (!pCodecCtx->hwaccel_context)
{
    return FALSE;
}

// 设置硬件加速上下文
struct dxva_context *dxva2Ctx = (dxva_context *)pCodecCtx->hwaccel_context;
dxva2Ctx->cfg = &m_config;
dxva2Ctx->decoder = m_pDecoder;
dxva2Ctx->surface = m_pSurface;
dxva2Ctx->surface_count = m_surfaceNums;

// 对老的intel GPU 的支持
if (IsEqualGUID(m_decoderGuid, DXVADDI_Intel_ModeH264_E))
{
    dxva2Ctx->workaround |= FF_DXVA2_WORKAROUND_INTEL_CLEARVIDEO;
}

以上是关于基于FFmpeg的Dxva2硬解码及Direct3D显示的主要内容,如果未能解决你的问题,请参考以下文章

基于FFmpeg的Dxva2硬解码及Direct3D显示

Qt之dxva2硬解码

无法使用DXVA2和ffmpeg解码HEVC视频

dxva2 硬件解码器支持哪些 h264 配置文件

Qt之cuda硬解码

Android硬编码——音频编码视频编码及音视频混合