[代码重构](master): 通过WebRTC进行音视频设备检测

2022年11月22日14:16:59
master
土豆兄弟 2 years ago
parent d437802b12
commit 742fb4335f

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

@ -33,6 +33,9 @@
CDN 拉取媒体流 CDN 拉取媒体流
## 2. WebRTC1对1通话 ## 2. WebRTC1对1通话
### 2.1 通过浏览器访问摄像头
- 随着 WebRTC 1.0 规范的推出,现在主流浏览器 **Chrome、Firefox、Safari 以及 Edge** 都已经支持了 WebRTC 库。换句话说,在这些浏览器之间进行实时音视频通信已经很成熟了。 - 随着 WebRTC 1.0 规范的推出,现在主流浏览器 **Chrome、Firefox、Safari 以及 Edge** 都已经支持了 WebRTC 库。换句话说,在这些浏览器之间进行实时音视频通信已经很成熟了。
- WebRTC 处理过程 - WebRTC 处理过程
@ -209,3 +212,141 @@ navigator.mediaDevices.getUserMedia(mediaStreamContrains).then(
使用localhost访问navigator.mediaDevices. 存在 使用localhost访问navigator.mediaDevices. 存在
- 出于安全的原因你只能用localhost 访问或https 访问时才能检测到mediaDevice - 出于安全的原因你只能用localhost 访问或https 访问时才能检测到mediaDevice
### 2.2 通过WebRTC进行音视频设备检测
- 在打开摄像头Camera或麦克风Micphone的时候首先要对其进行检测检测的内容包括
- 电脑 / 手机上都有那些音视频设备?
- 我们选中的音视频设备是否可用?
- WebRTC 处理过程
- WebRTC 的整体处理过程图:
- ![WebRTC的整体处理过程图](pic/WebRTC的整体处理过程图.png)
- 图中两个音视频设备检测模块置红了
- 音视频设备的基本原理
- 音频设备
- 音频有**采样率和采样大小**的概念,实际上这两个概念就与音频设备密不可分。
- 音频输入设备的主要工作是采集音频数据而采集音频数据的本质就是模数转换A/D即将模似信号转换成数字信号。
- 模数转换使用的采集定理称为**奈奎斯特定理**,其内容如下:
- 在进行模拟 / 数字信号的转换过程中,当采样率大于信号中最高频率的 2 倍时,采样之后的数字信号就完整地保留了原始信号中的信息。
- 你也知道,人类听觉范围的频率是 20Hz20kHz 之间。对于日常语音交流像电话8kHz 采样率就可以满足人们的需求。
但为了追求高品质、高保真,你需要将音频输入设备的采样率设置在 40kHz 以上,这样才能完整地将原始信号保留下来。
例如我们平时听的数字音乐,一般其采样率都是 44.1k、48k 等,以确保其音质的无损。
- 采集到的数据再经过量化、编码,最终形成数字信号,这就是音频设备所要完成的工作。在量化和编码的过程中,采样大小(保存每个采样的二进制位个数)决定了每个采样最大可以表示的范围。
如果采样大小是 8 位,则它表示的最大值是就是 28 -1即 255如果是 16 位,则其表示的最大数值是 65535。
- 视频设备
- 至于视频设备则与音频输入设备很类似。当实物光通过镜头进行到摄像机后它会通过视频设备的模数转换A/D模块即光学传感器 将光转换成数字信号,即 RGBRed、Green、Blue数据。
- 获得 RGB 数据后,还要通过 DSPDigital Signal Processer进行优化处理如自动增强、白平衡、色彩饱和等都属于这一阶段要做的事情。
- 通过 DSP 优化处理后,你就得到了 24 位的真彩色图片。因为每一种颜色由 8 位组成,而一个像素由 RGB 三种颜色构成,所以一个像素就需要用 24 位表示,故称之为**24 位真彩色**。
- 另外,此时获得的 RGB 图像只是临时数据。因最终的图像数据还要进行压缩、传输,而编码器一般使用的输入格式为 YUV I420所以在摄像头内部还有一个专门的模块用于将 RGB 图像转为 YUV 格式的图像。
- 那什么是 YUV 呢YUV 也是一种色彩编码方法主要用于电视系统以及模拟视频领域。它将亮度信息Y与色彩信息UV分离即使没有 UV 信息一样可以显示完整的图像,只不过是黑白的,
这样的设计很好地解决了彩色电视机与黑白电视的兼容问题。
- YUV 格式还是蛮复杂的,它有好几种存储方式
- WebRTC 设备管理的基本概念
- MediaDevices该接口提供了访问连接到计算机上的媒体设备如摄像头、麦克风以及截取屏幕的方法。实际上它允许你访问任何硬件媒体设备。而咱们**要获取可用的音视频设备列表**,就是通过该接口中的方法来实现的。
- ps: https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices
- MediaDeviceInfo它表示的是每个输入 / 输出设备的信息。包含以下三个重要的属性:
- deviceID**设备的唯一标识**
- label设备 **名称**
- kind设备 **种类**,可用于识别出是音频设备还是视频设备,是输入设备还是输出设备。
- ps: https://developer.mozilla.org/en-US/docs/Web/API/MediaDeviceInfo
- **另外label 可以用作指纹识别机制的一部分,以识别是否是合法用户**。对于这一点我们以后再专门讨论。
- Promise它是一种 JavaScript 异步处理机制。其思想是,首先执行指定的业务逻辑,而不管逻辑的对错,然后再根据结果做具体的操作:如果成功了做些什么,失败了做些什么。
结合下面的例子,可以让你对 Promise 有个清楚的认识,生成 Promise 对象时,首先会执行 function 函数中的逻辑,该函数会根据随机数生成 timeOut
然后定时地对 timeOut 做出判断:
- ps: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
- 如果 timeOut 小于 1则调用 resolve 方法。resolve 又会调用 Promise 中 then 部分传入的函数。
- 如果 timeOut 大于等于 1则调用 reject 方法。reject 则会调用 Promise 中 catch 部分传入的函数。
```js
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 获取音视频设备列表的接口,其格式如下:
```js
MediaDevices.enumerateDevices()
```
- 通过调用 MediaDevices 的 **enumerateDevices()** 方法就可以获取到媒体输入和输出设备列表,例如: 麦克风、相机、耳机等。是不是非常简单?
- ps: https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/enumerateDevices
- 该函数返回的是一个 Promise 对象。我们只需要向它的 then 部分传入一个函数,就可以通过该函数获得所有的音视频设备信息了。
- 传入的函数有一个参数,它是一个 MediaDeviceInfo 类型的数组,用来存放 WebRTC 获取到的每一个音视频设备信息。
- 这样说可能有点抽象,还是让我们结合下面代码看一个具体的例子吧。
```js
...
// 判断浏览器是否支持这些 API
if (!navigator.mediaDevices || !navigator.mediaDevices.enumerateDevices) {
console.log("enumerateDevices() not supported.");
return;
}
// 枚举 cameras and microphones.
navigator.mediaDevices.enumerateDevices()
.then(function(deviceInfos) {
// 打印出每一个设备的信息
deviceInfos.forEach(function(deviceInfo) {
console.log(deviceInfo.kind + ": " + deviceInfo.label +
" id = " + deviceInfo.deviceId);
});
})
.catch(function(err) {
console.log(err.name + ": " + err.message);
});
```
- 总结起来,上面的代码中做了以下几件事儿:
- 首先,判断浏览器是否支持 MediaDevice 接口(老版本浏览器可能不支持)。
- 如果支持则调用navigator.mediaDevices.enumerateDevices()方法获取音视频设备列表,该方法会返回一个 Promise 对象。
- 如果返回 Promise 对象成功,则执行 then 中的函数。而then分支中的函数非常简单它遍历每一个 MediaDeviceInfo
并将每个 MediaDeviceInfo 中的基本信息打印出来,也就是我们想要的每个音视频设备的基本信息。
- 但如果失败的话,则执行 catch 中的函数。
- 设备检测
- 在获取到电脑 / 手机上的所有设备信息后,我们就可以对设备的可用性做真正的检测了。在我们的设备列表中,
可以通过MediaDeviceInfo结构中的 **kind** 字段,将设备分类为音频设备或视频设备。
- ps: https://developer.mozilla.org/en-US/docs/Web/API/MediaDeviceInfo
- 如果再细分的话,还可以通过 kind 字段再将音视设备分为输入设备和输出设备。如我们平时使用的耳机,从大的方面说它是一个音频设备,但它同时兼有**音频输入设备和音频输出设备**的功能。
- 对于区分出的音频设备和视频设备,每种不同种类的设备还会设置各自的默认设备。还是以耳机这个音频设备为例,将耳机插入电脑后,耳机就变成了音频的**默认设备**;将耳机拔出后,默认设备又切换成了系统的音频设备。
- 因此,在获取到所有的设备列表后,如果我们不指定某个具体设备,直接调用介绍的 getUserMedia API 来采集音视频数据时,它就会从设备列表中的默认设备上采集数据。
当然,我们是可以通过 MediaDeviceInfo 中的 deviceID 字段来指定从哪个具体设备采集数据的,不过这就是后话了。
- 如果我们能从指定的设备上采集到音视频数据,那说明这个设备就是有效的设备。我们在排查设备问题的时候,就可以利用上面的方法,对每个设备都一项一项进行检测,即**先排查视频设备,然后再排查音频设备**。
因此,需要**调用两次 getUserMedia API 进行设备检测**。
- 第一次,调用 getUserMedia API 只采集视频数据并将其展示出来。如果用户能看到自己的视频,说明视频设备是有效的;否则,设备无效,可以再次选择不同的视频设备进行重新检测。
- 第二次,如果用户视频检测通过了,再次调用 getUserMedia API 时,则只采集音频数据。由于音频数据不能直接展示,所以需要使用 JavaScript 中的 AudioContext 对象,将采集到的音频计算后,
再将其绘制到页面上。这样,当用户看到音频数值的变化后,说明音频设备也是有效的。

Loading…
Cancel
Save