前提条件
- 注册腾讯云账号,创建实时音视频应用。
- 获取临时 userSig ,或者部署 userSig 签发服务。
- 为了体验完整的 TRTC 能力,建议开发时使用
http://localhost
,生产环境用https://[域名]
访问页面,参考文档页面访问协议说明。 - 为了避免防火墙安全策略限制正常的 TRTC 数据传输,需要参考文档应对防火墙策略进行设置。
- 为了保证通话体验,建议在正式开始音视频通话前,进行设备检测和浏览器兼容性检测,可以参考文档浏览器兼容信息,通话前环境与设备检测。
集成 SDK
SDK 提供了 UMD、ES Module 类型的模块,以及 TypeScript Type Definition 文件,满足在不同类型项目中集成。
NPM 集成
- 您可以在项目中使用 npm 安装 trtc-js-sdk。
npm install trtc-js-sdk --save
- 在项目脚本里导入模块。
import TRTC from 'trtc-js-sdk';
Script 集成
- 在您的 Web 页面中添加如下代码即可:
<script src="trtc.js"></script>
资源下载
SDK 使用逻辑概览
基本概念
您在使用 TRTC Web SDK 时,会接触到以下概念:
- Client 类,其实例代表一个本地客户端。Client 的对象方法提供了加入通话房间、发布本地流、订阅远端流等功能。
- Stream 类,其实例代表一个音视频流对象,包括本地音视频流对象 LocalStream 和远端音视频流对象 RemoteStream。Stream 的对象方法主要提供对音视频流的控制操作,包括音频和视频的播放控制。
实现音视频通话基本逻辑
- 调用 TRTC.createClient() 方法创建 client 对象。
- 调用 client.join() 进入音视频通话房间。
- 您进入房间成功后,可以发布本地流,远端用户可以订阅您的流:
- 调用 TRTC.createStream() 创建本地音视频流。
- 调用 localStream.initialize() 初始化本地音视频流,开始获取系统设备权限,采集音视频。
- 在本地流初始化成功后,调用 client.publish() 方法发布本地流。
- 当一个远端用户加入房间,并发布音视频流时,您可以订阅远端流进行播放:
- 在您进房前监听 client.on('stream-added') 事件,就能收到所有远端用户的推流事件。
- 调用 client.subscribe() 方法订阅远端流。
- 触发订阅成功事件 client.on('stream-subscribed') 后,调用 remoteStream.play() 方法播放。
下图展示了实现音视频通话全过程的基础 API 调用流程:
创建 Client 对象
通过 TRTC.createClient() 方法创建 Client 对象,关键参数设置:
mode
: 实时音视频通话模式,设置为 'rtc'。sdkAppId
: 您在腾讯云创建的音视频应用的 sdkAppId。userId
: 用户 ID,由您指定。userSig
: 用户签名,参考获取临时 userSig,或者部署 userSig 签发服务。
更详细的参数说明参考接口文档 TRTC.createClient()。
const client = TRTC.createClient({ mode: 'rtc', sdkAppId, userId, userSig });
进入音视频通话房间
调用 client.join() 进入音视频通话房间。通常在开始通话
按钮的点击回调里进行调用。
关键参数:
roomId
:房间 ID,由您指定,通常是生成唯一的房间ID。 更详细的参数说明参考接口文档 client.join()。
try {
await client.join({ roomId: 8888 });
console.log('进房成功');
} catch (error) {
console.error('进房失败 ' + error);
}
发布本地流和订阅远端流
创建本地音视频流
使用 TRTC.createStream() 方法创建本地音视频流。 以下实例从摄像头及麦克风中采集音视频流,参数设置如下:
userId
:用户 IDaudio
:是否开启音频video
:是否开启视频
const localStream = TRTC.createStream({ userId, audio: true, video: true });
初始化本地音视频流
接下来调用 initialize() 初始化本地音视频流。
try {
await localStream.initialize();
console.log('初始化本地流成功');
} catch (error) {
console.error('初始化本地流失败 ' + error);
}
发布本地音视频流
在本地流初始化成功后,调用 publish() 方法发布本地流。
try {
await client.publish(localStream);
console.log('本地流发布成功');
} catch (error) {
console.error('本地流发布失败 ' + error);
}
订阅远端音视频流
远端流通过监听事件 client.on('stream-added') 获得,请在 client.join() 进房前注册该事件,确保您不会错过远端用户进房通知。 收到上述事件后要通过 client.subscribe() 订阅远端音视频流。
client.on('stream-added', event => {
const remoteStream = event.stream;
console.log('远端流增加: ' + remoteStream.getId());
//订阅远端流
client.subscribe(remoteStream);
});
client.on('stream-subscribed', event => {
const remoteStream = event.stream;
console.log('远端流订阅成功:' + remoteStream.getId());
// 播放远端流,传入的元素 ID 必须是页面里存在的 div 元素
remoteStream.play('remote_stream-' + remoteStream.getId());
});
播放音视频流
在本地流初始化成功的回调中,或远端流订阅成功事件回调中,通过调用 stream.play() 方法在网页中播放音视频。play
方法接受一个 div 元素 ID 作为参数,SDK 内部会在该 div 元素下自动创建相应的音视频标签并在其上播放音视频。
- 初始化本地流成功时播放本地流。
try {
await localStream.initialize();
console.log('初始化本地流成功');
// 播放本地流,'local_stream' 是在 DOM 中的一个 div 标签的 ID
localStream.play('local_stream');
} catch (error) {
console.error('初始化本地流失败 ' + error);
}
- 订阅远端流成功时播放远端流。
client.on('stream-subscribed', event => {
const remoteStream = event.stream;
console.log('远端流订阅成功:' + remoteStream.getId());
// 如果您遇到播放失败的问题,需要使用 remoteStream 监听 error 并处理 0x4043 浏览器限制自动播放的问题
remoteStream.on('error', error => {
const errorCode = error.getCode();
if (errorCode === 0x4043) {
// PLAY_NOT_ALLOWED, 引导用户手势操作并调用 stream.resume 恢复音视频播放,
// 在点击回调里调用 remoteStream.resume() 进行恢复播放。
}
});
// 播放远端流,传入的元素 ID 必须是页面里存在的 div 元素
remoteStream.play('remote_stream-' + remoteStream.getId());
});
退出音视频通话房间
退出房间有主动退房和被动退房两种情况,需要分别处理。
主动退出当前房间
通话结束时调用 client.leave() 方法退出音视频通话房间,整个音视频通话会话结束。
await client.leave();
// 退房成功,如果没有调用 client.destroy(),可再次调用 client.join 重新进房开启新的通话
// 调用 destroy() 结束当前 client 的生命周期
client.destroy();
- Client 生命周期 说明。
被动退出当前房间
除了用户主动退出房间,在以下情况下,用户会收到 CLIENT_BANNED 事件,表示当前用户被动退出房间,这时不需要调用 client.leave()
,client 会自动进入退房状态。
client.on('client-banned', error => {
console.error(`client-banned observed, reason:${error.reason}, message:${error.message}`);
// error.reason 有以下几种情况
// 'kick' 由于相同 userId 进房导致先进入的用户被移出房间
// 'banned' 被管理员移出房间
// 'room_disband' 管理员解散了房间
});
-
情况一:被同名用户踢出当前房间 在一个房间内,如果同时出现两个 userId 一样且角色都为主播的用户,那么先进入房间的用户会被踢出房间。 例如:A、B 两个用户,先后以相同的
userId
进入房间, A 会被 B 挤出房间。 同名用户同时进入同一房间是不允许的行为,可能会导致双方音视频通话异常,应避免出现这种情况。 -
情况二:通过服务端 API 踢出当前房间 您可以通过服务端的 RemoveUser | RemoveUserByStrRoomId 接口将某个用户踢出某个 TRTC 房间,将该用户踢出后,该用户会收到 CLIENT_BANNED 事件。
-
情况三:通过服务端 API 解散当前房间 通过服务端的 DismissRoom | DismissRoomByStrRoomId接口将某个 TRTC 房间解散,解散房间之后,该房间的所有用户都会 CLIENT_BANNED 事件。
完整代码
HTML:
<!-- 在页面插入代码 -->
<div class="container">
<input type="number" id="roomId" name="roomId" placeholder="roomId" required>
<input type="text" id="userId" name="userId" placeholder="userId" required>
<input type="text" id="userSig" name="userSig" placeholder="userSig" required>
<button type="button" id="startCall">开始通话</button>
<button type="button" id="finishCall">结束通话</button>
</div>
<div id="localStreamContainer"></div>
<div id="remoteStreamContainer"></div>
JavaScript:
let sdkAppId = 1400000000; // '填入您创建应用的 sdkAppId'
let roomId ; // '您指定的房间号'
let userId ; // '您指定的用户ID'
let userSig ; // '生成的userSig'
let client, localStream;
document.getElementById("startCall").onclick = async function () {
roomId = parseInt(document.querySelector('#roomId').value);
userId = document.querySelector('#userId').value;
userSig = document.querySelector('#userSig').value;
client = TRTC.createClient({ mode: 'rtc', sdkAppId, userId, userSig });
// 1.监听事件
client.on('stream-added', event => {
const remoteStream = event.stream;
console.log('远端流增加: ' + remoteStream.getId());
//订阅远端流
client.subscribe(remoteStream);
});
client.on('stream-subscribed', event => {
// 远端流订阅成功
const remoteStream = event.stream;
// 播放远端流,传入的元素 ID 必须是页面里存在的 div 元素
remoteStream.play('remoteStreamContainer');
});
// 2.进房成功后开始推流
try {
await client.join({ roomId });
localStream = TRTC.createStream({ userId, audio: true, video: true });
await localStream.initialize();
// 播放本地流
localStream.play("localStreamContainer");
await client.publish(localStream);
} catch (error) {
console.error(error);
}
}
document.getElementById("finishCall").onclick = async function () {
// 停止本地流预览
localStream.close();
await client.leave();
// 退房成功,如果没有调用 client.destroy(),可再次调用 client.join 重新进房开启新的通话
// 调用 destroy() 结束当前 client 的生命周期
client.destroy();
}
联系我们
如在接入实现过程中遇到问题,欢迎到 GitHub issue 创建 issue,我们会尽快处理。