李勇

1.修改FLASH推流拉流的规则-使用腾讯云

  1 +1.开启录制(开始推流)共用一个接口
  2 +{
  3 + "appId": "xxxxxxx",
  4 + "channel": "xxxxx_xxxx",
  5 + "channelKey": "xxxxxx",
  6 + "uid": 11111,
  7 + "userId": "xxx",
  8 + "userName": "xxx",
  9 + "userRole": "xxx",
  10 + "timestamp": "发送消息的的客户端时间戳",
  11 + "recordTimestamp": "当前录制进行的时间戳(回放时使用)"
  12 +}
  13 +
  14 +2.停止推流
  15 +{
  16 + "appId": "xxxxxxx",
  17 + "channel": "xxxxx_xxxx",
  18 + "channelKey": "xxxxxx",
  19 + "uid": 11111,
  20 + "userId": "xxx",
  21 + "userName": "xxx",
  22 + "userRole": "xxx",
  23 + "timestamp": "发送消息的的客户端时间戳",
  24 + "recordTimestamp": "当前录制进行的时间戳(回放时使用)"
  25 +}
  26 +
  27 +3.停止录制
  28 +{
  29 + "appId": "xxxxxxx",
  30 + "channel": "xxxxx_xxxx",
  31 + "channelKey": "xxxxxx",
  32 + "uid": 11111,
  33 + "userId": "xxx",
  34 + "userName": "xxx",
  35 + "userRole": "xxx",
  36 + "timestamp": "发送消息的的客户端时间戳",
  37 + "recordTimestamp": "当前录制进行的时间戳(回放时使用)"
  38 +}
  1 +1.时间戳 recordTimestamp
  2 +2.开始录制 startRecord
  3 +3.停止录制 stopRecord
  4 +4.录制权限 :
  5 + A.第一个进入课堂的人开启录制;
  6 + B.老师或最后一个离开课堂的人停止录制(课堂中没有人的时候10分钟后会停止录制)
  7 + C.课堂录制时间更新:老师->1个人->多个人且没有老师的时候,按nodeId值最小的人来更新(人员离开或更新的时候需要选择一个人)
  8 +5.课堂进行时间单独更新,和之前的逻辑一致,暂停和未开始的时候不记录;
  9 +6.录制的时间更新只要开启录制之后就更新,10秒一次发送到Saas和MCU同步;
  10 +
  11 +7.刚进入的人员的录制时间从Saas获取默认的,MCU同步更新的时候更改为最新时间;
  12 +
  13 +8.计时器:
  14 + updateTimer(){
  15 + //1.更新课堂时间,课堂开始上课的情况下更新
  16 +
  17 + //2.更新录制时间,开启录制的情况下更新
  18 +
  19 + }
  1 +type 1(error) 2(warning) 3(info)
  2 +classId
  3 +userId
  4 +nodeId
  5 +
  6 +data:'aaa\nbbb'
  7 +
  8 +
  9 +//sdk定义好消息格式,多个消息用换行符 \n隔开
  10 +
  11 +20170729 16:42:00 INFO XX.js receive param name=lipeng
  1 +[
  2 + {
  3 + "app": "txlivepush.xuedianyun.com ",
  4 + "appid": 1251457656,
  5 + "appname": "live",
  6 + "channel_id": "marketflashtest_1966232762_user_627052_983041_1506308668",
  7 + "errcode": 0,
  8 + "errmsg": "Maybe Error return Data no record!",
  9 + "event_time": 1506308670,
  10 + "event_type": 1,
  11 + "idc_id": 34,
  12 + "node": "125.39.15.21",
  13 + "sequence": "7037777160241646428",
  14 + "set_id": 2,
  15 + "sign": "2eb0dcbd2ac7c3bade3089bb9aa999be",
  16 + "stream_id": "marketflashtest_1966232762_user_627052_983041_1506308668",
  17 + "stream_param": "bizid=11220&txSecret=339c4094afe1ae427e7bcd7f1f8d1128&txTime=59c91afc&record=hls&record_interval=5400",
  18 + "t": 1506309271,
  19 + "user_ip": "61.135.194.200"
  20 + },
  21 + {
  22 + "appid": 1251457656,
  23 + "channel_id": "marketflashtest_1966232762_user_627052_983041_1506308688",
  24 + "duration": 63,
  25 + "end_time": 1506308756,
  26 + "event_type": 100,
  27 + "file_format": "hls",
  28 + "file_id": "9031868223267109922",
  29 + "file_size": 1349,
  30 + "media_start_time": 2931,
  31 + "record_file_id": "9031868223267109922",
  32 + "sign": "21f73f7e75f9e75196d1b16a536eaff2",
  33 + "start_time": 1506308695,
  34 + "stream_id": "marketflashtest_1966232762_user_627052_983041_1506308688",
  35 + "stream_param": "bizid=11220&txSecret=2a53a100442dbd5d660ff14edb4af853&txTime=59c91b10&record=hls&record_interval=5400",
  36 + "t": 1506309357,
  37 + "task_id": "479501701",
  38 + "video_id": "5158_e9a44513cce1471790f8b0c50c983ccc",
  39 + "video_url": "http://1251457656.vod2.myqcloud.com/9425a3e9vodgzp1251457656/6367ba459031868223267109922/playlist.m3u8 "
  40 + },
  41 + {
  42 + "app": "txlivepush.xuedianyun.com",
  43 + "appid": 1251457656,
  44 + "appname": "live",
  45 + "channel_id": "marketflashtest_1966232762_user_627052_983041_1506308688",
  46 + "errcode": 2,
  47 + "errmsg": "recv rtmpcloseStream",
  48 + "event_time": 1506308756,
  49 + "event_type": 0,
  50 + "idc_id": 34,
  51 + "node": "125.39.15.21",
  52 + "sequence": "7037777160241957731",
  53 + "set_id": 2,
  54 + "sign": "21f73f7e75f9e75196d1b16a536eaff2",
  55 + "stream_id": "marketflashtest_1966232762_user_627052_983041_1506308688",
  56 + "stream_param": "bizid=11220&txSecret=2a53a100442dbd5d660ff14edb4af853&txTime=59c91b10&record=hls&record_interval=5400",
  57 + "t": 1506309357,
  58 + "user_ip": "61.135.194.200"
  59 + }
  60 +]
@@ -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.10.7.20170921"; 65 + GlobalConfig.sdkVersion = "v2.11.13.20170925";
66 loger.warn("sdkVersion:" + GlobalConfig.sdkVersion); 66 loger.warn("sdkVersion:" + GlobalConfig.sdkVersion);
67 67
68 //设置 68 //设置
@@ -493,7 +493,7 @@ export default class MessageEntrance extends Emiter { @@ -493,7 +493,7 @@ export default class MessageEntrance extends Emiter {
493 if(!_data){ 493 if(!_data){
494 return; 494 return;
495 } 495 }
496 - if(GlobalConfig.deviceType==1||GlobalConfig.deviceType==2||_data.nodeId==GlobalConfig.nodeId){ 496 + if(GlobalConfig.deviceType==1||GlobalConfig.deviceType==2||GlobalConfig.deviceType==3||_data.nodeId==GlobalConfig.nodeId){
497 return; 497 return;
498 } 498 }
499 if(_webRtc){ 499 if(_webRtc){
@@ -552,6 +552,7 @@ export default class MessageEntrance extends Emiter { @@ -552,6 +552,7 @@ export default class MessageEntrance extends Emiter {
552 GlobalConfig.isRecordPlayBack = false; //设置为非录制回放状态 552 GlobalConfig.isRecordPlayBack = false; //设置为非录制回放状态
553 GlobalConfig.classId = parseInt(_param.classId); 553 GlobalConfig.classId = parseInt(_param.classId);
554 GlobalConfig.portal = _param.portal||""; 554 GlobalConfig.portal = _param.portal||"";
  555 + GlobalConfig.openFlash=Boolean(_param.openFlash);
555 if(GlobalConfig.isHttps==true){ 556 if(GlobalConfig.isHttps==true){
556 //https的时候替换所有80端口 557 //https的时候替换所有80端口
557 GlobalConfig.portal= GlobalConfig.replacePort(GlobalConfig.portal,":80",""); 558 GlobalConfig.portal= GlobalConfig.replacePort(GlobalConfig.portal,":80","");
@@ -1292,11 +1293,13 @@ export default class MessageEntrance extends Emiter { @@ -1292,11 +1293,13 @@ export default class MessageEntrance extends Emiter {
1292 //加入课堂成功,广播消息 1293 //加入课堂成功,广播消息
1293 this._emit(MessageTypes.CLASS_JOIN_SUCCESS, joinClassSuccessCallBackData); 1294 this._emit(MessageTypes.CLASS_JOIN_SUCCESS, joinClassSuccessCallBackData);
1294 1295
  1296 + if(GlobalConfig.appId&&!GlobalConfig.openFlash){
1295 setTimeout(()=>{ 1297 setTimeout(()=>{
1296 //加入音视频通话模块,延迟一秒处理,因为视频需要根据用户列表信息来判断放的位置,太早的话用户列表没有数据 1298 //加入音视频通话模块,延迟一秒处理,因为视频需要根据用户列表信息来判断放的位置,太早的话用户列表没有数据
1297 this._joinChannel({channelId:GlobalConfig.channelId,channelKey:GlobalConfig.channelKey ,uid:GlobalConfig.userUid,info:""+GlobalConfig.userRole}); 1299 this._joinChannel({channelId:GlobalConfig.channelId,channelKey:GlobalConfig.channelKey ,uid:GlobalConfig.userUid,info:""+GlobalConfig.userRole});
1298 },1000); 1300 },1000);
1299 } 1301 }
  1302 + }
1300 1303
1301 //切换MCU ->_param->{reConnect:false} //reConnect(是否立即替换当前的ip并且重新连接) 1304 //切换MCU ->_param->{reConnect:false} //reConnect(是否立即替换当前的ip并且重新连接)
1302 _switchMcuIpHandler(_param) { 1305 _switchMcuIpHandler(_param) {
@@ -1889,14 +1892,22 @@ export default class MessageEntrance extends Emiter { @@ -1889,14 +1892,22 @@ export default class MessageEntrance extends Emiter {
1889 loger.warn("开启录制回放流程失败->还未创建模块"); 1892 loger.warn("开启录制回放流程失败->还未创建模块");
1890 } 1893 }
1891 } else { 1894 } else {
1892 -  
1893 //初始化音视频通话sdk 1895 //初始化音视频通话sdk
  1896 + if(GlobalConfig.appId&&!GlobalConfig.openFlash){
  1897 + loger.log("使用webRtc通话模式");
  1898 + //加入webRtc
1894 this._initWebRtcSdk({ 1899 this._initWebRtcSdk({
1895 appId:GlobalConfig.appId 1900 appId:GlobalConfig.appId
1896 },()=>{ 1901 },()=>{
1897 //音视频通话SDK初始化完成之后,根据用户的userIp获取信息,获取服务列表选点,选点测速完成后才加入MCU 1902 //音视频通话SDK初始化完成之后,根据用户的userIp获取信息,获取服务列表选点,选点测速完成后才加入MCU
1898 this.loadServerJsonAndgetUserIpInfo(); 1903 this.loadServerJsonAndgetUserIpInfo();
1899 }); 1904 });
  1905 + }else {
  1906 + //加入flash
  1907 + loger.log("使用flash通话模式");
  1908 + this.loadServerJsonAndgetUserIpInfo();
  1909 + }
  1910 +
1900 /* 1911 /*
1901 //根据用户的userIp获取信息,获取服务列表选点,选点测速完成后才加入MCU 1912 //根据用户的userIp获取信息,获取服务列表选点,选点测速完成后才加入MCU
1902 this.loadServerJsonAndgetUserIpInfo(); 1913 this.loadServerJsonAndgetUserIpInfo();
@@ -371,6 +371,19 @@ class GlobalConfig { @@ -371,6 +371,19 @@ class GlobalConfig {
371 } 371 }
372 372
373 } 373 }
  374 + //通过IP查找IP对象
  375 + static getIpItem(ip,ipList){
  376 + if(!ip||!ipList) {
  377 + return null;
  378 + }
  379 + for(let i=0;i<ipList.length;i++){
  380 + let item=ipList[i];
  381 + if(item&&item.ip==ip){
  382 + return item;
  383 + }
  384 + }
  385 + return null;
  386 + }
374 } 387 }
375 388
376 GlobalConfig.statusCode_0 = { "code": 0, message: "SDK 未初始化" }; 389 GlobalConfig.statusCode_0 = { "code": 0, message: "SDK 未初始化" };
@@ -578,7 +591,7 @@ GlobalConfig.locationProtocol="http://";//https;或http: @@ -578,7 +591,7 @@ GlobalConfig.locationProtocol="http://";//https;或http:
578 GlobalConfig.websocketProtocol="ws://";//wss或ws 591 GlobalConfig.websocketProtocol="ws://";//wss或ws
579 GlobalConfig.isHttps=false;//是否是https 592 GlobalConfig.isHttps=false;//是否是https
580 593
581 - 594 +GlobalConfig.openFlash=false;//使用flash通话模式。默认为false,使用的是webRtc
582 //webRtc 595 //webRtc
583 GlobalConfig.appId = ''; 596 GlobalConfig.appId = '';
584 GlobalConfig.appCertificate = ""; 597 GlobalConfig.appCertificate = "";
@@ -123,7 +123,6 @@ class RecordPlayBackParse extends Emiter { @@ -123,7 +123,6 @@ class RecordPlayBackParse extends Emiter {
123 123
124 //解析和储存,录制回放EverSocket底层消息处理 data-数据;timestamp-数据对应的时间戳 124 //解析和储存,录制回放EverSocket底层消息处理 data-数据;timestamp-数据对应的时间戳
125 _parseSaveSocketMsgReceivedHandler(data, timestamp) { 125 _parseSaveSocketMsgReceivedHandler(data, timestamp) {
126 -  
127 let pduMsg = pdu.decode_pdu(data); 126 let pduMsg = pdu.decode_pdu(data);
128 let pduType = pduMsg.get("type"); 127 let pduType = pduMsg.get("type");
129 let pduData = pduMsg.get("data"); 128 let pduData = pduMsg.get("data");
@@ -135,7 +134,7 @@ class RecordPlayBackParse extends Emiter { @@ -135,7 +134,7 @@ class RecordPlayBackParse extends Emiter {
135 pduMsg.type = PduType.RCPDU_SEND_DATA_REQUEST; 134 pduMsg.type = PduType.RCPDU_SEND_DATA_REQUEST;
136 pduType = PduType.RCPDU_SEND_DATA_REQUEST; 135 pduType = PduType.RCPDU_SEND_DATA_REQUEST;
137 } 136 }
138 - loger.log('解析和储存->pduType', pduType); 137 + //loger.log('解析和储存->pduType', pduType);
139 switch (pduType) { 138 switch (pduType) {
140 case PduType.RCPDU_CONNECT_PROVIDER_RESPONSE: 139 case PduType.RCPDU_CONNECT_PROVIDER_RESPONSE:
141 //加入课堂请求返回数据处理 140 //加入课堂请求返回数据处理
@@ -161,7 +160,7 @@ class RecordPlayBackParse extends Emiter { @@ -161,7 +160,7 @@ class RecordPlayBackParse extends Emiter {
161 let ape = this._apes[pduMsg.sessionId]; 160 let ape = this._apes[pduMsg.sessionId];
162 let sessionLabel = ApeConsts(pduMsg.sessionId); 161 let sessionLabel = ApeConsts(pduMsg.sessionId);
163 //只做解析存储,不对外发送 162 //只做解析存储,不对外发送
164 - loger.log('解析数据-timestamp->', timestamp, 'sessionId->', pduMsg.sessionId, 'sessionLabel->', sessionLabel,"subType:"+pduMsg.subType); 163 + //loger.log('解析数据-timestamp->', timestamp, 'sessionId->', pduMsg.sessionId, 'sessionLabel->', sessionLabel,"subType:"+pduMsg.subType);
165 switch (pduMsg.sessionId) { 164 switch (pduMsg.sessionId) {
166 case ApeConsts.CONFERENCE_SESSION_ID: 165 case ApeConsts.CONFERENCE_SESSION_ID:
167 this.saveParseData(data, timestamp, this._conferApeMssages); 166 this.saveParseData(data, timestamp, this._conferApeMssages);
@@ -183,11 +182,12 @@ class RecordPlayBackParse extends Emiter { @@ -183,11 +182,12 @@ class RecordPlayBackParse extends Emiter {
183 break; 182 break;
184 case ApeConsts.VIDEO_SESSION_ID: 183 case ApeConsts.VIDEO_SESSION_ID:
185 this.saveParseData(data, timestamp, this._videoApeMssages); 184 this.saveParseData(data, timestamp, this._videoApeMssages);
186 - this.unPackpduRegAdapterHandler(pduMsg.data,timestamp,data,ApeConsts.VIDEO_SESSION_ID) 185 + this.unpackVideoBroadcastMessage(pduMsg,timestamp,data);
  186 + this.unPackpduRegAdapterHandler(pduMsg.data,timestamp,data,ApeConsts.VIDEO_SESSION_ID,pduMsg.subType)
187 break; 187 break;
188 case ApeConsts.AUDIO_SESSION_ID: 188 case ApeConsts.AUDIO_SESSION_ID:
189 this.saveParseData(data, timestamp, this._audioApeMssages); 189 this.saveParseData(data, timestamp, this._audioApeMssages);
190 - this.unPackpduRegAdapterHandler(pduMsg.data,timestamp,data,ApeConsts.AUDIO_SESSION_ID) 190 + this.unPackpduRegAdapterHandler(pduMsg.data,timestamp,data,ApeConsts.AUDIO_SESSION_ID,pduMsg.subType)
191 break; 191 break;
192 default: 192 default:
193 break; 193 break;
@@ -625,10 +625,62 @@ class RecordPlayBackParse extends Emiter { @@ -625,10 +625,62 @@ class RecordPlayBackParse extends Emiter {
625 } 625 }
626 } 626 }
627 } 627 }
  628 + //解析视频模块的广播消息
  629 + unpackVideoBroadcastMessage(pduMsg,timestamp,data){
  630 + //console.log("VIDEO_SESSION_ID-pduMsg",pduMsg);
  631 + if(!pduMsg){
  632 + return;
  633 + }
  634 + if(pduMsg.subType!=pdu.RCPDU_SEND_VIDEO_DATA_REQUEST){
  635 + //视频广播消息,只处理501消息
  636 + return;
  637 + }
  638 + try{
  639 + let videoReceivePdu = pdu['RCVideoSendDataRequestPdu'].decode(pduMsg.data);
  640 + if (videoReceivePdu == null) {
  641 + loger.warn("视频模块广播消息-decode->失败");
  642 + return;
  643 + }
  644 + videoReceivePdu.data = ArrayBufferUtil.uint8ArrayToStr(videoReceivePdu.data, 2);//开头两个字会乱码
  645 + let dataObj = {};
  646 + try {
  647 + dataObj = JSON.parse(videoReceivePdu.data);
  648 + } catch (err) {
  649 + loger.warn('视频模块广播消息->JSON转换失败');
  650 + dataObj = videoReceivePdu.data;
  651 + }
  652 + videoReceivePdu.data = dataObj;
  653 + //actionType:40
  654 + //data :Object
  655 + //fromNodeId :902994631
  656 + //toNodeId : 0
  657 + if(videoReceivePdu.actionType==40){
  658 + if(!this._videoApeBroadcastMssages[videoReceivePdu.fromNodeId]){
  659 + this._videoApeBroadcastMssages[videoReceivePdu.fromNodeId]={};
  660 + }
  661 + /*
  662 + //结构
  663 + {
  664 + nodeId:{
  665 + 时间戳:{数据}
  666 + },
  667 + nodeId:{
  668 + 时间戳:{数据}
  669 + },.....
  670 + }*/
  671 +
  672 + this._videoApeBroadcastMssages[videoReceivePdu.fromNodeId][timestamp]={parseData:videoReceivePdu,byteData:data,timestamp: timestamp};
  673 + //this._videoApeBroadcastMssages[timestamp]={parseData:videoReceivePdu,byteData:data,timestamp: timestamp};
  674 + }
  675 +
  676 + }catch (err){
  677 + console.log("视频模块广播消息->解析失败",err.message);
  678 + }
  679 + }
628 680
629 //音视频的数据需要解析,然后按频道储存数据 681 //音视频的数据需要解析,然后按频道储存数据
630 // 解析pdu RCAdapterPdu的数据: regBuffer(RCAdapterPdu数据),timestamp(时间戳), data(mcu的原始数据) sessionId(类型) 682 // 解析pdu RCAdapterPdu的数据: regBuffer(RCAdapterPdu数据),timestamp(时间戳), data(mcu的原始数据) sessionId(类型)
631 - unPackpduRegAdapterHandler(regBuffer, timestamp, data, sessionId) { 683 + unPackpduRegAdapterHandler(regBuffer, timestamp, data, sessionId,subType) {
632 let regPdu; 684 let regPdu;
633 let regItems ; 685 let regItems ;
634 let regItemSize ; 686 let regItemSize ;
@@ -639,13 +691,7 @@ class RecordPlayBackParse extends Emiter { @@ -639,13 +691,7 @@ class RecordPlayBackParse extends Emiter {
639 regItemSize = regItems.length; 691 regItemSize = regItems.length;
640 692
641 }catch (err){ 693 }catch (err){
642 - console.warn('RCAdapterPdu->unpack-error->type类型不对');  
643 - try {  
644 - let sendDataPdu = pdu['RCVideoSendDataRequestPdu'].decode(regBuffer);  
645 - console.log("RCVideoSendDataRequestPdu",sendDataPdu);  
646 - }catch (err){  
647 -  
648 - } 694 + console.warn('RCAdapterPdu->unpack-error->type类型不对',"subType:"+subType);
649 return; 695 return;
650 } 696 }
651 697
@@ -670,7 +716,8 @@ class RecordPlayBackParse extends Emiter { @@ -670,7 +716,8 @@ class RecordPlayBackParse extends Emiter {
670 let sub_type = regUpdatedItem.subType; 716 let sub_type = regUpdatedItem.subType;
671 let object_id = regUpdatedItem.objId; 717 let object_id = regUpdatedItem.objId;
672 let user_data = regUpdatedItem.userData; 718 let user_data = regUpdatedItem.userData;
673 - //console.log('RCRegistryUpdateObjPdu',regUpdatedItem) 719 +
  720 + console.log('RCRegistryUpdateObjPdu',regUpdatedItem)
674 721
675 switch (sub_type) { 722 switch (sub_type) {
676 case pdu.RCPDU_REG_ROSTER_INSERT_PDU: 723 case pdu.RCPDU_REG_ROSTER_INSERT_PDU:
@@ -723,7 +770,7 @@ class RecordPlayBackParse extends Emiter { @@ -723,7 +770,7 @@ class RecordPlayBackParse extends Emiter {
723 loger.warn("视频控制消息处理,收到的消息为null,不做处理"); 770 loger.warn("视频控制消息处理,收到的消息为null,不做处理");
724 return; 771 return;
725 } 772 }
726 - videoReceivePdu.data = this._rCArrayBufferUtil.uint8ArrayToStr(videoReceivePdu.data, 2);//开头两个字会乱码 773 + videoReceivePdu.data = ArrayBufferUtil.uint8ArrayToStr(videoReceivePdu.data, 2);//开头两个字会乱码
727 let dataObj = {}; 774 let dataObj = {};
728 try { 775 try {
729 dataObj = JSON.parse(videoReceivePdu.data); 776 dataObj = JSON.parse(videoReceivePdu.data);
@@ -52,28 +52,6 @@ class SystemConfig { @@ -52,28 +52,6 @@ class SystemConfig {
52 } 52 }
53 53
54 //获取浏览器和信息 54 //获取浏览器和信息
55 - /* static getBrowserInfo() {  
56 - var Sys = {};  
57 - var ua = navigator.userAgent.toLowerCase();  
58 - var re = /(trident|msie|firefox|chrome|opera|version).*?([\d.]+)/;  
59 - var m = ua.match(re);  
60 - if (!m) m = ["version/1.0.0", "version", "1.0.0"];  
61 - Sys.explorer = m[1].replace(/version/, "'safari");  
62 - //判断是否是IE11  
63 - if (Sys.explorer == "trident") {  
64 - Sys.explorer = "IE11"  
65 - Sys.explorerVersion = "11.0";  
66 - } else if (Sys.explorer == "msie") {  
67 - //IE  
68 - Sys.explorer = "IE"  
69 - Sys.explorerVersion = m[2];  
70 - } else {  
71 - //非IE  
72 - Sys.explorerVersion = m[2];  
73 - }  
74 - return Sys;  
75 - }*/  
76 - //获取浏览器和信息  
77 static getBrowserInfo() { 55 static getBrowserInfo() {
78 let Sys = {}; 56 let Sys = {};
79 Sys.explorer = "unknow"; 57 Sys.explorer = "unknow";
@@ -83,7 +61,7 @@ class SystemConfig { @@ -83,7 +61,7 @@ class SystemConfig {
83 let re = /(trident|msie|firefox|chrome|version).*?([\d.]+)/; 61 let re = /(trident|msie|firefox|chrome|version).*?([\d.]+)/;
84 let m = ua.match(re); 62 let m = ua.match(re);
85 if (!m) m = ["version/1.0.0", "version", "1.0.0"]; 63 if (!m) m = ["version/1.0.0", "version", "1.0.0"];
86 - Sys.explorer = m[1].replace(/version/, "'safari"); 64 + Sys.explorer = m[1].replace(/version/, "safari");
87 //判断是否是IE11 65 //判断是否是IE11
88 if (Sys.explorer == "trident") { 66 if (Sys.explorer == "trident") {
89 Sys.explorer = "IE11" 67 Sys.explorer = "IE11"
@@ -7,6 +7,7 @@ import Loger from 'Loger'; @@ -7,6 +7,7 @@ import Loger from 'Loger';
7 import MessageTypes from 'MessageTypes'; 7 import MessageTypes from 'MessageTypes';
8 import GlobalConfig from 'GlobalConfig'; 8 import GlobalConfig from 'GlobalConfig';
9 import EngineUtils from 'EngineUtils'; 9 import EngineUtils from 'EngineUtils';
  10 +import MD5 from "md5";
10 11
11 let loger = Loger.getLoger('MediaModule'); 12 let loger = Loger.getLoger('MediaModule');
12 13
@@ -37,22 +38,36 @@ class MediaModule { @@ -37,22 +38,36 @@ class MediaModule {
37 if (!GlobalConfig.MS_PLAY_HLS_IP) { 38 if (!GlobalConfig.MS_PLAY_HLS_IP) {
38 loger.error("HLS拉流地址地址无效"); 39 loger.error("HLS拉流地址地址无效");
39 } 40 }
40 - //http://123.56.73.119:6001/live/h5dev_2106728010_8ab3b0ed5a3a9220015a3a958f0d0003_983041_1489113860/index.m3u8 41 + /* //http://123.56.73.119:6001/live/h5dev_2106728010_8ab3b0ed5a3a9220015a3a958f0d0003_983041_1489113860/index.m3u8
41 if (GlobalConfig.siteId == GlobalConfig.siteId_letv) { 42 if (GlobalConfig.siteId == GlobalConfig.siteId_letv) {
42 //乐视的hls地址规则 43 //乐视的hls地址规则
43 fileName = 'desc.m3u8'; 44 fileName = 'desc.m3u8';
44 } else { 45 } else {
45 //fileName='index.m3u8'; 46 //fileName='index.m3u8';
46 fileName = 'playlist.m3u8'; 47 fileName = 'playlist.m3u8';
47 - } 48 + }*/
48 port = (GlobalConfig.MS_PLAY_HLS_PORT == "" || GlobalConfig.MS_PLAY_HLS_PORT == null) ? "" : ":" + GlobalConfig.MS_PLAY_HLS_PORT; 49 port = (GlobalConfig.MS_PLAY_HLS_PORT == "" || GlobalConfig.MS_PLAY_HLS_PORT == null) ? "" : ":" + GlobalConfig.MS_PLAY_HLS_PORT;
49 //path = "http://" + GlobalConfig.MS_PLAY_HLS_IP 50 //path = "http://" + GlobalConfig.MS_PLAY_HLS_IP
  51 + let ipItem=GlobalConfig.getIpItem(GlobalConfig.MS_PLAY_HLS_IP,GlobalConfig.hlsPullListFinal);//获取IP对象
  52 + let type="live";//默认值
  53 + let hlsSuffix="/playlist.m3u8";
  54 + if(ipItem){
  55 + type=ipItem.type||"live";
  56 + hlsSuffix=ipItem.hlsSuffix||"/playlist.m3u8";
  57 + }
  58 +
50 path =GlobalConfig.locationProtocol + GlobalConfig.MS_PLAY_HLS_IP 59 path =GlobalConfig.locationProtocol + GlobalConfig.MS_PLAY_HLS_IP
  60 + + port + "/"+type+"/"
  61 + + _param.streamId
  62 + +hlsSuffix;//
  63 + //+ "/"+fileName;//
  64 +
  65 + /*path =GlobalConfig.locationProtocol + GlobalConfig.MS_PLAY_HLS_IP
51 + port + "/live/" 66 + port + "/live/"
52 + _param.streamId 67 + _param.streamId
53 + ".m3u8";// 68 + ".m3u8";//
54 //+ "/"+fileName;// 69 //+ "/"+fileName;//
55 - 70 + */
56 71
57 } else { 72 } else {
58 //RTMP 73 //RTMP
@@ -60,12 +75,20 @@ class MediaModule { @@ -60,12 +75,20 @@ class MediaModule {
60 loger.error("RTMP拉流地址地址无效"); 75 loger.error("RTMP拉流地址地址无效");
61 } 76 }
62 port = (GlobalConfig.MS_PLAY_RTMP_PORT == "" || GlobalConfig.MS_PLAY_RTMP_PORT == null) ? "" : ":" + GlobalConfig.MS_PLAY_RTMP_PORT; 77 port = (GlobalConfig.MS_PLAY_RTMP_PORT == "" || GlobalConfig.MS_PLAY_RTMP_PORT == null) ? "" : ":" + GlobalConfig.MS_PLAY_RTMP_PORT;
  78 +
  79 + let ipItem=GlobalConfig.getIpItem(GlobalConfig.MS_PLAY_RTMP_IP,GlobalConfig.rtmpPullListFinal);//获取IP对象
  80 + let type="live";//默认值
  81 + if(ipItem){
  82 + type=ipItem.type||"live";
  83 + }
63 path = "rtmp://" + GlobalConfig.MS_PLAY_RTMP_IP 84 path = "rtmp://" + GlobalConfig.MS_PLAY_RTMP_IP
64 - + port + "/live/" 85 + + port + "/"+type+"/"
65 + _param.streamId; 86 + _param.streamId;
66 - }  
67 - 87 + //path = "rtmp://" + GlobalConfig.MS_PLAY_RTMP_IP
  88 + // + port + "/live/"
  89 + // + _param.streamId;
68 90
  91 + }
69 path = path.replace("::", ":");//如果ip和port之间有多的:需要去掉 92 path = path.replace("::", ":");//如果ip和port之间有多的:需要去掉
70 return {"code": ApeConsts.RETURN_SUCCESS, "data": "", "playUrl": path}; 93 return {"code": ApeConsts.RETURN_SUCCESS, "data": "", "playUrl": path};
71 } 94 }
@@ -81,15 +104,41 @@ class MediaModule { @@ -81,15 +104,41 @@ class MediaModule {
81 //M3U8 http://123.56.73.119:6001/live/h5dev_2106728010_8ab3b0ed5a3a9220015a3a958f0d0003_983041_1489113860/total.m3u8 104 //M3U8 http://123.56.73.119:6001/live/h5dev_2106728010_8ab3b0ed5a3a9220015a3a958f0d0003_983041_1489113860/total.m3u8
82 let port = (GlobalConfig.RS_RECORD_PLAY_PORT == "" || GlobalConfig.RS_RECORD_PLAY_PORT == null) ? "" : ":" + GlobalConfig.RS_RECORD_PLAY_PORT; 105 let port = (GlobalConfig.RS_RECORD_PLAY_PORT == "" || GlobalConfig.RS_RECORD_PLAY_PORT == null) ? "" : ":" + GlobalConfig.RS_RECORD_PLAY_PORT;
83 //let path = "http://" + GlobalConfig.RS_RECORD_PLAY_IP 106 //let path = "http://" + GlobalConfig.RS_RECORD_PLAY_IP
  107 + let ipItem=GlobalConfig.getIpItem(GlobalConfig.RS_RECORD_PLAY_IP,GlobalConfig.rsPullListFinal);//获取IP对象
  108 + let type="live";//默认值
  109 + let rHlsSuffix="/total.m3u8";
  110 + if(ipItem){
  111 + type=ipItem.type||"live";
  112 + rHlsSuffix=ipItem.rHlsSuffix||"/total.m3u8";
  113 + }
  114 +
84 let path =GlobalConfig.locationProtocol + GlobalConfig.RS_RECORD_PLAY_IP 115 let path =GlobalConfig.locationProtocol + GlobalConfig.RS_RECORD_PLAY_IP
85 - + port + "/live/" 116 + + port + "/"+type+"/"
86 + _param.streamId 117 + _param.streamId
87 - + "/total.m3u8"; 118 + +rHlsSuffix;
88 119
89 path = path.replace("::", ":");//如果ip和port之间有多的:需要去掉 120 path = path.replace("::", ":");//如果ip和port之间有多的:需要去掉
90 return {"code": ApeConsts.RETURN_SUCCESS, "data": "", "playUrl": path}; 121 return {"code": ApeConsts.RETURN_SUCCESS, "data": "", "playUrl": path};
91 } 122 }
92 123
  124 + //推流地址后缀拼接参数
  125 + setPublishSuffix(url,publishSuffix,streamId){
  126 + let newUrl= url;
  127 + if(!url||!publishSuffix){
  128 + return newUrl;
  129 + }
  130 + //publishSuffix就是外部传入的key
  131 + //txSecret = MD5(key + stream+txTime)
  132 + let txTime=new Date().getTime()/1000+(12*60*60);
  133 + //过去时间精确到分钟,转为16进制
  134 + txTime=parseInt(txTime);
  135 + txTime=txTime.toString(16);
  136 + let txSecret= MD5(publishSuffix + streamId+txTime);
  137 + //rtmp://11220.livepush.myqcloud.com/live/11220_c5a1ea0bce?bizid=11220&txSecret=b1d8af72bf62366eef31cbb5dc5c8778&txTime=59C5337F
  138 + newUrl=url +"?bizid=11220&txSecret="+txSecret+"&txTime="+txTime+"&record=hls&record_interval=5400";
  139 + loger.log("生成的推流地址->"+newUrl);
  140 + return newUrl;
  141 + }
93 //获取推流地址 142 //获取推流地址
94 getMediaPublishPath(_param) { 143 getMediaPublishPath(_param) {
95 loger.log('获取推流地址->'); 144 loger.log('获取推流地址->');
@@ -116,8 +165,21 @@ class MediaModule { @@ -116,8 +165,21 @@ class MediaModule {
116 + "_" + freeChannel + "_" + timestamp; 165 + "_" + freeChannel + "_" + timestamp;
117 166
118 //生成推流地址和推流数据(同步数据的时候用) 167 //生成推流地址和推流数据(同步数据的时候用)
  168 + //let publishUrl = "rtmp://" + GlobalConfig.MS_PUBLISH_IP
  169 + // + port + "/" + pubType + "/" + streamId;
  170 +
  171 + let ipItem=GlobalConfig.getIpItem(GlobalConfig.MS_PUBLISH_IP,GlobalConfig.msListFinal);//获取IP对象
  172 + let type="live";//默认值
  173 + if(ipItem){
  174 + type=ipItem.type||"live";
  175 + }
119 let publishUrl = "rtmp://" + GlobalConfig.MS_PUBLISH_IP 176 let publishUrl = "rtmp://" + GlobalConfig.MS_PUBLISH_IP
120 - + port + "/" + pubType + "/" + streamId; 177 + + port + "/"+ type + "/" + streamId;
  178 +
  179 + //设置推流地址的后缀,有的推流地址需要在地址后面加一些参数
  180 + if(ipItem&&ipItem.publishSuffix){
  181 + publishUrl=this.setPublishSuffix(publishUrl,ipItem.publishSuffix,streamId);
  182 + }
121 183
122 publishUrl = publishUrl.replace("::", ":");//如果ip和port之间有多的:需要去掉 184 publishUrl = publishUrl.replace("::", ":");//如果ip和port之间有多的:需要去掉
123 this.needPublishMediaChannel[publishUrl] = { 185 this.needPublishMediaChannel[publishUrl] = {
@@ -169,9 +231,21 @@ class MediaModule { @@ -169,9 +231,21 @@ class MediaModule {
169 + "_" + freeChannel + "_" + timestamp; 231 + "_" + freeChannel + "_" + timestamp;
170 232
171 //生成推流地址和推流数据(同步数据的时候用) 233 //生成推流地址和推流数据(同步数据的时候用)
  234 + //let publishUrl = "rtmp://" + GlobalConfig.MS_PUBLISH_IP
  235 + // + port + "/" + pubType + "/" + streamId;
  236 +
  237 + let ipItem=GlobalConfig.getIpItem(GlobalConfig.MS_PUBLISH_IP,GlobalConfig.msListFinal);//获取IP对象
  238 + let type="live";//默认值
  239 + if(ipItem){
  240 + type=ipItem.type||"live";
  241 + }
172 let publishUrl = "rtmp://" + GlobalConfig.MS_PUBLISH_IP 242 let publishUrl = "rtmp://" + GlobalConfig.MS_PUBLISH_IP
173 - + port + "/" + pubType + "/" + streamId; 243 + + port + "/"+ type + "/" + streamId;
174 244
  245 + //设置推流地址的后缀,有的推流地址需要在地址后面加一些参数
  246 + if(ipItem&&ipItem.publishSuffix){
  247 + publishUrl=this.setPublishSuffix(publishUrl,ipItem.publishSuffix,streamId);
  248 + }
175 publishUrl = publishUrl.replace("::", ":");//如果ip和port之间有多的:需要去掉 249 publishUrl = publishUrl.replace("::", ":");//如果ip和port之间有多的:需要去掉
176 this.needPublishMediaChannel[publishUrl] = { 250 this.needPublishMediaChannel[publishUrl] = {
177 "channelId": freeChannel, 251 "channelId": freeChannel,
@@ -213,8 +287,20 @@ class MediaModule { @@ -213,8 +287,20 @@ class MediaModule {
213 + "_" + shareChannel + "_" + timestamp; 287 + "_" + shareChannel + "_" + timestamp;
214 288
215 //生成推流地址和推流数据(同步数据的时候用) 289 //生成推流地址和推流数据(同步数据的时候用)
  290 + //let publishUrl = "rtmp://" + GlobalConfig.MS_PUBLISH_IP
  291 + // + port + "/" + pubType + "/" + streamId;
  292 + let ipItem=GlobalConfig.getIpItem(GlobalConfig.MS_PUBLISH_IP,GlobalConfig.msListFinal);//获取IP对象
  293 + let type="live";//默认值
  294 + if(ipItem){
  295 + type=ipItem.type||"live";
  296 + }
216 let publishUrl = "rtmp://" + GlobalConfig.MS_PUBLISH_IP 297 let publishUrl = "rtmp://" + GlobalConfig.MS_PUBLISH_IP
217 - + port + "/" + pubType + "/" + streamId; 298 + + port + "/"+ type + "/" + streamId;
  299 +
  300 + //设置推流地址的后缀,有的推流地址需要在地址后面加一些参数
  301 + if(ipItem&&ipItem.publishSuffix){
  302 + publishUrl=this.setPublishSuffix(publishUrl,ipItem.publishSuffix,streamId);
  303 + }
218 304
219 publishUrl = publishUrl.replace("::", ":");//如果ip和port之间有多的:需要去掉 305 publishUrl = publishUrl.replace("::", ":");//如果ip和port之间有多的:需要去掉
220 /* this.needPublishMediaChannel[publishUrl]={ 306 /* this.needPublishMediaChannel[publishUrl]={