# 实现视频双录

本文档为您展示通过 SDK 实现视频双录(无排队机)的相关步骤,帮助您在多人视频通话场景下实现创建会议、邀请新成员加入、结束/离开会议的相关能力。

# 加入会议

通过JCMediaChannelJoinParam (opens new window)可在加入会议前设置是否参会最大人数,参会密码,最大分辨率,全局长宽比等。

JCMediaChannelJoinParam joinParam=new JCMediaChannelJoinParam();
joinParam.setUploadLocalAudio(true);//是否上传本地音频流
joinParam.setUploadLocalVideo(true);//是否上传本地视频流
mMediaChannel.join(channelId, joinParm);

加入频道结果通过 onJoin (opens new window) 回调:

/**
 * 加入频道结果回调
 *
 * @param result    true 表示成功,false 表示失败
 * @param reason    加入失败原因,当 result 为 false 时该值有效
 * @param channelId 频道标识符
 */
void onJoin(boolean result, @JCMediaChannel.MediaChannelReason int reason, String channelId, JCMediaChannel mediaChannel);

示例代码:

// 加入频道结果回调
public void onJoin(boolean result, @JCMediaChannel.MediaChannelReason int reason, String channelId, JCMediaChannel mediaChannel) {
    if (result) {
        // 加入频道成功
    } else {
        // 加入频道失败
    }
}

TIP

当加入会议后可通过 MediaChannel.getParticipants 获取已经在会议中的成员 然后渲染当前所有成员(requestVideo,startVideo)

# 创建本地视频画面

加入频道后,如果想看到本地视图画面,需要打开摄像头以创建视图画面。

创建视图画面需要用到 JCMediaDevice (opens new window) 类和 JCMediaDeviceVideoCanvas (opens new window) 类。

本地视图渲染调用 startCameraVideo (opens new window) 接口。

TIP

startCameraVideo (opens new window) 接口会打开摄像头

/**
 * 获得视频预览对象,通过此对象能获得视图用于UI显示
 *
 * @param renderType    渲染模式
 * @return              JCMediaDeviceVideoCanvas 对象
 * @see RenderType
 */
public abstract JCMediaDeviceVideoCanvas startCameraVideo(@RenderType int renderType);

TIP

调用该方法后,在挂断通话或者关闭摄像头时需要对应调用 stopVideo (opens new window) 方法停止视频

示例代码:

// 创建本地视频画面对象
JCMediaDeviceVideoCanvas localCanvas = mediaDevice.startCameraVideo(JCMediaDevice.RENDER_FULL_CONTENT);
viewGroup.addView(localCanvas.getVideoView(), 0);

# 新成员加入

当新成员加入频道后,其他成员会收到成员加入的回调。

/**
 * 新成员加入回调
 *
 * @param participant 成员对象
 */
void onParticipantJoin(JCMediaChannelParticipant participant, JCMediaChannel mediaChannel);

此时可以进行远端视图渲染并请求远端成员的视频流。

# 创建远端视频画面

远端视频渲染调用 startVideo (opens new window) 接口获取视频对象。

/**
 * 获得视频对象,通过此对象能获得视图用于UI显示
 *
 * @param videoSource   渲染标识串,比如 JCMediaChannelParticipant JCCallItem 中的 renderId
 * @param renderType    渲染模式
 * @return              JCMediaDeviceVideoCanvas 对象
 * @see RenderType
 */
public abstract JCMediaDeviceVideoCanvas startVideo(String videoSource, @RenderType int renderType);

TIP

调用该方法后,在挂断通话或者关闭摄像头时需要对应调用 stopVideo (opens new window) 方法停止视频。

由于服务器默认是不转发视频数据的,所以如果想看到远端成员画面需要请求远端成员的视频流。

/**
 * 请求频道中其他用户的视频流
 * 当 pictureSize 为 JCMediaChannelPictureSizeNone 时表示关闭请求
 *
 * @param participant   频道中其他成员对象
 * @param pictureSize   视频请求的尺寸类型
 * @return              返回 true 表示正常执行调用流程,false 表示调用异常
 * @see JCMediaChannel.PictureSize
 */
public abstract boolean requestVideo(JCMediaChannelParticipant participant, @PictureSize int pictureSize);

其中,视频尺寸 JCMediaChannelPictureSize 有以下几种:

名称 描述
public static final int PICTURESIZE_NONE = 0 不请求
public static final int PICTURESIZE_MIN = 1 最小尺寸,160x90
public static final int PICTURESIZE_SMALL = 2 小尺寸,320x180,小窗口模式下可以使用小尺寸
public static final int PICTURESIZE_LARGE = 3 大尺寸,640x360
public static final int PICTURESIZE_MAX = 4 最大尺寸,360P 的会议为 640x360 ,720P 的会议为 1280x720

TIP

您可以根据相应的窗口大小使用相应的视频尺寸,比如窗口的大小是 160x90,则应该使用 JCMediaChannelPictureSizeMin,避免造成不必要的流量浪费和额外的功耗。

示例代码:

public void onParticipantJoin(JCMediaChannelParticipant participant) {
    // 创建远端视频画面对象,renderId来源JCMediaChannelParticipant对象
    List<JCMediaChannelParticipant> partps = mediaChannel.getParticipants();
    JCMediaChannelParticipant item = partps.get(0);
    String renderId = item.getRenderId();
    JCMediaDeviceVideoCanvas remoteCanvas = mediaDevice.startVideo(renderId, JCMediaDevice.RENDER_FULL_CONTENT);
    viewGroup.addView(remoteCanvas.getVideoView(), 0);
    // 请求远端视频流,participant为JCMediaChannelParticipant对象
    mediaChannel.requestVideo(participant, JCMediaChannel.PICTURESIZE_LARGE);
}

# 离开会议

如果想要离开频道,可以调用以下接口。

/**
 * 离开频道
 *
 * @return 返回 true 表示正常执行调用流程,false 表示调用异常
 */
public abstract boolean leave();

如果是多方视频通话,则在离开频道后还需要调用 stopVideo (opens new window) 接口移除视频画面。

/**
 * 停止视频
 *
 * @param canvas JCMediaDeviceVideoCanvas 对象,由 startVideo 获得
 */
public abstract void stopVideo(JCMediaDeviceVideoCanvas canvas);

离开频道后,UI监听回调离开的原因:

/**
 * 离开频道结果回调
 *
 * @param reason    离开原因
 * @param channelId 频道标识符
 */
void onLeave(@JCMediaChannel.MediaChannelReason int reason, String channelId);

示例代码:

// 离开频道结果回调
public void onLeave(@JCMediaChannel.MediaChannelReason int reason, String channelId) {
    // 销毁视频
    JCManager.getInstance().mediaDevice.stopVideo(canvas);
    constraintLayout.removeView(canvas.getVideoView());
    canvas = null;
}

# 成员离开

当成员离开频道后,其他成员会收到成员离开的回调。

/**
 * 成员离开回调
 *
 * @param participant  成员对象
 * @param reason       成员离开原因
 * @param mediaChannel 当前mediaChannel对象
 */
 void onParticipantLeft(JCMediaChannelParticipant participant, int reason, JCMediaChannel mediaChannel);

# 结束会议

如果想结束会议,可以调用下面的接口,此时所有成员都将被退出。

/**
 * 关闭频道,所有成员都将被退出
 *
 * @return 返回 true 表示正常执行调用流程,false 表示调用异常
 */
public abstract boolean stop();

示例代码:

// 结束频道
mediaChannel.stop();

关闭频道的回调通过 onStop (opens new window) 进行上报

/**
 * 解散频道结果回调
 *
 * @param result    true 表示成功,false 表示失败
 * @param reason    解散失败原因,当 result 为 false 时该值有效
 */
void onStop(boolean result, @JCMediaChannel.MediaChannelReason int reason);

示例代码:

// 离开频道结果回调
public void onStop(boolean result, @JCMediaChannel.MediaChannelReason int reason) {
    // 销毁视频
    JCManager.getInstance().mediaDevice.stopVideo(canvas);
    constraintLayout.removeView(canvas.getVideoView());
    canvas = null;
}