iOS

# 实现视频通话

# 前提条件

请确认您已完成以下操作:

    • 已获取 App Key。

AppKey 作为同个环境的分域依据,同一个域的终端才能实现互通,AppKey 由 Juphoon 视频平台提供。

    • 集成 SDK(Harmony)。

# 快速跑通 Sample

  1. 在 Juphoon RTC SDK 文档中心,选择 Harmony 平台下载体验 JCCSample 示例项目。

访问下载地址,示例如下:

  1. 下载完成后,打开安装包,解压 JCCSample,然后安装 JCCSample.apk

  2. 打开应用程序后,设置正确的 appkey,环境地址以及账号。

    1. 首先点击初始化按钮,成功后,登入按钮变为可点击;
    2. 确认账号输入无误之后,点击登入按钮,按钮字样变为登出,即登录成功;
    3. 点击多方通话(MpCall)按钮,即可进入多方体验相关的功能;
    4. 进入网络电话页面后,输入房间号,如果有房间密码输入密码,点击加入,即可进入房间。

# 功能实现

img

# 初始化

注:我们所有的方法都建议在主线程调用,否则可能会出现异常无法正常使用,回调接口也都在主线程上报。

在使用业务接口前,需对 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 初始化之后,即可进行登录的集成,登录接口调用流程如下所示:

img

登录到 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) => {

}

# 登出

离开房间后,可以做登出操作,登出接口调用流程如下所示:

img

登出结果通过 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();
    });
}