实现双人通话

实现双人通话

前提条件

  1. 受浏览器安全协议限制,建议开发时使用 http://localhost ,生产环境用 https://[域名] 访问页面,参考文档页面访问协议说明
  2. 为了避免防火墙安全策略限制正常的 TRTC 数据传输,需要参考文档应对防火墙策略进行设置。
  3. 为了保证通话体验,建议在正式开始音视频通话前,进行设备检测和浏览器兼容性检测,可以参考文档浏览器兼容信息

SDK 集成

NPM 方式

  1. 您可以在项目中使用 npm 安装 tuicall-engine-webrtc

    npm install tuicall-engine-webrtc --save
    
  2. 在项目脚本里导入模块。

    import { TUICallEngine, TUICallEvent, TUICallType } from "tuicall-engine-webrtc";
    

Script 方式

在您的 Web 页面中添加如下代码:

<script src="./trtc.js"></script>
<script src="./tim-js.js"></script>
<script src="./tsignaling-js.js"></script>
<script src="./tuicall-engine-webrtc.js"></script>
<script>
    const { TUICallEngine, TUICallEvent, TUICallType } =  window['tuicall-engine-webrtc'];
</script>

以上文件下载地址:trtc.jstim-js.jstsignaling.jstuicall-engine-webrtc.js

SDK 使用

1. 创建 TUICallEngine 实例

let tuiCallEngine = TUICallEngine.createInstance({
  SDKAppID: 0, // 接入时需要将0替换为您的云通信应用的 SDKAppID
  // tim: tim // tim 参数适用于业务中已存在 TIM 实例,需要传入保证 TIM 实例唯一性
});

参数说明

  • SDKAppID:云通信应用的 SDKAppID,访问 开通音视频应用 查看详情。
  • tim:非必填项,若您没有,将会由内部代码自动创建。

2. 登录 TUICallEngine

try {
  await tuiCallEngine.login({ userID: "your userID", userSig: "your userSig" });
  console.log("登录 TUICallEngine 成功");
} catch (error) {
  console.error("登录 TUICallEngine 失败" + error);
}

参数说明

  • userID:用户 ID,由您指定,字符串类型,只允许包含英文字母(a-z 和 A-Z)、数字(0-9)、连词符(-)和下划线(_)。
  • userSig:用户签名,参考 获取登录签名 userSig

3. 发起通话

推荐写在一个函数里,业务侧在需要时再进行触发(如 button 点击时执行)。

async function startVideoCall() {
  try {
    await tuiCallEngine.call({ userID: "call userID", type: TUICallType.VIDEO_CALL });
    console.log("拨打电话成功", userID);
  } catch (error) {
    console.error("拨打电话失败" + error);
  }
}

参数说明

  • userID:想要拨打的用户 ID,需要是已存在的用户,参考 创建 userID

  • type:视频通话为 TUICallType.VIDEO_CALL,语音通话为 TUICallType.AUDIO_CALL

4. 监听对方是否接受通话

拨打电话后,需要增加事件监听,这样才可以知道对方是否接受你的通话。

tuiCallEngine.on(TUICallEvent.USER_ACCEPT, handleUserAccept); // 对方接受了通话 
tuiCallEngine.on(TUICallEvent.REJECT, handleReject); // 对方拒绝了通话 
function handleUserAccept(event) {
  const { userID } = event;
  console.log(`${userID} has accepted this call.`);
}
function handleReject(event) {
  alert(`${userID} has rejected this call.`);
}

5. 监听是否来电

增加事件监听,这样才可以收到对方的通话请求,实现接听或拒绝等操作。

tuiCallEngine.on(TUICallEvent.INVITED, handleInvited); // 收到通话请求 
function handleInvited(event) {
  const { sponsor, isFromGroup, inviteData: { callType } } = event;
  const callTypeString = callType === TUICallType.VIDEO_CALL ? "Video" : "Voice";
  console.log(`${callTypeString} call from ${sponsor}`);
}

6. 监听到来电之后选择接受或者挂断

收到上方的来电监听事件后,可以选择接听或者挂断,大部分情况下,这个步骤也由您页面上的按钮触发。

async function acceptCall() {
  try {
    await tuiCallEngine.accept(); // 接听通话
    // await tuiCallEngine.reject(); 也可以拒绝
    console.log("接通电话成功");
  } catch (error) {
    console.error("接通电话失败" + error);
  }
}

7. 打开自己的摄像头和渲染对方的摄像头画面

作为主叫,在通话拨打出去之后,即 await tuiCallEngine.call(); 之后,就可以打开自己摄像头进行预览了。

作为被叫,在接受对方邀请之后,即 await tuiCallEngine.accept(); 函数里,就可以打开自己摄像头进行预览了,如:

// 主叫打开时机
async function startVideoCall() {
  try {
    const callUserIDInputElement = document.getElementById("call-userID");
    await tuiCallEngine.call({ userID: callUserIDInputElement.value, type: TUICallType.VIDEO_CALL });
    console.log("拨打电话成功", userID);
    // 打开自己摄像头
    await tuiCallEngine.openCamera("local");
  } catch (error) {
    console.error("拨打电话或打开摄像头失败" + error);
  }
}
// 被叫打开时机
async function acceptCall() {
  try {
    await tuiCallEngine.accept(); // 接听通话
    // await tuiCallEngine.reject(); 也可以拒绝
    console.log("接通电话成功");
    // 打开自己摄像头
    await tuiCallEngine.openCamera("local");
  } catch (error) {
    console.error("接通电话或打开摄像头失败" + error);
  }
}

其中,参数 "local" 是代表将本地摄像头渲染到页面上 id 为 localdiv 中,如页面中:

<div id="local" style="width: 400px; height: 400px"></div>

在监听到事件 TUICallEvent.USER_VIDEO_AVAILABLE 之后,如果 event 里的值为 true,则代表对方摄像头可用,就可以渲染对方的摄像头画面,示例代码:

tuiCallEngine.on(TUICallEvent.USER_VIDEO_AVAILABLE, handleUserVideoAvailable);
async function handleUserVideoAvailable(event) {
  const { userID, isVideoAvailable } = event;
  if (isVideoAvailable === true) { // 对方的摄像头可以渲染了
    await tuiCallEngine.startRemoteView({ userID, videoViewDomID: "remote" });
  }
}

同样,参数 "remote" 是代表将本地摄像头渲染到页面上 id 为 remotediv 中,如页面中:

<div id="remote" style="width: 400px; height: 400px"></div>

8. 增加其他方法事件

运行到这里,这部分代码已经可以进行一个非常基础的双人音视频通话了,想要您的工程更为可用,需要增加更多您需要监听的事件,如通话超时、对方麦克风关闭等等,详细事件列表可以参考:API 事件详情

您还需要其他的方法,如挂断 hangup,可以在 API 列表 中找到。

成功截图

image

完整代码

HTML:

<!-- 在页面插入代码 -->
userID: <input id="userID" type="text" />
userSig: <input id="userSig" type="text" />
<button onclick="login()"> login </button>
<hr />
Call someone: <input id="call-userID" type="text" />
<button onclick="startVideoCall()"> call </button>
<hr />
<button id="accept" onclick="acceptCall()" disabled> accept </button>
<hr />
<div id="local" style="width: 400px; height: 400px"></div>
<div id="remote" style="width: 400px; height: 400px"></div>

JavaScript:

const { TUICallEngine, TUICallEvent, TUICallType } = window['tuicall-engine-webrtc'];
let tuiCallEngine = TUICallEngine.createInstance({
  SDKAppID: 0, // 接入时需要将 0 替换为您的云通信应用的 SDKAppID
  // tim: tim // tim 参数适用于业务中已存在 TIM 实例,需要传入保证 TIM 实例唯一性
});
async function login() {
  try {
    const userIDInputElement = document.getElementById("userID");
    const userSigInputElement = document.getElementById("userSig");
    await tuiCallEngine.login({
      userID: userIDInputElement.value,
      userSig: userSigInputElement.value
    });
    console.log("登录 TUICallEngine 成功");
  } catch (error) {
    console.error("登录 TUICallEngine 失败" + error);
  }
}
async function startVideoCall() {
  try {
    const callUserIDInputElement = document.getElementById("call-userID");
    await tuiCallEngine.call({ userID: callUserIDInputElement.value, type: TUICallType.VIDEO_CALL });
    console.log("拨打电话成功", userID);
    // 打开自己摄像头
    await tuiCallEngine.openCamera("local");
  } catch (error) {
    console.error("拨打电话或打开摄像头失败" + error);
  }
}
tuiCallEngine.on(TUICallEvent.USER_ACCEPT, handleUserAccept); // 对方接受了通话 
tuiCallEngine.on(TUICallEvent.REJECT, handleReject); // 对方拒绝了通话 
function handleUserAccept(event) {
  const { userID } = event;
  console.log(`${userID} has accepted this call.`);
}
function handleReject(event) {
  alert(`${userID} has rejected this call.`);
}
tuiCallEngine.on(TUICallEvent.INVITED, handleInvited); // 收到通话请求 
async function handleInvited(event) {
  const { sponsor, isFromGroup, inviteData: { callType } } = event;
  const callTypeString = callType === TUICallType.VIDEO_CALL ? "Video" : "Voice";
  console.log(`${callTypeString} call from ${sponsor}`);
  document.getElementById("accept").disabled = false;
}
async function acceptCall() {
  try {
    await tuiCallEngine.accept(); // 接听通话
    // await tuiCallEngine.reject(); 也可以拒绝
    console.log("接通电话成功");
    // 打开自己摄像头
    await tuiCallEngine.openCamera("local");
  } catch (error) {
    console.error("接通电话或打开摄像头失败" + error);
  }
}
tuiCallEngine.on(TUICallEvent.USER_VIDEO_AVAILABLE, handleUserVideoAvailable);
async function handleUserVideoAvailable(event) {
  const { userID, isVideoAvailable } = event;
  try {
    if (isVideoAvailable === true) { // 对方的摄像头可以渲染了
      await tuiCallEngine.startRemoteView({ userID, videoViewDomID: "remote" });
      console.log("渲染对方摄像头成功");
    }
  } catch (error) {
    console.error("打开摄像头失败" + error);
  }
}

联系我们

如在接入实现过程中遇到问题,欢迎进入 IM 社群 进行咨询和反馈。