小程序音视频解决方案 -- TRTC

812 阅读5分钟

1、TRTC是什么

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

对于即时通信,腾讯有着深度的技术积累,QQ、微信的音视频通话、腾讯会议,这些常用的场景都是基于TRTC技术方案,TRTC不仅有着详尽的官方API文档,还提供了详细的demo演示,社区也非常完善。

流程图

image.png

2、简述TRTC中的核心概念

(1)房间

房间是TRTC的虚拟概念,本质上用于用户之间的信息隔离,一个ID对应一个房间号,TRTC云服务通过房间ID确定音视频信息流要发送给哪些人。加入房间,是音视频通讯的开始,退出房间,是音视频通讯的结束。

(2)trtc-wx.js文件

trtc-wx.js是管理 TRTC 状态的类文件,它集成了腾讯云的trtc-room协议,同时集成了很多操作TRTC的方法,trtc-wx 可以管理所有与实时音视频相关的状态,以及调用挂载在live-pusher/live-player上的方法,最终将信息透传到底层,底层调用驱动控制硬件设备,从而对终端硬件进行驱动(摄像头、麦克风、音视频播放等)。

trtc-wx SDK 意在帮助用户处理原生标签复杂的状态码以及各种属性状态变更。用户通过对live-pusher/live-player标签绑定回调,从而与 trtx-wx.js 建立联系:

<live-pusher
    url="{{pusher.url}}"
    bindstatechange="_pusherStateChangeHandler"
    bindnetstatus="_pusherNetStatusHandler"
    binderror="_pusherErrorHandler"
    bindbgmstart="_pusherBGMStartHandler"
    bindbgmprogress="_pusherBGMProgressHandler"
    bindbgmcomplete="_pusherBGMCompleteHandler"
    bindaudiovolumenotify="_pusherAudioVolumeNotify"
/>

// 请保持跟 wxml 中绑定的事件名称一致
_pusherStateChangeHandler(event) {
    this.TRTC.pusherEventHandler(event)
},
_pusherNetStatusHandler(event) {
    this.TRTC.pusherNetStatusHandler(event)
},
_pusherErrorHandler(event) {
    this.TRTC.pusherErrorHandler(event)
},
_pusherBGMStartHandler(event) {
    this.TRTC.pusherBGMStartHandler(event)
},
_pusherBGMProgressHandler(event) {
    this.TRTC.pusherBGMProgressHandler(event)
},
_pusherBGMCompleteHandler(event) {
    this.TRTC.pusherBGMCompleteHandler(event)
},
_pusherAudioVolumeNotify(event) {
    this.TRTC.pusherAudioVolumeNotify(event)
}

(3)音视频的推拉流逻辑

小程序通过live-pusher/live-player与底层建立联系,trtc-wx.js和live-pusher/live-player的调用逻辑相对简单,他们的调用关系如下:

image.png

回到trtc-wx.js,通过以下代码实例化TRTC对象:

import TRTC from '@/common/trtc/trtc-wx.js'
this.TRTC = new TRTC()

通过以下代码返回一个pusher对象,并将相关参数传递给live-pusher标签,这个数据初始化过程完成后,就可以调用this.TRTC.getPusherInstance().start()进行推流。

this.pusher = this.TRTC.enterRoom({
    userID: userId,
    sdkAppID: xxx,
    userSig: xxx,
    roomID: roomId,
    enableMic: true,
    enableCamera: true
})

3、开发过程中遇到的一些问题:

1、进入房间后越来越卡顿

开启房间后,正常推拉流后开始即时通话,在测试的过程中,出现了一个异常:通话大概2分钟后,页面上的按钮交互出现明显的卡顿。

我们用不同手机进行测试,排除了是终端设备的原因,随后切换不同的网络,从4G、5G到WiFi,甚至把手机放在路由器旁边做测试,发现手机App各种网页、视频加载飞起,但是卡顿问题依然存在...

跑到腾讯云的官方专栏查看是否有类似问题总结,各种社区、博客搜了一遍,发现没有类似的问题记录,接着各种尝试:杀掉小程序,重新加载,手机关机重启,....,问题还是没有解决...

盯着开发调试模式,看着开发模式下欢快的打印着日志,突然想到:有没有可能日志打印过多过于频繁,导致内存占用过大?赶紧试试,先做个测试:for循环console.log() 200次,果然,卡顿问题复现了,经过查找分析,发现控制台打印的日志基本来自trtc-wx.js文件,trtc-wx.js是一个压缩文件,定位到频繁打印的日志有6个:

console.log("Pusher reset",this.context)
console.log("Pusher context.stop()")
console.log(e.currentTarget.dataset,"playerNetStatus")
console.log("pusherInstance",this.pusherInstance)
console.log(e.currentTarget.dataset,"playerAudioVolumeNotify")
console.log(e,"====")

删除以上文件,问题搞定!

2、扬声器设置禁音异常情况

在开启房间后,测试扬声器和麦克风的开启和关闭功能,发现扬声器无法正常设置静音,通过对上行音频流和下行音频流的理解,对pusher / player 的属性进行了有效的设置,示例代码如下:

麦克风: 麦克风是上行本地音视频流,上行指的是音视频采集端将画面通过采集设备(摄像头,麦克风)采集后,通过编码后上行到TRTC服务器,针对麦克风我们操作的对象是pusher,使用 this.TRTC.setPusherAttributes:

pusherAudioHandler() {
    let options = {
        enableMic: !this.pusher.enableMic, // 关闭音频上行
    }
    this.pusher = this.TRTC.setPusherAttributes(options)
}

扬声器: 扬声器是下行音视频流,下行是指上行的视频流经过在server处理或者转发后,传输到 CDN 或者观众端。扬声器是本地控制远端流的状态,针对扬声器操作的对象是playerList,如果远端用户超过1个,则需要遍历用户列表,更新soundMode的属性值,使用 this.setPlayerAttributesHandler:

playerSoundModeHandler() {
    this.isMuted = !this.isMuted;
    this.playerList.forEach(list => {
        this.setPlayerAttributesHandler(list, {
            soundMode: this.isMuted ? 'ear' : 'speaker',
        })
    })
}

3、this.setData()引起的坑

this.setData()是小程序中用于修改页面数据的函数,它会将传入的对象的属性和值应用到小程序页面的数据对象上,并触发页面的重新渲染,这是小程序的响应式设计。this.setData()有一个页面数据更新渲染完毕后的回调函数

this.setData()的用法如下:

    this.setData({
        key: value,
        key2: value2,
        .......
    }, () => { }) //第二个参数是回调函数,可选

官网上的示例代码:

enterRoom(options) {
    this.setData({
        pusher: this.TRTC.enterRoom( options ),
    }, () => {
        this.TRTC.getPusherInstance().start() // 开始进行推流
    })
}

在项目中,大家基本上都是基于开源框架进行业务开发,习惯上更多的使用如下方式赋值:

    this.pusher = this.TRTC.enterRoom({ })
    this.TRTC.getPusherInstance().start()

以上写法可能会出现pusher对象还未完成初始化,就开始推流的情况,直观的表现就是用户无法正常进入房间,这里我们可以加一个百毫秒级别的推流延迟:

this.pusher = this.TRTC.enterRoom({
    userID: userId,
    sdkAppID: xxx,
    userSig: xxx,
    roomID: roomId,
    enableMic: true,
    enableCamera: true,
})
setTimeout(() => {
    this.TRTC.getPusherInstance().start();
},100)

4、页面效果

音视频页面.svg

5、兼容问题

小程序的兼容问题在开发中经常遇到,可以多逛逛微信开发社区,很多时候可以找到类似问题的解决方案:社区地址,以下是本人在开发过程中遇到的兼容问题:

  • 微信APP版本兼容
  • 小程序基础库版本兼容
  • 不同机型的样式兼容
  • 不同机型的日期格式兼容
  • 接口API版本升级的兼容
  • 原生组件对部分属性的兼容
  • 自定义导航栏的兼容问题