上一章节介绍了媒体流的摄像头使用,这一章节就来介绍下设备检测,就是在使用webrtc之前先检测一下你的设备中是否包含音视频设备,以及这些音视频设备是否可用。
有些电脑/手机可能不包含音视频设备,或者是包含了多套设备,比如:
- 蓝牙耳机
- 系统自带麦克风
- 外插耳机
- 虚拟摄像头
- 外插摄像头
- ......
实际情况下是非常复杂的。也有可能你就一套音视频设备,但已经被其他程序调用了,这时候你不经过检测就直接调用必然是会出问题的。
所以,在直接使用之前需要先进行设备检测,看看电脑/手机中的音视频设备到底是个什么情况,有没有设备?能不能用?是不是已经被使用了?有多少是可以使用的?有多个的情况下应该使用哪个?那个设备是最理想的?等等好多问题需要考虑。
但不必担心,webrtc也是提供了相关的接口,可以很方便的让我们检测自己电脑/手机上的音视频设备,这个API就是mediaDevices。
设备信息基本概念
mediaDevices提供对连接的媒体输入设备(如摄像头和麦克风)的访问,以及屏幕共享。从本质上讲,它允许您访问任何媒体数据的硬件源。当然,对于使用webrtc而言,只需要使用摄像头、麦克风以及屏幕流即可,用不着都获取。
对于获取到的设备信息,主要关注以下三个属性:
设备的ID是唯一标识,Kind值只有audioinput、audiooutput和videoinput三种,label就是设备的名称。
设备ID的用处就是,当你有多个设备时,比如你的手机有前置左摄像头和前置右摄像头,或者后置摄像头,你就可以通过传入想要的设备ID来使用该设备,即上一节中的约束条件deviceId。
kind值则是用来区分音视频设备和输入输出设备,这个在静音和关闭视频时需要用到。比如只给某个人静音,或者关闭自己的视频,不关闭声音。这个功能后面也是会有代码实现的。
label值则有点特殊。网页在 HTTPS 下才能使用 getUserMedia 来获取用户的摄像头和麦克风权限。或者,对于非 HTTPS 环境下的本地测试或开发,浏览器通常会允许使用 localhost 或者 127.0.0.1 的方式进行开发测试,因为这些情况下用户的隐私和安全问题较少。但这仅限于本地测试环境。
也就是说,在非https和本地测试的情况下,你是拿不到label值的,即使拿到了也是空值。这是浏览器出于安全问题的限制,所以在上线时必须要使用https,不然没法正常使用。
讲完了设备信息,再回到mediaDevices这个API,看看它是如何使用的。
获取设备信息
在mediaDevices这个对象中可以枚举所有已连接的设备,监听设备更改(当设备连接或断开连接时),并打开设备以检索媒体流。其实也相当简单,如下:
const enumerateDevices = async () => {
try {
const devices = await navigator.mediaDevices.enumerateDevices();
}catch(e){
console.log(e)
}
}
就是这么简单调用,不需要传入其他额外的参数,即可拿到电脑/手机上的音视频设备信息。
在我电脑上打印出来的设备信息如下:
可以看到,这里不光包括上面讲的三种属性,还多了一个
groupId。这个groupId就是用来标识属于同一组的设备,个人感觉用处不是很大。
还有一点要注意,就是有些浏览器不一定会支持这个API,所以在调用之前要先判断该浏览器是否支持,这个要写在最前面:
// 判断浏览器是否支持
if (!navigator.mediaDevices || !navigator.mediaDevices.enumerateDevices) {
console.log("enumerateDevices() not supported.");
return;
}
那么拿到这些设备信息之后就要进行划分了,根据kind来进行划分:
const list = ref([
{
type: "select",
label: "选择摄像头",
value: "videoId",
options: [],
},
{
type: "select",
label: "选择麦克风",
value: "audioInId",
options: [],
},
{
type: "select",
label: "选择听筒",
value: "audioOutId",
options: [],
}
]);
// 初始化
const init = async () => {
// 判断浏览器是否支持
if (!navigator.mediaDevices || !navigator.mediaDevices.enumerateDevices) {
console.log("enumerateDevices() not supported.");
return;
}
const enumerateDevices = async () => {
try {
const devices = await navigator.mediaDevices.enumerateDevices();
devices.forEach(device => {
const option = { label: device.label, value: device.deviceId };
switch (device.kind) {
case "videoinput":
if (!list.value[0].options.some(opt => opt.value === device.deviceId)) {
list.value[0].options.push(option);
}
break;
case "audioinput":
if (!list.value[1].options.some(opt => opt.value === device.deviceId)) {
list.value[1].options.push(option);
}
break;
case "audiooutput":
if (!list.value[2].options.some(opt => opt.value === device.deviceId)) {
list.value[2].options.push(option);
}
break;
default:
break;
}
});
} catch (error) {
throw error;
}
};
try {
await enumerateDevices();
} catch (error) {
handleError(error);
}
};
成功的拿到信息后,就可以自由的选择了。
是不是感觉很简单?一个不用传参的函数就能轻松获取到所有音视频设备了,非常Nice!
之后就是检测设备了,看看这个设备到底能不能用?
设备检测
既然拿到了所有音视频设备的信息了,那么就可以采用排除法来检测设备了。
首先从摄像头里面选取要使用的那个,然后将其ID作为约束条件传入getUserMedia中,然后加上{video:true},只显示视频。如果能够看到自己的视频,当然就是能用的;看不到说明该设备无效,可以换一个再试试了。
然后,再从麦克风里选取要使用的那个,同样传入ID,再加上{audio:true},只需要音频。然后检查返回的音频流是否为空。如果获取到了非空的音频流,说明设备可以使用;如果返回了空的音频流,则可能是因为设备没用了。或者可以查看 getUserMedia() 错误,如果错误类型为 NotFoundError,表示没有找到可用的音频设备。也就是说该设备没用了。
进行完一轮的排除法检测后,就可以放心的去使用了。
拓展
还有一种场景需要考虑到,就是当你正在使用某个音视频设备时,突然这个设备坏了,不能用了,这时候该咋办呢?或者是在运行时有新插入的设备,你想用这个新插入的,那该怎么办呢?
webrtc也是考虑到了这种情况,在navigator.mediaDevices上有一个devicechange事件,可以监听这个事件来获取设备变动的信息,如下:
async function getConnectedDevices(type) {
const devices = await navigator.mediaDevices.enumerateDevices();
return devices.filter(device => device.kind === type)
}
navigator.mediaDevices.addEventListener('devicechange', event => {
const newCameraList = getConnectedDevices('videoinput');
console.log(newCameraList)
});
最后,根据设备变动的信息来选择合适的设备即可。
小结
本文介绍完了设备的相关信息,也知道了如何检测设备。在使用时设备遇见损坏的突发情况,也有devicechange事件可以去处理。
总结下来关于设备需要掌握的部分,就一个方法enumerateDevices,一个事件devicechange和三个属性kind,label,ID。非常简单。
既然我们已经知道自己有哪些设备了,就可以用这些设备来实现一些简单的功能了,比如拍照,录视频。下一节来介绍如何利用webrtc来实现拍照功能。
项目地址
项目功能如下:
感兴趣的可以直接去体验一下,欢迎star和提pr