作者:Hector Zelaya, webrtc.ventures(原文链接)
翻译:刘通
原标题:Can You Hear Me Now? … A WebRTC Audio Demo
对WebRTC是如何处理音频的感兴趣?本文包括一个简单的WebRTC音频demo,会给你展示如何获取音频设备,如何实时的检测数据流中产生的变化。
引文
设想一个场景:你等了好久的重要视频通话终于要进行了,花了几分钟检查一下头发是不是凌乱,牙缝上没有菜叶,自己打扮的光鲜亮丽。现在你打开了浏览器,粘贴会议链接。你看到另一端的人已经上线了,所以你非常兴奋的点了“加入”按钮,清清嗓子打个招呼。你看见对方的嘴巴在动可是你却听不见任何声音,能看出来他说的是“你能听见我说话吗?”但还是没有声音。
两个人都在疯狂的对着麦克风说“你现在能听见我了吗”,而且弄明白哪出问题了浪费了宝贵的时间。对方很有可能还不会用这个会议应用,不知道是麦克风静音了还是根本就没有音频数据。
和上述场景差不多的情况发生时,很有可能是因为多种原因造成的,需要进行一些故障排除工作。可能是因为网络不好,可能是硬件问题,也有可能是第八层出现的错误。不管原因是什么,作为开发人员我们都必须确保在这种情况发生时,我们的应用能尽可能的帮助到用户。
我们必须要问自己这三个问题:
# 我们要如何确认有输入音频?
# 这个问题要如何监控?
# 有什么标准内置函数或者开源资源可以用吗?
在回答这些问题之前,让我们花几分钟的时间来回顾一下WebRTC是如何处理媒体设备的。
获取用户媒体(getUserMedia)
正如我们知道的那样,WebRTC有三个过程:
1. 浏览器获取媒体设备(麦克风,摄像头)的权限
2. 信令服务器交换两个对等端的握手消息
3. 端到端连接建立
我们现在要专注分析第一步,这步是通过GetUserMedia() API完成的。获取媒体设备的权限就是调用下面这个函数这么简单:
getUserMedia()返回一个Promise。有趣的是数据流要如何被获取,取决于作为参数传递给API的约束变量。
让我们看看上述变量是什么样的:
正如你所看到的,我们有两个变量—一个是给音频的一个是给视频的。每一个可以有两个值之一:一个布尔型的值,或者一个有特殊约束的对象。
注意的是布尔型ture和对象值是等价的,并且告诉getUserMedia API在当指定资源可用时尝试进行获取,当布尔值是false时,会告诉getUserMedia API完全不获取任何媒体设备。
这意味着像{video:ture, audio:false}会使getUserMedia只获得视频设备,不获取音频。如果不是一个布尔值true而是像上面例子展示的那样视频值是一个对象的话,与true是完全一样的。
理论上,一个WebRTC会议应用会想要同时获取音频和视频设备。当然也有例外。比如,一个只有音频通话的应用或者只有视频图像的沟通。
但是如果用户没有麦克风,或者由于任何原因出现的故障改怎么办?如果我们设置“audio:true”,getUserMedia会尝试获取音频设备,如果无法取到的话就会发生错误。这个时候,开发人员就必须对异常进行正确处理,并且发送一个看起来非常友好的提示给用户,或者至少把应用做的足够聪明,能够判断音频约束是要设置成true还是false然后再继续。
这个时候你就需要对WebRTC是如何通过getUserMedia() API获取媒体设备和约束有一个清楚的认识。
现在让我们回过头来看看刚才提出的三个问题,然后一一回答。
问题解答
好了现在我们开启了一则通话,而且我们没有从另一方那里获得到任何音频信息。应用应该如何帮助我们解决问题呢?让我们一步一步来看。
我们要如何确认有输入音频?
之前,我们说了getUserMedia()是如何使用约束来得知应该要获取哪些媒体设备的,但是我们并没有说结果流。一个流有很多轨道。如果一个资源在约束中被指定为true,那么该流就会至少有一个轨道给它。
流给音频轨道类型提供的方法是getAudioTracks(),会返回一个包含数据流所有音轨的数组。所以为了确保用户是真的在发送音频,我们可以轻易地估计数组的内容,因为如果发生了什么状况的话,音频约束就会飞设置成false,然后数据流就不会拥有任何音频轨道了,所以会返回一个空的数组。考虑这样一种情况:
在上述代码中,我们估计一个返回数组的长度。如果它比0大的话,就以为着它有至少一条音频轨道。对于两种情况来说,我们调用自己设的一个函数addAudioEvent(),用来提供一个合适的消息给用户,可能是一个警告或者是关于音频状态的一个提醒。
这个问题要如何监控?
所以我们现在知道了远端用户确实在发送音频,但是我们听不见他说话。可能是因为他不小心关掉了他的音频轨道,也就是静音了。但是我们要怎么确认是这种情况呢?
目前,WebRTC还不提供onmute或者onunmute这种事件,但是我们可以使用信令服务器来做一个我们自定义的事件。这样我们就可以让另一方用户知道什么时候其他用户在通话过程中静音或者关闭静音了。
通过使用getAudioTracks()我们可以获得轨道数组,其中每个元素在true的情况下都有一个启用的属性,意味着此时音频没有被静音,false的时候则是静音。
下面的代码假设只是用了一个音频轨道,并且使用socket.io作为信令服务器:
有什么标准内置函数或者开源资源可以用吗?
幸运的是,我们在本文中提到的所有“好”的类函数都已经捆绑在了WebRTC标准中,所以你不需要自己添加任何额外的东西。如果你用的浏览器是WebRTC兼容的,那么就一切没问题了。
但是,你有可能不想直接用API做你的WebRTC应用,想自己设定全部的基础设施,那么你可能就会想要依靠第三方服务,如Tokbox,Pubnub等等来实现。
当你使用第三方WebRTC库的时候,事情可能会因为每个库处理流和设备的方式不同而有些差别。但是即便这些差别存在,原理还是一样的,应该有一个获取媒体设备的约束机制,以及处理静音/非静音事件的flag,只要确保查清楚你用的库的说明文件就可以了。
Demo
使用git clone https://github.com/agilityfeat/webrtc-audio-demo来复制repo。现在进到webrtc-audio-demo路径,然后安装所有的npm依赖。
cd webrtc-audio-demo
npm install
npm start
当你执行了“npm start”之后,然后打开浏览器进入http://localhost:3000/。你应该看到:
然后就进行demo测试吧