最近在给MBWebPhone增加音视频设备配置功能界面时,发现在浏览器中正常但NWJS封装的APP中异常的bug——呼叫正常,但设备枚举为空白。
一开始以为是nwjs的bug,但翻遍他们的社区,报告同样问题的解决方案是给出了–use-fake-ui-for-media-stream方式启动,这样能正常显示出设备列表了,但视频通话打开摄像头、桌面共享全部报错。
正常的:
image

异常的:
image

仔细看这段代码并没有特殊:

1
2
3
4
5
6
navigator.mediaDevices?.enumerateDevices()
.then(devices => {
devices.forEach(device => {
console.log(`${device.kind}: ${device.label} (ID: ${device.deviceId})`);
}
});

但打印出来就是 :

1
2
3
audioinput:  (ID: )
videoinput: (ID: )
audiooutput: (ID: )

又去翻google的社区,大部分回复都是localhost或http://权限限制,不可能获取设备等等说法,但这个时候webrtc通话是正常的,即:

1
2
3
4
navigator.mediaDevices.getUserMedia({
audio: true,
video: true
})

功能完全正常。

就在快放弃时,使用chrome浏览器打开了nwjs封装的设备配置页面http://localhost:3000 ,发现问题不对了,同样是http://localhost:4444 上的页面功能完全正常。
而3000端口是由nwjs中的nodejs启动的一个内置miniweb服务器,只使用了最小参数启动。

理论上,同样的页面源码,浏览器行为不同,差距就在两个web服务器的返回标头上了:
先看正常的:
image

再看nodejs的http server返回的:
image

我们一行一行的把缺的标头加上看。
却发现还是太天真了。。。并没有用。

只有http://localhost:4444 端口能正常枚举设备,又继续翻资料,发现还应该是设备权限问题,突然发现浏览器地址栏上:
image
这个地址上有麦克风,摄像头权限。

而3000端口的地址栏上:
image

答案呼之欲出,chrome没有识别到webrtc权限请求,而权限请求只有getusermedia时才会弹出申请,enumerateDevices枚举设备时并不出现。
加上这段代码:

1
2
3
4
5
6
7
8
9
navigator.mediaDevices.getUserMedia({audio:true, video:true})
.then(stream => {
stream.getTracks().forEach(track => track.stop());
navigator.mediaDevices?.enumerateDevices()
.then();
})
.catch(error => {
alert("未能获取到设备权限!");
});

image
顺利出现权限弹窗,允许,设备列表终于顺利出现。