WebRTC源码研究(13)音频约束

1,245 阅读7分钟

WebRTC系列文章

  1. WebRTC源码研究(1)WebRTC架构

  2. WebRTC源码研究(2)webrtc源码目录结构

  3. WebRTC源码研究(3)webrtc运行机制

4.WebRTC源码研究(4)web服务器工作原理和常用协议基础

  1. [WebRTC源码研究(5)Nodejs搭建环境

  2. [WebRTC源码研究(6)创建简单的HTTP服务

  3. [WebRTC源码研究(7)

  4. [WebRTC源码研究(8)

  5. [WebRTC源码研究(9)

  6. [WebRTC源码研究(10)

  7. [WebRTC源码研究(11)获取音视频设备的访问权限

  8. [WebRTC源码研究(12)视频约束

  9. [WebRTC源码研究(13)音频约束

  10. [WebRTC源码研究(14)视频特效

  11. [WebRTC源码研究(15)从视频中获取图

  12. [WebRTC源码研究(16)只采集音频数据

  13. [WebRTC源码研究(17)MediaStreamAPI 获取视频约束

  14. [WebRTC源码研究(18)WebRTC录制视频原理

  15. [WebRTC源码研究(19)WebRTC录采集平面数据

  16. [WebRTC源码研究(20)WebRTC信令服务器原理

  17. WebRTC源码研究(21)WebRTC信令服务器实现

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);