# 实现视频通话
# 前提条件
请确认您已完成以下操作:
- 已获取 App Key。
AppKey 作为同个环境的分域依据,同一个域的终端才能实现互通,AppKey 由 Juphoon 视频平台提供。
- 集成 SDK(Harmony)。
# 快速跑通 Sample
- 在 Juphoon RTC SDK 文档中心,选择 Harmony 平台下载体验 JCCSample 示例项目。
访问下载地址,示例如下:
下载完成后,打开安装包,解压 JCCSample,然后安装 JCCSample.apk
打开应用程序后,设置正确的 appkey,环境地址以及账号。
- 首先点击初始化按钮,成功后,登入按钮变为可点击;
- 确认账号输入无误之后,点击登入按钮,按钮字样变为登出,即登录成功;
- 点击多方通话(MpCall)按钮,即可进入多方体验相关的功能;
- 进入网络电话页面后,输入房间号,如果有房间密码输入密码,点击加入,即可进入房间。
# 功能实现
# 初始化
注:我们所有的方法都建议在主线程调用,否则可能会出现异常无法正常使用,回调接口也都在主线程上报。
在使用业务接口前,需对 Juphoon RTC SDK 进行初始化操作。
| 类 | 模块 | 描述 |
|---|---|---|
| JRTCClient (opens new window) | 登录模块 | 负责视频平台的登录登出,只有登录到视频平台才可以使用视频相关的业务 |
| JRTCMediaDevice (opens new window) | 媒体模块 | 负责本地的媒体设备操作,视频画面渲染等功能 |
| JRTCCall (opens new window) | 通话模块 | 可以通过流水号加入通话,邀请其他成员加入通话 |
| JRTCRecord (opens new window) | 录制模块 | 实现本地音视频录制(不需要建立通话),本地分片录制等功能 |
class JRTCManager implements JRTCClientCallback, JRTCCallCallback, JRTCMediaDeviceCallback {
private client: JRTCClient;
private mediaDevice: JRTCMediaDevice;
private call: JRTCCall;
private record: JRTCRecord;
public init(context: Context) {
let param: JRTCClientInitParam = new JRTCClientInitParam();
param.appName = "appName" // 设置应用名称
param.SDKInfoDir = "SDKInfoDir" // 设置SDK信息存储目录
param.appKey = "appKey" // 设置AppKey
param.server = "server" //设置接入服务器地址
param.logConsole = true // 设置是否控制台日志输出,默认true
param.logLocalFile = true // 获取是否是否本地文件日志输出,默认true
param.looseTimeoutControl = true // 设置是否开启 RPC 抗信令丢包控制(70%的上下行信令丢包),默认false
this.client = JRTCClient.create(context.getApplicationContext(), this, initParam);
this.mediaDevice = JRTCMediaDevice.create(this.mClient, this, undefined);
this.call = JRTCCall.create(this.client, this.mediaDevice, this);
this.record = JRTCRecord.create(this.client,this.mediaDevice, this);
// 设置基本参数
this.client.setServer("server"); // 设置接入服务器地址
this.client.setAppKey("appKey"); // 设置AppKey
this.client.setDisplayName("displayName"); // 设置显示名称
this.client.setAppName("appName"); // 设置应用名称
}
}
根据需求实现 对应 JRTCCallCallback (opens new window) 的接口即可。
# 登录
SDK 初始化之后,即可进行登录的集成,登录接口调用流程如下所示:
登录到 Juphoon 视频平台主要调用的是 JRTCClient (opens new window) 的登录接口 login (opens new window)。
/**
* 登录 Juphoon RTC 平台,只有登录成功后才能进行平台上的各种业务
* <p>
* 登录结果通过 {@link JRTCClientCallback.onLogin onLogin} 回调通知
*
* @param { string } userId 用户ID
* @param { string } password 密码,不能为空
* @param { JRTCClientLoginParam? } clientLoginParam 登录参数,一般不需要设置,如需设置请询问客服,传 undefined 则按默认值
* @return { boolean } 接口调用结果
* - true:接口调用成功
* - false:接口调用异常
* @warning 目前只支持免鉴权模式,服务器不校验账号密码,免鉴权模式下当账号不存在时会自动去创建该账号
* @warning 用户名为英文数字和'+' '-' '_' '.',长度不要超过64字符,'-' '_' '.'不能作为第一个字符
*/
public abstract login(userId: string, password: string, clientLoginParam?: JRTCClientLoginParam): boolean;
JRTCClientLoginParam (opens new window) 属性介绍
| 属性 | 描述 |
|---|---|
| accountEntry | 设置账户分录参数,如果支持国密S3则需要设置 certificate 参数,否则可以不设置 certificate 参数 |
| certificate | 设置 S3 国密证书 Base64 编码内容 |
| acceptExpiredCertificate | 设置是否允许过期证书校验通过 |
| token | 设置token |
| tokenType | 设置 token 校验类型 |
| deviceId | 设置设备id |
| logFilter | 设置日志过滤标签(用于日志管理平台过滤终端日志使用) |
| terminalType | 设置终端登录类型,支持多终端登录,默认所有终端相同会导致互踢 |
| autoCreateAccount | 是否自动创建账号(免鉴权使用),默认true |
| accelerateKey | 设置加速云KEY |
| accelerateKeySecret | 设置加速云KEY密钥 |
| optimizeDataRouter | 设置是否开启数据路由优化,默认true |
登录的结果将会通过 JRTCClientCallback (opens new window) 的接口上报:
/**
* 登录结果回调
*
* @param { boolean } result 登录结果
* - true:表示登录成功,
* - false:表示登录失败
* @param { JRTCReasonCode } reason 登录失败原因,当 result 为 false 时该值有效
*/
onLogin?: (result: boolean, reason: JRTCReasonCode) => void;
示例代码:
// 创建登录配置参数
const loginParam: JRTCClientLoginParam = new JRTCClientLoginParam();
// 登录
client.login("juphoon", "123456", loginParam);
public onLogin(result: boolean, reason: number): void {
if (result) {
// 登录成功
} else {
// 登录失败,具体原因查询 reason 错误码
}
}
# 加入通话
/**
* 加入通话
* @note 需要已经登录
* @note 该接口支持加入通话并且邀请其他用户加入,通过 {@link JRTCCallJoinParam#setInviteCalleeUserId(string)} setInviteCalleeUserId}
* 和 {@link JRTCCallJoinParam#setInviteCalleeUserType(int)} setInviteCalleeUserType} 设置需要邀请的用户ID和用户类型
*
* <p>
* 该方法让用户加入通话,在同一个通话内的用户可以互相视频语音。<br>
* 如果用户已在通话中,必须退出当前通话,即处于空闲状态,才能进入其他通话,否则将直接返回 false,且不会收到回调通知。
*
* @param serialId 业务流水号,保证唯一,必选
* @param param 加入通话参数,传 undefined 则使用默认参数
* @see JRTCCallJoinParam
* @return 接口调用结果
* - true: 接口调用成功,会收到 {@link JRTCCallCallback#onJoin onJoin} 回调
* - false: 接口调用异常
*/
public abstract join(serialId: string, param: JRTCCallJoinParam | undefined): boolean;
JRTCCallJoinParam (opens new window) 参数介绍
| inviteCalleeUserId | 被邀请者用户ID。如果不为空,会在自身加入通话同时邀请用户加入 |
|---|---|
| inviteCalleeUserType | 被邀请者用户类型,默认APP用户。当被邀请者用户ID不为空时,该值有效 |
| routeId | 线路ID。当被邀请用户ID不为空,并且被邀请者用户类型是SIP用户的时候有效 |
| extraInfo | 随路参数。当被邀请用户ID不为空时,该值有效 |
示例代码:
let param:JRTCCallJoinParam = new JRTCCallJoinParam()
...
param.video = true;
...
call.join("serialId",param))
onJoin: (result: boolean, reasonCode: JRTCReasonCode) => {
if (result) {
//登录成功
}
}
# 邀请
/**
* 邀请其他成员加入通话
* @note 需要已经加入通话
*
* @param calleeUserId 被邀请者用户ID
* @param param 邀请参数
* @return
* - 操作id: 接口调用成功,对应 {@link JRTCCallCallback#onInviteResult onInviteResult} 回调的 operatorId 参数
* - -1: 接口调用异常,不会收到回调
*/
public abstract invite(calleeUserId: string, param: JRTCCallExtraParam | undefined): number;
JRTCCallExtraParam (opens new window)参数介绍
| video | 是否视频通话 |
|---|---|
| serialId | 业务流水号 |
| callerUserId | 邀请人用户ID |
| callerDisplayName | 邀请人昵称 |
| calleeUserId | 被邀请者用户ID |
| calleeDisplayName | 被邀请者昵称 |
| calleeUserType | 被邀请者用户类型 |
| routeId | 线路ID |
| extraInfo | 随路参数 |
如果类型是 JRTCCallUserType.App (opens new window),被邀请人会收到 onInviteReceived (opens new window) 回调
/**
* 收到邀请通知
*
* @param param 其他参数
* @see JRTCCallExtraParam
*/
onInviteReceived?: (param: JRTCCallExtraParam) => void;
示例代码
const param: JRTCCallExtraParam = new JRTCCallExtraParam();
param.callerUserId = "userId";
param.video = true;
param.calleeUserType = JRTCCallUserType.APP;
param.routeId = "routeId";
call.invite("userId", param);
# 取消邀请
在被邀请人未回应邀请前,可以取消当前的邀请。
/**
* 取消邀请
*
* @return
* - 操作id: 接口调用成功,对应 {@link JRTCCallCallback#onCancelInviteResult(int, boolean, string)} onCancelInviteResult} 回调的 operatorId 参数
* - -1: 接口调用异常,不会收到回调
*/
public abstract cancelInvite(): number;
取消结果回调
/**
* 取消邀请结果回调
* @param operatorId 操作id,对应 {@link JRTCCall#cancelInvite() invite} 的返回值
* @param result 加入通话是否成功
* - true: 成功
* - false: 失败
* @param reason 取消邀请失败原因
*/
onCancelInviteResult?: (operatorId: number, result: boolean, reason: string) => void;
示例代码
call.cancelInvite()
同时对方将收到取消邀请通知
/**
* 对方取消邀请通知
*
* @param param 其他参数
* @see JRTCCallExtraParam
*/
onInviteCanceled?: (param: JRTCCallExtraParam) => void;
# 收到邀请
被邀请人收到邀请处理,可以接收或者拒绝邀请
示例代码:
/**
* 收到邀请通知
*
* @param param 其他参数
* @see JRTCCallExtraParam
*/
onInviteReceived?: (param: JRTCCallExtraParam) => void;
# 接收邀请
/**
* 接受邀请
*
* @param video 是否需要视频
*
* @return
* - 操作id: 接口调用成功,对应 {@link JRTCCallCallback#onAcceptInviteResult(int, boolean, string)} onAcceptInviteResult} 回调的 operatorId 参数
* - -1: 接口调用异常,不会收到回调
*/
public abstract acceptInvite(video: boolean): number;
邀请处理结果回调
/**
* 接受邀请结果回调
* @param operatorId 操作id,对应 {@link JRTCCall#acceptInvite(boolean) acceptInvite} 的返回值
* @param result 加入通话是否成功
* - true: 成功
* - false: 失败
* @param reason 接受邀请失败原因
*/
onAcceptInviteResult?: (operatorId: number, result: boolean, reason: string) => void;
示例代码:
// 接受邀请
call.acceptInvite(true)
同时对方能收到接受邀请通知
/**
* 对方接受邀请通知
*
* @param param 其他参数
* @see JRTCCallExtraParam
*/
onInviteAccepted?: (param: JRTCCallExtraParam) => void;
# 拒绝邀请
/**
* 拒绝邀请
*
* @return
* - 操作id: 接口调用成功,对应 {@link JRTCCallCallback#onAcceptInviteResult(int, boolean, string)} onAcceptInviteResult} 回调的 operatorId 参数
* - -1: 接口调用异常,不会收到回调
*/
public abstract rejectInvite(): number;
示例代码
// 拒绝邀请
call.rejectInvite();
同时对方能收到拒绝邀请通知
/**
* 对方拒绝邀请通知
*
* @param param 其他参数
* @see JRTCCallExtraParam
*/
onInviteRejected?: (param: JRTCCallExtraParam) => void;
# 视频渲染
当加入房间后,除了本地的视频画面,还有房间内其他成员的视频画面,如果房间内其他成员有视频流上传,本端可以获取到其他成员的的视频流并进行渲染;
当成员视频状态变化,比如该成员开始上传视频,此时可以去渲染该成员视频,该成员结束上传视频,则可以去停止渲染该成员视频。
通过调用 requestVideo (opens new window) 方法订阅该视频,当成员离开需要调用 unRequestVideo (opens new window) 及时取消订阅该视频流。
渲染视频画面需要调用 JRTCVideoComponent 组件传入视频流id即可渲染
/**
* 订阅房间中其他用户的视频流
*
* @param participant JRTCRoomParticipant 成员对象
* @param videoSize 视频请求的尺寸,详见 {@link JRTCVideoSize}
* @return 接口调用结果
* - true: 接口调用成功,会收到 {@link JRTCRoomCallback#onParticipantUpdate onParticipantUpdate} 回调
* - false: 接口调用异常
*/
public abstract requestVideo(participant: JRTCRoomParticipant, videoSize: JRTCVideoSize): boolean;
/**
* 取消订阅房间中其他用户的视频流
*
* @param participant JRTCRoomParticipant 房间中其他成员对象
* @return 调用是否正常
* - true: 正常执行调用流程,会收到 {@link JRTCRoomCallback#onParticipantUpdate onParticipantUpdate} 回调
* - false: 调用失败,不会收到回调通知
*/
public abstract unRequestVideo(participant: JRTCRoomParticipant): boolean;
示例代码
@State participantArray: Array<JRTCRoomParticipant> = [];
onParticipantUpdate: (participant: JRTCRoomParticipant | undefined,changeParam: JRTCRoomParticipantChangeParam | undefined) => {
for (const participant of call.getParticipants() || []) {
this.participantArray.push(participant);
}
}
ForEach(this.participantArray, (participant: JRTCRoomParticipant) => {
JRTCVideoComponent({
streamId: participant.streamId,
mediaDevice: JRTCManager.getInstance().mMediaDevice
})
})
# 新成员加入
当新成员加入房间后,其他成员会收到 onParticipantJoin (opens new window) 成员加入的回调。
/**
* 成员加入回调
* <p>
* 当有用户调用 {@link JRTCRoom#join join} 接口加入房间成功时,已在房间中的成员会收到此回调。
*
* @param participant JRTCRoomParticipant 成员对象
* @param room 当前 JRTCRoom 对象
*/
onParticipantJoin?: (participant: JRTCRoomParticipant | undefined) => void;
# 成员更新
当房间内成员状态发生改变时,其他成员能收到该成员状态变化通知 onParticipantUpdate (opens new window),具体成员变化属性参考 JRTCRoomParticipantChangeParam (opens new window) ,包含成员音量、网络状态、音视频上传状态、成员类型、视频订阅尺寸变化等。
/**
* 成员更新回调
*
* @param participant 成员对象
* @param changeParam 更新标识类
*/
onParticipantUpdate?: (participant: JRTCRoomParticipant, changeParam: C
JRTCRoomPropChangeParam) => void;
# 成员离开
当成员离开房间后,其他成员会收到成员离开的回调 onParticipantLeft (opens new window) 回调通知。
/**
* 成员离开回调
*
* @param participant 成员对象
* @param reason 成员离开原因
*/
onParticipantLeft?: (participant: JRTCRoomParticipant, reason: JRTCReasonCode) => void;
# 结束离开
成员可以自己离开通话或者结束通话。
/**
* 离开通话
* @note 仅自己离开
* @note 需要已经加入通话
*
* @return 接口调用结果
* - true: 接口调用成功,非空闲状态下,会收到 {@link JRTCCallCallback#onLeave onLeave} 回调
* - false: 接口调用异常
*/
public abstract leave(): boolean;
/**
* 结束通话
* @note 自己离开并踢出通话中的其他成员
* @note 需要已经加入通话
*
* @return 接口调用结果
* - true: 接口调用成功,非空闲状态下,会收到 {@link JRTCCallCallback#onLeave onLeave} 回调
* - false: 接口调用异常
*/
public abstract stop(): boolean;
离开或者结束通话会收到 onLeave (opens new window) 回调
/**
* 离开通话结果回调
* <p>
* 调用 {@link JRTCCall#leave leave} 接口成功后,会收到此回调。
*
* @param serialId 业务流水号
* @param reasonCode 离开原因,参见:{@link JRTCEnum.JRTCReasonCode 离开原因}
*/
onLeave?: (serialId: string, reasonCode: JRTCReasonCode) => void;
示例代码
// 离开通话
call.leave();
// 结束通话
call.stop();
// 离开或者结束回调
onLeave:(serialId: string, reasonCode: number) => {
}
# 登出
离开房间后,可以做登出操作,登出接口调用流程如下所示:
登出结果通过 JRTCClientCallback (opens new window) 中的 onLogout (opens new window) 接口上报:
/**
* 登出 Juphoon RTC 平台,登出后不能进行平台上的各种业务
* <p>
* 登出结果通过 {@link JRTCClientCallback.onLogout onLogout} 回调通知
*
* @return { boolean } 接口调用结果
* - true:接口调用成功
* - false:接口调用异常
*/
public abstract logout(): boolean;
登出结果通过 JRTCClientCallback (opens new window) 中的 onLogout (opens new window) 接口进行上报。
/**
* 登出回调
*
* @param { JRTCReasonCode } reason 登出原因
*/
onLogout?: (reason: JRTCReasonCode) => void;
示例代码:
// 调用登出接口
client.logout();
// 监听登出结果回调
public onLogout(reason: number) {
// 登出完成
}
# 4.3.13 登录登出状态改变通知
登录状态通过 JRTCClientCallback (opens new window) 中的 onClientStateChanged (opens new window) 接口上报
/**
* 登录状态变化通知
*
* @param { JRTCClientState } state 当前状态值
* @param { JRTCClientState } oldState 之前状态值
*/
onClientStateChanged?: (state: JRTCClientState, oldState: JRTCClientState) => void;
示例代码
//登录状态改变通知
onClientStateChanged:(state: JRTCClientState, oldState: JRTCClientState) => {
//state 当前状态
//oldState 之前状态
}
# 4.3.14 销毁SDK
每个模块都有对应的销毁接口。如不需再使用 SDK 的相关功能,可以强制释放 SDK 的资源。
注:该方法为同步调用,调用此方法后,你将无法再使用该模块的其它方法和回调。 我们不建议在 JRTC SDK 的所有回调方法中调用此方法销毁对象,否则可能出现崩溃现象,如果一定要在回调方法中调用,需要异步调用。
/**
* 销毁 JRTCCall 对象
*
* @note 该方法为同步调用,需要等待 JRTCCall 实例资源释放后才能执行其他操作,调用此方法后,你将无法再使用 JRTCCall 的其它方法和回调。<br>
* 我们 **不建议** 在 JRTCSDK 的回调中调用此方法销毁 JRTCCall 对象,否则可能出现崩溃现象。<br>
* 如需在销毁后再次创建 JRTCCall 实例,需要等待 destroy 方法执行结束后再创建实例。
*/
public static destroy(): void;
示例代码:
//建议按照初始化顺序反顺序销毁
JRTCCall.destroy();
JRTCMediaDevice.destroy();
JRTCClient.destroy();
//异步调用示例
// 登出回调
public onLogout(reason: number) {
setTimeout(() => {
JRTCCall.destroy();
JRTCMediaDevice.destroy();
JRTCClient.destroy();
});
}
