李勇

1.视频模块增加推流播流接口的逻辑判断;

2.用户退出会议的时候,如果当前自己正在推流,需要停止推流并且同步数据
@@ -41,6 +41,7 @@ let _joinClassSuccessCallBackFun; @@ -41,6 +41,7 @@ let _joinClassSuccessCallBackFun;
41 //监听mcu所有错误异常回调函数 41 //监听mcu所有错误异常回调函数
42 let _mcuErrorCallBackFun; 42 let _mcuErrorCallBackFun;
43 43
  44 +let leaveDelay;
44 45
45 //MCUClient 外部实例化主类 46 //MCUClient 外部实例化主类
46 export default class MessageEntrance extends Emiter { 47 export default class MessageEntrance extends Emiter {
@@ -311,6 +312,14 @@ export default class MessageEntrance extends Emiter { @@ -311,6 +312,14 @@ export default class MessageEntrance extends Emiter {
311 GlobalConfig.MCUServerPort = server.split(":")[1]; 312 GlobalConfig.MCUServerPort = server.split(":")[1];
312 } 313 }
313 314
  315 + //视频推流播流地址
  316 + if (_data.ms) {
  317 + //MS地址默认使用第一个
  318 + let server = _data.ms.split(";")[0];
  319 + GlobalConfig.MSServerIP = server.split(":")[0];
  320 + GlobalConfig.MSServerPort = server.split(":")[1];
  321 + }
  322 +
314 GlobalConfig.docServer = _data.doc; 323 GlobalConfig.docServer = _data.doc;
315 GlobalConfig.h5_mcu_list = _data.h5_mcu_list; 324 GlobalConfig.h5_mcu_list = _data.h5_mcu_list;
316 GlobalConfig.h5Module = _data.h5Module; 325 GlobalConfig.h5Module = _data.h5Module;
@@ -606,6 +615,9 @@ export default class MessageEntrance extends Emiter { @@ -606,6 +615,9 @@ export default class MessageEntrance extends Emiter {
606 } 615 }
607 // 离开会议 616 // 离开会议
608 _leaveClass() { 617 _leaveClass() {
  618 + if(_video_ape){
  619 + _video_ape.stopPublishVideo();
  620 + }
609 if(_confer_ape){ 621 if(_confer_ape){
610 _confer_ape.leaveClass(); 622 _confer_ape.leaveClass();
611 } 623 }
@@ -637,7 +649,7 @@ export default class MessageEntrance extends Emiter { @@ -637,7 +649,7 @@ export default class MessageEntrance extends Emiter {
637 649
638 sendVideoCommandMsg(_param){ 650 sendVideoCommandMsg(_param){
639 if(_video_ape){ 651 if(_video_ape){
640 - _video_ape.sendVideoCommandMsg(_param); 652 + return _video_ape.sendVideoCommandMsg(_param);
641 } 653 }
642 } 654 }
643 655
@@ -141,7 +141,9 @@ class EverSocket extends Emiter { @@ -141,7 +141,9 @@ class EverSocket extends Emiter {
141 if (this._lastActiveTime && 141 if (this._lastActiveTime &&
142 this._lastActiveTime >= pongTime - EverSocket.PONG_INTERVAL && 142 this._lastActiveTime >= pongTime - EverSocket.PONG_INTERVAL &&
143 this._lastActiveTime <= pongTime 143 this._lastActiveTime <= pongTime
144 - ) {} else { 144 + ) {
  145 +
  146 + } else {
145 loger.warn('---服务器PINGPONG超时-----'); 147 loger.warn('---服务器PINGPONG超时-----');
146 this._reConnection(); 148 this._reConnection();
147 } 149 }
@@ -244,7 +244,7 @@ GlobalConfig.statusCode_3={"code":3,message:"已经离开会议"}; @@ -244,7 +244,7 @@ GlobalConfig.statusCode_3={"code":3,message:"已经离开会议"};
244 GlobalConfig.statusCode_4={"code":4,message:"未知状态"}; 244 GlobalConfig.statusCode_4={"code":4,message:"未知状态"};
245 245
246 GlobalConfig.md5=""; 246 GlobalConfig.md5="";
247 -GlobalConfig.msType=1; 247 +GlobalConfig.msType=1;//目前固定用这个
248 GlobalConfig.mcuDelay=3000;//默认的延迟时间 248 GlobalConfig.mcuDelay=3000;//默认的延迟时间
249 GlobalConfig.docDelay=1600;//文档模块加入成功之后延迟发送送成功的消息给主模块 249 GlobalConfig.docDelay=1600;//文档模块加入成功之后延迟发送送成功的消息给主模块
250 GlobalConfig.portal="112.126.80.182:80"; 250 GlobalConfig.portal="112.126.80.182:80";
@@ -253,6 +253,10 @@ GlobalConfig.port="80"; @@ -253,6 +253,10 @@ GlobalConfig.port="80";
253 GlobalConfig.MCUServerIP="114.215.195.70"; 253 GlobalConfig.MCUServerIP="114.215.195.70";
254 GlobalConfig.docServer="";//当前的文档地址加载的服务器地址 254 GlobalConfig.docServer="";//当前的文档地址加载的服务器地址
255 GlobalConfig.MCUServerPort=9003; 255 GlobalConfig.MCUServerPort=9003;
  256 +
  257 +GlobalConfig.MSServerIP = "";//推流 播流的地址
  258 +GlobalConfig.MSServerPort ="";
  259 +
256 GlobalConfig.maxVideoChannels=0; 260 GlobalConfig.maxVideoChannels=0;
257 GlobalConfig.maxAudioChannels=0; 261 GlobalConfig.maxAudioChannels=0;
258 GlobalConfig.maxMediaChannels=0; 262 GlobalConfig.maxMediaChannels=0;
@@ -24,350 +24,344 @@ import EngineUtils from 'EngineUtils'; @@ -24,350 +24,344 @@ import EngineUtils from 'EngineUtils';
24 let loger = Loger.getLoger('VideoChat'); 24 let loger = Loger.getLoger('VideoChat');
25 25
26 class VideoChat extends Ape { 26 class VideoChat extends Ape {
27 - constructor() {  
28 - super(  
29 - ApeConsts.VIDEO_SESSION_ID,  
30 - ApeConsts.VIDEO_SESSION_NAME,  
31 - ApeConsts.VIDEO_SESSION_TAG  
32 -  
33 -  
34 - );  
35 -  
36 - //Attributes  
37 - this.videoChannels = {};  
38 -  
39 - // Ape Models  
40 - this.registerKey(this._session_id, this._session_name, this._session_tag, new ArrayBuffer);  
41 - this.registerObj(pdu.RCPDU_REG_REGISTER_TABLE, ApeConsts.VIDEO_OBJ_TABLE_ID, ApeConsts.VIDEO_OBJ_TABLE_NAME, ApeConsts.VIDEO_OBJ_TABLE_TAG, 0, new ArrayBuffer);  
42 -  
43 - // videoApe 监听视频控制消息,用户之间的消息传递  
44 - this.on(pdu.RCPDU_VIDEO_SEND_DATA_REQUEST, this.videoCommandHandler.bind(this));  
45 - }  
46 - /////////////发送数据操作//////////////////////////////////////////////////////  
47 - //获取播流地址  
48 - getPlayVideoPath(_param){  
49 - loger.log('getPlayVideoPath');  
50 - return {"code":0,"data":"播放流地址XXXXXXXXXXXXXXXXXXXXX"};  
51 - }  
52 - //获取推流地址  
53 - getPublishVideoPath(_param){  
54 - loger.log('getPublishVideoPath');  
55 - //判断当前开启的视频数量是否已经是最大值,如果已经是最大值,不能再开启  
56 -  
57 - return {"code":0,"data":"推流地址XXXXXXXXXXXXXXXXXXXXXXX"};  
58 - }  
59 -  
60 - //推流  
61 - publishVideo(_param){  
62 - loger.log('publishVideo -> maxVideoChannels',GlobalConfig.maxVideoChannels);  
63 - this.sendTableUpdateHandler();  
64 - /* protected function startPushMessageFlash():void  
65 - {  
66 - if((!Config.enableCDNServer && !Config.isH5Moudle) ||  
67 - Config.customer == Config.customerTaobao ||  
68 - Config.msType == Config.MS_TYPE_FMS  
69 - )  
70 - return;  
71 -  
72 - var obj:Object = new Object;  
73 - obj.confID = Config.confID;  
74 - obj.streamType = RCNetStream.PRT_CAM_SOUND_FLASH;  
75 - obj.uriPush = "rtmp://" + Config.liveServerAddr + ":" + Config.liveServerPort + "/3m/" + streamName;  
76 - _streamPublished.send("startPush", obj);  
77 - log.info("startPushMessageFlash,startPush streamType:" + obj.streamType);  
78 - }*/  
79 -  
80 -/* var rtmpUrl:String = "";  
81 - if(Config.msType == Config.MS_TYPE_DEFAULT)  
82 - {  
83 - rtmpUrl = "rtmp://" + mediaServerAddr + ":" + mediaServerPort; 27 + constructor() {
  28 + super(
  29 + ApeConsts.VIDEO_SESSION_ID,
  30 + ApeConsts.VIDEO_SESSION_NAME,
  31 + ApeConsts.VIDEO_SESSION_TAG
  32 + );
  33 +
  34 + //Attributes
  35 + this.videoChannels = {};
  36 +
  37 + // Ape Models
  38 + this.registerKey(this._session_id, this._session_name, this._session_tag, new ArrayBuffer);
  39 + this.registerObj(pdu.RCPDU_REG_REGISTER_TABLE, ApeConsts.VIDEO_OBJ_TABLE_ID, ApeConsts.VIDEO_OBJ_TABLE_NAME, ApeConsts.VIDEO_OBJ_TABLE_TAG, 0, new ArrayBuffer);
  40 +
  41 + // videoApe 监听视频控制消息,用户之间的消息传递
  42 + this.on(pdu.RCPDU_VIDEO_SEND_DATA_REQUEST, this.receiveVideoCommandHandler.bind(this));
84 } 43 }
85 - else if(Config.msType == Config.MS_TYPE_FMS)//fms下链接地址 pzm+ 2016.4.12  
86 - {  
87 - if(Config.clientType == Config.CT_RECORDPLAYER)  
88 - {  
89 - rtmpUrl = "rtmp://" + mediaServerAddr + ":" + mediaServerPort + "/vod";  
90 - }  
91 - else  
92 - {  
93 - rtmpUrl = "rtmp://" + mediaServerAddr + ":" + mediaServerPort + "/live/" + Config.confID;  
94 - }  
95 - }*/  
96 - }  
97 - //停止推流  
98 - stopPublishVideo(_param){  
99 - loger.log('stopPublishVideo -> maxVideoChannels',GlobalConfig.maxVideoChannels);  
100 - this.sendTableUpdateHandler();  
101 - }  
102 -  
103 - //  
104 - //aaaa(){  
105 - // if (event.cmd == ApplicationFacade.OPEN_MIC)  
106 - // {  
107 - // if (audioDeviceOpenedUsers() >= Config.maxAudioChannels)  
108 - // {  
109 - // item.closeAudioDevice();  
110 - // Alert.show(resourceManager.getString('meeting', 'CannotOpenDeviceAnyMore'),  
111 - // resourceManager.getString('meeting', 'AlertWarning'), Alert.OK, item);  
112 - // event.stopImmediatePropagation();  
113 - // }  
114 - // else  
115 - // {  
116 - // super.updateCamMicStatus(event.camOpened, event.micOpened, event.node_id);  
117 - // }  
118 - // }  
119 - // else if (event.cmd == ApplicationFacade.OPEN_CAMERA)  
120 - // {  
121 - // if (videoDeviceOpenedUsers() >= Config.maxVideoChannels)  
122 - // {  
123 - // item.closeVideoDevice();  
124 - // Alert.show(resourceManager.getString('meeting', 'CannotOpenDeviceAnyMore'),  
125 - // resourceManager.getString('meeting', 'AlertWarning'), Alert.OK, item);  
126 - // event.stopImmediatePropagation();  
127 - // }  
128 - // else  
129 - // {  
130 - // super.updateCamMicStatus(event.camOpened, event.micOpened, event.node_id);  
131 - // }  
132 - // }  
133 - //}  
134 -  
135 - curAudioDeviceOpenedUsers()  
136 - {  
137 - //var openedUsers:int = 0;  
138 - //var index:int = 0;  
139 - //  
140 - //for(index = 0; index < _userArray.length; index++)  
141 - //{  
142 - // var tmpNode:RCNodeInfoRecordPdu = RCNodeInfoRecordPdu(_userArray.getItemAt(index));  
143 - // if(tmpNode.status & RCNodeStatus_E.NR_MIC_OPEN)  
144 - // {  
145 - // openedUsers++;  
146 - // }  
147 - //}  
148 - //  
149 - //return openedUsers;  
150 - }  
151 -  
152 - curVideoDeviceOpenedUsers()  
153 - {  
154 - //var openedUsers:int = 0;  
155 - //var index:int = 0;  
156 - //  
157 - //for(index = 0; index < _userArray.length; index++)  
158 - //{  
159 - // var tmpNode:RCNodeInfoRecordPdu = RCNodeInfoRecordPdu(_userArray.getItemAt(index));  
160 - // if(tmpNode.status & RCNodeStatus_E.NR_CAMERA_OPEN)  
161 - // {  
162 - // openedUsers++;  
163 - // }  
164 - //}  
165 - //return openedUsers;  
166 - }  
167 -  
168 - sendVideoCommandMsg(_param) {  
169 - if(this._classInfo===null||EngineUtils.isEmptyObject(this._classInfo)){  
170 - loger.log('不能发送Video消息.McuClient还未初始化数据!');  
171 - if(GlobalConfig.getCurrentStatus().code==0||GlobalConfig.getCurrentStatus().code==1){  
172 - this._emit(MessageTypes.MCU_ERROR,MessageTypes.ERR_APE_SEND_FAILED_NO_JOIN);  
173 - return ;  
174 - }  
175 - return 1;  
176 - }  
177 - if(_param==null){  
178 - loger.warn('sendVideoCommandMsg失败,参数错误',paramInfo);  
179 - this._emit(MessageTypes.MCU_ERROR,MessageTypes.ERR_APE_INTERFACE_PARAM_WRONG);  
180 - return 1; 44 +
  45 + /////////////发送数据操作////////////////////////////////////////////
  46 + //获取播流地址
  47 + getPlayVideoPath(_param) {
  48 + loger.log('getPlayVideoPath');
  49 + if (_param == null||_param.classId == null||_param.channelId == null|| _param.timestamp==null) {
  50 + loger.warn('getPlayVideoPath,参数错误', _param);
  51 + this._emit(MessageTypes.MCU_ERROR, MessageTypes.ERR_APE_INTERFACE_PARAM_WRONG);
  52 + return {"code": 1, "data": "getPlayVideoPath,参数错误"};
  53 + }
  54 + let path = "";
  55 + if (_param.type == "m3u8") {
  56 + path = "http://" + GlobalConfig.MSServerIP + ":80" + "/hls/" + _param.classId + "_" + _param.channelId + "_" + _param.timestamp + ".m3u8";
  57 + } else {
  58 + let port = (GlobalConfig.MSServerPort == "" || GlobalConfig.MSServerPort == null) ? "" : ":" + GlobalConfig.MSServerPort;
  59 + path = "rtmp://" + GlobalConfig.MSServerIP + port + "/live/" + _param.classId + "_" + _param.channelId + "_" + _param.timestamp;
  60 + }
  61 + return {"code": 0, "data": path};
181 } 62 }
182 - // to, message  
183 - loger.log('发送Video消息.', _param);  
184 63
  64 + //获取推流地址
  65 + getPublishVideoPath(_param) {
  66 + loger.log('getPublishVideoPath');
  67 + //if(_param==null){
  68 + // loger.warn('getPublishVideoPath,参数错误',_param);
  69 + // this._emit(MessageTypes.MCU_ERROR,MessageTypes.ERR_APE_INTERFACE_PARAM_WRONG);
  70 + // return {"code":1,"data":"getPublishVideoPath,参数错误"};;
  71 + //}
  72 + //判断当前开启的视频数量是否已经是最大值,如果已经是最大值,不能再开启
  73 + let freeChannel = this.getFreeVideoChannel();
  74 + if (freeChannel == 0) {
  75 + return {"code": 1, "data": "不能再打开更多的设备"};
  76 + }
185 77
  78 + let port = (GlobalConfig.MSServerPort == "" || GlobalConfig.MSServerPort == null) ? "" : ":" + GlobalConfig.MSServerPort;
  79 + let timestamp = EngineUtils.creatTimestamp();
  80 + let publishUrl = "rtmp://" + GlobalConfig.MSServerIP + port + "/live/" + GlobalConfig.classId + "_" + freeChannel + "_" + timestamp;
  81 + return {"code": 0, "data": {"channelId": freeChannel, "timestamp": timestamp, "publishUrl": publishUrl}};
  82 + }
186 83
187 - /* message RCVideoSendDataRequestPdu {  
188 - required uint32 from_node_id = 1;//发起人  
189 - optional uint32 to_node_id = 2;//接收人,如果是0就是所有人都接收  
190 - optional uint32 actionType = 3;//消息指令类型;  
191 - optional bytes data = 4;//其他数据,这个根据actionType来确定数据的结构  
192 - }*/ 84 + //推流
  85 + publishVideo(_param) {
  86 + loger.log('publishVideo -> maxVideoChannels', GlobalConfig.maxVideoChannels);
193 87
194 - let videoSendPdu = new pdu['RCVideoSendDataRequestPdu'];  
195 - videoSendPdu.type = pdu.RCPDU_VIDEO_SEND_DATA_REQUEST;  
196 - videoSendPdu.isPublic = true; 88 + //同一个nodeId只允许推一个流,如果已经推了就不能再推
  89 + if(this.getOpeningVideoChannel(GlobalConfig.nodeId)!=0){
  90 + loger.warn("publishVideo,已经存在一个流,不能再推");
  91 + return;
  92 + }
197 93
198 - videoSendPdu.fromNodeId = GlobalConfig.nodeId;//发起人  
199 - videoSendPdu.toNodeId = parseInt(_param.toNodeID)||0;//接收者,0就是所有人  
200 - videoSendPdu.actionType = parseInt(_param.actionType)||ApeConsts.MEDIA_ACTION_DEFAULT; 94 + let freeChannel = this.getFreeVideoChannel();
  95 + if (freeChannel == 0) {
  96 + loger.warn("publishVideo,没有空闲的channel ");
  97 + return {"code": 1, "data": "不能再打开更多的设备"};
  98 + }
  99 + let channelInfo={};
  100 + channelInfo.status=ApeConsts.CHANNEL_STATUS_OPENING;
  101 + channelInfo.fromNodeId=GlobalConfig.nodeId;
  102 + channelInfo.channelId=freeChannel;
  103 + channelInfo.timestamp=EngineUtils.creatTimestamp();
  104 + channelInfo.classId=GlobalConfig.classId;
  105 + channelInfo.toNodeId=0;
  106 + channelInfo.mediaType=ApeConsts.MEDIA_TYPE_VIDEO;
  107 + this.sendTableUpdateHandler(channelInfo);
  108 + }
201 109
202 - videoSendPdu.data=this._rCArrayBufferUtil.strToUint8Array("h5" + _param.data);//开头两个字会乱码 110 + //停止推流
  111 + stopPublishVideo(_param) {
  112 + loger.log('stopPublishVideo -> maxVideoChannels', GlobalConfig.maxVideoChannels);
  113 + let openingChannel = this.getOpeningVideoChannel(GlobalConfig.nodeId);
  114 + if (openingChannel == 0) {
  115 + loger.warn("stopPublishVideo,没有打开的channel");
  116 + return {"code": 1, "data": "没有打开的设备channel"};
  117 + }
203 118
204 - if (!videoSendPdu.isPublic && 0!=videoSendPdu.toNodeId) {  
205 - //发送给制定的人  
206 - loger.log('发送私聊Video消息.');  
207 - this.send(videoSendPdu);  
208 - } else {  
209 - //发送给所有人  
210 - loger.log('发送公聊Video消息.');  
211 - this.sendChatUniform(videoSendPdu); 119 + let channelInfo={};
  120 + channelInfo.status=ApeConsts.CHANNEL_STATUS_RELEASED;
  121 + channelInfo.fromNodeId=0;
  122 + channelInfo.channelId=openingChannel;
  123 + channelInfo.timestamp=0;
  124 + channelInfo.classId=GlobalConfig.classId;
  125 + channelInfo.toNodeId=0;
  126 + channelInfo.mediaType=ApeConsts.MEDIA_TYPE_DEFAULT;
  127 + this.sendTableUpdateHandler(channelInfo);
212 } 128 }
213 - return 0;  
214 - }  
215 -  
216 - sendTableUpdateHandler(){  
217 - loger.log("video===sendTableUpdateHandler ");  
218 - /* //验证坐标点集合数组是否合法  
219 - if(_docDataModel==null||_itemIdx==null){  
220 - this._emit(MessageTypes.MCU_ERROR,MessageTypes.ERR_APE_INTERFACE_PARAM_WRONG);  
221 - return null;  
222 - }*/  
223 - loger.log("video===sendTableUpdateHandler ");  
224 -  
225 - let updateModelPdu=this.packPdu({},ApeConsts.VIDEO_OBJ_TABLE_ID+2);  
226 -  
227 -  
228 -  
229 - let tableItemPdu = new pdu['RCRegistryTableItemPdu'];  
230 - tableItemPdu.itemIdx=ApeConsts.VIDEO_OBJ_TABLE_ID+2;//直接用时间戳作为id  
231 - tableItemPdu.owner = 0;//收到flash的是这个值,不清楚先写固定  
232 - tableItemPdu.itemData =updateModelPdu.toArrayBuffer();  
233 -  
234 - //insert  
235 - let tableInsertItemPdu = new pdu['RCRegistryTableUpdateItemPdu'];  
236 - //optional RCPduType_E type = 1 [default = RCPDU_REG_TABLE_UPDATE_PDU];  
237 - //repeated RCRegistryTableItemPdu items = 2;  
238 - tableInsertItemPdu.type = pdu.RCPDU_REG_TABLE_UPDATE_PDU;//  
239 - tableInsertItemPdu.items.push(tableItemPdu);  
240 -  
241 - let updateObjPdu = new pdu['RCRegistryUpdateObjPdu'];  
242 - updateObjPdu.objId = ApeConsts.VIDEO_OBJ_TABLE_ID;//  
243 - updateObjPdu.subType = tableInsertItemPdu.type;  
244 - updateObjPdu.userData = tableInsertItemPdu.toArrayBuffer();  
245 -  
246 - //同步  
247 - let adapterItemPdu = new pdu['RCAdapterItemPdu'];  
248 - adapterItemPdu.type = pdu.RCPDU_REG_UPDATE_OBJ;  
249 - adapterItemPdu.itemData = updateObjPdu.toArrayBuffer();  
250 -  
251 - let adapterPdu = new pdu['RCAdapterPdu'];  
252 - adapterPdu.type = pdu.RCPDU_REG_ADAPTER;  
253 - adapterPdu.item.push(adapterItemPdu);  
254 -  
255 - loger.log("发送更新文档.itemIdx="+tableItemPdu.itemIdx);  
256 - this.sendUniform(adapterPdu,true);  
257 - }  
258 - /////收到消息处理/////////////////////////////////////////////////////////////////////////////////  
259 -  
260 - // 视频消息处理,内部处理,不需要告诉应用层  
261 - videoCommandHandler(_data) {  
262 - let videoReceivePdu = pdu['RCVideoSendDataRequestPdu'].decode(_data);  
263 - if(videoReceivePdu==null) {  
264 - loger.warn("视频消息处理,收到的消息为null,不做处理");  
265 - return; 129 +
  130 + sendVideoCommandMsg(_param) {
  131 + if (this._classInfo === null || EngineUtils.isEmptyObject(this._classInfo)) {
  132 + loger.log('不能发送Video消息.McuClient还未初始化数据!');
  133 + if (GlobalConfig.getCurrentStatus().code == 0 || GlobalConfig.getCurrentStatus().code == 1) {
  134 + this._emit(MessageTypes.MCU_ERROR, MessageTypes.ERR_APE_SEND_FAILED_NO_JOIN);
  135 + return {"code": 1, "data": "不能发送Video消息.McuClient还未初始化数据"};
  136 + }
  137 + return {"code": 1, "data": "不能发送Video消息.McuClient还未初始化数据"};
  138 + }
  139 + if (_param == null) {
  140 + loger.warn('sendVideoCommandMsg失败,参数错误', _param);
  141 + this._emit(MessageTypes.MCU_ERROR, MessageTypes.ERR_APE_INTERFACE_PARAM_WRONG);
  142 + return {"code": 1, "data": "sendVideoCommandMsg失败,参数错误"};
  143 + ;
  144 + }
  145 + // to, message
  146 + loger.log('发送Video消息.', _param);
  147 +
  148 + if (_param.actionType != null && _param.actionType == ApeConsts.MEDIA_ACTION_OPEN_CAMERA) {
  149 + //判断当前开启的视频数量是否已经是最大值,如果已经是最大值,不能再开启
  150 + let freeChannel = this.getFreeVideoChannel();
  151 + if (freeChannel == 0) {
  152 + loger.warn('sendVideoCommandMsg,不能再打开更多的设备', _param);
  153 + return {"code": 1, "data": "不能再打开更多的设备"};
  154 + }
  155 + }
  156 + /* message RCVideoSendDataRequestPdu {
  157 + required uint32 from_node_id = 1;//发起人
  158 + optional uint32 to_node_id = 2;//接收人,如果是0就是所有人都接收
  159 + optional uint32 actionType = 3;//消息指令类型;
  160 + optional bytes data = 4;//其他数据,这个根据actionType来确定数据的结构
  161 + }*/
  162 +
  163 + let videoSendPdu = new pdu['RCVideoSendDataRequestPdu'];
  164 + videoSendPdu.type = pdu.RCPDU_VIDEO_SEND_DATA_REQUEST;
  165 + videoSendPdu.isPublic = true;
  166 +
  167 + videoSendPdu.fromNodeId = GlobalConfig.nodeId;//发起人
  168 + videoSendPdu.toNodeId = parseInt(_param.toNodeID) || 0;//接收者,0就是所有人
  169 + videoSendPdu.actionType = parseInt(_param.actionType) || ApeConsts.MEDIA_ACTION_DEFAULT;
  170 +
  171 + videoSendPdu.data = this._rCArrayBufferUtil.strToUint8Array("h5" + _param.data);//开头两个字会乱码
  172 +
  173 + if (!videoSendPdu.isPublic && 0 != videoSendPdu.toNodeId) {
  174 + //发送给制定的人
  175 + loger.log('发送私聊Video消息.');
  176 + this.send(videoSendPdu);
  177 + } else {
  178 + //发送给所有人
  179 + loger.log('发送公聊Video消息.');
  180 + this.sendChatUniform(videoSendPdu);
  181 + }
  182 + return {"code": 0, "data": ""};
266 } 183 }
267 - videoReceivePdu.data=this._rCArrayBufferUtil.uint8ArrayToStr(videoReceivePdu.data,2);//开头两个字会乱码  
268 - loger.log('视频消息处理 videoCommandHandler.', videoReceivePdu);  
269 -  
270 - //判断接收者的id,如果不是0,并且也不是自己的nodeId,那么消息不做处理  
271 - if(videoReceivePdu.toNodeId!=0&&videoReceivePdu.toNodeId!=GlobalConfig.nodeId){  
272 - loger.log('视频消息不处理 toNodeId=', videoReceivePdu.toNodeId,"my nodeId=",GlobalConfig.nodeId);  
273 - }else {  
274 - this._emit(MessageTypes.VIDEO_COMMAND,videoReceivePdu); 184 +
  185 + sendTableUpdateHandler(_channelInfo) {
  186 + loger.log("video===sendTableUpdateHandler ");
  187 + /* //验证坐标点集合数组是否合法
  188 + if(_docDataModel==null||_itemIdx==null){
  189 + this._emit(MessageTypes.MCU_ERROR,MessageTypes.ERR_APE_INTERFACE_PARAM_WRONG);
  190 + return null;
  191 + }*/
  192 +
  193 + //let freeChannel=this.getFreeVideoChannel();
  194 + //if(freeChannel==0){
  195 + // loger.warn("sendTableUpdateHandler,没有空闲的channel ");
  196 + // return;
  197 + //}
  198 + let updateModelPdu = this.packPdu(_channelInfo, _channelInfo.channelId);//let updateModelPdu=this.packPdu({},ApeConsts.VIDEO_OBJ_TABLE_ID+2);
  199 +
  200 + let tableItemPdu = new pdu['RCRegistryTableItemPdu'];
  201 + tableItemPdu.itemIdx = _channelInfo.channelId;//tableItemPdu.itemIdx=ApeConsts.VIDEO_OBJ_TABLE_ID+2;
  202 + tableItemPdu.owner = 0;//收到flash的是这个值,不清楚先写固定
  203 + tableItemPdu.itemData = updateModelPdu.toArrayBuffer();
  204 +
  205 + //insert
  206 + let tableInsertItemPdu = new pdu['RCRegistryTableUpdateItemPdu'];
  207 + //optional RCPduType_E type = 1 [default = RCPDU_REG_TABLE_UPDATE_PDU];
  208 + //repeated RCRegistryTableItemPdu items = 2;
  209 + tableInsertItemPdu.type = pdu.RCPDU_REG_TABLE_UPDATE_PDU;//
  210 + tableInsertItemPdu.items.push(tableItemPdu);
  211 +
  212 + let updateObjPdu = new pdu['RCRegistryUpdateObjPdu'];
  213 + updateObjPdu.objId = ApeConsts.VIDEO_OBJ_TABLE_ID;//
  214 + updateObjPdu.subType = tableInsertItemPdu.type;
  215 + updateObjPdu.userData = tableInsertItemPdu.toArrayBuffer();
  216 +
  217 + //同步
  218 + let adapterItemPdu = new pdu['RCAdapterItemPdu'];
  219 + adapterItemPdu.type = pdu.RCPDU_REG_UPDATE_OBJ;
  220 + adapterItemPdu.itemData = updateObjPdu.toArrayBuffer();
  221 +
  222 + let adapterPdu = new pdu['RCAdapterPdu'];
  223 + adapterPdu.type = pdu.RCPDU_REG_ADAPTER;
  224 + adapterPdu.item.push(adapterItemPdu);
  225 +
  226 + loger.log("发送更新VIDEO.itemIdx=" + tableItemPdu.itemIdx);
  227 + this.sendUniform(adapterPdu, true);
275 } 228 }
276 - }  
277 -  
278 - tableUpdateHandler(owner, itemIdx, itemData) {  
279 - // debugger;  
280 - let videoChannelInfo =this.unPackPdu(owner, itemIdx, itemData);  
281 - //videoChannelInfo.owner = owner;  
282 - //videoChannelInfo.channelId = itemIdx;  
283 - //videoChannelInfo.status = owner === 0 ? ApeConsts.CHANNEL_STATUS_RELEASED : videoChannelInfo.status;  
284 - //loger.log('视频消息处理 tableUpdateHandler.',videoChannelInfo);  
285 - this.videoChannels[itemIdx] = videoChannelInfo;  
286 -  
287 - this._emit(MessageTypes.VIDEO_UPDATE,videoChannelInfo);  
288 -/* switch (videoChannelInfo.status) {  
289 - case ApeConsts.CHANNEL_STATUS_RELEASED:  
290 - // 只能关闭自己的流  
291 - if (this.activeChannelId === videoChannelInfo.channelId) {  
292 - this.activeChannelId = 0;  
293 - this.activeURL = '';  
294 - this.emitVideoChange(); 229 +
  230 + /////收到消息处理//////////////////////////////////////////////////
  231 +
  232 + // 视频消息处理,内部处理,不需要告诉应用层
  233 + receiveVideoCommandHandler(_data) {
  234 + let videoReceivePdu = pdu['RCVideoSendDataRequestPdu'].decode(_data);
  235 + if (videoReceivePdu == null) {
  236 + loger.warn("视频消息处理,收到的消息为null,不做处理");
  237 + return;
  238 + }
  239 + videoReceivePdu.data = this._rCArrayBufferUtil.uint8ArrayToStr(videoReceivePdu.data, 2);//开头两个字会乱码
  240 + loger.log('视频消息处理 receiveVideoCommandHandler.', videoReceivePdu);
  241 +
  242 + //判断接收者的id,如果不是0,并且也不是自己的nodeId,那么消息不做处理
  243 + if (videoReceivePdu.toNodeId != 0 && videoReceivePdu.toNodeId != GlobalConfig.nodeId) {
  244 + loger.log('视频消息不处理 toNodeId=', videoReceivePdu.toNodeId, "my nodeId=", GlobalConfig.nodeId);
  245 + } else {
  246 + this._emit(MessageTypes.VIDEO_COMMAND, videoReceivePdu);
295 } 247 }
296 - break;  
297 - case ApeConsts.CHANNEL_STATUS_OPENING:  
298 - //_playUrl = "rtmfp://" + Config.mediaServerAddr + ":" + Config.mediaServerPort + "/message/" + _streamName;  
299 - //_cdnUrl = "rtmp://" + Config.mediaCDNServerAddr + ":" + Config.mediaCDNServerPort + "/message/" + _streamName;  
300 - //this.activeChannelId = videoChannelInfo.channelId;  
301 - //// AMS/FMS  
302 - //if (this._classInfo.msType ==ApeConsts.MS_TYPE_FMS) {  
303 - // this.activeURL = `http://dazhi.3mang.com/live/${this._classInfo.classId}/${this._classInfo.classId}_${videoChannelInfo.channelId}_flash_cam_mic_aac/playlist.m3u8`;  
304 - //}else {  
305 - // this.activeURL = `http://hls.3mang.com/live/${this._classInfo.classId}_${videoChannelInfo.channelId}_flash_cam_mic_aac/playlist.m3u8`;  
306 - //}  
307 - // 任何人都可以打开流  
308 - this.emitVideoChange();  
309 - break;  
310 - default:  
311 - break;  
312 - }*/  
313 - }  
314 -  
315 - emitVideoChange() {  
316 - this._emit(MessageTypes.VIDEO_UPDATE, {  
317 - activeChannelId: this.activeChannelId,  
318 - HLSURL: this.activeURL,  
319 - });  
320 - };  
321 -  
322 -  
323 - ///////数据的封包和解包/////////////////////////////////////////  
324 - packPdu(_param,_itemIdx){  
325 - loger.log("VIDEO===packPdu ");  
326 - //验证坐标点集合数组是否合法  
327 - if(_param==null||_itemIdx==null){  
328 - this._emit(MessageTypes.MCU_ERROR,MessageTypes.ERR_APE_INTERFACE_PARAM_WRONG);  
329 - return null;  
330 } 248 }
331 249
332 - /* message RCVideoChannelInfoPdu {  
333 - optional uint32 status = 1;//开启的状态  
334 - optional uint32 channel_id = 2;//唯一的频道id  
335 - optional uint32 timestamp = 3;//更新的时间戳  
336 - optional uint32 from_node_id = 4;//发起者的id  
337 - optional uint32 to_node_id = 5;//接收者的id,(如果是0,所有人都接收)  
338 - }*/  
339 -  
340 - //判断type类型,根据type设置不同的参数  
341 - let packPduModel =new pdu['RCVideoChannelInfoPdu'];  
342 - packPduModel.status=ApeConsts.CHANNEL_STATUS_OPENING;  
343 - packPduModel.channelId=_itemIdx;  
344 - packPduModel.classId=parseInt(GlobalConfig.classId);  
345 - packPduModel.mediaType=ApeConsts.MEDIA_TYPE_VIDEO;  
346 - packPduModel.timestamp=EngineUtils.creatTimestamp();  
347 - packPduModel.fromNodeId =GlobalConfig.nodeId;  
348 - packPduModel.toNodeId = 0;  
349 - console.log(packPduModel);  
350 - return packPduModel;  
351 - }  
352 -  
353 - unPackPdu(owner, itemIdx,itemData){  
354 - loger.log("VIDEO==unPackPdu ");  
355 - if(owner==null||itemIdx==null||itemData==null){  
356 - this._emit(MessageTypes.MCU_ERROR,MessageTypes.ERR_APE_INTERFACE_PARAM_WRONG);  
357 - return null; 250 + tableUpdateHandler(owner, itemIdx, itemData) {
  251 + // debugger;
  252 + let videoChannelInfo = this.unPackPdu(owner, itemIdx, itemData);
  253 + //videoChannelInfo.owner = owner;
  254 + //videoChannelInfo.channelId = itemIdx;
  255 + //videoChannelInfo.status = owner === 0 ? ApeConsts.CHANNEL_STATUS_RELEASED : videoChannelInfo.status;
  256 + //loger.log('视频消息处理 tableUpdateHandler.',videoChannelInfo);
  257 + this.videoChannels[itemIdx] = videoChannelInfo;
  258 +
  259 + this._emit(MessageTypes.VIDEO_UPDATE, videoChannelInfo);
  260 + /* switch (videoChannelInfo.status) {
  261 + case ApeConsts.CHANNEL_STATUS_RELEASED:
  262 + // 只能关闭自己的流
  263 + if (this.activeChannelId === videoChannelInfo.channelId) {
  264 + this.activeChannelId = 0;
  265 + this.activeURL = '';
  266 + this.emitVideoChange();
  267 + }
  268 + break;
  269 + case ApeConsts.CHANNEL_STATUS_OPENING:
  270 + //_playUrl = "rtmfp://" + Config.mediaServerAddr + ":" + Config.mediaServerPort + "/message/" + _streamName;
  271 + //_cdnUrl = "rtmp://" + Config.mediaCDNServerAddr + ":" + Config.mediaCDNServerPort + "/message/" + _streamName;
  272 + //this.activeChannelId = videoChannelInfo.channelId;
  273 + //// AMS/FMS
  274 + //if (this._classInfo.msType ==ApeConsts.MS_TYPE_FMS) {
  275 + // this.activeURL = `http://dazhi.3mang.com/live/${this._classInfo.classId}/${this._classInfo.classId}_${videoChannelInfo.channelId}_flash_cam_mic_aac/playlist.m3u8`;
  276 + //}else {
  277 + // this.activeURL = `http://hls.3mang.com/live/${this._classInfo.classId}_${videoChannelInfo.channelId}_flash_cam_mic_aac/playlist.m3u8`;
  278 + //}
  279 + // 任何人都可以打开流
  280 + this.emitVideoChange();
  281 + break;
  282 + default:
  283 + break;
  284 + }*/
358 } 285 }
359 - try{  
360 286
361 - let videoChannelInfo = pdu['RCVideoChannelInfoPdu'].decode(itemData);  
362 - loger.log(videoChannelInfo);  
363 - return videoChannelInfo;  
364 - }catch (err){  
365 - loger.log("VIDEO收到数据 unPackPdu Pdu解析错误,itemIdx="+itemIdx+" err:"+err.message); 287 + ///////数据的封包和解包/////////////////////////////////////////
  288 + packPdu(_param, _itemIdx) {
  289 + loger.log("packPdu ");
  290 + //验证坐标点集合数组是否合法
  291 + if (_param == null || _itemIdx == null) {
  292 + this._emit(MessageTypes.MCU_ERROR, MessageTypes.ERR_APE_INTERFACE_PARAM_WRONG);
  293 + return null;
  294 + }
  295 +
  296 + /* message RCVideoChannelInfoPdu {
  297 + optional uint32 status = 1;//开启的状态
  298 + optional uint32 channel_id = 2;//唯一的频道id
  299 + optional uint32 timestamp = 3;//更新的时间戳
  300 + optional uint32 from_node_id = 4;//发起者的id
  301 + optional uint32 to_node_id = 5;//接收者的id,(如果是0,所有人都接收)
  302 + }*/
  303 +
  304 + //判断type类型,根据type设置不同的参数
  305 + let packPduModel = new pdu['RCVideoChannelInfoPdu'];
  306 + packPduModel.status = _param.status||ApeConsts.CHANNEL_STATUS_RELEASED;
  307 + packPduModel.channelId = _itemIdx;
  308 + packPduModel.classId =parseInt(_param.classId)||parseInt(GlobalConfig.classId);
  309 + packPduModel.mediaType =_param.mediaType|| ApeConsts.MEDIA_TYPE_VIDEO;
  310 + packPduModel.timestamp =_param.timestamp||EngineUtils.creatTimestamp();
  311 + packPduModel.fromNodeId = GlobalConfig.nodeId;
  312 + packPduModel.toNodeId = 0;
  313 + console.log(packPduModel);
  314 + return packPduModel;
366 } 315 }
367 - return null;  
368 - }  
369 316
  317 + unPackPdu(owner, itemIdx, itemData) {
  318 + loger.log("unPackPdu ");
  319 + if (owner == null || itemIdx == null || itemData == null) {
  320 + this._emit(MessageTypes.MCU_ERROR, MessageTypes.ERR_APE_INTERFACE_PARAM_WRONG);
  321 + return null;
  322 + }
  323 + try {
370 324
  325 + let videoChannelInfo = pdu['RCVideoChannelInfoPdu'].decode(itemData);
  326 + loger.log("unPackPdu",videoChannelInfo);
  327 + return videoChannelInfo;
  328 + } catch (err) {
  329 + loger.log("unPackPdu error,itemIdx=" + itemIdx + " err:" + err.message);
  330 + }
  331 + return null;
  332 + }
  333 +
  334 + //获取当前空闲的channel,返回值为0代表没有空闲的,否则返回的就是空闲的channelId
  335 + getFreeVideoChannel() {
  336 + loger.log("getFreeVideoChannel");
  337 + console.log(this.videoChannels);
  338 + let counter = 0;
  339 + for (let key in this.videoChannels) {
  340 + let item = this.videoChannels[key];
  341 + if (item && item.status == ApeConsts.CHANNEL_STATUS_RELEASED) {
  342 + return item.channelId;
  343 + }
  344 + counter++;
  345 + }
  346 + if (counter < GlobalConfig.maxVideoChannels) {
  347 + return ApeConsts.VIDEO_OBJ_TABLE_ID + (counter);
  348 + }
  349 + return 0;//没有空闲的
  350 + }
  351 +
  352 + //获取当前属于nodeId的已经打开的的channel,返回值为0代表没有打开的,否则返回的就是打开的channelId
  353 + getOpeningVideoChannel(_nodeId){
  354 + if(_nodeId==null||_nodeId==0){
  355 + return 0;
  356 + }
  357 + for (let key in this.videoChannels) {
  358 + let item = this.videoChannels[key];
  359 + if (item && item.status == ApeConsts.CHANNEL_STATUS_OPENING&&item.fromNodeId==_nodeId) {
  360 + return item.channelId;
  361 + }
  362 + }
  363 + return 0;
  364 + }
371 } 365 }
372 366
373 export default VideoChat; 367 export default VideoChat;