====== 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. 安装依赖包
npm install
4. 启动项目
HTTPS=true npm start
====== 快速集成 ======
===== 1. 环信后台注册 appkey =====
在开始集成前,你需要注册环信开发者账号并创建后台应用,参见[[http://docs-im.easemob.com/im/ios/sdk/prepare#注册并创建应用|注册并创建应用]]
===== 2. 创建项目 =====
a.可以简单的写一个 html,引入 SDK 测试
b.或者使用 脚手架搭建一个项目
===== 3.引入 SDK =====
=== 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 文件]]
===3.2使用 npm 获取 SDK===
npm install easemob-emedia
===3.3 在文件内引入 SDK===
import emedia from 'easemob-emedia';
=== 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 =====
emedia.config({
appkey, // 从环信后台 获取的appkey、必填
consoleLogger: true, // boolean 是否开启打印日志,默认true
... 其他的一些配置
});
===== 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 **
emedia.mgr.setIdentity(memName, token); //memName:appkey +'_'+ 环信ID, token: 环信ID登录后返回的access_token
** params 为进入会议需要的参数 **
var params = {
roomName, // string 房间名称 必需
password, // string 房间密码 必需
role // number 进入会议的角色 1: 观众 3:主播 必需
config:{
rec:false, //是否开启录制会议
recMerge:false, //是否开启合并录制
supportWechatMiniProgram: true //是否允许小程序加入会议
}
}
调用 ** emedia.mgr.joinRoom ** 进入会议, 若该会议不存在,服务器将会自动创建
const user_room = await emedia.mgr.joinRoom(params);
返回的参数 ** user_room **,组成如下
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 //会议类型
}
===== 7.发布本地流 =====
加入会议之后 调用 ** emedia.publish ** 发布流
var constaints = { // 发布音频流的配置参数, Object 必需。 video或audio属性 至少存在一个
audio: true, // 是否发布音频
video: true // 是否发布视频
}
var ext = {} // 发布流的扩展信息 Object 非必需。会议其他成员可接收到
const pushedStream = await emedia.mgr.publish(constaints, ext);
发布本地流成功后, 调用 emedia.mgr.streamBindVideo 用于在video标签显示流
var videoTag = document.getElementById('#xxx') //需要显示本地流的 video 标签
emedia.mgr.streamBindVideo(pushedStream, videoTag);
===== 8.订阅远端流 =====
当远端流加入频道时,会触发 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标签,显示本地流 \\ **
emedia.mgr.onStreamAdded = function(member, stream) {
// 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)
}
}
** 在emedia.mgr.subscribe 方法中注意以下参数的设置 **
* option.member:发布流人员的信息,必需。也就是 onStreamAdded 方法的 member
* option.stream:流信息,必需。也就是 onStreamAdded 方法的 stream
* option.subVideo: 是否订阅视频,必需
* option.subAudio: 是否订阅音频,必需
* option.videotag: 需要显示流的 video 标签,必需
=== 8.2 监听 onStreamRemoved 方法,当远端流被移除时(例如远端用户调用了 Stream.unpublish), 停止订阅该流并移除它的画面。===
emedia.mgr.onStreamRemoved = function(member, stream) {
// member:发布流人员的信息、stream:流信息
emedia.mgr.unsubscribe(stream) // 停止订阅流
removeView(stream.id) // 移除video标签,removeView方法需自己实现
}
===== 9.退出会议 =====
调用 emedia.mgr.exitConference 方法退出会议
emedia.mgr.exitConference() //无参数
===== 10.会议常用监听函数 =====
** ''强烈建议:''在加入会议之前定义需要的 SDK 回调函数 **
//有人加入会议
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: 变更后的角色
//会议退出;自己主动退 或 服务端主动关闭;
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;
}
};
//管理员变更
emedia.mgr.onAdminChanged = admin => {} //admin 管理员信息
//监听弱网状态
emedia.mgr.onNetworkWeak = streamId => {} //streamId 会议中的流 ID
//监听断网状态
emedia.mgr.onNetworkDisconnect = streamId => {} //streamId 会议中的流 ID
====== 进阶功能 ======
===== 会议管理 =====
==== 1.创建会议并加入 ====
这里的创建会议 不同于快速集成中的加入会议,这里的是属于另一套逻辑,建议使用快速集成中的加入会议 \\
** 注意: 如果只单纯的创建,没进行操作,则创建者不是会议的成员,没有相应的角色,不能进行其他操作 ** \\
1.1 调用 emedia.mgr.createConference 方法创建会议
let params = {
confrType,
password,
rec,
recMerge,
supportWechatMiniProgram
... 其他参数
}
const confr = await emedia.mgr.createConference(params);
在 emedia.mgr.createConference 方法中,注意以下参数的设置:
* confrType 会议类型 10:普通会议模式、11:大会议模式、12:直播模式。number 必需
* password 会议密码 string 必需
* rec 是否开启通话录制 boolean 非必需
* recMerge 是否开启通话录制合并 boolean 非必需
* supportWechatMiniProgram 会议是否支持小程序端 boolean 非必需, 默认不支持
创建会议成功后,返回的参数 confr 结构如下:
confr:{
error: 0,
confrId: "LBJ13H9WJJEVJTGL1U1PIQ00C2", // 会议id
password: "xxxx", // 创建会议时设置的密码
role: 7, // 在会议中的角色 这里因为是创建者,所以是 7: 管理员
roleToken:"eyJ0eXAiOiJKV1QiLCJhbG *** f1gg_QJWxhs-jqmuFok", //创建者的token
type: 10 // 会议类型
}
1.2 调用 emedia.mgr.joinUsePassword 方法加入会议
const join_result = await this.emedia.joinUsePassword(confrId, password) // 参数为 创建会议成功后返回的 confrId和password
加入会议成功后返回的结果,结构如下:
join_result: {
confrId: "LBJ13H9WJJEVJTGL1U1PIQ00C7", // 会议id
joinId: "LBJ13H9WJJEVJTGL1U1PIQ00C7M9", //创建会议时设置的密码
password: "0.010568535799199363", // 会议成员在会议中的身份id(唯一)
role, // 在会议中的角色 创建者加入会议永远是 7: 管理员,其他人加入返回的是 3: 主播
roleToken: "eyJ0eXAiOiJK *** FaXuMVlqPOTofRRdE", // 加入会议者的token
type: 10 // 会议类型
}
==== 2.开启录制、录制合并 ====
2.1 调用 emedia.mgr.createConference 创建会议时配置
let params = {
...
rec: true,
recMerge: true,
... 其他参数
}
const confr = await emedia.mgr.createConference(params);
2.2 调用 emedia.mgr.joinRoom 加入会议时配置\\
注意:只有第一个加入会议的人,配置的才有效\\
参数需要放到 config 对象中
let params = {
... 其他参数
config: {
rec: true,
recMerge: true
}
}
const confr = await emedia.mgr.createConference(params);
rec: 是否开启录制,默认 false\\
recMerge: 是否开启录制合并,默认false
==== 3.邀请成员加入会议 ====
SDK 不提供邀请接口。邀请的形式,完全可以由用户自行定义,可以是条文本消息,也可以是个控制消息等等。SDK 不做限制。实现方式可以参考官方 demo,通过群组消息邀请,具体代码 可查询 demo/src/components/webrtc/AddAVMemberModal.js 中的 70-89 行。
3.1 成员收到邀请加入会议 \\
解析出邀请消息中带的 confrId 和 password ,调用 ** emedia.mgr.joinUsePassword ** 加入会议
==== 4.管理员销毁会议 ====
调用 emedia.mgr.destroyConference 方法, ** 注意:只有管理员有权限,其他角色调用不生效 **
await emedia.mgr.destroyConference(confrId); //confrId: 会议Id
其他人会收到会议结束的回调
emedia.mgr.onConferenceExit = reason => {} // reason:退出会议的原因,因为会议被销毁了,所以这里应为 11: "会议关闭"
==== 5.设置会议人数限制 ====
5.1 在 emedia.mgr.joinRoom 方法中设置
** 只有第一个加入房间的人员(也就是管理员),设置的才能生效 **
var params = {
... 其他参数,
config:{ // 在 config 中指定字段
maxTalkerCount:3,
maxAudienceCount:100,
maxVideoCount:2,
maxPubDesktopCount: 1
}
}
const user_room = await emedia.mgr.joinRoom(params);
5.2 在 emedia.mgr.createConference(创建会议) 方法中设置
var params = {
... 其他参数,
maxTalkerCount:3
maxVideoCount:2
maxAudienceCount:100
maxPubDesktopCount: 1
}
const confr = await emedia.mgr.createConference(params);
以上设置会议参数的注意事项如下:
* maxTalkerCount:自定义会议最大主播人数,默认 100,
* maxAudienceCount:自定义会议最大观众数 默认 600
* maxVideoCount:自定义会议最大视频数, 默认 9
* maxPubDesktopCount: 自定义会议共享屏幕最大数 默认 2
==== 6.设置会议昵称 ====
在 emedia.mgr.joinRoom 方法中设置
var params = {
... 其他参数,
config:{
... 其他参数,
nickName: xxx, // string
}
}
const user_room = await emedia.mgr.joinRoom(params);
==== 7. 获取会议信息 ====
调用 ** emedia.mgr.selectConfr ** 方法获取会议信息
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
}
==== 8.CDN合流推流 ====
CDN推流是指将会议画面,合并到一张画布推送到远程CDN,其他人可以从CDN拉流而不用加入会议
== 8.1 开启CDN推流 ==
**CDN推流参数 liveCfg为必需 结构如下:**
let liveCfg = {
cdn:'', //推流地址、字符串;必需
layoutStyle: 'GRID' | 'CUSTOM', // 格子显示 | 自定义,必需
canvas :{// canvas 参数在 layoutStyle == 'CUSTOM' 必填
bgclr : 0x980000,//背景色 980000 为 十六进制色值
w : 640, //宽度
h : 480, //高度
fps: 20, //输出帧率
bps: 1200000, //输出码率
codec: "H264" //视频编码,现在必须是H264
}
}
**1.创建会议时指定 CDN推流**
let option = {
...
liveCfg // 创建CDN推流参数
}
emedia.mgr.createConference(option)
**2.加入房间时 指定CDN推流**
// 只有第一个加入房间的人才能创建 CDN、以后加入的人指定CDN也无效
let params = {
config:{
...
liveCfg // 创建CDN推流参数
}
}
emedia.mgr.joinRoom(params);
== 8.2 多路推流 ==
通过 ** emedia.mgr.addLive ** 方法指定一路推流CDN,需要几路CDN,就调用几次方法 \\
** 注意:只有管理员,可创建 CDN **
// confrId: 会议id, 必需
// liveCfg: cdn 配置,必需
media.mgr.addLive(confrId, liveCfg);
== 8.3 更新CDN布局 ==
// 只有管理员才能 更新布局。更新布局会 将layoutStyle 变为 CUSTOM 而且不可逆
emedia.mgr.updateLiveLayout(confrId, liveId, regions)
// confrId 会议id 必需
// liveId 推流CDN id, 必需 可通过 emedia.config.liveCfgs 获取 Array
regions:[ // 希望定义视频流 显示的配置集合
{
"sid": stream_id,//视频流的id
"x": 320,//距离 x 轴的距离 Number
"y": 240,//距离 y 轴的距离 Number
"w": 960,//宽度 Number
"h": 720,//高度 Number
"style": "fill" | "AspectFit" //视频显示模式 fill:铺满、AspectFit:原比例显示
},
.... 其他视频流配置(数组有几个项,就显示几个视频流)
]
== 8.4 删除CDN ==
// 只有管理员可操作
//confrId 会议id 必需
// liveId 推流CDN id, 必需 可通过 emedia.config.liveCfgs 获取 Array
emedia.mgr.deleteLive(confrId, liveId)
=== 9.海外代理 ===
** 开启集群部署,SDK内部会自动请求代理,实现通话最优 ** \\
调用 emedia.config 方法开启
emedia.config({
...
useDeployMore: true // 默认:false 不开启
rtcConfigUrl: 'yourConfigUrl' // 自定义config文件路径, 默认有一个路径
...
})
==== 10. 取日志 ====
在浏览器控制台,输入 ** emedia.fileReport **,敲下回车键会下载下一份日志文件
emedia.fileReport() //无参数
==== 11. 会议属性 ====
// 用来自定义一些属性,广播给会议中的成员
// 有人设置会议属性,所有的成员都能收到
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
==== 12.海外代理 ====
1v1通话支持不同集群区域的人员通话使用代理,减小延迟;
使用多集群代理需要音视频后台配置IP及端口的映射文件rtcconfig.json,并禁用相关appkey的直连,
也可以指定 rtcconfig.json 的私有化部署
调用 ** emedia.config 方法开启海外代理 **
emedia.config({
...
useDeployMore: true, // 默认:false 不开启
rtcConfigUrl: 'your rtcConfigUrl' // 如果不设置,将使用 sdk 后台配置的代理文件
})
===== 音视频管理 =====
==== 1.设置通话参数 ====
发布本地媒体流时,可指定音视频的码率和分辨率,非必需。** 共享桌面不可指定 **
var constaints = {
audio: {bitrate: 100},// 指定音频码率
video: {
width: { // 指定视频分辨率宽度
exact: 1280
},
height: { // 指定视频分辨率高度
exact: 720
},
bitrate: 200,// 指定视频码率
}
}
emedia.mgr.publish(constaints)
==== 2. 指定设备打开音视频 ====
const devices = await emedia.mgr.mediaDevices(); //获取设备列表
// 设备信息
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) // 推流
==== 3. 停止发布流 ====
调用 ** emedia.mgr.unpublish ** 方法停止自己已经发布的流
emedia.mgr.unpublish(pushedStream); // pushedStream:自己发布的流
会议中人员(包括自己)会收到 流被移除的回调函数 ** emedia.mgr.onStreamRemoved **
emedia.mgr.onStreamRemoved = function (member, stream) {
// member: 停止发布流人员信息
// stream:流的信息,stream.located() == true 代表是自己的流,false则为其他人的流
};
==== 4. 停止订阅流 ====
调用 ** emedia.mgr.unsubscribe ** 方法停止订阅别人的流
emedia.mgr.unsubscribe(stream); // stream:已经订阅的流
==== 5. 通话过程中音视频控制 ====
5.1 打开/关闭自己的视频
const res_stream = await emedia.mgr.pauseVideo(own_stream) //关闭视频
const res_stream = await emedia.mgr.resumeVideo(own_stream) //开启视频
5.2 打开/关闭自己的音频
const res_stream = await emedia.mgr.pauseAudio(own_stream) // 关闭音频
const res_stream = await emedia.mgr.resumeAudio(own_stream) // 开启音频
执行开启/关闭音视频方法时,
* own_stream:自己已经发布的媒体流(不能是桌面流)
* res_stream:设置音视频成功后返回的流对象
** 执行上述操作后,会议中其他人员会收到流变化的回调 emedia.mgr.onMediaChanaged ** \\
emedia.mgr.onMediaChanaged 应该在流变化之前监听
var videoTag = document.getElementById('#xxx') // 获取 video 标签
emedia.mgr.onMediaChanaged(videoTag, function(constaints, stream) {
});
// 回调函数中 constaints、stream
constaints: {
audio: true // true: 开启了音频,false:关闭了音频
video: true // true: 开启了视频,false:关闭了视频
}
stream:媒体流变化后的 stream 对象
5.3 切换摄像头
// 随机切换摄像头
emedia.mgr.changeCamera(confrId).then(function(){
// 无参数
}).catch(function(){
})
// 切换手机前后摄像头
emedia.mgr.switchMobileCamera(confrId).then(function(){
// 无参数
}).catch(function(){
})
==== 6. 音视频网络状态监听 ====
** 建议进入会议之前绑定网络状态监听函数 **
//监听弱网状态
emedia.mgr.onNetworkWeak = streamId => {} //streamId 会议中的流 ID
//监听断网状态
emedia.mgr.onNetworkDisconnect = streamId => {} //streamId 会议中的流 ID
==== 7. 监听谁在说话 ====
这是监听的video标签,\\
** 建议:在将video标签与stream绑定时(emedia.mgr.streamBindVideo), 调用 emedia.mgr.onSoundChanaged 方法 **
var videoTag = getElementById('#xxx');
emedia.mgr.onSoundChanaged(videoTag,, function (meterData) {});
// 返回的参数 meterData 结构如下:
meterData: {
instant: 0.26280892641627845 // instant 大约每50毫秒变化一次, 小于1的浮点数
slow: 0.06802768487276245 // slow大约是一秒钟内的平均音量,小于1的浮点数
clip: 0
}
==== 8. 共享桌面 ====
仅支持PC Chrome浏览器或electron平台\\
** SDK 3.2.1 版本 文档 **
=== 8.1无插件共享 ===
需要 SDK 3.2.1 版本开始支持,并且 Chrome 72 或以上版本。
const screenStream = await emedia.mgr.shareDesktopWithAudio({
confrId: confrId, // 会议ID, 必须
audio: false,
});
=== 8.2有插件共享 ===
//在 sdk 内部会自动判断,浏览器是否含有 navigator.mediaDevices.getDisplayMedia API,
//如果没有,将会跳转至 使用 插件的 API,如果没有安装插件将给出提示
=== 8.3分享音频 ===
//1. 版本起支持在 Windows 平台的 Chrome 浏览器 74 及以上版本同时共享屏幕和本地播放的背景音,
//2. 将 audio 设置为 true 即可
=== 8.4Electron 屏幕共享 ===
//1. sdk 内部会判断是否是在 electron 平台内
//2. electron 平台 会默认选择 第一个屏幕
//3. 如果需要自定义选择框,请重新定义 emedia.chooseElectronDesktopMedia 方法
emedia.chooseElectronDesktopMedia = function(sources, accessApproved, accessDenied){
sources // Array 获取到的屏幕列表
accessApproved(source)// 选中的 source 对象,进行分享
accessDenied()// 取消分享,关闭自定义框需要调用此方法
}
=== 8.5 停止共享桌面 ===
停止共享桌面,执行 取消流的发布 emedia.mgr.unpublish(screenStream)
** SDK 3.2.1 之前版本 文档 **
/**
* let params = {
* videoConstaints,
* withAudio,
* videoTag,
* ext,
* confrId,
* stopSharedCallback
* }
*/
/**
* videoConstaints {screenOptions: ['screen', 'window', 'tab']} or true
* withAudio: true 携带语音,false不携带 如携带语音,需自己调用关闭流,不会执行 stopSharedCallback 回调
* ext 用户自定义扩展,其他成员可以看到这个字段
* stopSharedCallback 共享插件 点击【停止共享】的回调函数,做相应的处理(比如删除流...)
*/
emedia.mgr.shareDesktopWithAudio(params).then(function(pushedStream){
//stream 对象
}).catch(function(error){
});
//electron平台 默认选择第一个屏幕,如果需要选择其他,需要重写方法
emedia.chooseElectronDesktopMedia = function(sources, accessApproved){
var firstSources = sources[0];
accessApproved(firstSources);
}
**注意:**
%%在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 获取会议中的角色类型
emedia.mgr.Role: {
ADMIN: 7, // 会议管理员: 能创建会议,销毁会议,移除会议成员,切换其他成员的角色
TALKER: 3, // 主播: 能上传自己的音视频,能观看收听其他主播的音视频,即能发布流和订阅流)
AUDIENCE: 1 // 观众: 只能观看收听音视频,即只能订阅流
};
// 可在会议中定义更加语义化的判断:
if(member.role == emedia.mgr.Role.ADMIN){ }
等同于
if(member.role == 7){ }
在以下申请主播和申请管理员的过程,请注意:
1.从发起申请到管理员回复(同意或拒绝),是一个完整的过程
2.因此可以认为:当管理员收到请求,会收到两个函数参数(同意和拒绝),用于管理员调用
3.当管理员处理了请求(同意或拒绝),申请者会收到处理结果(同意或拒绝),未处理则不会收到
** 以下方法非回调函数均为异步函数 **
try {
await emedia.mgr.xxx;
} catch(error) { }
// 以下出现的参数注解
confrId: 会议id
memberId: 与会人员的id,member 中的id
nickName: 昵称
==== 1. 观众申请成为主播 ====
观众通过调用 ** emedia.mgr.requestToTalker ** 方法申请主播
// 观众上麦申请方法
emedia.mgr.requestToTalker(confrId)
// 管理员收到上麦申请的回调 (主播不会收到这个回调)
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: 拒绝
}
==== 2.主播申请成为管理员 ====
// 主播申请管理员
emedia.mgr.requestToAdmin(confrId);
//管理员收到申请管理员的回调 (主播不会收到这个回调)
emedia.mgr.onRequestToAdmin = function(applicat, agreeCallback, refuseCallback) {
/*
* applicat { memberId, nickName } object 申请者信息
* agreeCallback 管理员同意的回调 示例:agreeCallback(memberId) memberId 申请者 id 必需
* refuseCallback 管理员拒绝的回调 示例:refuseCallback(memberId) memberId 申请者 id必需
*/
}
// 主播收到 申请管理员的回复
emedia.mgr.onRequestToAdminReply = function(result) {
// result 0: 同意 1: 拒绝
}
====3.授权 ====
** 只有管理员有权限授权 ** \\
调用 方法改变与会人员的角色(可升可降)
var option = {
confr:, //会议对象 Object 必需
memberNames: // 被授权人员的memberName集合(可同时给多人授权)Array
role: // 需要授权成什么角色 Number 必需
}
emedia.mgr.grantRole(option.confr, option.memberNames, option.role)
==== 4.角色降级 ====
** 注意:只能角色降级 从管理员到主播、从主播到观众、从管理员到观众,不可逆向操作 ** \\
与会成员调用 ** emedia.mgr.degradeRole ** 方法就会角色降级
//[memName] 与会人员的memName、 toRole 想要 达到的角色
emedia.mgr.degradeRole(confrId, [memName], toRole);
==== 5.管理员踢人 ====
** 只有管理员可踢人 **
// confr: 会议对象,必需 Object
// memberNames: 被踢掉人员的 memberName, 必需 Array
emedia.mgr.kickMembersById(confr, memberNames)
==== 6.管理员执行全体静音/取消全体静音 ====
** 只有管理员可操作,其他角色操作不生效,管理员不会被静音 ** \\
6.1管理员静音全体
await emedia.mgr.muteAll(confrId); // confrId: 会议Id
// 主播收到回调
emedia.mgr.onMuteAll = () => {
// 在收到回调后,需要在程序中执行关闭麦克风的逻辑(emedia.mgr.pauseAudio(own_stream))
}
6.2取消全体静音
await emedia.mgr.unmuteAll(confrId); // confrId: 会议Id
//主播收到回调
emedia.mgr.onUnMuteAll = () => {
// 在收到回调后,需要在程序中执行关闭麦克风的逻辑(emedia.mgr.resumeAudio(own_stream))
}
==== 7.管理员指定成员静音/取消指定成员静音 ====
** 只有管理员可操作,其他角色操作不生效 ** \\
7.1管理员指定成员静音
emedia.mgr.muteBymemberId(confrId, memberId);// memberId 被静音主播的memberId
// 单个主播被管理员静音的回调(只他自己收到回调)
emedia.mgr.onMuted = () => {
// 在收到回调后,需要在程序中执行关闭麦克风的逻辑(emedia.mgr.pauseAudio(own_stream))
}
7.2管理员取消指定成员静音
emedia.mgr.unmuteBymemberId(confrId, memberId);// memberId 被取消静音主播的memberId
// 单个主播被管理员取消静音的回调 (只他自己收到回调)
emedia.mgr.onUnmuted = () => {
// 在收到回调后,需要在程序中执行关闭麦克风的逻辑(emedia.mgr.resumeAudio(own_stream))
}
==== 8.本身角色变更回调 ====
** 如果想要变更自己的角色,需要向管理员申请,管理员同意后,角色就会变更 **
emedia.mgr.onRoleChanged = function (role) {
// role: 变更后的角色
};
==== 9.管理员变更回调 ====
** 当会议中的管理员变更时,与会人员都会收到这个回调 **
emedia.mgr.onAdminChanged = admin => {} //admin 管理员信息
===== 其他接口 =====
==== 抓取 video图像,并保存 ====
emedia.mgr.captureVideo(videoTag, true, filename)
等价于
emedia.mgr.triggerCaptureVideo(videoTag, true, filename);
==== 控制远程视频(手机端)定格 ====
emedia.mgr.freezeFrameRemote(stream);
等价于
emedia.mgr.triggerFreezeFrameRemote(videoTag).catch(function(){
alert("定格失败");
});
==== 控制手机闪光灯打开/关闭 ====
/**
* torch true 打开,否则 关闭; 可缺失
*/
emedia.mgr.torchRemote(stream, torch);
等价于
emedia.mgr.triggerTorchRemote(videoTag, torch).catch(function(){
alert("Torch失败");
});
==== 控制手机截屏 ====
emedia.mgr.capturePictureRemote(stream);
等价于
emedia.mgr.triggerCapturePictureRemote(videoTag).catch(function(){
alert("抓图失败");
});
==== 控制手机摄像头放大缩小 ====
emedia.mgr.zoomRemote(stream, multiples);
等价于
emedia.mgr.triggerZoomRemote(videoTag, multiples).catch(function(){
alert("zoom失败");
});
==== 控制手机摄像头聚焦曝光 ====
/**
* 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);
==== 取消在videoTag上的事件 ====
//用来对onFocusExpoRemoteWhenClickVideo的撤销
emedia.mgr.offEventAtTag(videoTag);
//视频收发数据统计
emedia.mgr.onMediaTransmission(videoTag, function notify(trackId, type, subtype, data) {
var $iceStatsShow = $div.find("#iceStatsShow");
var $em = $iceStatsShow.find("#"+subtype);
if(!$em.length){
$em = $("").appendTo($iceStatsShow).attr("id", subtype);
}
$em.text(subtype + ":" + (data*8/1000).toFixed(2));
});
//连接状态变化
emedia.mgr.onIceStateChanged(videoTag, function (state) {
console.log(state);
});
==== 支持会议属性 ====
// 用来自定义一些属性,广播给会议中的成员
// 有人设置会议属性,所有的成员都能收到
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
====== 客户端api ======
多人音视频通话的API包括以下接口
* createConference: 创建会议
* destroyConference:销毁会议
* getConferenceInfo:获取会议信息
* publish:发布媒体流
* unpublish:取消发布媒体流
* grantRole:改变角色
* joinConference:通过password 加入会议
* kickMembersById:通过id踢出成员
** 以下方法均为 emedia.mgr 对象的属性方法: **
^方法 ^功能
|[[http://webim-h5.easemob.com/emedia/jsdoc/out/global.html#createConference | createConference]]| 创建会议 |
|[[http://webim-h5.easemob.com/emedia/jsdoc/out/global.html#destroyConference | createConference]]| 销毁会议|
|[[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 加入会议 |
|[[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]]|发布媒体流|
|[[http://webim-h5.easemob.com/emedia/jsdoc/out/global.html#unpublish | unpublish]]|取消发布媒体流|