集成会议功能 - iOS

简介

本篇文档描述了会议基本功能的实现。在集成时,涉及到 JCConferenceManagerJCConferenceModelJCParticipantModel 这三个类。

  • JCConferenceManager会议管理类,包含了会议的所有接口。
  • JCConferenceModel会议对象,定义了会议信息的属性,用于 App 界面(以下简称 UI)展示会议的信息。
  • JCParticipantModel会议成员对象,定义了成员信息的属性,用于 UI 展示会议成员的信息。

注:在集成会议功能前,确保已经完成 SDK 的导入和初始化工作。

基本交互

会议中 UI 和后台的交互基本都是异步方式,在 iOS 侧定义了 JCConferenceDelegate protocol,用于 UI 接收会议事件会议成员事件的回调。

具体实现如下:

  1. UI 实现 JCConferenceDelegate protocol 方法

     //ViewController添加JCConferenceDelegate协议
     @interface ConferenceViewController () <JCConferenceDelegate>
    
     //会议相关的事件回调
     - (void)conferenceEvent:(JCConferenceEvent)event 
                 eventReason:(JCEventReason)eventReason
     {
         //todo
     }
    
     //会议成员相关的事件回调
     - (void)participantChangedEvent:(JCParticipantEvent)event 
                         eventReason:(JCEventReason)eventReason 
                              userId:(NSString *)userId
     {
         //todo
     }
    
  2. 设置 delegate

     //_confManager会议管理对象
     JCConferenceManager *_confManager = [JCConferenceManager sharedManager];
     //self为ViewController对象
     [_confManager setDelegate:self];
    

加入会议

开发者需自定义一个字符串作为会议的标识 roomId,不同的终端加入相同 roomId 的会议,即可加入到同一个会议中。

具体实现如下:

  1. 调用加入会议的接口。

     //_roomId 会议房间id,是由中文、英文、数字组成的字符串,需自定义。
     //_displayName 是在会议中显示的昵称。
     int ret = [_confManager joinWithRoomId:_roomId displayName:_displayName];
     if (ret == JCOK) {
         //如果加入视频会议,需开启摄像头
         [_confManager startCamera];    
     } else {
         //UI加入失败的提示
     }
    

    该接口为异步接口,调用接口返回 JCOK 仅表明发起加入会议的请求。会议加入成功或失败需通过回调函数判断(详见第2步)。

  2. 处理会议加入事件的回调。

    • 当 UI 收到加入会议 JCConferenceEventJoin 的事件回调,并且事件的结果为 JCReasonJoinOk,表明加入会议成功。
    • 加入成功后,可调用接口 getConferenceInfo 获取会议信息,然后可以将会议信息更新到 UI。

    回调实现如下:

     //会议相关的事件回调
     - (void)conferenceEvent:(JCConferenceEvent)event 
                 eventReason:(JCEventReason)eventReason
     {
         //处理会议加入结果
         //JCConferenceEventJoin是加入会议的事件
         if (event == JCConferenceEventJoin) {  
             //JCReasonJoinOk表示加入会议成功
             if (eventReason == JCReasonJoinOk) {
                 //获取会议信息,JCConferenceModel对象包含了会议的基本信息
                 JCConferenceModel *confInfo = [_confManager getConferenceInfo];
                 //会议roomId
                 NSString *roomId = confInfo.roomId;
                 //会议主题    
                 NSString *title = confInfo.title;
                 //获取会议中的成员列表
                 NSArray *participantsArray = confInfo.participants;
                 //遍历成员列表
                 for (JCParticipantModel *model in participantsArray) {
                     //将所有成员的userId保存到界面定义的数组中
                     //方便界面更新UITableView这类的容器
                     [_participantDataSource addObject:model.userId];
                 }
    
                 //更新 UI 界面
                 //todo...
             } else {
                 //UI加入失败的提示
                 //todo...
             }
         }
     }
    

注:如果终端已经在某个会议中,在离开此会议前,不能加入另一个会议。

离开会议

离开会议分两种情况:

  • 调用离开会议的接口主动离开;
  • 因终端断网超时导致离开会议。

调用离开会议的接口主动离开

已加入会议的终端想要离开会议,需调用leave接口来退出会议。

具体实现如下:

  1. 调用离开会议的接口。

     [_confManager leave];
    

    该接口为异步接口,调用接口时并没有真正离开会议。离开会议成功通过回调函数判断(详见第2步)。

  2. 处理离开会议事件的回调。

    • 当 UI 收到离开会议 JCConferenceEventEnd 的事件回调,并且事件的结果为 JCReasonEndQuit,表明离开会议成功。
    • UI 销毁会议界面。

    回调实现如下:

     //会议相关的事件回调
     - (void)conferenceEvent:(JCConferenceEvent)event 
                 eventReason:(JCEventReason)eventReason 
     {
         //处理离开会议
         //JCConferenceEventEnd是离开会议的事件 
         if (event == JCConferenceEventEnd) {
             //JCReasonEndQuit 是调用leave接口主动离开会议的结果
             if (eventReason == JCReasonEndQuit) {
    
                 //如果加入会议时打开了摄像头,需关闭摄像头
                 [_confManager stopCamera];
                 //销毁会议界面
                 [self dismissViewControllerAnimated:YES completion:nil];
             } else {
    
             }
         }
     }
    

因断网离开会议

已经加入会议的终端,如果发生断网并且持续时间超过30秒,就会导致该终端离开会议。

具体实现如下:

  1. UI 会收到离开会议 JCConferenceEventEnd 的事件回调,并且事件的结果为 JCReasonEndOffline,表明因断网离开会议。

  2. UI 可以提示用户断网,再销毁会议界面。

//会议相关的事件回调
- (void)conferenceEvent:(JCConferenceEvent)event 
            eventReason:(JCEventReason)eventReason 
{
    //处理离开会议
    //JCConferenceEventEnd是离开会议的事件 
    if (event == JCConferenceEventEnd) {
        //JCReasonEndOffline 是因断网导致离开会议的结果
        if (eventReason == JCReasonEndOffline) {
            //UI 界面可以提示用户
            //todo...

            //关闭摄像头
            [_confManager stopCamera];
            //销毁会议界面
            [self dismissViewControllerAnimated:YES completion:nil];
        }
    }
}

终端若想回到会议中,需要再次调用加入会议的接口。

会议中有新成员加入

终端在会议中时,若有新成员加入,UI 会收到 JCParticipantEventJoined 的事件回调,可在回调中更新 UI。

具体实现如下:

//会议成员相关的事件回调
- (void)participantChangedEvent:(JCParticipantEvent)event 
                    eventReason:(JCEventReason)eventReason 
                         userId:(NSString *)userId 
{
    //JCParticipantEventJoined表示有新成员加入会议
    //userId是加入成员的userId
    if (event == JCParticipantEventJoined) {
        //更新 UI
        //todo...
    }
}

会议中有成员离开

终端在会议中时,若有成员离开,UI 会收到 JCParticipantEventLeaved 的事件回调,可在回调中更新 UI。

具体实现如下:

//会议成员相关的事件回调
- (void)participantChangedEvent:(JCParticipantEvent)event 
                    eventReason:(JCEventReason)eventReason 
                         userId:(NSString *)userId 
{
    //JCParticipantEventLeaved表示有成员离开会议
    //userId是离开成员的userId
    if (event == JCParticipantEventLeaved) {
        //更新 UI 
        //todo...
    }
}

请求会议成员视频

在加入会议后,UI 若要显示会议中其他成员的视频,要先向服务器请求该成员的视频,服务器根据请求的视频分辨率大小结合当前网络状况发送最优分辨率的视频流到终端。

//请求成员视频流,userId对应成员的id,JCVideoPictureSizeLarge请求视频的分辨率
[_confManager requestVideoWithUserId:userId pictureSize:JCVideoPictureSizeLarge];

请求的视频分辨率越高,占用的网络带宽越大。为达到流畅的视频效果,请求视频分辨率的原则如下:

  • 大的视频窗口请求高分辨率,小的视频窗口请求低分辨率。
  • 应避免同时请求多个成员的高分辨率的视频。
  • 服务器发送某个成员的视频流是最后一次请求该成员的视频分辨率,因此要注意同一个成员的不同分辨率的切换。
  • 同时有大的视频窗口和小的视频窗口要显示同一个成员的视频,此时应请求高分辨率。
  • UI 不显示某个成员时,可以不请求或者停止请求该成员的视频。

渲染会议成员视频

请求会议成员视频后,UI 调用startRender接口渲染该成员的视频。

//renderView渲染的View,userId对应成员的id,JCRenderFullScreen视频渲染的填充方式
[_confManager startRender:renderView 
                   userId:userId 
                     mode:JCRenderFullScreen 
                completed:nil];

在离开会议时,已经在渲染的 View 应调用 stopRender 停止渲染,释放资源。

//释放的renderView是在渲染的view
[_confManager stopRender:renderView];

发送音视频

成员在会议中可通过调用异步接口打开/关闭向会场发送的本地音视频流,若操作成功,会场内所有成员(包括发起操作的成员)的 UI 都会收到JCParticipantEventStatusChanged(成员状态改变)事件回调,通过读取对应成员对象内的相关内容,来判断音视频发送的状态。

发送语音

接口enableLocalAudioStream用于打开/关闭发向会场的本地语音流。当 UI 收到JCParticipantEventStatusChanged的事件回调时,对应成员对象内的isAudioUpload即表示该成员是否打开了他的本地语音流。

具体实现如下:

  1. 调用接口打开/关闭本地语音流。

     //enable YES是开启语音流,NO是关闭语音流
     - (int)enableLocalAudioStream:(BOOL)enable;
    
  2. 处理成员状态变化的事件回调。

    在回调中可获取该成员本地语音流的开关状态,并以此更新 UI 上有关该成员的内容。

     //会议成员相关的事件回调
     - (void)participantChangedEvent:(JCParticipantEvent)event     
                         eventReason:(JCEventReason)eventReason 
                              userId:(NSString *)userId 
     {
         //JCParticipantEventStatusChanged表示有成员状态变化,userId是对应的成员
         if (event == JCParticipantEventStatusChanged) {
             //获取对应的成员对象
             JCParticipantModel *model = [_confManager getParticipantWithUserId:userId];
    
             //isAudioUpload为YES表示该成员是开启语音的状态,为NO表示该成员是关闭语音的状态
             BOOL canAudio = model.isAudioUpload;
    
             //根据canAudio的值,更新对应成员的 UI
             //todo...
         }
     }
    

注:新成员加入会议后,本地语音流默认未打开,此时会场中的其他成员听不到该成员的声

发送视频

接口enableLocalVideoStream用于打开/关闭发向会场的本地视频流。当 UI 收到 JCParticipantEventStatusChanged 的事件回调时,对应成员对象内的isVedioUpload即表示该成员是否打开了他的本地视频流。

具体实现如下:

  1. 调用接口打开/关闭本地视频流。

     //enable YES是开启视频流,NO是关闭视频流
     - (int)enableLocalVideoStream:(BOOL)enable;
    
  2. 处理成员状态变化的事件回调。

    在回调中可获取该成员本地视频流的开关状态,并以此更新 UI 上有关该成员的内容。

     //会议成员相关的事件回调
     - (void)participantChangedEvent:(JCParticipantEvent)event     
                         eventReason:(JCEventReason)eventReason 
                              userId:(NSString *)userId 
     {
         //JCParticipantEventStatusChanged表示有成员状态变化,userId是对应的成员
         if (event == JCParticipantEventStatusChanged) {
             //获取对应的成员对象
             JCParticipantModel *model = [_confManager getParticipantWithUserId:userId];
    
             //isVideoUpload为YES表示该成员是打开视频流的状态,为NO表示该成员是关闭视频流的状态。
             BOOL canVideo = model.isVideoUpload;
    
             //根据canVideo的值更新对应成员的 UI
             //todo...
         }
     }
    

注:新成员加入会议后,本地视频流默认打开,此时会场中的其他成员可接收该成员的视频。