# 实现视频会议

本文档为您展示通过 SDK 实现视频会议的相关步骤,帮助您在多人会议场景下实现创建/加入会议、查询会议、结束/离开会议、发送消息等相关能力。

开始之前,请您先做好如下准备工作:

  • Windows SDK 下载
  • Windows 配置和初始化
  • Windows 登录

如果您已经做好相关准备工作,即可继续以下的内容。

# 模块初始化

开始集成会控功能前,请先进行模块的初始化。

// 初始化各模块,因为这些模块实例将被频繁使用,建议声明在单例中
JCClient _client = JCClient.create(Application ,"your appkey", this, null);
JCMediaDevice _mediaDevice = JCMediaDevice.create(_client, this);
JCConference _conference = JCConference.Create(_client, _mediaDevice, this);

其中,创建 JCConference (opens new window) 实例的接口如下:

/// <summary>
/// 创建JCConference对象
/// </summary>
/// <param name="client"> JCClient 对象</param>
/// <param name="mediaDevice">JCMediaDevice 对象</param>
/// <param name="callback">实现 JCConferenceCallback 接口的对象,用于接收 JCConference 相关通知</param>
/// <returns>JCConference对象</returns>
public static JCConference Create(JCClient.JCClient client, JCMediaDevice.JCMediaDevice mediaDevice, JCConferenceCallback callback)

会控模块涉及以下类:

名称 描述
JCConference 会控模块类
JCConferenceInfo 会议对象类
JCConferenceParticipant 会议成员类
JCConferenceReserveInfo 预约会议信息类
JCConferenceInviteInfo 邀请成员类
JCConferenceCommandInfo 会议属性变化或会议其他通知
JCConferenceQueryConfResult 预约会议查询结果类
JCConferenceCandidate 被邀请者对象类
JCConferenceCallback 回调代理
JCConferenceRecordRemoteParams 远程录制参数类
JCConferenceSipInviteInfo Sip呼叫参数类

更多关于类的详细信息请查看对应的API文件。

# 基础业务接口调用

fullsizeoutput_1.jpeg

# 创建并加入会议

/// <summary>
/// 创建并加入一个会议,结果回调 onConferenceJoin
/// </summary>
/// <param name="confNumber">传 "0" 由服务器生成10位的会议号,可传字母或数字作为自定义会议号</param>
/// <param name="video">true 视频会议,false 语音会议</param>
/// <param name="config">会议的其他配置,具体查看JCConferenceConfig的配置参数</param>
/// <returns>true 表示接口调用成功</returns>
public bool Start(string confNumber, bool video, Dictionary<string, string> config)

其中,配置关键字有:

配置项 描述 关键字
设置会议人数 默认8人,发起会议时可携带 Capacity
设置会议title 发起会议时可携带 Title
设置会议密码 发起或加入会议时可携带 Password
会议的扩展字段 用于设置会议的自定义信息,发起会议时可携带 ConfExpand
设置会议中视频的比例 16:9或正方形,正方形设置 true,16:9设置 false,默认视频为16:9,发起会议时可携带 VideoSquare
设置会议平滑模式 开启设置 true,不开启设置 false,默认不开启。发起会议时可携带 SmoothMode
最大分辨率 360p设置”0” 720p设置”1” 1080p设置”2”,默认360p, 发起会议时可携带,取值枚举JCConferenceMaxResolution MaxResolution
CDN地址参数 发起会议时可携带 CDN
设置会议最大发送路数 范围1~16,默认16,发起会议时可携带 MaxSender
如果会议不存在是否创建并加入 创建设置 true,不创建设置 false,默认不创建 ConfCloseWhenAlone
设置昵称(自己) 发起或加入会议时可携带 ParticipantDisplayName
成员的扩展字段 用于设置成员(自己)的自定义信息,发起或加入会议时可携带 ParticipantExpand
创会者决定其他成员入会时的默认角色 视频设置”0” 音频设置”1” 观众设置”2”,默认视频,取值枚举JCConferenceParticipantType DefaultMemberRole
是否开启服务器录制 开启传true,不开启传false RemoteRecord
是否固定会议分辨率(360p) 固定传true,不固定传false FixedResolution
如果会议不存在是否创建并加入 创建设置 true,不创建设置 false,默认不创建 ConfCreatAndJoin
是否音视频默认开启 开启传true,则加入会议成功后选择默认音频输入输出设备以及默认摄像头打开;否则传false,需要应用实现打开关闭音频设备和摄像头,默认开启 MediaHosting
CDN推流和服务器录制分辨率 360p设置”0”,720p设置”1”, 发起会议时可携带,取值枚举JCConferenceCDRecResolution CDRecResolution
成员入会时的角色 视频设置”0” 音频设置”1” 观众设置”2”,默认视频,取值枚举JCConferenceParticipantType ParticipantJoinRole
设置(自己)为主持人 发起或加入会议时可携带 ParticipantSetChairman

示例代码:

JSONObject object = new JSONObject();
object.put(CONFIG_CAPACITY,"6");
object.put(CONFIG_TITLE,"会议title");
conference.Start("confNumber",true,null);

调用该方法后会收到 onConferenceJoin (opens new window) 回调,该回调返回加入的结果。

/// <summary>
/// 加入会议结果回调
/// </summary>
/// <param name="result">调用成功失败</param>
/// <param name="reason">失败原因:JCConferenceReason的AlreadyJoined、TimeOut、Full、InvalidPassword、Lock、Other</param>
void onConferenceJoin(bool result, int reason);

其中,加入失败的原因 JCConferenceReason (opens new window) 有:

/// <summary>
/// 正常
/// </summary>
None = 0,


/// <summary>
/// 未登录
/// </summary>
NotLogin,


/// <summary>
/// 超时
/// </summary>
TimeOut,


/// <summary>
/// 网络异常
/// </summary>
NewWorkError,


/// <summary>
/// 被踢
/// </summary>
Kicked,


/// <summary>
/// 掉线
/// </summary>
Offline,


/// <summary>
/// 主动离开
/// </summary>
Quit,


/// <summary>
/// 会议结束
/// </summary>
Over,


/// <summary>
/// 成员满
/// </summary>
Full,


/// <summary>
/// 无效密码
/// </summary>
InvalidPassword,

/// <summary>
/// 会议被锁定
/// </summary>
Locked,

/// <summary>
/// 群组已有会议
/// </summary>
GroupConfExist,

/// <summary>
/// 群组没有会议
/// </summary>
NoConfInGroup,

/// <summary>
/// 该会议号的会议不存在
/// </summary>
ConfNumberNotFound,

/// <summary>
/// 会议号已存在
/// </summary>
ConfNumberExist,

/// <summary>
/// 服务器会议成员总数上限(移动端会议人数)
/// <summary>
ConfAppConcurrencyFul,

/// <summary>
///服务器会议成员总数上限(总会议人数)
/// <summary>
ConfAllConcurrencyFul,

/// <summary>
///该会议已经结束(已经结束的预约会议不能重新通过join接口加入)
/// <summary>
ConfAlreadyEnded,

/// <summary>
/// 其他错误
/// </summary>
Other = 100

示例代码:

public void onConferenceJoin(bool result, int reason)
{
  if (result)
  {
    // 加入成功的处理
  } else
  {
    // 加入失败的处理
  }
}

# 结束会议

stopconf.png

如果想结束会议,可以调用下面的方法,只有主持人才能调用该接口结束会议。

/**
 * 结束会议,主持人才能调用该接口结束会议,结果回调 onConferenceLeave
 *
 * @return true 表示接口调用成功
 */
public abstract boolean stop();

调用该方法会收到 onConferenceLeave (opens new window) 回调。

示例代码:

conference.stop();

# 加入已经存在的会议

joinconf.png

如果要加入已经存在的会议,则调用下面的接口,调用该接口会收到 onConferenceJoin (opens new window) 回调。

/// <summary>
/// 加入会议,结果回调 onConferenceJoin
/// </summary>
/// <param name="confNumber">会议号</param>
/// <param name="video">接口暂时没用该参数</param>
/// <param name="config">会议的其他配置,具体查看JCConferenceConfig的配置参数</param>
/// <returns>true 表示接口调用成功</returns>
public bool Join(string confNumber, bool video, Dictionary<string, string> config)

示例代码:

conference.Join("confNumber",true, null);

加入会议成功后会自动打开音频设备。

# 离开会议

leaveconf.png

如果想离开会议,则调用下面的接口。

/// <summary>
/// 离开会议,结果回调 onConferenceLeave
/// </summary>
/// <returns>true 表示接口调用成功</returns>
public bool Leave()

示例代码

conference.Leave();

离开会议成功后会收到 onConferenceLeave (opens new window) 回调,该回调会返回离开会议的原因。

/// <summary>
/// 离开会议回调
/// </summary>
/// <param name="reason">离开原因: JCConferenceReason的Kicked、Offline、Quit、Over、Other</param>
void onConferenceLeave(int reason);

# 视频渲染

  • 本地视频渲染

您可以调用 JCMediaDevice (opens new window) 类中的 startCameraVideo (opens new window) 方法进行本地视频画面的渲染,调用该方法会打开摄像头。

/// <summary>
/// 获取预览视频对象,通过此对象能获得视图用于UI显示
/// </summary>
/// <param name="camera">摄像头对象</param>
/// <param name="mode">渲染方式</param>
/// <returns>JCMediaDeviceVideoCanvas对象</returns>
public JCMediaDeviceVideoCanvas startCameraVideo(JCMediaDeviceCamera camera, JCMediaDeviceRenderMode mode)

示例代码:

// 创建本地视频画面
List<JCMediaDeviceCamera> cameras = mediaDevice.cameraDevices;
JCMediaDeviceVideoCanvas canvas = mediaDevice.startCameraVideo(cameras[0], JCMediaDeviceRenderMode.FULLCONTENT);
ImageBrush image = new ImageBrush(canvas.videoView);
  • 远端视频渲染

调用 JCMediaDevice (opens new window) 类中的 startVideo (opens new window) 方法进行远端视频画面的渲染。

/// <summary>
/// 获得视频对象,通过此对象能获得视图用于UI显示
/// </summary>
/// <param name="videoSource">渲染标识串,比如 JCMediaChannelParticipant JCCallItem 中的 renderId,当videoSource 为 videoFileId 时,内部会调用 startVideoFile</param>
/// <param name="mode">渲染模式</param>
/// <returns>JCMediaDeviceVideoCanvas对象</returns>
public JCMediaDeviceVideoCanvas startVideo(string videoSource, JCMediaDeviceRenderMode mode)

示例代码:

// 创建远端视频画面
JCMediaDeviceVideoCanvas canvas = mediaDevice.startVideo(renderId, JCMediaDeviceRenderMode.FULLSCREEN);
ImageBrush image = new ImageBrush(canvas.videoView);

# 停止视频

如果想停止视频,可以调用下面的方法,该方法会关闭摄像头。

/// <summary>
/// 停止视频
/// </summary>
/// <param name="canvas">JCMediaDeviceVideoCanvas对象,由startVideo获得</param>
public void stopVideo(JCMediaDeviceVideoCanvas canvas)

# 开启关闭发送本地音频流

sendlocaldata.png

加入会议后,可以调用下面的接口开启或者关闭发送本地音频流。

/// <summary>
/// 开启/关闭音频发送,会议中的成员会收到 onConferenceParticipantUpdate 回调,JCConferenceParticipant.IsAudio 表示该成员是否开启了音频发送
/// 当前的状态@see JCConferenceInfo 对象属性 UploadAudio
/// </summary>
/// <param name="enable">true 开启发送,false 关闭发送</param>
/// <returns>接口调用成功失败</returns>
public bool EnableUploadAudioStream(bool enable)

示例代码:

conference.EnableUploadAudioStream(true);

该方法调用后,会议中的成员会收到 onConferenceParticipantUpdate (opens new window) 回调。

/// <summary>
/// 成员更新的回调
/// </summary>
/// <param name="participant">成员对象</param>
/// <param name="changedParam">变化标识集合</param>
void onConferenceParticipantUpdate(JCConferenceParticipant participant, JCConferenceParticipantChangedParam changedParam);

# 开启关闭发送本地视频流

subscriberemote.png

如果想开启或者关闭发送本地视频流,可以调用下面的方法,该方法调用成功后,会议中的成员会收到 onConferenceParticipantUpdate (opens new window) 回调。

/// <summary>
/// 开启/关闭视频发送,会议中的成员会收到 onConferenceParticipantUpdate 回调,JCConferenceParticipant.IsVideo 表示该成员是否开启了音频发送
/// 当前的状态@see JCConferenceInfo 对象属性 UploadVideo
/// </summary>
/// <param name="enable">true 开启发送,false 关闭发送</param>
/// <returns>接口调用成功失败</returns>
public bool EnableUploadVideoStream(bool enable)

示例代码:

conference.EnableUploadVideoStream(true);

# 开启关闭订阅会议音频流

还可以调用下面的方法开启或者关闭会议音频流订阅。

/// <summary>
/// 开启/关闭本端会议音频输出
/// 当前的状态@see JCConferenceInfo 对象属性 AudioOutput
/// </summary>
/// <param name="enable">true 开启,false 关闭</param>
/// <returns>true 成功,false 失败</returns>
public bool EnableAudioOutput(bool enable)

示例代码:

conference.EnableAudioOutput(true);

# 请求用户的视频流

加入会议后,调用下面的方法请求用户的视频流。

/// <summary>
/// 订阅会议成员视频
/// JCConferenceParticipant.IsVideo 如果成员开启了视频发送,就可以调用该接口订阅该成员的视频。
/// </summary>
/// <param name="userId">成员对象userId</param>
/// <param name="pictureSize">订阅视频的分辨率,传 None 取消订阅 </param>
/// <returns>接口调用成功失败</returns>
public bool RequestVideo(string userId, JCConferencePictureSize pictureSize)

其中,视频请求尺寸 JCConferencePictureSize (opens new window) 有:

/// <summary>
/// 初始值
/// </summary>
Init = JCConference.VALUE_INIT,

/// <summary>
/// 不请求
/// </summary>
None,

/// <summary>
/// 最小尺寸
/// </summary>
Min,

/// <summary>
/// 小尺寸
/// </summary>
Small,

/// <summary>
/// 大尺寸
/// </summary>
Large,

/// <summary>
/// 最大尺寸
/// </summary>
Max

TIP

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

示例代码:

conference.RequestVideo("userId", JCConferencePictureSize.Large);

# 查询会议

queryconf.png

加入会议之前,可以调用下面的方法查询进行中的会议。

/// <summary>
/// 查询进行中的会议,结果回调 onQueryGoingConfResult
/// </summary>
/// <param name="confNumber">会议Id</param>
/// <returns>返回操作Id,返回-1表示接口调用失败</returns>
public int QueryGoingConf(string confNumber)

示例代码:

conference.QueryGoingConf("confNumber");

调用该接口后会收到 onQueryGoingConfResult (opens new window) 回调,该方法返回操作id、查询会议的结果、查询结果的原因、会议对象和会议中的成员。

/// <summary>
/// 查询进行中的会议结果
/// </summary>
/// <param name="operationId">操作id,对应QueryGoingConf接口返回值</param>
/// <param name="result"></param>
/// <param name="reason">当 result 为 false 时该值有效,原因:JCConferenceReason的None</param>
/// <param name="confInfo">会议对象</param>
/// <param name="memberList">会议中的成员</param>
void onQueryGoingConfResult(int operationId, bool result, int reason, JCConferenceInfo confInfo, List<JCConferenceParticipant> memberList);

示例代码:

public void onQueryGoingConfResult(int operationId, bool result, int reason, JCConferenceInfo confInfo, List<JCConferenceParticipant> memberList)
{
    if (result)
    {
      // 查询成功
      String title = confInfo.Title
                     confInfo.creator; 
                     confInfo.Creator;
      String creator = confInfo.creator;
      ...
    }
}

# 发送消息

/// <summary>
/// 发送消息,其他成员会收到回调 onMessageReceive
/// </summary>
/// <param name="type">消息类型</param>
/// <param name="content">消息内容,当 toUserId 不为 null 时,content 不能大于 4k</param>
/// <param name="toUserId">传 null 发送给所有人</param>
/// <returns>是否发送成功</returns>
public bool SendMessage(string type, string content, string toUserId)

其他成员会收到 onMessageReceive (opens new window) 回调:

/// <summary>
/// 收到其他成员发送的消息
/// </summary>
/// <param name="type">消息类型</param>
/// <param name="content">消息内容</param>
/// <param name="fromUserId">消息发送成员userId</param>
void onMessageReceive(String type, String content, String fromUserId);