iOS

# 实现视频通话

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

当您成功初始化 Juphoon RTC SDK 之后,您可以简单体验本产品的基本业务流程。

# 1. 前提条件

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

  • 已获取 App Key。

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

TIP

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

image-20210315095937896
  • 集成 SDK(Android)。

    TIP

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

# 2. 快速跑通Sample

  1. 在Juphoon RTC SDK 文档中心,选择视频客服-访客选择项,在SDK下载页面下载体验 Sample 示例项目。

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

img

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

img

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

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

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

  4. c. 点击访客按钮,即可进入对应访客页面;

img

  1. 进入访客页面后,有两种呼叫座席的方式,业务号呼叫和指定座席呼叫,业务号可以通过查询业务号列表获取。

  2. a. 业务号呼叫,输入业务号,然后点击呼叫;

  3. b. 指定座席呼叫,输入座席用户ID,然后点击直呼;

img

# 3. 实现视频通话

img

# 初始化

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

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

JRTCClient (opens new window) 基础模块 负责视频平台的登录登出,只有登录到视频平台才可以使用视频相关的业务,AppKey 作为同个环境的分域依据,同一个域的终端才能实现互通。
JRTCMediaDevice (opens new window) 媒体模块 负责本地的媒体设备操作,视频画面渲染等功能。
JRTCGuest (opens new window) 访客模块 负责访客的视频业务的处理

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

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

示例代码:

class JRTCManager implements JRTCClientCallback, JRTCGuestCallback, JRTCMediaDeviceCallback {
    public void init() {
        
        JRTCClientInitParam param = new JRTCClientInitParam();
        /**
         * 设置AppKey
         * 用户从 Juphoon RTC 平台上申请的 AppKey 字符串
         */
        param.setAppKey("appKey");
        /**
         * 设置应用名称,比如:菊风访客
         */
        param.setAppName("appName");
        /**
         * 设置是否需要自动加载so库,默认true
         */
        param.setLoadLibrary(true);
        /**
         * 设置接入服务器地址
         */
        param.setServer("server");
        /**
         * 设置SDK信息存储目录,该目录下的log目录为日志目录
         */
        param.setSdkInfoDir("sdkInfoDir");

        client = JRTCClient.create(context, this, param);
        mediaDevice = JRTCMediaDevice.create(client, this, null);
        guest = JRTCGuest.create(client, mediaDevice, this);
        
        //设置基本参数
		client.setServer("server");
        client.SetAppKey("appKey");
        client.setDisplayName("displayName");
        client.setAppName("appName");
    }
}

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

# 登录

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

img

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

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

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

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

JRTCClientLoginParam (opens new window) 属性介绍

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

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

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

示例代码:

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

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

# 获取业务号列表

在发起呼叫前,可以先调用 queryAllGroups (opens new window)接口获取业务号、业务描述等详细信息;业务号一般由业务管理人员在业务管理平台上配置业务,然后将业务号给开发人员,或者也可以通过该接口查询到所有的业务号列表进行业务。

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

获取结果通过实现 JRTCGuestCallback (opens new window) 中的 onGetAllGroups (opens new window) 接口获取。

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

示例代码:

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

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

# 发起呼叫

在登录成功之后,访客就可以通过发起呼叫的接口来呼叫自己要做的业务;也可以发起呼叫的同时,设置呼叫参数;JRTCCallCenterCallParam (opens new window) 包含了很多呼叫参数,比如随路参数、SVC等。

访客有两个呼叫方法,分别为 call (opens new window)oneToOneCall (opens new window) 如下:

/**
 * 呼叫指定业务
 *
 * @param number 业务号,如10087,一般由业务管理人员在业务管理平台上配置业务,然后将业务号给开发人员
 * @param callParam 呼叫参数设置,可以设置通话分辨率、全局宽高比等参数,此参数可传 null 则使用默认配置,详见 {@link JRTCCallCenterCallParam JRTCCallCenterCallParam}
 * @return 接口调用结果
 * - true: 接口调用成功,通话状态会通过 {@link JRTCGuestCallback#onCallStateChanged onCallStateChanged} 回调上报
 * - false: 接口调用异常
 */
public abstract boolean call(String number, JRTCCallCenterCallParam callParam);
    
/**
 * 呼叫指定座席
 *
 * @param userId 座席 id,如agent1,一般由业务管理人员在业务管理平台上配置座席id,然后将座席id给开发人员
 * @param callParam 呼叫参数设置,可以设置通话分辨率、全局宽高比等参数,此参数可传 null 则使用默认配置,详见 {@link JRTCCallCenterCallParam JRTCCallCenterCallParam}
 * @return 接口调用结果
 * - true: 接口调用成功,通话状态会通过 {@link JRTCGuestCallback#onCallStateChanged onCallStateChanged} 回调上报
 * - false: 接口调用异常
 */
public abstract boolean oneToOneCall(String userId, JRTCCallCenterCallParam callParam);

示例代码:

// 创建呼叫配置参数
JRTCCallCenterCallParam param = new JRTCCallCenterCallParam();
param.setExtraInfo("extraInfo");      //呼叫随路参数
param.setAutoRecord(false);           //不开启自动录制
guest.call("10086", param);            //呼叫指定业务号
guest.oneToOneCall("agent1", param);   //点对点呼叫

# 通话状态改变通知

当访客发起呼叫、通话建立或者通话结束,都会通过 JRTCGuestCallback (opens new window) 里的 onCallStateChanged (opens new window) 接口进行上报。

/**
 * 通话状态改变回调
 *
 * @param type 访客通话状态改变类型,即以下情况会收到此回调:
 * - {@link JRTCGuest#GUEST_CHANGE_TYPE_CALLING GUEST_CHANGE_TYPE_CALLING} 访客呼叫成功 
 * - {@link JRTCGuest#GUEST_CHANGE_TYPE_WAITING GUEST_CHANGE_TYPE_WAITING} 访客呼叫成功后等待座席接听
 * - {@link JRTCGuest#GUEST_CHANGE_TYPE_INCOMING GUEST_CHANGE_TYPE_INCOMING} 作为第三方访客收到通话邀请
 * - {@link JRTCGuest#GUEST_CHANGE_TYPE_TALKING GUEST_CHANGE_TYPE_TALKING} 通话接通(第三方访客)或被接通(主访客)
 * - {@link JRTCGuest#GUEST_CHANGE_TYPE_TERMED GUEST_CHANGE_TYPE_TERMED} 通话挂断或被挂断
 * @param incomingType 来电类型,当 type == {@link JRTCGuest#GUEST_CHANGE_TYPE_INCOMING GUEST_CHANGE_TYPE_INCOMING} 时有效
 * @param inviter 邀请成员对象,当 type == {@link JRTCGuest#GUEST_CHANGE_TYPE_INCOMING GUEST_CHANGE_TYPE_INCOMING} 时有效
 * @param reason 挂断原因,只在 type 为 {@link JRTCGuest#GUEST_CHANGE_TYPE_TERMED GUEST_CHANGE_TYPE_TERMED} 时需要关注,详见 {@link JRTCGuest.CallTermReason}
 */
void onCallStateChanged(@GuestCallStateChangeType int type, @CallIncomingType int incomingType, JRTCInviter inviter, @CallTermReason int reason);

访客通话状态改变详见 GuestCallStateChangeType (opens new window)

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

示例代码:

public void onCallStateChanged(@GuestCallStateChangeType int type, @CallIncomingType int incomingType, JRTCInviter inviter, @CallTermReason int reason) {
    switch (type) {
        case GUEST_CHANGE_TYPE_CALLING:
            //发起呼叫
            break;
        case GUEST_CHANGE_TYPE_WAITING:
            //发起方等待接听
            break;
        case GUEST_CHANGE_TYPE_TALKING:
            //通话建立
            break;        
        case GUEST_CHANGE_TYPE_TERMED:
            //通话结束
            if (reason == JRTCCallCenter.TERM_REASON_JOIN_LICENCE_LIMIT) {
                //结束原因会场人数到达上限
            }
            break;           
        case GUEST_CHANGE_TYPE_INCOMING:
            //来电
            switch (incomingType) {
                case CALL_INCOMING_TYPE_CALL:
                    //普通来电(座席场景)
                break;
                case CALL_INCOMING_TYPE_INVITE:
                    //三方邀请来电
                break;
                    case CALL_INCOMING_TYPE_FORWARD:
                    //转接来电(座席场景)
                break;
                case CALL_INCOMING_TYPE_DIRECT_CALL:
                    //直呼来电
                break;
            }
            break;          
    }
}

# 创建本地视频画面

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

  1. 通过调用 JRTCMediaDevice (opens new window) 里的 startCameraVideo (opens new window) 方法获取本地的视频对象。
  2. 在界面画布上渲染本地视频对象画面;
/**
 * 开始本端视频渲染
 *
 * 获取本端视频预览对象 JRTCMediaDeviceVideoCanvas,通过此对象能获得视图用于UI显示
 * @note
 * 调用此方法时需要保证默认摄像头不为空,即 {@link #defaultCamera} 不为空,否则将直接返回 null
 * @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(@RenderType int renderType);

/**
 * 开始本端视频渲染
 *
 * 获取本端视频预览对象 JRTCMediaDeviceVideoCanvas,通过此对象能获得视图用于UI显示
 * @note
 * 调用此方法时需要保证默认摄像头不为空,即 {@link #defaultCamera} 不为空,否则将直接返回 null
 * @param renderType 渲染模式:
 * - {@link #RENDER_FULL_SCREEN RENDER_FULL_SCREEN} : 铺满窗口,会有裁剪
 * - {@link #RENDER_FULL_CONTENT RENDER_FULL_CONTENT} : 全图像显示,会有黑边
 * - {@link #RENDER_FULL_AUTO RENDER_FULL_AUTO} : 自适应
 * @param useTextureView 是否使用TextureView作为视图控件,默认SurfaceView作为视图控件
 * @return
 * - JRTCMediaDeviceVideoCanvas 对象: 开始自身视频渲染成功
 * - null: 开始自身视频渲染失败
 */
public abstract JRTCMediaDeviceVideoCanvas startCameraVideo(@RenderType int renderType, boolean useTextureView);

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

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

img

示例代码:

// 创建本端视图渲染对象
JRTCMediaDeviceVideoCanvas localCanvas = 
    mediaDevice.startCameraVideo(JRTCMediaDevice.RENDER_FULL_SCREEN);
// 获取视频渲染组件
SurfaceView view = localCanvas.getVideoView();
// 将渲染视图添加到界面上
viewGroup.addView(view);

# 创建远端视频画面

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

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

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

/**
 * 开始其他端的视频渲染
 *
 * 获取其他端的视频预览对象 JRTCMediaDeviceVideoCanvas,通过此对象能获得视图用于UI显示
 *
 * @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(String streamId, @RenderType int renderType);

/**
 * 开始其他端的视频渲染
 *
 * 获取其他端的视频预览对象 JRTCMediaDeviceVideoCanvas,通过此对象能获得视图用于UI显示
 *
 * @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} : 自适应
 * @param useTextureView 是否使用TextureView作为视图控件,默认SurfaceView作为视图控件
 * @return
 * - JRTCMediaDeviceVideoCanvas 对象: 开始自身视频渲染成功
 * - null: 开始自身视频渲染失败
 */
public abstract JRTCMediaDeviceVideoCanvas startVideo(String streamId, @RenderType int renderType, boolean useTextureView);

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

示例代码:

// 创建其他端视图渲染对象
JRTCMediaDeviceVideoCanvas remoteCanvas = 
    mediaDevice.startVideo("视频流ID",JRTCMediaDevice.RENDER_FULL_SCREEN);
// 将渲染视图添加到界面上
viewGroup.addView(remoteCanvas.getVideoView());

# 结束通话

访客可以在呼叫等待或者通话中调用 term (opens new window) 接口主动取消或者结束通话。

/**
 * 结束通话
 * @note
 * - 主访客调用此接口会结束通话,通话中所有成员都会离开,此通通话销毁,所有成员会收到 {@link JRTCAgentCallback#onCallStateChanged onCallStateChanged} 或 {@link JRTCGuestCallback#onCallStateChanged onCallStateChanged} 通话结束回调。
 * - 第三方访客调用此接口仅自身离开通话,通话中其他成员会收到该成员离开的回调 {@link JRTCGuestCallback#onMemberLeave onMemberLeave} 或 {@link JRTCAgentCallback#onMemberLeave onMemberLeave} 回调,通话继续进行。
 * @return 接口调用结果
 * - true: 接口调用成功,会收到 {@link JRTCGuestCallback#onCallStateChanged onCallStateChanged} 回调
 * - false: 接口调用异常
 */
public abstract boolean term();

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

示例代码:

// 访客主动结束通话
guest.term();

// 通话状态改变回调
public void onCallStateChanged(@GuestCallStateChangeType int type, @CallIncomingType int incomingType, JRTCInviter inviter, @CallTermReason int reason) {
    if (type == JRTCCallCenter.GUEST_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(@GuestCallStateChangeType int type, @CallIncomingType int incomingType, JRTCInviter inviter, @CallTermReason int reason) {
    if (type == JRTCCallCenter.GUEST_CHANGE_TYPE_TERMED) {
     	mediaDevice.stopVideo(localCanvas);
		mediaDevice.stopVideo(remoteCanvas);
    }
}
// 成员离开
public void onMemberLeave(JRTCRoomParticipant part) {
     // 停止该成员画面渲染
     mediaDevice.stopVideo(remoteCanvas);
}

# 登出

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

img

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

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

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

/**
 * 登出回调
 *
 * @param reason 登出原因
 */
void onLogout(@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(@ClientState int state, @ClientState int oldState);

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

示例代码:

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

# 销毁SDK

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

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

public static void destroy()

示例代码:

JRTCGuest.destroy();
JRTCMediaDevice.destroy();
JRTCClient.destroy();