The SoundNet Video SDK is widely used in a variety of real-time interactive scenarios, such as video conferences, video calls, audio and video social networking, online education, etc. In order to allow developers who have just come into contact with the SoundNet SDK to implement the basic video calling function more smoothly, we have sorted out this tutorial based on the SoundNet Web SDK 4.x version.
At the end of this article, the corresponding Demo and document addresses will be provided for your reference. At the same time, you are welcome to click here to register a Shengwang account to experience. Shengwang will provide developers with 10,000 free minutes every month.
This article is the content of the " Soundnet SDK Tutorial " series
01 Demo experience
We provide an open source basic video call sample project on GitHub, and you can experience the audio and video call effects through this sample project before starting development. Demo and online experience address can be obtained at the end of the article.
02 Hands-on practice
Practical tasks
Import the SoundNet SDK from the web front-end page to initiate a video call.
development environment
The compatibility of the SoundNet SDK is good, and the requirements for hardware devices and software systems are not high. The development environment and test environment can meet the following conditions:
- Chrome
- Firefox
- Safari
- Edge
The following are the development and test environments for this article:
development environment
- MacBook Pro (13-inch, M1, 2020)
- Visual Studio Code (1.67.1)
test environment
- Chrome (101.0.4951.64)
If you have not been exposed to the SoundNet SDK before, then you need to do the following preparations:
- Register a Shengwang account, enter the background to create an AppID, and obtain a Token;
- Download the latest official video SDK of Sound.com.
Project settings
file organization
Before implementing video calling, follow the steps below to set up your project:
To create a new project, you can create a web project in Visual Studio Code by going to File > New Window. The complete directory structure is as follows, subject to change based on personal experience.
.
├── index.css # 用于设计 Web 应用的用户界面样式
├── index.html # 用于设计 Web 应用的用户界面
├── index.js # 通过 AgoraRTCClient 实现具体应用逻辑的代码。
└── vendor # 第三方前端插件,辅助页面布局和交互,本教程中是下载到本地使用,你也可以使用 CDN 的方式
├── bootstrap.bundle.min.js
├── bootstrap.min.css
└── jquery-3.4.1.min.js
Integrated SoundNet SDK
It can be downloaded for local use, or imported directly using the CDN of SoundNet. This article recommends using CDN to integrate the SoundNet SDK.
Add the following code in 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>
...
The final complete code is
Can be copied directly.
<!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>
Video call logic
Implement video call logic
The following figure shows the API call sequence for video calls. Note that the methods in the figure are called on different objects.
Refer to the following steps to implement the logic of audio and video calls:
1. Call the createClient method to create the AgoraRTCClient
object.
2. Call the join method to join an RTC channel. You need to pass in the App ID, user ID, Token, and channel name in this method.
3. First call createMicrophoneAudioTrack to create a local audio track object through the audio collected by the microphone, call createCameraVideoTrack to create a local video track object through the video collected by the camera; then call the publish method and use these local audio and video track objects as parameters to publish the audio and video to the channel.
4. When a remote user joins a channel and publishes audio and video tracks:
a. Listen to the client.on("user-published") event. When the SDK triggers this event, you can get the remote user AgoraRTCRemoteUser
object in the parameter of the event callback function.
b. Call the subscribe method to subscribe the remote user AgoraRTCRemoteUser
object, and obtain the remote user's remote audio track RemoteAudioTrack
and remote video track RemoteVideoTrack
object.
c. Call play
method to play the remote audio and video track.
Note: The following code will be added in index.js **
initialize client
var client = AgoraRTC.createClient({ mode: "rtc", codec: "vp8" });
Join RTC channel and create local audio track
// 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 local video
// Play the local video track to the local browser and update the UI with the user ID.
localTracks.videoTrack.play("local-player");
Publish local audio and video to the channel
// Publish the local video and audio tracks to the channel.
await client.publish(Object.values(localTracks));
Monitor audio and video of remote users
// 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();
}
}
leave the channel
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();
}
final complete code
// 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();
}
running result
[External link image transfer failed, the source site may have anti-leech mechanism, it is recommended to save the image and upload it directly (img-qHpzqvtR-1658572023354)(data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQImWNgYGBgAAAABQABh6FO1AAAAABJRU5ErkJggg==)] Open two in the browser Tab to run the web page, use two users to join the same channel, if you can see two yourself, you have succeeded.
03 Complete code download
Visit Shengwang Documentation Center, and download the corresponding SDK package according to the path shown in the figure below. The complete code is included in the zip package.
Reference link
1. Github source code
https://github.com/AgoraIO/API-Examples-Web/tree/main/Demo/basicVideoCall
2. Online experience demo
https://webdemo.agora.io/basicVideoCall/index.html
3. SoundNet Documentation Center
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。