diff --git a/src/EngineEntrance.js b/src/EngineEntrance.js
index 49030e8..b918b90 100644
--- a/src/EngineEntrance.js
+++ b/src/EngineEntrance.js
@@ -62,19 +62,19 @@ export default class MessageEntrance extends Emiter {
   constructor() {
     super();
     //sdk 信息
-    GlobalConfig.sdkVersion = "v2.21.3.20171024";
+    GlobalConfig.sdkVersion = "v2.22.7.20171026";
     loger.warn("sdkVersion:" + GlobalConfig.sdkVersion);
     console.log("sdkVersion:" + GlobalConfig.sdkVersion);
     //设置
-    let locationProtocol= location.protocol;
-    if(locationProtocol=="https:"){
-      GlobalConfig.isHttps=true;
-      GlobalConfig.locationProtocol="https://";
-      GlobalConfig.websocketProtocol="wss://";
-    }else {
-      GlobalConfig.isHttps=false;
-      GlobalConfig.locationProtocol="http://";
-      GlobalConfig.websocketProtocol="ws://";
+    let locationProtocol = location.protocol;
+    if (locationProtocol == "https:") {
+      GlobalConfig.isHttps = true;
+      GlobalConfig.locationProtocol = "https://";
+      GlobalConfig.websocketProtocol = "wss://";
+    } else {
+      GlobalConfig.isHttps = false;
+      GlobalConfig.locationProtocol = "http://";
+      GlobalConfig.websocketProtocol = "ws://";
     }
     loger.warn("protocol:" + GlobalConfig.locationProtocol);
     //获取设备和系统信息
@@ -91,12 +91,12 @@ export default class MessageEntrance extends Emiter {
     this.isGetFastestRtmpPullCallback = false; //是否RTMP拉流地址测试结束
     this.isGetFastestHlsPullCallback = false; //是否HLS拉流地址测试结束
     this.isGetFastestRsCallback = false; //是否录制回放HLS拉流地址测试结束
-    this.saveClassStatusTimer=0;//保存课堂数据的计时器间隔,防止同一瞬间多次提交
-    this.joinChannelTimer=0;//加入音视频通道的间隔
+    this.saveClassStatusTimer = 0;//保存课堂数据的计时器间隔,防止同一瞬间多次提交
+    this.joinChannelTimer = 0;//加入音视频通道的间隔
     //全局的Error处理
     this.on(MessageTypes.MCU_ERROR, this._mcuErrorHandler.bind(this));
 
-    _webRtc=WebRtcApe;
+    _webRtc = WebRtcApe;
     _webRtc.on('*', (type, data) => this._emit(type, data));
     _webRtc.on(MessageTypes.USER_DEVICE_STATUS_CHAANGE, this.userDeviecStatusChange.bind(this)); //监听摄像头和麦克风的开启状态
     _webRtc.on(MessageTypes.MEDIA_PUBLISH_STATUS_CHANGE, this.mediaPublishStatusChange.bind(this)); //webRtc推流状态发生改变
@@ -107,7 +107,7 @@ export default class MessageEntrance extends Emiter {
     _sass.on(_sass.SUCCESS, this._sassJoinSuccessHandler.bind(this)); //通过SASS平台验证(密码和MD5)
     _sass.on(_sass.CLASS_INIT_SUCCESS, this._sassInitSuccessHandler.bind(this)); //获取课堂初始化信息
     //_sass.on(_sass.CLASS_GET_CLASS_DETAIL, this._sassGetClassDetailSuccessHandler.bind(this));//获取课堂的基本信息
-    _sass.on(_sass.CLASS_GET_CLASS_PARAM, this._sassGetClassParamSuccessHandler.bind(this)); //获取课堂的最全信息和历史保存的数据
+    _sass.on(_sass.CLASS_GET_CLASS_PARAM, this._sassGetClassParamSuccessHandler.bind(this)); //SAAS获取课堂的最全信息和历史保存的数据
 
     _sass.on(_sass.CLASS_SAVE_STATUS_INFO_SUCCESS, this._sassSaveClassStatusInfoSuccessHandler.bind(this)); //保存课堂状态信息
     _sass.on(_sass.CLASS_SAVE_RECORD_INFO_SUCCESS, this._sassSaveClassRecordInfoSuccessHandler.bind(this)); //保存课堂录制信息
@@ -117,7 +117,7 @@ export default class MessageEntrance extends Emiter {
 
     //选点模块
     _ipManager = new IpManager();
-    _base64=new Base64Module();
+    _base64 = new Base64Module();
 
     // 底层MCU消息层
     _mcu = Mcu;
@@ -148,7 +148,7 @@ export default class MessageEntrance extends Emiter {
     //_confer_ape.on(MessageTypes.SWITCH_RTMP_PULL_IP, this._switchRtmpPullIpHandler.bind(this)); //MS 拉流地址动态选点
     //_confer_ape.on(MessageTypes.SWITCH_HLS_IP, this._switchHlsIpHandler.bind(this)); //MS HLS动态选点
     _confer_ape.on(MessageTypes.STOP_ALL_MEDIA_PUBLISH, this._stopAllMediaPublishHandler.bind(this)); //课堂状态发生改变,需要停止当前的所有推流
-    _confer_ape.on(MessageTypes.CLASS_UPDATE_ROSTER,this._onRosterUpdateHandler.bind(this));
+    _confer_ape.on(MessageTypes.CLASS_UPDATE_ROSTER, this._onRosterUpdateHandler.bind(this));
 
     _chat_ape = new ChatApe();
     _chat_ape.on('*', (type, data) => this._emit(type, data));
@@ -222,6 +222,10 @@ export default class MessageEntrance extends Emiter {
     this.publishScreenShare = this._publishScreenShare.bind(this);
     this.stopPublishScreenShare = this._stopPublishScreenShare.bind(this);
 
+    //推送外部流
+    this.publishExternalLink = this._publishExternalLink.bind(this);
+    this.stopPublishExternalLink = this._stopPublishExternalLink.bind(this);
+
     //videoApe
     this.getVideoPublishPath = this._getVideoPublishPath.bind(this);
     this.getVideoAllChannelInfo = this._getVideoAllChannelInfo.bind(this);
@@ -261,7 +265,7 @@ export default class MessageEntrance extends Emiter {
     //获取文档图片的完整路径
     this.getDocPDFFullPath = this._getDocPDFFullPath.bind(this); //获取文档的完整路径
     this.getDocFullAddress = this._getDocFullAddress.bind(this); //获取文档资源地址
-    this.hideCurrentDocument=this._hideCurrentDocument.bind(this);//隐藏当前显示的文档
+    this.hideCurrentDocument = this._hideCurrentDocument.bind(this);//隐藏当前显示的文档
     this.switchToWhiteboard = this._switchToWhiteboard.bind(this); //切换到白板文档
     //媒体共享模块
     this.mediaSharedUpload = this._sendMediaSharedUpload.bind(this); //上传
@@ -287,19 +291,19 @@ export default class MessageEntrance extends Emiter {
     this.stopQuestion = this._stopQuestion.bind(this);
 
     //webrtc
-    this.publishMedia=this._publishMedia.bind(this);
-    this.unpublishMedia=this._unpublishMedia.bind(this);
-    this.changeDevices=this._changeDevices.bind(this);
-    this.setConfigPublisher=this._setConfigPublisher.bind(this);
-    this.setLocalMediaView=this._setLocalMediaView.bind(this);//设置自己的视图
-    this.setHostRemoteMediaView=this._setHostRemoteMediaView.bind(this);//设置远程老师的视图
-    this.setNormalRemoteMediaView=this._setNormalRemoteMediaView.bind(this);//设置远程学生的视图
-    this.setInvisibleMediaView=this._setInvisibleMediaView.bind(this);//设置监课身份的视图
+    this.publishMedia = this._publishMedia.bind(this);
+    this.unpublishMedia = this._unpublishMedia.bind(this);
+    this.changeDevices = this._changeDevices.bind(this);
+    this.setConfigPublisher = this._setConfigPublisher.bind(this);
+    this.setLocalMediaView = this._setLocalMediaView.bind(this);//设置自己的视图
+    this.setHostRemoteMediaView = this._setHostRemoteMediaView.bind(this);//设置远程老师的视图
+    this.setNormalRemoteMediaView = this._setNormalRemoteMediaView.bind(this);//设置远程学生的视图
+    this.setInvisibleMediaView = this._setInvisibleMediaView.bind(this);//设置监课身份的视图
 
-    this.setAppConfig=this._setAppConfig.bind(this);
-    this.recordControl=this._mediaRecordControl.bind(this);
+    this.setAppConfig = this._setAppConfig.bind(this);
+    this.recordControl = this._mediaRecordControl.bind(this);
 
-    this.changeRtcVideoConfig=this._changeRtcVideoConfig.bind(this);//设置webRtc视频视图的缩放
+    this.changeRtcVideoConfig = this._changeRtcVideoConfig.bind(this);//设置webRtc视频视图的缩放
 
     this.setDeviceInfo = this._setDeviceInfo.bind(this); //设置设备信息(麦克风,摄像头等等.....)
     this.setMessageDelay = this._setMessageDelay.bind(this); //设置是否延迟消息
@@ -314,7 +318,12 @@ export default class MessageEntrance extends Emiter {
     this.addWarn = this._addWarn.bind(this);
     this.addError = this._addError.bind(this);
 
-    this.hasFreePublishChannel=this._hasFreePublishChannel.bind(this);//判断是否还有空闲的推流通道
+    this.hasFreePublishChannel = this._hasFreePublishChannel.bind(this);//判断是否还有空闲的推流通道
+
+
+    //添加外部流数据和删除外部流数据
+    this.deleteMediaExternalLink=this._deleteMediaExternalLink.bind(this);
+    this.addMediaExternalLink=this._addMediaExternalLink.bind(this);
   }
 
   //设置是否输出日志
@@ -337,21 +346,21 @@ export default class MessageEntrance extends Emiter {
   //上传log日志
   _addLog(_data) {
     if (_data) {
-      LogManager.addLog(LogManager.LOG, _data.msg||"");
+      LogManager.addLog(LogManager.LOG, _data.msg || "");
     }
   }
 
   //上传warn日志
   _addWarn(_data) {
     if (_data) {
-      LogManager.addLog(LogManager.WARN, _data.msg||"");
+      LogManager.addLog(LogManager.WARN, _data.msg || "");
     }
   }
 
   //上传error日志
   _addError(_data) {
     if (_data) {
-      LogManager.addLog(LogManager.ERROR, _data.msg||"");
+      LogManager.addLog(LogManager.ERROR, _data.msg || "");
     }
   }
 
@@ -434,7 +443,7 @@ export default class MessageEntrance extends Emiter {
   //当前的课堂状态信息发生改变,需要保存课堂状态到Sass
   _onClassStatusInfoChange(_param) {
     //如果MCU连接已经断开,不发送
-    if (!_mcu||!_mcu.connected) {
+    if (!_mcu || !_mcu.connected) {
       loger.warn("不能保存课堂状态", GlobalConfig.getCurrentStatus());
       return;
     }
@@ -443,7 +452,7 @@ export default class MessageEntrance extends Emiter {
 
   //如果是第一次点击开始上课,需要创建录制时的文件名
   _onClassRecordStart(_param) {
-    if (!_mcu||!_mcu.connected) {
+    if (!_mcu || !_mcu.connected) {
       loger.warn("不能保存课堂状态", GlobalConfig.getCurrentStatus());
       return;
     }
@@ -463,7 +472,7 @@ export default class MessageEntrance extends Emiter {
 
   //录制状态发送改变,更新所有模块的当前数据发送到MCU
   updaterRecordAllApeStatus(_param) {
-    if(GlobalConfig.isRecordPlayBack||!_confer_ape){
+    if (GlobalConfig.isRecordPlayBack || !_confer_ape) {
       return;
     }
     //老师身份和非录制回放的时候执行,录制状态发送改变,需要更新当前的数据,否则已有的消息会录制不上
@@ -496,17 +505,18 @@ export default class MessageEntrance extends Emiter {
   _onClassDeleteRoster(_data) {
 
   }
+
   //人员更新
-  _onClassUpdateRoster(_data){
-    if(!_data){
+  _onClassUpdateRoster(_data) {
+    if (!_data) {
       return;
     }
-    if(GlobalConfig.deviceType==1||GlobalConfig.deviceType==2||GlobalConfig.deviceType==3||_data.nodeId==GlobalConfig.nodeId){
+    if (GlobalConfig.deviceType == 1 || GlobalConfig.deviceType == 2 || GlobalConfig.deviceType == 3 || _data.nodeId == GlobalConfig.nodeId) {
       return;
     }
-    if(_webRtc){
-      let user=GlobalConfig.rosters[_data.nodeId];
-      if(user&&user.openCamera==0){
+    if (_webRtc) {
+      let user = GlobalConfig.rosters[_data.nodeId];
+      if (user && user.openCamera == 0) {
         _webRtc.closeRemoteVideoView(_data);
       }
     }
@@ -559,14 +569,14 @@ export default class MessageEntrance extends Emiter {
     //保存参数
     GlobalConfig.isRecordPlayBack = false; //设置为非录制回放状态
     GlobalConfig.classId = parseInt(_param.classId);
-    GlobalConfig.portal = _param.portal||"";
-    GlobalConfig.openFlash=Boolean(_param.openFlash);
-    if(GlobalConfig.isHttps==true){
+    GlobalConfig.portal = _param.portal || "";
+    GlobalConfig.openFlash = Boolean(_param.openFlash);
+    if (GlobalConfig.isHttps == true) {
       //https的时候替换所有80端口
-      GlobalConfig.portal= GlobalConfig.replacePort(GlobalConfig.portal,":80","");
+      GlobalConfig.portal = GlobalConfig.replacePort(GlobalConfig.portal, ":80", "");
     }
 
-    GlobalConfig.userId = ""+_param.userId || "0";
+    GlobalConfig.userId = "" + _param.userId || "0";
     //H5处理
     GlobalConfig.isH5 = _param.isH5 || false;//外部传入的参数,是否是H5
     if (GlobalConfig.isH5 == true) {
@@ -591,21 +601,21 @@ export default class MessageEntrance extends Emiter {
       GlobalConfig.userRole = ApeConsts.normal;
     }
     //如果没有名字或没有userId的时候,需要随机生成
-    let timestampStr=new Date().getTime().toString();
-    timestampStr=timestampStr.substr(timestampStr.length-4);
-    if(GlobalConfig.userRole==ApeConsts.host){
-      timestampStr="T"+timestampStr;
-    }else if(GlobalConfig.userRole==ApeConsts.assistant) {
+    let timestampStr = new Date().getTime().toString();
+    timestampStr = timestampStr.substr(timestampStr.length - 4);
+    if (GlobalConfig.userRole == ApeConsts.host) {
+      timestampStr = "T" + timestampStr;
+    } else if (GlobalConfig.userRole == ApeConsts.assistant) {
       timestampStr = "A" + timestampStr;
-    }else if(GlobalConfig.userRole==ApeConsts.presenter){
-      timestampStr="P"+timestampStr;
-    }else {
-      timestampStr="S"+timestampStr;
+    } else if (GlobalConfig.userRole == ApeConsts.presenter) {
+      timestampStr = "P" + timestampStr;
+    } else {
+      timestampStr = "S" + timestampStr;
     }
     //如果没有名字,随机起一个名字
-    GlobalConfig.userName = _param.userName ||timestampStr;
+    GlobalConfig.userName = _param.userName || timestampStr;
     //如果没有userId或者为"0",随机生成
-    if (!GlobalConfig.userId||GlobalConfig.userId == "0") {
+    if (!GlobalConfig.userId || GlobalConfig.userId == "0") {
       GlobalConfig.userId = timestampStr;
     }
 
@@ -646,10 +656,10 @@ export default class MessageEntrance extends Emiter {
     //loger.log("autoLoginMd5", GlobalConfig.classId, GlobalConfig.userId, GlobalConfig.userRole);
     let autoLoginMd5 = MD5("" + GlobalConfig.classId + GlobalConfig.userId + GlobalConfig.userRole);
     //loger.log("joinClass-GlobalConfig.autoLogin", GlobalConfig.autoLogin, "autoLoginMd5-", autoLoginMd5);
-    if (GlobalConfig.autoLogin && autoLoginMd5 == GlobalConfig.autoLogin||GlobalConfig.isInvisible) {
+    if (GlobalConfig.autoLogin && autoLoginMd5 == GlobalConfig.autoLogin || GlobalConfig.isInvisible) {
       // MD5(classId+userId+userRole)==m
       //自动登录,跳过验证流程
-      loger.log("自动登录->"+GlobalConfig.userRole);
+      loger.log("自动登录->" + GlobalConfig.userRole);
       this._sassJoinSuccessHandler();
     } else {
       //不能自动登录,开始校验
@@ -695,7 +705,7 @@ export default class MessageEntrance extends Emiter {
 
   // 通过SASS平台验证(密码和MD5)
   _sassJoinSuccessHandler(_data) {
-    //获取课堂最完整的数据
+    //SAAS获取课堂最完整的数据
     if (_sass) {
       _sass.getClassParam();
     }
@@ -896,15 +906,15 @@ export default class MessageEntrance extends Emiter {
     loger.warn(" HLS-List", GlobalConfig.hlsPullListFinal);
     loger.warn(" RS-List", GlobalConfig.rsPullListFinal);
 
-   /* //使用webRtc不需要再使用MS列表中的数据---------
-    GlobalConfig.msListFinal=[];//清空数据
-    GlobalConfig.rtmpPullListFinal=[];
-    //不是录制回放的时候hls的也清空
-    if(!GlobalConfig.isRecordPlayBack){
-      GlobalConfig.hlsPullListFinal=[];
-      GlobalConfig.rsPullListFinal=[]
-    }
-    //-------------------------------------------*/
+    /* //使用webRtc不需要再使用MS列表中的数据---------
+     GlobalConfig.msListFinal=[];//清空数据
+     GlobalConfig.rtmpPullListFinal=[];
+     //不是录制回放的时候hls的也清空
+     if(!GlobalConfig.isRecordPlayBack){
+     GlobalConfig.hlsPullListFinal=[];
+     GlobalConfig.rsPullListFinal=[]
+     }
+     //-------------------------------------------*/
 
   }
 
@@ -970,16 +980,16 @@ export default class MessageEntrance extends Emiter {
     loger.warn(" HLS-List", GlobalConfig.hlsPullListFinal);
     loger.warn(" RS-List", GlobalConfig.rsPullListFinal);
 
-  /*
-    //使用webRtc不需要再使用MS列表中的数据---------
-    GlobalConfig.msListFinal=[];//清空数据
-    GlobalConfig.rtmpPullListFinal=[];
-    //不是录制回放的时候hls的也清空
-    if(!GlobalConfig.isRecordPlayBack){
-      GlobalConfig.hlsPullListFinal=[];
-      GlobalConfig.rsPullListFinal=[]
-    }
-    //-------------------------------------------*/
+    /*
+     //使用webRtc不需要再使用MS列表中的数据---------
+     GlobalConfig.msListFinal=[];//清空数据
+     GlobalConfig.rtmpPullListFinal=[];
+     //不是录制回放的时候hls的也清空
+     if(!GlobalConfig.isRecordPlayBack){
+     GlobalConfig.hlsPullListFinal=[];
+     GlobalConfig.rsPullListFinal=[]
+     }
+     //-------------------------------------------*/
 
   }
 
@@ -1041,11 +1051,11 @@ export default class MessageEntrance extends Emiter {
 
   //保存课堂状态信息
   _sassSaveClassStatusInfo(_param) {
-    if (!_mcu||!_mcu.connected) {
+    if (!_mcu || !_mcu.connected) {
       loger.warn("不能保存课堂数据->MCU已经断开");
-      return ;
+      return;
     }
-    if(!_confer_ape){
+    if (!_confer_ape) {
       return;
     }
     //{isForce:true}   isForce->是否强制提交(true为是)
@@ -1054,14 +1064,14 @@ export default class MessageEntrance extends Emiter {
     if (_param && _param.isForce == true) {
       isForce = true;
     }
-    if (_confer_ape.checkHasRecordControl()||isForce) {
-       //POST 保存数据
-        clearTimeout(this.saveClassStatusTimer);
-        this.saveClassStatusTimer=setTimeout(()=>{
-          _sass.saveClassStatusInfo({"classStatusInfo": GlobalConfig.classStatusInfo}); //保存课堂状态信息
-        },1000);
+    if (_confer_ape.checkHasRecordControl() || isForce) {
+      //POST 保存数据
+      clearTimeout(this.saveClassStatusTimer);
+      this.saveClassStatusTimer = setTimeout(()=> {
+        _sass.saveClassStatusInfo({"classStatusInfo": GlobalConfig.classStatusInfo}); //保存课堂状态信息
+      }, 1000);
     } else {
-      loger.log("没有保存课堂状态信息的权限->当前身份->"+GlobalConfig.userRole);
+      loger.log("没有保存课堂状态信息的权限->当前身份->" + GlobalConfig.userRole);
     }
   }
 
@@ -1123,29 +1133,30 @@ export default class MessageEntrance extends Emiter {
     GlobalConfig.screenWidth = window.screen.width;
     GlobalConfig.screenHeight = window.screen.height;
 
-    GlobalConfig.channelId = ""+GlobalConfig.siteId+"_"+GlobalConfig.classId;
+    GlobalConfig.channelId = "" + GlobalConfig.siteId + "_" + GlobalConfig.classId;
     GlobalConfig.userUid = GlobalConfig.nodeId;
-    GlobalConfig.channelKey="";
-    GlobalConfig.rosters={};//情况人员数据列表
+    GlobalConfig.channelKey = "";
+    GlobalConfig.rosters = {};//情况人员数据列表
 
     //判断是否需要获取加入音视频通话频道的channelKey
-    if(GlobalConfig.appCertificate){
+    if (GlobalConfig.appCertificate) {
       loger.log("加入视频通话模块->需要先获取channelKey")
       //获取channelKey
-      _sass.getChannelKeyToken((_data)=>{
+      _sass.getChannelKeyToken((_data)=> {
         //{"code":200,"channelKey":"005AQAoAEQzQUQxNzFDOEQwOEU3OTVGMjlCMzZDRUZENTNGOTU0RDY4N0ZGMUEQANylukzO70ocgrNX9hlkNNWvpLBZ9buDAy/fuVkAAA==","uid":"751373669"}
-        if(_data&&_data.channelKey){
-          GlobalConfig.channelKey=_data.channelKey||"";
+        if (_data && _data.channelKey) {
+          GlobalConfig.channelKey = _data.channelKey || "";
         }
         this._joinClassSuccessSeting();
       })
-    }else {
+    } else {
       loger.log("加入视频通话模块->不需要获取channelKey")
       this._joinClassSuccessSeting();
     }
   }
+
   //加入课堂成功之后设置本地数据和返回数据给客户端
-  _joinClassSuccessSeting(){
+  _joinClassSuccessSeting() {
     //返回给客户端初始化成功的数据
     let joinClassSuccessCallBackData = {};
     joinClassSuccessCallBackData.isRecordPlayBack = GlobalConfig.isRecordPlayBack;
@@ -1205,8 +1216,8 @@ export default class MessageEntrance extends Emiter {
     joinClassSuccessCallBackData.explorerVersion = GlobalConfig.explorerVersion;
     joinClassSuccessCallBackData.os = GlobalConfig.os;
 
-    joinClassSuccessCallBackData.channelId =GlobalConfig.channelId ;
-    joinClassSuccessCallBackData.channelKey =GlobalConfig.channelKey ;
+    joinClassSuccessCallBackData.channelId = GlobalConfig.channelId;
+    joinClassSuccessCallBackData.channelKey = GlobalConfig.channelKey;
     joinClassSuccessCallBackData.userUid = GlobalConfig.userUid;
     joinClassSuccessCallBackData.appId = GlobalConfig.appId;
     joinClassSuccessCallBackData.appCertificate = GlobalConfig.appCertificate;
@@ -1220,7 +1231,7 @@ export default class MessageEntrance extends Emiter {
     LogManager.userRole = GlobalConfig.userRole;//userRole
     LogManager.userName = GlobalConfig.userName;//用户名称
     LogManager.logUrl = GlobalConfig.logUrl;//日志服务器地址 //http://log.3mang.com
-    LogManager.platform=GlobalConfig.platform;
+    LogManager.platform = GlobalConfig.platform;
     loger.log('加入课堂成功->');
     loger.log(joinClassSuccessCallBackData);
 
@@ -1228,25 +1239,30 @@ export default class MessageEntrance extends Emiter {
     this._emit(MessageTypes.CLASS_JOIN_SUCCESS, joinClassSuccessCallBackData);
 
     //主讲人和老师可以设置旁录
-    if(GlobalConfig.appId&&!GlobalConfig.openFlash){
+    if (GlobalConfig.appId && !GlobalConfig.openFlash) {
       //加入之前先设置旁录地址,只有直播支持旁路(1路流)
-      if(_webRtc&&GlobalConfig.isTeachOrAssistant&&GlobalConfig.maxMediaChannels==1){
+      if (_webRtc && GlobalConfig.isTeachOrAssistant && GlobalConfig.maxMediaChannels == 1) {
         let curTimestamp = new Date().getTime();
-        let streamId=GlobalConfig.siteId+"_"+GlobalConfig.classId+"_"+GlobalConfig.userId+"_"+curTimestamp;
+        let streamId = GlobalConfig.siteId + "_" + GlobalConfig.classId + "_" + GlobalConfig.userId + "_" + curTimestamp;
         //传入固定的流Id
-        let publishData=this._getVideoPublishPath({streamId:streamId});
-        loger.log("加入之前先设置旁录地址",publishData);
-        if(publishData&&publishData.code==0){
+        let publishData = this._getVideoPublishPath({streamId: streamId});
+        loger.log("加入之前先设置旁录地址", publishData);
+        if (publishData && publishData.code == 0) {
           _webRtc.setConfigPublisherUrl(publishData.publishUrl);
-          let m3u8Stream = _video_ape.getPlayVideoPath({"type": "m3u8", "streamId":streamId});
-          let rtmpStream = _video_ape.getPlayVideoPath({"type": "rtmp", "streamId":streamId});
-          _webRtc.setRtmpM3u8Path({m3u8Url:m3u8Stream.playUrl,rtmpUrl:rtmpStream.playUrl});
+          let m3u8Stream = _video_ape.getPlayVideoPath({"type": "m3u8", "streamId": streamId});
+          let rtmpStream = _video_ape.getPlayVideoPath({"type": "rtmp", "streamId": streamId});
+          _webRtc.setRtmpM3u8Path({m3u8Url: m3u8Stream.playUrl, rtmpUrl: rtmpStream.playUrl});
         }
       }
-      setTimeout(()=>{
+      setTimeout(()=> {
         //加入音视频通话模块,延迟一秒处理,因为视频需要根据用户列表信息来判断放的位置,太早的话用户列表没有数据
-        this._joinChannel({channelId:GlobalConfig.channelId,channelKey:GlobalConfig.channelKey ,uid:GlobalConfig.userUid,info:""+GlobalConfig.userRole});
-      },1600);
+        this._joinChannel({
+          channelId: GlobalConfig.channelId,
+          channelKey: GlobalConfig.channelKey,
+          uid: GlobalConfig.userUid,
+          info: "" + GlobalConfig.userRole
+        });
+      }, 1600);
     }
   }
 
@@ -1269,21 +1285,21 @@ export default class MessageEntrance extends Emiter {
       } else {
         if (GlobalConfig.mcuListFinal && GlobalConfig.mcuListFinal.length > 0) {
           //如果当前没有设置过mcu的ip和端口随机选择一个
-          if(!GlobalConfig.MCUServerIP||GlobalConfig.mcuListFinal.length==1){
+          if (!GlobalConfig.MCUServerIP || GlobalConfig.mcuListFinal.length == 1) {
             let index = parseInt(Math.random() * GlobalConfig.mcuListFinal.length);
             GlobalConfig.MCUServerIP = GlobalConfig.mcuListFinal[index].ip || "";
             GlobalConfig.MCUServerPort = GlobalConfig.mcuListFinal[index].port || "";
-          }else {
+          } else {
             //当前mcu已经有值,需要选择一个新的
-            for(let i=0;i<GlobalConfig.mcuListFinal.length;i++){
-              if(GlobalConfig.MCUServerIP == GlobalConfig.mcuListFinal[i].ip){
+            for (let i = 0; i < GlobalConfig.mcuListFinal.length; i++) {
+              if (GlobalConfig.MCUServerIP == GlobalConfig.mcuListFinal[i].ip) {
                 //获取下一个MCU
-                let nextMcu= GlobalConfig.mcuListFinal[i+1];
-                if(!nextMcu){
+                let nextMcu = GlobalConfig.mcuListFinal[i + 1];
+                if (!nextMcu) {
                   //如果下一个mcu不存在就使用第一个
-                  nextMcu=GlobalConfig.mcuListFinal[0];
+                  nextMcu = GlobalConfig.mcuListFinal[0];
                 }
-                if(nextMcu){
+                if (nextMcu) {
                   GlobalConfig.MCUServerIP = nextMcu.ip || "";
                   GlobalConfig.MCUServerPort = nextMcu.port || "";
                 }
@@ -1315,20 +1331,21 @@ export default class MessageEntrance extends Emiter {
     loger.log('课堂状态发生改变,需要停止当前的所有推流');
     this._emit(MessageTypes.MEDIA_STOP_PUBLISH);
   }
+
   //用更状态数据发送变更
-  _onRosterUpdateHandler(_data){
+  _onRosterUpdateHandler(_data) {
     //数据无效/ios/android  不处理数据
-    if(!_data||GlobalConfig.deviceType==1||GlobalConfig.deviceType==2){
+    if (!_data || GlobalConfig.deviceType == 1 || GlobalConfig.deviceType == 2) {
       return;
     }
-    let nodeData=_data.nodeData;
+    let nodeData = _data.nodeData;
     //数据用户是pc或H5不处理
-    if(!nodeData||nodeData.deviceType==0||nodeData.deviceType==3){
+    if (!nodeData || nodeData.deviceType == 0 || nodeData.deviceType == 3) {
       return;
     }
-    if(nodeData.openCamera>0&&_webRtc){
-        loger.log("收到移动端用户数据更新,当前是开启摄像头状态,需要尝试添加一个远程视频");
-        _webRtc.tryAddMobileStream(_data.nodeId);
+    if (nodeData.openCamera > 0 && _webRtc) {
+      loger.log("收到移动端用户数据更新,当前是开启摄像头状态,需要尝试添加一个远程视频");
+      _webRtc.tryAddMobileStream(_data.nodeId);
     }
   }
 
@@ -1344,7 +1361,7 @@ export default class MessageEntrance extends Emiter {
       GlobalConfig.MS_PUBLISH_PORT = _param.port || "";
     }
     GlobalConfig.MS_PLAY_RTMP_IP = GlobalConfig.MS_PUBLISH_IP;
-    GlobalConfig.MS_PLAY_RTMP_PORT =GlobalConfig.MS_PUBLISH_PORT;
+    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地址,需要更新用户数据
@@ -1353,10 +1370,10 @@ export default class MessageEntrance extends Emiter {
     }
 
     //音视频模块对当前正在播放的流进行更换MS
-    if(_video_ape){
+    if (_video_ape) {
       _video_ape.changeMediaMs();
     }
-    if(_audio_ape){
+    if (_audio_ape) {
       _audio_ape.changeMediaMs();
     }
   }
@@ -1523,14 +1540,14 @@ export default class MessageEntrance extends Emiter {
       loger.warn(GlobalConfig.getCurrentStatus());
       return;
     }
-    if(GlobalConfig.isRecordPlayBack){
+    if (GlobalConfig.isRecordPlayBack) {
       return;
     }
     if (_confer_ape) {
       //开始上课
       _confer_ape.startClass(_param);
-     ////开始录制
-     //_confer_ape.startRecord();
+      ////开始录制
+      //_confer_ape.startRecord();
     }
   }
 
@@ -1548,7 +1565,7 @@ export default class MessageEntrance extends Emiter {
   }
 
   //控制课堂全局是否可绘制的状态
-  _changeDrawStatus(_param){
+  _changeDrawStatus(_param) {
     if (!_mcu.connected) {
       loger.warn(GlobalConfig.getCurrentStatus());
       return;
@@ -1558,6 +1575,7 @@ export default class MessageEntrance extends Emiter {
       _confer_ape.changeDrawStatus(_param);
     }
   }
+
   //暂停上课
   _sendPauseClass(_param) {
     if (!_mcu.connected) {
@@ -1648,10 +1666,11 @@ export default class MessageEntrance extends Emiter {
       _confer_ape.changeHandUpStatus(_param);
     }
   }
+
   /*
-  * 控制画笔使用状态
-  * */
-  _controlDrawStatus(_param){
+   * 控制画笔使用状态
+   * */
+  _controlDrawStatus(_param) {
     //{nodeId:333333,isDisEnableDraw:true}
     if (!_mcu.connected) {
       loger.warn(GlobalConfig.getCurrentStatus());
@@ -1661,9 +1680,10 @@ export default class MessageEntrance extends Emiter {
       _confer_ape.controlDrawStatus(_param);
     }
   }
+
   //停止上课
   _sendCloseClass(_param) {
-    if (!_mcu||!_mcu.connected) {
+    if (!_mcu || !_mcu.connected) {
       loger.warn(GlobalConfig.getCurrentStatus());
       return {"code": ApeConsts.RETURN_FAILED, "data": ""};
     }
@@ -1680,11 +1700,11 @@ export default class MessageEntrance extends Emiter {
     }
 
     //离开视频通话频道
-   /* if(GlobalConfig.deviceType==0||GlobalConfig.deviceType==3){
-      if(_webRtc){
-        _webRtc.leaveChannel();
-      }
-    }*/
+    /* if(GlobalConfig.deviceType==0||GlobalConfig.deviceType==3){
+     if(_webRtc){
+     _webRtc.leaveChannel();
+     }
+     }*/
 
     //停止推流
     if (_video_ape) {
@@ -1721,28 +1741,28 @@ export default class MessageEntrance extends Emiter {
 
   //获取课堂所有参数(20170727新规则) api/meeting/detail.do? flash中的接口文件是 getClassParam.do
   _sassGetClassParamSuccessHandler(_data) {
-    loger.log('获取课堂课堂信息完成.',_data.appConfig);
+    loger.log('获取课堂课堂信息完成.', _data.appConfig);
     //包含整个课堂最全的信息,储存数据
     if (_data) {
       //老师\助教默认启用画笔功能,其他身份默认禁用画笔功能
-      if(GlobalConfig.userRole==ApeConsts.host||
-        GlobalConfig.userRole==ApeConsts.assistant||
-        GlobalConfig.userRole==ApeConsts.presenter){
-        GlobalConfig.selfDisEnableDrawTime=0;
+      if (GlobalConfig.userRole == ApeConsts.host ||
+        GlobalConfig.userRole == ApeConsts.assistant ||
+        GlobalConfig.userRole == ApeConsts.presenter) {
+        GlobalConfig.selfDisEnableDrawTime = 0;
       }
 
       GlobalConfig.mcuDelay = _data.h5Delay || 0; //mcu消息延迟的时间间隔,单位(秒),结合客户端传的messageDelay的值使用
       GlobalConfig.className = _data.meetingName || "";
       GlobalConfig.classBeginTime = _data.beginTime || "";
       GlobalConfig.classEndTime = _data.endTime || "";
-      GlobalConfig.siteId=_data.siteID||"";//这个字段ID是大写的
-      GlobalConfig.channelId = ""+GlobalConfig.siteId+"_"+GlobalConfig.classId;
+      GlobalConfig.siteId = _data.siteID || "";//这个字段ID是大写的
+      GlobalConfig.channelId = "" + GlobalConfig.siteId + "_" + GlobalConfig.classId;
       //sdk获取ip失败就使用saas返回的
-      if(!GlobalConfig.userIp){
+      if (!GlobalConfig.userIp) {
         GlobalConfig.userIp = _data.userIp || "";
-        loger.warn("使用从Sass返回的userIp",GlobalConfig.userIp);
-      }else {
-        loger.warn("使用SDK获取的userIp",GlobalConfig.userIp);
+        loger.warn("使用从Sass返回的userIp", GlobalConfig.userIp);
+      } else {
+        loger.warn("使用SDK获取的userIp", GlobalConfig.userIp);
       }
 
 
@@ -1762,9 +1782,9 @@ export default class MessageEntrance extends Emiter {
       //是否自动开始(身份是host的时候才用到的)
       GlobalConfig.isAutoStartClass = _data.autoRecord || 0;
       GlobalConfig.logUrl = _data.logUrl || "";
-      GlobalConfig.logUrl=GlobalConfig.logUrl.replace("https://","");
-      GlobalConfig.logUrl=GlobalConfig.logUrl.replace("http://","");
-      GlobalConfig.logUrl=GlobalConfig.locationProtocol+GlobalConfig.logUrl;
+      GlobalConfig.logUrl = GlobalConfig.logUrl.replace("https://", "");
+      GlobalConfig.logUrl = GlobalConfig.logUrl.replace("http://", "");
+      GlobalConfig.logUrl = GlobalConfig.locationProtocol + GlobalConfig.logUrl;
 
       GlobalConfig.serverTime = _data.serverTime || new Date().getTime(); //获取服务器时间戳
       GlobalConfig.serverAndLoacTimeDistanc = (new Date().getTime() - GlobalConfig.serverTime) / 1000; //当前系统时间和服务器时间的差值 (秒)
@@ -1779,29 +1799,29 @@ export default class MessageEntrance extends Emiter {
       GlobalConfig.setMediaShareList(_data.sharedMediaList); //提前上传的媒体共享文件列表
 
       //设置白板文档,固定ID
-      let whiteBoradData={
-        itemIdx:GlobalConfig.whiteboardId,//指定的白板文档ID
+      let whiteBoradData = {
+        itemIdx: GlobalConfig.whiteboardId,//指定的白板文档ID
         name: "白板.pdf",
         creatUserId: 0,
-        md5:"b153313f6f390328a30db5389b6cee53",
+        md5: "b153313f6f390328a30db5389b6cee53",
         pageNum: 30,
         docId: "b153313f6f390328a30db5389b6cee53",
-        url:"http://pclive.xuedianyun.com/DocSharing/data/whiteboard/default/whiteboard.pdf",
+        url: "http://pclive.xuedianyun.com/DocSharing/data/whiteboard/default/whiteboard.pdf",
         dynamicTransferStatic: "0",
         relativeUrl: "/DocSharing/data/whiteboard/default/whiteboard.pdf",
         fileType: "pdf"
       }
       GlobalConfig.docListPrepare.push(whiteBoradData);
 
-      let appConfigStr=_data.appConfig;
-      appConfigStr=_base64.decode(appConfigStr);
-      let appConfig={};
-      try{
-        appConfig=JSON.parse(appConfigStr);
+      let appConfigStr = _data.appConfig;
+      appConfigStr = _base64.decode(appConfigStr);
+      let appConfig = {};
+      try {
+        appConfig = JSON.parse(appConfigStr);
         //储存app相关信息
         this._setAppConfig(appConfig);
-      }catch (err){
-        loger.warn("appConfig->解析失败",appConfigStr);
+      } catch (err) {
+        loger.warn("appConfig->解析失败", appConfigStr);
       }
 
 
@@ -1812,9 +1832,9 @@ export default class MessageEntrance extends Emiter {
         GlobalConfig.DOCServerIP = GlobalConfig.docList[index].ip || "";
         GlobalConfig.DOCServerPort = GlobalConfig.docList[index].port || "";
 
-        if(GlobalConfig.isHttps){
+        if (GlobalConfig.isHttps) {
           //https的时候替换所有80端口
-          GlobalConfig.DOCServerPort= GlobalConfig.replacePort(GlobalConfig.DOCServerPort,"80","");
+          GlobalConfig.DOCServerPort = GlobalConfig.replacePort(GlobalConfig.DOCServerPort, "80", "");
         }
 
       }
@@ -1824,9 +1844,9 @@ export default class MessageEntrance extends Emiter {
         GlobalConfig.RecordServerIP = GlobalConfig.recordList[index].ip || "";
         GlobalConfig.RecordServerPort = GlobalConfig.recordList[index].port || "";
 
-        if(GlobalConfig.isHttps){
+        if (GlobalConfig.isHttps) {
           //https的时候替换所有80端口
-          GlobalConfig.RecordServerPort= GlobalConfig.replacePort(GlobalConfig.RecordServerPort,"80","");
+          GlobalConfig.RecordServerPort = GlobalConfig.replacePort(GlobalConfig.RecordServerPort, "80", "");
         }
       }
       loger.warn('默认->文档服务器地址->.', GlobalConfig.DOCServerIP, GlobalConfig.DOCServerPort);
@@ -1842,26 +1862,107 @@ export default class MessageEntrance extends Emiter {
       }
     }
 
-    //课堂获取Sass数据完成
-    this._emit(MessageTypes.CLASS_GET_INFO_SUCCESS, GlobalConfig.getClassInfo());
-
     //存储Sass数据到本地
     if (_data.currentInfo) {
       //根据从Sass获取的数据信息,同步最后一次保存的课堂状态信息
-      loger.log("从Saas返回的课堂状态信息数据",_data.currentInfo);
+      loger.log("从Saas返回的课堂状态信息数据", _data.currentInfo);
       try {
-        let dataObj=JSON.parse(_data.currentInfo);
-        dataObj.recordStatus=false;
+        let dataObj = JSON.parse(_data.currentInfo);
+        dataObj.recordStatus = false;
         GlobalConfig.setClassStatusInfo(dataObj);
       } catch (err) {
         loger.warn("从Sass获取的课堂数据JSON转换失败->");
         GlobalConfig.setClassStatusInfo(_data.currentInfo);
       }
       loger.log(GlobalConfig.classStatusInfo);
+      //课堂获取Sass数据完成
+      this._emit(MessageTypes.CLASS_GET_INFO_SUCCESS, GlobalConfig.getClassInfo());
+
+      //课堂数据获取完成->进入课堂或进入录制回放
+      //录制回放不需要获取ip信息和选点
+      if (GlobalConfig.isRecordPlayBack) {
+        if (_recordPlayback) {
+          //开启录制回放流程
+          loger.warn("开启录制回放流程");
+          //根据用户的userIp信息从sever.json和Sass中选择最终mcu和推流拉流数据列表
+          ServerConfig.serverList = ServerConfig.sassServerJson;
+          this._choiceMcuAndMsListFromSass();
+          //获取MCU和MS 推流拉流、录制回放的默认地址
+          this.getMcuAndMsDefaultServerIp();
+          _recordPlayback.readyLoadRecordPlayData();
+        } else {
+          loger.warn("开启录制回放流程失败->还未创建模块");
+        }
+      } else {
+        //初始化音视频通话sdk
+        if (GlobalConfig.appId && !GlobalConfig.openFlash) {
+          loger.log("使用webRtc通话模式");
+          //加入webRtc
+          this._initWebRtcSdk({
+            appId: GlobalConfig.appId
+          }, ()=> {
+            //音视频通话SDK初始化完成之后,根据用户的userIp获取信息,获取服务列表选点,选点测速完成后才加入MCU
+            this.loadServerJsonAndgetUserIpInfo();
+          });
+        } else {
+          //加入flash
+          loger.log("使用flash通话模式");
+          this.loadServerJsonAndgetUserIpInfo();
+        }
+      }
     } else {
-      loger.log("还没有保存过课堂状信息");
+      loger.warn("从Sass获取的课堂数据currentInfo无效->再次获取");
+      //如果获取的数据中没有课堂保存的状态数据,需要单独的接口获取一次
+      _sass.getClassRecordInfo((_currentInfo)=> {
+        if(_currentInfo){
+          try {
+            let dataObj = JSON.parse(_currentInfo);
+            dataObj.recordStatus = false;
+            GlobalConfig.setClassStatusInfo(dataObj);
+          } catch (err) {
+            loger.warn("getClassRecordInfo获取的课堂数据JSON转换失败->");
+          }
+        }
+        //课堂获取Sass数据完成
+        this._emit(MessageTypes.CLASS_GET_INFO_SUCCESS, GlobalConfig.getClassInfo());
+        //课堂数据获取完成->进入课堂或进入录制回放
+        //录制回放不需要获取ip信息和选点
+        if (GlobalConfig.isRecordPlayBack) {
+          if (_recordPlayback) {
+            //开启录制回放流程
+            loger.warn("开启录制回放流程");
+            //根据用户的userIp信息从sever.json和Sass中选择最终mcu和推流拉流数据列表
+            ServerConfig.serverList = ServerConfig.sassServerJson;
+            this._choiceMcuAndMsListFromSass();
+            //获取MCU和MS 推流拉流、录制回放的默认地址
+            this.getMcuAndMsDefaultServerIp();
+            _recordPlayback.readyLoadRecordPlayData();
+          } else {
+            loger.warn("开启录制回放流程失败->还未创建模块");
+          }
+        } else {
+          //初始化音视频通话sdk
+          if (GlobalConfig.appId && !GlobalConfig.openFlash) {
+            loger.log("使用webRtc通话模式");
+            //加入webRtc
+            this._initWebRtcSdk({
+              appId: GlobalConfig.appId
+            }, ()=> {
+              //音视频通话SDK初始化完成之后,根据用户的userIp获取信息,获取服务列表选点,选点测速完成后才加入MCU
+              this.loadServerJsonAndgetUserIpInfo();
+            });
+          } else {
+            //加入flash
+            loger.log("使用flash通话模式");
+            this.loadServerJsonAndgetUserIpInfo();
+          }
+        }
+      })
     }
 
+ /*   //课堂获取Sass数据完成
+    this._emit(MessageTypes.CLASS_GET_INFO_SUCCESS, GlobalConfig.getClassInfo());
+
     //课堂数据获取完成->进入课堂或进入录制回放
     //录制回放不需要获取ip信息和选点
     if (GlobalConfig.isRecordPlayBack) {
@@ -1879,26 +1980,21 @@ export default class MessageEntrance extends Emiter {
       }
     } else {
       //初始化音视频通话sdk
-      if(GlobalConfig.appId&&!GlobalConfig.openFlash){
+      if (GlobalConfig.appId && !GlobalConfig.openFlash) {
         loger.log("使用webRtc通话模式");
         //加入webRtc
         this._initWebRtcSdk({
-          appId:GlobalConfig.appId
-        },()=>{
+          appId: GlobalConfig.appId
+        }, ()=> {
           //音视频通话SDK初始化完成之后,根据用户的userIp获取信息,获取服务列表选点,选点测速完成后才加入MCU
           this.loadServerJsonAndgetUserIpInfo();
         });
-      }else {
+      } else {
         //加入flash
         loger.log("使用flash通话模式");
         this.loadServerJsonAndgetUserIpInfo();
       }
-
-     /*
-      //根据用户的userIp获取信息,获取服务列表选点,选点测速完成后才加入MCU
-      this.loadServerJsonAndgetUserIpInfo();
-      */
-    }
+    }*/
   }
 
   //获取各个服务的默认ip,之后会进行测速选择更快的ip
@@ -1907,8 +2003,8 @@ export default class MessageEntrance extends Emiter {
     if (GlobalConfig.mcuListFinal && GlobalConfig.mcuListFinal.length > 0) {
       //还未开始选点之前随机选择一个
       let index = parseInt(Math.random() * GlobalConfig.mcuListFinal.length);
-      if(!GlobalConfig.MCUServerIP){
-        index=0;
+      if (!GlobalConfig.MCUServerIP) {
+        index = 0;
       }
       GlobalConfig.MCUServerIP = GlobalConfig.mcuListFinal[index].ip || "";
       GlobalConfig.MCUServerPort = GlobalConfig.mcuListFinal[index].port || "";
@@ -1918,8 +2014,8 @@ export default class MessageEntrance extends Emiter {
     if (GlobalConfig.rsPullListFinal && GlobalConfig.rsPullListFinal.length > 0) {
       //还未开始选点之前随机选择一个
       let index = parseInt(Math.random() * GlobalConfig.rsPullListFinal.length);
-      if(!GlobalConfig.RS_RECORD_PLAY_IP){
-        index=0;
+      if (!GlobalConfig.RS_RECORD_PLAY_IP) {
+        index = 0;
       }
       GlobalConfig.RS_RECORD_PLAY_IP = GlobalConfig.rsPullListFinal[index].ip || "";
       GlobalConfig.RS_RECORD_PLAY_PORT = GlobalConfig.rsPullListFinal[index].port || "";
@@ -1929,8 +2025,8 @@ export default class MessageEntrance extends Emiter {
     if (GlobalConfig.msListFinal && GlobalConfig.msListFinal.length > 0) {
       //还未开始选点之前随机选择一个
       let index = parseInt(Math.random() * GlobalConfig.msListFinal.length);
-      if(!GlobalConfig.MS_PUBLISH_IP){
-        index=0;
+      if (!GlobalConfig.MS_PUBLISH_IP) {
+        index = 0;
       }
       GlobalConfig.MS_PUBLISH_IP = GlobalConfig.msListFinal[index].ip || "";
       GlobalConfig.MS_PUBLISH_PORT = GlobalConfig.msListFinal[index].port || "";
@@ -1940,8 +2036,8 @@ export default class MessageEntrance extends Emiter {
     if (GlobalConfig.rtmpPullListFinal && GlobalConfig.rtmpPullListFinal.length > 0) {
       // //还未开始选点之前随机选择一个
       let index = parseInt(Math.random() * GlobalConfig.rtmpPullListFinal.length);
-      if(!GlobalConfig.MS_PLAY_RTMP_IP){
-        index=0;
+      if (!GlobalConfig.MS_PLAY_RTMP_IP) {
+        index = 0;
       }
       GlobalConfig.MS_PLAY_RTMP_IP = GlobalConfig.rtmpPullListFinal[index].ip || "";
       GlobalConfig.MS_PLAY_RTMP_PORT = GlobalConfig.rtmpPullListFinal[index].port || "";
@@ -1955,8 +2051,8 @@ export default class MessageEntrance extends Emiter {
     if (GlobalConfig.hlsPullListFinal && GlobalConfig.hlsPullListFinal.length > 0) {
       //有单独的hls拉流地址
       let index = parseInt(Math.random() * GlobalConfig.hlsPullListFinal.length);
-      if(!GlobalConfig.MS_PLAY_HLS_IP){
-        index=0;
+      if (!GlobalConfig.MS_PLAY_HLS_IP) {
+        index = 0;
       }
       GlobalConfig.MS_PLAY_HLS_IP = GlobalConfig.hlsPullListFinal[index].ip || "";
       GlobalConfig.MS_PLAY_HLS_PORT = GlobalConfig.hlsPullListFinal[index].port || "";
@@ -2007,25 +2103,27 @@ export default class MessageEntrance extends Emiter {
       _confer_ape.updaterUserDeviecStatusChange(_data);
     }
   }
+
   //webRtc推流状态发生改变
-  mediaPublishStatusChange(_data){
+  mediaPublishStatusChange(_data) {
     if (!_mcu.connected) {
       loger.warn(GlobalConfig.getCurrentStatus());
       return {"code": ApeConsts.RETURN_FAILED, "data": ""};
     }
     if (_video_ape) {
       //if(_data.status==WebRtcApe.RECORD_STATUS_1&&!_data.publishUrl){
-      loger.log("webRtc推流状态发生改变->发送同步消息",_data);
-     if(_data.status==WebRtcApe.RECORD_STATUS_1){
-        let publishData=this._getVideoPublishPath();
-        let publishUrl="";
-        if(publishData&&publishData.code==0){
-          _data.publishUrl=publishData.publishUrl||"";
+      loger.log("webRtc推流状态发生改变->发送同步消息", _data);
+      if (_data.status == WebRtcApe.RECORD_STATUS_1) {
+        let publishData = this._getVideoPublishPath();
+        let publishUrl = "";
+        if (publishData && publishData.code == 0) {
+          _data.publishUrl = publishData.publishUrl || "";
         }
       }
       _video_ape.mediaPublishStatusChange(_data);
     }
   }
+
   //屏幕共享
   //开始屏幕共享
   _publishScreenShare(_param) {
@@ -2041,6 +2139,54 @@ export default class MessageEntrance extends Emiter {
     }
   }
 
+  //添加外部流
+  _addMediaExternalLink(_params){
+    if(!_params||!_params.rtmpUrl){
+      return;
+    }
+        let fileInfo = {};
+        fileInfo.pageNum =1;// 文档的总页数
+        fileInfo.fileName = _params.fileName||"视频"+EngineUtils.creatTimestamp()+".video";//文档名字
+        fileInfo.fileType ="video";
+        fileInfo.relativeUrl ="";   //文档相对地址
+        fileInfo.url = _params.rtmpUrl||"unkown";   //文档绝对地址  默认值: null
+        fileInfo.docId =""+EngineUtils.creatTimestamp();    //文档在数据库中的唯一id标识 默认值: null
+        fileInfo.visible = false;    // 是否显示  默认值: false
+        fileInfo.publishUrl = _params.rtmpUrl||"";
+        fileInfo.rtmpUrl = _params.rtmpUrl||"";
+        fileInfo.m3u8Url = _params.m3u8Url||"";
+        fileInfo.replay = _params.replay||"";
+        loger.log("添加外部流", fileInfo);
+        this._sendDocumentUpload(fileInfo);
+  }
+  //删除外部流
+  _deleteMediaExternalLink(_param){
+    this._sendDocumentDelete(_param);
+  }
+  //推送外部流地址
+  _publishExternalLink(_param) {
+    if (!_mcu.connected) {
+      loger.warn("推送外部流地址失败,mcu连接已经断开");
+      return {"code": ApeConsts.RETURN_FAILED, "data": ""};
+    }
+    loger.log("推送外部流地址", _param);
+    if (_video_ape) {
+      _video_ape.publishExternalLink(_param);
+    }
+  }
+
+  //停止推送外部流地址
+  _stopPublishExternalLink(_param) {
+    if (!_mcu.connected) {
+      loger.warn("停止推送外部流地址失败,mcu连接已经断开");
+      return {"code": ApeConsts.RETURN_FAILED, "data": ""};
+    }
+    loger.log("停止推送外部流地址", _param);
+    if (_video_ape) {
+      _video_ape.stopPublishExternalLink(_param);
+    }
+  }
+
   //VidoeApe
   videoUpdate(_data) {
     //视频同步的消息发送改变,需要通知ferApe模块中的用户更新状态
@@ -2257,22 +2403,24 @@ export default class MessageEntrance extends Emiter {
       _doc_ape.documentSwitchDoc(_param);
     }
   }
+
   //切换到白板文档
-  _switchToWhiteboard(_param){
+  _switchToWhiteboard(_param) {
     if (!_mcu.connected) {
       loger.warn("连接已经断开->不能切换到白板文档");
       return;
     }
     //白板文档的数据
-    let data={
-        itemIdx:GlobalConfig.whiteboardId,
-        visible:true
+    let data = {
+      itemIdx: GlobalConfig.whiteboardId,
+      visible: true
     }
     if (_doc_ape) {
       loger.log("切换到白板文档");
       _doc_ape.documentSwitchDoc(data);
     }
   }
+
   //操作文档(翻页)
   _sendDocumentSwitchPage(_param) {
     if (!_mcu.connected) {
@@ -2349,11 +2497,12 @@ export default class MessageEntrance extends Emiter {
   }
 
   //隐藏当前显示的文档
-  _hideCurrentDocument(_params){
+  _hideCurrentDocument(_params) {
     if (_doc_ape) {
       _doc_ape.hideCurrentDocument(_params);
     }
   }
+
   //文档加入频道成功,同步到MCU服务器上的数据
   docJoinChannelSuccess() {
     loger.log("文档加入频道成功->isHost=", GlobalConfig.isHost, "当前总人数:", GlobalConfig.rosterNumber, "sassDoclength=", GlobalConfig.docListPrepare.length);
@@ -2366,16 +2515,16 @@ export default class MessageEntrance extends Emiter {
           if (value) {
             //loger.log("判断是否需要把提前上传的文档上传到mcu", value);
             let paramInfo = {
-              "pageNum": value.pdfSize||value.pageNum,
+              "pageNum": value.pdfSize || value.pageNum,
               "fileName": value.name,
               "fileType": value.type,
-              "relativeUrl": value.relativeLocation||value.relativeUrl,
-              "url": value.absoluteLocation||value.url,
-              "creatUserId": value.createUserID||0,
-              "docId": value.id||value.docId,
-              "md5": value.MD5||"",
+              "relativeUrl": value.relativeLocation || value.relativeUrl,
+              "url": value.absoluteLocation || value.url,
+              "creatUserId": value.createUserID || 0,
+              "docId": value.id || value.docId,
+              "md5": value.MD5 || "",
               "visible": false,
-              "itemIdx":value.itemIdx||0
+              "itemIdx": value.itemIdx || 0
             };
             this._sendDocumentUpload(paramInfo);
           }
@@ -2621,10 +2770,10 @@ export default class MessageEntrance extends Emiter {
     //保存参数
     GlobalConfig.isRecordPlayBack = true; //设置为录制回放状态
     GlobalConfig.classId = parseInt(_param.classId);
-    GlobalConfig.portal = _param.portal||"";
-    if(GlobalConfig.isHttps==true){
+    GlobalConfig.portal = _param.portal || "";
+    if (GlobalConfig.isHttps == true) {
       //https的时候替换所有80端口
-      GlobalConfig.portal= GlobalConfig.replacePort(GlobalConfig.portal,":80","");
+      GlobalConfig.portal = GlobalConfig.replacePort(GlobalConfig.portal, ":80", "");
     }
 
     GlobalConfig.userRole = ApeConsts.normal; //*************很重要,录制回放的时候,身份模式是普通人********
@@ -2688,7 +2837,7 @@ export default class MessageEntrance extends Emiter {
   //录制回放加入 课堂成功
   _joinRecordPlaybackSuccessHandler(_data) {
     loger.log('加入录制回放成功.');
-    LogManager.IS_OPEN_SEND_LOG=false;//录制回放不需要上报日志
+    LogManager.IS_OPEN_SEND_LOG = false;//录制回放不需要上报日志
     GlobalConfig.setCurrentStatus(GlobalConfig.statusCode_2);
 
     //返回给客户端初始化成功的数据
@@ -2811,104 +2960,112 @@ export default class MessageEntrance extends Emiter {
 
   //WEB RTC-------------------------------------------------------------------------------------------
   /*
-  * 初始化webRtc
-  * */
-  _initWebRtcSdk(_params,_callback){
-    if(GlobalConfig.deviceType==1||GlobalConfig.deviceType==2){
+   * 初始化webRtc
+   * */
+  _initWebRtcSdk(_params, _callback) {
+    if (GlobalConfig.deviceType == 1 || GlobalConfig.deviceType == 2) {
       loger.warn("移动端不需要处理初始化webRtc");
-      if(_callback){
+      if (_callback) {
         _callback();
       }
-      return ;
+      return;
     }
-    if(_webRtc){
-      _webRtc.initApp(_params,(_callbackData)=>{
+    if (_webRtc) {
+      _webRtc.initApp(_params, (_callbackData)=> {
         //_callback({isSuccess:false,error:err});
-        if(_callbackData&&_callbackData.isSuccess==true){
-          this._emit(MessageTypes.WEB_RTC_INIT_SUCCESS,_callbackData);
-        }else {
-          this._emit(MessageTypes.WEB_RTC_INIT_FAILED,_callbackData);
+        if (_callbackData && _callbackData.isSuccess == true) {
+          this._emit(MessageTypes.WEB_RTC_INIT_SUCCESS, _callbackData);
+        } else {
+          this._emit(MessageTypes.WEB_RTC_INIT_FAILED, _callbackData);
         }
-        if(_callback){
+        if (_callback) {
           _callback();
         }
       });
     }
   }
+
   /*
-  * 重新加入频道
-  * */
-  _reJoinChannel(_params){
-    if(GlobalConfig.appId&&!GlobalConfig.openFlash){
-        //先离开频道
-        this._leaveChannel();
+   * 重新加入频道
+   * */
+  _reJoinChannel(_params) {
+    if (GlobalConfig.appId && !GlobalConfig.openFlash) {
+      //先离开频道
+      this._leaveChannel();
       //主讲人和老师可以设置旁录
 
       //加入之前先设置旁录地址,只有直播支持旁路(1路流)
-      if(_webRtc&&GlobalConfig.isTeachOrAssistant&&GlobalConfig.maxMediaChannels==1){
+      if (_webRtc && GlobalConfig.isTeachOrAssistant && GlobalConfig.maxMediaChannels == 1) {
         let curTimestamp = new Date().getTime();
-        let streamId=GlobalConfig.siteId+"_"+GlobalConfig.classId+"_"+GlobalConfig.userId+"_"+curTimestamp;
+        let streamId = GlobalConfig.siteId + "_" + GlobalConfig.classId + "_" + GlobalConfig.userId + "_" + curTimestamp;
         //传入固定的流Id
-        let publishData=this._getVideoPublishPath({streamId:streamId});
-        loger.log("加入之前先设置旁录地址",publishData);
-        if(publishData&&publishData.code==0){
+        let publishData = this._getVideoPublishPath({streamId: streamId});
+        loger.log("加入之前先设置旁录地址", publishData);
+        if (publishData && publishData.code == 0) {
           _webRtc.setConfigPublisherUrl(publishData.publishUrl);
-          let m3u8Stream = _video_ape.getPlayVideoPath({"type": "m3u8", "streamId":streamId});
-          let rtmpStream = _video_ape.getPlayVideoPath({"type": "rtmp", "streamId":streamId});
-          _webRtc.setRtmpM3u8Path({m3u8Url:m3u8Stream.playUrl,rtmpUrl:rtmpStream.playUrl});
+          let m3u8Stream = _video_ape.getPlayVideoPath({"type": "m3u8", "streamId": streamId});
+          let rtmpStream = _video_ape.getPlayVideoPath({"type": "rtmp", "streamId": streamId});
+          _webRtc.setRtmpM3u8Path({m3u8Url: m3u8Stream.playUrl, rtmpUrl: rtmpStream.playUrl});
         }
       }
       clearTimeout(this.joinChannelTimer);
-      this.joinChannelTimer=setTimeout(()=>{
+      this.joinChannelTimer = setTimeout(()=> {
         //加入音视频通话模块,延迟一秒处理,因为视频需要根据用户列表信息来判断放的位置,太早的话用户列表没有数据
-        this._joinChannel({channelId:GlobalConfig.channelId,channelKey:GlobalConfig.channelKey ,uid:GlobalConfig.userUid,info:""+GlobalConfig.userRole});
-      },1600);
+        this._joinChannel({
+          channelId: GlobalConfig.channelId,
+          channelKey: GlobalConfig.channelKey,
+          uid: GlobalConfig.userUid,
+          info: "" + GlobalConfig.userRole
+        });
+      }, 1600);
     }
   }
+
   /*
-  * 加入视频通话
-  * */
-  _joinChannel(_params){
-    if(GlobalConfig.deviceType==1||GlobalConfig.deviceType==2){
+   * 加入视频通话
+   * */
+  _joinChannel(_params) {
+    if (GlobalConfig.deviceType == 1 || GlobalConfig.deviceType == 2) {
       loger.warn("移动端不需要处理加入视频房间");
-      return ;
+      return;
     }
-    if(_webRtc){
+    if (_webRtc) {
       _webRtc.joinChannel(_params);
     }
   }
+
   /*
-  * 离开视频通话频道
-  * */
-  _leaveChannel(_params){
-    if(GlobalConfig.deviceType==1||GlobalConfig.deviceType==2){
-      return ;
+   * 离开视频通话频道
+   * */
+  _leaveChannel(_params) {
+    if (GlobalConfig.deviceType == 1 || GlobalConfig.deviceType == 2) {
+      return;
     }
-    if(_webRtc){
+    if (_webRtc) {
       _webRtc.leaveChannel(_params);
     }
   }
 
   /*
-  * 发布流
-  * */
-  _publishMedia(_params){
+   * 发布流
+   * */
+  _publishMedia(_params) {
     //判断是否能推流,当前课堂推流人数是有限制的
-    let premission=GlobalConfig.getPublishPermission();
-    loger.log("判断是否能推流->",premission);
-    if(!premission&&GlobalConfig.userRole!=ApeConsts.invisible){
+    let premission = GlobalConfig.getPublishPermission();
+    loger.log("判断是否能推流->", premission);
+    if (!premission && GlobalConfig.userRole != ApeConsts.invisible) {
       loger.warn("不能再打开更多设备");
-      console.log("当前用户列表",GlobalConfig.rosters);
+      console.log("当前用户列表", GlobalConfig.rosters);
       this._emit(MessageTypes.MCU_ERROR, MessageTypes.ERR_WEBRTC_PUBLISH_FULL);
-      return ;
+      return;
     }
     //ios和安卓的只需要更新数据即可
-    if(GlobalConfig.deviceType==1||GlobalConfig.deviceType==2){
+    if (GlobalConfig.deviceType == 1 || GlobalConfig.deviceType == 2) {
       loger.log("调用webRtc推流");
       GlobalConfig.openCamera = EngineUtils.creatTimestamp();
       GlobalConfig.openMicrophones = GlobalConfig.openCamera;
 
-    this.userDeviecStatusChange({
+      this.userDeviecStatusChange({
         nodeId: GlobalConfig.nodeId,
         userRole: GlobalConfig.userRole,
         userName: GlobalConfig.userName,
@@ -2916,25 +3073,25 @@ export default class MessageEntrance extends Emiter {
         openCamera: GlobalConfig.openCamera,
         openMicrophones: GlobalConfig.openMicrophones
       });
-      this._mediaRecordControl({"status":WebRtcApe.RECORD_STATUS_1});
-      return ;
+      this._mediaRecordControl({"status": WebRtcApe.RECORD_STATUS_1});
+      return;
     }
 
     //PC端的先推流再同步数据
-   if(_webRtc){
+    if (_webRtc) {
       _webRtc.publish(_params);
     }
   }
 
   /*
-  * 停止发布流
-  * */
-  _unpublishMedia(_params){
+   * 停止发布流
+   * */
+  _unpublishMedia(_params) {
     //ios和安卓的只需要更新数据即可
-    if(GlobalConfig.deviceType==1||GlobalConfig.deviceType==2){
+    if (GlobalConfig.deviceType == 1 || GlobalConfig.deviceType == 2) {
       loger.log("调用webRtc停止推流");
-      GlobalConfig.openCamera =0;
-      GlobalConfig.openMicrophones =0;
+      GlobalConfig.openCamera = 0;
+      GlobalConfig.openMicrophones = 0;
       this.userDeviecStatusChange({
         nodeId: GlobalConfig.nodeId,
         userRole: GlobalConfig.userRole,
@@ -2943,12 +3100,12 @@ export default class MessageEntrance extends Emiter {
         openCamera: GlobalConfig.openCamera,
         openMicrophones: GlobalConfig.openMicrophones
       });
-      this._mediaRecordControl({"status":WebRtcApe.RECORD_STATUS_0});
-      return ;
+      this._mediaRecordControl({"status": WebRtcApe.RECORD_STATUS_0});
+      return;
     }
 
-   if(_webRtc){
-      this._mediaRecordControl({"status":WebRtcApe.RECORD_STATUS_0});
+    if (_webRtc) {
+      this._mediaRecordControl({"status": WebRtcApe.RECORD_STATUS_0});
       _webRtc.unpublish(_params);
     }
   }
@@ -2956,45 +3113,47 @@ export default class MessageEntrance extends Emiter {
   /*
    * 切换摄像头和麦克风设备
    * */
-  _changeDevices(_params){
-    if(GlobalConfig.deviceType==1||GlobalConfig.deviceType==2){
+  _changeDevices(_params) {
+    if (GlobalConfig.deviceType == 1 || GlobalConfig.deviceType == 2) {
       return;
     }
-    if(_webRtc){
+    if (_webRtc) {
       _webRtc.changeDevices(_params);
     }
   }
+
   /*
    * 设置旁路推流
    * */
-  _setConfigPublisher(_params){
-    if(GlobalConfig.deviceType==1||GlobalConfig.deviceType==2){
+  _setConfigPublisher(_params) {
+    if (GlobalConfig.deviceType == 1 || GlobalConfig.deviceType == 2) {
       return;
     }
-    if(_webRtc){
+    if (_webRtc) {
       _webRtc.setConfigPublisher(_params);
     }
   }
 
   /*
-  * 设置本地video视图
-  * */
-  _setLocalMediaView(_params){
-    if(GlobalConfig.deviceType==1||GlobalConfig.deviceType==2){
+   * 设置本地video视图
+   * */
+  _setLocalMediaView(_params) {
+    if (GlobalConfig.deviceType == 1 || GlobalConfig.deviceType == 2) {
       return;
     }
-    if(_webRtc){
+    if (_webRtc) {
       _webRtc.setLoaclView(_params);
     }
   }
+
   /*
-  * 设置房间内老师身份的视图
-  * */
-  _setHostRemoteMediaView(_params){
-    if(GlobalConfig.deviceType==1||GlobalConfig.deviceType==2){
+   * 设置房间内老师身份的视图
+   * */
+  _setHostRemoteMediaView(_params) {
+    if (GlobalConfig.deviceType == 1 || GlobalConfig.deviceType == 2) {
       return;
     }
-   if(_webRtc){
+    if (_webRtc) {
       _webRtc.setHostRemoteMediaView(_params);
     }
   }
@@ -3002,117 +3161,119 @@ export default class MessageEntrance extends Emiter {
   /*
    * 设置房间内普通身份的视图
    * */
-  _setNormalRemoteMediaView(_params){
-    if(GlobalConfig.deviceType==1||GlobalConfig.deviceType==2){
+  _setNormalRemoteMediaView(_params) {
+    if (GlobalConfig.deviceType == 1 || GlobalConfig.deviceType == 2) {
       return;
     }
-    if(_webRtc){
+    if (_webRtc) {
       _webRtc.setNormalRemoteMediaView(_params);
     }
   }
 
   /*
-  * 设置RTC视频属性
-  * */
-  _changeRtcVideoConfig(_params){
-    loger.log("设置RTC视频属性",_params);
-    if(!_params){
+   * 设置RTC视频属性
+   * */
+  _changeRtcVideoConfig(_params) {
+    loger.log("设置RTC视频属性", _params);
+    if (!_params) {
       return;
     }
-    if(GlobalConfig.deviceType==1||GlobalConfig.deviceType==2){
+    if (GlobalConfig.deviceType == 1 || GlobalConfig.deviceType == 2) {
       return;
     }
-    if(_webRtc){
+    if (_webRtc) {
       _webRtc.changeRtcVideoConfig(_params);
 
       //如果是老师和主讲人操作,需要同步给所有人
-      if(GlobalConfig.isTeachOrAssistant){
-        if(_confer_ape){
-          let newVideoScale=_params.videoScale||1;
-          if(GlobalConfig.videoScale==newVideoScale){
-            loger.log("不需要设置视频视图大小,没有发生改变",newVideoScale);
+      if (GlobalConfig.isTeachOrAssistant) {
+        if (_confer_ape) {
+          let newVideoScale = _params.videoScale || 1;
+          if (GlobalConfig.videoScale == newVideoScale) {
+            loger.log("不需要设置视频视图大小,没有发生改变", newVideoScale);
             return;
           }
-          loger.log("设置视频视图大小->",newVideoScale);
-          GlobalConfig.videoScale=newVideoScale;
-          _confer_ape.sendUpdaterClassStatusInfo({videoScale:_params.videoScale});
+          loger.log("设置视频视图大小->", newVideoScale);
+          GlobalConfig.videoScale = newVideoScale;
+          _confer_ape.sendUpdaterClassStatusInfo({videoScale: _params.videoScale});
         }
       }
     }
   }
 
   /*
-  * 设置监课和需要隐藏显示的用户视图
-  * */
-  _setInvisibleMediaView(_params){
-    if(GlobalConfig.deviceType==1||GlobalConfig.deviceType==2){
+   * 设置监课和需要隐藏显示的用户视图
+   * */
+  _setInvisibleMediaView(_params) {
+    if (GlobalConfig.deviceType == 1 || GlobalConfig.deviceType == 2) {
       return;
     }
-    if(_webRtc){
+    if (_webRtc) {
       _webRtc.setInvisibleMediaView(_params);
     }
   }
+
   //设置app相关数据
-  _setAppConfig(_params){
-    if(!_params){
+  _setAppConfig(_params) {
+    if (!_params) {
       return;
     }
-    loger.log("设置appConfig",_params);
-    if(GlobalConfig.appId){
+    loger.log("设置appConfig", _params);
+    if (GlobalConfig.appId) {
       loger.log("本地已经设置appConfig,不需要再设置");
       return;
     }
-    GlobalConfig.appId=_params.appId||"";
-    GlobalConfig.appCertificate=_params.appCertificate||"";
-    GlobalConfig.appRecordingKey=_params.appRecordingKey||"";
-    GlobalConfig.recordInterfaces=_params.recordInterfaces||"";
-    GlobalConfig.getChannelToken=_params.getChannelToken||""
-    GlobalConfig.getRecordInfoInterfaces=_params.getRecordInfoInterfaces||"";
-    GlobalConfig.stopRecordingInterfaces=_params.stopRecordingInterfaces||"";
-    GlobalConfig.getTxRecordInfoInterfaces=_params.getTxRecordInfoInterfaces||"";
-    GlobalConfig.getRecordFileURLAgoInterfaces=_params.getRecordFileURLAgoInterfaces||"";
+    GlobalConfig.appId = _params.appId || "";
+    GlobalConfig.appCertificate = _params.appCertificate || "";
+    GlobalConfig.appRecordingKey = _params.appRecordingKey || "";
+    GlobalConfig.recordInterfaces = _params.recordInterfaces || "";
+    GlobalConfig.getChannelToken = _params.getChannelToken || ""
+    GlobalConfig.getRecordInfoInterfaces = _params.getRecordInfoInterfaces || "";
+    GlobalConfig.stopRecordingInterfaces = _params.stopRecordingInterfaces || "";
+    GlobalConfig.getTxRecordInfoInterfaces = _params.getTxRecordInfoInterfaces || "";
+    GlobalConfig.getRecordFileURLAgoInterfaces = _params.getRecordFileURLAgoInterfaces || "";
 
     //去掉协议头
-    try{
-      if(GlobalConfig.recordInterfaces){
-        GlobalConfig.recordInterfaces=GlobalConfig.recordInterfaces.replace('http://',"");
-        GlobalConfig.recordInterfaces=GlobalConfig.recordInterfaces.replace('https://',"");
+    try {
+      if (GlobalConfig.recordInterfaces) {
+        GlobalConfig.recordInterfaces = GlobalConfig.recordInterfaces.replace('http://', "");
+        GlobalConfig.recordInterfaces = GlobalConfig.recordInterfaces.replace('https://', "");
       }
-      if(GlobalConfig.getRecordInfoInterfaces){
-        GlobalConfig.getRecordInfoInterfaces=GlobalConfig.getRecordInfoInterfaces.replace('http://',"");
-        GlobalConfig.getRecordInfoInterfaces=GlobalConfig.getRecordInfoInterfaces.replace('https://',"");
+      if (GlobalConfig.getRecordInfoInterfaces) {
+        GlobalConfig.getRecordInfoInterfaces = GlobalConfig.getRecordInfoInterfaces.replace('http://', "");
+        GlobalConfig.getRecordInfoInterfaces = GlobalConfig.getRecordInfoInterfaces.replace('https://', "");
       }
-      if(GlobalConfig.getTxRecordInfoInterfaces){
-        GlobalConfig.getTxRecordInfoInterfaces=GlobalConfig.getTxRecordInfoInterfaces.replace('http://',"");
-        GlobalConfig.getTxRecordInfoInterfaces=GlobalConfig.getTxRecordInfoInterfaces.replace('https://',"");
+      if (GlobalConfig.getTxRecordInfoInterfaces) {
+        GlobalConfig.getTxRecordInfoInterfaces = GlobalConfig.getTxRecordInfoInterfaces.replace('http://', "");
+        GlobalConfig.getTxRecordInfoInterfaces = GlobalConfig.getTxRecordInfoInterfaces.replace('https://', "");
       }
-      if(GlobalConfig.getRecordFileURLAgoInterfaces){
-        GlobalConfig.getRecordFileURLAgoInterfaces=GlobalConfig.getRecordFileURLAgoInterfaces.replace('http://',"");
-        GlobalConfig.getRecordFileURLAgoInterfaces=GlobalConfig.getRecordFileURLAgoInterfaces.replace('https://',"");
+      if (GlobalConfig.getRecordFileURLAgoInterfaces) {
+        GlobalConfig.getRecordFileURLAgoInterfaces = GlobalConfig.getRecordFileURLAgoInterfaces.replace('http://', "");
+        GlobalConfig.getRecordFileURLAgoInterfaces = GlobalConfig.getRecordFileURLAgoInterfaces.replace('https://', "");
       }
 
-      if(GlobalConfig.stopRecordingInterfaces){
-        GlobalConfig.stopRecordingInterfaces=GlobalConfig.stopRecordingInterfaces.replace('http://',"");
-        GlobalConfig.stopRecordingInterfaces=GlobalConfig.stopRecordingInterfaces.replace('https://',"");
+      if (GlobalConfig.stopRecordingInterfaces) {
+        GlobalConfig.stopRecordingInterfaces = GlobalConfig.stopRecordingInterfaces.replace('http://', "");
+        GlobalConfig.stopRecordingInterfaces = GlobalConfig.stopRecordingInterfaces.replace('https://', "");
       }
-      if(GlobalConfig.getChannelToken){
-        GlobalConfig.getChannelToken=GlobalConfig.getChannelToken.replace('http://',"");
-        GlobalConfig.getChannelToken=GlobalConfig.getChannelToken.replace('https://',"");
+      if (GlobalConfig.getChannelToken) {
+        GlobalConfig.getChannelToken = GlobalConfig.getChannelToken.replace('http://', "");
+        GlobalConfig.getChannelToken = GlobalConfig.getChannelToken.replace('https://', "");
       }
-    }catch (err){
+    } catch (err) {
 
     }
 
 
   }
+
   //录制状态控制和推流状态控制
-  _mediaRecordControl(_params){
-    if(!GlobalConfig.recordInterfaces||!_params){
-      loger.log("录制控制->失败->接口地址无效",_params);
-      return ;
+  _mediaRecordControl(_params) {
+    if (!GlobalConfig.recordInterfaces || !_params) {
+      loger.log("录制控制->失败->接口地址无效", _params);
+      return;
     }
-    if(_webRtc){
-      switch (_params.status){
+    if (_webRtc) {
+      switch (_params.status) {
         case WebRtcApe.RECORD_STATUS_0:
         case WebRtcApe.RECORD_STATUS_1:
           //推流/停止推流/开启录制 统一使用一个接口
@@ -3127,14 +3288,15 @@ export default class MessageEntrance extends Emiter {
       }
     }
   }
+
   //webRtc-----------------end --------------------------------
   //判断是否能推流,当前课堂推流人数是有限制的
-  _hasFreePublishChannel(){
-    let premission=GlobalConfig.getPublishPermission();
-    loger.log("判断是否能推流->",premission);
-    if(!premission&&GlobalConfig.userRole!=ApeConsts.invisible){
+  _hasFreePublishChannel() {
+    let premission = GlobalConfig.getPublishPermission();
+    loger.log("判断是否能推流->", premission);
+    if (!premission && GlobalConfig.userRole != ApeConsts.invisible) {
       loger.warn("不能再打开更多设备");
-      console.log("当前用户列表",GlobalConfig.rosters);
+      console.log("当前用户列表", GlobalConfig.rosters);
       this._emit(MessageTypes.MCU_ERROR, MessageTypes.ERR_WEBRTC_PUBLISH_FULL);
       return premission;
     }
diff --git a/src/Sass.js b/src/Sass.js
index c52f254..e5697c2 100644
--- a/src/Sass.js
+++ b/src/Sass.js
@@ -862,8 +862,74 @@ class Sass extends Emiter {
         this._emit(Sass.SASS_GET_QUESTION_RESULT_FAILED);
       });
   }
-
   //点名---------------------------------------------------------
+
+
+  //获取课堂保存的信息
+  getClassRecordInfo(_callback){
+    //http://networkschool.xuedianyun.com/server/recordInfo/getOldRecordInfo
+    let path="networkschool.xuedianyun.com/server/recordInfo/getOldRecordInfo";
+    let url = `${GlobalConfig.locationProtocol+path}`;
+    loger.log('获取课堂保存的状态信息', url);
+    //接口中用的是GET
+    fetch(encodeURI(url), {
+      method: 'POST',
+      headers: {
+        "Content-Type": "application/x-www-form-urlencoded; charset=utf-8"
+      },
+      body: `meetingNumber=${ GlobalConfig.classId}`,
+      timeout: 5000
+    })
+      .then(ret => {
+        if (ret.ok) {
+          return ret.json();
+        } else {
+          loger.error(`获取课堂保存的状态信息-网络异常.状态码:${ret}`);
+          if(_callback){
+            _callback();
+          }
+          throw '';
+        }
+      })
+      .then(ret => {
+        loger.log('获取课堂保存的状态信息-完成');
+        /* {
+         "code": 200,
+         "returnData": {
+         "data": {
+         "id": 28,
+         "meeting_number": "1170105412",
+         "info": "{\"siteId\":\"h5dev\",\"classId\":1170105412,\"className\":\"mcuClientSdk20170318\",\"classType\":1,\"classStatus\":2,\"classStartTime\":\"2017-3-24-20-9-56\",\"classStopTime\":\"2017-3-28-22-20-56\",\"classTimestamp\":25402,\"recordPlaybackMaxTime\":0,\"classBeginTime\":\"2017-03-18 19:00:00\",\"classEndTime\":\"2017-03-29 21:00:00\",\"recordStatus\":false,\"recordTimestamp\":0,\"recordFileName\":\"h5dev/20170318/1170105412_20170318.rec\",\"recordDownloadUrl\":\"\",\"serverTimestamp\":1490710856,\"activeDocId\":666519474,\"activeDocCurPage\":1}",
+         "create_time": "2017-03-18T21:12:19.000Z"
+         }
+         }
+         }*/
+        if(ret&&ret.code==200){
+          try{
+            if(ret.returnData&&ret.returnData.data){
+              if(_callback) {
+                _callback(ret.returnData.data.info);
+              }
+            }
+          }catch (err){
+            if(_callback){
+              _callback();
+            }
+          }
+
+        }else {
+          if(_callback){
+            _callback();
+          }
+        }
+      })
+      .catch(err => {
+        loger.error(`AGOR-获取媒体录制信息-异常.状态码:${err}`);
+        if(_callback){
+          _callback();
+        }
+      });
+  }
 }
 
 Sass.prototype.SUCCESS = Sass.SUCCESS = 'Sass_success';
diff --git a/src/SystemConfig.js b/src/SystemConfig.js
index e0baa18..23510b6 100644
--- a/src/SystemConfig.js
+++ b/src/SystemConfig.js
@@ -180,7 +180,7 @@ class SystemConfig {
             if (window.clientInformation.languages.length > 2) {
               Sys.explorer = "chrome";
               loger.log("chrome", Sys);
-            } else if (window.clientInformation.languages.length == 2&&versionNum<60) {
+            } else if (window.clientInformation.languages.length == 2&&versionNum<55) {
               var _track = 'track' in document.createElement('track');
               var webstoreKeysLength = window.chrome && window.chrome.webstore ? Object.keys(window.chrome.webstore).length : 0;
               if (_track) {
diff --git a/src/apes/ApeConsts.js b/src/apes/ApeConsts.js
index 1f24497..694f84f 100644
--- a/src/apes/ApeConsts.js
+++ b/src/apes/ApeConsts.js
@@ -110,6 +110,7 @@ ApeConsts.MEDIA_TYPE_DEFAULT = 0; //没有类型
 ApeConsts.MEDIA_TYPE_VIDEO = 1; //视频流(包含音频)
 ApeConsts.MEDIA_TYPE_AUDIO = 2; //音频流
 ApeConsts.MEDIA_TYPE_SHARE = 3; //屏幕共享
+ApeConsts.MEDIA_TYPE_EXTERNAL_LINK = 4; //外部推流地址
 
 //return返回值状态
 ApeConsts.RETURN_SUCCESS = 0; //成功
diff --git a/src/apes/DocApe.js b/src/apes/DocApe.js
index 461ef42..85a8e82 100644
--- a/src/apes/DocApe.js
+++ b/src/apes/DocApe.js
@@ -684,7 +684,7 @@ class DocApe extends Ape {
     let tempDocItemIdx;//临时记录文档数据,用于显示默认文档
     for (let key in this.docList) {
       tempDocItemIdx = this.docList[key];
-      if (tempDocItemIdx) {
+      if (tempDocItemIdx&&tempDocItemIdx.fileType!="video") {
         loger.log("选择一个文档作为默认文档显示->", tempDocItemIdx);
         let paramInfo = {
           "itemIdx": tempDocItemIdx.itemIdx,
@@ -763,6 +763,13 @@ class DocApe extends Ape {
     docModelPdu.showType = _param.showType || 0;//文档显示模式
     docModelPdu.animationStep = _param.animationStep || 1;//当前页面上的动画步数(动态ppt时有这个字段)
     //loger.log(docModelPdu);
+
+    //新增的外部流数据,
+    docModelPdu.publishUrl = _param.rtmpUrl||"";
+    docModelPdu.rtmpUrl = _param.rtmpUrl||"";
+    docModelPdu.m3u8Url = _param.m3u8Url||"";
+    docModelPdu.replay = _param.replay||"";
+
     return docModelPdu;
   }
 
diff --git a/src/apes/ShareApe.js b/src/apes/ShareApe.js
index d5fdf83..f24afb5 100644
--- a/src/apes/ShareApe.js
+++ b/src/apes/ShareApe.js
@@ -138,6 +138,24 @@ class ShareApe extends Emiter {
       this.shareScreen.startConnect(this.fullIpPort);
     }
   }
+  /*
+  * 推送外部流地址
+  * */
+  publishExternalLink(_result) {
+    /* return {"code": ApeConsts.RETURN_SUCCESS,
+     "data":"",
+     "mediaId":shareChannel,
+     "publishUrl": publishUrl,
+     "streamId":streamId,
+     "port":""
+     };*/
+    clearTimeout(this.reConnectTimer);
+    //if (_result) {
+    //  this.publishUrl = _result.publishUrl || '';
+    //  this.streamId = _result.streamId || '';
+    //}
+    this._emit(MessageTypes.PUBLISH_SCREEN_SHARE_SUCCESS,_result);
+  }
 
   //屏幕共享推流,如果没有连接需要先建立连接
   publish(_result) {
diff --git a/src/apes/VideoApe.js b/src/apes/VideoApe.js
index 692b972..d292537 100644
--- a/src/apes/VideoApe.js
+++ b/src/apes/VideoApe.js
@@ -254,7 +254,7 @@ class VideoApe extends Ape {
         this.stopPublishVideo(_data)
       }
   }
-  //==========================屏幕共享=========================================================================
+  //-----------------------屏幕共享-----------------------
 
   //屏幕共享连接打开
   onPublishScreenShareFaile() {
@@ -291,8 +291,8 @@ class VideoApe extends Ape {
   }
 
   //监听屏幕共享发布成功
-  onPublishScreenShareSuccess() {
-    loger.log('屏幕共享推流成功之后才能更新同步消息->');
+  onPublishScreenShareSuccess(data) {
+    loger.log('屏幕共享推流成功之后才能更新同步消息->',data);
     //屏幕共享推流成功之后才能更新同步消息
     let channelInfo = this.shareApe.getPublishChannelInfo();
     this.sendTableUpdateHandler(channelInfo);
@@ -337,14 +337,48 @@ class VideoApe extends Ape {
       let channelInfo = this.shareApe.getDefaultChannelInfo();
       channelInfo.status = ApeConsts.CHANNEL_STATUS_RELEASED;
       this.sendTableUpdateHandler(channelInfo);
-
       this.shareApe.stopPublish();
     }
+  }
+
+  //-----------------------屏幕共享 end-----------------------
 
+  //-----------------------推送和停止外部流地址----------------
+  //推送外部流地址
+  publishExternalLink(_param) {
+    loger.log('推送外部流地址->',_param);
+    //屏幕共享推流成功之后才能更新同步消息
+    let channelInfo = this.shareApe.getPublishChannelInfo();
+    channelInfo.mediaType = ApeConsts.MEDIA_TYPE_EXTERNAL_LINK;//外部推流地址
+    //如果
+    if(_param){
+      channelInfo.publishUrl=_param.rtmpUrl||"";//推流拉流地址一样
+      channelInfo.m3u8Url=_param.m3u8Url||"";
+      channelInfo.rtmpUrl=_param.rtmpUrl||"";
+      channelInfo.replay=_param.replay||"";
+    }
+    this.sendTableUpdateHandler(channelInfo);
   }
 
-  //=============================屏幕共享 end=================================================
+  //停止推送外部流地址
+  stopPublishExternalLink(_param) {
+    loger.log('停止推送外部流地址->', _param);
+    if (!this.mcu.connected) {
+      loger.warn(GlobalConfig.getCurrentStatus());
+      return {"code": ApeConsts.RETURN_FAILED, "data": "已经断开连接"};
+    }
+    //只有老师能停止屏幕共享
+    if (GlobalConfig.isHost||GlobalConfig.isAssistant||GlobalConfig.isPresenter) {
+      let channelInfo = this.shareApe.getDefaultChannelInfo();
+      channelInfo.status = ApeConsts.CHANNEL_STATUS_RELEASED;
+      channelInfo.mediaType=ApeConsts.MEDIA_TYPE_EXTERNAL_LINK;
+      this.sendTableUpdateHandler(channelInfo);
+      //调用停止的时候自己的也要停止,同步的消息不会再发给自己
+      this._emit(MessageTypes.SCREEN_SHARE_STOP, channelInfo);
+    }
+  }
 
+  //-----------------------推送和停止外部流地址END-------------
   //释放nodeId占用的指定的channelId频道
   _releaseChannelForNodeId(nodeId, channelId) {
     loger.log(nodeId, "停止-->channelId", channelId);
@@ -609,11 +643,14 @@ class VideoApe extends Ape {
       unpackChannelInfo.streamId = "";
     }
     //屏幕共享的流不保存
-    if (unpackChannelInfo.mediaType != ApeConsts.MEDIA_TYPE_SHARE && unpackChannelInfo.channelId > 0) {
+    if (unpackChannelInfo.mediaType != ApeConsts.MEDIA_TYPE_SHARE &&
+      unpackChannelInfo.mediaType != ApeConsts.MEDIA_TYPE_EXTERNAL_LINK &&
+      unpackChannelInfo.channelId > 0) {
       this.mediaModule.mediaChannels[itemIdx] = unpackChannelInfo;
     }
 
-    if (unpackChannelInfo && unpackChannelInfo.fromNodeId != GlobalConfig.nodeId) {
+    //数据存在并且不是自己发送的消息,或者是外部设置的播放地址
+    if (unpackChannelInfo && (unpackChannelInfo.fromNodeId != GlobalConfig.nodeId||unpackChannelInfo.mediaType== ApeConsts.MEDIA_TYPE_EXTERNAL_LINK)) {
       let receiveChannelInfo = {};
       receiveChannelInfo.mediaId = unpackChannelInfo.channelId;
       receiveChannelInfo.fromNodeId = unpackChannelInfo.fromNodeId;
@@ -639,6 +676,7 @@ class VideoApe extends Ape {
           "type": "m3u8",
           "streamId": unpackChannelInfo.streamId
         });
+
         //如果接收的消息中已经有拉流地址,优先使用
         if(unpackChannelInfo.m3u8Url){
           m3u8Stream.playUrl=unpackChannelInfo.m3u8Url;
@@ -646,6 +684,9 @@ class VideoApe extends Ape {
         if(unpackChannelInfo.rtmpUrl){
           rtmpStream.playUrl=unpackChannelInfo.rtmpUrl;
         }
+        if(unpackChannelInfo.replay){
+          replay.playUrl=unpackChannelInfo.replay;
+        }
 
 
         if (m3u8Stream.code == 0) {
@@ -658,7 +699,9 @@ class VideoApe extends Ape {
           receiveChannelInfo.replay = replay.playUrl;
         }
 
-        if (unpackChannelInfo.mediaType != ApeConsts.MEDIA_TYPE_SHARE) {
+        if (unpackChannelInfo.mediaType != ApeConsts.MEDIA_TYPE_SHARE&&
+          unpackChannelInfo.mediaType != ApeConsts.MEDIA_TYPE_EXTERNAL_LINK
+        ) {
           //广播播放视频的消息
           loger.log("VIDEO_PLAY", receiveChannelInfo);
           this._emit(MessageTypes.VIDEO_PLAY, receiveChannelInfo);
@@ -669,7 +712,9 @@ class VideoApe extends Ape {
 
         }
       } else {
-        if (unpackChannelInfo.mediaType != ApeConsts.MEDIA_TYPE_SHARE) {
+        if (unpackChannelInfo.mediaType != ApeConsts.MEDIA_TYPE_SHARE&&
+           unpackChannelInfo.mediaType != ApeConsts.MEDIA_TYPE_EXTERNAL_LINK
+        ) {
           //停止播放视频
           loger.log("VIDEO_STOP", receiveChannelInfo);
           this._emit(MessageTypes.VIDEO_STOP, receiveChannelInfo);
@@ -679,7 +724,6 @@ class VideoApe extends Ape {
             loger.log("SCREEN_SHARE_STOP", receiveChannelInfo);
             this._emit(MessageTypes.SCREEN_SHARE_STOP, receiveChannelInfo);
           } else {
-
             loger.log("停止播放视频->channelId=0->不合法的id", receiveChannelInfo);
           }
         }
@@ -697,7 +741,9 @@ class VideoApe extends Ape {
       }
 
       //更新用户的摄像头和麦克风状态
-      if (unpackChannelInfo.mediaType != ApeConsts.MEDIA_TYPE_SHARE) {
+      if (unpackChannelInfo.mediaType != ApeConsts.MEDIA_TYPE_SHARE&&
+        unpackChannelInfo.mediaType != ApeConsts.MEDIA_TYPE_EXTERNAL_LINK
+      ) {
         //非屏幕共享的情况下才更新状态
         this._emit(MessageTypes.USER_DEVICE_STATUS_CHAANGE, {
           nodeId: GlobalConfig.nodeId,
@@ -709,7 +755,9 @@ class VideoApe extends Ape {
         });
       }
     }
-    if (unpackChannelInfo.mediaType != ApeConsts.MEDIA_TYPE_SHARE) {
+    if (unpackChannelInfo.mediaType != ApeConsts.MEDIA_TYPE_SHARE&&
+       unpackChannelInfo.mediaType != ApeConsts.MEDIA_TYPE_EXTERNAL_LINK
+    ) {
       //非屏幕共享情况的处理
       MediaModule.allMediaChannelsList[itemIdx] = unpackChannelInfo;
       console.log('MediaModule.allMediaChannelsList', MediaModule.allMediaChannelsList);
@@ -798,6 +846,7 @@ class VideoApe extends Ape {
     packPduModel.streamId = _param.streamId || "";
     packPduModel.m3u8Url=_param.m3u8Url || "";
     packPduModel.rtmpUrl=_param.rtmpUrl || "";
+    packPduModel.replay=_param.replay || "";
 
     packPduModel.siteId = _param.siteId || GlobalConfig.siteId;//GlobalConfig.siteId;
     packPduModel.classId = parseInt(_param.classId) || parseInt(GlobalConfig.classId);
diff --git a/src/pdus/pro.js b/src/pdus/pro.js
index 40612d4..550f824 100644
--- a/src/pdus/pro.js
+++ b/src/pdus/pro.js
@@ -743,7 +743,10 @@ message RCDocSendDataModelPdu {
     optional uint32 show_type=19;//文档显示模式
     optional uint32 animation_step=20 [default =1];//当前页码的动画步数
     optional bool isFullScreen=21 ;//是否全屏显示
-
+    optional string publishUrl=22;
+    optional string rtmpUrl=23;
+    optional string m3u8Url=24;
+    optional string replay=25;
 }
 
 message RCMediaSharedSendDataModelPdu {
@@ -855,6 +858,7 @@ message RCVideoChannelInfoPdu {
     optional string optionJsonData =16;//其他参数的json对象
     optional string m3u8Url =17;//m3u8拉流地址
     optional string rtmpUrl =18;//rtmp拉流地址
+    optional string replay =19;//回放的拉流地址
 }
 
 message RCVideoChannelInfoRecordPdu {