李勇

1.增加控制礼物发送的功能;2.修复录制回放rec文件中消息时间戳重复叠加的问题;修复课堂中音视频频道被占用导致无法推流回放时缺少视频的问题

此 diff 太大无法显示。
... ... @@ -63,7 +63,7 @@ export default class MessageEntrance extends Emiter {
super();
this.lastClassActiveTime=0;//最后一次课堂激活的时间戳
//sdk 信息
GlobalConfig.sdkVersion = "v2.36.11.20171204";
GlobalConfig.sdkVersion = "v2.36.8.20171206";
loger.warn("sdkVersion:" + GlobalConfig.sdkVersion);
console.log("sdkVersion:" + GlobalConfig.sdkVersion);
//设置
... ... @@ -213,6 +213,8 @@ export default class MessageEntrance extends Emiter {
this.kickOutRosterFormNodeId = this._kickOutRosterFormNodeId.bind(this); //把指定nodeId的人踢出课堂
this.sendThridChannelMessage = this._sendThridChannelMessage.bind(this);//通道第三方消息通道发送消息
this.changeDrawStatus = this._changeDrawStatus.bind(this);//切换绘制状态
this.changeGiftStatus = this._changeGiftStatus.bind(this);//切换送礼物状态
//录制回放
this.initRecordPlayback = this._initRecordPlayback.bind(this);
... ... @@ -735,7 +737,7 @@ export default class MessageEntrance extends Emiter {
GlobalConfig.md5 = _data.md5 || ""; //这个暂时用假数据,后台接口写完就有数据了
GlobalConfig.msType = _data.msType || 1;
GlobalConfig.siteId = _data.siteId || "";
GlobalConfig.classType = _data.meetingType || ApeConsts.CLASS_TYPE_1v1;
GlobalConfig.classType = _data.meetingType|| ApeConsts.CLASS_TYPE_1v1;
//host默认需要密码,Sass服务器只判断学生是否需要密码,没有判断老师的
GlobalConfig.passwordRequired = _data.passwordRequired || false; //md5验证的时候需要Sass返回的值,不能更改
... ... @@ -1654,12 +1656,22 @@ export default class MessageEntrance extends Emiter {
loger.warn(GlobalConfig.getCurrentStatus());
return;
}
if (_confer_ape) {
_confer_ape.changeDrawStatus(_param);
}
}
//控制课堂全局是否可送礼物的状态
_changeGiftStatus(_param) {
if (!_mcu.connected) {
loger.warn(GlobalConfig.getCurrentStatus());
return;
}
if (_confer_ape) {
_confer_ape.changeGiftStatus(_param);
}
}
//暂停上课
_sendPauseClass(_param) {
if (!_mcu.connected) {
... ...
... ... @@ -91,6 +91,7 @@ class GlobalConfig {
classStatusInfo.classBeginTime = this.classBeginTime; //课堂创建的时间,这个是Sass返回的
classStatusInfo.classEndTime = this.classEndTime; //课堂结束的时间,这个是Sass返回的
classStatusInfo.isEnableGift = this.isEnableGift;
classStatusInfo.isEnableDraw = this.isEnableDraw; //当前是否开启绘制权限
classStatusInfo.recordStatus = this.recordStatus; //当前录制状态
classStatusInfo.silence = this.silence; //当前课堂禁言状态
... ... @@ -130,8 +131,8 @@ class GlobalConfig {
this.className = data.className;
}
if (!this.classType) {
this.classType = data.classType;
if (data.classType) {
this.classType = parseInt(data.classType);
}
if (!this.classBeginTime) {
this.classBeginTime = data.classBeginTime; //课堂创建的时间,这个是Sass返回的
... ... @@ -155,6 +156,7 @@ class GlobalConfig {
this.currentSceneTableId = data.currentSceneTableId || 0; //文档区域的模块显示
this.isEnableDraw = data.isEnableDraw || false;//是否开启所有人的绘制权限
this.isEnableGift = data.isEnableGift || false;
this.videoScale = parseInt(data.videoScale) || 1;
// 全局禁言状态
this.silence = data.silence || false;
... ... @@ -634,6 +636,7 @@ GlobalConfig.classJoinSuccess = false; //是否已经加入课堂
GlobalConfig.recordPlaybackMaxTime = 0; //录制回放的总时间
GlobalConfig.recordStatus = false; //当前录制状态
GlobalConfig.isEnableDraw = false; //当前所有人是否开启绘制状态(老师除外)
GlobalConfig.isEnableGift = false; //当前所有人是否开启送礼物状态
GlobalConfig.silence = false; //当前课堂禁言状态
GlobalConfig.silenceUsers = {}; //当前课堂用户禁言状态
GlobalConfig.selfSilence = {}; //当前课堂禁言状态
... ...
... ... @@ -108,12 +108,14 @@ MessageTypes.WEB_RTC_INIT_SUCCESS = "web_rtc_init_success";
MessageTypes.WEB_RTC_INIT_FAILED = "web_rtc_init_failed";
MessageTypes.WEB_RTC_JOIN_SUCCESS = "web_rtc_join_success";
MessageTypes.WEB_RTC_JOIN_FAILED = "web_rtc_join_failed";
MessageTypes.WEB_RTC_LEAVE_CHANNEL = "web_rtc_leave_channel";//离开频道
MessageTypes.WEB_RTC_PUBLISH_FAILED = "web_rtc_publish_failed";
MessageTypes.GET_DEVICES_SUCCESS = "get_devices_success";
MessageTypes.MEDIA_PUBLISH_STATUS_CHANGE = "media_publish_status_change";//音视频推流的状态发生改变
MessageTypes.MEDIA_ENABLED_CHANGE = "media_enabled_change";//媒体禁用或开启状态改变
MessageTypes.RECEIVE_MEDIA_ENABLED_CHANGE = "receive_media_enabled_change";//收到媒体禁用状态控制的消息
//MCU MS
MessageTypes.SWITCH_MCU_IP = "switch_mcu_ip"; //切换mcu 重新选点
MessageTypes.SWITCH_MS_IP = "switch_ms_ip"; //切换ms 重新选点
... ...
... ... @@ -25,22 +25,22 @@ class RecordInfoMatch extends Emiter {
}
start(_recordMessagesList) {
this.recordMessagesList=_recordMessagesList;
this.recordMessagesList = _recordMessagesList;
let serverIp = GlobalConfig.recordFileSever || "playbak.xuedianyun.com:8080";
this.filePath = `${GlobalConfig.locationProtocol + serverIp}`;
if(GlobalConfig.locationProtocol=="https://"){
this.filePath=this.filePath.replace(":8080","");
if (GlobalConfig.locationProtocol == "https://") {
this.filePath = this.filePath.replace(":8080", "");
}
if (!_recordMessagesList||_recordMessagesList.length<1) {
if (!_recordMessagesList || _recordMessagesList.length < 1) {
console.log("没有视频消息", _recordMessagesList);
this.matchRecordFile(this.recordMessages, this.m3u8jsonDataList);
}
//this.getRecordInfo(()=> {
this.parseRecordMessage(_recordMessagesList,()=> {
console.log("JSON数据列表",this.jsonPathList);
this.parseRecordMessage(_recordMessagesList, ()=> {
console.log("JSON数据列表", this.jsonPathList);
let requestLen = Object.keys(this.jsonPathList).length;
let currentLen = 0;
let fileNameArr;
... ... @@ -115,32 +115,32 @@ class RecordInfoMatch extends Emiter {
})
.then(ret => {
loger.log('AG-获取媒体录制信息-完成');
this.parseRecordMessage(ret.returnData.data,_callback);
/* for (let i = 0; i < ret.returnData.data.length; i++) {
let item = ret.returnData.data[i];
if (item.status == 1) {
this.streamInfoLen++;
//console.log(JSON.stringify(item));
delete item.appId;
delete item.id;
let time = parseInt(item.creatTime);
let timeInfo = this.timestampToUTCTime(time);
//return {yymmddhhmmss:yymmddhhmmss,yymmdd:yymmdd}
item.seek = 0;
item.creatTimeUTC = timeInfo.yymmddhhmmss;
item.creatDate = timeInfo.yymmdd;
this.jsonPathList [timeInfo.yymmdd] = this.filePath + "/" + timeInfo.yymmdd + "/json/" + item.channel + ".json";
item.stream_id = item.channel + "_" + item.userId + "_" + item.timestamp;
if (!this.recordMessages[item.uid]) {
this.recordMessages[item.uid] = {};
}
this.recordMessages[item.uid][item.timestamp] = item;
}
}
if (_callback) {
_callback();
}*/
this.parseRecordMessage(ret.returnData.data, _callback);
/* for (let i = 0; i < ret.returnData.data.length; i++) {
let item = ret.returnData.data[i];
if (item.status == 1) {
this.streamInfoLen++;
//console.log(JSON.stringify(item));
delete item.appId;
delete item.id;
let time = parseInt(item.creatTime);
let timeInfo = this.timestampToUTCTime(time);
//return {yymmddhhmmss:yymmddhhmmss,yymmdd:yymmdd}
item.seek = 0;
item.creatTimeUTC = timeInfo.yymmddhhmmss;
item.creatDate = timeInfo.yymmdd;
this.jsonPathList [timeInfo.yymmdd] = this.filePath + "/" + timeInfo.yymmdd + "/json/" + item.channel + ".json";
item.stream_id = item.channel + "_" + item.userId + "_" + item.timestamp;
if (!this.recordMessages[item.uid]) {
this.recordMessages[item.uid] = {};
}
this.recordMessages[item.uid][item.timestamp] = item;
}
}
if (_callback) {
_callback();
}*/
})
.catch(err => {
loger.error(`AG-获取媒体录制信息-异常.状态码:${err}`);
... ... @@ -150,13 +150,13 @@ class RecordInfoMatch extends Emiter {
});
}
parseRecordMessage(_data,_callback){
if(!_data||_data.length<1){
console.log("录制消息数据无效",_data)
parseRecordMessage(_data, _callback) {
if (!_data || _data.length < 1) {
console.log("录制消息数据无效", _data)
return;
}
for (let i = 0; i <_data.length; i++) {
let item =_data[i];
for (let i = 0; i < _data.length; i++) {
let item = _data[i];
if (item.status == 1) {
this.streamInfoLen++;
//console.log(JSON.stringify(item));
... ... @@ -250,7 +250,7 @@ class RecordInfoMatch extends Emiter {
console.log("_recordMessages", _recordMessages);
console.log("_m3u8jsonDataList", _m3u8jsonDataList);
console.log("最终匹配完成的数量为->" + Object.keys(this.matchStreams).length);
console.log("没有录制文件的用户", this.unMatchUid);
console.error("没有录制文件的用户", this.unMatchUid);
this._emit(RecordInfoMatch.RECORD_INFO_MATCH_COMPLETE, this.matchStreams);
}
... ... @@ -271,6 +271,7 @@ class RecordInfoMatch extends Emiter {
return parseInt(a.creatTimeUTC) - parseInt(b.creatTimeUTC);
});
if (uidRecordInfoArr.length == uidRecordFileArr.length && uidRecordFileArr.length > 0) {
//1.如果文件数量和消息数量相等,直接关联即可
console.log(k + "->数量相同->直接匹配", uidRecordInfoArr.length);
... ... @@ -285,11 +286,12 @@ class RecordInfoMatch extends Emiter {
for (let f in uidRecordInfo) {
let time = parseInt(uidRecordInfo[f].creatTimeUTC);
let video_url = "";
//A.精确匹配
let video_urlObj = uidRecordFile[k + "_" + time];
if (video_urlObj && video_urlObj.video_url) {
video_url = video_urlObj.video_url;
}
//模糊
//B.模糊匹配-前后相差10秒左右
if (!video_url) {
video_urlObj = uidRecordFile[k + "_" + (time + 1)];
if (video_urlObj && video_urlObj.video_url) {
... ... @@ -302,7 +304,6 @@ class RecordInfoMatch extends Emiter {
video_url = video_urlObj.video_url;
}
}
//通过时间戳匹配 结束
if (video_url) {
let item = uidRecordInfo[f];
... ... @@ -318,12 +319,34 @@ class RecordInfoMatch extends Emiter {
console.log(k + "->数量不同 消息数量:" + uidRecordInfoArr.length, "视频数量:" + uidRecordFileArr.length);
//按消息的数量大于视频数量处理
let lastVideoFileItem;//记录最后一个
for (let h = 0; h < uidRecordInfoArr.length; h++) {
let infoItem = uidRecordInfoArr[h];
for (let s = 0; s < uidRecordInfoArr.length; s++) {
if(s==0){
let infoItem = uidRecordInfoArr[s];
let videoFileItem = uidRecordFileArr[s];
infoItem.video_url = videoFileItem.video_url;
//如果之前没有匹配到才设置
if (!this.matchStreams[infoItem.stream_id]) {
this.matchStreams[infoItem.stream_id] = infoItem;
}
}else {
let infoItem = JSON.stringify(uidRecordInfoArr[s]);
infoItem=JSON.parse(infoItem);
console.log("查找->",s,uidRecordInfoArr);
let videoFileItem=this.getNearestTimeData(infoItem.creatTimeUTC,uidRecordFileArr);
if(videoFileItem){
infoItem.video_url = videoFileItem.video_url;
infoItem.seek=videoFileItem.seek;
//如果之前没有匹配到才设置
if (!this.matchStreams[infoItem.stream_id]) {
this.matchStreams[infoItem.stream_id] = infoItem;
}
}
}
/*let infoItem = uidRecordInfoArr[h];
let videoFileItem = uidRecordFileArr[h];
if (videoFileItem) {
infoItem.video_url = videoFileItem.video_url;
//如果之前没有匹配到才设置
if (!this.matchStreams[infoItem.stream_id]) {
this.matchStreams[infoItem.stream_id] = infoItem;
... ... @@ -337,48 +360,44 @@ class RecordInfoMatch extends Emiter {
this.matchStreams[infoItem.stream_id] = infoItem;
}
}
}
}*/
}
}
}
}
}
/*
//没有查找到之后,如果两次推流的消息时间过短,只会有一个视频,需要再次匹配
console.log("uidRecordInfoUnsearch", uidRecordInfoUnsearch, uidRecordInfo);
if (Object.keys(uidRecordInfoUnsearch).length > 0) {
let item;
let item2;
let minInterval = 10000;
let nearItem = null;
for (let s in uidRecordInfoUnsearch) {
item = uidRecordInfoUnsearch[s];
for (let h in uidRecordInfo) {
item2 = uidRecordInfo[h];
if (item2 && item2.video_url) {
let interval = parseInt((item.creatTime) * 0.001 - parseInt(item2.creatTime) * 0.001);
if (minInterval > interval) {
nearItem = item2;
minInterval = interval;
}
minInterval = Math.min(minInterval, interval);
//console.log("两次推流消息的间隔",interval);
}
}
}
if (item && nearItem) {
console.log("查找到最接近的消息", nearItem, "seek", minInterval);
item.video_url = nearItem.video_url;
item.seek = minInterval;
matchStreams[item.stream_id] = item;
}
}*/
/*
* 根据时间获取间隔最短的的数据
* */
getNearestTimeData(time,videoArr){
let _time=parseInt(time);
let _nearstTimeDistance=100000;//默认设置为一个超长的值
let nearstTimeItem=null;
for(let k=0;k<videoArr.length;k++){
//计算最近时间
let interval=_time-parseInt(videoArr[k].creatTimeUTC);
console.log("getNearestTimeData",interval,_time,k,videoArr[k].creatTimeUTC);
if(interval>-12&&interval<_nearstTimeDistance){
//记录时间点最接近
nearstTimeItem=videoArr[k];
nearstTimeItem.seek=interval;
_nearstTimeDistance=Math.abs(_nearstTimeDistance);
}
}
let itemJson="";
if(nearstTimeItem){
itemJson=JSON.stringify(nearstTimeItem);
let finelItem=JSON.parse(itemJson);
finelItem.seek=_time-parseInt(finelItem.creatTimeUTC);
console.log("nearstTimeItem->",finelItem);
return finelItem;
}
console.log("nearstTimeItem->没有找到数据");
return null
}
timestampToUTCTime(_timestamp) {
let date = new Date(_timestamp - 8 * 60 * 60 * 1000);//GMT 转UTC 减8
let y = "" + date.getFullYear();
... ...
... ... @@ -32,6 +32,7 @@ class RecordPlayBackParse extends Emiter {
parseBuffer = new ByteBuffer(ByteBuffer.DEFAULT_CAPACITY, ByteBuffer.LITTLE_ENDIAN);
parseBuffer.clear();
this._recordPlaybackTimestamp = 0;//回放的时间
this.lastParseTime=0;
this._recordPlaybackMaxTime = 0;//录制回放的总时间
this._isReady = false;//录制回放是否已经准备完成
this.isLoadTxRecordInfo = false;//是否已经加载TXY的录制数据
... ... @@ -42,6 +43,9 @@ class RecordPlayBackParse extends Emiter {
this.videoPublishMessages=[];//记录推流控制的消息
this._videoApeBroadcastMssages = {};//视频模块的广播消息
this.mediaChannleList = {};
this.conferApeUnPackMssages = {};//人员数据按时间戳记录数据
this.allRosterList = {};//记录所有人员数据
this._conferApeMssages = {};//会议数据
this._chatApeMssages = {};//聊天数据
this._videoApeMssages = {};//视频数据
... ... @@ -140,7 +144,7 @@ class RecordPlayBackParse extends Emiter {
let pduMsg = pdu.decode_pdu(data);
let pduType = pduMsg.get("type");
let pduData = pduMsg.get("data");
//console.log("REC->timestamp:"+timestamp,"pduType:"+pduType);
//*************非常重要******************
//客户端发送的所有125消息,MCU收到之后会痛120把消息返回给客户端,
//所以需要把125消息type转换为120,因为MCU在录制的时候是直接录制客户端发送的消息而不是MCU转换之后的
... ... @@ -178,6 +182,11 @@ class RecordPlayBackParse extends Emiter {
switch (pduMsg.sessionId) {
case ApeConsts.CONFERENCE_SESSION_ID:
this.saveParseData(data, timestamp, this._conferApeMssages);
//不处理广播消息,只处理同步消息
if(pduMsg.subType!=ApeConsts.RCPDU_SEND_CONFERENCE_DATA_REQUEST){
this.unPackpduRegAdapterHandler(pduMsg.data, timestamp, data, ApeConsts.CONFERENCE_SESSION_ID, pduMsg.subType);
}
break;
case ApeConsts.CHAT_SESSION_ID:
this.saveParseData(data, timestamp, this._chatApeMssages);
... ... @@ -197,7 +206,7 @@ class RecordPlayBackParse extends Emiter {
case ApeConsts.VIDEO_SESSION_ID:
this.saveParseData(data, timestamp, this._videoApeMssages);
this.unpackVideoBroadcastMessage(pduMsg, timestamp, data);
this.unPackpduRegAdapterHandler(pduMsg.data, timestamp, data, ApeConsts.VIDEO_SESSION_ID, pduMsg.subType)
this.unPackpduRegAdapterHandler(pduMsg.data, timestamp, data, ApeConsts.VIDEO_SESSION_ID, pduMsg.subType);
break;
case ApeConsts.AUDIO_SESSION_ID:
this.saveParseData(data, timestamp, this._audioApeMssages);
... ... @@ -280,9 +289,9 @@ class RecordPlayBackParse extends Emiter {
this._searchMessageFromTime(this._recordPlaybackTimestamp, this._mediaShareApeMssages, "mediaShareApe");
this._searchMessageFromTime(this._recordPlaybackTimestamp, this._musicShareApeMssages, "musicShareApe");
this._searchMessageFromTime(this._recordPlaybackTimestamp, this._whiteApeMssages, "whiteApe");
this._searchMessageFromTime(this._recordPlaybackTimestamp, this._videoApeBroadcastMssages, "videoApBroadcast");
this._searchMessageFromTime(this._recordPlaybackTimestamp, this._videoApeMssages, "videoAp");
this._searchMessageFromTime(this._recordPlaybackTimestamp, this._audioApeMssages, "audioApe");
this._searchMessageFromTime(this._recordPlaybackTimestamp, this._videoApeBroadcastMssages, "videoApBroadcast");
}
//加载录制文件rec
... ... @@ -745,11 +754,19 @@ class RecordPlayBackParse extends Emiter {
let byteData = parseBuffer.buffer.slice(position, (position + byteLen));
position += byteLen;
if(this._recordPlaybackMaxTime>timestamp){
loger.warn("脚本录制文件中的数据时间戳重叠了!!!!",this._recordPlaybackMaxTime,timestamp,"相差:"+this._recordPlaybackMaxTime-timestamp);
if( this.lastParseTime==0){
this.lastParseTime=this._recordPlaybackMaxTime;
this.lastParseTimeDistance=(this._recordPlaybackMaxTime-timestamp)*2;
}
timestamp= this.lastParseTime+timestamp;
}
//记录最后一个数据的时间戳作为整个录制回放的总时间戳
this._recordPlaybackMaxTime = Math.max(this._recordPlaybackMaxTime,timestamp);
//按时间戳解保存数据
this._parseSaveSocketMsgReceivedHandler(byteData, timestamp);
//记录最后一个数据的时间戳作为整个录制回放的总时间戳
this._recordPlaybackMaxTime = timestamp;
}
this._recordPlaybackTimestamp = 0;
this._isReady = true;
... ... @@ -776,6 +793,8 @@ class RecordPlayBackParse extends Emiter {
console.log('视频模块广播消息', this._videoApeBroadcastMssages);
loger.log("录制回放数据解析完成,录制回放的总时间长为->", this._recordPlaybackMaxTime, "recordTimestamp:" + GlobalConfig.recordTimestamp);
console.log('videoPublishMessages', this.videoPublishMessages);
console.log('conferApeUnPackMssages', this.conferApeUnPackMssages);
console.log('allRosterList', this.allRosterList);
//AGO启动录制消息和文件匹配
this.recordInfoMatch.start(this.videoPublishMessages);
... ... @@ -805,12 +824,12 @@ class RecordPlayBackParse extends Emiter {
return {"code": ApeConsts.RETURN_FAILED, "data": "录制回放还未准备完成"};
}
loger.log("classStatusInfo", GlobalConfig.classStatusInfo);
console.log("所有流ID", this.allStreams);
for (let i in this.allStreams) {
if (MediaModule.streams[i]) {
console.log("匹配成功的流:" + i, MediaModule.streams[i]);
console.log("所有流ID-videoPublishMessages", this.videoPublishMessages);
for (let i of this.videoPublishMessages) {
if (MediaModule.streams[i.streamId]) {
//console.log("匹配成功的流:" + i, MediaModule.streams[i.streamId]);
} else {
console.warn("未匹配成功的流:" + i);
//console.warn("未匹配成功的流:" + i.streamId);
}
}
this._startTimerCounter();
... ... @@ -866,10 +885,11 @@ class RecordPlayBackParse extends Emiter {
*/
//先查广播消息再查频道消息,频道消息在广播消息之后
//音视频模块的查找规则和其他模块不一样,音视频按频道查找,如果课堂内存在多个频道,都要查
this.searchMediaApeMessageKeyfram(this.mediaChannleList);
//视频广播消息
this.searchVideoBroadcastMessageKeyfram(this._videoApeBroadcastMssages);
//音视频模块的查找规则和其他模块不一样,音视频按频道查找,如果课堂内存在多个频道,都要查
//this.searchMediaApeMessageKeyfram(this.mediaChannleList);
//媒体共享模块
... ... @@ -930,7 +950,7 @@ class RecordPlayBackParse extends Emiter {
messageItem = channelInfos[i];
if (messageItem) {
keyFrameSeekTime = (this._recordPlaybackTimestamp - i);
loger.log("频道:" + k + "->SEEK->查找音视频模块数据->", messageItem, 'keyFrameSeekTime->', keyFrameSeekTime)
loger.log("频道:" + k + "->SEEK->查找音视频模块数据->", messageItem, 'keyFrameSeekTime->')
this._everSocketMsgReceivedHandler(messageItem.byteData, keyFrameSeekTime);
break;
}
... ... @@ -954,9 +974,12 @@ class RecordPlayBackParse extends Emiter {
for (let i = this._recordPlaybackTimestamp; i > 0; i--) {
messageItem = nodeIdVideoBrodcast[i];
if (messageItem) {
keyFrameSeekTime = (this._recordPlaybackTimestamp - i);
loger.log("nodeId:" + k + "->SEEK->查找音视频模块广播数据->", messageItem, 'keyFrameSeekTime->', keyFrameSeekTime)
this._everSocketMsgReceivedHandler(messageItem.byteData, keyFrameSeekTime);
//根据时间戳查找到音视频广播消息,需要判断当前是否有频道消息,不能重复
if(!this.hasVideoChannelMessage(i)){
keyFrameSeekTime = (this._recordPlaybackTimestamp - i);
loger.log("nodeId:" + k + "->SEEK->查找音视频模块广播数据->", messageItem, 'keyFrameSeekTime->', keyFrameSeekTime)
this._everSocketMsgReceivedHandler(messageItem.byteData, keyFrameSeekTime);
}
break;
}
}
... ... @@ -964,7 +987,16 @@ class RecordPlayBackParse extends Emiter {
}
}
}
hasVideoChannelMessage(_time){
for (let k in this.mediaChannleList) {
let channelInfos = this.mediaChannleList[k];
let messageItem = channelInfos[_time];
if(messageItem){
return true;
}
}
return false;
}
//媒体共享模块查找关键帧时间戳的消息
searchMediaShareApeMessageKeyfram(_apeMessages) {
if (!_apeMessages) {
... ... @@ -1132,7 +1164,46 @@ class RecordPlayBackParse extends Emiter {
console.log("视频模块广播消息->解析失败", err.message);
}
}
/*
* 根据用户的userId userName userRole 获取nodeId
* */
getNodeIdFromUserInfo(userInfo){
return userInfo.nodeId||userInfo.fromNodeId;
if(!userInfo) {
console.error("根据用户的userId userName userRole 获取nodeId 失败->传入的参数无效",userInfo);
return 0;
}
let user=null;
//全匹配 nodeId+userName+userId
for (let k in this.allRosterList){
user= this.allRosterList[k];
if(user){
if(user.userId==userInfo.userId&&
user.name==userInfo.userName&&
user.nodeId==userInfo.nodeId
){
//名字和userId相同就可以返回,角色身份暂时不用(XXXXXXXXXXXXXXXX 老师的需要做处理,有一种情况就是userId和userName相同,nodeId不一样
return user.nodeId;
}
}
}
//模糊匹配 nodeId+userName+userId
for (let k in this.allRosterList){
user= this.allRosterList[k];
if(user){
if(user.userId==userInfo.userId&&
user.name==userInfo.userName
){
//名字和userId相同就可以返回,角色身份暂时不用(XXXXXXXXXXXXXXXX 老师的需要做处理,有一种情况就是userId和userName相同,nodeId不一样
return user.nodeId;
}
}
}
console.warn("根据用户的userId userName userRole 获取nodeId 失败->用户列表中找不到用户",userInfo);
return userInfo.nodeId||userInfo.fromNodeId;
}
//音视频的数据需要解析,然后按频道储存数据
// 解析pdu RCAdapterPdu的数据: regBuffer(RCAdapterPdu数据),timestamp(时间戳), data(mcu的原始数据) sessionId(类型)
unPackpduRegAdapterHandler(regBuffer, timestamp, data, sessionId, subType) {
... ... @@ -1188,7 +1259,12 @@ class RecordPlayBackParse extends Emiter {
break;
case pdu.RCPDU_REG_ROSTER_DELETE_PDU:
let rosterDelData = pdu['RCRegistryRosterDeleteItemPdu'].decode(user_data);
// console.log('RCRegistryRosterDeleteItemPdu',rosterDelData)
console.log('RCRegistryRosterDeleteItemPdu',rosterDelData);
if(!this.conferApeUnPackMssages[rosterDelData.nodeId]){
this.conferApeUnPackMssages[rosterDelData.nodeId]={};
}
this.conferApeUnPackMssages[rosterDelData.nodeId][timestamp]=rosterDelData;
break;
case pdu.RCPDU_REG_ROSTER_UPDATE_PDU:
let rosterUpdateData = pdu['RCRegistryRosterUpdateItemPdu'].decode(user_data);
... ... @@ -1199,7 +1275,13 @@ class RecordPlayBackParse extends Emiter {
let node = rosterUpdateItems[i];
let nodeId = node.nodeId;
let nodeData = pdu['RCNodeInfoRecordPdu'].decode(node.nodeData);
// console.log('RCNodeInfoRecordPdu',nodeData)
//console.log('RCNodeInfoRecordPdu',nodeData);
if(!this.conferApeUnPackMssages[nodeId]){
this.conferApeUnPackMssages[nodeId]={};
}
this.conferApeUnPackMssages[nodeId][timestamp]=nodeData;
this.allRosterList[nodeId]=nodeData;
}
break;
case pdu.RCPDU_REG_TABLE_INSERT_PDU:
... ... @@ -1266,7 +1348,17 @@ class RecordPlayBackParse extends Emiter {
//console.log("视频流" + videoChannelInfo.streamId);
if(videoChannelInfo){
videoChannelInfo.channel=videoChannelInfo.siteId+"_"+videoChannelInfo.classId;
videoChannelInfo.uid=videoChannelInfo.fromNodeId;
//音视频频道在课堂中会出现fromNodeId和推流者nodeId不一样的问题,需要检查和修复
let matchNodeIdFromUserInfo=this.getNodeIdFromUserInfo(videoChannelInfo);
if(videoChannelInfo.fromNodeId==matchNodeIdFromUserInfo){
videoChannelInfo.uid=videoChannelInfo.fromNodeId;
}else {
console.error("推流者的nodeId和消息中的nodeId不一致,需要替换",videoChannelInfo.fromNodeId,"真实nodeId:"+matchNodeIdFromUserInfo);
videoChannelInfo.fromNodeId=matchNodeIdFromUserInfo;
videoChannelInfo.uid=matchNodeIdFromUserInfo;
}
let streamInfo=videoChannelInfo.streamId.split("_");
//kaifangwebrtc_1092942818_S8908_1510741823354
if(streamInfo&&streamInfo.length>1){
... ... @@ -1274,9 +1366,8 @@ class RecordPlayBackParse extends Emiter {
}else {
videoChannelInfo.creatTime=parseInt(videoChannelInfo.timestamp)*1000;//秒转换为毫秒
}
}
//****1310721是屏幕共享和外部流 不需要匹配*****
//!****1310721是屏幕共享和外部流 不需要匹配*****
if(parseInt(videoChannelInfo.channelId)<1310721){
this.videoPublishMessages.push(videoChannelInfo);
}
... ...
... ... @@ -431,6 +431,21 @@ class ConferApe extends Ape {
}
}
//全局送礼物权限控制
changeGiftStatus(params) {
loger.log("全局送礼物权限控制",params);
if (!GlobalConfig.isNormal) {
if (params && params.isEnableGift == true) {
GlobalConfig.isEnableGift = true;
} else {
GlobalConfig.isEnableGift = false;
}
this.sendUpdaterClassStatusInfo(params);
} else {
loger.warn('学生没有开启送礼物的权限');
}
}
//控制指定用户的画笔状态
changeUserDrawStatus(_param) {
loger.log('控制指定用户的画笔状态->', _param);
... ... @@ -1251,6 +1266,7 @@ class ConferApe extends Ape {
classStatusInfo.silence = GlobalConfig.silence;
classStatusInfo.silenceUsers = JSON.stringify(GlobalConfig.silenceUsers);
classStatusInfo.isEnableDraw = GlobalConfig.isEnableDraw;
classStatusInfo.isEnableGift = GlobalConfig.isEnableGift;
classStatusInfo.videoScale=GlobalConfig.videoScale||1;
//loger.log("classStatusInfo--->", classStatusInfo);
... ...
... ... @@ -599,9 +599,9 @@ class VideoApe extends Ape {
videoInfo=this.packVideoInfoAndSendToClient(videoData,seek);
this._emit(MessageTypes.VIDEO_PLAY, videoInfo);
}else if(data.status==0){
loger.log("停止播放文件->", videoData);
videoInfo=this.packVideoInfoAndSendToClient(videoData,seek);
this._emit(MessageTypes.VIDEO_STOP, videoInfo);
// loger.log("停止播放文件->", videoData);
// videoInfo=this.packVideoInfoAndSendToClient(videoData,seek);
// this._emit(MessageTypes.VIDEO_STOP, videoInfo);
}
break;
default:
... ... @@ -796,7 +796,7 @@ class VideoApe extends Ape {
packVideoInfoAndSendToClient(_param,seek){
let mediaChannelInfo = {};
mediaChannelInfo.mediaId = _param.channelId;
mediaChannelInfo.fromNodeId = _param.fromNodeId;
mediaChannelInfo.fromNodeId = _param.fromNodeId||_param.nodeId||_param.uid;
mediaChannelInfo.userName = _param.userName || "";
mediaChannelInfo.userRole = _param.userRole || ApeConsts.normal;
mediaChannelInfo.mediaType = _param.mediaType || ApeConsts.MEDIA_TYPE_DEFAULT;
... ...
... ... @@ -206,17 +206,17 @@ class WebRtcApe extends Emiter {
let uid = evt.uid;
let user = GlobalConfig.getUserInfoFromeNodeId(parseInt(uid));
//console.log("active-speaker",user);
if (user) {
/* if (user) {
let roleRole = GlobalConfig.getUserRoleToString(user.role);
this.speakerList.push("当前正在讲话的 [" + roleRole + "] name:" + user.name + " uid:" + uid);
} else {
this.speakerList.push("当前正在讲话的uid:" + uid);
}
//记录正在讲话的数据,大于50条上报一次
}*/
/* //记录正在讲话的数据,大于50条上报一次
if(this.speakerList.length>20){
loger.log(this.speakerList);
this.speakerList=[];
}
}*/
});
}
... ... @@ -454,7 +454,7 @@ class WebRtcApe extends Emiter {
clearTimeout(this.rePublishDelay);
this.rePublishDelay = setTimeout(()=> {
this.publish();
}, 200);
}, 400);
}
}, (err)=> {
loger.error("加入视频通话频道->失败->", err);
... ... @@ -484,6 +484,7 @@ class WebRtcApe extends Emiter {
this.localStream.close();
this.localStream=null;
}*/
loger.log("获取本地视频流");
this.localStream = AgoraRTC.createStream({
streamID: this.uid,
audio: true,
... ... @@ -507,6 +508,7 @@ class WebRtcApe extends Emiter {
leaveChannel() {
loger.log("调用离开视频通话频道->isPublish" + this.isPublish);
this._emit(MessageTypes.WEB_RTC_LEAVE_CHANNEL);
if (!this.client) {
return;
}
... ... @@ -568,7 +570,7 @@ class WebRtcApe extends Emiter {
}
publish(_params) {
if (!this.client || !this.localStream) {
if (!this.client) {
loger.warn("推流失败->未加入频道!");
return;
}
... ... @@ -677,10 +679,15 @@ class WebRtcApe extends Emiter {
unpublish() {
clearTimeout(this.rePublishDelay);
loger.log("webRtc->停止推流 ", new Date().getTime());
if (!this.client || !this.localStream) {
if (!this.client) {
loger.log("webRtc->停止推流失败->没有加入频道", new Date().getTime());
return;
}
if (!this.localStream) {
loger.log("webRtc->停止推流失败->没有本地视频流", new Date().getTime());
return;
}
loger.log("webRtc->停止推流 ", new Date().getTime());
this.client.unpublish(this.localStream, (err)=> {
loger.log("webRtc->停止推流->失败" + err);
});
... ...
... ... @@ -31,6 +31,7 @@ const TYPE_LINE = 1;//直线
const TYPE_RECT = 2;//矩形
const TYPE_CIRCLE = 3;//圆形
const TYPE_TEXT = 4;//文本
const TYPE_ELLIPSE = 5;//椭圆
class WhiteBoardApe extends Ape {
constructor() {
... ... @@ -440,6 +441,9 @@ class WhiteBoardApe extends Ape {
case TYPE_CIRCLE:
whiteBoardModelPdu.radius = parseInt(parseFloat(_param.radius)*100);//外部传入的值有可能是小数,放大100倍,收到数据后再转换
break;
case TYPE_ELLIPSE:
whiteBoardModelPdu.radius = parseInt(parseFloat(_param.radius)*100);//外部传入的值有可能是小数,放大100倍,收到数据后再转换
break;
case TYPE_TEXT:
whiteBoardModelPdu.fontSize = parseInt(parseFloat(_param.fontSize)*100);//外部传入的值有可能是小数,放大100倍,收到数据后再转换
whiteBoardModelPdu.fontName = _param.fontName || null;
... ...
... ... @@ -1056,6 +1056,7 @@ message RCClassStatusInfoPdu {
optional string silenceUsers=25;//课堂用户禁言状态列表
optional bool isEnableDraw=26;//课堂用户是否开启绘制权限
optional uint32 videoScale=27;//视频显示的缩放倍数
optional bool isEnableGift=28;//是否开启送礼物权限
}
message RCConferenceRecordRequestPdu {
... ...