实现视频通话
如果您已经完成了上文中提到的开发准备,现在就让我们开始编写一个基本的实时音视频应用吧。这个应用会展示 SDK 基本的连麦功能,包括 加入房间,采集,发布,订阅 等过程。但是在实际的连麦应用开发过程中,是需要后端介入的(用于给用户鉴权并生成 RoomToken),在这里为了减少开发的时间,请您先预先生成 2 个 RoomToken(要求同一个 APPID,同一个 RoomName,不同的 UserName)。
如果您还不知道如何生成 RoomToken,请先阅读 七牛实时音视频云接入指南
下文代码中的 ROOMTOKEN_1
和 ROOMTOKEN_2
表示生成的两个RoomToken。
我们将会编写 1 个独立的 html
页面,主要包括采集本地的摄像头/麦克风数据并将其发布与订阅发布流并将其展示在页面上的功能。
您可以从 这里 查看这个 Demo 最终的成品代码,但是还是建议您在按照步骤阅读完之后再参阅。
引入 SDK 并准备页面
如果一切准备就绪,让我们开始吧,首先创建一个 oneOnOne.html
文件。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>One on One Sample</title>
<script src="https://docs.qnsdk.com/qnweb-rtc.js"></script>
<style>
#localtracks, #remotetracks {
width: 320px;
height: 240px;
background: #000;
}
</style>
</head>
<body>
<label>请输入 RoomToken 加入房间开始连麦</label>
<input id="roomtoken" type="text" />
<button onclick="joinRoom()">加入房间</button>
<p>本地视频</p>
<div id="localtracks"></div>
<p>远端视频</p>
<div id="remotetracks"></div>
</body>
</html>
https://docs.qnsdk.com/qnweb-rtc.js 该链接仅供测试,正式环境需要自行准备 JS 文件或自行 NPM 下载。JS SDK 文件 放在我们的 Github 上,只需要访问我们的 Github 页面,然后替换一下自己的 js 文件即可。
然后在项目的目录下起一个 http
服务,打开 Chrome 通过 localhost
或者 127.0.0.1
访问刚刚那 2 个页面(因为屏幕和摄像头采集只能在 localhost
或者 https
下完成), 观察到控制台打印出了 sdk 的版本信息说明这一步完成了。
如果您不知道如何在本地起一个
http
服务,推荐使用基于 nodejs 的 http-server。可以参考这篇文章。
注意!Chrome 只允许
localhost
或者https
的网络页面访问媒体设备。请在之后的开发和上线中注意这一点
加入房间
上文提到过,SDK 所有的功能都是从 RoomToken
开始的,所以加入房间只需要将 RoomToken
作为参数传给 SDK 就可以了。编辑 html
在 <script>
中添加如下代码。
// 确认引入成功
console.log("current version", QNRTC.VERSION);
// 这里采用的是 async/await 的异步方案,您也可以根据需要或者习惯替换成 Promise 的写法
async function joinRoom() {
// 创建QNRTCClient对象
const client = QNRTC.createClient();
const roomTokenInput = document.getElementById("roomtoken");
const roomToken = roomTokenInput.value;
// 这里替换成刚刚生成的 RoomToken
await client.join(roomToken);
console.log("joinRoom success!");
}
再次访问这个页面,输入 ROOMTOKEN_1
点击加入房间按钮,看到控制台打印出 joinRoom success!
表示这一步完成。
采集并发布本地的音视频轨
接下来,我们的任务是在 html
下采集本地的媒体数据并把它发布到房间中。在编写代码之前,请先确认您的电脑已经连接了摄像头和麦克风设备(支持 USB 设备)。
如果没有摄像头或者没有麦克风(缺少其中一个),请稍微修改下文中的采集参数关闭音频或者视频采集
这里我们将要采集 2 个音视频轨,分别是 摄像头视频轨
和 麦克风音频轨
。
编辑 html
中的 <script>
,添加如下代码。
// 增加一个函数 publish,用于采集并发布自己的媒体流
// 这里的参数 client 是指刚刚初始化的 QNRTCClient 对象
async function publish(client) {
// 同时采集麦克风音频和摄像头视频轨道。
// 这个函数会返回一组audio track 与 video track
const localTracks = await QNRTC.createMicrophoneAndCameraTracks();
console.log("my local tracks", localTracks);
// 将刚刚的 Track 列表发布到房间中
await client.publish(localTracks);
console.log("publish success!");
}
这个函数很简单,主要就 2 个语句,我们现在仔细分析一下这 2 行代码分别做了什么。
第一句,调用了 QNRTC 提供的静态方法 createMicrophoneAndCameraTracks
,从名字我们可以看出,这个方法负责采集本地的麦克风频轨与摄像头轨道,并将其包装成 SDK 的 Track
对象并以列表的形式返回给我我们。
第二句,调用了 QNRTCClient 提供的方法 publish
,将本地 Track
发布至远端。
然后在刚刚 joinRoom
的函数末尾调用我们刚刚编写的 publish
async function joinRoom() {
// 创建QNRTCClient对象
const client = QNRTC.createClient();
const roomTokenInput = document.getElementById("roomtoken");
const roomToken = roomTokenInput.value;
// 这里替换成刚刚生成的 RoomToken
await client.join(roomToken);
console.log("joinRoom success!");
await publish(client);
}
现在访问页面,加入房间后 Chrome 应该会申请媒体设备的权限,同意之后观察控制台打印出 publish success!
表示发布成功。
如果您有需要想要看到自己刚刚采集的视频画面,可以在 publish
函数的末尾添加如下代码
...
...
await client.publish(localTracks);
console.log("publish success!");
// 在这里添加
// 获取页面上的一个元素作为播放画面的父元素
const localElement = document.getElementById("localtracks");
// 遍历本地采集的 Track 对象
for (const localTrack of localTracks) {
console.log(localTrack)
// 如果这是麦克风采集的音频 Track,我们就不播放它。
if (localTrack.isAudio()) continue;
// 调用 Track 对象的 play 方法在这个元素下播放视频轨
localTrack.play(localElement, {
mirror: true
});
}
下一步,我们将在 html
中完成订阅的代码,实现对我们刚刚发布的音视频轨道的订阅。
订阅远端发布的音视频轨
从上文发布的过程中我们可以知道,一个用户可以发布多个 Track
,而在实际需求中我们不一定会把他所有发布的 Track 都订阅(可能只订阅音频之类的),所以订阅的目标是以 Track
为单位的。
那么,我怎么知道房间中有哪些 Track
,这些 Track
又分别对应哪些用户呢?这就是我们接下来讲述的重点。
我们可以通过 SDK 的 user-published
事件获取当前房间的 Track
列表:
需要在join房间之前监听对应事件,
join
加入房间成功后,user-published
会返回当前所有房间内已发布Track
列表以及用户userID
。
进入房间后,当一个远端用户发布了自己的 Track
,此时在我们这边也会触发 user-published
事件,事件会带上远端用户的 userID
以及所发布的 Track
列表。
具体让我们通过代码来理解,在 oneOnOne.html
的 <script>
中添加函数 subscribe
和 autoSubscribe
, subscribe
用于传入 TrackInfo 完成订阅操作并播放订阅流,autoSubscribe
用于实时检测房间的 TrackInfo 变化并在合适的时机调用 subscribe
。
// 这里的参数 client 是指刚刚初始化的 QNRTCClient 对象
async function subscribe(client, tracks) {
// 传入 Track 对象数组调用订阅方法发起订阅,异步返回成功订阅的 Track 对象。
const remoteTracks = await client.subscribe(tracks);
// 选择页面上的一个元素作为父元素,播放远端的音视频轨
const remoteElement = document.getElementById("remotetracks");
// 遍历返回的远端 Track,调用 play 方法完成在页面上的播放
for (const remoteTrack of [...remoteTracks.videoTracks, ...remoteTracks.audioTracks]) {
remoteTrack.play(remoteElement);
}
}
在一次批量订阅中,如果想区分返回的 Track(比如播放屏幕分享的
Track
时给更大的界面),和上文一样,通过 tag 来区分。这里的 tag 来自于远端采集它的时候所指定的 tag。
代码不多,总共就两步。先是发起订阅拿到返回的 Track
对象,之后调用 Track
的 play 方法在页面上播放。
下面我们实现 autoSubscribe
将 TrackInfo
传给 subscribe
。
// 这里的参数 client 是指刚刚初始化的 QNRTCClient 对象, 同上
function autoSubscribe(client) {
// 添加事件监听,当房间中出现新的 Track 时就会触发,参数是 trackInfo 列表
client.on("user-published", (userId,tracks) => {
console.log("user-published!", userId,tracks);
subscribe(client, tracks)
.then(() => console.log("subscribe success!"))
.catch(e => console.error("subscribe error", e));
});
// 就是这样,就像监听 DOM 事件一样通过 on 方法监听相应的事件并给出处理函数即可
}
最后,让我们在 html
的 joinRoom
中调用 autoSubscribe
即可
async function joinRoom() {
// 创建QNRTCClient对象
const client = QNRTC.createClient();
// 需要先监听对应事件再加入房间
autoSubscribe(client);
const roomTokenInput = document.getElementById("roomtoken");
const roomToken = roomTokenInput.value;
// 这里替换成刚刚生成的 RoomToken
await client.join(roomToken);
console.log("joinRoom success!");
await publish(client);
}
好啦,这个时候再访问这个页面,使用两个不同的 RoomToken
分别进入房间中,在 html
中看到你自己和屏幕的画面就代表成功啦。您成功完成了一个最简单的一对一连麦应用(而且还支持单用户多流)。
接下来...
经过上面的步骤之后,相信您已经对一次连麦互动的流程以及 SDK 的使用有了大概的了解。如果您已经准备正式开始接入我们的 SDK,下面一些建议可以让您更加熟悉 SDK。
- SDK 使用中每个功能的细节都可以通过左侧的功能列表去查看
- 如果您想了解 SDK 的架构以及所暴露的模块细节,可以阅读 API 文档 。
如果您在使用过程中遇到什么疑问,或者遇到了 BUG,请在 Github 的 issue 列表提问,我们会尽快为您解答。