博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【GPU编解码】GPU硬解码---DXVA
阅读量:5840 次
发布时间:2019-06-18

本文共 7982 字,大约阅读时间需要 26 分钟。

前面介绍利用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\n"); 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.\n"); 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.\n"); 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.\n"); 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.\n"); 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\n");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\n", 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.\n");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 }
View Code

其中,在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://yxvcx.baihongyu.com/

你可能感兴趣的文章
java设计模式之——代理模式
查看>>
php页面防止重复提交
查看>>
Perl DBI模块的例子
查看>>
python中str和repr区别
查看>>
升级win10后无法使用桥接网络解决方法
查看>>
如何进行跨网段的远程唤醒
查看>>
数据挖掘-同比与环比
查看>>
nginx+php详解
查看>>
怎样取php一个字符串中的某个字符
查看>>
我的友情链接
查看>>
RedHat6 管理应用服务【11】
查看>>
stm32F10x复习-1
查看>>
redis的学习使用(ubuntu系统下)
查看>>
20135226黄坤信息安全系统设计基础期末总结
查看>>
轻松快捷创建VSFTP虚拟用户
查看>>
[转]Javascript原型继承
查看>>
[转] vue异步处理错误
查看>>
CSS 3D动画概述菜鸟级解读之一
查看>>
分布式系列文章 —— 从 ACID 到 CAP / BASE
查看>>
方法签名与方法重载
查看>>