当今,视频直播技术和实时音视频技术已经是很多行业必备,典型的应用场景有教育直播、远程视频会议、互联网娱乐等。在移动端发起直播,其画面源的种类是十分有限的,无非是取摄像头、截屏等。PC端由于其系统资源充足,应用程序丰富,画面源种类多样,更适合作为主播程序运行的平台。在实际应用中,经常有一些场景是需要将不同的画面源合在一起,然后推流出去的。本文粗浅介绍一些网易云信在开发过程中总结的一些获取不同画面源的画面并将其合并的方法。

相关阅读推荐

《如何快速实现移动端短视频功能?》

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

各类画面源的截取

  1. 摄像头画面

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

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

在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); //释放

  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截屏的代码:

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();

}

GetFrontBufferData之后,也可以调用IDirect3DSurface9::GetDC()从pSurface得到HDC,然后用BitBlt将其拷贝到目标HDC。

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

将本地图片(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等硬件加速解决方案,对程序性能以及用户体验的提升应该是比较明显的。

  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中各类画面源的截取和合成方法总结。

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

视频直播:Windows中各类画面源的截取和合成方法总结相关推荐

  1. Windows中各类画面源的截取和合成方法总结

    曹伟 2015年毕业于华中科技大学后入职网易,先后参与过易信PC版.云信PC demo以及教育直播产品的研发,目前在云信音视频组PC端组从事开发. 作者简介 ●●● 概要 当今,视频直播和实时音视频技 ...

  2. POLYV直播助手客户端软件做视频直播时如何添加画面源

    原创文章,转载注明出来csdn. POLYV直播助手客户端软件界面左下方,有三个画面源选项,分别是:[添加摄像头][添加截屏源][添加图片源]. [添加摄像头]:这个很好理解,就是添加摄像头的画面源. ...

  3. 实时音频混音技术在视频直播场景中的实践

    最近半年,视频直播领域中产生不少创新玩法,其中包括K歌直播和合唱直播.这些创新玩法都用到实时音频混音技术.今天我们来聊一下混音技术的实现,及其在创新玩法中的应用. \\ 混音的应用场景 \\ 混音,顾 ...

  4. lisp画弯箭头_在CAD中直接画箭头的命令的一个方法

    在CAD中直接画箭头的命令的一个方法!!! 众所周知,在天正中可直接绘制箭头,而在AutoCAD中不得.最近我发现一个在命令行直接输入命令就可画出你想要的尺寸的箭头的方法,具体实施如下: 1.首先拷贝 ...

  5. 视频直播/远程会议中的AI关键技术探索应用

    随着各种AI新技术的发展和应用,它们不仅为我们带来了对于服务形式.内容,如各种视频特效.功能实现上的改变,更为重要的是对于服务质量以及成本上的优化.本文由NVIDIA深度学习解决方案架构师 吴金钟 在 ...

  6. 在Windows中运行Linux bash命令的几种方法

    如果你正在课程中正在学习 shell 脚本,那么需要使用 Linux 命令来练习命令和脚本. 你的学校实验室可能安装了 Linux,但是你自己没有安装了 Linux 的笔记本电脑,而是像其他人一样的 ...

  7. linux获取bios版本号,在Windows中获取BIOS版本信息的4种方法 | MOS86

    BIOS,或技术上称为基本输入和输出系统,是计算机中最重要的软件之一.它位于主板上,并管理硬件设备(如处理器,硬盘,图形卡等)之间的所有连接.主板制造商不时会发布BIOS或UEFI(统一可扩展固件接口 ...

  8. 移动端实时音视频直播技术中推流和传输详解

    推流是直播的第一公里,直播的推流对这个直播链路影响非常大,如果推流的网络不稳定,无论我们如何做优化,观众的体验都会很糟糕.所以也是我们排查问题的第一步,如何系统地解决这类问题需要我们对相关理论有基础的 ...

  9. 计算机维修直播,电脑直播过程中出现杂声怎么办?三种解决方法任你选

    现在,越来越多的人开始用电脑做直播.比如说利用直播来卖货或者分享个人的工作.生活状态.在电脑直播的时候,也会遇到各种各样的问题,其中,很多人就问到电脑直播有杂音滋滋响怎么办?电脑直播中发生杂音滋滋响, ...

最新文章

  1. 程序员必备技能:如何画好架构图?
  2. python music21将音符转为mid格式音乐
  3. @ComponentScan配置扫描多个包
  4. OBD技术速成——J1850协议解析软件实现
  5. 渐变颜色Qt学习:QPainter之渐变填充
  6. Zabbix---1 监控主机磁盘空间
  7. Debian/Ubuntu Apache Mod_Rewrite 安装
  8. 阶段1 语言基础+高级_1-3-Java语言高级_05-异常与多线程_第1节 异常_13_自定义异常类...
  9. 2015年c语言等级考试题1 10 2分,2015年计算机二级《C语言》精选练习题及答案(2)...
  10. PHP 生成 ppt,PhpPresentation生成ppt
  11. OSI七层网络模型 TCP/IP五层网络模型
  12. 抓包工具charles下载安装(破解版)
  13. 论文阅读 [TPAMI-2022] Deep Visual Odometry With Adaptive Memory
  14. CentOS 9 镜像下载
  15. C语言将标准时间转成秒,c标准时间与时间戳转换
  16. 网易云 音乐 url 引入步骤
  17. NO.35——qq音乐全站分布式爬虫(一)
  18. Core Data的使用
  19. 什么是Kusama (KSM)以及与波卡的区别
  20. 图文讲解如何使用QQ企业邮箱绑定域名开通企业邮箱(使用时代互联的域名管理后台)

热门文章

  1. 2.异步回调检测线程结束
  2. [转]Delphi中QuotedStr介绍及使用
  3. 【filezilla】 ubuntu下安装filezilla
  4. python怎么把所有标点符号置空_Python从小白到攻城狮(1)——python环境搭建
  5. CSS如何设置高度为屏幕高度_(15)让这些“展示”有更好的扩展性——媒体查询 | CSS...
  6. 第17天学习Java的笔记(匿名对象,随机数,猜数字小游戏)
  7. printf输出颜色和ANSI控制码(高亮,下划线,闪烁,光标位置,清屏等)
  8. 机器人学习--自主移动机器人导论资料(附下载链接)
  9. 数据库 | 菜鸟成长记之MySQL数据库
  10. 在PyPI上发布自己的python包