前言
实时音视频通话已经成为远程办公、在线教育、远程医疗、客服系统等场景的标配功能。
本文将结合 腾讯云 TRTC(实时音视频) 和 IM(即时通信),实现一个 React Native 移动端与 Web 端互通的视频通话 Demo。
核心思路:
TRTC:承载音视频流,实现低延迟、高质量的视频通话。
IM:作为信令通道,实现呼叫、接听、挂断、状态同步。
最终效果:React Native 移动端和 Web H5 可以互相进行音视频通话。
一、环境准备
注册并开通腾讯云 TRTC 与 IM。
获取 SDKAppID 和 密钥,用于生成
userSig
。开发环境:
React Native:
react-native-cli
或 Expo bare。Web:
npm install trtc-js-sdk tim-js-sdk
。
二、整体架构
1. 架构分工
IM:信令通道,处理呼叫邀请、接听、挂断、超时等逻辑。
TRTC:音视频流通道,负责推流和拉流。
跨端支持:React Native 使用
trtc-react-native-sdk
,Web 使用trtc-js-sdk
。
2. 信令状态机
stateDiagram-v2
[*] --> Idle
Idle --> Calling : 发起呼叫
Idle --> InCall : 被叫直接加入(快速接听)
Calling --> InCall : 对方接听
Calling --> Idle : 对方拒绝/超时
InCall --> Ended : 挂断
InCall --> Ended : 网络断开/异常退出
Ended --> Idle : 重置状态
说明:
Idle:空闲状态,无通话。
Calling:呼叫方等待被叫响应。
InCall:双方进入房间,音视频通话中。
Ended:挂断或异常退出,通话结束。
3. 通话流程图
发起方 A ------------------> 接收方 B
| |
发送呼叫信令 --------------------> 弹窗提示
| |
进入 TRTC 房间 <--------------- 接受邀请,进入房间
| |
视频通话进行中 <-------------- 双方流订阅成功
| |
发送挂断信令 -------------------> 退出房间
| |
退出房间 <--------------------- 停止播放/释放资源
三、信令约定
IM 消息体 JSON 示例:
// 呼叫邀请
{ "type": "call", "roomId": 1234 }
// 接听同意
{ "type": "accept", "roomId": 1234 }
// 拒绝通话
{ "type": "reject" }
// 挂断
{ "type": "hangup" }
// 超时未接听
{ "type": "timeout" }
四、核心实现
1. 初始化 IM
import TIM from 'tim-js-sdk';
const tim = TIM.create({ SDKAppID: YOUR_APPID });
tim.login({ userID, userSig });
// 监听消息
tim.on(TIM.EVENT.MESSAGE_RECEIVED, ({ data }) => {
data.forEach(msg => {
const payload = JSON.parse(msg.payload.data);
console.log('收到消息:', payload);
});
});
2. React Native 端逻辑
进入房间
import { TRTCCloud, TRTCCloudDef } from 'trtc-react-native-sdk';
const trtc = TRTCCloud.sharedInstance();
const enterRoom = (roomId, userId, userSig) => {
trtc.enterRoom({
sdkAppId: YOUR_APPID,
userId,
userSig,
roomId,
role: TRTCCloudDef.TRTCRoleAnchor
}, TRTCCloudDef.TRTC_APP_SCENE_VIDEOCALL);
trtc.startLocalPreview(true, viewId); // 打开摄像头
trtc.startLocalAudio(TRTCCloudDef.TRTC_AUDIO_QUALITY_DEFAULT); // 打开麦克风
};
挂断/退出房间
const hangUp = () => {
trtc.stopLocalPreview();
trtc.stopLocalAudio();
trtc.exitRoom();
tim.sendMessage({
to: remoteUserId,
conversationType: 'C2C',
payload: { data: JSON.stringify({ type: 'hangup' }) }
});
};
监听对方挂断
tim.on(TIM.EVENT.MESSAGE_RECEIVED, ({ data }) => {
data.forEach(msg => {
const payload = JSON.parse(msg.payload.data);
if (payload.type === 'hangup') {
trtc.stopLocalPreview();
trtc.stopLocalAudio();
trtc.exitRoom();
alert('对方已挂断');
}
});
});
3. Web 端逻辑
进入房间
import TRTC from 'trtc-js-sdk';
const client = TRTC.createClient({ mode: 'rtc', sdkAppId, userId, userSig });
await client.join({ roomId: 1234 });
const localStream = TRTC.createStream({ audio: true, video: true });
await localStream.initialize();
await client.publish(localStream);
client.on('stream-added', event => client.subscribe(event.stream));
client.on('stream-subscribed', event => {
event.stream.play('remote-container'); // 渲染远端视频
});
挂断/退出房间
const hangUp = async () => {
if (localStream) {
await client.unpublish(localStream);
localStream.stop();
localStream.close();
}
await client.leave();
tim.sendMessage({
to: remoteUserId,
conversationType: 'C2C',
payload: { data: JSON.stringify({ type: 'hangup' }) }
});
};
页面关闭/刷新处理
window.addEventListener('beforeunload', async () => {
if (localStream) {
await client.unpublish(localStream);
localStream.stop();
localStream.close();
}
await client.leave();
});
五、通话时序图
sequenceDiagram
participant A as 呼叫方 (RN)
participant IM as IM 信令
participant B as 被叫方 (Web)
participant TRTC as TRTC 服务
A->>IM: 发送呼叫消息 {type:"call",roomId}
IM->>B: 推送呼叫消息
B->>B: 弹出接听/拒绝 UI
B->>IM: 同意 {type:"accept",roomId}
IM->>A: 通知接听
A->>TRTC: enterRoom(roomId)
B->>TRTC: enterRoom(roomId)
TRTC-->>A: 远端流事件
TRTC-->>B: 远端流事件
A->>B: 视频通话中
A->>IM: 发送 {type:"hangup"}
IM->>B: 通知挂断
B->>TRTC: exitRoom()
A->>TRTC: exitRoom()
六、常见问题与解决
进房成功但看不到对方
未调用
subscribe/play
(Web)或startRemoteView
(RN)。UDP 被阻塞 → 放通 TRTC 官方端口。
userId 冲突
每个用户必须唯一,否则会互踢。
浏览器自动播放限制
H5 页面必须用户点击才能播放视频。
退出异常导致“假用户”残留
挂断、刷新页面、APP 退出时必须调用
exitRoom/leave
。
弱网适配
SDK 内置弱网抗抖,但建议开启视频降级策略,优先保证音频。
使用
client.getTransportStats()
监控网络质量。
七、最佳实践
信令与媒体分层
IM 专注信令,TRTC 专注媒体流。
UI 状态机
Idle → Calling → InCall → Ended
每个状态对应 UI 和 TRTC/IM 逻辑。
错误码处理
TRTC 返回错误码时,结合官方文档快速定位。
安全
userSig
必须由服务端生成,防止泄露。