在网页应用中实现WebRTC屏幕共享

作者:Chris Ward(原文链接

翻译:刘通

screen1    

         我最近在我们最新的视频会议应用Locus中加入了屏幕共享功能。为了实现这项功能,我在网上找了很多有用的资源,但是很散乱,很多都是过时的文章。在这里我想要把近期的内容总结一下,希望能够帮助到后来的开发者。我不想自称为专家,有可能有比我这种更好的方法来实现这项功能,但是下面写的内容对我来说是有用的。

 

访问视频捕捉流

         Chrome和Firefox目前都在其电脑版浏览器中内置了屏幕捕捉功能。据我所知,移动端并不支持屏幕共享(手机可以接收共享的屏幕内容)。在这两个浏览器中,屏幕捕捉是通过MediaDevices.getUserMedia() (gUM)借口来实现的。gUM可以被调用来获得用户的音频/视频流,二次调用可以获得屏幕流。不好的一点是这个过程在不同浏览器之间的细节是不一样的,并且没有被记录成文件。需要注意的是我们还使用了adapter.js

 

Chrome

         Chrome有内置的屏幕捕捉功能,但是想要获得这个功能的使用许可,应用必须使用Chrome的插件。这个插件使用chrome.desktopCapture.chooseDesktopMedia()来返回sourceID。之后sourceID会被作为gUM约束的一部分来使用。

screen2

         Google提供了对插件的说明,在这里我就不全部进行重复了。但是简而言之,屏幕共享的插件在你的网页代码中运行一个内容脚本,并且在单独的插件代码中运行一个背景脚本。内容脚本可以与通过给窗口发送信息来与你的网页应用进行通信,或者通过DOM操纵进行通信,但是背景脚本不能完成这个。背景脚本可以访问所有Chrome插件的api,但是内容脚本无法进行访问。内容脚本和背景脚本可以通过chrome.runtime.connect()互相进行通信。所以基本的流程是:

#1 你的网页应用向内容脚本索要屏幕共享sourceID。

#2 内容脚本将这个请求发送给背景脚本。

#3 背景脚本调用chrome.desktopCapture.chooseDesktopMedia()并且将sourceID返回给内容脚本。

#4 内容脚本将其返回给网页应用,最终调用有sourceID的getUserMedia()作为约束之一。

         Muaz Khan有一个开源的示例插件,可以作为入手点。其中有一些复杂内容使其可以作为一个合格的产品,比如确保在安装之后直接就能在已经打开的标签页上运行。

         对于Chrome而言,gUM约束是:

screen3

注意:第一组约束是我们在Locus中使用的,然后adapter.js将其转化到第二组。奇怪的是,我们使用的adapter.js的版本拒绝将第二组作为输入。

         编辑:根据注释,mediaSource可以在mediaStreamSource失败的地方继续工作。

         有了这些约束,Chrome会给用户弹出一个选择UI界面。在这个UI上,用户可以选择是分享整个屏幕,指定应用的窗口,还是指定的Chrome标签页。

screen4

 

Firefox

         对于Firefox,情况(可能)要简单一些,但是扩展性就没有那么强。不是像Chrome一样使用插件来控制屏幕共享许可,Firefox而是用一个可信网站的白名单。白名单的信息可以在这里看到,其会将特定的bug报告给Mozilla。注意的是在这片文章中,我是按照这些说明来编写Locus的,但是并没有收到任何的反馈。所以我不能担保这个真的可以工作。

          白名单是可以进行编辑的。在Firefox中:

# 进入about:config

# 搜索media.getusermedia.screensharing这应该显示至少两个选项。

# 确保media.getusermedia.screensharing.enabled的值是ture

# 对于media.getusermedia.allowed_domains,其数值属性包括一系列的域名。将inthelocus.com*.inthelocus.com加入列表(或者你自己的域名)。

         对于Firefox,gUM约束是:

screen5

         也许你可以从这些约束中猜到,Firefox目前不能给用户弹出一个UI让他们选择共享内容。反之,如果screen被请求,用户可以指共享他们的全部屏幕,如果window被请求,用户会看到一个UI让他们选择想要共享哪个窗口。不管上面哪种情况,都会有一个授权对话框弹出来,让你对麦克风和摄像头的使用进行授权。我没有在这上面研究的太深,但是window和application看起来应该是一回事情。

 

通过peerConnection分享流

         屏幕共享gUM调用返回一个mediaStream,其可以通过peerConnection进行分享,就像WebRTC里任何其他的mediaStream一样。一个简单且常见的实现只是通过peerConnection发送一个视频轨道。所以当用户开始共享时,他们的网络摄像头视频就被停掉了。

         要是同时想要分享视频和屏幕就有一点点困难,尤其是在端到端网格中。WebRTC浏览器针对每一个peerConnection都会单独进行视频流的转码。这会计算上的加强和聚集带宽,甚至在刚刚开始分享单一视频流的时候限制通话大小。因为屏幕共享通常是有很高的分辨率的,分辨率越高转码的代价就越高,所以如果再加入一个视频流进行解码的话,会加倍CPU的负荷,或者有更不好的影响。降低帧率会起到帮助的作用,但是在我们的网格结构中,我们还会发现同事转码视频加屏幕视频的CPU负载过大了。

         积极的一面是,音频编解码器在低码率传输屏幕共享的工作做的非常好。当屏幕上发生大的变化时,码率会高达3—4Mbps,但是当屏幕基本没有变化时,比特率就降低到大约0.07Mbps。

screen6

         所以我们该如何同时发送网络摄像头所采集的影像和屏幕内容呢?一个解决方法是忘掉网格网络,而取而代之使用SFU或者MCU方式的解决方案。在Locus中,我们顽固的想要避开服务器解决方案。我们最终使用了我们自己的一个简单的使用图像的转码方式,并且将其通过dataChannel发送出去。需要意识到的是,屏幕共享帧要么是静态的,要么是会突然发生大改变,使解决方式可能不会对典型视频有用。

 

总结

         浏览器支持屏幕共享功能已经较为完备和简单了。我是一个机械工程师,并不是一个专业的开发人员,并且从来没有写过任何Chrome插件,但是也可以在一周之内完成发布屏幕共享功能的任务。

         在这其中还有一些限制和发展的空间:

# 在Firefox中可以有更好的选择UI,就像Chrome现在所用的那样

# 用户可以远程控制屏幕共享者的鼠标

# 可以允许在屏幕中进行划线,线条也可以超出浏览器标签页的范围。

填写常用邮箱,接收社区更新

WebRTC 中文社区由

运营