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

​ 2. 下载完成后,打开安装包,解压 JCCSample:

  • Windows

双击安装 JCCSampleSetup-java.exe

bb.png

  • UOS-X86 和 UOS-ARM

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

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

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

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

cc.png

dd.png

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

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

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

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

​ d. 进入多方页面后,输入房间号,如果有房间密码输入密码,点击加入,即可进入房间。

jj.png

kk.png

# 3. 功能实现

img

# 初始化 SDK

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

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

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

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

示例代码:

class JRTCManager implements JRTCClientCallback, JRTCMediaDeviceCallback, JRTCRoomCallback {
    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 对象
        client = JRTCClient.create(context, this, param);
        // 创建 mediaDevice 对象
        mediaDevice = JRTCMediaDevice.create(client, this, null);
    	// 创建 room 对象
    	room = JRTCRoom.create(client, mediaDevice, this);
        
        //设置基本参数
        client.setServer("server");
        client.SetAppKey("appKey");
        client.setDisplayName("displayName");
        client.setAppName("appName");
    }
}

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

# 登录

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

img

登录到 Juphoon 视频平台主要调用的是 JRTCClient (opens new window) 的登录接口 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("6666", "123456", loginParam);

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

# 加入房间

通过 JRTCRoomJoinParam (opens new window) 可在加入房间前设置房间最大人数,房间密码,最大分辨率等。

/**
 * 加入房间
 *
 * 该方法让用户加入通话房间,在同一个房间内的用户可以互相通话。<br>
 * 如果用户已在房间中,必须退出当前房间,即处于空闲状态,才能进入其他房间,否则将直接返回 false,且不会收到回调通知。
 * @param roomId 房间标识
 * @param joinParam JRTCRoomJoinParam 对象,传 null 则使用默认配置
 * @see JRTCRoomJoinParam
 * @return 接口调用结果
 * - true: 接口调用成功,会收到 {@link JRTCRoomCallback#onJoin onJoin} 回调
 * - false: 接口调用异常
 */
public abstract boolean join(String roomId, JRTCRoomJoinParam joinParam);

加入房间结果通过 onJoin (opens new window) 回调。

/**
 * 加入房间结果回调
 *
 * 调用 {@link JRTCRoom#join join} 接口成功后,会收到此回调。
 * @param result 加入房间是否成功
 * - true: 成功
 * - false: 失败
 * @param reason        加入失败原因,当 result 为 false 时该值有效。失败原因参见:{@link ReasonCode 错误码}
 * @param channelId     房间标识
 * @param room  当前 JRTCRoom 对象
 */
void onJoin(boolean result, @ReasonCode int reason, String channelId, JRTCRoom room);

当加入房间后可通过 getParticipants (opens new window) 获取已经在房间中的成员,然后渲染当前所有成员。

示例代码:

// 创建加入房间参数
JRTCRoomJoinParam param = new JRTCRoomJoinParam();
param.setUploadLocalAudio(true);//是否上传本地音频流
param.setUploadLocalVideo(true);//是否上传本地视频流
// 加入房间
room.join(roomId, param);

// 加入房间结果回调
void JRTCRoomCallback.onJoin(boolean result, @ReasonCode int reason, String roomId, JRTCRoom room){
    if (result == true) {
        // 加入成功
        // 获取当前房间中所有成员
        List<JRTCRoomParticipant> participantList = room.getParticipants();
    } else {
        // 加入失败
    }
}

# 自身在房间中的状态变化

当自身在房间中的状态发生变化时,会收到 onRoomStateChanged (opens new window) 回调,例如加入房间、加入房间成功、离开回调等。

房间状态详见 RoomState (opens new window)

/**
 * 自身在房间中的状态变化回调
 *
 * 当自身在房间中的状态发生变化时,会收到此回调,例如加入房间、加入房间成功、离开回调等。
 * 状态:
 * - {@link JRTCEnum#STATE_IDLE STATE_IDLE} : 空闲状态
 * - {@link JRTCEnum#STATE_JOINING STATE_JOINING}: 加入中
 * - {@link JRTCEnum#STATE_JOINED STATE_JOINED} : 已加入
 * - {@link JRTCEnum#STATE_LEAVING STATE_LEAVING} : 离开中
 * @param state        当前状态
 * @param oldState     变化前状态
 * @param room 当前 JRTCRoom 对象
 */
void onRoomStateChanged(@RoomState int state, @RoomState int oldState, JRTCRoom room);

示例代码:

void onRoomStateChanged(@ConfState int state, @ConfState int oldState, JRTCRoom room){
    if (state == JRTCEnum.STATE_IDLE) {
        // 空闲状态
    } else if (state == JRTCEnum.STATE_JOINING) {
        // 加入中   
    } else if (state == JRTCEnum.STATE_JOINED) {
        // 已加入   
    } else if (state == JRTCEnum.STATE_LEAVING) {
        // 离开中   
    }
}

# 创建本地视频画面

初始化成功后,可以创建本地的视频画面,创建本地视频画面的时机没有具体要求,在加入房间前后调用皆可。

  1. 通过 startCameraVideo (opens new window) 方法,获取 JRTCMediaDeviceVideoCanvas (opens new window)本地的视频对象。
  2. 在界面画布上渲染本地视频对象画面。
/**
 * 开始本端视频渲染
 * @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();
    }
}

调用 startCameraVideo (opens new window) 渲染本地视频后方法后,在自己离开房间时需要对应调用 stopVideo (opens new window) 方法停止视频渲染。

# 新成员加入

当新成员加入房间后,其他成员会收到 onParticipantJoin (opens new window) 成员加入的回调。

/**
* 新成员加入回调
*
* 当有用户调用 {@link JRTCRoom#join join} 接口加入房间成功时,已在房间中的成员会收到此回调。
* @param participant  JRTCRoomParticipant 成员对象
* @param room 当前 JRTCRoom 对象
*/
void onParticipantJoin(JRTCRoomParticipant participant, JRTCRoom room);

此时可以通过回调获取到成员对象进行远端视图渲染并订阅远端成员的视频流。

示例代码:

// 新成员加入房间
void onParticipantJoin(JRTCRoomParticipant participant, JRTCRoom room ) {
    // 订阅该成员视频流
    room.requestVideo(participant, videoSize);
    // 渲染该成员视频画面
    JRTCMediaDeviceVideoCanvas canvas = mediaDevice.startVideo(participant.getStreamId(), JRTCMediaDevice.RENDER_FULL_CONTENT); 
    SurfaceView view = canvas.getVideoView();
    viewGroup.addView(view);
}

# 成员更新

当房间内成员状态发生改变时,其他成员能收到该成员状态变化通知 onParticipantUpdate (opens new window),具体成员变化属性参考 JRTCRoomParticipant.ChangeParam (opens new window),包含成员音量、网络状态、音视频上传状态、成员类型、视频订阅尺寸变化等。

/**
 * 成员属性更新回调
 *
 * 当房间中有成员的属性发生变化时,房间中的其他成员会收到此回调,例如音频上传状态、视频上传状态、网络状态等发生变化。
 * @param participant  JRTCRoomParticipant 成员对象
 * @param changeParam  {@link JRTCRoomParticipant.ChangeParam} 更新标识类对象
 * @param room 当前 JRTCRoom 对象
 */
void onParticipantUpdate(JRTCRoomParticipant participant, JRTCRoomParticipant.ChangeParam changeParam, JRTCRoom room);

示例代码:

public void onParticipantUpdate(JRTCRoomParticipant participant, JRTCRoomParticipant.ChangeParam changeParam, JRTCRoom room) {
    if (changeParam.volume) {
        //成员音量大小变化
    } else if (changeParam.volumeStatus) {
        //成员音量状态变化
    } else if (changeParam.netStatus) {
        //成员网络状态变化
    } else if (changeParam.netStatus) {
        //成员网络状态变化
    } else if (changeParam.audio) {
        //成员音频上传状态变化
    } else if (changeParam.video) {
        //成员视频上传状态变化
    } else if (changeParam.videoSize) {
        //订阅该成员视频尺寸变化
    } else if (changeParam.type) {
        //该成员类型变化
    }
}

# 创建远端视频画面

当加入房间后,除了本地的视频画面,还有房间内其他成员的视频画面,如果房间内其他成员成员有视频流上传,本端可以获取到其他成员的的视频流并进行渲染,详细见 4.3.6 章节。

当成员视频状态变化,比如该成员开始上传视频,此时可以去渲染该成员视频,该成员结束上传视频,则可以去停止渲染该成员视频。

# 订阅/取消订阅视频

在渲染成员视频前,需要先通过调用 requestVideo (opens new window) 方法订阅该视频,当成员离开需要调用 unRequestVideo (opens new window) 及时取消订阅该视频流。

/**
 * 订阅房间中其他用户的视频流
 *
 * @param participant  JRTCRoomParticipant 成员对象
 * @param videoSize    视频请求的尺寸,详见 {@link JRTCVideoSize}
 * @return 接口调用结果
 * - true: 接口调用成功,会收到 {@link JRTCRoomCallback#onParticipantUpdate onParticipantUpdate} 回调
 * - false: 接口调用异常
 */
public abstract boolean requestVideo(JRTCRoomParticipant participant, JRTCVideoSize videoSize);

/**
 * 取消订阅房间中其他用户的视频流
 *
 * @param participant  JRTCRoomParticipant 房间中其他成员对象
 * @return 调用是否正常
 * - true: 正常执行调用流程,会收到 {@link JRTCRoomCallback#onParticipantUpdate onParticipantUpdate} 回调
 * - false: 调用失败,不会收到回调通知
 */
public abstract boolean unRequestVideo(JRTCRoomParticipant participant);

# 渲染/停止渲染视频

远端视频渲染可以通过调用 startVideo (opens new window) 来实现,停止渲染通过调用 stopVideo (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);

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

示例代码:

public void onParticipantUpdate(JRTCRoomParticipant participant, JRTCRoomParticipant.ChangeParam changeParam, JRTCRoom room) {
    if (changeParam.video) {
        if (participant.isVideo()) {
            //订阅该成员视频
            room.requestVideo(participant, new JRTCVideoSize(1280, 720));
            //该成员开始上传视频
            mediaDevice.startVideo(getComponentPointer(view), participantgetStreamId(), JRTCMediaDevice.RENDER_FULL_SCREEN);
        } else {
            //取消订阅该成员视频
            room.unRequestVideo(participant);
            //停止渲染该成员视频
            mediaDevice.stopVideo(canvas);
        }
    }
}

// 以下提供两种获取窗口句柄的方法,使用其中一个即可。如果出现黑屏应该先检查句柄是否获取正确。
//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();
    }
}

# 成员离开

当成员离开房间后,其他成员会收到成员离开的回调 onParticipantLeft (opens new window) 回调通知,此时可以调用 stopVideo (opens new window) 取消订阅该成员视频流以及停止渲染该成员视频。

/**
 * 成员离开回调
 *
 * 当房间中有成员调用 {@link JRTCRoom#leave leave} 接口离开房间后,房间中的其他成员会收到此回调。
 * @param participant  成员对象
 * @param reason       成员离开原因
 * @param room 当前 JRTCRoom 对象
 */
void onParticipantLeft(JRTCRoomParticipant participant, @ReasonCode int reason, JRTCRoom room);

示例代码:

public void onParticipantLeft(JRTCRoomParticipant participant, int reason, JRTCRoom room) {
    //其中remoteCanvas对象是通过 startVideo 返回
    mediaDevice.stopVideo(remoteCanvas);
}

# 离开房间并解散房间

如果想离开房间,可以调用下面的接口 leave (opens new window),此时自己会收到 onLeave (opens new window) 回调,房间内其他成员会收到该成员离开的回调 onParticipantLeft (opens new window)

/**
 * 离开房间
 * @return 接口调用结果
 * - true: 接口调用成功,非空闲状态下,会收到 {@link JRTCRoomCallback#onLeave onLeave} 回调
 * - false: 接口调用异常
 */
public abstract boolean leave();
/**
 * 离开房间结果回调
 *
 * 调用 {@link JRTCRoom#leave leave} 接口成功后,会收到此回调。
 * @param reason       离开原因,参见:{@link ReasonCode 离开原因}
 * @param roomId    房间标识
 * @param room 当前 JRTCRoom 对象
 */
void onLeave(@ReasonCode int reason, String roomId, JRTCRoom room);

示例代码:

// 离开房间
room.leave();
// 离开房间结果回调
public void onLeave(@ReasonCode int reason, String roomId, JRTCRoom room) {
    // 可以停止渲染其他成员视频以及本地视频
    //其中localCanvas对象是通过startCameraVideo 返回
    mediaDevice.stopVideo(localCanvas);
    //其中remoteCanvas对象是通过 startVideo 返回
    mediaDevice.stopVideo(remoteCanvas);
}

# 离开房间

如果想离开房间的同时解散会议,可以调用下面的接口 stop (opens new window),此时自己会收到 onStop (opens new window) 回调。其他成员也会收到会议结束的通知。

/**
 * 解散房间
 * 此房间内的成员都将被强制退出
 * @return 接口调用结果
 * - true: 接口调用成功,
 *  - 空闲状态下,不会收到回调
 *  - 非空闲状态下,会收到 {@link JRTCRoomCallback#onStop onStop} 回调
 * - false: 接口调用异常
 */
public abstract boolean stop();
/**
 * 解散房间结果回调
 *
 * 调用 {@link JRTCRoom#stop stop} 接口成功后,会收到此回调。
 * @param result 解散房间是否成功
 * - true:成功
 * - false:失败
 * @param reason       解散失败原因,当 result 为 false 时该值有效。失败原因参见:{@link ReasonCode 失败原因}
 * @param room 当前 JRTCRoom 对象
 */
void onStop(boolean result, @ReasonCode int reason, JRTCRoom room);

示例代码:

// 解散房间
room.stop();
// 解散房间结果回调
public void onStop(boolean result, @ReasonCode int reason, JRTCRoom room) {
   
}

# 把成员踢出房间

把成员踢出房间kickUser (opens new window)。接口调用成功,非空闲状态下,会收到onKickUserResult (opens new window)回调。

/**
 * 把成员踢出房间
 *
 * @param userId 成员用户ID
 * @return 接口调用结果
 * - true: 接口调用成功,非空闲状态下,会收到 {@link JRTCRoomCallback#onKickUserResult(boolean, String, JRTCRoom) onKickUserResult} 回调
 * - false: 接口调用异常
 */
public abstract boolean kickUser(String userId);
/**
 * 踢出成员结果回调
 * <p>
 * 调用 {@link JRTCRoom#kickUser(String) kickUser} 接口成功后,会收到此回调。
 *
 * @param result 操作结果是否成功
 *               - true: 操作成功
 *               - false: 操作失败
 * @param reason 失败原因描述
 * @param room   当前 JRTCRoom 对象
 */
void onKickUserResult(boolean result, String reason, JRTCRoom room);

示例代码:

// 踢掉某人
room.kickUser(userId);
// 踢掉某人结果回调
public void onKickUserResult(boolean result, String reason, JRTCRoom room) {
   
}

# 销毁本地和远端画面

当不再需要查看视频画面,包括房间成员离开,或者离开房间,需要调用 stopVideo (opens new window) 接口来停止渲染的资源,该方法需传入要释放的 JRTCMediaDeviceVideoCanvas (opens new window) 对象,或者调用 stopAllVideos (opens new window) 接口来时停止所有正在渲染的视频,必须进行这步操作,不然会造成渲染内存不释放。

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

/**
* 停止所有视频渲染
*/
public abstract void stopAllVideos();

示例代码:

// 离开房间结果回调
public void onLeave(@ReasonCode int reason, String roomId, JRTCRoom room) {
    // 可以停止渲染其他成员视频以及本地视频
    //其中localCanvas对象是通过startCameraVideo 返回
    mediaDevice.stopVideo(localCanvas);
    //其中remoteCanvas对象是通过 startVideo 返回
    mediaDevice.stopVideo(remoteCanvas);
    // 停止所有正在渲染的视频
    mediaDevice.stopAllVideo();
}

public void onParticipantLeft(JRTCRoomParticipant participant, int reason, JRTCRoom room) {
    //其中remoteCanvas对象是通过 startVideo 返回
    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

成员可以通过调用 logout (opens new window) 方法登出视频平台,与平台断开一切连接。

/**
* 登出 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 的所有回调方法中调用此方法销毁对象,否则可能出现崩溃现象。

public static void destroy()

示例代码:

JRTCRoom.destroy();
JRTCMediaDevice.destroy();
JRTCClient.destroy();