声网视频 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">×</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">×</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">×</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 clientvar client = AgoraRTC.createClient({ mode: "rtc", codec: "vp8" }); var localTracks = { videoTrack: null, audioTrack: null};var remoteUsers = {};// Agora client optionsvar 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();}
运行成果
[外链图片转存失败,源站可能有防盗链机制,倡议将图片保留下来间接上传(img-qHpzqvtR-1658572023354)(data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQImWNgYGBgAAAABQABh6FO1AAAAABJRU5ErkJggg==)]在浏览器开两个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