差别

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

到此差别页面的链接

后一修订版
前一修订版
im:web:draft:multiuserconference [2019/03/11 06:55]
jk 创建
— (当前版本)
行 1: 行 1:
-====== 多人实时通话 ====== 
  
----- 
- 
-===== 产品简介 ===== 
- 
- ​为满足不同场景需求,[[https://​github.com/​easemob/​webim/​tree/​master/​webrtc/​dist/​EMedia_x1v1.js|EMedia_x1v1.js]]不仅提供了对1v1通话的支持,而且提供了多人通话功能。多人实时音视频会议划分了不同的类型,不同类型对应了不同场景,使你能够轻松地将实时音视频功能集成到你的应用或者网站中。本片将介绍多人音视频使用接入。可参照[[https://​github.com/​easemob/​webim|demo]]。 
- ​在创建会议时可以传入以下几种类型: 
- 
-      - Communication:普通通信会议,最多支持参会者6人,会议里的每个参会者都可以自由说话和发布视频,该会议类型在服务器不做语音的再编码,音质最好,适用于远程医疗,在线客服等场景; 
-      - Large Communication:大型通信会议,最多参会者30人,会议里的每个参会者都可以自由说话,最多支持6个人发布视频,该会议模式在服务器做混音处理,支持更多的人说话,适用于大型会议等场景; 
-      - Live:互动视频会议,会议里支持最多6个主播和600个观众,观众可以通过连麦与主播互动,该会议类型适用于在线教育,互动直播等场景。 
- 
- 
-==== 产品特性 ==== 
- 
-    *支持现代浏览器:Chrome/​50+、Safari/​11+、Firefox; 
- 
-    *遵守:UMD通用模块规范,支持require导入; 
- 
-    *支持Promise; 
- 
-    *支持手机端和 Web 端互通,极大方便开发者的全平台业务; 
- 
-    *依赖https站点 
- 
-==== 音视频通信的简要步骤 ==== 
- 
-SDK 能够支持音频和视频通信。创建音视频通信的过程简单来说,可以分为以下几步: 
- 
-    1. 初始化 SDK,设置监听代理 
-    2. create: 创建会议 
-    3. join: 加入会议 
-    4. pub: 发布音视频数据流 
-    5. sub: 订阅并播放音视频数据流 
-    6. leave: 离开会议 
-==== 基本知识 ==== 
- 
-emedia.mgr.ConfrType:多人会议类型 
-    1. Communication:普通通信会议,最多支持参会者6人,成员都可以自由说话和发布视频,成员角色Speaker 
-    2. Large Communication:大型通信会议,最多参会者30人,成员都可以自由说话和发布视频,成员角色Speaker 
-    3. Live:互动视频会议,会议里支持最多6个主播和600个观众 
-<code javascript>​ 
-emedia.mgr.ConfrType = { 
-   ​COMMUNICATION:​ 10, //​普通会议模式 
-   ​COMMUNICATION_MIX:​ 11, //​大会议模式 
-   LIVE: 12, //​直播模式 
-}; 
-</​code>​ 
-<code javascript>​ 
-emedia.mgr.Role = { 
-    ADMIN: 7, // 能创建会议,销毁会议,移除会议成员,切换其他成员的角色,订阅流,发布流 
-    TALKER: 3, // 能上传自己的音视频,能观看收听其他主播的音视频,即能发布流和订阅流) 
-    AUDIENCE: 1 // 观众Audience:只能观看收听音视频,即只能订阅流 
-}; 
-</​code>​ 
-<code javascript>​ 
-//​conference|confr 
-{ 
-    confrId:"​TS_X296786295944036352C27",​ 
-    id:"​TS_X296786295944036352C27",​ 
-    password: "​password123", ​ 
-    roleToken:"​roleToken",​ 
-    ticket:"​ticket",​ 
-    type:12 
-}; 
-</​code>​ 
-<code javascript>​ 
-//member 
-{ 
-    "​ext":​{ //​emedia.mgr.joinConference(confrId,​ password, {role: '​admin'​})/​* 用户可自定义扩展字段*/​);​ 
-        "​role":"​admin"​ 
-    }, 
-    "​id":"​MS_X197721744293023744C19M197756407719972865VISITOR",​ 
-    "​globalName":"​easemob-demo#​chatdemoui_yss000@easemob.com",​ 
-    "​name":​ "​yss000"​ 
-} 
-</​code>​ 
-<code javascript>​ 
-//stream 
-{ 
-    "​id":"​RTC2__Of_C19M197756407719972865VISITOR",​ 
-    "​voff":​0,​ //1 视频关闭 ​ 
-    "​aoff":​0,​ //1 音频关闭 ​ 
-    "​memId":"​MS_X197721744293023744C19M197756407719972865VISITOR",​ 
-    "​owner":​ member ,//​member对象 
-    "​rtcId":"​RTC1"​ 
-} 
-var islocated = stream.located();​ //islocated true 本地媒体流 
-</​code>​ 
-    ​ 
-    注意: 
-    >> 每个人必须调用join接口成功后,才算是加入会议(即成为会议成员)。会议成员才允许进行其他操作比如订阅流、发布流等 
-    >> 成员如果想改变自己角色,必须想办法通知管理员,只有管理员才能修改 
- 
-===== 多人通信会议功能实现 ===== 
- 
-如何使用SDK实现多人实时音视频会议 
-  
-Communication和Large Communication除了最大成员数不一样,流程几乎是一样的。以下是从创建会议到离开会议完整的流程讲解: 
- 
-==== 设置SDK回调 ==== 
- 
-进入会议之前,设置SDK回调后,可获知成员加入或离开会议,数据流更新等。 
- 
-<code javascript>​ 
-//​有人加入会议,其他人调用joinXX等方法,如果加入成功,已经在会议中的人将会收到 
-emedia.mgr.onMemberJoined = function (member) { 
-    ​ 
-}; 
-</​code>​ 
-<code javascript>​ 
-//​有人退出会议 
-emedia.mgr.onMemberExited = function (member) { 
-    ​ 
-}; 
-</​code>​ 
-<code javascript>​ 
-//​有媒体流添加;比如 自己调用了publish方法(stream.located() === true时),或其他人调用了publish方法。 
-emedia.mgr.onStreamAdded = function (member, stream) { 
- 
-}; 
-</​code>​ 
-<code objc> 
-//​有媒体流移除 
-emedia.mgr.onStreamRemoved = function (member, stream) { 
- 
-}; 
-</​code>​ 
-<code javascript>​ 
-//​角色改变 
-emedia.mgr.onRoleChanged = function (role) { 
- 
-}; 
-</​code>​ 
-<code javascript>​ 
-//​会议退出;自己主动退 或 服务端主动关闭; 
-emedia.mgr.onConferenceExit = function (reason, failed) { 
-    reason = (reason || 0); 
-    switch (reason){ 
-        case 0: 
-            reason = "​正常挂断";​ 
-            break; 
-        case 1: 
-            reason = "​没响应";​ 
-            break; 
-        case 2: 
-            reason = "​服务器拒绝";​ 
-            break; 
-        case 3: 
-            reason = "​对方忙";​ 
-            break; 
-        case 4: 
-            reason = "​失败,​可能是网络或服务器拒绝";​ 
-            if(failed === -9527){ 
-                reason = "​失败,​网络原因";​ 
-            } 
-            if(failed === -500){ 
-                reason = "​Ticket失效";​ 
-            } 
-            if(failed === -502){ 
-                reason = "​Ticket过期";​ 
-            } 
-            if(failed === -504){ 
-                reason = "​链接已失效";​ 
-            } 
-            if(failed === -508){ 
-                reason = "​会议无效";​ 
-            } 
-            if(failed === -510){ 
-                reason = "​服务端限制";​ 
-            } 
-            break; 
-        case 5: 
-            reason = "​不支持";​ 
-            break; 
-        case 10: 
-            reason = "​其他设备登录";​ 
-            break; 
-        case 11: 
-            reason = "​会议关闭";​ 
-            break; 
-    } 
-}; 
-</​code>​ 
- 
- 
-==== 用户A创建会议 ==== 
- 
-<code javascript>​ 
-emedia.mgr.createConference(confrType,​ password).then(function(confr){ 
-}).catch(function(error){ 
-}) 
-</​code>​ 
- 
-    注意: 如果A只单纯的create,没进行join操作,则A不是Conference的成员,没有相应的角色,不能进行其他操作 
-    ​ 
-==== 用户A进入会议 ==== 
-<code javascript>​ 
-emedia.mgr.joinConferenceWithTicket(confrId,​ ticket, ext).then(function(confr){ 
- 
-}).catch(function(error){ 
- 
-}) 
-</​code>​ 
-==== 管理员A邀请其他人加入会议 ==== 
- 
-SDK没有提供邀请接口,你可以自己实现,比如使用环信IM通过发消息邀请,比如通过发邮件邀请等等。 
- 
-至于需要发送哪些邀请信息,可以参照SDK中的join接口,目前是需要Conference的confrId和password 
- 
-比如用环信IM发消息邀请 
-<code javascript>​ 
-WebIM.call.inviteConference(confrId,​ password, jid, gid) 
-</​code>​ 
- 
-    注意:使用环信IM邀请多个人时,建议使用群组消息。如果使用单聊发消息请注意每条消息中间的时间间隔,以防触发环信的垃圾消息防御机制 
-==== 用户B接收到邀请加入会议 ==== 
- 
- 
-用户B解析出邀请消息中带的confrId和password,调用SDK的join接口加入会议,成为会议成员且角色是Speaker. 
- 
-<code javascript>​ 
-//​javascript 
-emedia.mgr.joinConference(confr,​ password, ext).then(function(confr){ 
- 
-}).catch(function(error){ 
- 
-}) 
-</​code>​ 
- 
- 
- 
-用户B成功加入会议后,会议中其他成员会收到回调[emedia.mgr.onMemberJoined(memberB)] 
- 
-==== 成员A发布音视频流 ==== 
- 
- 
-成员A和成员B都有发布流的权限 
- 
-<code javascript>​ 
-/** 
- * constaints: {audio: true, video: true} 
- * videoTag 可缺失,如果有 此次publish的媒体数据将会在这个video上显示 将会与stream绑定 
- * ext 用户自定义扩展,其他成员可以看到这个字段 
- ​* ​ 
- */ 
-emedia.mgr.publish(constaints,​ videoTag, ext).then(function(pushedStream){ 
-    //stream 对象 
-}).catch(function(error){ 
- 
-}); 
- 
-/** 
- * videoTag 可缺失时 
- * 
- */ 
-emedia.mgr.publish(constaints,​ ext).then(function(pushedStream){ 
-    //stream 对象 
-    //​如果需要将这个stream对象 显示,需要 emedia.mgr.streamBindVideo(videoTag,​ pushedStream) 
-}).catch(function(error){ 
- 
-}); 
-</​code>​ 
-  *** 注意:A推流成功后,onStreamAdded 将被回调 *** 
-==== 其他成员收到通知并订阅流 ====  
- 
-成员A成功发布数据流后,会议中其他成员会收到监听类回调[emedia.mgr.onStreamAdded],如果成员B想看成员A的音视频,可以调用subscribe接口进行订阅 
- 
-<code javascript>​ 
-//​其他成员 
-emedia.mgr.onStreamAdded = function (member, stream) { 
-   ​if(!stream.located()){ //​stream.located() === true, 是自己发布刚刚发布的流 
-      emedia.mgr.subscribe(member,​ stream, true, true, video) 
-   } 
-}; 
-</​code>​ 
- 
- 
-==== 成员B取消订阅流 ==== 
- 
-成员B如果不想再看成员A的音视频,可以调用SDK接口unsubscribe 
- 
-<code javascript>​ 
-//​emedia.mgr.hungup(stream);​ 
-emedia.mgr.unsubscribe(stream);​ 
-</​code>​ 
- 
-==== 成员A取消发布流 ==== 
- 
-成员A可以调用unpublish接口取消自己已经发布的数据流,操作成功后,会议中收到回调[emedia.mgr.onStreamRemoved:​removeStream:​] ,将对应的数据流信息移除 
- 
-<code javascript>​ 
-//​emedia.mgr.hungup(pushedStream);​ 
-emedia.mgr.unpublish(pushedStream);​ 
-</​code>​ 
- 
-<code javascript>​ 
-emedia.mgr.onStreamRemoved = function (member, stream) { 
-   ​if(stream.located(){ //​自己发布流 
-   ​}else{ //​会议其他人发布的流 
-   } 
-}; 
-</​code>​ 
- 
-==== 成员B离开会议 ==== 
- 
-成员B调用SDK接口exitConference离开会议,会议中的其他成员会收到回调[emedia.mgr.onMemberExited:​member:​] 
- 
-<code javascript>​ 
-emedia.mgr.exitConference();​ 
-</​code>​ 
- 
- 
-===== 互动视频会议功能实现 ===== 
- 
-互动视频会议的基本操作(创建、邀请人、发布流、取消发布流、订阅流、取消订阅流、更新发布流程、离开)对应的接口和回调同通信会议是一样的。也可以说 互动视频会议是在通信会议的基础上,增加了角色管理功能,以下着重讲解互动视频会议中的角色管理相关知识点 
-  
- 1. 创建互动视频会议时,接口emedia.mgr.createConference传入的type参数是emedia.mgr.ConfrType.LIVE 
-  
- 2. 创建者createAndJoin后的角色是Admin,其他成员第一次调用接口[emedia.mgr.joinConference(confrId,​ password, ext)]加入直播后的权限是观众Audience,Audience只能订阅数据流 
-  
- 3. 观众Audience如果想发布数据流 即上麦,需要给管理员发申请。SDK没有提供申请接口,你可以自定义。 
-  
-管理员如果同意Audience上麦,需要调用接口emedia.mgr.grantRole将角色Audience更改为Speaker 
-  
-<code javascript>​ 
-emedia.mgr.grantRole(confr,​ [memberName1,​ memberName2],​ emedia.mgr.Role.TALKER) 
-</​code> ​ 
-      
-成员角色改变后,被改变的成员会收到回调 
- 
-<code javascript>​ 
-emedia.mgr.onRoleChanged = function (role) { 
- 
-}; 
-</​code> ​ 
-  
-4. 角色从Audience变为Speaker,成员就可以发布数据流了 
- 
-===== 其他接口 ===== 
-==== 获取加入会议ticket ==== 
-<code javascript>​ 
-emedia.mgr.getConferenceTkt(confrType,​ password).then(function(confr){ 
-    ​ 
-}).catch(function(error){ 
- 
-}) 
-</​code> ​ 
- 
-==== 销毁会议 ==== 
-<code javascript>​ 
-emedia.mgr.destroyConference(confr).then(function(){ 
-    ​ 
-}).catch(function(error){ 
- 
-}) 
-</​code> ​ 
-==== 踢人 ==== 
-<code javascript>​ 
-emedia.mgr.kickMembersById(confr,​ memberNames).then(function(){ 
-    ​ 
-}).catch(function(error){ 
- 
-}) 
-</​code> ​ 
-==== 授权 ==== 
-<code javascript>​ 
-emedia.mgr.grantRole(confr,​ memberNames,​ role).then(function(){ 
-    ​ 
-}).catch(function(error){ 
- 
-}) 
-</​code> ​ 
- 
-==== 使用用户名密码加入会议,可自定义ext ==== 
-<code javascript>​ 
-emedia.mgr.joinConference(confrId,​ password, ext).then(function(){ 
-    ​ 
-}).catch(function(error){ 
- 
-}) 
-</​code> ​ 
- 
-==== 使用ticket加入会议,可自定义ext ==== 
-<code javascript>​ 
-emedia.mgr.joinConference(confrId,​ ticket, ext).then(function(){ 
-    ​ 
-}).catch(function(error){ 
- 
-}) 
-</​code> ​ 
- 
-==== 退出会议 ==== 
-<code javascript>​ 
-emedia.mgr.exitConference();​ 
-</​code>​ 
- 
-==== publish 媒体流 ==== 
-<code javascript>​ 
-/** 
- * constaints: {audio: true, video: true} 
- * videoTag 可缺失,如果有 此次publish的媒体数据将会在这个video上显示 将会与stream绑定 
- * ext 用户自定义扩展,其他成员可以看到这个字段 
- ​* ​ 
- */ 
-emedia.mgr.publish(constaints,​ videoTag, ext).then(function(pushedStream){ 
-    //stream 对象 
-}).catch(function(error){ 
- 
-}); 
- 
-/** 
- * videoTag 可缺失时 
- * 
- */ 
-emedia.mgr.publish(constaints,​ ext).then(function(pushedStream){ 
-    //stream 对象 
-    //​如果需要将这个stream对象 显示,需要 emedia.mgr.streamBindVideo(videoTag,​ pushedStream) 
-}).catch(function(error){ 
- 
-}); 
-</​code>​ 
- 
-==== 共享桌面,​仅支持PC Chrome或electron平台 ==== 
-<code javascript>​ 
-/** 
- * videoConstaints {screenOptions:​ ['​screen',​ '​window',​ '​tab'​]} or true 
- * withAudio: true 携带语音,false不携带 
- * videoTag 可缺失,如果有 此次publish的媒体数据将会在这个video上显示 将会与stream绑定 
- * ext 用户自定义扩展,其他成员可以看到这个字段 
- ​* ​ 
- */ 
-emedia.mgr.shareDesktopWithAudio(videoConstaints,​ withAudio, videoTag, ext).then(function(pushedStream){ 
-    //stream 对象 
-}).catch(function(error){ 
- 
-}); 
- 
-//​electron平台 默认选择第一个屏幕,如果需要选择其他,需要重写方法 
-emedia.chooseElectronDesktopMedia = function(sources,​ accessApproved){ 
-    var firstSources = sources[0]; 
-    accessApproved(firstSources);​ 
-} 
-</​code> ​ 
- 
-==== 取消publish ==== 
-<code javascript>​ 
-emedia.mgr.unpublish(pushedStream);​ 
- 
-emedia.mgr.hungup(pushedStream);​ 
-</​code> ​ 
- 
-==== 订阅 ==== 
-<code javascript>​ 
-/** 
- * subSVideo 看视频 
- * subSAudio 看音频 
- * videoTag 可缺失,如果有 将会与stream绑定,并播放stream 
- */ 
-emedia.mgr.subscribe(member,​ stream, subSVideo, subSAudio, videoTag).then(function(){ 
-    //stream 对象 
-}).catch(function(error){ 
- 
-}); 
-emedia.mgr.triggerSubscribe(videoTag,​ subSVideo, subSAudio).then(function(){ 
-    //stream 对象 
-}).catch(function(error){ 
- 
-}); 
-也可以 
-//​仅订阅音频 
-emedia.mgr.triggerResumeAudio(videoTag).then(function(){ 
-}).catch(function(error){ 
-}); 
-//​仅暂停订阅音频 
-emedia.mgr.triggerPauseAudio(videoTag).then(function(){ 
-}).catch(function(error){ 
-}); 
- 
-//​仅订阅视频 
-emedia.mgr.triggerResumeVideo(videoTag).then(function(){ 
-}).catch(function(error){ 
-}); 
-//​仅暂停订阅视频 
-emedia.mgr.triggerPauseVideo(videoTag).then(function(){ 
-}).catch(function(error){ 
-}); 
- 
-</​code>​ 
- 
-==== stream video 绑定 ==== 
-<code javascript>​ 
-//stream 与 video绑定后,video才可以播放stream;并且可以通过触发/​监听video上的事件,来达到对stream的便捷操作 
-emedia.mgr.streamBindVideo(stream,​ videoTag); 
-</​code>​ 
- 
-==== 获取stream绑定的video ==== 
-<code javascript>​ 
-emedia.mgr.getBindVideoBy(stream);​ 
-</​code>​ 
- 
-==== 切换摄像头==== 
-<code javascript>​ 
-emedia.mgr.switchCamera().then(function(){ 
- 
-}).catch(function(){ 
- 
-}) 
-</​code>​ 
- 
-==== 暂停/​恢复自己的视频 ==== 
-<code javascript>​ 
-emedia.mgr.pauseVideo(pubS).then(function(){ 
- 
-}).catch(function(){ 
- 
-}) 
-等价于 
-emedia.mgr.triggerResumeVideo(localVideoTag).then(function(){ 
- 
-}).catch(function(){ 
- 
-}) 
-</​code>​ 
-<code javascript>​ 
-emedia.mgr.pauseVideo(pubS).then(function(){ 
- 
-}).catch(function(){ 
- 
-}) 
-等价于 
-emedia.mgr.triggerPauseVideo(localVideoTag).then(function(){ 
- 
-}).catch(function(){ 
- 
-}) 
-</​code>​ 
- 
-==== 抓取 video图像,并保存 ==== 
-<code javascript>​ 
-emedia.mgr.captureVideo(videoTag,​ true, filename) 
-等价于 
-emedia.mgr.triggerCaptureVideo(videoTag,​ true, filename); 
-</​code>​ 
- 
-==== 控制远程视频(手机端)定格 ==== 
-<code javascript>​ 
-emedia.mgr.freezeFrameRemote(stream);​ 
-等价于 
-emedia.mgr.triggerFreezeFrameRemote(videoTag).catch(function(){ 
-    alert("​定格失败"​);​ 
-}); 
-</​code>​ 
- 
-==== 控制手机闪光灯打开/​关闭 ==== 
-<code javascript>​ 
-/** 
- * torch true 打开,否则 关闭; 可缺失 
- */ 
-emedia.mgr.torchRemote(stream, torch); 
-等价于 
-emedia.mgr.triggerTorchRemote(videoTag,​ torch).catch(function(){ 
-    alert("​Torch失败"​);​ 
-}); 
-</​code>​ 
- 
-==== 控制手机截屏 ==== 
-<code javascript>​ 
-emedia.mgr.capturePictureRemote(stream);​ 
-等价于 
-emedia.mgr.triggerCapturePictureRemote(videoTag).catch(function(){ 
-    alert("​抓图失败"​);​ 
-}); 
-</​code>​ 
- 
-==== 控制手机摄像头放大缩小 ==== 
-<code javascript>​ 
-emedia.mgr.zoomRemote(stream,​ multiples); 
-等价于 
-emedia.mgr.triggerZoomRemote(videoTag,​ multiples).catch(function(){ 
-    alert("​zoom失败"​);​ 
-}); 
-</​code>​ 
- 
-==== 控制手机摄像头聚焦曝光 ==== 
-<code javascript>​ 
-/** 
- * clickEvent 为 videoTag的点击事件。通过event计算点击的坐标传给sdk进行控制 
- * 
- */ 
-emedia.mgr.focusExpoRemote(stream,​ videoTag, clickEvent).catch(function(){ 
-    alert("​focusExpoRemote失败"​);​ 
-}); 
-等价于 
-/** 
- * event string. 如点击 “click” 
- * fail 失败回调;success成功回调 
- */ 
-emedia.mgr.onFocusExpoRemoteWhenClickVideo(videoTag,​ event, fail, success); 
-</​code>​ 
- 
-==== 取消在videoTag上的事件 ==== 
-<code javascript>​ 
-//​用来对onFocusExpoRemoteWhenClickVideo的撤销 
-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 javascript>​ 
-//​视频收发数据统计 
-emedia.mgr.onMediaTransmission(videoTag,​ function notify(trackId,​ type, subtype, data) { 
-    var $iceStatsShow = $div.find("#​iceStatsShow"​);​ 
-    var $em = $iceStatsShow.find("#"​+subtype);​ 
-    if(!$em.length){ 
-        $em  = $("<​em></​em>"​).appendTo($iceStatsShow).attr("​id",​ subtype); 
-    } 
- 
-    $em.text(subtype + ":"​ + (data*8/​1000).toFixed(2));​ 
-}); 
-</​code>​ 
- 
-<code javascript>​ 
-//​连接状态变化 
-emedia.mgr.onIceStateChanged(videoTag,​ function (state) { 
-    console.log(state);​ 
-}); 
-</​code>​ 
- 
- 
----- 
-<WRAP group> 
-<WRAP half column> 
-上一页:[[im:​web:​basics:​videocall|实时通话]] 
-</​WRAP>​ 
- 
-<WRAP half column> 
-下一页:[[im:​web:​other:​toolrelated|工具类说明]] 
-</​WRAP>​ 
-</​WRAP>​