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

这篇具有很好参考价值的文章主要介绍了【项目】基于TRTC的实时多人会议实现。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、需求分析

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

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

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

二、TRTC 的实现

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

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

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

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

trtc.createclient,Java,音视频,腾讯云,云计算
通过这个时序图,我们可以很清楚地得知实现流程:

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

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

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

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

⑤ 退出房间。退出房间这个步骤将会对应很多状态的改变,比如其他人发送的远端信号接收者列表,房间人数,用户状态等。同时也代表生命周期结束,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)id
            localStream.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的APPID
  secretKey: 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)文章来源地址https://www.toymoban.com/news/detail-828778.html

到了这里,关于【项目】基于TRTC的实时多人会议实现的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 新知实验室-TRTC如此简单

    腾讯实时音视频(Tencent Real-Time Communication,TRTC),将腾讯多年来在网络与音视频技术上的深度积累,以多人音视频通话和低延时互动直播两大场景化方案,通过腾讯云服务向开发者开放,致力于帮助开发者快速搭建低成本、低延时、高品质的音视频互动解决方案。 1、多人音

    2023年04月27日
    浏览(95)
  • 【新知实验室】腾讯云TRTC初体验

    今年腾讯云音视频发布了“三合一”的RT-ONE™网络。该网络整合了腾讯云实时通信网络(TRTC)、即时通信网络(IM)以及流媒体分发网络(CDN)三张网络,为业界最完整的音视频通信PaaS平台构建基座,面向教育、零售、泛娱乐等行业需求提供服务。 接下来我主要为大家展示

    2023年04月08日
    浏览(55)
  • 腾讯云TRTC接入测试以及状态同步功能重点验证

    此项目用于实时视频语音,例如学生与老师进行网课,可以通过视频进行沟通,了解学生动态。老师并且通过视频进行网上授课,更加直观。学生课下自主学习,有问题可及时通过视频语音进行沟通。项目已经上线,并得到学生与老师的反馈。 老师反馈:更好的监督学生学习

    2024年02月14日
    浏览(38)
  • 移动端Web接入腾讯云TRTC实践(React+TS)

    实时音视频服务在项目中出现的频率越来越高的,自己写webRTC的成本相对腾讯云的TRTC-SDK要多大得多,最近公司项目移动端和PC端Web(React+TS)小程序(Taro)中都有使用到,这里针对移动端web做实时通话做个记录与大家交流下 相信准备接入或者已经在接入TRTC的小伙伴是看过文档

    2023年04月08日
    浏览(36)
  • 使用WebRTC实现多人视频会议

    1.1.使用同事nodejs开发的一个信令服务器。提供Https的WebSocket功能 1.2.准备一个多人视频会议的客户端Web程序client.html 2.1.在Tomcat下打开client.html,如果不使用http://127.0.0.1:8081/vedio/client.html,而使用实际IP地址访问如http://192.168.1.136:8081/vedio/client.html  就会出现如下图的问题,没有

    2024年02月16日
    浏览(43)
  • Vue中webSocket+webRtc实现多人会议,webRtc实现

    已经搭建好 websocket 双端通信(可以先模拟),用于实时交换双方信息。交换的信息也就是所谓的信令。实现 webRtc 进行多人会议,屏幕共享、摄像头共享。 我这里定义的websocket信息格式如下 发给某个人,下面会用【消息格式one】指代 发给会议中所有人,下面会用【消息格式

    2024年04月23日
    浏览(51)
  • [开源]企业级在线办公系统,基于实时音视频完成在线视频会议功能

    本项目使用了SpringBoot+Mybatis+SpringMVC框架,技术功能点应用了WebSocket、Redis、Activiti7工作流引擎, 基于TRTC腾讯实时音视频完成在线视频会议功能。 使用GPL-3.0开源协议                   办公系统应用了工作流引擎,基于RBAC的权限管理系统,工作流审批有请假流程审批、会议

    2024年02月05日
    浏览(83)
  • 如何在Linux系统部署ONLYOFFICE协作办公利器并实现多人实时编辑文档

    本篇文章讲解如何使用Docker在本地服务器上安装ONLYOFFICE,并结合cpolar内网穿透实现公网访问。 Community Edition允许您在本地服务器上安装ONLYOFFICE文档,并将在线编辑器与ONLYOFFICE协作平台或其他流行系统集成。它是一个在线办公套件,包括文本、电子表格和演示文稿的查看器和

    2024年04月17日
    浏览(47)
  • 【多人会议功能】uniapp - 微信小程序 - 腾讯云

    目的:uniapp微信小程序通过腾讯云实现多人会议功能。 源码下载地址:https://download.csdn.net/download/qq_39891453/86790793 效果展示: 功能实现: 前提条件: 注册腾讯云 账号,并完成 实名认证。 推拉流标签不支持个人小程序, 要求申请的 企业类 微信小程序。 步骤一:开通微信

    2024年02月06日
    浏览(42)
  • 团队协作:如何利用 Gitee 实现多人合作项目的版本控制

    在软件开发中,有效地管理代码是至关重要的。 Gitee 是一个功能强大的代码托管平台,提供了丰富的工具和功能,使团队能够协作开发,并轻松管理项目。而 Visual Studio Code(VS Code)是一款流行的轻量级代码编辑器,具有丰富的插件生态系统,可帮助开发人员提高效率。本文

    2024年04月25日
    浏览(50)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包