【项目】基于TRTC的实时多人会议实现

2023-12-08 00:59

本文主要是介绍【项目】基于TRTC的实时多人会议实现,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、需求分析

最近项目中需要实现一个多人会议的功能。由于考虑到功能的复杂性,选择接入第三方平台实现。为了功能的稳定性,选取当前比较流行的 TRTC (Tencent Real-Time Communication,腾讯实时音视频)。

完成技术选型之后,我们需要了解该技术是如何实现和使用的。这一部分参考腾讯云对于 TRTC 的官方文档说明可以得知:

实时音视频 新手指引-文档中心-腾讯云-腾讯云 (tencent.com)

二、TRTC 的实现

TRTC 在使用的时候,主要是作为一个转发云平台,负责处理多端的音视频流数据,将实时的多人音视频信号转到腾讯云 TRTC 处理,再将远端的数据传送回各自的本地主机。

TRTC 的存在使得开发者只需要利用其提供的 SDK 就可以实现多人音视频即时通讯。

这个 SDK 的存在意义主要有两点:① 通过 SDK 可以访问到 TRTC 的特定云服务器;② 使用 SDK 进行 TRTC 云服务时,可以进行通讯处理和传输,这其中包括了音视频压缩,协议转换交互,状态控制推送等。将非常复杂的内部逻辑全部封装,并且提供多平台的支持,这就是 TRTC 使用 SDK 的意义。

TRTC 的 SDK调用流程时序/流程图:

image
通过这个时序图,我们可以很清楚地得知实现流程:

① 加入房间。音视频即时通讯,通常都会有一个房间的抽象概念。只有知道用户所在的房间,才知道用户的音视频需要转发给哪些人,需要接收到来自哪些人的音视频信号。

② 发布本地流。我们加入房间后,需要给其他人发送自己的音视频信号,就需要本地主机采集到用户的音视频信号,并且以流的形式将信号发送给腾讯云进行处理。

③ 订阅远端流。既然有发布本地的音视频信号,同时我们还需要就受到该房间里其他人的音视频信号,所以我们需要订阅远端的音视频流,获取到该房间内的其他人的信号。

④ 取消发布本地流。这个流程对应于用户停止发布本地的音视频信号,代表用户即将推出房间。

⑤ 退出房间。退出房间这个步骤将会对应很多状态的改变,比如其他人发送的远端信号接收者列表,房间人数,用户状态等。同时也代表生命周期结束,SDK 应该停止采集本地流和订阅远端流。

总结

1、TRTC是用来实现实时音视频通信的云服务。
2、通过官方SDK调用TRTC云服务实现音视频通讯。
3、以房间为概念推送和接收音视频数据流。

三、TRTC 的使用

根据项目所需,本次主要针对使用 Web 端的 TRTC 进行实现远程会议。

参考项目地址:TRTC_Web: High-quality WebRTC SDK - Gitee.com

TRTC 的使用主要是针对前端项目中的使用,后端涉及的并不多。

前端项目

前端项目中需要的使用如下所示:

① 首先安装 TRTC 的 web 依赖。

npm install trtc-js-sdk --save

② 在所需的页面中导入依赖。

import TRTC from 'trtc-js-sdk'

③ 查询会议的房间ID。

this.$http("meeting/searchRoomId", "POST", data, true, resp => {if (resp.roomId === null) {this.$message({message: "不存在的视频会议",type: "error",duration: 1200})} else {this.roomId = resp.roomId;}
})

此处应该获取后端提供的房间 ID 。关于房间 ID ,TRTC 并没有给出非常严格的限制。具体关于 RoomId 的要求,可以参考:

Client - Documentation (qcloud.com)

为了简化使用,我们的 roomId 直接使用取值为 [1, 4294967294] 的整数。

④ 检查浏览器的支持。

// 检查当前浏览器是否支持在线视频会议
TRTC.checkSystemRequirements().then(checkResult => {if (!checkResult.result) {this.$alert("当前浏览器不支持在线视频会议", '提示信息', {confirmButtonText: '确定'})} else {// ...}
}

⑤ 获取用户签名。

// 发起请求,生成用户签名(进入视频会议才需要用户签名,不需要提前生成) 异步!
_this.$http("meeting/searchMyUserSig", "GET", {}, false, resp => {if (resp.code === 200) {_this.appId = resp.appId;_this.userSig = resp.userSig;_this.userId = resp.userId}
})

这一步骤向后端发起请求,获取用户签名。

⑥ 创建本地 Client 对象。

const client = TRTC.createClient({mode: 'rtc',sdkAppId: _this.appId,userId: _this.userId + "",userSig: _this.userSig
});

⑦ 订阅远端流。

// 远端流订阅成功事件
client.on('stream-subscribed', event => {let remoteStream = event.stream;let userId = remoteStream.getUserId();// 找到视频墙中某个远程用户的格子,把其中用于显示视频的div,置顶覆盖用户信息$('#' + userId).css({'z-index': 1});// 把这个置顶的div中播放远端音视频讯号remoteStream.play(userId + "");
})

⑧ 加入房间。

// 进入视频会议
client.join({roomId: _this.roomId}).then(() => {// 成功进入会议室,然后创建本地流const localStream = TRTC.createStream({userId: _this.userId + "",audio: true,video: true});// 把本地流保存到模型层_this.localStream = localStream;// 设置分辨率localStream.setVideoProfile("480p");// 把自己添加到上线用户列表中_this.putUserList(_this, _this.userId);// 初始化本地音视频流localStream.initialize().then(() => {console.log("初始化本地音视频流成功");// 视频墙中第一个格子中的视频div置顶$('#localStream').css({'z-index': 1});// 播放本地音视频流, 传入控件(dom)idlocalStream.play('localStream');// 向远端用户推送本地流client.publish(localStream).then(() => {console.log("本地流发布成功");}).catch(error => {console.error("本地流发布失败" + error);})}).catch(error => {console.error("初始化本地音视频流失败" + error);})})// 进入视频会议失败.catch(error => {console.error("进入房间失败" + error);})

⑨ 退出会议。

// 关闭视频会议
// 获取当前本地使用的流, 有可能是本地流或者共享流
let stream = this.getStream();// 执行取消发布
_this.client.unpublish(stream).then(() => {// 执行退出_this.client.leave().then(() => {// 退出成功console.log("成功退出会议室")// 关闭本地流或者共享流stream.stop();stream.close();// 销毁TRTCClient对象_this.client = null;}).catch(error => {console.error("成功退出会议室失败" + error);})
})

后端项目

在前面的前端项目的使用中,后端主要提供了房间 ID 以及用户签名。为什么这两项需要后端项目提供?根据官方文档的说明:

其中 SDKAppID 用于标识您的应用,UserID 用于标识您的用户,而 UserSig 则是基于前两者计算出的安全签名,它由 HMAC SHA256 加密算法计算得出。只要攻击者不能伪造 UserSig,就无法盗用您的云服务流量。

① 生成用户签名。

计算 UserSig 的逻辑简述为:

//UserSig 计算公式,其中 secretkey 为计算 usersig 用的加密密钥
usersig = hmacsha256(secretkey, (userid + sdkappid + currtime + expire + base64(userid + sdkappid + currtime + expire)))

我们根据官方的 Demo 可以很轻易完成用户签名的生成。

tls-sig-api-v2-java/TLSSigAPIv2.java at master · tencentyun/tls-sig-api-v2-java (github.com)

根据官方代码,我们将 TRTC 的一些参数配置在 application.yml 文件中:

trtc:appid: 140XXXX # TRTC的APPIDsecretKey: 99b4XXXXX # TRTC的密钥expire: 86400 # 用户签名的过期时间,一天

之后我们直接使用官方提供的方法,生成用户签名:

@Component
public class TrtcUtil {@Value("${trtc.appid}")private int appId;@Value("${trtc.expire}")private int expire;@Value("${trtc.secretKey}")private String secretKey;public String genUserSig(String userId) {return GenTLSSignature(appId, userId, expire, null, secretKey);}private String GenTLSSignature(long sdkappid, String userId, long expire, byte[] userbuf, String priKeyContent) {if (StrUtil.isEmpty(priKeyContent)) {return "";}long currTime = System.currentTimeMillis() / 1000;JSONObject sigDoc = new JSONObject();sigDoc.set("TLS.ver", "2.0");sigDoc.set("TLS.identifier", userId);sigDoc.set("TLS.sdkappid", sdkappid);sigDoc.set("TLS.expire", expire);sigDoc.set("TLS.time", currTime);String base64UserBuf = null;if (null != userbuf) {base64UserBuf = Base64.encode(userbuf);sigDoc.set("TLS.userbuf", base64UserBuf);}String sig = hmacsha256(sdkappid, userId, currTime, expire, priKeyContent, base64UserBuf);if (sig.length() == 0) {return "";}sigDoc.set("TLS.sig", sig);Deflater compressor = new Deflater();compressor.setInput(sigDoc.toString().getBytes(StandardCharsets.UTF_8));compressor.finish();byte[] compressedBytes = new byte[2048];int compressedBytesLength = compressor.deflate(compressedBytes);compressor.end();return new String(base64EncodeUrl(Arrays.copyOfRange(compressedBytes, 0, compressedBytesLength)));}private static String hmacsha256(long sdkappid, String userId, long currTime, long expire, String priKeyContent, String base64Userbuf) {String contentToBeSigned = "TLS.identifier:" + userId + "\n"+ "TLS.sdkappid:" + sdkappid + "\n"+ "TLS.time:" + currTime + "\n"+ "TLS.expire:" + expire + "\n";if (null != base64Userbuf) {contentToBeSigned += "TLS.userbuf:" + base64Userbuf + "\n";}try {byte[] byteKey = priKeyContent.getBytes(StandardCharsets.UTF_8);Mac hmac = Mac.getInstance("HmacSHA256");SecretKeySpec keySpec = new SecretKeySpec(byteKey, "HmacSHA256");hmac.init(keySpec);byte[] byteSig = hmac.doFinal(contentToBeSigned.getBytes(StandardCharsets.UTF_8));return Base64.encode(byteSig);} catch (NoSuchAlgorithmException | InvalidKeyException e) {return "";}}private static byte[] base64EncodeUrl(byte[] input) {byte[] base64 = Base64.encode(input).getBytes();for (int i = 0; i < base64.length; ++i) {switch (base64[i]) {case '+':base64[i] = '*';break;case '/':base64[i] = '-';break;case '=':base64[i] = '_';break;default:break;}}return base64;}}

在这之后,我们只需要提供用户签名的数据接口即可。

② 生成 roomId 。

这个步骤中,我们直接根据数据表中会议的 ID ,生成一串长数字,存在 Redis 中。这样既方便了数据的获取,也保证了请求的速度。

redisUtil.setIfAbsent(meeting.getId(), RandomUtil.randomNumber(8),endTimeLong - startTimeLong + 120 * 60 * 1000, TimeUnit.MILLISECONDS);

前面两个参数为 Key-Value ,后面两个参数为有效时间。我们直接生成随机长度为 8 的数字,之后保存在缓存中,之后只需要提供对应的接口获取缓存中的数据即可。

四、总结

在使用前后端项目中描述的操作步骤之后,我们可以直接使用 TRTC 进行多人会议的实现。

参考文档列表:

tls-sig-api-v2-java/TLSSigAPIv2.java at master · tencentyun/tls-sig-api-v2-java (github.com)

什么是TRTC?TRTC是怎么实现的? - 云+社区 - 腾讯云 (tencent.com)

关于 TRTC (实时音视频通话模式)在我司的实践_B站-江辰的博客-CSDN博客

实时音视频 UserSig 相关-常见问题-文档中心-腾讯云-腾讯云 (tencent.com)

音视频终端 SDK(腾讯云视立方) API 概览-API 文档-文档中心-腾讯云-腾讯云 (tencent.com)

这篇关于【项目】基于TRTC的实时多人会议实现的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/467939

相关文章

这15个Vue指令,让你的项目开发爽到爆

1. V-Hotkey 仓库地址: github.com/Dafrok/v-ho… Demo: 戳这里 https://dafrok.github.io/v-hotkey 安装: npm install --save v-hotkey 这个指令可以给组件绑定一个或多个快捷键。你想要通过按下 Escape 键后隐藏某个组件,按住 Control 和回车键再显示它吗?小菜一碟: <template

如何用Docker运行Django项目

本章教程,介绍如何用Docker创建一个Django,并运行能够访问。 一、拉取镜像 这里我们使用python3.11版本的docker镜像 docker pull python:3.11 二、运行容器 这里我们将容器内部的8080端口,映射到宿主机的80端口上。 docker run -itd --name python311 -p

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

【Prometheus】PromQL向量匹配实现不同标签的向量数据进行运算

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Python全栈,前后端开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi

让树莓派智能语音助手实现定时提醒功能

最初的时候是想直接在rasa 的chatbot上实现,因为rasa本身是带有remindschedule模块的。不过经过一番折腾后,忽然发现,chatbot上实现的定时,语音助手不一定会有响应。因为,我目前语音助手的代码设置了长时间无应答会结束对话,这样一来,chatbot定时提醒的触发就不会被语音助手获悉。那怎么让语音助手也具有定时提醒功能呢? 我最后选择的方法是用threading.Time

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

C#实战|大乐透选号器[6]:实现实时显示已选择的红蓝球数量

哈喽,你好啊,我是雷工。 关于大乐透选号器在前面已经记录了5篇笔记,这是第6篇; 接下来实现实时显示当前选中红球数量,蓝球数量; 以下为练习笔记。 01 效果演示 当选择和取消选择红球或蓝球时,在对应的位置显示实时已选择的红球、蓝球的数量; 02 标签名称 分别设置Label标签名称为:lblRedCount、lblBlueCount

在cscode中通过maven创建java项目

在cscode中创建java项目 可以通过博客完成maven的导入 建立maven项目 使用快捷键 Ctrl + Shift + P 建立一个 Maven 项目 1 Ctrl + Shift + P 打开输入框2 输入 "> java create"3 选择 maven4 选择 No Archetype5 输入 域名6 输入项目名称7 建立一个文件目录存放项目,文件名一般为项目名8 确定

Kubernetes PodSecurityPolicy:PSP能实现的5种主要安全策略

Kubernetes PodSecurityPolicy:PSP能实现的5种主要安全策略 1. 特权模式限制2. 宿主机资源隔离3. 用户和组管理4. 权限提升控制5. SELinux配置 💖The Begin💖点点关注,收藏不迷路💖 Kubernetes的PodSecurityPolicy(PSP)是一个关键的安全特性,它在Pod创建之前实施安全策略,确保P