跳到主要内容

实现音视频通话

CallKit 基于 CallLib SDK 基础增加了一套默认呼叫界面,包含了单人、多人音视频呼叫的各种场景和功能。

提示

房间人数上限

  1. 考虑移动设备的带宽(主要是在多路视频情况下)和 UI 交互效果,建议单次通话或房间内,视频不超过 16 人,纯音频不超过 32 人。超过此上限可能影响通话效果。
  2. CallKit 代码中已设置人数上限。默认发起视频呼叫时,最多可选 7 人。发起音频呼叫时,最多可选 20 人。如需调整,建议勿超过建议上限。

环境要求

适用于 HarmonyOS 的 CallKit SDK 的最低要求是:

  • DevEco Studio NEXT Release(5.0.3.900) 及以上。
  • HarmonyOS SDK API 12 及以上。
  • 手机系统版本号:NEXT.0.0.31

前置条件

  • 创建融云开发者账号,获取 App Key。注册成功后,融云控制台会默认自动创建您的首个应用,默认生成开发环境下的 App Key,使用国内数据中心。注意:同一个应用的开发环境与生产环境提供不同的 App Key,两个环境之间数据隔离。
  • 完成开通音视频服务。您需要开通音视频通话服务。

快速上手

步骤 1 导入 SDK

导入融云音视频通话能力 UI 库 CallKit 具体步骤请参阅 导入 CallKit SDK

步骤 2 初始化

RTC 音视频能力是基于 IM 作为信令通道的,CallKit 又依赖于 IMKit & CallLib,所以要先初始化 IM 。如果不换 AppKey,在整个应用生命周期中,初始化一次即可。建议调用位置放在应用启动位置处,或在音视频功能模块的加载位置处。 在 UIAbility 的 onCreate() 方法中,调用初始化方法,传入生产或开发环境的 App Key。

TypeScript
// 在 UIAbility 中获取 context
let context = this.context

let initOption = new InitOption();
let appKey = "从融云后台获取的 appKey";
IMEngine.getInstance().init(context, appKey, initOption);

步骤 3 连接 IM 服务

音视频用户之间的信令传输依赖于融云的即时通信(IM)服务,因此需要先调用 connect 与 IM 服务建立好 TCP 长连接。建议在功能模块的加载位置处调用,之后再进行音视频呼叫业务。当模块退出后调用 disconnectlogout 断开该连接。

TypeScript
// 连接 IM
IMEngine.getInstance().connect(token, 20).then(result => {
if (EngineError.Success === result.code) {
// 连接成功
let userId = result.userId;
return
}
if (EngineError.ConnectTokenExpired === result.code) {
// Token 过期,从 APP 服务请求新 token,获取到新 token 后重新 connect()
} else if (EngineError.ConnectionTimeout === result.code) {
// 连接超时,弹出提示,可以引导用户等待网络正常的时候再次点击进行连接
} else {
//其它业务错误码,请根据相应的错误码作出对应处理。
}
});

步骤 4 实现监听

在通话前需要设置 RCCallRCCallListener 监听,在 CallKit 页面跳转时提供必要的 UI 组件。

TypeScript
RCCall.getInstance().callListener = {
// 获取当前Ability页面的WindowStage, 可以按照示例中的实现,先在 onWindowStageCreate 方法中缓存 windowStage
/**
* onWindowStageCreate(windowStage: window.WindowStage): void {
// Main window is created, set main page for this ability
AppStorage.setOrCreate('windowStage', windowStage) //保存windowStage
}
*/
didWindowStage: () => {
let windowStage = AppStorage.get('windowStage') as window.WindowStage
return windowStage
},
/**
* 多人通话选人页面导航栈, 用于选人页面导航, 必须设置, 否则无法导航到选人页面
* @returns
*/
didMultiCallNavPathStack: () => {
/// 该方法返回,当前页面所在的导航, NavPathStack 类型
return new NavPathStack()
},
/**
* 点击CallKit横幅或左上角胶囊后跳转回到的Ability页面名
* @returns
*/
didCallKitNavAbilityName: () => {
/// 该方法返回,所集成项目的 UIAbility
return 'CallKitEntryAbility'
}
}

步骤 5 呼叫方

通常 App 内呼叫和被叫方逻辑会同时存在,所以需要分别集成。

发起单人呼叫

TypeScript
/**
* 发起单聊呼叫
* @param userId 被叫端Id
* @param mediaType 呼叫媒体类型
*/
public startSingleCall(targetId: string, mediaType: RCCallMediaType)
参数类型必填说明
targetIdNSString对方的用户ID
mediaTypeRCCallMediaType媒体类型
  • 示例代码:
TypeScript
// 使用时填写真实 id
let targetId = '被叫端UserId'
// 媒体类型 RCCallMediaType
let mediaType = RCCallMediaType.VIDEO
RCCall.getInstance().startSingleCall(targetId, mediaType)

发起多人呼叫

  1. 调用 startMultiCall 会先弹出群组选择成员界面,所以发起多人呼叫前,需要优先实现 IMKit 的 UserDataProvider 接口,来提供群组信息,群成员信息等数据来源。
TypeScript
RongIM.getInstance().userDataService().setUserDataProvider({        
/// 获取用户信息数据,当需要展示用户头像、昵称时,如果 SDK 没有对应的信息时触发该方法
fetchUserInfo: (userId: string) => {
return new Promise<UserInfoModel | undefined>((resolve, reject) => {
// 调试代码
if (userId === 'test1') {
let userInfo =
new UserInfoModel(userId, 'test1_name', 'https://xxx.png')
resolve(userInfo)
}
})
},
///获取群组成员信息数据,当需要展示群组成员头像、名称时,如果 SDK 没有对应的信息时触发该方法
fetchGroupMemberInfo: (groupId: string, userId: string) => {
return new Promise<GroupMemberInfoModel | undefined>((resolve, reject) => {
// 调试代码
let member: GroupMemberInfoModel = new GroupMemberInfoModel(groupId, userId, '', '')
if (userId === 'test1') {
member = new GroupMemberInfoModel(groupId, userId, 'test1_name',
'https://xxx')
}
resolve(member)
})
},
/// 获取群成员信息数据列表,当需要展示群成员头像、名称时,如果 SDK 没有对应的信息时触发该方法
fetchGroupMemberInfos: (groupId: string) => {
return new Promise<Array<GroupMemberInfoModel> | undefined>((resolve, reject) => {
/// 使用 app server 提供的群成员列表信息,以下是示例测试数据
let members: Array<GroupMemberInfoModel> = new Array<GroupMemberInfoModel>()
for (let i = 0; i < 5; i++) {
let member: GroupMemberInfoModel = new GroupMemberInfoModel('', '', '', '')
member.userId = `test${i + 1}`
members.push(member)
}
resolve(members)
});
},
fetchGroupInfo: (groupId: string) => {
return new Promise<GroupInfoModel | undefined>((resolve, reject) => {
if (groupId === 'test_group') {
let model = new GroupInfoModel('test_group', '测试群', '', 3)
resolve(model)
}
else {
resolve(undefined)
}
})
}
})
  1. 实现了步骤1 中的相关协议方法提供了群信息数据,调用 startMultiCall 会先弹出选择成员界面。如下图所示,选择联系人点击 确认 后 CallKit 发起多人呼叫。
Example banner
TypeScript
 /**
* 发起群聊呼叫
* @param targetId 群组Id
* @param mediaType 呼叫媒体类型
*/
public startMultiCall(targetId: string, mediaType: RCCallMediaType)
  • 参数说明:

    参数类型必填说明
    targetIdNSString群组ID
    mediaTypeRCCallMediaType媒体类型
  • 示例代码:

TypeScript
// 多人呼叫时,需要填写 IM 群组id
let targetId = 'groupId'
// 媒体类型 RCCallMediaType
let mediaType = RCCallMediaType.VIDEO
RCCall.getInstance().startSingleCall(targetId, mediaType)

步骤 6 接听方

鸿蒙 CallKit 中已经默认实现了 CallLib 库提供 RCCallClientListener 监听回调。被叫端收到呼叫,会自动弹出通话界面,点击来电页面的接听按钮即可接听通话。

App 在后台或者未启动时会收到来电通知,点击来电通知条 App 打开后会显示来电页面。