视频直播:Windows中各种画面源的截取和合成方法总结

当今,视频直播技术和实时音视频技术已是不少行业必备,典型的应用场景有教育直播、远程视频会议、互联网娱乐等。在移动端发起直播,其画面源的种类是十分有限的,无非是取摄像头、截屏等。PC端因为其系统资源充足,应用程序丰富,画面源种类多样,更适合做为主播程序运行的平台。在实际应用中,常常有一些场景是须要将不一样的画面源合在一块儿,而后推流出去的。本文粗浅介绍一些网易云信在开发过程当中总结的一些获取不一样画面源的画面并将其合并的方法。
相关阅读推荐
《如何快速实现移动端短视频功能?》网络

《视频私有云实战:基于Docker构建点播私有云平台》性能

各种画面源的截取ui

  1. 摄像头画面

Windows下采集摄像头画面,DShow是最经常使用的方法之一。经过DShow采集摄像头数据,建立视频采集Filter,将其加入到图表IGraphBuilder中,用IMediaControl接口来控制流媒体在Filter Graph中的流动,再经过Render来获取视频的原始数据。以上流程封装在了咱们的SDK中,用户能够直接调用SDK接口。视频

  1. 桌面取屏及应用程序窗口截取

在Windows系统中,桌面和全部应用程序窗口同样,自己也是一个HWND窗口,所以能够放在一块儿讨论。获取一个窗口的位图数据,最经常使用的方法是:建立一个用来接收窗口画面的HBITMAP位图对象以及一个HDC设备上下文对象,用SelectObject将二者绑定,而后用BitBlt从被截取窗口的HDC将数据拷贝到目标HDC。下面列出关键代码:对象

  1. 其余截屏/截窗口方法

教育直播中,PPT分享是很是重要的一个场景。可是据我考查,自从Microsoft Office 2013以后,BitBlt就取不到Word、Excel、PPT窗口的内容了,截到的是一片白色。可是用PrintWindow这个Windows API却能够取到。调用PrintWindow的程序会收到WM_PRINT或WM_PRINTCLIENT消息。PrintWindow的效率比BitBlt低,但当BitBlt没法取到时,能够用PrintWindow。接口

愈来愈多的程序的画面是在显存中的,此时,BitBlt和PrintWindow都无论用(获得的都是一块黑色的位图)。能够考虑用DirectX的方法。并且DirectX方法因为使用了GPU,因此相较前面两种方法效率更高。如下是DirectX截屏的代码:图片

  1. 获取本地图片的位图数据

将本地图片(jpg、bmp、png、gif等格式)加载到内存,并取得其位图句柄或像素首地址的方法有不少种。这里列举几种最多见的。ip

GdiPlus方法比较简单。首先是经过图片路径建立一个Gdiplus::Bitmap对象,经过Gdiplus::Bitmap::LockBits()方法能够获得图片的数据,存放在一个Gdiplus::BitmapData结构中。Gdiplus::BitmapData::Scan0就是图片像素数据的首地址。若是想获得该图片的HBITMAP句柄,只需调Gdiplus::Bitmap::GetHBITMAP()便可。内存

另外一种方法是使用Windows API LoadImage来加载一个本地bmp图片获得HBITMAP句柄,但这种方法彷佛只能加载位图文件(.bmp格式)。使用ATL的CImage只须要3行代码便可获得一个图片文件的HBITMAP句柄。资源

画面合成
主播经常但愿同时将本身的摄像头画面和桌面内容或者某个程序的画面共享给观众,有时甚至须要同一时刻分享10个以上的画面源。这时候,须要将多个画面粘贴到一个目标画面上,咱们称这个过程为画面合成。合成的画面一般还要支持改变各个画面的尺寸、位置等操做。这样一来,程序性能成了瓶颈问题。

首先,对于各类画面源的截取应该尽可能采用高效的方式,其次,画面的拉伸压缩是比较耗性能的地方。在1秒钟须要合成20帧画面的要求下,应该避免直接强行压缩HBITMAP,而是采用一些有加速的方案。

  1. LibYuv方案

咱们找到一个一个yuv库(LibYuv Project),支持图形数据从rgb格式到各类yuv格式之间的互相转换(定义在libyuv/convert.h中)。比较重要的一点是,它对yuv格式图形的拉伸和压缩以及其余各类变换(定义在libyuv/scale.h中)是有加速的。正好咱们最终要推流的格式也是yuv格式的,因此咱们方案的流程是:取得各个画面源的画面以后,先将它们各自转化为yuv格式,而后把这些yuv画面按照咱们制定的方式粘贴到一个目标yuv画面上,最后将目标yuv画面数据推流出去。另外,因为主播的窗口上也要显示合并画面,因此还要把目标画面转成rgb格式渲染到窗口HDC上。

固然,因为存在rgb格式和yuv格式之间反复的转换以及频繁的scale,并且yuv加速毕竟是软件方式,程序的CPU占用率仍是有点高。若是能采用DirectX、OpenGL等硬件加速解决方案,对程序性能以及用户体验的提高应该是比较明显的。

  1. DirectX 9方案

在DirectX 9方案中,咱们的每一个画面源以及最终的目标合成画面,都对应一个表面(IDirect3DSurface9)和一个纹理(IDirect3DTexture9)。

因为画面源的颜色内存可能会被频繁访问和修改,因此建立其表面或纹理时,应该将其建立在系统内存或AGP中(D3DPOOL_MANAGED)而不是显存中。对于yuv格式的摄像头数据或网络视频帧,DirectX能够建立能直接接受yuv数据的纹理(D3DFMT_UYVY)。合成的时候,调用IDirect3DDevice9::DrawPrimitive()来将每一个画面源绘制到目标画面上。

而最终合成画面是要显示到窗口上的,因此应该建立在显存中(D3DPOOL_DEFAULT)。渲染的时候,调用IDirect3DDevice9::DrawPrimitive()将目标画面的纹理绘制到窗口的渲染目标纹理上,或者调用IDirect3DDevice9::StretchRect()将目标画面的表面粘贴到窗口的back buffer上。

另外,因为要取得目标画面的数据用于推流,咱们还要调用IDirect3DDevice9::CreateOffscreenPlainSurface()在系统内存中(D3DPOOL_SYSTEMMEM)建立一个离屏表面,用IDirect3DDevice9::GetRenderTargetData()将目标画面取到离屏表面上,而后IDirect3DSurface9::LockRect()就能获得目标画面的rgb格式数据了,将其转化为yuv格式就能够推流出去了。

总 结
直播产品因为须要对每一帧画面作处理,画面的清晰度要高,帧率还不能过低,因此一般会存在消耗系统资源过多的问题。不管是取画面仍是合成画面,方法有不少,不只限于上面几种。Win API效率通常,若是对程序性能要求高,就要在其余方面去想法设法减小资源消耗。而DirectX虽然对2D图形加速不如3D加速那么显著,但仍是赛过Win API的。须要注意的是,使用DirectX时要很是清楚各个参数的意义,好比设备类型(D3DDEVTYPE)、内存池类型(D3DPOOL)、用途类型(D3DUSAGE)等等。参数用错,可能致使其性能还不如Win API。

以上就是视频直播中Windows中各种画面源的截取和合成方法总结。

另外,想要获取更多产品干货、技术干货,记得关注网易云信博客。