GPU硬解码---DXVA


前面介绍利用NVIDIA公司提供的CUVID库进行视频硬解码,下面将介绍利用DXVA进行硬解码。

一、DXVA介绍

   DXVA是微软公司专门定制的视频加速规范,是一种接口规范。DXVA规范制定硬件加速解码可分四级:VLD,控制BitStream;IDCT,反余弦变换;Mocomp,运动补偿,Pixel Prediction;PostProc,显示后处理。其中,VLD加速等级最高,所以其包含IDCTMoCoopmPostProcIDCT加速次之,包含MoCoopmPostProc;最后MoComp加速仅包含PostProc。一款显卡芯片在硬件支持DXVA规范,并不代表它就实现了DXVA所有功能。DXVA_Checker可用于检测硬件所支持的等级,DXVA_Checker运行示意图如下所示:


二、使用FFmpeg中DXVA技术硬解码

基本思路:

1.根据FFmpeg对编码器的描述,实现自定义的硬解码器。

2.通过REGISTER_ENCODEC(X,x)将自定义的视频编码器添加到视频编解码器。

3.在视频解码,根据编码器ID或编码器名称找到视频编解码器中自定义的视频解码器。

4.利用自定义的视频解码器,解码视频。

其关键步骤是:自定义解码器的实现,需要参考FFmpeg源码中,解码器的定义和接口设计。

基于DXVA的自定义解码器实现

1.熟悉FFmpeg中编解码的组织方式

下图是ffmpeg编解码组织的简单示意图:


由示意图可知,编解码器由全局链表组织,可根据编码器的名称或ID,获取编解码器。

编解码器的具体编解码的具体工作,由编解码器定义的函数指针完成。

自定义解码器时,需要按照AVCodec结构体,定义解码器的属性,然后注册到全局编解码器链表中。

2.基于DXVA解码器的定义实现

ff_h264_dxva2_decoder的定义如下:

AVCodec ff_h264_dxva2_decoder = {
    .name           = "h264_dxva2",
    .type           = AVMEDIA_TYPE_VIDEO,
    .id             = AV_CODEC_ID_H264,
    .priv_data_size = sizeof(DXVA2_DecoderContext),
    .init           = h264_dxva2dec_init,
    .close          = h264_dxva2dec_close,
    .decode         = h264_dxva2dec_decode,
    .capabilities   = CODEC_CAP_DELAY,
    .flush          = h264_dxva2dec_flush,
    .long_name      = NULL_IF_CONFIG_SMALL("H.264 (DXVA2 acceleration)"),
};

ff_h264_dxva2_decoder的函数指针对应的函数定义如下:

static int h264_dxva2dec_decode(AVCodecContext *avctx, void *data, int *got_frame,
                                  AVPacket *avpkt)
{
    return ff_dxva2dec_decode(avctx,data,got_frame,avpkt,&ff_h264_decoder);
}

static av_cold int h264_dxva2dec_close(AVCodecContext *avctx)
{
    return ff_dxva2dec_close(avctx,&ff_h264_decoder);
}

static av_cold int h264_dxva2dec_init(AVCodecContext *avctx)
{
    return ff_dxva2dec_init(avctx,&ff_h264_dxva2_decoder,&ff_h264_decoder);
}

static void h264_dxva2dec_flush(AVCodecContext *avctx)
{
    ff_dxva2dec_flush(avctx,&ff_h264_decoder);
}

上述代码,只是ff_dxva2dec_init(),ff_dxva2dec_flush(),ff_dxva2dec_decode(),ff_dxva2dec_close()的封装,具体解码的实现,由ff_dxva2dec_xxx相关函数完成,其代码实现如下:

static int get_buffer(struct AVCodecContext *avctx, AVFrame *pic)
{
    int ret;
    DXVA2_DecoderContext *ctx = (DXVA2_DecoderContext *)avctx->priv_data;
    dxva2_context *dxva2_ctx = &ctx->dxva2_ctx;
    avctx->pix_fmt = ctx->pix_fmt;
    ff_init_buffer_info(avctx, pic);
    if ((ret = ctx->get_buffer(avctx,pic)) < 0) {
        av_log(avctx, AV_LOG_ERROR, "get_buffer() failed\n");
        return ret;
    }
    if (dxva2_ctx) {
        if (av_get_dxva2_surface(dxva2_ctx, pic)) {
            av_log(NULL, AV_LOG_ERROR, "VaGrabSurface failed");
            return -1;
        }
        return 0;
    } else {
        av_log(NULL, AV_LOG_ERROR, "No dxva2 context, get buffer failed");
        return -1;
    }
}

static void release_buffer(struct AVCodecContext *avctx, AVFrame *pic)
{
    DXVA2_DecoderContext *ctx = (DXVA2_DecoderContext *)avctx->priv_data;
    dxva2_context *dxva2_ctx = &ctx->dxva2_ctx;
    if (dxva2_ctx) {
        av_release_dxva2_surface(dxva2_ctx, pic);
    }
    ctx->release_buffer(avctx,pic);
    for (int i = 0; i < 4; i++)
        pic->data[i] = NULL;
}

static enum PixelFormat get_format(AVCodecContext *p_context,
                                       const enum PixelFormat *pi_fmt)
{
     return AV_PIX_FMT_DXVA2_VLD;
}
static int check_format(AVCodecContext *avctx)
{
    uint8_t *pout;
    int psize;
    int index;
    H264Context *h;
    int ret = -1;
    AVCodecParserContext *parser = NULL;
    /* check if support */
    switch (avctx->codec_id) {
    case AV_CODEC_ID_H264:
        /* init parser & parse file */
        parser = av_parser_init(avctx->codec->id);
        if (!parser) {
            av_log(avctx, AV_LOG_ERROR, "Failed to open parser.\n");
            break;
        }
        parser->flags = PARSER_FLAG_COMPLETE_FRAMES;
        index = av_parser_parse2(parser, avctx, &pout, &psize, NULL, 0, 0, 0, 0);
        if (index < 0) {
            av_log(avctx, AV_LOG_ERROR, "Failed to parse this file.\n");
            av_parser_close(parser);
        }
        h = parser->priv_data;
        if (8 == h->sps.bit_depth_luma) {
            if (!CHROMA444 && !CHROMA422) {
                // only this will decoder switch to hwaccel
                av_parser_close(parser);
                ret = 0;
                break;
            }
        } else {
            av_log(avctx, AV_LOG_ERROR, "Unsupported file.\n");
            av_parser_close(parser);
            break;
        }
        break;
    case AV_CODEC_ID_MPEG2VIDEO:
        if (CHROMA_420 == get_mpeg2_video_format(avctx)) {
            ret = 0;
            break;
        } else {
            av_log(avctx, AV_LOG_ERROR, "Unsupported file.\n");
            break;
        }
    default:
        ret = 0;
        break;
    }
    return ret;
}

int ff_dxva2dec_decode(AVCodecContext *avctx, void *data, int *got_frame,
                                  AVPacket *avpkt,AVCodec *codec)
{
    DXVA2_DecoderContext *ctx = (DXVA2_DecoderContext *)avctx->priv_data;
    AVFrame *pic = data;
    int ret;
    ret = codec->decode(avctx, data, got_frame, avpkt);
    if (*got_frame) {
        pic->format = ctx->pix_fmt;
        av_extract_dxva2(&(ctx->dxva2_ctx),pic);
    }
    avctx->pix_fmt = ctx->pix_fmt;
    return ret;
}

int ff_dxva2dec_close(AVCodecContext *avctx,AVCodec *codec)
{
    DXVA2_DecoderContext *ctx = avctx->priv_data;
    /* release buffers and decoder */
    av_release_dxva2(&ctx->dxva2_ctx);
    /* close decoder */
    codec->close(avctx);
    return 0;
}


int ff_dxva2dec_init(AVCodecContext *avctx,AVCodec *hwcodec,AVCodec *codec)
{
    DXVA2_DecoderContext *ctx = (DXVA2_DecoderContext *)avctx->priv_data;
    dxva2_context *dxva2_ctx = (dxva2_context *)(&ctx->dxva2_ctx);
    int ret;
    ctx->initialized = 0;
    /* init pix_fmts of codec */
    if (!(hwcodec->pix_fmts)) {
        hwcodec->pix_fmts = dxva2_pixfmts;
    }
    /* check if DXVA2 supports this file */
    if (check_format(avctx) < 0)
        goto failed;

    /* init vda */
    memset(dxva2_ctx, 0, sizeof(dxva2_context));
    ret = av_create_dxva2(avctx->codec_id,dxva2_ctx);
    if (ret < 0) {
        av_log(NULL,AV_LOG_ERROR,"create dxva2 error\n");
        return 0;
    }
    ctx->pix_fmt = avctx->get_format(avctx, avctx->codec->pix_fmts);
    ret = av_setup_dxva2(dxva2_ctx, &avctx->hwaccel_context, &avctx->pix_fmt, avctx->width, avctx->height);
    if (ret < 0) {
        av_log(NULL,AV_LOG_ERROR,"error DXVA setup %d\n", ret);
        goto failed;
    }
    /* changes callback functions */
    ctx->get_buffer = avctx->get_buffer;
    avctx->get_format = get_format;
    avctx->get_buffer = get_buffer;
    avctx->release_buffer = release_buffer;
    /* init decoder */
    ret = codec->init(avctx);
    if (ret < 0) {
        av_log(avctx, AV_LOG_ERROR, "Failed to open decoder.\n");
        goto failed;
    }
    ctx->initialized = 1;
    return 0;
failed:
    ff_dxva2dec_close(avctx,codec);
    return -1;
}

void ff_dxva2dec_flush(AVCodecContext *avctx,AVCodec *codec)
{
    return codec->flush(avctx);
}

其中,在ff_dxva2dec_init()函数中,利用av_create_dxva2()函数创建dxva2_context,av_setup_dxva2()设置dxva2_context。

在ff_dxva2dec_close()函数中,利用av_release_dxva2()释放dxva2_context。

av_xxx_dxva2()相关函数,主要利用DXVA2的API接口,创建dxva2的上下文,并进行管理。

总体而言,经过四次封装,形成方便的硬解码接口。

DXVA2 API接口 ---> av_xxx_dxva2 ---> ff_dxva2dec_xxx ---> h264_dxva2dec_xxx ---> ff_h264_dxva2_decoder

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
FFmpeg是一种开源的多媒体处理工具,其中包含了dxva2解码功能。DXVA2是DirectX Video Acceleration 2的缩写,它是一种能够充分利用显卡件加速的视频解码技术。 使用FFmpeg进行dxva2解码时,可以获得以下几个优势: 1. 提高解码效率:dxva2解码能够使用显卡的件加速能力,从而大大提高视频解码的效率。相较于软解码,在相同的件条件下,dxva2解码可以更快地完成对视频的解码工作。 2. 减轻CPU负担:由于dxva2解码利用显卡进行解码,可以减轻CPU的负担,从而为其他任务提供更多的计算资源。这对于同时进行多任务处理的用户来说,尤其是一些对计算性能要求较高的场景,是非常有益的。 3. 优化视频播放体验:dxva2解码可以提供更平滑和流畅的视频播放体验。它能够更好地处理高分辨率、高比特率的视频文件,避免视频卡顿或者出现掉帧的情况。 然而,dxva2解码也存在一些限制和要求: 1. 件支持要求:要使用dxva2解码功能,需要具备支持DXVA2的显卡件。不同显卡型号和厂商支持的dxva2版本可能会有所不同,所以在使用前需要确认自己的显卡是否支持dxva2解码。 2. FFmpeg版本要求:为了能够使用dxva2解码功能,需要使用支持dxva2的FFmpeg版本。更新的FFmpeg版本通常会修复一些bug并加入新的功能,所以建议使用最新版本的FFmpeg。 总结来说,通过FFmpeg的dxva2解码功能,我们可以提高解码效率、减轻CPU负担,并获得更好的视频播放体验。需要注意的是,确保件和软件环境支持dxva2解码,并及时更新FFmpeg版本可以保证最佳的使用效果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值