Bootstrap

🏆【声网 Agora】「PC端实现实时语音通讯4.x」

承接上文

基于上文的语音服务端搭建之后[],我们即可以实现相关的客户端的开发,进行语音通讯的实现方案。这里终端方式很多,例如Android 或者IOS,这里我们采用Web服务端进行开发。

语音通话

语音通话可以实现纯语音的一对一单聊和多人群聊,不具备视频通话功能,包体积更小,适用于各种语音社交、语音会议等场景。

开发特性

  • 针对于语音通话的功能场景可参考

  • 针对于语音通话的关键特性可参考

  • 针对于语音通话的平台兼容性可参考

环境准备

由于浏览器的安全策略对除 127.0.0.1 以外的 HTTP 地址作了限制,Agora Web SDK 仅支持 HTTPS 协议或者 http://localhost(http://127.0.0.1)。请勿使用 HTTP 协议在 http://localhost(http://127.0.0.1) 之外访问你的项目。

需要准备Agora账号

具体参考或【

里面的相关步骤介绍即可。

集成AgoraSDK

使用 npm 获取 SDK

使用该方法需要先安装 npm,详见 

npm install agora-rtc-sdk-ng --save
import AgoraRTC from "agora-rtc-sdk-ng" 
const client = AgoraRTC.createClient({ mode: "rtc", codec: "vp8" });

import AgoraRTC, { IAgoraRTCClient } from "agora-rtc-sdk-ng" 
const client: IAgoraRTCClient = AgoraRTC.createClient({ mode: "rtc", codec: "vp8" });
使用CDN方法获取 SDK

(我最喜欢的方法,但会非常依赖于网络)该方法无需下载安装包。在项目 HTML 文件中,添加如下代码:

手动下载 SDK

使用AgoraAPI

  • 在方法 2 和方法 3 中,SDK 都会在全局导出一个 AgoraRTC 对象,直接访问这个对象即可操作 SDK。

  • 我们已经将 Agora Web SDK 集成到项目中了。下一步我们要调用 Agora Web SDK 提供的核心 API。

音频流由音频轨道构成。在 Agora Web SDK 中,我们通过操作音频轨道对象来控制音频流的行为。

图片来源:https://web-cdn.agora.io/docs-files/1617875857986

参数全局定义
var rtc = {
  // 用来放置本地客户端。
  client: null,
  // 用来放置本地音频轨道对象。
  localAudioTrack: null,
};

var options = {
  // 替换成你自己项目的 App ID。
  appId: "",
  // 传入目标频道名。
  channel: "demo_channel_name",
  // 如果你的项目开启了 App 证书进行 Token 鉴权,这里填写生成的 Token 值。
  token: null,
};

async function startBasicCall() {
  /**
   * 接下来的代码写在这里。
   */
}

startBasicCall();
创建本地客户端

AgoraRTCClient它代表一个本地客户端。 AgoraRTCClient类的方法提供了音频通话的主要功能,例如加入频道、发布音频轨道等。根据项目的App ID创建一个本地客户端 AgoraRTCClient对象调用 createClient 方法创建本地客户端对象。具体代码如下:

const client = AgoraRTC.createClient({
  codec: "vp8",
  mode: "rtc",
});

需注意 mode 和 codec 这两个参数的设置:

  • mode 用于设置是 Agora 为了对不同的实时音视频场景进行针对性算法优化而提供的一种设置选项。SDK 支持两种频道场景:"rtc"(通信场景) 和 "live"(直播场景)。Agora Web SDK 会根据使用场景的不同实行不同的优化策略。一对一或多人通话中,建议设为 "rtc",使用通信场景。互动直播中,建议设为 "live",使用直播场景。

rtc"(通信场景)适用于频道内所有用户需要相互交流且用户总数不太多的场景,如多人会议和在线聊天。

"live"(直播场景)适用于发布端很少但是订阅端很多的场景,这种场景下 SDK 定义了两种用户角色:观众(默认)和主播。主播能够发送和接收音视频,观众不能发送、只能接收音视频。你可以通过设置 createClient 的  参数来指定用户角色,也可以调用  来动态修改用户角色。

  •  用于设置浏览器使用的编解码格式,支持 (VP8)和 (H.264)两种视频编码格式。。如果你需要使用 Safari 12.1 及之前版本,将该参数设为 ;其他情况我们推荐使用 ,设置只会影响发布端的视频编码格式,对于订阅端来说只要其支持该格式的解码,都能正常完成订阅。

桌面端 Chrome 58 及以上版本既支持 VP8 也支持 H.264,而 Safari 12.1 以下版本不支持 VP8 编解码;如果频道中有两个主播分别发布了 VP8 和 H.264 的视频流,使用桌面端 Chrome 58 的观众可以解码这两个主播的视频,使用 Safari 12.1 以下版本浏览器的观众只能解码 H.264 的视频流。

加入目标频道

调用 join 加入目标频道。该方法返回一个 Promise,当返回 resolve 时表示加入频道成功,返回 reject 时表示加入频道出现错误。我们可以利用 async/await 极大地简化我们的代码。

const uid = await rtc.client.join(options.appId, options.channel, options.token, null);

// 自动分配数字 UID
const uid = await client.join("APPID", "CHANNEL", "TOKEN");

// 指定数字 UID
await client.join("APPID", "CHANNEL", "TOKEN", 393939);

调用  方法时你需要注意以下参数:

  • appid: 你的 App ID。

  • channel: 频道名,长度在 64 字节以内的字符串。在我们的示例项目中,channel的值设为 demo_channel_name

  • token: (可选)如果你的 Agora 项目开启了 App 证书,你需要在该参数中传入一个 Token,详见。在测试环境,我们推荐使用控制台生成临时 Token,详见。在生产环境,我们推荐你在自己的服务端生成 Token,详见

  • 在我们的示例项目中,为了叙述方便,没有开启 App 证书,所以不需要校验 Token, 的值为 。如果你启用了 App 证书,请确保上面传入的 channel 值和生成 Token 时传入的 channel 值保持一致。

  • uid用户 ID,频道内每个用户的 UID 必须是唯一的。你可以填 null,Agora 会自动分配一个 UID 并在 join 的结果中返回。

创建并发布本地音频轨道

 对象和  对象,代表本地和远端的音频轨道对象,用于播放等音频相关的控制。

//案例1
// 通过麦克风采集的音频创建本地音频轨道对象。
rtc.localAudioTrack = await AgoraRTC.createMicrophoneAudioTrack();
// 将这些音频轨道对象发布到频道中。
await rtc.client.publish([rtc.localAudioTrack]);
console.log("publish success!");

//案例2
const localAudioTrack = ...;
const localVideoTrack = ...;

// 你可以多次调用 publish 发布多个轨道
await client.publish(localAudioTrack);
await client.publish(localVideoTrack);

// 也可以一次性将需要发布的轨道一起发布
await client.publish([localAudioTrack, localVideoTrack]);

我们先调用 createMicrophoneAudioTrack 通过麦克风采集的音频创建本地音频轨道对象,然后调用 publish 方法,将这些本地音频轨道对象当作参数即可将音频发布到频道中。

  • 以上方法都会返回 Promise,resolve 时代表成功,reject 时代表失败。我们使用 async/await 来让代码逻辑更清晰。

  • 由于加入频道和创建本地音频轨道没有依赖关系,你可以利用 Promise.all 同时执行这些异步操作。

取消发布音视频

成功发布本地轨道后,如果想取消发布,可以调用 

// 发布音视频

await client.publish([localAudioTrack, localVideoTrack]);

// 取消发布视频,此时音频还在正常发布

await client.unpublish(localVideoTrack);

// 也可以一次将所有正在发布的轨道全部取消发布

await client.unpublish();

// 或者批量取消发布

await client.unpublish([localAudioTrack, localVideoTrack]);

关于取消发布,注意事项如下:

  • 该方法可以多次调用。你可以使用 publish 和 unpublish 实现发布和取消发布某个本地轨道。

  • 该方法为异步方法,使用时需要配合 Promise 或 async/await。

订阅远端用户

当远端用户成功发布音视频轨道之后,SDK 会触发  事件。这个事件携带两个参数:远端用户对象(user)和远端发布的媒体类型(mediaType)。此时,你可以调用  发起订阅。

当远端用户发布音频轨道时,SDK 会触发 client.on("user-published") 事件。我们需要通过 client.on 监听该事件并在回调中订阅新加入的远端用户。

建议在创建客户端对象之后立即监听事件,以避免错过任何事件。放在这里介绍是因为叙述顺序。

在 createClient 后下一行插入以下代码,监听 client.on("user-published") 事件,当有远端用户发布时开始订阅,并在订阅后自动播放远端音频轨道对象。


// 该方法为异步方法,使用时需要配合 Promise 或者 async/await。

client.on("user-published", async (user, mediaType) => {
  // 发起订阅
  await client.subscribe(user, mediaType);

  // 如果订阅的是音频轨道
  if (mediaType === "audio") {
    const audioTrack = user.audioTrack;
    // 自动播放音频
    audioTrack.play();
  } else {
    const videoTrack = user.videoTrack;
    // 自动播放视频
    videoTrack.play(DOM_ELEMENT);
  }
});


// 监听1
rtc.client.on("user-published", async (user, mediaType) => {
  // 开始订阅远端用户。
  await rtc.client.subscribe(user, mediaType);
  console.log("subscribe success");
  // 表示本次订阅的是音频。
  if (mediaType === "audio") {
    // 订阅完成后,从 `user` 中获取远端音频轨道对象。
    const remoteAudioTrack = user.audioTrack;
    // 播放音频因为不会有画面,不需要提供 DOM 元素的信息。
    remoteAudioTrack.play();
  }
});

当订阅方法调用完成之后,你可以通过 user.audioTrack 和 user.videoTrack 获取相应的  和  对象。

订阅和发布不同,每次订阅只能订阅一个音频或视频轨道。即使发布端同时发布了音频轨道和视频轨道,SDK 也会触发两次 user-published 事件:一次 user-published(audio),一次 user-published(video)。按照上面的代码逻辑,会完成两次订阅。

user-published 事件的第二个参数 mediaType,代表远端用户当前发布的媒体类型:

  • audio: 远端用户发布了音频轨道。

  • video: 远端用户发布了视频轨道。

当远端用户取消发布或离开频道时,SDK 会触发 client.on("user-unpublished") 事件。

取消订阅音视频

你可以通过  取消订阅远端的音视频。

[订阅目标用户的音视频]

// 
await client.subscribe(user, "audio");
await client.subscribe(user, "video");

// 取消订阅视频
await client.unsubscribe(user, "video");
// 也可以取消订阅当前用户的所有媒体类型
await client.unsubscribe(user);

关于取消订阅,注意事项如下:

  • 取消订阅成功后,SDK 会释放相应的 RemoteTrack 对象。一旦远端轨道对象被释放,SDK 会自动移除视频的播放元素,音频播放也会停止。

  • 如果远端用户主动取消发布,本地会收到 user-unpublished 回调,收到该回调时 SDK 会自动释放相应的 RemoteTrack 对象,你无需再调用 unsubscribe。

  • 该方法为异步方法,使用时需要配合 Promise 或 async/await。

调整通话音量

在使用 Agora Web SDK 时,你可以对 SDK 采集到的声音及 SDK 播放的声音音量进行调整,以满足产品在声音上的个性化需求。比如进行双人通话时,可以通过这个功能调整麦克风采集音量或者远端用户音量。

SDK 为本地音频轨道和远端音频轨道对象分别提供 setVolume 方法用于调整本地音频的采集音量和远端音频的播放音量。

调节远端音频的播放音量

以下示例中 remoteUser 是指已订阅的远端用户对象。

// 将音量减少一半
remoteUser.audioTrack.setVolume(50);
// 将音量增大一倍
remoteUser.audioTrack.setVolume(200);
// 将远端音量设置为 0
remoteUser.audioTrack.setVolume(0);

调节本地音频的采集音量

以下示例中 localAudioTrack 是指自己创建的本地音频轨道对象。

AgoraRTC.createMicrophoneAudioTrack().then(localAudioTrack => {
  // 麦克风音量减半
  localAudioTrack.setVolume(50);
  // 麦克风音量增大一倍
  localAudioTrack.setVolume(200);
  // 将麦克风音量设置为 0
  localAudioTrack.setVolume(0);
});
离开频道

调用 leave 后,SDK 会立刻销毁与当前频道相关的对象,包括订阅的远端用户对象、远端轨道对象、

记录连接状态的对象等。如果需要再次加入频道,在调用 leave 后再调用 join 即可。

通过以下步骤离开频道:

async function leaveCall() {
  // 销毁本地音频轨道。
  rtc.localAudioTrack.close();

  // 离开频道。
  await rtc.client.leave();
}

在加入频道的过程中,因为 SDK 使用不当或者网络异常等原因,可能会抛出以下错误:

  • INVALID_PARAMS: 提供的参数错误,比如提供了格式非法的 Token。

  • INVALID_OPERATION: 非法操作。该错误通常是重复加入频道引起的,请确保重复加入时先调用 leave。

  • OPERATION_ABORTED: 加入被中止,表示在 join 方法成功之前就调用了 leave 方法。

  • UNEXPECTED_RESPONSE: Agora 服务器返回了非预期的响应,通常是因为 App ID 或 Token 鉴权失败,例如开启了 App 证书却未传入 Token。

  • UID_CONFLICT: 创建了多个 AgoraRTCClient 对象,且重复使用了同一个用户 ID。