正在显示
6 个修改的文件
包含
88 行增加
和
24 行删除
| @@ -61,8 +61,9 @@ let _base64; | @@ -61,8 +61,9 @@ let _base64; | ||
| 61 | export default class MessageEntrance extends Emiter { | 61 | export default class MessageEntrance extends Emiter { |
| 62 | constructor() { | 62 | constructor() { |
| 63 | super(); | 63 | super(); |
| 64 | + this.lastClassActiveTime=0;//最后一次课堂激活的时间戳 | ||
| 64 | //sdk 信息 | 65 | //sdk 信息 |
| 65 | - GlobalConfig.sdkVersion = "v2.22.9.20171027"; | 66 | + GlobalConfig.sdkVersion = "v2.23.0.20171030"; |
| 66 | loger.warn("sdkVersion:" + GlobalConfig.sdkVersion); | 67 | loger.warn("sdkVersion:" + GlobalConfig.sdkVersion); |
| 67 | console.log("sdkVersion:" + GlobalConfig.sdkVersion); | 68 | console.log("sdkVersion:" + GlobalConfig.sdkVersion); |
| 68 | //设置 | 69 | //设置 |
| @@ -1240,8 +1241,8 @@ export default class MessageEntrance extends Emiter { | @@ -1240,8 +1241,8 @@ export default class MessageEntrance extends Emiter { | ||
| 1240 | 1241 | ||
| 1241 | //主讲人和老师可以设置旁录 | 1242 | //主讲人和老师可以设置旁录 |
| 1242 | if (GlobalConfig.appId && !GlobalConfig.openFlash) { | 1243 | if (GlobalConfig.appId && !GlobalConfig.openFlash) { |
| 1243 | - //加入之前先设置旁录地址,只有直播支持旁路(1路流) | ||
| 1244 | - if (_webRtc && GlobalConfig.isTeachOrAssistant && GlobalConfig.maxMediaChannels == 1) { | 1244 | + //加入之前先设置旁录地址,老师和主讲人开启旁路 |
| 1245 | + if (_webRtc && GlobalConfig.isTeachOrAssistant) { | ||
| 1245 | let curTimestamp = new Date().getTime(); | 1246 | let curTimestamp = new Date().getTime(); |
| 1246 | let streamId = GlobalConfig.siteId + "_" + GlobalConfig.classId + "_" + GlobalConfig.userId + "_" + curTimestamp; | 1247 | let streamId = GlobalConfig.siteId + "_" + GlobalConfig.classId + "_" + GlobalConfig.userId + "_" + curTimestamp; |
| 1247 | //传入固定的流Id | 1248 | //传入固定的流Id |
| @@ -1870,6 +1871,7 @@ export default class MessageEntrance extends Emiter { | @@ -1870,6 +1871,7 @@ export default class MessageEntrance extends Emiter { | ||
| 1870 | let dataObj = JSON.parse(_data.currentInfo); | 1871 | let dataObj = JSON.parse(_data.currentInfo); |
| 1871 | dataObj.recordStatus = false; | 1872 | dataObj.recordStatus = false; |
| 1872 | GlobalConfig.setClassStatusInfo(dataObj); | 1873 | GlobalConfig.setClassStatusInfo(dataObj); |
| 1874 | + this.lastClassActiveTime=dataObj.lastClassActiveTime||0; | ||
| 1873 | } catch (err) { | 1875 | } catch (err) { |
| 1874 | loger.warn("从Sass获取的课堂数据JSON转换失败->"); | 1876 | loger.warn("从Sass获取的课堂数据JSON转换失败->"); |
| 1875 | GlobalConfig.setClassStatusInfo(_data.currentInfo); | 1877 | GlobalConfig.setClassStatusInfo(_data.currentInfo); |
| @@ -1919,6 +1921,7 @@ export default class MessageEntrance extends Emiter { | @@ -1919,6 +1921,7 @@ export default class MessageEntrance extends Emiter { | ||
| 1919 | let dataObj = JSON.parse(_currentInfo); | 1921 | let dataObj = JSON.parse(_currentInfo); |
| 1920 | dataObj.recordStatus = false; | 1922 | dataObj.recordStatus = false; |
| 1921 | GlobalConfig.setClassStatusInfo(dataObj); | 1923 | GlobalConfig.setClassStatusInfo(dataObj); |
| 1924 | + this.lastClassActiveTime=dataObj.lastClassActiveTime||0; | ||
| 1922 | } catch (err) { | 1925 | } catch (err) { |
| 1923 | loger.warn("getClassRecordInfo获取的课堂数据JSON转换失败->"); | 1926 | loger.warn("getClassRecordInfo获取的课堂数据JSON转换失败->"); |
| 1924 | } | 1927 | } |
| @@ -2505,8 +2508,15 @@ export default class MessageEntrance extends Emiter { | @@ -2505,8 +2508,15 @@ export default class MessageEntrance extends Emiter { | ||
| 2505 | 2508 | ||
| 2506 | //文档加入频道成功,同步到MCU服务器上的数据 | 2509 | //文档加入频道成功,同步到MCU服务器上的数据 |
| 2507 | docJoinChannelSuccess() { | 2510 | docJoinChannelSuccess() { |
| 2508 | - loger.log("最后一次记录的时间->"+GlobalConfig.classStopTime,"当前时间:"+EngineUtils.creatTimestamp()); | 2511 | + let distance=new Date().getTime()-parseInt(this.lastClassActiveTime); |
| 2512 | + loger.log("最后一次记录的时间->"+this.lastClassActiveTime,"当前时间:"+new Date().getTime(),"间隔:"+distance/1000); | ||
| 2509 | loger.log("文档加入频道成功->isHost=", GlobalConfig.isHost, "当前总人数:", GlobalConfig.rosterNumber, "sassDoclength=", GlobalConfig.docListPrepare.length); | 2513 | loger.log("文档加入频道成功->isHost=", GlobalConfig.isHost, "当前总人数:", GlobalConfig.rosterNumber, "sassDoclength=", GlobalConfig.docListPrepare.length); |
| 2514 | + | ||
| 2515 | + //如果当前课堂内只有自己或者离开上次课堂的时间大于15分钟,需要停止服务端的视频录制 | ||
| 2516 | + if(GlobalConfig.rosterNumber<=1||distance>=(15*60)){ | ||
| 2517 | + this._mediaRecordControl({"status": WebRtcApe.RECORD_STATUS_2}); | ||
| 2518 | + } | ||
| 2519 | + | ||
| 2510 | //如果是主持人,那么需要判断一下文档模块同步的数据和从sass获取的文档数据是否相同,如果mcu服务器不存在的,需要上传 | 2520 | //如果是主持人,那么需要判断一下文档模块同步的数据和从sass获取的文档数据是否相同,如果mcu服务器不存在的,需要上传 |
| 2511 | if (GlobalConfig.docListPrepare && GlobalConfig.docListPrepare.length > 0) { | 2521 | if (GlobalConfig.docListPrepare && GlobalConfig.docListPrepare.length > 0) { |
| 2512 | //如果当前身份是老师或者当前课堂内只有一个人,有权限同步文档到MCU | 2522 | //如果当前身份是老师或者当前课堂内只有一个人,有权限同步文档到MCU |
| @@ -3282,7 +3292,8 @@ export default class MessageEntrance extends Emiter { | @@ -3282,7 +3292,8 @@ export default class MessageEntrance extends Emiter { | ||
| 3282 | break; | 3292 | break; |
| 3283 | case WebRtcApe.RECORD_STATUS_2: | 3293 | case WebRtcApe.RECORD_STATUS_2: |
| 3284 | //停止录制 | 3294 | //停止录制 |
| 3285 | - _webRtc.stopRecordingMedia(); | 3295 | + //_webRtc.stopRecordingMedia(); |
| 3296 | + _webRtc.changePublishStatusAndServerRecord(WebRtcApe.RECORD_STATUS_2); | ||
| 3286 | break; | 3297 | break; |
| 3287 | default : | 3298 | default : |
| 3288 | break; | 3299 | break; |
| @@ -51,7 +51,15 @@ class EngineUtils{ | @@ -51,7 +51,15 @@ class EngineUtils{ | ||
| 51 | timeStr+=curTime.getSeconds(); | 51 | timeStr+=curTime.getSeconds(); |
| 52 | return timeStr; | 52 | return timeStr; |
| 53 | } | 53 | } |
| 54 | - | 54 | + //根据时间戳字符串转换为时间戳 2017-10-27-15-38-15 |
| 55 | + static getTimestampFromStr(_timeStr){ | ||
| 56 | + if(!_timeStr) return this.creatTimestamp(); | ||
| 57 | + let timeArr=_timeStr.split("_"); | ||
| 58 | + if(timeArr&&timeArr.length==6){ | ||
| 59 | + return parseInt( new Date(timeArr[0],parseInt(timeArr[1])-1,timeArr[2],timeArr[3],timeArr[4],timeArr[5]).getTime()/1000); | ||
| 60 | + } | ||
| 61 | + return this.creatTimestamp(); | ||
| 62 | + } | ||
| 55 | //生成时间戳 格式:"20170209" | 63 | //生成时间戳 格式:"20170209" |
| 56 | static creatTimestampYMD(){ | 64 | static creatTimestampYMD(){ |
| 57 | let curTime = new Date(); | 65 | let curTime = new Date(); |
| @@ -330,13 +330,13 @@ class RecordPlayBackParse extends Emiter { | @@ -330,13 +330,13 @@ class RecordPlayBackParse extends Emiter { | ||
| 330 | } | 330 | } |
| 331 | //时间戳转换为UTC 时间字符串 | 331 | //时间戳转换为UTC 时间字符串 |
| 332 | timestampToUTCTime(_timestamp){ | 332 | timestampToUTCTime(_timestamp){ |
| 333 | - var date=new Date(_timestamp); | ||
| 334 | - var y=""+date.getFullYear(); | ||
| 335 | - var month=""+(date.getMonth()+1); | ||
| 336 | - var d=""+date.getDate(); | ||
| 337 | - var h=""+(date.getHours()-8);//GMT 转UTC 减8 | ||
| 338 | - var minutes=""+date.getMinutes(); | ||
| 339 | - var s=""+date.getSeconds(); | 333 | + let date=new Date(_timestamp); |
| 334 | + let y=""+date.getFullYear(); | ||
| 335 | + let month=""+(date.getMonth()+1); | ||
| 336 | + let d=""+date.getDate(); | ||
| 337 | + let h=""+(date.getHours()-8);//GMT 转UTC 减8 | ||
| 338 | + let minutes=""+date.getMinutes(); | ||
| 339 | + let s=""+date.getSeconds(); | ||
| 340 | if (month.length<2){ | 340 | if (month.length<2){ |
| 341 | month="0"+month; | 341 | month="0"+month; |
| 342 | } | 342 | } |
| @@ -352,7 +352,7 @@ class RecordPlayBackParse extends Emiter { | @@ -352,7 +352,7 @@ class RecordPlayBackParse extends Emiter { | ||
| 352 | if (s.length<2){ | 352 | if (s.length<2){ |
| 353 | s="0"+s; | 353 | s="0"+s; |
| 354 | } | 354 | } |
| 355 | - var tiemStr=y+month+d+h+minutes+s; | 355 | + let tiemStr=y+month+d+h+minutes+s; |
| 356 | //console.log(_timestamp,tiemStr,date); | 356 | //console.log(_timestamp,tiemStr,date); |
| 357 | return tiemStr | 357 | return tiemStr |
| 358 | } | 358 | } |
| @@ -362,7 +362,13 @@ class RecordPlayBackParse extends Emiter { | @@ -362,7 +362,13 @@ class RecordPlayBackParse extends Emiter { | ||
| 362 | console.log("没有数据无法解析匹配",_tiemstampMessages,_allMedias) | 362 | console.log("没有数据无法解析匹配",_tiemstampMessages,_allMedias) |
| 363 | return; | 363 | return; |
| 364 | } | 364 | } |
| 365 | + if(Object.keys(_tiemstampMessages).length<1||Object.keys(_allMedias).length<1){ | ||
| 366 | + console.log("数据没有加载完成->不做匹配"); | ||
| 367 | + return; | ||
| 368 | + }; | ||
| 369 | + | ||
| 365 | let finelMediaInfo={}; | 370 | let finelMediaInfo={}; |
| 371 | + let unknowFiles={};//记录没有匹配到的,需要再次匹配 | ||
| 366 | let tItem; | 372 | let tItem; |
| 367 | for(let j in _tiemstampMessages){ | 373 | for(let j in _tiemstampMessages){ |
| 368 | tItem=_tiemstampMessages[j]; | 374 | tItem=_tiemstampMessages[j]; |
| @@ -377,13 +383,41 @@ class RecordPlayBackParse extends Emiter { | @@ -377,13 +383,41 @@ class RecordPlayBackParse extends Emiter { | ||
| 377 | if(!videoUrl){ | 383 | if(!videoUrl){ |
| 378 | videoUrl=_allMedias[tItem.uid+"_"+(t+1)]; | 384 | videoUrl=_allMedias[tItem.uid+"_"+(t+1)]; |
| 379 | } | 385 | } |
| 386 | + if(!videoUrl){ | ||
| 387 | + unknowFiles[tItem.timestamp]=tItem; | ||
| 388 | + } | ||
| 380 | } | 389 | } |
| 381 | //console.log(tItem.uid+"_"+t+" "+videoUrl); | 390 | //console.log(tItem.uid+"_"+t+" "+videoUrl); |
| 382 | tItem.video_url=videoUrl; | 391 | tItem.video_url=videoUrl; |
| 383 | finelMediaInfo[tItem.timestamp]=tItem; | 392 | finelMediaInfo[tItem.timestamp]=tItem; |
| 384 | - MediaModule.streams[tItem.stream_id]=tItem.video_url; | 393 | + // MediaModule.streams[tItem.stream_id]=tItem.video_url; |
| 394 | + MediaModule.streams[tItem.stream_id]={video_url:tItem.video_url,seek:0}; | ||
| 385 | } | 395 | } |
| 386 | console.log("finelMediaInfo",finelMediaInfo); | 396 | console.log("finelMediaInfo",finelMediaInfo); |
| 397 | + | ||
| 398 | + //最后处理没有匹配到的 | ||
| 399 | + //检测没有匹配到数据的消息 | ||
| 400 | + for(let i in unknowFiles){ | ||
| 401 | + let noItem=unknowFiles[i]; | ||
| 402 | + for(let k in finelMediaInfo){ | ||
| 403 | + let okItem=finelMediaInfo[k]; | ||
| 404 | + let seek=(parseInt(noItem.timestamp)-parseInt(okItem.timestamp))/1000; | ||
| 405 | + console.log("seek",seek); | ||
| 406 | + if(noItem.uid==okItem.uid&&seek<15&&noItem.timestamp!=okItem.timestamp){ | ||
| 407 | + console.log(noItem,okItem); | ||
| 408 | + noItem.video_url=okItem.video_url; | ||
| 409 | + noItem.seek=seek; | ||
| 410 | + finelMediaInfo [noItem.timestamp]=noItem; | ||
| 411 | + //MediaModule.streams[noItem.stream_id]=noItem.video_url; | ||
| 412 | + console.log("根据时间戳查找流",noItem.stream_id,{video_url:noItem.video_url,seek:seek}) | ||
| 413 | + MediaModule.streams[noItem.stream_id]={video_url:noItem.video_url,seek:seek}; | ||
| 414 | + } | ||
| 415 | + } | ||
| 416 | + } | ||
| 417 | + console.log("服务端所有m3u8文件",_allMedias); | ||
| 418 | + console.log("所有推流消息",_tiemstampMessages); | ||
| 419 | + console.log("最终匹配结束的文件",finelMediaInfo); | ||
| 420 | + console.log("回放中最终使用的视频流数据",MediaModule.streams); | ||
| 387 | } | 421 | } |
| 388 | 422 | ||
| 389 | //获取媒体录制的地址信息-时间戳流名称和文件地址对应 | 423 | //获取媒体录制的地址信息-时间戳流名称和文件地址对应 |
| @@ -647,7 +681,8 @@ class RecordPlayBackParse extends Emiter { | @@ -647,7 +681,8 @@ class RecordPlayBackParse extends Emiter { | ||
| 647 | } | 681 | } |
| 648 | if(itemJson&&itemJson.video_url){ | 682 | if(itemJson&&itemJson.video_url){ |
| 649 | if(itemJson.video_url.indexOf(".m3u8")>0){ | 683 | if(itemJson.video_url.indexOf(".m3u8")>0){ |
| 650 | - MediaModule.streams[itemJson.stream_id]=itemJson.video_url; | 684 | + //MediaModule.streams[itemJson.stream_id]=itemJson.video_url; |
| 685 | + MediaModule.streams[itemJson.stream_id]={video_url:itemJson.video_url,seek:0}; | ||
| 651 | } | 686 | } |
| 652 | } | 687 | } |
| 653 | } | 688 | } |
| @@ -433,9 +433,13 @@ class Sass extends Emiter { | @@ -433,9 +433,13 @@ class Sass extends Emiter { | ||
| 433 | loger.log('录制回放中,不需要保存课堂信息'); | 433 | loger.log('录制回放中,不需要保存课堂信息'); |
| 434 | return; | 434 | return; |
| 435 | } | 435 | } |
| 436 | + if(!_param||!_param.classStatusInfo){ | ||
| 437 | + return; | ||
| 438 | + } | ||
| 436 | //{"classStatusInfo":classStatusInfo} | 439 | //{"classStatusInfo":classStatusInfo} |
| 437 | let timestamp = new Date().getTime(); | 440 | let timestamp = new Date().getTime(); |
| 438 | let authId = MD5(GlobalConfig.classId + "" + timestamp); // (classId+timestamp)的字符串,转成MD5 | 441 | let authId = MD5(GlobalConfig.classId + "" + timestamp); // (classId+timestamp)的字符串,转成MD5 |
| 442 | + _param.classStatusInfo.lastClassActiveTime=timestamp; | ||
| 439 | let classStatusInfo = JSON.stringify(_param.classStatusInfo); | 443 | let classStatusInfo = JSON.stringify(_param.classStatusInfo); |
| 440 | //let url = `http://${GlobalConfig.portal}/3m/api/meeting/saveInfo.do`; | 444 | //let url = `http://${GlobalConfig.portal}/3m/api/meeting/saveInfo.do`; |
| 441 | let url = `${GlobalConfig.locationProtocol+GlobalConfig.portal}/3m/api/meeting/saveInfo.do`; | 445 | let url = `${GlobalConfig.locationProtocol+GlobalConfig.portal}/3m/api/meeting/saveInfo.do`; |
| @@ -103,15 +103,20 @@ class MediaModule { | @@ -103,15 +103,20 @@ class MediaModule { | ||
| 103 | } | 103 | } |
| 104 | 104 | ||
| 105 | //如果是外部的流地址,不需要拼接,直接使用就可以,是完整的地址 | 105 | //如果是外部的流地址,不需要拼接,直接使用就可以,是完整的地址 |
| 106 | - let streamPlayUrl=MediaModule.streams[_param.streamId]; | ||
| 107 | - if(streamPlayUrl){ | ||
| 108 | - loger.log("使用外部的流地址->",streamPlayUrl); | ||
| 109 | - streamPlayUrl=streamPlayUrl.replace("http://",""); | ||
| 110 | - streamPlayUrl=streamPlayUrl.replace("https://",""); | ||
| 111 | - streamPlayUrl=GlobalConfig.locationProtocol+streamPlayUrl; | ||
| 112 | - return {"code": ApeConsts.RETURN_SUCCESS, "data": "", "playUrl":streamPlayUrl}; | 106 | + let streamItem=MediaModule.streams[_param.streamId]; |
| 107 | + if(streamItem){ | ||
| 108 | + let streamPlayUrl=streamItem.video_url||""; | ||
| 109 | + let seek=parseInt(streamItem.seek)||0; | ||
| 110 | + if(streamPlayUrl){ | ||
| 111 | + loger.log("使用外部的流地址->",streamPlayUrl); | ||
| 112 | + streamPlayUrl=streamPlayUrl.replace("http://",""); | ||
| 113 | + streamPlayUrl=streamPlayUrl.replace("https://",""); | ||
| 114 | + streamPlayUrl=GlobalConfig.locationProtocol+streamPlayUrl; | ||
| 115 | + return {"code": ApeConsts.RETURN_SUCCESS, "data": "", "playUrl":streamPlayUrl,"seek":seek}; | ||
| 116 | + } | ||
| 113 | } | 117 | } |
| 114 | 118 | ||
| 119 | + | ||
| 115 | //M3U8 http://123.56.73.119:6001/live/h5dev_2106728010_8ab3b0ed5a3a9220015a3a958f0d0003_983041_1489113860/total.m3u8 | 120 | //M3U8 http://123.56.73.119:6001/live/h5dev_2106728010_8ab3b0ed5a3a9220015a3a958f0d0003_983041_1489113860/total.m3u8 |
| 116 | let port = (GlobalConfig.RS_RECORD_PLAY_PORT == "" || GlobalConfig.RS_RECORD_PLAY_PORT == null) ? "" : ":" + GlobalConfig.RS_RECORD_PLAY_PORT; | 121 | let port = (GlobalConfig.RS_RECORD_PLAY_PORT == "" || GlobalConfig.RS_RECORD_PLAY_PORT == null) ? "" : ":" + GlobalConfig.RS_RECORD_PLAY_PORT; |
| 117 | //let path = "http://" + GlobalConfig.RS_RECORD_PLAY_IP | 122 | //let path = "http://" + GlobalConfig.RS_RECORD_PLAY_IP |
| @@ -129,7 +134,7 @@ class MediaModule { | @@ -129,7 +134,7 @@ class MediaModule { | ||
| 129 | +rHlsSuffix; | 134 | +rHlsSuffix; |
| 130 | 135 | ||
| 131 | path = path.replace("::", ":");//如果ip和port之间有多的:需要去掉 | 136 | path = path.replace("::", ":");//如果ip和port之间有多的:需要去掉 |
| 132 | - return {"code": ApeConsts.RETURN_SUCCESS, "data": "", "playUrl": path}; | 137 | + return {"code": ApeConsts.RETURN_SUCCESS, "data": "", "playUrl": path,"seek":0}; |
| 133 | } | 138 | } |
| 134 | 139 | ||
| 135 | //推流地址后缀拼接参数 | 140 | //推流地址后缀拼接参数 |
| @@ -697,6 +697,7 @@ class VideoApe extends Ape { | @@ -697,6 +697,7 @@ class VideoApe extends Ape { | ||
| 697 | } | 697 | } |
| 698 | if (replay.code == 0) { | 698 | if (replay.code == 0) { |
| 699 | receiveChannelInfo.replay = replay.playUrl; | 699 | receiveChannelInfo.replay = replay.playUrl; |
| 700 | + receiveChannelInfo.seek=receiveChannelInfo.seek+replay.seek; | ||
| 700 | } | 701 | } |
| 701 | 702 | ||
| 702 | if (unpackChannelInfo.mediaType != ApeConsts.MEDIA_TYPE_SHARE&& | 703 | if (unpackChannelInfo.mediaType != ApeConsts.MEDIA_TYPE_SHARE&& |
-
请 注册 或 登录 后发表评论