声网视频 SDK 被广泛应用于多种实时互动场景中,例如视频会议、视频通话、音视频社交、在线教育等。为了让刚刚接触声网 SDK 的开发者,可以更顺畅地实现基础的视频通话功能,我们基于声网 Web SDK 4.x 版本梳理了本篇教程。

在本文末,会提供相应 Demo 、文档地址供大家参考使用。同时,欢迎点击此处注册声网账号体验。声网每个月会为开发者提供 10000 分钟的免费额度。

本文为「声网 SDK 教程」系列内容

01 Demo 体验

我们在 GitHub 上提供一个开源的基础视频通话示例项目,在开始开发之前你可以通过该示例项目体验音视频通话效果。Demo 与线上体验地址,可在文末获取。

02 动手实践

实践任务

从 Web 前端页面引入声网 SDK,发起视频通话。

开发环境

声网 SDK 的兼容性良好,对硬件设备和软件系统的要求不高,开发环境和测试环境满足以下条件即可:

  • Chrome

  • Firefox

  • Safari

  • Edge

以下是本文的开发环境和测试环境:

开发环境

  • MacBook Pro (13-inch, M1, 2020)

  • Visual Studio Code (1.67.1)

测试环境

  • Chrome (101.0.4951.64)

如果你此前还未接触过声网 SDK,那么你还需要做以下准备工作:

  • 注册一个声网账号,进入后台创建 AppID、获取 Token;

  • 下载声网官方最新的 视频 SDK。

项目设置

文件组织结构

实现视频通话之前,参考如下步骤设置你的项目:

如需创建新项目,可以在 Visual Studio Code 里 File > New Window,创建 Web 项目。完整的目录结构如下,根据个人经验会有所变化。

.
├── index.css # 用于设计 Web 应用的用户界面样式
├── index.html # 用于设计 Web 应用的用户界面
├── index.js # 通过 AgoraRTCClient 实现具体应用逻辑的代码。
└── vendor # 第三方前端插件,辅助页面布局和交互,本教程中是下载到本地使用,你也可以使用 CDN 的方式
├── bootstrap.bundle.min.js
├── bootstrap.min.css
└── jquery-3.4.1.min.js
 

集成声网 SDK

可以下载到本地使用,也可以直接使用声网的 CDN 引入, 本文推荐使用 CDN 方式集成声网 SDK。

在 index.html 中添加以下代码

<!DOCTYPE html>
...
<link rel="stylesheet" href="./vendor/bootstrap.min.css">
<link rel="stylesheet" href="./index.css">
...
<script src="./vendor/jquery-3.4.1.min.js"></script>
<script src="./vendor/bootstrap.bundle.min.js"></script>
<script src="https://download.agora.io/sdk/release/AgoraRTC_N.js"></script>
<script src="./index.js"></script>
...
 

最终完整代码为

可以直接复制运行。

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Basic Video Call -- Agora</title>
<link rel="stylesheet" href="./vendor/bootstrap.min.css">
<link rel="stylesheet" href="./index.css">
</head>
<body>
<div class="container-fluid banner">
<p class="banner-text">Basic Video Call</p>
<a style="color: rgb(255, 255, 255);fill: rgb(255, 255, 255);fill-rule: evenodd; position: absolute; right: 10px; top: 4px;"
class="Header-link " href="https://github.com/AgoraIO-Community/AgoraWebSDK-NG/tree/master/Demo">
<svg class="octicon octicon-mark-github v-align-middle" height="32" viewBox="0 0 16 16" version="1.1" width="32" aria-hidden="true"><path fill-rule="evenodd" d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"></path></svg>
</a>
</div> <div id="success-alert" class="alert alert-success alert-dismissible fade show" role="alert">
<strong>Congratulations!</strong><span> You can invite others join this channel by click </span><a href="" target="_blank">here</a>
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div id="success-alert-with-token" class="alert alert-success alert-dismissible fade show" role="alert">
<strong>Congratulations!</strong><span> Joined room successfully. </span>
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div id="success-alert-with-token" class="alert alert-success alert-dismissible fade show" role="alert">
<strong>Congratulations!</strong><span> Joined room successfully. </span>
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div> <div class="container">
<form id="join-form">
<div class="row join-info-group">
<div class="col-sm">
<p class="join-info-text">AppID</p>
<input id="appid" type="text" placeholder="enter appid" required>
<p class="tips">If you don`t know what is your appid, checkout <a href="https://docs.agora.io/en/Agora%20Platform/terms?platform=All%20Platforms#a-nameappidaapp-id">this</a></p>
</div>
<div class="col-sm">
<p class="join-info-text">Token(optional)</p>
<input id="token" type="text" placeholder="enter token">
<p class="tips">If you don`t know what is your token, checkout <a href="https://docs.agora.io/en/Agora%20Platform/terms?platform=All%20Platforms#a-namekeyadynamic-key">this</a></p>
</div>
<div class="col-sm">
<p class="join-info-text">Channel</p>
<input id="channel" type="text" placeholder="enter channel name" required>
<p class="tips">If you don`t know what is your channel, checkout <a href="https://docs.agora.io/en/Agora%20Platform/terms?platform=All%20Platforms#channel">this</a></p>
</div>
</div> <div class="button-group">
<button id="join" type="submit" class="btn btn-primary btn-sm">Join</button>
<button id="leave" type="button" class="btn btn-primary btn-sm" disabled>Leave</button>
</div>
</form> <div class="row video-group">
<div class="col">
<p id="local-player-name" class="player-name"></p>
<div id="local-player" class="player"></div>
</div>
<div class="w-100"></div>
<div class="col">
<div id="remote-playerlist"></div>
</div>
</div>
</div> <script src="./vendor/jquery-3.4.1.min.js"></script>
<script src="./vendor/bootstrap.bundle.min.js"></script>
<script src="https://download.agora.io/sdk/release/AgoraRTC_N.js"></script>
<script src="./index.js"></script>
</body>
</html>
 

视频通话逻辑

实现视频通话逻辑

下图展示视频通话的 API 调用时序,注意图中的方法是对不同的对象调用的。

参考以下步骤实现音视频通话的逻辑:

1.调用 createClient 方法创建 AgoraRTCClient 对象。

2.调用 join 方法加入一个 RTC 频道,你需要在该方法中传入 App ID 、用户 ID、Token、频道名称。

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

4.当一个远端用户加入频道并发布音视频轨道时:

a.监听 client.on("user-published") 事件。当 SDK 触发该事件时,在这个事件回调函数的参数中你可以获取远端用户 AgoraRTCRemoteUser 对象 。

b.调用 subscribe 方法订阅远端用户 AgoraRTCRemoteUser 对象,获取远端用户的远端音频轨道 RemoteAudioTrack 和远端视频轨道 RemoteVideoTrack 对象。

c.调用 play 方法播放远端音视频轨道。

注:以下代码都将在 index.js 中添加**

初始化client

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

加入RTC 频道并创建本地音频轨道

// Join a channel and create local tracks. Best practice is to use Promise.all and run them concurrently.
[ options.uid, localTracks.audioTrack, localTracks.videoTrack ] = await Promise.all([
// Join the channel.
client.join(options.appid, options.channel, options.token || null, options.uid || null),
// Create tracks to the local microphone and camera.
AgoraRTC.createMicrophoneAudioTrack(),
AgoraRTC.createCameraVideoTrack()
]);
 

播放本地视频

// Play the local video track to the local browser and update the UI with the user ID.
localTracks.videoTrack.play("local-player");
 

发布本地音视频到频道中

// Publish the local video and audio tracks to the channel.
await client.publish(Object.values(localTracks));
 

监听远端用户音视频

// Add an event listener to play remote tracks when remote user publishes.
client.on("user-published", handleUserPublished);
client.on("user-unpublished", handleUserUnpublished); function handleUserPublished(user, mediaType) {
const id = user.uid;
remoteUsers[id] = user;
subscribe(user, mediaType);
} function handleUserUnpublished(user, mediaType) {
if (mediaType === 'video') {
const id = user.uid;
delete remoteUsers[id];
$(`#player-wrapper-${id}`).remove();
}
} async function subscribe(user, mediaType) {
const uid = user.uid;
// subscribe to a remote user
await client.subscribe(user, mediaType);
console.log("subscribe success");
if (mediaType === 'video') {
const player = $(`
<div id="player-wrapper-${uid}">
<p class="player-name">remoteUser(${uid})</p>
<div id="player-${uid}" class="player"></div>
</div>
`);
$("#remote-playerlist").append(player);
user.videoTrack.play(`player-${uid}`);
} if (mediaType === 'audio') {
user.audioTrack.play();
}
}
 

离开频道

async function leave() {
for (trackName in localTracks) {
var track = localTracks[trackName];
if(track) {
track.stop();
track.close();
localTracks[trackName] = undefined;
}
} // Remove remote users and player views.
remoteUsers = {};
$("#remote-playerlist").html(""); // leave the channel
await client.leave(); }
 

最终完整的代码

// create Agora client
var client = AgoraRTC.createClient({ mode: "rtc", codec: "vp8" }); var localTracks = {
videoTrack: null,
audioTrack: null
};
var remoteUsers = {};
// Agora client options
var options = {
appid: null,
channel: null,
uid: null,
token: null
}; // the demo can auto join channel with params in url
$(() => {
var urlParams = new URL(location.href).searchParams;
options.appid = urlParams.get("appid");
options.channel = urlParams.get("channel");
options.token = urlParams.get("token");
if (options.appid && options.channel) {
$("#appid").val(options.appid);
$("#token").val(options.token);
$("#channel").val(options.channel);
$("#join-form").submit();
}
}) $("#join-form").submit(async function (e) {
e.preventDefault();
$("#join").attr("disabled", true);
try {
options.appid = $("#appid").val();
options.token = $("#token").val();
options.channel = $("#channel").val();
await join();
if(options.token) {
$("#success-alert-with-token").css("display", "block");
} else {
$("#success-alert a").attr("href", `index.html?appid=${options.appid}&channel=${options.channel}&token=${options.token}`);
$("#success-alert").css("display", "block");
}
} catch (error) {
console.error(error);
} finally {
$("#leave").attr("disabled", false);
}
}) $("#leave").click(function (e) {
leave();
}) async function join() { // add event listener to play remote tracks when remote user publishs.
client.on("user-published", handleUserPublished);
client.on("user-unpublished", handleUserUnpublished); // join a channel and create local tracks, we can use Promise.all to run them concurrently
[ options.uid, localTracks.audioTrack, localTracks.videoTrack ] = await Promise.all([
// join the channel
client.join(options.appid, options.channel, options.token || null),
// create local tracks, using microphone and camera
AgoraRTC.createMicrophoneAudioTrack(),
AgoraRTC.createCameraVideoTrack()
]); // play local video track
localTracks.videoTrack.play("local-player");
$("#local-player-name").text(`localVideo(${options.uid})`); // publish local tracks to channel
await client.publish(Object.values(localTracks));
console.log("publish success");
} async function leave() {
for (trackName in localTracks) {
var track = localTracks[trackName];
if(track) {
track.stop();
track.close();
localTracks[trackName] = undefined;
}
} // remove remote users and player views
remoteUsers = {};
$("#remote-playerlist").html(""); // leave the channel
await client.leave(); $("#local-player-name").text("");
$("#join").attr("disabled", false);
$("#leave").attr("disabled", true);
console.log("client leaves channel success");
} async function subscribe(user, mediaType) {
const uid = user.uid;
// subscribe to a remote user
await client.subscribe(user, mediaType);
console.log("subscribe success");
if (mediaType === 'video') {
const player = $(`
<div id="player-wrapper-${uid}">
<p class="player-name">remoteUser(${uid})</p>
<div id="player-${uid}" class="player"></div>
</div>
`);
$("#remote-playerlist").append(player);
user.videoTrack.play(`player-${uid}`);
}
if (mediaType === 'audio') {
user.audioTrack.play();
}
} function handleUserPublished(user, mediaType) {
const id = user.uid;
remoteUsers[id] = user;
subscribe(user, mediaType);
} function handleUserUnpublished(user) {
const id = user.uid;
delete remoteUsers[id];
$(`#player-wrapper-${id}`).remove();
}
 

运行效果

在浏览器开两个tab运行网页,使用两个用户加入同一个频道,如果能看见两个自己,说明你成功了。

03 完整代码下载

访问声网文档中心,根据下图所示路径,下载对应 SDK 压缩包。压缩包中包含完整代码。

参考链接

1.Github 源码

https://github.com/AgoraIO/API-Examples-Web/tree/main/Demo/basicVideoCall

2.线上体验Demo

https://webdemo.agora.io/basicVideoCall/index.html

3.声网文档中心

https://docs.agora.io/cn/Video/downloads?platform=Web

基于 Web SDK 实现视频通话场景 | 声网 SDK 教程的更多相关文章

  1. 基于 Autojs 的 APP、小程序自动化测试 SDK

    原文:https://blog.csdn.net/laobingm/article/details/98317394 autojs sdk基于 Autojs 的 APP.小程序自动化测试 SDK,支持 ...

  2. 基于 Autojs 的 APP、小程序自动化测试 SDK - 2019年8月3日

    原文:https://blog.csdn.net/laobingm/article/details/98317394 autojs sdk基于 Autojs 的 APP.小程序自动化测试 SDK,支持 ...

  3. 基于web的IM软件通信原理分析

    关于IM(InstantMessaging)即时通信类软件(如微信,QQ),大多数都是桌面应用程序或者native应用较为流行,而网上关于原生IM或桌面IM软件类的通信原理介绍也较多,此处不再赘述.而 ...

  4. 基于Web的企业网和互联网的信息和应用( 1194.22 )

    基于Web的企业网和互联网的信息和应用( 1194.22 ) 原文更新日期: 2001年6月21日原文地址: http://www.access-board.gov/sec508/guide/1194 ...

  5. 使用 ADD-ON SDK 开发 基于 Html JQuery 和 CSS 的 firefox 插件入门教程1: 创建一个简单的 Add-on

    [本文转载自http://sixpoint.me/942/implementing-simple-addon/] 实现一个简单的插件 教程的这个部分带你使用 SDK 来实现, 运行并打包一个插件. 这 ...

  6. 宣布发布 Windows Azure 导入/导出服务的预览版以及 Web 和移动解决方案场景的若干增强功能

    客户评估基于云的存储解决方案时,面临的挑战之一是以经济高效.安全快速的方式从 Blob 存储区移进和移出大量数据.今天,我们很高兴地宣布发布 Windows Azure 导入/导出的预览版,这款新服务 ...

  7. 【转】如何在CentOS/RHEL中安装基于Web的监控系统 linux-das

    Linux-dash是一款为Linux设计的基于Web的轻量级监控面板.这个程序会实时显示各种不同的系统属性,比如CPU负载.RAM使用率.磁盘使用率.网速.网络连接.RX/TX带宽.登录用户.运行的 ...

  8. 基于Web在线考试系统的设计与实现

    这是一个课程设计的文档,源码及文档数据库我都修改过了,貌似这里复制过来的时候图片不能贴出,下载地址:http://download.csdn.net/detail/sdksdk0/9361973   ...

  9. WEB API系列(一):WEB API的适用场景、第一个实例

    在我前一篇博客中已经给各位简单介绍了HTTP协议与RestFul API的关系,以及一些基本的HTTP协议知识,在这些知识的铺垫下,今天,我们一起来讨论一下WEB API的适用场景,然后写我们第一个W ...

  10. 基于Web实现网络拓扑图

    想想好像好久没用写博客了! 由于最近想跳槽了(ps:尽管公司挽留,提出一些异与往常的挽留“制度”,But确实已经死心了) ,发现前一段时间一些做Hadoop,和Spark同事时常来请教网络拓扑图的有关 ...

随机推荐

  1. UniCode 下char*转CString ,利用MultiByteToWideChar进行转换,中文乱码的解决方案

    //计算char *数组大小,以字节为单位,一个汉字占两个字节 int charLen = strlen(sText); //计算多字节字符的大小,按字符计算. int len = MultiByte ...

  2. 记录一次echarts 中bar 定时跳跃并显示内容

    查看echarts api -----   https://www.echartsjs.com/zh/api.html#echarts 搜索 1.dispatchAction   执行的关键 2.hi ...

  3. eggjs中egg-mysql不支持mysql集群,代码修改为支持集群

    说明:暂不支持egg-mysql动态数据源,用到动态数据源请自行修改.欢迎各位大佬指导... 集群配置: exports.mysql = { // 单数据库信息配置 client: { db1: { ...

  4. Android 系统完整的权限列表

    访问登记属性  android.permission.ACCESS_CHECKIN_PROPERTIES ,读取或写入登记check-in数据库属性表的权限  获取错略位置  android.perm ...

  5. idea中的快捷键

  6. HDFS 内部工作机制

    HDFS 内部工作机制 HDFS集群分为两大角色:NameNode.DataNode (Secondary Namenode) NameNode 负责管理整个文件系统的元数据 DataNode 负责管 ...

  7. python修改图片名

    1 import glob 2 import os 3 4 inputPath = r'E:/data/pic/cat' 5 fileList = glob.glob(inputPath + '/*' ...

  8. 2020年第11届蓝桥杯C/C++B组 第一轮省赛

    # JJU-干干 试题 A: 跑步训练 代码: #include <stdio.h> #include <stdlib.h> /* run this program using ...

  9. 解决mikumikudance丢失dxdx_43.dll问题

    首先是MMD软件下载 我的操作系统是win10-x64 设备是19版小新pro13 mmd官网地址https://learnmmd.com/downloads/ 或者戳这里 链接:https://pa ...

  10. MyBatis面试题汇总

    1.什么是Mybatis? Mybatis是对象关系映射一个框架,它内部封装了JDBC,开发的时候只要关注SQL语句本身,可以严格控制sql的执行性能,灵活,其二可以通过XML或者注解来配置映射信息 ...