差别

这里会显示出您选择的修订版和当前版本之间的差别。

到此差别页面的链接

两侧同时换到之前的修订记录 前一修订版
后一修订版
前一修订版
rtc:conference:ios [2020/07/30 07:27]
victorli [多路推流]
rtc:conference:ios [2020/12/25 08:18] (当前版本)
victorli [全体静音/指定成员静音]
行 1: 行 1:
-====== iOS集成多人通话 ======+====== iOS 多人音视频通话 ======
  
------- 
  
 +-----
 +
 +====== 跑通Demo ======
 + ​环信提供开源的多人视频会议项目EMiOSVideoCallDemo,在使用SDK集成App前,您可以参考相关代码
 +===== 示例代码 =====
 +进入[[http://​docs-im.easemob.com/​rtc/​scenario/​meeting#​demo_源码|环信音视频Demo源码下载]]页面,选择iOS端Demo下载,或直接进入github开源网站https://​github.com/​easemob/​videocall-ios
 +===== 前提条件 =====
 +运行Demo前你需要具备以下条件
 +  * mac操作系统10.11以上
 +  * 安装Xcode 11以上
 +  * iPhone设备iPhone 6以上,安装系统iOS 9.0以上
 +===== 运行Demo =====
 +
 +==== Demo代码目录简介 ====
 +目录 VideoCallDemo/​EMiOSVideoCallDemo 中的 Demo 关键类介绍
 +
 +{{:​rtc:​conference:​videocalldemo.png?​400|}}
 +  ​
 +多人音视频Demo主要包含3个target,SharedDesktop和SharedDesktopSetupUI用于实现共享桌面功能,EMiOSVideoCallDemo是会议target,实现类如下:
 +
 +  * RoomJoinViewController 加入房间页面
 +  * ConferenceViewController 视频展示页面
 +  * AccountSettingViewController 个人设置页面
 +  * RoomSettingViewController 房间设置页面
 +  * SpeakerListViewController 主播列表页面
 +  * KickSpeakerViewController 选人下麦页面
 +  * EMStreamView 视频小窗口类
 +  * EMDemoOption 会议管理类,存储会议的设置和单例属性
 +  * ChangeRoleView 管理员处理上麦申请页面
 +  * ProfileVidwController 个人资料页面
 +  * UpdateNicknameViewController 修改昵称页面
 +  * SelectHeadImageViewController 修改头像页面
 +
 +
 +==== 工程设置 ====
 +
 +Demo中的源码不包含SDK,SDK可以从cocospods安装,或者从官网下载,然后拷贝到当前工程下
 +  * 使用cocoapods安装SDK
 +如果当前系统上没有安装cocoapods,需要用户自己安装,安装过程参考 [[https://​guides.cocoapods.org/​using/​getting-started.html#​getting-started|Getting Started with CocoaPods 安装说明]]。
 +
 +修改VideoCallDemo/​EMiOSVideoCallDemo目录下的Podfile文件,添加如下: ​
 +<​code>​
 +pod '​Hyphenate',​ '~> 3.7.1'
 +</​code>​
 +Terminal进入该目录,运行命令:
 +<​code>​
 +pod install
 +</​code>​
 +成功安装后,Terminal 中会显示 Pod installation complete!,此时项目文件夹下会生成podfile目录,Pod已加入工作空间中
 +  * 从官网下载SDK
 +进入环信IM下载页面,选择iOS SDK + Demo下载,目录中包含HyphenateFullSDK目录,该目录下的Hyphenate.framework即为SDK,将SDK拷贝到VideoCallDemo/​EMiOSVideoCallDemo/​目录下。
 +
 +Terminal进入VideoCallDemo/​EMiOSVideoCallDemo/​目录,运行命令:
 +<​code>​
 +pod install
 +</​code>​
 +打开EMiOSVideoCallDemo.xcworkspace工作空间,进入工程TARGETS > Project Name > General > Frameworks, Libraries, and Embedded Content 菜单,将Hyphenate.framework拖拽进来完成添加。为保证动态库的签名和 app 的签名一致,需要将动态库的 Embed 属性设置为 Embed & Sign
 +
 +==== 运行 ====
 +
 +连接iPhone手机,选择目标设备,点击运行
 +
 +====== 快速集成 ======
  
-===== 音视频通信的简要步骤 ===== 
 多人音视频会议主要涉及到的环信SDK头文件如下:​ 多人音视频会议主要涉及到的环信SDK头文件如下:​
 <code objc> <code objc>
行 20: 行 81:
 </​code>​ </​code>​
  
-SDK 能够支持音频和视频通信。创建音视频通信的过程简单来说,可以分为以下几步:+创建音视频通信的过程简单来说,可以分为以下几步:
  
     1. 初始化 SDK,设置监听代理     1. 初始化 SDK,设置监听代理
行 28: 行 89:
     5. sub: 订阅并播放音视频数据流     5. sub: 订阅并播放音视频数据流
     6. leave: 离开会议     6. leave: 离开会议
-===== 基本知识 ​=====+===== 1 环信后台注册appkey ​===== 
 +在开始集成前,你需要注册环信开发者账号并创建后台应用,参见[[http://​docs-im.easemob.com/​im/​ios/​sdk/​prepare#​注册并创建应用|注册并创建应用]] 
 +===== 2 创建项目 ===== 
 +参考以下步骤创建一个iOS 应用项目,如果已有项目,可以直接进行下一步集成。创建过程如下 
 +  * 打开 Xcode 并点击 Create a new Xcode project。 
 +  * 选择项目类型为 Single View App,并点击 Next。 
 +  * 输入项目信息,如项目名称、开发团队信息、组织名称和语言,语言为Object-C,并点击 Next。 
 +  * 选择项目存储路径,并点击 Create。 
 +  * 进入工程设置页面的Signing & Capaabilities菜单,选择 Automatically manage signing,并在弹出菜单中点击 Enable Automatic 
 +===== 3 导入SDK到工程 ===== 
 +集成SDK有两种方法,分别是使用cocoapods和手动导入SDK 
 +==== 使用cocoapods导入SDK ==== 
 +开始前确保你已安装 Cocoapods。 
 +  * 在 Terminal 里进入项目根目录,并运行 pod init 命令。项目文件夹下会生成一个 Podfile 文本文件。 
 +  * 打开 Podfile 文件,修改文件为如下内容。注意将 AppName 替换为你的 Target 名称,并将 version 替换为你需集成的 SDK 版本,如3.7.1。 
 +<​code>​ 
 +target '​AppName'​ do 
 +    pod '​Hyphenate',​ '~> version'​ 
 +end 
 +</​code>​ 
 +  * 在 Terminal 内运行 pod update 命令更新本地库版本。 
 +  * 运行 pod install 命令安装Hyphenate SDK。成功安装后,Terminal 中会显示 Pod installation complete!,此时项目文件夹下会生成一个 xcworkspace 文件。 
 +  * 打开新生成的 xcworkspace 文件。
  
-IEMConferenceManager.h:多人音视频操作接口+==== 手动导入SDK ====
  
-EMConferenceManagerDelegate:会议监听类会回调会议成员变化会议数据流变化等需要在代码中设置监听+  * 将在跑通Demo阶段下载的HyphenateFullSDK下的Hyphenate.framework拷贝到项目工程目录下 
 +  * 打开工程设置/​Genaral菜单下将Hyphenate.framework拖拽到Frameworkslibraries,​and Embedded Content下设置为Embed and Signed
  
-EMConferenceType:多人会议类型,**目前已经进行优化,请使用Communication类型**。 +工程入SDK,需要头文件Hyphenate.h 
- +<​code>​ 
-    1. Communication:普通通信会议,最多支持参会者9人,成员都可以自由说话和发布视频,成员角色Speaker +#import <​Hyphenate/Hyphenate.h>
-    2. Large Communication:大型通信会议,最多参会者30人,成员都可以自由说话和发布视频,成员角色Speaker<​ +
-    3. Live:互动视频会议,会议里支持最多9个主播和600个观众 +
- +
-EMCallConference:会议类,用户可以维护SDK返回的实例,不允许进行copy和new +
- +
-    注意: EMCallConference会出现两个ID属性,分别是callId和confId,​两个ID都是标识符,callId是本地生成,confId是服务器端生成,邀请或者加所需要的均为confId  +
-  +
-EMCallMember:会议成员类,用户可以维护SDK返回的实例,不允许进行copy和new +
-  +
-EMCallStream:会议中的数据流类,包含音频数据和视频数据,用户可以维护SDK返回的实例,不允许进行copy和new +
-  +
-EMStreamParam:自己发布的数据流的各种配置,需要在调发布接口时作为参数传入 +
-  +
-EMConferenceRole:多人会议成员角色 +
- +
-    1观众Audience:只能观看收听音视频,即只能订阅流 +
-    2. 主播Speaker:能上传自己的音视频,能观看收听其他主播的音视频,即能发布流和订阅流 +
-    3. 管理员Admin:能创建会议,销毁会议,移除会议成员,切换其他成员的角色,订阅流,发布流 +
-     +
-    注意: +
-    >> 每个人必须调用join接口成功后,才算是加入会议(即成为会议成员)。会议成员才允许进行其他操作比如订阅流、发布流等 +
-    >> 成员如果想改变自己角色,必须想办法通知管理员,只有管理员才能修改 +
- +
-===== 多人音视频会议功能实现 ===== +
- +
-如何使用SDK实现多人实时音视频会议 +
-  +
-Communication和Large Communication除了最大成员数不一样,流程几乎是一样的。以下是从创建会议到离开会议完整的流程讲解: +
-==== 注册监听 ==== +
- +
-进入会议之前,调用[IEMConferenceManager addDelegate:​delegateQueue:​]方法指定回调监听对象,在该方法中: +
- +
-    ∵ 指定一个 delegate 对象,SDK 通过指定的 delegate 通知应用程序 SDK 的运行事件,如:成员加入或离开会议,数据流更新等。 +
-    ∵ 回调方法在哪个队列中调用 +
- +
-<​code ​objc+
-//​Objective-C +
-[[EMClient sharedClient].conferenceManager addDelegate:​self delegateQueue:​nil];​+
 </​code>​ </​code>​
 +===== 4 添加权限 =====
  
 +应用需要音频设备及摄像头权限,在 info.plist 文件中,点击 + 图标,添加如下信息
 +^Key                                    ^Type    ^Value ​                           ^
 +|Privacy - Microphone Usage Description |String |描述信息,如“环信需要使用您的麦克风” ​  |
 +|Privacy - Camera Usage Description |String |描述信息,如“环信需要使用您的摄像头” ​  |
  
-==== 用户A创建会议 ​====+如果希望在后台运行,还需要添加后台运行音视频权限,在info.plist文件中,点击 + 图标,添加Required background modes ,​Type为Array,在Array下添加元素App plays audio or streams audio/video using AirPlay 
 +===== 5 创建UI ====
 +video视图参考Demo中的EMStreamView,会议控制界面展示参考Demo中的ConferenceViewController
  
-SDK没有提供单独的创建接口,提供了createAndJoinConference和joinRoom接口,A调接口将拥有一个会议实例Conference,同时A将成为该Conference的成员且角色是Admin. +===== 6 初始化SDK ===== 
- +初始化HyhpenateSDK使initializeSDKWithOptions:​接口,需要设置自己的appkey调用如下: 
-用户创建会议时可以设置参数指定是否支持小程序音视频,是否需要在服务器端录制,录制时是否合并流 +<​code>​ 
- +// 这里替换成自己的appkey 
-    注意:  +EMOptions ​*retOpt ​[EMOptions optionsWithAppkey:​@"​easemob-demo#​chatdemoui"​]
-    创建加入会议有两组api:​ createAndJoinConference 和 joinRoom.  +// 这里打开日志输出 
-    joinConference 是通过会议ID,密码方式加入会议,而joinRoom是通过房间名称加入。 +retOpt.enableConsoleLog ​YES
-    创建会议成功以后,默认超时时间为三分钟,超过三分钟没有人加入,会议会动销毁;另外当会议中所有人离开2分钟后会议也会被销毁。 +[[EMClient sharedClient] ​initializeSDKWithOptions:retOpt];
-    joinRoom api在会议不存在会自动去创建。 +
- +
-<​code ​objc+
-//Objective-C +
-- (void)createAndJoinConference +
-+
-    __weak typeof(self) weakself = self; +
-    void (^block)(EMCallConference ​*aCall, NSString *aPassword, EMError *aError) ​^(EMCallConference *aCall, NSString *aPassword, EMError *aError) { +
-        if (aError) { +
-            //​错误提示 +
-            return ​+
-        } +
-             +
-        ​//更新页面显示 +
-    }; +
-         +
-    EMConferenceType type EMConferenceTypeCommunication+
-    //​EMConferenceType type = EMConferenceTypeLargeCommunication;​ +
-     +
-    /*! +
-    *  \~chinese +
-    *  创建并加入会议 +
-    * +
-    *  @param aType             ​会议类型 +
-    *  @param aPassword ​        ​会议密码 +
-    *  @param isRecord ​            ​是否开启服务端录制 +
-    *  @param isMerge ​             录制时是否合并数据流 +
-    *  @param aCompletionBlock ​ 完成的回调 +
-    * +
-    */ +
-    - (void)createAndJoinConferenceWithType:​(EMConferenceType)aType +
-                               ​password:​(NSString *)aPassword +
-                                 ​record:​(BOOL)isRecord +
-                            mergeStream:​(BOOL)isMerge +
-                                completion:​(void (^)(EMCallConference *aCall, NSString *aPassword, EMError *aError))aCompletionBlock;​ +
- +
-    // record 与 mergeStream 根据自己场景需求设置 +
-    ​[[EMClient sharedClient].conferenceManager createAndJoinConferenceWithType:type password:​@"​password"​ record:NO mergeStream:​NO completion:​block]; +
-}+
 </​code>​ </​code>​
 +===== 7 环信ID注册、登录 =====
 +在进行音视频通话前,需要首先登录IM账户,登录过程参见[[http://​docs-im.easemob.com/​im/​ios/​sdk/​basic#​登录|账号登录]]。
  
-如果想在创建会议时指定会议中的最大视频数、最大主播数或开启cdn推流可以使用带RoomCofnig参数的创建会议接口,​此时会议类型使用aType而不是roomconfig中指定的会议型。接口如下: +若您还没有IM账户,需要先注册账户,注册过程参见[[http://​docs-im.easemob.com/​im/​ios/​sdk/​basic#​注册|账号注册]] 
-<​code ​objc+===== 8 音视频功能初始化 ===== 
-/*! +账号登录成功后需要进行音视频通话功能的初始化设置监听类 
- ​* ​ \~chinese +<​code>​ 
- ​* ​ 创建并加入会议 +[[EMClient sharedClient].conferenceManager addDelegate:self delegateQueue:nil];
- * +
- ​* ​ @param aType             ​会议类型 +
- ​* ​ @param aPassword ​        ​会议密码 +
- ​* ​ @param aConfrConfig ​  ​会议属性配置 +
- ​* ​ @param aCompletionBlock ​ 完成的回调 +
- * +
- ​* ​ \~english +
- ​* ​ Create and join a conference +
- * +
- ​* ​ @param aType             The type of the conference +
- ​* ​ @param aPassword ​        The password of the conference +
- ​* ​ @param aConfrConfig ​  The config of conference +
- ​* ​ @param aCompletionBlock ​ The callback block of completion +
- */ +
-- (void)createAndJoinConferenceWithType:(EMConferenceType)aType +
-                               ​password:​(NSString *)aPassword +
-                            confrConfig:​(RoomConfig*)aConfrConfig +
-                             ​completion:(void (^)(EMCallConference *aCall, NSString *aPassword, EMError *aError))aCompletionBlock;+
 </​code>​ </​code>​
- +===== 9 加入会议 ===== 
-==== 加入房间 ​==== +用户可以根据房间名和密码,快速加入一个会议,若该会议不存在,服务器将会自动创建。
- +
-用户可以根据房间名和密码,使用加入房间的接口,快速加入一个会议,若该会议不存在,服务器将会自动创建。使用加入房间的接口,加入会议后,其他会议操作与使用createAndJoin接口加入会议后的操作完全相同+
  
 加入房间使用的接口在IEMConferenceManager.h中,用户加入时可以选择使用的角色为观众还是主播,主播可以发布自己的音视频,观众只能订阅音视频。 加入房间使用的接口在IEMConferenceManager.h中,用户加入时可以选择使用的角色为观众还是主播,主播可以发布自己的音视频,观众只能订阅音视频。
  
-加入房间有基础版和高级版两个API。基础API只能加入默认的会议选项不支持服务录制,使API如下:+加入房间时,可以选择房间的会议类型是否开启服务录制,是否混音以及是否支持小程序。调方法如下:
 <code objc> <code objc>
 //​Objective-C //​Objective-C
--(void)joinRoom:​(NSString*)roomName +    RoomConfigroomConfig = [[RoomConfig alloc] init]; 
-          ​password:​(NSString*)aPassword +    ​roomConfig.confrType = EMConferenceTypeCommunication;​ 
-                    ​role:​(EMConferenceRole)role +    ​roomConfig.nickName = @"​昵称"​
-        completion:​(void (^)(EMCallConference *aCall, EMError *aError))aCompletionBlock+    //​是否开启服务端录制 
-</code> +    ​roomConfig.isRecord = YES; 
-高级版API接口加入房间时,可以选择房间的会议类型,是否开启服务端录制,是否混音以及是否支持小程序。API如下 +    //录制时是否合并数据流 
-<code objc> +    ​roomConfig.isMerge = YES; 
-//Objective-C +    [[[EMClient sharedClient] conferenceManager] ​joinRoom:​roomName password:pswd role:role roomConfig:​roomConfig completion:​^(EMCallConference *aCall, EMError *aError) ​
--(void)joinRoom:(NSString*)roomName +    self.conference = aCall; 
-         password:(NSString*)aPassword +    }];
-                  ​role:(EMConferenceRole)role +
-     roomConfig:(RoomConfig*)roomConfig +
-      ​completion:(void (^)(EMCallConference *aCall, EMError *aError))aCompletionBlock;+
 </​code>​ </​code>​
 +加入房间成功后,返回的回调中有EMCallConference会议实例,需要本地保存下来。
 +
 其中RoomConfig定义如下: 其中RoomConfig定义如下:
 <code objc> <code objc>
行 184: 行 179:
 *  \~chinese *  \~chinese
 *  会议类型 *  会议类型
-* 
-*  \~english 
-*  The  type of conference 
 */ */
 @property (nonatomic) EMConferenceType confrType; @property (nonatomic) EMConferenceType confrType;
行 192: 行 184:
 *  \~chinese *  \~chinese
 *  录制时是否合并数据流 *  录制时是否合并数据流
-* 
-*  \~english 
-* Whether to merge data streams while recording 
 */ */
 @property (nonatomic) BOOL isMerge; @property (nonatomic) BOOL isMerge;
行 200: 行 189:
 *  \~chinese *  \~chinese
 *  是否开启服务端录制 *  是否开启服务端录制
-* 
-*  \~english 
-*  Whether to record data streams 
 */ */
 @property (nonatomic) BOOL isRecord; @property (nonatomic) BOOL isRecord;
行 208: 行 194:
 *  \~chinese *  \~chinese
 *  是否支持微信小程序 *  是否支持微信小程序
-* 
-*  \~english 
-*  Weather to support wechat mini program 
 */ */
 @property (nonatomic) BOOL isSupportWechatMiniProgram;​ @property (nonatomic) BOOL isSupportWechatMiniProgram;​
行 216: 行 199:
 *  \~chinese *  \~chinese
 *  会议中使用的昵称 *  会议中使用的昵称
-* 
-*  \~english 
-*  The nickName userd in conference 
 */ */
 @property (nonatomic) NSString* nickName; @property (nonatomic) NSString* nickName;
行 224: 行 204:
 *  \~chinese *  \~chinese
 *  成员扩展信息 *  成员扩展信息
-* 
-*  \~english 
-*  The extension info of member 
 */ */
 @property (nonatomic) NSString* ext; @property (nonatomic) NSString* ext;
行 232: 行 209:
 *  \~chinese *  \~chinese
 *  会议最大主播数 *  会议最大主播数
-* 
-*  \~english 
-*  The  limit count of talkers 
 */ */
 @property (nonatomic) NSInteger maxTalkerCount ; @property (nonatomic) NSInteger maxTalkerCount ;
行 240: 行 214:
 *  \~chinese *  \~chinese
 *  会议最大视频上传数 *  会议最大视频上传数
-* 
-*  \~english 
-*  The  limit count of video streams 
 */ */
 @property (nonatomic) NSInteger maxVideoCount;​ @property (nonatomic) NSInteger maxVideoCount;​
行 248: 行 219:
 *  \~chinese *  \~chinese
 *  会议最大观众数 *  会议最大观众数
-* 
-*  \~english 
-*  The  limit count of audience 
 */ */
 @property (nonatomic) NSInteger maxAudienceCount;​ @property (nonatomic) NSInteger maxAudienceCount;​
行 256: 行 224:
 *  \~chinese *  \~chinese
 *  cdn 直播推流配置 *  cdn 直播推流配置
-* 
-*  \~english 
-*  The  cdn live config 
 */ */
 @property (nonatomic) LiveConfig* liveConfig; @property (nonatomic) LiveConfig* liveConfig;
行 264: 行 229:
 </​code>​ </​code>​
  
-注:RoomConfig中的ext表示会议成员的扩展信息,一般用于存储头像等信息,与pub流的接口中的ext信息是不同的 +注:RoomConfig中的ext表示会议成员的扩展信息,一般用于存储头像等信息,与pub流的接口中的ext信息是不同的
-==== 管理员A邀请其他人加入会议 ====+
  
-SDK没有提供邀请接口你可以自己实现使IM通过发消邀请过发邮件邀请等等+加入会议后会议中的其他人员会收到回调通知,如下: 
 +<​code>​ 
 +- (void)memberDidJoin:​(EMCallConference *)aConference 
 +               ​member:​(EMCallMember *)aMember 
 +
 +    // aMember中包含加入户的信息,如ID,昵称等 
 +
 +</​code>​ 
 +===== 10 发布流 ===== 
 +成员加入会议后,可以在会议中发布音视频流,发流程如下: 
 +<​code>​ 
 +EMStreamParam *pubConfig = [[EMStreamParam alloc] init]; 
 +EMCallLocalView *localView = [[EMCallLocalView alloc] init]; 
 +//​显示本地视频的页面 
 +pubConfig.localView = localView;​ 
 +//​上传本地摄像头的数据流 
 + ​[[EMClient sharedClient].conferenceManager publishConference:​[EMDemoOption sharedOptions].conference streamParam:​pubConfig completion:​^(NSString *aPubId, EMError *aError) { 
 +// 若流成功,aError应为nil,否则为失败信息 
 +// 发流成功时,aPubId为本地流的pubId,此时应把localView添加到界面上,​pubId需要在本地保存,​用于操作音视频流 
 +}]; 
 +streamParam参数提供了更丰富的发流选项
  
-至于需要送哪些邀请信息可以参照SDK中的join接口,目前是需要ConferenceconfrId和password +当成员流成功后会议中的其他成员会收到有流加入回调通知,通知下: 
- +<​code>​ 
-用环信IM发消息邀请 +//有新的数据流上传 
-<​code ​objc+- (void)streamDidUpdate:​(EMCallConference *)aConference 
-//Objective-C +              addStream:(EMCallStream ​*)aStream
-- (void)inviteUser:(NSString ​*)aUserName+
 { {
-    ​NSString *confrId = self.conference.confId;​ +    ​// aStream中包含了流信息,此时应该订阅流 
-    ​NSString *password = self.password;​ +
-    ​EMConferenceType type self.type; +</​code>​ 
-    ​NSString *currentUser = [EMClient sharedClient].currentUsername;​ +===== 11 订阅流 ===== 
-    ​EMTextMessageBody ​*textBody ​= [[EMTextMessageBody ​alloc] ​initWithText:​[[NSString alloc] initWithFormat:​@"​%@ 邀请你加入直播室:​ %@", currentUser,​ confrId]]; +成员收到有数据流加入的通知后,可以订阅该流,订阅后可以接收到流内的音视频,订阅过程如下 
-    ​EMMessage *message = [[EMMessage allocinitWithConversationID:aUserName from:currentUser to:aUserName body:textBody ext:​@{@"​em_conference_op":​@"​invite",​ @"​em_conference_id":​confrId,​ @"​em_conference_password":​password,​ @"​em_conference_type":​@(type)}]; +<​code>​ 
-    ​message.chatType ​EMChatTypeChat;​ +    ​// remoteView用于在UI上展示对方的视频 
-    [[EMClient sharedClient].chatManager sendMessage:message progress:​nil ​completion:​nil];​+    ​EMCallRemoteView ​*remoteView ​= [[EMCallRemoteView ​alloc] ​init]; 
 +    ​//​订阅其他人的数据流,,即订阅当前会议上麦主播的数据流 
 +    ​[[EMClient sharedClient].conferenceManager subscribeConference:[EMDemoOption sharedOptions].conference streamId:aStream.streamId remoteVideoView:remoteView completion:^(EMError *aError
 +        // 若订阅成功,aError应为nil,否则为失败信息。 
 +        // 订阅成功后,应把remoteView展示到UI上 
 +    ​}]; 
 +</​code>​ 
 +===== 12 退出会议 ====
 +用户退出会议的过程如下: 
 +<​code>​ 
 +[[EMClient sharedClient].conferenceManager leaveConference:[EMDemoOption sharedOptions].conference ​completion:​nil];​ 
 +</​code>​ 
 +用户退出会议后,会议中的其他成员会收到以下回调通知: 
 +<​code>​ 
 +- (void)memberDidLeave:​(EMCallConference *)aConference 
 +                member:​(EMCallMember *)aMember 
 +
 +   // aMember为退出的成员信息
 } }
 </​code>​ </​code>​
 +====== 进阶功能 ======
 +===== 会议管理 =====
 +==== 取日志 ====
 +SDK会写入日志文件到本地。日志文件路径如下:沙箱Documents/​HyphenateSDK/​easemoblog,以真机为例,获取过程如下:
 +  * 打开Xcode连接设备,前往Xcode --> Window --> Devices and Simulators
 +  * 进入Devices选项卡,在左侧选择目标设备,界面如下:
  
-    注意:使用环信IM邀请多个人时,建议使用群组消息。如果使用单聊发消息请注意每条消息中间的时间间隔,以防触发环信的垃圾消息防御机制 +{{:​rtc:​one2one:​fetchlogfile.png?400|}}
-==== 用户B接收到邀请加入会议 ==== +
- +
- +
-用户B解析出邀请消息中带的confrId和password,调用SDK的join接口加入会议,成为会议成员且角色是Speaker.+
  
 +日志文件easemob.log文件在下载包内容的AppData/​Library/​Application Support/​HyphenateSDK/​easemobLog目录下
 +==== 创建会议并加入 ====
 +除根据房间名和房间密码加入会议的api外,SDK还提供了直接创建并加入会议的api,接口如下:
 <code objc> <code objc>
 //​Objective-C //​Objective-C
-- (void)joinConferenceWithConfrId:​(NSString *)aConfrId password:​(NSString *)aPassword+- (void)createAndJoinConference
 { {
     __weak typeof(self) weakself = self;     __weak typeof(self) weakself = self;
-    void (^block)(EMCallConference *aCall, EMError *aError) = ^(EMCallConference *aCall, EMError *aError) {+    void (^block)(EMCallConference *aCall, NSString *aPassword, EMError *aError) = ^(EMCallConference *aCall, NSString *aPassword, EMError *aError) {
         if (aError) {         if (aError) {
             //​错误提示             //​错误提示
行 307: 行 312:
     };     };
         ​         ​
-    [[EMClient sharedClient].conferenceManager ​joinConferenceWithConfId:aConfrId ​password:aPassword ​completion:​block];​+    ​EMConferenceType type = EMConferenceTypeCommunication;​ 
 +    // record 与 mergeStream、isSupportWechatMiniProgram 根据自己场景需求设置 
 +    ​[[EMClient sharedClient].conferenceManager ​createAndJoinConferenceWithType:type password:@"​password"​ record:NO mergeStream:​NO isSupportWechatMiniProgram:​NO ​completion:​block];​
 } }
 </​code>​ </​code>​
 +调用该接口后,将拥有一个会议实例Conference,同时成员将成为该Conference的管理员.
  
 +用户创建会议时可以设置参数指定是否支持小程序音视频,是否需要在服务器端录制,录制时是否合并流、是否支持微信小程序
  
 +创建会议成功以后,默认超时时间为三分钟,超过三分钟没有人加入,会议会自动销毁;另外当会议中所有人离开2分钟后,会议也会被销毁。
  
-户B成功加入会议后,会议中其他成员收到回调[EMConferenceManagerDelegate memberDidJoin:member:]+如果想在创建会议时指定会议中的最大视频数、最大主播数,或开启cdn推流,可以使带RoomCofnig参数的创建会议接口,​此时roomconfig中指定的会议类型无效。接口如下: 
 +<code objc> 
 +/*! 
 + ​* ​ \~chinese 
 + ​* ​ 创建并加入会议 
 + * 
 + ​* ​ @param aType             会议类型 
 + ​* ​ @param aPassword ​        议密码 
 + ​* ​ @param aConfrConfig ​  ​会议属性配置 
 + ​* ​ @param aCompletionBlock ​ 完成的回调 
 + */ 
 +- (void)createAndJoinConferenceWithType:(EMConferenceType)aType 
 +                               ​password:(NSString *)aPassword 
 +                            confrConfig:​(RoomConfig*)aConfrConfig 
 +                             ​completion:​(void (^)(EMCallConference *aCall, NSString *aPassword, EMError *aError))aCompletionBlock;​ 
 +</​code>​ 
 +==== 邀请成员加入会议 ==== 
 +SDK没有提供邀请接口,你可以自己实现,比如使用环信IM通过发消息邀请,比如通过发邮件邀请等等。
  
 +至于需要发送哪些邀请信息,可以参照SDK中的join接口,可以发送Conference的confrId和password,如果是用joinRoom接口创建的会议,也可以发送房间名和房间密码。
  
 +比如用环信IM发消息邀请
 <code objc> <code objc>
 //​Objective-C //​Objective-C
-- (void)memberDidJoin:(EMCallConference ​*)aConference member:​(EMCallMember *)aMember+- (void)inviteUser:(NSString ​*)aUserName
 { {
-    ​if ([aConference.callId isEqualToString: ​self.conference.callId]) { +    ​NSString *confrId = self.conference.confId; 
-        NSString *message ​= [NSString ​stringWithFormat:@"用户 ​%@ 加入会议", ​aMember.memberName]; +    NSString *password = self.password;​ 
-        [self showHint:message]; +    EMConferenceType type = self.type;​ 
-    ​}+    NSString *currentUser ​[EMClient sharedClient].currentUsername;​ 
 +    EMTextMessageBody *textBody = [[EMTextMessageBody alloc] initWithText:​[[NSString ​alloc] initWithFormat:​@"​%@ ​邀请你加入会议: %@", ​currentUser,​ confrId]]; 
 +    ​EMMessage *message = [[EMMessage alloc] initWithConversationID:aUserName from:​currentUser to:​aUserName body:​textBody ext:​@{@"​em_conference_op":​@"​invite",​ @"​em_conference_id":​confrId,​ @"​em_conference_password":​password,​ @"​em_conference_type":​@(type)}]; 
 +    ​message.chatType = EMChatTypeChat;​ 
 +    [[EMClient sharedClient].chatManager sendMessage:​message progress:​nil completion:​nil];​
 } }
 </​code>​ </​code>​
  
-==== 成员A布音视频流 ==== +    注意:使用环信IM邀请多个人时,建议使用群组消息。如果使用单聊消息请注意每条消息中间的时间间隔,以防触发环信的垃圾消息防御机制 
- +     
- +    ​被邀请人解析出邀请消息中带的confrIdpassword,调用SDK的join接口加入会议成为会议成员且角色Speaker.
-成员A成员B都有发布流的权限可以调用SDK的publish接口发布流该接口用到了EMStreamParam参数,你可以自由配置,比如否上传视频,是否上传音频,使用前置或后置摄像头,视频码率,显示视频页面等等+
  
 <code objc> <code objc>
 //​Objective-C //​Objective-C
-- (void)pubLocalStream+- (void)joinConferenceWithConfrId:​(NSString *)aConfrId password:​(NSString *)aPassword
 { {
-    EMStreamParam *pubConfig = [[EMStreamParam alloc] init]; 
-    pubConfig.streamName = @"​自己的音视频数据流";​ 
-    pubConfig.enableVideo = YES; //​是否上传视频数据 
-    pubConfig.localView = self.localVideoView;​ //​视频显示页面 
-    pubConfig.isFixedVideoResolution = YES; //​是否固定视频分辨率 
-    pubConfig.videoResolution = EMCallVideoResolution640_480;​ //​视频分辨率 
-    pubConfig.maxVideoKbps = 0; //​最大视频码率 
-    pubConfig.maxAudioKbps = 0; //​最大音频码率 
-    pubConfig.isBackCamera = NO; //​是否使用后置摄像头 
-        ​ 
     __weak typeof(self) weakself = self;     __weak typeof(self) weakself = self;
-    ​[[EMClient sharedClient].conferenceManager publishConference:​self.conference streamParam:​pubConfig completion:^(NSString ​*aPubStreamId, EMError *aError) {+    ​void (^block)(EMCallConference *aCall, EMError *aError) = ^(EMCallConference ​*aCall, EMError *aError) {
         if (aError) {         if (aError) {
-            //显示错误信息 +            //错误提示 
-        } else { +            ​return ;
-            ​//​更新页面显示+
         }         }
-    ​}];+             
 +        //​更新页面显示 
 +    ​}
 +         
 +    [[EMClient sharedClient].conferenceManager joinConferenceWithConfId:​aConfrId password:​aPassword completion:​block];
 } }
 </​code>​ </​code>​
  
-    注意:如果是纯音频会议,只需要在发布数据流时将EMStreamParam中的enableVideo置为NO即可 
-==== 其他成员收到通知并订阅流 ====  
  
-成员成功发布数据流后,会议中其他成员会收到监听类回调[EMConferenceManagerDelegate streamDidUpdate:​addStream:​],如果需要关注某一条流,可以调用subscribe接口进行订阅 
  
-<code objc> +用户B成功加入会议后,会议中其他成员会收到回调[EMConferenceManagerDelegate memberDidJoin:member:]
-//​Objective-C +
-- (void)streamDidUpdate:​(EMCallConference *)aConference addStream:​(EMCallStream *)aStream +
-+
-    if ([aConference.callId isEqualToString:self.conference.callId]) { +
-        [self subStream:aStream]+
-    } +
-}+
     ​     ​
-- (void)subStream:​(EMCallStream *)aStream +==== 管理员销毁会议 ==== 
-+会议中管理员可以主动销毁会议销毁会议过程下: 
-    //​构建数据流视频显示页面,如果不支持视频可以传nil +<​code>​ 
-    ​EMCallRemoteView *remoteView = [[EMCallRemoteView alloc] initWithFrame:​frame];​ +[[EMClient sharedClient].conferenceManager ​destroyConferenceWithId:​self.conference.confId ​completion:nil];
-    remoteView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;​ +
-    remoteView.scaleMode = EMCallViewScaleModeAspectFill;​ +
-    ​[self.scrollView addSubview:​remoteView];​ +
-         +
-    __weak typeof(self) weakSelf = self; +
-    ​[[EMClient sharedClient].conferenceManager ​subscribeConference:​self.conference ​streamId:​aStream.streamId remoteVideoView:​remoteView ​completion:^(EMError *aError) { +
-        if (aError) { +
-            //​提示错误信息 +
-        } +
-    }]; +
-}+
 </​code>​ </​code>​
- +会议销毁后,会议中的其他成员收到会议结束的回调通知 
-==== 操作自己发布的流 ==== +<​code>​ 
- +- (void)conferenceDidEnd:​(EMCallConference *)aConference 
-成员成功的发布了自己的音视频流,在会议过程中,可以进行以下操作: +                  reason:​(EMCallEndReason)aReason 
- +                   error:(EMError ​*)aError
-    >> 切换前后摄像头: updateConferenceWithSwitchCamera:​ +
-    >> 开关静音,即订阅成员是否能听到A的声音:updateConference:​isMute:​ +
-    >> 开关视频,即订阅成员是否能看到A的视频:updateConference:​enableVideo:​ +
-    >> 重置视频显示页面:updateConference:​streamId:​remoteVideoView:​completion:​ +
- +
-当成员对自己的数据流做以上操作成功后,会议中的其他成员收到回调[EMConferenceManagerDelegate streamDidUpdate:​stream:​] +
- +
-<​code ​objc> +
-//​Objective-C +
-- (void)streamDidUpdate:​(EMCallConference *)aConference ​stream:(EMCallStream ​*)aStream+
 { {
-    if ([aConference.callId isEqualToString:​self.conference.callId] && aStream != nil) { 
-        //​判断本地缓存的EMCallStream实例与aStream有哪些属性不同,并做相应更新 
-    } 
 } }
 </​code>​ </​code>​
  
-==== 操作自己订阅的流 ​==== +==== 设置会议人数限制 ​==== 
- +使用createAndJoinConference接口创建会议或者第一个使用joinRoom接口加入的员为会议创建者会议创建者在创建会议时可以设置会议中的最大视频数、主播数、观众数、共享桌面数的上限,默认最大视频数12,最大主播数100,最大观众数600,最大共享桌面数2.创建会议时使用RoomConfig参数指定人数限制,过程如下: 
-对于订阅功的流,可以有以操作 +<​code>​ 
- +RoomConfigroomConfig = [[RoomConfig alloc] init]; 
- <​code ​objc+    ​roomConfig.maxVideoCount = 6; 
-   /*+    ​roomConfig.maxTalkerCount = 9; 
- * \~chinese +    ​roomConfig.maxAudienceCount = 30; 
- * mute远端音频 +    ​roomConfig.maxPubDesktopCount = 1; 
- * +    ​[[[EMClient sharedClient] conferenceManager] joinRoom:roomName password:pswd role:role roomConfig:​roomConfig completion:block];
- * @param aStreamId ​       要操作的Steam id +
- * @param isMute ​           是否静音 +
- * +
- * \~english +
- * Mute remote audio +
- * +
- * @param aStreamId ​       Steam id +
- * @param isMute ​           is mute +
- */ +
-- (void)muteRemoteAudio:(NSString *)aStreamId mute:(BOOL)isMute;​ +
- +
-/** +
- * \~chinese +
- * mute远端视频 +
- * +
- * @param aStreamId ​       要操作的Steam id +
- * @param isMute ​           是否显示 +
- * +
- * \~english +
- * Mute remote video +
- * +
- * @param aStreamId ​       Steam id +
- * @param isMute ​           is mute +
- */ +
-- (void)muteRemoteVideo:(NSString *)aStreamId mute:(BOOL)isMute;+
 </​code>​ </​code>​
-==== 取消订阅流 ====+若加入会议时,超过最大主播数上限,加入会议返回失败,error为EMErrorCallSpeakerFull
  
-成员B如果不想再看成员A的音视频,可以用SDK接口unsubscribe +若发视频流时超过最大视频数上限,将收到如下回通知: 
- +<​code>​ 
-<​code ​objc> +- (void)streamPubDidFailed:​(EMCallConference *)aConference error:​(EMError*)aError
-//​Objective-C +
-- (void)unsubStream+
 { {
-    __weak typeof(self) weakself = self; 
-    [[EMClient sharedClient].conferenceManager unsubscribeConference:​self.conference streamId:​self.pubStreamId completion:​^(EMError *aError) { 
-        //code 
-    }]; 
 } }
 </​code>​ </​code>​
- +共享桌面时,超过最大上限收到如下回调通知: 
-==== 成员A取消流 ==== +<​code>​ 
- +- (void)DesktopStreamDidPubFailed:​(EMCallConference *)aConference error:​(EMError*)aError
-成员A可以调用unpublish接口取消自己已经发布的据流操作成功后,会议中的其他成员会收到回调[EMConferenceManagerDelegate streamDidUpdate:​removeStream:​] ,将对应的数据流信息移除 +
- +
-<​code ​objc> +
-//​Objective-C +
-- (void)unpubStream+
 { {
-    [[EMClient sharedClient].conferenceManager unpublishConference:​self.conference ​ 
-                                                          streamId:​self.pubStreamId completion:​^(EMError *aError) ​ 
-                                                          { 
-                                                              //code 
-                                                          }]; 
 } }
 </​code>​ </​code>​
 +==== 获取会议信息 ====
  
-==== 成员B离开会议 ​====+会议进行中,可以通过getConference 方法来查询会议信息,从而可以拿到主播列表,观众人数等信息。
  
-成员B调用SDK接口离开会议,会议中的其他成员会收到回调[EMConferenceManagerDelegate memberDidLeave:​member:​] 
-<code objc> 
-[[EMClient sharedClient].conferenceManager leaveConference:​aCall completion:​nil];​ 
-</​code>​ 
- 
-<code objc> 
-//​Objective-C 
-- (void)memberDidLeave:​(EMCallConference *)aConference member:​(EMCallMember *)aMember 
-{ 
-    if ([aConference.callId isEqualToString:​self.conference.callId]) { 
-        NSString *message = [NSString stringWithFormat:​@"​成员 %@ 已经离开会议",​ aMember.memberName];​ 
-        [self showHint:​message];​ 
-    } 
-} 
-</​code>​ 
- 
-==== 管理员销毁会议 ==== 
- 
-管理员离开会议时,可以选择离开会议或销毁会议,销毁会议后,其他成员都将收到会议结束的回调[EMConferenceManagerDelegate conferenceDidEnd:​reason:​error];离开会议后,若此会议是调用 joinRoom 方法创建的,且当前会议中无管理员,服务器会将管理员权限随机分配给某个主播。只有管理员权限可以进行销毁会议操作。 
- 
-销毁会议使用接口 
-<code objc> 
-[[EMClient sharedClient].conferenceManager destroyConferenceWithId:​aConfId completion:​nil];​ 
-</​code>​ 
-离开会议使用接口 
-<code objc> 
-[[EMClient sharedClient].conferenceManager leaveConference:​aConference completion:​nil];​ 
-</​code>​ 
- 
-当会议中的成员都离开会议后,服务器会自动将会议结束销毁 
-==== 会议结束 ====  
-当会议结束时,成员将收到会议结束的回调[EMConferenceManagerDelegate conferenceDidEnd:​reason:​error],该回调的详细接口如下: 
 <code objc> <code objc>
 /*! /*!
  ​* ​ \~chinese  ​* ​ \~chinese
- ​* ​ 会议已经结束+ ​*  ​获取会议信息
  *  *
- ​* ​ @param ​aConference ​      会议实例 + ​* ​ @param ​aConfId ​          会议ID(EMCallConference.confId) 
- ​* ​ @param ​aReason ​          ​结束原因 + ​* ​ @param ​aPassword ​        ​会议密码 
- ​* ​ @param ​aError ​           错误信息 + ​* ​ @param ​aCompletionBlock ​ 完成的回调
- * +
- ​* ​ \~english +
- ​* ​ The conference is over +
- * +
- ​* ​ @param aConference ​      ​EMConference instance +
- ​* ​ @param aReason ​          The end reason +
- ​* ​ @param aError ​           The error+
  */  */
-- (void)conferenceDidEnd:(EMCallConference ​*)aConference +- (void)getConference:(NSString ​*)aConfId 
-                  ​reason:(EMCallEndReason)aReason +             password:(NSString *)aPassword 
-                   error:(EMError *)aError;+           completion:(void (^)(EMCallConference *aCall, ​EMError *aError))aCompletionBlock;
 </​code>​ </​code>​
- 
-EMCallEndReason表示会议结束的原因,管理员正常结束会议为EMCallEndReasonDestroy,被踢出会议为EMCallEndReasonBeenkicked 
- 
 ==== 会议属性 ==== ==== 会议属性 ====
- 
 会议属性是会议的状态信息,由一组(key,value)组成。会议中的所有角色成员(管理员、主播、观众)都可以设置/​删除会议频道属性,设置的会议属性会通知给会议中的所有人。 会议属性是会议的状态信息,由一组(key,value)组成。会议中的所有角色成员(管理员、主播、观众)都可以设置/​删除会议频道属性,设置的会议属性会通知给会议中的所有人。
  
行 544: 行 452:
  * {@link EMConferenceManagerDelegate#​conferenceAttributeUpdated:​attributeAction:​attributeKey:​}回调.  * {@link EMConferenceManagerDelegate#​conferenceAttributeUpdated:​attributeAction:​attributeKey:​}回调.
  * 该方法需要在加入会议后调用.  * 该方法需要在加入会议后调用.
- * 
- * @param attrKey 
- * @param attrValue 
- * @param aCompletionBlock 
- * 
- * \~english 
- * Set conference attribute,​All members in this conference(include myself) will receive a callback 
- * in {@link EMConferenceManagerDelegate#​conferenceAttributeUpdated:​attributeAction:​attributeKey:​}. 
- * this method can only be used after join a conference. 
  *  *
  * @param attrKey  * @param attrKey
行 570: 行 469:
  * {@link EMConferenceManagerDelegate#​conferenceAttributeUpdated:​attributeAction:​attributeKey:​}回调.  * {@link EMConferenceManagerDelegate#​conferenceAttributeUpdated:​attributeAction:​attributeKey:​}回调.
  * 该方法需要在加入会议后调用.  * 该方法需要在加入会议后调用.
- * 
- * @param aKey 
- * @param aCompletionBlock 
- * 
- * \~english 
- * Delete conference attribute,​All members in this conference(include myself) will receive a callback 
- * in {@link EMConferenceManagerDelegate#​conferenceAttributeUpdated:​attributeAction:​attributeKey:​}. 
- * this method can only be used after join a conference. 
  *  *
  * @param aKey  * @param aKey
行 592: 行 483:
 </​code>​ </​code>​
 每一个EMConferenceAttribute包括了会议属性中的key,value,以及本次修改的action,action包括ADD、UPDATE、DELETE 每一个EMConferenceAttribute包括了会议属性中的key,value,以及本次修改的action,action包括ADD、UPDATE、DELETE
-==== 踢人 ​====+==== cdn合流推流 ​==== 
 +多人音视频支持将会议中的音视频流合并成一个流,推送到第三方的cdn直播服务器。整个合流推流过程包括开启cdn推流,更新推流布局,停止推流。
  
-管理员可以强制会议成员离开会议使用接口+== 开启cdn推流 == 
 +会议的创建者在创建会议使用RoomConfig的接口,可以决定是否开启cdn推流,推流配置LiveConfig是RoomConfig的一个参数,可设置cdn推流的相关信息。开启过程如下:
 <code objc> <code objc>
-/*! +LiveConfigliveconfig = [[LiveConfig alloc] init]; 
- ​* ​ ​\~chinese +CDNCanvascanvas = [[CDNCanvas alloc] init]; 
- *  踢人,需要管理员权限 +canvas.fps = 18; 
- * +canvas.kbps = 900; 
- *  @param aConfId ​          ​会议ID(EMCallConference.confId) +canvas.codec = @"​H264";​ 
- ​*  ​@param aMemberNameList ​  ​成员名列表 +canvas.bgclr = 0x0000ff; 
- *  @param aCompletionBlock ​ 完成的回调 +canvas.width = [EMDemoOption sharedOptions].liveWidth;​ 
- * +canvas.height = [EMDemoOption sharedOptions].liveHeight;​ 
- *  \~english +liveconfig.canvas = canvas; 
- *  Kick members, requires administrator privileges +liveconfig.cdnUrl = [EMDemoOption sharedOptions].cdnUrl;​ 
- * +liveconfig.layoutStyle = CUSTOM; 
- *  @param aConfId ​          ​Conference ID (EMCallConference.confId) +liveconfig.record = YES; //​是否录制推流到cdn的音视频 
- *  @param aMemberNameList ​  ​Member Name list +roomConfig.liveConfig = liveconfig;
- ​* ​ @param aCompletionBlock ​ The callback block of completion +
- *+
-- (void)kickMemberWithConfId:​(NSString *)aConfId +
-                 ​memberNames:​(NSArray<​NSString *> *)aMemberNameList +
-                  completion:​(void (^)(EMError *aError))aCompletionBlock;+
 </​code>​ </​code>​
  
-==== 管理员变更 ==== +canvas设置width、height0时,cdn推流为**纯音频推流**
-会议中普通成员成管理员,或管理员降级为普通成员时,会议中的其他成员将收到管理员变更的回调。管理员变更回调分管理员新增和管理员移除,回调接口如下: +
-<code objc> +
-/*! +
- ​* ​ \~chinese +
- ​* ​ 管理员新增 +
- * +
- ​* ​ @param aConference ​      ​会议实例 +
- ​* ​ @param adminmemid ​        ​新的管理员memid +
- * +
- ​* ​ \~english +
- ​* ​ The admin has added +
- * +
- ​* ​ @param aConference ​      ​EMConference instance +
-  ​@param adminmemid ​        The new admin memid +
- *+
-- (void)adminDidChanged:​(EMCallConference ​*)aConference +
-               ​newAdmin:​(NSString*)adminmemid;​+
  
-/*! +推流成功后,可以在EMConference对象中查看liveId,如果只有一路推流,可直接使用EMConference对象liveId,如果存在多路推流,可访问EMConference对象的liveCfgs对象,liveCfgs存储了所有的推流信息
- ​* ​ \~chinese +
- ​* ​ 管理员放弃 +
- * +
- ​* ​ @param aConference ​      ​会议实例 +
- ​* ​ @param adminmemid ​        ​放弃管理员memid +
- * +
- ​* ​ \~english +
- ​* ​ The admin has removed +
- * +
- ​* ​ @param aConference ​      EMConference ​instance +
- ​* ​ @param adminmemid ​        The removed admin memid +
- */ +
-- (void)adminDidChanged:​(EMCallConference *)aConference +
-            removeAdmin:​(NSString*)adminmemid;​ +
-</​code>​ +
-==== 获取会议信息 ​====+
  
-在会议进行中,以通过getConferenceInfo 方法来查询会议信息,从而可以拿到主播列表,观众人等信息。+LiveConfig设置的参如下:
  
 <code objc> <code objc>
 /*! /*!
- *  \~chinese +*  \~chinese 
- ​*  ​判断会议是否存在 +*  ​cdn 画布设置,创建会议时使用 
- * +*/ 
- ​* ​ @param aConfId ​          ​会议ID(EMCallConference.confId+@interface CDNCanvas : NSObject 
- ​* ​ @param aPassword ​        ​会议密码 + /*! \~chinese 画布宽度 ​ */ 
- ​* ​ ​@param aCompletionBlock ​ 完成回调 +@property ​(nonatomicNSInteger width; 
- * +/*! \~chinese 画布高度 ​ */ 
- ​* ​ \~english +@property (nonatomic) NSInteger height; 
-  ​Determine if the conference exists +/*! \~chinese 画布的背景色,格式为 RGB 定义下的 Hex 值,不要带 # 号,如 0xFFB6C1 表示浅粉色。默认0x000000,黑色。 
- +*/ 
- ​*  ​@param aConfId ​          ​Conference ID (EMCallConference.confId+@property (nonatomic) NSInteger bgclr; 
- ​*  ​@param aPassword ​        The password of the conference +/*\~chinese 推流帧率,可设置范围10-30 ​*/ 
- ​* ​ @param aCompletionBlock ​ The callback block of completion +@property ​(nonatomicNSInteger fps; 
- */ +/*! \~chinese 推流码率,单位kbps,width和height较大时,码率需要提高,可设置范围1-5000 ​ */ 
-(void)getConference:​(NSString *)aConfId +@property ​(nonatomicNSInteger kbps; 
-             password:​(NSString ​*)aPassword +/*! \~chinese 推流编码格式,目前只支持"​H264"​ */ 
-           completion:(void (^)(EMCallConference ​*aCall, EMError *aError))aCompletionBlock+@property ​(nonatomicNSStringcodec
-</​code>​+ 
 +@end
  
-==== 管理会议角色 ==== 
-管理员可以对其他观众、主播的角色进行升级、降级处理,接口如下: 
-<code objc> 
 /*! /*!
- *  \~chinese +*  \~chinese 
- ​*  ​改变成员角色,需要管理员权限 +*  ​cdn推流使用的画布类型
- ​* ​户角色: Admin > Talker > Audience +
- * 当角色升级时,​用户需要给管理员发送申请,​管理通过该接口改变用户接口. +
- * 当角色降级时,​用户直接调用该接口即可. +
- * +
- ​* ​ @param aConfId ​          ​会议ID(EMCallConference.confId) +
- ​* ​ @param aMember ​       成员 +
- ​* ​ @param aRole             ​成员角色 +
- ​* ​ @param aCompletionBlock ​ 完成回调 +
- * +
- ​* ​ \~english +
- ​* ​ Changing member roles, requires administrator privileges +
- * Role: Admin > Talker > Audience +
- * When role upgrade, you need to send a request to Admin, only Admin can upgrade a role. +
- * When role degrade, you can degrade with this method yourself. +
- * +
- ​* ​ @param aConfId ​          ​Conference ID (EMCallConference.confId) +
- ​* ​ @param aMember ​       Member +
- ​* ​ @param aRole             The Role +
- ​* ​ @param aCompletionBlock ​ The callback block of completion +
- */ +
-- (void)changeMemberRoleWithConfId:​(NSString *)aConfId +
-                            member:​(EMCallMember *)aMember +
-                              role:​(EMConferenceRole)toRole +
-                        completion:​(void (^)(EMError *aError))aCompletionBlock;​ +
-</​code>​ +
- +
-==== 全体静音/​解除全体静音 ==== +
-管理员可以对会议进行全体静音/​解除全体静音设置,设置后,会议中的主播都将处于静音状态,新加入的主播也将自动处于静音状态。只有管理员可以调用此接口。 +
-接口API如下: +
-<code objc> +
-/** +
-* \~chinese +
-* 开启/​停止全体静音,​只有管理员可调用此接口 +
-+
-* @param enable 是否启用全体静音 +
-* @param completion 回调 +
-+
-* \~english +
-* start/stop mute all members +
-* @params enable whether to start +
-* @params completion the callback functio+
 */ */
-(void)muteAll:​(BOOL)mute +typedef NS_ENUM(NSInteger, LayoutStyle{ 
-     completion:​(void(^)(EMError *aError))aCompletionBlock;​ +    ​CUSTOM,​ 
-</​code>​ +    DEMO, 
- +    GRID 
-管理员调用此接口后,会议中的主播将收到全体静音状态的回调,回调函数如下 +};
-<code objc>+
 /*! /*!
- \~chinese + ​\~chinese 
- ​* ​收到全体静音/​解除全体静音的回调 +*  ​cdn推流设置
- * +
- * @param aConference ​    ​会议 +
- * @param aMuteAll ​  ​是否全体静音 +
- * +
- ​*\~english +
- * callback when admin set muteAll/​unmuteAll +
- * +
- * @param aConference ​    ​EMCallConference instance +
- * @param aMuteAll ​ Weather muteAll or not+
 */ */
-(void)conferenceDidUpdated:​(EMCallConference ​*)aConference +@interface LiveConfig : NSObject 
-                  ​muteAll:​(BOOL)aMuteAll;+ 
 +/*! \~chinese 推流url地址*/​ 
 +@property ​(nonatomic,​strongNSString *cdnUrl; 
 + 
 +/*! \~chinese 推流画布的配置*/​ 
 +@property ​(nonatomic) CDNCanvascanvas; 
 + 
 +/*! \~chinese 推流方式,GRID或者CUSTOM,GRID将由服务器设置位置信息,CUSTOM将由用户自定义流的位置信息*/​ 
 +@property (nonatomicLayoutStyle layoutStyle;​ 
 + 
 +/*! \~chinese 是否开启自定义录制*/​ 
 +@property ​(nonatomic) ​BOOL record; 
 + 
 +/*! \~chinese 音频录制参数*/​ 
 +@property (nonatomicAudioConfig* audioCfg; 
 + 
 +@end
 </​code>​ </​code>​
  
-==== 指定成员静音/​解除静音 ==== +== 更新布局 ​== 
-管理员可以对会议中的指定成员进行静音/​解除静音设置被指定员可以是主播也可以是管理员。设置后被指定成员将静音/​解除静音。只管理员可以调此接口。 +当用户调用更新布局接口后cdn推流方式将强制变CUSTOM模式流的位置信息都由户自己定义。 
-接口API如下:+更新布局的接口如下:
 <code objc> <code objc>
 /*! /*!
 *  \~chinese *  \~chinese
-*  ​将指定成员静音/​解除静音,管理员调用+*  ​修改会议的cdn推流位置
 * *
 *  @param aCall             ​会议实例(自己创建的无效) *  @param aCall             ​会议实例(自己创建的无效)
-*  @param ​aMemId 指定成员memId +*  @param ​aReagionList 媒体流位置信息 
-*  @param ​aMute 操作,YES为静音,NO为解除静音+*  @param ​aLiveId ​          ​推流Id
 *  @param aCompletionBlock 回调函数 *  @param aCompletionBlock 回调函数
-+*/ 
-*  \~english +- (void)updateConference:​(EMCallConference*)aCall 
-*  mute/unmute one member,only admin call this function +                  ​liveId:​(NSString*)aLiveId 
-+              ​setRegions:(NSArray<​LiveRegion*>​*)aReagionList 
-*  @param aCall             ​EMConference instance (invalid by yourself) +              completion:​(void(^)(EMError *aError))aCompletionBlock;​
-*  @param aMemId The memid of member +
-*  @param aMute Opereation,​YES means mute,and NO means unmute +
-*  @param aCompletionBlock The callback function +
- */ +
-- (void)setMuteMember:​(EMCallConference *)aCall +
-                memId:(NSString *)aMemId +
-                 mute:(BOOL)aMute +
-           ​completion:​(void (^)(EMError *aError))aCompletionBlock;​+
 </​code>​ </​code>​
 +LiveRegion的结构如下:
 +<code objc>
  
-管理员调用此接口后,被指定的成员将收到静音状态的回调,回调函数如下 
-<code objc> 
 /*! /*!
- \~chinese + ​\~chinese 
- ​* ​收到静音/​解除静音回调 + ​cdn推流每一路流的模式
- * +
- * @param aConference ​    ​会议 +
- * @param aMute   ​是否静音 +
- * +
- ​*\~english +
- * callback when recv mute command +
- * +
- * @param aConference ​    ​EMCallConference instance +
- * @param aMute  Weather mute or not+
 */ */
-(void)conferenceDidUpdated:(EMCallConference*)aConference +typedef NS_ENUM(NSInteger, LiveRegionStyle
-                        mute:(BOOL)aMute;+    /*! \~chinese FIt模式 */ 
 +    LiveRegionStyleFit,​ 
 +    /*! \~chinese FIll模式 */ 
 +    LiveRegionStyleFill 
 +}; 
 + 
 +/*! 
 +*  \~chinese 
 +*  cdn推流的每一路流的区域位置信息 
 +*/ 
 +@interface LiveRegion ​NSObject 
 + 
 +/*! \~chinese 流ID */ 
 +@property ​(nonatomic) NSStringstreamId; 
 + 
 +/*! \~chinese 流的左上角在x轴坐标 ​ */ 
 +@property (nonatomicNSInteger x; 
 + 
 +/*! \~chinese 流的左上角在y轴坐标 ​ */ 
 +@property ​(nonatomicNSInteger y; 
 + 
 +/*! \~chinese 流的宽度 ​ */ 
 +@property (nonatomic) NSInteger w; 
 + 
 +/*! \~chinese 流的高度 */ 
 +@property (nonatomic) NSInteger h; 
 + 
 +/*! \~chinese 流的图层顺序,越小越在底层,从1开始 */ 
 +@property (nonatomic) NSInteger z; 
 + 
 +/*! \~chinese 流的显示模式,Fit或Fill */ 
 +@property (nonatomic) LiveRegionStyle style; 
 + 
 +@end
 </​code>​ </​code>​
  
-==== 观众申请主播 ==== +使用方法如下
-会议中的观众角色可以向管理员发申请成为主播,管理员可以选择同意或者拒绝。观众申请主持人的接口需要管理员的memId,先通过获取会议属性接口获取到管理员的memName,然后根据memName以及成员加入的回调中获取到的EMCallMember,获取到memId。接口如下+
 <code objc> <code objc>
 +NSMutableArray<​LiveRegion*>​* regionsList = [NSMutableArray array];
 +LiveRegion* region = [[LiveRegion alloc] init];
 +region.streamId = _streamId;
 +region.style = LiveRegionStyleFill;​
 +region.x = 80;
 +region.y = 60;
 +region.w = 320;
 +region.h = 240;
 +region.z = 9;
 +[regionsList addObject:​region];​
 +[[[EMClient sharedClient] conferenceManager] updateConference:​[EMDemoOption sharedOptions].conference liveId:​aLiveId setRegions:​regionsList completion:​^(EMError *aError) {
 +    }];
 +</​code>​
 +
 +== 多路推流 ==
 +多人音视频支持加入会议后,增加一路推流,只有管理员权限可进行次操作。增加一路推流的api方法如下:
 +
 +<​code>​
 /*! /*!
 *  \~chinese *  \~chinese
-*  ​观众申请连麦成为主播,观众角色调用+*  ​添加一路推流
 * *
 *  @param aCall             ​会议实例(自己创建的无效) *  @param aCall             ​会议实例(自己创建的无效)
-*  @param ​aAdminId 管理员的memId+*  @param ​aLiveConfig 推流配置
 *  @param aCompletionBlock 回调函数 *  @param aCompletionBlock 回调函数
-* 
-*  \~english 
-*  Request tobe Speaker,​only audience call this function 
-* 
-*  @param aCall             ​EMConference instance (invalid by yourself) 
-*  @param aAdminId The memid of admin 
-*  @param aCompletionBlock The callback function 
 */ */
-- (void)requestTobeSpeaker:​(EMCallConference *)aCall ​adminId:(NSString ​*)aAdminId ​completion:​(void (^)(EMError *aError))aCompletionBlock;​+- (void)addConferenceLive:​(EMCallConference*)aCall 
 +                  LiveCfg:(LiveConfig*)aLiveConfig 
 +               completion:​(void(^)(EMError *aError))aCompletionBlock;​
 </​code>​ </​code>​
-观众发出申请后,管理员将会收到以下回调: +== 停止推流 == 
-<​code ​objc>+管理员控制停止某一路推流,停止推流接口如下: 
 +<​code>​
 /*! /*!
- \~chinese + ​\~chinese 
- ​* ​收到观众申请主播的请求,只有管理员会触发 +*  ​删除一路推流
- * +
- * @param aConference ​    ​会议 +
- * @param aMemId ​  ​申请人memId +
- * @param aNickName 申请人昵称 +
- * @param aMemName 申请人memName +
- * +
- ​*\~english +
- * callback when admin recv the request of become speaker +
- * +
- * @param aConference ​    ​EMCallConference instance +
- * @param aMemId ​  The memId of requster +
- * @param aNickName ​ The nickname of requster +
- * @param aMemName ​ The memname of requster+
 */ */
-- (void)conferenceReqSpeaker:​(EMCallConference*)aConference memId:​(NSString*)aMemId nickName:(NSString*)aNickName memName:(NSString*)aMemName;+- (void)deleteConferenceLive:​(EMCallConference*)aCall 
 +                      liveId:​(NSString*)aLiveId 
 +                  completion:(void(^)(EMError ​*aError))aCompletionBlock;
 </​code>​ </​code>​
-在回调中管理员可选择同意或者拒绝如果同意需要调用改变用户权限的接口,然后调用回复接口如果拒绝则直接调用回复接口。回复接口如下:+==== 云端录制 ==== 
 +多人音视频会议支持云端录制功能包括服务器默认录制及自定义布局录制两种 
 +== 服务器默认录制 == 
 +服务器默认录制为九宫格布局在会议创建时由创建者指定是否开启开启方法为创建会议时指定isRecord为YES,如下:
 <code objc> <code objc>
 +//​Objective-C
 +    RoomConfig* roomConfig = [[RoomConfig alloc] init];
 +    roomConfig.isMerge = YES;
 +    roomConfig.isRecord = YES;
 +    [[[EMClient sharedClient] conferenceManager] joinRoom:​roomName password:​pswd role:role roomConfig:​roomConfig completion:​^(EMCallConference *aCall, EMError *aError) {
 +    self.conference = aCall;
 +    }];
 +</​code>​
 +== 自定义布局录制 ==
 +在推流的LiveConfig设置里,设record为YES,可以开启自定义录制,开启后会把推流到cdn的音视频按照推流布局录制下来。如果推流时未开启,也可以在推流后进行开启/​停止自定义录制布局操作。开启/​停止自定义录制布局的api如下:
 +<​code>​
 /*! /*!
 *  \~chinese *  \~chinese
-*  ​管理员同意/拒绝观众的上麦申请,管理员调用+*  ​启动/停止自定义录制
 * *
 *  @param aCall             ​会议实例(自己创建的无效) *  @param aCall             ​会议实例(自己创建的无效)
-*  @param ​aMemId 上麦申请的观众的memId +*  @param ​aLiveId 推流/​录制Id 
-*  @param ​aResult ​操作结果0为同意,1为拒绝+*  @param ​aEnabled ​操作,启动/​停止
 *  @param aCompletionBlock 回调函数 *  @param aCompletionBlock 回调函数
-+*/ 
-*  \~english +- (void)enableRecordLiveStream:​(EMCallConference*)aCall 
-*  Admin agree/​disagree ​ the audience request tobe speaker.only admin call this function +                        ​liveId:​(NSString*)aLiveId 
-+                       enabled:(BOOL)aEnabled 
-*  @param aCall             ​EMConference instance (invalid by yourself) +                    completion:​(void(^)(EMError *aError))aCompletionBlock;​
-*  @param aMemId The memid of member who requeset tobe speaker +
-*  @param aMute Opereation result,0 means agree,and 1 means disagree +
-*  @param aCompletionBlock The callback function +
- */ +
-- (void)responseReqSpeaker:​(EMCallConference *)aCall +
-                     memId:(NSString *)aMemId +
-                    ​result:(NSInteger)aResult +
-                completion:​(void (^)(EMError *aError))aCompletionBlock;​+
 </​code>​ </​code>​
  
-==== 主播申请管理员 ​==== +==== 设置昵称,头像解决方案 ​==== 
-会议中的主播角色可以向管理员发申请成为管理员,管理员可以选择同意或者拒绝,成为管理员后,各管理员之间的权限是相同的。主播申请主持人的接口需要管理员的memId先通过获取会议属性接口获取到管理员的memName然后根据memName以及成员加入的回调获取到的EMCallMember获取到管理员memId。接口如下:+音视频通话提供设置昵称的接口,在加入会议时设置;不直接提供头像设置接口,但提供头像设置方案,在加入会议时将头像url设置到RoomConfigext中,过程如下:
 <code objc> <code objc>
-/*! +//​Objective-C 
- ​\~chinese +    ​RoomConfigroomConfig = [[RoomConfig alloc] init]; 
-*  主播申请成为管理员,主播角色调用 +    ​roomConfig.nickName = @"​昵称";​ 
-+    ​NSMutableDictionaryextDic = [NSMutableDictionary dictionary];​ 
- ​@param aCall             ​会议实例(自己创建的无效) +    ​NSStringheadImage = [EMDemoOption sharedOptions].headImage;​ 
-*  ​@param aAdminId 管理员的memId +    ​[extDic setObject:​headImage forKey:@"​headImage"​];​ 
- ​@param aCompletionBlock 回调函数 +    ​NSError ​*jsonError = nil; 
-+    ​NSData ​*jsonData = [NSJSONSerialization dataWithJSONObject:​extDic options:​NSJSONWritingPrettyPrinted error:&​jsonError];​ 
- ​\~english +    ​NSString ​*jsonStr = @"";​ 
-*  Request tobe Admin,only speraker call this function +    ​if ​(jsonData && !jsonError{ 
-+        ​jsonStr = [[NSString alloc] initWithData:​jsonData encoding:​NSUTF8StringEncoding];​ 
-*  @param aCall             ​EMConference instance ​(invalid by yourself+    } 
-*  @param aAdminId The memid of admin +    ​roomConfig.ext = jsonStr; 
-*  @param aCompletionBlock The callback function +    ​[[[EMClient sharedClient] conferenceManager] joinRoom:roomName password:pswd role:role roomConfig:​roomConfig ​completion:​^(EMCallConference *aCall, ​EMError *aError) ​
- */ +    self.conference = aCall; 
-- (void)requestTobeAdmin:(EMCallConference *)aCall adminId:(NSString *)aAdminId ​completion:(void (^)(EMError *aError))aCompletionBlock;+    }];
 </​code>​ </​code>​
-主播发出申请后,管理员将会收到以回调+==== 海外代理 ==== 
 +1v1通话支持不同集群区域的人通话使用代理,减小延迟。使用多集群代理需要音视频后台配置IP及端口的映射文件rtcconfig.json,​并禁用相关appkey的直连。启用多集群代理功能需要配置如属性
 <code objc> <code objc>
-/*!+[EMClient sharedClient].options.isUseRtcConfig = YES; 
 +</code> 
 + 
 +==== 私有部署 ==== 
 +私有部署设置方法参见[[http://​docs-im.easemob.com/​im/​ios/​other/​privatecloud#​%E7%A7%81%E6%9C%89%E4%BA%91sdk%E9%9B%86%E6%88%90%E9%85%8D%E7%BD%AE|私有云sdk集成配置]] 
 +===== 音视频管理 ===== 
 +==== 设置通话参数 ==== 
 +通话之前,可以设置音频通话的最大音频码率,最小视频码率、最大视频码率、分辨率和是否清晰度优先,设置方法如下 
 +<​code>​ 
 +EMCallOptions *options = [[EMClient sharedClient].callManager getCallOptions];​ 
 +options.maxAudioKBps = 32; 
 +options.maxVideoKBps = 3000; 
 +options.minVideoKBps = 500; 
 +options.maxVideoFrameRate = 20; 
 +options.videoResolution = EMCallVideoResolution352_288;​ 
 +options.isClarityFirst = YES;//​若设为清晰度优先,将在弱网环境下保证视频的分辨率 
 +</​code>​ 
 +==== 停止发布流 ==== 
 +成员A可以调用unpublish接口取消自己已经发布的数据流,操作成功后,会议中的其他成员会收到回调[EMConferenceManagerDelegate streamDidUpdate:​removeStream:​] ,将对应的数据流信息移除 
 + 
 +<code objc> 
 +//​Objective-C 
 +- (void)unpubStream 
 +
 +    [[EMClient sharedClient].conferenceManager unpublishConference:​self.conference  
 +                                                          streamId:​self.pubStreamId completion:​^(EMError *aError)  
 +                                                          { 
 +                                                              //code 
 +                                                          }]; 
 +
 +</​code>​ 
 + 
 +==== 停止订阅流 ==== 
 + 
 +成员B如果不想再看成员A的音视频,可以调用SDK接口unsubscribe 
 + 
 +<code objc> 
 +//​Objective-C 
 +- (void)unsubStream 
 +
 +    __weak typeof(self) weakself = self; 
 +    [[EMClient sharedClient].conferenceManager unsubscribeConference:​self.conference streamId:​self.pubStreamId completion:​^(EMError *aError) { 
 +        //code 
 +    }]; 
 +
 +</​code>​ 
 + 
 +==== 操作自己订阅的流 ==== 
 + 
 +对于订阅成功的流,可以有以下操作: 
 + 
 + <​code objc> 
 +   /**
  * \~chinese  * \~chinese
- ​* ​收到主播申请管理员的请求,只有管理员会触发+ ​* ​mute远端音频
  *  *
- * @param ​aConference ​    ​会议 + * @param ​aStreamId ​       要操作的Steam id 
- * @param ​aMemId ​  ​申请人memId + * @param ​isMute ​           是否静音 
- ​* ​@param aNickName 申请人昵称 + */ 
- ​* ​@param aMemName 申请人memName+- (void)muteRemoteAudio:​(NSString ​*)aStreamId mute:​(BOOL)isMute;​ 
 + 
 +/** 
 + * \~chinese 
 + * mute远端视频
  *  *
- *\~english + * @param ​aStreamId ​       要操作的Steam id 
- * callback when admin recv the request of become admin + * @param ​isMute ​           是否关闭 
- * + */ 
- * @param ​aConference ​    ​EMCallConference instance +- (void)muteRemoteVideo:(NSString *)aStreamId mute:(BOOL)isMute;
- * @param ​aMemId ​  The memId of requster +
- * @param aNickName ​ The nickname of requster +
- * @param aMemName ​ The memname of requster +
-*/ +
-- (void)conferenceReqAdmin:​(EMCallConference*)aConference memId:​(NSString*)aMemId nickName:(NSString*)aNickName memName:​(NSString*)aMemName;+
 </​code>​ </​code>​
-回调中,管理员可以选择同意或者拒绝如果同意,需要调用改变用户权限接口然后调用复接口,如果拒绝,则直接用回复接口。回复接口如下:+ 
 +==== 通话中音视频控制 ==== 
 +成员发布了自己的音视频流后,会议过程中,可以进行以下操作: 
 +  * 切换前后摄像头,调用如下 
 +<​code>​ 
 +[[[EMClient sharedClient] conferenceManager] updateConferenceWithSwitchCamera:​self.conference];​ 
 +</​code>​ 
 +  * 开关静音 
 +<​code>​ 
 +[[[EMClient sharedClient] conferenceManager] updateConference:​self.conference isMute:​YES];​ 
 +</​code>​ 
 +  * 开关视频 
 +<​code>​ 
 +[[[EMClient sharedClient] conferenceManager] updateConference:​self.conference enableVideo:​YES];​ 
 +</​code>​ 
 + 
 +当成员对自己数据流做开关静音/​视频时会议中的其他成员会收到回调[EMConferenceManagerDelegate streamDidUpdate:​stream:​] 
 <code objc> <code objc>
-/*! +//Objective-C 
-*  \~chinese +- (void)streamDidUpdate:​(EMCallConference *)aConference stream:(EMCallStream ​*)aStream 
-*  管理员同意/​拒绝主播的申请管理员请求,管理员调用 +
-+    if ([aConference.callId isEqualToString:self.conference.callId] && aStream != nil
-*  @param aCall             ​会议实例(自己创建的无效) +        //​判断本地缓存的EMCallStream实例与aStream有哪些属性不同,并做相应更新 
-*  @param aMemId 申请管理员的主播的memId +    } 
-*  @param aResult 操作结果,0为同意,1为拒绝 +}
-*  @param aCompletionBlock 回调函数 +
-+
-*  \~english +
-*  Admin agree/​disagree ​ the speaker request tobe admin.only admin call this function +
-+
-*  @param aCall             ​EMConference instance (invalid by yourself) +
-*  @param aMemId The memid of speaker who request tobe admin +
-*  @param aMute Opereation result,0 means agree,and 1 means disagree +
-*  @param aCompletionBlock The callback function +
- *+
-- (void)responseReqAdmin:​(EMCallConference *)aCall memId:(NSString ​*)aMemId result:(NSInteger)aResult completion:(void (^)(EMError *aError))aCompletionBlock;​+
 </​code>​ </​code>​
 +==== 音视频首帧回调 ====
  
-==== 视频质量统计数据 ​====+当成员发布流成功,发送第一帧音视频数据时,成员收到以下通知
  
-使用[conferenceManager enableStatistics:​YES]开启视频质量统计后,用户会周期性收到视频质量数据的回调,回调函数定义如下: 
 <​code>​ <​code>​
 /*! /*!
  * \~chinese  * \~chinese
- ​* ​当前会议的媒体流质量报告回调+ ​* ​发送第一帧音视频数据时,收到此回调
  *  *
  * @param aConference ​    ​会议  * @param aConference ​    ​会议
 + * @param aType 流类型,音频或视频
  * @param streamId 流ID  * @param streamId 流ID
- * @param aReport ​  ​会议的质量参数 
- * 
- ​*\~english 
- * get the streamId of stream.callback when publish success 
- * 
- * @param aConference ​    ​EMCallConference instance 
- * @param streamId streamId 
- * @param aReport ​ The stat report of stream 
 */ */
-- (void)conferenceDidUpdate:​(EMCallConference*)aConference ​streamId:(NSString*)streamId ​statReport:(EMRTCStatsReport ​*)aReport;+- (void)streamDidFirstFrameSended:​(EMCallConference*)aConference ​type:(EMMediaType)aType streamId:(NSString*)streamId;
 </​code>​ </​code>​
-EMRTCStatsReport的详细信息参见apiDoc 
  
 +当成员订阅流成功,收到第一帧音视频数据时,成员会收到以下通知
  
-==== 成员无音视频数据 ==== +<​code>​ 
 +/*! 
 + * \~chinese 
 + * 接收流第一帧音视频数据时,收到此回调 
 + * 
 + * @param aConference ​    ​会议 
 + * @param aType 流类型,音频或视频 
 + * @param streamId 流ID 
 +*/ 
 +- (void)streamDidFirstFrameReceived:​(EMCallConference*)aConference type:​(EMMediaType)aType streamId:​(NSString*)streamId;​ 
 +</​code>​ 
 +==== 音视频数据回调 ​====
 当会议中的成员A因断网或异常退出,而无音视频数据上传时,订阅该流的其他成员会收到下面的回调通知。 当会议中的成员A因断网或异常退出,而无音视频数据上传时,订阅该流的其他成员会收到下面的回调通知。
  
行 958: 行 861:
  * @param aType 流类型,音频或视频  * @param aType 流类型,音频或视频
  * @param streamId 流ID  * @param streamId 流ID
- * 
- ​*\~english 
- * callback when the sub audio stream has no datas 
- * 
- * @param aConference ​    ​EMCallConference instance 
- * @param aType Weather the stream is audio or video 
- * @param streamId The streamId 
 */ */
 - (void)streamStateUpdated:​(EMCallConference*)aConference type:​(EMMediaType)aType state:​(EMMediaState)state streamId:​(NSString*)streamId;​ - (void)streamStateUpdated:​(EMCallConference*)aConference type:​(EMMediaType)aType state:​(EMMediaState)state streamId:​(NSString*)streamId;​
 </​code>​ </​code>​
  
-该功能需要会议中开启质量统计+该功能需要会议中开启质量统计(必须在创建或者加入会议成功之后,在调用此方法)
  
 <​code>​ <​code>​
行 975: 行 871:
 </​code>​ </​code>​
  
-==== 发送/​接收音视频首帧 ​==== +==== 弱网检测 ​==== 
- +SDK提供多人音视频会议的网络连接状态检测当本地网络断开、重连、质量差时收到以下回调。该功能需要会议中开启质量统计 
-当成员发布流成功,发送第一帧音视频数据时成员收到以下通 +<​code>​ 
 +- (void)conferenceNetworkDidChange:​(EMCallConference *)aSession 
 +                            status:​(EMCallNetworkStatus)aStatus 
 +                            { 
 +                            } 
 +</​code>​ 
 +==== 话质量 ==== 
 +使用[conferenceManager enableStatistics:​YES]开启视频质量统计后,用户会周期性收到视频质量数据的回调,回调函数定义如下:
 <​code>​ <​code>​
 /*! /*!
  * \~chinese  * \~chinese
- ​* ​发送第一帧音视频数据时,收到此回调+ ​* ​当前会议的媒体流质量报告回调
  *  *
  * @param aConference ​    ​会议  * @param aConference ​    ​会议
- * @param aType 流类型,音频或视频 
  * @param streamId 流ID  * @param streamId 流ID
- + * @param ​aReport ​  ​会议的质量参数
- ​*\~english +
- * callback when the pub stream send the first audio/video frame +
- * +
- * @param ​aConference ​    ​EMCallConference instance +
- * @param aType Weather the stream is audio or video +
- * @param streamId The streamId+
 */ */
-- (void)streamDidFirstFrameSended:​(EMCallConference*)aConference ​type:(EMMediaType)aType streamId:(NSString*)streamId;+- (void)conferenceDidUpdate:​(EMCallConference*)aConference ​streamId:(NSString*)streamId ​statReport:(EMRTCStatsReport ​*)aReport;
 </​code>​ </​code>​
 +EMRTCStatsReport的详细信息参见http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​interface_e_m_r_t_c_stats_report.html
 +==== 监听谁在说话 ====
 +多人音视频会议可以实时监听谁在说话,该功能需要开启,启动/​停止控制如下:
 +<​code>​
 +// 开始监听说话者,参数为间隔时间
 + ​[[[EMClient shareClient] conferenceManager] startMonitorSpeaker:​(EMCallConference *)aCall
 +                                                    timeInterval:​(long long)aTimeMillisecond
 +                                                      completion:​(void (^)(EMError *aError))aCompletionBlock];​
 + 
 + // 停止监听说话者
 + ​[[[EMClient shareClient] conferenceManager] stopMonitorSpeaker:​(EMCallConference *)aCall];
  
-成员订阅流成功,收到第一帧音视频数据时成员收到以通知+</​code>​ 
 +有人说话时,会议成员会收到如下回调通知 
 +<​code>​ 
 +- (void)conferenceSpeakerDidChange:​(EMCallConference *)aConference 
 +                 ​speakingStreamIds:​(NSArray *)aStreamIds 
 +
 +
 +</​code>​ 
 +==== mute远端音视频流 ==== 
 +会议成员可以对自己订阅的远端音视频进行mute操作操作后自己不接远端的音视频不影响议中的其他人。操作接口如
  
 <​code>​ <​code>​
-/*!+/**
  * \~chinese  * \~chinese
- ​* ​接收流第一帧数据时,收到此回调+ ​* ​mute远端音频
  *  *
- * @param ​aConference ​    ​会议 + * @param ​aStreamId ​       要操作的Steam id 
- * @param ​aType 流类型,频或视频 + * @param ​isMute ​           是否静音 
- ​* ​@param streamId 流ID+ *
 +- (void)muteRemoteAudio:​(NSString *)aStreamId mute:​(BOOL)isMute;​ 
 + 
 +/** 
 + * \~chinese 
 + * mute远端视频
  *  *
- *\~english + * @param ​aStreamId ​       要操作的Steam id 
- * callback when the sub stream recieve the first audio/video frame + * @param ​isMute ​           是否显示 
- * + */ 
- * @param ​aConference ​    ​EMCallConference instance +- (void)muteRemoteVideo:(NSString ​*)aStreamId mute:(BOOL)isMute;
- * @param ​aType Weather the stream is audio or video +
- * @param streamId The streamId +
-*/ +
-- (void)streamDidFirstFrameReceived:(EMCallConference*)aConference type:(EMMediaType)aType streamId:​(NSString*)streamId;+
 </​code>​ </​code>​
 +==== 变声/​自定义音频 ====
 +用户可以通过自己采集音频数据,使用外部输入音频数据的接口进行通话,从而实现变声等音频数据加工功能
  
-===== 用户自定义数据采集及数据处理 =====+== 配置属性 ​==
  
-如果用户需要自己采集特定的数据或者对于数据需要先进行一些处理,如变声、美颜等,可以使用SDK的外部输入数据的方法进行。+用户使用自定义音频数据时,需要配置外部输入音频数据的开关,以及音频采样率,通道数(当前通道数只支持1),配置方法如下: 
 +<code objc> 
 +EMCallOptions *options = [[EMClient sharedClient].callManager getCallOptions];​ 
 +options.enableCustomAudioData = YES; 
 +options.audioCustomSamples = 48000; 
 +options.audioCustomChannels = 1; 
 +//​这里调用加入会议接口 
 +</​code>​ 
 + 
 +== 输入音频数据 == 
 + 
 +音频数据采集可参考1v1音视频通话Demo中的AudioRecord类实现,​音频数据的输入必须在加入会议成功的回调后开始,否则会导致网络阻塞,影响通话质量。 
 +<code objc> 
 +[[[EMClient sharedClient] conferenceManager] joinRoom:​roomName password:​pswd role:role roomConfig:​roomConfig completion:​^(EMCallConference *aCall, EMError *aError) { 
 +    self.conference = aCall; 
 +    EMCallOptions *options = [[EMClient sharedClient].callManager getCallOptions];​ 
 +    if(options.enableCustomAudioData){ 
 +        [self audioRecorder].channels = options.audioCustomChannels;​ 
 +        [self audioRecorder].samples = options.audioCustomSamples;​ 
 +        [[self audioRecorder] startAudioDataRecord];​ 
 +    } 
 +    }]; 
 +</​code>​ 
 +音频采集过程开始后,在音频数据的回调里调用外部输入音频数据接口 
 +<code objc> 
 +[[[EMClient sharedClient] conferenceManager] inputCustomAudioData:​data];​ 
 +</​code>​ 
 +会话挂断时,停止音频采集及输入过程 
 +<code objc> 
 +//​多人会议挂断触发事件 
 +- (void)hangupAction 
 +
 +      EMCallOptions *options = [[EMClient sharedClient].callManager getCallOptions];​ 
 +      if(options.enableCustomAudioData) { 
 +           ​[[self audioRecorder] stopAudioDataRecord];​ 
 +      } 
 +
 +</​code>​ 
 +==== 美颜/​自定义视频 ==== 
 +如果用户需要自己采集特定的数据或者对于数据需要先进行一些处理,如滤镜、美颜等,可以使用SDK的外部输入视频数据的方法进行。
 == 配置属性 == == 配置属性 ==
 使用外部输入视频数据接口前,需要先进行配置,配置参数为EMStreamParam中的enableCustomizeVideoData,设为YES可开启外部输入视频功能,开启后需要在publishConference的成功回调中开始视频数据采集。 使用外部输入视频数据接口前,需要先进行配置,配置参数为EMStreamParam中的enableCustomizeVideoData,设为YES可开启外部输入视频功能,开启后需要在publishConference的成功回调中开始视频数据采集。
行 1070: 行 1027:
 } }
 </​code>​ </​code>​
- +==== 共享桌面 ====
-外部音频数据的参数除开关外,还需要配置采样率及通道数,目前通道数只支持1 +
- +
-<code objc> +
- +
-EMStreamParam *streamParam = [[EMStreamParam alloc] init]; +
-streamParam.enableCustomizeAudioData = YES; // 开启自定义音频流 +
-streamParam.enableCustomizeAudioData.customAudioSamples = 48000; +
-streamParam.enableCustomizeAudioData.customAudioChannels = 1; +
-[[EMClient sharedClient].conferenceManager publishConference:​self.conference +
-                                                 ​streamParam:​streamParam +
-                                                  completion:​^(NSString *aPubStreamId,​ EMError *aError)  +
-+
- +
-}]; +
- +
-</​code>​ +
- +
-外部输入音频数据的接口为 +
-<code objc> +
-[[[EMClient sharedClient] conferenceManager] inputCustomAudioData:​data];​ +
-</​code>​ +
-开启和关闭过程与外部视频的接口使用一致。 +
- +
-===== 共享桌面 ​=====+
 使用sdk共享桌面,只能共享指定的view 使用sdk共享桌面,只能共享指定的view
  
行 1141: 行 1074:
 }]; }];
 </​code>​ </​code>​
- +==== 水印 ====
- +
-===== 设置视频流水印 =====  +
 视频通话时,可以添加图片作为水印,添加时使用[IEMConferenceManager addVideoWatermark]接口,需要指定水印图片的NSUrl,添加位置参见[[http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​interface_e_m_water_mark_option.html|EMWaterMarkOption]]。 视频通话时,可以添加图片作为水印,添加时使用[IEMConferenceManager addVideoWatermark]接口,需要指定水印图片的NSUrl,添加位置参见[[http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​interface_e_m_water_mark_option.html|EMWaterMarkOption]]。
  
行 1158: 行 1088:
 * *
 *  @param option 水印配置项,包括图片URL,marginX,​marginY以及起始点 *  @param option 水印配置项,包括图片URL,marginX,​marginY以及起始点
-+*/
-*  \~english +
-*  Enable water mark feature +
-+
-*  @param option the option of watermark picture,​include url,​margingX,​marginY,​margin point +
- */+
 - (void)addVideoWatermark:​(EMWaterMarkOption*)option;​ - (void)addVideoWatermark:​(EMWaterMarkOption*)option;​
 /*! /*!
行 1169: 行 1094:
 *  取消水印功能 *  取消水印功能
 * *
-*  \~english +*/
-*  Disable water mark feature +
-+
- */+
 - (void)clearVideoWatermark;​ - (void)clearVideoWatermark;​
 </​code>​ </​code>​
行 1192: 行 1114:
  
 </​code>​ </​code>​
- +===== 角色管理 ===== 
-===== 多集群代理 =====  +==== 观众申请主播 ==== 
-多人音视频支持不同集群区域使用代理减小延迟、丢包率。使用多集群代理需要音视频台配置IP端口映射文件rtcconfig.json,​sdk开启相应开关 +会议中观众角色可以向管理发申请成为主播员可以选择同意或者拒绝。观众申请主播的接口需要管理员的memId,先通过获取会议属性接口获取到管理员的memName,然根据memName以成员加入回调中获取到的EMCallMember,获取到memId接口如下
-启用多集群代理功能开关如下+
 <code objc> <code objc>
-[EMClient sharedClient].options.isUseRtcConfig = YES;//YES开启NO或不设置为不开启+/*! 
 +*  \~chinese 
 +*  观众申请连麦成主播观众角色调用 
 +
 +*  @param aCall             ​会议实例(自己创建的无效) 
 +*  @param aAdminId 管理员的memId 
 +*  @param aCompletionBlock 回调函数 
 +*/ 
 +- (void)requestTobeSpeaker:​(EMCallConference *)aCall adminId:​(NSString *)aAdminId completion:​(void (^)(EMError *aError))aCompletionBlock;​
 </​code>​ </​code>​
- +观众发出申请后,管理员将会到以下回调
-===== cdn合流推流 =====  +
-多人音视频支持将会议中的音视频流合并成一个流,推送第三方的cdn直播服务器。整个合流推流过程包括开启cdn推流,更新推流布局,停止推流。 +
- +
-==== 开启cdn推流 ==== +
-会议的创建者在创建会议时使用RoomConfig的接口,可决定是否开启cdn推流,推流配置LiveConfig是RoomConfig的一个参数,可设置cdn推流的相关信息。开启过程如下:+
 <code objc> <code objc>
-LiveConfigliveconfig = [[LiveConfig alloc] init]; +/*! 
-CDNCanvascanvas = [[CDNCanvas alloc] init]; + ​* ​\~chinese 
-canvas.fps = 18; + * 收到观众申请主播的请求,只有管理员会触发 
-canvas.kbps = 900; + * 
-canvas.codec = @"​H264";​ + @param aConference ​    ​会议 
-canvas.bgclr = 0x0000ff; + * @param aMemId ​  ​申请人memId 
-canvas.width = [EMDemoOption sharedOptions].liveWidth;​ + * @param aNickName 申请人昵称 
-canvas.height = [EMDemoOption sharedOptions].liveHeight;​ + * @param aMemName 申请人memName 
-liveconfig.canvas = canvas; +*
-liveconfig.cdnUrl = [EMDemoOption sharedOptions].cdnUrl;​ +- (void)conferenceReqSpeaker:​(EMCallConference*)aConference memId:​(NSString*)aMemId nickName:​(NSString*)aNickName memName:​(NSString*)aMemName;
-liveconfig.layoutStyle = CUSTOM; +
-liveconfig.record = YES; //​是否录制推流到cdn的音视频 +
-roomConfig.liveConfig = liveconfig;+
 </​code>​ </​code>​
- +在回调中管理员可以选择同意或者拒绝,如果同意需要调用口changeMemberRoleWithConfId授权,然后回复申请人,如果拒绝则直接调用回复接口。回复接口如下:
-当canvas设置的width、height为0时,cdn推流为**纯音频推流** +
- +
-推流成功后,可以在EMConference对象中查看liveId,如果只有一路推流可直使用EMConference对象的liveId,如果存在多路推流可访问EMConference对象的liveCfgs对象,liveCfgs存储了所有的推流信息 +
- +
-LiveConfig可设置的参数如下: +
 <code objc> <code objc>
 /*! /*!
 *  \~chinese *  \~chinese
-*  ​cdn 画布设置创建会议时使+*  ​管理员同意/​拒绝观众的上麦申请管理员调
 * *
-*  ​\~english +*  ​@param aCall             ​会议实例(自己创建的无效) 
-*  ​The cdn canvas config+*  ​@param aMemId 上麦申请的观众的memId 
 +*  @param aResult 操作结果,0为同意,1为拒绝 
 +*  @param aCompletionBlock 回调函数
 */ */
-@interface CDNCanvas ​NSObject +- (void)responseReqSpeaker:(EMCallConference ​*)aCall 
- /*! \~chinese 画布宽度 \~english The width of canvas */ +                     memId:(NSString *)aMemId 
-@property ​(nonatomicNSInteger width; +                    ​result:​(NSInteger)aResult 
-/*! \~chinese 画布高度 \~english The height of canvas */ +                ​completion:​(void ​(^)(EMError ​*aError))aCompletionBlock
-@property ​(nonatomicNSInteger height; +</code>
-/*! \~chinese 画布的背景色,格式为 RGB 定义下的 Hex 值,不要带 # 号,如 0xFFB6C1 表示浅粉色。默认0x000000,黑色。 +
- * \~english The bgclr of canvas ,use ​ interger as 0x112233, 0x11 is red value,0x22 is green value,0x33 is blue value*/ +
-@property ​(nonatomicNSInteger bgclr; +
-/*! \~chinese 推流帧率,可设置范围10-30 \~english The fps of cdn live,valid value is 10-30 */ +
-@property (nonatomicNSInteger fps; +
-/*! \~chinese 推流码率,单位kbps,width和height较大时,码率需要提高,可设置范围1-5000 \~english The bps of cdn live. Unit is kbps,valid value is 1-5000 */ +
-@property (nonatomicNSInteger kbps+
-/*! \~chinese 推流编码格式,目前只支持"​H264"​ \~english The codec of cdn live,now only support "H264* codec */ +
-@property (nonatomic) NSString* codec; +
- +
-@end+
  
 +==== 主播申请管理员 ====
 +会议中的主播角色可以向管理员发申请成为管理员,管理员可以选择同意或者拒绝,成为管理员后,各管理员之间的权限是相同的。主播申请管理员的接口需要管理员的memId,先通过获取会议属性接口获取到管理员的memName,然后根据memName以及成员加入的回调中获取到的EMCallMember,获取到管理员memId,调用接口changeMemberRoleWithConfId授权,然后回复申请人,接口如下:
 +<code objc>
 /*! /*!
 *  \~chinese *  \~chinese
-*  ​cdn推流使的画布类型+*  ​主播申请成为管理员,主播角色调
 * *
-*  ​\~english +*  ​@param aCall             ​会议实例(自己创建的无效) 
-*  ​cdn  live layout style+*  ​@param aAdminId 管理员的memId 
 + @param aCompletionBlock 回调函数
 */ */
-typedef NS_ENUM(NSInteger, LayoutStyle{ +(void)requestTobeAdmin:​(EMCallConference *)aCall adminId:​(NSString *)aAdminId completion:​(void (^)(EMError *aError))aCompletionBlock;​ 
-    ​CUSTOM,​ +</​code>​ 
-    DEMO, +主播发出申请后,管理员将会收到以下回调: 
-    ​GRID +<code objc>
-};+
 /*! /*!
- ​\~chinese + \~chinese 
- ​cdn推流设置 + ​* ​收到主播申请管理员的请求,只有管理员会触发 
-+ * 
- ​\~english + ​* ​@param aConference ​    ​会议 
- The cdn push stream config+ ​* ​@param aMemId ​  ​申请人memId 
 + * @param aNickName 申请人昵称 
 + * @param aMemName 申请人memName
 */ */
-@interface LiveConfig ​NSObject +- (void)conferenceReqAdmin:(EMCallConference*)aConference memId:(NSString*)aMemId nickName:(NSString*)aNickName memName:(NSString*)aMemName;
- +
-/*! \~chinese 推流url地址\~english The url address of cdn live*/ +
-@property ​(nonatomic,​strong) ​NSString *cdnUrl; +
- +
-/*! \~chinese 推流画布的配置\~english The config of live canvas*/ +
-@property ​(nonatomic) CDNCanvascanvas; +
- +
-/*! \~chinese 推流方式,GRID或者CUSTOM,GRID将由服务器设置位置信息,CUSTOM将由用户自定义流的位置信息\~english The style of cdn live,GRID or CUSTOM.If GRID,server set region of streams.If CUSTOM,user set region of streams*/ +
-@property (nonatomicLayoutStyle layoutStyle;​ +
- +
-/*! \~chinese 是否开启自定义录制\~english Weather custom servre record*/ +
-@property ​(nonatomic) BOOL record; +
- +
-/*! \~chinese 音频录制参数\~english audio record config*/ +
-@property (nonatomicAudioConfig* audioCfg; +
- +
-@end+
 </​code>​ </​code>​
- +在回调中,管理员可以选择同意或者拒绝,如果同意,需要调用改变用户权限的接口changeMemberRoleWithConfId,然后调用回复接口,如果拒绝则直接调回复接口回复接口如下:
-==== 更新布局 ==== +
-用户调用更新布局接口cdn推流方式将强制变成CUSTOM模式所有流的位置信息都由户自己定义 +
-更新布局的接口如下:+
 <code objc> <code objc>
 /*! /*!
 *  \~chinese *  \~chinese
-*  ​修改会议cdn推流位置+*  ​管理员同意/​拒绝主播申请管理员请求,管理员调用
 * *
 *  @param aCall             ​会议实例(自己创建的无效) *  @param aCall             ​会议实例(自己创建的无效)
-*  @param ​aReagionList 媒体流位置信息 +*  @param ​aMemId 申请管理员主播的memId 
-*  @param ​aLiveId ​          ​推流Id+*  @param ​aResult 操作结果,0为同意,1为拒绝
 *  @param aCompletionBlock 回调函数 *  @param aCompletionBlock 回调函数
-+ */ 
- \~english +- (void)responseReqAdmin:​(EMCallConference *)aCall ​memId:(NSString *)aMemId result:(NSInteger)aResult ​completion:​(void (^)(EMError *aError))aCompletionBlock;​
-*  Set region of live stream +
-+
-*  @param aCall             ​EMConference instance (invalid by yourself) +
-*  @param aLiveId ​         推流ID +
-*  @param aReagionList The list of all stream region +
-*  @param aCompletionBlock The callback function +
-*/ +
-- (void)updateConference:​(EMCallConference*)aCall +
-                  liveId:​(NSString*)aLiveId +
-              setRegions:(NSArray<​LiveRegion*>​*)aReagionList +
-              ​completion:​(void(^)(EMError *aError))aCompletionBlock;​+
 </​code>​ </​code>​
-LiveRegion结构如下:+管理员对其他观众、主播角色进行升级、降级处理的接口如下: 
 + 
 +aMemberName​ 是appkey拼接环信id,例如:easemob-demo#​chatdemoui_lulu1 
 + 
 +**管理员/​主播也可以直接使用该接口对自己进行降级**
 <code objc> <code objc>
- 
 /*! /*!
 *  \~chinese *  \~chinese
-*  ​cdn推流的每一路流的模式+*  ​改变成员角色,需要管理员权限 
 +* 用户角色:​ Admin > Talker > Audience 
 +* 当角色升级时,​用户需要给管理员发送申请,​管理通过该接口改变用户接口. 
 +* 当角色降级时,​用户直接调用该接口即可.
 * *
-*  ​\~english +*  ​@param aConfId ​          ​会议ID(EMCallConference.confId) 
-*  ​The style of stream in cdn live+*  ​@param aMemberName ​       成员在会议中的memName 
 +*  @param aRole             ​成员角色 
 +*  @param aCompletionBlock ​ 完成的回调
 */ */
-typedef NS_ENUM(NSInteger, LiveRegionStyle{ +(void)changeMemberRoleWithConfId:​(NSString *)aConfId 
-    /*! \~chinese FIt模式\~english The fit content mode */ +                        ​memberName:​(NSString ​*)aMemberName 
-    ​LiveRegionStyleFit,​ +                              role:​(EMConferenceRole)toRole 
-    /*! \~chinese ​FIll模式\~english The fill content mode */ +                        completion:​(void (^)(EMError ​*aError))aCompletionBlock;​ 
-    ​LiveRegionStyleFill +</code> 
-};+==== 管理员踢人 ==== 
 +管理员可以强制会议成员离开会议,使用接口 
 +<code objc> 
 +/*! 
 + ​*  ​\~chinese 
 + ​* ​ 踢人,需要管理员权限 
 + * 
 + ​* ​ @param aConfId ​          ​会议ID(EMCallConference.confId) 
 + ​* ​ @param aMemberNameList ​  ​成员名列表 
 + ​* ​ @param aCompletionBlock ​ 完成的回调 
 + */ 
 +- (void)kickMemberWithConfId:​(NSString *)aConfId 
 +                 memberNames:​(NSArray<​NSString *> *)aMemberNameList 
 +                  completion:​(void (^)(EMError *aError))aCompletionBlock; 
 +</​code>​
  
-/*! +==== 全体静音/指定成员静音 ==== 
- \~chinese +该系列接口调用需要加入会议后,先获取一下会议信息,调用getConference接口 
- ​cdn推流的每一路流的区域位置信息+== 全体静音 == 
 +管理员可以对会议进行全体静音/​解除全体静音设置,设置后,会议中的主播都将处于静音状态,新加入的主播也将自动处于静音状态。只有管理员可以调用此接口。 
 +接口API如下: 
 +<code objc> 
 +/*
 +* \~chinese 
 +开启/​停止全体静音,​只有管理员可调用此接口
 * *
- ​\~english +@param enable 是否启用全体静音 
- The region info of each stream in cdn live+@param completion 回调
 */ */
-@interface LiveRegion : NSObject +(void)muteAll:(BOOL)mute 
- +     completion:(void(^)(EMError ​*aError))aCompletionBlock;
-/*! \~chinese 流ID \~english The stream Id */ +
-@property ​(nonatomicNSString* streamId; +
- +
-/*! \~chinese 流的左上角在x轴坐标 \~english The pointX of left top */ +
-@property ​(nonatomicNSInteger x; +
- +
-/*! \~chinese 流的左上角在y轴坐标 \~english The pointY of left top */ +
-@property ​(nonatomic) NSInteger y; +
- +
-/*! \~chinese 流的宽度 \~english The width of stream */ +
-@property ​(nonatomicNSInteger w; +
- +
-/*! \~chinese 流的高度 \~english The height of stream */ +
-@property ​(nonatomic) NSInteger h; +
- +
-/*! \~chinese 流的图层顺序,越小越在底层,从1开始 \~english The zorder of stream,start from 1,the smaller is under others*/ +
-@property (nonatomicNSInteger z; +
- +
-/*! \~chinese 流的显示模式,Fit或Fill \~english The content mode of stream,fit or fill*/ +
-@property (nonatomicLiveRegionStyle style; +
- +
-@end+
 </​code>​ </​code>​
  
-使方法如下+管理员调此接口后,会议中的主播将收到全体静音状态的回调,回调函数如下
 <code objc> <code objc>
-NSMutableArray<​LiveRegion*>regionsList = [NSMutableArray array]; +/*
-LiveRegionregion = [[LiveRegion alloc] init]; + \~chinese 
-region.streamId = _streamId; + ​* ​收到全体静音/​解除全体静音的回调 
-region.style = LiveRegionStyleFill;​ + * 
-region.x = 80; + * @param aConference ​    ​会议 
-region.y = 60; + * @param aMuteAll ​  ​是否全体静音 
-region.w = 320; +*/ 
-region.h = 240; +- (void)conferenceDidUpdated:(EMCallConference ​*)aConference 
-region.z = 9; +                  ​muteAll:​(BOOL)aMuteAll;
-[regionsList addObject:​region];​ +
-[[[EMClient sharedClient] conferenceManager] updateConference:[EMDemoOption sharedOptions].conference liveId:​aLiveId setRegions:​regionsList completion:​^(EMError ​*aError{ +
-    }];+
 </​code>​ </​code>​
  
-==== 多路推流 ==== +== 指定成员静音 ​== 
-多人音视频支持加入会议后,增加一路推流,只有管理员权限进行次操作增加一路推流的api方法如下: +管理员可以对会议中的指定成员进行静音/​解除静音设置,被指定成员可以是主播也可以是管理员。设置后,被指定成员将静音/​解除静音。只有管理员可以调用此接口 
- +接口API如下: 
-<​code>​+<​code ​objc>
 /*! /*!
 *  \~chinese *  \~chinese
-*  ​添加一路推流+*  ​将指定成员静音/​解除静音,管理员调用
 * *
 *  @param aCall             ​会议实例(自己创建的无效) *  @param aCall             ​会议实例(自己创建的无效)
-*  @param ​aLiveConfig 推流配置+*  @param ​aMemId 指定成员的memId 
 +*  @param aMute 操作,YES为静音,NO为解除静音
 *  @param aCompletionBlock 回调函数 *  @param aCompletionBlock 回调函数
-* 
-*  \~english 
-*  Add a live push 
-* 
-*  @param aCall             ​EMConference instance (invalid by yourself) 
-*  @param aLiveConfig The config of live 
-*  @param aCompletionBlock The callback function 
 */ */
-- (void)addConferenceLive:​(EMCallConference*)aCall +- (void)setMuteMember:​(EMCallConference *)aCall 
-                  ​LiveCfg:(LiveConfig*)aLiveConfig +                memId:(NSString ​*)aMemId 
-               ​completion:​(void(^)(EMError *aError))aCompletionBlock;​+                 mute:​(BOOL)aMute 
 +           completion:​(void (^)(EMError *aError))aCompletionBlock;​
 </​code>​ </​code>​
  
-==== 自义录制布局 ==== +管理员调用此接口后,被指定的成员将收静音状态回调回调函数如下 
- +<​code ​objc>
-在推流LiveConfig设置里,设record为YES,可以开启自定义录制,开启后会把推流cdn音视频按照推流布局录制下来。如果推流时未开启也可以在推流后进行开启/​停止自定义录制布局操作。开启/​停止自定义录制布局的api如下 +
-<​code>​+
 /*! /*!
- ​\~chinese + \~chinese 
- ​启动/停止自定义录制 + ​* ​收到静音/解除静音的回调 
-+ * 
-*  @param aCall             ​会议实例(自己创建无效) + * @param ​aConference ​    ​会议 
-*  @param aLiveId 推流/​录制Id + * @param ​aMute   ​是否静音
-*  @param aEnabled 操作,启动/​停止 +
-*  @param aCompletionBlock ​回调函数 +
-+
- ​\~english +
-*  Add a live push +
-+
-*  ​@param ​aCall             ​EMConference instance (invalid by yourself) +
- ​@param aLiveId The live id +
-*  @param aEnabled operation,start/​end +
-*  ​@param ​aCompletionBlock The callback function+
 */ */
-- (void)enableRecordLiveStream:​(EMCallConference*)aCall +- (void)conferenceDidUpdated:​(EMCallConference*)aConference 
-                        ​liveId:​(NSString*)aLiveId +                        ​mute:(BOOL)aMute;
-                       ​enabled:(BOOL)aEnabled +
-                    completion:​(void(^)(EMError *aError))aCompletionBlock;+
 </​code>​ </​code>​
-==== 停止推流 ​==== +==== 本身角色变更回调 ​==== 
-多人音视频支持停止向某一个地址推流停止推流接口如+当成员本身角色发生变化时收到以回调
 <code objc> <code objc>
 /*! /*!
-*  \~chinese + *  \~chinese 
-*  ​删除一路推流 + ​*  ​自己的角色发生变化 
-+ * 
-*  @param ​aCall             会议实例(自己创建的无效) + ​* ​ @param ​aConference ​      会议实例 
-*  @param aLiveId live Id + */ 
-*  @param aCompletionBlock 回调函数 +- (void)roleDidChanged:​(EMCallConference *)aConference;
-+
-*  \~english +
-*  Remove a live push +
-+
-*  @param aCall             ​EMConference instance (invalid by yourself) +
-*  @param aLiveId The live Id +
-*  @param aCompletionBlock The callback function +
-*/ +
-- (void)deleteConferenceLive:​(EMCallConference*)aCall +
-                      liveId:​(NSString*)aLiveId +
-                  completion:​(void(^)(EMError *aError))aCompletionBlock;+
 </​code>​ </​code>​
-===== 其他方法 ===== +==== 管理员变更回调 ​==== 
 +当会议中的普通成员成为管理员,或管理员降级为普通成员时,会议中的其他成员将收到管理员变更的回调。管理员变更回调分为管理员新增和管理员移除,回调接口如下:
 <code objc> <code objc>
-//​Objective-C +/*! 
-// 开启/​关闭音频传输 + *  \~chinese 
- [[[EMClient shareClient] conferenceManager] updateConference:​(EMCallConference ​*)aCall + ​* ​ ​管理员新增 
-                                                       isMute:​(BOOL)aIsMute];​ + * 
- // 开启/​关闭启视频传输 + ​* ​ ​@param aConference ​      议实例 
- ​[[[EMClient shareClient] conferenceManager] updateConference:​(EMCallConference ​*)aCall + ​* ​ ​@param adminmemid ​        新的管理员memid 
-                                                  enableVideo:​(BOOL)aEnableVideo];​ + */ 
-  +(void)adminDidChanged:​(EMCallConference *)aConference 
- ​PS:以上这四个方法都是修改 stream,群里其他成员都收到 EMConferenceListener.onStreamUpdate()回调 +               newAdmin:(NSString*)adminmemid;
-  +
- // 切换摄像头 +
- ​[[[EMClient shareClient] conferenceManager] updateConferenceWithSwitchCamera:​(EMCallConference ​*)aCall]; +
- // 更展示远端画面的 view +
- [[[EMClient shareClient] conferenceManager] updateConference:​(EMCallConference ​*)aCall +
-                                                     streamId:​(NSString *)aStreamId +
-                                              remoteVideoView:​(EMCallRemoteVideoView *)aRemoteView +
-                                                   ​completion:​(void (^)(EMError *aError))aCompletionBlock];​ +
-  +
- // 开始监听说话者,参数为间隔时间 +
- ​[[[EMClient shareClient] conferenceManager] startMonitorSpeaker:​(EMCallConference *)aCall +
-                                                    ​timeInterval:(long long)aTimeMillisecond +
-                                                      completion:​(void (^)(EMError *aError))aCompletionBlock];​ +
-  +
- // 停止监听说话者 +
- ​[[[EMClient shareClient] conferenceManager] stopMonitorSpeaker:​(EMCallConference ​*)aCall];+
  
- // 开启统计数据报告,用于debug视频过程中媒体流的具体参数,开启后在[EMConferenceManagerDelegate conferenceDidUpdate:​streamId:​statReport]回调中读取到 +/*! 
- // 各项数据参数。 + ​* ​ \~chinese 
- [[EMClient sharedClient].conferenceManager enableStatistics:YES];+ ​* ​ 管理员放弃 
 + * 
 + ​* ​ @param aConference ​      议实例 
 + *  @param adminmemid ​        ​放弃管理员的memid 
 + *
 +- (void)adminDidChanged:​(EMCallConference *)aConference 
 +            removeAdmin:(NSString*)adminmemid;
 </​code>​ </​code>​
 +====== 客户端api ======
 +多人音视频通话的API包括以下接口
 +  * EMCallOption 多人音视频通话配置类
 +  * EMConferenceManager 是多人音视频通话的主要管理类,提供了加入会议、退出、发流、订阅等接口
 +  * EMConferenceManagerDelegate 是多人音视频通话的监听回调类,多人音视频通话相关的回调
 +  * EMCallConference 多人音视频通话的会议实例接口
 +
 +== EMCallOption ==
 +^属性 ^描述 ^
 +|[[http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​interface_e_m_call_options.html#​aea45547ce623a0fd5d53a36f00334520|maxAudioKbps]] |最大音频码率 ​ |
 +|[[http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​interface_e_m_call_options.html#​a4130c8f7488d6d6f58c3783dc63be5fd|maxVideoKbps]] ​      ​|最大视频码率 ​ |
 +|[[http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​interface_e_m_call_options.html#​ae070e260597c913634a21c03aa31826a|minVideoKbps]] |最小视频码率 ​ |
 +|[[http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​interface_e_m_call_options.html#​a45f020f84b4513fdc0cb3f8a123fe678|maxVideoFrameRate]] |最大视频帧率 ​ |
 +|[[http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​interface_e_m_call_options.html#​af49ac4f01b9bca538fbce40e1de5f866|videoResolution]] |视频分辨率 ​   |
 +|[[http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​interface_e_m_call_options.html#​af2129acdd0d3d73a583e8d37dfc087e2|enableReportQuality]] ​ |是否监听通话质量 ​ |
 +|[[http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​interface_e_m_call_options.html#​ae927cb60d05c59b389517648941e7ec8|enableCustomAudioData]] ​ |是否使用自定义音频数据 ​ |
 +|[[http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​interface_e_m_call_options.html#​a76482a8e2702e1905c036142e41c8a6d|audioCustomSamples]] ​   |自定义音频数据的采样率,默认48000 ​ |
 +|[[http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​interface_e_m_call_options.html#​a89f38b15e05ed5d6636af5fde628d114|enableCustomizeVideoData]] |是否使用自定义视频数据 ​ |
 +|[[http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​interface_e_m_call_options.html#​a38497058ab86b80f156ba3b362100b4e|isClarityFirst]] |是否清晰度优先 ​ |
 +
 +== EMConferenceManager ==
 +^方法 ​ ^描述 ​ ^
 +|[[http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​protocol_i_e_m_conference_manager-p.html#​a2b985523160a8a9f47346e1065667dfb|addDelegate:​delegateQueue:​]] |添加回调代理 ​ |
 +|[[http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​protocol_i_e_m_conference_manager-p.html#​acc88916806c943608d41c307012d2113|removeDelegate:​]] |移除回调代理 ​ |
 +|[[http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​protocol_i_e_m_conference_manager-p.html#​a16c1d9b53642b97e07e6ae1c55925519|setAppkey:​username:​token:​]] |设置应用Appkey,​ 环信ID, 环信ID对应的Token |
 +|[[http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​protocol_i_e_m_conference_manager-p.html#​a53da60e7a584697191b52734c4517669|getConference:​password:​completion:​]] |获取会议信息 |
 +|[[http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​protocol_i_e_m_conference_manager-p.html#​a838273c88e5264cbf0508de0d0e3aeef| createAndJoinConferenceWithType:​password:​completion:​]] |创建并加入会议 |
 +|[[http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​protocol_i_e_m_conference_manager-p.html#​a0f5820f9c560ca0a6ccfee963c423185| createAndJoinConferenceWithType:​password:​confrConfig:​completion:​]] |创建并加入会议 |
 +|[[http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​protocol_i_e_m_conference_manager-p.html#​acead28618cffee8a9068897bbb238df8|joinConferenceWithConfId:​password:​completion:​]] ​   |加入已有会议 |
 +|[[http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​protocol_i_e_m_conference_manager-p.html#​a1a81bcafde9fa0d9a826a80dee15f981|joinConferenceWithTicket:​completion:​]] |加入已有会议 |
 +|[[http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​protocol_i_e_m_conference_manager-p.html#​a9dc56f6f8c505322baff4074964640d8|joinRoom:​password:​role:​roomConfig:​completion:​]] ​   |加入房间 |
 +|[[http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​protocol_i_e_m_conference_manager-p.html#​a45cb75e64fd0abd04d71ae7cbaa39668|publishConference:​streamParam:​completion:​]] ​   |上传音视频数据流 |
 +|[[http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​protocol_i_e_m_conference_manager-p.html#​ab80d817ea9953a75d59284bece48e7ac|unpublishConference:​streamId:​completion:​]] ​   |取消上传本地音视频数据流 |
 +|[[http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​protocol_i_e_m_conference_manager-p.html#​a5bc905bb6427f586c18ea6a3b87fb58a|subscribeConference:​streamId:​remoteVideoView:​completion:​]] ​   |订阅其他人的数据流 |
 +|[[http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​protocol_i_e_m_conference_manager-p.html#​af95a48dc945a95062e868db32615b1d0|unsubscribeConference:​streamId:​completion:​]] ​   |取消订阅的数据流 |
 +|[[http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​protocol_i_e_m_conference_manager-p.html#​ac6df4fd1e2b16c98e4f515d45aa08d21|changeMemberRoleWithConfId:​memberName:​role:​completion:​]] ​   |改变成员角色,需要管理员权限 |
 +|[[http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​protocol_i_e_m_conference_manager-p.html#​a094785f9b5ee245226399234bd72fe58|kickMemberWithConfId:​memberNames:​completion:​]] ​   |踢人,需要管理员权限 |
 +|[[http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​protocol_i_e_m_conference_manager-p.html#​ac3c7426a3b6dc1b50bad419d2f722ee8|destroyConferenceWithId:​completion:​]] ​   |销毁会议,需要管理员权限 |
 +|[[http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​protocol_i_e_m_conference_manager-p.html#​ae54239e73e21df83450961a8263a14f7|leaveConference:​completion:​]] ​   |离开会议 |
 +|[[http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​protocol_i_e_m_conference_manager-p.html#​a28726fcdc8ce28006a30d713f40227c0|startMonitorSpeaker:​timeInterval:​completion:​]] ​   |开始监听说话者 |
 +|[[http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​protocol_i_e_m_conference_manager-p.html#​aa67291522b649278e770c7bb1b3ee9b3|stopMonitorSpeaker:​]] ​   |结束监听说话者 |
 +|[[http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​protocol_i_e_m_conference_manager-p.html#​a00098d155bc5b6b8a0941f2b7b140b3f|updateConference:​liveId:​setRegions:​completion:​]] ​   |修改会议的cdn推流布局 |
 +|[[http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​protocol_i_e_m_conference_manager-p.html#​ad36907936af8fcf74f64cd311d6456c7|addConferenceLive:​LiveCfg:​completion:​]] ​   |添加一路推流 |
 +|[[http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​protocol_i_e_m_conference_manager-p.html#​a391e86342e05c90345c6f0594008c368|enableRecordLiveStream:​liveId:​enabled:​completion:​]] ​   |启动/​停止自定义录制 |
 +|[[http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​protocol_i_e_m_conference_manager-p.html#​ad879b9caafabcfcbc271e5e23937805a|deleteConferenceLive:​liveId:​completion:​]] ​   |删除一路推流 |
 +|[[http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​protocol_i_e_m_conference_manager-p.html#​a94e5010c1349d40fddde33b7cc489274| requestTobeSpeaker:​adminId:​completion:​]] ​   |观众申请连麦成为主播 |
 +|[[http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​protocol_i_e_m_conference_manager-p.html#​af2e99d266d5d3f67490903b17818b17c|requestTobeAdmin:​adminId:​completion:​]] ​   | 主播申请成为管理员, |
 +|[[http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​protocol_i_e_m_conference_manager-p.html#​a097c52f9ea833b9ac289290247c25860|responseReqSpeaker:​memId:​result:​completion:​]] ​   |管理员同意/​拒绝观众的上麦申请,管理员调用 |
 +|[[http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​protocol_i_e_m_conference_manager-p.html#​a34ff57a1a78c507d5118249e4863b8ff|responseReqAdmin:​memId:​result:​completion:​]] ​   |管理员同意/​拒绝主播的申请管理员请求,管理员调用 |
 +|[[http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​protocol_i_e_m_conference_manager-p.html#​a6432c2f2a68760b1cad2dabb6e9cba6d|setMuteMember:​memId:​mute:​completion:​]] ​   |将指定成员静音/​解除静音,管理员调用 |
 +|[[http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​protocol_i_e_m_conference_manager-p.html#​a28bd7490cb8e238a07a5aec62d164cea|updateConferenceWithSwitchCamera:​]] ​   |切换前后摄像头 |
 +|[[http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​protocol_i_e_m_conference_manager-p.html#​abc3d1658875a99bdd1f5f1158a74e789|updateConference:​isMute:​]] ​   |设置是否静音 |
 +|[[http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​protocol_i_e_m_conference_manager-p.html#​a478c1c8b08758a55fdea520d6772ae9e| updateConference:​enableVideo:​]] ​   |设置视频是否可用 |
 +|[[http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​protocol_i_e_m_conference_manager-p.html#​ab57a72f199fa779b7b3158712e2d0a0a| updateConference:​maxVideoKbps:​]] ​   |更新视频最大码率 |
 +|[[http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​protocol_i_e_m_conference_manager-p.html#​af906e2f6de9ca0d9005bf6c46c9d46e8|inputVideoSampleBuffer:​rotation:​conference:​publishedStreamId:​completion:​]] ​   |输入自定义本地视频数据 |
 +|[[http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​protocol_i_e_m_conference_manager-p.html#​a544a757e0f2a16c9b265243c784f1fd4|inputVideoPixelBuffer:​sampleBufferTime:​rotation:​conference:​publishedStreamId:​completion:​]] ​   |输入自定义本地视频数据 |
 +|[[http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​protocol_i_e_m_conference_manager-p.html#​ae98d762cb6aed35de22da40779455fcb|setConferenceAttribute:​value:​completion:​]] ​   |设置会议属性, |
 +|[[http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​protocol_i_e_m_conference_manager-p.html#​a226dbe5816201a32fa3850dd1bb3c991|deleteAttributeWithKey:​completion:​]] ​   |删除频道属性 |
 +|[[http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​protocol_i_e_m_conference_manager-p.html#​aa7e7d1a088f746f98b796d94b3eb631d|startAudioMixing:​loop:​sendMix:​]] ​   |开启本地伴音功能 |
 +|[[http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​protocol_i_e_m_conference_manager-p.html#​a703c15925f723cc3c4ec59f347226a91|stopAudioMixing]] ​   | 关闭本地混音功能 |
 +|[[http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​protocol_i_e_m_conference_manager-p.html#​a9b9a168a7e160ff08b5ccf13d2b8ab2f|adjustAudioMixingVolume:​]] ​   | 设置伴奏音量 |
 +|[[http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​protocol_i_e_m_conference_manager-p.html#​a28f9297ad2411c6aa9bdbb291bb8df26|muteRemoteAudio:​mute:​]] ​   | mute远端音频 |
 +|[[http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​protocol_i_e_m_conference_manager-p.html#​a870a36303635529c953194b9d240521b| muteRemoteVideo:​mute:​]] ​   | mute远端视频 |
 +|[[http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​protocol_i_e_m_conference_manager-p.html#​aa57d7c7aaec58f84fb0f81b38f36a2e4|enableStatistics:​]] ​   | 启用统计 |
 +|[[http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​protocol_i_e_m_conference_manager-p.html#​a0a7340e4fe935143ba0ff529858ad295| muteAll:​completion:​]] ​   | 全体静音 |
 +|[[http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​protocol_i_e_m_conference_manager-p.html#​a2fb9faf9d767ed2e58315c1db354df37| inputCustomAudioData:​]] ​   | 输入自定义外部音频数据 |
 +|[[http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​protocol_i_e_m_conference_manager-p.html#​a64ed13e19b65e3c8fb06113a96da37c5| addVideoWatermark:​]] ​   | 开启水印功能 |
 +|[[http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​protocol_i_e_m_conference_manager-p.html#​a950faf99db0d40e2a89c62fd8113f34d|clearVideoWatermark]] ​   | 取消水印功能 |
 +
 +== EMConferenceManagerDelegate ==
 +
 +^回调事件 ^描述 ^
 +|[[http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​protocol_e_m_conference_manager_delegate-p.html#​a35ae6bcf830548162cc9c7e6f6df0cd8|memberDidJoin:​member:​]] ​   | 有人加入会议 |
 +|[[http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​protocol_e_m_conference_manager_delegate-p.html#​a2349384a054912620858346dff81eb41|memberDidLeave:​member:​]] ​   | 有人离开会议 |
 +|[[http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​protocol_e_m_conference_manager_delegate-p.html#​ada8c07dac796d492a5165b28b50fb02c|roleDidChanged:​]] ​   | 自己的角色发生变化 |
 +|[[http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​protocol_e_m_conference_manager_delegate-p.html#​a0532f12f9e7ae6cf5f7837793062f4ea|adminDidChanged:​newAdmin:​]] ​   | 管理员新增 |
 +|[[http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​protocol_e_m_conference_manager_delegate-p.html#​a56cc91df21df41aad12e33940bff987c|adminDidChanged:​removeAdmin:​]] ​   | 管理员放弃 |
 +|[[http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​protocol_e_m_conference_manager_delegate-p.html#​a99ce878eb647a0ae04b8de3829e41f9b|streamPubDidFailed:​error:​]] ​   | 本地pub流失败 |
 +|[[http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​protocol_e_m_conference_manager_delegate-p.html#​a3d7f3ca91389d95c6fe29f15d3d41fbb|DesktopStreamDidPubFailed:​error:​]] ​   | 发布共享桌面流失败 |
 +|[[http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​protocol_e_m_conference_manager_delegate-p.html#​a0ac987ff2ecb9e34dc786f1da50466df|streamUpdateDidFailed:​error:​]] ​   | 本地update流失败,​视频超限 |
 +|[[http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​protocol_e_m_conference_manager_delegate-p.html#​aaec86cf13eaa8930fa5261c1f3848785|streamDidUpdate:​addStream:​]] ​   | 有新的数据流上传 |
 +|[[http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​protocol_e_m_conference_manager_delegate-p.html#​adee2f6109508e383000d5e99e33b9bde|streamDidUpdate:​stream:​]] ​   | 数据流有更新(是否静音,视频是否可用) |
 +|[[http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​protocol_e_m_conference_manager_delegate-p.html#​a4a92350f846d00e913c48a3286e9da77|streamDidUpdate:​removeStream:​]] ​   | 有数据流移除 |
 +|[[http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​protocol_e_m_conference_manager_delegate-p.html#​a9e35574e2e6fcedcc5cf3d4f0f9a26e6|conferenceDidEnd:​reason:​error:​]] ​   | 会议已经结束 |
 +|[[http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​protocol_e_m_conference_manager_delegate-p.html#​a37025ed1fda0ff26798c31aabbcf7105|streamStartTransmitting:​streamId:​]] ​   | 数据流已经开始传输数据 |
 +|[[http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​protocol_e_m_conference_manager_delegate-p.html#​a2db2fd8d67522325ecb8412479842ccd|conferenceDidUpdated:​muteAll:​]] ​   | 收到全体静音/​解除全体静音的回调 |
 +|[[http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​protocol_e_m_conference_manager_delegate-p.html#​a0917065a18ca3ce9433d6677288830c3|conferenceSpeakerDidChange:​speakingStreamIds:​]] ​   | 用户A用户B在同一个会议中,用户A开始说话时,用户B会收到该回调 |
 +|[[http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​protocol_e_m_conference_manager_delegate-p.html#​a0917065a18ca3ce9433d6677288830c3|conferenceAttributeUpdated:​attributes:​]] ​   | 会议属性变更 |
 +|[[http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​protocol_e_m_conference_manager_delegate-p.html#​afca1af08d628fd0be0ddd1bed1bf9138|conferenceReqSpeaker:​memId:​nickName:​memName:​]] ​   | 收到观众申请主播的请求,只有管理员会触发 |
 +|[[http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​protocol_e_m_conference_manager_delegate-p.html#​ad90e5d598c1b8cb57a02cd6b81fc929e|conferenceReqAdmin:​memId:​nickName:​memName:​]] ​   | 收到主播申请管理员的请求,只有管理员会触发 |
 +|[[http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​protocol_e_m_conference_manager_delegate-p.html#​a2b09dd5dbb6144cc561027378bc3f1e7|conferenceDidUpdated:​mute:​]] ​   | 收到静音/​解除静音的回调 |
 +|[[http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​protocol_e_m_conference_manager_delegate-p.html#​ae598e6c668121780573ce2402be3d745|conferenceReqSpeakerRefused:​adminId:​]] ​   | 收到申请主播请求被拒绝的回调 |
 +|[[http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​protocol_e_m_conference_manager_delegate-p.html#​aa285cef35bc14db40ef17be9245798ed|conferenceReqAdminRefused:​adminId:​]] ​   | 收到申请管理员请求被拒绝的回调 |
 +|[[http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​protocol_e_m_conference_manager_delegate-p.html#​acccb25d9f4eef388781be85cfeefb5ff|conferenceDidUpdated:​liveCfg:​]] ​   | 会议开启cdn推流或自定义录制,收到LiveCfg的回调,只有管理员能收到 |
 +|[[http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​protocol_e_m_conference_manager_delegate-p.html#​adde6567323a5ffaf249841da585174b8|streamIdDidUpdate:​rtcId:​streamId:​]] ​   | 收到本地流streamId的回调,发布流成功后收到此回调 |
 +|[[http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​protocol_e_m_conference_manager_delegate-p.html#​aff906c9db5f6be3685a44e5c83d0ca0e|streamStateUpdated:​type:​state:​streamId:​]] ​   | 下行音频流无数据时,收到此回调 |
 +|[[http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​protocol_e_m_conference_manager_delegate-p.html#​ab7ce7a7c76c5dca0918c8f8b5875b5e9|streamDidFirstFrameSended:​type:​streamId:​]] ​   | 发送第一帧音视频数据时,收到此回调 |
 +|[[http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​protocol_e_m_conference_manager_delegate-p.html#​a1879f50eea95cdabbf0e265bc1a03fef|streamDidFirstFrameReceived:​type:​streamId:​]] ​   | 接收流第一帧音视频数据时,收到此回调 |
 +|[[http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​protocol_e_m_conference_manager_delegate-p.html#​ac03c8bd548fd6ae0623eb0b6f99f8b80|confrenceDidUpdated:​state:​]] ​   | 会议状态改变时,收到此回调 |
 +|[[http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​protocol_e_m_conference_manager_delegate-p.html#​ac03c8bd548fd6ae0623eb0b6f99f8b80|confrenceDidUpdated:​state:​]] ​   | 会议状态改变时,收到此回调 |
 +|[[http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​protocol_e_m_conference_manager_delegate-p.html#​a8940c01890169f6c5a68f8d6787264e9|onferenceDidUpdate:​streamId:​statReport:​]] ​   | 当前会议的媒体流质量报告回调 |
  
 +== EMCallConference ==
 +参见http://​sdkdocs.easemob.com/​apidoc/​ios/​chat3.0/​interface_e_m_call_conference.html