实现音视频通话
在本教程中,您可以体验集成融云 Web 端音视频通话(呼叫)SDK CallLib 的集成流程。 CallLib 支持单人音视频呼叫和基于群组的多人呼叫场景。
示例项目
融云音视频通话 QuickDemo(GitHub · Gitee)演示了融云产品音视频通话在 Web 端的功能,以便开发者体验产品,快速集成,实现单群聊、音视频通话等场景需求。
QuickDemo 开放源代码,您可以对感兴趣的部分进行代码改造,以便进一步了解细节。具体实现详见运行示例项目。
前置条件
注意
浏览器页面地址必须为 https 协议地址,或使用 localhost 域名。
CallLib 概念说明
注意
通过 onSessionClose 监听函数获得通话结束。
步骤 1:开通服务
您在融云创建的应用不会默认启用音视频服务。在使用融云提供的任何音视频服务前,您需要前往控制台,为应用开通音视频服务。
具体步骤请参阅控制台文档开通音视频服务。
注意
服务开通、关闭等设置完成后 30 分钟后生效。
步骤 2:导入 SDK
您可以使用 NPM 安装 CallLib 与其依赖的 IMLib 与 RTCLib,或者使用 CDN 方式安装。
CallLib 相关业务依赖 IMLib 作为信令通道。因此,开发音视频通话必须安装融云音视频核心能力库 RTCLib,即时通讯能力库 IMLib。您可选用 2.X 或 4.X 或 5.X 版本的 IMLib。
本部分仅针对新集成客户简要说明 NPM 方式安装方法。以下步骤中均以 IMLib 5.X 为例说明。
注意
CallLib 配合使用 IMLib 2.X,4.X 的详细说明,以及 CDN 方式安装方法,请参阅 安装 CallLib SDK。
-
安装 5.X 版本 IMLib(推荐新集成客户使用 IMLib 5.X)。
# 安装 IMLib v5
npm install @rongcloud/engine@latest @rongcloud/imlib-next --save -
安装 RTCLib 5.X。
# 安装 RTCLib
npm install @rongcloud/plugin-rtc --save -
安装 CallLib 5.X。
# 安装 CallLib
npm install @rongcloud/plugin-call --save -
全部下载安装完成后,即可在代码中导入 IMLib(5.X)、RTCLib 与 CallLib 库。
// 导入 IMLib 5.X
import * as RongIMLib from "@rongcloud/imlib-next";
// 导入 RTCLib
import { installer as rtcInstaller, RCRTCClient, RCTrack, RCFrameRate, RCResolution } from "@rongcloud/plugin-rtc";
// 导入 CallLib
import { installer as callInstaller, RCCallClient, RCCallSession, RCCallErrorCode, ISessionListener, IEndSummary, ISenderInfo, IMuteUser, IInvitedUsers, RCCallLanguage, RCCallEndReason, RCCallMediaType, IOfflineRecord, RCCallSessionState } from "@rongcloud/plugin-call";
步骤 3:初始化
本部分仅针对新集成客户简要说明使用 IMLib 5.X 时,IM 客户端、RTC 客户端与 CallLib 客户端的初始化方式。
注意
注意,CallLib 配合使用 IMLib 2.X,4.X 时,IM、RTC 与 CallLib 客户端初始化方式均有不同。详见初始化。
请依次初始化 IM 客户端、RTC 客户端与 CallLib 客户端。
-
调用 IMLib 的
init
方法,初始化 IM 客户端(IMLib 5.X)。appkey
即您的融云应用的 App Key。// IM 客户端初始化(IMLib 5.X)
RongIMLib.init({
appkey: '<your-app-key>',
}); -
初始化 RTC 客户端与 CallLib 客户端。在初始化 CallLib 客户端实例时,传入
onSession
函数监听来电。初始化完成后,可获取RCCallClient
实例对象caller
。RCCallClient
主要用于发起呼叫。// RTC 客户端初始化
// RTCLib 全局变量定义为 RCRTC,使用 CDN 文件方式集成时,示例如下:
// const rtcClient = RongIMLib.installPlugin(RCRTC.installer, { /* 配置项参数 */ })
const rtcClient: RCRTCClient = RongIMLib.installPlugin(rtcInstaller, { /* 配置项参数 */ });
// CallLib 客户端初始化
// CallLib 全局变量定义为 RCCall,使用 CDN 文件集成时,示例如下:
// const caller = RongIMLib.installPlugin(RCCall.installer)
const caller: RCCallClient = RongIMLib.installPlugin(callInstaller, {
// rtcClient 实例 (必填)
rtcClient,
/**
* 被动收到邀请 (收到一个远端发起的新会话), 会产生一个新的 session 对象 (必填)
*/
onSession(session: RCCallSession){
/**
* **收到新的 session 后需要立即注册事件监听**
*/
session.registerSessionListener({
/**
* 当远端用户已开始响铃,表示对方已收到呼叫请求
* @param sender 已响铃的用户
* @param session 当前的 session 对象
*/
onRinging(sender: ISenderInfo, session: RCCallSession){
const { userId } = sender;
},
/**
* 当远端用户同意接听
* @param sender 远端用户
* @param session 当前的 session 对象
*/
onAccept(sender: ISenderInfo, session: RCCallSession){
const { userId } = sender;
},
/**
* 当有远端用户挂断
* @param sender 远端用户
* @param reason 挂断的原因
* @param session 当前的 session 对象
*/
onHungup(sender: ISenderInfo, reason: RCCallEndReason, session: RCCallSession){
const { userId } = sender;
},
/**
* 本端资源或远端资源已获取
* @param track 本端资源或远端资源, track 不可设置成 Vue 组件的响应式数据
* @param session 当前的 session 对象
*/
onTrackReady(track: RCTrack, session?: RCCallSession){
// track.isLocalTrack() 是否为本地资源
// track.isAudioTrack() 是否为音频
// track.isVideoTrack() 是否为视频
// track.getUserId() 产生该 track 的用户id
// 播放音频。如果为远端音频,建议直接播放。如为本端音频,建议不播放,以减少回音。
if (track.isAudioTrack() && !track.isLocalTrack()) {
track.play();
}
// 视频在对应的容器里播放
if (track.isVideoTrack()) {
const video = document.getElementById(
"video" + user.userId
) as HTMLVideoElement;
track.play(video);
}
},
});
},
/**
* 以下三条只要满足一条,就会触发onSessionClose
* 1、本端用户自己主动挂断
* 2、服务端把本端用户踢出 RTC 房间
* 3、房间里小于2个人
*
* @param {RCCallSession} session 被结束的 session 对象
* @param summaryInfo 结束一个 session 的后汇总信息
*/
onSessionClose(session: RCCallSession, summaryInfo?: IEndSummary){
},
/**
* 接收 IM 离线期间收到的呼叫记录(按需监听)
*/
onOfflineRecord(record: IOfflineRecord){
},
});
步骤 4:建立 IM 连接
音视频用户之间的信令传输依赖于融云的即时通信(IM)服务,因此需要先与 IM 服务建立连接,之后再进行音视频呼叫业务。
//与 IM 服务建立连接(IMLib 5.X)
RongIMLib.connect('<user-token>').then(res => {
if (res.code === 0) {
console.log('链接成功, 链接用户 id 为: ', res.data.userId);
} else {
console.warn('链接失败, code:', res.code)
}
})
注意
注意,如 CallLib 配合使用 IMLib 2.X 或 4.X 版本,建立 IM 连接的方式如下:
// 与 IM 服务建立连接(IMLib 4.X)
imClient.connect({ token: '<user-token>' })
.then((user) => {
console.log("链接成功, 链接用户 id 为: ", user.id);
})
.catch((error) => {
console.log("链接失败: ", error.code, error.msg);
});
// 与 IM 服务建立连接(IMLib 2.X)
RongIMClient.connect(('<user-token>', {
onSuccess: (userId) => {
console.log('连接成功, 用户 ID 为', userId);
},
onTokenIncorrect: function() {
console.log('连接失败, 失败原因: token 无效');
},
onError: function(errorCode) {
console.log('连接失败, 失败原因: ', errorCode);
}
});
步骤 5:发起呼叫
主动呼叫分为发起单人通话和发起多人通话,可根据实际需求调用。多人通话的场景必须在一个群组内。
发起单人通话
成功建立 IM 连接后,使用初始化 CallLib 时获取的 RCCallClient
实例的 call 方法发起单人通话。
/**
* 发起单人通话,如果成功后会产生一个新的session
* @param targetId 被呼叫一方的用户 id 必填
* @param mediaType 1->音频呼叫 or 2->音视频呼叫 必填
* @param listener session对象上注册的事件 (必填)
* @param constraints 获取音频或音视频资源时的参数 可选
* @param params.channelId 组织 Id 可选
*/
const { code, session } = await caller.call({
targetId: this.targetId,
mediaType,
listener: {
/**
* 当远端用户已开始响铃,表示对方已收到呼叫请求 (必填)
* @param sender 已响铃的用户
* @param session 当前的 session 对象
*/
onRinging(sender: ISenderInfo, session: RCCallSession){
const { userId } = sender;
},
/**
* 当远端用户同意接听 (必填)
* @param sender 远端用户
* @param session 当前的 session 对象
*/
onAccept(sender: ISenderInfo, session: RCCallSession){
const { userId } = sender;
},
/**
* 当有远端用户挂断 (必填)
* @param sender 远端用户
* @param reason 挂断的原因
* @param session 当前的 session 对象
*/
onHungup(sender: ISenderInfo, reason: RCCallEndReason, session: RCCallSession){
const { userId } = sender;
},
/**
* 本端资源或远端资源已获取 (必填)
* @param track 本端资源或远端资源, track 不可设置成 Vue 组件的响应式数据
* @param session 当前的 session 对象
*/
onTrackReady(track: RCTrack, session?: RCCallSession){
// track.isLocalTrack() 是否为本地资源
// track.isAudioTrack() 是否为音频
// track.isVideoTrack() 是否为视频
// track.getUserId() 产生该 track 的用户id
// 播放音频。如果为远端音频,建议直接播放。如为本端音频,建议不播放,以减少回音。
if (track.isAudioTrack() && !track.isLocalTrack()) {
track.play();
}
// 视频在对应的容器里播放
if (track.isVideoTrack()) {
const video = document.getElementById(
'video' + user.userId
) as HTMLVideoElement;
track.play(video);
}
},
},
});
if (code === RCCallErrorCode.SUCCESS) {
// do something
}
发起多人通话
成功建立 IM 连接后,使用初始化 CallLib 时获取的 RCCallClient
实例的 callInGroup 发起多人通话。多人通话必须在同一群组中。
/**
* 发起多人通话, 如果成功后会产生一个新的session
* @param targetId 群组 Id (必填)
* @param userIds 被呼叫的群内成员 Id (必填)
* @param mediaType 0->音频呼叫 or 2->音视频呼叫 (必填)
* @param listener session对象上注册的事件 (必填)
* @param constraints 获取音频或音视频资源时的参数 (可选)
* @param channelId 组织 Id (可选)
*/
const { code, session } = await caller.callInGroup({
targetId: this.targetId,
userIds,
mediaType,
listener: {
/**
* 当远端用户已开始响铃,表示对方已收到呼叫请求 (必填)
* @param sender 已响铃的用户
* @param session 当前的 session 对象
*/
onRinging(sender: ISenderInfo, session: RCCallSession){
const { userId } = sender;
},
/**
* 当远端用户同意接听 (必填)
* @param sender 远端用户
* @param session 当前的 session 对象
*/
onAccept(sender: ISenderInfo, session: RCCallSession){
const { userId } = sender;
},
/**
* 当有远端用户挂断 (必填)
* @param sender 远端用户
* @param reason 挂断的原因
* @param session 当前的 session 对象
*/
onHungup(sender: ISenderInfo, reason: RCCallEndReason, session: RCCallSession){
const { userId } = sender;
},
/**
* 本端资源或远端资源已获取 (必填)
* @param track 本端资源或远端资源, track 不可设置成 Vue 组件的响应式数据
* @param session 当前的 session 对象
*/
onTrackReady(track: RCTrack, session?: RCCallSession){
// track.isLocalTrack() 是否为本地资源
// track.isAudioTrack() 是否为音频
// track.isVideoTrack() 是否为视频
// track.getUserId() 产生该 track 的用户id
// 播放音频。如果为远端音频,建议直接播放。如为本端音频,建议不播放,以减少回音。
if (track.isAudioTrack() && !track.isLocalTrack()) {
track.play();
}
// 视频在对应的容器里播放
if (track.isVideoTrack()) {
const video = document.getElementById(
"video" + user.userId
) as HTMLVideoElement;
track.play(video);
}
},
/**
* 群组通话中有其他人被邀请加入 (必填)
* @param sender 发送者
* @param invitedUsers 被邀请的用户列表
* @param session 当前的 session 对象
*/
onMemberModify: (sender: ISenderInfo, invitedUsers: IInvitedUsers[], session: RCCallSession) => {
}
},
});
if (code === RCCallErrorCode.SUCCESS) {
// do something
}
步骤 6:接听
调用 accept 接通电话。如果通话类型为音视频通话,接通前可设置视频参数。接通之后,可将音视频通话转为纯音频通话,支持通话中切换音频输入设备。
通话媒体类型(纯音频或音视频)由发起者决定。
const { code } = await session.accept();
if (code === RCCallErrorCode.SUCCESS) {
// do something
}
步骤 7:挂断
调用 hungup 挂断(或拒绝)通话。SDK 内部会自动告知对方挂 断(或拒绝)的原因。
const { code } = await session.hungup();
if (code === RCCallErrorCode.SUCCESS) {
// do something
}