跳到主要内容

实现音视频会议

融云开发者账户是使用融云 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 需要的所有权限。

JSON
{
"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 进行初始化和建立连接。

可以在 UIAbilityonCreate() 方法中,调用初始化方法,传入 生产开发 环境的 App Key。

typescript
//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:

TypeScript
// 在 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 长连接。建议在功能模块的加载位置处调用,之后再进行音视频业务。当模块退出后调用 disconnectlogout 断开该连接。

IM 连接成功建立后可以初始化 RTCLib SDK。

TypeScript
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:加入房间

  1. 调用 RCRTCEngineInstance.joinRoom 加入房间。
typescript
// 房间 ID
let roomId: string = "roomId";
// 房间配置信息
let config: RCRTCRoomConfig = new RCRTCRoomConfig();
// 加入房间
let rtcRoom = await RCRTCEngineInstance.joinRoom(roomId, config);
  1. 进入房间成功后调用 RCRTCRoom#setRoomEventListener 注册房间信息回调。

  2. 调用 RCRTCCameraVideoOutputStream#addRender 方法设置本地视频的预览视图。

typescript
// 视图 ID
let viewId = "viewId";
// 视图填充模式
let fillMode = RCRTCVideoFillMode.ASPECT_FIT;
let view = new RCRTCVideoView(viewId, fillMode);
RCRTCEngineInstance.defaultVideoStream.addRender(view);

步骤 7:发布资源

  1. 调用 RCRTCEngineInstance.defaultVideoStream.startCapture() 开启摄像头。不开启摄像头会导致对端订阅默认视频流后出现黑屏问题。

  2. 调用 RCRTCLocalUser 中的 publishDefaultStreams 方法发布默认音视频资源。

typescript
// 发布默认音视频资源
room?.localUser.publishDefaultStreams().then((result) => {
if (result.code === RCRTCCode.SUCCESS) {
// 发布成功
} else {
// 发布失败
}
});

步骤 8:订阅资源

  1. 调用 RCRTCLocalUser 中的 subscribeStreams 方法订阅会议参与者的资源。当远端用户发布资源时,会通过 RCRTCRoomEventListener#didPublishStreams 回调通知,需要订阅音视频流并显示视图。
typescript
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 {
// 订阅失败
}
}));
  1. 在注册的房间事件回调中可根据业务需求监听远端用户发布的资源,并进行订阅。
typescript
const roomEventsListener = {
/**
* 房间内用户发布资源
*
* @param streams 发布的资源
*/
didPublishStreams: (streams: RCRTCInputStream[]) => {
room?.localUser.subscribeStreams(streams).then((result => {
if (result.code === RCRTCCode.SUCCESS) {
// 订阅成功
} else {
// 订阅失败
}
}));
}
};