差别

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

到此差别页面的链接

两侧同时换到之前的修订记录 前一修订版
后一修订版
前一修订版
rtc:conference:web [2020/06/30 10:02]
suqx [暂停/恢复自己的视频]
rtc:conference:web [2021/03/26 08:01] (当前版本)
huangfeipeng [2. 指定设备打开音视频]
行 1: 行 1:
-====== Web集成多人通话 ======+====== Web集成多人通话 ======
 +====== 跑通Demo ======= 
 +环信提供开源的多人视频会议项目WebVideoCallDemo,在使用SDK集成App前,您可以参考相关代码 
 +===== 示例代码 ===== 
 +** 1.可在[[http://​docs-im.easemob.com/​rtc/​scenario/​meeting#​demo_%E6%BA%90%E7%A0%81|环信音视频Demo源码]]下载页面,选择Web端Demo下载 **\\ 
 +** 2.进入github开源网站https://​github.com/​easemob/​videocall-web,​ 克隆代码 **\\ 
 +==== 前提条件 ==== 
 +** 1.安装一款 Easemob Web SDK  [[http://​docs-im.easemob.com/​rtc/​common/​introduction#​%E5%85%BC%E5%AE%B9%E6%80%A7%E8%AF%B4%E6%98%8E|支持的浏览器]] \\ 
 +2.本地安装 node 环境 >= 6.3.0 \\ 3.必须为https+webkit内核浏览器**  
 +===== 运行 Demo ===== 
 +1. 下载下Demo \\ 
 +2. 进入 videocall-web 文件夹\\ 
 +3. 安装依赖包 
 +<code javascript>​ 
 +   npm install 
 +</​code>​ 
 +4. 启动项目 
 +<code javascript>​ 
 +   ​HTTPS=true npm start 
 +</​code>​
  
----- 
  
-===== 产品特性 ​=====+====== 快速集成 ====== 
 +===== 1. 环信后台注册 appkey ===== 
 +在开始集成前,你需要注册环信开发者账号并创建后台应用,参见[[http://​docs-im.easemob.com/​im/​ios/​sdk/​prepare#​注册并创建应用|注册并创建应用]] 
 +===== 2. 创建项目 ===== 
 +  a.可以简单的写一个 html,引入 SDK 测试  
 +  b.或者使用 脚手架搭建一个项目 
 +===== 3.引入 SDK ===== 
  
-    *支持现代浏览器:Chrome/​50+、Safari/​11+、Firefox; +=== 3.1过 scrpit 标签的 src 引入 ​=== 
- +[[http://docs-im.easemob.com/​rtc/​conference/​web#​%E8%8E%B7%E5%8F%96sdk%E9%9D%99%E6%80%81%E8%B5%84%E6%BA%90%E6%96%87%E4%BB%B6|获取静态SDK 文件]]
-    *遵守:UMD通用模块规范,支持require导入; +
- +
-    *支持Promise; +
- +
-    *支持手机端和 Web 端互通,极大方便开发者的全平台业务; +
- +
-    *依赖https站点 +
- +
-===== 音视频简要步骤 ===== +
- +
-SDK 能够支持音频和视频通信。创建和操作音视频通信的过程简单来说,可以分为以下几步: +
- +
-  - 初始化 SDK,设置监听代理 +
-  - create创建会议 +
-  ​join: 加入会议 +
-  - pub: 发布音视频数据流 +
-  - sub: 订阅并播放音视频数据流 +
-  - leave: 离开会议 +
-===== 基本知识 ===== +
- +
-emedia.mgr.ConfrType:多人会议类型 +
- +
-    1. Communication:普通通信会议,最多支持参会者9人,成员都可以自由说话和发布视频,成员角色Speaker +
-    2. Large Communication:大型通信会议,最多参会者30人,成员都可以自由说话和发布视频,成员角色Speaker +
-    3. Live:互动视频会议,会议里支持最多9个主播和600个观众+
 <code javascript>​ <code javascript>​
-emedia.mgr.ConfrType = { +<script src="​EMedia_sdk-dev.js"><​/script>
-   ​COMMUNICATION:​ 10, //​普通会议模式 +
-   ​COMMUNICATION_MIX:​ 11, //​大会议模式 +
-   LIVE: 12, //直播模式 +
-};+
 </​code>​ </​code>​
 +===3.2使用 npm 获取 SDK===
 <code javascript>​ <code javascript>​
-emedia.mgr.Role = { +npm install easemob-emedia
-    ADMIN: 7, // 能创建会议,销毁会议,移除会议成员,切换其他成员的角色,订阅流,发布流 +
-    TALKER: 3, // 能上传自己的音视频,能观看收听其他主播的音视频,即能发布流和订阅流) +
-    AUDIENCE: 1 // 观众Audience:只能观看收听音视频,即只能订阅流 +
-};+
 </​code>​ </​code>​
 +===3.3 在文件内引入 SDK===
 <code javascript>​ <code javascript>​
-emedia.mgr.StreamType = { +import ​emedia ​from '​easemob-emedia'​;
-    VIDEO:​0,//​普通视频 +
-    DESKTOP:​1,//​共享桌面 +
-    MIXVIDEO:2 //​混音视频 +
-};+
 </​code>​ </​code>​
 +=== 3.4 获取SDK静态资源文件 ===
 +** 1. 首先 [[https://​www.easemob.com/​download/​rtc|下载 WebIM Demo 包]] **
 +{{ :​rtc:​conference:​wechatimg129.png?​600 |}}
 +** 2. 从Demo 中选取 SDK 文件(EMedia_sdk-dev.js) **
 +{{ :​rtc:​conference:​wechatimg130.png?​300 |}}
 +===== 4.初始化SDK =====
 +
 <code javascript>​ <code javascript>​
-//​conference|confr +emedia.config(
-+   appkey// 从环信后台 获取的appkey、必填 
-    ​confrId:"​TS_X296786295944036352C27"​+   consoleLoggertrue// boolean 是否开启打印日志,默认true 
-    id:"​TS_X296786295944036352C27"​+   ... 其他的一些配置 
-    ​password:​ "​password123",​  +});
-    roleToken:"​roleToken",​ +
-    ticket:"​ticket",​ +
-    type:12 +
-};+
 </​code>​ </​code>​
 +===== 5. 环信ID注册、登录 =====
 +在进行音视频通话前,需要首先登录IM账户,登录过程参见[[http://​docs-im.easemob.com/​im/​web/​intro/​basic#​登录|账号登录]]。
 +
 +若您还没有IM账户,需要先注册账户,注册过程参见[[http://​docs-im.easemob.com/​im/​web/​intro/​basic#​注册|账号注册]]
 +
 +
 +===== 6.进入会议 ===== 
 +** 注意:加入会议之前必须先要 调用 emedia.mgr.setIdentity 方法设置 emedia 对象的memName 、token **
 <code javascript>​ <code javascript>​
-//member +emedia.mgr.setIdentity(memNametoken)//memName:appkey +'​_'​+ 环信IDtoken环信ID登录返回access_token
-+
-    "​ext":​{ //emedia.mgr.joinConference(confrIdpassword, {role: '​admin'​})/* 用户可自定义扩展字段*/); +
-        "​role"​:"​admin"​ +
-    }, +
-    "​id"​:"​MS_X197721744293023744C19M197756407719972865VISITOR",​ +
-    "​globalName":"​easemob-demo#​chatdemoui_yss000@easemob.com",​ +
-    "​name":​ "​yss000",​ +
-    "​nickName":​ "​yss000"​ //设置会生效,不设置默认取name值 +
-}+
 </​code>​ </​code>​
 +** params 为进入会议需要的参数 **
 <code javascript>​ <code javascript>​
-//stream +var params = 
-+    ​roomName// string 房间名称 必需 
-    ​"​id":"​RTC2__Of_C19M197756407719972865VISITOR"​+    ​password, // string 房间密码 必需 
-    ​"​voff":​0, //1 视频关闭 ​ +    ​role  ​// number 进入会议的角色 ​1: 观众 ​ 3:主播 必需 
-    ​"​aoff":​0, ​//1 音频关闭 ​ +    ​config:
-    ​"​memId"​:"​MS_X197721744293023744C19M197756407719972865VISITOR"​+            rec:false//​是否开启录制会议 
-    "​owner"​member ​,//member对象 +            ​recMerge:false, //是否开启合并录制 
-    "​rtcId"​:"​RTC1"​+            ​supportWechatMiniProgramtrue //​是否允许小程序加入会议 
 +        }
 } }
-var islocated = stream.located();​ //islocated true 本地媒体流 
 </​code>​ </​code>​
-     +调用 ​** emedia.mgr.joinRoom ** 进入会议, 若该会议不存在,服务器将会自动创建
-    注意: +
-    >> 每个人必须调用join接口成功后,才算是加入会议(即成为会议成员)。议成员才允许进行其他操作比如订阅流、发布流等 +
-    >> 成员如果想改变己角色,必须想办法通知管理员,只有管理员才能修改+
  
-===== 多人音视频会议功能实现 =====+<code javascript>​ 
 +const user_room ​await emedia.mgr.joinRoom(params);​ 
 +</​code>​
  
-是从创建会议到离开会议完整流程讲解:+返回的参数 ** user_room **,组成如 
 +<code javascript>​ 
 +user_room: { 
 +       ​confrId:​ "​IM3U9Z0AHDYQTF8KNDAAD00C147" ​会议ID 
 +       id: "​IM3U9Z0AHDYQTF8KNDAAD00C147"​ 
 +       ​joinId:​ "​IM3U9Z0AHDYQTF8KNDAAD00C147M2"​ 在会议唯一ID 
 +       role: 1|3|7 //角色 1观众 3主播 7管理员 
 +       ​roleToken:"​eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJlYXNlbW9iLWRlbW8j..."​ 
 +       ​ticket:​ "​{\"​tktId\":​\"​IM3U9Z0AHDYQTF8KNDAAD00C147TK1\",​..."​ 
 +       type: 10 //​会议类型 
 +
 +</​code>​
  
  
-==== 引入SDK ​====+===== 7.发布本地流 ​=====  
 +加入会议之后 调用 ** emedia.publish ** 发布流
 <code javascript>​ <code javascript>​
-// 可以使用easemob-webrtc ​者 easemob-emedia +var constaints = { // 发布音频流的配置参数,​ Object 必需。 videoaudio属性 至少存在一个 
-// 使用easemob-webrtc +    ​audio:​ true, // 是否发布音频 
-import webrtc from '​easemob-webrtc'​ +    ​video:​ true  // 是否发布视频 
-const emedia ​webrtc.emedia+
 +var ext {} // 发布流的扩展信息 Object 非必需。会议其他成员可接收到
  
-// 使用easemob-emedia +const pushedStream = await emedia.mgr.publish(constaints,​ ext);
-import emedia from '​easemob-emedia'​+
 </​code>​ </​code>​
-==== 初始化SDK ====+ 
 +发布本地流成功后, 调用 emedia.mgr.streamBindVideo 用于在video标签显示流
 <code javascript>​ <code javascript>​
-// 初始化一些 sdk 的使用功能 +var videoTag = document.getElementById('#xxx'//需要显示本地流的 video 标签 
-emedia.config(+emedia.mgr.streamBindVideo(pushedStream,​ videoTag);
-   ​restPrefix,​ //​配置服务器域名、必填 比如: ​'https://​a1-hsb.easemob.com' +
-   ​appkey, ​// 配置appkey、必填 +
-   ​... 其他的一些配置 +
-});+
 </​code>​ </​code>​
  
-==== 设置SDK回调 ​==== +===== 8.订阅远端流 =====  
-进入会议之前,设置SDK回调后,可获知成员加入或离开数据流更新等。+当远端流加入频道时,触发 emedia.mgr.onStreamAdded 方法我们需要给 emedia.mgr 赋值 onStreamAdded 用来接收 stream \\
  
 +** 我们建议项目初始化后,立即设置[[http://​docs-im.easemob.com/​rtc/​conference/​web#​%E4%BC%9A%E8%AE%AE%E5%B8%B8%E7%94%A8%E7%9B%91%E5%90%AC%E5%87%BD%E6%95%B0|常用监听函数]] **
 +=== 8.1 监听 onStreamAdded 方法, 当有远端流加入时订阅流 ===
 +** 注意: \\
 +自己发布的本地流也会触发 onStreamAdded 方法, \\
 +stream.located() == true 为自己发的本地流, \\
 +也可以在 这里绑定 video标签,显示本地流 \\ **
 <code javascript>​ <code javascript>​
-//加入会议,其他人调用joinXX等方法,如果加入成功,已经在会议中人将会收到 +emedia.mgr.onStreamAdded = function(member,​ stream) { 
-emedia.mgr.onMemberJoined = function ​(member) ​{ +    ​// member:发布流信息、stream:流信息 
-     +    if(!stream.located()) { 
-};+         var option = { 
 +             ​member:​ member,  
 +             ​stream:​ stream,  
 +             ​subVideo:​ true, 
 +             ​subAudio:​ true, 
 +             ​videoTag:​ document.getElementById('#​xxx'​) 
 +         } 
 +         ​emedia.mgr.subscribe(option.member, option.stream,​ option.subVideo,​ option.subAudio,​ option.videoTag    
 +    ​} 
 +}
 </​code>​ </​code>​
 +
 +** 在emedia.mgr.subscribe 方法中注意以下参数的设置 **
 +  * option.member:发布流人员的信息,必需。也就是 onStreamAdded 方法的 member
 +  * option.stream:流信息,必需。也就是 onStreamAdded 方法的 stream
 +  * option.subVideo:​ 是否订阅视频,必需
 +  * option.subAudio:​ 是否订阅音频,必需
 +  * option.videotag:​ 需要显示流的 video 标签,必需
 +
 +=== 8.2 监听 onStreamRemoved 方法,当远端流被移除时(例如远端用户调用了 Stream.unpublish), 停止订阅该流并移除它的画面。===
 <code javascript>​ <code javascript>​
-//​有人退出会议 +emedia.mgr.onStreamRemoved ​= function(member, stream) { 
-emedia.mgr.onMemberExited ​= function (member) { +    ​// member:发布流人员的信息、stream:流信息 
-     +    emedia.mgr.unsubscribe(stream) // 停止订阅流 
-};+    removeView(stream.id) // 移除video标签,removeView方法需自己实现 
 +}
 </​code>​ </​code>​
 +
 +===== 9.退出会议 =====
 +调用 emedia.mgr.exitConference 方法退出会议
 <code javascript>​ <code javascript>​
-//​有媒体流添加;比如 自己调用了publish方法(stream.located() === true时),或其他人调用了publish方法。 + emedia.mgr.exitConference() //无参数
-emedia.mgr.onStreamAdded = function ​(member, stream+
- +
-};+
 </​code>​ </​code>​
-<code objc> 
-//​有媒体流移除 
-emedia.mgr.onStreamRemoved = function (member, stream) { 
  
-}; +===== 10.会议常用监听函数 ===== 
-</​code>​+** ''​强烈建议:''​在加入会议之前定义需要的 SDK 回调函数 **
 <code javascript>​ <code javascript>​
-//角色变 +//有人加入会议 
-emedia.mgr.onRoleChanged = function (role{+emedia.mgr.onMemberJoined = function (member) { } // member: 加入会议成员信息 
 +     
 +//​有人退出会议 
 +emedia.mgr.onMemberExited = function (member) {} // member: 退出会议成员信息 
 +  
 +//​有媒体流添加 (自己发布的流也会触发 stream.located() == true ) 
 +emedia.mgr.onStreamAdded = function (member, stream) { }; // member: 发布流的成员信息,stream:流信息 
 + 
 +//​有媒体流移除 
 +emedia.mgr.onStreamRemoved = function (member, stream) { } // member: 移除流的成员信息,stream:流信息 
 + 
 +//自己角色变 
 +emedia.mgr.onRoleChanged = role => {} // role: 变更后的角色
  
-}; 
-</​code>​ 
-<code javascript>​ 
 //​会议退出;自己主动退 或 服务端主动关闭; //​会议退出;自己主动退 或 服务端主动关闭;
 emedia.mgr.onConferenceExit = function (reason, failed) { emedia.mgr.onConferenceExit = function (reason, failed) {
行 204: 行 235:
 <code javascript>​ <code javascript>​
 //​管理员变更 //​管理员变更
-emedia.mger.onAdminChanged = admin => {} //admin 管理员信息+emedia.mgr.onAdminChanged = admin => {} //admin 管理员信息 
 + 
 +//​监听弱网状态 
 +emedia.mgr.onNetworkWeak = streamId => {} //streamId 会议中的流 ID 
 + 
 +//​监听断网状态 
 +emedia.mgr.onNetworkDisconnect = streamId => {} //streamId 会议中的流 ID 
 </​code>​ </​code>​
-==== 用户A创建会议 ==== 
  
 +
 +====== 进阶功能 ======
 +===== 会议管理 =====
 +==== 1.创建会议并加入 ==== 
 +这里的创建会议 不同于快速集成中的加入会议,这里的是属于另一套逻辑,建议使用快速集成中的加入会议 \\
 +
 +** 注意: 如果只单纯的创建,没进行操作,则创建者不是会议的成员,没有相应的角色,不能进行其他操作 ** \\ 
 +
 +1.1 调用 emedia.mgr.createConference 方法创建会议
 <code javascript>​ <code javascript>​
-// confrType: 会议类型;password:​ 会议密码;rec:​是否录制;recMerge:是否合并录制;supportWechatMiniProgram:会议是否支持微信小程序 
-emedia.mgr.createConference(confrType,​ password, rec, recMerge, supportWechatMiniProgram).then(function(confr){ 
-  console.log(confr.confrId);​ 
-}).catch(function(error){ 
-}) 
-// 支持对象形式的参数 
 let params = { let params = {
      ​confrType, ​      ​confrType, ​
行 220: 行 260:
      ​rec, ​      ​rec, ​
      ​recMerge, ​      ​recMerge, ​
-     ​supportWechatMiniProgram, +     ​supportWechatMiniProgram 
-     maxTalkerCount:​2,//​会议最大主播人数 +     ... 其他参
-     ​maxVideoCount:​1,​ //​会议最大视频数 +
-     ​maxPubDesktopCount:​1,​ //​会议最大共享桌面数 +
-     ​maxAudienceCount:100 //​会议最大观众+
 } }
  
-const confr = await emedia.mgr.createConference(params)+const confr = await emedia.mgr.createConference(params);
 </​code>​ </​code>​
- +在 emedia.mgr.createConference 方法中,注意以下参数设置: 
-    ​注意: 如果A只单纯create,没进行join操作,则A不Conference的成员,没有相应的角色,不能进行其他操作 +  * confrType 会议类型 10:​普通会议模式、11:​大会议模式、12:​直播模式。number 必需 
-     +  * password 会议密码 string 必需 
-==== 用户A进入会议 ​====+  * rec 否开启通话录制 boolean 非必需 
 +  * recMerge 是否开启通话录制合并 boolean 非必需 
 +  * supportWechatMiniProgram 会议是否支持小程序端 boolean 非必需, 默认支持 
 +   
 +创建会议成功后,返回的参数 confr 结构如下:
 <code javascript>​ <code javascript>​
-emedia.mgr.joinConferenceWithTicket(confrId, ​ticketext).then(function(confr){+confr:{ 
 +    error: 0, 
 +    ​confrId: "​LBJ13H9WJJEVJTGL1U1PIQ00C2"​// 会议id 
 +    password: "​xxxx"​// 创建会议时设置的密码 
 +    role: 7, // 在会议中的角色 这里因为是创建者,所以是 7: 管理员 
 +    roleToken:"​eyJ0eXAiOiJKV1QiLCJhbG *** f1gg_QJWxhs-jqmuFok",​ //​创建者的token 
 +    type: 10 // 会议类型 
 +
 +</​code>​
  
-}).catch(function(error){ 
  
-})+1.2 调用 emedia.mgr.joinUsePassword 方法加入会议 
 +<code javascript>​ 
 +const join_result = await this.emedia.joinUsePassword(confrId,​ password// 参数为 创建会议成功后返回的 confrId和password 
 +</​code>​ 
 +加入会议成功后返回的结果,结构如下:​ 
 +<code javascript>​ 
 +  join_result:​ { 
 +     ​confrId:​ "​LBJ13H9WJJEVJTGL1U1PIQ00C7",​ // 会议id 
 +     ​joinId:​ "​LBJ13H9WJJEVJTGL1U1PIQ00C7M9",​ //​创建会议时设置的密码 
 +     ​password:​ "​0.010568535799199363",​ // 会议成员在会议中的身份id(唯一) 
 +     role, // 在会议中的角色 创建者加入会议永远是 7: 管理员,其他人加入返回的是 3: 主播 
 +     ​roleToken:​ "​eyJ0eXAiOiJK *** FaXuMVlqPOTofRRdE",​ // 加入会议者的token 
 +     type: 10 // 会议类型 
 +  }
 </​code>​ </​code>​
 +==== 2.开启录制、录制合并 ====
 +2.1 调用 emedia.mgr.createConference 创建会议时配置
 +<code javascript>​
 +let params = {
 +     ...
 +     rec: true,
 +     ​recMerge:​ true,
 +     ... 其他参数
 +}
  
-户也可以使用下列接口加入房间。可以指定房间的名称,并且在房间不存在时自动创建。 另外可以在加入会议时指定角色。 +const confr = await emedia.mgr.createConference(params);​ 
 +</​code>​ 
 +2.2 调用 emedia.mgr.joinRoom ​加入会议时配置\\ 
 +注意:只有第一个加入会议的人,配置的才有效\\ 
 +参数需要放到 config 对象中
 <code javascript>​ <code javascript>​
-/** +let params = 
- * 创建房间并加入 +     ​... 其他参数 
- * @method joinRoom +     ​config: { 
- * @param ​{Object} option +         ​rec: true, 
- * @param {string} option.roomName - 房间名称 +         ​recMerge: true 
- * @param {string} option.password - 房间密码 +     } 
- * @param {string} option.role - 进入房间时的角色 +}
- * @param {object} option.config - 扩展能力 可设置以下参数 +
- config:{  +
-                ​nickName,//​进入会议的昵称 +
-                ext: {}, //​扩展字段 用于自定义 +
-                ​rec: true, // 开启录制 +
-                recMerge: true, //​开启录制合并 +
-                ​supportWechatMiniProgram:​ true, //​是否支持微信小程序 +
-                ​maxTalkerCount:3 //​自定义会议最大主播人数 +
-                maxVideoCount:2 //​自定义会议最大视频数 +
-                maxAudienceCount:100 //​自定义会议最大观众数 +
-                maxPubDesktopCount:​ 1 //​自定义会议共享屏幕最大数+
  
-           } +const confr = await emedia.mgr.createConference(params);​ 
- */+</code>
  
 +rec: 是否开启录制,默认 false\\
 +recMerge: 是否开启录制合并,默认false
  
 +==== 3.邀请成员加入会议 ==== 
 +  SDK 不提供邀请接口。邀请的形式,完全可以由用户自行定义,可以是条文本消息,也可以是个控制消息等等。SDK 不做限制。实现方式可以参考官方 demo,通过群组消息邀请,具体代码 可查询 demo/​src/​components/​webrtc/​AddAVMemberModal.js 中的 70-89 行。
 +3.1 成员收到邀请加入会议 \\
  
-        try { +解析出邀请消息中带的 confrId 和 password ,调用 ** emedia.mgr.joinUsePassword ** 加入会议 
-            const user_room = await emedia.mgr.joinRoom(option);​ + 
-             +==== 4.管理员销毁会议 ​====  
-            user_room:  +调用 emedia.mgr.destroyConference 方法, ** 注意:只有管理员有权限,其他角色调用不生效 ** 
-            { +<code javascript>​ 
-                confrId: "​IM3U9Z0AHDYQTF8KNDAAD00C147" ​会议ID +await emedia.mgr.destroyConference(confrId); ​//confrId: ​会议Id
-                id: "​IM3U9Z0AHDYQTF8KNDAAD00C147"​ +
-                ​joinId:​ "​IM3U9Z0AHDYQTF8KNDAAD00C147M2"​ 在会议中的唯一ID +
-                ​mixed:​ false +
-                role: 7 //角色 +
-                ​roleToken:"​eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJlYXNlbW9iLWRlbW8j..."​ +
-                ​ticket:​ "​{\"​tktId\":​\"​IM3U9Z0AHDYQTF8KNDAAD00C147TK1\",​..." +
-                type: 10 //会议类型 +
-             } +
-                        +
-        } catch (error) {  +
-             +
-        }+
 </​code>​ </​code>​
-==== 管理员A邀请其他人加入会议 ====+其他人会收到会议结束的回调 
 +<code javascript>​ 
 +emedia.mgr.onConferenceExit ​reason ​=> {} // reason:退出会议的原因,因为会议被销毁了,所以这里应为 11: "​会议关闭"​ 
 +</​code>​ 
 + 
  
-SDK 不提供邀请接口。邀请的形式,完全可以由用户自行定义,可以是条文本消息,也可以是个控制消息等等。SDK 不做限制。实现方式可以参考官方 demo,通过群组消息邀请,具体代码 可查询 demo/​src/​components/​webrtc/​AddAVMemberModal.js ​ 中的 70-89 行。 +==== 5.设置会议人数限制 ​==== 
-==== 用户B接收到邀请加入会议 ====+5.1 在 emedia.mgr.joinRoom 方法中设置 
 +** 只有第一个加入房间的人员(也就是管理员),设置的才能生效 ** 
 +<code javascript>​ 
 +var params = { 
 +    ... 其他参数, 
 +    config:{ // 在 config 中指定字段  
 +      maxTalkerCount:3,​ 
 +      maxAudienceCount:100,​ 
 +      maxVideoCount:2,​ 
 +      maxPubDesktopCount:​ 1 
 +   } 
 +}
  
 +const user_room = await emedia.mgr.joinRoom(params);​
 +</​code>​
 +5.2 在 emedia.mgr.createConference(创建会议) 方法中设置
 +<code javascript>​
 +var params = {
 +    ... 其他参数, ​  
 +    maxTalkerCount:3
 +    maxVideoCount:2
 +    maxAudienceCount:100
 +    maxPubDesktopCount:​ 1
  
-用户B解析出邀请消息中带的 confrId 和 password ​调用 SDK 的 join 接口加入会议,成为会议成员且角色是 Speaker.+
 +const confr = await emedia.mgr.createConference(params);​ 
 +</​code>​ 
 +以上设置会议参数注意事项如下: 
 +  * maxTalkerCount:自定义会议最大主播人数默认 100,  
 +  * maxAudienceCount:自定义会议最大观众数 默认 600 
 +  * maxVideoCount:自定义会议最大视频数, 默认 9 
 +  * maxPubDesktopCount:​ 自定义会议共享屏幕最大数 默认 2
  
 +==== 6.设置会议昵称 ====
 +在 emedia.mgr.joinRoom 方法中设置
 <code javascript>​ <code javascript>​
-//javascript +var params = { 
-emedia.mgr.joinConference(confr, password, ext).then(function(confr){+    ... 其他参数,​ 
 +    config:{  
 +      ... 其他参数, 
 +      nickName: xxx, // string 
 +   } 
 +
 +const user_room = await emedia.mgr.joinRoom(params)
 +</​code>​
  
-}).catch(function(error){ +==== 7获取会议信息 ==== 
- +调用 ** emedia.mgr.selectConfr ** 方法获取会议信息  
-})+<code javascript>​ 
 +const confr_info = await emedia.mgr.selectConfr(confrId, password); // confrId:​会议id、password:​会议密码 
 +// 返回的参数 confr_info 结构如下: 
 +confr_info: ​
 +    ​confr:​ { 
 +        id: "​LBJ13H05522QATGIJKXUF800C45639",​ // 会议id 
 +        type: 10, // 会议类型 
 +        memTotal: 1, // 会议中总人数 主播和观众 
 +        audienceTotal:​ 0, // 观众人数 
 +        talkers: ["​018ae39 *** 663282d7495"​] // 主播的memName集合 
 +    } 
 +    error: 0 
 +}
 </​code>​ </​code>​
  
 +==== 8.CDN合流推流 ====
 +CDN推流是指将会议画面,合并到一张画布推送到远程CDN,其他人可以从CDN拉流而不用加入会议
 +== 8.1 开启CDN推流 ==
  
 +**CDN推流参数 liveCfg为必需 结构如下:**
 +<code javascript>​
 +let liveCfg = {
 +    cdn:'',​ //​推流地址、字符串;必需 ​
 +    layoutStyle:​ '​GRID'​ | '​CUSTOM',​ // 格子显示 | 自定义,必需
 +    canvas :{// canvas 参数在 layoutStyle == '​CUSTOM'​ 必填
 +           bgclr : 0x980000,//​背景色 980000 为 十六进制色值
 +           w : 640, //宽度
 +           h : 480, //高度
 +           fps: 20, //​输出帧率
 +           bps: 1200000, ​ //​输出码率
 +           ​codec:​ "​H264"​ //​视频编码,现在必须是H264
 +    }
 +}
 +</​code>​
  
-用户B成功加入会议后,会议中其他成员会收到回调 [emedia.mgr.onMemberJoined(memberB)] 
  
-==== 成员A发布音视频流 ====+**1.创建会议时指定 CDN推**
  
 +<code javascript>​
 +let option = {
 +    ...
 +    liveCfg // 创建CDN推流参数 ​
 +}
 +emedia.mgr.createConference(option)
 +</​code>​
  
-成员A和成员B都有发布流的权限+**2.加入房间时 指定CDN推** 
 +<code javascript>​ 
 +// 只有第一个加入房间人才能创建 CDN、以后加入的人指定CDN也无效 
 +let params = { 
 +       ​config:​{  
 +          ... 
 +          liveCfg // 创建CDN推流参数 
 +       } 
 +
 +emedia.mgr.joinRoom(params);​ 
 +</​code>​ 
 +== 8.2 多路推流 == 
 +通过 ** emedia.mgr.addLive ** 方法指定一路推流CDN,需要几路CDN,就调用几次方法 \\ 
 +** 注意:只有管理员,可创建 CDN **
  
 <code javascript>​ <code javascript>​
-/** +// confrId会议id必需 
- * constaints: {audiotruevideotrue} +// liveCfgcdn 配置必需 
- * videoTag 可缺失如果有 此次publish的媒体数据将会在这个video上显示 将会与stream绑定 +media.mgr.addLive(confrIdliveCfg); 
- * ext 用户自定义扩展,其他成员可以看到这个字段 +</code>
- *  +
- */ +
-emedia.mgr.publish(constaintsvideoTag, ext).then(function(pushedStream){ +
-    //stream 对象 +
-}).catch(function(error){+
  
-});+== 8.3 更新CDN布局 == 
 +<code javascript>​ 
 +// 只有管理员才能 更新布局。更新布局会 将layoutStyle 变为 CUSTOM 而且不可逆 
 +emedia.mgr.updateLiveLayout(confrId,​ liveId, regions)
  
-/** +// confrId 会议id 必需 
- * videoTag 可缺失时 +// liveId 推流CDN id, 必需 可通过 ​emedia.config.liveCfgs 获取 Array 
- * +
- *+
-emedia.mgr.publish(constaints,​ ext).then(function(pushedStream){ +
-    ​//stream 对象 +
-    //如果要将这个stream对象 显示,需要 ​emedia.mgr.streamBindVideo(videoTag,​ pushedStream) +
-}).catch(function(error){+
  
-});+regions:[ // 希望定义视频流 显示的配置集合 
 +    { 
 +       "​sid":​ stream_id,//​视频流的id 
 +       "​x":​ 320,//​距离 x 轴的距离 Number 
 +       "​y":​ 240,//​距离 y 轴的距离 Number 
 +       "​w":​ 960,//​宽度 Number 
 +       "​h":​ 720,//​高度 Number 
 +       "​style":​ "​fill"​ | "​AspectFit"​ //​视频显示模式 fill:​铺满、AspectFit:​原比例显示 
 +    ​},         
 +    .... 其他视频流配置(数组有几个项,就显示几个视频流) 
 +]
 </​code>​ </​code>​
-  *** 注意:A推流成功后,onStreamAdded 将被回调 *** 
-==== 其他成员收到通知并订阅流 ====  
  
-A成功发布数据流后,会议中其他成员会收到监听类回调[emedia.mgr.onStreamAdded]如果成员B想看成员A的音视频可以调用subscribe接口进行订阅+== 8.4 删除CDN == 
 +<code javascript>​ 
 +// 只有管理可操作 
 +//​confrId ​会议id 必需 
 +// liveId 推流CDN id, 必需 可通过 emedia.config.liveCfgs 获取 Array  
 +emedia.mgr.deleteLive(confrId,​ liveId)  
 +</​code>​ 
 +=== 9.海外代理 === 
 +** 开启集群部署SDK内部会自动请求代理实现通话最优 ** \\ 
 +调用 ​emedia.config 方法开启 
 +<code javascript>​ 
 +emedia.config({ 
 +   ... 
 +   ​useDeployMore:​ true // 默认:false 不开启 
 +   ​rtcConfigUrl:​ '​yourConfigUrl'​ // 自定义config文件路径, 默认有一个路径 
 +   ... 
 +}) 
 +</​code>​
  
 +==== 10. 取日志 ====
 +在浏览器控制台,输入 ** emedia.fileReport **,​敲下回车键会下载下一份日志文件
 <code javascript>​ <code javascript>​
-//​其他成员 +emedia.fileReport() //无参
-emedia.mgr.onStreamAdded = function ​(member, stream+
-   ​if(!stream.located()){ ​//stream.located() === true,         ​自己发送的据流 +
-      emedia.mgr.subscribe(member,​ stream, true, true, video) +
-   } +
-};+
 </​code>​ </​code>​
  
 +==== 11. 会议属性 ====
 +<code javascript>​
 +    // 用来自定义一些属性,广播给会议中的成员
 +    // 有人设置会议属性,所有的成员都能收到
 +  let options = {
 +            key:​username,​
 +            val:'​request_tobe_speaker'​
 +        }
 +      // a. 设置会议属性 ​
 +  emedia.mgr.setConferenceAttrs(options)
 +      // b. 删除会议属性 ​
 +  emedia.mgr.deleteConferenceAttrs(options)
 +      // c. 会议属性变更回调 ​
 +  emedia.mgr.onConfrAttrsUpdated = attrs => {} //attrs 会议属性集合 Array
 +</​code>​
  
-==== 成员B取消订阅流 ​====+==== 12.海外代理 ​==== 
 +  1v1通话支持不同集群区域的人员通话使用代理,减小延迟; 
 +  使用多集群代理需要音视频后台配置IP及端口的映射文件rtcconfig.json,​并禁用相关appkey的直连,​ 
 +  也可以指定 rtcconfig.json 的私有化部署 
 +调用 ** emedia.config 方法开启海外代理 **  
 +<code javascript>​ 
 +emedia.config({ 
 +   ... 
 +   ​useDeployMore:​ true, // 默认:false 不开启 
 +   ​rtcConfigUrl:​ 'your rtcConfigUrl'​ // 如果不设置,将使用 sdk 后台配置的代理文件 
 +}) 
 +</​code>​
  
-成员B如果不想再看成员A的音视频,可以调用SDK接口unsubscribe 
  
 +
 +
 +
 +
 +===== 音视频管理 =====
 +==== 1.设置通话参数 ====
 +发布本地媒体流时,可指定音视频的码率和分辨率,非必需。** 共享桌面不可指定 **
 <code javascript>​ <code javascript>​
-//emedia.mgr.hungup(stream);​ +var constaints = { 
-emedia.mgr.unsubscribe(stream);+     ​audio:​ {bitrate: 100},// 指定音频码率 
 +     ​video:​ { 
 +        width: { // 指定视频分辨率宽度 
 +             ​exact:​ 1280 
 +        }, 
 +        height: { // 指定视频分辨率高度 
 +             ​exact:​ 720 
 +        }, 
 +        bitrate: 200,// 指定视频码率 
 +     } 
 +} 
 +emedia.mgr.publish(constaints)
 </​code>​ </​code>​
  
-==== 成员A取消发布流 ​====+==== 2. 指定设备打开音视频 ​==== 
 +<code javascript>​ 
 +const devices = await emedia.mgr.mediaDevices();​ //​获取设备列表
  
-成员A可以调用unpublish接口取消自己已经发布的数据流操作成功,会议回调[emedia.mgr.onStreamRemoved:​removeStream:​] ,将对应的数据信息移除+    // 设备信息 
 +    device: Object {  
 +        deviceId: "​529a6fe76467d****9498ab22f5f362cd"​ // 设备ID 
 +        groupId: "​2b74c9b9ab99*****d513fbabc1e86b3c5d99f7f8a0c16"​ 
 +        kind: "​audioinput"​ | audiooutput | videoinput | videooutput // 设备类型 
 +        label: "​Internal Microphone (Built-in)"​ 
 +    } 
 +    constraints: { // 选择设备, 指定设备(只需要传入设备信息的deviceId属性,为String类型,其他的属性在推流时暂时用不) 
 +        audio: {deviceId: deviceId ? {exact: deviceId} : undefined}, //​判断如果deviceId存在那么就传入对象。 
 +        video: {deviceId: deviceId ? {exact: deviceId} : undefined} 
 +    }, 
 +     
 +const stream = await emedia.mgr.publish(constraints) // 推 
 +</​code>​
  
 +==== 3. 停止发布流 ====
 +调用 ** emedia.mgr.unpublish ** 方法停止自己已经发布的流
 <code javascript>​ <code javascript>​
-//emedia.mgr.hungup(pushedStream);​ +emedia.mgr.unpublish(pushedStream); ​// pushedStream:自己发布的流
-emedia.mgr.unpublish(pushedStream);+
 </​code>​ </​code>​
 +会议中人员(包括自己)会收到 流被移除的回调函数 ** emedia.mgr.onStreamRemoved **
 <code javascript>​ <code javascript>​
 emedia.mgr.onStreamRemoved = function (member, stream) { emedia.mgr.onStreamRemoved = function (member, stream) {
-   if(stream.located(){ ​//自己发布流 +   // ​member: 停止发布流人员信息 
-   }else{ ​//会议其他人发布的流 +   // ​stream:流的信息,stream.located() == true 代表是自己的流,false则为其他人的流
-   }+
 }; };
 </​code>​ </​code>​
  
-==== 成员B离开会议 ​====+==== 4. 停止订阅流 ​==== 
 +调用 ** emedia.mgr.unsubscribe ** 方法停止订阅别人的流 
 +<code javascript>​ 
 +emedia.mgr.unsubscribe(stream);​ // stream:已经订阅的流 
 +</​code>​
  
-成员B调用SDK接口exitConference离开会议,会议中的其他成员会收到回调[emedia.mgr.onMemberExited:​member:​]+==== 5. 通话过程音视频控制 ==== 
 +5.1 打开/​关闭自己视频 
 +<code javascript>​ 
 +const res_stream = await emedia.mgr.pauseVideo(own_stream) //​关闭视频 
 +const res_stream = await emedia.mgr.resumeVideo(own_stream) //​开启视频  
 +</​code>​
  
 +5.2 打开/​关闭自己的音频
 <code javascript>​ <code javascript>​
-emedia.mgr.exitConference();+const res_stream = await emedia.mgr.pauseAudio(own_stream// 关闭音频 
 +const res_stream = await emedia.mgr.resumeAudio(own_stream) // 开启音频
 </​code>​ </​code>​
  
-===== 共享桌面 ​===== +  执行开启/​关闭音视频方法时, 
-共享桌面,​仅支持PC Chrome浏览器或electron平台。+  * own_stream:自己已经发布的媒体流(不能是桌面流) 
 +  * res_stream:设置音视频成功后返回的流对象
  
 +** 执行上述操作后,会议中其他人员会收到流变化的回调 emedia.mgr.onMediaChanaged ​ ** \\
 +emedia.mgr.onMediaChanaged 应该在流变化之前监听
 <code javascript>​ <code javascript>​
-/** +var videoTag ​document.getElementById('#xxx'// 获取 video 标签 
- * let params ​{  +emedia.mgr.onMediaChanaged(videoTag, ​function(constaints, ​stream) { 
- ​* ​      ​videoConstaints,​  +     ​
- ​* ​      ​withAudio,​  +
- ​* ​      ​videoTag,​  +
- ​* ​      ext,  +
- ​* ​      ​confrId,​ +
- ​* ​      ​stopSharedCallback +
- ​* ​ } +
- */ +
- +
-/** +
- * videoConstaints {screenOptions:​ ['screen', '​window',​ '​tab'​]} or true +
- * withAudio: true 携带语音,false不携带 +
- * ext 用户自定义扩展,其他成员可以看到这个字段 +
- * stopSharedCallback 共享插件 点击【停止共享】的回调函数,做相应的处理(比如删除流...) +
- *+
-emedia.mgr.shareDesktopWithAudio(params).then(function(pushedStream){ +
-    //stream ​对象 +
-}).catch(function(error){ +
 }); });
- +// 回调函数中 constaints、stream 
-//electron平台 默认选择第一个屏幕,如果需要选择其他,需要重写方法 +constaints: ​
-emedia.chooseElectronDesktopMedia = function(sources,​ accessApproved)+    ​audio: true // true: 开启了音频,false:关闭了音频 
-    ​var firstSources = sources[0]; +    ​video: true // true: 开启了视频,false:关闭了视频
-    ​accessApproved(firstSources);​+
 } }
-</​code> ​+stream:媒体流变化后的 stream 对象 
 +</​code>​
  
-**注意:** ​ 
-%%在chrome浏览器中使用时,需要从%%[[https://​chrome.google.com/​webstore/​detail/​rtc-share-desktop/​ccahbcjalpomijfpjemdgpnbogofnlgl|chrome store]] 或者从[[https://​download-sdk.oss-cn-beijing.aliyuncs.com/​rtc_desktop_share.zip|环信服务器]] %%中下载插件,解压后在chrome浏览器中输入 chrome://​extensions/,​ 选择“Load unpacked” 选择解压后的文件夹中的1.0_0文件夹,加载插件。%% 
  
- +5.3 切换摄像头 ​
-===== 其他接口 ===== +
-==== 获取加入会议ticket ====+
 <code javascript>​ <code javascript>​
-emedia.mgr.getConferenceTkt(confrType, password).then(function(confr){ +// 随机切换摄像头 
-     +emedia.mgr.changeCamera(confrId).then(function(){ 
-}).catch(function(error){+      // 无参数 
 +}).catch(function(){
  
 }) })
-</code> ​+// 切换手机前后摄像头 
 +emedia.mgr.switchMobileCamera(confrId).then(function(){ 
 +     // 无参数 
 +}).catch(function(){
  
-==== 销毁会议 ​====+}) 
 +</​code>​ 
 +==== 6. 音视频网络状态监听 ​==== 
 +** 建议进入会议之前绑定网络状态监听函数 **
 <code javascript>​ <code javascript>​
-emedia.mgr.destroyConference(confrId).then(function(){ +//​监听弱网状态 
-     +emedia.mgr.onNetworkWeak = streamId => {} //streamId 会议中的流 ID
-}).catch(function(error){+
  
-}) +//​监听断网状态 
-</​code> ​+emedia.mgr.onNetworkDisconnect = streamId => {//streamId 会议中的流 ID 
 +</​code>​
  
-==== 踢人 ​==== +==== 7. 监听谁在说话 ​==== 
 +这是监听的video标签,\\ 
 +** 建议:在将video标签与stream绑定时(emedia.mgr.streamBindVideo),​ 调用 emedia.mgr.onSoundChanaged 方法 **
 <code javascript>​ <code javascript>​
-emedia.mgr.kickMembersById(confrmemberNames).then(function(){ +var videoTag = getElementById('#​xxx'​);​ 
-     +emedia.mgr.onSoundChanaged(videoTag,, function (meterData) {});
-}).catch(function(error){+
  
-}) +// 返回的参数 meterData 结构如下: 
-</​code> ​+meterData: { 
 +    instant: 0.26280892641627845 // instant 大约每50毫秒变化一次, 小于1的浮点数 
 +    slow: 0.06802768487276245 // slow大约是一秒钟内的平均音量,小于1的浮点数 
 +    clip: 0 
 +
 +</​code>​
  
-==== 获取会议信息 ​==== +==== 8. 共享桌面 ​==== 
-在会议进行中通过selectConfr 方法来查询会议信息,从而可以拿到主播列表,观众人数等信息+仅支持PC Chrome浏览器或electron平台\\ 
 + 
 +** SDK 3.2.1 版本 文档 **  
 +===  8.1无插件共享 === 
 + ​需要 SDK 3.2.1 版本开始支持并且 Chrome 72 或上版本
  
 <code javascript>​ <code javascript>​
-// confrId: 会议Id +const screenStream = await emedia.mgr.shareDesktopWithAudio(
-// password: 会议密码 +    confrId: ​confrId, ​// 会议ID, 必须 
-emedia.mgr.selectConfr(confrId, ​password).then(function(){ +    ​audio: false, ​ 
-     +})
-}).catch(function(error){+</​code>​
  
-}) +=== 8.2有插件共享 === 
-</​code> ​+
  
-==== 授权 ==== 
 <code javascript>​ <code javascript>​
-emedia.mgr.grantRole(confr,​ memberNames,​ role).then(function(){ +//在 sdk 内部会自动判断,浏览器是否含有 navigator.mediaDevices.getDisplayMedia API, 
-     +//​如果没有,将会跳转至 使用 插件的 API,如果没有安装插件将给出提示 
-}).catch(function(error){+</​code>​
  
-}) 
-</​code> ​ 
  
-==== 使用用户名密码加入会议,可自定义ext ====+=== 8.3分享音频 ​=== 
 <code javascript>​ <code javascript>​
-emedia.mgr.joinConference(confrId,​ password, ext).then(function(){ +//1版本起支持在 Windows 平台的 Chrome 浏览器 74 及以上版本同时共享屏幕和本地播放的背景音, 
-     +//2将 audio 设置为 true 即可 
-}).catch(function(error){+</​code>​
  
-}) +=== 8.4Electron 屏幕共享 === 
-</​code> ​+
  
-==== 使用ticket加入会议,可自定义ext ==== 
 <code javascript>​ <code javascript>​
-emedia.mgr.joinConference(confrIdticketext).then(function(){ +//1. sdk 内部会判断是否是在 electron 平台内 
-     +//2. electron 平台 会默认选择 第一个屏幕 
-}).catch(function(error){+//3. 如果需要自定义选择框,请重新定义  ​emedia.chooseElectronDesktopMedia 方法 
 +emedia.chooseElectronDesktopMedia = function(sourcesaccessApprovedaccessDenied){ 
 +       sources // Array 获取到的屏幕列表 ​         
 +       ​accessApproved(source)//​ 选中的 source 对象,进行分享 
 +       ​accessDenied()//​ 取消分享,关闭自定义框需要调用此方法 
 +} 
 +</​code>​
  
-}) +=== 8.5 停止共享桌面 ===
-</​code> ​+
  
-==== 退出会议 ==== 
 <code javascript>​ <code javascript>​
-emedia.mgr.exitConference();+停止共享桌面,执行 取消流的发布 ​emedia.mgr.unpublish(screenStream)
 </​code>​ </​code>​
  
-==== publish 媒体流 ====+ 
 +** SDK 3.2.1 之前版本 文档 ** 
 <code javascript>​ <code javascript>​
 /** /**
- ​* ​constaints: ​{audio: truevideo: true} + ​* ​let params =  
- * videoTag ​可缺失,如果有 此次publish的媒体数据将会在这个video上显示 将会与stream绑定 + ​* ​      ​videoConstaints,  
- * ext 用户自定义扩展,其他成员可以看到这个字段 + ​* ​      withAudio,  
- ​* ​+ ​* ​      videoTag 
 + ​* ​      ​ext 
 + ​* ​      confrId, 
 + ​* ​      ​stopSharedCallback 
 + ​* ​ }
  */  */
-emedia.mgr.publish(constaints,​ videoTag, ext).then(function(pushedStream){ 
-    //stream 对象 
-}).catch(function(error){ 
- 
-}); 
  
 /** /**
- ​* ​videoTag ​缺失时 + ​* ​videoConstaints {screenOptions:​ ['​screen',​ '​window',​ '​tab'​]} or true 
- *+ * withAudio: true 携带语音,false不携带 ​ 如携带语音,需自己调用关闭流,不会执行 stopSharedCallback 回调 
 + * ext 用户自定义扩展,其他成员以看到这个字段 
 + ​* ​stopSharedCallback 共享插件 点击【停止共享】的回调函数,做相应的处理(比如删除流...)
  */  */
-emedia.mgr.publish(constaints, ext).then(function(pushedStream){+emedia.mgr.shareDesktopWithAudio(params).then(function(pushedStream){
     //stream 对象     //stream 对象
-    //​如果需要将这个stream对象 显示,需要 emedia.mgr.streamBindVideo(videoTag,​ pushedStream) 
 }).catch(function(error){ }).catch(function(error){
  
 }); });
-</​code>​ 
  
-==== 取消publish ==== +//​electron平台 默认选择第一个屏幕,如果需要选择其他,需要重写方法 
-<code javascript>​ +emedia.chooseElectronDesktopMedia = function(sources, accessApproved)
-emedia.mgr.unpublish(pushedStream); +    var firstSources = sources[0]
-或 +    ​accessApproved(firstSources); 
-emedia.mgr.hungup(pushedStream);+}
 </​code> ​ </​code> ​
  
-==== 订阅 ​====+**注意:**  
 +%%在chrome浏览器中使用时,需要从%%[[https://​chrome.google.com/​webstore/​detail/​rtc-share-desktop/​ccahbcjalpomijfpjemdgpnbogofnlgl|chrome store]] 或者从[[https://​download-sdk.oss-cn-beijing.aliyuncs.com/​rtc_desktop_share.zip|环信服务器]] %%中下载插件,解压后在chrome浏览器中输入 chrome://​extensions/,​ 选择“Load unpacked” 选择解压后的文件夹中的1.0_0文件夹,加载插件。%% ​  
 +   
 +===== 角色管理 ​===== 
 +可通过 emedia.mgr.Role 获取会议中的角色类型
 <code javascript>​ <code javascript>​
-/** +emedia.mgr.Role:​ { 
- * subSVideo ​看视频 +    ADMIN: 7, // 会议管理员:​ 能创建会议,销毁会议,移除会议成员,切换其他成员的角色 
- * subSAudio ​看音频 +    ​TALKER:​ 3, // 主播: 能上传自己的音视频,能观收听其他主播的音视频,即能发布流和订阅流) 
- * videoTag ​缺失,如果有 将与stream绑,并播放stream +    ​AUDIENCE:​ 1 // 观众: 只能观收听,即只能订阅流 
- */ +}; 
-emedia.mgr.subscribe(member, stream, subSVideo, subSAudio, videoTag).then(function(){ +// 议中义更加语义化的判断: 
-    //stream 对象 +if(member.role == emedia.mgr.Role.ADMIN){ } 
-}).catch(function(error){+等同于 
 +if(member.role == 7){ } 
 +</code> 
 +  ​在以下申请主播和申请管理员的过程,请注意: 
 +  1.从发起申请到管理员回复(同意或拒绝),​是一个完整的过程 
 +  2.因此可以认为:当管理员收到请求,会收到两个函数参数(同意和拒绝),用于管理员调用 
 +  3.当管理员处理了请求(同意或拒绝),申请者会收到处理结果(同意或拒绝),未处理则不会收到 
 +  
 +** 以下方法非回调函数均为异步函数 ** 
 +<code javascript>​ 
 +  try { 
 +    await emedia.mgr.xxx;​ 
 +  } catch(error) { 
 +      
 +// 以下出现的参数注解 
 +  confrId: 会议id 
 +  memberId: 与会人员的id,member 中的id 
 +  nickName: 昵称 
 +</​code>​ 
 +==== 1. 观众申请成为主播 ==== 
 +观众通过调用 ** emedia.mgr.requestToTalker ** 方法申请主播 
 +<code javascript>​ 
 +// 观众上麦申请方法 
 +emedia.mgr.requestToTalker(confrId) ​
  
-}); +// 管理员收到上麦申请的回调 (主播不会收到这个回调) 
-emedia.mgr.triggerSubscribe(videoTagsubSVideosubSAudio).then(function(){ +emedia.mgr.onRequestToTalker = function(applicatagreeCallbackrefuseCallback
-    //stream 对象 +   /* 
-}).catch(function(error){+    * applicat { memberId, nickName } object 申请者信息 
 +    * agreeCallback 管理员同意的回调 示例:agreeCallback(memberIdmemberId 申请者 id 必需 
 +    ​* refuseCallback 管理员拒绝的回调 示例:refuseCallback(memberId) memberId 申请者 id必需 
 +   *
 +} 
 +    
 +// 观众收到 上麦申请的回复  
 +emedia.mgr.onRequestToTalkerReply = function(result) { 
 +   // result 0: 同意 1: 拒绝 
 +}  
 +</​code>​
  
-}); +==== 2.主播申请成为管理员 ==== 
-也可以 +<code javascript>​ 
-//仅订阅音频 +// 主播申请管理员 
-emedia.mgr.triggerResumeAudio(videoTag).then(function(){ +emedia.mgr.requestToAdmin(confrId); 
-}).catch(function(error){ +  
-}); +//管理员收到申请管理员的回调 (主播不会收到这个回调) 
-//仅暂停订阅音频 +emedia.mgr.onRequestToAdmin = function(applicat, agreeCallback,​ refuseCallback) { 
-emedia.mgr.triggerPauseAudio(videoTag).then(function(){ +   /* 
-}).catch(function(error){ +    * applicat ​memberId, nickName ​object 申请者信息 
-}); +    * agreeCallback 管理员同意的回调 示例:agreeCallback(memberIdmemberId 申请者 id 必需 
- +    * refuseCallback 管理员拒绝的回调 示例:refuseCallback(memberIdmemberId 申请者 id必需 
-//​仅订阅视频 +   *
-emedia.mgr.triggerResumeVideo(videoTag).then(function(){ +}
-}).catch(function(error)+
-}); +
-//​仅暂停订阅视频 +
-emedia.mgr.triggerPauseVideo(videoTag).then(function(){ +
-}).catch(function(error){ +
-});+
  
 +// 主播收到 申请管理员的回复 ​
 +emedia.mgr.onRequestToAdminReply = function(result) {
 +   // result 0: 同意 1: 拒绝
 +}
 </​code>​ </​code>​
  
-==== stream video 绑定 ​====+====3.授权 ​==== 
 +** 只有管理员有权限授权 ** \\ 
 +调用 方法改变与会人员的角色(可升可降)
 <code javascript>​ <code javascript>​
-//stream 与 video绑定后,video才可以播放stream;并且可以通过触发/监听video上事件,来达到对stream的便捷操作 +var option = { 
-emedia.mgr.streamBindVideo(streamvideoTag); +    confr:, ​//会议对象 Object 必需 
-</​code>​+    memberNames:​ /被授权人员memberName集合(可同时给多人授权)Array 
 +    role: // 需要授权成什么角色 Number 必需 
 +} 
 +emedia.mgr.grantRole(option.confroption.memberNames,​ option.role
 +</​code> ​
  
-==== 获取stream绑定的video ​====+==== 4.角色降级 ​==== 
 +** 注意:只能角色降级 从管理员到主播、从主播到观众、从管理员到观众,不可逆向操作 ** \\ 
 +与会成员调用 ** emedia.mgr.degradeRole ** 方法就会角色降级
 <code javascript>​ <code javascript>​
-emedia.mgr.getBindVideoBy(stream);+//[memName] 与会人员的memName、 toRole 想要 达到的角色 
 +emedia.mgr.degradeRole(confrId, [memName], toRole); 
 </​code>​ </​code>​
  
-==== 切换摄像头====+==== 5.管理员踢人 ​====  
 +** 只有管理员可踢人 **
 <code javascript>​ <code javascript>​
-// 随机切换摄像头 +// confr: 会议对象,必需 Object 
-emedia.mgr.changeCamera().then(function(){ +// memberNames:​ 被踢掉人员的 memberName, 必需 Array  
-      // 无参数 +emedia.mgr.kickMembersById(confr, memberNames
-}).catch(function(){+</code> ​
  
-}) +==== 6.管理员执行全体静音/取消全体静音 ==== 
-// 切换手机前后摄像头 +** 只有管理员可操作,其他角色操作不生效,管理员不会被静音 ** \\
-emedia.mgr.switchMobileCamera().then(function(){ +
-     // 无参数 +
-}).catch(function(){+
  
-}) +6.1管理员静音全体 
-</code>+<​code ​javascript 
 +await emedia.mgr.muteAll(confrId);​ // confrId: 会议Id
  
-==== 暂停/恢复自己视频 ====+// 主播收到回调  
 +emedia.mgr.onMuteAll ​() => { 
 +    ​// 在收到回调后,需要在程序中执行关闭麦克风逻辑(emedia.mgr.pauseAudio(own_stream)) 
 +
 +</​code>​ 
 +6.2取消全体静音
 <code javascript>​ <code javascript>​
-emedia.mgr.pauseVideo(pubS).then(function(){+await emedia.mgr.unmuteAll(confrId); // confrId: 会议Id
  
-}).catch(function(){+//​主播收到回调  
 +emedia.mgr.onUnMuteAll = () => { 
 +     // 在收到回调后,需要在程序中执行关闭麦克风的逻辑(emedia.mgr.resumeAudio(own_stream)) 
 +
 +</​code>​ 
 +==== 7.管理员指定成员静音/​取消指定成员静音 ==== 
 +** 只有管理员可操作,其他角色操作不生效 ** \\
  
-}) +7.1管理员指定成员静音 
-等价于 +<code javascript>​ 
-emedia.mgr.triggerPauseVideo(localVideoTag).then(function(){+emedia.mgr.muteBymemberId(confrId, memberId);// memberId 被静音主播的memberId
  
-}).catch(function(){ +// 单个主播被管理员静音的回调(只他自己收到回调) 
- +emedia.mgr.onMuted = () => 
-})+     // 在收到回调后,需要在程序中执行关闭麦克风的逻辑(emedia.mgr.pauseAudio(own_stream)) 
 +}
 </​code>​ </​code>​
 +7.2管理员取消指定成员静音
 <code javascript>​ <code javascript>​
-emedia.mgr.resumeVideo(pubS).then(function(){+emedia.mgr.unmuteBymemberId(confrId, memberId);// memberId 被取消静音主播的memberId
  
-}).catch(function(){+// 单个主播被管理员取消静音的回调 (只他自己收到回调) 
 +emedia.mgr.onUnmuted = () =>  
 +     // 在收到回调后,需要在程序中执行关闭麦克风的逻辑(emedia.mgr.resumeAudio(own_stream)) 
 +
 +</​code>​
  
-}) +==== 8.本身角色变更回调 ==== 
-等价于 +** 如果想要变更自己的角色,需要向管理员申请,管理员同意后,角色就会变更 ** 
-emedia.mgr.triggerResumeVideo(localVideoTag).then(function(){+<code javascript>​ 
 +emedia.mgr.onRoleChanged = function (role) { 
 +     // role: 变更后的角色 
 +}; 
 +</​code>​
  
-}).catch(function(){ +==== 9.管理员变更回调 ==== 
- +** 当会议中的管理员变更时,与会人员都会收到这个回调 ** 
-})+<code javascript>​ 
 +emedia.mgr.onAdminChanged = admin => {//admin 管理员信息
 </​code>​ </​code>​
  
 +===== 其他接口 =====
 ==== 抓取 video图像,并保存 ==== ==== 抓取 video图像,并保存 ====
 <code javascript>​ <code javascript>​
行 690: 行 980:
 //​用来对onFocusExpoRemoteWhenClickVideo的撤销 //​用来对onFocusExpoRemoteWhenClickVideo的撤销
 emedia.mgr.offEventAtTag(videoTag);​ emedia.mgr.offEventAtTag(videoTag);​
-</​code>​ 
- 
-==== video监听的事件 ==== 
-<code javascript>​ 
-//​video的音频视频开关,将会触发 
-emedia.mgr.onMediaChanaged(videoTag,​ function (constaints) { 
-    $div.find("#​aoff"​).html(constaints.audio ? "​有声"​ : "​无声"​);​ 
-    $div.find("#​voff"​).html(constaints.video ? "​有像"​ : "​无像"​);​ 
- 
-    //​可以通过video标签 得知发布方 是否关闭 音频 视频 
-    console.warn($(videoTag).attr("​easemob_stream"​),​ "​aoff",​ $(videoTag).attr("​aoff"​));​ 
-    console.warn($(videoTag).attr("​easemob_stream"​),​ "​voff",​ $(videoTag).attr("​voff"​));​ 
-}); 
-</​code>​ 
- 
-<code javascript>​ 
-//​video的声音大小出发,比如谁在说话 
-emedia.mgr.onSoundChanaged(videoTag,,​ function (meterData) { 
-    var $volume = $div.find('#​volume'​);​ 
- 
-    $volume.html(meterData.instant.toFixed(2) 
-        + " " + meterData.slow.toFixed(2) 
-        + " " + meterData.clip.toFixed(2) 
-        + " " + (meterData.trackAudioLevel ? parseFloat(meterData.trackAudioLevel).toFixed(4) : "​--"​) 
-        + " " + (meterData.trackTotalAudioEnergy ? parseFloat(meterData.trackAudioLevel).toFixed(4) : "​--"​) 
-    ); 
-}); 
 </​code>​ </​code>​
  
行 757: 行 1020:
 </​code>​ </​code>​
  
-===== 进阶功能 ​=====   +====== ​客户端api ​====== 
-==== 房间内动作消息 ​==== +多人音视频通话的API包括以下接口 
-<code javascript>​ +  ​* createConference:​ 创建会议 
-// 以下方法非回调函数均为异步函数 +  ​* destroyConference:销毁会议 
-  ​try { +  ​* getConferenceInfo:获取会议信息 
-    await emedia.mgr.xxx;​ +  ​* publish:发布媒体流 
-  ​} catch(error) { } +  ​* unpublish:取消发布媒体流 
-      +  * grantRole:改变角色 
-// 以下出现的参数注解 +  * joinConference:通过password 加入会议 
-  ​confrId: ​会议id +  * kickMembersById:通过id踢出成员
-  ​memberId: 与会人员的id +
-  ​nickName: 昵称 +
-</​code>​ +
-=== 1.上麦申请 === +
-<code javascript>​ +
-// 观众上麦申请方法 +
-emedia.mgr.requestToTalker(confrId) ​+
  
-// 主持人收到上麦申请的回调 (主播不会收到这个回调) +** 以下方法均为 ​emedia.mgr ​对象属性方法: **
-emedia.mgr.onRequestToTalker = function(applicat,​ agreeCallback,​ refuseCallback) { +
-   /* +
-    * applicat { memberId, nickName } object 申请者信息 +
-    * agreeCallback 主持人同意回调 示例agreeCallback(memberId) memberId 申请者 id 必需 +
-    ​refuseCallback 主持人拒绝的回调 示例:refuseCallback(memberId) memberId 申请者 id必需 +
-   *+
-+
-    +
-// 观众收到 上麦申请的回复  +
-emedia.mgr.onRequestToTalkerReply = function(result) { +
-   // result 0: 同意 1: 拒绝 +
-}  +
-</​code>​+
  
-=== 2.主播申请主持人 === +^方法 ​     ^功能 
-<code javascript>​ +|[[http://webim-h5.easemob.com/emedia/jsdoc/out/global.html#​createConference | createConference]]| 创建会议 | 
-// 主播申请主持人 +|[[http://webim-h5.easemob.com/emedia/jsdoc/out/global.html#​destroyConference | createConference]]| 销毁会议| 
-emedia.mgr.requestToAdmin(confrId);​ +|[[http://webim-h5.easemob.com/emedia/jsdoc/out/global.html#​getConferenceInfo | getConferenceInfo]]|获会议信息 | 
-  +|[[http://webim-h5.easemob.com/emedia/jsdoc/out/global.html#​grantRole | grantRole]]|改变角色 ​| 
-//​主持人收到申请主持人的回调 (主播不会收到这个回调) +|[[http://webim-h5.easemob.com/emedia/jsdoc/out/global.html#​joinConference | joinConference]]|通过password 加入会议 ​| 
-emedia.mgr.onRequestToAdmin = function(applicat,​ agreeCallback,​ refuseCallback) { +|[[http://webim-h5.easemob.com/emedia/jsdoc/out/global.html#​kickMembersById | kickMembersById]]|通过id踢出成员 | 
-   /+|[[http://webim-h5.easemob.com/emedia/jsdoc/out/global.html#​publish | publish]]|发布媒体| 
-    * applicat { memberId, nickName } object 申请者信息 +|[[http://webim-h5.easemob.com/emedia/jsdoc/out/global.html#​unpublish | unpublish]]|取消发布媒体流|
-    * agreeCallback 主持人同意的回调 示例:agreeCallback(memberId) memberId 申请者 id 必需 +
-    * refuseCallback 主持人拒绝的回调 示例:refuseCallback(memberId) memberId 申请者 id必需 +
-   */ +
-+
- +
-// 主播收到 申请主持人的回复  +
-emedia.mgr.onRequestToAdminReply = function(result) { +
-   // result 0同意 1: 拒绝 +
-+
-</code> +
-=== 3.主持人执行全体静音 === +
-<code javascript>​ +
-// 只有主持人可操作,其他角色操作不生效,主持人不会被静音  +
- +
-// 主持人静音全体 +
-await emedia.mgr.muteAll(confrId);​ +
- +
-// 主播收到回调  +
-emedia.mgr.onMuteAll = () => { } +
-</code> +
-=== 4.主持人执行取消全体静音 === +
-<code javascript>​ +
-// 只有主持人可操作,其他角色操作不生效 +
- +
-// 主持人取消全体静音  +
-await emedia.mgr.unmuteAll(confrId);​ +
- +
-//主播收到回调  +
-emedia.mgr.onUnMuteAll = () => { } +
-</code> +
-=== 5.主持人静音单个主播 === +
-<code javascript>​ +
-// 主持人静音单个主播 +
-emedia.mgr.muteBymemberId(confrId,​ memberId);// memberId 被静音主播的memberId +
- +
-// 单个主播被管理员静音的回调(只他自己收到回调) +
-emedia.mgr.onMuted = () => { } +
-</​code>​ +
-=== 6.主持人消静音 某个主播 === +
-<code javascript>​ +
-// 主持人取消静音单个主播 +
-emedia.mgr.unmuteBymemberId(confrId,​ memberId);// memberId 被取消静音主播的memberId +
- +
-// 单个主播被管理员取消静音的回调 (只他自己收到回调) +
-emedia.mgr.onUnmuted = () => { } +
-</​code>​ +
-=== 7.角色降级接口 === +
-<code javascript>​ +
-// 只能角色降级 从主持人到主播、从主播到观众、从主持人到观众,不可逆向操作  +
- +
-//[memName被降级人员的memName、 toRole 想要 达到的角色 +
-emedia.mgr.degradeRole(confrId, ​[memName], toRole);  +
-</code> +
-   +
-==== 集群代理 ==== +
-<code javascript>​ +
-//​开启集群部署,SDK内部会自动请求代理,实现通话最优 +
- +
-//​开启方式 +
-emedia.config({ +
-   ... +
-   ​useDeployMore:​ true // 默认:false 不开启 +
-   ... +
-}) +
-</code> +
-==== 合流推流 CDN ==== +
-=== 1.创建CDN === +
-<code javascript>​ +
-// 创建会议、加入房间时都可以创建CDN +
- +
-// 添加一个 liveCfg 参数 +
-liveCfg = { +
-    cdn:'',​ //​推流地址、字符串;必需  +
-    layoutStyle:​ '​GRID' ​'​CUSTOM',​ // 格子显示 | 自定义,必需 +
-    canvas ​:{// canvas 参数在 layoutStyle == '​CUSTOM'​ 必填 +
-           bgclr : 0x980000,//​背景色 980000 为 十六进制色值 +
-           w : 640, //宽度 +
-           h : 480, //高度 +
-           fps: 20, //​输出帧率 +
-           bps: 1200000, ​ //​输出码率 +
-           ​codec:​ "​H264"​ //​视频编码,现在必须是H264 +
-    } +
-+
- +
-//​创建会议时指定 CDN推流 +
-let option = { +
-    confrType,  +
-    password,  +
-    ​..+
-    liveCfg ​// 创建CDN推流参数  +
-+
-emedia.mgr.createConference(option) +
- +
-// 加入房间时 指定CDN推 +
-// 只有第一个加入房间的人才能创建 CDN、以后加入的人指定CDN也无效 +
-let params = { +
-       ​roomName,​ +
-       ​password,​ +
-       ..+
-       config:{  +
-          ... +
-          liveCfg ​// 创建CDN推流参数 +
-       } +
-+
-emedia.mgr.joinRoom(params);​ +
-</code> +
-=== 2.更新CDN布局 === +
-<code javascript>​ +
-// 只有管理员才能 更新布局。更新布局会 将layoutStyle 变为 CUSTOM 而且不可逆 +
- +
-emedia.mgr.updateLiveLayout(confrId,​ regions) +
- +
-// confrId 会议id +
-regions:​[ ​// 希望定义视频流 显示的配置集合 +
-    { +
-       "​sid"​stream_id,//视频流的id +
-       "​x":​ 320,//​距离 x 轴的距离 Number +
-       "​y":​ 240,//​距离 y 轴的距离 Number +
-       "​w":​ 960,//​宽度 Number +
-       "​h":​ 720,//​高度 Number +
-       "​style":​ "​fill"​ | "​AspectFit"​ //​视频显示模式 fill:​铺满、AspectFit:​原比例显示 +
-    },  +
-    ​.... 其他视频流配置(数组有几个项,就显示几个视频流) +
-+
-</code> +
-=== 3.删除CDN === +
-<code javascript>​ +
-// 只有管理员可操作 +
-emedia.mgr.deleteLive(confrId) ​//confrId 会议id +
-</code>+