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

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

前面介绍利用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

时间: 2024-10-20 18:20:13

【GPU编解码】GPU硬解码---DXVA的相关文章

Android中软解码和硬解码的优先级

我们先来看一下Android系统中解码器的命名,软解码器通常是以OMX.google开头的.硬解码器通常是以OMX.[hardware_vendor]开头的,比如TI的解码器是以OMX.TI开头的.当然还有一些不遵守这个命名规范的,不以OMX.开头的,那也会被认为是软解码器. 判断规则见frameworks/av/media/libstagefright/OMXCodec.cpp: static bool IsSoftwareCodec(const char *componentName) {

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

前面介绍利用NVIDIA公司提供的CUVID库进行视频硬解码,下面将介绍利用DXVA进行硬解码. 一.DXVA介绍 DXVA是微软公司专门定制的视频加速规范,是一种接口规范.DXVA规范制定硬件加速解码可分四级:VLD,控制BitStream:IDCT,反余弦变换:Mocomp,运动补偿,Pixel Prediction:PostProc,显示后处理.其中,VLD加速等级最高,所以其包含IDCT.MoCoopm和PostProc:IDCT加速次之,包含MoCoopm和PostProc:最后MoC

【GPU编解码】GPU硬编码

[GPU编解码]GPU硬编码 一.OpenCV中的硬编码 OpenCV2.4.6中,已实现利用GPU进行写视频,编码过程由cv::gpu::VideoWriter_GPU完成,其示例程序如下. 1 int main(int argc, const char* argv[]) 2 { 3 if (argc != 2) 4 { 5 std::cerr << "Usage : video_writer <input video file>" << std:

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

问题描述:项目中,需要对高清监控视频分析处理,经测试,其解码过程所占CPU资源较多,导致整个系统处理效率不高,解码成为系统的瓶颈. 解决思路: 利用GPU解码高清视频,降低解码所占用CPU资源,加速解码过程. 一.OpenCV中的硬解码 OpenCV2.4.6中,已实现利用GPU进行读取视频,由cv::gpu::VideoReader_GPU完成,其示例程序如下. 1 int main(int argc, const char* argv[]) 2 { 3 if (argc != 2) 4 re

【GPU编解码】GPU硬编码 (转)

一.OpenCV中的硬编码 OpenCV2.4.6中,已实现利用GPU进行写视频,编码过程由cv::gpu::VideoWriter_GPU完成,其示例程序如下. 1 int main(int argc, const char* argv[]) 2 { 3 if (argc != 2) 4 { 5 std::cerr << "Usage : video_writer <input video file>" << std::endl; 6 return

vlc源码分析(六) 调用OpenMAX硬解码H.265

http://www.cnblogs.com/jiayayao/p/6964506.html H.265(HEVC)编码格式能够在得到相同编码质量视频的前提下,使用相当于H.264(AVC)一半的存储容量,虽然H.265的算法复杂度比H.264高一个数量级,但是硬件水平在不断提高,因此H.265使用场合逐渐多了起来.好多硬件厂商芯片内部实现了H.265的硬解码.最近调试了vlc-android调用OpenMAX硬解码H.265的部分,使用的硬件平台是ZX-2000,系统是Android5.1.

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

配置FFmpeg硬解码 设置解码输出格式回调 static AVPixelFormat GetHwFormat(AVCodecContext * pCodecCtx, const AVPixelFormat * pPixFmt) { // 因为采用的是DXVA2,所以这里直接写死了 return AV_PIX_FMT_DXVA2_VLD; } m_pDecoderCtx->get_format = GetHwFormat; 设置解码数据回调 // 个人理解就是将LPDIRECT3DSURFACE

H.264硬编码&硬解码

Firefly-RK3288拥有强大的VPU(视像处理器),能够流畅实现720P和1080P视频的H.264编解码: 而H.264的压缩率更高,可以更大程度更小视频的空间占用. 详细看视频演示 1. 演示介绍 基于Firefly开发板:视频监控演示: 需要两块开发板:一块开发板摄像头采集+硬编码,网络传输. 另一块开发板 网络接收.硬解码+显示. Demo中采样5GHz Wi-Fi传输,摄像头使用OV13850,或UVC camera 2. H.264技术介绍 H.264是一种高性能视频编解码技

什么是“软解码”,什么又是“硬解码”呢?

我们在计算机上播放的视频文件都是经过压缩的,因为这样有利于节约存储空间:那么在播放过程,就需要进行一个反射的解压缩过程.在以前这项工作都是由CPU来完成的,对于普通分辨率的AVI.RMVB等文件,绝大多数的CPU都可以胜任:但是发展到高清视频(1080i/p)之后,数据解压缩的工作量比以前翻了数倍,这让很多处理器叫苦不迭. 随着技术的发展,工程师们发现显卡的GPU/VPU要比CPU更适合这类大数据量的.低难度的重复工作.视频解码工作从处理器那里分离出来,交给显卡去做,这就叫做“硬解码”,例如NV