李勇
c47b2bca 1 parent 9845310e master ... 20170922-1 20171019-1 20171120-1w dev letv-20170426 ly20170622 ly20170622-2 ly20170622-3 ly20170627-2 ly20170706-1 ly20170708-2 ly20170708-3 ly20170710-1 ly20170717-1 ly20170719-1 ly20170723-1 ly20170724-2 ly20170726-1 ly20170731-1 ly20170731-2 ly20170801-2 ly20170802-1 ly20170818-1 ly20170818-2 ly20170820-1 ly20170821-1 ly20170824-1 ly20170829-1 ly20170925-1 ly20170926-1 ly20170927-1 ly20170929-1 ly20171011-1 ly20171013-1 ly20171013-2 ly20171016-1 ly20171021-1 ly20171023-1 ly20171024-1w ly20171025-1w ly20171026-1w ly20171027-1w ly20171030-1 ly20171030-2w ly20171107-1 ly20171110-1w ly20171113-1w ly20171204-1w ly20171208-1w ly20171211-1w ly20171211-2w ly20171214-1w mcuClientBranch mcuClientBranch20170228 webRtc-dev 1.7.0 v2.38.13.20171216 v2.38.11.20171214 v2.38.3.201712011 v2.38.1.201712011 v2.36.11.20171204 v2.36.8.20171206 v2.36.4.20171201 v2.35.11.20171130 v2.34.16.20171128 v2.34.5.20171127 v2.33.6.20171123 v2.32.1.20171123 v2.31.12.20171122 v2.31.10.20171122 v2.30.5.20171117 v2.30.2.20171117 v2.29.5.20171114 v2.28.1.20171110 v2.27.11.20171109 v2.26.9.20171107 v2.26.6.20171103 v2.26.2.20171102 v2.25.7.20171031 v2.25.6.20171031 v2.25.0.20171030 v2.24.2.20171030 v2.23.0.20171030 v2.22.7.20171026 v2.20.5.20171023 v2.20.0.20171021 v2.19.8.20171020 v2.18.14.20171020 v2.18.10.20171019 v2.17.11.20171014 v2.16.8.20171012 v2.16.5.20171012 v2.15.5.20171001 v2.15.3.20170929 v2.14.5.20170927 v2.13.5.20170927 v2.12.14.20170927 v2.12.8.20170926 v2.12.6.20170925 v2.11.13.20170925 v2.10.7.20170921 v2.10.6.20170921 v2.10.5.20170920 v2.10.4.20170920 v2.9.3.20170919 v2.8.17.20170918 v2.8.8.20170917 v2.8.2.20170916 v2.6.2.20170915 v2.5.12.20170915 v2.5.6.20170914 v2.5.5.20170914 v2.4.4.20170908 v2.4.2.20170908 v2.4.0.20170907 v2.3.6.20170907 v2.2.16.20170905 v2.1.22.20170904 v1.84.0.20170912 v1.83.2.20170831 v1.82.11.20170829 v1.81.19.20170828 v1.80.2.20170824 v1.79.6.20170822 v1.79.5.20170821 v1.79.4.20170821 v1.79.3.20170821 v1.78.4.20170820 v1.77.4.20170819 v1.76.2.20170818 v1.75.0.20170815 v1.74.0.20170814 v1.73.2.20170814 v1.73.1.20170814 v1.71.0.20170813 v1.70.5.20170812 v1.68.2.20170812 v1.66.1.20170809 v1.65.25.20170808 v1.65.24.20170806 v1.63.1.20170731 v1.62.3.20170731 v1.61.0.20170729 v1.60.0.20170729 v1.59.0.20170729 v1.58.0.20170729 v1.57.0.20170727 v1.56.1.20170727 v1.56.0.20170727 v1.52.1.20170726 v1.51.0.20170724 v1.50.7.20170724 v1.49.1.20170724 v1.48.2.20170723 v1.46.1.20170722 v1.45.1.20170717 v1.43.1.20170711 v1.42.1.20170708 v1.41.0.20170708 v1.40.0.20170706 v1.39.2.20170706 v1.39.1.20170705 v1.38.4.20170629 v1.37.5.20170627 v1.37.2.20170622 v1.36.7.20170620 v1.36.4.20170620 v1.36.1.20170619 v1.35.4.20170619 v1.34.2.20170615 v1.33.2.20170615 v1.32.1.20170614 v1.31.11.20170613 v1.30.20.20170607 v1.30.7.20170606 v1.30.6.20170606 v1.30.5.20170605 v1.30.3.20170602 v1.29.8.20170601 v1.28.0.201705031 v1.27.16.201705027 v1.27.14.201705027 v1.27.10.201705026 v1.25.2.201705025 v1.23.5.201705023 v1.23.4.201705018 v1.21.1.201705017 v1.20.1.201705015 v1.19.1.201705012 v1.19.0.201705011 v1.18.0.201705010 v1.16.1.201705010 v1.15.2.20170507 v1.14.1.20170505 v1.13.0.20170504 v1.11.3.20170504 v1.10.2.20170428 v1.10.0.20170427 v1.9.20.20170426 v1.9.19.20170425 v1.9.18.20170425 v1.9.17.20170421 v1.9.16.20170420 v1.9.15.20170420 v1.9.11.20170419 v1.9.6.20170418 v1.9.4.20170417 v.1.9.2.20170413 v.1.9.0.20170411 v.1.8.22.20170411 v.1.8.19.20170411 v.1.8.16.20170410 v.1.8.13.20170409 v.1.8.10.20170407 v.1.8.9.20170407 v1.8.8.20170406 v.1.8.7.20170405-1 v.1.8.6.20170401-2 v.1.8.5.20170331-1 v.1.8.3.20170329-4 v1.8.1.20170321 mcuClient20170302 mcuClient_v1.8.0.20170314 修复音视频channel占用问题

1.调整视频模块,把视频和音频模块共用的功能单独分离出来

... ... @@ -91,7 +91,7 @@ export default class MessageEntrance extends Emiter {
_audio_ape= new AudioApe();
_audio_ape.on('*', (type, data) => this._emit(type, data));
_audio_ape.on(MessageTypes.AUDIO_UPDATE, this.videoUpdate.bind(this));
_audio_ape.on(MessageTypes.AUDIO_UPDATE, this.audioUpdate.bind(this));
_whiteboard_ape = new WhiteBoardApe();
_whiteboard_ape.on('*', (type, data) => this._emit(type, data));
... ... @@ -198,7 +198,7 @@ export default class MessageEntrance extends Emiter {
_video_ape.stopPublishVideo(_data);
}
if(_audio_ape){
_video_ape.stopPublishAudio(_data);
_audio_ape.stopPublishAudio(_data);
}
}
}
... ... @@ -206,15 +206,15 @@ export default class MessageEntrance extends Emiter {
//当前会议中视频或音频占用channel的nodeId ,在人员列表中不存在,这种情况是占用channel的人员掉线或离开的时候没有释放channel
//的占用状态导致,对于这种情况,需要释放掉
_onClassNonentityRoster(_param){
if(_param==null||_param.fromNodeId==null){
if(_param==null||_param.nodeId==null){
loger.warn("onClassNonentityRoster.参数错误")
return;
}
if(_video_ape){
_video_ape.stopPublishVideo({"nodeId":_param.fromNodeId});
_video_ape.stopPublishVideo({"nodeId":_param.nodeId});
}
if(_audio_ape){
_audio_ape.stopPublishAudio({"nodeId":_param.fromNodeId});
_audio_ape.stopPublishAudio({"nodeId":_param.nodeId});
}
}
... ... @@ -738,7 +738,7 @@ export default class MessageEntrance extends Emiter {
//AudioApe
audioUpdate(_data){
//频同步的消息发送改变,需要通知ferApe模块中的用户更新状态
//频同步的消息发送改变,需要通知ferApe模块中的用户更新状态
if(_confer_ape){
_confer_ape.updaterRosterStatus(_data);
}
... ...
//对外暴露的对象
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};
... ...
... ... @@ -454,7 +454,7 @@ class ConferApe extends Ape {
//视频模块发生更新,人员状态需要更新
updaterRosterStatus(_param){
if(_param){
loger.log("视频模块发生更新,人员状态需要更新,fromNodeId->",_param.fromNodeId);
loger.log("媒体模块发生更新,人员状态需要更新,fromNodeId->",_param.fromNodeId);
loger.log(_param.status,_param.fromNodeId,this.rosters[_param.fromNodeId]);
//console.log(_param.fromNodeId);
//如果是自己。改变自己的状态同步到MCU
... ... @@ -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});
}
}
}
... ...
// //////////////////////////////////////////////////////////////////////////////
//
// 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,6 +9,7 @@ import Loger from 'Loger';
import MessageTypes from 'MessageTypes';
import GlobalConfig from 'GlobalConfig';
import EngineUtils from 'EngineUtils';
import MediaModule from "./MediaModule";
let loger = Loger.getLoger('VideoApe');
... ... @@ -31,8 +21,9 @@ class VideoApe 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 VideoApe 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);
}
//推流
... ... @@ -135,20 +65,20 @@ class VideoApe extends Ape {
loger.log('publishVideo -> maxVideoChannels', GlobalConfig.maxVideoChannels);
//同一个nodeId只允许推一个流,如果已经推了就不能再推
if(this.getOpeningVideoChannel(GlobalConfig.nodeId)!=0){
if(this.mediaModule.getOpeningMediaChannel(GlobalConfig.nodeId)!=0){
loger.warn("publishVideo,已经存在一个流,不能再推");
return;
}
//判断当前是否还有空闲的channle
let freeChannel = this.getFreeVideoChannel();
let freeChannel = this.mediaModule.getFreeMediaChannel();
if (freeChannel == 0) {
loger.warn("publishVideo,没有空闲的channel ");
return {"code": 1, "data": "不能再打开更多的设备"};
}
//判断当前的频道是否已经占用
if(this.checkChannelIsOpening(_param.channelId)){
if(this.mediaModule.checkChannelIsOpening(_param.channelId)){
loger.warn(_param.channelId,"频道已经被占用");
return {"code":1,"data":"频道已经被占用!"};
}
... ... @@ -177,7 +107,7 @@ class VideoApe 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,不需要关闭"};
... ... @@ -214,7 +144,7 @@ class VideoApe extends Ape {
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": "不能再打开更多的设备"};
... ... @@ -325,7 +255,7 @@ class VideoApe extends Ape {
//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) {
... ... @@ -403,52 +333,6 @@ class VideoApe 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 VideoApe;
... ...