iOS

# 实现视频通话

本文档为您展示通过 SDK 实现视频通话(座席侧集成)的相关步骤,帮助您在远程银行和视频客服的场景下实现智能排队、座席转接、全景录像、座席管理的相关能力。

# 1. 前提条件

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

  • 已获取 App Key。

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

  • 集成 SDK(iOS)。

TIP

请扫描下方二维码联系 Juphoon 市场售前工程师获取 AppKey。

image-20210315095937896
  • 集成 SDK(iOS)。

    TIP

    如需集成指导,请扫描上方二维码联系 Juphoon 市场售前工程师获取相关咨询。

# 2. 快速跑通Sample

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

访问下载地址 (opens new window),示例如下:

image (68)

  1. 下载完成后,安装 Juphoon_Rtc_Sample_for_iOS_R22C03_CallCenter 目录下的 JCCSample.ipa

截屏2022-09-05 11.20.00.png

  1. 打开应用程序后,设置合适的 Appkey,即可体验 Sample 的功能。

  2. 操作步骤:

    a. 确认 AppKey 的正确性,输入账号,密码,服务器地址等信息;

    b. 点击 登录 按钮,待 登录 按钮变为 "登出" 时表示已经成功登录;

    c. 点击座席的右上角 进入 按钮,选择座席即可体验座席相关的功能。

    d. 进入座席页面,首先座席签入到排队机,签入按钮变为签出时表示签入成功,签入成功时自动设置为示闲状态;当有访客呼入时,座席即可接听或挂断当前通话,进行视频客服座席的基本业务流程。

IMG_4803.JPG

IMG_4801.JPG

IMG_4802.JPG

# 3. 实现视频通话

94decfc30b3e8159d5fb7400dfb854cf

# 初始化

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

初始化JRTCSDK需要创建 JRTCClient (opens new window)JRTCMediaDevice (opens new window)JRTCAgent (opens new window)的实例。

JRTCClient (opens new window) 登录模块 负责视频平台的登录登出,只有登录到视频平台才可以使用视频相关的业务
JRTCMediaDevice (opens new window) 媒体模块 负责本地的媒体设备操作,视频画面渲染等功能
JRTCAgent (opens new window) 座席模块 负责座席的视频业务的处理,比如接听来电,控制音视频流等

AppKey 作为同个环境的分域依据,同一个域的终端才能实现互通,AppKey 由 Juphoon 视频平台提供。 在使用业务接口前,需对 Juphoon SDK 进行初始化操作。 注:Guest 访客模块和 Agent 座席模块只支持初始化一个,否则会出现接收不到呼叫,接收不到邀请等现象影响正常的业务流程。

初始化参数详见 JRTCClientInitParam (opens new window)

示例代码:

@interface JCManager () <JRTCClientCallback, JRTCMediaDeviceCallback, JRTCAgentCallback>

- (void)initSDK {
	// 创建初始化参数
    JRTCClientInitParam *param = [[JRTCClientInitParam alloc] init];
    // 设置appkey
    param.appKey = @"AppKey"; 
    // 设置接入服务器地址
    param.server = @"Server"; 
    // 设置应用名
    param.appName = @"AppName";
    // 创建 JRTCClient 对象
    _client = [JRTCClient create:self initParam:param];
    // 创建 JRTCMediaDevice 对象
    _mediaDevice = [JRTCMediaDevice create:_client callback:self];
    // 创建 JRTCAgent 对象
    _agent = [JRTCAgent create:_client mediaDevice:_mediaDevice callback:self]; 
}

@end@interface JCManager () <JRTCClientCallback, JRTCMediaDeviceCallback, JRTCAgentCallback>

- (void)initSDK {
	// 创建初始化参数
    JRTCClientInitParam *param = [[JRTCClientInitParam alloc] init];
    // 设置appkey
    param.appKey = @"AppKey"; 
    // 设置接入服务器地址
    param.server = @"Server"; 
    // 设置应用名
    param.appName = @"AppName";
    // 创建 JRTCClient 对象
    _client = [JRTCClient create:self initParam:param];
    // 创建 JRTCMediaDevice 对象
    _mediaDevice = [JRTCMediaDevice create:_client callback:self];
    // 创建 JRTCAgent 对象
    _agent = [JRTCAgent create:_client mediaDevice:_mediaDevice callback:self]; 
}

@end

然后根据需求实现 Callback 的接口即可。

# 登录

SDK 初始化之后,即可进行登录的集成。登录接口调用流程如下所示: img 调用 login (opens new window) 接口登录到 Juphoon 视频平台。

/**
 * 登录 Juphoon RTC 平台,只有登录成功后才能进行平台上的各种业务
 * 登录结果通过 {@link JRTCClientCallback.onLogin:reason: onLogin} 回调通知
 *
 * @param userId 用户ID
 * @param password 密码,不能为空
 * @return 接口调用结果
 * - true: 接口调用成功
 * - false: 接口调用异常
 * @warning 目前只支持免鉴权模式,服务器不校验账号密码,免鉴权模式下当账号不存在时会自动去创建该账号
 * @warning 用户名为英文数字和'+' '-' '_' '.',长度不要超过64字符,'-' '_' '.'不能作为第一个字符
 */
- (bool)login:(NSString* _Nonnull)userId password:(NSString* _Nonnull)password;

/**
 * 登录 Juphoon RTC 平台,只有登录成功后才能进行平台上的各种业务
 * 登录结果通过 {@link JRTCClientCallback.onLogin:reason: onLogin} 回调通知
 *
 * @param userId 用户ID
 * @param password 密码,不能为空
 * @param loginParam      登录参数,一般不需要设置,如需设置请询问客服,传 nil 则按默认值,详见 @ref JRTCClientLoginParam
 * @return 接口调用结果
 * - true: 接口调用成功
 * - false: 接口调用异常
 * @warning 目前只支持免鉴权模式,服务器不校验账号密码,免鉴权模式下当账号不存在时会自动去创建该账号
 * @warning 用户名为英文数字和'+' '-' '_' '.',长度不要超过64字符,'-' '_' '.'不能作为第一个字符
 */
- (bool)login:(NSString* _Nonnull)userId password:(NSString* _Nonnull)password loginParam:(JRTCClientLoginParam* _Nullable)loginParam;

/**
 * 重登录,该接口在如果有其他同类型终端登录着则会登录失败,一般用于记住了账号后重启自动登录逻辑
 * @param password 密码,不能为空
 * @return 接口调用结果
 * - true: 接口调用成功
 * - false: 接口调用异常
 */
- (bool)relogin:(NSString* _Nonnull)userId password:(NSString* _Nonnull)password;/**
 * 登录 Juphoon RTC 平台,只有登录成功后才能进行平台上的各种业务
 * 登录结果通过 {@link JRTCClientCallback.onLogin:reason: onLogin} 回调通知
 *
 * @param userId 用户ID
 * @param password 密码,不能为空
 * @return 接口调用结果
 * - true: 接口调用成功
 * - false: 接口调用异常
 * @warning 目前只支持免鉴权模式,服务器不校验账号密码,免鉴权模式下当账号不存在时会自动去创建该账号
 * @warning 用户名为英文数字和'+' '-' '_' '.',长度不要超过64字符,'-' '_' '.'不能作为第一个字符
 */
- (bool)login:(NSString* _Nonnull)userId password:(NSString* _Nonnull)password;

/**
 * 登录 Juphoon RTC 平台,只有登录成功后才能进行平台上的各种业务
 * 登录结果通过 {@link JRTCClientCallback.onLogin:reason: onLogin} 回调通知
 *
 * @param userId 用户ID
 * @param password 密码,不能为空
 * @param loginParam      登录参数,一般不需要设置,如需设置请询问客服,传 nil 则按默认值,详见 @ref JRTCClientLoginParam
 * @return 接口调用结果
 * - true: 接口调用成功
 * - false: 接口调用异常
 * @warning 目前只支持免鉴权模式,服务器不校验账号密码,免鉴权模式下当账号不存在时会自动去创建该账号
 * @warning 用户名为英文数字和'+' '-' '_' '.',长度不要超过64字符,'-' '_' '.'不能作为第一个字符
 */
- (bool)login:(NSString* _Nonnull)userId password:(NSString* _Nonnull)password loginParam:(JRTCClientLoginParam* _Nullable)loginParam;

/**
 * 重登录,该接口在如果有其他同类型终端登录着则会登录失败,一般用于记住了账号后重启自动登录逻辑
 * @param password 密码,不能为空
 * @return 接口调用结果
 * - true: 接口调用成功
 * - false: 接口调用异常
 */
- (bool)relogin:(NSString* _Nonnull)userId password:(NSString* _Nonnull)password;

登录的结果将会通过 JRTCClientCallback (opens new window) 中的 onLogin (opens new window) 接口上报。

/**
 * 登录结果回调
 * @param result true 表示登录成功,false 表示登录失败
 * @param reason 当 result 为 false 时该值有效
 */
- (void)onLogin:(bool)result reason:(ReasonCode)reason;/**
 * 登录结果回调
 * @param result true 表示登录成功,false 表示登录失败
 * @param reason 当 result 为 false 时该值有效
 */
- (void)onLogin:(bool)result reason:(ReasonCode)reason;

示例代码:

// 创建登录配置参数
JRTCClientLoginParam *param = [[JRTCClientLoginParam alloc] init];
// 登录
[_client login:@"agent1" password:@"123456" loginParam:param];

// 登录结果回调
- (void)onLogin:(bool)result reason:(ReasonCode)reason {
    if (result == true) {
        // 登录成功   
    }
    else {
        // 登录失败,具体原因查询 reason 错误码
    }
}// 创建登录配置参数
JRTCClientLoginParam *param = [[JRTCClientLoginParam alloc] init];
// 登录
[_client login:@"agent1" password:@"123456" loginParam:param];

// 登录结果回调
- (void)onLogin:(bool)result reason:(ReasonCode)reason {
    if (result == true) {
        // 登录成功   
    }
    else {
        // 登录失败,具体原因查询 reason 错误码
    }
}

# 获取业务号列表

通过调用 queryAllGroups (opens new window) 接口获取当前业务号列表。

/**
 * 获取业务号列表
 *
 * @return 接口调用结果
 * - true: 接口调用成功
 * - false: 接口调用异常
 */
- (bool)queryAllGroups;/**
 * 获取业务号列表
 *
 * @return 接口调用结果
 * - true: 接口调用成功
 * - false: 接口调用异常
 */
- (bool)queryAllGroups;

业务组信息通过实现 JRTCAgentCallback (opens new window) 中的 onGetAllGroups (opens new window) 接口上报。

/**
 * 获取业务号列表结果回调
 * @param groups 坐席业务实体对象列表,获取失败时为 nil
 * @param result 获取结果
 * - true: 获取成功
 * - false: 获取失败
 */
- (void)onGetAllGroups:(NSArray <JRTCCallCenterGroupItem *> *)groups result:(bool)result;/**
 * 获取业务号列表结果回调
 * @param groups 坐席业务实体对象列表,获取失败时为 nil
 * @param result 获取结果
 * - true: 获取成功
 * - false: 获取失败
 */
- (void)onGetAllGroups:(NSArray <JRTCCallCenterGroupItem *> *)groups result:(bool)result;

示例代码:

// 获取业务号列表
[_agent queryAllGroups];

// 获取业务号列表结果回调
- (void)onGetAllGroups:(NSArray <JRTCCallCenterGroupItem *> *)groups result:(bool)result {
    if (result == true) {
        // 查询成功
    }
    else {
     	// 查询失败   
    }
}// 获取业务号列表
[_agent queryAllGroups];

// 获取业务号列表结果回调
- (void)onGetAllGroups:(NSArray <JRTCCallCenterGroupItem *> *)groups result:(bool)result {
    if (result == true) {
        // 查询成功
    }
    else {
     	// 查询失败   
    }
}

# 查询指定组号的空闲座席

调用 queryAvailableAgentList (opens new window) 接口查询指定组号的空闲座席。

/**
 * 查询指定组号的空闲座席
 *
 * 查询结果通过 {@link JRTCAgentCallback.onQueryAvailableAgentList:result:userIds: onQueryAvailableAgentList} 回调上报
 * @param businessNumber 业务号,例如 10087
 * @return 操作id,与 {@link JRTCAgentCallback.onQueryAvailableAgentList:result:userIds: onQueryAvailableAgentList} 的 operationId 参数对应
 */
- (int)queryAvailableAgentList:(NSString *)businessNumber;/**
 * 查询指定组号的空闲座席
 *
 * 查询结果通过 {@link JRTCAgentCallback.onQueryAvailableAgentList:result:userIds: onQueryAvailableAgentList} 回调上报
 * @param businessNumber 业务号,例如 10087
 * @return 操作id,与 {@link JRTCAgentCallback.onQueryAvailableAgentList:result:userIds: onQueryAvailableAgentList} 的 operationId 参数对应
 */
- (int)queryAvailableAgentList:(NSString *)businessNumber;

指定组号的空闲座席查询结果通过实现 JRTCAgentCallback (opens new window) 中的 onQueryAvailableAgentList (opens new window) 接口上报。

/**
 * 查询空闲座席回调
 *
 * 座席调用 {@link JRTCAgent.queryAvailableAgentList: queryAvailableAgentList} 接口查询空闲座席成功时,会收到此回调。
 * @param operationId 操作ID,对应 {@link JRTCAgent.queryAvailableAgentList: queryAvailableAgentList} 接口的返回值
 * @param result 查询结果
 * - true: 查询成功
 * - false: 查询失败
 * @param userIds 查询到的空闲座席列表
 */
- (void)onQueryAvailableAgentList:(int)operationId result:(bool)result userIds:(NSArray <NSString *> *)userIds;
/**
 * 查询空闲座席回调
 *
 * 座席调用 {@link JRTCAgent.queryAvailableAgentList: queryAvailableAgentList} 接口查询空闲座席成功时,会收到此回调。
 * @param operationId 操作ID,对应 {@link JRTCAgent.queryAvailableAgentList: queryAvailableAgentList} 接口的返回值
 * @param result 查询结果
 * - true: 查询成功
 * - false: 查询失败
 * @param userIds 查询到的空闲座席列表
 */
- (void)onQueryAvailableAgentList:(int)operationId result:(bool)result userIds:(NSArray <NSString *> *)userIds;

示例代码:

// 查询指定组号的空闲座席
[_agent queryAvailableAgentList:@"10087"];

// 查询空闲座席回调
- (void)onQueryAvailableAgentList:(int)operationId result:(bool)result userIds:(NSArray <NSString *> *)userIds {
 	if (result == true) {
        // 查询成功,具体查看 userIds
    } else {
     	// 查询失败   
    }
}// 查询指定组号的空闲座席
[_agent queryAvailableAgentList:@"10087"];

// 查询空闲座席回调
- (void)onQueryAvailableAgentList:(int)operationId result:(bool)result userIds:(NSArray <NSString *> *)userIds {
 	if (result == true) {
        // 查询成功,具体查看 userIds
    } else {
     	// 查询失败   
    }
}

# 座席签入

登录视频能力平台成功后,需要签入到排队机才能由排队机进行路由分配。 座席可调用的 checkIn (opens new window) 接口签入到排队机。

/**
 * 签入
 *
 * 签入到排队机,签入后默认示闲状态 <br>
 * 示闲状态下正常呼叫进线 <br>
 * 调用接口前确定 JRTCClient 登录成功 <br>
 * 调用该接口前需先确定 @ref operatorState 来获取到当前的签入状态
 * @param userId 座席用户ID,需与业务管理平台上配置的座席staffId对应
 * @param role 座席角色类型,目前该参数已有服务端控制,不再通过终端设置,可忽略
 * @return 接口调用结果
 * - true: 接口调用成功,签入结果会触发 {@link JRTCAgentCallback.onCheckinResult: onCheckinResult} 回调上报
 * - false: 接口调用异常
 */
- (bool)checkIn:(NSString *)userId role:(AgentRoleType)role;

/**
 * 签入
 *
 * 签入到排队机,签入后默认示闲状态 <br>
 * 示闲状态下正常呼叫进线 <br>
 * 调用接口前确定 JRTCClient 登录成功 <br>
 * 调用该接口前需先确定 @ref operatorState 来获取到当前的签入状态 <br>
 * 该方法座席用户ID默认为 {@link JRTCClient.userId userId} ,与业务管理平台上配置的座席staffId对应
 * @param role 座席角色类型,目前该参数已有服务端控制,不再通过终端设置,可忽略
 * @return 接口调用结果
 * - true: 接口调用成功,签入结果会触发 {@link JRTCAgentCallback.onCheckinResult: onCheckinResult} 回调上报
 * - false: 接口调用异常
 */
- (bool)checkIn:(AgentRoleType)role;

/**
 * 签入
 *
 * 签入到排队机,签入后示忙/示闲状态由 busy 参数决定 <br>
 * 示闲状态下正常呼叫进线 <br>
 * 调用接口前确定 JRTCClient 登录成功 <br>
 * 调用该接口前需先确定 @ref operatorState 来获取到当前的签入状态
 * @param userId 座席用户ID,需与业务管理平台上配置的座席staffId对应
 * @param role 座席角色类型,目前该参数已有服务端控制,不再通过终端设置,可忽略
 * @param busy 签入后示忙/示闲状态
 * - true: 示忙
 * - false: 示闲
 * @return 接口调用结果
 * - true: 接口调用成功,签入结果会触发 {@link JRTCAgentCallback.onCheckinResult: onCheckinResult} 回调上报
 * - false: 接口调用异常
 */
- (bool)checkIn:(NSString *)userId role:(AgentRoleType)role busy:(bool)busy;/**
 * 签入
 *
 * 签入到排队机,签入后默认示闲状态 <br>
 * 示闲状态下正常呼叫进线 <br>
 * 调用接口前确定 JRTCClient 登录成功 <br>
 * 调用该接口前需先确定 @ref operatorState 来获取到当前的签入状态
 * @param userId 座席用户ID,需与业务管理平台上配置的座席staffId对应
 * @param role 座席角色类型,目前该参数已有服务端控制,不再通过终端设置,可忽略
 * @return 接口调用结果
 * - true: 接口调用成功,签入结果会触发 {@link JRTCAgentCallback.onCheckinResult: onCheckinResult} 回调上报
 * - false: 接口调用异常
 */
- (bool)checkIn:(NSString *)userId role:(AgentRoleType)role;

/**
 * 签入
 *
 * 签入到排队机,签入后默认示闲状态 <br>
 * 示闲状态下正常呼叫进线 <br>
 * 调用接口前确定 JRTCClient 登录成功 <br>
 * 调用该接口前需先确定 @ref operatorState 来获取到当前的签入状态 <br>
 * 该方法座席用户ID默认为 {@link JRTCClient.userId userId} ,与业务管理平台上配置的座席staffId对应
 * @param role 座席角色类型,目前该参数已有服务端控制,不再通过终端设置,可忽略
 * @return 接口调用结果
 * - true: 接口调用成功,签入结果会触发 {@link JRTCAgentCallback.onCheckinResult: onCheckinResult} 回调上报
 * - false: 接口调用异常
 */
- (bool)checkIn:(AgentRoleType)role;

/**
 * 签入
 *
 * 签入到排队机,签入后示忙/示闲状态由 busy 参数决定 <br>
 * 示闲状态下正常呼叫进线 <br>
 * 调用接口前确定 JRTCClient 登录成功 <br>
 * 调用该接口前需先确定 @ref operatorState 来获取到当前的签入状态
 * @param userId 座席用户ID,需与业务管理平台上配置的座席staffId对应
 * @param role 座席角色类型,目前该参数已有服务端控制,不再通过终端设置,可忽略
 * @param busy 签入后示忙/示闲状态
 * - true: 示忙
 * - false: 示闲
 * @return 接口调用结果
 * - true: 接口调用成功,签入结果会触发 {@link JRTCAgentCallback.onCheckinResult: onCheckinResult} 回调上报
 * - false: 接口调用异常
 */
- (bool)checkIn:(NSString *)userId role:(AgentRoleType)role busy:(bool)busy;

checkIn (opens new window) 接口包含几个重载,用来适应各种可配置的参数,比如签入后默认为示闲还是示忙。这些接口的使用结合具体的需求来使用。

签入的结果将会通过实现 JRTCAgentCallback (opens new window) 中的 onCheckIn (opens new window) 接口上报。

/**
 * 签入回调
 *
 * 座席调用 {@link JRTCAgent.checkIn:role:busy: checkIn} 接口成功,会收到此回调。
 * @param result 签入结果
 * - true: 签入成功
 * - false: 签入失败
 * @param pause 签入后的示闲/示忙状态
 * - true: 签入后示忙
 * - false: 签入后示闲
 * @param onlineTime 座席累计在线时长
 * @param breakTime 座席累计示忙时长
 * @param callTimes 通话次数
 * @param reason 签入失败原因
 */
- (void)onCheckIn:(bool)result pause:(bool)pause onlineTime:(long)onlineTime breakTime:(long)breakTime callTimes:(long)callTimes reason:(int)reason;/**
 * 签入回调
 *
 * 座席调用 {@link JRTCAgent.checkIn:role:busy: checkIn} 接口成功,会收到此回调。
 * @param result 签入结果
 * - true: 签入成功
 * - false: 签入失败
 * @param pause 签入后的示闲/示忙状态
 * - true: 签入后示忙
 * - false: 签入后示闲
 * @param onlineTime 座席累计在线时长
 * @param breakTime 座席累计示忙时长
 * @param callTimes 通话次数
 * @param reason 签入失败原因
 */
- (void)onCheckIn:(bool)result pause:(bool)pause onlineTime:(long)onlineTime breakTime:(long)breakTime callTimes:(long)callTimes reason:(int)reason;

示例代码:

// 签入,签入后为示闲状态
[_agent checkIn:_client.userId role:0 busy:false];

// 签入结果回调
- (void)onCheckIn:(bool)result pause:(bool)pause onlineTime:(long)onlineTime breakTime:(long)breakTime callTimes:(long)callTimes reason:(int)reason {
    if (result == true) {
        NSLog(@"签入成功,签入后状态为 %@", pause == true ? @"示忙" : @"示闲");   
    } else {
        NSLog(@"签入失败,失败原因为 %d", reason);
    }
}// 签入,签入后为示闲状态
[_agent checkIn:_client.userId role:0 busy:false];

// 签入结果回调
- (void)onCheckIn:(bool)result pause:(bool)pause onlineTime:(long)onlineTime breakTime:(long)breakTime callTimes:(long)callTimes reason:(int)reason {
    if (result == true) {
        NSLog(@"签入成功,签入后状态为 %@", pause == true ? @"示忙" : @"示闲");   
    } else {
        NSLog(@"签入失败,失败原因为 %d", reason);
    }
}

# 座席签出

当所有业务处理完毕,座席可以使用 Agent 对象中的 checkout (opens new window) 签出后排队机将不会分发访客到这个座席终端,业务管理平台上该座席的状态将变为离线。

/**
* 签出
*
* 签出后将不会收到排队机的呼叫分配 <br>
* 签出排队机不影响 JRTCClient 的登录状态 <br>
* 调用 @ref operatorState 可获取当前签入状态
* @return 接口调用结果
* - true: 接口调用成功,签出结果通过 {@link JRTCAgentCallback.onCheckoutResult:reason: onCheckoutResult} 回调上报
* - false: 接口调用异常
*/
- (bool)checkout;/**
* 签出
*
* 签出后将不会收到排队机的呼叫分配 <br>
* 签出排队机不影响 JRTCClient 的登录状态 <br>
* 调用 @ref operatorState 可获取当前签入状态
* @return 接口调用结果
* - true: 接口调用成功,签出结果通过 {@link JRTCAgentCallback.onCheckoutResult:reason: onCheckoutResult} 回调上报
* - false: 接口调用异常
*/
- (bool)checkout;

签出结果通过 JRTCAgentCallback (opens new window)onCheckout (opens new window) 上报。

/**
* 签出回调
*
* 座席调用 {@link JRTCAgent.checkout checkout} 接口签出排队机,会收到此回调。
* @param result 签出结果
* - true: 签出成功
* - false: 签出失败
* @param reason 签出原因
*/
- (void)onCheckout:(bool)result reason:(AgentCheckoutReason)reason;/**
* 签出回调
*
* 座席调用 {@link JRTCAgent.checkout checkout} 接口签出排队机,会收到此回调。
* @param result 签出结果
* - true: 签出成功
* - false: 签出失败
* @param reason 签出原因
*/
- (void)onCheckout:(bool)result reason:(AgentCheckoutReason)reason;

座席签出原因详见 AgentCheckoutReason (opens new window)

示例代码:

// 签出
[_agent checkout];

// 签出结果回调
- (void)onCheckout:(bool)result reason:(AgentCheckoutReason)reason {
    if (result == true) {
        // 签出成功,签出原因查看 reason   
    } else {
        // 签出失败   
    }
}// 签出
[_agent checkout];

// 签出结果回调
- (void)onCheckout:(bool)result reason:(AgentCheckoutReason)reason {
    if (result == true) {
        // 签出成功,签出原因查看 reason   
    } else {
        // 签出失败   
    }
}

# 座席签入签出状态改变

座席签入状态发生变化通过 JRTCAgentCallback (opens new window)onCheckStateChanged (opens new window) 接口上报。

/**
 * 签入/签出状态改变
 *
 * 座席签入/签出状态发生改变时,会收到此回调,例如,调用签入/签出接口。
 * @param currentState 当前座席状态
 * @param oldState 旧的座席状态
 */
- (void)onCheckStateChanged:(AgentOperatorState)currentState oldState:(AgentOperatorState)oldState;/**
 * 签入/签出状态改变
 *
 * 座席签入/签出状态发生改变时,会收到此回调,例如,调用签入/签出接口。
 * @param currentState 当前座席状态
 * @param oldState 旧的座席状态
 */
- (void)onCheckStateChanged:(AgentOperatorState)currentState oldState:(AgentOperatorState)oldState;

座席状态详见 AgentOperatorState (opens new window)

- (void)onCheckStateChanged:(AgentOperatorState)currentState oldState:(AgentOperatorState)oldState {
    if (currentState == AgentOperatorStateCheckining) { 
        //正在签入中
    } else if (currentState == AgentOperatorStateCheckinOK) {
        //已签入
    } else if (currentState == AgentOperatorStateCheckouting) {
        //签出中
    } else if (currentState == AgentOperatorStateCheckinIdle) {
        //签入失败
    } 
}

- (void)onCheckStateChanged:(AgentOperatorState)currentState oldState:(AgentOperatorState)oldState {
    if (currentState == AgentOperatorStateCheckining) { 
        //正在签入中
    } else if (currentState == AgentOperatorStateCheckinOK) {
        //已签入
    } else if (currentState == AgentOperatorStateCheckouting) {
        //签出中
    } else if (currentState == AgentOperatorStateCheckinIdle) {
        //签入失败
    } 
}

# 获取座席签入状态

通过 operatorState (opens new window) 获取座席签入状态。

/**
 * 座席签入状态
 */
@property (nonatomic, readonly, assign) AgentOperatorState operatorState;/**
 * 座席签入状态
 */
@property (nonatomic, readonly, assign) AgentOperatorState operatorState;

# 示忙示闲

通过调用 JRTCAgent 对象的 applyStatePause (opens new window) 方法来切换该状态。

  • 示忙:座席暂停服务,访客无法呼到该座席
  • 示闲:座席保持服务,访客可以呼到该座席
/**
 * 示忙/示闲
 *
 * 示忙状态下不会收到呼叫来电,示闲状态下正常呼叫进线 <br>
 * 通话过程中调用该接口不会影响当前通话,从下个通话开始状态生效 <br>
 * 示忙/示闲的结果通过 {@link JRTCAgentCallback.onApplyResult:result: onApplyResult} 回调上报
 * @param pause 示忙或是示闲
 * - true: 示忙
 * - false: 示闲
 * @return 接口调用结果
 * - > 0: 操作id,对应 {@link JRTCAgentCallback.onApplyResult:result: onApplyResult} 回调的 operationId 参数
 * - -1:接口调用异常
 */
- (int)applyStatePause:(bool)pause;/**
 * 示忙/示闲
 *
 * 示忙状态下不会收到呼叫来电,示闲状态下正常呼叫进线 <br>
 * 通话过程中调用该接口不会影响当前通话,从下个通话开始状态生效 <br>
 * 示忙/示闲的结果通过 {@link JRTCAgentCallback.onApplyResult:result: onApplyResult} 回调上报
 * @param pause 示忙或是示闲
 * - true: 示忙
 * - false: 示闲
 * @return 接口调用结果
 * - > 0: 操作id,对应 {@link JRTCAgentCallback.onApplyResult:result: onApplyResult} 回调的 operationId 参数
 * - -1:接口调用异常
 */
- (int)applyStatePause:(bool)pause;

示忙示闲结果通过实现 JRTCAgentCallback (opens new window) 中的 onApplyResult (opens new window) 接口上报。

/**
 * 示忙/示闲回调
 *
 * 座席示忙/示闲状态发生改变时,会收到此回调,例如调用 {@link JRTCAgent.applyStatePause: applyStatePause} 接口修改忙闲状态。 <br>
 * 可以通过 {@link JRTCAgent.pause pause} 获取当前的忙闲状态。
 * @param operationId  对应 {@link JRTCAgent.applyStatePause: applyStatePause} 接口的返回值
 * @param result 示忙/示闲操作结果
 * - true: 示忙/示闲成功
 * - false: 示忙/示闲失败
 */
- (void)onApplyResult:(int)operationId result:(bool)result;/**
 * 示忙/示闲回调
 *
 * 座席示忙/示闲状态发生改变时,会收到此回调,例如调用 {@link JRTCAgent.applyStatePause: applyStatePause} 接口修改忙闲状态。 <br>
 * 可以通过 {@link JRTCAgent.pause pause} 获取当前的忙闲状态。
 * @param operationId  对应 {@link JRTCAgent.applyStatePause: applyStatePause} 接口的返回值
 * @param result 示忙/示闲操作结果
 * - true: 示忙/示闲成功
 * - false: 示忙/示闲失败
 */
- (void)onApplyResult:(int)operationId result:(bool)result;

可通过 termState (opens new window) 属性设置通话结束后设置示闲示忙。

/**
* 结束通话后的示闲/示忙状态,默认为示闲状态
*
* 结束通话后,默认为示闲状态,可由上层设置
*/
@property (nonatomic, assign) AgentTermState termState;/**
* 结束通话后的示闲/示忙状态,默认为示闲状态
*
* 结束通话后,默认为示闲状态,可由上层设置
*/
@property (nonatomic, assign) AgentTermState termState;

可通过 pause (opens new window) 查询示闲示忙的状态。

/**
* 示忙/示闲状态
*/
@property (nonatomic, readonly, assign) bool pause;/**
* 示忙/示闲状态
*/
@property (nonatomic, readonly, assign) bool pause;

示例代码:

// 示忙
[_agent applyStatePause:true];

// 示闲
[_agent applyStatePause:false];

// 示闲/示忙结果回调
- (void)onApplyResult:(int)operationId result:(bool)result {
    if (result == true) {
        NSLog(@"示忙/示闲操作成功");   
    } else {
        NSLog(@"示忙/示闲操作失败");
    }
}

// 设置签出后示忙/示闲状态
_agent.termState = AgentTermStateBusy;

// 获取当前示闲/示忙状态
bool pause = _agent.pause;
if (pause == true) {
    NSLog(@"当前为示忙状态");   
} else {
    NSLog(@"当前为示闲状态"); 
}// 示忙
[_agent applyStatePause:true];

// 示闲
[_agent applyStatePause:false];

// 示闲/示忙结果回调
- (void)onApplyResult:(int)operationId result:(bool)result {
    if (result == true) {
        NSLog(@"示忙/示闲操作成功");   
    } else {
        NSLog(@"示忙/示闲操作失败");
    }
}

// 设置签出后示忙/示闲状态
_agent.termState = AgentTermStateBusy;

// 获取当前示闲/示忙状态
bool pause = _agent.pause;
if (pause == true) {
    NSLog(@"当前为示忙状态");   
} else {
    NSLog(@"当前为示闲状态"); 
}

# 座席回呼

座席可以通过调用 recall (opens new window) 接口回呼访客。

/**
 * 座席回呼
 *
 * @param userId 访客用户ID
 * @param callParam 呼叫参数设置,此参数可传 nil 则使用默认配置,详见 {@link JRTCallCenterCallParam}
 * @return 接口调用结果
 * - true: 接口调用成功,通话状态会通过 {@link JRTCAgentCallback#onCallStateChanged onCallStateChanged} 回调上报
 * - false: 接口调用异常
 */
- (bool)recall:(NSString *)userId callParam:(JRTCCallCenterCallParam *)param;/**
 * 座席回呼
 *
 * @param userId 访客用户ID
 * @param callParam 呼叫参数设置,此参数可传 nil 则使用默认配置,详见 {@link JRTCallCenterCallParam}
 * @return 接口调用结果
 * - true: 接口调用成功,通话状态会通过 {@link JRTCAgentCallback#onCallStateChanged onCallStateChanged} 回调上报
 * - false: 接口调用异常
 */
- (bool)recall:(NSString *)userId callParam:(JRTCCallCenterCallParam *)param;

示例代码:

JRTCCallCenterCallParam *callParam = [JRTCCallCenterCallParam new];
// callParam参数设置
...

[agent recall:userId callParam:callParam];JRTCCallCenterCallParam *callParam = [JRTCCallCenterCallParam new];
// callParam参数设置
...

[agent recall:userId callParam:callParam];

# 接听来电

在座席收到来电的通知后,可以通过调用 answer (opens new window) 接口来接起访客的呼叫。

来电类型详见 CallIncomingType (opens new window)

CallIncomingTypeCall (opens new window):普通来电,访客调用 call (opens new window) 接口呼叫;

CallIncomingTypeInvite (opens new window):三方邀请来电,座席调用 inviteThirdAgent (opens new window) 接口邀请三方座席;

CallIncomingTypeForward (opens new window):转接来电,座席调用 transferCall (opens new window) 接口转接当前通话;

CallIncomingTypeDirectCall (opens new window):直呼来电,访客调用 oneToOneCall (opens new window) 接口进行直呼座席;

/**
 * 接听通话
 *
 * 主座席、第三方座席或是被转接的座席,都调用此接口接听通话
 * @return 接口调用结果
 * - true: 接口调用成功,会收到 {@link JRTCAgentCallback.onCallStateChanged:incomingType:inviter:reason: onCallStateChanged} 回调
 * - false: 接口调用异常
 */
- (bool)answer;/**
 * 接听通话
 *
 * 主座席、第三方座席或是被转接的座席,都调用此接口接听通话
 * @return 接口调用结果
 * - true: 接口调用成功,会收到 {@link JRTCAgentCallback.onCallStateChanged:incomingType:inviter:reason: onCallStateChanged} 回调
 * - false: 接口调用异常
 */
- (bool)answer;

示例代码:

- (void)onCallStateChanged:(AgentCallStateChangeType)type incomingType:(CallIncomingType)incomingType inviter:(nullable JRTCInviter *)inviter reason:(CallTermReason)reason {    
    if (type == AgentCallStateChangeTypeIncoming) {
        if (incomingType == CallIncomingTypeCall) {
            // 普通呼叫来电
        } else if (incomingType == CallIncomingTypeInvite) {
            // 三方邀请来电
        } else if (incomingType == CallIncomingTypeInvite) {
            // 转接来电
        } else if (incomingType == CallIncomingTypeInvite) {
            // 直呼来电
        }
        // 接听
        [_agent answer];
    }
}- (void)onCallStateChanged:(AgentCallStateChangeType)type incomingType:(CallIncomingType)incomingType inviter:(nullable JRTCInviter *)inviter reason:(CallTermReason)reason {    
    if (type == AgentCallStateChangeTypeIncoming) {
        if (incomingType == CallIncomingTypeCall) {
            // 普通呼叫来电
        } else if (incomingType == CallIncomingTypeInvite) {
            // 三方邀请来电
        } else if (incomingType == CallIncomingTypeInvite) {
            // 转接来电
        } else if (incomingType == CallIncomingTypeInvite) {
            // 直呼来电
        }
        // 接听
        [_agent answer];
    }
}

接听的结果同样通过 JRTCAgentCallback (opens new window)onCallStateChanged (opens new window) 接口上报。

# 通话状态改变通知

座席签入成功后即为一个等待通话接入的状态,具体的通话由排队机进分配。

  • 访客呼叫场景:
    1. 座席收到来电,通话状态就改变为 AgentCallStateChangeTypeIncoming (opens new window) ;
    2. 座席接听,成功进入通话,通话状态改变为 AgentCallStateChangeTypeTalking (opens new window)
    3. 座席挂断或者其他成员挂断导致通话结束,通话状态改变为 AgentCallStateChangeTypeTermed (opens new window)

均通过实现 JRTCAgentCallback (opens new window) 中的 onCallStateChanged (opens new window) 的接口上报。

/**
 * 通话状态改变回调
 * @param type 通话状态改变类型,即以下情况会收到此回调:
 * - @ref AgentCallStateChangeTypeIncoming 收到来电(访客呼叫、座席邀请、转接)
 * - @ref AgentCallStateChangeTypeTalking 通话接通
 * - @ref AgentCallStateChangeTypeTermed 通话挂断或被挂断
 * @param incomingType 来电类型,当 type == {@link AgentCallStateChangeTypeIncoming} 时有效
 * @param inviter 邀请成员对象,当 type == {@link AgentCallStateChangeTypeIncoming} 时有效
 * @param reason 挂断原因,只在 type 为 @ref AgentCallStateChangeTypeTermed 时需要关注,详见 @ref CallTermReason
 */
- (void)onCallStateChanged:(AgentCallStateChangeType)type incomingType:(CallIncomingType)incomingType inviter:(nullable JRTCInviter *)inviter reason:(CallTermReason)reason;/**
 * 通话状态改变回调
 * @param type 通话状态改变类型,即以下情况会收到此回调:
 * - @ref AgentCallStateChangeTypeIncoming 收到来电(访客呼叫、座席邀请、转接)
 * - @ref AgentCallStateChangeTypeTalking 通话接通
 * - @ref AgentCallStateChangeTypeTermed 通话挂断或被挂断
 * @param incomingType 来电类型,当 type == {@link AgentCallStateChangeTypeIncoming} 时有效
 * @param inviter 邀请成员对象,当 type == {@link AgentCallStateChangeTypeIncoming} 时有效
 * @param reason 挂断原因,只在 type 为 @ref AgentCallStateChangeTypeTermed 时需要关注,详见 @ref CallTermReason
 */
- (void)onCallStateChanged:(AgentCallStateChangeType)type incomingType:(CallIncomingType)incomingType inviter:(nullable JRTCInviter *)inviter reason:(CallTermReason)reason;

通话状态详见 AgentCallStateChangeType (opens new window) 。 通话结束原因详见 CallTermReason (opens new window)

示例代码:

- (void)onCallStateChanged:(AgentCallStateChangeType)type incomingType:(CallIncomingType)incomingType inviter:(nullable JRTCInviter *)inviter reason:(CallTermReason)reason {
    if (type == AgentCallStateChangeTypeIncoming) {
        // 收到来电   
    } else if (type == AgentCallStateChangeTypeTalking) {
        // 通话建立   
    } else if (type == AgentCallStateChangeTypeTermed) {
        // 通话挂断   
    } else if (type == AgentCallStateChangeTypeCalling) {
        // 呼叫(坐席回呼)
    }        
}- (void)onCallStateChanged:(AgentCallStateChangeType)type incomingType:(CallIncomingType)incomingType inviter:(nullable JRTCInviter *)inviter reason:(CallTermReason)reason {
    if (type == AgentCallStateChangeTypeIncoming) {
        // 收到来电   
    } else if (type == AgentCallStateChangeTypeTalking) {
        // 通话建立   
    } else if (type == AgentCallStateChangeTypeTermed) {
        // 通话挂断   
    } else if (type == AgentCallStateChangeTypeCalling) {
        // 呼叫(坐席回呼)
    }        
}

# 获取通话状态

可通过调用 callState (opens new window) 方法获取当前通话状态,通话状态改变详见 CallState (opens new window)

/**
 * 当前通话状态
 */
@property (nonatomic, readonly, assign) CallState callState;/**
 * 当前通话状态
 */
@property (nonatomic, readonly, assign) CallState callState;

示例代码:

CallState callState = _agent.callState;
if (callState == CallStateIdle) {
    // 空闲
} else if (callState == CallStateCalling) {
    // 呼叫中(回呼场景)
} else if (callState == CallStateIncoming) {
    // 来电
} else if (callState == CallStateTalking) {
    // 通话中
}CallState callState = _agent.callState;
if (callState == CallStateIdle) {
    // 空闲
} else if (callState == CallStateCalling) {
    // 呼叫中(回呼场景)
} else if (callState == CallStateIncoming) {
    // 来电
} else if (callState == CallStateTalking) {
    // 通话中
}

# 创建本地视频画面

初始化成功后,可以创建本地的视频画面,创建本地视频画面的时机没有具体要求,在通话前通话中皆可。

  1. 通过 startCameraVideo (opens new window)方法,获取 JRTCMediaDeviceVideoCanvas (opens new window)本地的视频对象。
  2. 在界面画布上渲染本地视频对象画面。
/**
 * 开始自身视频渲染
 *
 * 获取本端视频预览对象 JRTCMediaDeviceVideoCanvas ,通过此对象能获得视图用于UI显示
 * @note
 * 调用此方法时需要保证默认摄像头不为空,即 @ref defaultCamera 不为空,否则将直接返回 nil
 * @param type 渲染模式:
 * - @ref JRTCMediaDeviceRenderFullScreen : 铺满窗口,会有裁剪
 * - @ref JRTCMediaDeviceRenderFullContent : 全图像显示,会有黑边
 * - @ref JRTCMediaDeviceRenderFullAuto : 自适应
 * @return
 * - JRTCMediaDeviceVideoCanvas 对象: 开始自身视频渲染成功
 * - nil: 开始自身视频渲染失败
 */
- (JRTCMediaDeviceVideoCanvas* __nullable)startCameraVideo:(JRTCMediaDeviceRender)type;

/**
 * 开始自身视频渲染
 *
 * 获取本端视频预览对象 JRTCMediaDeviceVideoCanvas ,通过此对象能获得视图用于UI显示
 * @note
 * 调用此方法时需要保证默认摄像头不为空,即 @ref defaultCamera 不为空,否则将直接返回 nil
 * @param type 渲染模式:
 * - @ref JRTCMediaDeviceRenderFullScreen : 铺满窗口,会有裁剪
 * - @ref JRTCMediaDeviceRenderFullContent : 全图像显示,会有黑边
 * - @ref JRTCMediaDeviceRenderFullAuto : 自适应
 * @param view 渲染视图控件
 * @return
 * - JRTCMediaDeviceVideoCanvas 对象: 开始自身视频渲染成功
 * - nil: 开始自身视频渲染失败
 */
- (JRTCMediaDeviceVideoCanvas* __nullable)startCameraVideo:(JRTCMediaDeviceRender)type view:(JCView* __nonnull)view;/**
 * 开始自身视频渲染
 *
 * 获取本端视频预览对象 JRTCMediaDeviceVideoCanvas ,通过此对象能获得视图用于UI显示
 * @note
 * 调用此方法时需要保证默认摄像头不为空,即 @ref defaultCamera 不为空,否则将直接返回 nil
 * @param type 渲染模式:
 * - @ref JRTCMediaDeviceRenderFullScreen : 铺满窗口,会有裁剪
 * - @ref JRTCMediaDeviceRenderFullContent : 全图像显示,会有黑边
 * - @ref JRTCMediaDeviceRenderFullAuto : 自适应
 * @return
 * - JRTCMediaDeviceVideoCanvas 对象: 开始自身视频渲染成功
 * - nil: 开始自身视频渲染失败
 */
- (JRTCMediaDeviceVideoCanvas* __nullable)startCameraVideo:(JRTCMediaDeviceRender)type;

/**
 * 开始自身视频渲染
 *
 * 获取本端视频预览对象 JRTCMediaDeviceVideoCanvas ,通过此对象能获得视图用于UI显示
 * @note
 * 调用此方法时需要保证默认摄像头不为空,即 @ref defaultCamera 不为空,否则将直接返回 nil
 * @param type 渲染模式:
 * - @ref JRTCMediaDeviceRenderFullScreen : 铺满窗口,会有裁剪
 * - @ref JRTCMediaDeviceRenderFullContent : 全图像显示,会有黑边
 * - @ref JRTCMediaDeviceRenderFullAuto : 自适应
 * @param view 渲染视图控件
 * @return
 * - JRTCMediaDeviceVideoCanvas 对象: 开始自身视频渲染成功
 * - nil: 开始自身视频渲染失败
 */
- (JRTCMediaDeviceVideoCanvas* __nullable)startCameraVideo:(JRTCMediaDeviceRender)type view:(JCView* __nonnull)view;

JRTCMediaDeviceRender (opens new window) 决定了视频的渲染模式:

  • FullScreen 为填充模式:即将画面内容居中等比缩放以充满整个显示区域,超出显示区域的部分将会被裁剪掉,此模式下画面可能不完整;
  • FullContent 为适应模式:即按画面长边进行缩放以适应显示区域,短边部分会被填充为黑色,此模式下图像完整但可能留有黑边。

img

示例代码:

// 创建本端视图渲染对象
JRTCMediaDeviceVideoCanvas *canvas = [_mediaDevice startCameraVideo:JRTCMediaDeviceRenderFullContent];
// 设置视图位置与尺寸
canvas.videoView.frame = CGRectMake(0, 0, 100, 100);
// 将渲染视图添加到界面上
[self.view addSubview:canvas.videoView];// 创建本端视图渲染对象
JRTCMediaDeviceVideoCanvas *canvas = [_mediaDevice startCameraVideo:JRTCMediaDeviceRenderFullContent];
// 设置视图位置与尺寸
canvas.videoView.frame = CGRectMake(0, 0, 100, 100);
// 将渲染视图添加到界面上
[self.view addSubview:canvas.videoView];

# 创建远端视频画面

当通话建立后,除了本地的视频画面,还有通话成员的远端视频画面,如果通话成员有视频流的上传,座席端可以获取到其他成员的视频流并进行渲染。 座席可在收到以下回调时进行界面处理:

调用 startVideo (opens new window) 接口在界面画布上渲染远端视频对象画面。

/**
 * 开始其他端的视频渲染
 *
 * 获取其他端的视频预览对象 JRTCMediaDeviceVideoCanvas ,通过此对象能获得视图用于UI显示
 *
 * @param streamId 视频流ID
 * @param type  渲染模式:
 * - @ref JRTCMediaDeviceRenderFullScreen : 铺满窗口,会有裁剪
 * - @ref JRTCMediaDeviceRenderFullContent : 全图像显示,会有黑边
 * - @ref JRTCMediaDeviceRenderFullAuto : 自适应
 * @return
 * - JRTCMediaDeviceVideoCanvas 对象: 开始其他端视频渲染成功
 * - nil: 开始其他端视频渲染失败
 */
- (JRTCMediaDeviceVideoCanvas* __nullable)startVideo:(NSString* __nonnull)streamId renderType:(JRTCMediaDeviceRender)type;

/**
 * 开始其他端的视频渲染
 *
 * 获取其他端的视频预览对象 JRTCMediaDeviceVideoCanvas ,通过此对象能获得视图用于UI显示
 *
 * @param streamId 视频流ID
 * @param type  渲染模式:
 * - @ref JRTCMediaDeviceRenderFullScreen : 铺满窗口,会有裁剪
 * - @ref JRTCMediaDeviceRenderFullContent : 全图像显示,会有黑边
 * - @ref JRTCMediaDeviceRenderFullAuto : 自适应
 * @param view 渲染视图控件
 * @return
 * - JRTCMediaDeviceVideoCanvas 对象: 开始其他端视频渲染成功
 * - nil: 开始其他端视频渲染失败
 */
- (JRTCMediaDeviceVideoCanvas* __nullable)startVideo:(NSString* __nonnull)streamId renderType:(JRTCMediaDeviceRender)type view:(JCView* __nonnull)view;/**
 * 开始其他端的视频渲染
 *
 * 获取其他端的视频预览对象 JRTCMediaDeviceVideoCanvas ,通过此对象能获得视图用于UI显示
 *
 * @param streamId 视频流ID
 * @param type  渲染模式:
 * - @ref JRTCMediaDeviceRenderFullScreen : 铺满窗口,会有裁剪
 * - @ref JRTCMediaDeviceRenderFullContent : 全图像显示,会有黑边
 * - @ref JRTCMediaDeviceRenderFullAuto : 自适应
 * @return
 * - JRTCMediaDeviceVideoCanvas 对象: 开始其他端视频渲染成功
 * - nil: 开始其他端视频渲染失败
 */
- (JRTCMediaDeviceVideoCanvas* __nullable)startVideo:(NSString* __nonnull)streamId renderType:(JRTCMediaDeviceRender)type;

/**
 * 开始其他端的视频渲染
 *
 * 获取其他端的视频预览对象 JRTCMediaDeviceVideoCanvas ,通过此对象能获得视图用于UI显示
 *
 * @param streamId 视频流ID
 * @param type  渲染模式:
 * - @ref JRTCMediaDeviceRenderFullScreen : 铺满窗口,会有裁剪
 * - @ref JRTCMediaDeviceRenderFullContent : 全图像显示,会有黑边
 * - @ref JRTCMediaDeviceRenderFullAuto : 自适应
 * @param view 渲染视图控件
 * @return
 * - JRTCMediaDeviceVideoCanvas 对象: 开始其他端视频渲染成功
 * - nil: 开始其他端视频渲染失败
 */
- (JRTCMediaDeviceVideoCanvas* __nullable)startVideo:(NSString* __nonnull)streamId renderType:(JRTCMediaDeviceRender)type view:(JCView* __nonnull)view;

渲染其他用户视图画面的方法法与渲染摄像头画面的用法基本一致,需关注 JRTCMediaDeviceRender (opens new window) 渲染模式。

示例代码:

// 创建其他端视图渲染对象
JRTCMediaDeviceVideoCanvas *canvas = [_mediaDevice startVideo:@"视频流ID" renderType:JRTCMediaDeviceRenderFullContent];
// 设置视图位置与尺寸
canvas.videoView.frame = CGRectMake(0, 0, 100, 100);
// 将渲染视图添加到界面上
[self.view addSubview:canvas.videoView];// 创建其他端视图渲染对象
JRTCMediaDeviceVideoCanvas *canvas = [_mediaDevice startVideo:@"视频流ID" renderType:JRTCMediaDeviceRenderFullContent];
// 设置视图位置与尺寸
canvas.videoView.frame = CGRectMake(0, 0, 100, 100);
// 将渲染视图添加到界面上
[self.view addSubview:canvas.videoView];

# 结束通话

当业务办理结束,访客可以主动结束通话,座席也可以通过 term (opens new window) 方法来主动结束通话。

/**
 * 结束通话
 *
 * 来电过程中调用此接口拒绝接听,访客分配到其他座席继续呼叫等待不会挂断。<br>
 * 主座席调用此接口会结束通话,通话中所有成员都会离开,此通通话销毁,所有成员会收到 {@link JRTCAgentCallback.onCallStateChanged:incomingType:inviter:reason: onCallStateChanged} 或 {@link JRTCGuestCallback.onCallStateChanged:incomingType:inviter:termReason: onCallStateChanged} 通话结束回调。<br>
 * 第三方座席调用此接口仅自身离开通话,通话中其他成员会收到该成员离开的回调 {@link JRTCGuestCallback.onMemberLeave: onMemberLeave} 或 {@link JRTCAgentCallback.onMemberLeave: onMemberLeave} 回调,通话继续进行。
 * @return 接口调用结果
 * - true: 接口调用成功,会收到 {@link JRTCAgentCallback.onCallStateChanged:incomingType:inviter:reason: onCallStateChanged} 回调
 * - false: 接口调用异常
 */
- (bool)term;/**
 * 结束通话
 *
 * 来电过程中调用此接口拒绝接听,访客分配到其他座席继续呼叫等待不会挂断。<br>
 * 主座席调用此接口会结束通话,通话中所有成员都会离开,此通通话销毁,所有成员会收到 {@link JRTCAgentCallback.onCallStateChanged:incomingType:inviter:reason: onCallStateChanged} 或 {@link JRTCGuestCallback.onCallStateChanged:incomingType:inviter:termReason: onCallStateChanged} 通话结束回调。<br>
 * 第三方座席调用此接口仅自身离开通话,通话中其他成员会收到该成员离开的回调 {@link JRTCGuestCallback.onMemberLeave: onMemberLeave} 或 {@link JRTCAgentCallback.onMemberLeave: onMemberLeave} 回调,通话继续进行。
 * @return 接口调用结果
 * - true: 接口调用成功,会收到 {@link JRTCAgentCallback.onCallStateChanged:incomingType:inviter:reason: onCallStateChanged} 回调
 * - false: 接口调用异常
 */
- (bool)term;

主动和被动的挂断事件与来电、接通一样,都通过 JRTCAgentCallback (opens new window)onCallStateChanged (opens new window) 接口上报。 其中 CallTermReason (opens new window) 可以用来判断挂断原因,如:主动挂断、对端挂断等。

示例代码:

// 坐席主动结束通话
[_agent term];

// 通话状态改变回调
- (void)onCallStateChanged:(AgentCallStateChangeType)type incomingType:(CallIncomingType)incomingType inviter:(nullable JRTCInviter *)inviter reason:(CallTermReason)reason {
    if (type == AgentCallStateChangeTypeTermed) {
     	// 通话结束,结束原因查看 reason
    }
}// 坐席主动结束通话
[_agent term];

// 通话状态改变回调
- (void)onCallStateChanged:(AgentCallStateChangeType)type incomingType:(CallIncomingType)incomingType inviter:(nullable JRTCInviter *)inviter reason:(CallTermReason)reason {
    if (type == AgentCallStateChangeTypeTermed) {
     	// 通话结束,结束原因查看 reason
    }
}

# 销毁本地和远端视频画面

当不再需要查看视频画面,或者通话结束,需要 JRTCMediaDevice (opens new window) 对象的 stopVideo (opens new window) 方法来释放渲染资源。 该方法需传入要释放的 JRTCMediaDeviceVideoCanvas (opens new window) 对象。必须进行这步操作,不然会造成渲染内存不释放。

/**
* 停止视频渲染
* @param canvas JRTCMediaDeviceVideoCanvas 对象,由 {@link startVideo:renderType: startVideo} 或 {@link startCameraVideo: startCameraVideo} 接口返回
*/
- (void)stopVideo:(JRTCMediaDeviceVideoCanvas* __nonnull)canvas;/**
* 停止视频渲染
* @param canvas JRTCMediaDeviceVideoCanvas 对象,由 {@link startVideo:renderType: startVideo} 或 {@link startCameraVideo: startCameraVideo} 接口返回
*/
- (void)stopVideo:(JRTCMediaDeviceVideoCanvas* __nonnull)canvas;

示例代码:

// 通话结束
- (void)onCallStateChanged:(AgentCallStateChangeType)type incomingType:(CallIncomingType)incomingType inviter:(nullable JRTCInviter *)inviter reason:(CallTermReason)reason {
    if (type == AgentCallStateChangeTypeTermed) {
        [_mediaDevice stopVideo:_localCanvas];
        [_mediaDevice stopVideo:_remoteCanvas];
    }
}

// 成员离开
- (void)onMemberLeave:(JRTCRoomParticipant *)part {
     // 停止远端坐席的画面渲染
     [_mediaDevice StopVideo:_remoteCanvas];
 }// 通话结束
- (void)onCallStateChanged:(AgentCallStateChangeType)type incomingType:(CallIncomingType)incomingType inviter:(nullable JRTCInviter *)inviter reason:(CallTermReason)reason {
    if (type == AgentCallStateChangeTypeTermed) {
        [_mediaDevice stopVideo:_localCanvas];
        [_mediaDevice stopVideo:_remoteCanvas];
    }
}

// 成员离开
- (void)onMemberLeave:(JRTCRoomParticipant *)part {
     // 停止远端坐席的画面渲染
     [_mediaDevice StopVideo:_remoteCanvas];
 }

# 登出

结束通话后,可以做登出操作,与平台断开一切连接。 登出接口调用流程如下所示: img

调用 logout (opens new window)接口登出 Juphoon RTC 平台。

/**
 * 登出 Juphoon RTC 平台,登出后不能进行平台上的各种业务
 *
 * 登出结果通过 {@link JRTCClientCallback.onLogout: onLogout} 回调通知
 * @return 接口调用结果
 * - true: 接口调用成功
 * - false: 接口调用异常
 */
- (bool)logout;/**
 * 登出 Juphoon RTC 平台,登出后不能进行平台上的各种业务
 *
 * 登出结果通过 {@link JRTCClientCallback.onLogout: onLogout} 回调通知
 * @return 接口调用结果
 * - true: 接口调用成功
 * - false: 接口调用异常
 */
- (bool)logout;

登出结果通过 JRTCClientCallback (opens new window) 中的 onLogout (opens new window) 接口上报。

/**
 * 登出回调
 * @param reason 登出原因
 */
- (void)onLogout:(ReasonCode)reason;/**
 * 登出回调
 * @param reason 登出原因
 */
- (void)onLogout:(ReasonCode)reason;

示例代码:

// 调用登出接口
[_client logout];

// 监听登出结果回调
- (void)onLogout:(ReasonCode)reason {
    // 登出完成   
}// 调用登出接口
[_client logout];

// 监听登出结果回调
- (void)onLogout:(ReasonCode)reason {
    // 登出完成   
}

# 登录状态改变通知

登录状态通过 JRTCClientCallback (opens new window) 中的 onClientStateChanged (opens new window) 接口上报

/**
* 登录状态变化通知
* @param state 当前状态值
* @param oldState 之前状态值
*/
- (void)onClientStateChanged:(JRTCClientState)state oldState:(JRTCClientState)oldState;/**
* 登录状态变化通知
* @param state 当前状态值
* @param oldState 之前状态值
*/
- (void)onClientStateChanged:(JRTCClientState)state oldState:(JRTCClientState)oldState;

登录状态详见 JRTCClientState (opens new window)

示例代码:

//登录状态改变通知
- (void)onClientStateChanged:(JRTCClientState)state oldState:(JRTCClientState)oldState {
    //state 当前状态
    //oldState 之前状态
}//登录状态改变通知
- (void)onClientStateChanged:(JRTCClientState)state oldState:(JRTCClientState)oldState {
    //state 当前状态
    //oldState 之前状态
}

# 销毁 SDK

每个模块都有对应的销毁接口。如不需再使用 SDK 的相关功能,可以强制释放 SDK 的资源。 注:该方法为同步调用,调用此方法后,你将无法再使用该模块的其它方法和回调。 我们不建议在 JRTCSDK 的所有回调方法中调用此方法销毁对象,否则可能出现崩溃现象。

+ (void)destroy;+ (void)destroy;

示例代码:

- (void)destroySDK {
    [JRTCAgent destroy];
    [JRTCMediaDevice destroy];
    [JRTCClient destroy];
}- (void)destroySDK {
    [JRTCAgent destroy];
    [JRTCMediaDevice destroy];
    [JRTCClient destroy];
}