zoukankan      html  css  js  c++  java
  • 【计算机视觉】【并行计算与CUDA开发】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的定义如下:

    复制代码
     1 AVCodec ff_h264_dxva2_decoder = {
     2     .name           = "h264_dxva2",
     3     .type           = AVMEDIA_TYPE_VIDEO,
     4     .id             = AV_CODEC_ID_H264,
     5     .priv_data_size = sizeof(DXVA2_DecoderContext),
     6     .init           = h264_dxva2dec_init,
     7     .close          = h264_dxva2dec_close,
     8     .decode         = h264_dxva2dec_decode,
     9     .capabilities   = CODEC_CAP_DELAY,
    10     .flush          = h264_dxva2dec_flush,
    11     .long_name      = NULL_IF_CONFIG_SMALL("H.264 (DXVA2 acceleration)"),
    12 };
    复制代码

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

    复制代码
     1 static int h264_dxva2dec_decode(AVCodecContext *avctx, void *data, int *got_frame,
     2                                   AVPacket *avpkt)
     3 {
     4     return ff_dxva2dec_decode(avctx,data,got_frame,avpkt,&ff_h264_decoder);
     5 }
     6 
     7 static av_cold int h264_dxva2dec_close(AVCodecContext *avctx)
     8 {
     9     return ff_dxva2dec_close(avctx,&ff_h264_decoder);
    10 }
    11 
    12 static av_cold int h264_dxva2dec_init(AVCodecContext *avctx)
    13 {
    14     return ff_dxva2dec_init(avctx,&ff_h264_dxva2_decoder,&ff_h264_decoder);
    15 }
    16 
    17 static void h264_dxva2dec_flush(AVCodecContext *avctx)
    18 {
    19     ff_dxva2dec_flush(avctx,&ff_h264_decoder);
    20 }
    复制代码

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

    复制代码
      1 static int get_buffer(struct AVCodecContext *avctx, AVFrame *pic)
      2 {
      3     int ret;
      4     DXVA2_DecoderContext *ctx = (DXVA2_DecoderContext *)avctx->priv_data;
      5     dxva2_context *dxva2_ctx = &ctx->dxva2_ctx;
      6     avctx->pix_fmt = ctx->pix_fmt;
      7     ff_init_buffer_info(avctx, pic);
      8     if ((ret = ctx->get_buffer(avctx,pic)) < 0) {
      9         av_log(avctx, AV_LOG_ERROR, "get_buffer() failed
    ");
     10         return ret;
     11     }
     12     if (dxva2_ctx) {
     13         if (av_get_dxva2_surface(dxva2_ctx, pic)) {
     14             av_log(NULL, AV_LOG_ERROR, "VaGrabSurface failed");
     15             return -1;
     16         }
     17         return 0;
     18     } else {
     19         av_log(NULL, AV_LOG_ERROR, "No dxva2 context, get buffer failed");
     20         return -1;
     21     }
     22 }
     23 
     24 static void release_buffer(struct AVCodecContext *avctx, AVFrame *pic)
     25 {
     26     DXVA2_DecoderContext *ctx = (DXVA2_DecoderContext *)avctx->priv_data;
     27     dxva2_context *dxva2_ctx = &ctx->dxva2_ctx;
     28     if (dxva2_ctx) {
     29         av_release_dxva2_surface(dxva2_ctx, pic);
     30     }
     31     ctx->release_buffer(avctx,pic);
     32     for (int i = 0; i < 4; i++)
     33         pic->data[i] = NULL;
     34 }
     35 
     36 static enum PixelFormat get_format(AVCodecContext *p_context,
     37                                        const enum PixelFormat *pi_fmt)
     38 {
     39      return AV_PIX_FMT_DXVA2_VLD;
     40 }
     41 static int check_format(AVCodecContext *avctx)
     42 {
     43     uint8_t *pout;
     44     int psize;
     45     int index;
     46     H264Context *h;
     47     int ret = -1;
     48     AVCodecParserContext *parser = NULL;
     49     /* check if support */
     50     switch (avctx->codec_id) {
     51     case AV_CODEC_ID_H264:
     52         /* init parser & parse file */
     53         parser = av_parser_init(avctx->codec->id);
     54         if (!parser) {
     55             av_log(avctx, AV_LOG_ERROR, "Failed to open parser.
    ");
     56             break;
     57         }
     58         parser->flags = PARSER_FLAG_COMPLETE_FRAMES;
     59         index = av_parser_parse2(parser, avctx, &pout, &psize, NULL, 0, 0, 0, 0);
     60         if (index < 0) {
     61             av_log(avctx, AV_LOG_ERROR, "Failed to parse this file.
    ");
     62             av_parser_close(parser);
     63         }
     64         h = parser->priv_data;
     65         if (8 == h->sps.bit_depth_luma) {
     66             if (!CHROMA444 && !CHROMA422) {
     67                 // only this will decoder switch to hwaccel
     68                 av_parser_close(parser);
     69                 ret = 0;
     70                 break;
     71             }
     72         } else {
     73             av_log(avctx, AV_LOG_ERROR, "Unsupported file.
    ");
     74             av_parser_close(parser);
     75             break;
     76         }
     77         break;
     78     case AV_CODEC_ID_MPEG2VIDEO:
     79         if (CHROMA_420 == get_mpeg2_video_format(avctx)) {
     80             ret = 0;
     81             break;
     82         } else {
     83             av_log(avctx, AV_LOG_ERROR, "Unsupported file.
    ");
     84             break;
     85         }
     86     default:
     87         ret = 0;
     88         break;
     89     }
     90     return ret;
     91 }
     92 
     93 int ff_dxva2dec_decode(AVCodecContext *avctx, void *data, int *got_frame,
     94                                   AVPacket *avpkt,AVCodec *codec)
     95 {
     96     DXVA2_DecoderContext *ctx = (DXVA2_DecoderContext *)avctx->priv_data;
     97     AVFrame *pic = data;
     98     int ret;
     99     ret = codec->decode(avctx, data, got_frame, avpkt);
    100     if (*got_frame) {
    101         pic->format = ctx->pix_fmt;
    102         av_extract_dxva2(&(ctx->dxva2_ctx),pic);
    103     }
    104     avctx->pix_fmt = ctx->pix_fmt;
    105     return ret;
    106 }
    107 
    108 int ff_dxva2dec_close(AVCodecContext *avctx,AVCodec *codec)
    109 {
    110     DXVA2_DecoderContext *ctx = avctx->priv_data;
    111     /* release buffers and decoder */
    112     av_release_dxva2(&ctx->dxva2_ctx);
    113     /* close decoder */
    114     codec->close(avctx);
    115     return 0;
    116 }
    117 
    118 
    119 int ff_dxva2dec_init(AVCodecContext *avctx,AVCodec *hwcodec,AVCodec *codec)
    120 {
    121     DXVA2_DecoderContext *ctx = (DXVA2_DecoderContext *)avctx->priv_data;
    122     dxva2_context *dxva2_ctx = (dxva2_context *)(&ctx->dxva2_ctx);
    123     int ret;
    124     ctx->initialized = 0;
    125     /* init pix_fmts of codec */
    126     if (!(hwcodec->pix_fmts)) {
    127         hwcodec->pix_fmts = dxva2_pixfmts;
    128     }
    129     /* check if DXVA2 supports this file */
    130     if (check_format(avctx) < 0)
    131         goto failed;
    132 
    133     /* init vda */
    134     memset(dxva2_ctx, 0, sizeof(dxva2_context));
    135     ret = av_create_dxva2(avctx->codec_id,dxva2_ctx);
    136     if (ret < 0) {
    137         av_log(NULL,AV_LOG_ERROR,"create dxva2 error
    ");
    138         return 0;
    139     }
    140     ctx->pix_fmt = avctx->get_format(avctx, avctx->codec->pix_fmts);
    141     ret = av_setup_dxva2(dxva2_ctx, &avctx->hwaccel_context, &avctx->pix_fmt, avctx->width, avctx->height);
    142     if (ret < 0) {
    143         av_log(NULL,AV_LOG_ERROR,"error DXVA setup %d
    ", ret);
    144         goto failed;
    145     }
    146     /* changes callback functions */
    147     ctx->get_buffer = avctx->get_buffer;
    148     avctx->get_format = get_format;
    149     avctx->get_buffer = get_buffer;
    150     avctx->release_buffer = release_buffer;
    151     /* init decoder */
    152     ret = codec->init(avctx);
    153     if (ret < 0) {
    154         av_log(avctx, AV_LOG_ERROR, "Failed to open decoder.
    ");
    155         goto failed;
    156     }
    157     ctx->initialized = 1;
    158     return 0;
    159 failed:
    160     ff_dxva2dec_close(avctx,codec);
    161     return -1;
    162 }
    163 
    164 void ff_dxva2dec_flush(AVCodecContext *avctx,AVCodec *codec)
    165 {
    166     return codec->flush(avctx);
    167 }
    复制代码

    其中,在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

    参考资料:

    http://web.archiveorange.com/archive/v/4q4BhNz4oevWmMtHL3eY

  • 相关阅读:
    51nod 1138 【数学-等差数列】
    hdoj3665【简单DFS】
    hdoj3664【DP】
    51nod1270 【dp】
    51nod 1069【思维】
    关于一些数学符号和概率的阐述;
    51nod 1428【贪心】
    51nod 1133【贪心】
    51nod1127【尺取】
    51nod1126【矩阵快速幂】
  • 原文地址:https://www.cnblogs.com/huty/p/8517786.html
Copyright © 2011-2022 走看看