iOS

# 实现视频通话

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

# 1. 前提条件

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

  • 已获取 App Key。

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

TIP

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

image-20210315095937896
  • 集成 SDK(Windows Java)。

    TIP

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

# 2. 快速跑通 Sample

  1. 在Juphoon RTC SDK 文档中心,选择视频客服-座席选择项,在 SDK 下载页面下载体验 Sample 示例项目。访问下载地址 (opens new window),示例如下:

aa.png

  1. 下载完成后,打开安装包,解压 JCCSample:
  • Windows

双击安装 JCCSampleSetup-java.exe

bb.png

  • UOS-X86 和 UOS-ARM

需要依赖 jre 运行环境(请提前安装),然后

​ a. 在终端 cd 到 Program 目录下;

​ b. chmod 777 ./start.sh,修改程序运行权限;

​ c. ./start.sh,运行程序,出现 Sample 主界面;

cc.png

dd.png

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

    a. 首先点击初始化按钮,成功后,登录按钮变为可点击;

    b. 确认账号输入无误之后,点击登录按钮,按钮字样变为登出,即登录成功;

    c. 点击座席按钮,即可进入对应座席页面;

hh.png

  1. 进入座席页面后,点击签入即可回呼访客及等待访客来电

ii.png

# 3. 实现视频通话(视频客服)

img

# 初始化 SDK

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

在使用业务接口前,需对 Juphoon SDK 进行初始化操作。

JRTCClient (opens new window) 基础模块 负责视频平台的登录登出,只有登录到视频平台才可以使用视频相关的业务,AppKey 作为同个环境的分域依据,同一个域的终端才能实现互通。
JRTCMediaDevice (opens new window) 媒体模块 负责本地的媒体设备操作,视频画面渲染等功能。
JRTCAgent (opens new window) 座席模块 负责座席的视频业务的处理,比如接听来电,控制音视频流等 这些模块初始化的时候需要有 callback 作为入参来监听各模块的事件
JRTCRecord (opens new window) 录制模块 负责分片录制与上传

注:Agent 座席模块和 Guest 访客模块只支持初始化一个,否则会出现接收不到呼叫,接收不到邀请等现象影响正常的业务流程。

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

示例代码:

class JRTCManager implements JRTCClientCallback, JRTCGuestCallback, 
JRTCMediaDeviceCallback {
    public void init() {
        JRTCClientInitParam param = new JRTCClientInitParam();
        /**
        * 设置应用名称,比如:菊风访客
        */
        param.setAppName("appName");
        /**
        * 设置SDK信息存储目录,该目录下的log目录为日志目录
        */
        param.setSdkInfoDir("sdkInfoDir");
        /**
        * 设置SDK dll库的放置目录
        */
        param.setSdkInfoDir("sdkLibPath")
        /**
        * 设置AppKey
        * 用户从 Juphoon RTC 平台上申请的 AppKey 字符串
        */
        param.setAppKey("appKey");
        /**
        * 设置接入服务器地址
        */
        param.setServer("server");
        /**
        * 设置是否需要自动加载so库,默认true
        */
        param.setLoadLibrary(true);
        /**
        * 设置是否控制台日志输出, 默认true
        */
        param.setLogConsole(true);
        /**
        * 获取是否是否本地文件日志输出, 默认true
        */
        param.setLogLocalFile(true);
        /**
        * 设置是否开启 RPC 抗信令丢包控制(70%的上下行信令丢包), 默认false
        */
        param.setLooseTimeoutControl(true);
        /**
     	* 是否使用hik,如果使用将会在初始化时初始化海康资源
     	*
     	* @param useHik 是否使用海康
     	*             - tue 使用,并初始化
     	*             - false 不使用,不初始化
     	*/
        param.setUseHik(true);
        /**
     	* 是否使用dahua,如果使用将会在初始化时初始化大华资源
     	*
     	* @param useDahua 是否使用海康
     	*             - tue 使用,并初始化
     	*             - false 不使用,不初始化
     	*/
        param.setUseDahua(true);
            
        client = JRTCClient.create(context, this, param);
        mediaDevice = JRTCMediaDevice.create(client, this, null);
        record = JRTCRecord.create(client, mediaDevice, mRecordCallback);
        agent = JRTCAgent.create(client, mediaDevice, this);

        //设置基本参数
        client.setServer("server");
        client.setAppKey("appKey");
        client.setDisplayName("displayName");
        client.setAppName("appName");
    }
}

然后根据 idea 提示将 interface 的接口实现补全即可。

# 登录

SDK 初始化之后,即可进行登录的集成,登录接口调用流程如下所示:

img

登录到 Juphoon 视频平台主要调用的是 JRTCClient 的登录接口 login (opens new window)

/**
 * 登录 Juphoon RTC 平台,只有登录成功后才能进行平台上的各种业务<br>
 * 登录结果通过 JRTCClientCallback 通知<br>
 *
 * @param userId   用户名
 * @param password 密码,不能为空
 * @return 返回 true 表示正常执行调用流程,false 表示调用异常,异常错误通过 JRTCClientCallback 通知
 * @note 目前只支持免鉴权模式,服务器不校验账号密码,免鉴权模式下当账号不存在时会自动去创建该账号
 * @note 注意:用户名为英文数字和'+' '-' '_' '.',长度不要超过64字符,'-' '.' '_'字符不能处于第一位<br>
 */
public abstract boolean login(String userId, String password);

/**
 * 登录 Juphoon RTC 平台,只有登录成功后才能进行平台上的各种业务<br>
 * 登录结果通过 JRTCClientCallback 通知<br>
 *
 * @param userId           用户名
 * @param password         密码,不能为空
 * @param clientLoginParam 登录校验参数,传 null 则按默认值。一般不需要设置,如需设置请询问客服
 * @return 返回 true 表示正常执行调用流程,false 表示调用异常,异常错误通过 JRTCClientCallback 通知
 * @note 目前只支持免鉴权模式,服务器不校验账号密码,免鉴权模式下当账号不存在时会自动去创建该账号
 * @note 注意:用户名为英文数字和'+' '-' '_' '.',长度不要超过64字符,'-' '.' '_'字符不能处于第一位<br>
 */
public abstract boolean login(String userId, String password, JRTCClientLoginParam clientLoginParam);

/**
 * 重登录,该接口在如果有其他同类型终端登录着则会登录失败,一般用于记住了账号后重启自动登录逻辑
 *
 * @param userId   用户名
 * @param password 密码,不能为空
 * @return 返回 true 表示正常执行调用流程,false 表示调用异常,异常错误通过 JRTCClientCallback 通知
 */
public abstract boolean reLogin(String userId, String password);

JRTCClientLoginParam (opens new window) 属性介绍

属性 描述
setAccountEntry 设置账户分录参数,如果支持国密S3则需要设置 certificate 参数,否则可以不设置 certificate 参数
setCertificate 设置 S3 国密证书 Base64 编码内容
setAcceptExpiredCertificate 设置是否允许过期证书校验通过
setToken 设置token
setTokenType 设置 token 校验类型
setDeviceId 设置设备id
setLogFilter 设置日志过滤标签(用于日志管理平台过滤终端日志使用)
setTerminalType 设置终端登录类型,支持多终端登录,默认所有终端相同会导致互踢
setAutoCreateAccount 是否自动创建账号(免鉴权使用),默认true
setAccelerateKey 设置加速云KEY
setAccelerateKeySecret 设置加速云KEY密钥

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

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

示例代码:

// 创建登录配置参数
JRTCClientLoginParam loginParam = new JRTCClientLoginParam();
// 登录
client.login("juphoon", "123456", loginParam);

public void onLogin(boolean result, @ReasonCode int reason) {
    if(result) {
        // 登录成功
    } else {
        // 登录失败,具体原因查询 reason 错误码
    } 
}

# 获取业务号列表

调用 queryAllGroups (opens new window) 可以获取业务号列表。

/**
 * 获取业务号列表
 *
 * @return 接口调用结果
 * - true: 接口调用成功,返回结果通过 {@link JRTCAgentCallback#onGetAllGroups onGetAllGroups} 回调上报
 * - false: 接口调用异常
 */
public abstract boolean queryAllGroups();

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

/**
 * 获取业务号列表结果回调
 * <p>
 * 座席调用 {@link JRTCAgent#queryAllGroups queryAllGroups} 接口获取业务号列表,会收到此回调。
 *
 * @param groups 座席业务实体对象列表,获取失败时为 null
 * @param result 获取结果
 *               - true: 获取成功
 *               - false: 获取失败
 */
void onGetAllGroups(boolean result, List<JRTCCallCenterGroupItem> groups);

示例代码:

//获取业务号列表
agent.queryAllGroups();

public void onGetAllGroups(boolean result, List<JRTCCallCenterGroupItem> groups) {
    if(result) {
        //获取成功
    } else {
        //获取失败
    }
}

# 查询指定业务号的空闲座席

通过业务号查询空闲的座席,详见 queryAvailableAgentList (opens new window)

/**
 * 查询指定业务号的空闲座席
 * <p>
 * 查询结果通过 {@link JRTCAgentCallback#onQueryAvailableAgentList onQueryAvailableAgentList} 回调上报
 *
 * @param businessNumber 业务号,例如 10087
 * @return 操作id,与 {@link JRTCAgentCallback#onQueryAvailableAgentList onQueryAvailableAgentList} 的 operationId 参数对应
 */
public abstract int queryAvailableAgentList(String businessNumber);

查询指定业务号的空闲座席查询结果通过实现 onQueryAvailableAgentList (opens new window) 接口上报:

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

示例代码:

//查询业务号为10086的空闲座席列表
agent.queryAvailableAgentList("10086");
void onQueryAvailableAgentList(int operationId, boolean result, List<String> userIds) {
    if (result) {
        // 查询成功,具体查看 userIds
    } else {
        // 查询失败   
    }
}

# 座席签入

登录视频能力平台成功后,需要签入到排队机才能进行业务(回呼访客和接收访客呼叫)。

/**
 * 签入
 * <p>
 * 签入到排队机,签入后示忙/示闲状态由 busy 参数决定 <br>
 * 示闲状态下正常呼叫进线 <br>
 * 调用接口前确定 JRTCClient 登录成功 <br>
 * 调用该接口前需先确定 {@link #getOperatorState operatorState} 来获取到当前的签入状态
 *
 * @param userId 座席用户ID,需与业务管理平台上配置的座席staffId对应,如果传空,则默认用当前登录用户ID签入
 * @param busy   签入后示忙/示闲状态
 *               - true: 示忙
 *               - false: 示闲
 * @oaram checkinParam 签入其他参数
 * @return 接口调用结果
 * - true: 接口调用成功,签入结果会触发 {@link JRTCAgentCallback#onCheckin onCheckin} 回调上报
 * - false: 接口调用异常
 */
public abstract boolean checkin(String userId, boolean busy, JRTCAgentCheckinParam checkinParam);

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

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

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

签入方法包含几个重载,用来适应各种可配置的参数,比如签入后默认为示闲还是示忙、比如座席的角色。这些方法的需结合具体的需求来使用。

签入结果通过 JRTCAgentCallback (opens new window)onCheckIn (opens new window) 接口上报。

/**
 * 签入回调
 * <p>
 * 座席调用 {@link JRTCAgent#checkIn checkIn} 接口成功,会收到此回调。
 *
 * @param result     签入结果
 *                   - true: 签入成功
 *                   - false: 签入失败
 * @param pause      签入后的示闲/示忙状态
 *                   - true: 签入后示忙
 *                   - false: 签入后示闲
 * @param onlineTime 座席累计在线时长
 * @param breakTime  座席累计示忙时长
 * @param callTimes  通话次数
 * @param reason     签入失败原因
 * @deprecated 该回调方法即将废弃,请使用 {@link #onCheckin(boolean, boolean, String)} onCheckin} 代替
 */
@Deprecated
void onCheckIn(boolean result, boolean pause, long onlineTime, long breakTime, long callTimes, int reason);

/**
 * 签入回调
 * <p>
 * 座席调用 {@link JRTCAgent#checkin checkin} 接口成功,会收到此回调。
 *
 * @param result     签入结果
 *                   - true: 签入成功
 *                   - false: 签入失败
 * @param pause      签入后的示闲/示忙状态
 *                   - true: 签入后示忙
 *                   - false: 签入后示闲
 * @param reason     失败原因
 */
void onCheckin(boolean result, boolean pause, String reason);

示例代码:

// 签入,签入后为示闲状态
agent.checkIn(client.getUserId(), JRTCCallCenter.AGENT_ROLE_ADMINISTRATOR, false);

// 签入结果回调
void onCheckIn(boolean result, boolean pause, long onlineTime, long breakTime, long callTimes, long callTime, int reason) {
    if (result) {
        // 签入成功
    } else {
        // 签入失败
    }
}

# 座席签出

当所有业务处理完毕,座席可以调用 JRTCAgent (opens new window) 对象中的 checkout (opens new window) 方法签出排队机,将不会再收到终端业务,业务管理平台上该座席的状态将变为离线。

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

结果通过 JRTCAgentCallback (opens new window)onCheckout (opens new window) 接口上报:

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

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

示例代码:

//座席签出
agent.checkout();

void onCheckout(boolean result, @JRTCAgent.AgentCheckoutReason int reason) {
    if(result) {
        // 签出成功
    } else {
        // 签出失败
    }
}

# 座席签入签出状态改变

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

/**
 * 签出状态改变
 *
 * @param currState 当前座席状态
 * @param oldState  旧的座席状态
 */
void onCheckStateChanged(@JRTCAgent.AgentOperatorState int currState, @JRTCAgent.AgentOperatorState int oldState);

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

示例代码:

public void onCheckStateChanged(@JRTCAgent.AgentOperatorState int currState, @JRTCAgent.AgentOperatorState int oldState) {
    if (currState == AGENT_OPERATOR_CHECKING_IN) {
            //正在签入中
    } else if (currState == AGENT_OPERATOR_CHECKED_IN) {
            //已签入
    } else if (currState == AGENT_OPERATOR_CHECKING_OUT) {
            //已签出
    } else if (currState == AGENT_OPERATOR_IDLE) {
            //签入失败
    }
}

# 获取座席签入状态

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

/**
 * 获取座席签入状态
 *
 * @return 座席签入状态
 */
@AgentOperatorState
public abstract int getOperatorState();

# 示忙示闲

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

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

/**
 * 示忙/示闲
 * <p>
 * 示忙状态下不会收到呼叫来电,示闲状态下正常呼叫进线 <br>
 * 通话过程中调用该接口不会影响当前通话,从下个通话开始状态生效 <br>
 * 示忙/示闲的结果通过 {@link JRTCAgentCallback#onApplyResult onApplyResult} 回调上报
 *
 * @param pause 示忙或是示闲
 *              - true:  示忙
 *              - false: 示闲
 * @param subState 子状态,报表数据统计需要,取值[0, 9]
 *              - 0:   默认子状态
 *              - 1~9: 自定义子状态
 * @return 接口调用结果
 * - > 0: 操作id,对应 {@link JRTCAgentCallback#onApplyResult onApplyResult} 回调的 operationId 参数
 * - -1:接口调用异常
 */
public abstract int applyStatePause(boolean pause, int subState);

如果需要在通话结束后设置默认示闲示忙,可以通过调用调用 JRTCAgent (opens new window) 对象的 setTermState (opens new window) 方法实现

/**
 * 获取结束通话后的示闲/示忙状态,默认为示闲状态
 *
 * @return 通话结束后状态
 */
@AgentTermState
public abstract int getTermState();

/**
 * 设置通话结束后示闲/示忙状态,默认为示闲状态
 *
 * @param state 示闲/示忙状态
 */
public abstract void setTermState(@AgentTermState int state);

示忙示闲结果通过 JRTCAgentCallback (opens new window)onApplyResult (opens new window) 接口上报:

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

还可以调用 isPause (opens new window) 查询当前示闲示忙的状态:

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

示例代码:

//座席示忙
agent.applyStatePause(true);

//座席示忙示闲结果
public void onApplyResult(int operationId, boolean result) {
    if (result) {
        // 示忙示闲的操作成功
    }
    // 获取当前的忙闲状态
    agent.isPause();
}

# 座席回呼

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

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

示例代码:

// 座席回呼
JRTCCallCenterCallParam callParam = new JRTCCallCenterCallParam();
callParam.setExtraInfo("测试");
//callParam 其他参数设置
agent.recall("guestUserId", callParam);

# 接听来电

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

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

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

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

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

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

/**
 * 接听
 * <p>
 * 主座席、第三方座席或是被转接的座席,都调用此接口接听通话 <br>
 *
 * @return 接口调用结果
 * - true: 接口调用成功,会收到 {@link JRTCAgentCallback#onCallStateChanged onCallStateChanged} 回调
 * - false: 接口调用异常
 */
public abstract boolean answer();

示例代码:

public void onCallStateChanged(@JRTCAgent.AgentCallStateChangeType int type, @JRTCAgent.CallIncomingType int incomingType, JRTCInviter inviter, @JRTCAgent.CallTermReason int reason) {
    if (type == AGENT_CHANGE_TYPE_INCOMING) {
        if (incomingType == CALL_INCOMING_TYPE_CALL) {
            // 普通呼叫来电
        } else if (incomingType == CALL_INCOMING_TYPE_INVITE) {
            // 三方邀请来电
        } else if (incomingType == CALL_INCOMING_TYPE_FORWARD) {
            // 转接来电
        } else if (incomingType == CALL_INCOMING_TYPE_DIRECT_CALL) {
            // 直呼来电
        }
        // 接听
		agent.answer();
    }
}

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

# 通话状态改变通知

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

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

通过 JRTCAgentCallback (opens new window)onCallStateChanged (opens new window) 的接口上报:

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

通话状态改变详见 AgentCallStateChangeType (opens new window)

通话结束原因详见 CallTermReason (opens new window)

示例代码:

public void onCallStateChanged(@JRTCAgent.AgentCallStateChangeType int type, @JRTCAgent.CallIncomingType int incomingType, JRTCInviter inviter, @JRTCAgent.CallTermReason int reason) {
    if (type == AGENT_CHANGE_TYPE_INCOMING) {
        // 来电
    } else if (type == AGENT_CHANGE_TYPE_TALKING) {
        // 通话建立
    } else if (type == AGENT_CHANGE_TYPE_TERMED) {
        // 通话结束
    } else if (type == AGENT_CHANGE_TYPE_CALLING) {
        // 正在呼叫(座席回呼)
    }
}

# 获取通话状态

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

/**
 * 获取当前通话状态
 *
 * @return 当前通话状态
 */
@CallState
public abstract int getCallState();

示例代码:

@CallState int callState = agent.getCallState();
if (callState == CALL_STATE_IDLE) {
    // 空闲状态
} else if (callState == CALL_STATE_INCOMING) {
    // 收到来电
} else if (callState == CALL_STATE_TALKING) {
    // 通话中
} else if (callState == CALL_STATE_CALLING) {
    // 呼叫中(回呼场景)
}

# 创建本地视频画面

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

  1. 通过调用 JRTCMediaDevice (opens new window) 里的 startCameraVideo (opens new window) 方法获取本地的视频对象。
  2. 通过 startCameraVideo (opens new window) 方法里传入渲染的画布的句柄进行视频画面渲染;
/**
 * 开始本端视频渲染
 * @note 该接口默认在 native 层使用硬件渲染,部分机型可能不支持
 * <p>
 * 获取本端视频预览对象 JRTCMediaDeviceVideoCanvas,通过此对象能获得视图用于UI显示
 *
 * @param view       视频窗口句柄
 * @param camera     摄像头对象
 * @param renderType 渲染模式:
 *                   - {@link #RENDER_FULL_SCREEN RENDER_FULL_SCREEN} : 铺满窗口,会有裁剪
 *                   - {@link #RENDER_FULL_CONTENT RENDER_FULL_CONTENT} : 全图像显示,会有黑边
 *                   - {@link #RENDER_FULL_AUTO RENDER_FULL_AUTO} : 自适应
 * @return - JRTCMediaDeviceVideoCanvas 对象: 开始自身视频渲染成功
 * - null: 开始自身视频渲染失败
 */
public abstract JRTCMediaDeviceVideoCanvas startCameraVideo(int view, JRTCMediaDeviceCamera camera, @RenderType int renderType);

/**
 * 开始本端视频渲染
 * @note 该接口默认在 java 层进行渲染,兼容性较好
 * <p>
 * 获取本端视频预览对象 JRTCMediaDeviceVideoCanvas,通过此对象能获得视图用于UI显示
 *
 * @param camera     摄像头对象
 * @param renderType 渲染模式:
 *                   - {@link #RENDER_FULL_SCREEN RENDER_FULL_SCREEN} : 铺满窗口,会有裁剪
 *                   - {@link #RENDER_FULL_CONTENT RENDER_FULL_CONTENT} : 全图像显示,会有黑边
 * @return - JRTCMediaDeviceVideoCanvas 对象: 开始自身视频渲染成功
 * - null: 开始自身视频渲染失败
 */
public abstract JRTCMediaDeviceVideoCanvas startCameraVideo(JRTCMediaDeviceCamera camera, @RenderType int renderType);

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

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

img

示例代码:

​ 这里用 JavaSwing 的使用为例,JavaSwing 是 Java 桌面的 UI 库,Java 编写桌面程序的重要资源。JavaSwing 中常用的界面对象 JFrame,自定义类继承 JFrame 就可以进一步实现窗口。

//获取第一个摄像头对象
JRTCMediaDeviceCamera camera = mediaDevice.getCameras().get(0);
//打开指定摄像图,并开始渲染画面,其实viewPointer是渲染窗口句柄
JRTCMediaDeviceVideoCanvas localCanvas = mediaDevice.startCameraVideo(viewPointer, camera, JRTCMediaDevice.RENDER_FULL_SCREEN);

// 以下提供两种获取窗口句柄的方法,使用其中一个即可。如果出现黑屏应该先检查句柄是否获取正确。
//Windows窗口句柄可以通过如下方式获取,swing只支持JFrame窗口控件渲染视频,awt支持Pannel等
public static long getComponentPointer(Component component) {
    long peerValue = -1;
    Pointer componentPointer = Native.getComponentPointer(component);
    Class a = componentPointer.getClass();
    try {
        Field peer = a.getDeclaredField("peer");
        peer.setAccessible(true);
        try {
            peerValue = (long) peer.get(componentPointer);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    } catch (NoSuchFieldException e) {
        e.printStackTrace();
    }
    return peerValue;
}
//UOS&Kylin窗口句柄可以通过如下方式获取,swing只支持JFrame窗口控件渲染视频,awt支持Pannel等
public static long getComponentPointer(Component component) {
    if (component == null) {
        return -1;
    }
    if (!component.isDisplayable()) {
        return -1;
    }
    XBaseWindow xBaseWindow = (XBaseWindow) component.getPeer();
    return xBaseWindow.getContentWindow();
    }
}

# 创建远端视频画面

当通话建立后,除了本地的视频画面,还有通话成员的远端视频画面,如果通话成员有视频流的上传,座席端可以获取到访客的视频流并进行渲染。

座席可在收到以下回调时进行界面处理:

调用 startVideo (opens new window) 方法里传入渲染的画布的句柄进行视频画面渲染。

/**
 * 开始其他端的视频渲染
 * @note 该接口默认在 native 层使用硬件渲染,部分机型可能不支持
 * <p>
 * 获取其他端的视频预览对象 JRTCMediaDeviceVideoCanvas,通过此对象能获得视图用于UI显示
 *
 * @param view       视频窗口句柄
 * @param streamId   视频流ID
 * @param renderType 渲染模式:
 *                   - {@link #RENDER_FULL_SCREEN RENDER_FULL_SCREEN} : 铺满窗口,会有裁剪
 *                   - {@link #RENDER_FULL_CONTENT RENDER_FULL_CONTENT} : 全图像显示,会有黑边
 *                   - {@link #RENDER_FULL_AUTO RENDER_FULL_AUTO} : 自适应
 * @return
 * - JRTCMediaDeviceVideoCanvas 对象: 开始其他端视频渲染成功
 * - null: 开始其他端视频渲染失败
 */
public abstract JRTCMediaDeviceVideoCanvas startVideo(int view, String streamId, @RenderType int renderType);

/**
 * 开始其他端的视频渲染
 * @note 该接口默认在 java 层进行渲染,兼容性较好
 * <p>
 * 获取其他端的视频预览对象 JRTCMediaDeviceVideoCanvas,通过此对象能获得视图用于UI显示
 *
 * @param streamId   视频流ID
 * @param renderType 渲染模式:
 *                   - {@link #RENDER_FULL_SCREEN RENDER_FULL_SCREEN} : 铺满窗口,会有裁剪
 *                   - {@link #RENDER_FULL_CONTENT RENDER_FULL_CONTENT} : 全图像显示,会有黑边
 * @return
 * - JRTCMediaDeviceVideoCanvas 对象: 开始其他端视频渲染成功
 * - null: 开始其他端视频渲染失败
 */
public abstract JRTCMediaDeviceVideoCanvas startVideo(String streamId, @RenderType int renderType);
/**
 * 停止视频渲染
 *
 * @param canvas JRTCMediaDeviceVideoCanvas 对象,由 {@link #startVideo startVideo} 或 {@link #startCameraVideo startCameraVideo} 接口返回
 */
public abstract void stopVideo(JRTCMediaDeviceVideoCanvas canvas);

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

示例代码:

这里用 JavaSwing 的使用为例,JavaSwing 是 Java 桌面的 UI 库,Java 编写桌面程序的重要资源。JavaSwing中常用的界面对象 JFrame,自定义类继承 JFrame 就可以进一步实现窗口。

public class MyVideoFrame extends JFrame {
   
    public MyVideoFrame() {
        ......
    }
}
MyVideoFrame view = new MyVideoFrame();// 继承了JFrame的类,获取对象。在获取对象失败时应该检查类内部UI方法是否有误。
JRTCMediaDeviceVideoCanvas remoteCanvas = mediaDevice.startVideo(getComponentPointer(view), agent.getGuestParticipant().getRenderId(), RENDER_FULL_CONTENT);

// 以下提供两种获取窗口句柄的方法,使用其中一个即可。如果出现黑屏应该先检查句柄是否获取正确。
//Windows窗口句柄可以通过如下方式获取,swing只支持JFrame窗口控件渲染视频,awt支持Pannel等
public static long getComponentPointer(Component component) {
    long peerValue = -1;
    Pointer componentPointer = Native.getComponentPointer(component);
    Class a = componentPointer.getClass();
    try {
        Field peer = a.getDeclaredField("peer");
        peer.setAccessible(true);
        try {
            peerValue = (long) peer.get(componentPointer);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    } catch (NoSuchFieldException e) {
        e.printStackTrace();
    }
    return peerValue;
}
//UOS&Kylin窗口句柄可以通过如下方式获取,swing只支持JFrame窗口控件渲染视频,awt支持Pannel等
public static long getComponentPointer(Component component) {
    if (component == null) {
        return -1;
    }
    if (!component.isDisplayable()) {
        return -1;
    }
    XBaseWindow xBaseWindow = (XBaseWindow) component.getPeer();
    return xBaseWindow.getContentWindow();
    }
}

# 结束通话

当业务办理结束,访客可以主动结束通话,座席也可以调用 term (opens new window) 接口主动结束通话。

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

主动和被动的挂断事件与来电、接通一样,都通过 onCallStateChanged (opens new window) 接口上报,其中 CallTermReason (opens new window) 可以用来判断挂断原因,如:主动挂断、对端挂断等,详细可参考接口文档或者 SDK 注释。

示例代码:

// 座席主动结束通话
agent.term();

// 通话状态改变回调
public void onCallStateChanged(@AgentCallStateChangeType int type, @CallIncomingType int incomingType, JRTCInviter inviter, @CallTermReason int reason) {
    if (type == JRTCCallCenter.AGENT_CHANGE_TYPE_TERMED) {
     	// 通话结束,结束原因查看 reason
    }
}

# 销毁本地和远端画面

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

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

示例代码:

// 通话结束
public void onCallStateChanged(@AgentCallStateChangeType int type, @CallIncomingType int incomingType, JRTCInviter inviter, @CallTermReason int reason) {
    if (type == JRTCCallCenter.AGENT_CHANGE_TYPE_TERMED) {
     	mediaDevice.stopVideo(localCanvas);
		mediaDevice.stopVideo(remoteCanvas);
    }
}
// 成员离开
public void onMemberLeave(JRTCRoomParticipant part) {
     // 停止该成员画面渲染
     mediaDevice.stopVideo(remoteCanvas);
}

# 登出

座席结束通话后,可以做登出操作;登出接口调用流程如下所示:

@startuml

autonumber

participant "用户" as User
participant "JRTCSDK" as JRTCSDK #orange

activate JRTCSDK

User -> JRTCSDK: client.logout()

group 登出成功
JRTCSDK -> User: onLogout(reason)
end

group 登出失败,强制登出
JRTCSDK -> User: onLogout(reason)
end

@enduml

座席可以登出视频平台,与平台断开一切连接。

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

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

/**
 * 登出回调
 *
 * @param reason 登出原因
 */
void onLogout(@JRTCEnum.ReasonCode int reason);

示例代码:

// 调用登出接口
client.logout();

// 监听登出结果回调
public void onLogout(@ReasonCode int reason) {
    // 登出完成   
}

# 登录状态改变通知

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

/**
 * 登录状态变化通知
 * @param state 当前状态值
 * @param oldState 之前状态值
 */
void onClientStateChanged(@JRTCClient.ClientState int state, @JRTCClient.ClientState int oldState);

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

示例代码:

//登录状态改变通知
public void onClientStateChanged(@ClientState int state, @ClientState int oldState) {
    //state 当前状态
    //oldState 之前状态
}

# 销毁SDK

每个模块都有对应的销毁接口。如不需再使用 SDK 的相关功能,可以强制释放 SDK 的资源。

注:该方法为同步调用,调用此方法后,你将无法再使用该模块的其它方法和回调。 我们不建议在 JRTC SDK 的所有回调方法中调用此方法销毁对象,否则可能出现崩溃现象。

/**
 * 销毁 JRTCAgent 对象
 *
 * @note 该方法为同步调用,需要等待 JRTCAgent 实例资源释放后才能执行其他操作,调用此方法后,你将无法再使用 JRTCAgent 的其它方法和回调。<br>
 * 我们 **不建议** 在 JRTCSDK 的回调中调用此方法销毁 JRTCAgent 对象,否则可能出现崩溃现象。<br>
 * 如需在销毁后再次创建 JRTCAgent 实例,需要等待 destroy 方法执行结束后再创建实例。
 */
public static void destroy();

示例代码:

JRTCAgent.destroy();
JRTCMediaDevice.destroy();
JRTCClient.destroy();

# 4. 实现视频通话(房间)

# 加入房间

joinCall (opens new window)设置自己的角色直接通过房间号加入会议。需要先签入并且示闲,目前只支持以观察者座席PART_ROLE_MAIN_AGENT (opens new window)和次座席PART_ROLE_AGENT (opens new window)角色加入。同样可接收onCallStateChanged (opens new window)通知

int partRole = -1;
switch (mCBPartRole.getSelectedIndex()) {
    case 1:
        partRole = PART_ROLE_AGENT;
        break;
    case 2:
        partRole = PART_ROLE_VIEWER_AGENT;
        break;
    default:
        break;
}
if (partRole == -1) {
    JOptionPane.showMessageDialog(null, "加入失败,目前只支持以观察者座席和次座席角色加入", "提示", JOptionPane.ERROR_MESSAGE);
} else {
    if (!agent.joinCall(serialNumber, partRole)) {
        JOptionPane.showMessageDialog(null, "加入失败", "提示", JOptionPane.ERROR_MESSAGE);
    }
}

# 获取房间号

获取房间号 getRoomId (opens new window)

/**
 * 获取房间号
 *
 * @return 房间号
 */
public abstract String getRoomId();

# 切换自己在通话中角色

switchPartRole (opens new window)切换自己在通话中角色,需要先进入通话,目前只支持从观察者座席角色切换到主座席角色。成员角色partRole (opens new window):PART_ROLE_MAIN_AGENT 主座席;PART_ROLE_AGENT 次座席。

if (agent.switchPartRole(partRole) == -1) {
    JOptionPane.showMessageDialog(null, "切换角色失败", "提示", JOptionPane.ERROR_MESSAGE);
}

切换自己在通话中角色结果回调:onSwitchPartRoleResult (opens new window)

int operationId 操作id,对应 switchPartRole接口的返回值
boolean result      切换自己在通话中角色结果
                    - true:  操作成功
                    - false: 操作失败
String error       切换自己在通话中角色操作失败原因

示例代码;

@Override
public void onSwitchPartRoleResult(int operationId, boolean result, String error) {
    super.onSwitchPartRoleResult(operationId, result, error);
    BridgeNotify.NotifySwitchPartRoleResult switchPartRoleResult = new BridgeNotify.NotifySwitchPartRoleResult();
    switchPartRoleResult.result = result;
    switchPartRoleResult.error = error;
    BaseSocketEngine.sendMessage(switchPartRoleResult);
    BaseSocketEngine.sendMessage(switchPartRoleResult);
}

# 踢出通话成员

kickParticipant (opens new window)踢出通话成员,只有主座席角色可以使用该功能。

if (agent.kickParticipant(userId) == -1) {
    JOptionPane.showMessageDialog(null, "踢出成员失败", "提示", JOptionPane.ERROR_MESSAGE);
}

踢出通话成员结果回调:onKickParticipantResult (opens new window)

int operationId 操作id,对应kickParticipant 接口的返回值
boolean result      踢出通话成员结果
                    - true:  操作成功
                    - false: 操作失败
String error       踢出通话成员操作失败原因

示例代码:

@Override
public void onKickParticipantResult(int operationId, boolean result, String error) {
    super.onKickParticipantResult(operationId, result, error);
    BridgeNotify.NotifyKickParticipantResult kickParticipantResult = new BridgeNotify.NotifyKickParticipantResult();
    kickParticipantResult.result = result;
    kickParticipantResult.error = error;
    BaseSocketEngine.sendMessage(kickParticipantResult);
}

# 5. SIP通话

# 设置通话全局属性

setAcdProperty (opens new window)设置通话全局属性,目前用于有SIP成员通话中,改变SIP通话的属性。

(1)value 属性的value值;

(2)key 属性的key值AcdPropertyType (opens new window),除枚举提供得key之外也可以自定义;

/**
 * 未知属性类别
 */
int ACD_PROPERTY_TYPE_UNKNOWN = -1;
/**
 * SIP音视频属性
 */
int ACD_PROPERTY_TYPE_SIP_VIDEO = 0;

(3) publishType 发布类型AcdPropertyPublishType (opens new window)

/**
 * 未知类型
 */
int ACD_PROPERTY_PUBLISH_TYPE_UNKNOWN = -1;
/**
 * 广播全员
 */
int ACD_PROPERTY_PUBLISH_TYPE_BROADCAST = 0;
/**
 * 指定发送
 */
int ACD_PROPERTY_PUBLISH_TYPE_DIRECT_SEND = 1;
/**
 * 需要等待确定
 */
int ACD_PROPERTY_PUBLISH_TYPE_WAIT_RESPONSE = 2;

示例代码:

agent.setAcdProperty(ACD_PROPERTY_TYPE_SIP_VIDEO, String.valueOf(mCBACDCallType.getSelectedIndex()), ACD_PROPERTY_PUBLISH_TYPE_WAIT_RESPONSE);

# 收到通话全局属性变化通知

onAcdPropertyChange (opens new window)收到通话全局属性变化通知,通话中,修改通话全局属性属性其他成员将会收到该通知,根据消息发布类型进行响应。消息发布类型为ACD_PROPERTY_PUBLISH_TYPE_WAIT_RESPONSE (opens new window)时,需要本端确认是否同意切换。通过调用confirmAcdPropertyChange (opens new window) 来确认是否同意属性修改

@Override
public void onAcdPropertyChange(String userId, @JRTCCallCenter.AcdPropertyType int key, String value, boolean needConfirm) {
    super.onAcdPropertyChange(userId, key, value, needConfirm);
    // 响应别人的要求
    // 用于sip通话中切换sip音视频,升级视频需要同意,降级音频不需要用户同意。目前适用于浙江移动和一站式外呼业务逻辑。
    if (key == ACD_PROPERTY_TYPE_SIP_VIDEO) {
        
    }
}

# 收到通话全局属性属性确认结果

onAcdPropertyResponse (opens new window)收到通话全局属性属性确认结果,通话中本端修改通话全局属性属性,消息发布类型如果为ACD_PROPERTY_PUBLISH_TYPE_WAIT_RESPONSE (opens new window),则其他成员响应后本端将会收到该通知。消息发布类型为其他类型,也会收到该通知,则结果为默认同意。

@Override
public void onAcdPropertyResponse(boolean accept, String userId, @JRTCCallCenter.AcdPropertyType int key, String value) {
    super.onAcdPropertyResponse(accept, userId, key, value);
    // 执行自己的要求
    // 用于sip通话中切换sip音视频,升级视频需要同意,降级音频不需要用户同意。目前适用于浙江移动和一站式外呼业务逻辑。
    if (key == ACD_PROPERTY_TYPE_SIP_VIDEO) {
        
    }
}