码农之家

移动端Web接入腾讯云TRTC实践(React+TS)

移动端Web接入腾讯云TRTC实践(React+TS)

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

TRTC流程图

相信准备接入或者已经在接入TRTC的小伙伴是看过文档实时音视频,这里不说这些概念了,直接上代码

一、安装TRTC依赖

pnpm i trtc-js-sdk @types/trtc-js-sdk -D

二、初始化TRTC实例

import TRTC from 'trtc-js-sdk'
// 创建TRTC实例
trtcInstance.current = TRTC.createClient({ 
    sdkAppId, // 腾讯云申请的appId
    userSig, // 用户签名,后端获取
    userId, // 随机生成的用户ID,不可重复
    mode: 'rtc' // 实时音视频通话模式
})
// 创建本地流Stream
localStreamRef.current = TRTC.createStream({ userId, audio: true, video: false })
// 绑定TRTC事件
bindTRTCEvent()

三、绑定TRTC事件

// TRTCEvent 可自行从trtc-js-sdk中获取到

trtcInstance.current?.on(TRTCEvent.NETWORK_QUALITY, (event) => {
  subscribeTrtcNetwork?.(event)
  // 监听网络变化 
  if (event.uplinkNetworkQuality > 3 || uplinkLoss > 3) {
     // 本地用户网络差,uplinkLoss(丢包率) 的值可自行调试
  } else if (network.downlinkNetworkQuality > 3 || network.downlinkLoss > 3) {
     // 远端用户网络差,downlinkLoss(丢包率) 的值可自行调试
  }
})
// 添加远程流
trtcInstance.current?.on(TRTCEvent.STREAM_ADDED, (event) => {
  const remoteStream = event.stream
  trtcInstance.current?.subscribe(remoteStream)
    .then(() => {
       // 远端用户进入房间
    })
    .catch((error) => {
      // 远端进入房间失败
    })
})
// 订阅远程流
trtcInstance.current?.on(TRTCEvent.STREAM_SUBSCRIBED, (event) => {
  const remoteStream = event.stream
  remoteStream.on('error', (error) => {
    // 远端流异常
  })
  // 创建播放容器
  const remotePlayerElement = document.createElement('div')
  remotePlayerElement.id = 'remote-stream-' + remoteStream.getId()
  document.body.appendChild(remotePlayerElement)
  remoteStream.play(remotePlayerElement.id)
})
// 有用户被赶出事件
trtcInstance.current?.on(TRTCEvent.CLIENT_BANNED, (error) => {
    // 远端用户异常推出房间
})
// 远端用户推流断开
trtcInstance.current?.on(TRTCEvent.STREAM_REMOVED, (event) => {
  // 远端用户推流断开
  const remoteStream = event.stream
  remoteStream.stop()
  remoteStream.close() // 离开房间释放资源
})
// 远端用户离开房间
trtcInstance.current?.on(TRTCEvent.PEER_LEAVE, (event) => {
  // 远端用户离开房间
})

四、进入房间

await trtcInstance.current?.join({ roomId })
// 初始化本地音视频流
await localStream.current?.initialize?.()
// 本地需创建一个localStream的dom
localStream.current?.play?.('localStream')
if (localStream?.current) {
    await trtcInstance.current?.publish?.(localStream.current)
}

五、退出房间

if (trtcInstance.current &&  localStream.current) {
  await trtcInstance.current?.unpublish?.(localStreamRef?.current)
  await trtcInstance.current?.leave()
  localStreamRef.current?.stop()
  localStreamRef.current?.close()
}

六、完整demo (hooks,无UI)

import { useRef } from 'react'
import TRTC, { Client, LocalStream } from 'trtc-js-sdk'
const useTRTC = () => {
const trtcInstance = useRef<Client | null>(null)
const localStream = useRef<LocalStream | null>(null)
const init = () => {
try {
// 创建TRTC实例
trtcInstance.current = TRTC.createClient({ 
sdkAppId, // 腾讯云申请的appId
userSig, // 用户签名,后端获取
userId, // 随机生成的用户ID,不可重复
mode: 'rtc' // 实时音视频通话模式
})
// 创建本地流Stream
localStream.current = TRTC.createStream({ userId, audio: true, video: false })
// 绑定TRTC事件
bindTRTCEvent()
} catch(error) {
console.log('初始化TRTC失败', error)
} 
}
const enterRoom = async () => {
try {
await trtcInstance.current?.join({ roomId })
// 初始化本地音视频流
await localStream.current?.initialize?.()
// 本地需创建一个localStream的dom
localStream.current?.play?.('localStream')
if (localStream?.current) {
await trtcInstance.current?.publish?.(localStream.current)
}
} catch (error) {
console.log('进入房间失败', error)
}
}
const exitRoom = async () => {
if (trtcInstance.current &&  localStream.current) {
await trtcInstance.current?.unpublish?.(localStream?.current)
await trtcInstance.current?.leave()
localStream.current?.stop()
localStream.current?.close()
}
}
const bindTRTCEvent = () => {
trtcInstance.current?.on(TRTCEvent.NETWORK_QUALITY, (event) => {
subscribeTrtcNetwork?.(event)
// 监听网络变化 
if (event.uplinkNetworkQuality > 3 || uplinkLoss > 3) {
// 本地用户网络差,uplinkLoss(丢包率) 的值可自行调试
} else if (network.downlinkNetworkQuality > 3 || network.downlinkLoss > 3) {
// 远端用户网络差,downlinkLoss(丢包率) 的值可自行调试
}
})
// 添加远程流
trtcInstance.current?.on(TRTCEvent.STREAM_ADDED, (event) => {
const remoteStream = event.stream
trtcInstance.current?.subscribe(remoteStream)
.then(() => {
// 远端用户进入房间
})
.catch((error) => {
// 远端进入房间失败
})
})
// 订阅远程流
trtcInstance.current?.on(TRTCEvent.STREAM_SUBSCRIBED, (event) => {
const remoteStream = event.stream
remoteStream.on('error', (error) => {
// 远端流异常
})
// 创建播放容器
const remotePlayerElement = document.createElement('div')
remotePlayerElement.id = 'remote-stream-' + remoteStream.getId()
document.body.appendChild(remotePlayerElement)
remoteStream.play(remotePlayerElement.id)
})
// 有用户被赶出事件
trtcInstance.current?.on(TRTCEvent.CLIENT_BANNED, (error) => {
// 远端用户异常推出房间
})
// 远端用户推流断开
trtcInstance.current?.on(TRTCEvent.STREAM_REMOVED, (event) => {
// 远端用户推流断开
const remoteStream = event.stream
remoteStream.stop()
remoteStream.close() // 离开房间释放资源
})
// 远端用户离开房间
trtcInstance.current?.on(TRTCEvent.PEER_LEAVE, (event) => {
// 远端用户离开房间
})
}
return {
init,
enterRoom,
exitRoom,
....
}
}

以上是TRTC的流程,除了这些,我们还有其他的重要流程,如:

  • 授权麦克风、摄像头权限(这里只针对麦克风)
  • 关闭麦克风
  • 通话期间保持常亮
  • 当前标签页是否进入后台
  • …(其他的还有的可交流)

注意:由于浏览器的安全策略,音视频自动播放必须用户与页面产生交互,否则无法自动播放;

移动端目前无法实现听筒和扬声器的切换(如果有小伙伴有实现方案,可以交流下)

其他更多的业务层的逻辑了,如有疑惑的,欢迎探讨,如果对你有帮助的,请手动点赞,谢谢!

原文链接:https://juejin.cn/post/7217057805782220857 作者:SGYIDONG