网易MC:Windows中各类画面源的截取和合成方法总结
概要
当今,视频直播和实时音视频技术已经是很多行业必需的,典型的应用场景有教育直播、远程视频会议、互联网娱乐等。在移动端发起直播,其画面源的种类是十分有限的,无非是取摄像头、截屏等。PC端由于其系统资源充足,应用程序丰富,画面源种类多样,更适合作为主播程序运行的平台。在实际应用中,经常有一些场景是需要将不同的画面源合在一起,然后推流出去的。本文粗浅介绍一些我们在开发过程中总结的一些获取不同画面源的画面并将其合并的方法。
各类画面源的截取
1)摄像头画面
Windows下采集摄像头画面,DShow是最常用的方法之一。通过DShow采集摄像头数据,创建视频采集Filter,将其加入到图表IGraphBuilder中,用IMediaControl接口来控制流媒体在Filter Graph中的流动,再通过Render来获取视频的原始数据。以上流程封装在了我们的SDK中,用户可以直接调用SDK接口。
2)桌面取屏及应用程序窗口截取
在Windows系统中,桌面和所有应用程序窗口一样,本身也是一个HWND窗口,因此可以放在一起讨论。获取一个窗口的位图数据,最常用的方法是:创建一个用来接收窗口画面的HBITMAP位图对象以及一个HDC设备上下文对象,用SelectObject将两者绑定,然后用BitBlt从被截取窗口的HDC将数据拷贝到目标HDC。下面列出关键代码:
HDChDc = GetDC(capture_hwnd_);
HDCmem_dc = CreateCompatibleDC(hDc); //创建一个兼容DC
HBITMAPcapture_bitmap_ = ::CreateDIBSection(mem_dc, &bmi, DIB_RGB_COLORS,
(void**)&capture_data_, NULL, 0); //创建HBITMAP
HBITMAPold_hbitmap = (HBITMAP)SelectObject(mem_dc, capture_bitmap_); //将mem_dc和capture_bitmap_绑定
BitBlt(mem_dc, 0, 0, capture_width, capture_height, hDc, real_rect.left, real_rect.top, SRCCOPY);
SelectObject(mem_dc, old_hbitmap); //还原
DeleteDC(mem_dc); //销毁
ReleaseDC(capture_hwnd_, hDc); //释放
3)其他截屏/截窗口方法
教育直播中,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截屏的代码:
externIDirect3DDevice9* g_pd3dDevice;
voidCaptureScreen()
{
IDirect3DSurface9* pSurface;
g_pd3dDevice->CreateOffscreenPlainSurface(ScreenWidth, ScreenHeight,
D3DFMT_A8R8G8B8, D3DPOOL_SCRATCH, &pSurface, NULL);
g_pd3dDevice->GetFrontBufferData(0, pSurface);
D3DXSaveSurfaceToFile("Desktop.bmp", D3DXIFF_BMP, pSurface, NULL, NULL);
pSurface->Release();
}
4)获取本地图片的位图数据
将本地图片(jpg、bmp、png、gif等格式)加载到内存,并取得其位图句柄或像素首地址的方法有很多种。这里列举几种最常见的。
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句柄。
CImagecbmp;
cbmp.Load(path);
HBITMAP image_bitmap = cbmp.Detach();
画面合成
主播常常希望同时将自己的摄像头画面和桌面内容或者某个程序的画面共享给观众,有时甚至需要同一时刻分享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等硬件加速解决方案,对程序性能以及用户体验的提升应该是比较明显的。
2)DirectX 9方案
总结
直播产品由于需要对每一帧画面做处理,画面的清晰度要高,帧率还不能太低,所以通常会存在消耗系统资源过多的问题。无论是取画面还是合成画面,方法有很多,不仅限于上面几种。Win API效率一般,如果对程序性能要求高,就要在其他方面去想法设法减少资源消耗。而DirectX虽然对2D图形加速不如3D加速那么显着,但还是胜过Win API的。需要注意的是,使用DirectX时要非常清楚各个参数的意义,比如设备类型(D3DDEVTYPE)、内存池类型(D3DPOOL)、用途类型(D3DUSAGE)等等。参数用错,可能导致其性能还不如Win API。
以上由网易企业信息化服务提供商,湖南领先网络科技整理发布。
网易企业服务(qiye163.co)是网易凭借其20年品牌优势与经验在企业邮箱的基础上,为进一步布局企业市场而打造的企业级产品矩阵,致力于提供一站式企业信息化解决方案。湖南领先网络科技是网易企业产品授权经销商,专业为企业提供网易企业邮箱、网易办公套件等一站式企业信息化专业解决方案。
最后更新:2017-10-08 00:17:33
上一篇:
Windows10有这些功能,你还要第三方杀软吗?
下一篇:
同时享用Mac和 Windows最新最强大的功能,Parallels让你不纠结
你的设备已过期,并缺少重要的安全和质量更新,因此存在风险。让我们带你重回正轨,这样
Microsoft store 无法联网,显示Microsoft Store需要联网,你似乎没有联网
设备以迁移 由于仅部分匹配或匹配不明确,因此无法迁移设备
由于在创建转储期间出错,创建转储文件失败。
发生临时 DNS 错误
应用商店,在我们这边发生问题,无法使你登陆,错误代码: 0xD000000D
照相机不可用,错误代码:0xA00F4244(0xC00DABE0)
应用商店打开异常提示“清单中指定了未知的布局”
自定义扫描Windows defender里面的设备性能和运行状况 黄色感叹号问题
windows预口体验成员内口版本遇到问题需要重启
热门内容
windows10 点开此电脑后,有两个显示硬盘盘符的目录是怎么回事?
windows 10 专业版无法下载中文语言包
KB4056892
win10不能共享文件夹
在Surfacebook上用Windows to go 1703版本,更新后重启蓝屏,无法进入系统
windows10 1709版本更新失败,错误0x8007001f
microdoft visual c++ 2015 redistributable
WIN10 Insider Preview 17025更新失败,错误代码0x80096004
计算机管理服务 出现一个内部错误(INVALID
关于控制面板中的安全和维护内提示Windows defender 防病毒已关闭的问题