实时音视频服务在项目中出现的频率越来越高的,自己写webRTC的成本相对腾讯云的TRTC-SDK要多大得多,最近公司项目移动端和PC端Web(React+TS)小程序(Taro)中都有使用到,这里针对移动端web做实时通话做个记录与大家交流下
相信准备接入或者已经在接入TRTC的小伙伴是看过文档实时音视频,这里不说这些概念了,直接上代码
pnpm i trtc-js-sdk @types/trtc-js-sdk -D
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()
// 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()
}
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的流程,除了这些,我们还有其他的重要流程,如:
注意:由于浏览器的安全策略,音视频自动播放必须用户与页面产生交互,否则无法自动播放;
移动端目前无法实现听筒和扬声器的切换(如果有小伙伴有实现方案,可以交流下)
其他更多的业务层的逻辑了,如有疑惑的,欢迎探讨,如果对你有帮助的,请手动点赞,谢谢!