李勇

1.视频模块增加字段,推流的时候可以设置流名称和拉流的地址;2.录制回放增加接获取声网视频文件地址的接口;3.修复chrome浏览器被识别为360极速的问题;4…

….录制回放模块增加流名称和视频地址的匹配接口
1 market_webrtc_发布版 1 market_webrtc_发布版
2 mcu/ms/rs服务器:腾讯云测试 2 mcu/ms/rs服务器:腾讯云测试
3 appConfig: 3 appConfig:
4 -eyJhcHBJZCI6IjBmZTQ1YmJlYTU5YjRlZTU4NmM4ZGVkNjlmMmJjOGY3IiwiYXBwQ2VydGlmaWNhdGUiOiIiLCJhcHBSZWNvcmRpbmdLZXkiOiIiLCJyZWNvcmRJbnRlcmZhY2VzIjoibmV0d29ya3NjaG9vbC54dWVkaWFueXVuLmNvbS9zZXJ2ZXIvdXNlcnMvUmVjb3JkaW5nIiwiZ2V0Q2hhbm5lbFRva2VuIjoibmV0d29ya3NjaG9vbC54dWVkaWFueXVuLmNvbS9zZXJ2ZXIvdXNlcnMvZW5jcnlwdGlvblRva2VuIiwiZ2V0UmVjb3JkSW5mb0ludGVyZmFjZXMiOiJuZXR3b3Jrc2Nob29sLnh1ZWRpYW55dW4uY29tL3NlcnZlci9yZWNvcmRJbmZvL2dldFJlY29yZEluZm8iLCJzdG9wUmVjb3JkaW5nSW50ZXJmYWNlcyI6Im5ldHdvcmtzY2hvb2wueHVlZGlhbnl1bi5jb20vc2VydmVyL3JlY29yZEluZm8vc3RvcFJlY29yZGluZyIsImdldFR4UmVjb3JkSW5mb0ludGVyZmFjZXMiOiJuZXR3b3Jrc2Nob29sLnh1ZWRpYW55dW4uY29tL3NlcnZlci9yZWNvcmRJbmZvL3RlbmNlbnRSZWNvcmRJbmZvIn0= 4 +eyJhcHBJZCI6IjBmZTQ1YmJlYTU5YjRlZTU4NmM4ZGVkNjlmMmJjOGY3IiwiYXBwQ2VydGlmaWNhdGUiOiIiLCJhcHBSZWNvcmRpbmdLZXkiOiIiLCJyZWNvcmRJbnRlcmZhY2VzIjoibmV0d29ya3NjaG9vbC54dWVkaWFueXVuLmNvbS9zZXJ2ZXIvdXNlcnMvUmVjb3JkaW5nIiwiZ2V0Q2hhbm5lbFRva2VuIjoibmV0d29ya3NjaG9vbC54dWVkaWFueXVuLmNvbS9zZXJ2ZXIvdXNlcnMvZW5jcnlwdGlvblRva2VuIiwiZ2V0UmVjb3JkSW5mb0ludGVyZmFjZXMiOiJuZXR3b3Jrc2Nob29sLnh1ZWRpYW55dW4uY29tL3NlcnZlci9yZWNvcmRJbmZvL2dldFJlY29yZEluZm8iLCJzdG9wUmVjb3JkaW5nSW50ZXJmYWNlcyI6Im5ldHdvcmtzY2hvb2wueHVlZGlhbnl1bi5jb20vc2VydmVyL3JlY29yZEluZm8vc3RvcFJlY29yZGluZyIsImdldFR4UmVjb3JkSW5mb0ludGVyZmFjZXMiOiJuZXR3b3Jrc2Nob29sLnh1ZWRpYW55dW4uY29tL3NlcnZlci9yZWNvcmRJbmZvL3RlbmNlbnRSZWNvcmRJbmZvIiwiZ2V0UmVjb3JkRmlsZVVSTEFnb0ludGVyZmFjZXMiOiJuZXR3b3Jrc2Nob29sLnh1ZWRpYW55dW4uY29tL3NlcnZlci9yZWNvcmRJbmZvL2dldFJlY29yZEZpbGVVUkwifQ==
5 5
6 6
7 market_webrtc_测试版 7 market_webrtc_测试版
8 mcu/ms/rs服务器:腾讯云测试 8 mcu/ms/rs服务器:腾讯云测试
9 -appConfig: eyJhcHBJZCI6ImViMjUzY2M3YjQwYzRhOGI4MmYwYTViNmY5M2MyY2UwIiwiYXBwQ2VydGlmaWNhdGUiOiIiLCJhcHBSZWNvcmRpbmdLZXkiOiIiLCJyZWNvcmRJbnRlcmZhY2VzIjoibmV0d29ya3NjaG9vbC54dWVkaWFueXVuLmNvbS9zZXJ2ZXIvdXNlcnMvUmVjb3JkaW5nIiwiZ2V0Q2hhbm5lbFRva2VuIjoibmV0d29ya3NjaG9vbC54dWVkaWFueXVuLmNvbS9zZXJ2ZXIvdXNlcnMvZW5jcnlwdGlvblRva2VuIiwiZ2V0UmVjb3JkSW5mb0ludGVyZmFjZXMiOiJuZXR3b3Jrc2Nob29sLnh1ZWRpYW55dW4uY29tL3NlcnZlci9yZWNvcmRJbmZvL2dldFJlY29yZEluZm8iLCJzdG9wUmVjb3JkaW5nSW50ZXJmYWNlcyI6Im5ldHdvcmtzY2hvb2wueHVlZGlhbnl1bi5jb20vc2VydmVyL3JlY29yZEluZm8vc3RvcFJlY29yZGluZyIsImdldFR4UmVjb3JkSW5mb0ludGVyZmFjZXMiOiJuZXR3b3Jrc2Nob29sLnh1ZWRpYW55dW4uY29tL3NlcnZlci9yZWNvcmRJbmZvL3RlbmNlbnRSZWNvcmRJbmZvIn0= 9 +appConfig: eyJhcHBJZCI6ImViMjUzY2M3YjQwYzRhOGI4MmYwYTViNmY5M2MyY2UwIiwiYXBwQ2VydGlmaWNhdGUiOiIiLCJhcHBSZWNvcmRpbmdLZXkiOiIiLCJyZWNvcmRJbnRlcmZhY2VzIjoibmV0d29ya3NjaG9vbC54dWVkaWFueXVuLmNvbS9zZXJ2ZXIvdXNlcnMvUmVjb3JkaW5nIiwiZ2V0Q2hhbm5lbFRva2VuIjoibmV0d29ya3NjaG9vbC54dWVkaWFueXVuLmNvbS9zZXJ2ZXIvdXNlcnMvZW5jcnlwdGlvblRva2VuIiwiZ2V0UmVjb3JkSW5mb0ludGVyZmFjZXMiOiJuZXR3b3Jrc2Nob29sLnh1ZWRpYW55dW4uY29tL3NlcnZlci9yZWNvcmRJbmZvL2dldFJlY29yZEluZm8iLCJzdG9wUmVjb3JkaW5nSW50ZXJmYWNlcyI6Im5ldHdvcmtzY2hvb2wueHVlZGlhbnl1bi5jb20vc2VydmVyL3JlY29yZEluZm8vc3RvcFJlY29yZGluZyIsImdldFR4UmVjb3JkSW5mb0ludGVyZmFjZXMiOiJuZXR3b3Jrc2Nob29sLnh1ZWRpYW55dW4uY29tL3NlcnZlci9yZWNvcmRJbmZvL3RlbmNlbnRSZWNvcmRJbmZvIiwiZ2V0UmVjb3JkRmlsZVVSTEFnb0ludGVyZmFjZXMiOiJuZXR3b3Jrc2Nob29sLnh1ZWRpYW55dW4uY29tL3NlcnZlci9yZWNvcmRJbmZvL2dldFJlY29yZEZpbGVVUkwifQ==
10 10
11 11
12 market_flash_发布版 12 market_flash_发布版
@@ -62,7 +62,7 @@ export default class MessageEntrance extends Emiter { @@ -62,7 +62,7 @@ export default class MessageEntrance extends Emiter {
62 constructor() { 62 constructor() {
63 super(); 63 super();
64 //sdk 信息 64 //sdk 信息
65 - GlobalConfig.sdkVersion = "v2.17.11.20171014"; 65 + GlobalConfig.sdkVersion = "v2.18.10.20171019";
66 loger.warn("sdkVersion:" + GlobalConfig.sdkVersion); 66 loger.warn("sdkVersion:" + GlobalConfig.sdkVersion);
67 67
68 //设置 68 //设置
@@ -311,6 +311,7 @@ export default class MessageEntrance extends Emiter { @@ -311,6 +311,7 @@ export default class MessageEntrance extends Emiter {
311 this.addWarn = this._addWarn.bind(this); 311 this.addWarn = this._addWarn.bind(this);
312 this.addError = this._addError.bind(this); 312 this.addError = this._addError.bind(this);
313 313
  314 + this.hasFreePublishChannel=this._hasFreePublishChannel.bind(this);//判断是否还有空闲的推流通道
314 } 315 }
315 316
316 //设置是否输出日志 317 //设置是否输出日志
@@ -1227,10 +1228,16 @@ export default class MessageEntrance extends Emiter { @@ -1227,10 +1228,16 @@ export default class MessageEntrance extends Emiter {
1227 if(GlobalConfig.appId&&!GlobalConfig.openFlash){ 1228 if(GlobalConfig.appId&&!GlobalConfig.openFlash){
1228 //加入之前先设置旁录地址,只有直播支持旁路 1229 //加入之前先设置旁录地址,只有直播支持旁路
1229 if(_webRtc&&GlobalConfig.isTeachOrAssistant){ 1230 if(_webRtc&&GlobalConfig.isTeachOrAssistant){
1230 - let publishData=this._getVideoPublishPath(); 1231 + let curTimestamp = new Date().getTime();
  1232 + let streamId=GlobalConfig.siteId+"_"+GlobalConfig.classId+"_"+GlobalConfig.userId+"_"+curTimestamp;
  1233 + //传入固定的流Id
  1234 + let publishData=this._getVideoPublishPath({streamId:streamId});
1231 loger.log("加入之前先设置旁录地址",publishData); 1235 loger.log("加入之前先设置旁录地址",publishData);
1232 if(publishData&&publishData.code==0){ 1236 if(publishData&&publishData.code==0){
1233 _webRtc.setConfigPublisherUrl(publishData.publishUrl); 1237 _webRtc.setConfigPublisherUrl(publishData.publishUrl);
  1238 + let m3u8Stream = _video_ape.getPlayVideoPath({"type": "m3u8", "streamId":streamId});
  1239 + let rtmpStream = _video_ape.getPlayVideoPath({"type": "rtmp", "streamId":streamId});
  1240 + _webRtc.setRtmpM3u8Path({m3u8Url:m3u8Stream.playUrl,rtmpUrl:rtmpStream.playUrl});
1234 } 1241 }
1235 } 1242 }
1236 setTimeout(()=>{ 1243 setTimeout(()=>{
@@ -1987,6 +1994,14 @@ export default class MessageEntrance extends Emiter { @@ -1987,6 +1994,14 @@ export default class MessageEntrance extends Emiter {
1987 return {"code": ApeConsts.RETURN_FAILED, "data": ""}; 1994 return {"code": ApeConsts.RETURN_FAILED, "data": ""};
1988 } 1995 }
1989 if (_video_ape) { 1996 if (_video_ape) {
  1997 + //if(_data.status==WebRtcApe.RECORD_STATUS_1&&!_data.publishUrl){
  1998 + if(_data.status==WebRtcApe.RECORD_STATUS_1){
  1999 + let publishData=this._getVideoPublishPath();
  2000 + let publishUrl="";
  2001 + if(publishData&&publishData.code==0){
  2002 + _data.publishUrl=publishData.publishUrl||"";
  2003 + }
  2004 + }
1990 _video_ape.mediaPublishStatusChange(_data); 2005 _video_ape.mediaPublishStatusChange(_data);
1991 } 2006 }
1992 } 2007 }
@@ -2808,6 +2823,7 @@ export default class MessageEntrance extends Emiter { @@ -2808,6 +2823,7 @@ export default class MessageEntrance extends Emiter {
2808 _webRtc.leaveChannel(_params); 2823 _webRtc.leaveChannel(_params);
2809 } 2824 }
2810 } 2825 }
  2826 +
2811 /* 2827 /*
2812 * 发布流 2828 * 发布流
2813 * */ 2829 * */
@@ -2826,7 +2842,8 @@ export default class MessageEntrance extends Emiter { @@ -2826,7 +2842,8 @@ export default class MessageEntrance extends Emiter {
2826 loger.log("调用webRtc推流"); 2842 loger.log("调用webRtc推流");
2827 GlobalConfig.openCamera = EngineUtils.creatTimestamp(); 2843 GlobalConfig.openCamera = EngineUtils.creatTimestamp();
2828 GlobalConfig.openMicrophones = GlobalConfig.openCamera; 2844 GlobalConfig.openMicrophones = GlobalConfig.openCamera;
2829 - this.userDeviecStatusChange({ 2845 +
  2846 + this.userDeviecStatusChange({
2830 nodeId: GlobalConfig.nodeId, 2847 nodeId: GlobalConfig.nodeId,
2831 userRole: GlobalConfig.userRole, 2848 userRole: GlobalConfig.userRole,
2832 userName: GlobalConfig.userName, 2849 userName: GlobalConfig.userName,
@@ -2943,10 +2960,16 @@ export default class MessageEntrance extends Emiter { @@ -2943,10 +2960,16 @@ export default class MessageEntrance extends Emiter {
2943 if(_webRtc){ 2960 if(_webRtc){
2944 _webRtc.changeRtcVideoConfig(_params); 2961 _webRtc.changeRtcVideoConfig(_params);
2945 2962
2946 - //如果是老师操作,需要同步给所有人  
2947 - if(GlobalConfig.isHost){ 2963 + //如果是老师和主讲人操作,需要同步给所有人
  2964 + if(GlobalConfig.isTeachOrAssistant){
2948 if(_confer_ape){ 2965 if(_confer_ape){
2949 - GlobalConfig.videoScale=_params.videoScale||1; 2966 + let newVideoScale=_params.videoScale||1;
  2967 + if(GlobalConfig.videoScale==newVideoScale){
  2968 + loger.log("不需要设置视频视图大小,没有发生改变",newVideoScale);
  2969 + return;
  2970 + }
  2971 + loger.log("设置视频视图大小->",newVideoScale);
  2972 + GlobalConfig.videoScale=newVideoScale;
2950 _confer_ape.sendUpdaterClassStatusInfo({videoScale:_params.videoScale}); 2973 _confer_ape.sendUpdaterClassStatusInfo({videoScale:_params.videoScale});
2951 } 2974 }
2952 } 2975 }
@@ -2982,6 +3005,7 @@ export default class MessageEntrance extends Emiter { @@ -2982,6 +3005,7 @@ export default class MessageEntrance extends Emiter {
2982 GlobalConfig.getRecordInfoInterfaces=_params.getRecordInfoInterfaces||""; 3005 GlobalConfig.getRecordInfoInterfaces=_params.getRecordInfoInterfaces||"";
2983 GlobalConfig.stopRecordingInterfaces=_params.stopRecordingInterfaces||""; 3006 GlobalConfig.stopRecordingInterfaces=_params.stopRecordingInterfaces||"";
2984 GlobalConfig.getTxRecordInfoInterfaces=_params.getTxRecordInfoInterfaces||""; 3007 GlobalConfig.getTxRecordInfoInterfaces=_params.getTxRecordInfoInterfaces||"";
  3008 + GlobalConfig.getRecordFileURLAgoInterfaces=_params.getRecordFileURLAgoInterfaces||"";
2985 3009
2986 //去掉协议头 3010 //去掉协议头
2987 try{ 3011 try{
@@ -2997,6 +3021,10 @@ export default class MessageEntrance extends Emiter { @@ -2997,6 +3021,10 @@ export default class MessageEntrance extends Emiter {
2997 GlobalConfig.getTxRecordInfoInterfaces=GlobalConfig.getTxRecordInfoInterfaces.replace('http://',""); 3021 GlobalConfig.getTxRecordInfoInterfaces=GlobalConfig.getTxRecordInfoInterfaces.replace('http://',"");
2998 GlobalConfig.getTxRecordInfoInterfaces=GlobalConfig.getTxRecordInfoInterfaces.replace('https://',""); 3022 GlobalConfig.getTxRecordInfoInterfaces=GlobalConfig.getTxRecordInfoInterfaces.replace('https://',"");
2999 } 3023 }
  3024 + if(GlobalConfig.getRecordFileURLAgoInterfaces){
  3025 + GlobalConfig.getRecordFileURLAgoInterfaces=GlobalConfig.getRecordFileURLAgoInterfaces.replace('http://',"");
  3026 + GlobalConfig.getRecordFileURLAgoInterfaces=GlobalConfig.getRecordFileURLAgoInterfaces.replace('https://',"");
  3027 + }
3000 3028
3001 if(GlobalConfig.stopRecordingInterfaces){ 3029 if(GlobalConfig.stopRecordingInterfaces){
3002 GlobalConfig.stopRecordingInterfaces=GlobalConfig.stopRecordingInterfaces.replace('http://',""); 3030 GlobalConfig.stopRecordingInterfaces=GlobalConfig.stopRecordingInterfaces.replace('http://',"");
@@ -3035,5 +3063,17 @@ export default class MessageEntrance extends Emiter { @@ -3035,5 +3063,17 @@ export default class MessageEntrance extends Emiter {
3035 } 3063 }
3036 } 3064 }
3037 //webRtc-----------------end -------------------------------- 3065 //webRtc-----------------end --------------------------------
  3066 + //判断是否能推流,当前课堂推流人数是有限制的
  3067 + _hasFreePublishChannel(){
  3068 + let premission=GlobalConfig.getPublishPermission();
  3069 + loger.log("判断是否能推流->",premission);
  3070 + if(!premission&&GlobalConfig.userRole!=ApeConsts.invisible){
  3071 + loger.warn("不能再打开更多设备");
  3072 + console.log("当前用户列表",GlobalConfig.rosters);
  3073 + this._emit(MessageTypes.MCU_ERROR, MessageTypes.ERR_WEBRTC_PUBLISH_FULL);
  3074 + return premission;
  3075 + }
  3076 + return premission;
  3077 + }
3038 } 3078 }
3039 3079
@@ -617,7 +617,7 @@ GlobalConfig.recordInterfaces="";//控制开启录制和录制状态改变的接 @@ -617,7 +617,7 @@ GlobalConfig.recordInterfaces="";//控制开启录制和录制状态改变的接
617 GlobalConfig.getRecordInfoInterfaces="";//获取媒体录制信息数据的接口 617 GlobalConfig.getRecordInfoInterfaces="";//获取媒体录制信息数据的接口
618 GlobalConfig.stopRecordingInterfaces="";//停止录制的接口 618 GlobalConfig.stopRecordingInterfaces="";//停止录制的接口
619 GlobalConfig.getTxRecordInfoInterfaces="";//获取媒体录制信息数据的接口(tx) 619 GlobalConfig.getTxRecordInfoInterfaces="";//获取媒体录制信息数据的接口(tx)
620 - 620 +GlobalConfig.getRecordFileURLAgoInterfaces="";//获取媒体录制信息数据的接口(ago)
621 GlobalConfig.getChannelToken="";//获取token的地址 621 GlobalConfig.getChannelToken="";//获取token的地址
622 622
623 GlobalConfig.videoScale=1;//视频的缩放倍数,默认1倍无缩放 623 GlobalConfig.videoScale=1;//视频的缩放倍数,默认1倍无缩放
@@ -33,8 +33,9 @@ class RecordPlayBackParse extends Emiter { @@ -33,8 +33,9 @@ class RecordPlayBackParse extends Emiter {
33 this._recordPlaybackTimestamp = 0;//回放的时间 33 this._recordPlaybackTimestamp = 0;//回放的时间
34 this._recordPlaybackMaxTime = 0;//录制回放的总时间 34 this._recordPlaybackMaxTime = 0;//录制回放的总时间
35 this._isReady = false;//录制回放是否已经准备完成 35 this._isReady = false;//录制回放是否已经准备完成
36 - this.isLoadTxRecordInfo=false;//是否已经加载腾讯云的录制数据  
37 - this.isLoadAgoraRecordInfo=false;//是否已经加载声网的录制数据 36 + this.isLoadTxRecordInfo=false;//是否已经加载TXY的录制数据
  37 + this.isLoadAgoRecordInfo=false;//是否已经加载AGO的录制数据
  38 + this.isgetRecordFileURLFromAgo=false;//是否已经加载AGO的录制文件地址数据
38 39
39 this._apes = {}; 40 this._apes = {};
40 this._videoApeBroadcastMssages={};//视频模块的广播消息 41 this._videoApeBroadcastMssages={};//视频模块的广播消息
@@ -301,20 +302,85 @@ class RecordPlayBackParse extends Emiter { @@ -301,20 +302,85 @@ class RecordPlayBackParse extends Emiter {
301 this.getMediaRecrodInfoFromTx(()=>{ 302 this.getMediaRecrodInfoFromTx(()=>{
302 //解析录制的rec数据 303 //解析录制的rec数据
303 this.isLoadTxRecordInfo=true; 304 this.isLoadTxRecordInfo=true;
304 - if(this.isLoadTxRecordInfo&&this.isLoadAgoraRecordInfo){ 305 + if(this.isLoadTxRecordInfo&&this.isLoadAgoRecordInfo&&this.isgetRecordFileURLFromAgo){
305 this.parseArrayBuf(); 306 this.parseArrayBuf();
306 } 307 }
307 }); 308 });
308 this.getMediaRecrodInfoFromAgora(()=>{ 309 this.getMediaRecrodInfoFromAgora(()=>{
309 - this.isLoadAgoraRecordInfo=true; 310 + this.isLoadAgoRecordInfo=true;
310 //解析录制的rec数据 311 //解析录制的rec数据
311 - if(this.isLoadTxRecordInfo&&this.isLoadAgoraRecordInfo){ 312 + if(this.isLoadTxRecordInfo&&this.isLoadAgoRecordInfo&&this.isgetRecordFileURLFromAgo){
312 this.parseArrayBuf(); 313 this.parseArrayBuf();
313 } 314 }
314 }); 315 });
  316 + this.getRecordFileURLFromAgo(()=>{
  317 + this.isgetRecordFileURLFromAgo=true;
  318 + //解析录制的rec数据
  319 + if(this.isLoadTxRecordInfo&&this.isLoadAgoRecordInfo&&this.isgetRecordFileURLFromAgo){
  320 + this.parseArrayBuf();
  321 + }
  322 + })
315 } 323 }
316 } 324 }
317 - 325 + //获取媒体录制的地址信息-时间戳流名称和文件地址对应
  326 + getRecordFileURLFromAgo(_callback){
  327 + if(!GlobalConfig.getRecordFileURLAgoInterfaces){
  328 + loger.log("AGOR-获取媒体录制信息->失败->接口地址无效");
  329 + if(_callback){
  330 + _callback();
  331 + }
  332 + return ;
  333 + }
  334 + let url = `${GlobalConfig.locationProtocol+GlobalConfig.getRecordFileURLAgoInterfaces}`;
  335 + loger.log('AGOR-获取媒体录制信息.', url);
  336 + //接口中用的是GET
  337 + fetch(encodeURI(url), {
  338 + method: 'POST',
  339 + headers: {
  340 + "Content-Type": "application/x-www-form-urlencoded; charset=utf-8"
  341 + },
  342 + body: `path=${ GlobalConfig.channelId}`,
  343 + timeout: 15000
  344 + })
  345 + .then(ret => {
  346 + if (ret.ok) {
  347 + return ret.json();
  348 + } else {
  349 + loger.error(`AGOR-获取媒体录制信息-网络异常.状态码:${ret}`);
  350 + if(_callback){
  351 + _callback();
  352 + }
  353 + throw '';
  354 + }
  355 + })
  356 + .then(ret => {
  357 + loger.log('AGOR-获取媒体录制地址信息-完成',ret);
  358 + /* {"code": 200,
  359 + "returnData":{
  360 + "data":[
  361 + {channel:"markettest_153452314"
  362 + createTime:"1508125785740"
  363 + fileUrl:"http://networkschool.xuedianyun.com:8899/20171016/markettest_153452314_033717/125533402_20171016034945745_av.mp4"
  364 + timestamp :"1508125785519"
  365 + uid:"125533402" }
  366 + ]
  367 + } }*/
  368 + if(ret&&ret.code==200){
  369 + if(ret.returnData&&ret.returnData.data){
  370 + this.parseAndSavaStreamInfoFromAgor(ret.returnData.data);
  371 + }
  372 + }
  373 + if(_callback){
  374 + _callback();
  375 + }
  376 + })
  377 + .catch(err => {
  378 + loger.error(`AGOR-获取媒体录制信息-异常.状态码:${err}`);
  379 + if(_callback){
  380 + _callback();
  381 + }
  382 + });
  383 + }
318 //获取媒体录制信息 384 //获取媒体录制信息
319 getMediaRecrodInfoFromAgora(_callback){ 385 getMediaRecrodInfoFromAgora(_callback){
320 //获取课堂录制信息 get localhost:3000/recordInfo/getRecordInfo/7d72365eb9834353397e3e3f9d460bdda 386 //获取课堂录制信息 get localhost:3000/recordInfo/getRecordInfo/7d72365eb9834353397e3e3f9d460bdda
@@ -373,7 +439,7 @@ class RecordPlayBackParse extends Emiter { @@ -373,7 +439,7 @@ class RecordPlayBackParse extends Emiter {
373 "Content-Type": "application/x-www-form-urlencoded; charset=utf-8" 439 "Content-Type": "application/x-www-form-urlencoded; charset=utf-8"
374 }, 440 },
375 body: `channelId=${ GlobalConfig.channelId}`, 441 body: `channelId=${ GlobalConfig.channelId}`,
376 - timeout: 5000 442 + timeout: 15000
377 }) 443 })
378 .then(ret => { 444 .then(ret => {
379 if (ret.ok) { 445 if (ret.ok) {
@@ -415,8 +481,38 @@ class RecordPlayBackParse extends Emiter { @@ -415,8 +481,38 @@ class RecordPlayBackParse extends Emiter {
415 } 481 }
416 }); 482 });
417 } 483 }
  484 + //解析agor录制的视频数据,数组[]
  485 + parseAndSavaStreamInfoFromAgor(_dataArr){
  486 + //{channel:"markettest_153452314"
  487 + // createTime:"1508125785740"
  488 + // fileUrl:"http://networkschool.xuedianyun.com:8899/20171016/markettest_153452314_033717/125533402_20171016034945745_av.mp4"
  489 + // timestamp :"1508125785519",
  490 + // userId:"T9663",
  491 + // uid:"125533402" }
  492 + if(!_dataArr){
  493 + loger.log("AGOR-外部录制的视频数据无效");
  494 + return;
  495 + }
  496 + let item;
  497 + loger.log("AGOR-外部录制的视频数据-length="+_dataArr.length)
  498 + for(let i=0;i<_dataArr.length;i++){
  499 + item=_dataArr[i];
  500 + if(item&&item.fileUrl){
  501 + let streamId=item.channel+"_"+item.userId+"_"+item.timestamp;
  502 + //if(item.fileUrl.indexOf(".m3u8")>0){
  503 + // MediaModule.streams[itemJson.stream_id]=itemJson.video_url;
  504 + //}
  505 + if(!MediaModule.streams[streamId]){
  506 + MediaModule.streams[streamId]=item.fileUrl;
  507 + }else {
  508 + loger.log("流数据已经存在->",streamId, MediaModule.streams[streamId],item.fileUrl);
  509 + }
  510 + }
  511 + }
  512 + console.log("AGO MediaModule.streams", MediaModule.streams);
  513 + }
418 514
419 - //解析腾讯云录制的视频数据,数组[] 515 + //解析TXY录制的视频数据,数组[]
420 parseAndSavaStreamInfoFromTx(_dataArr){ 516 parseAndSavaStreamInfoFromTx(_dataArr){
421 /* "id": "0zo9GALQdmkwrJVYxqgaE6xM7j8yvOZN", 517 /* "id": "0zo9GALQdmkwrJVYxqgaE6xM7j8yvOZN",
422 "channelId": "marketflashtest_1966232762", 518 "channelId": "marketflashtest_1966232762",
@@ -445,7 +541,7 @@ class RecordPlayBackParse extends Emiter { @@ -445,7 +541,7 @@ class RecordPlayBackParse extends Emiter {
445 } 541 }
446 } 542 }
447 } 543 }
448 - console.log(" MediaModule.streams", MediaModule.streams); 544 + console.log("TX MediaModule.streams", MediaModule.streams);
449 } 545 }
450 546
451 //解析录制的rec数据 547 //解析录制的rec数据
@@ -170,23 +170,40 @@ class SystemConfig { @@ -170,23 +170,40 @@ class SystemConfig {
170 } 170 }
171 else if (Sys.explorer == "chrome") { 171 else if (Sys.explorer == "chrome") {
172 if (window.clientInformation.languages) { 172 if (window.clientInformation.languages) {
  173 + let versionNum=0;
  174 + let versions=[];
  175 + if(Sys.explorerVersion){
  176 + versions=Sys.explorerVersion.split(".");
  177 + }
  178 + versionNum=parseInt(versions[0]);
  179 + loger.log("versionNum", versionNum);
173 if (window.clientInformation.languages.length > 2) { 180 if (window.clientInformation.languages.length > 2) {
174 Sys.explorer = "chrome"; 181 Sys.explorer = "chrome";
175 loger.log("chrome", Sys); 182 loger.log("chrome", Sys);
176 - }else if (window.clientInformation.languages.length == 2) { 183 + } else if (window.clientInformation.languages.length == 2&&versionNum<60) {
177 var _track = 'track' in document.createElement('track'); 184 var _track = 'track' in document.createElement('track');
178 var webstoreKeysLength = window.chrome && window.chrome.webstore ? Object.keys(window.chrome.webstore).length : 0; 185 var webstoreKeysLength = window.chrome && window.chrome.webstore ? Object.keys(window.chrome.webstore).length : 0;
179 if (_track) { 186 if (_track) {
180 webstoreKeysLength > 1 ? '360ee' : '360se'; 187 webstoreKeysLength > 1 ? '360ee' : '360se';
181 - if(webstoreKeysLength>1){ 188 + if (webstoreKeysLength > 1) {
182 //loger.log("当前是360极速浏览器", Sys); 189 //loger.log("当前是360极速浏览器", Sys);
183 Sys.explorer = "360极速"; 190 Sys.explorer = "360极速";
184 - }else { 191 + } else {
185 loger.log("当前是360安全浏览器", Sys); 192 loger.log("当前是360安全浏览器", Sys);
186 Sys.explorer = "360安全"; 193 Sys.explorer = "360安全";
187 } 194 }
188 } 195 }
189 } 196 }
  197 +
  198 + //61版本的chrome速浏览器在有的系统会被误认为是360极速,需要处理
  199 + if (Sys.explorer == "360极速") {
  200 + if (Sys.explorerVersion.indexOf("60.") == 0 ||
  201 + Sys.explorerVersion.indexOf("61.") == 0) {
  202 + //这个是chrome浏览器
  203 + Sys.explorer = "chrome";
  204 + }
  205 + }
  206 +
190 } 207 }
191 } 208 }
192 //safari浏览器 209 //safari浏览器
@@ -103,7 +103,7 @@ class MediaModule { @@ -103,7 +103,7 @@ class MediaModule {
103 } 103 }
104 104
105 //如果是外部的流地址,不需要拼接,直接使用就可以,是完整的地址 105 //如果是外部的流地址,不需要拼接,直接使用就可以,是完整的地址
106 - let streamPlayUrl=MediaModule.streams[_param.streamId] 106 + let streamPlayUrl=MediaModule.streams[_param.streamId];
107 if(streamPlayUrl){ 107 if(streamPlayUrl){
108 loger.log("使用外部的流地址->",streamPlayUrl); 108 loger.log("使用外部的流地址->",streamPlayUrl);
109 streamPlayUrl=streamPlayUrl.replace("http://",""); 109 streamPlayUrl=streamPlayUrl.replace("http://","");
@@ -175,6 +175,11 @@ class MediaModule { @@ -175,6 +175,11 @@ class MediaModule {
175 + GlobalConfig.classId + "_" + GlobalConfig.userId 175 + GlobalConfig.classId + "_" + GlobalConfig.userId
176 + "_" + freeChannel + "_" + timestamp; 176 + "_" + freeChannel + "_" + timestamp;
177 177
  178 + //如果外部传入了流ID就使用外部的
  179 + if(_param&&_param.streamId){
  180 + streamId=_param.streamId;
  181 + }
  182 +
178 //生成推流地址和推流数据(同步数据的时候用) 183 //生成推流地址和推流数据(同步数据的时候用)
179 //let publishUrl = "rtmp://" + GlobalConfig.MS_PUBLISH_IP 184 //let publishUrl = "rtmp://" + GlobalConfig.MS_PUBLISH_IP
180 // + port + "/" + pubType + "/" + streamId; 185 // + port + "/" + pubType + "/" + streamId;
@@ -241,6 +246,10 @@ class MediaModule { @@ -241,6 +246,10 @@ class MediaModule {
241 + GlobalConfig.classId + "_" + GlobalConfig.userId 246 + GlobalConfig.classId + "_" + GlobalConfig.userId
242 + "_" + freeChannel + "_" + timestamp; 247 + "_" + freeChannel + "_" + timestamp;
243 248
  249 + //如果外部传入了流ID就使用外部的
  250 + if(_param&&_param.streamId){
  251 + streamId=_param.streamId;
  252 + }
244 //生成推流地址和推流数据(同步数据的时候用) 253 //生成推流地址和推流数据(同步数据的时候用)
245 //let publishUrl = "rtmp://" + GlobalConfig.MS_PUBLISH_IP 254 //let publishUrl = "rtmp://" + GlobalConfig.MS_PUBLISH_IP
246 // + port + "/" + pubType + "/" + streamId; 255 // + port + "/" + pubType + "/" + streamId;
@@ -121,7 +121,6 @@ class VideoApe extends Ape { @@ -121,7 +121,6 @@ class VideoApe extends Ape {
121 121
122 //根据推流的地址获取对应的频道信息 122 //根据推流的地址获取对应的频道信息
123 let needPublishChannelInfo = this.mediaModule.getNeedPublishMediaChannel(_param.publishUrl); 123 let needPublishChannelInfo = this.mediaModule.getNeedPublishMediaChannel(_param.publishUrl);
124 - console.log("needPublishChannelInfo",needPublishChannelInfo);  
125 if (needPublishChannelInfo == null) { 124 if (needPublishChannelInfo == null) {
126 loger.warn('推流->推流数据已经无效', _param); 125 loger.warn('推流->推流数据已经无效', _param);
127 this._emit(MessageTypes.VIDEO_PUBLISH_RESULT, { 126 this._emit(MessageTypes.VIDEO_PUBLISH_RESULT, {
@@ -132,6 +131,14 @@ class VideoApe extends Ape { @@ -132,6 +131,14 @@ class VideoApe extends Ape {
132 return {"code": ApeConsts.RETURN_FAILED, "data": "推流数据已经无效"}; 131 return {"code": ApeConsts.RETURN_FAILED, "data": "推流数据已经无效"};
133 } 132 }
134 133
  134 + //如果外部传入了流Id就使用外部的
  135 + if(_param.streamId){
  136 + needPublishChannelInfo.streamId=_param.streamId;
  137 + loger.log("推流信息->使用外部传递的流ID",needPublishChannelInfo);
  138 + }else {
  139 + loger.log("推流信息->使用原始的流ID",needPublishChannelInfo);
  140 + }
  141 +
135 //判断当前是否还有空闲的channle 142 //判断当前是否还有空闲的channle
136 let freeChannel = this.mediaModule.getFreeMediaChannel(); 143 let freeChannel = this.mediaModule.getFreeMediaChannel();
137 if (freeChannel == 0) { 144 if (freeChannel == 0) {
@@ -175,6 +182,8 @@ class VideoApe extends Ape { @@ -175,6 +182,8 @@ class VideoApe extends Ape {
175 channelInfo.status = ApeConsts.CHANNEL_STATUS_OPENING; 182 channelInfo.status = ApeConsts.CHANNEL_STATUS_OPENING;
176 channelInfo.channelId = needPublishChannelInfo.channelId; 183 channelInfo.channelId = needPublishChannelInfo.channelId;
177 channelInfo.streamId = needPublishChannelInfo.streamId;//按规则拼接的流名称 184 channelInfo.streamId = needPublishChannelInfo.streamId;//按规则拼接的流名称
  185 + channelInfo.m3u8Url=_param.m3u8Url||"";
  186 + channelInfo.rtmpUrl=_param.rtmpUrl||""
178 channelInfo.timestamp = needPublishChannelInfo.timestamp;//时间戳 187 channelInfo.timestamp = needPublishChannelInfo.timestamp;//时间戳
179 channelInfo.mediaType = ApeConsts.MEDIA_TYPE_VIDEO; 188 channelInfo.mediaType = ApeConsts.MEDIA_TYPE_VIDEO;
180 this.sendTableUpdateHandler(channelInfo); 189 this.sendTableUpdateHandler(channelInfo);
@@ -218,14 +227,14 @@ class VideoApe extends Ape { @@ -218,14 +227,14 @@ class VideoApe extends Ape {
218 } 227 }
219 //推流状态发生改变 228 //推流状态发生改变
220 mediaPublishStatusChange(_data){ 229 mediaPublishStatusChange(_data){
221 - loger.log("推流状态发生改变->"); 230 + loger.log("推流状态发生改变->",_data);
222 this.sendVideoBroadcastMsg({ 231 this.sendVideoBroadcastMsg({
223 "actionType": ApeConsts.MEDIA_ACTION_PUBLISH_STATUS, 232 "actionType": ApeConsts.MEDIA_ACTION_PUBLISH_STATUS,
224 "toNodeId": 0, 233 "toNodeId": 0,
225 "data":_data|| "" 234 "data":_data|| ""
226 }); 235 });
227 236
228 - //如果是老师或主讲人助教,推流需要同步 237 + /* //如果是老师或主讲人助教,推流需要同步
229 if(GlobalConfig.isTeachOrAssistant){ 238 if(GlobalConfig.isTeachOrAssistant){
230 _data.nodeId=GlobalConfig.nodeId; 239 _data.nodeId=GlobalConfig.nodeId;
231 if(_data.status==1){ 240 if(_data.status==1){
@@ -234,8 +243,16 @@ class VideoApe extends Ape { @@ -234,8 +243,16 @@ class VideoApe extends Ape {
234 _data.nodeId=GlobalConfig.nodeId; 243 _data.nodeId=GlobalConfig.nodeId;
235 this.stopPublishVideo(_data) 244 this.stopPublishVideo(_data)
236 } 245 }
237 - } 246 + }*/
238 247
  248 + //推流需要同步
  249 + _data.nodeId=GlobalConfig.nodeId;
  250 + if(_data.status==1){
  251 + this.publishVideo(_data);
  252 + }else if(_data.status==0){
  253 + _data.nodeId=GlobalConfig.nodeId;
  254 + this.stopPublishVideo(_data)
  255 + }
239 } 256 }
240 //==========================屏幕共享========================================================================= 257 //==========================屏幕共享=========================================================================
241 258
@@ -622,6 +639,14 @@ class VideoApe extends Ape { @@ -622,6 +639,14 @@ class VideoApe extends Ape {
622 "type": "m3u8", 639 "type": "m3u8",
623 "streamId": unpackChannelInfo.streamId 640 "streamId": unpackChannelInfo.streamId
624 }); 641 });
  642 + //如果接收的消息中已经有拉流地址,优先使用
  643 + if(unpackChannelInfo.m3u8Url){
  644 + m3u8Stream.playUrl=unpackChannelInfo.m3u8Url;
  645 + }
  646 + if(unpackChannelInfo.rtmpUrl){
  647 + rtmpStream.playUrl=unpackChannelInfo.rtmpUrl;
  648 + }
  649 +
625 650
626 if (m3u8Stream.code == 0) { 651 if (m3u8Stream.code == 0) {
627 receiveChannelInfo.m3u8Url = m3u8Stream.playUrl; 652 receiveChannelInfo.m3u8Url = m3u8Stream.playUrl;
@@ -771,6 +796,9 @@ class VideoApe extends Ape { @@ -771,6 +796,9 @@ class VideoApe extends Ape {
771 packPduModel.status = _param.status || ApeConsts.CHANNEL_STATUS_RELEASED; 796 packPduModel.status = _param.status || ApeConsts.CHANNEL_STATUS_RELEASED;
772 packPduModel.channelId = _itemIdx; 797 packPduModel.channelId = _itemIdx;
773 packPduModel.streamId = _param.streamId || ""; 798 packPduModel.streamId = _param.streamId || "";
  799 + packPduModel.m3u8Url=_param.m3u8Url || "";
  800 + packPduModel.rtmpUrl=_param.rtmpUrl || "";
  801 +
774 packPduModel.siteId = _param.siteId || GlobalConfig.siteId;//GlobalConfig.siteId; 802 packPduModel.siteId = _param.siteId || GlobalConfig.siteId;//GlobalConfig.siteId;
775 packPduModel.classId = parseInt(_param.classId) || parseInt(GlobalConfig.classId); 803 packPduModel.classId = parseInt(_param.classId) || parseInt(GlobalConfig.classId);
776 packPduModel.userId = _param.userId || "0"; 804 packPduModel.userId = _param.userId || "0";
@@ -21,6 +21,8 @@ class WebRtcApe extends Emiter { @@ -21,6 +21,8 @@ class WebRtcApe extends Emiter {
21 this.appRecordingKey = ""; 21 this.appRecordingKey = "";
22 22
23 this.configPublisherUrl = "";//旁路地址; 23 this.configPublisherUrl = "";//旁路地址;
  24 + this.m3u8Url="";//旁路拉流地址
  25 + this.rtmpUrl="";//旁路拉流地址
24 26
25 this.channelKey = null; 27 this.channelKey = null;
26 this.channelId = ""; 28 this.channelId = "";
@@ -434,7 +436,17 @@ class WebRtcApe extends Emiter { @@ -434,7 +436,17 @@ class WebRtcApe extends Emiter {
434 loger.warn("设置旁路地址->失败->为初始化或旁路地址无效",_publishUrl); 436 loger.warn("设置旁路地址->失败->为初始化或旁路地址无效",_publishUrl);
435 } 437 }
436 } 438 }
437 - 439 + /*
  440 + * 设置旁录拉流地址
  441 + * */
  442 + setRtmpM3u8Path(_param){
  443 + //_webRtc.setRtmpM3u8Path({m3u8Url:m3u8Stream,rtmpUrl:rtmpStream});
  444 + loger.log("设置旁录拉流地址",_param);
  445 + if(_param){
  446 + this.m3u8Url=_param.m3u8Url||"";
  447 + this.rtmpUrl=_param.rtmpUrl||"";
  448 + }
  449 + }
438 publish(_params) { 450 publish(_params) {
439 if (!this.client || !this.localStream) { 451 if (!this.client || !this.localStream) {
440 return; 452 return;
@@ -574,7 +586,6 @@ class WebRtcApe extends Emiter { @@ -574,7 +586,6 @@ class WebRtcApe extends Emiter {
574 this.videoScale=scale; 586 this.videoScale=scale;
575 loger.log("更新视频视图大小->videoScale:"+this.videoScale); 587 loger.log("更新视频视图大小->videoScale:"+this.videoScale);
576 this.updateAllVideoSize(); 588 this.updateAllVideoSize();
577 -  
578 } 589 }
579 590
580 /* 591 /*
@@ -730,6 +741,8 @@ class WebRtcApe extends Emiter { @@ -730,6 +741,8 @@ class WebRtcApe extends Emiter {
730 let curTimestamp = new Date().getTime(); 741 let curTimestamp = new Date().getTime();
731 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}`; 742 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}`;
732 743
  744 + //markettest_623790840_T9540_1508207080
  745 + let streamId=GlobalConfig.siteId+"_"+GlobalConfig.classId+"_"+GlobalConfig.userId+"_"+curTimestamp;
733 //mcu记录一份数据 746 //mcu记录一份数据
734 this._emit(MessageTypes.MEDIA_PUBLISH_STATUS_CHANGE, { 747 this._emit(MessageTypes.MEDIA_PUBLISH_STATUS_CHANGE, {
735 appId: GlobalConfig.appId, 748 appId: GlobalConfig.appId,
@@ -742,7 +755,10 @@ class WebRtcApe extends Emiter { @@ -742,7 +755,10 @@ class WebRtcApe extends Emiter {
742 userRole: GlobalConfig.userRole, 755 userRole: GlobalConfig.userRole,
743 timestamp: curTimestamp, 756 timestamp: curTimestamp,
744 recordTimestamp: GlobalConfig.recordTimestamp, 757 recordTimestamp: GlobalConfig.recordTimestamp,
745 - publishUrl:this.configPublisherUrl 758 + streamId:streamId,
  759 + publishUrl:this.configPublisherUrl,
  760 + m3u8Url:this.m3u8Url,
  761 + rtmpUrl:this.rtmpUrl
746 }); 762 });
747 return data; 763 return data;
748 } 764 }
@@ -853,6 +853,8 @@ message RCVideoChannelInfoPdu { @@ -853,6 +853,8 @@ message RCVideoChannelInfoPdu {
853 optional uint32 screenHeight = 14;//屏幕分辨率高 853 optional uint32 screenHeight = 14;//屏幕分辨率高
854 optional uint32 deviceType = 15;//设备类型 854 optional uint32 deviceType = 15;//设备类型
855 optional string optionJsonData =16;//其他参数的json对象 855 optional string optionJsonData =16;//其他参数的json对象
  856 + optional string m3u8Url =17;//m3u8拉流地址
  857 + optional string rtmpUrl =18;//rtmp拉流地址
856 } 858 }
857 859
858 message RCVideoChannelInfoRecordPdu { 860 message RCVideoChannelInfoRecordPdu {