diff --git a/node_modules/iphunter/dist/main.js b/node_modules/iphunter/dist/main.js index c98d393..7da04af 100644 --- a/node_modules/iphunter/dist/main.js +++ b/node_modules/iphunter/dist/main.js @@ -1 +1 @@ -!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 diff --git a/node_modules/iphunter/src/main.js b/node_modules/iphunter/src/main.js index 80bb5a5..1728d93 100644 --- a/node_modules/iphunter/src/main.js +++ b/node_modules/iphunter/src/main.js @@ -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); } diff --git a/src/EngineEntrance.js b/src/EngineEntrance.js index 8bc12eb..1a6f6b6 100644 --- a/src/EngineEntrance.js +++ b/src/EngineEntrance.js @@ -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) { - //POST 保存数据 - _sass.saveClassStatusInfo({"classStatusInfo": GlobalConfig.classStatusInfo}); //保存课堂状态信息 - } else { - loger.error("不能保存课堂数据", GlobalConfig.getCurrentStatus()); - }*/ - if (_mcu&&_mcu.connected) { + if (_confer_ape.checkHasRecordControl()||isForce) { //POST 保存数据 - _sass.saveClassStatusInfo({"classStatusInfo": GlobalConfig.classStatusInfo}); //保存课堂状态信息 - } else { - loger.error("不能保存课堂数据", GlobalConfig.getCurrentStatus()); - } - + clearTimeout(this.saveClassStatusTimer); + this.saveClassStatusTimer=setTimeout(()=>{ + _sass.saveClassStatusInfo({"classStatusInfo": GlobalConfig.classStatusInfo}); //保存课堂状态信息 + },600); } 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) { diff --git a/src/GlobalConfig.js b/src/GlobalConfig.js index 99a3eae..9725f4d 100644 --- a/src/GlobalConfig.js +++ b/src/GlobalConfig.js @@ -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 获取 diff --git a/src/IpManager.js b/src/IpManager.js index 22d8fa7..45271f2 100644 --- a/src/IpManager.js +++ b/src/IpManager.js @@ -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; diff --git a/src/LogManager.js b/src/LogManager.js index 1c0ecba..00eb028 100644 --- a/src/LogManager.js +++ b/src/LogManager.js @@ -148,7 +148,7 @@ class LogManager { }) .then(ret => { if (ret == 0) { - console.log('保存日志信息 完成'); + //console.log('保存日志信息 完成'); tempArr=[]; } else { console.warn('保存日志信息 失败.', ret); diff --git a/src/Sass.js b/src/Sass.js index d9340e8..380d915 100644 --- a/src/Sass.js +++ b/src/Sass.js @@ -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', diff --git a/src/apes/ApeConsts.js b/src/apes/ApeConsts.js index 02d6c38..7797de0 100644 --- a/src/apes/ApeConsts.js +++ b/src/apes/ApeConsts.js @@ -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路流) diff --git a/src/apes/AudioApe.js b/src/apes/AudioApe.js index 8141e96..6f93700 100644 --- a/src/apes/AudioApe.js +++ b/src/apes/AudioApe.js @@ -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); } } diff --git a/src/apes/ConferApe.js b/src/apes/ConferApe.js index a9fa710..dbf21bf 100644 --- a/src/apes/ConferApe.js +++ b/src/apes/ConferApe.js @@ -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,50 +271,64 @@ 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); - GlobalConfig.classStopTime = EngineUtils.creatTimestampStr(); + //如果录制的文件名不存在,需要创建一个名字 + 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); this._emit(MessageTypes.CLASS_RECORD_START); //课堂开始录制 } } + //检测是否有控制录制操作的权限 + 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) { - loger.warn('停止录制', "isHost", GlobalConfig.isHost, "recordStatus", 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.startTimerCounter(); + this.startClass(); + clearTimeout(this.startRecordTimer); + this.startRecordTimer=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(); - } },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); //收到课堂关闭,所有人都退出,执行自己关闭的流程 - this._emit(MessageTypes.CLASS_RUN_EXIT, {'type': 1}); + 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; } diff --git a/src/apes/DocApe.js b/src/apes/DocApe.js index ced70a4..8c38bce 100644 --- a/src/apes/DocApe.js +++ b/src/apes/DocApe.js @@ -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(); diff --git a/src/apes/VideoApe.js b/src/apes/VideoApe.js index 0761585..40a9bba 100644 --- a/src/apes/VideoApe.js +++ b/src/apes/VideoApe.js @@ -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); }