李勇
d8a59eb5 1 parent d6b52330 webRtc-dev ... 20170922-1 20171019-1 20171120-1w dev ly20170824-1 ly20170829-1 ly20170925-1 ly20170926-1 ly20170927-1 ly20170929-1 ly20171011-1 ly20171013-1 ly20171013-2 ly20171016-1 ly20171021-1 ly20171023-1 ly20171024-1w ly20171025-1w ly20171026-1w ly20171027-1w ly20171030-1 ly20171030-2w ly20171107-1 ly20171110-1w ly20171113-1w ly20171204-1w ly20171208-1w ly20171211-1w ly20171211-2w ly20171214-1w v2.38.13.20171216 v2.38.11.20171214 v2.38.3.201712011 v2.38.1.201712011 v2.36.11.20171204 v2.36.8.20171206 v2.36.4.20171201 v2.35.11.20171130 v2.34.16.20171128 v2.34.5.20171127 v2.33.6.20171123 v2.32.1.20171123 v2.31.12.20171122 v2.31.10.20171122 v2.30.5.20171117 v2.30.2.20171117 v2.29.5.20171114 v2.28.1.20171110 v2.27.11.20171109 v2.26.9.20171107 v2.26.6.20171103 v2.26.2.20171102 v2.25.7.20171031 v2.25.6.20171031 v2.25.0.20171030 v2.24.2.20171030 v2.23.0.20171030 v2.22.7.20171026 v2.20.5.20171023 v2.20.0.20171021 v2.19.8.20171020 v2.18.14.20171020 v2.18.10.20171019 v2.17.11.20171014 v2.16.8.20171012 v2.16.5.20171012 v2.15.5.20171001 v2.15.3.20170929 v2.14.5.20170927 v2.13.5.20170927 v2.12.14.20170927 v2.12.8.20170926 v2.12.6.20170925 v2.11.13.20170925 v2.10.7.20170921 v2.10.6.20170921 v2.10.5.20170920 v2.10.4.20170920 v2.9.3.20170919 v2.8.17.20170918 v2.8.8.20170917 v2.8.2.20170916 v2.6.2.20170915 v2.5.12.20170915 v2.5.6.20170914 v2.5.5.20170914 v2.4.4.20170908 v2.4.2.20170908 v2.4.0.20170907 v2.3.6.20170907 v2.2.16.20170905 v2.1.22.20170904 v1.84.0.20170912 v1.83.2.20170831 v1.82.11.20170829

修改录制的逻辑;录制的时间和课堂时间完全区分开;课堂的状态控制和录制也区分开;课堂内进入的第一个人有权限开启录制;录制的时间戳每4秒想SAAS保存一次;MCU10同步一次

!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.iphunter=t():e.iphunter=t()}(this,function(){return function(e){function t(i){if(n[i])return n[i].exports;var o=n[i]={exports:{},id:i,loaded:!1};return e[i].call(o.exports,o,o.exports,t),o.loaded=!0,o.exports}var n={};return t.m=e,t.c=n,t.p="",t(0)}([function(e,t,n){e.exports=n(1)},function(e,t){"use strict";function n(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function i(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:3e3;if(!(e&&e.length&&t))throw new Error("ips and callback are required.");new r(e,t,n)}Object.defineProperty(t,"__esModule",{value:!0});var o=function(){function e(e,t){for(var n=0;n<t.length;n++){var i=t[n];i.enumerable=i.enumerable||!1,i.configurable=!0,"value"in i&&(i.writable=!0),Object.defineProperty(e,i.key,i)}}return function(t,n,i){return n&&e(t.prototype,n),i&&e(t,i),t}}();t.default=i;var r=function(){function e(t,i,o){n(this,e),this.version="v2.0.1.20170819",this.ip="",this.ipcallback=i,this.timeoutId=null,this.reqsCache=[];for(var r=0;r<t.length;r++)this.reqsCache.push(this.send(t[r],o-10));this.timeoutId=setTimeout(this.notify.bind(this),o)}return o(e,[{key:"clearAll",value:function(){this.reqsCache&&this.reqsCache.length&&this.reqsCache.forEach(function(e){e.abort()}),clearTimeout(this.timeoutId),this.ip="",this.ipcallback=null,this.timeoutId=null,this.reqsCache=[]}},{key:"clearReq",value:function(e){this.reqsCache.splice(this.reqsCache.indexOf(e),1)}},{key:"notify",value:function(){this.ipcallback&&this.ipcallback(this.ip),this.clearAll()}},{key:"send",value:function(e,t){var n=this,i=new XMLHttpRequest;return i.open("HEAD","//"+e+"/?_="+Date.now()),i.timeout=t,i.onload=function(){n.ip=e,n.clearReq(i),i.onload=null,n.notify()},i.ontimeout=function(){n.clearReq(i),i.ontimeout=null},i.onerror=function(){n.clearReq(i),i.onerror=null},i.onabort=function(){n.clearReq(i),i.onabort=null},i.send(),i}}]),e}();(function(){"undefined"!=typeof __REACT_HOT_LOADER__&&(__REACT_HOT_LOADER__.register(r,"IpHunter","D:/work/McuClient/node_modules/iphunter/src/main.js"),__REACT_HOT_LOADER__.register(i,"check","D:/work/McuClient/node_modules/iphunter/src/main.js"))})()}])});
\ No newline at end of file
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.iphunter=t():e.iphunter=t()}(this,function(){return function(e){function t(i){if(n[i])return n[i].exports;var o=n[i]={exports:{},id:i,loaded:!1};return e[i].call(o.exports,o,o.exports,t),o.loaded=!0,o.exports}var n={};return t.m=e,t.c=n,t.p="",t(0)}([function(e,t,n){e.exports=n(1)},function(e,t){"use strict";function n(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function i(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:3e3;if(!(e&&e.length&&t))throw new Error("ips and callback are required.");new r(e,t,n)}Object.defineProperty(t,"__esModule",{value:!0});var o=function(){function e(e,t){for(var n=0;n<t.length;n++){var i=t[n];i.enumerable=i.enumerable||!1,i.configurable=!0,"value"in i&&(i.writable=!0),Object.defineProperty(e,i.key,i)}}return function(t,n,i){return n&&e(t.prototype,n),i&&e(t,i),t}}();t.default=i;var r=function(){function e(t,i,o){n(this,e),this.version="v2.0.1.20170819",this.ip="",this.ipcallback=i,this.timeoutId=null,this.reqsCache=[];for(var r=0;r<t.length;r++)this.reqsCache.push(this.send(t[r],o));this.timeoutId=setTimeout(this.notify.bind(this),o)}return o(e,[{key:"clearAll",value:function(){this.reqsCache&&this.reqsCache.length&&this.reqsCache.forEach(function(e){e.abort()}),clearTimeout(this.timeoutId),this.ip="",this.ipcallback=null,this.timeoutId=null,this.reqsCache=[]}},{key:"clearReq",value:function(e){this.reqsCache.splice(this.reqsCache.indexOf(e),1)}},{key:"notify",value:function(){this.ipcallback&&this.ipcallback(this.ip),this.clearAll()}},{key:"send",value:function(e,t){var n=this,i=new XMLHttpRequest;return i.open("HEAD","//"+e+"/?_="+Date.now()),i.timeout=t,i.onload=function(){n.ip=e,n.clearReq(i),i.onload=null,n.notify()},i.ontimeout=function(){n.clearReq(i),i.ontimeout=null},i.onerror=function(){n.clearReq(i),i.onerror=null},i.onabort=function(){n.clearReq(i),i.onabort=null},i.send(),i}}]),e}();(function(){"undefined"!=typeof __REACT_HOT_LOADER__&&(__REACT_HOT_LOADER__.register(r,"IpHunter","D:/work/McuClient/node_modules/iphunter/src/main.js"),__REACT_HOT_LOADER__.register(i,"check","D:/work/McuClient/node_modules/iphunter/src/main.js"))})()}])});
\ No newline at end of file
... ...
... ... @@ -7,7 +7,8 @@ class IpHunter {
this.reqsCache = [];
for (let i = 0; i < ips.length; i++) {
this.reqsCache.push(this.send(ips[i], timeout - 10));
//this.reqsCache.push(this.send(ips[i], timeout - 10));
this.reqsCache.push(this.send(ips[i], timeout));
}
this.timeoutId = setTimeout(this.notify.bind(this), timeout);
}
... ...
... ... @@ -58,7 +58,7 @@ export default class MessageEntrance extends Emiter {
constructor() {
super();
//sdk 信息
GlobalConfig.sdkVersion = "v1.81.19.20170828";
GlobalConfig.sdkVersion = "v1.82.11.20170829";
loger.warn("sdkVersion:" + GlobalConfig.sdkVersion);
//设置
... ... @@ -87,7 +87,7 @@ export default class MessageEntrance extends Emiter {
this.isGetFastestRtmpPullCallback = false; //是否RTMP拉流地址测试结束
this.isGetFastestHlsPullCallback = false; //是否HLS拉流地址测试结束
this.isGetFastestRsCallback = false; //是否录制回放HLS拉流地址测试结束
this.saveClassStatusTimer=0;//保存课堂数据的计时器间隔,防止同一瞬间多次提交
//全局的Error处理
this.on(MessageTypes.MCU_ERROR, this._mcuErrorHandler.bind(this));
... ... @@ -420,21 +420,19 @@ export default class MessageEntrance extends Emiter {
//开启录制成功
_onClassRecordSuccess(_param) {
clearTimeout(this.classRecordStatusUpdateTimer);
let _this = this;
this.classRecordStatusUpdateTimer = setTimeout(function () {
this.classRecordStatusUpdateTimer = setTimeout(()=> {
clearTimeout(this.classRecordStatusUpdateTimer);
_this.updaterRecordAllApeStatus(_param);
this.updaterRecordAllApeStatus(_param);
}, 1600);
}
//录制状态发送改变,更新所有模块的当前数据发送到MCU
updaterRecordAllApeStatus(_param) {
if(GlobalConfig.isRecordPlayBack){
if(GlobalConfig.isRecordPlayBack||!_confer_ape){
return;
}
//老师身份和非录制回放的时候执行,录制状态发送改变,需要更新当前的数据,否则已有的消息会录制不上
if (GlobalConfig.isHost||GlobalConfig.rosterNumber<=1) {
if (_confer_ape.checkHasRecordControl()) {
loger.warn('录制状态发送改变->更新所有模块的当前数据发送到MCU');
//目前录制的模块[文档模块、白板模块、视频模块(包含屏幕共享)、音频模块、媒体共享,聊天模块]
if (_doc_ape) {
... ... @@ -452,6 +450,9 @@ export default class MessageEntrance extends Emiter {
if (_mediaShareApe) {
_mediaShareApe.updaterRecordApeStatus();
}
if (_musicShareApe) {
_musicShareApe.updaterRecordApeStatus();
}
//聊天模块不需要更新
}
}
... ... @@ -825,11 +826,11 @@ export default class MessageEntrance extends Emiter {
loger.warn("课堂最终使用的服务列表->来自本地Server.json");
}
loger.warn("mcuListFinal", GlobalConfig.mcuListFinal);
loger.warn("msListFinal", GlobalConfig.msListFinal);
loger.warn("rtmpPullListFinal", GlobalConfig.rtmpPullListFinal);
loger.warn("hlsListFinal", GlobalConfig.hlsPullListFinal);
loger.warn("rsListFinal", GlobalConfig.rsPullListFinal);
loger.warn(" MCU-List", GlobalConfig.mcuListFinal);
loger.warn(" MS-List", GlobalConfig.msListFinal);
loger.warn(" RTMP-List", GlobalConfig.rtmpPullListFinal);
loger.warn(" HLS-List", GlobalConfig.hlsPullListFinal);
loger.warn(" RS-List", GlobalConfig.rsPullListFinal);
}
//从Sass中选择的mcu、ms列表
... ... @@ -876,7 +877,6 @@ export default class MessageEntrance extends Emiter {
GlobalConfig.isp,
ServerConfig.serverList);
}
loger.warn("Sass中获取的服务器信息;");
//拉流地址列表的特殊处理,
// 1.如果RTMP拉流地址没有配置,就默认使用MS推流地址列表
... ... @@ -889,11 +889,11 @@ export default class MessageEntrance extends Emiter {
}
loger.warn("课堂最终使用的服务列表->来自Sass");
loger.warn(" GlobalConfig.mcuListFinal", GlobalConfig.mcuListFinal);
loger.warn(" GlobalConfig.msListFinal", GlobalConfig.msListFinal);
loger.warn(" GlobalConfig.rtmpPullListFinal", GlobalConfig.rtmpPullListFinal);
loger.warn(" GlobalConfig.hlsListFinal", GlobalConfig.hlsPullListFinal);
loger.warn(" GlobalConfig.rsListFinal", GlobalConfig.rsPullListFinal);
loger.warn(" MCU-List", GlobalConfig.mcuListFinal);
loger.warn(" MS-List", GlobalConfig.msListFinal);
loger.warn(" RTMP-List", GlobalConfig.rtmpPullListFinal);
loger.warn(" HLS-List", GlobalConfig.hlsPullListFinal);
loger.warn(" RS-List", GlobalConfig.rsPullListFinal);
}
... ... @@ -955,54 +955,37 @@ export default class MessageEntrance extends Emiter {
//保存课堂状态信息
_sassSaveClassStatusInfo(_param) {
if (!_mcu||!_mcu.connected) {
loger.warn("不能保存课堂数据->MCU已经断开");
return ;
}
if(!_confer_ape){
return;
}
//{isForce:true} isForce->是否强制提交(true为是)
//这个是特殊权限
let isForce = false;
if (_param && _param.isForce == true) {
isForce = true;
}
/* if (GlobalConfig.isHost || isForce) {
//只有加入课堂之后才能保存数据
if (GlobalConfig.getCurrentStatus().code == GlobalConfig.statusCode_2.code) {
//POST 保存数据
_sass.saveClassStatusInfo({"classStatusInfo": GlobalConfig.classStatusInfo}); //保存课堂状态信息
} else {
loger.error("不能保存课堂数据", GlobalConfig.getCurrentStatus());
}
} else {
loger.log("没有保存课堂状态信息的权限->身份", GlobalConfig.userRole);
}*/
if (GlobalConfig.isHost || isForce||GlobalConfig.rosterNumber<=1) {
//只有加入课堂之后才能保存数据
/*if (GlobalConfig.getCurrentStatus().code == GlobalConfig.statusCode_2.code) {
if (_confer_ape.checkHasRecordControl()||isForce) {
//POST 保存数据
clearTimeout(this.saveClassStatusTimer);
this.saveClassStatusTimer=setTimeout(()=>{
_sass.saveClassStatusInfo({"classStatusInfo": GlobalConfig.classStatusInfo}); //保存课堂状态信息
},600);
} else {
loger.error("不能保存课堂数据", GlobalConfig.getCurrentStatus());
}*/
if (_mcu&&_mcu.connected) {
//POST 保存数据
_sass.saveClassStatusInfo({"classStatusInfo": GlobalConfig.classStatusInfo}); //保存课堂状态信息
} else {
loger.error("不能保存课堂数据", GlobalConfig.getCurrentStatus());
}
} else {
loger.log("没有保存课堂状态信息的权限->身份", GlobalConfig.userRole);
loger.log("没有保存课堂状态信息的权限->当前身份->"+GlobalConfig.userRole);
}
}
//保存会态信息成功
_sassSaveClassStatusInfoSuccessHandler(_data) {
loger.log('保存课堂状态信息成功.');
loger.log(_data);
//loger.log('Saas保存课堂状态信息成功');
}
_sassSaveClassRecordInfoSuccessHandler(_data) {
loger.log('保存课堂录制信息成功.');
loger.log(_data);
//loger.log('Saas保存课堂录制信息成功',_data);
}
//Sass校验流程结束之后,开始加入MCU
... ... @@ -1208,11 +1191,22 @@ export default class MessageEntrance extends Emiter {
GlobalConfig.MS_PUBLISH_IP = _param.ip || "";
GlobalConfig.MS_PUBLISH_PORT = _param.port || "";
}
GlobalConfig.MS_PLAY_RTMP_IP = GlobalConfig.MS_PUBLISH_IP;
GlobalConfig.MS_PLAY_RTMP_PORT =GlobalConfig.MS_PUBLISH_PORT;
loger.warn('手动切换MS->', GlobalConfig.MS_PUBLISH_IP + ":" + GlobalConfig.MS_PUBLISH_PORT);
loger.warn('手动切换RTMP->', GlobalConfig.MS_PLAY_RTMP_IP + ":" + GlobalConfig.MS_PLAY_RTMP_PORT);
//更换完用户当前的MS地址,需要更新用户数据
if (_confer_ape) {
_confer_ape.updateUserInfo();
}
//音视频模块对当前正在播放的流进行更换MS
if(_video_ape){
_video_ape.changeMediaMs();
}
if(_audio_ape){
_audio_ape.changeMediaMs();
}
}
//切换MS ->_param->{reConnect:false} //reConnect(是否立即替换当前的ip并且重新连接)
... ... @@ -1381,13 +1375,10 @@ export default class MessageEntrance extends Emiter {
return;
}
if (_confer_ape) {
//开始录制
setTimeout(()=> {
_confer_ape.startRecord();
},2000);
//开始上课
_confer_ape.startClass(_param);
////开始录制
//_confer_ape.startRecord();
}
}
... ... @@ -2082,7 +2073,7 @@ export default class MessageEntrance extends Emiter {
//文档加入频道成功,同步到MCU服务器上的数据
docJoinChannelSuccess() {
loger.log("docJoinChannelSuccess->isHost=", GlobalConfig.isHost, "当前总人数:", GlobalConfig.rosterNumber, "sassDoclength=", GlobalConfig.docListPrepare.length);
loger.log("文档加入频道成功->isHost=", GlobalConfig.isHost, "当前总人数:", GlobalConfig.rosterNumber, "sassDoclength=", GlobalConfig.docListPrepare.length);
//loger.log("docJoinChannelSuccess docListPrepare=");
//如果是主持人,那么需要判断一下文档模块同步的数据和从sass获取的文档数据是否相同,如果mcu服务器不存在的,需要上传
if (GlobalConfig.docListPrepare && GlobalConfig.docListPrepare.length > 0) {
... ...
... ... @@ -100,9 +100,9 @@ class GlobalConfig {
}
//设置当前的课堂状态的信息
static setClassStatusInfo(_data) {
loger.log("setClassStatusInfo");
//loger.log("setClassStatusInfo");
if (_data == null) {
loger.warn("classStatusInfo error,_data:", _data);
loger.warn("设置当前的课堂状态的信失败:", _data);
return;
}
let data = _data;
... ... @@ -137,7 +137,7 @@ class GlobalConfig {
//this.recordStatus = data.recordStatus || this.recordStatus; //当前录制状态
this.recordStatus = data.recordStatus ||false; //当前录制状态
this.recordTimestamp = data.recordTimestamp || this.recordTimestamp; //相对于首次开始录制的时间戳
this.recordTimestamp =Math.max(parseInt(data.recordTimestamp), this.recordTimestamp); //录制时间取最大值
this.recordFileName = data.recordFileName || this.recordFileName; //录制的文件名
this.recordDownloadUrl = data.recordDownloadUrl || this.recordDownloadUrl; //下载地址
this.currentSceneTableId = data.currentSceneTableId || 0; //文档区域的模块显示
... ... @@ -523,7 +523,8 @@ GlobalConfig.recordReplaytickValues = {}; // 滚动条关键点,用于快进
GlobalConfig.isAutoStartClass = 0; //是否自动开始上课 0-否 ;1 是
GlobalConfig.updateClassInfoDelay = 20; //(秒),每隔30秒同步一次课堂状态的并保存到Sass
GlobalConfig.updateRecordTimeDelay = 4; //(秒),同步一次课堂录制状态的并保存到Sass
GlobalConfig.updateClassInfoDelay = 10; //(秒),同步一次课堂状态的并保存到Sass
GlobalConfig.msDynamicChooseIpDelay = 60 * 3; //(秒)MS动态选点的间隔
//GlobalConfig.serverTimestamp=0;//当前的系统时间戳 用get set 获取
... ...
... ... @@ -404,7 +404,7 @@ class IpManager extends Emiter {
}
//isp province都没有,使用default
let defaultData = countryData.default
let defaultData = countryData.default;
if (defaultData) {
// loger.log("_returnServerMS->defaultData",defaultData);
return defaultData;
... ...
... ... @@ -148,7 +148,7 @@ class LogManager {
})
.then(ret => {
if (ret == 0) {
console.log('保存日志信息 完成');
//console.log('保存日志信息 完成');
tempArr=[];
} else {
console.warn('保存日志信息 失败.', ret);
... ...
... ... @@ -440,7 +440,7 @@ class Sass extends Emiter {
//let url = `http://${GlobalConfig.portal}/3m/api/meeting/saveInfo.do`;
let url = `${GlobalConfig.locationProtocol+GlobalConfig.portal}/3m/api/meeting/saveInfo.do`;
loger.log('saveClassStatusInfo', url);
//loger.log('saveClassStatusInfo', url);
fetch(url, {
method: 'POST',
headers: {
... ... @@ -460,7 +460,7 @@ class Sass extends Emiter {
})
.then(ret => {
if (ret.code === 0) {
loger.log('saveClassStatusInfo 完成');
//loger.log('saveClassStatusInfo 完成');
this._emit(Sass.CLASS_SAVE_STATUS_INFO_SUCCESS, _param);
} else if (ret.code === 1) {
loger.log('saveClassStatusInfo 失败 课堂号为空');
... ... @@ -485,7 +485,7 @@ class Sass extends Emiter {
loger.log('录制回放中,不需要保存');
return;
}
loger.log('保存开始录制信息');
// loger.log('保存开始录制信息');
let key = "3mang123A";
let siteID = GlobalConfig.siteId;
let meetingID = GlobalConfig.classId;
... ... @@ -499,14 +499,14 @@ class Sass extends Emiter {
let confRecordFileName = GlobalConfig.recordFileName;
let downloadUrl = "";
let recordStatus = GlobalConfig.classStatus;
let recordTimestamp = GlobalConfig.classTimestamp;
let recordTimestamp = GlobalConfig.recordTimestamp;//GlobalConfig.classTimestamp;//使用录制时间,不再使用课堂的进行时间
let timestamp = new Date().getTime();
let authId = MD5(key + siteID + meetingID + timestamp);
//let url = `http://${GlobalConfig.portal}/3m/recordingMeeting/insertRecordingMeeting.do`;
let url = `${GlobalConfig.locationProtocol+GlobalConfig.portal}/3m/recordingMeeting/insertRecordingMeeting.do`;
loger.log('saveClassRecordContrlInfo', url);
loger.log('保存开始录制信息:', url);
fetch(encodeURI(url), {
method: 'POST',
... ...
... ... @@ -25,6 +25,8 @@ ApeConsts.CLASS_ACTION_CLOSE_ALL = 1; //所有人关闭课堂
ApeConsts.CLASS_ACTION_HANDUP_STATUS_CHANGE = 2; //更改用户的举手状态
ApeConsts.USER_ACTION_SILENCE_STATUS_CHANGE = 3; //更改用户的禁言状态
ApeConsts.CLASS_ACTION_KICK_OUT_ROSTER=4; //指定的人踢出课堂
ApeConsts.STOP_ALL_PUBLISH=5; //所有人停止推流
//课堂类型 1:1v1(2路流) 2:直播(1路流) 3:小班课(可以多路流)
ApeConsts.CLASS_TYPE_1 = 1; // 互动课堂,通过MS转发音视频,不能进行H5观看 1v1(2路流)
... ...
... ... @@ -384,7 +384,10 @@ class AudioApe extends Ape {
this._emit(MessageTypes.AUDIO_BROADCAST, audioReceivePdu);
}
}
//切换了MS,重新更新一下当前正在播放的流地址
changeMediaMs(){
}
tableUpdateHandler(owner, itemIdx, itemData, seek) {
let unpackChannelInfo = this.unPackPdu(owner, itemIdx, itemData);
loger.log("tableUpdateHandler->channel", itemIdx, 'status->', unpackChannelInfo.status, "seek->", seek);
... ... @@ -479,6 +482,10 @@ class AudioApe extends Ape {
} else {
channelInfo.owner = channelInfo.fromNodeId;
}
//owner为0就是没有使用
if(channelInfo.owner==0){
channelInfo.status = ApeConsts.CHANNEL_STATUS_RELEASED
}
this.sendTableUpdateHandler(channelInfo);
}
}
... ...
... ... @@ -27,7 +27,7 @@ class ConferApe extends Ape {
this.rosters = {}; //用户列表
this.rosterLen = 0;//当前课堂人数
this.timerCounter = new TimerCounter(); //计时器
this.startClassTimer;//开始课堂和开始录制的计时器
this.startRecordTimer;//开始录制的计时器
//第三方消息控制 parent和Iframe直接的通讯
this.thirdMessage = new ThirdMessage();
... ... @@ -271,19 +271,19 @@ class ConferApe extends Ape {
loger.warn('不能再录制,录制时间已经达到最大限制', GlobalConfig.recordTimestamp);
return;
}
loger.warn('检测是否需要开启录制', "isHost", GlobalConfig.isHost, "recordStatus", GlobalConfig.recordStatus,"当前人数:"+this.rosterLen);
//如果已经开始录制就不再开启
if(GlobalConfig.recordStatus&&this.rosterLen>1){
loger.warn('目前已经是录制状态->当前课堂人数:'+this.rosterLen);
return false;
}
//如果是host或者当前课堂只有1个人
if (GlobalConfig.isHost||this.rosterLen<=1) {
/* if(GlobalConfig.recordStatus){
loger.warn("课堂已经是录制状态不->不需要再开启,isHost", GlobalConfig.isHost, "recordStatus", GlobalConfig.recordStatus,"当前人数:"+this.rosterLen);
}else{
loger.warn('开启录制', "isHost", GlobalConfig.isHost, "recordStatus", GlobalConfig.recordStatus,"当前人数:"+this.rosterLen);
GlobalConfig.classStopTime = EngineUtils.creatTimestampStr();
this.sendConferRecordMsg({"recordStatus": true});
this._emit(MessageTypes.CLASS_STATUS_INFO_CHANGE);
this._emit(MessageTypes.CLASS_RECORD_START); //课堂开始录制
}*/
if (this.checkHasRecordControl()) {
loger.warn('开启录制', "isHost", GlobalConfig.isHost, "recordStatus", GlobalConfig.recordStatus,"当前人数:"+this.rosterLen);
//如果录制的文件名不存在,需要创建一个名字
let timestampYMD = EngineUtils.creatTimestampYMD();
if(!GlobalConfig.recordFileName){
GlobalConfig.recordFileName=GlobalConfig.siteId + "/" + timestampYMD + "/" + GlobalConfig.classId + "_" + timestampYMD + ".rec"; //4、文件名称 $RECORD_HOME/`site id`/`日期`/`filename` 例:/data/record/su/20161216/`filename`
}
GlobalConfig.classStopTime = EngineUtils.creatTimestampStr();
this.sendConferRecordMsg({"recordStatus": true});
this._emit(MessageTypes.CLASS_STATUS_INFO_CHANGE);
... ... @@ -291,30 +291,44 @@ class ConferApe extends Ape {
}
}
//检测是否有控制录制操作的权限
checkHasRecordControl(){
//loger.warn('检测是否有控制录制操作的权限', "isHost", GlobalConfig.isHost, "recordStatus", GlobalConfig.recordStatus,"当前人数:"+this.rosterLen);
//1.如果自己是老师或者当前课堂只有一个人
if(GlobalConfig.isHost||this.rosterLen<=1){
return true;
}
//2.如果自己不是老师,需要判断当前课堂内是否有老师,如果有老师就不做操作
for(let i in this.rosters){
//如果就老师就停止
let rosterItem=this.rosters[i];
if(rosterItem&&rosterItem.userRole==ApeConsts.host){
return false;
}
}
//3.课堂内有多个人并且都不是老师,选择一个nodeId最小的来操作
for(let k in this.rosters){
//如果选择的nodeId是自己就有权限,否则没有权限
if(k==GlobalConfig.nodeId){
return true;
}else {
return false;
}
}
return false
}
//停止录制
stopRecord(isForce) {
/* if (isForce && isForce == true) {
//强制停止,可以是host之外的身份(比如当前课堂老师异常退出,没有老师,会随机选择一个人来做释放操作)
if (GlobalConfig.recordStatus) {
//判断是否有权限
if (this.checkHasRecordControl()){
loger.warn('停止录制', "isHost", GlobalConfig.isHost, "recordStatus", GlobalConfig.recordStatus);
//this.sendUpdaterClassStatusInfo({"actionType":GlobalConfig.classStatus, isStopAllPublishMedia: true});
this.sendConferMsg({"to": 0, "message": "STOP_ALL_PUBLISH", "actionType": ApeConsts.STOP_ALL_PUBLISH});
setTimeout(()=>{
GlobalConfig.classStopTime = EngineUtils.creatTimestampStr();
this.sendConferRecordMsg({"recordStatus": false});
this._emit(MessageTypes.CLASS_STATUS_INFO_CHANGE);
}
} else {
//身份是host,并且当前正在录制中
if (GlobalConfig.isHost && GlobalConfig.recordStatus) {
GlobalConfig.classStopTime = EngineUtils.creatTimestampStr();
this.sendConferRecordMsg({"recordStatus": false});
this._emit(MessageTypes.CLASS_STATUS_INFO_CHANGE);
}
}*/
//身份是host
if (GlobalConfig.isHost) {
loger.warn('停止录制', "isHost", GlobalConfig.isHost, "recordStatus", GlobalConfig.recordStatus);
GlobalConfig.classStopTime = EngineUtils.creatTimestampStr();
this.sendConferRecordMsg({"recordStatus": false});
this._emit(MessageTypes.CLASS_STATUS_INFO_CHANGE);
},2000);
}else {
loger.warn('没有权限停止录制', "isHost", GlobalConfig.isHost, "recordStatus", GlobalConfig.recordStatus);
}
... ... @@ -363,7 +377,6 @@ class ConferApe extends Ape {
this._emit(MessageTypes.CLASS_STATUS_INFO_CHANGE);
//this.sendUpdaterClassStatusInfo({"actionType": 0, isStopAllPublishMedia: true});
this.sendUpdaterClassStatusInfo({"actionType": ApeConsts.CLASS_STATUS_WAIT, isStopAllPublishMedia: false});
loger.log('restorClass');
}
// 全局禁言
... ... @@ -400,10 +413,12 @@ class ConferApe extends Ape {
let timestamp = EngineUtils.creatTimestampStr();
GlobalConfig.classStopTime = timestamp;
/*
//如果录制的文件名不存在,需要创建一个名字
let timestampYMD = EngineUtils.creatTimestampYMD();
GlobalConfig.recordFileName = GlobalConfig.recordFileName ||
GlobalConfig.siteId + "/" + timestampYMD + "/" + GlobalConfig.classId + "_" + timestampYMD + ".rec"; //4、文件名称 $RECORD_HOME/`site id`/`日期`/`filename` 例:/data/record/su/20161216/`filename`
*/
if (GlobalConfig.classStatus == ApeConsts.CLASS_STATUS_WAIT) {
//之前是为开始状态,第一次点开始
... ... @@ -418,8 +433,8 @@ class ConferApe extends Ape {
//同步课堂状态
//this.sendUpdaterClassStatusInfo({"actionType": 1, isStopAllPublishMedia: true});
this.sendUpdaterClassStatusInfo({"actionType": ApeConsts.CLASS_STATUS_STARTED, isStopAllPublishMedia: false});
//开始计时
this.startTimerCounter();
////开始计时
//this.startTimerCounter();
} else {
loger.warn('没有开始课堂的权限');
}
... ... @@ -451,7 +466,7 @@ class ConferApe extends Ape {
loger.warn("调用关闭课堂->");
this.stopTimerCounter();
this.restorClass();
//把所有人都踢出课堂
//停止所有人推流,把所有人都踢出课堂
this.sendConferMsg({"to": 0, "message": "所有人退出课堂", "actionType": ApeConsts.CLASS_ACTION_CLOSE_ALL});
this.stopRecord();//关闭课堂的时候停止录制
}
... ... @@ -557,7 +572,7 @@ class ConferApe extends Ape {
//更新课堂信息
sendUpdaterClassStatusInfo(_param) {
//{"actionType": 1,isStopAllPublishMedia:false} //actionType课堂状态 isStopAllPublishMedia是否停止当前的所有推流
loger.log('发送更新课堂信息->');
//loger.log('发送更新课堂信息->');
if (_param == null || EngineUtils.isEmptyObject(_param)) {
loger.log('发送更新课堂信息->参数错误');
this._emit(MessageTypes.MCU_ERROR, MessageTypes.ERR_APE_INTERFACE_PARAM_WRONG);
... ... @@ -610,21 +625,11 @@ class ConferApe extends Ape {
}
loger.log('课堂模块初始完成->当前课堂状态:'+ GlobalConfig.classStatus,"recordStatus:"+GlobalConfig.recordStatus);
this.timerCounter.addTimerCallBack(this.timerCounterUptate.bind(this), 1);
//2秒后执行开始上课和开始录制的判断逻辑
clearTimeout(this.startClassTimer);
this.startClassTimer=setTimeout(()=>{
//开启录制,录制接口中会自动判断是否需要录制
this.startRecord();
//如果当前课堂正在进行中,开启计时器
if (GlobalConfig.classStatus == ApeConsts.CLASS_STATUS_STARTED) {
//开始计时
this.startTimerCounter();
//this.startRecord();
} else {
loger.log('自动开始上课->classStatus:', GlobalConfig.classStatus, " isHost:", GlobalConfig.isHost, " isAutoStartClass:", GlobalConfig.isAutoStartClass, " isRecordPlayBack:", GlobalConfig.isRecordPlayBack);
this.startClass();
}
clearTimeout(this.startRecordTimer);
this.startRecordTimer=setTimeout(()=>{
this.startRecord();
},2000);
/* //如果当前课堂正在进行中,开启计时器
... ... @@ -672,7 +677,7 @@ class ConferApe extends Ape {
//以一定的时间间隔同步课堂内所有人的累积上课时间
if (GlobalConfig.classTimestamp % GlobalConfig.updateClassInfoDelay == 0) {
//如果是host身份,需要同步时间给其他人,同时把当前的状态上传到服务器
if (GlobalConfig.isHost||this.rosterLen<=1) {
if (this.checkHasRecordControl()) {
//保存数据到Sass
this._emit(MessageTypes.CLASS_STATUS_INFO_CHANGE);
//同步消息给其他人
... ... @@ -690,8 +695,23 @@ class ConferApe extends Ape {
this._emit(MessageTypes.SWITCH_HLS_IP);
}
}
//更新录制进行时间
GlobalConfig.recordTimestamp = GlobalConfig.recordTimestamp + 1;
if (this.checkHasRecordControl()) {
//以一定的时间间隔同步课堂内所有人的累积上课时间
if (GlobalConfig.recordTimestamp % GlobalConfig.updateRecordTimeDelay == 0) {
//保存数据到Sass
this._emit(MessageTypes.CLASS_STATUS_INFO_CHANGE);
}
if (GlobalConfig.recordTimestamp % GlobalConfig.updateClassInfoDelay == 0) {
//MCU同步消息给其他人
this.sendUpdaterClassStatusInfo({"actionType": GlobalConfig.classStatus, isStopAllPublishMedia: false});
}
}
/* if (GlobalConfig.classStatus == ApeConsts.CLASS_STATUS_STARTED) {
GlobalConfig.classTimestamp = GlobalConfig.classTimestamp + 1; //计时
//老师身份的时候要记录录制的时间
... ... @@ -774,9 +794,11 @@ class ConferApe extends Ape {
tableUpdateHandler(owner, itemIdx, itemData) {
try {
let model = this.unPackPdu(owner, itemIdx, itemData);
loger.log("课堂更新->",model);
//loger.log("课堂更新->",model.classStatusInfo);
//{"itemIdx":720899,"from":976168842,"owner":976168842,"actionType":null,"classStatusInfo":{"nodeId":976168842,"userId":"user_979813","userName":"user_979813","siteId":"markettest","classId":889112694,"className":"ly828-1v1","classType":1,"classStatus":1,"classStartTime":"2017-8-28-16-33-18","classStopTime":"2017-8-29-11-9-28","classTimestamp":1930,"classBeginTime":"2017-08-28 16:00:00","classEndTime":"2017-08-31 18:00:00","recordStatus":false,"recordTimestamp":2825,"recordFileName":"markettest/20170828/889112694_20170828.rec","recordDownloadUrl":"","serverTimestamp":1503976246,"activeDocId":976170739,"activeDocCurPage":1,"isStopAllPublishMedia":false,"currentSceneTableId":0,"silence":false,"silenceUsers":"{}","isEnableDraw":true}}
//处理课堂更新的信息
if (model && model.classStatusInfo) {
// loger.log("课堂更新->",model.classStatusInfo);
try {
model.classStatusInfo.silenceUsers = JSON.parse(model.classStatusInfo.silenceUsers);
} catch (err) {
... ... @@ -801,13 +823,13 @@ class ConferApe extends Ape {
return;
}
if (GlobalConfig.classStatus == ApeConsts.CLASS_STATUS_STARTED) {
/* if (GlobalConfig.classStatus == ApeConsts.CLASS_STATUS_STARTED) {
//如果课堂在进行中,开始计时器
this.startTimerCounter();
} else {
//停止计时
this.stopTimerCounter();
}
}*/
} catch (err) {
loger.warn('课堂更新->error', err.message);
}
... ... @@ -825,8 +847,15 @@ class ConferApe extends Ape {
switch (chatMsg.actionType) {
case ApeConsts.CLASS_ACTION_CLOSE_ALL:
loger.log(chatMsg.message);
//关闭之前停止所有推流
this._emit(MessageTypes.STOP_ALL_MEDIA_PUBLISH);
//收到课堂关闭,所有人都退出,执行自己关闭的流程
setTimeout(()=>{
this._emit(MessageTypes.CLASS_RUN_EXIT, {'type': 1});
},2000);
break;
case ApeConsts.STOP_ALL_PUBLISH:
this._emit(MessageTypes.STOP_ALL_MEDIA_PUBLISH);
break;
case ApeConsts.CLASS_ACTION_KICK_OUT_ROSTER:
if (chatMsg.toNodeID == GlobalConfig.nodeId) {
... ... @@ -1116,23 +1145,15 @@ class ConferApe extends Ape {
//当前人员列表中抽一个人来检查离开人员是否占用频道
for (let key in this.rosters) {
let randNodeId = parseInt(key);
//如果抽到的人是自己就处理以下操作
//判断是否是自己就处理以下操作
if (randNodeId == GlobalConfig.nodeId) {
loger.log(randNodeId, "有权限检查离开的人员是否占用channel");
loger.log("检查离开的人员是否占用channel");
this._emit(MessageTypes.CLASS_NONENTITY_ROSTER, {"nodeId": nodeId, "rosterLen": this.rosterLen});
//如果离开的人员是老师,需要暂停当前的课堂
if (user && user.role == ApeConsts.NR_HOST) {
this.pauseClass();
/* //强制停止录制
this.stopRecord(true)*/;
}
} else {
loger.warn(GlobalConfig.nodeId, "没有权限检查离开的人员是否占用channel");
}
//查找到一个就跳出操作
return;
}
}
... ... @@ -1207,7 +1228,7 @@ class ConferApe extends Ape {
let modelPdu = pdu['RCClassSendDataModelPdu'].decode(itemData);
return modelPdu;
} catch (err) {
loger.log("课堂收到数据 unPackPdu Pdu解析错误,itemIdx=" + itemIdx + " err:" + err.message);
loger.log("课堂收到数据解析错误:" + itemIdx + " err:" + err.message);
}
return null;
}
... ...
... ... @@ -389,7 +389,7 @@ class DocApe extends Ape {
docDataModel.animationStep = 1;//切换文档之后动画步数还原
//loger.log('切换文档,当前文档和上一个显示的文档都需要更新状态');
loger.log({"oldDoc": oldDocModel, "nowDoc": docDataModel});
//loger.log({"oldDoc": oldDocModel, "nowDoc": docDataModel});
//更新当前选择的文档
this.updaterDoc(docDataModel, docDataModel.itemIdx);
... ... @@ -673,7 +673,7 @@ class DocApe extends Ape {
}
onJoinChannelHandlerSuccess() {
loger.log(this._session_name + ' onJoinChannelHandlerSuccess===========================');
//loger.log(this._session_name + ' onJoinChannelHandlerSuccess===========================');
if (this._apeDelayed) {
// this._apeDelayedMsgs.push(regBuffer);
// this._apeDelayedStart();
... ...
... ... @@ -309,7 +309,7 @@ class VideoApe extends Ape {
//释放nodeId占用的指定的channelId频道
_releaseChannelForNodeId(nodeId, channelId) {
loger.log(nodeId, "_releaseChannelForNodeId-->channelId", channelId);
loger.log(nodeId, "停止-->channelId", channelId);
let channelInfo = this.mediaModule.mediaChannels[channelId];
if (channelInfo && channelInfo.status == ApeConsts.CHANNEL_STATUS_OPENING) {
if (channelInfo.fromNodeId == nodeId) {
... ... @@ -480,11 +480,13 @@ class VideoApe extends Ape {
this._emit(MessageTypes.VIDEO_BROADCAST, videoReceivePdu);
}
}
//切换了MS,重新更新一下当前正在播放的流地址
changeMediaMs(){
}
tableUpdateHandler(owner, itemIdx, itemData, seek) {
let unpackChannelInfo = this.unPackPdu(owner, itemIdx, itemData);
loger.log("tableUpdateHandler->channel", itemIdx, 'mediaType', unpackChannelInfo.mediaType, 'status->', unpackChannelInfo.status, "seek->", seek);
loger.log("视频模块数据更新->channel", itemIdx, 'mediaType', unpackChannelInfo.mediaType, 'status->', unpackChannelInfo.status, "seek->", seek);
//****很重要********
//如果owner的值为0,代表的是这个歌频道已经被释放了(mcu服务端对于占用channel的掉线用户,就是把owner设置为0)
... ... @@ -586,8 +588,6 @@ class VideoApe extends Ape {
});
}
}
if (unpackChannelInfo.mediaType != ApeConsts.MEDIA_TYPE_SHARE) {
//非屏幕共享情况的处理
MediaModule.allMediaChannelsList[itemIdx] = unpackChannelInfo;
... ... @@ -610,7 +610,7 @@ class VideoApe extends Ape {
}
//owner为0就是没有使用
if(channelInfo.owner==0){
channelInfo.status == ApeConsts.CHANNEL_STATUS_RELEASED
channelInfo.status = ApeConsts.CHANNEL_STATUS_RELEASED
}
this.sendTableUpdateHandler(channelInfo);
}
... ...