WebRTC系列文章
4.WebRTC源码研究(4)web服务器工作原理和常用协议基础
WebRTC源码研究(13)音频约束
本篇博客 下载地址:点击这里下载
1. WebRTC音频设置api
在浏览器中,webrtc允许你使用如下代码去设置视频,音频参数:
var constraints = { // 表示同时采集视频金和音频
video : {
width: 640, // 宽带
height: 480, // 高度
frameRate:15, // 帧率
facingMode: 'enviroment', // 设置为后置摄像头
deviceId : deviceId ? deviceId : undefined // 如果deviceId不为空直接设置值,如果为空就是undefined
},
audio : {
noiseSuppression: true, // 降噪
echoCancellation: true // 回音消除
},
}
// 从指定的设备中去采集数据
navigator.mediaDevices.getUserMedia(constraints)
.then(gotMediaStream) // 使用Promise串联的方式,获取流成功了
.then(gotDevices)
.catch(handleError);
如上面代码中的 :noiseSuppression: true, // 降噪 echoCancellation: true // 回音消除
这些属性就是设置音频参数的api了,类似的还有如下这些:
api | 含义 | 使用说明 |
---|---|---|
volume | 音量大小 | 数值是从0到1.0,0就是静音 ,1.0就是最大音量。 |
sampleRate | 采样率 | 在音频里面有很多前人已经调好的采样率,固定的几个如:四万八,三万二,一万六,八千等,根据自己的需要设置就好了 |
sampleSize | 采样大小 | 每一个采样大小是由多少位表示,一般情况下我们都是用16位,也就是两个字节 |
echoCancellation | 回音消除 | 一个bool类型的值,用来开启关闭回音消除功能。我们在开启采集 数据之后,是否要开启回音消除,在实时直播的过程中,回音消除是 一个非常重要的功能,当双方通信的时候 ,如果有回音传过来,对这个通话质量会造成极大的影响,设置可以通过true或false来开启或关闭回音消除。 |
autoGainControl | 自动增益 | 一个bool类型的值,用来开启关闭自动增益功能。在我原有的录制的声音的基础上是否给他增加这个音量,它增加的范围也是有一定范围的,这也是一个用 true或false设置的,也就是开始或者关闭 |
noiseSuppression | 降噪 | 一个bool类型的值,用来开启关闭降噪功能。我们在采集数据的时候是否要开启降噪功能 |
latency | 延迟大小 | 直播过程中的音视频,如果latency 设置小的话,就表示它的 视频传输试试通信的时候它的延迟就会小,延迟小的后果就是当你网络状况不好的时候,它就会出现卡顿甚至花屏等质量问题。但是它的好处是我们双方可以实时通信,一般是低于500毫秒,这是非常好的一个质量了。当然最好的是200毫秒以内,从采集到编码到传输到对方接受且解码再到渲染整个过程中是200毫秒以内是最好的 。我们正常通话应该是这样一个延迟。我们感受500毫秒还可以,再往上800毫秒 就比较大了。如果你的latency 设置的大,它的好处是你的画面比你声音更平滑,但是它这种及时性,就是说如果做实时通信的话 ,就会很麻烦,你说了一句话,你过了一秒多,对方才听到,在回答你又过了一秒。这种交互的话就没法忍受了。 |
channelCount | 声道数量,表示单声道还是双声道 | 一般情况下我们会使用单声道,如果是对于一些乐器,都是双声道,这样的话音质才更好。 |
deviceID | 设备ID,用来区分硬件设备 | devideID就是当我有多个输入输出设备的时候,我可以进行设备的切换 ,比如在手机上当我改变了devideID之后,我从前置摄像头就可以切换到后置摄像头。 |
groupID | 组ID | 是groupID,它代表 是同一个物理设备,对于音频来说,音频的输入输出是同一个物理设备,不同浏览器其实它的实现是不一样的,对于chrome来说它分成了音频的输入输出,对于FireFOX和safari就没有音频的输出,音频视频设备就是一个音频设备。 |
2. 音频约束
上面表中的约束也可以写成最大最小的范围限制,这些 都可以设置,这样它就是一个动态的,它会在这个范围内选择一个最好的,不是说摄像的时候实时变化 ,是说它选择的时候会在这个范围内选择一个最好的。帧率他是可以根据网络的情况去调整的。
如下:
完整音视频约束的JS代码如下:
'use strict'
var audioSource = document.querySelector('select#audioSource');
var audioOutput = document.querySelector('select#audioOutput');
var videoSource = document.querySelector('select#videoSource');
// 获取video标签
var videoplay = document.querySelector('video#player');
// deviceInfos是设备信息的数组
function gotDevices(deviceInfos){
// 遍历设备信息数组, 函数里面也有个参数是每一项的deviceinfo, 这样我们就拿到每个设备的信息了
deviceInfos.forEach(function(deviceinfo){
// 创建每一项
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);
}
})
}
// 获取到流做什么, 在gotMediaStream方面里面我们要传人一个参数,也就是流,
// 这个流里面实际上包含了音频轨和视频轨,因为我们通过constraints设置了要采集视频和音频
// 我们直接吧这个流赋值给HTML中赋值的video标签
// 当时拿到这个流了,说明用户已经同意去访问音视频设备了
function gotMediaStream(stream){
videoplay.srcObject = stream; // 指定数据源来自stream,这样视频标签采集到这个数据之后就可以将视频和音频播放出来
// 当我们采集到音视频的数据之后,我们返回一个Promise
return navigator.mediaDevices.enumerateDevices();
}
function handleError(err){
console.log('getUserMedia error:', err);
}
function start() {
// 判断浏览器是否支持
if(!navigator.mediaDevices ||
!navigator.mediaDevices.getUserMedia){
console.log('getUserMedia is not supported!');
}else{
// 获取到deviceId
var deviceId = videoSource.value;
// 这里是约束参数,正常情况下我们只需要是否使用视频是否使用音频
// 对于视频就可以按我们刚才所说的做一些限制
var constraints = { // 表示同时采集视频金和音频
video : {
width: 640, // 宽带
height: 480, // 高度
frameRate:15, // 帧率
facingMode: 'enviroment', // 设置为后置摄像头
deviceId : deviceId ? deviceId : undefined // 如果deviceId不为空直接设置值,如果为空就是undefined
},
audio : {
noiseSuppression: true, // 降噪
echoCancellation: true // 回音消除
},
}
// 从指定的设备中去采集数据
navigator.mediaDevices.getUserMedia(constraints)
.then(gotMediaStream) // 使用Promise串联的方式,获取流成功了
.then(gotDevices)
.catch(handleError);
}
}
start();
// 当我选择摄像头的时候,他可以触发一个事件,
// 当我调用start之后我要改变constraints
videoSource.onchange = start;
3. 音频约束实战
我们来看一个WebRTC 官方在git上获取音频的demo,
运行效果如下:
test.html内容如下:
<html>
<head>
<meta charset="utf-8">
<meta name="description" content="kongyulu WebRTC audio samples">
<meta name="viewport" content="width=device-width, user-scalable=yes, initial-scale=1, maximum-scale=1">
<meta itemprop="description" content="Client-side WebRTC code samples">
<meta itemprop="image" content="../../../images/webrtc-icon-192x192.png">
<meta itemprop="name" content="WebRTC code samples">
<meta name="mobile-web-app-capable" content="yes">
<meta id="theme-color" name="theme-color" content="#ffffff">
<base target="_blank">
<title>get audio</title>
</head>
<body>
<div id="container">
<h1><a href="//webrtc.github.io/samples/" title="WebRTC samples homepage">WebRTC samples</a> <span>getUserMedia, audio only</span>
</h1>
<audio id="gum-local" controls autoplay></audio>
<p class="warning">Warning: if you're not using headphones, pressing play will cause feedback.</p>
<p>Render the audio stream from an audio-only <code>getUserMedia()</code> call with an audio element.</p>
<p>The <code>MediaStream</code> object <code><em>stream</em></code> passed to the <code>getUserMedia()</code>
callback is in global scope, so you can inspect it from the console.</p>
<div>
<span id="errorMsg"></span>
</div>
<a href="https://github.com/webrtc/samples/tree/gh-pages/src/content/getusermedia/audio"
title="View source for this page on GitHub" id="viewSource">View source on GitHub</a>
</div>
<script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
<script src="js/test.js"></script>
</body>
</html>
test.js 内容如下:
'use strict';
// Put variables in global scope to make them available to the browser console.
const audio = document.querySelector('audio');
const constraints = window.constraints = {
audio: true, //设置约束,只获取音频
video: false //设置约束,只获取音频,不获取视频
};
function handleSuccess(stream) {
//获取所有音频轨
const audioTracks = stream.getAudioTracks();
console.log('Got stream with constraints:', constraints);
console.log('Using audio device: ' + audioTracks[0].label);
stream.oninactive = function() {
console.log('Stream ended');
};
window.stream = stream; // make variable available to browser console
audio.srcObject = stream;
}
function handleError(error) {
const errorMessage = 'navigator.MediaDevices.getUserMedia error: ' + error.message + ' ' + error.name;
errorMsgElement.innerHTML = errorMessage;
console.log(errorMessage);
}
navigator.mediaDevices.getUserMedia(constraints).then(handleSuccess).catch(handleError);