iOS

# 通话操作

本文将介绍坐席基于 Juphoon RTC SDK 提供的通话操作能力实现的功能。

# 1. 通话属性变化

通话属性变化通过实现 JRTCAgentCallback (opens new window) 中的 onCallPropertyChanged (opens new window) 接口上报

/**
 * 通话属性改变回调
 * @note
 * 重点关注屏幕共享,即当{@link JRTCRoomPropChangeParam.screenShare screenShare} 属性为 true 时,去处理屏幕共享相关事件。<br>
 * 可根据 {@link JRTCGuest.shareStreamId shareStreamId} 和 {@link JRTCGuest.shareUserId shareUserId} 属性进行屏幕共享画面的渲染和停止渲染。
 * @param changeParam 通话改变的属性
 */
- (void)onCallPropertyChanged:(JRTCRoomPropChangeParam *)changeParam;

示例代码:

- (void)onCallPropertyChanged:(JRTCRoomPropChangeParam *)changeParam;
    if(changeParam.uploadAudio) {
        //上传声音状态是否变化
    } else if(changeParam.uploadVideo) {
        //上传视频状态是否变化
    } else if(changeParam.audioOut) {
        //输出声音状态是否变化
    } else if(changeParam.cdnState) {
        //CDN推流状态是否变化
    } else if(changeParam.remoteRecordState) {
        //远程录制状态是否变化
    } else if(changeParam.screenShare) {
        //屏幕共享状态是否变化
    }
}

通话属性变化详见 JRTCRoomPropChangeParam (opens new window) 。 其中对应通话属性的 bool 值,有变化的为 true,没有变化为 false

# 2. 成员属性变化

成员属性变化通过实现 JRTCAgentCallback (opens new window) 中的 onMemberUpdate (opens new window) 接口上报。

/**
 * 通话中成员属性更新回调
 *
 * 常用的有 {@link JRTCRoomParticipantChangeParam.volume 音量值}、{@link JRTCRoomParticipantChangeParam.audio 音频上传状态}、{@link JRTCRoomParticipantChangeParam.video 视频上传状态}等。<br>
 * 例如当通话中有成员关闭视频传输,通话中所有成员都会收到此回调。
 * @param participant 属性更新的成员对象
 * @param changeParam 更新的属性对象
 */
- (void)onMemberUpdate:(JRTCRoomParticipant *)participant changeParam:(JRTCRoomParticipantChangeParam *)changeParam;

示例代码:

- (void)onMemberUpdate:(JRTCRoomParticipant *)part changeParam:(JRTCRoomParticipantChangeParam *)changeParam {
    if (changeParam.video) {
        if (part.video) {
        	// 视频流打开
        } else {
        	// 视频流关闭
        }
    }
    if (changeParam.audio) {
        if (part.audio) {
        	// 音频流打开
        } else {
        	// 音频流关闭
        }
    }
}

成员属性变化详见 JRTCRoomParticipantChangeParam (opens new window) 。 其中有对应成员属性的 bool 值,有变化的为 true,没有变化为 false

# 3. 获取所有通话成员

通过 participants (opens new window) 获取所有参会者成员,JRTCRoomParticipant (opens new window) 详见API文档。

/**
 * 所有成员(包含自己、访客和其他坐席)
 */
@property (nonatomic, readonly, strong) NSArray <JRTCRoomParticipant *> *participants;

示例代码:

NSArray <JRTCRoomParticipant *> *participants = _agent.participants;

# 4. 获取访客成员

SDK 支持邀请三方座席(座席端功能)进入通话,可以通过以下接口获取所有访客成员列表,以及获取主访客成员。

/**
* 获取主访客成员
*
* 只有在通话中才能获得访客成员对象,否则为 nil
*/
@property (nonatomic, readonly, strong) JRTCRoomParticipant *mainGuestParticipant;


/**
* 访客成员列表
*
* 只有在通话中且通话中才能获得含有访客成员对象的数组,否则为空数组 <br>
* 当通话中不存在第三方访客时,获得一个仅含有主访客成员对象的数组 <br>
* 当通话中存在第三方访客时,获得一个含有主访客和第三方访客成员对象的数组
*/
@property (nonatomic, readonly, strong) NSArray<JRTCRoomParticipant *> *guestParticipants;

示例代码:

// 获取主访客对象
JRTCRoomParticipant *guest = _agent.mainGuestParticipant;

// 获取访客成员列表
NSArray<JRTCRoomParticipant *> *guestList = _agent.agentParticipants;

# 5. 获取自己对象

/**
 * 获取自己对象
 *
 * @return 自己对象
 */
@property (nonatomic, strong, readonly) JRTCRoomParticipant *selfParticipant;

示例代码:

JRTCRoomParticipant *selfParticipant = _agent.selfParticipant;

# 6. 是否主座席

/**
 * 是否主座席
 *
 * @return
 * - true 主座席
 * - false 其他三方座席
 */
@property (nonatomic, readonly, assign) bool isMainAgent;

示例代码:

// 是否主座席
_agent.isMainAgent;

# 7. 通话保持/取回

通话中座席可发起保持通话的操作,保持通话之后座席和访客皆停发音视频数据,双方将互相听不到声音看不到视频画面。

座席可以调用 setHoldState (opens new window) 接口保持/取回通话。

/**
 * 保持/取回
 *
 * 可以通过 {@link getHoldState} 获取当前通话保持状态 <br>
 * 保持通话后双方互相停止音视频流的发送与接收
 * @param hold 保持或取回
 * - true: 设置为保持状态
 * - false: 设置为取回状态
 * @return 接口调用结果
 * - true: 接口调用成功,通话中的所有成员将会收到 {@link  JRTCGuestCallback.onHoldStateChanged: onHoldStateChanged} 或 {@link JRTCAgentCallback.onHoldStateChanged: onHoldStateChanged} 回调
 * - false: 接口调用异常
 */
- (bool)setHoldState:(bool)hold;

座席可以调用 getHoldState (opens new window) 获取当前通话保持状态。

/**
 * 获取当前通话保持状态
 *
 * 座席可调用 {@link JRTCAgent.setHoldState: setHoldState} 接口修改当前通话的保持状态。
 * @return 当前通话保持状态
 * - true: 当前通话状态为保持
 * - false: 当前通话状态为取回
 */
- (bool)getHoldState;

座席发起通话保持之后,通话中的所有成员都将收到 onHoldStateChanged (opens new window) 通话被保持的回调。

/**
 * 收到通话保持或取回的回调
 *
 * 通话中座席可调用 {@link JRTCAgent.setHoldState: setHoldState} 接口保持通话或取回通话,通话中所有成员都会收到此回调。
 * @param hold true 表示通话被保持,false 表示通话取回
 */
- (void)onHoldStateChanged:(bool)hold;

示例代码:

// 保持
[_agent setHoldState:true];

// 取回
[_agent setHoldState:false];

// 通话保持状态改变
- (void)onHoldStateChanged:(bool)hold {
    if (hold == true) {
        // 通话保持
    } else {
        // 通话取回
    }
}

// 主动获取通话保持状态
bool hold = [_agent getHoldState];
if (hold == true) {
    // 通话保持
} else {
    // 通话取回
}

# 8. 音视频切换

通话中座席与访客皆可发起音视频通话切换的操作。

视频通话状态下座席访客互相可听到对方声音看到视频画面,语音通话状态下座席访客只能听到对方声音,看不到对方视频画面。

座席可以调用 turnCallType (opens new window) 接口进行音视频通话切换。

/**
 * 音视频通话切换
 *
 * 可以通过 {@link getCallType} 接口获取当前的通话类型 <br>
 * 通话中所有成员都将收到{@link JRTCGuestCallback.onCallTypeChanged: onCallTypeChanged} 或 {@link JRTCAgentCallback.onCallTypeChanged: onCallTypeChanged} 回调
 * @param callType 通话类型
 * - @ref CallTypeAudio: 语音通话
 * - @ref CallTypeVideo: 视频通话
 */
- (void)turnCallType:(CallType)callType;

访客可以调用 getCallType (opens new window) 接口获取当前通话类型。

/**
 * 获取当前通话类型
 *
 * 可通过 {@link turnCallType: turnCallType} 接口修改当前通话类型。
 * @return 当前通话类型
 * - @ref CallTypeAudio : 语音通话
 * - @ref CallTypeVideo : 视频通话
 */
- (CallType)getCallType;

通话类型切换之后,参加通话的所有成员都将收到 onCallTypeChanged (opens new window) 通话类型切换的通知。

/**
 * 音视频通话切换回调
 *
 * 通话中的访客和座席可分别调用 {@link JRTCGuest.turnCallType: turnCallType} 和 {@link JRTCAgent.turnCallType: turnCallType} 方法切换音视频通话模式,通话中所有成员都会收到此回调。
 * @param callType 通话模式
 * - @ref CallTypeAudio 语音通话模式
 * - @ref CallTypeVideo 视频通话模式
 */
- (void)onCallTypeChanged:(CallType)callType;

示例代码:

// 切换到语音通话
[_agent turnCallType:CallTypeAudio];

// 切换到视频通话
[_agent turnCallType:CallTypeVideo];

// 获取当前通话类型
CallType callType = [_agent getCallType];
if (callType == CallTypeAudio) {
    // 当前为语音通话
} else if (callType == CallTypeVideo) {
    // 当前为视频通话
}

// 音视频通话切换回调
- (void)onCallTypeChanged:(CallType)callType {
    if (callType == CallTypeAudio) {
        // 切换为语音通话
    } else if (callType == CallTypeVideo) {
        // 切换为视频通话
    }
}

# 9. 通话转接

在通话过程中将通话转给其他座席。可以指定座席或指定座席所在的业务组,如指定业务组将会随机选择业务组中一个空闲座席进行转接。

座席可调用 transferCall (opens new window) 接口进行通话转接。

/**
 * 通话转接
 *
 * 支持转接到具体座席和转接到某个业务组 <br>
 * 转接结果通过 {@link JRTCAgentCallback.onTransferCallResult:result: onTransferCallResult} 回调上报 <br>
 * 座席调用转接接口成功时,访客会收到 {@link JRTCGuestCallback.onCallForwarding onCallForwarding} 回调
 * @param type 转接类型,转接到具体座席或是转接到业务组
 * - @ref TransferTypeGroup 转接到业务组
 * - @ref TransferTypeStuff 转接到具体座席
 * @param toUserId
 * - 当转接类型为 @ref TransferTypeGroup 时,toUserId 需传入业务组号
 * - 当转接类型为 @ref TransferTypeStuff 时,toUserId 需传入座席用户ID
 * @return 接口调用结果
 * - > 0: 操作id,对应 {@link JRTCAgentCallback.onTransferCallResult:result: onTransferCallResult} 回调的 operationId 参数
 * - -1: 接口调用异常
 */
- (int)transferCall:(TransferType)type toUserId:(NSString *)toUserId;

转接接口调用成功时,转接结果会通过 JRTCAgentCallback (opens new window)onTransferCallResult (opens new window) 回调通知到座席。

/**
 * 通话转接回调
 *
 * 调用 {@link JRTCAgent.transferCall:toUserId: transferCall} 接口将通话转接给其他座席或业务组时,会收到此回调。
 * @param operationId 操作id,对应 {@link JRTCAgent.transferCall:toUserId: transferCall} 接口的返回值
 * @param result 通话转接结果
 * - true: 转接成功
 * - false: 转接失败
 */
- (void)onTransferCallResult:(int)operationId result:(bool)result;

转接成功,访客会收到 JRTCGuestCallback (opens new window)onCallForwarding (opens new window) 回调。

/**
 * 座席通话转接回调
 *
 * 通话中座席调用 {@link JRTCAgent.transferCall:toUserId: transferCall} 接口将通话转接到某个座席或转接到某个业务组后,访客会收到此回调,然后访客需等待新的座席接听该通话。<br>
 * 该过程中,访客通话状态不变,新的座席接听通话后,访客可通过 {@link onMemberJoin: onMemberJoin} 回调得知。
 */
- (void)onCallForwarding;

示例代码:

// 转接到指定坐席
[_agent transferCall:TransferTypeStuff toUserId:@"agent1"];

// 转接到业务组
[_agent transferCall:TransferTypeGroup toUserId:@"10086"];

// 通话转接结果回调
- (void)onTransferCallResult:(int)operationId result:(bool)result {
 	if (result == true) {
     	// 发起转接成功   
    } else {
        // 发起转接失败
    }
}

# 10. 单向视频

单向视频发起流程:

/**
 * 请求单向视频
 *
 * 座席调用此接口后,会向全体访客发送单向视频请求,访客会收到 {@link JRTCGuestCallback.onOnewayVideoChanged: onOnewayVideoChanged} 回调 <br>
 * 应用需要自行实现单向视频功能,例如访客用图片遮挡该座席画面,SDK不会对画面进行单向处理
 * @param turnOn 打开或关闭单向视频
 * - true: 打开单向视频
 * - false: 关闭单向录制
 */
- (void)requestOnewayVideo:(bool)turnOn;

示例代码:

// 请求单向视频
[_agent requestOnewayVideo:true];

// 取消单向视频
[_agent requestOnewayVideo:false];

# 11. 获取随路参数

访客呼叫时携带的额外信息,可以在座席收到来电通知 onCallStateChanged (opens new window) 回调中的 inviter 参数对象获取。

/**
 * 通话状态改变回调
 * @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;

示例代码:

- (void)onCallStateChanged:(AgentCallStateChangeType)type incomingType:(CallIncomingType)incomingType inviter:(nullable JRTCInviter *)inviter reason:(CallTermReason)reason {
    if (type == AgentCallStateChangeTypeIncoming) {
        //收到来电
        NSString *extra = inviter.extraInfo;
    }
}

# 12. 切换自己在通话中角色

/**
 * 切换自己在通话中角色
 *
 * @note 需要先进入通话,目前只支持从观察者座席角色切换到主座席角色
 *
 * @param partRole  通话成员角色,目前只支持
 * - @ref PartRoleTypeMainAgent : 主座席
 * - @ref PartRoleTypeAgent : 次座席
 * @return 接口调用结果
 * - > 0: 操作id,对应 {@link JRTCAgentCallback.onSwitchPartRoleResult:operationId:error: onSwitchPartRoleResult} 回调的 operationId 参数
 * - -1: 接口调用异常
 */
- (int)switchPartRole:(PartRoleType)partRole;

# 13. 踢出通话成员

/**
  * 踢出通话成员
  * @note 只有主座席角色可以使用该功能
  *
  * @param userId 成员用户ID
  * @return 接口调用结果
  * - > 0:  操作id,对应 {@link JRTCAgentCallback.onKickParticipantResult:operationId:error: onKickParticipantResult} 回调的 operationId 参数
  * - -1:   接口调用异常
  */
- (int)kickParticipant:(NSString *)userId;