差别

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

到此差别页面的链接

两侧同时换到之前的修订记录 前一修订版
后一修订版
前一修订版
rtc:conference:android [2020/06/28 10:17]
allenlee [开启cdn推流]
rtc:conference:android [2020/12/18 06:25] (当前版本)
allenlee
行 4: 行 4:
  
  
- ===== 基本知识 ===== 
-   * EMConference:​ 会议实例,保存着与当前会议的相关信息,会议加入成功后会在callback中返回该实例 
-  
-   * EMConferenceMember:​ 会议成员实例,保存着该会议成员的相关信息,有成员加入或退出会议时会在callback中返回该实例 ​ 
-  
-   * EMConferenceStream:​ 会议中的数据流,包含音频数据和视频数据的相关信息,有成员推流或停止推流时会在相应callback中返回该实例 
-  
-   * EMStreamParam:自己发布的数据流的各种配置,需要在调用发布接口时作为参数传入 
-  
-   * EMConferenceListener:会议监听类,会回调会议成员变化,会议数据流变化等,需要在代码中设置监听 
-  
-   * EMConferenceRole:多人会议成员角色 
  
-       1. 观众Audience:只能观看收听音视频,即只能订阅流 +====== 跑Demo ======
-       2. 主播Speaker:能上传自己的音视频,能观看收听其他主播的音视频,即能发布流和订阅流 +
-       3. 管理员Admin:能创建会议,销毁会议,移除会议成员,切换其他成员的角色,订阅流,发布流 +
-     +
-       ​注意: +
-       >>​ 每个人必须调用join接口成功后,才算是加入会议(即成为会议成员)。会议成员才允许进行其他操作比如订阅流、发布流等 +
-       >>​ 成员如果想改变自己角色,必须想办法知管理员,只有管理员才能修改 +
-  +
-   * EMConferenceType:多人会议类型,​ **目前已经优化,请使用SmallCommunication类型。**+
  
-        1SmallCommunication:普通通信会议最多支持参会者9人,成员都可以自由说话和发布视频,成员角色Speaker +===== 示例代码 ===== 
-        2. LargeCommunication:大型通信会议,最多参会30人,成员都可以自由说话和发布视频,成员角色Speaker<​ +进入环信[[https://​www.easemob.com/​download/​rtc|IM下载]]页面选择视频会议Demo 下载 \\ 
-        3LiveStream:互动视频会议,会议里支持最多9个主播和600个观众+直接去Github下载 [[https://​github.com/​easemob/​videocall-android|克隆代码]] \\
  
- ===== 多人音视频会议功能实现 ===== 
-  
-以下是从创建会议到离开会议完整的流程讲解: 
  
-==== 注册监听 ​====+===== 前提条件 ​===== 
 +Android Studio 3.0 或以上版本 \\ 
 +Android SDK API 等级 19 或以上 \\  
 +支持 Android 4.4 或以上版本的设备 \\
  
-进入会议之前调用''​EMConferenceManager#​addConferenceListener(EMConferenceListener listener)''​方法指定回调监听成员加入或离开会议,数据流更新等 + 
-注意:该回调监听中的所有方法运行在子线程中,请勿在其中操作UI +===== 运行Demo ===== 
-<​code ​java+**Demo代码目录简介** \\ 
-EMConferenceListener listener = new EMConferenceListener() {+ 
 +{{:​rtc:​conference:​多人音视频demo.png?​400|}} \\ 
 +源码目录如上所示,主要目录如以下介绍 \\ 
 +ui是有关所有Activity的具体实现 \\ 
 +runtimepermissions是有关运行是有关动态权限的获取的封装 \\ 
 +untils是有关工具类的封装 \\ 
 +DemoHelper和DemoApplication类是全局单例,用来做一些初始化 \\ 
 + 
 +**工程设置,SDK导入** \\ 
 + 
 +选择如下任意一种方式将环信语音SDK集成到你的项目中:​ \\  
 + 
 +**方法一:使用 JCenter 自动集成** \\ 
 + 
 +在项目的 /​app/​build.gradle 文件中,添加如下行 \\ 
 +<​code>​ 
 +dependencies {  
 +   ...  
 +   // x.x.x 请填写具体版本号,如:3.7.0  
 +   // 可通过 SDK 发版说明取得最新版本号  
 +   api '​com.hyphenate:​hyphenate-sdk:​x.x.x'​  
 + }  
 +</​code>​ 
 + 
 +**方法二:手动复制 SDK 文件** \\  
 + 
 +前往SDK载页面,获取最新版的环信音视频SDK,然后解压;​ \\ 
 +将SDK包内libs路径下的如下文件,拷贝到你的项目路径下 \\  
 +hyphenatechat_3.7.0.jar ​  ​文件 /app/libs/ \\   
 +arm-v8a 文件夹 ​           /​app/​src/​main/​jniLibs/​ \\ 
 +armeabi-v7a 文件夹 ​      /​app/​src/​main/​jniLibs/​ \\ 
 +x86文件夹 ​              /​app/​src/​main/​jniLibs/​ \\ 
 +x86_64文件夹 ​          /​app/​src/​main/​jniLibs/​ \\ 
 + 
 +**运行项目** \\ 
 + 
 +用Android Studio 运行examples\ChatDemoUI3.0 或者 连接Android 手机,直接运行即可 \\ 
 +需要注意的的是项目build.gradle中的,配置成和自己Android Studio相匹配的 gradle 版本,如下所示 \\ 
 +<​code>​ 
 +dependencies { 
 +    classpath '​com.android.tools.build:​gradle:​3.2.1'​ 
 + } 
 +</​code>​ 
 + 
 +====== 快速集成 ====== 
 + 
 +===== 1 环信后台注册appkey ===== 
 +环信为管理者与开发者提供了方便易用的App工作台–环信管理后台; \\ 
 +通过环信管理后台可以完成应用创建、服务购买、企业信息修改基础功能; ​ \\ 
 +同时,管理后台也提供了发送消息、用户管理、群组管理、聊天室管理和数据统计等管理监控功能;​ \\ 
 +有关注册appkey的详细操作大家可以参考[[im:​quickstart:​guide:​experience|注册AppKey]] 。 \\ 
 + 
 +===== 2 创建项目 ===== 
 +打开 Android Studio点击Start a new Android Studio project; \\ 
 +在Select a Project Template界面,选择 Phone and Tablet > Empty Activity; \\ 
 +然后点击 Next; \\  
 +在 Configure Your Project界面,依次填入以下内容:Name:你的Android项目名称,如 HelloWrold; \\ 
 +Package name:​你的项目包的名称,如 package.easemob.helloworld;​ \\ 
 +Save location:项目的存储路径;​ \\ 
 +Language:项目的编程语言,如 Java; \\ 
 +Minimum API level:项目的最低 API 等级;​(**注意本SDK 支持API 19或以上**) \\ 
 +然后点击 Finish; \\ 
 +根据屏幕提示,安装可能需要的插件;​ \\ 
 +上述步骤使用 Android Studio 3.6.2 示例; \\ 
 +你也可以直接参考Android Studio 官网文档创建首个应用。 \\ 
 + 
 +===== 3 导入SDK到工程 ===== 
 +新建项目完成以后,可以选择以上任何一种导入SDK的方法,把环信音视频SDK集成到你的项目里面,参考[[http://​docs-im.easemob.com/​rtc/​conference/​android#​%E8%BF%90%E8%A1%8Cdemo|导入环信SDK]]。 \\ 
 + 
 +===== 4 添加权限 ===== 
 +根据场景需要,在 /​app/​src/​main/​AndroidManifest.xml 文件中添加如下行,获取相应的设备权限;​ \\ 
 +在清单文件 AndroidManifest.xml 里加入以下权限,以及写上你注册的 AppKey; \\ 
 +权限配置(实际开发中可能需要更多的权限,可参考 Demo); \\ 
 +<​code>​ 
 +<​manifest xmlns:​android="​http://​schemas.android.com/​apk/​res/​android"​ 
 + ​xmlns:​tools="​http://​schemas.android.com/​tools"​ 
 + ​package="​com.a.b">​ 
 + 
 + <​uses-feature android:​name="​android.hardware.camera"​ /> 
 + <​uses-feature android:​name="​android.hardware.camera.autofocus"​ /> 
 + <​uses-permission android:​name="​android.permission.VIBRATE"​ /> 
 + <​uses-permission android:​name="​android.permission.INTERNET"​ /> 
 + <​uses-permission android:​name="​android.permission.RECORD_AUDIO"​ /> 
 + <​uses-permission android:​name="​android.permission.CAMERA"​ /> 
 + <​uses-permission android:​name="​android.permission.ACCESS_NETWORK_STATE"​ /> 
 + <​uses-permission android:​name="​android.permission.CHANGE_NETWORK_STATE"​ /> 
 + <​uses-permission android:​name="​android.permission.WRITE_EXTERNAL_STORAGE"​ /> 
 + <​uses-permission android:​name="​android.permission.READ_EXTERNAL_STORAGE"​ /> 
 + <​uses-permission android:​name="​android.permission.MOUNT_UNMOUNT_FILESYSTEMS"​ /> 
 + <​uses-permission android:​name="​android.permission.MODIFY_AUDIO_SETTINGS"​ /> 
 + <​uses-permission android:​name="​android.permission.BLUETOOTH_ADMIN"​ /> 
 + <​uses-permission android:​name="​android.permission.BLUETOOTH"​ /> 
 + <​uses-permission android:​name="​android.permission.READ_PHONE_STATE"​ /> 
 + <​uses-permission android:​name="​android.permission.RECEIVE_BOOT_COMPLETED"​ /> 
 + <​uses-permission android:​name="​com.android.launcher.permission.READ_SETTINGS"​ /> 
 + <​uses-permission android:​name="​android.permission.BROADCAST_STICKY"​ /> 
 + <​uses-permission android:​name="​android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"​ /> 
 +<!-- 设置环信应用的appkey --> 
 + <​meta-data 
 + ​android:​name="​EASEMOB_APPKEY"​ 
 + ​android:​value="​easemob-demo#​chatdemoui"​ /> 
 + <​!-- 声明sdk所需的service --> 
 + <​service 
 + ​android:​name="​com.hyphenate.chat.EMChatService"​ 
 + ​android:​exported="​true"​ 
 + ​tools:​ignore="​ExportedService"​ /> 
 + <​service 
 + ​android:​name="​com.hyphenate.chat.EMJobService"​ 
 + ​android:​exported="​true"​ 
 + ​android:​permission="​android.permission.BIND_JOB_SERVICE"​ /> 
 + 
 + <​!-- 声明sdk所需的receiver --> 
 + <​receiver android:​name="​com.hyphenate.chat.EMMonitorReceiver">​ 
 + <​intent-filter>​ 
 + <​action android:​name="​android.intent.action.PACKAGE_REMOVED"​ /> 
 + 
 + <​data android:​scheme="​package"​ /> 
 + ... 
 +</​manifest>​ 
 +</​code>​  
 + 
 +注意:关于EASEMOB_APPKEY对应的value获取,在创建应用后, \\ 
 +      申请 AppKey并进行相关配置,环信Demo中AppKey为easemob-demo#​chatdemoui;​ 
 + 
 +===== 5 打包混淆 ===== 
 +在 app/​proguard-rules.pro 文件中添加如下行,防止混淆环信SDK的代码。 
 +<​code>​ 
 +  -keep class com.hyphenate.** {*;} 
 +  -dontwarn ​ com.hyphenate.** 
 +  //​3.6.8版本之后移除apache,无需再添加 
 +  -keep class internal.org.apache.http.entity.** {*;} 
 +  //​如果使用了实时音视频功能 
 +  -keep class com.superrtc.** {*;} 
 +  -dontwarn ​ com.superrtc.** 
 +</​code>​  
 + 
 + 
 + 
 +===== 6 初始化SDK ===== 
 +初始化环信SDK,可以参考使用DemoHelper中的init方法,可设置私有部署地址或者是否允许日志输出,调用如下: \\ 
 +<​code>​ 
 +   ​public void init(Context context) { 
 + EMOptions options = initChatOptions(context);​ 
 +        appContext = context; 
 +        //​可设置私有服务地址 
 +        options.setRestServer("​a.b.c"​);​  
 +        options.setIMServer("​1.2.3.4"​);​ 
 +        options.setImPort(8081);​ 
 +        EMClient.getInstance().init(context,​ options); 
 +        //​打开日志输出 
 +        EMCallManager manager = EMClient.getInstance().callManager();​ 
 + EMClient.getInstance().setDebugMode(true);​ 
 +
 +</​code>​ 
 + 
 +===== 7 音视频功能初始化 ===== 
 +**设置会议监听** \\ 
 +DemoHelper还有一个重要的功能就是设置[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​interfacecom_1_1hyphenate_1_1_e_m_conference_listener.html|EMConferenceListener]] 进行会议监听 ​  \\ 
 +通过这个监听可以再加入会议的时候获取到已经在会议进入会议之前的流或人员 ​ \\ 
 +调用EMConferenceManager#​addConferenceListener(EMConferenceListener listener)方法指定回调监听 ​\\ 
 +成员加入或离开会议,数据流更新等 注意:该回调监听中的所有方法运行在子线程中,请勿在其中操作UI ​\\ 
 +调用EMConferenceManager#​addConferenceListener(EMConferenceListener listener)方法指定回调监听 \\ 
 +<​code>​ 
 + ​EMConferenceListener listener = new EMConferenceListener() {
     @Override public void onMemberJoined(String username) {     @Override public void onMemberJoined(String username) {
         // 有成员加入         // 有成员加入
     }     }
-                                                           +                                                            ​
     @Override public void onMemberExited(String username) {     @Override public void onMemberExited(String username) {
         // 有成员离开         // 有成员离开
     }     }
-                                                               +                                                                ​
     @Override public void onStreamAdded(EMConferenceStream stream) {     @Override public void onStreamAdded(EMConferenceStream stream) {
         // 有流加入         // 有流加入
     }     }
-                                                               +                                                                ​
     @Override public void onStreamRemoved(EMConferenceStream stream) {     @Override public void onStreamRemoved(EMConferenceStream stream) {
         // 有流移除         // 有流移除
     }     }
-                                                               +                                                                ​
     @Override public void onStreamUpdate(EMConferenceStream stream) {     @Override public void onStreamUpdate(EMConferenceStream stream) {
         // 有流更新         // 有流更新
     }     }
-                                                               +                                                                ​
     @Override public void onPassiveLeave(int error, String message) {     @Override public void onPassiveLeave(int error, String message) {
         // 被动离开         // 被动离开
     }     }
-                                                               +                                                                ​
     @Override public void onConferenceState(ConferenceState state) {     @Override public void onConferenceState(ConferenceState state) {
         // 聊天室状态回调         // 聊天室状态回调
     }     }
-                                                               +                                                                ​
     @Override public void onStreamSetup(String streamId) {     @Override public void onStreamSetup(String streamId) {
         // 流操作成功回调         // 流操作成功回调
     }     }
-                                                                   +                                                                    ​
     @Override public void onSpeakers(final List<​String>​ speakers) {     @Override public void onSpeakers(final List<​String>​ speakers) {
         // 当前说话者回调         // 当前说话者回调
     }     }
-                                                                   +                                                                    ​
     @Override public void onReceiveInvite(String confId, String password, String extension) {     @Override public void onReceiveInvite(String confId, String password, String extension) {
         // 收到会议邀请         // 收到会议邀请
行 89: 行 232:
         appContext.startActivity(conferenceIntent);​         appContext.startActivity(conferenceIntent);​
     }     }
-}); 
     ​     ​
-// 在Activity#​onCreate()中添加监听 +    ​// 在Activity#​onCreate()中添加监听 
-EMClient.getInstance().conferenceManager().addConferenceListener(conferenceListener);​ +    EMClient.getInstance().conferenceManager().addConferenceListener(conferenceListener);​ 
-     +});     ​ 
-// 在Activity#​onDestroy()中移除监听 +</​code>​
-EMClient.getInstance().conferenceManager().removeConferenceListener(conferenceListener); +
-</​code> ​   +
  
-==== 用户A创建会议 ==== 
  
-SDK没有提供单独的创建接口,提供了createAndJoinConference和joinRoom接口,A调用该接口后,将拥有一个会议实例Conference,同时A将成为该Conference的成员且角色是Admin. 
  
-用户创建会议时可以设置参数指定是否支持小程序音视频,是否需要在服务器端录制,录制时是否合并流。 
  
-    注意:  +===== 8 加入会议 ​===== 
-    创建加入会议有两组apicreateAndJoinConference 和 joinRoom.  +在进行音视频通话前,需要首先登录IM账户,登录过程参见[[http://​docs-im.easemob.com/​im/​android/​sdk/​basic#​登录|账号登录]]。 \\ 
-    ​joinConference 是通过会议ID,密码方式加入会议,而joinRoom是通过房间名称加入 +若您还没IM账户,需要先注册账户,注册过程参见[[http://docs-im.easemob.com/​im/​android/​sdk/​basic#​注册|账号注册]] \\ 
-    创建会议成功以后,默认超时时间为三分钟,超过三分钟没有人加入,会议会自动销毁;另外当会议中所有人离开2分钟后,会议也会被销毁 +登录环信ID以后可以加入会议,通过 ​[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_conference_manager.html#​a8940f54febf2086ccd978d75980c7763|EMConferenceManager#​joinRoom]] API加入房间 ​ \\ 
-    joinRoom api在会议不存在会自动去创建。 +创建会议成功以后,默认超时时间为三分钟,超过三分钟没有人加入,会议会自动销毁; ​\\ 
-   +另外当会议中所有人离开2分钟后,会议也会被销毁; \\ 
-<code java> +joinRoom api在会议不存在会自动去创建。 ​\\ 
-EMClient.getInstance().conferenceManager().createAndJoinConference(emConferenceType,​ +joinRoom有以下两个接口,请看下面详细介绍(第二个多参的方法可以指定创建会议的各种属性) \\ 
-                password, true, false, false, new EMValueCallBack<​EMConference>​() { +<​code>​ 
-                    @Override + /**
-                    public void onSuccess(EMConference value) { +
-                        // 返回当前会议对象实例 value +
-                        // 可进行推流等相关操作 +
-                        // 运行在子线程中,勿直接操作UI +
-                    } +
-     +
-                    @Override +
-                    public void onError(int error, String errorMsg) { +
-                        // 运行在子线程中,勿直接操作UI +
-                    } +
-                }); +
-</​code>​ +
- +
-<code java> +
-EMClient.getInstance().conferenceManager().joinConference(confId,​ +
-                password,​new EMValueCallBack<​EMConference>​() { +
-                    @Override +
-                    public void onSuccess(EMConference value) { +
-                        // 返回当前会议对象实例 value +
-                        // 可进行推流等相关操作 +
-                        // 运行在子线程中,勿直接操作UI +
-                    } +
-     +
-                    @Override +
-                    public void onError(int error, String errorMsg) { +
-                        // 运行在子线程中,勿直接操作UI +
-                    } +
-                }); +
-</​code>​ +
- +
-用户也可使用接口加入房间。可以指定房间的名称,并且在房间不存在时会自动创建。 +
-另外可以在加入会议时指定角色。 +
- +
-<​code ​java+
-/**+
      * \~chinese      * \~chinese
      * 加入多人音视频加议房间      * 加入多人音视频加议房间
行 157: 行 259:
      */      */
     public void joinRoom(final String room ,final String password,​final EMConferenceRole role, final EMValueCallBack<​EMConference>​ callback)     public void joinRoom(final String room ,final String password,​final EMConferenceRole role, final EMValueCallBack<​EMConference>​ callback)
- + 
     /**     /**
      * \~chinese      * \~chinese
行 169: 行 270:
      */      */
     public void joinRoom(final String room ,final String password,​final EMConferenceRole roletype ,final EMRoomConfig param, final EMValueCallBack<​EMConference>​ callback)     public void joinRoom(final String room ,final String password,​final EMConferenceRole roletype ,final EMRoomConfig param, final EMValueCallBack<​EMConference>​ callback)
 +     
 +   ​有关EMRoomConfig定义如下:
 +   /**
 +    * 会议房间属性类
 +    * confrTyp ​  ​会议类型
 +    * isSupportVxmini ​ 是否支持小程序
 +    * isRecord ​   是否支持录制
 +    * isMergeRecord ​ 是否支持和流
 +    * nickName ​ 用户昵称
 +    * ext     ​用户头像
 +    * maxTalkerCount ​  ​房间最多主播
 +    * maxVideoCount ​   房间最多视频流媒体
 +    * maxAudienceCount 房间最多观众
 +    * maxPubDesktopCount ​ 房间最多共享桌面流
 +  */
 +  public class EMRoomConfig {
 +       ​EMConferenceManager.EMConferenceType confrTyp = EMConferenceManager.EMConferenceType.SmallCommunication;​
 +        private boolean isSupportMiniProgram = false;
 +        private boolean isRecord = false;
 +        private boolean isMergeRecord = false;
 +        private String ​ nickName = null;
 +        private String ​ ext = null;
 +        private int  maxTalkerCount = -1;
 +        private int  maxVideoCount = -1;
 +        private int  maxAudienceCount = -1;
 +        private int  maxPubDesktopCount = -1;
 +        private EMLiveConfig liveConfig = null;
 +           
 +       ​public EMRoomConfig(EMConferenceManager.EMConferenceType confrTyp,
 +                               ​boolean ​ isSupportMiniProgram,​boolean isRecord,
 +                               ​boolean ​ isMergeRecord , String ​ nickName,
 +                               ​String ext, int maxTalkerCount,​
 +                               int maxVideoCount,​int maxAudienceCount,​
 +                               int maxPubDesktopCount)
 +                               ​{...};​
 +      }
 </​code>​ </​code>​
  
-==== 管理员A邀请其他人加入会议 ==== 
  
-SDK没有提供邀请接口,你可以自己实现,比如使用环信IM通过发消息邀请,比如通过发邮件邀请等等。 
-至于需要发送哪些邀请信息,可以参照SDK中的join接口,目前是需要Conference的confrId和password 
  
-比如用环信IM消息邀请 +===== 9 布流 ===== 
-<code java> +展示从DemoHelper类EMConferenceListener中的onStreamAdded回调和onMemberJoined获取到的流和主列表 \\ 
-final EMMessage message = EMMessage.createTxtSendMessage("​邀请你观看直", to); +在ConferenceActivity中实现EMConferenceListener,然后直接把 ConferenceActivity 注册监听 ​ \\ 
-message.setAttribute(Constant.EM_CONFERENCE_IDconference.getConferenceId());​ +可以用以下方法EMClient.getInstance().conferenceManager().addConferenceListener(this \\ 
-message.setAttribute(Constant.EM_CONFERENCE_PASSWORD,​ conference.getPassword());​ +这样就可实现 EMConferenceListener 事件的处理,比如主播 进出房间 增加流 移除流 角色变更 管理员信息变更等事件 ​ \\
-EMClient.getInstance().chatManager().sendMessage(message); +
-</​code>​+
  
-    注意: 使用环信IM邀请多个人时,建使用群组消息,如果使用单聊发消息请注意每条消息中间的时间间隔,以防触发环信的垃圾消息防御机制 +进入会房间以后如果用户角色为主播进行发布视频流 ,观众只能订阅视频流 不能发布视频流 ​ \\ 
- +可以调用SDK的publish接口发布流,该接口用到了EMStreamParam参数,你可以自由配置 ​ \\ 
-==== 用户B接收到邀请加入会议 ==== +比如是否上传视频 是否上传音频 使用前置或后置摄像头 视频码率 显示视频页面等等 ​   \\ 
- +具体实现可以参考中发布订阅视频流的内容,关于以上的代码逻辑如以 如以下: \\ 
-用户B解析出邀请消息中带的confrId和password,调用SDK的join接口加入会议,成为会议成员且角色是Speaker. +<​code>​ 
- +  pirvate void pubLocalStream() {   ​
-<code java> +
-EMClient.getInstance().conferenceManager().joinConference(conferenceId,​ password, new  +
-    EMValueCallBack<​EMConference>​() { +
-        @Override +
-        public void onSuccess(EMConference value) { +
-            // 返回当前会议对象实例 value +
-            // 可进行等相关操作 +
-            // 运行在子线程中勿直接操作UI +
-        } +
-     +
-        @Override +
-        public void onError(int error, String errorMsg) { +
-            // 运行在子线程中,勿直接操作UI +
-        } +
-    }); +
-</​code>​ +
- +
-用户B成功加入会议后,会议中其他成员会收到回调''​EMConferenceListener#​onMemberJoined(EMConferenceMember member)''​ +
- +
-<code java> +
-@Override +
-public void onMemberJoined(final EMConferenceMember member) { +
-    runOnUiThread(new Runnable() { +
-        @Override +
-        public void run() { +
-            Toast.makeText(activity,​ member.memberName + " joined conference!",​ Toast.LENGTH_SHORT).show();​ +
-        } +
-    }); +
-+
-</​code>​ +
- +
-==== 成员A发布视频流 ​==== +
- +
-成员A和成员B都有发布流的权限,可以调用SDK的publish接口发布流,该接口用到了EMStreamParam参数,你可以自由配置比如是否上传视频是否上传音频使用前置或后置摄像头视频码率显示视频页面等等 +
- +
-<​code ​java+
-pirvate void pubLocalStream() {+
     EMStreamParam param = new EMStreamParam();​     EMStreamParam param = new EMStreamParam();​
     param.setStreamType(EMConferenceStream.StreamType.NORMAL);​     param.setStreamType(EMConferenceStream.StreamType.NORMAL);​
     param.setVideoOff(false);​     param.setVideoOff(false);​
     param.setAudioOff(false);​     param.setAudioOff(false);​
-        ​+    param.setVideoWidth(720);​ 
 +    param.setVideoHeight(480); ​    
     EMClient.getInstance().conferenceManager().publish(param,​ new EMValueCallBack<​String>​() {     EMClient.getInstance().conferenceManager().publish(param,​ new EMValueCallBack<​String>​() {
         @Override         @Override
         public void onSuccess(String streamId) {         public void onSuccess(String streamId) {
         }         }
-        ​+         
         @Override         @Override
         public void onError(int error, String errorMsg) {         public void onError(int error, String errorMsg) {
行 244: 行 340:
 } }
 </​code>​ </​code>​
-    ​ 
-    注意:如果是纯音频会议,只需要在发布数据流时将EMStreamParam中的setVideoOff置为true即可 
- 
- 
- 
-==== 设置视频流的参数 ==== 
- 
- 
- 
-如果用户想对发布的音频或者视频流的参数进行调整,可以使用EMStreamParam中的参数进行设置。 
-例如只发布音频流,可以设置关闭视频。 
  
 <​code>​ <​code>​
-normalParam.setVideoOff(true); +注意:如果是纯音频会议,只需要在发布数据流时将EMStreamParam中的setVideoOff置为true即可  
-</​code>​+      共享桌面时候发布的是桌面流 EMConferenceStream.StreamType.DESKTOP  ​ 
 +</​code> ​     
  
-例如,设置视频流的分辨率+**设置视频流的参数 **
  
 +如果用户想对发布的音频或者视频流的参数进行调整,可以使用EMStreamParam中的参数进行设置。 ​
 +只发布音频流,可以设置关闭视频。 \\
 +设置视频流的分辨率 \\
 <​code>​ <​code>​
-normalParam.setVideoHeight(720);​ + ​normalParam.setVideoOff(true);​ 
-normalParam.setVideoWidth(1280);​+ normalParam.setVideoHeight(720); ​  ​ 
 + ​normalParam.setVideoWidth(1280);​
 </​code>​ </​code>​
- +===== 10 订阅流 ===== 
-另外为保证视频清晰度的话需要注意最小码率的控制,比如720p,需要保证码率在900k ~2.5Mbps范围内 +成员A成功发布数据流后,会议中其他成员会收到 ​  \\ 
-因此需要设置最小码率为900 +监听类回调EMConferenceListener#​onStreamAdded ​ \\ 
 +如果成员B想看成员A的音视频,可以调用subscribe接口进行订阅 \\
 <​code>​ <​code>​
-normalParam.setMinVideoKbps(900);​ + @Override 
-</​code>​ + ​public void onStreamAdded(final EMConferenceStream stream) {
-==== 其他成员收到通知并订阅流 ==== +
- +
-成员A成功发布数据流后,会议中其他成员会收到监听类回调''​EMConferenceListener#​onStreamAdded(EMConferenceStream stream)''​,如果成员B想看成员A的音视频,可以调用subscribe接口进行订阅 +
- +
-<code java> +
-@Override +
-public void onStreamAdded(final EMConferenceStream stream) {+
     runOnUiThread(new Runnable() {     runOnUiThread(new Runnable() {
         @Override         @Override
行 292: 行 375:
             memberView.setVideoOff(stream.isVideoOff());​             memberView.setVideoOff(stream.isVideoOff());​
             memberView.setDesktop(stream.getStreamType() == EMConferenceStream.StreamType.DESKTOP);​             memberView.setDesktop(stream.getStreamType() == EMConferenceStream.StreamType.DESKTOP);​
-                ​+                 
             EMClient.getInstance().conferenceManager().subscribe(stream,​ memberView.getSurfaceView(),​ new EMValueCallBack<​String>​() {             EMClient.getInstance().conferenceManager().subscribe(stream,​ memberView.getSurfaceView(),​ new EMValueCallBack<​String>​() {
                 @Override                 @Override
                 public void onSuccess(String value) {                 public void onSuccess(String value) {
                 }                 }
-                ​+                 
                 @Override                 @Override
                 public void onError(int error, String errorMsg) {                 public void onError(int error, String errorMsg) {
-                ​+                 
                 }                 }
             });             });
行 308: 行 391:
 </​code>​ </​code>​
  
-==== 成员A设置自己发布的流 ​==== +===== 11 退出会议 ===== 
- +成员B可调用调用EMConferenceManager#​exitConference接口离开会议 ​ \\ 
-成员A对自己的数据流做上操作成功后,会议中的其他成员会收到回调''​EMConferenceListener#​onStreamUpdate(EMConferenceStream stream)''​ +会议中的其他成员会收到回调EMConferenceListener#​onMemberExited ​ \\ 
- +注意:当最后一个成员调用leave接口后,会议会自动销毁 
-<​code ​java+<​code>​ 
-@Override + EMClient.getInstance().conferenceManager().exitConference()
-public void onStreamUpdate(final EMConferenceStream stream+
-    runOnUiThread(new Runnable() { +
-        @Override +
-        public void run() { +
-            Toast.makeText(activity,​ stream.getUsername() + " stream update!",​ Toast.LENGTH_SHORT).show()+
-            // 更新当前stream所对应View +
-            updateConferenceMemberView(stream);​ +
-    } +
-    }); +
-}+
 </​code>​ </​code>​
  
- +<​code>​ 
-==== 成员B取消订阅流 ==== +   ​@Override 
- + ​public void onMemberExited(final ​EMConferenceMember member) {
-成员B如果不想再看成员A的音视频,可以调用SDK接口unsubscribe +
- +
-<code java> +
-EMClient.getInstance().conferenceManager().unsubscribe(emConferenceStream,​ new EMValueCallBack<​String>​() { +
-    @Override +
-    public void onSuccess(String value) { +
-    } +
-     +
-    @Override +
-    public void onError(int error, String errorMsg) { +
-     +
-    } +
-}); +
-</​code>​ +
- +
-==== 成员A取消发布流 ==== +
- +
-成员A可以调用unpublish接口取消自己已经发布的数据流,操作成功后,会议中的其他成员会收到回调''​EMConferenceListener#​onStreamRemoved(EMConferenceStream stream)''​,将对应的数据流信息移除 +
- +
-<​code ​java+
-@Override +
-public void onStreamRemoved(final ​EMConferenceStream stream) {+
     runOnUiThread(new Runnable() {     runOnUiThread(new Runnable() {
         @Override         @Override
         public void run() {         public void run() {
-            Toast.makeText(activity, ​stream.getUsername() ​+ " ​stream ​removed!",​ Toast.LENGTH_SHORT).show();​ +            Toast.makeText(activity, ​member.memberName ​+ " removed ​conference!", Toast.LENGTH_SHORT).show();​
-            if (streamList.contains(stream)) { +
-                removeConferenceView(stream);​ +
-            }+
         }         }
     });     });
行 363: 行 411:
 </​code>​ </​code>​
  
-==== 成员B离开会议 ==== 
  
-成员B调用SDK接口离开会议议中的其他成员会收回调''​EMConferenceListener#​onMemberExited(EMConferenceMember member)''​+====== 进阶功能 ====== 
 +===== 会议管理 ===== 
 +==== 取日志 ==== 
 +SDK写入日志文件本地。日志文件路径如下:sdcard/​Android/​data/​(自己App包名)/​(appkey)/​core_log/​easemob.log;​ \\ 
 +以Demo为例,通过adb命令获取本地的log: \\ 
 +<​code>​ 
 +adb pull /​sdcard/​Android/​data/​com.hyphenate.chatuidemo/​easemob-demo#​chatdemoui/​core_log/​easemob.log 
 +</​code>​
  
-<​code ​java+==== 创建会议并加入 ==== 
-EMClient.getInstance().conferenceManager().exitConference()+SDK还提供了createAndJoinConference接口创建并加入会议 ​ \\ 
 +A调用该接口后,将拥有一个会议实例Conference,同时A将成为该Conference的成员且角色是Admin ​ \\ 
 +用户创建会议时可以设置参数指定是否支持小程序音视频,是否需要在服务器端录制,录制时是否合并流 \\ 
 +通过这个AIP创建会议以后,其他人可以通过joinConference通过会议ID,密码方式加入会议 ​ \\ 
 +createAndJoinConference可以有以下四种方法去创建,请看以下方法详细介绍 ​ \\ 
 +<​code>​ 
 + ​**方法一:参数(会议类型,会议密码,是否支持小程序,是否录制,是否合流,​ 回调);​** 
 +  
 + ​public void createAndJoinConference(final EMConferenceType type, final String password, 
 +                                     final boolean isSupportMiniProgram,​final  
 +                                     ​boolean recordOnServer,​final boolean mergeStream,​ 
 +                                     final EMValueCallBack<​EMConference>​ callback) 
 + ​EMClient.getInstance().conferenceManager().createAndJoinConference(emConferenceType,​ 
 +                password, true, false, false, new EMValueCallBack<​EMConference>​() 
 +                    @Override 
 +                    public void onSuccess(EMConference value) { 
 +                        // 返回当前会议对象实例 value 
 +                        // 可进行推流等相关操作 
 +                        // 运行在子线程中,勿直接操作UI 
 +                    } 
 +      
 +                    @Override 
 +                    public void onError(int error, String errorMsg) { 
 +                        // 运行在子线程中,勿直接操作UI 
 +                    } 
 +                }); 
 +                  
 +  
 +** 方法二:参数(会议类型,会议密码,回调);​** 
 +  
 + ​public void createAndJoinConference(final EMConferenceType type, final String password, 
 +                                     final EMValueCallBack<​EMConference>​ callback){} 
 + ​EMClient.getInstance().conferenceManager().createAndJoinConference(emConferenceType,​ 
 +                password, new EMValueCallBack<​EMConference>​() { 
 +                    @Override 
 +                    public void onSuccess(EMConference value) { 
 +                        // 返回当前会议对象实例 value 
 +                        // 可进行推流等相关操作 
 +                        // 运行在子线程中,勿直接操作UI 
 +                    } 
 +      
 +                    @Override 
 +                    public void onError(int error, String errorMsg) { 
 +                        // 运行在子线程中,勿直接操作UI 
 +                    } 
 +                }); 
 +                    
 + ​**方法三:参数(会议类型,会议密码,是否录制,是否合流,​ 回调);​** 
 +  
 + ​public void createAndJoinConference(final EMConferenceType type, final String password, 
 +                                     ​final ​ boolean recordOnServer,​final boolean mergeStream,​ 
 +                                     final EMValueCallBack<​EMConference>​ callback) {}; 
 + ​EMClient.getInstance().conferenceManager().createAndJoinConference(emConferenceType,​ 
 +                password, false, false, new EMValueCallBack<​EMConference>​() { 
 +                    @Override 
 +                    public void onSuccess(EMConference value) { 
 +                        // 返回当前会议对象实例 value 
 +                        // 可进行推流等相关操作 
 +                        // 运行在子线程中,勿直接操作UI 
 +                    } 
 +      
 +                    @Override 
 +                    public void onError(int error, String errorMsg) { 
 +                        // 运行在子线程中,勿直接操作UI 
 +                    } 
 +                }); 
 +                    
 + ​**方法四:参数(会议类型,会议密码,房间信息对象,推流信息,​ 回调);**  
 +               
 + ​public void createAndJoinConference(final @NonNull EMConferenceType type, final String password, 
 +                                     final EMRoomConfig roomConfig, final EMStreamParam param, 
 +                                     final EMValueCallBack<​EMConference>​ callback){};​ 
 + ​有关EMRoomConfig定义如下: 
 +   /** 
 +    * 会议房间属性类 
 +    * confrTyp ​  ​会议类型 
 +    * isSupportVxmini ​ 是否支持小程序 
 +    * isRecord ​   是否支持录制 
 +    * isMergeRecord ​ 是否支持和流 
 +    * nickName ​ 用户昵称 
 +    * ext     ​用户头像 
 +    * maxTalkerCount ​  ​房间最多主播 
 +    * maxVideoCount ​   房间最多视频流媒体 
 +    * maxAudienceCount 房间最多观众 
 +    * maxPubDesktopCount ​ 房间最多共享桌面流 
 +  */ 
 +  public class EMRoomConfig { 
 +       ​EMConferenceManager.EMConferenceType confrTyp = EMConferenceManager.EMConferenceType.SmallCommunication;​ 
 +        private boolean isSupportMiniProgram = false; 
 +        private boolean isRecord = false; 
 +        private boolean isMergeRecord = false; 
 +        private String ​ nickName = null; 
 +        private String ​ ext = null; 
 +        private int  maxTalkerCount = -1; 
 +        private int  maxVideoCount = -1; 
 +        private int  maxAudienceCount = -1; 
 +        private int  maxPubDesktopCount = -1; 
 +        private EMLiveConfig liveConfig = null; 
 +            
 +       ​public EMRoomConfig(EMConferenceManager.EMConferenceType confrTyp, 
 +                               ​boolean ​ isSupportMiniProgram,​boolean isRecord, 
 +                               ​boolean ​ isMergeRecord , String ​ nickName, 
 +                               ​String ext, int maxTalkerCount,​ 
 +                               int maxVideoCount,​int maxAudienceCount,​ 
 +                               int maxPubDesktopCount) 
 +                               ​{...};​ 
 +      }
 </​code>​ </​code>​
  
-    注意:当最后一个成员调用leave接口后,会议会自动销毁 +<​code>​ 
- +EMClient.getInstance().conferenceManager().joinConference(confId,​ 
-<​code ​java+                password,new EMValueCallBack<​EMConference>​() { 
-@Override +                    @Override 
-public void onMemberExited(final EMConferenceMember member+                    public void onSuccess(EMConference value) { 
-    runOnUiThread(new Runnable() { +                        // 返回当前会议对象实例 value 
-        @Override +                        // 可进行推流等相关操作 
-        public void run() { +                        // 运行在子线程中,勿直接操作UI 
-            ​Toast.makeText(activity, member.memberName + " removed conference!"​Toast.LENGTH_SHORT).show(); +                    } 
-        +      
-    }); +                    @Override 
-}+                    public void onError(int errorString errorMsg{ 
 +                        // 运行在子线程中,勿直接操作UI 
 +                    ​
 +                });
 </​code>​ </​code>​
  
-==== 成员B销毁会议 ==== +==== 邀请成员加入会议 ==== 
- +SDK没有提供邀请接口,你可以自己实现,比如使用环信IM通过发消息邀请 ​ \\ 
-成员B调用SDK接口销毁会议,会议中的其他成员会收到回调 +比如通过发邮件邀请等等。 至于需要发送哪些邀请信息,可以参照SDK中的join接口 \\ 
-''​EMConferenceListener#​onPassiveLeave(final int error, final String message)''​ +目前是需要Conference的confrId和password比如用环信IM发消息邀请,​如下所示 ​ \\ 
 +<​code>​ 
 +  final EMMessage message = EMMessage.createTxtSendMessage("​邀请你观看直播",​ to); 
 +message.setAttribute(Constant.EM_CONFERENCE_ID,​ conference.getConferenceId());​ 
 +message.setAttribute(Constant.EM_CONFERENCE_PASSWORD,​ conference.getPassword());​ 
 +EMClient.getInstance().chatManager().sendMessage(message);​ 
 +</​code>​ 
 +==== 管理员销毁会议 ==== 
 +会议中的成员可以调用SDK中的 
 +[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_conference_manager.html#​a8d95d4e839a2598d76be85ed69e06244|EMConferenceManager#​destoryConference]]接口销毁会议 ​\\ 
 +会议被销毁以后,会议中的其他成员会收到回调[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​interfacecom_1_1hyphenate_1_1_e_m_conference_listener.html|EMConferenceListener#​onPassiveLeave]]  \\
 <code java> <code java>
 EMClient.getInstance().conferenceManager().destoryConference() EMClient.getInstance().conferenceManager().destoryConference()
行 419: 行 591:
 </​code>​ </​code>​
  
-==== 获取会议信息 ==== 
-在会议进行中,可以通过getConferenceInfo 方法来查询会议信息,从而可以拿到主播列表,观众人数等信息。 
  
 +==== 设置会议人数限制 ====
 +通过带EMRoomConfig参数的joinRoom接口加入会议的时候 可以设置 ​ \\
 +房间最多视频主播数 房间最多视频数 房间最多观众 房间最多共享桌面流等会议信息 ​  ​\\ ​
 +默认最多主播是100个,最大视频数是9个,房间最多观众是600个 ,最多共享桌面流2个 \\
 +设置的人数限制有效,如下所示: \\
 +<​code> ​
 +    /**
 +     * \~chinese
 +     * 加入多人音视频加议房间
 +     * @param room        会议房间名
 +     * @param password ​   会议房间密码
 +     * @param roletype ​   当前用户加入时指定角色 (EMConferenceRole类型)
 +     * @param param       ​设置会议参数 (EMRoomConfig类型)
 +     * @param callback ​   回调函数
 +     */
 +    public void joinRoom(final String room ,final String password,​final EMConferenceRole roletype ,final EMRoomConfig param, final EMValueCallBack<​EMConference>​ callback)
 +     
 +   ​有关EMRoomConfig定义如下:
 +   /**
 +    * 会议房间属性类
 +    * confrTyp ​  ​会议类型
 +    * isSupportVxmini ​ 是否支持小程序
 +    * isRecord ​   是否支持录制
 +    * isMergeRecord ​ 是否支持和流
 +    * nickName ​ 用户昵称
 +    * ext     ​用户头像
 +    * maxTalkerCount ​  ​房间最多主播
 +    * maxVideoCount ​   房间最多视频流媒体
 +    * maxAudienceCount 房间最多观众
 +    * maxPubDesktopCount ​ 房间最多共享桌面流
 +  */
 +  public class EMRoomConfig {
 +       ​EMConferenceManager.EMConferenceType confrTyp = EMConferenceManager.EMConferenceType.SmallCommunication;​
 +        private boolean isSupportMiniProgram = false;
 +        private boolean isRecord = false;
 +        private boolean isMergeRecord = false;
 +        private String ​ nickName = null;
 +        private String ​ ext = null;
 +        private int  maxTalkerCount = -1;
 +        private int  maxVideoCount = -1;
 +        private int  maxAudienceCount = -1;
 +        private int  maxPubDesktopCount = -1;
 +        private EMLiveConfig liveConfig = null;
 +           
 +       ​public EMRoomConfig(EMConferenceManager.EMConferenceType confrTyp,
 +                               ​boolean ​ isSupportMiniProgram,​boolean isRecord,
 +                               ​boolean ​ isMergeRecord , String ​ nickName,
 +                               ​String ext, int maxTalkerCount,​
 +                               int maxVideoCount,​int maxAudienceCount,​
 +                               int maxPubDesktopCount)
 +                               ​{...};​
 +      }
 +</​code>​
 +<​code>​
 +  注意:设置会议人数限制只有是加入会议的第一个设置才有效,也就是会议的创建者。
 +</​code>​
  
 +
 +==== 获取会议信息 ====
 +在会议进行中,可以通过getConferenceInfo 方法来查询会议信息,从而可以拿到主播列表,观众人数等信息。
 <code java> <code java>
 /** /**
行 447: 行 676:
  
  
 +==== cdn合流推流 ====
 +多人音视频支持将会议中的音视频流合并成一个流,推送到第三方的cdn直播服务器。 \\
 +整个合流推流过程包括开启cdn推流,更新推流布局,停止推流。 \\
  
-==== 会议角色管理 ==== +** 开启cdn推流**
-会议管理员可以通过下面的接口来更改会议中成员的角色或者在会议中进行踢人。 +
-其他成员可以通过会议属性或者IM 消息等方式来跟管理员申请。+
  
 +会议的创建者在创建会议时使用EMRoomConfig的接口,可以决定是否开启cdn推流,推流配置EMLiveConfig是EMRoomConfig的一个参数,可设置cdn推流的相关信息,然后调用 创建会议接口,可以开启cdn推流(注意:​只有会议创建者才能开启cdn推流,如果会议已经创建好,其他人再调用开启cdn推流无效)。
 +开启过程如下:
 +<code java>
 +  EMCDNCanvas canvas = new EMCDNCanvas(ConferenceInfo.CanvasWidth,​ ConferenceInfo.CanvasHeight,​ 0,​30,​900,"​H264"​);​
 + ​String url = PreferenceManager.getInstance().getCDNUrl();​
 + ​EMLiveConfig liveConfig = new EMLiveConfig(url,​ canvas);
 + ​roomConfig.setLiveConfig(liveConfig);​
 + ​EMClient.getInstance().conferenceManager().joinRoom(currentRoomname,​ currentPassword,​ conferenceRole,​roomConfig,​
 +                                                     new EMValueCallBack<​EMConference>​() ​                                                   ​
 +</​code> ​
 +
 +当EMCDNCanvas设置的width、height为0时,cdn推流为**纯音频推流**
 +
 +推流成功后,可以在EMConferenceManager中 getLiveCfgs() 方法可以获取到liveId,​liveCfgs存储了所有的推流信息,liveCfgs为一个Map,​key为liveId,​value为对应的推流Url。
 +                                                    ​
 +EMLiveConfig可设置的参数如下:
 +
 +<code java>
 +/**
 + ​* ​ \~chinese
 + ​* ​ CDN 画布设置,创建会议时使用
 + ​* ​ width (画布 宽)
 + ​* ​ height(画布 高)
 + * bgclr (画布 背景色 ,格式为 RGB 定义下的 Hex 值,不要带 # 号,
 + *        如 0xFFB6C1 表示浅粉色。默认0x000000,黑色)
 + ​* ​ fps (推流帧率,可设置范围10-30 )
 + ​* ​ kpbs (推流码率,单位kbps,width和height较大时,码率需要提高,可设置范围1-5000)
 + ​* ​ codec (推流编码格式,目前只支持"​H264"​)
 + *
 + ​* ​ \~english
 + ​* ​ The cdn canvas config
 + ​* ​ width (Canvas width)
 + ​* ​ height(Canvas height)
 + ​* ​ bgclr (Canvas background color, Hex value defined by RGB, no #,
 + ​* ​        such as 0xFFB6C1 means light pink.Default 0x000000, black))
 + ​* ​ fps (The fps of cdn live,valid valuei is 10-30)
 + ​* ​ kpbs (The birateBps of cdn live,the unit is kbps,valid value is 1-5000 )
 + ​* ​ codec (The codec of cdn live,now only support H264)
 + */
 +public class EMCDNCanvas {
 +    private ​ int  width = -1;
 +    private ​ int  height = -1;
 +    private ​ int  bgclr = 0;
 +    private ​ int  fps = -1;
 +    private ​ int  kpbs = -1;
 +    private ​ String ​ codec = null;
 +
 +    public EMCDNCanvas(){
 +
 +    }
 +    public EMCDNCanvas(int width, int height, int bgclr,int fps,int kpbs,String codec){
 +        this.width = width;
 +        this.height = height;
 +        this.bgclr = bgclr;
 +        this.fps = fps;
 +        this.kpbs = kpbs;
 +        this.codec ​ = codec;
 +    } 
 +}
 +
 +/**
 + ​\~chinese
 + ​* ​ 音频录制的配置信息
 + ​* ​ bps        音频比特率,类型为 EMAudioBpsType枚举;
 + ​* ​ channels ​  ​音频通道数,​可选(1,​2),类型为ChannelsType枚举;
 + ​* ​ samples ​   音频采样率,类型为 EMAudioSamplesType枚举;
 + *
 + *
 + ​* ​ \~english
 + ​* ​ The config of audio record.
 + ​* ​ bps       audio bit rate,  type is EMAudioBpsType enum;
 + ​* ​ channels ​ number of audio channels, optional (1,2), type is ChannelsType enum;
 + ​* ​ samples ​  audio sampling rate, type is EMAudioSamplesType enum;
 + *
 + */
 +
 +public class EMAudioConfig {
 +    EMAudioBpsType bps = EMAudioBpsType.BPS_64K;​
 +    ChannelsType channels = ChannelsType.DUAL;​
 +    EMAudioSamplesType samples = EMAudioSamplesType.SAMPLES_16K;​
 +
 +    public enum ChannelsType{
 +        SINGLE, ​ //单声道
 +        DUAL    //双声道
 +    }
 +
 +    public EMAudioConfig(EMAudioBpsType bps, ChannelsType channels,​EMAudioSamplesType samples){
 +        this.bps = bps;
 +        this.channels = channels;
 +        this.samples = samples;
 +    }
 + }
 +
 +/**
 + ​* ​ \~Chinese
 + ​* ​ CDN推流设置
 + ​* ​ cdnurl ​    ​cdn推流地址
 + ​* ​ cdnCanvas ​ 画布设置 (cdnCanvas可以缺省)
 + ​* ​ record ​    ​设置是否录制推往CDN的流
 + ​* ​ audioConfig ​ 设置音频录制的配置信息
 + ​* ​ liveLayoutStyle 设置CDN推流使用的画布类型
 + *
 + ​* ​ \~English
 + ​* ​ The CDN push stream config
 + ​* ​ cdnurl ​     cdn push stream address
 + ​* ​ cdnCanvas ​  ​canvas settings (cdnCanvas can be default)
 + ​* ​ record ​     whether to enable the recording push network CDN to remain
 + ​* ​ audioConfig ​  set the audio recording configuration information
 + ​* ​ liveLayoutStyle ​ sets the canvas type used by the CDN push stream
 + */
 +
 +public class EMLiveConfig {
 +    private String cdnurl;
 +    private EMCDNCanvas cdnCanvas = null;
 +    private boolean record = false;
 +    private EMAudioConfig audioConfig = null;
 +
 +    private EMLiveLayoutStyle liveLayoutStyle = EMLiveLayoutStyle.GRID;​
 +
 +    public EMLiveConfig(){
 +
 +    }
 + }
 +</​code>​
 +=== **获取cdn推流LiveId** ===
 +推流成功后,可以在EMConferenceManager中 getLiveCfgs() 方法可以获取到liveId,​liveCfgs存储了所有的推流信息,liveCfgs为一个Map,​key为liveId,​value为对应的推流Url。
 +<code java>
 + /**
 +     * CDN推流liveID (key为推流liveID,​value为推流URL)
 +     * @return
 +     */
 +    public Map<​String,​ String> getLiveCfgs();​
 +</​code>​
 +
 +**更新布局**
 +
 +当用户调用更新布局接口后,cdn推流方式将强制变成CUSTOM模式,所有流的位置信息都由用户自己定义。
 +更新布局的接口如下:
 <code java> <code java>
  /**  /**
      * \~chinese      * \~chinese
 +     * CDN 推流更新布局(只有管理员可操作)
      * 用户角色:​ Admin > Talker > Audience      * 用户角色:​ Admin > Talker > Audience
-     ​* ​当角色升级时,​用户需要给管理员发送申请,​管理通过该接口改变用户接口. +     ​* ​注意: ​ 更新布局只允许Admin 去操作
-     * 当角色降级时,​用户直接调用该接口即可.+
      *      *
-     * @param ​confId ​  ​会议id +     * @param ​liveId ​ 推流CDNliveId 
-     * @param member ​  ​{@link EMConferenceMember},​目前使用memberName进行操作 +     * @param ​regions EMCanvasRegion布局对象列表
-     * @param ​toRole ​  ​目标角色,{@link EMConferenceRole}+
      * @param callback 结果回调      * @param callback 结果回调
      *      *
 +     * \~english
 +     * CDN pushes to update the layout
 +     *
 +     * @param liveId ​ Push the liveId of CDN
 +     * @param regions Layout EMCanvasRegion list
 +     * @param callback Result callback
      */      */
-    public void grantRole(final String ​confIdfinal EMConferenceMember member, final EMConferenceRole toRole, final EMValueCallBack<​String>​ callback)+    public void updateLiveLayout(String ​liveId ​,List<​EMLiveRegion>​ regions ​, final EMValueCallBack<​String>​ callback){}
 </​code>​ </​code>​
 +EMLiveRegion的结构如下:
 +<code java>
 +/**
 + ​* ​ \~Chinese
 + ​* ​ 视频流在画布宽高及显示位置等参数
 + ​* ​ x         ​在画布横坐标位置
 + ​* ​ y         ​在画布纵坐标位置
 + ​* ​ width     ​视频流宽度(64~1280)
 + ​* ​ height ​   视频流高度(64~1280)
 + ​* ​ zorder ​   视频流zorder层位置(最小值为 0(默认值),表示该区域图像位于最下层
 + ​* ​                             最大值为 100,表示该区域图像位于最上层)
 + ​* ​ style     ​视频流显示方式(fit模式或者fill模式)
 + ​* ​ streamId ​ 视频流ID
 + *
 + *
 + ​* ​ \~English
 + ​* ​ Video streaming in the canvas width and height and display location and other parameters
 + ​* ​ x         On the horizontal position of the canvas
 + ​* ​ y         On the vertical position of the canvas
 + ​* ​ width     Video stream width
 + ​* ​ height ​   Video stream height
 + ​* ​ zorder ​   Video stream zorder layer location
 + ​* ​ style     Video stream display mode (fit mode or fill mode)
 + ​* ​ streamId ​ The video stream ID
 + */
 +public class EMLiveRegion {
 +    private int x;
 +    private int y;
 +    private int width;
 +    private int height;
 +    private int zorder;
 +    private EMRegionStyle style;
 +    private String streamId;
  
-==== 管理员踢人操作 ==== +    public EMLiveRegion(){} 
-管理员可以调用kickMember api强制将成员从会议中移除。+    public EMLiveRegion(int x,int y, int width, int height, int zorder, EMRegionStyle style, String streamId){ 
 +        this.x ​x; 
 +        this.y ​y; 
 +        this.width ​width; 
 +        this.height ​height; 
 +        this.zorder ​zorder; 
 +        this.style ​style; 
 +        this.streamId ​streamId; 
 +    }   
 +    public enum EMRegionStyle { 
 +        FIT, // fit模式 
 +        FILL // fill模式 
 +    } 
 +} 
 +</​code>​
  
-    注意只有管理员角色可以调用踢人接口,可以在会议中踢走主播,被踢的主播在EMConferenceListener#​onPassiveLeave回调里收到 error为 -412,​message为 reason-beenkicked,收到这个以后调用 EMClient.getInstance().conferenceManager().exitConference() 主动退出会议。 +使用方法如下
-    ​+
 <code java> <code java>
-  ​/**+List<​EMLiveRegion>​ regionsList = new LinkedList<>​();​ 
 +EMLiveRegion region = new EMLiveRegion();​ 
 +region.setStreamId(streamInfo.getStreamId()); ​           
 +region.setStyle(EMLiveRegion.EMRegionStyle.FILL);​ 
 +region.setX(80);​ 
 +region.setY(60);​ 
 +region.setWidth(320);​ 
 +region.setHeight(640);​ 
 +region.setZorder(1);​ 
 +regionsList.add(region);​ 
 +Map<​String,​ String> livecfgs = EMClient.getInstance().conferenceManager().getLiveCfgs();​ 
 +String liveId = null;       
 +Iterator<​String>​ iter = livecfgs.keySet().iterator();​ 
 +while(iter.hasNext()) { 
 +       ​liveId = iter.next();​ 
 +       ​break;​ 
 +
 +EMClient.getInstance().conferenceManager().updateLiveLayout(liveId,​regionsList,​new EMValueCallBack<​String>​();​ 
 +</​code>​ 
 + 
 +**多路推流** 
 + 
 +多人音视频支持加入会议后,增加一路推流,只有管理员权限可进行次操作。增加一路推流的api方法如下: 
 + 
 +<​code>​ 
 +/**
      * \~chinese      * \~chinese
-     ​* ​踢走会议中存在的主播 +     ​* ​增加CDN多路推流 
-     * 用户角色:​ Admin > Talker > Audience +     * @param ​liveConfig ​ CDN推流布局配置
-     * 注意: 踢人只允许Admin 去操作,Admin 不能踢自己 +
-     * +
-     * @param confId ​  ​会议id +
-     * @param ​members ​ 移除的主播列表+
      * @param callback 结果回调      * @param callback 结果回调
      *      *
      * \~english      * \~english
-     ​* ​Remove talkers from the Conference+     ​* ​Add CDN multiplex push stream 
 +     * @param liveConfig ​ CDN push stream layout configuration 
 +     * @param callback Result callback 
 +     */ 
 +    public void addLiveStream(EMLiveConfig liveConfig , final EMValueCallBack<​String>​ callback){} 
 +</​code>​ 
 + 
 +**自定义录制布局** 
 + 
 +在推流的EMLiveConfig设置里,设record为true,可以开启自定义录制,开启后会把推流到cdn的音视频按照推流布局录制下来。如果推流时未开启,也可以在推流后进行开启/​停止自定义录制布局操作。开启/​停止自定义录制布局的api如下: 
 +<​code>​ 
 +/** 
 +     * \~chinese 
 +     * 开启或停止录制CDN的流 
 +     * @param liveId ​ CDN推流 liveID 
 +     * @param enable ​ 是否开启录制,true为开启 false为停止 
 +     * @param callback 结果回调
      *      *
-     * @param ​confId ​  ​Conference id +     * \~english 
-     * @param ​members ​ Removed list of talkers+     * Start or stop recording CDN streams 
 +     * @param ​liveId ​ CDN liveID push stream 
 +     * @param ​enable ​ Whether recording is on or off, true means false or stop
      * @param callback Result callback      * @param callback Result callback
 +     *
      */      */
-    public void kickMember(final String ​confIdfinal List<​String>​ members, final EMValueCallBack<​String>​ callback)+    public void enableRecordLivestream(String ​liveIdboolean enable, final EMValueCallBack<​String>​ callback){}
 </​code>​ </​code>​
  
-调用踢人接口,会议中被踢的主播收到回调 ''​EMConferenceListener#​onPassiveLeave(final int error, final String message)''​+**停止推流**
  
-==== 全体静/​解除全体静音 ==== +多人视频支持停止向某一个地址推流停止推流接口如下: 
-管理员可以对会议进行全体静音/​解除全体静音设置,设置后,会议中主播都将处于静音状态新加入的主播也将自动处于静音状态。只有管理员可以调用此接口。 +<​code>​
-接口API如下: +
-<​code ​java>+
  /**  /**
      * \~chinese      * \~chinese
-     ​* ​全体静音 取消全体静音 +     ​* ​停止CDN推流 
-     * 用户角色:​ Admin > Talker > Audience +     * @param ​liveId ​ 推流CDN的liveId
-     * 注意: ​ 全体静音只允许Admin 去操作 +
-     * +
-     * @param confId ​  ​会议id +
-     * @param ​mute  是否设置静音(ture 为设置全体静音 false 为取消全体静音)+
      * @param callback 结果回调      * @param callback 结果回调
      *      *
      * \~english      * \~english
-     ​* ​All mute cancel all mute+     *@param liveId ​ Push the liveId of CDN 
 +     ​*@param callback Result callback 
 +     * Stop the CDN push
      *      *
-     * @param confId ​  ​Conference id 
-     * @param mute  Whether to set mute (true to set all mute , false to cancel all mute) 
      * @param callback Result callback      * @param callback Result callback
      */      */
-    public void muteAll(final String ​confId ,final boolean mute, final EMValueCallBack<​String>​ callback);+    public void stopLiveStream(String ​liveId, final EMValueCallBack<​String>​ callback)
 </​code>​ </​code>​
  
-管理员调用此接口后,会议的主播将收到全静音状态回调,回调函数如下 +==== 云端录制 ==== 
-<​code ​java+通过带EMRoomConfig参数的joinRoom接口加入会议的时候 可以通过 \\ 
-/**+isRecord(是否支持录制)和isMergeRecord(是否支持和流)这两个参数 \\ 
 +可以设置是否开始云端录制和合流,如下所示: \\  
 +<​code>​  
 +    /** 
 +     * \~chinese 
 +     * 加入多人音视频加议房间 
 +     * @param room        会议房间名 
 +     * @param password ​   会议房间密码 
 +     * @param roletype ​   当前用户加入时指定角色 (EMConferenceRole类型) 
 +     * @param param       ​设置会议参数 (EMRoomConfig类型) 
 +     * @param callback ​   回调函数 
 +     */ 
 +    public void joinRoom(final String room ,final String password,​final EMConferenceRole roletype , 
 +                         final EMRoomConfig param, final EMValueCallBack<​EMConference>​ callback) 
 +      
 +   ​有关EMRoomConfig定义如下: 
 +   /** 
 +    * 会议房间属性类 
 +    * confrTyp ​  ​会议类型 
 +    * isSupportVxmini ​ 是否支持小程序 
 +    * isRecord ​   是否支持录制 
 +    * isMergeRecord ​ 是否支持和流 
 +    * nickName ​ 用户昵称 
 +    * ext     ​用户头像 
 +    * maxTalkerCount ​  ​房间最多主播 
 +    * maxVideoCount ​   房间最多视频流媒 
 +    * maxAudienceCount 房间最多观众 
 +    * maxPubDesktopCount ​ 房间最多共享桌面流 
 +  */ 
 +  public class EMRoomConfig { 
 +       ​EMConferenceManager.EMConferenceType confrTyp = EMConferenceManager.EMConferenceType.SmallCommunication;​ 
 +        private boolean isSupportMiniProgram = false; 
 +        private boolean isRecord = false; 
 +        private boolean isMergeRecord = false; 
 +        private String ​ nickName = null; 
 +        private String ​ ext = null; 
 +        private int  maxTalkerCount = -1; 
 +        private int  maxVideoCount = -1; 
 +        private int  maxAudienceCount = -1; 
 +        private int  maxPubDesktopCount = -1; 
 +        private EMLiveConfig liveConfig = null; 
 +            
 +       ​public EMRoomConfig(EMConferenceManager.EMConferenceType confrTyp, 
 +                               ​boolean ​ isSupportMiniProgram,​boolean isRecord, 
 +                               ​boolean ​ isMergeRecord , String ​ nickName, 
 +                               ​String ext, int maxTalkerCount,​ 
 +                               int maxVideoCount,​int maxAudienceCount,​ 
 +                               int maxPubDesktopCount) 
 +                               ​{...};​ 
 +      } 
 +</​code>​ 
 +<​code>​ 
 +  注意:设置会议人数限制只有是加入会议第一个设置才有效也就是会议的创建者。 
 +</​code>​ 
 + 
 +==== 设置昵称 头像 ==== 
 +通过带EMRoomConfig参数的joinRoom接口加入会议的时候 可以通过 \\ 
 +nickName(昵称)ext(可设置扩展字段比如 用户头像等)这两个参数 \\ 
 +可以设置昵称和头像等扩展字段,如下所示: \\  
 +<​code>​  
 +    /** 
 +     * \~chinese 
 +     * 加入多人音视频加议房间 
 +     * @param room        会议房间名 
 +     * @param password ​   会议房间密码 
 +     * @param roletype ​   当前用户加入时指定角色 (EMConferenceRole类型) 
 +     * @param param       ​设置会议参数 (EMRoomConfig类型) 
 +     * @param callback ​   ​回调函数 
 +     */ 
 +    public void joinRoom(final String room ,final String password,​final EMConferenceRole roletype , 
 +                         final EMRoomConfig param, final EMValueCallBack<​EMConference>​ callback) 
 +      
 +   ​有关EMRoomConfig定义如下: 
 +   /** 
 +    * 会议房间属性类 
 +    * confrTyp ​  ​会议类型 
 +    * isSupportVxmini ​ 是否支持小程序 
 +    * isRecord ​   是否支持录制 
 +    * isMergeRecord ​ 是否支持和流 
 +    * nickName ​ 用户昵称 
 +    * ext     ​用户头像 
 +    * maxTalkerCount ​  ​房间最多主播 
 +    * maxVideoCount ​   房间最多视频流媒体 
 +    * maxAudienceCount 房间最多观众 
 +    * maxPubDesktopCount ​ 房间最多共享桌面流 
 +  */ 
 +  public class EMRoomConfig { 
 +       ​EMConferenceManager.EMConferenceType confrTyp = EMConferenceManager.EMConferenceType.SmallCommunication;​ 
 +        private boolean isSupportMiniProgram = false; 
 +        private boolean isRecord = false; 
 +        private boolean isMergeRecord = false; 
 +        private String ​ nickName = null; 
 +        private String ​ ext = null; 
 +        private int  maxTalkerCount = -1; 
 +        private int  maxVideoCount = -1; 
 +        private int  maxAudienceCount = -1; 
 +        private int  maxPubDesktopCount = -1; 
 +        private EMLiveConfig liveConfig = null; 
 +            
 +       ​public EMRoomConfig(EMConferenceManager.EMConferenceType confrTyp, 
 +                               ​boolean ​ isSupportMiniProgram,​boolean isRecord, 
 +                               ​boolean ​ isMergeRecord , String ​ nickName, 
 +                               ​String ext, int maxTalkerCount,​ 
 +                               int maxVideoCount,​int maxAudienceCount,​ 
 +                               int maxPubDesktopCount) 
 +                               ​{...};​ 
 +      } 
 +</​code>​ 
 +==== 会议属性 ==== 
 +会议属性是会议的状态信息,由一组(key,value)组成 // 
 +会议中的所有角色成员(管理员、主播、观众)都可以设置/​删除会议频道属性 ​ // 
 +设置的会议属性会通知给会议中的所有人 ​ // 
 +设置会议属性的api方法如下 ​ // 
 +<​code>​ 
 +  /**
      * \~chinese      * \~chinese
-     ​* ​被全体静音 取消全体静音通知+     ​* ​设置频道属性,​该会议中的所有人(包括自己)都会收到{@link EMConferenceListener#​onAttributesUpdated}回调. 
 +     * 该方法需要在加入会议后调用. 
 +     * 
 +     * @param key 
 +     * @param value 
 +     * @param callback
      *      *
      * \~english      * \~english
-     ​* ​Be or cancel all muted  notification+     ​* ​Set conference attribute,​All members in this conference(include myself) will receive a callback 
 +     * in {@link EMConferenceListener#​onAttributesUpdated}. 
 +     * this method can only be used after join a conference. 
 +     * 
 +     * @param key 
 +     * @param value 
 +     * @param callback
      */      */
-    ​default ​void onMuteAll(boolean mute){};+    ​public ​void setConferenceAttribute(@NonNull String key, @NonNull String value, final EMValueCallBack<​Void>​ callback) {}
 </​code>​ </​code>​
  
-==== 指定成员静音/​解静音 ==== +除会议属性api方法如下: 
-管理员可以对会议指定成员进行静音/​解除静音设置,被指定成员可以是主播也可以是管理员。设置后,被指定成员将静音/​解除静音。只有管理员可以调用此接口。 +<​code>​
-接口API如下: +
-<​code ​java>+
  /**  /**
      * \~chinese      * \~chinese
-     ​* ​发送静音命令 +     ​* ​删除频道属性,​该会议中的所有人(包括自己)都会收到{@link EMConferenceListener#​onAttributesUpdated}回调. 
-     ​* ​注意: 只允许Admin 去操作+     ​* ​该方法需要在加入会议后调用.
      *      *
-     * @param ​memberId ​ memberId ​ 被静音的成员的memberId;+     * @param ​key 
 +     * @param callback
      *      *
      * \~english      * \~english
-     ​* ​Send the mute command+     ​* ​Delete conference attribute,​All members in this conference(include myself) will receive a callback 
 +     * in {@link EMConferenceListener#​onAttributesUpdated}. 
 +     * this method can only be used after join a conference.
      *      *
-     * @param ​memberId ​ MemberId of the member being mute (only admin can sends mute command); +     * @param ​key 
-     * +     ​* ​@param callback 
-     */ +     */ 
-    public void muteMember(String ​memberId);+    public void deleteConferenceAttribute(@NonNull ​String ​key, final EMValueCallBack<​Void>​ callback{}
 </​code>​ </​code>​
  
 +当会议属性信息改变时,会议中的在EMConferenceListener中的 onAttributesUpdated 回调方法收到通知,如下:
 +<​code>​
 +     ​public void onAttributesUpdated(EMConferenceAttribute[] attributes) {}
 +</​code>​
 +每一个EMConferenceAttribute包括了会议属性中的key,value,以及本次修改的action,action包括ADD、UPDATE、DELETE
 +  ​
 +==== 海外代理 ====
 +1v1通话支持不同集群区域的人员通话使用代理,减小延迟; \\
 +使用多集群代理需要音视频后台配置IP及端口的映射文件rtcconfig.json,​并禁用相关appkey的直连,​ \\
 +启用多集群代理功能需要配置如下方法:\\
 +<​code>​
 +  EMOptions options = new EMOptions();​
 +  //​开启代理
 +  options.setUseRtcConfig(true);​
 +</​code>​
 +
 +==== 私有部署 ====
 + ​私有部署设置方法参见[[http://​docs-im.easemob.com/​im/​android/​other/​privatecloud#​%E7%A7%81%E6%9C%89%E4%BA%91sdk%E9%9B%86%E6%88%90%E9%85%8D%E7%BD%AE|私有云sdk集成配置]]。 \\
 +
 +===== 音视频管理 =====
 +
 +==== 设置视频流参数 ====
 +如果用户想对发布的音频或者视频流的参数进行调整,可以进行一些设置 \\
 +用EMStreamParam中的参数进行设置,​只发布音频流,可以设置关闭视频 \\
 +设置视频流的分辨率 ​ 最小码率 最大码率 最大音频码率等,如下所示:\\
 +<​code>​
 +//​设置分辨率
 +   ​normalParam.setVideoOff(true);​
 + ​normalParam.setVideoHeight(720); ​  
 + ​normalParam.setVideoWidth(1280);​
 +
 +//​设置最大 最小码率
 + ​normalParam.setMinVideoKbps(200);​
 + ​normalParam.setMaxVideoKbps(300);​
 + ​normalParam.setMaxAudioKbps(300);​
 +</​code>​
 +**设置流畅度或者清晰度优先 **
 +
 +可以通过以下方法设置视频流畅度优先还是清晰度优先(true 为清晰度优先,false为流畅度优先)。 \\
 +<​code>​
 +  EMClient.getInstance().callManager().getCallOptions().setClarityFirst(true);​
 +</​code>​
 +
 +==== 停止发布流 ====
 +成员A可以调用unpublish接口取消自己已经发布的数据流,操作成功后,会议中的其他成员会收到回调''​EMConferenceListener#​onStreamRemoved(EMConferenceStream stream)''​,将对应的数据流信息移除
 <code java> <code java>
-  ​/**+@Override 
 +public void onStreamRemoved(final EMConferenceStream stream) { 
 +    runOnUiThread(new Runnable() { 
 +        @Override 
 +        public void run() { 
 +            Toast.makeText(activity,​ stream.getUsername() + " stream removed!",​ Toast.LENGTH_SHORT).show();​ 
 +            if (streamList.contains(stream)) { 
 +                removeConferenceView(stream);​ 
 +            } 
 +        } 
 +    }); 
 +
 +</​code>​ 
 + 
 +==== 停止订阅流 ==== 
 +成员B如果不想再看成员A的音视频,可以调用SDK接口unsubscribe 
 +<code java> 
 +EMClient.getInstance().conferenceManager().unsubscribe(emConferenceStream,​ new EMValueCallBack<​String>​() { 
 +    @Override 
 +    public void onSuccess(String value) { 
 +    } 
 +     
 +    @Override 
 +    public void onError(int error, String errorMsg) { 
 +     
 +    } 
 +}); 
 +</​code>​ 
 + 
 + 
 +==== 通话中音视频控制 ==== 
 +会议过程中可以做一些音视频进行控制,比如开启关闭音频 视频 切换前后摄像头 \\ 
 +具体API如下所示: \\ 
 +<​code>​ 
 + // 开启音频传输 ​  
 + ​EMClient.getInstance().conferenceManager().openVoiceTransfer();​  
 + // 关闭音频传输 ​  
 + ​EMClient.getInstance().conferenceManager().closeVoiceTransfer(); ​  
 + // 开启视频传输 ​  
 + ​EMClient.getInstance().conferenceManager().openVideoTransfer(); ​  
 + // 关闭视频传输  
 + ​EMClient.getInstance().conferenceManager().closeVideoTransfer();​ 
 +</​code>​ 
 +  
 + ​**PS:以上这四个方法都是修改 stream,群里其他成员都会收到 EMConferenceListener.onStreamUpdate()回调** 
 +  
 +<​code>​ 
 + // 切换摄像头  
 + ​EMClient.getInstance().conferenceManager().switchCamera(); ​  
 + // 设置展示本地画面的 view   
 + ​EMClient.getInstance().conferenceManager().setLocalSurfaceView(localView);​  
 + // 更新展示本地画面 view  
 + ​EMClient.getInstance().conferenceManager().updateLocalSurfaceView(localView); ​  
 + // 更新展示远端画面的 view   
 + ​EMClient.getInstance().conferenceManager().updateRemoteSurfaceView(streamId,​ remoteView);​  
 +</​code>​ 
 + 
 +==== 音视频首帧回调 ==== 
 +当成员发布流成功,发送第一帧音视频数据时,会触发EMConferenceListener的以下回调函数 \\ 
 +<​code>​ 
 +/**
      * \~chinese      * \~chinese
-     ​* ​发送解除静音命令 +     ​* ​Pub 首帧回调 
-     ​* ​注意: 只允许Admin 去操作+     ​* ​streamId ​ 流ID 
 +     * frameType ​   the first frame callback type of the stream
      *      *
-     ​* ​@param memberId ​ 被取消静音的成员的memberId;+     ​* ​\~english 
 +     * Pub first frame callback 
 +     * streamId ​  ​streamId 
 +     * frameType ​   the first frame callback type of the stream 
 +     */ 
 +    public void onFirstFrameSent(String stremId,​StreamFrameType frameType){} 
 +</​code>​ 
 +成员订阅流成功,收到第一帧音视频数据时,会触发EMConferenceListener以下回调函数 \\ 
 +<​code>​ 
 + /** 
 +     * \~chinese 
 +     * Sub 首帧回调 
 +     * streamId ​ 流ID 
 +     * frameType ​   the first frame callback type of the stream
      *      *
      * \~english      * \~english
-     ​* ​Send the unmute command+     ​* ​Sub first frame callback 
 +     * streamId ​ streamId 
 +     * state    ​the first frame callback type of the stream
      *      *
-     * @param ​memberId ​ Send the memberId of the member whose unmute command is cancelled;+     */ 
 +    public void onFirstFrameRecived(String streamId,​StreamFrameType frameType){} 
 +</​code>​ 
 + 
 + 
 +==== 音视频无数据回调 ==== 
 +当会议中的成员A因断网或异常退出,而无音视频数据上传时,订阅该流的其他成员会收到EMConferenceListener 中的以下回调通知。 
 +<​code>​ 
 +/** 
 +     * \~chinese 
 +     * 订阅流的数据状态回调 
 +     * @param ​streamId ​ 订阅的流ID 
 +     * @param state  流的视频或音频数据状态
      *      *
 +     * \~english
 +     * A data state callback for a subscription stream
 +     * @param streamId ​ StreamId
 +     * @param state  Stream video or audio data status
 +     */
 +    default void onStreamStateUpdated(String streamId,​StreamState state){};
 +</​code>​
 +
 +该功能需要会议中开启质量统计
 +<​code>​
 + /**
 +     * \~chinese
 +     * 启用统计
      *      *
-     * */ +     ​* ​@param enable 是否启用统计 
-    public void unmuteMember(String memberId);+     * 
 +     * \~english 
 +     * enable statistics 
 +     * @params enable enable statistics 
 +     */ 
 +    public void enableStatistics(boolean enable{};
 </​code>​ </​code>​
  
  
-管理员调用此接口后,被指定的成员将收静音状态的回调,回调函数如下 + 
-<​code ​java>+==== 弱网监控 ==== 
 +在会议中可以实时获取自己的网络状态,通过EMConferenceListener中以下回调通知 \\ 
 +<​code>​ 
 +  @Override 
 +  public void onConferenceState(final ConferenceState state) { 
 +        runOnUiThread(new Runnable() { 
 +            @Override 
 +            public void run() { 
 +            } 
 +        }); 
 +    } 
 +    //​有以下状态 
 +     ​STATE_NORMAL, ​  // 正常状态 
 +     ​STATE_DISCONNECTION, ​  // 连接断开 
 +     ​STATE_RECONNECTION, ​   // 重新连接 
 +     ​STATE_POOR_QUALITY, ​   // 通话质量差 
 +</​code>​ 
 + 
 +==== 通话质量 ==== 
 +通过过程中可以实时获取到自己活着订阅流的通话信息,比如视频宽高 丢包率 帧率等等 \\ 
 +具体信息可参考 [[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_stream_statistics.html|EMStreamStatistics]] 
 +该功能需要会议中开启质量统计,如下面的接口: 
 +<​code>​
  /**  /**
      * \~chinese      * \~chinese
-     ​* ​被静音通知+     ​* ​启用统计 
 +     * 
 +     * @param enable 是否启用统计
      *      *
      * \~english      * \~english
-     ​* ​Be muted notification+     ​* ​enable statistics 
 +     * @params enable enable statistics
      */      */
-    ​default ​void onMute(String adminId, String memId){};+    ​public ​void enableStatistics(boolean enable) {}; 
 +</​code>​ 
 +开启质量统计后,在视频过程中会实时触发EMConferenceListener 中的回调函数 onStreamStatistics ​ \\ 
 +如下所示,从而可以获取到每路流的质量统计信息: 
 +<​code>​ 
 +  @Override 
 +    public void onStreamStatistics(EMStreamStatistics statistics) { 
 +        EMLog.e(TAG, ​ "​Encode Resolution: " + statistics.getLocalEncodedWidth() + " ​ "  
 +        + statistics.getLocalEncodedHeight() + " bps: " +statistics.getLocalVideoActualBps() +  
 +        " ​ FPS: " + statistics.getLocalEncodedFps()); 
 +    }
 </​code>​ </​code>​
  
-<​code ​java+==== 监听谁在说话 ==== 
- /**+多人音视频会议可以实时监听谁在说话,该功能需要开启,启动/​停止控制如下: 
 +<​code>​ 
 + //​开始监听说话者,参数为间隔时间 
 + ​EMClient.getInstance().conferenceManager().startMonitorSpeaker(int interval);​ 
 +  
 + //​停止监听说话者 
 +EMClient.getInstance().conferenceManager().stopMonitorSpeaker();​ 
 +</​code>​ 
 + 
 +有人说话时,会议成员会收到如下回调通知 
 +<​code>​ 
 +  @Override 
 +public void onSpeakers(final List<​String>​ speakers)(){ } 
 +</​code>​ 
 + 
 +==== mute远端音视频流 ==== 
 +在视频会议过程中可以 mute订阅的流的音频或者视频 
 +方法如下所示: 
 +<​code>​ 
 +   ​/**
      * \~chinese      * \~chinese
-     ​* ​被取消静通知+     ​* ​mute远端
      *      *
      * \~english      * \~english
-     ​* ​Be unmuted notification+     ​* ​Mute remote audio 
 +     * 
 +     * @param mute
      */      */
-    ​default ​void onUnMute(String ​adminIdString memId){};+    ​public ​void muteRemoteAudio(String ​streamIdboolean mute) { 
 +        mediaManager.muteRemoteAudio(streamId,​ mute); 
 +    ​} 
 + 
 +    /** 
 +     * ~\chinese 
 +     * mute远端视频 
 +     * 
 +     * \~english 
 +     * Mute remote video 
 +     * 
 +     * @param mute 
 +     */ 
 +    public void muteRemoteVideo(String streamId, boolean mute) { 
 +        mediaManager.muteRemoteVideo(streamId,​ mute); 
 +    }
 </​code>​ </​code>​
  
-==== 观众申请播 ==== +==== 变声/​自定义音频 ==== 
-会议中的观众角色可以向管理员发申请成为主播,管理员可以选择同意或者拒绝观众申请主持人的接口需要管理员的memId,先通过获取会议属性接口获取到管理员的memName然后根据memName以及成员加入的回调中获取到的EMConferenceMember,获取到memId接口如下+开启外边音频输入 \\ 
 + 
 +用户使用自定义音频数据时,需要配置外部输入音频数据的开关, \\ 
 +以及音频采样率,通道数(当前通道数只支持1),开启方式如下(true 为开启,false为不开启)。 \\ 
 +<​code>​ 
 + ​EMClient.getInstance().callManager().getCallOptions().setExternalAudioParam(true,​ 44100,1); 
 +</​code>​ 
 +输入音频数据 \\ 
 + 
 +音频数据采集可参考Demo中的ExternalAudioInputRecord.java类实现, ​ \\ 
 +音频数据的输入必须在会话接通后开始,否则会导致网络阻塞,影响通话质量。 \\ 
 +建议用户将音频数据采集的开始放在会话接通的回调里,及callDidAccept回调中 \\ 
 +<​code>​ 
 +    EMClient.getInstance().conferenceManager().publish(normalParam,​ new EMValueCallBack<​String>​() { 
 +            @Override 
 +            public void onSuccess(String value) { 
 + 
 +                //​如果启动外部音频输入 ,启动音频录制 
 +                if(PreferenceManager.getInstance().isExternalAudioInputResolution()){ 
 +                    ExternalAudioInputRecord.getInstance().startRecording();​ 
 +                } 
 +                .... 
 + ​} ​               
 +</​code>​ 
 +音频采集过程参考Demo中的ExternalAudioInputRecord类实现;​ \\ 
 +音频采集过程开始后,在音频数据采集线程里调用外部输入音频数据接口,具体参考Demo中的实现。 \\ 
 +<​code>​ 
 + ​EMClient.getInstance().conferenceManager().inputExternalAudioData(byteBuffer.array(),​ byteBuffer.capacity());​ 
 +</​code>​ 
 +停止音频输入 \\ 
 + 
 +会话挂断时,停止音频采集及输入过程 ​ \\ 
 +<​code>​ 
 + ​if(PreferenceManager.getInstance().isExternalAudioInputResolution()){ 
 +                     ​ExternalAudioInputRecord.getInstance().stopRecording();​ 
 +  } 
 +</​code>​ 
 + 
 +==== 美颜/​自定义视频 ==== 
 +如果用户需要自己采集特定的数据或者对于数据需要先进行一些处理,可以使用SDK的外部输入数据的方法进行。 \\ 
 +例如如果想要使用美颜等功能,需要用户使用系统的摄像头, \\ 
 +然后启动监听系统设备,获取到数据后进行处理,处理后再调用我们输入数据的api发布出去。 \\ 
 +使用自定义视频接口如下:开启外边视频输入;​ \\ 
 +用户使用自定义视频数据时,需要配置外部输入数据数据的开关(true 为开启,false为不开启)。 \\ 
 +<​code>​ 
 +  EMStreamParam normalParam = new EMStreamParam();​ 
 +normalParam.setUsingExternalSource(true);​ //​设置使用外部视频数据输入 
 +EMClient.getInstance().conferenceManager().publish(normalParam,​ new EMValueCallBack<​String>​() {} 
 +</​code>​ 
 + 
 +输入视频数据 \\ 
 + 
 +然后就是自己获取视频数据,进行美颜等处理,循环调用以下方法输入数据就行了, \\ 
 +这个调用频率就相当于你的帧率,调用间隔可以自己进行控制,一般最大30帧/​秒) \\ 
 +输入视频数据的方法如下: \\ 
 +<​code>​ 
 + /** 
 + * 
 + * 视频数据的格式是摄像头采集的格式即:NV21 420sp 自己手动传入时需要将自己处理的数据转为 yuv 格式输入 
 + */ 
 + ​EMClient.getInstance().conferenceManager().inputExternalVideoData(data,​ width, height, rotate); 
 +</​code>​ 
 +==== 视频水印 ==== 
 +在Android系统可以将图片资源设置为视频流的水印。首先将图片资源转换为Bitmap对象, \\ 
 +然后设置WaterMarkOption中的属性,比如位置,分辨率及距离边缘的margin等。 \\ 
 +<​code>​ 
 + try { 
 +    InputStream in = this.getResources().getAssets().open("​watermark.png"​);​ 
 +    watermarkbitmap = BitmapFactory.decodeStream(in);​ 
 +} catch (Exception e) { 
 +    e.printStackTrace();​ 
 +
 +watermark = new WaterMarkOption(watermarkbitmap,​ 75, 25, WaterMarkPosition.TOP_RIGHT,​ 8, 8); 
 +//​设置水印 
 +EMClient.getInstance().conferenceManager().setWaterMark(watermark);​ 
 +</​code>​ 
 +==== 共享桌面 ==== 
 +在android 5.0以上系统中,可以使用外部输入视频数据的方法,将采集的桌面图像数据发布出去。 
 +要方法如下: 
 + 
 +<code java> 
 + 
 +desktopParam ​new EMStreamParam();​ 
 +desktopParam.setAudioOff(true);​ 
 +desktopParam.setVideoOff(true);​ 
 +desktopParam.setStreamType(EMConferenceStream.StreamType.DESKTOP);​ 
 + 
 +public void publishDesktop() { 
 +        if (Build.VERSION.SDK_INT >Build.VERSION_CODES.LOLLIPOP) { 
 +            desktopParam.setShareView(null);​ 
 +        } else { 
 +            desktopParam.setShareView(activity.getWindow().getDecorView());​ 
 +        } 
 +        EMClient.getInstance().conferenceManager().publish(desktopParam,​ new EMValueCallBack<​String>​() { 
 +            @Override 
 +            public void onSuccess(String value) { 
 +                conference.setPubStreamId(value,​ EMConferenceStream.StreamType.DESKTOP);​ 
 +                startScreenCapture();​ 
 +            } 
 + 
 +            @Override 
 +            public void onError(int error, String errorMsg) { 
 + 
 +            } 
 +        }); 
 +    } 
 +     
 +     
 +    private void startScreenCapture() { 
 +        if (Build.VERSION.SDK_INT >Build.VERSION_CODES.LOLLIPOP) { 
 +            if (ScreenCaptureManager.getInstance().state ​== ScreenCaptureManager.State.IDLE) { 
 +                ScreenCaptureManager.getInstance().init(activity);​ 
 +                ScreenCaptureManager.getInstance().setScreenCaptureCallback(new ScreenCaptureManager.ScreenCaptureCallback() { 
 +                    @Override 
 +                    public void onBitmap(Bitmap bitmap) { 
 +                        EMClient.getInstance().conferenceManager().inputExternalVideoData(bitmap);​ 
 +                    } 
 +                }); 
 +            } 
 +        } 
 +    } 
 +    
 +</​code>​ 
 +具体实现可以参考videocall-android中的ConferenceActivity.java文件 
 +===== 角色管理 ===== 
 + 
 +==== 角色管理 ==== 
 +**申请主播** \\ 
 + 
 +会议中的观众角色可以向管理员发申请成为主播,管理员可以选择同意或者拒绝 ​\\ 
 +观众申请管理员的接口需要管理员的memId,先通过获取会议属性接口获取到管理员的memName ​\\ 
 +然后根据memName以及成员加入的回调中获取到的EMConferenceMember,获取到memId接口如下 ​\\
 <code java> <code java>
 /** /**
行 673: 行 1605:
 </​code>​ </​code>​
  
-==== 主播申请管理员 ​==== +**申请管理员** \\ 
-会议中的主播角色可以向管理员发申请成为管理员,管理员可以选择同意或者拒绝,成为管理员后,各管理员之间的权限是相同的主播申请主持人的接口需要管理员的memId,先通过获取会议属性接口获取到管理员的memName,然后根据memName以及成员加入的回调中获取到的EMConferenceMember,获取到管理员memId。接口如下:+ 
 +会议中的主播角色可以向管理员发申请成为管理员, ​\\ 
 +管理员可以选择同意或者拒绝, ​\\ 
 +成为管理员后,各管理员之间的权限是相同的 ​\\ 
 +主播申请管理员的接口需要管理员的memId, ​\\ 
 +先通过获取会议属性接口获取到管理员的memName, ​\\ 
 +然后根据memName以及成员加入的回调中获取到的EMConferenceMember, ​\\ 
 +获取到管理员memId。接口如下: ​\\
 <code java> <code java>
   /**   /**
行 752: 行 1691:
     public void handleAdminApplication(String memberId, boolean accept);     public void handleAdminApplication(String memberId, boolean accept);
 </​code>​ </​code>​
-===== 用户自定义数据采集及数据处理 ===== 
-如果用户需要自己采集特定的数据或者对于数据需要先进行一些处理,可以使用SDK的外部输入数据的方法进行。 
  
-例如如果想要使用美颜或者变音等功能,需要用户使用系统的摄像头或者麦克风,然后启动监听系统设备,获取到数据后进行处理,处理后再调用我们输入数据的api发布出去。 
  
-外部视频数据的使 +==== 管理员踢人 ==== 
-EMStreamParam将使外部数据打开+管理员可以调kickMember api强制将成员从会议中移除。 \\ 
 +<​code>​ 
 +注意:只有管理员角色可以调用踢人接口,可以会议踢走主播被踢的主播在EMConferenceListener#​onPassiveLeave回调里 
 +收到 ​ error为 -412,​message为 reason-beenkicked 收到这个以后调用 EMClient.getInstance().conferenceManager(). 
 +exitConference()主动退出会议。 
 +</​code>​
 <code java> <code java>
-//​需要初始化camera, 监听camera数据 +  ​/** 
-//​处理数据后使下面接口将数据输入 +     * \~chinese 
-streamParam.setUsingExternalSource(true);​ +     * 踢走会议中存在的主播 
-EMClient.getInstance().conferenceManager().inputExternalVideoData(bitmap);+     ​* ​户角色: Admin > Talker > Audience 
 +     * 注意: 踢人只允许Admin 去操作,Admin 不能踢自己 
 +     * 
 +     * @param confId ​  ​会议id 
 +     * @param members ​ 移除主播列表 
 +     * @param callback 结果回调 
 +     * 
 +     * \~english 
 +     * Remove talkers from the Conference 
 +     * 
 +     * @param confId ​  ​Conference id 
 +     * @param members ​ Removed list of talkers 
 +     * @param callback Result callback 
 +     */ 
 +    public void kickMember(final String confId, final List<​String>​ members, final EMValueCallBack<​String>​ callback)
 </​code>​ </​code>​
  
-外部音频数据的使+踢人接口,会议中被踢的主播收到回调 ​ \\ 
 +<​code>​ 
 +EMConferenceListener#​onPassiveLeave(final int error, final String message) 
 +</​code>​
  
 +==== 全体静音/​管理员指定成员静音 ====
 +管理员可以对会议中的指定成员进行静音/​解除静音设置 \\
 +被指定成员可以是主播也可以是管理员 \\
 +设置后,被指定成员将静音/​解除静音。只有管理员可以调用此接口。 \\
 +接口API如下:
 <code java> <code java>
-        ​//​初始化AudioRecord + /** 
-        try { +     * \~chinese 
-            ​audioRecord = new AudioRecord(audioSource,​ sampleRate, AudioFormat.CHANNEL_IN_MONO,​ +     * 发送静音命令 
-                    ​AudioFormat.ENCODING_PCM_16BIT,​ bufferSizeInBytes);​ +     * 注意: 只允许Admin 去操作 
-        } catch (IllegalArgumentException e) { +     * 
-            ​Log.d(TAG,​ "​AudioRecord ctor error: " + e.getMessage());​ +     * @param memberId ​ memberId ​ 被静音的成员的memberId; 
-            ​releaseAudioResources();​ +     * 
-            ​return -1; +     * \~english 
-        } +     * Send the mute command 
-         +     * 
-        //​在单独的线程中读取数据 +     * @param memberId ​ MemberId of the member being mute (only admin can sends mute command); 
-        int bytesRead = audioRecord.read(byteBuffer, byteBuffer.capacity()); +     * 
-         +     * *
-        //​输入处理后的数据 +    ​public void muteMember(String memberId);
-        int ret =  +
-        EMClient.getInstance().conferenceManager().inputExternalAudioData(byteBuffer.array(),​ byteBuffer.capacity());+
 </​code>​ </​code>​
- 
- ===== 共享桌面 ===== 
-在android 5.0以上系统中,可以使用外部输入视频数据的方法,将采集的桌面图像数据发布出去。 
-主要方法如下: 
  
 <code java> <code java>
 +  /**
 +     * \~chinese
 +     * 发送解除静音命令
 +     * 注意: 只允许Admin 去操作
 +     *
 +     * @param memberId ​ 被取消静音的成员的memberId;​
 +     *
 +     * \~english
 +     * Send the unmute command
 +     *
 +     * @param memberId ​ Send the memberId of the member whose unmute command is cancelled;
 +     *
 +     *
 +     * */
 +    public void unmuteMember(String memberId);
 +</​code>​
  
-desktopParam = new EMStreamParam();​ 
-desktopParam.setAudioOff(true);​ 
-desktopParam.setVideoOff(true);​ 
-desktopParam.setStreamType(EMConferenceStream.StreamType.DESKTOP);​ 
  
-public void publishDesktop() { +管理员调用此接口后,被指定的成员将收到静音状态的回调,回调函数如下 
-        if (Build.VERSION.SDK_INT ​>= Build.VERSION_CODES.LOLLIPOP) { +<code java
-            ​desktopParam.setShareView(null);​ + /** 
-        } else { +     * \~chinese 
-            ​desktopParam.setShareView(activity.getWindow().getDecorView());​ +     * 被静音通知 
-        } +     * 
-        ​EMClient.getInstance().conferenceManager().publish(desktopParam,​ new EMValueCallBack<​String>​() { +     * \~english 
-            ​@Override +     * Be muted notification 
-            ​public ​void onSuccess(String ​value) { +     */ 
-                conference.setPubStreamId(value,​ EMConferenceStream.StreamType.DESKTOP)+    default ​void onMute(String ​adminId, String memId){}
-                ​startScreenCapture();​ +</​code>​
-            }+
  
-            @Override +<code java> 
-            ​public ​void onError(int error, String ​errorMsg) {+ /** 
 +     * \~chinese 
 +     * 被取消静音通知 
 +     * 
 +     * \~english 
 +     * Be unmuted notification 
 +     */ 
 +    default ​void onUnMute(String adminId, String ​memId){}; 
 +</​code>​
  
-            } +==== 本身角色变更回调 ==== 
-        ​});+在会议中如果 申请主播或者管理员成功等角色改变 则触发EMConferenceListener中以下角色变更回调 \\ 
 +回调中参数为自己目前的角色 \\ 
 +<​code>​ 
 +   ​@Override 
 + ​public void onRoleChanged(EMConferenceManager.EMConferenceRole role) { 
 +        ​EMLog.i(TAG,​ "​onRoleChanged,​ role: " + role); 
 + } 
 +</​code>​  
 +==== 管理员变更回调 ==== 
 +在会议中如果其他人新增管理员或者移除管理员则会触发 EMConferenceListener中以下回调函数 \\ 
 +<​code>​ 
 +   /** 
 +     * 管理员增加回调 
 +     * 
 +     * @param streamId 
 +     */ 
 +    @Override 
 +    public void onAdminAdded(String streamId){ 
 +       ...
     }     }
     ​     ​
-     +    ​/** 
-    ​private void startScreenCapture() { +     * 管理员移除回调 
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { +     * 
-            if (ScreenCaptureManager.getInstance().state == ScreenCaptureManager.State.IDLE) { +     * @param streamId 
-                ​ScreenCaptureManager.getInstance().init(activity);​ +     */ 
-                ​ScreenCaptureManager.getInstance().setScreenCaptureCallback(new ScreenCaptureManager.ScreenCaptureCallback() { +    @Override 
-                    ​@Override +    public void onAdminRemoved(String streamId) { 
-                    public void onBitmap(Bitmap bitmap) { +     ​...
-                        ​EMClient.getInstance().conferenceManager().inputExternalVideoData(bitmap);​ +
-                    } +
-                }); +
-            } +
-        }+
     }     }
-    
 </​code>​ </​code>​
  
-具体实现可以参考IM demo中的ConferenceActivity.java文件 
  
-===== 设置视频流水印 ===== +====== 客户端api ====== 
-在Android系统可以将图片资源设置为视频流的水印。首先将片资源转为Bitmap对象,然后设置WaterMarkOption中的属性如位及距离边缘margin+多人视频会议的API包括以下接口 \\ 
 +  * EMConferenceManager ​   会议管理单例类 ​ \\ 
 +  * EMConference ​  ​会议实体类 ​ \\ 
 +  * EMConferenceListener ​  ​会议监听类 ​ \\ 
 +  * EMConferenceMember ​  ​会议人员实体类 ​ \\ 
 +  * EMConferenceStream ​  ​会议流实体类 ​ \\ 
 +  * EMStreamParam ​   会议流参数类 ​ \\ 
 +  * EMStreamStatistics ​  ​音视频通话统计信息实体类 ​ \\ 
 +  * EMConferenceAttribute ​  ​会议属性实体类 ​ \\ 
 +  * EMRoomConfig ​   会议房间属性类\\ 
 +  * EMLiveConfig ​   CDN推流设置类  \\ 
 +  * EMAudioConfig ​  ​音频录制的配置信息 ​ \\ 
 +  * EMCDNCanvas ​   CDN画布设置类 ​ \\ 
 +  * EMLiveRegion ​  视频流在画布宽高及显示位置等参数类 ​ \\ 
 + 
 +== EMConferenceManager ​  == 
 +^方法 ^描述 ^ 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_conference_manager.html#​a8940f54febf2086ccd978d75980c7763|joinRoom]] ​ |加入会议房间 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_conference_manager.html#​a420a13e4bc932f7ee98502ee40168d50|createAndJoinConference]] ​ |创建并加入会议 ​ | 
 +|isCreator|是否为会议创建者 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_conference_manager.html#​a62bb92e3ae75f9c55a5da4b63471c34b|addConferenceListener ]]  |增加会议监听 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_conference_manager.html#​a254acf2c8ea56054157baa4bfacb811d|removeConferenceListener]] ​ |移除会议监听 | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_conference_manager.html#​a339fc678b9a22b05e6cd441a6f2c1128|startAudioMixing]] ​ |开启本地伴音功能 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_conference_manager.html#​a8940f54febf2086ccd978d75980c7763|stopAudioMixing]] ​ |停止本地伴音功能 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_conference_manager.html#​a0546239a8e45778d59deeb5796770505|adjustAudioMixingVolume]] ​ |设置伴奏音量 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_conference_manager.html#​a4b76f361f9f94a9aa6a38c1af4dd0a05|getConferenceInfo]] ​ |获取会议信息 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_conference_manager.html#​aa04a85ab5f36f3f4ac14dc23ac18afb8|joinConference]] ​ |加入会议房间 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_conference_manager.html#​a08f1d1f058cb40a6bf745dfa4e0ab1e1|updateLiveLayout]] ​ |更新CDN推布局 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_conference_manager.html#​a6f154980142e31629ca5786d756f38fa|stopLiveStream ]]  |停止CDN推流 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_conference_manager.html#​a1682b222486d14ffd77da06a6c111c88|addLiveStream]] ​ |增加CDN旁路推流 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_conference_manager.html#​a6f154980142e31629ca5786d756f38fa|stopLiveStream ]]  |停止CDN推流 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_conference_manager.html#​abf0e222c3ec094c9dea7ec9c7b89a1a1|enableRecordLivestream]] ​ |开启或停止录制CDN流  | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_conference_manager.html#​aab4ae14065e192f9a1f284443c911149|joinConferenceWithTicket]] ​ |通过Ticket加入会议 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_conference_manager.html#​a46185b19b85ee8366c0f22ef36a3e05e|inviteUserToJoinConference]] ​ |邀请其他人加入会议 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_conference_manager.html#​a52a1e73bc4b588bc57227c7a57512409|grantRole]] ​ |改变会议角色 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_conference_manager.html#​af8762ac9fa0fd293f05b4bcf1efbef10|kickMember]] ​ |会议中踢人 | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_conference_manager.html#​a6f7709002fb62d3279f101e4032954bf|muteAll ]]  |设置全体静音 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_conference_manager.html#​a785be01c2f30dbe661fb91c9c8cac7a9|setConferenceAttribute]] ​ |设置会议属性 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_conference_manager.html#​a5527cfbd5bcb9851eb01bdb8ffd97eaa|deleteConferenceAttribute ]]  |删除会议属性 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_conference_manager.html#​a8d95d4e839a2598d76be85ed69e06244|destroyConference]] ​ |销毁会议 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_conference_manager.html#​a1b52063a838ac0a89b0c5bd2ef0187ad|exitConference]] ​ |退出会议 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_conference_manager.html#​aa087a548e2d8f79ce06f50927decc137|publish]] ​ |开始推流 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_conference_manager.html#​a926c631717ce04a6c2a31401c8e095f7|unpublish]] ​ |停止推流 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_conference_manager.html#​a405c38df38cbb0e6bf11420ff80540e0|subscribe]] ​ |订阅流 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_conference_manager.html#​a084981244bf4840d717a7d49ba472bcb|updateSubscribe]] ​ |更新订阅流 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_conference_manager.html#​a87f0977b0e7aa91fe5c1b5a6855d311b|unsubscribe]] ​ |取消订阅流 ​ | 
 +|setWaterMark ​ |设置水印 ​ | 
 +|clearWaterMark ​ |取消水印 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_conference_manager.html#​ace04306620a500e1892d6759af9c34f5|inputExternalAudioData]] ​ |外部属于音频 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_conference_manager.html#​a606c489f65d2ca02ca8269604b9b5ef4|inputExternalVideoData]] ​ |外部视频输入 | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_conference_manager.html#​ade491f1314d5275d8880d0466aa044ed|startMonitorSpeaker]] ​ |开启正在说话监听器 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_conference_manager.html#​ac1bd1ff347f4ddcf55395c855bdafc60|stopMonitorSpeaker]] ​ |停止正在说话监听器 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_conference_manager.html#​a9594aa797296a3e7327fadd699f2ac53|setLocalSurfaceView]] ​ |设置本地视频视图  | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_conference_manager.html#​a63bd88ed797f3eeb650ac592dc1464b2|updateLocalSurfaceView]] ​ |更新本地视频视图| 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_conference_manager.html#​a4e1350c5b350054474ba85c8d29d67d5|updateRemoteSurfaceView]] ​ |更新远端视图 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_conference_manager.html#​a0be64c2071bb8275a64fe877f31b8648|switchCamera]] ​ |切置摄像头 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_conference_manager.html#​afce9a2bea4d0ee1397d696da5940d9dd|closeVideoTransfer]] ​ |停止视频传输 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_conference_manager.html#​a807cea2439323c341c5111e30e6aac8b|openVideoTransfer]] ​ |恢复视频传输 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_conference_manager.html#​a7b4bd022d9daf8fe127d89494897bf99|closeVoiceTransfer]] ​ |停止音频传输 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_conference_manager.html#​ab140d5e3185760c5dcefabf367385df6|openVoiceTransfer]] ​ |恢复音频传输 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_conference_manager.html#​aa55d14555c59930263659b1f608e8f18|muteRemoteAudio]] ​ |mute远端音频 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_conference_manager.html#​aba05a1e466a74a12ba42a10bea5afae8|muteRemoteVideo]] ​ |mute远端视频 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_conference_manager.html#​a8332e86b981056d930dde2965c047274|enableStatistics]] ​ |开始会议质量统计 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_conference_manager.html#​ae9183eaa2c0d105d426ddf917aff0fd4|getConferenceMemberList]] ​ |获取当前会议成员 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_conference_manager.html#​a4f0bad698a8c6e0b62d781b8225129a3|getAvailableStreamMap]] ​ |获取当前会议可订阅Stream ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_conference_manager.html#​a8332e86b981056d930dde2965c047274|getSubscribedStreamMap]] ​ |获取当前会议已订阅 Stream| 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_conference_manager.html#​acc62694d0c18068081209f6d987b0db6|setLocalVideoViewMirror]] ​ |设置本地视频view镜像 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_conference_manager.html#​ad29c3544ec98b0ac4596d7d9abe35df9|setRotation]] ​ |设置视频会议Camera采集到VideoFrame的rotation ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_conference_manager.html#​ad386890a370780b0dd4f80a1406a65b7|createWhiteboardRoom]] ​ |创建白板房间 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_conference_manager.html#​a887013e116d229c48b45ce12177051f4|destroyWhiteboardRoom]] ​ |销毁白板房间 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_conference_manager.html#​a4aaf75f7416029bca4c3dc98bcbb81b9|joinWhiteboardRoomWithId]] ​ |通过白板id加入房间 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_conference_manager.html#​a39704e3e68b7786261d4de0b3005409f|joinWhiteboardRoomWithName]] ​ |通过白板名称加入房间 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_conference_manager.html#​ae005fa11c9f9087f6582fff84340f363|updateWhiteboardRoomWithRoomId]] ​ |通过白板id修改白板操作权限 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_conference_manager.html#​a59d53f3c392714f201486d788f38cf07|applyTobeSpeaker]] ​ |发送上麦请求 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_conference_manager.html#​affaa83f9c32e5a99caa7ec0627ddfcb2|handleSpeakerApplication]] ​ |管理员处理上麦请求 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_conference_manager.html#​a5240c7d53051f0d14b27f0a59a2fc945|applyTobeAdmin]] ​ |发送申请管理员请求 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_conference_manager.html#​a5240c7d53051f0d14b27f0a59a2fc945|applyTobeAdmin]] ​ |管理员处理申请管理员请求 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_conference_manager.html#​a5176c44768bec0b59578315281dae6ad|muteMember]] ​ |发送静音命令 ​  | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_conference.html#​a5ed44f5a7f5f0e222c1a1181f0080ca8|unmuteMember]] ​ |发送解除静音命令 ​ | 
 + 
 + 
 +== EMConference == 
 +^属性 ^描述 ^ 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1EMCallOptions.html#​adcda2ab5ab431592ef906983c8a9df26|PubStreamId]] ​ |pub流id ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_conference.html#​abbc72b5f188a81ce4ca5838e0d978279|ConferenceId]] ​ |会议id ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_conference.html#​ac4670d3c5bdf52c470bb86153038a781|ConferenceType]] ​ |会议类型 | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_conference.html#​a55d2d906bc8338b457dac33277a196f9|Admins]] ​ |会议中的管理员列表 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1EMCallOptions.html#​adcda2ab5ab431592ef906983c8a9df26|PubStreamId]] ​ |pub流id ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_conference.html#​a705ffe7f293ec11ec4fe41c431eb9557|Talkers]] ​ |会议中的主播列表 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_conference.html#​ad9d0782589e50a43a7c9ce3f17497994|RecordOnServer ]]  |是否开启云端录制 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_conference.html#​af1de8816baeca6f69d49c7d4539740d0|AudienceTotal]] ​ |会议中的观众人数 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1EMCallOptions.html#​adcda2ab5ab431592ef906983c8a9df26|PubStreamId]] ​ |pub流id ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1EMCallOptions.html#​adcda2ab5ab431592ef906983c8a9df26|PubStreamId]] ​ |pub流id ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1EMCallOptions.html#​adcda2ab5ab431592ef906983c8a9df26|PubStreamId]] ​ |pub流id ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_conference.html#​a38a2d557655a55f51f1376a9497d64e4|Extension]] ​ |会议扩展信息 ​ | 
 + 
 + 
 +== EMConferenceListener == 
 +^方法 ^描述 ^ 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​interfacecom_1_1hyphenate_1_1_e_m_conference_listener.html#​af1d01093be1c3ba9301a23ca094cbb7a|onMemberJoined]] ​ |成员加入会议 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​interfacecom_1_1hyphenate_1_1_e_m_conference_listener.html#​aff81ccb071666c3f06197bedf1ed0f39|onMemberExited]] ​ |成员离开会议 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​interfacecom_1_1hyphenate_1_1_e_m_conference_listener.html#​ad06a034d00575fbf41798b98bebe6089|onStreamAdded]] ​ |有新的成员推流 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​interfacecom_1_1hyphenate_1_1_e_m_conference_listener.html#​a11fd34c44310869dc1b610d8929bdb65|onStreamUpdate]] ​ |有成员更新自己的推流 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​interfacecom_1_1hyphenate_1_1_e_m_conference_listener.html#​a1582a269102203e93373853d50ca7a74|onPassiveLeave]] ​ |被动离开会议 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​interfacecom_1_1hyphenate_1_1_e_m_conference_listener.html#​a544928e7b0fb91b5d86edfc86bad7bfa|onAdminAdded]] ​ |管理员增加通知 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​interfacecom_1_1hyphenate_1_1_e_m_conference_listener.html#​a9cb74ec8dc164d5e8be0ef41db4ed2fa|onAdminRemoved]] ​ |管理员移除通知 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​interfacecom_1_1hyphenate_1_1_e_m_conference_listener.html#​af1d01093be1c3ba9301a23ca094cbb7a|onApplyAdminRefused]] ​ |申请管理员失败通知(只有申请管理者收到) ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​interfacecom_1_1hyphenate_1_1_e_m_conference_listener.html#​a995b85e550b2de8c3055a9b1709267a1|onApplySpeakerRefused]] ​ |申请主播失败通知(只有申请管理者收到) ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​interfacecom_1_1hyphenate_1_1_e_m_conference_listener.html#​a0208f1a6c8610a1bc89bedfd8c1583bf|onPubStreamFailed]] ​ |pub 流失败 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​interfacecom_1_1hyphenate_1_1_e_m_conference_listener.html#​a63bdfe9b48ff313cd3d4458fd91fc262|onUpdateStreamFailed]] ​ |update 流失败 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​interfacecom_1_1hyphenate_1_1_e_m_conference_listener.html#​af54f319c0388bf4a9f0bbaa118baa4b0|onConferenceState]] ​ |会议状态通知回调 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​interfacecom_1_1hyphenate_1_1_e_m_conference_listener.html#​aa1bd09bf1b7532a234e66968c33b7c34|onStreamStatistics]] ​ |统计信息回调 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​interfacecom_1_1hyphenate_1_1_e_m_conference_listener.html#​ad27bc75a608abc06c48ddbbbe8449d52|onStreamSetup]] ​ |推本地流或订阅成员流成功回调 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​interfacecom_1_1hyphenate_1_1_e_m_conference_listener.html#​a3e7bec56a7a6b12da1314911a451871a|onStreamStateUpdated]] ​ |订阅流的数据状态回调 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​interfacecom_1_1hyphenate_1_1_e_m_conference_listener.html#​a4e9febe04b53b334e5c11b69fbf27749|onSpeakers]] ​ |当前说话者回调 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​interfacecom_1_1hyphenate_1_1_e_m_conference_listener.html#​a6ff0dfbd9286181dbfd5a5a26b3bc07a|onReceiveInvite]] ​ |收到会议邀请 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​interfacecom_1_1hyphenate_1_1_e_m_conference_listener.html#​a6df0e567fc534314cee3008c310dfe72|onRoleChanged]] ​ |当前登录用户角色被管理员改变 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​interfacecom_1_1hyphenate_1_1_e_m_conference_listener.html#​a316b29cdcf4b6e0b4887ba2ef7a339a3|onReqSpeaker]] ​ |请求成为主播通知 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​interfacecom_1_1hyphenate_1_1_e_m_conference_listener.html#​a745d99b8f8d7560a67f009cd1c280a4c|onReqAdmin]] ​ |请求成为管理员通知 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​interfacecom_1_1hyphenate_1_1_e_m_conference_listener.html#​acc356b01d68a1182a3ff658e1739cc66|onMuteAll]] ​ |被全体静音 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​interfacecom_1_1hyphenate_1_1_e_m_conference_listener.html#​acc356b01d68a1182a3ff658e1739cc66|onMute]] ​ |被静音通知 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​interfacecom_1_1hyphenate_1_1_e_m_conference_listener.html#​a42ed6aba161a01a7374311047a55e3a7|onUnMute]] ​ |被取消静音通知 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​interfacecom_1_1hyphenate_1_1_e_m_conference_listener.html#​a469617faf2cba13273c903875ad6c7a8|onGetLivecfg]] ​ |获取直播推流CDN 信息 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​interfacecom_1_1hyphenate_1_1_e_m_conference_listener.html#​a469617faf2cba13273c903875ad6c7a8|onGetLivecfg]] ​ |获取直播推流CDN 信息 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​interfacecom_1_1hyphenate_1_1_e_m_conference_listener.html#​ae6f40a14bf9b7e198877ba06178fb32b|onGetLocalStreamId]] ​ |获取自己StreamId ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​interfacecom_1_1hyphenate_1_1_e_m_conference_listener.html#​a427cf03bbd681f6e5a4c7d8cec24d0ef|onPubDesktopStreamFailed]] ​ |发布共享桌面流失败回调 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​interfacecom_1_1hyphenate_1_1_e_m_conference_listener.html#​a95313ebf76b573192f91fa586ebc832c|onFirstFrameSent]] ​ |Pub 首帧回调streamId流ID ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​interfacecom_1_1hyphenate_1_1_e_m_conference_listener.html#​a98e14b220cfa43ff992406cb3a9089e5|onFirstFrameRecived]] ​ |Pub 首帧回调 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​interfacecom_1_1hyphenate_1_1_e_m_conference_listener.html#​a95313ebf76b573192f91fa586ebc832c|onFirstFrameSent]] ​ |Sub 首帧回调 ​  | 
 + 
 + 
 + 
 + 
 +== EMConferenceMember == 
 +^属性 ^描述 ^ 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_conference_member.html|memberName]] ​ |会议成员Name ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_conference_member.html|memberId]] ​ |会议成员Id ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_conference_member.html|nickName ]]  |会议成员昵称 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_conference_member.html|extension]] ​ |会议成员扩展字段 ​ | 
 + 
 + 
 +== EMConferenceStream == 
 +^属性 ^描述 ^ 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_conference_stream.html#​a2c05fdc542e7de1fe510c86e5d2ffae7|StreamId]] ​ |流id ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_conference_stream.html#​a1f138ff27a4550e0660f013ff7a479cc|StreamName]] ​ |流Name ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_conference_stream.html#​a558e8149e02a2bed7ede363db600f8ca|MemberName]] ​ |流的所属成员的MemberName ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_conference_stream.html#​a851e77d978ef23db876d9f99c59789a8|Username]] ​ |流的所属成员的Username ​  | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_conference_stream.html#​a243ef8254bd354b6038e0779b9342115|Extension]] ​ |流的扩展字段 | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_conference_stream.html#​adf570ce4deafeff59ca289949fd3d48f|VideoOff]] ​ |流视频是否关闭 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_conference_stream.html#​a9de7ca96da69e90ba7c6fb8091938a40|AudioOff]] ​ |流音频是否关闭 ​  | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_conference_stream.html#​a9de7ca96da69e90ba7c6fb8091938a40|StreamType]] ​ |流的类型 ​ | 
 + 
 + 
 +== EMStreamParam ​ == 
 +^属性 ^描述 ^ 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_stream_param.html#​a210fcb233701b4c2e5698b08aa029fe9|name]] ​ |推流配置名称 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_stream_param.html#​a2cbb57448a013c854f552adf2837d0e6|videoOff]] ​ |是否关闭视频 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_stream_param.html#​a4e4bc1d46d1c82f324950aea9b0c8a0f|audioOff]] ​ |是否静音 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_stream_param.html#​a682014ca04fe6e692ee0470f3d59d325|useBackCamera]] ​ |使用后置摄像头 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_stream_param.html#​a4d7f3be9c29fe1146160fe81050076e5|videoHeight]] ​ |视频高度 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_stream_param.html#​af9e0a4165400cc07113936e976841ace|videoWidth]] ​ |视频宽度 | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_stream_param.html#​a65daa3acf8e0fc0b0e5b684b4265df77|extension]] ​ |扩展字段 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_stream_param.html#​a337e2c42212282ff188f119e478c885a|maxAudioKbps]] ​ |设置最大音频特率 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_stream_param.html#​adb8eb12d1d37b28f9a2216e57aa651d4|minVideoKbps]] ​ |设最小的网络带宽 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_stream_param.html#​a0065046c59c8fda054ad833b8c3cae5a|maxVideoKbps]] ​ |最大视频码率 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_stream_param.html#​a1a196f933b8504073732bcb19900699a|streamType]] ​ |stream 类型 ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_stream_param.html#​af6c63073334df4b09fc81a4f5a3b2c8e|shareView]] ​ |享的view ​ | 
 +|[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_stream_param.html#​a2818857429c256757573eb0ade1084a3|audioSampleRate]] ​ |设置音频采样频率  | 
 + 
 +== EMStreamStatistics == 
 + 
 +音视频通话统计信息实体类,具体请看[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_stream_statistics.html|EMStreamStatistics ]] 详细介绍 
 + 
 +== EMConferenceAttribute == 
 + 
 +会议属性设置类,具体请看[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_conference_attribute.html|EMConferenceAttribute]] 的详细介绍 
 + 
 + 
 +== EMRoomConfig ​ == 
 + 
 +会议房间属性类 ,具体请看[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_room_config.html|EMRoomConfig]] 的详细介绍 
 + 
 + 
 +== EMLiveConfig ​ == 
 + 
 +CDN推流设置类,具体请看[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_live_config.html|EMLiveConfig]] 的详细介绍 
 + 
 + 
 +== EMAudioConfig == 
 + 
 +自定义录制格式定义类,具体请看[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_audio_config.html|EMAudioConfig]] 的详细介绍 
 + 
 +== EMCDNCanvas ​ == 
 + 
 +CDN 画布设置类,具体请看[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_c_d_n_canvas.html|EMCDNCanvas]] 的详细介绍 
 + 
 + 
 +== EMLiveRegion == 
 + 
 +视频流在画布宽高及显示位置参数类,具体请看[[http://​sdkdocs.easemob.com/​apidoc/​android/​chat3.0/​classcom_1_1hyphenate_1_1chat_1_1_e_m_live_region.html|EMLiveRegion]] 的详细介绍 
  
-<code java> 
-try { 
-    InputStream in = this.getResources().getAssets().open("​watermark.png"​);​ 
-    watermarkbitmap = BitmapFactory.decodeStream(in);​ 
-} catch (Exception e) { 
-    e.printStackTrace();​ 
-} 
-watermark = new WaterMarkOption(watermarkbitmap,​ 75, 25, WaterMarkPosition.TOP_RIGHT,​ 8, 8); 
-</​code>​ 
  
-===== 多集群代理 =====  
-多人音视频支持不同集群区域的人员使用代理,减小延迟、丢包率。使用多集群代理需要音视频后台配置IP及端口的映射文件rtcconfig.json,​sdk开启相应开关。 
-启用多集群代理功能开关如下,​该方法在EMOptions类中,具体如下: 
-<code java> 
-public void setUseRtcConfig(boolean useRtcConfig);//​true为开启,false为不开启 
-</​code>​ 
  
-===== cdn合流推流 =====  
-多人音视频支持将会议中的音视频流合并成一个流,推送到第三方的cdn直播服务器。整个合流推流过程包括开启cdn推流,更新推流布局,停止推流。 
  
-==== 开启cdn推流 ==== 
-会议的创建者在创建会议时使用EMRoomConfig的接口,可以决定是否开启cdn推流,推流配置EMLiveConfig是EMRoomConfig的一个参数,可设置cdn推流的相关信息,然后调用 创建会议接口,可以开启cdn推流(注意:​只有会议创建者才能开启cdn推流,如果会议已经创建好,其他人再调用开启cdn推流无效)。 
-开启过程如下: 
-<code java> 
-  EMCDNCanvas canvas = new EMCDNCanvas(ConferenceInfo.CanvasWidth,​ ConferenceInfo.CanvasHeight,​ 0,​30,​900,"​H264"​);​ 
- ​String url = PreferenceManager.getInstance().getCDNUrl();​ 
- ​EMLiveConfig liveConfig = new EMLiveConfig(url,​ canvas); 
- ​roomConfig.setLiveConfig(liveConfig);​ 
- ​EMClient.getInstance().conferenceManager().joinRoom(currentRoomname,​ currentPassword,​ conferenceRole,​roomConfig,​ 
-                                                     new EMValueCallBack<​EMConference>​() 
-</​code> ​                                                     
  
-EMLiveConfig可设置的参数如下: 
  
-<code java> 
-/** 
- ​* ​ \~chinese 
- ​* ​ CDN 画布设置,创建会议时使用 
- ​* ​ width (画布 宽) 
- ​* ​ height(画布 高) 
- * bgclr (画布 背景色 ,格式为 RGB 定义下的 Hex 值,不要带 # 号, 
- *        如 0xFFB6C1 表示浅粉色。默认0x000000,黑色) 
- ​* ​ fps (推流帧率,可设置范围10-30 ) 
- ​* ​ kpbs (推流码率,单位kbps,width和height较大时,码率需要提高,可设置范围1-5000) 
- ​* ​ codec (推流编码格式,目前只支持"​H264"​) 
- * 
- ​* ​ \~english 
- ​* ​ The cdn canvas config 
- ​* ​ width (Canvas width) 
- ​* ​ height(Canvas height) 
- ​* ​ bgclr (Canvas background color, Hex value defined by RGB, no #, 
- ​* ​        such as 0xFFB6C1 means light pink.Default 0x000000, black)) 
- ​* ​ fps (The fps of cdn live,valid valuei is 10-30) 
- ​* ​ kpbs (The birateBps of cdn live,the unit is kbps,valid value is 1-5000 ) 
- ​* ​ codec (The codec of cdn live,now only support H264) 
- */ 
-public class EMCDNCanvas { 
-    private ​ int  width = -1; 
-    private ​ int  height = -1; 
-    private ​ int  bgclr = 0; 
-    private ​ int  fps = -1; 
-    private ​ int  kpbs = -1; 
-    private ​ String ​ codec = null; 
  
-    public EMCDNCanvas(){ 
  
-    } 
-    public EMCDNCanvas(int width, int height, int bgclr,int fps,int kpbs,String codec){ 
-        this.width = width; 
-        this.height = height; 
-        this.bgclr = bgclr; 
-        this.fps = fps; 
-        this.kpbs = kpbs; 
-        this.codec ​ = codec; 
-    }  
-} 
  
-/** 
- ​* ​ \~Chinese 
- ​* ​ CDN推流设置 
- ​* ​ cdnurl ​    ​cdn推流地址 
- ​* ​ cdnCanvas ​ 画布设置 (cdnCanvas可以缺省) 
- ​* ​ liveLayoutStyle 
- * 
- ​* ​ \~English 
- ​* ​ The CDN push stream config 
- ​* ​ cdnurl ​    cdn push stream address 
- ​* ​ cdnCanvas ​ canvas settings (cdnCanvas can be default) 
- */ 
-public class EMLiveConfig { 
-    private String cdnurl; 
-    private EMCDNCanvas cdnCanvas = null; 
-    public EMLiveConfig(){ 
-    } 
-    public EMLiveConfig(String cdnurl, EMCDNCanvas cdnCanvas){ 
-        this.cdnurl = cdnurl; 
-        this.cdnCanvas = cdnCanvas; 
-    } 
-} 
-</​code>​ 
  
-==== 更新布局 ==== 
-当用户调用更新布局接口后,cdn推流方式将强制变成CUSTOM模式,所有流的位置信息都由用户自己定义。 
-更新布局的接口如下: 
-<code java> 
- /** 
-     * \~chinese 
-     * CDN 推流更新布局(只有管理员可操作) 
-     * 用户角色:​ Admin > Talker > Audience 
-     * 注意: ​ 更新布局只允许Admin 去操作 
-     * 
-     * @param regions EMCanvasRegion布局对象列表 
-     * @param callback 结果回调 
-     * 
-     * \~english 
-     * CDN pushes to update the layout 
-     * 
-     * @param regions Layout EMCanvasRegion list 
-     * @param callback Result callback 
-     */ 
-    public void updateLiveLayout(List<​EMLiveRegion>​ regions , final EMValueCallBack<​String>​ callback) 
-</​code>​ 
-EMLiveRegion的结构如下: 
-<code java> 
-/** 
- ​* ​ \~Chinese 
- ​* ​ 视频流在画布宽高及显示位置等参数 
- ​* ​ x         ​在画布横坐标位置 
- ​* ​ y         ​在画布纵坐标位置 
- ​* ​ width     ​视频流宽度(64~1280) 
- ​* ​ height ​   视频流高度(64~1280) 
- ​* ​ zorder ​   视频流zorder层位置(最小值为 0(默认值),表示该区域图像位于最下层 
- ​* ​                             最大值为 100,表示该区域图像位于最上层) 
- ​* ​ style     ​视频流显示方式(fit模式或者fill模式) 
- ​* ​ streamId ​ 视频流ID 
- * 
- * 
- ​* ​ \~English 
- ​* ​ Video streaming in the canvas width and height and display location and other parameters 
- ​* ​ x         On the horizontal position of the canvas 
- ​* ​ y         On the vertical position of the canvas 
- ​* ​ width     Video stream width 
- ​* ​ height ​   Video stream height 
- ​* ​ zorder ​   Video stream zorder layer location 
- ​* ​ style     Video stream display mode (fit mode or fill mode) 
- ​* ​ streamId ​ The video stream ID 
- */ 
-public class EMLiveRegion { 
-    private int x; 
-    private int y; 
-    private int width; 
-    private int height; 
-    private int zorder; 
-    private EMRegionStyle style; 
-    private String streamId; 
  
-    public EMLiveRegion(){} 
-    public EMLiveRegion(int x,int y, int width, int height, int zorder, EMRegionStyle style, String streamId){ 
-        this.x = x; 
-        this.y = y; 
-        this.width = width; 
-        this.height = height; 
-        this.zorder = zorder; 
-        this.style = style; 
-        this.streamId = streamId; 
-    }  ​ 
-    public enum EMRegionStyle { 
-        FIT, // fit模式 
-        FILL // fill模式 
-    } 
-} 
-</​code>​ 
  
-使用方法如下: 
-<code java> 
-List<​EMLiveRegion>​ regionsList = new LinkedList<>​();​ 
-EMLiveRegion region = new EMLiveRegion();​ 
-region.setStreamId(streamInfo.getStreamId()); ​           
-region.setStyle(EMLiveRegion.EMRegionStyle.FILL);​ 
-region.setX(80);​ 
-region.setY(60);​ 
-region.setWidth(320);​ 
-region.setHeight(640);​ 
-region.setZorder(1);​ 
-regionsList.add(region);​ 
-EMClient.getInstance().conferenceManager().updateLiveLayout(regionsList,​new EMValueCallBack<​String>​();​ 
-</​code>​ 
  
-==== 停止推流 ==== 
-多人音视频支持停止向某一个地址的推流,停止推流接口如下: 
-<code java> 
- /** 
-     * \~chinese 
-     * 停止CDN推流 
-     * @param callback 结果回调 
-     * 
-     * \~english 
-     * Stop the CDN push 
-     * 
-     * @param callback Result callback 
-     */ 
-    public void stopLiveStream(final EMValueCallBack<​String>​ callback) 
-</​code>​ 
-  
-===== 其他方法 ===== 
- <​code java> 
- // 开启音频传输 
- ​EMClient.getInstance().conferenceManager().openVoiceTransfer();​ 
- // 关闭音频传输 
- ​EMClient.getInstance().conferenceManager().closeVoiceTransfer();​ 
- // 开启视频传输 
- ​EMClient.getInstance().conferenceManager().openVideoTransfer();​ 
- // 关闭视频传输 
- ​EMClient.getInstance().conferenceManager().closeVideoTransfer();​ 
-  
- ​PS:以上这四个方法都是修改 stream,群里其他成员都会收到 EMConferenceListener.onStreamUpdate()回调 
-  
- // 切换摄像头 
- ​EMClient.getInstance().conferenceManager().switchCamera();​ 
- // 设置展示本地画面的 view 
- ​EMClient.getInstance().conferenceManager().setLocalSurfaceView(localView);​ 
- // 更新展示本地画面 view 
- ​EMClient.getInstance().conferenceManager().updateLocalSurfaceView(localView);​ 
- // 更新展示远端画面的 view 
- ​EMClient.getInstance().conferenceManager().updateRemoteSurfaceView(streamId,​ remoteView);​ 
-  
- // 开始监听说话者,参数为间隔时间 
- ​EMClient.getInstance().conferenceManager().startMonitorSpeaker(300);​ 
-  
- // 停止监听说话者 
- ​EMClient.getInstance().conferenceManager().stopMonitorSpeaker();​ 
  
- // 开启数据统计,用于debug视频过程中的具体参数,开启后会在onStreamStatistics回调中读取到 
- // 各项数据参数,具体参见EMStreamStatistics。 
- ​EMClient.getInstance().conferenceManager().enableStatistics(true);​ 
-  
- ​public void onStreamStatistics(EMStreamStatistics statistics) { 
-        EMLog.i(TAG,​ "​onStreamStatistics"​ + statistics.toString());​ 
-        debugPanelView.onStreamStatisticsChange(statistics);​ 
-    } 
-  ​ 
- </​code>​