From c91a641dd2d699dab7829d97b4e43243ec2859f5 Mon Sep 17 00:00:00 2001
From: liyong <liyong@3mang.com>
Date: Wed, 27 Sep 2017 14:13:03 +0800
Subject: [PATCH] 1.腾讯云录制增加flv格式;2.视频模块获取空闲频道的时候增加对当前最大允许路数值为0时的处理;3.测试声网旁路功能

---
 src/EngineEntrance.js   |  14 ++++++++++++--
 src/apes/MediaModule.js |  10 +++++++---
 src/apes/WebRtcApe.js   | 543 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 3 files changed, 305 insertions(+), 262 deletions(-)

diff --git a/src/EngineEntrance.js b/src/EngineEntrance.js
index 2ec445e..c713cfa 100644
--- a/src/EngineEntrance.js
+++ b/src/EngineEntrance.js
@@ -62,7 +62,7 @@ export default class MessageEntrance extends Emiter {
   constructor() {
     super();
     //sdk 信息
-    GlobalConfig.sdkVersion = "v2.12.8.20170926";
+    GlobalConfig.sdkVersion = "v2.12.14.20170927";
     loger.warn("sdkVersion:" + GlobalConfig.sdkVersion);
 
     //设置
@@ -1209,9 +1209,17 @@ export default class MessageEntrance extends Emiter {
 
     if(GlobalConfig.appId&&!GlobalConfig.openFlash){
       setTimeout(()=>{
+      /*  //加入之前先设置旁录地址,只有直播支持旁路
+        if(_webRtc){
+          let publishData=this._getVideoPublishPath();
+          loger.log("加入之前先设置旁录地址",publishData);
+          if(publishData&&publishData.code==0){
+            _webRtc.setConfigPublisherUrl(publishData.publishUrl);
+          }
+        }*/
         //加入音视频通话模块,延迟一秒处理,因为视频需要根据用户列表信息来判断放的位置,太早的话用户列表没有数据
         this._joinChannel({channelId:GlobalConfig.channelId,channelKey:GlobalConfig.channelKey ,uid:GlobalConfig.userUid,info:""+GlobalConfig.userRole});
-      },1000);
+      },1600);
     }
   }
 
@@ -2740,6 +2748,7 @@ export default class MessageEntrance extends Emiter {
       });
     }
   }
+
   /*
   * 加入视频通话
   * */
@@ -2798,6 +2807,7 @@ export default class MessageEntrance extends Emiter {
       _webRtc.publish(_params);
     }
   }
+
   /*
   * 停止发布流
   * */
diff --git a/src/apes/MediaModule.js b/src/apes/MediaModule.js
index 62ae873..5557f78 100644
--- a/src/apes/MediaModule.js
+++ b/src/apes/MediaModule.js
@@ -143,7 +143,7 @@ class MediaModule {
     txTime=txTime.toString(16);
     let txSecret= MD5(publishSuffix + streamId+txTime);
     //rtmp://11220.livepush.myqcloud.com/live/11220_c5a1ea0bce?bizid=11220&txSecret=b1d8af72bf62366eef31cbb5dc5c8778&txTime=59C5337F
-    newUrl=url +"?bizid=11220&txSecret="+txSecret+"&txTime="+txTime+"&record=hls&record_interval=5400";
+    newUrl=url +"?bizid=11220&txSecret="+txSecret+"&txTime="+txTime+"&record=hls|flv&record_interval=5400";
     loger.log("生成的推流地址->"+newUrl);
     return newUrl;
   }
@@ -343,10 +343,14 @@ class MediaModule {
     }
     loger.log("获取空闲的通道", "mediaChannels", this.mediaChannels, "counter:", counter);
     //loger.log(this.mediaChannels);
-    if (counter < this.maxMediaChannel) {
+    if(this.maxMediaChannel>0){
+      if (counter < this.maxMediaChannel) {
+        return this.MEDIA_OBJ_TABLE_ID + (counter);
+      }
+      return 0;//没有空闲的
+    }else {
       return this.MEDIA_OBJ_TABLE_ID + (counter);
     }
-    return 0;//没有空闲的
   }
 
   //获取准备推流的频道信息
diff --git a/src/apes/WebRtcApe.js b/src/apes/WebRtcApe.js
index b12356f..d63a57d 100644
--- a/src/apes/WebRtcApe.js
+++ b/src/apes/WebRtcApe.js
@@ -14,18 +14,20 @@ let loger = Loger.getLoger('WebRtcApe');
 class WebRtcApe extends Emiter {
   constructor() {
     super();
-    this.nameDisplay="block";//默认显示名字
+    this.nameDisplay = "block";//默认显示名字
     this.appId = '';
     this.appCertificate = "";
     this.appRecordingKey = "";
 
-    this.channelKey=null;
+    this.configPublisherUrl = "";//旁路地址;
+
+    this.channelKey = null;
     this.channelId = "";
     this.uid = 0;
     this.info = ""
 
-    this.reAddRemoteStreamDelay=0;//重连远程视频的计时器
-    this.rePublishDelay=0;//重新推流的间隔
+    this.reAddRemoteStreamDelay = 0;//重连远程视频的计时器
+    this.rePublishDelay = 0;//重新推流的间隔
 
     this.mode = "interop";
     this.client = null;
@@ -35,40 +37,40 @@ class WebRtcApe extends Emiter {
     this.curCameraId = "";
     this.curMicrophoneId = "";
 
-    this.remoteVideoList={};//记录远程视频流
+    this.remoteVideoList = {};//记录远程视频流
 
     this.videoResolution = "240P";
     this.isOpenVideo = true;
 
-    this.isPublish=false;//当前是否正在推流
+    this.isPublish = false;//当前是否正在推流
 
-    this.normalRemoteViewId ="";
-    this.normalRemoteStyle ="";
-    this.normalRemoteVideoWidth=320;
-    this.normalRemoteVideoHeight=240;
+    this.normalRemoteViewId = "";
+    this.normalRemoteStyle = "";
+    this.normalRemoteVideoWidth = 320;
+    this.normalRemoteVideoHeight = 240;
 
     this.localViewId = "";
     this.localStyle = "";
-    this.localVideoWidth=320;
-    this.localVideoHeight=240;
+    this.localVideoWidth = 320;
+    this.localVideoHeight = 240;
 
     this.hostRemoteViewId = "";
     this.hostRemoteStyle = "";
-    this.hostRemoteVideoWidth=320;
-    this.hostRemoteVideoHeight=240;
+    this.hostRemoteVideoWidth = 320;
+    this.hostRemoteVideoHeight = 240;
 
-    this.invisibleViewId ="";
-    this.invisibleStyle ="";
-    this.invisibleVideoWidth=320;
-    this.invisibleVideoHeight=240;
+    this.invisibleViewId = "";
+    this.invisibleStyle = "";
+    this.invisibleVideoWidth = 320;
+    this.invisibleVideoHeight = 240;
     this.xdyRemote = "xdy_remote";
     //webRtc sdk
     this.client = AgoraRTC.createClient({mode: this.mode});
 
-    this.getDevices(null,(devices)=>{
+    this.getDevices(null, (devices)=> {
       if (this.cameras && this.cameras.length > 0) {
         this.curCameraId = this.cameras[0].deviceId || "";
-        GlobalConfig.curCamera =this.cameras[0].label || "";
+        GlobalConfig.curCamera = this.cameras[0].label || "";
       }
       if (this.microphones && this.microphones.length > 0) {
         this.curMicrophoneId = this.microphones[0].deviceId || "";
@@ -77,7 +79,7 @@ class WebRtcApe extends Emiter {
     });
   }
 
-  initApp(_params,_callback) {
+  initApp(_params, _callback) {
     loger.log("初始化WebRtc");
     if (_params) {
       this.appId = _params.appId;
@@ -85,13 +87,13 @@ class WebRtcApe extends Emiter {
     if (this.client) {
       this.client.init(this.appId, () => {
         loger.log("初始化WebRtc->成功");
-        if(_callback){
-          _callback({isSuccess:true});
+        if (_callback) {
+          _callback({isSuccess: true});
         }
       }, (err)=> {
         loger.error("初始化WebRtc->失败", err);
-        if(_callback){
-          _callback({isSuccess:false,error:err});
+        if (_callback) {
+          _callback({isSuccess: false, error: err});
         }
       });
       this.addEvent();
@@ -116,8 +118,8 @@ class WebRtcApe extends Emiter {
       }
     });
     this.client.on('stream-published', (evt)=> {
-      loger.log("webRtc->推流成功->",new Date().getTime());
-      this.isPublish=true;
+      loger.log("webRtc->推流成功->", new Date().getTime());
+      this.isPublish = true;
       GlobalConfig.openCamera = EngineUtils.creatTimestamp();
       GlobalConfig.openMicrophones = GlobalConfig.openCamera;
       this._emit(MessageTypes.USER_DEVICE_STATUS_CHAANGE, {
@@ -132,63 +134,63 @@ class WebRtcApe extends Emiter {
     });
     this.client.on('stream-added', (evt)=> {
       let stream = evt.stream;
-     /* loger.log("添加一个远程视频流: " + stream.getId(),new Date().getTime());
-      this.client.subscribe(stream, (err)=> {
-        loger.log("添加一个远程视频流->failed", err);
-      });*/
+      /* loger.log("添加一个远程视频流: " + stream.getId(),new Date().getTime());
+       this.client.subscribe(stream, (err)=> {
+       loger.log("添加一个远程视频流->failed", err);
+       });*/
       this.reAddRemoteStream(stream);
     });
     this.client.on('stream-subscribed', (evt)=> {
       let stream = evt.stream;
       this.addRemoetStreamView(stream);
     });
-  /*    this.client.on('stream-subscribed', (evt)=> {
-      let stream = evt.stream;
-      if(stream){
-        //let viewDiv=`<div id="${this.xdyRemote + stream.getId()}" style="width:${this.hostRemoteVideoWidth}px;height:${this.hostRemoteVideoHeight}px;"></div>`;
-        let uid=stream.getId();
-        let user=GlobalConfig.getUserInfoFromeNodeId(uid);
-        let userName="";
-        let userRole=""
-        if(user){
-          userName=user.name||"";
-          userRole=user.userRole;
-        }
-
-        let nameDiv=`<div style="width:98%;height:20px; position: absolute; z-index: 1;left: 4px;overflow:hidden;font-size: 14px; color: #cccccc;display:${this.nameDisplay}">${userName}</div>`;
-
-        if(userRole==ApeConsts.invisible){
-          //把远程视频添加到监课列表
-          loger.log("获取远程视频流成功->监课:"+userName+"->" + uid,new Date().getTime());
-          let viewDiv=`<div id="${this.xdyRemote + uid}" style="width:${this.invisibleVideoWidth}px;height:${this.invisibleVideoHeight}px;float: left;margin-right: 1px;pointer-events: none;">${nameDiv}</div>`;
-          $(this.invisibleViewId).append(viewDiv);
-        }else if(userRole==ApeConsts.host){
-          //把远程视图添加到老师列表
-          loger.log("获取远程视频流成功->老师:"+userName+"->" + uid,new Date().getTime());
-          let viewDiv=`<div id="${this.xdyRemote + uid}" style="width:${this.hostRemoteVideoWidth}px;height:${this.hostRemoteVideoHeight}px;float: left;margin-right: 1px;pointer-events: none;">${nameDiv}</div>`;
-          $(this.hostRemoteViewId).append(viewDiv);
-        }else {
-          //把视图添加到学生列表
-          loger.log("获取远程视频流成功->学生:"+userName+"->" +uid,new Date().getTime());
-          let viewDiv=`<div id="${this.xdyRemote + uid}" style="width:${this.normalRemoteVideoWidth}px;height:${this.normalRemoteVideoHeight}px;float: left;margin-right: 1px;pointer-events: none;">${nameDiv}</div>`;
-          $(this.normalRemoteViewId).append(viewDiv);
-        }
-        //播放视频,隐藏控制条
-        try{
-          $("bar_"+stream.getId()).hide();
-          stream.play(this.xdyRemote + stream.getId());
-        }catch (err){
-        }
-        if(user.deviceType==1||user.deviceType==2){
-          this.remoteVideoList[user.nodeId]=stream;
-        }
-        console.log("移动端远程视频流集合->",this.remoteVideoList);
-      }
-    });*/
+    /*    this.client.on('stream-subscribed', (evt)=> {
+     let stream = evt.stream;
+     if(stream){
+     //let viewDiv=`<div id="${this.xdyRemote + stream.getId()}" style="width:${this.hostRemoteVideoWidth}px;height:${this.hostRemoteVideoHeight}px;"></div>`;
+     let uid=stream.getId();
+     let user=GlobalConfig.getUserInfoFromeNodeId(uid);
+     let userName="";
+     let userRole=""
+     if(user){
+     userName=user.name||"";
+     userRole=user.userRole;
+     }
+
+     let nameDiv=`<div style="width:98%;height:20px; position: absolute; z-index: 1;left: 4px;overflow:hidden;font-size: 14px; color: #cccccc;display:${this.nameDisplay}">${userName}</div>`;
+
+     if(userRole==ApeConsts.invisible){
+     //把远程视频添加到监课列表
+     loger.log("获取远程视频流成功->监课:"+userName+"->" + uid,new Date().getTime());
+     let viewDiv=`<div id="${this.xdyRemote + uid}" style="width:${this.invisibleVideoWidth}px;height:${this.invisibleVideoHeight}px;float: left;margin-right: 1px;pointer-events: none;">${nameDiv}</div>`;
+     $(this.invisibleViewId).append(viewDiv);
+     }else if(userRole==ApeConsts.host){
+     //把远程视图添加到老师列表
+     loger.log("获取远程视频流成功->老师:"+userName+"->" + uid,new Date().getTime());
+     let viewDiv=`<div id="${this.xdyRemote + uid}" style="width:${this.hostRemoteVideoWidth}px;height:${this.hostRemoteVideoHeight}px;float: left;margin-right: 1px;pointer-events: none;">${nameDiv}</div>`;
+     $(this.hostRemoteViewId).append(viewDiv);
+     }else {
+     //把视图添加到学生列表
+     loger.log("获取远程视频流成功->学生:"+userName+"->" +uid,new Date().getTime());
+     let viewDiv=`<div id="${this.xdyRemote + uid}" style="width:${this.normalRemoteVideoWidth}px;height:${this.normalRemoteVideoHeight}px;float: left;margin-right: 1px;pointer-events: none;">${nameDiv}</div>`;
+     $(this.normalRemoteViewId).append(viewDiv);
+     }
+     //播放视频,隐藏控制条
+     try{
+     $("bar_"+stream.getId()).hide();
+     stream.play(this.xdyRemote + stream.getId());
+     }catch (err){
+     }
+     if(user.deviceType==1||user.deviceType==2){
+     this.remoteVideoList[user.nodeId]=stream;
+     }
+     console.log("移动端远程视频流集合->",this.remoteVideoList);
+     }
+     });*/
 
     this.client.on('stream-removed', (evt)=> {
       let stream = evt.stream;
-      if(stream){
+      if (stream) {
         stream.stop();
         $('#' + this.xdyRemote + stream.getId()).remove();
         loger.log("远程视频流已经断开:" + stream.getId());
@@ -210,109 +212,116 @@ class WebRtcApe extends Emiter {
     });
 
   }
-  addRemoetStreamView(stream){
-    if(stream){
+
+  addRemoetStreamView(stream) {
+    if (stream) {
       //let viewDiv=`<div id="${this.xdyRemote + stream.getId()}" style="width:${this.hostRemoteVideoWidth}px;height:${this.hostRemoteVideoHeight}px;"></div>`;
-      let uid=stream.getId();
-      let user=GlobalConfig.getUserInfoFromeNodeId(uid);
-      let userName="";
-      let userRole=""
-      if(user){
-        userName=user.name||"unknow";
-        userRole=user.userRole;
+      let uid = stream.getId();
+      let user = GlobalConfig.getUserInfoFromeNodeId(uid);
+      let userName = "";
+      let userRole = ""
+      if (user) {
+        userName = user.name || "unknow";
+        userRole = user.userRole;
       }
-      let nameDiv=`<div style="width:98%;height:20px; position: absolute; z-index: 1;left: 4px;overflow:hidden;font-size: 14px; color: #cccccc;display:${this.nameDisplay}">${userName}</div>`;
+      let nameDiv = `<div style="width:98%;height:20px; position: absolute; z-index: 1;left: 4px;overflow:hidden;font-size: 14px; color: #cccccc;display:${this.nameDisplay}">${userName}</div>`;
 
-      if(userRole==ApeConsts.invisible){
+      if (userRole == ApeConsts.invisible) {
         //把远程视频添加到监课列表
-        loger.log("获取远程视频流成功->监课:"+userName+"->" + uid,new Date().getTime());
-        let viewDiv=`<div id="${this.xdyRemote + uid}" style="width:${this.invisibleVideoWidth}px;height:${this.invisibleVideoHeight}px;float: left;margin-right: 1px;pointer-events: none;">${nameDiv}</div>`;
+        loger.log("获取远程视频流成功->监课:" + userName + "->" + uid, new Date().getTime());
+        let viewDiv = `<div id="${this.xdyRemote + uid}" style="width:${this.invisibleVideoWidth}px;height:${this.invisibleVideoHeight}px;float: left;margin-right: 1px;pointer-events: none;">${nameDiv}</div>`;
         $(this.invisibleViewId).append(viewDiv);
-      }else if(userRole==ApeConsts.host){
+      } else if (userRole == ApeConsts.host) {
         //把远程视图添加到老师列表
-        loger.log("获取远程视频流成功->老师:"+userName+"->" + uid,new Date().getTime());
-        let viewDiv=`<div id="${this.xdyRemote + uid}" style="width:${this.hostRemoteVideoWidth}px;height:${this.hostRemoteVideoHeight}px;float: left;margin-right: 1px;pointer-events: none;">${nameDiv}</div>`;
+        loger.log("获取远程视频流成功->老师:" + userName + "->" + uid, new Date().getTime());
+        let viewDiv = `<div id="${this.xdyRemote + uid}" style="width:${this.hostRemoteVideoWidth}px;height:${this.hostRemoteVideoHeight}px;float: left;margin-right: 1px;pointer-events: none;">${nameDiv}</div>`;
         $(this.hostRemoteViewId).append(viewDiv);
-      }else {
+      } else {
         //把视图添加到学生列表
-        loger.log("获取远程视频流成功->学生:"+userName+"->" +uid,new Date().getTime());
-        let viewDiv=`<div id="${this.xdyRemote + uid}" style="width:${this.normalRemoteVideoWidth}px;height:${this.normalRemoteVideoHeight}px;float: left;margin-right: 1px;pointer-events: none;">${nameDiv}</div>`;
+        loger.log("获取远程视频流成功->学生:" + userName + "->" + uid, new Date().getTime());
+        let viewDiv = `<div id="${this.xdyRemote + uid}" style="width:${this.normalRemoteVideoWidth}px;height:${this.normalRemoteVideoHeight}px;float: left;margin-right: 1px;pointer-events: none;">${nameDiv}</div>`;
         $(this.normalRemoteViewId).append(viewDiv);
       }
       //播放视频,隐藏控制条
-      try{
-        $("bar_"+stream.getId()).hide();
+      try {
+        $("bar_" + stream.getId()).hide();
         stream.play(this.xdyRemote + stream.getId());
-      }catch (err){
+      } catch (err) {
       }
-      if(user&&(user.deviceType==1||user.deviceType==2)){
-        this.remoteVideoList[user.nodeId]=stream;
+      if (user && (user.deviceType == 1 || user.deviceType == 2)) {
+        this.remoteVideoList[user.nodeId] = stream;
       }
-      console.log("移动端远程视频流集合->",this.remoteVideoList);
+      console.log("移动端远程视频流集合->", this.remoteVideoList);
     }
   }
-  
+
   //重新添加远程视频
-  reAddRemoteStream(_stream){
-    if(!_stream){
+  reAddRemoteStream(_stream) {
+    if (!_stream) {
       return;
     }
-    let user=GlobalConfig.getUserRoleFromeNodeId(_stream.getId());
-    if(user.openCamera<=0){
-      loger.log("远程流已经停止,不需要再连接: " + _stream.getId(),new Date().getTime());
+    let user = GlobalConfig.getUserRoleFromeNodeId(_stream.getId());
+    if (user.openCamera <= 0) {
+      loger.log("远程流已经停止,不需要再连接: " + _stream.getId(), new Date().getTime());
       return;
     }
-    loger.log("添加一个远程视频流: " + _stream.getId(),new Date().getTime());
+    loger.log("添加一个远程视频流: " + _stream.getId(), new Date().getTime());
     this.client.subscribe(_stream, (err)=> {
       //clearTimeout(this.reAddRemoteStreamDelay);
-      if(err=="INVALID_REMOTE_STREAM"){
+      if (err == "INVALID_REMOTE_STREAM") {
         //流已经无效,不需要再重连
         loger.warn("流已经无效->不需要再重连", err);
         return;
       }
-      if(err=="PEERCONNECTION_FAILED"){
-          setTimeout(()=>{
-          loger.warn("连接远程的流失败->尝试重新连接",err);
+      if (err == "PEERCONNECTION_FAILED") {
+        setTimeout(()=> {
+          loger.warn("连接远程的流失败->尝试重新连接", err);
           this.reAddRemoteStream(_stream);
-        },1400);
-      }else {
-        loger.warn("添加一个远程视频流->失败", err);      }
+        }, 1400);
+      } else {
+        loger.warn("添加一个远程视频流->失败", err);
+      }
     });
   }
+
   //尝试添加远程的移动设备视频流
-  tryAddMobileStream(nodeId){
-    let stream=this.remoteVideoList[nodeId];
-    if(!stream){
+  tryAddMobileStream(nodeId) {
+    let stream = this.remoteVideoList[nodeId];
+    if (!stream) {
       return;
     }
-    let remoteView=document.getElementById(this.xdyRemote +nodeId);
-    console.log("remoteView->",remoteView)
-    if(remoteView){
-      let player=document.getElementById("player_" +nodeId);
-      if(player){
-        loger.log(nodeId+" 流已经添加显示,不需要再处理");
+    let remoteView = document.getElementById(this.xdyRemote + nodeId);
+    console.log("remoteView->", remoteView)
+    if (remoteView) {
+      let player = document.getElementById("player_" + nodeId);
+      if (player) {
+        loger.log(nodeId + " 流已经添加显示,不需要再处理");
         return;
-      }else {
-        loger.log(nodeId+" 删除无效的视图->创建新的视图remoteView");
+      } else {
+        loger.log(nodeId + " 删除无效的视图->创建新的视图remoteView");
         remoteView.remove();
       }
     }
-    if(stream) {
+    if (stream) {
       loger.log("收到移动端推流的消息,主动添加一个远程视频流");
       this.addRemoetStreamView(stream);
     }
   }
 
   joinChannel(_params) {
-    this.channelId = _params.channelId||"";
-    this.uid = parseInt(_params.uid)||0;
-    this.info =_params.info || "";
-    this.channelKey  = _params.channelKey||null;
+    if (!this.client) {
+      loger.warn("入视频通话频道->失败->未初始化对象");
+      return;
+    }
+    this.channelId = _params.channelId || "";
+    this.uid = parseInt(_params.uid) || 0;
+    this.info = _params.info || "";
+    this.channelKey = _params.channelKey || null;
 
-    loger.log("开始加入视频通话频道->channelId:"+this.channelId,"uid:"+this.uid);
-    this.client.join(this.channelKey , ""+this.channelId, this.uid, (uid)=> {
+    loger.log("开始加入视频通话频道->channelId:" + this.channelId, "uid:" + this.uid);
+    this.client.join(this.channelKey, "" + this.channelId, this.uid, (uid)=> {
       this.uid = uid;
-      loger.log("加入视频通话频道->成功->channelId:"+this.channelId,"uid:"+this.uid);
+      loger.log("加入视频通话频道->成功->channelId:" + this.channelId, "uid:" + this.uid);
       this._emit(MessageTypes.WEB_RTC_JOIN_SUCCESS);
       this.openLoaclStream();
     }, (err)=> {
@@ -320,28 +329,29 @@ class WebRtcApe extends Emiter {
       this._emit(MessageTypes.WEB_RTC_JOIN_FAILED);
     });
   }
+
   //重新获取摄像头和麦克风并重新推流
-  reGetLoaclStream(){
-    if(this.isPublish){
+  reGetLoaclStream() {
+    if (this.isPublish) {
       loger.log("重新获取摄像头和麦克风并重新推流");
       //this.changePublishStatusAndServerRecord(WebRtcApe.RECORD_STATUS_0);
       this.unpublish();
       this.openLoaclStream();
       //切换设备后自动重推流
-      this.rePublishDelay=setTimeout(()=>{
+      this.rePublishDelay = setTimeout(()=> {
         this.publish();
-      },1200);
+      }, 1200);
       //this.publish();
-    }else {
+    } else {
       this.openLoaclStream();
     }
   }
 
   openLoaclStream() {
-   /* if(this.localStream){
-      this.localStream.close();
-      this.localStream=null;
-    }*/
+    /* if(this.localStream){
+     this.localStream.close();
+     this.localStream=null;
+     }*/
     this.localStream = AgoraRTC.createStream({
       streamID: this.uid,
       audio: true,
@@ -369,60 +379,76 @@ class WebRtcApe extends Emiter {
     });
   }
 
-  closeRemoteVideoView(_data){
-    if(!_data){
+  closeRemoteVideoView(_data) {
+    if (!_data) {
       return;
     }
     //根据nodeId查找视频
-    try{
+    try {
       loger.log("立即删除停止推流人员的视图");
       $('#' + this.xdyRemote + _data.nodeId).remove();
-    }catch (err){
+    } catch (err) {
 
     }
 
   }
+
+  /*
+   * 设置旁录地址
+   * */
+  setConfigPublisherUrl(_publishUrl){
+    loger.warn("设置旁路地址->",_publishUrl);
+    this.configPublisherUrl=_publishUrl;
+    if(this.client&& this.configPublisherUrl){
+      this.client.configPublisher(
+        {width: 320, height: 240, framerate: 15, bitrate: 200, publishUrl: this.configPublisherUrl}
+      );
+    }else {
+      loger.warn("设置旁路地址->失败->为初始化或旁路地址无效",_publishUrl);
+    }
+  }
+
   publish(_params) {
-    if (!this.client||!this.localStream) {
+    if (!this.client || !this.localStream) {
       return;
     }
     this.localStream.init(()=> {
-      let viewName='localVideoBox_'+this.uid;
-      let videoBox=document.createElement("div");
-      videoBox.id=viewName;
-      videoBox.style.width=this.localVideoWidth+'px';
-      videoBox.style.height=this.localVideoHeight+'px';
-      videoBox.style.float='left';
-      videoBox.style.marginRight="1px";
-      videoBox.style.pointerEvents='none';
+      let viewName = 'localVideoBox_' + this.uid;
+      let videoBox = document.createElement("div");
+      videoBox.id = viewName;
+      videoBox.style.width = this.localVideoWidth + 'px';
+      videoBox.style.height = this.localVideoHeight + 'px';
+      videoBox.style.float = 'left';
+      videoBox.style.marginRight = "1px";
+      videoBox.style.pointerEvents = 'none';
       //自己的视图往前添加
       $(this.localViewId).prepend(videoBox);
-      $("#"+viewName).css("transform", 'rotateY(180deg)');
+      $("#" + viewName).css("transform", 'rotateY(180deg)');
 
       //显示自己的名字
-      let user=GlobalConfig.getUserInfoFromeNodeId(this.uid);
-      let userName="";
-      if(user){
-        userName=user.name||"";
+      let user = GlobalConfig.getUserInfoFromeNodeId(this.uid);
+      let userName = "";
+      if (user) {
+        userName = user.name || "";
       }
-      let nameDiv=`<div id="${"videoOwnerName_"+this.uid}" style="width:98%;height:20px; position: absolute; z-index: 1;left: 4px;overflow:hidden;font-size: 14px; color: #cccccc;display:${this.nameDisplay}">${userName}</div>`;
+      let nameDiv = `<div id="${"videoOwnerName_" + this.uid}" style="width:98%;height:20px; position: absolute; z-index: 1;left: 4px;overflow:hidden;font-size: 14px; color: #cccccc;display:${this.nameDisplay}">${userName}</div>`;
       $(this.localViewId).prepend(nameDiv);
 
-      loger.log("webRtc->推流->",viewName,new Date().getTime());
+      loger.log("webRtc->推流->", viewName, new Date().getTime());
       this.localStream.play(viewName);
       this.client.publish(this.localStream, (err)=> {
         loger.log("webRtc->推流失败: " + err);
-        GlobalConfig.openCamera =0;
-        GlobalConfig.openMicrophones =0;
-        this.isPublish=false;
+        GlobalConfig.openCamera = 0;
+        GlobalConfig.openMicrophones = 0;
+        this.isPublish = false;
         this.unpublish();
       });
     }, (err)=> {
       loger.warn("webRtc->推流->本地流开启失败", err);
-      this.isPublish=false;
+      this.isPublish = false;
       this.clearLocalView();
-      this._emit(MessageTypes.WEB_RTC_PUBLISH_FAILED,err);
-      if(err&&err.msg=="STREAM_ALREADY_INITIALIZED"){
+      this._emit(MessageTypes.WEB_RTC_PUBLISH_FAILED, err);
+      if (err && err.msg == "STREAM_ALREADY_INITIALIZED") {
         this.unpublish();
       }
     });
@@ -430,8 +456,8 @@ class WebRtcApe extends Emiter {
 
   unpublish() {
     clearTimeout(this.rePublishDelay);
-    loger.log("webRtc->停止推流 ",new Date().getTime());
-    if (!this.client||!this.localStream) {
+    loger.log("webRtc->停止推流 ", new Date().getTime());
+    if (!this.client || !this.localStream) {
       return;
     }
     this.client.unpublish(this.localStream, (err)=> {
@@ -439,9 +465,9 @@ class WebRtcApe extends Emiter {
     });
     this.localStream.close();
     this.clearLocalView();
-    this.isPublish=false;
-    GlobalConfig.openCamera =0;
-    GlobalConfig.openMicrophones =0;
+    this.isPublish = false;
+    GlobalConfig.openCamera = 0;
+    GlobalConfig.openMicrophones = 0;
     this._emit(MessageTypes.USER_DEVICE_STATUS_CHAANGE, {
       nodeId: GlobalConfig.nodeId,
       userRole: GlobalConfig.userRole,
@@ -451,21 +477,23 @@ class WebRtcApe extends Emiter {
       openMicrophones: GlobalConfig.openMicrophones
     });
   }
+
   //清除本地视图
-  clearLocalView(){
+  clearLocalView() {
     $('#localVideoBox_' + this.uid).remove();
-    $("#videoOwnerName_"+this.uid).remove();
+    $("#videoOwnerName_" + this.uid).remove();
   }
+
   /*
    * 设置本地回显视图
    * */
   setLoaclView(_params) {
     loger.log("设置自己本地回显视图");
-    this.localViewId = _params.divId||"";
-    this.localStyle = _params.styleStr||"";
-    this.localVideoWidth=parseInt(_params.width)||320;
-    this.localVideoHeight=parseInt(_params.height)||240;
-    this.nameDisplay=_params.nameDisplay||"block";
+    this.localViewId = _params.divId || "";
+    this.localStyle = _params.styleStr || "";
+    this.localVideoWidth = parseInt(_params.width) || 320;
+    this.localVideoHeight = parseInt(_params.height) || 240;
+    this.nameDisplay = _params.nameDisplay || "block";
   }
 
   /*
@@ -473,10 +501,10 @@ class WebRtcApe extends Emiter {
    * */
   setHostRemoteMediaView(_params) {
     loger.log("设置老师视图容器");
-    this.hostRemoteViewId = _params.divId||"";
-    this.hostRemoteStyle = _params.styleStr||"";
-    this.hostRemoteVideoWidth=parseInt(_params.width)||320;
-    this.hostRemoteVideoHeight=parseInt(_params.height)||240;
+    this.hostRemoteViewId = _params.divId || "";
+    this.hostRemoteStyle = _params.styleStr || "";
+    this.hostRemoteVideoWidth = parseInt(_params.width) || 320;
+    this.hostRemoteVideoHeight = parseInt(_params.height) || 240;
   }
 
   /*
@@ -484,10 +512,10 @@ class WebRtcApe extends Emiter {
    * */
   setNormalRemoteMediaView(_params) {
     loger.log("设置学生视图容器");
-    this.normalRemoteViewId = _params.divId||"";
-    this.normalRemoteStyle = _params.styleStr||"";
-    this.normalRemoteVideoWidth=parseInt(_params.width)||320;
-    this.normalRemoteVideoHeight=parseInt(_params.height)||240;
+    this.normalRemoteViewId = _params.divId || "";
+    this.normalRemoteStyle = _params.styleStr || "";
+    this.normalRemoteVideoWidth = parseInt(_params.width) || 320;
+    this.normalRemoteVideoHeight = parseInt(_params.height) || 240;
   }
 
   /*
@@ -495,80 +523,81 @@ class WebRtcApe extends Emiter {
    * */
   setInvisibleMediaView(_params) {
     loger.log("设置监课视图容器");
-    this.invisibleViewId = _params.divId||"";
-    this.invisibleStyle = _params.styleStr||"";
-    this.invisibleVideoWidth=parseInt(_params.width)||320;
-    this.invisibleVideoHeight=parseInt(_params.height)||240;
+    this.invisibleViewId = _params.divId || "";
+    this.invisibleStyle = _params.styleStr || "";
+    this.invisibleVideoWidth = parseInt(_params.width) || 320;
+    this.invisibleVideoHeight = parseInt(_params.height) || 240;
   }
+
   /*
-  * 切换当前使用的设备
-  * */
-  changeDevices(_params){
-    loger.log("切换设备->",_params);
-    if(!_params){
+   * 切换当前使用的设备
+   * */
+  changeDevices(_params) {
+    loger.log("切换设备->", _params);
+    if (!_params) {
       return;
     }
     //设置摄像头
-    if(_params.curCamera){
-      for(let k in this.cameras){
-        let item=this.cameras[k];
-        if(item&&item.label==_params.curCamera){
-          this.curCameraId=item.deviceId;
-          GlobalConfig.curCamera=_params.curCamera;
+    if (_params.curCamera) {
+      for (let k in this.cameras) {
+        let item = this.cameras[k];
+        if (item && item.label == _params.curCamera) {
+          this.curCameraId = item.deviceId;
+          GlobalConfig.curCamera = _params.curCamera;
           break;
         }
       }
     }
     //设置麦克风
-    if(_params.curMicrophone){
-      for(let k in this.microphones){
-        let item=this.microphones[k];
-        if(item&&item.label==_params.curMicrophone){
-          this.curMicrophoneId=item.deviceId;
-          GlobalConfig.curMicrophone=_params.curMicrophone;
+    if (_params.curMicrophone) {
+      for (let k in this.microphones) {
+        let item = this.microphones[k];
+        if (item && item.label == _params.curMicrophone) {
+          this.curMicrophoneId = item.deviceId;
+          GlobalConfig.curMicrophone = _params.curMicrophone;
           break;
         }
       }
     }
     //分辨率
-    if(_params.videoResolution){
-      this.videoResolution=_params.videoResolution||"240P";//默认是240P 	20	320x240	15	200
+    if (_params.videoResolution) {
+      this.videoResolution = _params.videoResolution || "240P";//默认是240P 	20	320x240	15	200
     }
 
     clearTimeout(this.changeDevicesDelay);
-    this.changeDevicesDelay=setTimeout(()=>{
+    this.changeDevicesDelay = setTimeout(()=> {
       this.changePublishStatusAndServerRecord(WebRtcApe.RECORD_STATUS_0);
       //重新获取本地视图流
       this.reGetLoaclStream();
-    },1400);
+    }, 1400);
   }
 
   /*
-  * 获取设备信息
-  * */
-  getDevices(_params,_callback) {
+   * 获取设备信息
+   * */
+  getDevices(_params, _callback) {
     AgoraRTC.getDevices((devices)=> {
       //下面的数组存的是对象
-      this.microphones=[];
-      this.cameras=[];
+      this.microphones = [];
+      this.cameras = [];
 
       //选的数组存的是设备名称
-      GlobalConfig.cameras=[];
-      GlobalConfig.microphones=[];
+      GlobalConfig.cameras = [];
+      GlobalConfig.microphones = [];
 
       loger.log("devices", devices)
       for (let i = 0; i < devices.length; i++) {
         let device = devices[i];
         //{"deviceId":"default","kind":"audiooutput","label":"默认","groupId":"cf49a03ca26700235629fc13d3e6630bd34407c66438d157056a34dd3ae03ef5"}
         if (device.kind == 'audioinput') {
-          if(!device.label){
-            device.label="麦克风_"+i;
+          if (!device.label) {
+            device.label = "麦克风_" + i;
           }
           this.microphones.push(device);
           GlobalConfig.microphones.push(device.label);
         } else if (device.kind == 'videoinput') {
-          if(!device.label){
-            device.label="摄像头_"+i;
+          if (!device.label) {
+            device.label = "摄像头_" + i;
           }
           this.cameras.push(device);
           GlobalConfig.cameras.push(device.label);
@@ -576,45 +605,45 @@ class WebRtcApe extends Emiter {
           loger.warn('其他设备: ', device);
         }
       }
-      let _deviceData={cameras:GlobalConfig.cameras,microphones:GlobalConfig.microphones};
-      if(_callback){
+      let _deviceData = {cameras: GlobalConfig.cameras, microphones: GlobalConfig.microphones};
+      if (_callback) {
         _callback(_deviceData);
       }
-      this._emit(MessageTypes.GET_DEVICES_SUCCESS,_deviceData);
+      this._emit(MessageTypes.GET_DEVICES_SUCCESS, _deviceData);
     });
   }
 
   //组织数据,发送给服务器,控制录制和开启录制-推流和停止推流  status:0 停止推流  1:开始推流(同时开启录制),2:停止录制(同时停止推流)
-  packMediaInfoData(_status){
-    let curTimestamp= new Date().getTime();
-    let data=`appId=${GlobalConfig.appId}&channel=${GlobalConfig.channelId}&channelKey=${GlobalConfig.appCertificate}&uid=${GlobalConfig.userUid}&status=${_status}&userId=${GlobalConfig.userId}&userName=${GlobalConfig.userName}&userRole=${GlobalConfig.userRole}&timestamp=${curTimestamp}&recordTimestamp=${GlobalConfig.recordTimestamp}`;
-
-      //mcu记录一份数据
-      this._emit(MessageTypes.MEDIA_PUBLISH_STATUS_CHANGE, {
-        appId:GlobalConfig.appId,
-        channel:GlobalConfig.channelId,
-        channelKey:GlobalConfig.appCertificate,
-        uid:GlobalConfig.userUid,
-        status:_status,
-        userId:GlobalConfig.userId,
-        userName:GlobalConfig.userName,
-        userRole:GlobalConfig.userRole,
-        timestamp:curTimestamp,
-        recordTimestamp:GlobalConfig.recordTimestamp
-      });
+  packMediaInfoData(_status) {
+    let curTimestamp = new Date().getTime();
+    let data = `appId=${GlobalConfig.appId}&channel=${GlobalConfig.channelId}&channelKey=${GlobalConfig.appCertificate}&uid=${GlobalConfig.userUid}&status=${_status}&userId=${GlobalConfig.userId}&userName=${GlobalConfig.userName}&userRole=${GlobalConfig.userRole}&timestamp=${curTimestamp}&recordTimestamp=${GlobalConfig.recordTimestamp}`;
+
+    //mcu记录一份数据
+    this._emit(MessageTypes.MEDIA_PUBLISH_STATUS_CHANGE, {
+      appId: GlobalConfig.appId,
+      channel: GlobalConfig.channelId,
+      channelKey: GlobalConfig.appCertificate,
+      uid: GlobalConfig.userUid,
+      status: _status,
+      userId: GlobalConfig.userId,
+      userName: GlobalConfig.userName,
+      userRole: GlobalConfig.userRole,
+      timestamp: curTimestamp,
+      recordTimestamp: GlobalConfig.recordTimestamp
+    });
     return data;
   }
 
   //调用服务端接口
   //调用推流/停止推流->并且告诉服务器端开启录制
   //status:0 停止推流  1:开始推流(同时开启录制),2:停止录制(同时停止推流)
-  changePublishStatusAndServerRecord(_status){
-    if(!GlobalConfig.recordInterfaces){
+  changePublishStatusAndServerRecord(_status) {
+    if (!GlobalConfig.recordInterfaces) {
       loger.log("调用服务器端开启录制->失败->接口地址无效");
       return;
     }
-    let url=GlobalConfig.locationProtocol+GlobalConfig.recordInterfaces;
-    let data=this.packMediaInfoData(_status);
+    let url = GlobalConfig.locationProtocol + GlobalConfig.recordInterfaces;
+    let data = this.packMediaInfoData(_status);
     fetch(encodeURI(url), {
       method: 'POST',
       headers: {
@@ -633,7 +662,7 @@ class WebRtcApe extends Emiter {
       })
       .then(ret => {
         if (ret) {
-          loger.log('调用服务器端开启录制完成',ret);
+          loger.log('调用服务器端开启录制完成', ret);
         } else {
           loger.warn('调用服务器端开启录制 失败.', ret);
         }
@@ -644,14 +673,14 @@ class WebRtcApe extends Emiter {
   }
 
   //调用媒体服务停止录制
-  stopRecordingMedia(){
+  stopRecordingMedia() {
     //192.168.31.8:3000/recordInfo/stopRecording/
-    if(!GlobalConfig.stopRecordingInterfaces){
+    if (!GlobalConfig.stopRecordingInterfaces) {
       loger.log("调用服务器端开启录制->失败->接口地址无效");
       return;
     }
-    let url=GlobalConfig.locationProtocol+GlobalConfig.stopRecordingInterfaces;
-    let data=this.packMediaInfoData(WebRtcApe.RECORD_STATUS_2);
+    let url = GlobalConfig.locationProtocol + GlobalConfig.stopRecordingInterfaces;
+    let data = this.packMediaInfoData(WebRtcApe.RECORD_STATUS_2);
     fetch(encodeURI(url), {
       method: 'POST',
       headers: {
@@ -670,7 +699,7 @@ class WebRtcApe extends Emiter {
       })
       .then(ret => {
         if (ret) {
-          loger.log('调用服务器端开启录制完成',ret);
+          loger.log('调用服务器端开启录制完成', ret);
         } else {
           loger.warn('调用服务器端开启录制 失败.', ret);
         }
@@ -680,9 +709,9 @@ class WebRtcApe extends Emiter {
       });
   }
 }
-WebRtcApe.prototype.RECORD_STATUS_0 = WebRtcApe.RECORD_STATUS_0 =0; //停止推流
-WebRtcApe.prototype.RECORD_STATUS_1 = WebRtcApe.RECORD_STATUS_1 =1; //开始推流
-WebRtcApe.prototype.RECORD_STATUS_2 = WebRtcApe.RECORD_STATUS_2 =2; //停止录制
+WebRtcApe.prototype.RECORD_STATUS_0 = WebRtcApe.RECORD_STATUS_0 = 0; //停止推流
+WebRtcApe.prototype.RECORD_STATUS_1 = WebRtcApe.RECORD_STATUS_1 = 1; //开始推流
+WebRtcApe.prototype.RECORD_STATUS_2 = WebRtcApe.RECORD_STATUS_2 = 2; //停止录制
 export default new WebRtcApe;
 
 
--
libgit2 0.24.0