快速开始-Stream 模式
如果您已经完成了上文中提到的开发准备,现在就让我们开始编写一个基本的实时音视频应用吧。这个应用会展示 SDK 基本的连麦功能,包括 加入房间,采集,发布,订阅 等过程。但是在实际的连麦应用开发过程中,是需要后端介入的(用于给用户鉴权并生成 RoomToken),在这里为了减少开发的时间,请您先预先生成 2 个 RoomToken(要求同一个 APPID,同一个 RoomName,不同的 UserName)。
如果您还不知道如何生成 RoomToken,请先阅读 七牛实时音视频云接入指南
准备好之后,请将下文代码中的 ROOMTOKEN_1
和 ROOMTOKEN_2
替换成您生成的 RoomToken。
我们将会编写 2 个独立的 html
页面,其中第一个页面负责采集本地的摄像头/麦克风数据并将其发布,第二个页面负责订阅刚刚发布的这个流并将其展示在页面上。
注意,这里是以 Stream 模式编写的 Demo,如果您还不清楚 Stream 模式和 Track 模式的区别,请先阅读 模式选择
您可以从这里下载到这个 Demo 最终的成品代码,但是还是建议您在按照步骤阅读完之后再参阅。
引入 SDK 并准备页面
让我们开始吧,首先准备一个空文件夹作为我们项目的根目录。参考上文 引入 SDK 中的方式,下载 pili-rtc-web.js
的最新代码到我们项目的目录下。
然后分别创建 publish.html
和 subscribe.html
2 个文件,作为我们的 2 个页面。
<!-- publish.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Publish Page</title>
</head>
<body>
<p>本地流</p>
<div id="localstream" style="width: 640px;height: 480px;background: #000"></div>
<!-- 这里引入我们的 SDK -->
<script src="./pili-rtc-web.js"></script>
<button onclick="joinRoom()">加入房间</button>
<script>
// 确认引入成功
console.log("current version", QNRTC.version);
</script>
</body>
</html>
<!-- subscribe.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Subscribe Page</title>
</head>
<body>
<p>远端流</p>
<div id="remotestream" style="width: 640px;height: 480px;background: #000"></div>
<!-- 同样,这里引入我们的 SDK -->
<script src="./pili-rtc-web.js"></script>
<button onclick="joinRoom()">加入房间</button>
<script>
// 确认引入成功
console.log("current version", QNRTC.version);
</script>
</body>
</html>
可以看到 2 个页面的基本结构都是一样的,一个用来加入房间的按钮和一个准备用来播放媒体流的空元素。在正式开发过程中我们可以根据需要将订阅和发布放到一个页面上,这里为了阐述方便采用了这种模式。
使用 Chrome 访问刚刚那 2 个页面,观察到控制台打印出了 sdk 的版本信息说明这一步完成了。
推荐在项目的目录下起一个
http
服务,打开 Chrome 通过localhost
或者127.0.0.1
访问刚刚那 2 个页面。如果您不知道如何在本地起一个http
服务,推荐使用基于 nodejs 的 http-server。可以参考这篇文章。
注意!Chrome 只允许
localhost
或者https
的网络页面访问媒体设备。请在之后的开发和上线中注意这一点
加入房间
上文提到过,SDK 所有的功能都是从 RoomToken
开始的,所以加入房间只需要将 RoomToken
作为参数传给 SDK 就可以了。编辑 publish.html
在 <script>
中添加如下代码。
// 确认引入成功
console.log("current version", QNRTC.version);
// 这里采用的是 async/await 的异步方案,您也可以根据需要或者习惯替换成 Promise 的写法
async function joinRoom() {
// 初始化一个房间 Session 对象, 这里使用 Stream 模式
const myRoom = new QNRTC.StreamModeSession();
// 这里替换成刚刚生成的 RoomToken
await myRoom.joinRoomWithToken(ROOMTOKEN_1);
console.log("joinRoom success!");
}
同样,在 subscribe.html
中也添加一样的代码,不过注意要把 ROOMTOKEN_1
替换成 ROOMTOKEN_2
。
再次访问这 2 个页面,分别点击加入房间按钮,看到控制台打印出 joinRoom success!
表示这一步完成。
采集并发布本地的媒体流
接下来,我们的任务是在 publish.html
下采集本地的媒体数据并把它发布到房间中,供下文的 subscribe.html
去订阅。在编写代码之前,请先确认您的电脑已经连接了摄像头和麦克风设备(支持 USB 设备)。
如果没有摄像头或者没有麦克风(缺少其中一个),请稍微修改下文中的采集参数关闭音频或者视频采集
编辑 publish.html
中的 <script>
,添加如下代码。
// 增加一个函数 publish,用于采集并发布自己的媒体流
// 这里的参数 myRoom 是指刚刚加入房间时初始化的 Session 对象
async function publish(myRoom) {
// 调用采集模块采集本地的音频和视频数据,返回一个包含这些数据的 Stream 对象
const localStream = await QNRTC.deviceManager.getLocalStream({
audio: { enabled: true },
video: { enabled: true },
});
// 将刚刚的 Stream 对象发布到房间中
await myRoom.publish(localStream);
console.log("publish success!");
}
然后在刚刚 joinRoom
的函数末尾调用我们刚刚编写的 publish
async function joinRoom() {
// 初始化一个房间 Session 对象
const myRoom = new QNRTC.TrackModeSession();
// 这里替换成刚刚生成的 RoomToken
await myRoom.joinRoomWithToken(ROOMTOKEN_1);
console.log("joinRoom success!");
await publish(myRoom);
}
现在访问页面,加入房间后 Chrome 应该会申请媒体设备的权限,同意之后观察控制台打印出 publish success!
表示发布成功。
如果您有需要想要看到自己刚刚采集的媒体流画面,可以在 publish
函数的末尾添加如下代码
...
...
await myRTC.publish(localStream);
console.log("publish success!");
// 在这里添加
// 获取页面上的一个元素作为播放画面的父元素
const localElement = document.getElementById("localstream");
// 调用 Stream 对象的 play 方法在这个元素下播放媒体流,其中第二个参数代表 静音播放
localStream.play(localElement, true);
订阅远端发布的媒体流
订阅操作区别于发布操作一个很重要的点就是,订阅操作只有在满足以下条件时发起才有效:
- 确定订阅目标(获取订阅对象的 userId)
- 订阅目标必须已经发布
这里的关键在于第二点,如何知道目标已经发布,这里分为 2 种情况讨论:
- 如果加入房间时,订阅目标已经在房间里并且已经发布了
- 在加入房间后的某个时间点,订阅目标发布了
对于第一种情况,加入房间成功之后读取一下当前房间内的用户列表,判断其有没有发布即可。
对于第二种情况,SDK 会通过 事件
来通知,您只需要确认在 事件
触发之前 监听
其就好。
具体让我们通过代码来理解,在 subscribe.html
的 <script>
中添加函数 subscribe
和 autoSubscribe
, subscribe
用于完成订阅操作并播放订阅流,autoSubscribe
用于判断检测当是否能够发起订阅,如果能发起订阅,就调用 subscribe
// 这里的参数 myRoom 是指刚刚加入房间时初始化的 Session 对象, 同上
// userId 是指订阅对象的用户名
async function subscribe(myRoom, userId) {
// 调用订阅方法发起订阅,成功会返回一个 Stream 对象,这就是远端的流了
const remoteStream = await myRoom.subscribe(userId);
// 选择页面上的一个元素作为父元素,播放远端的流
const remoteElement = document.getElementById("remotestream");
remoteStream.play(remoteElement);
}
// 这里的参数 myRoom 是指刚刚加入房间时初始化的 Session 对象, 同上
function autoSubscribe(myRoom) {
const users = myRoom.users;
console.log("room current users", users)
// 遍历房间当前所有用户
for (const user of users) {
// 如果存在一个用户,用户名不是自己并且已经发布
// 就可以发起订阅了
if (user.userId !== myRoom.userId && user.published) {
// 调用我们刚刚编写的 subscribe 方法
// 注意这里我们没有使用 async/await,而是使用了 Promise,大家可以思考一下为什么
subscribe(myRoom, user.userId)
.then(() => console.log("subscribe success!"))
.catch(e => console.error("subscribe error", e));
}
}
// 接下来我们需要处理第二种情况,也就是监听事件来感知房间内有用户发布了
myRoom.on("user-publish", (user) => {
console.log("user", user.userId, "is published!");
subscribe(myRoom, user.userId)
.then(() => console.log("subscribe success!"))
.catch(e => console.error("subscribe error", e));
});
// 就是这样,就像监听 DOM 事件一样通过 on 方法监听相应的事件并给出处理函数即可
// 当房间内有其他用户发布时就会触发 "user-publish" 然后被我们订阅
}
最后,让我们在 subscribe.html
的 joinRoom
中调用 autoSubscribe
即可
async function joinRoom() {
// 初始化一个房间 Session 对象
const myRoom = new QNRTC.TrackModeSession();
// 这里替换成刚刚生成的 RoomToken
await myRoom.joinRoomWithToken(ROOMTOKEN_2);
console.log("joinRoom success!");
// 在这里添加
autoSubscribe(myRoom);
}
好啦,这个时候再访问这 2 个页面,分别进入房间中,在 subscribe.html
中看到你自己的画面就代表成功啦。您成功完成了一个最简单的一对一连麦应用。
接下来...
经过上面的步骤之后,相信您已经对一次连麦互动的流程以及 SDK 的使用有了大概的了解。如果您已经准备正式开始接入我们的 SDK,下面一些建议可以让您更加熟悉 SDK。
- 这个 Demo 是用 2 个页面完成的,一个页面负责发布一个负责订阅。尝试将它整合成一个页面(比如通过按钮来选择以哪个用户的身份加入房间)
- SDK 使用中每个功能的细节都可以通过左侧的功能列表去查看
- 如果您想了解 SDK 的架构以及所暴露的模块细节,可以阅读右上角的 API 文档
如果您在使用过程中遇到什么疑问,或者遇到了 BUG,请在 Github 的 issue 列表提问,我们会尽快为您解答。