DXVA硬件加速解码

DXVA硬件加速解码一 DXVA 介绍 DXVA 是微软公司专门定制的视频加速规范 是一种接口规范 DXVA 规范制定硬件加速解码可分四级 VLD 控制 BitStream IDCT 反余弦变换 Mocomp 运动补偿 PixelPredict PostProc 显示后处理 其中 VLD 加速等级最高 所以其包含 IDCT MoCoopm 和 PostProc IDCT 加速次之 包含 MoCoopm 和 PostProc 最

一、DXVA介绍

DXVA是微软公司专门定制的视频加速规范,是一种接口规范。DXVA规范制定硬件加速解码可分四级:VLD,控制BitStream;IDCT,反余弦变换;Mocomp,运动补偿,Pixel Prediction;PostProc,显示后处理。其中,VLD加速等级最高,所以其包含IDCTMoCoopmPostProcIDCT加速次之,包含MoCoopmPostProc;最后MoComp加速仅包含PostProc。一款显卡芯片在硬件支持DXVA规范,并不代表它就实现了DXVA所有功能。DXVA_Checker可用于检测硬件所支持的等级,DXVA_Checker运行示意图如下所示。

DXVA硬件加速解码

二、使用FFmpeg中DXVA技术硬解码

基本思路:

1.根据FFmpeg对编码器的描述,实现自定义的硬解码器。

2.通过REGISTER_ENCODEC(X,x)将自定义的视频编码器添加到视频编解码器。

3.在视频解码,根据编码器ID或编码器名称找到视频编解码器中自定义的视频解码器。

4.利用自定义的视频解码器,解码视频。

其关键步骤是:自定义解码器的实现,需要参考FFmpeg源码中,解码器的定义和接口设计。

基于DXVA的自定义解码器实现

1.熟悉FFmpeg中编解码的组织方式

下图是ffmpeg编解码组织的简单示意图。

DXVA硬件加速解码

由示意图可知,编解码器由全局链表组织,可根据编码器的名称或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.     return ff_dxva2dec_decode(avctx,data,got_frame,avpkt,&ff_h264_decoder); 
  4.  
  5. static av_cold int h264_dxva2dec_close(AVCodecContext *avctx) 
  6.     return ff_dxva2dec_close(avctx,&ff_h264_decoder); 
  7.  
  8. static av_cold int h264_dxva2dec_init(AVCodecContext *avctx) 
  9.     return ff_dxva2dec_init(avctx,&ff_h264_dxva2_decoder,&ff_h264_decoder); 
  10.  
  11. static void h264_dxva2dec_flush(AVCodecContext *avctx) 
  12.     ff_dxva2dec_flush(avctx,&ff_h264_decoder); 

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

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

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/229886.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月16日 下午3:38
下一篇 2026年3月16日 下午3:39


相关推荐

  • C语言如何计算数组的长度

    C语言如何计算数组的长度(1)借助sizeof()函数:#include&lt;stdio.h&gt;intmain(){ //定义一个整型数组,并进行初始化赋值9个数据:  intarr[]={1,2,3,4,5,6,7,8,9}; intlength=0; //计算数组中数据长度: //所有数据的字节数除以一个数据的字节数即为数据的个数:  length=sizeof(arr)/…

    2022年7月27日
    7
  • arcgis runtime for android 100.13.0 入门系列,一、初步引入与运行

    arcgis runtime for android 100.13.0 入门系列,一、初步引入与运行这是我来到csdn以来写的第一篇文章,希望能通过文字能把我的学习经过与心得分享给大家。我使用的是Kotlin来编写代码,我将默认各位具有一定的Android编程基础。言归正传,我们接下来要做的第一件事情就是使用AndroidStudio来创建一个空的新项目了我接下来的操作都是遵循arcgisandroid官方进行搭建的,读者看到的时候可能已经出了新的版本了,不过应该是小版本,arcgisandroid主体代码结构应该是不会变的,请放心阅读与搭建欢迎加入我们的QQ交流群249819194.

    2022年7月17日
    16
  • C中结构体和类的区别

    C中结构体和类的区别结构体和类同样能够定义字段 方法和构造函数 都能实例化对象 这样看来结构体和类的功能好像是一样的了 但是他们在数据的存储上是不一样的 C 结构体和类的区别问题这两种数据类型的本质区别主要是各自指向的内存位置不同 传递类的时候 主要表现为是否同时改变了源对象 1 结构体是值类型 类是引用类型虽然结构体的初始化也使用了 new 操作符 可是结构体对象依然分配在堆栈上而不是堆上

    2025年9月4日
    6
  • STM32入门开发: 介绍IIC总线、读写AT24C02(EEPROM)(采用模拟时序)

    STM32入门开发: 介绍IIC总线、读写AT24C02(EEPROM)(采用模拟时序)一 环境介绍编程软件 keil5 操作系统 win10MCU 型号 STM32F103ZET 编程方式 寄存器开发 方便程序移植到其他单片机 IIC 总线 STM32 本身支持 IIC 硬件时序的 本文采用的是模拟时序 下篇文章就介绍配置 STM32 的 IIC 硬件时序读写 AT24C02 和 AT24C08 模拟时序更加方便移植到其他单片机 通用性更高 不分 MCU 硬件时序效率更高 单每个 MCU 配置方法不同 依赖硬件本身支持 目前器件 采用 AT24C02EEPRO 存储芯

    2026年3月19日
    2
  • badboy设置中文_录制脚本badboy工具使用手册

    badboy设置中文_录制脚本badboy工具使用手册开源测试工具 badboy 使用手册一 Badboy 下载安装这里介绍的是 BadboyInstal 2 0 5 下载安装后 打开后 界面如下 图 1 二 Badboy 按钮功能讲解图 2 三 Badboy 使用大概分为以下步骤 1 录制脚本 2 参数化 3 检查点 4 脚本回放 录制脚本 打开界面 在界面 url 处填写录制地址 图 3 默认情况下 badboy 在双击打开的时候就自动开启录制 不过我们最好在一切准

    2026年3月16日
    1
  • Docker搭建n8n时,如何解决容器重启后数据丢失问题?

    Docker搭建n8n时,如何解决容器重启后数据丢失问题?

    2026年3月15日
    3

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号