李勇

1.增加视频模块AudioApe

// //////////////////////////////////////////////////////////////////////////////
//音频模块
// //////////////////////////////////////////////////////////////////////////////
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": 1, "data": ""};
}
//同一个nodeId只允许推一个流,如果已经推了就不能再推
if(this.mediaModule.getOpeningMediaChannel(GlobalConfig.nodeId)!=0){
loger.warn("publishVideo,已经存在一个流,不能再推");
return;
}
//判断当前是否还有空闲的channle
let freeChannel = this.mediaModule.getFreeMediaChannel();
if (freeChannel == 0) {
loger.warn("publishAudio,没有空闲的channel ");
return {"code": 1, "data": "不能再打开更多的设备"};
}
//判断当前的频道是否已经占用
if(this.mediaModule.checkChannelIsOpening(_param.channelId)){
loger.warn(_param.channelId,"频道已经被占用");
return {"code":1,"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":0,"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": 1, "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": 1, "data": "sendAudioBroadcastMsg.McuClient还未初始化数据"};
}
return {"code": 1, "data": "sendAudioBroadcastMsg.McuClient还未初始化数据"};
}
if (_param == null) {
loger.warn('sendAudioBroadcastMsg,参数错误', _param);
this._emit(MessageTypes.MCU_ERROR, MessageTypes.ERR_APE_INTERFACE_PARAM_WRONG);
return {"code": 1, "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": 1, "data": "不能再打开更多的设备"};
}
}
/* message RCVideoSendDataRequestPdu {
required uint32 from_node_id = 1;//发起人
optional uint32 to_node_id = 2;//接收人,如果是0就是所有人都接收
optional uint32 actionType = 3;//消息指令类型;
optional bytes data = 4;//其他数据,这个根据actionType来确定数据的结构
}*/
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": 0, "data": ""};
}
sendTableUpdateHandler(_channelInfo) {
loger.log("audio,sendTableUpdateHandler ");
let updateModelPdu = this.packPdu(_channelInfo, _channelInfo.channelId);//let updateModelPdu=this.packPdu({},ApeConsts.VIDEO_OBJ_TABLE_ID+2);
if(updateModelPdu==null){
loger.warn("sendTableUpdateHandler error,updateModelPdu=null");
return;
}
let tableItemPdu = new pdu['RCRegistryTableItemPdu'];
tableItemPdu.itemIdx = _channelInfo.channelId;//tableItemPdu.itemIdx=ApeConsts.VIDEO_OBJ_TABLE_ID+2;
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_COMMAND, audioReceivePdu);
}
}
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.mediaModule.mediaChannels[itemIdx] = videoChannelInfo;
this._emit(MessageTypes.AUDIO_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;
}*/
}
///////数据的封包和解包/////////////////////////////////////////
packPdu(_param, _itemIdx) {
loger.log("packPdu ");
//验证坐标点集合数组是否合法
if (_param == null || _itemIdx == null) {
this._emit(MessageTypes.MCU_ERROR, MessageTypes.ERR_APE_INTERFACE_PARAM_WRONG);
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['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 videoChannelInfo = pdu['RCAudioChannelInfoPdu'].decode(itemData);
loger.log("unPackPdu",videoChannelInfo);
return videoChannelInfo;
} catch (err) {
loger.log("unPackPdu error,itemIdx=" + itemIdx + " err:" + err.message);
}
return null;
}
}
export default AudioApe;
... ...
// //////////////////////////////////////////////////////////////////////////////
// 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": 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};
}
//获取推流地址
getMediaPublishPath(_param) {
loger.log('getMediaPublishPath');
//判断当前开启的视频数量是否已经是最大值,如果已经是最大值,不能再开启
let freeChannel = this.getFreeMediaChannel();
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
}
};
}
//获取当前空闲的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;
... ...