【声网】创建实时音频

文章目录
  1. 1. 快速上手
  2. 2. 创建实时音频
    1. 2.1. 采集音视频相关
      1. 2.1.1. 1. 获取摄像头列表
      2. 2.1.2. 2. 获取摄像头设备信息
      3. 2.1.3. 3. 创建麦克风音频轨道并渲染
      4. 2.1.4. #音频回调
        1. 2.1.4.1. 音频回调解码
          1. 2.1.4.1.1. 1) 属性
          2. 2.1.4.1.2. 2) 方法
          3. 2.1.4.1.3. 3) 例子
          4. 2.1.4.1.4. 4) 规格参数
    2. 2.2. 录音对象属性
    3. 2.3. 音频编码
      1. 2.3.1. AudioEncoderConfigurationPreset
  3. 3. 本地音频轨道
  4. 4. 对接

[Toc]

快速上手

  1. 安装
1
npm add @netless/fastboard @netless/window-manager white-web-sdk

创建实时音频

采集音视频相关

1. 获取摄像头列表

调用 getMicrophones 获取可用的摄像头列表

getMicrophones 方法会通过 Promise 异步返回一个 MediaDeviceInfo 对象的数组。MediaDeviceInfo 对象复用了 WebRTC API 中的 MediaDeviceInfo 对象。因为是异步方法,你可以使用 then/catch (ES6) 或 async/await (ES7) 获取返回值。

1
2
3
4
5
6
7
8
9
// 获取可用的麦克风设备列表。
// 调用时,如果浏览器还没有获得麦克风访问权限,会在界面上提示你是否允许浏览器访问麦克风。
AgoraRTC.getMicrophones()
.then((deviceInfoArray) => {
/* 返回 MediaDeviceInfo 数组对象之后的操作*/
})
.catch((e) => {
console.log("Failed to get microphones!", e);
});

2. 获取摄像头设备信息

在此教程中,我们在 HTML 中创建一个下拉菜单来供用户选择使用的摄像头设备。在用户界面上使用 label 属性显示设备信息。deviceId 属性用于保存设备 ID,用于后续的摄像头访问。

label,即设备标签,返回一个 DOMString,代表描述对应设备的标签。如果浏览器没有获取设备权限,则返回 ""

deviceId,即设备 ID,返回一个 DOMString,代表对应设备。设备 ID 对于应用是唯一的,只要浏览器的 cookie 没有被清除,即使你开启了新的浏览器会话(session),设备 ID 也会保持不变。如果你清除了浏览器 cookie,则设备 ID 会重置。同理,如果你开启了浏览器隐私模式,对于同一个设备,每个浏览器会话的设备 ID 都是不同的。因此,建议每次对设备进行操作时重新获取设备 ID。

HTML

1
2
3
4
5
6
7
8
9
<h1>通过麦克风采集并在本地渲染音频</h1>
<form>
<b> 选择你要使用的麦克风 </b>
<select id = "microphoneList" onchange = "getDeviceId()" >
<option> ---选择麦克风--- </option>
</select>
</form>
<p>你选择设备的 deviceId 是:</p>
<p id="deviceId"></p>

JavaScript

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 定义设备 ID 与标签的映射
let dict = {};

// 获取摄像头列表
AgoraRTC.getMicrophones()
.then((deviceInfoArray) => {
for (let deviceInfo of deviceInfoArray) {
let option = document.createElement("option");
document.getElementById("microphoneList").appendChild(option);
option.innerHTML = deviceInfo.label;
dict[deviceInfo.label] = deviceInfo.deviceId;
}
})
.catch((e) => {
console.log("Failed to get microphones!", e);
});

// 根据下拉菜单选择的设备标签,显示相应的设备 ID
function getDeviceId() {
let microphoneList = document.getElementById("microphoneList");
let deviceLabel = microphoneList.options[microphoneList.selectedIndex].text;
document.getElementById("deviceId").innerHTML = dict[deviceLabel];
}

3. 创建麦克风音频轨道并渲染

调用 createMicrophoneAudioTrack 创建麦克风音频轨道并调用成员方法 play 通过系统默认扬声器对视频进行渲染。这里轨道的概念和 WebRTC 中的 track 相似。一个轨道代表一路特定的视频源或音频源。声网 SDK 将不同来源的音视频轨道进行抽象,定义了摄像头视频轨道、屏幕采集视频轨道及自定义源视频轨道等。

这个步骤只是为了演示麦克风音频轨道的渲染。实际开发通话应用时,本地不需要播放本地麦克风采集的音频。

  1. HTML

    1
    2
    3
    4
    5
    6
    7
    8
    9
    <h1>通过麦克风采集并在本地渲染音频</h1>
    <form>
    <b> 选择你要使用的麦克风 </b>
    <select id="microphoneList" onchange="getDeviceId()">
    <option> ---选择麦克风--- </option>
    </select>
    </form>
    <p>你选择设备的 deviceId 是:</p>
    <p id="deviceId"></p>

    CSS

    1
    2
    body {font-family: system-ui;background: #f06d06;color: white;text-align: center;}
    div {height: 200px;width: 50%;}

    JavaScript

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    // 全局变量
    let dict = {}; // 使用 dict 映射设备标签和设备 ID
    let selectedDeviceId = ""; // 下选框选择的设备对应的 ID
    let microphoneAudioTrack = null; // 麦克风音频轨道对象

    // 获取本地麦克风列表
    AgoraRTC.getMicrophones()
    .then((deviceInfoArray) => {
    for (let deviceInfo of deviceInfoArray) {
    let option = document.createElement("option");
    document.getElementById("microphoneList").appendChild(option);
    option.innerHTML = deviceInfo.label;
    dict[deviceInfo.label] = deviceInfo.deviceId;
    }
    })
    .catch((e) => {
    console.log("Failed to get microphones!", e);
    });

    // 创建麦克风音频轨道
    AgoraRTC.createMicrophoneAudioTrack()
    .then((microphoneAudioTrack) => {
    // 渲染音频。SDK 使用系统默认的扬声器播放声音。
    microphoneAudioTrack.play();
    })
    .catch((e) => {
    console.log("Failed to play audio!", e);
    });

    // 根据选择的设备标签,返回对应的设备 ID 并传给麦克风音频轨道
    function getDeviceId() {
    let microphoneList = document.getElementById("microphoneList");
    let deviceLabel = microphoneList.options[microphoneList.selectedIndex].text;
    selectedDeviceId = dict[deviceLabel];
    document.getElementById("deviceId").innerHTML = selectedDeviceId;

    if (microphoneAudioTrack != null) {
    microphoneAudioTrack.setDevice(selectedDeviceId);
    }
    }

#音频回调

setAudioFrameCallback

  • setAudioFrameCallback(audioFrameCallback: null | function, frameSize?: undefined | number): void

设置原始音频数据(PCM)回调。

设置成功后,SDK 会不断地将远端音频轨道的音频帧以 AudioBuffer 的形式通过回调返回。

你可以通过 frameSize 来设置每次回调中音频帧的大小。该设置也会影响回调的间隔,frameSize 越大,每次回调的音频数据越多,回调间隔越长。

1
2
3
4
5
6
7
8
9
10
11
track.setAudioFrameCallback((buffer) => {
for (let channel = 0; channel < buffer.numberOfChannels; channel += 1) {
// Float32Array with PCM data
const currentChannelData = buffer.getChannelData(channel);
console.log("PCM data in channel", channel, currentChannelData);
}
}, 2048);

// ....
// Stop getting the raw audio data
track.setAudioFrameCallback(null);

音频回调解码

AudioBuffer - Web API 接口参考 | MDN

AudioBuffer 接口表示存在内存里的一段短小的音频资源,利用AudioContext.decodeAudioData()方法从一个音频文件构建,或者利用 AudioContext.createBuffer()从原始数据构建。把音频放入 AudioBuffer 后,可以传入到一个 AudioBufferSourceNode进行播放。

这些类型对象被设计来控制小音频片段,往往短于 45 秒。对于更长的声音,通过 MediaElementAudioSourceNode来实现更为合适。缓存区(buffer)包含以下数据:不间断的 IEEE754 32 位线性 PCM,从-1 到 1 的范围额定,就是说,32 位的浮点缓存区的每个样本在-1.0 到 1.0 之间。如果AudioBuffer有不同的频道,他们通常被保存在独立的缓存区。

1) 属性
2) 方法
3) 例子

以下的例子展示了如何构建一个 AudioBuffer 以及随机用白噪音填充。你可以在 audio-buffer demo库发现完整的源代码;一个running live 的版本也可获得。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// Stereo
var channels = 2;

// Create an empty two second stereo buffer at the
// sample rate of the AudioContext
var frameCount = audioCtx.sampleRate * 2.0;
var myArrayBuffer = audioCtx.createBuffer(channels, frameCount, audioCtx.sampleRate);

button.onclick = function() {
// Fill the buffer with white noise;
// just random values between -1.0 and 1.0
for (var channel = 0; channel < channels; channel++) {
// This gives us the actual array that contains the data
var nowBuffering = myArrayBuffer.getChannelData(channel);
for (var i = 0; i < frameCount; i++) {
// Math.random() is in [0; 1.0]
// audio needs to be in [-1.0; 1.0]
nowBuffering[i] = Math.random() * 2 - 1;
}
}

// Get an AudioBufferSourceNode.
// This is the AudioNode to use when we want to play an AudioBuffer
var source = audioCtx.createBufferSource();

// set the buffer in the AudioBufferSourceNode
source.buffer = myArrayBuffer;

// connect the AudioBufferSourceNode to the
// destination so we can hear the sound
source.connect(audioCtx.destination);

// start the source playing
source.start();

}
4) 规格参数
Specification
Web Audio API # AudioBuffer

录音对象属性

Agora Web API Reference - 语音通话 - 文档中心 - 声网Agora

音频编码

AudioEncoderConfigurationPreset

AudioEncoderConfigurationPreset: keyof typeof AUDIO_ENCODER_CONFIG_SETTINGS

SDK 预设的 [AudioEncoderConfiguration](https://docs.agora.io/cn/Voice/API Reference/web_ng/interfaces/audioencoderconfiguration.html) 配置。

你可以在以下方法中传入预设值来控制本地音频的编码配置:

下表列出了 SDK 所有内置的音频属性配置,SDK 默认使用 "music_standard"

音频属性 配置
"speech_low_quality" 16 kHz 采样率,单声道,编码码率约 24 Kbps
"speech_standard" 32 kHz 采样率,单声道,编码码率约 24 Kbps
"music_standard" 48 kHz 采样率,单声道,编码码率约 40 Kbps
"standard_stereo" 48 kHz 采样率,双声道,编码码率约 64 Kbps
"high_quality" 48 kHz 采样率,单声道, 编码码率约 128 Kbps
"high_quality_stereo" 48 kHz 采样率,双声道,编码码率约 192 Kbps

本地音频轨道

Agora Web API Reference - 语音通话 - 文档中心 - 声网Agora

对接

基于声网 Web SDK 实现视频通话场景 - 专栏 - 声网 Agora RTC 开发者社区