[特殊字符] 跨端视频通话实战:腾讯云 TRTC + IM(React Native & Web)

前言

实时音视频通话已经成为远程办公、在线教育、远程医疗、客服系统等场景的标配功能。

本文将结合 腾讯云 TRTC(实时音视频)IM(即时通信) ,实现一个 React Native 移动端与 Web 端互通的视频通话 Demo

核心思路:

  • TRTC:承载音视频流,实现低延迟、高质量的视频通话。

  • IM:作为信令通道,实现呼叫、接听、挂断、状态同步。

最终效果:React Native 移动端和 Web H5 可以互相进行音视频通话。


一、环境准备

  1. 注册并开通腾讯云 TRTC 与 IM。

  2. 获取 SDKAppID密钥 ,用于生成 userSig

  3. 开发环境:

    • 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 示例:

javascript 复制代码
// 呼叫邀请
{ "type": "call", "roomId": 1234 }

// 接听同意
{ "type": "accept", "roomId": 1234 }

// 拒绝通话
{ "type": "reject" }

// 挂断
{ "type": "hangup" }

// 超时未接听
{ "type": "timeout" }

四、核心实现

1. 初始化 IM

javascript 复制代码
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 端逻辑

进入房间
javascript 复制代码
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); // 打开麦克风
};
挂断/退出房间
javascript 复制代码
const hangUp = () => {
  trtc.stopLocalPreview();
  trtc.stopLocalAudio();
  trtc.exitRoom();

  tim.sendMessage({
    to: remoteUserId,
    conversationType: 'C2C',
    payload: { data: JSON.stringify({ type: 'hangup' }) }
  });
};
监听对方挂断
javascript 复制代码
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 端逻辑

进入房间
javascript 复制代码
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'); // 渲染远端视频
});
挂断/退出房间
javascript 复制代码
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' }) }
  });
};
页面关闭/刷新处理
javascript 复制代码
window.addEventListener('beforeunload', async () => {
  if (localStream) {
    await client.unpublish(localStream);
    localStream.stop();
    localStream.close();
  }
  await client.leave();
});

五、通话时序图

javascript 复制代码
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()

六、常见问题与解决

  1. 进房成功但看不到对方

    • 未调用 subscribe/play(Web)或 startRemoteView(RN)。

    • UDP 被阻塞 → 放通 TRTC 官方端口。

  2. userId 冲突

    • 每个用户必须唯一,否则会互踢。
  3. 浏览器自动播放限制

    • H5 页面必须用户点击才能播放视频。
  4. 退出异常导致"假用户"残留

    • 挂断、刷新页面、APP 退出时必须调用 exitRoom/leave
  5. 弱网适配

    • SDK 内置弱网抗抖,但建议开启视频降级策略,优先保证音频。

    • 使用 client.getTransportStats() 监控网络质量。


七、最佳实践

  1. 信令与媒体分层

    • IM 专注信令,TRTC 专注媒体流。
  2. UI 状态机

    • Idle → Calling → InCall → Ended

    • 每个状态对应 UI 和 TRTC/IM 逻辑。

  3. 错误码处理

    • TRTC 返回错误码时,结合官方文档快速定位。
  4. 安全

    • userSig 必须由服务端生成,防止泄露。
相关推荐
m0_726365833 小时前
Ai漫剧系统 几分钟,让AI 把一篇小说变成了一部漫剧成片:从剧本到视频的全流程系统实现
人工智能·语言模型·ai作画·音视频
非凡ghost7 小时前
可拓浏览器:给手机浏览器装上“外挂“!2W+拓展+AI搜索,玩出无限可能!
windows·智能手机·音视频·firefox
美狐美颜SDK开放平台8 小时前
多场景美颜SDK解决方案:直播APP(iOS/安卓)开发接入详解
android·人工智能·ios·音视频·美颜sdk·第三方美颜sdk·短视频美颜sdk
ai产品老杨10 小时前
深度解析:基于国产化异构计算的 AI 视频管理平台架构——从 GB28181 接入到 NPU 边缘推流的解耦实践
人工智能·架构·音视频
watson_pillow10 小时前
音视频相关基础知识储备入门-字幕
音视频
程序员JerrySUN11 小时前
Jetson边缘嵌入式实战课程第二讲:JetPack 和 SDK Manager 是什么
c语言·开发语言·网络·udp·音视频
祖国的好青年13 小时前
VS Code 搭建 React Native 开发环境(Windows 实战指南)
android·windows·react native·react.js
一个扣子13 小时前
性能面板解读:通过 Hermes Runtime 测量函数执行耗时
react native·chrome devtools·hermes·性能面板·函数耗时·performance api
weixin_66813 小时前
NVIDIA VSSVideo Search and Summarization视频搜索与摘要蓝图详尽使用说明与技术报告版本
人工智能·音视频
jiayong2314 小时前
国内外视频/图像大模型与智能体工具平台竞品对比
ai·音视频·agent