实现音视频会议
融云开发者账户是使用融云 SDK 产品的必要条件。在开始之前,请先前往融云官网注册开发者账户。注册后,控制台将自动为你创建一个应用,默认为开发环境应用,使用国内数据中心。请获取该应用的 App Key,在本教程中使用。
房间人数上限
考虑移动设备的带宽(主要是在多路视频情况下)和 UI 交互效果,建议单次通话或房间内,视频不超过 16 人,纯音频不超过 32 人。超过此上限可能影响通话效果。
环境要求
- DevEco Studio NEXT Release(5.0.3.900)及以上。
- HarmonyOS SDK API 12 及以上。
- 手机(真机)系统版本号:NEXT.0.0.31
步骤 1:服务开通
您在融云创建的应用默认不会启用音视频服务。在使用融云提供的任何音视频服务前,您需要前往控制台为应用开通音视频服务。
具体步骤请参阅开通音视频服务。
服务开通、关闭等设置完成后 15 分钟后生效。
步骤 2:SDK 导入
您需要导入融云音视频核心能力库 RTCLib 和 RTC 业务所依赖的即时通讯(IM)能力库 IMLib。
具体步骤请参阅导入 SDK。
步骤 3:权限配置
在 entry/src/main/module.json5 文件中声明 SDK 需要的所有权限。
{
"requestPermissions": [
{
"name": "ohos.permission.INTERNET",
"reason": "音视频需要网络权限"
},
{
"name": "ohos.permission.GET_NETWORK_INFO",
"reason": "监听网络状态权限"
},
{
"name": "ohos.permission.CAMERA",
"reason": "摄像头采集需要"
},
{
"name": "ohos.permission.MICROPHONE",
"reason": "音频采集需要"
}
]
}
步骤 4:使用 App Key 初始化
从 1.9.0 版本开始,必须先调用 RCRTCEngineInstance.install() 方法加载 RTCLib 模块,且该方法必须在 IM 初始化之前调用。
RTCLib 依赖融云即时通讯(IM)客户端 SDK 提供信令通道。要在您的应用程序中集成和运行,您需要先对 IM SDK 进行初始化和建立连接。
可以在 UIAbility 的 onCreate() 方法中,调用初始化方法,传入 生产 或 开发 环境的 App Key。
//1.9.0 版本新增方法:加载 RTCLib 模块,必需在 IM 初始化之前调用。
RCRTCEngineInstance.install();
// 在 UIAbility 中获取 context
let context = this.context
let initOption = new InitOption();
let appKey = "从融云后台获取的 App Key";
IMEngine.getInstance().init(context, appKey, initOption);
初始化配置 InitOption 中封装了区域码 areaCode、导航服务地址 naviServer、统计服务地址 statisticServer、文件下载路径 mediaSavePath。
- 如果 App Key 属于中国(北京)数据中心,您无需传入任何配置,SDK 会使用默认 配置。
- 如果 App Key 属于海外数据中心,则必须传入有效的区域码(
AreaCode)配置。请务必在控制台核验当前 App Key 所属海外数据中心后,找到AreaCode中对应的枚举值进行配置。
例如,使用新加坡数据中心的应用的生产或开发环境的 App Key:
// 在 UIAbility 中获取 context
let context = this.context
let initOption = new InitOption();
initOption.areaCode = AreaCode.SG;
let appKey = "Singapore_dev_AppKey";
IMEngine.getInstance().init(context, appKey, initOption);
关于 IM SDK 初始化的更多配置请参见初始化。
步骤 5:连接融云服务器
音视频用户之间的信令传输依赖于融云的即时通讯(IM)服务,因此需要先调用 connect 与 IM 服务建立 TCP 长连接。建议在功能模块的加载位置处调用,之后再进行音视频业务。当模块退出后调用 disconnect 或 logout 断开该连接。
IM 连接成功建立后可以初始化 RTCLib SDK。
let token = "IMToken";
let timeout = 5;
IMEngine.getInstance().connect(token, timeout)
.then(result => {
if (EngineError.Success === result.code) {
// 连接成功
let userId = result.userId;
return;
}
if (EngineError.ConnectTokenExpired === result.code) {
// Token 过期,从应用服务器请求新 Token,获取到新 Token 后重新 connect()
} else if (EngineError.ConnectionTimeout === result.code) {
// 连接超时,弹出提示,可以引导用户等待网络正常时再次点击进行连接
} else {
// 其他业务错误码,请根据相应的错误码作出对应处理
}
});
步骤 6:加入房间
- 调用
RCRTCEngineInstance.joinRoom加入房间。
// 房间 ID
let roomId: string = "roomId";
// 房间配置信息
let config: RCRTCRoomConfig = new RCRTCRoomConfig();
// 加入房间
let rtcRoom = await RCRTCEngineInstance.joinRoom(roomId, config);
-
进入房间成功后调用
RCRTCRoom#setRoomEventListener注册房间信息回调。 -
调用
RCRTCCameraVideoOutputStream#addRender方法设置本地视频的预览视图。
// 视图 ID
let viewId = "viewId";
// 视图填充模式
let fillMode = RCRTCVideoFillMode.ASPECT_FIT;
let view = new RCRTCVideoView(viewId, fillMode);
RCRTCEngineInstance.defaultVideoStream.addRender(view);
步骤 7:发布资源
-
调用
RCRTCEngineInstance.defaultVideoStream.startCapture()开启摄像头。不开启摄像头会导致对端订阅默认视频流后出现黑屏问题。 -
调用
RCRTCLocalUser中的publishDefaultStreams方法发布默认音视频资源。
// 发布默认音视频资源
room?.localUser.publishDefaultStreams().then((result) => {
if (result.code === RCRTCCode.SUCCESS) {
// 发布成功
} else {
// 发布失败
}
});
步骤 8:订阅资源
- 调用
RCRTCLocalUser中的subscribeStreams方法订阅会议参与者的资源。当远端用户发布资源时,会通过RCRTCRoomEventListener#didPublishStreams回调通知,需要订阅音视频流并显示视图。
let streams: Array<RCRTCInputStream> = new Array();
room?.remoteUsers?.forEach(remoterUser => {
streams.push(...remoterUser.streams);
});
if (streams.length === 0) {
promptAction.showToast({message: '无可用订阅流'});
return;
}
room?.localUser.subscribeStreams(streams).then((result => {
if (result.code === RCRTCCode.SUCCESS) {
// 订阅成功
} else {
// 订阅失败
}
}));
- 在注册的房间事件回调中 可根据业务需求监听远端用户发布的资源,并进行订阅。
const roomEventsListener = {
/**
* 房间内用户发布资源
*
* @param streams 发布的资源
*/
didPublishStreams: (streams: RCRTCInputStream[]) => {
room?.localUser.subscribeStreams(streams).then((result => {
if (result.code === RCRTCCode.SUCCESS) {
// 订阅成功
} else {
// 订阅失败
}
}));
}
};