RTE 大会回顾 | 基于 Web 引擎技术的 Web 内容录制

随着基于WebRTC技术的Web应用快速成长,记录web在线教育、视频会议等场景的互动内容并对其准确还原愈来愈成为一项迫切需求。在主流浏览器中,一般基础设施部分已实现了页面渲染结果的采集及编码。开发者能够利用浏览器提供的API对页面内容进行录制。但受限于Web标准以及浏览器厂商在专利受权方面的问题,使用Web API实现页面录制在易用性和可用性上均较难使人满意。针对上述问题,声网Agora Web 引擎高级架构师高纯在 RTE 2020 实时互联网大会上就Web引擎渲染采集原理进行了分享,并就基于Web引擎的服务端录制技术进行探讨。node

▶️点击「阅读原文」可观看视频回放,获取 PPTlinux

如下为演讲实录:web

你们好,我此次技术分享的主题是Web互动场景还原—基于Web引擎技术的Web内容录制。chrome

我叫高纯,是来自声网的Web引擎高级架构师,接下来我将会为你们介绍如下的内容:包括应用背景、浏览器内容采集、服务端Web录制引擎,服务端Web录制引擎性能优化,最后会进行一个总结。canvas

先看一下应用的背景,咱们知道最近几年随着RTC行业的火热,基于Web RTC技术的Web应用快速成长,记录Web在线教学、视频会议等场景的互动内容并对其进行准确的还原愈来愈成为一项迫切的需求。后端

在主流的浏览器当中,其基础设施部分已经实现了对页面渲染结果的采集及编码过程,开发者能够利用浏览器提供的Web API对页面内容进行采集以及录制。可是受限于Web标准以及浏览器厂商在专利受权方面的问题,要使用Web API实现页面录制,在易用性和可用性方面还难以知足业务需求。浏览器

浏览器内容采集安全

咱们先来看一下浏览器内容采集,所谓Web录制实际上就是对页面内容采集及编码并存储的过程,在主流的浏览器当中它的工做基本上都是在一个多进程的架构之上,页面的渲染会在一个或者多个的Render Process里面处理,最终的结果会在Browser Process里面来进行呈现,咱们要作的事情就是在Renderer Process里面渲染的内容包括视频、音频及其余动画来进行采集而且录制成文件,这个过程就是页面内容采集。性能优化

目前在chrome浏览器上面咱们可使用两种方法来进行页面的采集,一种方法是经过chrome extension,目前chrome extension API提供了一条叫作chrome.tabcapture的对象,这个对象当中的capture API能够用来进行页面内容的采集,结合HTML5标准当中的其余模块,好比说像LocalMediaStream、MediaRecorder以及Blob这些组件,咱们能够对页面内容进行采集及录制。服务器

它的大概流程如这个示例代码所展现的同样,在使用chrome.tabcapture API的时候,咱们须要给它传入一个是否容许video的采集,以及是否容许audio的采集,而且还要传入一些video相关的分辨率、采集帧率等信息。而后这条API在执行的时候会经过一个回调,把它采集到的结果以LocalMediaStream 的形式返回,咱们在回调函数中拿到LocalMediaStream的对象以后,能够利用这个Stream对象来建立一个Media Recorder,同时给这个Media Recorder来指定咱们视频编码的码率、音频编码的码率,以及咱们视频编码的格式。

目前在chrome上面,它所支持的视频编码格式主要有H26四、VP八、VP9。音频它只支持Opus格式,在文件格式方面他目前只支持WebM的媒体格式,经过调用MediaRecorder.start()方法,咱们能够发起录制过程,在MediaRecorder的ondataavailable回调当中,他会把采集到的编码处理以后的视频数据返回。

咱们拿到这个返回的数据以后能够把它交给一个blob来进行存储,在录制结束以后,咱们能够利用这个blob来创造一个超连接,把这个文件下载下来,这就是用chrome extension来进行采集的一个过程。

这个过程的主要问题有什么呢,主要有如下几点,一个是他的音频编码仅支持opus格式,opus实际上支持的媒体格式是比较有限的,像在TS这种流媒体文件中它是不被支持。视频编码方面他提供的可选参数很是有限,视频编码的性能相对比较弱,另外因为他仅支持webm格式的录制,且没有往这个文件当中写入进度信息,录制出来的文件在播放的时候是没有办法去拖动的,另外因为是在录制完毕的时候才会生成整个文件,它的可用性是比较差的,一旦在录制过程当中出现了故障,服务出现了崩溃,这意味着咱们以前所录制的全部内容都会丢失。

咱们来看另一种页面内容的采集方式,是利用chrome devtools API来进行采集,chrome devtools API它是第三方应用来驱动chrome工做的一个API,它支持chrome以headless模式来工做。第三方的应用和chrome之间进行通讯是使用chrome devtools protocol协议。可是它只能采集chrome可视的数据,音频是没有办法采集的,它目前提供了三条API来作页面内容的采集。包括StartScreenCast()、StopScreenCast()、以及CaptureScreenstot()。Capture Screenstot可对页面进行截图,只能采集单帧的数据存储到image文件。咱们主要使用的是StartScreenCast、StopScreenCast这两条API,它的方法大概是如下几个步骤:

首先咱们要启动chrome,启动chrome的时候须要给它加上Headlees参数,令chrome以Headless模式启动,所谓Headless模式就是以一个服务进程在后台运行,他是没有用户界面的,在启动它的时候,咱们须要给它带上remote debugging port调试端口的参数,同时要指定它渲染结果的窗口大小。

在启动Headless chrome以后咱们能够利用一个node应用,在应用中使用google提供的叫作chrome remote interface的插件。利用这个插件咱们就能够和Headless chrome进行通讯来获取它采集到的数据。

具体的调用过程,首先会建立一个chrome remote interface的对象,而且拿到这个对象当中的Page对象。咱们经过调用Page当中的startScreenCast的方法,来给它传入相应的编码格式,而后可以获取到它的每一单帧的数据,请求每一帧的数据方法叫作ScreenCastFrame,经过这个示例代码能够看到,浏览器在编码的时候,它采用的是图像的编码方式,目前chrome支持两种方式,一种是png一种是jpeg,咱们拿到了这个png/jpeg的数据以后须要对这个图像进行解码,利用咱们写的插件来对它进行视频的从新编码,而后保存为文件,这个是利用chrome devtools API来采集的整个过程。

用chrome devtools API采集主要的问题是什么,它主要有三种问题,一个是整个过程他会有png/jpeg数据的编码、解码,以及视频的编码过程,它整个开销是很大的,另外它不支持音频的采集,而后在node应用和chrome应用之间它是使用websocket来进行数据传输的,它的传输性能是比较差的。

在介绍完两种主要的chrome进行页面数据采集的方法以后,咱们来给你们介绍一下,chrome是怎么进行页面渲染的,它的流程大概是怎么样。而后chrome内部又提供了哪些接口来让咱们进行页面采集和录制。

咱们知道chrome浏览器或者web引擎在解析了HTML文档以后会建立DOM树,DOM Tree中的每个节点它都对应到HTML文档当中的一个节点。对DOM tree应用CSS属性以后,会生成相应的layout tree,layout tree中的每个节点就是layout object,它除了可以表达它在文档结构当中的位置,它还能表达它在排版过程当中的全部属性。

在页面绘制的过程当中,web引擎它不会对整个layout去进行完整的绘制,它会对layout tree进行一个分层,对每一层进行独立的绘制,最后经过合成器把不一样的绘制结果合成出来。

这个分层的过程实际上是有一些规则的,它主要包含哪些规则呢?

首先咱们的DOM Tree的根节点以及和它相关的这些节点,会做为一个layer来进行绘制,有一些特殊的节点好比包含一些位置信息应用了relative, absolute或者transform属性的这些节点,也会做为独立的层来进行绘制渲染,对于一些应用了CSS filter的节点也会做为独立层渲染。

假如说有一些节点会产生溢出会产生overflow它也会进行独立的绘制。另外在DOM Tree中,对于一些特殊的节点,好比说像video element 或者canvas element来进行2D或者3D内容绘制的时候,这些节点也会做为独立的层来进行绘制。

在分层以后,咱们的web engine会对render layer 或者paint layer来建立相应的Graphics Layer,由全部Graphics Layer组成的树就叫作Graphics Layer Tree,每个Graphics Layer当中会维护一个Graphics context,这个Graphics context就是这一层内容绘制的一个目标。每个Graphics layer还会维护一个叫paint controller,这个paint controller会来控制咱们每一层具体的绘制。

除了Graphics layer tree以外咱们的layout tree 在预绘制的过程中还会生成相应的Property Trees,所谓的Property实际上是指四种类型的属性,它包含了像Transfer,一些位置的变换;或者是clipping,对内容进行裁剪;包括effect,一些特效,好比像透明度或者mask信息;而后还有scroll信息,就是当个人内容须要进行滚动的时候它也会有本身的一些信息,这些属性都会存储在这个Property Trees里面。

当咱们的DOM文档结构发生变化的时候,或者CSS属性发生变化的,或者是Compositing发生变化的时候它会产生相应的invalidation,一旦invalidation发生,layout Tree会找到它发生变化的相应的节点,而后会利用它相应的Graphics layer来进行绘制。这个图是发生变化的区域。

绘制的过程实际上就是把layout Tree当中的layout Object属性转换成绘制指令的过程,这个绘制指令是经过术语display items来进行表达的。

有了这个Graphics layer Tree和Property Trees,以后咱们能够对Graphics layer tree中的每一层来使用Compositer来进行合成,因为Compositer的输入有本身的格式,咱们须要对Graphics layer tree和Property Trees进行转换,须要把Graphics layer tree转成layer list,把blink Property Trees转成CC Properties Trees。拿到这两个结构以后浏览器接下来就能够进行合成。

所谓的合成就如上面这张图所显示的,它是把浏览器当中的不一样的部分,以及浏览器的界面进行层叠。最终显示出咱们能够看到的最终效果的过程。可是在浏览器当中实际的合成过程是分红两个部分的,有两个阶段。

在第一阶段是在浏览器的渲染线程来实现的。它经过刚才咱们拿到的layer List和CC Properties Trees来造成光栅化,若是是软件光栅化会生成位图,若是是硬件光栅化会造成GPU当中的纹理。

拿到这些结果以后来进行层叠处理,最终Compositer会输出一个叫作Compositer Frame的数据,Compositer Frame并非实际的最终渲染的结果,它不是一个位图或者一个纹理,它实际上也是一系列的绘制指令,这些指令在光栅化以后才会产生实际的位图或者是纹理。

在第二个阶段咱们的浏览器会利用dispaly Compositer 把咱们上一阶段的合成结果和浏览器的UI部分,好比说像地址栏、标题、标签页的按钮来进行合成,最终输出到物理设备上面,这整个渲染过程就完成了。在render进程和browser(浏览器)进程之间它的数据传输是经过shared Memory的形式来传输的。

经过了解上面的这个过程其实咱们就能够很明显的发现,若是咱们须要对chrome当中的页面进行采集的话咱们须要的是什么,咱们须要的其实就是Compositer Frame光栅化以后的bitmap 或者是texture,经过对这个数据的编码以及通过muxer存储文件,咱们的录制就能够完成了。

接下来咱们来看一看chrome这个项目当中提供了哪些接口来让咱们对音视频数据进行采集。在chrome当中因为它的渲染进程是跑在sandbox(沙盒)里面的,在sandbox(沙盒)进程当中是没有办法对系统调用,它对系统API的调用是受限的,因为咱们的录制须要去访问本地文件,因此咱们整个的采集过程是在Browser进程来实现的。

chrome在Browser线程提供了ClientFrameSinkVideoCapturer这个类,这个类会向渲染线程发起相应的采集请求,客户程序只须要经过当前tab页对应的CreateVideoCapturer来实例化这个类。

同时实现FrameSinkVideoConsumer这个接口,经过这个接口当中的OnFrameCapturer的回调来获取它返回的每一帧的Video Frame数据。

在Render进程大概的过程是怎么样的呢(如上图所示),因为时间有限我不作太多的介绍。只描述一下简单的过程,在类FrameSinkVideoCapturer当中,当它接收到Browser端发来的采集请求,就会去Schedule一次采集的请求。而后会把这个请求转发给它聚合的CompositorFrameSinkVideoSupport对象,这个对象拿到请求以后,会把这个请求放在队列里面。

因为CompositorFrameSinkVideoSupport对象,它实现了SurfaceClient的接口,能够从Compositer当中拿到输出的Surface,一旦有新的Surface产生它就会获得通知,它会利用实现的CompositerFrameSink接口,经过其中的didAllocateSharedBitmap方法来建立相应的共享内存,而后把Surface当中的数据写到共享内存当中,同时会把这个共享内存的ID返回给Browser进程,Browser进程拿到这个ID以后会从Shared Memory里面把数据给解出来,这个采集过程就完成了。

那么对于音频的处理呢(如上图所示),chrome在进行音频播放的时候它会把页面当中全部的Audio Media Streams直接交给系统的Audio framework来进行混音和播放。它的混音的过程是由Audio framework来作的。若是咱们须要对全部的Audio media Streams进行采集的话咱们须要本身来实现混音的过程,混音以后的Audio 数据咱们把它交给encoder进行编码,最终存储到文件里面来。

一样在chrome当中它也提供了相应的接口,让咱们来进行音频数据的采集,这个接口主要有AudioLoopbackStreamCreator以及Audio input stream这两个类。

咱们经过建立AudioLoopbackStreamController来启动一个音频采集过程,在启动以后它会经过AudioLoopbackStreamCreator去建立一个AudioLoopbackStream,同时它会去建立相应的线程,这个线程会经过Socket不断的从Render 进程来获取采集到的音频数据,一旦这个Audio frames达到必定量以后足够多,它会去出发相应的callback,这个callback回调会最终把数据交给录制程序。

所谓的AudioLoopbackStream是chrome当中一个特殊的数据,它称为回环音频流,它会把chrome当中全部输出的Audio Streams进行合成,而后把合成的结果转换成一个输入流。

具体在Render端是怎么实现混音的过程咱们在这个地方就不展开描述了。

服务端 Web 录制引擎

接下来咱们聊一聊在服务端进行WEB录制它所须要的一些需求,主要有几点。

一个是无UI模式,咱们在服务端进行页面内容的采集,它一般是跑在一个无桌面的linux的服务环境下,同时它采集的格式须要知足实现流媒体格式。由于传统的媒体格式每每是存储单个的文件,一旦服务端发生了故障,它以前所录制视频内容颇有可能会丢失。同时录制引擎也须要去指定一些录制参数。好比说页面渲染的分辨率,最大的视频录制帧率,音频采样率、音频编码率,包括流媒体文件切片的时长等等。因为咱们的Web Engine是一个开放的平台,理论上它是能够运行全部的Web应用的。有一些Web应用可能性能开销会很是大,因此在服务端Web应用录制的过程中咱们须要对整个应用的开销进行监测。对于可能产生系统资源过分使用的HTML5组件来进行一些控制。同时对引擎内部运行的一些状态进行监测,发生错误要能进行上报。

咱们声网在实现相应的Web云录制引擎当中针对这四点也作了大量的工做,主要是Headless模式咱们是基于Chromium Headless模式实现的,它不须要像Xvfb这样的虚拟X Server环境做为页面的渲染目标。

另外因为咱们整个录制过程是一个无交互的过程,咱们须要容许引擎可以令这个音视频内容在无交互的状况下进行自动播放。

另外很重要一点是咱们的录制引擎是不提供Web API的,不须要Web应用主动发起录制的请求。页面的录制结果是一个所见即所得的过程。Web引擎当中渲染了什么内容,它就会作出相应的录制。

对于录制格式方面咱们主要支持TS和M4A两种流媒体格式,同时会输出相应的M3U8的文件列表,在编码器方面咱们能够选用Openh26四、X26四、声网自研的a264编码器来对录制内容进行编码。

咱们整个录制过程是一个动态帧率的编码过程,只有当页面发生变化的时候,输出新的video frame的时候咱们才会对它进行采集和录制。

在音频部分咱们使用的是刚才介绍过的AudioLoopbackStream来进行混音,对混音的数据进行采集再进行编码。音频的编码咱们使用AAC格式,由于像ts这种流媒体格式当中opus编码格式是不受支持的,AAC格式一般在更多的媒体文件类型下能获得更好的支持。

咱们提供的参数主要有这些(如上图),视频输出的路径包括日志的路径,同时能够指定音频的采样率是41000或者48000赫兹,录制的声道数,录制的音频的码率,视频编码的码率,以及视频的录制帧率,咱们页面渲染的尺寸高度或者宽度,流媒体HRS文件它切片的时长等等均可以进行设定。

同时也会对H5的一些性能开销比较大的组件进行一些控制,好比说像WebGL、Web Assembly ,同时对于一些比较大的尺寸的视频,对它的播放进行一些控制。

在性能和安全性方面,其实刚才也提到了,主要会对Web GL、Web Assembly包括高分辨率的视频进行播放的时候会进行一些开关。而后咱们引擎自己也会对CPU、内存、带宽等等系统资源进行自我监测和上报,对于一些文件操做,好比说像文件下载,包括对file://scheme访问会进行限制。在URL加载异常的时候会对异常以及异常的缘由会进行一个上报,对页面音视频的采集过程以及编码的状态也会进行上报,这些内容会上报到咱们服务端的应用框架当中,服务端应用框架收到这些上报信息以后会作相应的处理。

引擎对外发出的一些通知,主要有录制的开始结束,包括文件切片的开始以及完毕,有音视频编码器的初始化成功或者失败,还有采集到第一帧音频的时候或者第一帧视频的时候都会发出相应的通知,还会在音频编码失败的时候和视频编码失败的时候发出一些通知。录制引擎还会周期性的去监测咱们距离上一帧采集到音频或者视频的时间间隔。

当URL访问出现异常的时候会对URL异常的缘由进行上报。最后Recording Prof,会对CPU、内存、带宽使用率进行通知。

刚才介绍了服务端Web录制引擎的一些特征,包括声网在实现Web录制引擎当中作的一些事情。

服务端Web录制引擎性能优化

接下来咱们聊一聊Web录制引擎性能优化,Web录制引擎它的整个的过程本质上是一个从视频解码、音频解码到页面渲染、页面合成再到视频音频编码的过程。它整个过程的开销其实是很是大的。

咱们目前主要有几点对Web录制引擎进行优化。第一点就是chrome自己它在使用OpenH264的时候,出于平台兼容性的考虑,它没有启用AVX2指令来作CPU的优化,AVX2指令是在intel haswell平台以后推出的AVX指令的扩展,它扩展了原来AVX的指令计算数据的位宽,从128位扩展到了256位,可以有更好的向量运算的性能。

另一个优化点就是使用GPU来作编解码的加速,咱们知道limux平台上面一般它的显示驱动或者设备,性能通常不是太好,或者有各类问题,出于这个缘由chromium把linux平台的硬件加速的视频编解码列入了黑名单,也就是说它只用软件的方式来进行音视频的编解码。

因为咱们的整个系统是运行在服务端,这个平台是肯定的,在保证这个平台的图形的驱动稳定的状况下,咱们能够去把这个视频编解码从黑名单中移除来启用它的硬件加速。同时咱们在对视频进行编码的时候可以使用ffmpeg加VAAPI来进行硬件加速。

第三个就是对页面渲染性能自己的一个优化,chrome headness一些特殊的条件下,能够在limux服务器上去使用OpenGL进行硬件加速,可是它要求咱们的整个服务端的环境必须是在桌面系统上,也就是基于X11的OpenGL去进行硬件加速。

因为咱们的录制引擎一般是部署在multi user模式下的linux服务器上面,它是没有桌面环境的,在这个时候咱们能够在chromium当中使用ozone图形中间层,结合DRM后端来实现脱离X11的OpenGL硬件加速。ozone是ChromiumOS上面默认所采用的图形子系统,它是一个中间层。它的后端能够有各类不一样的实现,好比说有基于X11,Wayland, 或者基于GBM/DRM的,基于DRM的实现它能够脱离桌面环境,这样咱们能够在headness multi user模式的来enable硬件加速。

最后的一个优化过程是对web引擎渲染流程流程自己的一个优化,刚才我讲到整个的Web录制过程其实是音视频解码—页面渲染—合成—编码的过程,性能开销很是的大。

对于总体的渲染流程咱们能够来进行一些优化,好比说业务高峰时期在实时处理的时候,咱们接收到视频数据以后能够不进行解码也不进行播放,直接把接收到的视频流进行转储,存储为相应的视频文件。

在页面当中视频的区域咱们使用空白来进行占位,同时对区域的位置进行记录,以及对这个视频的播放状态,是播放仍是暂停、中止来进行一个记录。

咱们知道页面当中若是没有视频的渲染的话,一般它的FPS都是比较低的,这就意味着咱们从这个浏览器当中采集出来的video frame的帧率会很低,甚至是静止,这样它的编码开销就很小。咱们把没有视频部分的page进行编码进行录制来生成文件以后就能获得页面部分的文件。以及从Media stream(流媒体)转储的视频文件以及视频的一些状态信息,有了这三个信息以后,咱们能够在业务低谷时间进行视频的合成,来有效的下降咱们在业务高峰时段的服务器的压力。以上就是服务端的录制引擎的性能优化。

最后,咱们来进行一个总结,主要有如下几点。

  1. 当前主流的浏览器对Web录制的支持并不能知足咱们的业务需求。

  2. 咱们所须要的业务能力和可用性能够经过定制chromium浏览器来实现。

  3. Web云录制做为一种新的技术和业务形态,它面对性能和安全性的双重挑战。

  4. 咱们针对目标硬件平台来进行CPU和GPU的优化可以比较有效的缓解性能的问题。

  5. 经过对Web引擎渲染流程进行特殊优化可以有效的下降咱们的服务在业务高峰时的性能压力。

以上就是个人分享,很是感谢。

回顾更多演讲,可访问:rteconf.com/look-back

相关文章
相关标签/搜索