D2D D3D12 渲染视频帧思路及实现

article/2025/9/26 19:20:28

写在之前

耗时2个月,写完公司的音视频处理系统。对于整个音视频处理有了基本的了解。个人感觉最坑的地方有三:

  1. 编解码
  2. 音视频录制的同步
  3. 视频预览渲染(视频帧的渲染)

由于在以后要支持同时多路1080P录制及预览,所以对于性能的要求也是非常高的。虽然目前实现是分两步走,先录制再处理,但如果能做到一步到位就非常好了。有空再去优化整个项目。

渲染

选择API

对于视频的渲染来说,已经去世的雷博给了一个DEMO。其中包含了OPENGL/DIRECT3D9/GDI等不同的实现方式,也给了我很大的帮助。
必须要说的是,我个人喜欢用新不用旧。故在第一版的demo实现之后,便想摒弃D3D9的实现方式了。毕竟他是一个十几年前的API了,且WIN10系统已经不包含d3dx9这个组件了。在选择实现的方式上,通过各方资料来看。有这么一些选择:

  • D3D11 —D3D11貌似是现有很多商业视频播放器必须包含的组件了。但D3D11在WIN10系统上的编译还是需要从原来的SDK中拷贝过来的头文件和库,不爽!= =
  • D3D12 —D3D12是微软最新的库,而且是Win10 only,十分符合我的价值观嘻嘻嘻。但由于多线程及性能上的考虑,整个API设计的非常底层。原有的很多便利的接口已经没有了,原来很多显卡驱动做的事情也必须交由程序员自己去控制。学习成本我感觉是很高, 对于没有学习过图形API的人来说。然而我还是最终选择了他去实现。
  • D2D —D2D是微软在最近几个Direct版本中,独立出来用于2D开发的一套API(貌似用于替换原有的DirectShow)。其底层还是使用D3D进行硬件加速。而且API风格与D3D很像,并可以和D3D、MF等组件方便的沟通。第二版程序便是用此实现。相对于D3D12来说,的确在绘制视频帧上有很大的方便。
  • MMF —Microsoft Media Foundation是微软最新的音视频处理体系,在知乎上看轮子哥说非常强大,看官方的Demo的确非常好用,但是由于学习成本和我的职业规划路线,并没有选择这个API。(其实应该选用此的。。。)

所以最终,我们选择了D3D12来实现,本文会将第二版D2D实现一起给出。

D3D12 RGBA渲染

我们本文关注的重点为如何将获取到的RGBA数据或YUV数据以较低的开销渲染到屏幕上。

整个程序的UI是用QT5.8来画的,所以对于创建窗口来说也是非常简单。直接一个QWidget就解决问题了。直接利用Qt中的时间来驱动整个流程。

D3D12的初始化是比较麻烦的,借鉴了微软的官方例程D3D12HelloTexture(Git代码下载),其中已经利用循环生成了一个黑白块相间的纹理,并将其放在一个三角形上显示在屏幕上。
这里写图片描述
那么我们需要做的就是:

  1. 显示为全屏的矩形
  2. 在视频帧回调过来时,将纹理更新

所以我们改变灵活顶点的格式:

        Vertex RectVertices[] ={{ { -1.0f, 1.0f, 0.0f },{ 0.0f, 0.0f } },{ { 1.0f, 1.0f, 0.0f },{ 1.0f, 0.0f } },{ { -1.0f, -1.0f, 0.0f },{ 0.0f, 1.0f } },{ { 1.0f, 1.0f, 0.0f },{ 1.0f, 0.0f } },{ { 1.0f, -1.0f, 0.0f },{ 1.0f, 1.0f } },{ { -1.0f, -1.0f, 0.0f },{ 0.0f, 1.0f } },};

这样就是将屏幕划分为上下两个三角形,并设置了每个顶点的纹理坐标:
这里写图片描述

这里只要注意一下平面的正反就行了,根据左手坐标系。

在生成纹理的时候,我们直接读取文件中的RGBA数据作为测试,纹理的像素格式设置为DXGI_FORMAT_R8G8B8A8_UNORM

std::vector<UINT8> GenerateTextureData()
{const UINT rowPitch = 1920 * 4;const UINT cellPitch = rowPitch >> 3;       // The width of a cell in the checkboard texture.const UINT cellHeight = 1080 >> 3;  // The height of a cell in the checkerboard texture.const UINT textureSize = rowPitch * 1080;static FILE *fp = fopen("d:/rgb.rgb", "rb+");static BYTE* buffer = (BYTE*)malloc(1920 * 1080 * 4);if (fread(buffer, 1, 1920 * 1080 * 4, fp) != 1920 * 1080 * 4){fseek(fp, 0, SEEK_SET);fread(buffer, 1, 1920 * 1080 * 4, fp);}std::vector<UINT8> data(textureSize);UINT8* pData = &data[0];memcpy(pData, buffer, textureSize);return data;
}//OnRenderm_commandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_texture.Get(), D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, D3D12_RESOURCE_STATE_COPY_DEST));UpdateSubresources(m_commandList.Get(), m_texture.Get(), textureUploadHeap.Get(), 0, 0, 1, &textureData);m_commandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_texture.Get(), D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE));

这样就完成了在D3D12中渲染RGBA数据的操作。

D3D12 YUV420P渲染

在D3D12中,纹理直接支持了NV12等比较经常使用的YUV颜色格式,但是在使用上可能因为我的用法有些许问题,并不能直接以一张NV12或者YUV2的纹理来直接显示YUV格式(PS:我使用NV12渲染时,按照MSDN的说法,创建了两个子纹理,分变为R8->Y和R8G8->UV格式。但显示出来为红色,调试shader发现UV分量的子纹理数据为空,求高人解答)。

直接失败以后,我采用了比较直观的方法。

  1. 创建2个或3个纹理,这里我为了方便直接创建了3个纹理(Y,U,V),格式均为A8。当源数据为YUV420P时,Y纹理分辨率与原分辨率相同,UV纹理宽高均为原分辨率的一半。
  2. 在Pixshader中,从三个纹理中分别取出Y\U\V分量,然后按照YUV->RGB的算法直接转换成RGB格式输出。

算法:
c++

在DX12中,取纹理刚好是用采样器加上纹理坐标的形式。对于三张纹理,一个点的采样刚好均为纹理坐标。所以也不用去特殊的内存操作,直接上传纹理采样计算。
其余的内容与D3D12渲染RGB数据一样不再赘述。

D2D RGB/YUV渲染

在D2D中,渲染的流程基本与D3D是一样的。但需要注意的几点是:

  1. D2D原生不支持很多普通的颜色格式。所以我们需要通过WIC去转换。
  2. 注意渲染的位置及图片大小。
  3. 特别的,对于YUV相关格式来说,我们需要创建两个WIC的位图来分别装载Y分量和UV分量。再将两个WIC位图转化为D2D位图。最后将两个D2D位图添加到CLSID_D2D1YCbCr格式的d2dEffect中。

具体的细节就不赘述了,最重要的就是设备的创建和位图的转化。这里需要花费一点功夫去理解代码。

最后

代码:Git


http://chatgpt.dhexx.cn/article/DQgsTReE.shtml

相关文章

车载通信与导航(五):D2D通信流程

取自https://blog.csdn.net/u012159948/article/details/20059927 D2D技术可以应用于移动蜂窝网络&#xff0c;以提高资源利用率和网络容量。每一个D2D通信链路占用的资源与一个蜂窝通信链路占用的相等。D2D通信将在宏蜂窝基站的控制下获得通信所需的频率资源和传输功率。它与蜂…

关于字体绘制的baseline (QT 和 D2D)

常规字体绘制API调用: QT:QPainter::drawText D2D: ID2D1RenderTarget::DrawText 以上字体绘制某些特殊效果无法达到, 我们可以用Path绘制字体: QPainterPath::addText(qreal x, qreal y, const QFont &f, const QString &text); D2D: 将字用指定字体属性绘制好,…

DirectX12(D3D12)基础教程(十三)——D2D、DWrite On D3D12与文字输出

文章目录 1、 前言2、D2D、DWrite简介3、添加D2D、DWrite基础支持文件4、D2D、DWrite基本编程步骤5、基于D3D11On12设备创建D2D渲染目标6、创建DWrite字体用D2D显示文字7、D2D、D3D11on12与D3D12同步 1、 前言 在经过了前面一系列章节的“狂轰滥炸”式的学习之后&#xff0c;如…

D2D

D2D: Device-to-Device即是设备到设备的通信。 1.认识D2D 基于蜂窝网络的D2D通信&#xff0c;或称为邻近服务&#xff08;Proximity Service,ProSe&#xff09;&#xff0c;是指用户数据可不会经过网络中转直接在终端之间传输。D2D通信与传统的通信架构有显著区别&#xff0c…

D2D与蜂窝系统间的干扰

上行频段:D2D发射端UE3对基站的干扰,蜂窝UE1对D2D接收端UE4的干扰;下行频段:基站对D2D接收端UE4的干扰,D2D发射端UE3对蜂窝UE2的干扰。 D2D复用上行资源:eNB会受到D2D的干扰,D2D中的UE都受eNB的控制,eNB为其分配资源,同时也控制最大发射功率,而且还能够将蜂窝的功控信息…

关于D2D

前段时间发现微软的DirectX是如此的强悍&#xff0c;并且对于开发windows程序是如此的重要&#xff0c;决定到msdn去看看&#xff0c;下面是对D2D的介绍&#xff0c;后面会陆续贴上&#xff0c;这是我的第一篇译文&#xff0c;有误之处&#xff0c;还请指教。 什么是D2D? D2D…

移动通信网络规划:D2D通信技术

D2D通信技术 一、什么是D2D通信技术 D2D即Device-to-Device&#xff0c;也称之为终端直通。D2D通信技术是指两个对等的用户节点之间直接进行通信的一种通信方式。 如图中所示。在由D2D通信用户组成的分散式网络中&#xff0c;每个用户节点都能发送和接收信号&#xff0c;并具有…

D2D网络架构介绍

D2D(Device-to-Device)通信,也称为邻近服务(Proximity Service,ProSe),是由3GPP组织提出的一种点到点的无线通信技术,它可以在蜂窝通信系统的控制下允许LTE终端之间利用小区无线资源直接进行通信,而不经过蜂窝网络中转。作为面向5G的关键候选技术,D2D技术能够提升通信…

车载通信与导航(七):D2D通信详解

D2D&#xff08;设备到设备&#xff09;&#xff0c;即临近终端设备之间直接进行通信的技术&#xff0c;在通信网络中&#xff0c;一旦D2D通信链路建立起来&#xff0c;传输语音或数据消息就无需基站的干预&#xff0c;这样就可以减轻通信系统中基站及核心网络的数据压力&#…

C中strchr()函数用法

strchr()函数包含于头文件&#xff1a;#include<stdio.h>中&#xff1b; 函数原型为&#xff1a;char * strchr(char * str, char/int c); 函数功能为&#xff1a;在字符串str中寻找字符C第一次出现的位置&#xff0c;并返回其位置&#xff08;地址指针&#xff09;&am…

php strchr 截断,PHP strchr() 函数

w3school 教程 PHP String 函数 查找 "world" 在 "Hello world!" 中的第一次超并返回此字符串的其余个别&#xff1a; echo strchr("Hello world!","world"); ?> 运行实例 strchr() 函数搜他串在另一字符串中的第一次常 该函数是 …

php strchr 截断,php 常用字符串函数 - strchr

...转义的字符串 三、检索字符串函数/strstr()函数和strchr()函数$str "abc123.com"; $tem strchr($str,""); $tep strstr($str, ""); $ten 以下列出开发中常用的字符串函数&#xff0c;以供自己需要的时候查阅 长度 strlen($string):得到字符…

strchr的用法

strchr&#xff08;char *s,char *c&#xff09; 查找字符c第一次出现在字符串s中的位置 返回值&#xff1a;如果找到指定的字符则返回该字符所在地址&#xff1b;否则返回NULL. 例1&#xff1a; #include <cstring> #include <cstdio> int main() {char sstri…

php strchr和strrchr,strrchr与Strchr

strrchr 取得某字符最后出现处起的字符串。 语法: string strrchr(string haystack, string needle); 返回值: 整数 函数种类: 资料处理 内容说明 本函数用来寻找字符串 haystack 中的字符 needle 最后出现位置&#xff0c;并将此位置起至字符串 haystack 结束之间的字符串返回…

php strchr 截断,php字符串处理函数详解 - strchr

...substr($add,0,strlen($add)-1); }if($word>){ $flag1; } }if(strchr($add,$keytop)){ $found1; }if(strchr($add,$keybottom)){ $found0; $end1; }if(((strchr($add, PHP处理字符串的能力非常强大,方法也是多种多样,但有的时候你需要选择一种最简单且理想的解决方法,文章…

ipv6环境搭建

2018年4月30日 iOS上架必须适配ipv6的网络&#xff0c;我也因为这个原因被拒了。为适配ipv6&#xff0c;就必须创建一个ipv6环境&#xff0c;本文介绍了使用Mac搭建ipv6环境的2种方法。 根据有、无网线&#xff0c;可分为2种场景来搭建。 有线网情况下创建ipv6环境 目标 通过…

搭建一个简单的SDN网络环境

第1小题&#xff1a;简单网络 说明&#xff1a;由于对于SDN架构的理解在学界和业界并没有统一&#xff0c;为了方便参赛队员选择&#xff0c;对于初学者&#xff0c;大赛推荐OpenFlow作为南向接口来实现SDN环境&#xff0c;以下给出分别针对采用OpenFlow和采用其他接口的具体要…

Hyperledger Fabric网络环境手动配置及其链码自动化部署

目录 5.1 网络环境的搭建 5.1.1 生成组织结构与身份证书 5.1.2 生成创世区块和通道 5.1.3 启动Fabric网络 5.1.4 创建Fabric-SDK-GO对象并建立通道 5.1.5 Fabric-SDK-Go实现链码的自动部署 5.2 链码实现 5.1 网络环境的搭建 5.1.1 生成组织结构与身份证书 Hyperledger Fabric…

Linux 实用指令 -- 网络配置(查看网络IP和网关、 ping 测试主机之间网络连通、Linux网络环境配置(指定固定ip))

文章目录 1. 网络配置1.1 查看网络IP和网关1.1.1 查看虚拟网络编辑器1.1.2 这里可以修改ip地址&#xff08;修改虚拟网络的ip&#xff09;1.1.3 这里可以修改网关&#xff08;虚拟网络的网关&#xff09;1.1.4 查看windows环境的中VMnet8网络配置 1.2 ping 测试主机之间网络连通…

Linux系统配置网络环境的图文教程(完整版)

Linux系统配置网络环境步骤 1、网络配置查看 记住NAT设置中的子网IP、子网掩码、网关IP三项&#xff0c;接下来配置文件主要是这三项。 2、编辑Linux中的网络配置文件 命令&#xff1a;vi /etc/sysconfig/network-scripts/ifcfg-ens33 #注 网络配置文件名可能会有不同&#…