Dxva

来源:互联网 发布:淘宝账号注册自动 编辑:程序博客网 时间:2024/05/14 12:11

step 1.IDirectXVideoDecoderService::CreateVideoDecoder

step 2. IDirectXVideoDecoder::BeginFrame

step 3. <one or more times>

step 3-1. IDirectXVideoDecoder::GetBuffer

step 3-2. IDirectXVideoDecoder::ReleaseBuffer

step 3-3. IDirectXVideoDecoder::Execute

step 4.IDirectXVideoDecoder::EndFrame

 

 

 

前段时间做项目时需要用DXVA对高清视频做硬件解码。期间走了不少弯路。这里总结一下。

DXVA硬解的流程

详细的流程和示例代码可以参考微软的官方文档Supporting DXVA 2.0 in DirectShow。下面用DX9为例列举一下基本流程:

创建IDirect3DDevice9的指针pt_D3dDeviceIDirectXVideoDecoderService的指针pt_DecServiceIDirectXVideoAccelerationService的指针pt_AccService

利用pt_AccServiceCreateSurface方法创建IDirect3DSurface9的指针数组pppp中的每一项都是IDirect3DSurface9的指针;这个surface缓存区就是DXVA硬解后图片帧的存放位置。

利用pt_DecServiceCreateVideoDecoder方法创建IDirectXVideoDecoder的指针pt_decoder。这就是我们硬解的解码器结构。

逐帧解码。

逐帧解码的过程就是标准的dxva流程:

调用pt_decoderBeginFrame,需要注意的是这一帧解码后图片的存放位置在BeginFrame的参数中指定。

调用pt_decoderGetBuffer填充相应的bufferbuffer中填充的数据就是需要交给GPU解码的数据。

调用pt_decoderExecute方法,函数正确返回则解码无误。

调用EndFrame,此时从对应的Surface中寻找解码好的图片帧。

调用Execute做一些结果校验,这一步是可选的。

跳回第一步开始解码下一帧。

创建解码器时的注意事项

在创建解码器的时候,有一个参数有点让人很没头绪,就是那个GUID

GUID的含义这里就跳过了。在DXVA这里,可以把GUID理解成硬件的解码能力等级上的一种标记。比如对H264Mpeg2VC1这三种高清视频,微软就根据硬件解码能力的不同,划分了若干个GUID

H264

h264guid分了6个等级:

#define DXVA_ModeH264_MoComp_NoFGT              DXVA_ModeH264_A

#define DXVA_ModeH264_MoComp_FGT                DXVA_ModeH264_B

#define DXVA_ModeH264_IDCT_NoFGT                DXVA_ModeH264_C

#define DXVA_ModeH264_IDCT_FGT                  DXVA_ModeH264_D

#define DXVA_ModeH264_VLD_NoFGT                 DXVA_ModeH264_E

#define DXVA_ModeH264_VLD_FGT                   DXVA_ModeH264_F

 

上面的宏定义选自WinDDK里面的头文件dxva.h。宏定义中的MoComp, IDCT, VLD, NoFGT, FGT其实都是H264编码中的技术术语。FGTNoFGT指的是一个叫做“电影胶片质感技术”的东西。所谓胶片感,是指在不牺牲压缩能力的同时又保持视频源特有的颗粒信息。现在大部分的H264视频对这个好像都没有特别的支持。所以这里我们可以忽略掉它,而来重点看另外的三个分级标准:运动补偿(MoComp),离散余弦逆变换(IDCT)和变长数据流(VLD)

这三个等级分别表示了硬件解码器对H264码流解码的支持程度,并且从前到后支持能力是逐渐变强的。比如用MoCompguid创建出解码器,则硬件只能做运动补偿这么一个功能;如果用IDCT,则除了运动补偿外,还可以把离散余弦逆变换的计算任务也交给硬件;而如果是VLD的话,则表示硬件可以直接把H264一帧的码流直接还原成图像。

Mpeg2VC1

mpeg2guid分级:

DEFINE_GUID(DXVA2_ModeMPEG2_MoComp, 0xe6a9f44b,0x61b0, 0x4563,0x9e,0xa4,0x63,0xd2,0xa3,0xc6,0xfe,0x66);

DEFINE_GUID(DXVA2_ModeMPEG2_IDCT,   0xbf22ad00, 0x03ea,0x4690,0x80,0x77,0x47,0x33,0x46,0x20,0x9b,0x7e);

DEFINE_GUID(DXVA2_ModeMPEG2_VLD,    0xee27417f, 0x5e28, 0x4e65,0xbe,0xea,0x1d,0x26,0xb5,0x08,0xad,0xc9);

 

vc1guid分级:

#define DXVA2_ModeVC1_PostProc      DXVA2_ModeVC1_A

#define DXVA2_ModeVC1_MoComp        DXVA2_ModeVC1_B

#define DXVA2_ModeVC1_IDCT          DXVA2_ModeVC1_C

#define DXVA2_ModeVC1_VLD           DXVA2_ModeVC1_D

 

可以看到,mpeg2vc1也分别定义了自己的guid分级。其中MoComp, IDCTVLD三个级别的含义基本和h264类似。vc1PostProc指的是vc1的显示后处理。这些术语的详细定义最好也是去翻翻对应的标准才能理解的比较清楚,这里就不再赘述了。

那么怎么解码

在大致理清了前面的问题后我们再次把精力回到dxva的解码流程中。

前面流程描述的第2步中说要调用GetBuffer填写对应的数据给硬件解码。结合上面说到的GUID,我们基本就能够明确到底要填什么数据了。比如用vldguidh264就是要填写自己的nal层数据流,mpeg2填写自己es数据流里面的packetdata。然后按着api的流程解码,我们就能在对应的surface中拿到想要的视频帧图像了。

对于具体该填写哪些buffer,分别该各自填写什么,可以参考微软官方文档About DXVA 2.0。这篇文档下面给出的一系列链接都详细的描述了不同的高清格式该填什么样的数据。

如果觉得文档的描述还是不够详细,可以去参考鼎鼎大名的ffmpeg项目,里面有对dxva硬件高清的支持。

如何窥探自己的GPU能力

前面大致分析了如何调用硬件解码的问题。现在有还一个很现实的问题需要谈一下:怎么才能知道我电脑的显卡支持哪些guid呢?

有两种方法。一个是自己编写代码,同样参考上面提到的文档Supporting DXVA 2.0 inDirectShow。另一种方法是用一个叫做DXVAChecker的工具。上网搜索一下还是很容易下载到的。

让人哭笑不得的一些事实

如果各个硬件厂商能够按照微软的这个条条框框来做,那么事情到此为止的确可以告一段落了。但是,事实却并非如此。硬件厂商们除了实现了标准规定的几个guid,又去自定义了自己的一些guid——差异化的同时,也带来了问题。

我在自己的环境中测过两款显卡:一个是nvidia的,另一个是inteli5处理器内置的核芯显卡。nvidia比较给力,它基本只实现了微软标准中定义的那几个guid,而且硬解出来的数据也没有发现问题。intel的显卡则不然,如h264vc1,除了前面提到的那些guid之外,还有自己的clearvideoguid。当然,问题的关键不在此,而在于尽管intel实现了h264_vld_nofgth264_vld_fgt的标准guid,但是如果用这个guid去创建解码器,出来的视频帧就完全是花屏。想正确解码,只能用那些后缀为clearvideoguid。正是因为这个问题,现在有很多的播放器在intel的核芯显卡上硬解放视频会出现花屏的问题。我这里测试到过的有这个问题的播放器有:splayervlc,暴风影音。

其实在intel的平台上,想用硬解放视频最好还是用intel自己的多媒体库Intel Media SDK。比如QQ影音,它在intel的平台上高清会在标题栏上显示intel高清加速的字样,我猜想他就是调用了Intel Media SDK

继续谈播放器花屏的问题

之前搜帖子时,在高清论坛里会发现有部分解码器发烧友们在抱怨各种硬解时花屏的问题。追究一下,原因只有两类:

驱动问题

这个问题多见于intel的集成显卡。一般把显卡驱动更新到最新就可以解决。顺便吐槽一下,如果你查看一下intel的驱动描述,你就会发现诸如“fix bugs in videoacceleration”之类的字样。

解码器问题

要讲清楚这个问题,还是得拿dxva硬解的流程来说事。我们回到“调用GetBuffer填写待解码的数据”这个步骤。拿标准的h264 vld解码来说,要填的buffer有四种:h264帧的数据流(bistream),帧图像的描述信息(picture parameter),帧内数据流中各个片段的描述信息(sliceinfomation), 量化矩阵(quantization matrix)。其中帧图像描述和量化矩阵,通过分析帧的数据头获得,片段描述通过分析数据流获得。如果这些数据填写的不正确,就会导致解码花屏。

不过这也不是这么绝对。从理论上来讲,还原一帧图像,只需要有数据流、帧图像描述信息和量化矩阵就可以。所以如果硬件的容错性好的话,即便是片段描述信息错误,也能够正确解码;如若不然,就是花屏。实际测试时,发现nvidia的显卡容错性还是强于intel的核芯显卡的——不好意思,又给nvidia打广告了。

不说技术细节了

前面讲的大致都是技术问题。这里来讲几款播放器,来看看他们对硬件高清的支持情况。

QQ影音

硬件加速的兼容性最好,测试的时候没有出现过问题。但这并妨碍我对这款播放器的深恶痛绝。它调用了大量的第三方解码器,包括大家熟知的ffdshowffdshowwindows平台下的开源解码器,用了ffmpeg/libav项目,自己本身是以gpl协议发布的。我们都知道,如果采用了gpl的项目,不管是以二进制形式还是源代码形式,软件自己必须得开源。但QQ影音毫无节操的对此事充耳不闻,不得不让人汗颜。可怜FFmpeg/libav项目组,只能站在道德的制高点上把QQ影音挂在自己的Hall Of Shame上。

mpc-hc

windows下可以算是最经典的播放器框架了吧。没错,大多数播放器争相抄袭的对象就是它。它对h264mpeg2硬解的支持都非常好,可惜在intel平台上不支持vc1视频的硬解。

暴风影音

杂七杂八也收罗了一大堆的解码器,前面说过在intel核芯显卡上解码h264还是存在问题,而对mpeg2视频的只能用dxva1.0进行硬解。顺便提一下,它在ffmpeg/libav耻辱柱上也是榜上有名的。

射手

h264解码和暴风问题相同。不支持mpeg2视频的硬解,vc1只支持独立显卡。对不同环境和视频格式的支持可以上它开发者主页上去看。在最开始射手也受gpl感染,卷入了开源之争。不过后来改造态度良好,迅速开源。而且据说还像mpc-hc项目反馈了代码。——在此不得不感慨人和人之间差别真是大呀。

vlc

非常好用的开源播放器,在Linux也支持硬件加速,用了ffmpeg项目。h264解码与暴风有相同问题。

windows media player

接下来隆重登场的就是微软自家的wmp了。这是我见过的最神奇的一款播放器。基本上只要是高清视频它都能启用硬解,而且基本不错在花屏神马之类的问题。

最后的感想

有一点还是感触颇深的:接口不统一是滋生bug的一个绝佳温床。当硬件高清开始流行的时候,各大厂商都铆住了劲儿在宣扬着自己的技术:intelclearvideonvidiapurevideoatiavivo。但是商战为什么要蔓延到接口api上呢?就按照微软的统一标准不是一件很美妙的事情吗?厂家们在竞争过程中各出奇招,为难的是程序员,不知所措的却是最终前段时间做项目时需要用DXVA对高清视频做硬件解码。期间走了不少弯路。这里总结一下。

DXVA硬解的流程

详细的流程和示例代码可以参考微软的官方文档Supporting DXVA 2.0 in DirectShow。下面用DX9为例列举一下基本流程:

创建IDirect3DDevice9的指针pt_D3dDeviceIDirectXVideoDecoderService的指针pt_DecServiceIDirectXVideoAccelerationService的指针pt_AccService

利用pt_AccServiceCreateSurface方法创建IDirect3DSurface9的指针数组pppp中的每一项都是IDirect3DSurface9的指针;这个surface缓存区就是DXVA硬解后图片帧的存放位置。

利用pt_DecServiceCreateVideoDecoder方法创建IDirectXVideoDecoder的指针pt_decoder。这就是我们硬解的解码器结构。

逐帧解码。

逐帧解码的过程就是标准的dxva流程:

调用pt_decoderBeginFrame,需要注意的是这一帧解码后图片的存放位置在BeginFrame的参数中指定。

调用pt_decoderGetBuffer填充相应的bufferbuffer中填充的数据就是需要交给GPU解码的数据。

调用pt_decoderExecute方法,函数正确返回则解码无误。

调用EndFrame,此时从对应的Surface中寻找解码好的图片帧。

调用Execute做一些结果校验,这一步是可选的。

跳回第一步开始解码下一帧。

创建解码器时的注意事项

在创建解码器的时候,有一个参数有点让人很没头绪,就是那个GUID

GUID的含义这里就跳过了。在DXVA这里,可以把GUID理解成硬件的解码能力等级上的一种标记。比如对H264Mpeg2VC1这三种高清视频,微软就根据硬件解码能力的不同,划分了若干个GUID

H264

h264guid分了6个等级:

#define DXVA_ModeH264_MoComp_NoFGT              DXVA_ModeH264_A

#define DXVA_ModeH264_MoComp_FGT                DXVA_ModeH264_B

#define DXVA_ModeH264_IDCT_NoFGT                DXVA_ModeH264_C

#define DXVA_ModeH264_IDCT_FGT                  DXVA_ModeH264_D

#define DXVA_ModeH264_VLD_NoFGT                 DXVA_ModeH264_E

#define DXVA_ModeH264_VLD_FGT                   DXVA_ModeH264_F

上面的宏定义选自WinDDK里面的头文件dxva.h。宏定义中的MoComp, IDCT, VLD, NoFGT, FGT其实都是H264编码中的技术术语。FGTNoFGT指的是一个叫做“电影胶片质感技术”的东西。所谓胶片感,是指在不牺牲压缩能力的同时又保持视频源特有的颗粒信息。现在大部分的H264视频对这个好像都没有特别的支持。所以这里我们可以忽略掉它,而来重点看另外的三个分级标准:运动补偿(MoComp),离散余弦逆变换(IDCT)和变长数据流(VLD)

这三个等级分别表示了硬件解码器对H264码流解码的支持程度,并且从前到后支持能力是逐渐变强的。比如用MoCompguid创建出解码器,则硬件只能做运动补偿这么一个功能;如果用IDCT,则除了运动补偿外,还可以把离散余弦逆变换的计算任务也交给硬件;而如果是VLD的话,则表示硬件可以直接把H264一帧的码流直接还原成图像。

Mpeg2VC1

mpeg2guid分级:

DEFINE_GUID(DXVA2_ModeMPEG2_MoComp, 0xe6a9f44b,0x61b0, 0x4563,0x9e,0xa4,0x63,0xd2,0xa3,0xc6,0xfe,0x66);

DEFINE_GUID(DXVA2_ModeMPEG2_IDCT,   0xbf22ad00, 0x03ea,0x4690,0x80,0x77,0x47,0x33,0x46,0x20,0x9b,0x7e);

DEFINE_GUID(DXVA2_ModeMPEG2_VLD,    0xee27417f, 0x5e28,0x4e65,0xbe,0xea,0x1d,0x26,0xb5,0x08,0xad,0xc9);

vc1guid分级:

#define DXVA2_ModeVC1_PostProc      DXVA2_ModeVC1_A

#define DXVA2_ModeVC1_MoComp        DXVA2_ModeVC1_B

#define DXVA2_ModeVC1_IDCT          DXVA2_ModeVC1_C

#define DXVA2_ModeVC1_VLD           DXVA2_ModeVC1_D

可以看到,mpeg2vc1也分别定义了自己的guid分级。其中MoComp, IDCTVLD三个级别的含义基本和h264类似。vc1PostProc指的是vc1的显示后处理。这些术语的详细定义最好也是去翻翻对应的标准才能理解的比较清楚,这里就不再赘述了。

那么怎么解码

在大致理清了前面的问题后我们再次把精力回到dxva的解码流程中。

前面流程描述的第2步中说要调用GetBuffer填写对应的数据给硬件解码。结合上面说到的GUID,我们基本就能够明确到底要填什么数据了。比如用vldguidh264就是要填写自己的nal层数据流,mpeg2填写自己es数据流里面的packetdata。然后按着api的流程解码,我们就能在对应的surface中拿到想要的视频帧图像了。

对于具体该填写哪些buffer,分别该各自填写什么,可以参考微软官方文档About DXVA 2.0。这篇文档下面给出的一系列链接都详细的描述了不同的高清格式该填什么样的数据。

如果觉得文档的描述还是不够详细,可以去参考鼎鼎大名的ffmpeg项目,里面有对dxva硬件高清的支持。

如何窥探自己的GPU能力

前面大致分析了如何调用硬件解码的问题。现在有还一个很现实的问题需要谈一下:怎么才能知道我电脑的显卡支持哪些guid呢?

有两种方法。一个是自己编写代码,同样参考上面提到的文档Supporting DXVA 2.0 inDirectShow。另一种方法是用一个叫做DXVAChecker的工具。上网搜索一下还是很容易下载到的。

让人哭笑不得的一些事实

如果各个硬件厂商能够按照微软的这个条条框框来做,那么事情到此为止的确可以告一段落了。但是,事实却并非如此。硬件厂商们除了实现了标准规定的几个guid,又去自定义了自己的一些guid——差异化的同时,也带来了问题。

我在自己的环境中测过两款显卡:一个是nvidia的,另一个是inteli5处理器内置的核芯显卡。nvidia比较给力,它基本只实现了微软标准中定义的那几个guid,而且硬解出来的数据也没有发现问题。intel的显卡则不然,如h264vc1,除了前面提到的那些guid之外,还有自己的clearvideoguid。当然,问题的关键不在此,而在于尽管intel实现了h264_vld_nofgth264_vld_fgt的标准guid,但是如果用这个guid去创建解码器,出来的视频帧就完全是花屏。想正确解码,只能用那些后缀为clearvideoguid。正是因为这个问题,现在有很多的播放器在intel的核芯显卡上硬解放视频会出现花屏的问题。我这里测试到过的有这个问题的播放器有:splayervlc,暴风影音。

其实在intel的平台上,想用硬解放视频最好还是用intel自己的多媒体库Intel Media SDK。比如QQ影音,它在intel的平台上高清会在标题栏上显示intel高清加速的字样,我猜想他就是调用了Intel Media SDK

继续谈播放器花屏的问题

之前搜帖子时,在高清论坛里会发现有部分解码器发烧友们在抱怨各种硬解时花屏的问题。追究一下,原因只有两类:

驱动问题

这个问题多见于intel的集成显卡。一般把显卡驱动更新到最新就可以解决。顺便吐槽一下,如果你查看一下intel的驱动描述,你就会发现诸如“fix bugs in videoacceleration”之类的字样。

解码器问题

要讲清楚这个问题,还是得拿dxva硬解的流程来说事。我们回到“调用GetBuffer填写待解码的数据”这个步骤。拿标准的h264 vld解码来说,要填的buffer有四种:h264帧的数据流(bistream),帧图像的描述信息(picture parameter),帧内数据流中各个片段的描述信息(sliceinfomation), 量化矩阵(quantization matrix)。其中帧图像描述和量化矩阵,通过分析帧的数据头获得,片段描述通过分析数据流获得。如果这些数据填写的不正确,就会导致解码花屏。

不过这也不是这么绝对。从理论上来讲,还原一帧图像,只需要有数据流、帧图像描述信息和量化矩阵就可以。所以如果硬件的容错性好的话,即便是片段描述信息错误,也能够正确解码;如若不然,就是花屏。实际测试时,发现nvidia的显卡容错性还是强于intel的核芯显卡的——不好意思,又给nvidia打广告了。

不说技术细节了

前面讲的大致都是技术问题。这里来讲几款播放器,来看看他们对硬件高清的支持情况。

QQ影音

硬件加速的兼容性最好,测试的时候没有出现过问题。但这并妨碍我对这款播放器的深恶痛绝。它调用了大量的第三方解码器,包括大家熟知的ffdshowffdshowwindows平台下的开源解码器,用了ffmpeg/libav项目,自己本身是以gpl协议发布的。我们都知道,如果采用了gpl的项目,不管是以二进制形式还是源代码形式,软件自己必须得开源。但QQ影音毫无节操的对此事充耳不闻,不得不让人汗颜。可怜FFmpeg/libav项目组,只能站在道德的制高点上把QQ影音挂在自己的Hall Of Shame上。

mpc-hc

windows下可以算是最经典的播放器框架了吧。没错,大多数播放器争相抄袭的对象就是它。它对h264mpeg2硬解的支持都非常好,可惜在intel平台上不支持vc1视频的硬解。

暴风影音

杂七杂八也收罗了一大堆的解码器,前面说过在intel核芯显卡上解码h264还是存在问题,而对mpeg2视频的只能用dxva1.0进行硬解。顺便提一下,它在ffmpeg/libav耻辱柱上也是榜上有名的。

射手

h264解码和暴风问题相同。不支持mpeg2视频的硬解,vc1只支持独立显卡。对不同环境和视频格式的支持可以上它开发者主页上去看。在最开始射手也受gpl感染,卷入了开源之争。不过后来改造态度良好,迅速开源。而且据说还像mpc-hc项目反馈了代码。——在此不得不感慨人和人之间差别真是大呀。

vlc

非常好用的开源播放器,在Linux也支持硬件加速,用了ffmpeg项目。h264解码与暴风有相同问题。

windows media player

接下来隆重登场的就是微软自家的wmp了。这是我见过的最神奇的一款播放器。基本上只要是高清视频它都能启用硬解,而且基本不错在花屏神马之类的问题。

最后的感想

有一点还是感触颇深的:接口不统一是滋生bug的一个绝佳温床。当硬件高清开始流行的时候,各大厂商都铆住了劲儿在宣扬着自己的技术:intelclearvideonvidiapurevideoatiavivo。但是商战为什么要蔓延到接口api上呢?就按照微软的统一标准不是一件很美妙的事情吗?厂家们在竞争过程中各出奇招,为难的是程序员,不知所措的却是最终的用户。的用户。

1 0
原创粉丝点击