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

一、DXVA介绍

  DXVA是微软公司专门定制的视频加速规范,是一种接口规范。DXVA规范制定硬件加速解码可分四级:VLD,控制BitStream;IDCT,反余弦变换;Mocomp,运动补偿,Pixel Prediction;PostProc,显示后处理。其中,VLD加速等级最高,所以其包含IDCT、MoCoopm和PostProc;IDCT加速次之,包含MoCoopm和PostProc;最后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 }

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

【计算机视觉】【并行计算与CUDA开发】GPU硬解码---DXVA的更多相关文章

  1. 【视频开发】GPU编解码:GPU硬解码---DXVA

    GPU编解码:GPU硬解码---DXVA 一.DXVA介绍 DXVA是微软公司专门定制的视频加速规范,是一种接口规范.DXVA规范制定硬件加速解码可分四级:VLD,控制BitStream;IDCT,反 ...

  2. 【GPU编解码】GPU硬解码---DXVA

    前面介绍利用NVIDIA公司提供的CUVID库进行视频硬解码,下面将介绍利用DXVA进行硬解码. 一.DXVA介绍 DXVA是微软公司专门定制的视频加速规范,是一种接口规范.DXVA规范制定硬件加速解 ...

  3. 【GPU编解码】GPU硬解码---DXVA (转)

    前面介绍利用NVIDIA公司提供的CUVID库进行视频硬解码,下面将介绍利用DXVA进行硬解码. 一.DXVA介绍 DXVA是微软公司专门定制的视频加速规范,是一种接口规范.DXVA规范制定硬件加速解 ...

  4. 【计算机视觉】【并行计算与CUDA开发】GPU硬解码---CUVID

    问题描述:项目中,需要对高清监控视频分析处理,经测试,其解码过程所占CPU资源较多,导致整个系统处理效率不高,解码成为系统的瓶颈. 解决思路: 利用GPU解码高清视频,降低解码所占用CPU资源,加速解 ...

  5. 【并行计算与CUDA开发】基于NVIDIA显卡的硬编解码的一点心得 (完结)

    原文:基于NVIDIA显卡的硬编解码的一点心得 (完结) 1.硬解码软编码方法:大体流程,先用ffmpeg来读取视频文件的包,接着开启两个线程,一个用于硬解码,一个用于软编码,然后将读取的包传给解码器 ...

  6. 【GPU编解码】GPU硬解码---CUVID

    问题描述:项目中,需要对高清监控视频分析处理,经测试,其解码过程所占CPU资源较多,导致整个系统处理效率不高,解码成为系统的瓶颈. 解决思路: 利用GPU解码高清视频,降低解码所占用CPU资源,加速解 ...

  7. 【计算机视觉】【并行计算与CUDA开发】GPU硬编码

    一.OpenCV中的硬编码 OpenCV2.4.6中,已实现利用GPU进行写视频,编码过程由cv::gpu::VideoWriter_GPU完成,其示例程序如下. 1 int main(int arg ...

  8. 【计算机视觉】【并行计算与CUDA开发】OpenCV中GPU模块使用

    CUDA基本使用方法 在介绍OpenCV中GPU模块使用之前,先回顾下CUDA的一般使用方法,其基本步骤如下: 1.主机代码执行:2.传输数据到GPU:3.确定grid,block大小: 4.调用内核 ...

  9. 【并行计算与CUDA开发】英伟达硬件加速编解码

    硬件加速 并行计算 OpenCL OpenCL API VS SDK 英伟达硬件编解码方案 基于 OpenCL 的 API 自己写一个编解码器 使用 SDK 中的编解码接口 使用编码器对于 OpenC ...

随机推荐

  1. unity 用代码控制动画的播放的进度

    https://answers.unity.com/questions/1225328/imported-animated-object-and-slider-tutorial.html using ...

  2. Qt 程序自动重启的实现

    正常退出调用exit() 或quit()就行,想要自已重启可按下面代码: void XXX:onRestart() { //类中调用 qApp->exit(); } 主main函数中处理 int ...

  3. 如何使用Hasu USB to USB Controller Converter刷写tmk固件交换Caps和Ctrl

    相关链接 购买Hasu USB to USB Controller Converter:https://www.1upkeyboards.com/shop/controllers/usb-to-usb ...

  4. python不换行输出

    python默认的print是换行输出的.要想实现不换行输出,方法如下: python 2.X版本: print('要在print后面加个逗号-> , '), python 3.X版本: pri ...

  5. 公共钥匙盒(CCF)【模拟】

    问题描述 有一个学校的老师共用N个教室,按照规定,所有的钥匙都必须放在公共钥匙盒里,老师不能带钥匙回家.每次老师上课前,都从公共钥匙盒里找到自己上课的教室的钥匙去开门,上完课后,再将钥匙放回到钥匙盒中 ...

  6. 题解 BZOJ1026 & luogu P2657 [SCOI2009]windy数 数位DP

    BZOJ & luogu 看到某大佬AC,本蒟蒻也决定学习一下玄学的数位$dp$ (以上是今年3月写的话(叫我鸽神$qwq$)) 思路:数位$DP$ 提交:2次 题解:(见代码) #inclu ...

  7. PHP mysqli_init() 函数

    定义和用法 mysqli_init() 函数初始化 MySQLi 并返回 mysqli_real_connect() 使用的对象. 语法 mmysqli_init();   实例 mysqli_ini ...

  8. Enable file editing in Visual Studio's debug mode

    Visual Studio 使用及调试必知必会 http://www.cnblogs.com/luminji/p/3287728.html How do I enable file editing i ...

  9. php+文件夹上传实例

    核心原理: 该项目核心就是文件分块上传.前后端要高度配合,需要双方约定好一些数据,才能完成大文件分块,我们在项目中要重点解决的以下问题. * 如何分片: * 如何合成一个文件: * 中断了从哪个分片开 ...

  10. Java进阶知识12 Hibernate多对多双向关联(Annotation+XML实现)

    1.Annotation 注解版 1.1.应用场景(Student-Teacher):当学生知道有哪些老师教,老师也知道自己教哪些学生时,可用双向关联 1.2.创建Teacher类和Student类 ...