李勇

Merge branch 'mcuClientBranch'

... ... @@ -11,6 +11,7 @@ import Loger from 'Loger';
import ConferApe from 'apes/ConferApe';
import ChatApe from 'apes/ChatApe';
import VideoApe from 'apes/VideoApe';
import AudioApe from 'apes/AudioApe';
import DocApe from 'apes/DocApe';
import WhiteBoardApe from 'apes/WhiteBoardApe';
import EngineUtils from "EngineUtils";
... ... @@ -29,6 +30,7 @@ let _mcu ;
let _confer_ape;
let _chat_ape;
let _video_ape;
let _audio_ape;
let _doc_ape;
let _whiteboard_ape;
... ... @@ -68,7 +70,7 @@ export default class MessageEntrance extends Emiter {
// 底层MCU消息层
_mcu = Mcu;
_mcu.on('*', (type, data) => this._emit(type, data));
_mcu.on(MessageTypes.CLASS_JOIN_SUCCESS, this._joinClassSuccessHandler.bind(this));//加入MCU会议完成
_mcu.on(MessageTypes.CLASS_JOIN_SUCCESS, this._mcuJoinClassSuccessHandler.bind(this));//加入MCU会议完成
// 注册所有应用Ape
... ... @@ -87,11 +89,14 @@ export default class MessageEntrance extends Emiter {
_video_ape.on('*', (type, data) => this._emit(type, data));
_video_ape.on(MessageTypes.VIDEO_UPDATE, this.videoUpdate.bind(this));
_audio_ape= new AudioApe();
_audio_ape.on('*', (type, data) => this._emit(type, data));
_audio_ape.on(MessageTypes.AUDIO_UPDATE, this.audioUpdate.bind(this));
_whiteboard_ape = new WhiteBoardApe();
_whiteboard_ape.on('*', (type, data) => this._emit(type, data));
//_whiteboard_ape.on(MessageTypes.WHITEBOARD_ANNOTATION_UPDATE, this.annoUpdateHandler.bind(this));
_doc_ape = new DocApe();
_doc_ape.on('*', (type, data) => this._emit(type, data));
_doc_ape.on(MessageTypes.DOC_UPDATE, this.docUpdateHandler.bind(this));
... ... @@ -115,13 +120,20 @@ export default class MessageEntrance extends Emiter {
this.sendChatMsg = this._sendChatMsg;
//videoApe
this.getPlayVideoPath = this._getPlayVideoPath;
this.getPublishVideoPath = this._getPublishVideoPath;
this.getVideoPlayPath = this._getPlayVideoPath;
this.getVideoPublishPath = this._getPublishVideoPath;
this.publishVideo = this._publishVideo;
this.stopPublishVideo = this._stopPublishVideo;
this.sendVideoCommandMsg=this.sendVideoCommandMsg;
this.sendVideoBroadcastMsg=this.sendVideoCommandMsg;
//audioApe
this.getAudioPlayPath = this._getPlayAudioPath;
this.getAudioPublishPath = this._getPublishAudioPath;
this.publishAudio = this._publishAudio;
this.stopPublishAudio = this._stopPublishAudio;
this.sendAudioBroadcastMsg=this.sendAudioCommandMsg;
//whiteBoradApe
this.sendInsertAnnotaion = this._sendInsertAnnotaion;
//this.sendDeleteAnnotaion=this._sendDeleteAnnotaion;
... ... @@ -174,6 +186,7 @@ export default class MessageEntrance extends Emiter {
_onClassStatusInfoChange(_param) {
this._sassSaveClassStatusInfo();
}
//有人员离开
_onClassDeleteRoster(_data){
//{"nodeId":nodeId}
... ... @@ -184,18 +197,25 @@ export default class MessageEntrance extends Emiter {
if(_video_ape){
_video_ape.stopPublishVideo(_data);
}
if(_audio_ape){
_audio_ape.stopPublishAudio(_data);
}
}
}
//当前会议中视频或音频占用channel的nodeId ,在人员列表中不存在,这种情况是占用channel的人员掉线或离开的时候没有释放channel
//的占用状态导致,对于这种情况,需要释放掉
_onClassNonentityRoster(_param){
if(_param==null||_param.fromNodeId==null){
if(_param==null||_param.nodeId==null){
loger.warn("onClassNonentityRoster.参数错误")
return;
}
let data={"nodeId":_param.nodeId};
if(_video_ape){
_video_ape.stopPublishVideo({"nodeId":_param.fromNodeId});
_video_ape.stopPublishVideo(data);
}
if(_audio_ape){
_audio_ape.stopPublishAudio(data);
}
}
... ... @@ -219,12 +239,14 @@ export default class MessageEntrance extends Emiter {
return;
}
loger.log('init', _param);
//保存参数
GlobalConfig.classId = parseInt(_param.classId);
GlobalConfig.portal = _param.portal;
GlobalConfig.userRole = _param.userRole || ApeConsts.normal;
GlobalConfig.userId = _param.userId || "0";
GlobalConfig.userName=_param.userName || "";
//获取课堂校验信息
if (_sass) {
_sass.getJoinParams(GlobalConfig.getClassInfo());
... ... @@ -253,7 +275,7 @@ export default class MessageEntrance extends Emiter {
GlobalConfig.password = _param.password || "";
GlobalConfig.hasCamera=(typeof _param.hasCamera=="boolean")? _param.hasCamera:false;
GlobalConfig.hasMicrophone=(typeof _param.hasMicrophone=="boolean")? _param.hasMicrophone:false;
debugger;
//debugger;
//开始校验
if (_sass) {
_sass.passwordAndMd5Checking(GlobalConfig.getClassInfo());
... ... @@ -269,13 +291,7 @@ export default class MessageEntrance extends Emiter {
GlobalConfig.siteId = _data.siteId || "";
GlobalConfig.classType = _data.meetingType || 0;
/*//host默认需要密码,Sass服务器只判断学生是否需要密码,没有判断老师的
if (GlobalConfig.userRole == "host") {
GlobalConfig.passwordRequired = true;
} else {
GlobalConfig.passwordRequired = _data.passwordRequired || false;
}
*/
//host默认需要密码,Sass服务器只判断学生是否需要密码,没有判断老师的
GlobalConfig.passwordRequired = _data.passwordRequired || false;//md5验证的时候需要Sass返回的值,不能更改
loger.log('SASS平台获取入会验证信息成功.');
... ... @@ -303,7 +319,7 @@ export default class MessageEntrance extends Emiter {
}
}
// 通过SASS平台验证
// 通过SASS平台验证(密码和MD5)
_sassJoinSuccessHandler(_data) {
//返回值
/* flag 数值型 无 True:成功
... ... @@ -330,42 +346,55 @@ export default class MessageEntrance extends Emiter {
"maxAudioChannels": 1,
"h5_mcu_list": "123.56.73.119:7001;123.56.69.230:7001;112.126.80.182:7001"
}*/
if (_data.h5_mcu_list) {
//MCU地址默认使用第一个
let server = _data.h5_mcu_list.split(";")[0];
GlobalConfig.MCUServerIP = server.split(":")[0];
GlobalConfig.MCUServerPort = server.split(":")[1];
}
//视频推流播流地址
if (_data.ms) {
//MS地址默认使用第一个
let server = _data.ms.split(";")[0];
GlobalConfig.MSServerIP = server.split(":")[0];
GlobalConfig.MSServerPort = server.split(":")[1];
}
//m3u8播流地址
if(_data.rs){
//RS地址默认使用第一个
let server = _data.rs.split(";")[0];
GlobalConfig.RSServerIP = server.split(":")[0];
GlobalConfig.RSServerPort = server.split(":")[1];
}
GlobalConfig.docServer = _data.doc;
GlobalConfig.h5_mcu_list = _data.h5_mcu_list;
GlobalConfig.h5Module = _data.h5Module;
GlobalConfig.mcu = _data.mcu;
GlobalConfig.ms = _data.ms;
GlobalConfig.record = _data.record;
GlobalConfig.rs = _data.rs;
GlobalConfig.maxVideoChannels = _data.maxVideoChannels;
GlobalConfig.maxAudioChannels = _data.maxAudioChannels;
GlobalConfig.maxMediaChannels = _data.maxVideoChannels + _data.maxAudioChannels;
/*
if (_data.h5_mcu_list) {
//MCU地址默认使用第一个
let server = _data.h5_mcu_list.split(";")[0];
GlobalConfig.MCUServerIP = server.split(":")[0];
GlobalConfig.MCUServerPort = server.split(":")[1];
}
//视频推流播流地址
if (_data.ms) {
//MS地址默认使用第一个
let server = _data.ms.split(";")[0];
GlobalConfig.MSServerIP = server.split(":")[0];
GlobalConfig.MSServerPort = server.split(":")[1];
}
//m3u8播流地址
if(_data.rs){
//RS地址默认使用第一个
let server = _data.rs.split(";")[0];
GlobalConfig.RSServerIP = server.split(":")[0];
GlobalConfig.RSServerPort = server.split(":")[1];
}
GlobalConfig.docServer = _data.doc;
GlobalConfig.h5_mcu_list = _data.h5_mcu_list;
GlobalConfig.h5Module = _data.h5Module;
GlobalConfig.mcu = _data.mcu;
GlobalConfig.ms = _data.ms;
GlobalConfig.record = _data.record;
GlobalConfig.rs = _data.rs;
GlobalConfig.maxVideoChannels = _data.maxVideoChannels;
GlobalConfig.maxAudioChannels = _data.maxAudioChannels;
*/
/*
//这个接口获取的数据在getClassParam接口的数据中都有,内容重复,这个接口废弃
//获取会议基本信息
if (_sass) {
_sass.getClassDetail();
}
*/
//获取会议最完整的数据
if (_sass) {
_sass.getClassDetail();//会议基本信息
_sass.getClassParam();
}
}
... ... @@ -398,7 +427,7 @@ export default class MessageEntrance extends Emiter {
GlobalConfig.classBeginTime = _data.beginTime || "";
GlobalConfig.classEndTime = _data.endTime || "";
//获取会议所有信息和以前保存的会议状态信息
//获取会议所有信息和以前保存的会议状态信息(最全的信息)
if (_sass) {
_sass.getClassParam();
}
... ... @@ -411,8 +440,15 @@ export default class MessageEntrance extends Emiter {
// console.log(_data);
//包含整个会议最全的信息,储存数据
if (_data) {
GlobalConfig.className = _data.meetingName || "";
GlobalConfig.classBeginTime = _data.beginTime || "";
GlobalConfig.classEndTime = _data.endTime || "";
GlobalConfig.maxVideoChannels = _data.maxVideoChannels;
GlobalConfig.maxAudioChannels = _data.maxAudioChannels;
GlobalConfig.setDocListPrepare(_data.docListPrepare); //提前上传的文档列表
GlobalConfig.setDocRecordList(_data.docRecordList);//录制回放地址??
GlobalConfig.setRecordList(_data.recordList);//录制回放地址
GlobalConfig.setDocList(_data.docList);//文档地址
GlobalConfig.setMsList(_data.msList);//推流播流服务器地址
GlobalConfig.setRsList(_data.rsList);//播放m3u8格式的地址
... ... @@ -421,22 +457,45 @@ export default class MessageEntrance extends Emiter {
GlobalConfig.setMusicListPrepare(_data.musicListPrepare);//提前上传的声音文件列表
if (_data.mcuList) {
//MCU地址默认使用第一个
GlobalConfig.MCUServerIP =_data.mcuList[0].ip||"";
GlobalConfig.MCUServerPort =_data.mcuList[0].port||"";
}
//视频推流播流地址
if (_data.msList) {
//MS地址默认使用第一个
GlobalConfig.MSServerIP =_data.msList[0].ip||"";
GlobalConfig.MSServerPort =_data.msList[0].port||"";
}
//m3u8播流地址
if(_data.rsList){
//RS地址默认使用第一个
GlobalConfig.RSServerIP =_data.rsList[0].ip||"";
GlobalConfig.RSServerPort =_data.rsList[0].port||"";
}
//文档地址
if(_data.docList){
//doc地址默认使用第一个
GlobalConfig.DOCServerIP =_data.docList[0].ip||"";
GlobalConfig.DOCServerPort =_data.docList[0].port||"";
}
//record
if(_data.recordList){
//地址默认使用第一个
GlobalConfig.RecordServerIP =_data.recordList[0].ip||"";
GlobalConfig.RecordServerPort =_data.recordList[0].port||"";
}
}
if (_data.currentInfo) {
//根据从Sass获取的数据信息,同步最后一次保存的会议状态信息
loger.log("同步最后一次保存过的会议状态信息");
/*//解析BASE64的数据
loger.log("11111111111111111111111111111111");
console.log(GlobalConfig.classStatusInfo);
//已经保存过会议状态信息,读取之前的数据
let _classStatusInfo=EngineUtils.objectFromBase64(_data.currentInfo);
loger.log("classStatusInfo",_classStatusInfo);
GlobalConfig.classStatusInfo=_classStatusInfo;
loger.log("2222222222222222222222222");
console.log(GlobalConfig.classStatusInfo);*/
//console.log(_data.currentInfo);
GlobalConfig.setClassStatusInfo(_data.currentInfo);
console.log(GlobalConfig.classStatusInfo)
} else {
... ... @@ -450,11 +509,6 @@ export default class MessageEntrance extends Emiter {
//保存会议状态信息
_sassSaveClassStatusInfo() {
if (GlobalConfig.isHost) {
//用get保存数据,数据内容需要转为BASE64
//let _classStatusInfo=EngineUtils.objectToBase64(GlobalConfig.classStatusInfo);
//loger.log("保存会议状态信息",_classStatusInfo);
//_sass.saveClassStatusInfo({"classStatusInfo":_classStatusInfo});//保存会议状态信息
//只有加入会议之后才能保存数据
if (GlobalConfig.getCurrentStatus().code == GlobalConfig.statusCode_2.code) {
//POST 保存数据
... ... @@ -481,7 +535,7 @@ export default class MessageEntrance extends Emiter {
}
// MCU 会议成功
_joinClassSuccessHandler(_data) {
_mcuJoinClassSuccessHandler(_data) {
loger.log('MCU 会议成功.');
GlobalConfig.setCurrentStatus(GlobalConfig.statusCode_2);
... ... @@ -490,17 +544,15 @@ export default class MessageEntrance extends Emiter {
//返回给客户端初始化成功的数据
let initSuccessCallBackData = {};
//GlobalConfig.MCUServerIP=_data.MCUServerIP;
// GlobalConfig.MCUServerPort=_data.MCUServerPort;
initSuccessCallBackData.DOCServerIP =GlobalConfig.DOCServerIP;
initSuccessCallBackData.DOCServerPort =GlobalConfig.DOCServerPort;
initSuccessCallBackData.docServer = GlobalConfig.docServer;//文档服务器地址
initSuccessCallBackData.classId = GlobalConfig.classId;
initSuccessCallBackData.className = GlobalConfig.className;
initSuccessCallBackData.h5Module = GlobalConfig.h5Module;
initSuccessCallBackData.isHost = GlobalConfig.isHost;
initSuccessCallBackData.maxAudioChannels = GlobalConfig.maxAudioChannels;
initSuccessCallBackData.maxVideoChannels = GlobalConfig.maxVideoChannels;
initSuccessCallBackData.maxMediaChannels = GlobalConfig.maxMediaChannels;
initSuccessCallBackData.mcuDelay = GlobalConfig.mcuDelay;
initSuccessCallBackData.msType = GlobalConfig.msType;
... ... @@ -526,79 +578,6 @@ export default class MessageEntrance extends Emiter {
loger.log('加入会议成功', initSuccessCallBackData);
_joinClassSuccessCallBackFun(initSuccessCallBackData);
}
//loger.log('MCU 会议成功.');
//GlobalConfig.setCurrentStatus(GlobalConfig.statusCode_2);
//if(_sass){
// _sass.getClassDetail();//会议信息
// _sass.getClassParam();//会议参数大全
//}
//
////储存数据
//GlobalConfig.MCUServerIP=_data.MCUServerIP;
//GlobalConfig.MCUServerPort=_data.MCUServerPort;
//GlobalConfig.classId=_data.classId;
//GlobalConfig.h5Module=_data.h5Module;
//GlobalConfig.maxAudioChannels=_data.maxAudioChannels;
//GlobalConfig.maxVideoChannels=_data.maxVideoChannels;
//GlobalConfig.maxMediaChannels=_data.maxMediaChannels;
//GlobalConfig.mcuClassInfo=_data.mcuClassInfo;
//GlobalConfig.mcuDelay=_data.mcuDelay;
//GlobalConfig.md5=_data.md5;
//GlobalConfig.msType=_data.msType;
//GlobalConfig.nodeId=_data.nodeId;
//GlobalConfig.password=_data.password;
//GlobalConfig.portal=_data.portal;
//GlobalConfig.role=_data.role;
//GlobalConfig.siteId=_data.siteId;
//GlobalConfig.topNodeID=_data.topNodeID;
//GlobalConfig.userId=_data.userId;
//GlobalConfig.userName=_data.userName;
//GlobalConfig.userRole=_data.userRole;
//GlobalConfig.userType=_data.userType;
//
////返回给客户数据
//if(_joinClassSuccessCallBackFun){
// //返回给客户端初始化成功的数据
// let initSuccessCallBackData={};
//
// //GlobalConfig.MCUServerIP=_data.MCUServerIP;
// // GlobalConfig.MCUServerPort=_data.MCUServerPort;
//
// initSuccessCallBackData.docServer=GlobalConfig.docServer;//文档服务器地址
// initSuccessCallBackData.classId=GlobalConfig.classId;
// initSuccessCallBackData.className=GlobalConfig.className;
// initSuccessCallBackData.h5Module=GlobalConfig.h5Module;
// initSuccessCallBackData.isHost=GlobalConfig.isHost;
// initSuccessCallBackData.maxAudioChannels=GlobalConfig.maxAudioChannels;
// initSuccessCallBackData.maxVideoChannels=GlobalConfig.maxVideoChannels;
// initSuccessCallBackData.maxMediaChannels=GlobalConfig.maxMediaChannels;
// initSuccessCallBackData.mcuDelay=GlobalConfig.mcuDelay;
//
// initSuccessCallBackData.msType=GlobalConfig.msType;
// initSuccessCallBackData.nodeId=GlobalConfig.nodeId;
// initSuccessCallBackData.password=GlobalConfig.password;
// initSuccessCallBackData.passwordRequired=GlobalConfig.passwordRequired;// 老师的默认是true
// //GlobalConfig.passwordRequired 老师的默认是true
// //GlobalConfig.portal=_data.portal;
// initSuccessCallBackData.role=GlobalConfig.role;
// initSuccessCallBackData.siteId=GlobalConfig.siteId;
// initSuccessCallBackData.topNodeID=GlobalConfig.topNodeID;
// initSuccessCallBackData.userId=GlobalConfig.userId;
// initSuccessCallBackData.userName=GlobalConfig.userName;
// initSuccessCallBackData.userRole=GlobalConfig.userRole;
// initSuccessCallBackData.userType=GlobalConfig.userType;
//
// initSuccessCallBackData.siteId=GlobalConfig.siteId;
// initSuccessCallBackData.classId=GlobalConfig.classId;
// initSuccessCallBackData.userRole=GlobalConfig.userRole;
// initSuccessCallBackData.userId=GlobalConfig.userId;
// initSuccessCallBackData.passwordRequired=GlobalConfig.passwordRequired;
// initSuccessCallBackData.classType=GlobalConfig.classType||ApeConsts.CLASS_TYPE_INTERACT;
// loger.log('加入会议成功',initSuccessCallBackData);
// _joinClassSuccessCallBackFun(initSuccessCallBackData);
//}
}
//Sass删除文档数据
... ... @@ -653,6 +632,9 @@ export default class MessageEntrance extends Emiter {
if(_video_ape){
_video_ape.stopPublishVideo();
}
if(_audio_ape){
_audio_ape.stopPublishAudio();
}
//离开会议
if(_confer_ape){
_confer_ape.leaveClass();
... ... @@ -714,6 +696,45 @@ export default class MessageEntrance extends Emiter {
}
}
//AudioApe
audioUpdate(_data){
//音频同步的消息发送改变,需要通知ferApe模块中的用户更新状态
if(_confer_ape){
_confer_ape.updaterRosterStatus(_data);
}
}
sendAudioCommandMsg(_param){
if(_audio_ape){
return _audio_ape.sendAudioBroadcastMsg(_param);
}
}
_getPlayAudioPath(_param){
if(_audio_ape){
return _audio_ape.getAudioPlayPath(_param);
}
}
_getPublishAudioPath(_param){
if(_audio_ape){
return _audio_ape.getAudioPublishPath(_param);
}
}
_publishAudio(_param){
if(_audio_ape){
return _audio_ape.publishAudio(_param);
}
}
_stopPublishAudio(_param){
if(_audio_ape){
return _audio_ape.stopPublishAudio(_param);
}
}
//WhiteBoardApe
// 添加标注,发送信息
_sendInsertAnnotaion(_param){
... ... @@ -820,30 +841,32 @@ export default class MessageEntrance extends Emiter {
if(GlobalConfig.isHost){
for (let value of GlobalConfig.docListPrepare){
if(value){
//{
// "MD5": "f3feb3fac8cd3a953bded00e07a0c66b",
// "absoluteLocation": "http://101.200.150.192/DocSharing/data/526661904/20170203-115400026/f3feb3fac8cd3a953bded00e07a0c66b.swf",
// "createUserID": "972",
// "createUserIP": "114.241.81.175",
// "createUserName": "base",
// "dynamicPPT": 0,
// "dynamicTransferStatic": "",
// "id": "8ab3b0ed5a00f2fa015a0219a3df016c",
// "meetingNumber": "",
// "name": "McuClient_v.1.0.1_API.pdf",
// "orderStr": "",
// "pdfSize": 5,
// "processEndTime": "2017-02-03 11:54:31",
// "processRate": 0,
// "processStartTime": "2017-02-03 11:54:27",
// "relativeLocation": "/DocSharing/data/526661904/20170203-115400026/f3feb3fac8cd3a953bded00e07a0c66b.swf",
// "siteID": "h5test",
// "size": 360920,
// "status": 3,
// "type": "pdf",
// "uploadEndTime": "2017-02-03 11:54:27",
// "uploadStartTime": "2017-02-03 11:54:27"
//}
/* //提前上传的文档文档信息的结构
{
"MD5": "f3feb3fac8cd3a953bded00e07a0c66b",
"absoluteLocation": "http://101.200.150.192/DocSharing/data/526661904/20170203-115400026/f3feb3fac8cd3a953bded00e07a0c66b.swf",
"createUserID": "972",
"createUserIP": "114.241.81.175",
"createUserName": "base",
"dynamicPPT": 0,
"dynamicTransferStatic": "",
"id": "8ab3b0ed5a00f2fa015a0219a3df016c",
"meetingNumber": "",
"name": "McuClient_v.1.0.1_API.pdf",
"orderStr": "",
"pdfSize": 5,
"processEndTime": "2017-02-03 11:54:31",
"processRate": 0,
"processStartTime": "2017-02-03 11:54:27",
"relativeLocation": "/DocSharing/data/526661904/20170203-115400026/f3feb3fac8cd3a953bded00e07a0c66b.swf",
"siteID": "h5test",
"size": 360920,
"status": 3,
"type": "pdf",
"uploadEndTime": "2017-02-03 11:54:27",
"uploadStartTime": "2017-02-03 11:54:27"
}*/
loger.log("判断是否需要把提前上传的文档上传到mcu",value);
let paramInfo={
"pageNum": value.pdfSize,
... ...
... ... @@ -30,7 +30,9 @@ class GlobalConfig {
static getClassDetail(){
return this.classDetail;
}
static setClassDetail(_data){
this.classDetail=_data;
}
static getClassInfo (){
let classInfo={};
classInfo.siteId= this.siteId;
... ... @@ -55,7 +57,6 @@ class GlobalConfig {
classInfo.MCUServerPort=this.MCUServerPort;
classInfo.maxVideoChannels= this.maxVideoChannels;
classInfo.maxAudioChannels=this.maxAudioChannels;
classInfo.maxMediaChannels= this.maxMediaChannels;
return classInfo;
... ... @@ -175,13 +176,13 @@ class GlobalConfig {
static getDocListPrepare(){
return this.docListPrepare;
}
//储存文档录制列表
static setDocRecordList(_data){
//储存录制列表
static setRecordList(_data){
if(_data==null) return;
this.docRecordList=_data;
this.recordList=_data;
}
static getDocRecordList(){
return this.docRecordList;
static getRecordList(){
return this.recordList;
}
//文档服务器列表
... ... @@ -247,11 +248,12 @@ GlobalConfig.md5="";
GlobalConfig.msType=1;//目前固定用这个
GlobalConfig.mcuDelay=3000;//默认的延迟时间
GlobalConfig.docDelay=1600;//文档模块加入成功之后延迟发送送成功的消息给主模块
GlobalConfig.portal="112.126.80.182:80";
GlobalConfig.ip="112.126.80.182";
GlobalConfig.port="80";
GlobalConfig.portal="112.126.80.182:80";//Sass IP
//GlobalConfig.ip="112.126.80.182";
//GlobalConfig.port="80";
GlobalConfig.MCUServerIP="114.215.195.70";
GlobalConfig.docServer="";//当前的文档地址加载的服务器地址
GlobalConfig.MCUServerPort=9003;
GlobalConfig.MSServerIP = "";//推流 播流的地址
... ... @@ -261,9 +263,16 @@ GlobalConfig.MSServerPort ="";
GlobalConfig.RSServerIP ="";
GlobalConfig.RSServerPort ="";
//DOC
GlobalConfig.DOCServerIP ="";
GlobalConfig.DOCServerPort ="";
GlobalConfig.RecordServerIP ="";
GlobalConfig.RecordServerPort ="";
GlobalConfig.maxVideoChannels=0;
GlobalConfig.maxAudioChannels=0;
GlobalConfig.maxMediaChannels=0;
GlobalConfig.isDebug=false;
GlobalConfig.deBugData={};
... ... @@ -315,7 +324,7 @@ GlobalConfig.classAllParam={};//Sass直接返回的所有会议信息(最全)
GlobalConfig.classDetail={};//Sass直接返回的当前课堂基本信息
GlobalConfig.docListPrepare=[]; // 已经提前上传的文档,进入课堂后需要自动加载
GlobalConfig.docRecordList=[];//录制服务器地址集合
GlobalConfig.recordList=[];//录制服务器地址集合
GlobalConfig.docList=[];//文档服务器地址集合
GlobalConfig.mcuList=[];//录制服务器地址集合
GlobalConfig.msList=[];//ms服务器地址集合
... ... @@ -323,21 +332,5 @@ GlobalConfig.musicList=[];//music服务器地址集合
GlobalConfig.musicListPrepare=[];//提提前上传的music集合
GlobalConfig.rsList=[];
// client type
//点对点会议版本
GlobalConfig.CT_P2PMEETING = 0;
/**
* 直播教育版本和大型直播版本
*/
GlobalConfig.CT_LIVEEDUCATION = 1;
/**
* 录制播放器
*/
GlobalConfig.CT_RECORDPLAYER = 2;
/**
* 实时流版本
*/
GlobalConfig. CT_LIVESTREAMING = 3;
GlobalConfig.clientType =0;
export default GlobalConfig;
... ...
//对外暴露的对象
import EngineEntrance from 'EngineEntrance';
import MessageTypes from 'MessageTypes';
const MCU_CLIENT=new EngineEntrance();
const MCU_CLIENT=new EngineEntrance();//入口文件
export function createMcuClient() {
return MCU_CLIENT;
}
//监听是事件名和异常定义
export {MessageTypes};
... ...
... ... @@ -29,7 +29,12 @@ MessageTypes.CHAT_RECEIVE = 'chat.receive';
//视频模块事件定义
MessageTypes.VIDEO_UPDATE = 'video.update';
MessageTypes.VIDEO_COMMAND= 'video.command';
MessageTypes.VIDEO_BROADCAST= 'video.broadcast';
//音频模块事件定义
MessageTypes.AUDIO_UPDATE = 'audio.update';
MessageTypes.AUDIO_BROADCAST= 'audio.broadcast';
//文档模块事件定义
MessageTypes.DOC_DELETE='document.delete';//删除文档
... ...
... ... @@ -82,10 +82,13 @@ ApeConsts.USER_MIC_OPEN = 0x0040; // 麦克风开启
ApeConsts.USER_CAMERA_OPEN = 0x0080; // 视频开启
//VIDEO MIC 流媒体消息操作控制类型
ApeConsts.MEDIA_ACTION_DEFAULT=0;
ApeConsts.MEDIA_ACTION_OPEN_CAMERA=1;// "open.camera";
ApeConsts.MEDIA_ACTION_CLOSE_CAMERA=2;// "close.camera";
ApeConsts.MEDIA_ACTION_OPEN_MIC= 3;//"open.mic";
ApeConsts.MEDIA_ACTION_CLOSE_MIC= 4;//"close.mic";
... ... @@ -99,12 +102,16 @@ ApeConsts.MEDIA_TYPE_DEFAULT=0;//没有类型
ApeConsts.MEDIA_TYPE_VIDEO=1;//视频流(包含音频)
ApeConsts.MEDIA_TYPE_AUDIO=2;//音频流
//FLASH中使用下面4个
ApeConsts.CGS_RELEASED = 0;///< 无人占用状态
ApeConsts.CGS_PENDING = 1;///< 占用成功,等待打开
ApeConsts.CGS_OPENNED = 2;///< 打开成功
ApeConsts.CGS_GRABBING = 3; ///< 准备占用中, 属于本地状态机需要用的状态,在多点数据库中不存在。
//return返回值状态
ApeConsts.RETURN_SUCCESS=0;//成功
ApeConsts.RETURN_FAILED=1;//失败
////FLASH中使用下面4个
//ApeConsts.CGS_RELEASED = 0;///< 无人占用状态
//ApeConsts.CGS_PENDING = 1;///< 占用成功,等待打开
//ApeConsts.CGS_OPENNED = 2;///< 打开成功
//ApeConsts.CGS_GRABBING = 3; ///< 准备占用中, 属于本地状态机需要用的状态,在多点数据库中不存在。
//
ApeConsts.INVALIDATE_CHANNEL_ID = -1;
... ...
// //////////////////////////////////////////////////////////////////////////////
//音频模块
// //////////////////////////////////////////////////////////////////////////////
import Ape from './Ape';
import ApeConsts from './ApeConsts';
import pdu from 'pdus';
import Loger from 'Loger';
import MessageTypes from 'MessageTypes';
import GlobalConfig from 'GlobalConfig';
import EngineUtils from 'EngineUtils';
import MediaModule from "./MediaModule";
let loger = Loger.getLoger('AudioApe');
class AudioApe extends Ape {
constructor() {
super(
ApeConsts.AUDIO_SESSION_ID,
ApeConsts.AUDIO_SESSION_NAME,
ApeConsts.AUDIO_SESSION_TAG
);
this.mediaModule=new MediaModule();
this.mediaModule.MEDIA_OBJ_TABLE_ID=ApeConsts.AUDIO_OBJ_TABLE_ID;
this.mediaModule.mediaChannels={};
// Ape Models
this.registerKey(this._session_id, this._session_name, this._session_tag, new ArrayBuffer);
this.registerObj(pdu.RCPDU_REG_REGISTER_TABLE, ApeConsts.AUDIO_OBJ_TABLE_ID, ApeConsts.AUDIO_OBJ_TABLE_NAME, ApeConsts.AUDIO_OBJ_TABLE_TAG, 0, new ArrayBuffer);
// 广播消息,用户之间的消息传递
this.on(pdu.RCPDU_AUDIO_SEND_DATA_REQUEST, this.receiveAudiooCommandHandler.bind(this));
}
//ape加入成功
onJoinChannelHandlerSuccess(){
//这个设置很重要,因为只有Sass流程完成之后,APE才能取得GlobalConfig中的数据
this.mediaModule.maxMediaChannel=GlobalConfig.maxAudioChannels;
}
/////////////发送数据操作////////////////////////////////////////////
//获取播流地址
getAudioPlayPath(_param) {
loger.log('getAudioPlayPath');
return this.mediaModule.getMediaPlayPath(_param);
}
//获取推流地址
getAudioPublishPath(_param) {
loger.log('getAudioPublishPath');
return this.mediaModule.getMediaPublishPath(_param);
}
//推流
publishAudio(_param) {
if (_param == null||_param.channelId == null||
_param.classId == null||_param.userId == null||
_param.siteId == null|| _param.timestamp==null)
{
loger.warn('publishAudio,参数错误', _param);
this._emit(MessageTypes.MCU_ERROR, MessageTypes.ERR_APE_INTERFACE_PARAM_WRONG);
return {"code": ApeConsts.RETURN_FAILED, "data": "参数错误"};
}
//同一个nodeId只允许推一个流,如果已经推了就不能再推
if(this.mediaModule.getOpeningMediaChannel(GlobalConfig.nodeId)!=0){
loger.warn("publishAudio,已经存在一个流,不能再推");
return {"code": ApeConsts.RETURN_FAILED, "data": "已经存在一个流,不能再推"};
}
//判断当前是否还有空闲的channle
let freeChannel = this.mediaModule.getFreeMediaChannel();
if (freeChannel == 0) {
loger.warn("publishAudio,没有空闲的channel ");
return {"code": ApeConsts.RETURN_FAILED, "data": "不能再打开更多的设备"};
}
//判断当前的频道是否已经占用
if(this.mediaModule.checkChannelIsOpening(_param.channelId)){
loger.warn(_param.channelId,"频道已经被占用");
return {"code": ApeConsts.RETURN_FAILED, "data":"频道已经被占用!"};
}
let channelInfo={};
channelInfo.status=ApeConsts.CHANNEL_STATUS_OPENING;
channelInfo.fromNodeId=GlobalConfig.nodeId;
channelInfo.channelId=_param.channelId;//freeChannel
channelInfo.timestamp=_param.timestamp;//EngineUtils.creatTimestamp();
channelInfo.classId=_param.classId;//GlobalConfig.classId;
channelInfo.siteId=_param.siteId;//GlobalConfig.siteId;
channelInfo.toNodeId=0;
channelInfo.mediaType=ApeConsts.MEDIA_TYPE_AUDIO;
this.sendTableUpdateHandler(channelInfo);
return {"code": ApeConsts.RETURN_SUCCESS, "data":"推流成功!"}
}
//停止推流,
stopPublishAudio(_param) {
loger.log('stopPublishAudio');
//_param如果为空,那么默认就是当前自己的nodeId,否则用_param
let nodeId;
if(_param&&parseInt(_param.nodeId)>=0){
nodeId=parseInt(_param.nodeId);
}else {
nodeId=GlobalConfig.nodeId;
}
let openingChannel = this.mediaModule.getOpeningMediaChannel(nodeId);
if (openingChannel == 0) {
loger.warn(nodeId,"stopPublishAudio,没有占用channel,不需要关闭");
return {"code": ApeConsts.RETURN_FAILED, "data": "没有占用channel,不需要关闭"};
}
let channelInfo={};
channelInfo.status=ApeConsts.CHANNEL_STATUS_RELEASED;
channelInfo.fromNodeId=0;
channelInfo.channelId=openingChannel;
channelInfo.timestamp=0;
channelInfo.classId=GlobalConfig.classId;
channelInfo.toNodeId=0;
channelInfo.mediaType=ApeConsts.MEDIA_TYPE_DEFAULT;
this.sendTableUpdateHandler(channelInfo);
}
sendAudioBroadcastMsg(_param) {
if (this._classInfo === null || EngineUtils.isEmptyObject(this._classInfo)) {
loger.log('sendAudioBroadcastMsg.McuClient还未初始化数据!');
if (GlobalConfig.getCurrentStatus().code == 0 || GlobalConfig.getCurrentStatus().code == 1) {
this._emit(MessageTypes.MCU_ERROR, MessageTypes.ERR_APE_SEND_FAILED_NO_JOIN);
return {"code": ApeConsts.RETURN_FAILED, "data": "sendAudioBroadcastMsg.McuClient还未初始化数据"};
}
return {"code": ApeConsts.RETURN_FAILED, "data": "sendAudioBroadcastMsg.McuClient还未初始化数据"};
}
if (_param == null) {
loger.warn('sendAudioBroadcastMsg,参数错误', _param);
this._emit(MessageTypes.MCU_ERROR, MessageTypes.ERR_APE_INTERFACE_PARAM_WRONG);
return {"code": ApeConsts.RETURN_FAILED, "data": "sendAudioBroadcastMsg,参数错误"};
}
// to, message
loger.log('发送Audio消息.', _param);
if (_param.actionType != null && _param.actionType == ApeConsts.MEDIA_ACTION_OPEN_CAMERA) {
//判断当前开启的视频数量是否已经是最大值,如果已经是最大值,不能再开启
let freeChannel = this.mediaModule.getFreeMediaChannel();
if (freeChannel == 0) {
loger.warn('sendAudioBroadcastMsg,不能再打开更多的设备', _param);
return {"code": ApeConsts.RETURN_FAILED, "data": "不能再打开更多的设备"};
}
}
let audioSendPdu = new pdu['RCAudioSendDataRequestPdu'];
audioSendPdu.type = pdu.RCPDU_AUDIO_SEND_DATA_REQUEST;
audioSendPdu.isPublic = true;
audioSendPdu.fromNodeId = GlobalConfig.nodeId;//发起人
audioSendPdu.toNodeId = parseInt(_param.toNodeID) || 0;//接收者,0就是所有人
audioSendPdu.actionType = parseInt(_param.actionType) || ApeConsts.MEDIA_ACTION_DEFAULT;
audioSendPdu.data = this._rCArrayBufferUtil.strToUint8Array("h5" + _param.data);//开头两个字会乱码
if (!audioSendPdu.isPublic && 0 != audioSendPdu.toNodeId) {
//发送给制定的人
loger.log('发送私聊消息.');
this.send(audioSendPdu);
} else {
//发送给所有人
loger.log('发送公聊消息.');
this.sendChatUniform(audioSendPdu);
}
return {"code": ApeConsts.RETURN_SUCCESS, "data": ""};
}
sendTableUpdateHandler(_channelInfo) {
loger.log("audio,sendTableUpdateHandler ");
let updateModelPdu = this.packPdu(_channelInfo, _channelInfo.channelId);
if(updateModelPdu==null){
loger.warn("sendTableUpdateHandler error,updateModelPdu=null");
return;
}
let tableItemPdu = new pdu['RCRegistryTableItemPdu'];
tableItemPdu.itemIdx = _channelInfo.channelId;
tableItemPdu.owner = 0;//收到flash的是这个值,不清楚先写固定
tableItemPdu.itemData = updateModelPdu.toArrayBuffer();
//insert
let tableInsertItemPdu = new pdu['RCRegistryTableUpdateItemPdu'];
//optional RCPduType_E type = 1 [default = RCPDU_REG_TABLE_UPDATE_PDU];
//repeated RCRegistryTableItemPdu items = 2;
tableInsertItemPdu.type = pdu.RCPDU_REG_TABLE_UPDATE_PDU;//
tableInsertItemPdu.items.push(tableItemPdu);
let updateObjPdu = new pdu['RCRegistryUpdateObjPdu'];
updateObjPdu.objId = ApeConsts.AUDIO_OBJ_TABLE_ID;//
updateObjPdu.subType = tableInsertItemPdu.type;
updateObjPdu.userData = tableInsertItemPdu.toArrayBuffer();
//同步
let adapterItemPdu = new pdu['RCAdapterItemPdu'];
adapterItemPdu.type = pdu.RCPDU_REG_UPDATE_OBJ;
adapterItemPdu.itemData = updateObjPdu.toArrayBuffer();
let adapterPdu = new pdu['RCAdapterPdu'];
adapterPdu.type = pdu.RCPDU_REG_ADAPTER;
adapterPdu.item.push(adapterItemPdu);
loger.log("发送更新AUDIO.itemIdx=" + tableItemPdu.itemIdx);
this.sendUniform(adapterPdu, true);
}
/////收到消息处理//////////////////////////////////////////////////
// 消息处理,内部处理,不需要告诉应用层
receiveAudiooCommandHandler(_data) {
let audioReceivePdu = pdu['RCAudioSendDataRequestPdu'].decode(_data);
if (audioReceivePdu == null) {
loger.warn("音频消息处理,收到的消息为null,不做处理");
return;
}
audioReceivePdu.data = this._rCArrayBufferUtil.uint8ArrayToStr(audioReceivePdu.data, 2);//开头两个字会乱码
loger.log('音频消息处理 receiveAudiooCommandHandler.', audioReceivePdu);
//判断接收者的id,如果不是0,并且也不是自己的nodeId,那么消息不做处理
if (audioReceivePdu.toNodeId != 0 && audioReceivePdu.toNodeId != GlobalConfig.nodeId) {
loger.log('音频消息不处理 toNodeId=', audioReceivePdu.toNodeId, "my nodeId=", GlobalConfig.nodeId);
} else {
this._emit(MessageTypes.AUDIO_BROADCAST, audioReceivePdu);
}
}
tableUpdateHandler(owner, itemIdx, itemData) {
// debugger;
let updateChannelInfo = this.unPackPdu(owner, itemIdx, itemData);
this.mediaModule.mediaChannels[itemIdx] = updateChannelInfo;
this._emit(MessageTypes.AUDIO_UPDATE, updateChannelInfo);
}
///////数据的封包和解包/////////////////////////////////////////
packPdu(_param, _itemIdx) {
loger.log("packPdu ");
//验证坐标点集合数组是否合法
if (_param == null || _itemIdx == null) {
this._emit(MessageTypes.MCU_ERROR, MessageTypes.ERR_APE_INTERFACE_PARAM_WRONG);
return null;
}
//判断type类型,根据type设置不同的参数
let packPduModel = new pdu['RCAudioChannelInfoPdu'];
packPduModel.status = _param.status||ApeConsts.CHANNEL_STATUS_RELEASED;
packPduModel.channelId = _itemIdx;
packPduModel.siteId=_param.siteId||GlobalConfig.siteId;//GlobalConfig.siteId;
packPduModel.classId =parseInt(_param.classId)||parseInt(GlobalConfig.classId);
packPduModel.userId =_param.userId||"0";
packPduModel.mediaType =_param.mediaType|| ApeConsts.MEDIA_TYPE_AUDIO;
packPduModel.timestamp =_param.timestamp||EngineUtils.creatTimestamp();
packPduModel.fromNodeId = GlobalConfig.nodeId;
packPduModel.toNodeId = 0;
console.log("packPdu",packPduModel);
return packPduModel;
}
unPackPdu(owner, itemIdx, itemData) {
loger.log("unPackPdu ");
if (owner == null || itemIdx == null || itemData == null) {
this._emit(MessageTypes.MCU_ERROR, MessageTypes.ERR_APE_INTERFACE_PARAM_WRONG);
return null;
}
try {
let packChannelInfo = pdu['RCAudioChannelInfoPdu'].decode(itemData);
loger.log("unPackPdu",packChannelInfo);
return packChannelInfo;
} catch (err) {
loger.log("unPackPdu error,itemIdx=" + itemIdx + " err:" + err.message);
}
return null;
}
}
export default AudioApe;
... ...
... ... @@ -454,8 +454,8 @@ class ConferApe extends Ape {
//视频模块发生更新,人员状态需要更新
updaterRosterStatus(_param){
if(_param){
loger.log("视频模块发生更新,人员状态需要更新,fromNodeId->",_param.fromNodeId);
loger.log(_param.status,_param.fromNodeId,this.rosters[_param.fromNodeId]);
loger.log("媒体模块发生更新,人员状态需要更新,fromNodeId->",_param.fromNodeId);
loger.log(_param.status,_param.fromNodeId);
//console.log(_param.fromNodeId);
//如果是自己。改变自己的状态同步到MCU
//if(_param.fromNodeId==GlobalConfig.nodeId){
... ... @@ -465,8 +465,8 @@ class ConferApe extends Ape {
//如果视频消息中channel的占用人 fromNodeId在人员列表中不存在,需要释放这channel,因为这个有可能是之前没释放成功的
if(_param.status==ApeConsts.CHANNEL_STATUS_OPENING&&this.rosters[_param.fromNodeId]==null){
loger.log("视频模块被占用,占有人已经不存在课堂中,释放Channel,_param->",_param);
this._emit(MessageTypes.CLASS_NONENTITY_ROSTER,_param.fromNodeId);
loger.log("媒体模块被占用,占有人已经不存在课堂中,释放Channel,_param->",_param);
this._emit(MessageTypes.CLASS_NONENTITY_ROSTER,{"nodeId":_param.fromNodeId});
}
}
}
... ...
... ... @@ -145,7 +145,8 @@ class DocApe extends Ape {
return "";
}
let fullPath=GlobalConfig.docServer+_param.relativeUrl;
let port = (GlobalConfig.DOCServerPort == "" || GlobalConfig.DOCServerPort == null) ? "":":" + GlobalConfig.DOCServerPort;
let fullPath=GlobalConfig.DOCServerIP+port+_param.relativeUrl;
var index;
switch (_param.type){
case "jpg":
... ...
// //////////////////////////////////////////////////////////////////////////////
// VideoApe、AudioApe 共用的方法单独提取处理
// //////////////////////////////////////////////////////////////////////////////
import ApeConsts from './ApeConsts';
import Loger from 'Loger';
import MessageTypes from 'MessageTypes';
import GlobalConfig from 'GlobalConfig';
import EngineUtils from 'EngineUtils';
let loger = Loger.getLoger('MediaModule');
class MediaModule {
constructor() {
this.mediaChannels = {};
this.maxMediaChannel=0;
this.MEDIA_OBJ_TABLE_ID=0;
}
//获取播流地址
getMediaPlayPath(_param) {
loger.log('getMediaPlayPath');
if (_param == null||_param.siteId == null||
_param.classId == null||_param.userId == null||
_param.channelId == null|| _param.timestamp==null)
{
loger.warn('getMediaPlayPath,参数错误', _param);
this._emit(MessageTypes.MCU_ERROR, MessageTypes.ERR_APE_INTERFACE_PARAM_WRONG);
return {"code": ApeConsts.RETURN_FAILED, "data": ""};
}
let path = "";
let port="";
if (_param.type == "m3u8") {
//M3U8
//http://123.56.73.119:6001/hls/h5dev_403074980_0_983041_1487663265/index.m3u8
port = (GlobalConfig.RSServerPort == "" || GlobalConfig.RSServerPort == null) ? "":":" + GlobalConfig.RSServerPort;
path = "http://" + GlobalConfig.RSServerIP
+ port + "/live/"
+ _param.siteId
+ "_" + _param.classId
+ "_" + _param.userId
+ "_" + _param.channelId
+ "_" + _param.timestamp
+ "/index.m3u8";
} else {
port = (GlobalConfig.MSServerPort == "" || GlobalConfig.MSServerPort == null) ? "":":" + GlobalConfig.MSServerPort;
path = "rtmp://" + GlobalConfig.MSServerIP
+ port + "/live/"
+ _param.siteId
+ "_" + _param.classId
+ "_" + _param.userId
+ "_" + _param.channelId
+ "_" + _param.timestamp;
}
return {"code": ApeConsts.RETURN_SUCCESS, "data": "","playUrl": path};
}
//获取推流地址
getMediaPublishPath(_param) {
loger.log('getMediaPublishPath');
//判断当前开启的视频数量是否已经是最大值,如果已经是最大值,不能再开启
let freeChannel = this.getFreeMediaChannel();
if (freeChannel == 0) {
return {"code":ApeConsts.RETURN_FAILED, "data": "不能再打开更多的设备"};
}
//默认方式推流
let pubType="live";
//flash推流
if(_param&&_param.type=="flash"){
pubType ="flash";
}
//端口,有端口就显示 ":xxx",没有端口就是""
let port = (GlobalConfig.MSServerPort == "" || GlobalConfig.MSServerPort == null) ? "":":" + GlobalConfig.MSServerPort;
//时间戳
let timestamp = EngineUtils.creatTimestamp();
//生成推流地址和推流数据(同步数据的时候用)
let publishUrl = "rtmp://" + GlobalConfig.MSServerIP
+ port + "/"+pubType+"/" +GlobalConfig.siteId+"_"
+ GlobalConfig.classId + "_"+GlobalConfig.userId
+"_" + freeChannel + "_" + timestamp;
return {"code": ApeConsts.RETURN_SUCCESS,
"data":"",
"siteId":GlobalConfig.siteId,
"classId":GlobalConfig.classId,
"userId":GlobalConfig.userId,
"channelId": freeChannel,
"timestamp": timestamp,
"publishUrl": publishUrl
};
}
//获取当前空闲的channel,返回值为0代表没有空闲的,否则返回的就是空闲的channelId
getFreeMediaChannel() {
let counter = 0;
for (let key in this.mediaChannels) {
let item = this.mediaChannels[key];
if (item && item.status == ApeConsts.CHANNEL_STATUS_RELEASED) {
return item.channelId;
}
counter++;
}
loger.log("getFreeMediaChannel","maxMediaChannel",this.maxMediaChannel,"counter:",counter);
console.log(this.mediaChannels);
if (counter < this.maxMediaChannel) {
return this.MEDIA_OBJ_TABLE_ID + (counter);
}
return 0;//没有空闲的
}
//获取当前属于nodeId的已经打开的的channel,返回值为0代表没有打开的,否则返回的就是打开的channelId
getOpeningMediaChannel(_nodeId){
if(_nodeId==null||_nodeId==0){
return 0;
}
for (let key in this.mediaChannels) {
let item = this.mediaChannels[key];
if (item && item.status == ApeConsts.CHANNEL_STATUS_OPENING&&item.fromNodeId==_nodeId) {
return item.channelId;
}
}
return 0;
}
//检查频道是否已经被占用
checkChannelIsOpening(_channelId){
if(_channelId==null){
loger.warn("checkChannelIsOpening error,channel=",_channelId);
return true;
}
let channelInfo=this.mediaChannels[_channelId];
if(channelInfo==null||channelInfo.status==ApeConsts.CHANNEL_STATUS_RELEASED){
return false;
}
return true;
}
}
export default MediaModule;
... ...
// //////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2016-present All Rights Reserved.
// Licensed under the Apache License, Version 2.0 (the "License");
// http://www.apache.org/licenses/LICENSE-2.0
//
// Github Home: https://github.com/AlexWang1987
// Author: AlexWang
// Date: 2016-08-23 18:07:28
// QQ Email: 1669499355@qq.com
// Last Modified time: 2016-09-06 11:13:59
// Description: LiveClass-VideoApe
//
//视频模块
// //////////////////////////////////////////////////////////////////////////////
import Ape from './Ape';
... ... @@ -20,10 +9,11 @@ import Loger from 'Loger';
import MessageTypes from 'MessageTypes';
import GlobalConfig from 'GlobalConfig';
import EngineUtils from 'EngineUtils';
import MediaModule from "./MediaModule";
let loger = Loger.getLoger('VideoChat');
let loger = Loger.getLoger('VideoApe');
class VideoChat extends Ape {
class VideoApe extends Ape {
constructor() {
super(
ApeConsts.VIDEO_SESSION_ID,
... ... @@ -31,8 +21,9 @@ class VideoChat extends Ape {
ApeConsts.VIDEO_SESSION_TAG
);
//Attributes
this.videoChannels = {};
this.mediaModule=new MediaModule();
this.mediaModule.MEDIA_OBJ_TABLE_ID=ApeConsts.VIDEO_OBJ_TABLE_ID;
this.mediaModule.mediaChannels={};
// Ape Models
this.registerKey(this._session_id, this._session_name, this._session_tag, new ArrayBuffer);
... ... @@ -41,84 +32,23 @@ class VideoChat extends Ape {
// videoApe 监听视频控制消息,用户之间的消息传递
this.on(pdu.RCPDU_VIDEO_SEND_DATA_REQUEST, this.receiveVideoCommandHandler.bind(this));
}
//ape加入成功
onJoinChannelHandlerSuccess(){
//这个设置很重要,因为只有Sass流程完成之后,APE才能取得GlobalConfig中的数据
this.mediaModule.maxMediaChannel=GlobalConfig.maxVideoChannels;
}
/////////////发送数据操作////////////////////////////////////////////
//获取播流地址
getPlayVideoPath(_param) {
loger.log('getPlayVideoPath');
if (_param == null||_param.siteId == null||
_param.classId == null||_param.userId == null||
_param.channelId == null|| _param.timestamp==null)
{
loger.warn('getPlayVideoPath,参数错误', _param);
this._emit(MessageTypes.MCU_ERROR, MessageTypes.ERR_APE_INTERFACE_PARAM_WRONG);
return {"code": 1, "data": ""};
}
let path = "";
let port="";
if (_param.type == "m3u8") {
//M3U8
//http://123.56.73.119:6001/hls/h5dev_403074980_0_983041_1487663265/index.m3u8
port = (GlobalConfig.RSServerPort == "" || GlobalConfig.RSServerPort == null) ? "":":" + GlobalConfig.RSServerPort;
path = "http://" + GlobalConfig.RSServerIP
+ port + "/live/"
+ _param.siteId
+ "_" + _param.classId
+ "_" + _param.userId
+ "_" + _param.channelId
+ "_" + _param.timestamp
+ "/index.m3u8";
} else {
port = (GlobalConfig.MSServerPort == "" || GlobalConfig.MSServerPort == null) ? "":":" + GlobalConfig.MSServerPort;
path = "rtmp://" + GlobalConfig.MSServerIP
+ port + "/live/"
+ _param.siteId
+ "_" + _param.classId
+ "_" + _param.userId
+ "_" + _param.channelId
+ "_" + _param.timestamp;
}
return {"code": 0, "data": path};
return this.mediaModule.getMediaPlayPath(_param);
}
//获取推流地址
getPublishVideoPath(_param) {
loger.log('getPublishVideoPath');
//判断当前开启的视频数量是否已经是最大值,如果已经是最大值,不能再开启
let freeChannel = this.getFreeVideoChannel();
if (freeChannel == 0) {
return {"code": 1, "data": "不能再打开更多的设备"};
}
//默认方式推流
let pubType="live";
//flash推流
if(_param&&_param.type=="flash"){
pubType ="flash";
}
//端口,有端口就显示 ":xxx",没有端口就是""
let port = (GlobalConfig.MSServerPort == "" || GlobalConfig.MSServerPort == null) ? "":":" + GlobalConfig.MSServerPort;
//时间戳
let timestamp = EngineUtils.creatTimestamp();
//生成推流地址和推流数据(同步数据的时候用)
let publishUrl = "rtmp://" + GlobalConfig.MSServerIP
+ port + "/"+pubType+"/" +GlobalConfig.siteId+"_"
+ GlobalConfig.classId + "_"+GlobalConfig.userId
+"_" + freeChannel + "_" + timestamp;
return {"code": 0,
"data":
{ "siteId":GlobalConfig.siteId,
"classId":GlobalConfig.classId,
"userId":GlobalConfig.userId,
"channelId": freeChannel,
"timestamp": timestamp,
"publishUrl": publishUrl
}
};
return this.mediaModule.getMediaPublishPath(_param);
}
//推流
... ... @@ -129,28 +59,28 @@ class VideoChat extends Ape {
{
loger.warn('publishVideo,参数错误', _param);
this._emit(MessageTypes.MCU_ERROR, MessageTypes.ERR_APE_INTERFACE_PARAM_WRONG);
return {"code": 1, "data": ""};
return {"code": ApeConsts.RETURN_FAILED, "data": "参数错误"};
}
loger.log('publishVideo -> maxVideoChannels', GlobalConfig.maxVideoChannels);
//同一个nodeId只允许推一个流,如果已经推了就不能再推
if(this.getOpeningVideoChannel(GlobalConfig.nodeId)!=0){
if(this.mediaModule.getOpeningMediaChannel(GlobalConfig.nodeId)!=0){
loger.warn("publishVideo,已经存在一个流,不能再推");
return;
return {"code": ApeConsts.RETURN_FAILED, "data": "已经存在一个流,不能再推"};
}
//判断当前是否还有空闲的channle
let freeChannel = this.getFreeVideoChannel();
let freeChannel = this.mediaModule.getFreeMediaChannel();
if (freeChannel == 0) {
loger.warn("publishVideo,没有空闲的channel ");
return {"code": 1, "data": "不能再打开更多的设备"};
return {"code": ApeConsts.RETURN_FAILED, "data": "不能再打开更多的设备"};
}
//判断当前的频道是否已经占用
if(this.checkChannelIsOpening(_param.channelId)){
if(this.mediaModule.checkChannelIsOpening(_param.channelId)){
loger.warn(_param.channelId,"频道已经被占用");
return {"code":1,"data":"频道已经被占用!"};
return {"code": ApeConsts.RETURN_FAILED, "data":"频道已经被占用!"};
}
let channelInfo={};
... ... @@ -163,7 +93,7 @@ class VideoChat extends Ape {
channelInfo.toNodeId=0;
channelInfo.mediaType=ApeConsts.MEDIA_TYPE_VIDEO;
this.sendTableUpdateHandler(channelInfo);
return {"code":0,"data":"推流成功!"}
return {"code": ApeConsts.RETURN_SUCCESS, "data":"推流成功!"}
}
//停止推流,
... ... @@ -177,10 +107,10 @@ class VideoChat extends Ape {
nodeId=GlobalConfig.nodeId;
}
let openingChannel = this.getOpeningVideoChannel(nodeId);
let openingChannel = this.mediaModule.getOpeningMediaChannel(nodeId);
if (openingChannel == 0) {
loger.warn(nodeId,"stopPublishVideo,没有打开的channel,不需要关闭");
return {"code": 1, "data": "没有打开的channel,不需要关闭"};
loger.warn(nodeId,"stopPublishVideo,没有占用channel,不需要关闭");
return {"code": ApeConsts.RETURN_FAILED, "data": "没有占用channel,不需要关闭"};
}
let channelInfo={};
... ... @@ -201,23 +131,22 @@ class VideoChat extends Ape {
this._emit(MessageTypes.MCU_ERROR, MessageTypes.ERR_APE_SEND_FAILED_NO_JOIN);
return {"code": 1, "data": "不能发送Video消息.McuClient还未初始化数据"};
}
return {"code": 1, "data": "不能发送Video消息.McuClient还未初始化数据"};
return {"code": ApeConsts.RETURN_FAILED, "data": "不能发送Video消息.McuClient还未初始化数据"};
}
if (_param == null) {
loger.warn('sendVideoCommandMsg失败,参数错误', _param);
this._emit(MessageTypes.MCU_ERROR, MessageTypes.ERR_APE_INTERFACE_PARAM_WRONG);
return {"code": 1, "data": "sendVideoCommandMsg失败,参数错误"};
;
return {"code": ApeConsts.RETURN_FAILED, "data": "sendVideoCommandMsg失败,参数错误"};
}
// to, message
loger.log('发送Video消息.', _param);
if (_param.actionType != null && _param.actionType == ApeConsts.MEDIA_ACTION_OPEN_CAMERA) {
//判断当前开启的视频数量是否已经是最大值,如果已经是最大值,不能再开启
let freeChannel = this.getFreeVideoChannel();
let freeChannel = this.mediaModule.getFreeMediaChannel();
if (freeChannel == 0) {
loger.warn('sendVideoCommandMsg,不能再打开更多的设备', _param);
return {"code": 1, "data": "不能再打开更多的设备"};
return {"code": ApeConsts.RETURN_FAILED, "data": "不能再打开更多的设备"};
}
}
/* message RCVideoSendDataRequestPdu {
... ... @@ -246,7 +175,7 @@ class VideoChat extends Ape {
loger.log('发送公聊Video消息.');
this.sendChatUniform(videoSendPdu);
}
return {"code": 0, "data": ""};
return {"code": ApeConsts.RETURN_SUCCESS, "data": ""};
}
sendTableUpdateHandler(_channelInfo) {
... ... @@ -314,45 +243,15 @@ class VideoChat extends Ape {
if (videoReceivePdu.toNodeId != 0 && videoReceivePdu.toNodeId != GlobalConfig.nodeId) {
loger.log('视频消息不处理 toNodeId=', videoReceivePdu.toNodeId, "my nodeId=", GlobalConfig.nodeId);
} else {
this._emit(MessageTypes.VIDEO_COMMAND, videoReceivePdu);
this._emit(MessageTypes.VIDEO_BROADCAST, videoReceivePdu);
}
}
tableUpdateHandler(owner, itemIdx, itemData) {
// debugger;
let videoChannelInfo = this.unPackPdu(owner, itemIdx, itemData);
//videoChannelInfo.owner = owner;
//videoChannelInfo.channelId = itemIdx;
//videoChannelInfo.status = owner === 0 ? ApeConsts.CHANNEL_STATUS_RELEASED : videoChannelInfo.status;
//loger.log('视频消息处理 tableUpdateHandler.',videoChannelInfo);
this.videoChannels[itemIdx] = videoChannelInfo;
this.mediaModule.mediaChannels[itemIdx] = videoChannelInfo;
this._emit(MessageTypes.VIDEO_UPDATE, videoChannelInfo);
/* switch (videoChannelInfo.status) {
case ApeConsts.CHANNEL_STATUS_RELEASED:
// 只能关闭自己的流
if (this.activeChannelId === videoChannelInfo.channelId) {
this.activeChannelId = 0;
this.activeURL = '';
this.emitVideoChange();
}
break;
case ApeConsts.CHANNEL_STATUS_OPENING:
//_playUrl = "rtmfp://" + Config.mediaServerAddr + ":" + Config.mediaServerPort + "/message/" + _streamName;
//_cdnUrl = "rtmp://" + Config.mediaCDNServerAddr + ":" + Config.mediaCDNServerPort + "/message/" + _streamName;
//this.activeChannelId = videoChannelInfo.channelId;
//// AMS/FMS
//if (this._classInfo.msType ==ApeConsts.MS_TYPE_FMS) {
// this.activeURL = `http://dazhi.3mang.com/live/${this._classInfo.classId}/${this._classInfo.classId}_${videoChannelInfo.channelId}_flash_cam_mic_aac/playlist.m3u8`;
//}else {
// this.activeURL = `http://hls.3mang.com/live/${this._classInfo.classId}_${videoChannelInfo.channelId}_flash_cam_mic_aac/playlist.m3u8`;
//}
// 任何人都可以打开流
this.emitVideoChange();
break;
default:
break;
}*/
}
///////数据的封包和解包/////////////////////////////////////////
... ... @@ -364,14 +263,6 @@ class VideoChat extends Ape {
return null;
}
/* message RCVideoChannelInfoPdu {
optional uint32 status = 1;//开启的状态
optional uint32 channel_id = 2;//唯一的频道id
optional uint32 timestamp = 3;//更新的时间戳
optional uint32 from_node_id = 4;//发起者的id
optional uint32 to_node_id = 5;//接收者的id,(如果是0,所有人都接收)
}*/
//判断type类型,根据type设置不同的参数
let packPduModel = new pdu['RCVideoChannelInfoPdu'];
packPduModel.status = _param.status||ApeConsts.CHANNEL_STATUS_RELEASED;
... ... @@ -403,53 +294,7 @@ class VideoChat extends Ape {
}
return null;
}
//获取当前空闲的channel,返回值为0代表没有空闲的,否则返回的就是空闲的channelId
getFreeVideoChannel() {
loger.log("getFreeVideoChannel");
console.log(this.videoChannels);
let counter = 0;
for (let key in this.videoChannels) {
let item = this.videoChannels[key];
if (item && item.status == ApeConsts.CHANNEL_STATUS_RELEASED) {
return item.channelId;
}
counter++;
}
if (counter < GlobalConfig.maxVideoChannels) {
return ApeConsts.VIDEO_OBJ_TABLE_ID + (counter);
}
return 0;//没有空闲的
}
//获取当前属于nodeId的已经打开的的channel,返回值为0代表没有打开的,否则返回的就是打开的channelId
getOpeningVideoChannel(_nodeId){
if(_nodeId==null||_nodeId==0){
return 0;
}
for (let key in this.videoChannels) {
let item = this.videoChannels[key];
if (item && item.status == ApeConsts.CHANNEL_STATUS_OPENING&&item.fromNodeId==_nodeId) {
return item.channelId;
}
}
return 0;
}
//检查频道是否已经被占用
checkChannelIsOpening(_channelId){
if(_channelId==null){
loger.warn("checkChannelIsOpening error,channel=",_channelId);
return true;
}
let channelInfo=this.videoChannels[_channelId];
if(channelInfo==null||channelInfo.status==ApeConsts.CHANNEL_STATUS_RELEASED){
return false;
}
return true;
}
}
export default VideoChat;
export default VideoApe;
... ...
... ... @@ -740,10 +740,16 @@ message RCGiftSendDataRequestPdu {
optional bytes user_data = 5;
}
message RCAudioSendDataRequestPdu {
message RCAudioSendDataRequestPdu1 {
optional uint32 initiator = 1;
required bytes user_data = 2;
}
message RCAudioSendDataRequestPdu {
required uint32 from_node_id = 1;//发起人
optional uint32 to_node_id = 2;//接收人,如果是0就是所有人都接收
optional uint32 actionType = 3;//消息指令类型;
optional bytes data = 4;//其他数据,这个根据actionType来确定数据的结构
}
message RCVideoSendDataRequestPdu {
required uint32 from_node_id = 1;//发起人
... ... @@ -759,6 +765,17 @@ message RCAudioChannelInfoRecordPdu {
required uint32 bitrate = 4;
required uint32 codec = 5;
}
message RCAudioChannelInfoPdu {
optional uint32 status = 1;//开启的状态
optional uint32 channel_id = 2;//唯一的频道id
optional uint32 timestamp = 3;//更新的时间戳
optional uint32 from_node_id = 4;//发起者的id
optional uint32 to_node_id = 5;//接收者的id,(如果是0,所有人都接收)
optional uint32 media_type = 6;//媒体类型:视频(包含音频)或音频
optional uint32 class_id = 7;//课堂号
optional string site_id = 8;//站点号
optional string user_id = 9;//用户的userId
}
message RCVideoChannelInfoPdu {
optional uint32 status = 1;//开启的状态
... ... @@ -907,6 +924,7 @@ message RCClassStatusInfoPdu {
optional uint32 active_doc_cur_page=21;//当前激活的文档的当前页
}
//end
`;
... ...