• 注册账号、创建应用
  • 开发流程介绍
    • python后端
    • 获取 Web SDK 并准备主页
    • 加入房间
    • 预览并发布自己的视频流
    • 自动订阅其他用户
    • 自动退出房间
    • 大小窗切换
  • 效果图如下
  • room.html全部代码

WebRTC,名称源自网页实时通信(Web Real-Time Communication)的缩写,是一个支持网页浏览器进行实时语音对话或视频对话的技术,是谷歌2010年以6820万美元收购Global IP Solutions公司而获得的一项技术。2011年5月开放了工程的源代码,在行业内得到了广泛的支持和应用,成为下一代视频通话的标准。

今天来学习下如何集成七牛云的WebRTC

七牛云官网:
https://www.qiniu.com/

实时音视频相关产品文档:
https://developer.qiniu.com/rtn/manual/4373/cloud-real-time-audio-and-video-interaction

七牛官网的WebRTC-Demo是用React 项目
https://github.com/pili-engineering/QNRTC-Web

接下里我们就一步步来实现一个简易版本的WebRTC-Demo吧
点击此处下载demo,demo下载地址
此demo我屏蔽了appId, 需要自己填写自己的官网注册

注册账号、创建应用

去官网注册个人或者公司账号,并且实名认证,(注意如果是直播业务需要企业账号,并且相关备案)然后创建连麦应用,这里我贴出一张截图展示下

开发流程介绍

在正式进入开发流程之前,让我们先梳理一下接下来开发流程的大概结构。之后的篇幅会比较长,您可以根据整个流程结构选择阅读想要了解的细节,或者是对整个开发过程有个初步的认识。

整个开发过程分成 3 个部分,后端开发、连麦基本功能开发 和 应用功能完善。

后端开发将使用 PythonWeb框架Django 配合七牛的 Python SDK 来搭建一个简单的后台服务,负责计算 roomToken 提供给前端。

连麦基本功能开发会使用 Web SDK 完成一个基本的一对一通话功能。这里我们不会使用任何的 Web 开发框架,所有的代码都会是框架无关代码。

应用功能完善会一步一步完善我们目标中制定的所有功能,最终达成我们的目标。

python后端

首先简单介绍一下 roomToken,roomToken 是一个包含了一次连麦所需要的主要信息的 token,这些信息包括 七牛的账户标识、连麦的应用id(appId)、连麦的房间号(roomName)、连麦的用户名(userId)、连麦用户的权限(是否可以踢人)等等。这个 token 通过自己七牛账户的私钥进行加密,因为涉及到私钥加密,所以计算 roomToken 的工作不能放在客户端(前端), 所以这里我们需要搭建一个后台为我们计算 roomToken。

首先要安装七牛的依赖库

brew install qiniu

或者

pip install qiniu

另外后端获取roomToken需要七牛库中rtc_server.py工具类
https://github.com/qiniu/python-sdk/blob/master/qiniu/services/pili/rtc_server_manager.py
该类就是获取联麦房间的get_room_token、以及对房间的增删改查操作

然后如下是后端的代码:

access_key = '7E9Mchjq6htcQ0VtrJR-H5dv1ljI打码p1_lsdxxdVelU'
secret_key = 'VdwOhzhaLxH8acCmgqxVdqotuuwp打码头xLsKgLpNT5LqO'def index(request):if request.method == "GET":return render(request, "index.html")else:userid = request.POST.get("userid")roomname = request.POST.get("roomname")roomAccess = {"appId": "已经打码需要填写自己的ID",  # AppID: 房间所属帐号的 app 。"roomName": roomname,  # RoomName: 房间名称,需满足规格 ^[a-zA-Z0-9_-]{3,64}$"userId": userid,  # UserID: 请求加入房间的用户 ID,需满足规格 ^[a-zA-Z0-9_-]{3,50}$# ExpireAt: int64 类型,鉴权的有效时间,传入以秒为单位的64位Unix绝对时间,"expireAt": int(time.time()) + 3600,# token 将在该时间后失效。"permission": "user"  # 该用户的房间管理权限,"admin" 或 "user",默认为 "user" 。当权限角色为 "admin" 时,# 拥有将其他用户移除出房间等特权.}room_key = rtc_server.get_room_token(access_key, secret_key, roomAccess)print("room_key", room_key)return HttpResponse(room_key)

代码中 room_key = rtc_server.get_room_token(access_key, secret_key, roomAccess)
是自己官网注册的access_key、secret_key
roomAccess是自己创建的连麦应用的相关信息

先看下index.html页面

然后输入用户名、房间号即可,房间名字正则(^[a-zA-Z0-9_-]{3,64}$)

获取 Web SDK 并准备主页

接下里看看index.html前端页面

<!DOCTYPE html>
<html>
<head><meta charset="utf-8"/><title>Index</title><script src="/static/jquery-3.2.1.min.js"></script>
</head>
<body>
<h1>Qiniu Web SDK Demo 1v1ver.</h1>
<p>请输入以下信息加入房间</p>{% csrf_token %}
<form id="rtcroom"><input id="userid" type="text" placeholder="请输入用户名" required/><input id="roomname" type="text" placeholder="请输入房间号" required/>
</form>
<button form="rtcroom">加入房间</button><script>document.getElementById('rtcroom').onsubmit = joinRoom;function joinRoom(e) {e.preventDefault();const userId = document.getElementById('userid').value;const roomName = document.getElementById('roomname').value;// 获取 roomToken$.ajax({url: "/index/",type: "POST",data: {"userid": userId,"roomname": roomName,csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val()},success: function (data) {console.info("data", data);window.location = "/room/" + data;}});}</script>
</body>
</html>

里面就获取了userId、roomName值,然后发送了ajax请求,去python后端获取返回的roomToken,然后携带roomToken跳转到room界面

在加入房间之后需要进入房间页面进行连麦的逻辑。所以这里涉及到一个页面跳转,如果我们在页面跳转之前就调用连麦 SDK 的 joinRoomWithToken 方法,页面跳转后下一个页面同步上一个页面的 SDK 状态就会比较复杂。所以这里我们让第一个页面能在获取到 roomToken 的时候就跳转页面,将 roomToken 带给下一个页面,然后在房间页面调用 SDK 避免复杂的状态同步。

加入房间

以下来自七牛官网的介绍(整合修改版本)

从加入连麦房间开始,就进入到连麦 SDK 负责的步骤了,我们使用这个 API 来 加入房间 。这个方法需要 roomToken 作为加入房间的参数,所以加入房间就分为了 2 个步骤:获取 roomToken、调用 SDK 加入房间。

之后,让我们创建 room.html ,在下一个页面完成加入房间的逻辑,除了 html 文件以外,再创建 css/room.css文件来编写样式,以及 js/room.js 来放置我们 room 页面的 js 代码

我们通过直接引用 js 来引入 Web SDK。关于引入方式的细节,参照 引入方式。从 Github 上获取到 Web SDK 的最新代码,
https://github.com/pili-engineering/QNRTC-Web/blob/master/Release/pili-rtc-web.js
将其放置在 app/libs 文件夹下

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title><script src='/static/pili-rtc-web.js'></script><link rel="stylesheet" href="/static/room_css.css">
</head>
<body><div id="localplayer" class="mini-player"></div>
<div id="remoteplayer" class="fullscreen-player">{#    <canvas width="640" height="480" id="remotewave"></canvas>#}
</div><button class="btn screen-switch" onclick="switchScreen()">大小窗切换</button></body>
</html>

这里 localplayer 和 remoteplayer 分别用来放置自己和远端的视频流

/* css/room.css */
* {margin: 0;padding: 0;box-sizing: border-box;
}
.qnrtc-stream-player {width: 100%;height: 100%;object-fit: contain;
}
.fullscreen-player {width: 100vw;height: 100vh;position: absolute;top: 0;left: 0;background: #000;
}
.mini-player {width: 300px;height: 200px;position: absolute;bottom: 10px;right: 10px;background: #000;z-index: 10;
}

为 room 页面添加一些基本的 css,其中 .qnrtc-stream-player 这个类是 SDK 在播放音视频流自动生成的 video/audio 标签元素。

好了,下面让我们在 room 页面调用 SDK 完成加入房间吧,

 const roomToken = "{{ token }}";const myRTC = new QNRTC.QNRTCSession();(async () => {try {// 调用 SDK 加入房间const users = await myRTC.joinRoomWithToken(roomToken);console.log('joinRoom success! 当前房间用户:', users);} catch (e) {console.log('error!', e);}})();

好啦,现在访问 http://localhost:8000 输入一对合法的用户名和房间号(只能有数字、字母和下划线且不少于 3 个字符),就会先获取 roomToken 然后跳转到 room 页面完成加入房间。您可以在 room 页面打开浏览器控制台,看到如下输出就代表加入房间成功了

预览并发布自己的视频流

当用户加入房间之后,就可以将自己本地的视频流发布到房间里了,这就是我们通常所说的上麦(发布自己的流)。当然了,在发布自己的流之前,我们需要先采集自己的本地媒体流,所以整个过程就是 加入房间–采集本地媒体流—发布媒体流。让我们加入如下代码

   const users = await myRTC.joinRoomWithToken(roomToken);console.log('joinRoom success! 当前房间用户:', users);// 采集本地媒体流,视频和音频都采集const localStream = await QNRTC.deviceManager.getLocalStream({video: {enabled: true, width: 640, height: 480, bitrate: 600},audio: {enabled: true, bitrate: 32},});
// 获取我们 room.html 中准备用来显示本地媒体流的元素const localPlayer = document.getElementById('localplayer');
// 调用媒体流的 play 方法,在我们指定的元素上播放媒体流,其中第二个参数代表 静音播放localStream.play(localPlayer, true);
// 发布刚刚采集到的媒体流到房间await myRTC.publish(localStream);

这里涉及到 3 个 SDK 的 API,getLocalStream 用来采集本地的媒体流,play 用来指定一个页面元素播放媒体流, publish 用来将媒体流发布到房间。

好啦,重新访问 http://localhost:8000加入房间,一切顺利的话就能在右下角看到自己的视频流。打开控制台,看到 publish success! 就代表发布也成功了,说明我们采集到的视频流已经顺利地发布到房间中了。

自动订阅其他用户

对于一对一连麦来说,这是一个基本功能,即自动订阅房间里另一个用户来获取他发布的媒体流。但是订阅这个操作不像发布一样进入房间就可以调用,订阅操作成功必须满足 2 个条件:

获取订阅目标的用户名 userId
订阅目标必须已经发布了自己的媒体流
为了能让您更好地感受到这个 2 个条件何时被满足,建议您先感受一下两人依次加入房间的时候浏览器控制台的 log 输出的变化。浏览器打开 2 个 tab 页,都访问 http://localhost:8000,在第一个 tab 页以 user1 为用户名 roomtest 为房间号加入房间,加入房间后打开浏览器的控制台观察输出,在joinRoom success! 那一行我们可以看到当我们加入房间之后当前房间里的用户,此时只有user1 一人,也就是他自己。 这时我们切到第二个 tab 页,以 user2 为用户名 roomtest 为房间号也加入这个房间,观察第二个 tab 页的控制台输出,我们可以看到此时输出的当前房间里的用户就已经是 2 个人了。再切回第一个 tab 页观察控制台输出,我们收到了 2 个事件,分别是 user-join 代表有新用户加入了房间(user2),和 user-publish 代表房间内有其他用户发布了自己的媒体流。

自己感受过一次之后,我们就知道,当一个用户加入房间之后,他可以立刻获得这个房间内已有用户的信息,其中 published 字段代表这个用户是否已经发布了媒体流,此时如果有除自己以外的用户已经发布的话,就满足了订阅条件可以发起订阅了。之后我们再通过事件监听获取之后发生的用户加入/用户发布事件,当满足订阅条件时发起订阅,这就是我们实现自动订阅功能实现的基础。

讲的可能有点繁琐,让我们直接来看代码怎么写吧,订阅过程可能在加入房间后或者事件监听回调中发生,所以我们把这个过程抽出来作为一个函数复用。在一开始加入如下代码

function subscribeUser(myRTC, user) {// 如果用户没有发布就直接返回if (!user.published) {return;}// 注意这里订阅使用了 Promise 的写法而没有用 async/await// 因为在我们 Demo 中并没有依赖订阅这个操作的后续操作// 即没有操作必须等到订阅操作结束之后再运行myRTC.subscribe(user.userId).then(remoteStream => {// 我们在 room 页面上准备用来显示远端媒体流的元素const remotePlayer = document.getElementById('remoteplayer');// 在我们准备的元素上播放远端媒体流remoteStream.play(remotePlayer);}).catch(e => {console.log('subscribe error!', e);});
}

好了,准备好了订阅函数之后让我们看准时机发起订阅把,继续加入如下代码

 myRTC.on('user-publish', user => {subscribeUser(myRTC, user);});// 判断房间当前的用户是否有可以订阅的for (let i = 0; i < users.length; i += 1) {const user = users[i];// 如果当前房间的用户不是自己并且已经发布// 那就订阅他if (user.published && user.userId !== myRTC.userId) {subscribeUser(myRTC, user);}}

好啦,现在再重复之前 2 个 tab 页的操作,就能在页面上同时看到本地和远端了。这里我们使用了 SDK 的这 2 个功能,subscribe 用来订阅其他用户发布的媒体流,事件监听 用来通过事件回调同步房间各种状态的变化,事件列表见此。

自动退出房间

通过上面的步骤我们已经完成了一个连麦应用打大部分功能,这里我们做一个小优化。假设您现在正在使用这个应用进行 2 人连麦,此时关闭其中一人的浏览器窗口,我们在另一个人的页面发现远端的画面立刻卡住了,之后黑屏。此时打开控制台观察 log,发现 SDK 在不断尝试重新订阅,过了很久才会收到 user-unpublish 和 user-leave 这 2 个事件。这是因为我们在关闭浏览器之前没有立刻发出 “我马上要离开房间了” 这个信息给到房间其他人,其他端发现 P2P 连接断开后认为远端可能发生了网络波动在不断重试。

所以当我们在关闭浏览器页面之前,需要调用 SDK 的 leaveRoom 方法来离开房间。如何在浏览器页面被关闭之前完成这个操作呢,onbeforeunload 事件 的设计就是为了满足这个需求

在我们加入如下代码

window.onbeforeunload = () => {myRTC.leaveRoom();};

现在再重复我们刚刚关闭页面的操作,可以从远端的 log 中看出我们很快收到了用户取消发布和离开房间的消息。

至此,我们完成了一个基本可用的一对一连麦应用,下一步,我们将逐渐完善这个应用的功能来达到我们的目标

大小窗切换

严格来说大小窗切换并不算 SDK 需要负责实现的功能,但这个需求在一对一连麦场景中经常用到,这里就介绍其中一种实现方案。

我们所谓”大”“小”窗中的大小概念不过是 css 中的一些属性控制的,所以最简单的大小窗切换方案就是通过改变元素的 css 来实现。这里我们使用的方法更为简单,通过直接交换 2 个视频容器元素的 class 值来达到交换 2 者 css 属性的效果。再通过 transition 来为 css 切换的过程加上动画,一个低成本的大小窗切换就实现了。

回到我们的 room.html 上,我们注意到之前我们分别给 2 个容器元素设定了 2 个 class:mini-player 和 fullscreen-player, 我们现在只需要交换这 2 个 css 就行了。

function switchScreen() {console.info("---switchScreen-----")const localPlayer = document.getElementById("localplayer");const remotePlayer = document.getElementById("remoteplayer");// 交换 2 个元素的 classif (localPlayer.className === "mini-player") {localPlayer.className = "fullscreen-player";remotePlayer.className = "mini-player";} else {localPlayer.className = "mini-player";remotePlayer.className = "fullscreen-player";}}

效果图如下

room.html全部代码

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title><script src='/static/pili-rtc-web.js'></script><link rel="stylesheet" href="/static/room_css.css"></head>
<body><div id="localplayer" class="mini-player"></div><div id="remoteplayer" class="fullscreen-player">{#    <canvas width="640" height="480" id="remotewave"></canvas>#}
</div><button class="btn screen-switch" onclick="switchScreen()">大小窗切换</button><script>const roomToken = "{{ token }}";const myRTC = new QNRTC.QNRTCSession();function subscribeUser(myRTC, user) {// 如果用户没有发布就直接返回if (!user.published) {return;}// 注意这里订阅使用了 Promise 的写法而没有用 async/await// 因为在我们 Demo 中并没有依赖订阅这个操作的后续操作// 即没有操作必须等到订阅操作结束之后再运行myRTC.subscribe(user.userId).then(remoteStream => {// 我们在 room 页面上准备用来显示远端媒体流的元素const remotePlayer = document.getElementById('remoteplayer');// 在我们准备的元素上播放远端媒体流remoteStream.play(remotePlayer);}).catch(e => {console.log('subscribe error!', e);});}(async () => {// 先通过地址的 query 拿到上一个页面传过来的 roomToken// 初始化 SDKtry {// 调用 SDK 加入房间const users = await myRTC.joinRoomWithToken(roomToken);console.log('joinRoom success! 当前房间用户:', users);// 采集本地媒体流,视频和音频都采集const localStream = await QNRTC.deviceManager.getLocalStream({video: {enabled: true, width: 640, height: 480, bitrate: 600},audio: {enabled: true, bitrate: 32},});
// 获取我们 room.html 中准备用来显示本地媒体流的元素const localPlayer = document.getElementById('localplayer');
// 调用媒体流的 play 方法,在我们指定的元素上播放媒体流,其中第二个参数代表 静音播放localStream.play(localPlayer, true);
// 发布刚刚采集到的媒体流到房间await myRTC.publish(localStream);window.onbeforeunload = () => {myRTC.leaveRoom();};{#const users = await myRTC.joinRoomWithToken(roomToken);#}console.log('joinRoom success! 当前房间用户:', users);// 监听房间里的用户发布事件,一旦有用户发布,就订阅他myRTC.on('user-publish', user => {subscribeUser(myRTC, user);});// 判断房间当前的用户是否有可以订阅的for (let i = 0; i < users.length; i += 1) {const user = users[i];// 如果当前房间的用户不是自己并且已经发布// 那就订阅他if (user.published && user.userId !== myRTC.userId) {subscribeUser(myRTC, user);}}} catch (e) {console.log('error!', e);}})();function switchScreen() {console.info("---switchScreen-----")const localPlayer = document.getElementById("localplayer");const remotePlayer = document.getElementById("remoteplayer");// 交换 2 个元素的 classif (localPlayer.className === "mini-player") {localPlayer.className = "fullscreen-player";remotePlayer.className = "mini-player";} else {localPlayer.className = "mini-player";remotePlayer.className = "fullscreen-player";}}</script>
</body>
</html>

七牛云集成实时音视频云WebRTC相关推荐

  1. 开源实时音视频技术WebRTC在Windows下的简明编译教程

    1.前言 随着音视频技术的不断普及,Google推出的 WebRTC 越来越受到大家的喜欢.现在很多直播产品都是基于WebRTC 进行二次开发做出来的. WebRTC是提供了一整套处理实时音视频的开源 ...

  2. 【金猿产品展】拍乐云——新一代实时音视频云服务,构建云上的每一次美好互动...

    拍乐云产品 本项目由拍乐云投递并参与"数据猿年度金猿策划活动--2021大数据产业创新服务产品榜单及奖项"评选. 数据智能产业创新服务媒体 --聚焦数智 · 改变商业 拍乐云提供的 ...

  3. 如何构建全球实时音视频云及其海外网络传输优化

    点击上方"LiveVideoStack"关注我们 全球不同国家和地区的网络基建水平参差不齐,如何利用有限的网络资源提供更高质量的音视频通话体验是音视频服务商必须面对的挑战.在此次L ...

  4. 即时通讯开发之网页端实时音视频技术WebRTC

    WebRTC,名称源自网页实时通信(Web Real-Time Communication)的缩写,是一个支持网页浏览器进行实时语音通话或视频聊天的技术,是谷歌2010年以6820万美元收购Globa ...

  5. 多媒体库SDL以及实时音视频库WebRTC中的多线程问题实战详解

    目录 1.概述 2.开源跨平台多媒体库SDL介绍 3.开源音视频实时通信库WebRTC介绍 4.在国产化Linux桌面系统中遇到的SDL多线程问题 5.在给WebRTC新增外部音频插件库时遇到的多线程 ...

  6. 开源实时音视频技术WebRTC中RTP/RTCP数据传输协议的应用

    1.前言 RTP/RTCP协议是流媒体通信的基石.RTP协议定义流媒体数据在互联网上传输的数据包格式,而RTCP协议则负责可靠传输.流量控制和拥塞控制等服务质量保证.在WebRTC项目中,RTP/RT ...

  7. 清华同衡学术周 | 七牛云郜向阳:视频云助力智慧园区建设

    2020 年 11 月 20 日上午,由清华同衡规划设计研究院.北京城士科技有限公司举办的 2020 清华同衡学术周之<"十四五"时期产业园区创新发展和数字化转型>专题 ...

  8. 七牛云 RTN:基于 WebRTC 零基础搭建实时音视频平台

    近年来,在线教育.狼人杀.在线抓娃娃.线上 KTV 等多人视频互动模式不断涌现,实时音视频通信风头正劲,实时音视频技术 WebRTC 也因此受到了广泛关注.相关数据显示,2017-2021 年期间,全 ...

  9. 新增微信小程序和WebRTC连麦直播等多项能力,即构实时音视频SDK再升级!

    经过2018年小半年的闭关练功,即构ZEGO团队铸造了不少黑科技.本文将为你带来即构ZEGO实时语音视频SDK近半年新增能力和功能优化的最新进展. 更懂应用场景的语音视频云 作为全球领先的实时语音视频 ...

最新文章

  1. 如何在Java中解析命令行参数?
  2. python爬虫能干什么-Python爬虫可以做什么?
  3. 通过poi的XSSF实现生成excel文件
  4. Jackson 配置 ObjectMapper
  5. CPP第四版第五章:位操作符、sizeof及部分编程习题
  6. 程序员数学基础【七、等比数列 棋盘麦粒】
  7. 原理图中如何连线_Altium Designer10绘制原理图
  8. 【操作系统】信号量解决经典同步问题
  9. 钢筋符号怎样加入wps_钢筋知识「收藏备用」
  10. VS2010开发应用程序读写注册表
  11. HYSBZ - 2243 染色 (树链剖分+线段树)
  12. 网站点击数字翻页html代码,html5数字翻页时钟代码
  13. Excel控制AutoCad进行坐标标注
  14. 如何安装旧版iOS软件?
  15. 提高Interface Builder高效工作的8个技巧
  16. RN项目集成react-native-code-push(四)-- ReactNative项目集成react-native-code-push
  17. 计算共形几何讲座笔记
  18. obd协议 混动车_OBD协议介绍
  19. 国外免费(开放获取)学术资源大全
  20. js 当前时间减6个月

热门文章

  1. 【无标题】读孙子兵法,品启强人生
  2. 22.网络爬虫—APP数据抓取详讲
  3. 互联网加速带来的好处
  4. H3C配置多区域OSPF实验
  5. 【雕爷学编程】Arduino动手做(105)---压电陶瓷振动模块
  6. bootstrap浮窗
  7. 在Linux中修改打开文件数量限制的3种方法
  8. User Datagram Protocol (UDP)
  9. 苹果拨号键隐藏鸿蒙,iPhone的拨号按键居然还有这么多隐藏功能 以前真是白用iPhone了...
  10. 【PostgreSQL】WITH RECURSIVE递归查询语句