实现音视频通话
CallKit 基于 CallLib SDK 基础增加了一套默认呼叫界面,包含了单人、多人音视频呼叫的各种场景和功能。
提示
房间人数上限
- 考虑移动设备的带宽(主要是在多路视频情况下)和 UI 交互效果,建议单次通话或房间内,视频不超过 16 人,纯音频不超过 32 人。超过此上限可能影响通话效果。
- 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 初始化
重要提示
从 1.9.0 版本开始,必须先调用 RCCall.install() 方法加载 CallKit 模块,且该方法必须在 IM 初始化之前调用。
RTC 音视频能力是基于 IM 作为信令通道的,CallKit 又依赖于 IMKit & CallLib,所以要先初始化 IM。如果不换 AppKey,在整个应用生命周期中,初始化一次即可。建议调用位置放在应用启动位置处,或在音视频功能模块的加载位置处。在 UIAbility 的 onCreate() 方法中,调用初始化方法,传入生产或开发环境的 App Key。
TypeScript
//1.9.0 版本新增方法:加载 CallKit 模块,必需在 IM 初始化之前调用。
RCCall.install();
// 在 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 长连接。建议在功能模块的加载位置处调用,之后再进行音视频呼叫业务。当模块退出后调用 disconnect 或 logout 断开该连接。
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 实现监听
在通话前需要设置 RCCall 的 RCCallListener 监听,在 CallKit 页面跳转时提供必要的 UI 组件。
TypeScript
RCCall.getInstance().callListener = {
/**
* 获取当前 Ability 页面对应的 WindowStage 实例。
*
* 说明:
* - 在 Ability 的生命周期方法 `onWindowStageCreate` 中,首次创建并缓存 `windowStage`。
* - 通过 `didWindowStage` 方法获取缓存的 `windowStage`,方便在其他逻辑中调用。
*
* 示例:
* ```ts
* onWindowStageCreate(windowStage: window.WindowStage): void {
* // 主窗口创建完成, 缓存 windowStage 以便后续使用
* AppStorage.setOrCreate('windowStage', windowStage);
* }
* ```
*
* @returns 当前 Ability 缓存的 WindowStage 实例
*/
didWindowStage: () => {
let windowStage = AppStorage.get('windowStage') as window.WindowStage
return windowStage
},
/**
* 多人通话选人页面导航栈, 用于选人页面导航, 必须设置, 否则无法导航到选人页面
* ⚠️ 注意:
* 该方法必须返回当前页面中实际使用的 Navigation 容器绑定的 NavPathStack 对象。
*
* * 正确示例(页面中定义并绑定了 navStack):
*
* ```ts
* private navStack: NavPathStack = new NavPathStack();
*
* Navigation({ stack: this.navStack }) {
* // 页面内容
* }
*/
didMultiCallNavPathStack: () => {
return this.navStack; // ✅ 返回实际绑定在 UI 上的导航栈
},
/**
* 点击 CallKit 横幅或左上角胶囊后跳转回到的 Ability 页面名
* ⚠️ 注意:
* 此处的页面名须使用 module.json5 中 module -> abilities -> name 定义字义的字符串
*/
didCallKitNavAbilityName: () => {
/// 该方法返回,所集成项目的 UIAbility
return 'CallKitEntryAbility'
},
/**
* 是否允许选择某个用户
* 非必传,默认允许选择
* @param userId 用户 userId
*/
didUserSelectable: async (userId: string): Promise<boolean> => {
/**
* 根据条件判断是否允许选择该用户
* 不允许时返回 false
*/
return false;
},
/**
* 按钮插件过滤
* 非必传,插件默认显示
* @param id 会话对象
* @param mediaType 媒体类型,音频或视频
* @returns boolean
*/
didPluginFilter: (id: ConversationIdentifier, mediaType: RCCallMediaType): boolean => {
// 如需过滤掉视频插件
if (mediaType === RCCallMediaType.VIDEO) {
return false;
};
}
}