WebRTC-遍历音视频设备
WebRTC 遍历音视频设备
WebRTC 遍历音视频设备
WebRTC 遍历音视频设备
音视频设备的基本原理
音频设备
音频有采样率和采样大小的概念,实际上这两个概念就与音频设备密不可分。
音频输入设备的主要工作是采集音频数据,而采集音频数据的本质就是模数转换(A/D),即将模似信号转换成数字信号。
采集到的数据再经过量化、编码,最终形成数字信号,这就是音频设备所要完成的工作。在量化和编码的过程中,采样大小(保存每个采样的二进制位个数)决定了每个采样最大可以表示的范围。如果采样大小是 8 位,则它表示的最大值是就是 2 8
- 1,即 255;如果是 16 位,则其表示的最大数值是 65535。
视频设备
至于视频设备,则与音频输入设备很类似。当实物光通过镜头进行到摄像机后,它会通过视频设备的模数转换(A/D)模块,即光学传感器, 将光转换成数字信号,即 RGB(Red、Green、Blue)数据。
获得 RGB 数据后,还要通过 DSP(Digital Signal Processer)进行优化处理,如自动增强、白平衡、色彩饱和等都属于这一阶段要做的事情。
通过 DSP 优化处理后,你就得到了 24 位的真彩色图片。因为每一种颜色由 8 位组成,而一个像素由 RGB 三种颜色构成,所以一个像素就需要用 24 位表示,故称之为24 位真彩色。
此时获得的 RGB 图像只是临时数据。因最终的图像数据还要进行压缩、传输,而编码器一般使用的输入格式为 YUV I420,所以在摄像头内部还有一个专门的模块用于将 RGB图像转为 YUV 格式的图像。
那什么是 YUV 呢?YUV 也是一种色彩编码方法,主要用于电视系统以及模拟视频领域。它将亮度信息(Y)与色彩信息(UV)分离,即使没有 UV 信息一样可以显示完整的图像,只不过是黑白的,这样的设计很好地解决了彩色电视机与黑白电视的兼容问题。
WebRTC 设备管理
在讲解如何通过浏览器的 WebRTC API 获取音视频设备之前,咱们先了解几个 WebRTC关于设备的基本概念。如果这些基本概念不清楚的话,就很难理解后面的知识。
MediaDevices,该接口提供了访问(连接到计算机上的)媒体设备(如摄像头、麦克风)以及截取屏幕的方法。实际上,它允许你访问任何硬件媒体设备。而咱们要获取可用的音视频设备列表,就是通过该接口中的方法来实现的。
MediaDeviceInfo,它表示的是每个输入 / 输出设备的信息。这个结构体包含以下四个重要的属性:
- deviceId:设备的唯一标识;
- label:设备名称;
- kind:设备种类,可用于识别出是音频设备还是视频设备,是输入设备还是输出设备。共有3种:audioinput、audiooutput、videoinput。
- gruopId:组Id。如果两个设备在同一个硬件上,则它们属于同一组。
需要注意的是,出于安全原因,除非用户已被授予访问媒体设备的权限(要想授予权限需要使用 HTTPS 请求),否则 label 字段始终为空。
另外,label 可以用作指纹识别机制的一部分,以识别是否是合法用户。
Promise
Promise,它是一种 JavaScript 异步处理机制。其思想是,首先执行指定的业务逻辑(handle),而不管逻辑的对错,然后再根据结果做具体的操作:如果成功了做些什么(resolve),失败了做些什么(reject)。
结合下面的例子,可以让你对 Promise 有个清楚的认识,生成 Promise 对象时,首先会执行 function 函数中的逻辑,该函数会根据随机数生成 timeOut,然后定时地对timeOut 做出判断:
如果 timeOut 小于 1,则调用 resolve 方法。resolve 又会调用 Promise 中 then 部分传入的函数。
如果 timeOut 大于等于 1,则调用 reject 方法。reject 则会调用 Promise 中 catch 部分传入的函数。
new Promise(function (resolve, reject) {
console.log('start new Promise');
// 产生随机值
var timeOut = Math.random() * 2;
console.log('set timeout to: ' + timeOut + ' seconds.');
// 设置一个定时器函数,根据随机值触发该函数执行
setTimeout(function () {
if (timeOut < 1) {
console.log('call resolve()');
resolve('200 OK');
}
else {
console.log('call reject()');
reject('timeout in ' + timeOut + ' seconds.');
}
}, timeOut * 1000);
}).then(function (r) {
console.log('Done: ' + r);
}).catch(function (reason) {
console.log('Failed: ' + reason);
});
获取音视频设备列表
在 WebRTC 的规范中,给我们提供一个重要的API,叫 enumerateDevices。
通过这个 API 我们就可以获取到电脑中的音频和视频设备,首先我们来看一下它的基本格式:
/**
* 通过navigator.mediaDevices下的enumerateDevices方法获取所有的音频和视频设备
* 最后它返回的值是一个Promise,这是JavaScript中特有的一个对象
*/
var ePromise = navigator.mediaDevices.enumerateDevices();
在 Promise 里面有个重要的结构体:MediaDeviceInfo。这个前面有详细的介绍。
那么接下来我们就通过一个实际的例子,如何通过WebRTC的API来获取到我们的音视频设备,我们先新建一个文件夹,在文件夹里面建一个client.html。
client.html:
<html>
<head>
<title> WebRTC get audio and video devices</title>
</head>
<body>
<div>
<label>audio input device:</label>
<select id="audioSource"></select>
</div>
<div>
<label>audio output device:</label>
<select id="audioOutput"></select>
</div>
<div>
<label>video input device:</label>
<select id="videoSource"></select>
</div>
<script src="client.js"></script>
</body>
</html>
我在里面引入我们编写的脚本client.js,这样当我们打开页面的时候,这个JavaScript代码就会执行,就是chrome浏览器会给他交到底层的V8引擎去解析然后去渲染,这样我们的html就算写完了。
下面我们来实现client.js:
// 使用严格语法
'use strict'
var audioSource = document.querySelector("select#audioSource");
var audioOutput = document.querySelector("select#audioOutput");
var videoSource = document.querySelector("select#videoSource");
// 首先判断浏览器是否支持此方法,在浏览器支持的情况下才调用
if (!navigator.mediaDevices || !navigator.mediaDevices.enumerateDevices) {
console.log('enumerateDevices is not supported!');
} else {
// 先申请设备权限
navigator.mediaDevices.getUserMedia({
audio: true,
video: true
});
// 获取所有的音频和视频设备,它返回的值是一个 Promise
navigator.mediaDevices.enumerateDevices()
.then(gotDevices)
.catch(handleError);
}
function gotDevices(deviceInfos) {
// 当我们拿到 deviceInfos 之后,就开始遍历这个数组
// 在这个每一项里面实际上我们可以注册一个匿名函数去处理每一项的内容,它的参数就是每一项的值
deviceInfos.forEach(function (deviceInfo) {
console.log(deviceInfo.kind + ": label = "
+ deviceInfo.label + ": id = "
+ deviceInfo.deviceId + ": groupId = "
+ deviceInfo.groupId);
var option = document.createElement('option');
option.text = deviceInfo.label;
option.value = deviceInfo.deviceId;
if (deviceInfo.kind === 'audioinput') {
audioSource.appendChild(option);
} else if (deviceInfo.kind === 'audiooutput') {
audioOutput.appendChild(option);
} else if (deviceInfo.kind === 'videoinput') {
videoSource.appendChild(option);
}
});
}
function handleError(err) {
console.log(err.name + " : " + err.message);
}
调试页面:
页面如下所示:
一开始浏览器可能会申请摄像头等权限,同意即可。
按F12进入控制台,可以看到如下输出:
网页中,3个列表如下所示:
设备检测
在获取到电脑 / 手机上的所有设备信息后,我们就可以对设备的可用性做真正的检测了。在我们的设备列表中,可以通过MediaDeviceInfo结构中的kind字段,将设备分类为音频设备或视频设备。
如果再细分的话,还可以通过 kind 字段再将音视设备分为输入设备和输出设备。如我们平时使用的耳机,从大的方面说它是一个音频设备,但它同时兼有音频输入设备和音频输出设备的功能。
对于区分出的音频设备和视频设备,每种不同种类的设备还会设置各自的默认设备。还是以耳机这个音频设备为例,将耳机插入电脑后,耳机就变成了音频的默认设备;将耳机拔出后,默认设备又切换成了系统的音频设备。
因此,在获取到所有的设备列表后,如果我们不指定某个具体设备,直接调用所介绍的 getUserMedia API 来采集音视频数据时,它就会从设备列表中的默认设备上采集数据。当然,我们是可以通过MediaDeviceInfo 中的 deviceID 字段来指定从哪个具体设备采集数据的。
如果我们能从指定的设备上采集到音视频数据,那说明这个设备就是有效的设备。我们在排查设备问题的时候,就可以利用上面的方法,对每个设备都一项一项进行检测,即先排查视频设备,然后再排查音频设备。因此,需要调用两次 getUserMedia API 进行设备检测。
第一次,调用 getUserMedia API 只采集视频数据并将其展示出来。如果用户能看到自己的视频,说明视频设备是有效的;否则,设备无效,可以再次选择不同的视频设备进行重新检测。
第二次,如果用户视频检测通过了,再次调用 getUserMedia API 时,则只采集音频数据。由于音频数据不能直接展示,所以需要使用 JavaScript 中的 AudioContext 对象,将采集到的音频计算后,再将其绘制到页面上。这样,当用户看到音频数值的变化后,说明音频设备也是有效的。