写在之前

耗时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

D2D D3D12 渲染视频帧思路及实现相关推荐

  1. WebRTC视频帧渲染前处理——视频帧裁剪

    十一假期写了一篇<WebRTC视频帧渲染前处理--等比例填充显示窗口>,介绍了按照显示窗口,不损失原视频帧内容的前提下,左右或上下补黑的方式来构造视频帧的方法.这篇文章再说一下另外一种处理 ...

  2. QT界面中实现视频帧显示的多种方法及应用

    QT界面中实现视频帧显示的多种方法及应用 (一) 引言 1.1 视频帧在QT界面中的应用场景 1.2 不同方法的性能和适用性分析 1.2.1 使用QLabel和QPixmap 1.2.2 使用QPai ...

  3. Android快速获取视频帧

    2017年短视频应用如雨后春笋般先后上线,现在的短视频App大多支持本地视频的上传以及裁剪.下面讲一讲裁剪视频时预览视频图片的快速获取方法.当选择一个视频之后,底下通常有预览图片,这就是视频帧,比如快 ...

  4. ffmpeg如何在结尾添加帧_一种“视频帧对齐”的测试方案实践

    点击蓝字?关注[测试先锋],不再迷路!一起成为互联网测试精英,前瞻测试技术-导语全参考清晰度测算的时候,输入两个视频帧序列,但是视频帧序列没有对齐,怎么知道丢了哪帧?又怎么知道补回哪一帧?今天介绍一种 ...

  5. 基于 ffmpeg + Webassembly 实现前端视频帧提取

    作者:jordiwang  https://juejin.im/post/6854573219454844935 现有的前端视频帧提取主要是基于 canvas + video 标签的方式,在用户本地选 ...

  6. Flutter 使用Texture实现Windows渲染视频

    Flutter视频渲染系列 第一章 Android使用Texture渲染视频 第二章 Windows使用Texture渲染视频(本章) 第三章 Linux使用Texture渲染视频 第四章 全平台FF ...

  7. vue渲染动态渲染图片_动态/动态渲染视频和音频

    vue渲染动态渲染图片 Vue-Viaudio (vue-viaudio) Dynamically/Reactively render videos and audios. 动态/动态渲染视频和音频. ...

  8. Windows平台OpenGL渲染视频

    我之前写过一个简单的RTSP播放器(https://github.com/greenjim301/rtsp),当时的视频渲染是用D3D实现的.一直想尝试一下用OpenGL来渲染视频,但却不得空,最近有 ...

  9. 视频帧播放速度的单位

    选择帧速率和播放速度 动画的帧速率以每秒显示的帧数 (FPS) 表示. 即该软件每秒钟实时显示和渲染的帧数. 由于程序使用实时时间(内部精度为 1/4800 秒)来存储您的动画关键点,因此您可以随时更 ...

最新文章

  1. EffectiveC++ Item11
  2. python安装在哪个盘_python要安装在哪个盘
  3. 我的日常工具——gdb篇
  4. c#控制台应用程序,如何实现隐藏DOS窗口
  5. html菜单浮动,浮动菜单,可实现上下滚动的效果
  6. android 打开免打扰模式,Android 6.0设置模块免打扰功能浅析
  7. 终于解决“百年一遇”奇怪问题
  8. java 判断是合法语言_使用Java 怎么实现一个判断IP地址是否合法的功能
  9. C++——最长公共子串
  10. 从0开始学心电信号处理(1)——心电信号的读取
  11. vue、webpack、bable
  12. Android 开发技术周报
  13. 蓝牙耳机什么牌子音质好听?蓝牙耳机音质排行榜
  14. Altium Designer 10 PCB简要设计及其例程
  15. 中国智能制造发展趋势!
  16. 计算机信息管理面试自我介绍,信息管理专业的面试自我介绍
  17. 股票交易接口的应用场景-在miajs中导入apjs
  18. 基于python+django+vue学生作业管理系统
  19. Spring Cloud Alibaba 整合Nacos实战
  20. 第四代战斗机的标示性特点有哪些

热门文章

  1. 哈希表的概念(散列表)
  2. 牛客多校4J二分答案连续子段最大平均值
  3. 【MM VS价】移动平均价V标准价格S(一)
  4. 服务端开发or客户端开发的选择
  5. php+msyql在线教师备课系统
  6. 图片转为pdf怎么弄?发送图片安全高效的格式
  7. SVN代码迁移到Gitlab(保留SVN的提交记录)
  8. 马来亚大学研究生多久毕业?这份问卷调查结果拍了拍你
  9. 通过python调用海康威视工业摄像头并进行图像存储,同时使用opencv实时图像显示(数据流问题已解决)
  10. Excel使用---excel2016___一般操作(搬,侵删)