李勇

1.录制回放增加外部读取录制信息的接口,回放的时候先通过流名称获取播放地址,没有获取到的情况下再按拼接规则拼接

不能预览此文件类型
... ... @@ -62,7 +62,7 @@ export default class MessageEntrance extends Emiter {
constructor() {
super();
//sdk 信息
GlobalConfig.sdkVersion = "v2.11.13.20170925";
GlobalConfig.sdkVersion = "v2.12.6.20170925";
loger.warn("sdkVersion:" + GlobalConfig.sdkVersion);
//设置
... ... @@ -1123,91 +1123,6 @@ export default class MessageEntrance extends Emiter {
loger.log("加入视频通话模块->不需要获取channelKey")
this._joinClassSuccessSeting();
}
/*
//返回给客户端初始化成功的数据
let joinClassSuccessCallBackData = {};
joinClassSuccessCallBackData.isRecordPlayBack = GlobalConfig.isRecordPlayBack;
joinClassSuccessCallBackData.DOCServerIP = GlobalConfig.DOCServerIP;
joinClassSuccessCallBackData.DOCServerPort = GlobalConfig.DOCServerPort;
joinClassSuccessCallBackData.classStatus = GlobalConfig.classStatus;
joinClassSuccessCallBackData.classId = GlobalConfig.classId;
joinClassSuccessCallBackData.className = GlobalConfig.className;
joinClassSuccessCallBackData.h5Module = GlobalConfig.h5Module;
joinClassSuccessCallBackData.isHost = GlobalConfig.isHost;
joinClassSuccessCallBackData.maxAudioChannels = GlobalConfig.maxAudioChannels;
joinClassSuccessCallBackData.maxVideoChannels = GlobalConfig.maxVideoChannels;
joinClassSuccessCallBackData.maxMediaChannels = GlobalConfig.maxMediaChannels;
joinClassSuccessCallBackData.mcuDelay = GlobalConfig.mcuDelay;
joinClassSuccessCallBackData.msType = GlobalConfig.msType;
joinClassSuccessCallBackData.nodeId = GlobalConfig.nodeId;
joinClassSuccessCallBackData.password = GlobalConfig.password;
joinClassSuccessCallBackData.passwordRequired = GlobalConfig.passwordRequired; // 老师的默认是true
//GlobalConfig.passwordRequired 老师的默认是true
//GlobalConfig.portal=_data.portal;
joinClassSuccessCallBackData.role = GlobalConfig.role;
joinClassSuccessCallBackData.siteId = GlobalConfig.siteId;
joinClassSuccessCallBackData.topNodeID = GlobalConfig.topNodeID;
joinClassSuccessCallBackData.userId = GlobalConfig.userId;
joinClassSuccessCallBackData.userName = GlobalConfig.userName;
joinClassSuccessCallBackData.userRole = GlobalConfig.userRole;
joinClassSuccessCallBackData.userType = GlobalConfig.userType;
joinClassSuccessCallBackData.siteId = GlobalConfig.siteId;
joinClassSuccessCallBackData.classId = GlobalConfig.classId;
joinClassSuccessCallBackData.userRole = GlobalConfig.userRole;
joinClassSuccessCallBackData.userId = GlobalConfig.userId;
joinClassSuccessCallBackData.passwordRequired = GlobalConfig.passwordRequired;
joinClassSuccessCallBackData.classType = GlobalConfig.classType || ApeConsts.CLASS_TYPE_1;
joinClassSuccessCallBackData.country = GlobalConfig.country; //国家
joinClassSuccessCallBackData.city = GlobalConfig.city; //城市
joinClassSuccessCallBackData.province = GlobalConfig.province; //服务商
joinClassSuccessCallBackData.isp = GlobalConfig.isp; //服务商
joinClassSuccessCallBackData.classTimestamp = GlobalConfig.classTimestamp; //课堂进行的累积时间
joinClassSuccessCallBackData.recordTimestamp = GlobalConfig.recordTimestamp; //课堂录制的累积时间
joinClassSuccessCallBackData.recordPlaybackMaxTime = GlobalConfig.recordPlaybackMaxTime; //录制回放的总时间
joinClassSuccessCallBackData.fps = GlobalConfig.fps;
joinClassSuccessCallBackData.gop = GlobalConfig.gop;
joinClassSuccessCallBackData.videoQuality = GlobalConfig.videoQuality;
joinClassSuccessCallBackData.ssTunnelAppURL = GlobalConfig.ssTunnelAppURL;
joinClassSuccessCallBackData.currentSceneTableId = GlobalConfig.currentSceneTableId; //文档区域的模块显示
joinClassSuccessCallBackData.serverAndLoacTimeDistanc = GlobalConfig.serverAndLoacTimeDistanc;
joinClassSuccessCallBackData.deviceType = GlobalConfig.deviceType;
joinClassSuccessCallBackData.language = GlobalConfig.language;
joinClassSuccessCallBackData.explorer = GlobalConfig.explorer;
joinClassSuccessCallBackData.explorerVersion = GlobalConfig.explorerVersion;
joinClassSuccessCallBackData.os = GlobalConfig.os;
joinClassSuccessCallBackData.channelId =GlobalConfig.channelId ;
joinClassSuccessCallBackData.channelKey =GlobalConfig.channelKey ;
joinClassSuccessCallBackData.userUid = GlobalConfig.userUid;
joinClassSuccessCallBackData.appId = GlobalConfig.appId;
joinClassSuccessCallBackData.appCertificate = GlobalConfig.appCertificate;
joinClassSuccessCallBackData.appRecordingKey = GlobalConfig.appRecordingKey;
//设置日志上报所需的信息
LogManager.serverAndLoacTimeDistanc = GlobalConfig.serverAndLoacTimeDistanc;//本地时间和服务器时间的差值(秒)
LogManager.classId = GlobalConfig.classId;//课堂号
LogManager.userId = GlobalConfig.userId;//userId
LogManager.nodeId = GlobalConfig.nodeId;//nodeId
LogManager.userRole = GlobalConfig.userRole;//userRole
LogManager.userName = GlobalConfig.userName;//用户名称
LogManager.logUrl = GlobalConfig.logUrl;//日志服务器地址 //http://log.3mang.com
LogManager.platform=GlobalConfig.platform;
loger.log('加入课堂成功->');
loger.log(joinClassSuccessCallBackData);
//加入课堂成功,广播消息
this._emit(MessageTypes.CLASS_JOIN_SUCCESS, joinClassSuccessCallBackData);
//加入音视频通话模块
this._joinChannel({channelId:GlobalConfig.channelId,channelKey:GlobalConfig.channelKey ,uid:GlobalConfig.userUid,info:""+GlobalConfig.userRole});*/
}
//加入课堂成功之后设置本地数据和返回数据给客户端
_joinClassSuccessSeting(){
... ... @@ -1234,7 +1149,6 @@ export default class MessageEntrance extends Emiter {
//GlobalConfig.passwordRequired 老师的默认是true
//GlobalConfig.portal=_data.portal;
joinClassSuccessCallBackData.role = GlobalConfig.role;
joinClassSuccessCallBackData.siteId = GlobalConfig.siteId;
joinClassSuccessCallBackData.topNodeID = GlobalConfig.topNodeID;
joinClassSuccessCallBackData.userId = GlobalConfig.userId;
joinClassSuccessCallBackData.userName = GlobalConfig.userName;
... ... @@ -1758,13 +1672,14 @@ export default class MessageEntrance extends Emiter {
//获取课堂所有参数(20170727新规则) api/meeting/detail.do? flash中的接口文件是 getClassParam.do
_sassGetClassParamSuccessHandler(_data) {
loger.log('获取课堂课堂信息完成.');
loger.log('获取课堂课堂信息完成.',_data.appConfig);
//包含整个课堂最全的信息,储存数据
if (_data) {
GlobalConfig.mcuDelay = _data.h5Delay || 0; //mcu消息延迟的时间间隔,单位(秒),结合客户端传的messageDelay的值使用
GlobalConfig.className = _data.meetingName || "";
GlobalConfig.classBeginTime = _data.beginTime || "";
GlobalConfig.classEndTime = _data.endTime || "";
GlobalConfig.siteId=_data.siteID||"";//这个字段ID是大写的
GlobalConfig.channelId = ""+GlobalConfig.siteId+"_"+GlobalConfig.classId;
//sdk获取ip失败就使用saas返回的
if(!GlobalConfig.userIp){
... ... @@ -1887,7 +1802,7 @@ export default class MessageEntrance extends Emiter {
this._choiceMcuAndMsListFromSass();
//获取MCU和MS 推流拉流、录制回放的默认地址
this.getMcuAndMsDefaultServerIp();
_recordPlayback.readyRecordPlay();
_recordPlayback.readyLoadRecordPlayData();
} else {
loger.warn("开启录制回放流程失败->还未创建模块");
}
... ... @@ -2707,7 +2622,6 @@ export default class MessageEntrance extends Emiter {
//GlobalConfig.passwordRequired 老师的默认是true
//GlobalConfig.portal=_data.portal;
joinClassSuccessCallBackData.role = GlobalConfig.role;
joinClassSuccessCallBackData.siteId = GlobalConfig.siteId;
joinClassSuccessCallBackData.topNodeID = GlobalConfig.topNodeID;
joinClassSuccessCallBackData.userId = GlobalConfig.userId;
joinClassSuccessCallBackData.userName = GlobalConfig.userName;
... ... @@ -2986,6 +2900,7 @@ export default class MessageEntrance extends Emiter {
GlobalConfig.getChannelToken=_params.getChannelToken||""
GlobalConfig.getRecordInfoInterfaces=_params.getRecordInfoInterfaces||"";
GlobalConfig.stopRecordingInterfaces=_params.stopRecordingInterfaces||"";
GlobalConfig.getTxRecordInfoInterfaces=_params.getTxRecordInfoInterfaces||"";
//去掉协议头
try{
... ... @@ -2997,6 +2912,11 @@ export default class MessageEntrance extends Emiter {
GlobalConfig.getRecordInfoInterfaces=GlobalConfig.getRecordInfoInterfaces.replace('http://',"");
GlobalConfig.getRecordInfoInterfaces=GlobalConfig.getRecordInfoInterfaces.replace('https://',"");
}
if(GlobalConfig.getTxRecordInfoInterfaces){
GlobalConfig.getTxRecordInfoInterfaces=GlobalConfig.getTxRecordInfoInterfaces.replace('http://',"");
GlobalConfig.getTxRecordInfoInterfaces=GlobalConfig.getTxRecordInfoInterfaces.replace('https://',"");
}
if(GlobalConfig.stopRecordingInterfaces){
GlobalConfig.stopRecordingInterfaces=GlobalConfig.stopRecordingInterfaces.replace('http://',"");
GlobalConfig.stopRecordingInterfaces=GlobalConfig.stopRecordingInterfaces.replace('https://',"");
... ...
... ... @@ -602,6 +602,7 @@ GlobalConfig.userUid = 0;
GlobalConfig.recordInterfaces="";//控制开启录制和录制状态改变的接口
GlobalConfig.getRecordInfoInterfaces="";//获取媒体录制信息数据的接口
GlobalConfig.stopRecordingInterfaces="";//停止录制的接口
GlobalConfig.getTxRecordInfoInterfaces="";//获取媒体录制信息数据的接口(tx)
GlobalConfig.getChannelToken="";//获取token的地址
export default GlobalConfig;
... ...
... ... @@ -7,6 +7,7 @@ import pdu from 'pdus/index';
import PduType from 'pdus/PduType';
import PduConsts from 'pdus/PduConsts';
import ApeConsts from 'apes/ApeConsts';
import MediaModule from "apes/MediaModule";
import ArrayBufferUtil from 'libs/ArrayBufferUtil';
import Base64 from 'base64-js';
... ... @@ -32,6 +33,9 @@ class RecordPlayBackParse extends Emiter {
this._recordPlaybackTimestamp = 0;//回放的时间
this._recordPlaybackMaxTime = 0;//录制回放的总时间
this._isReady = false;//录制回放是否已经准备完成
this.isLoadTxRecordInfo=false;//是否已经加载腾讯云的录制数据
this.isLoadAgoraRecordInfo=false;//是否已经加载声网的录制数据
this._apes = {};
this._videoApeBroadcastMssages={};//视频模块的广播消息
this.mediaChannleList={};
... ... @@ -250,7 +254,7 @@ class RecordPlayBackParse extends Emiter {
}
//加载录制文件rec
readyRecordPlay() {
readyLoadRecordPlayData() {
this.initReplay();
loger.log("读取录制回放数据");
//let url = `http://123.56.73.119:80/h5dev/20170306/1357644520_20170306.rec`;
... ... @@ -277,7 +281,6 @@ class RecordPlayBackParse extends Emiter {
if (ret) {
loger.log('读取回放数据-完成');
this._loadRecordDataSuccess(ret);
this.getMediaRecrodInfo();
} else {
loger.warn('读取回放数据-失败.');
this._emit(MessageTypes.MCU_ERROR, MessageTypes.ERR_GET_RECORD_PLAY_BACK_DATA_FAILED);
... ... @@ -294,21 +297,37 @@ class RecordPlayBackParse extends Emiter {
if (parseBuffer) {
parseBuffer.clear();
parseBuffer.append(arrayBuffer);
//解析数据
this.parseArrayBuf();
//rec数据加载完成后,继续加载音视频相关的录制数据,然后再解析rec数据
this.getMediaRecrodInfoFromTx(()=>{
//解析录制的rec数据
this.isLoadTxRecordInfo=true;
if(this.isLoadTxRecordInfo&&this.isLoadAgoraRecordInfo){
this.parseArrayBuf();
}
});
this.getMediaRecrodInfoFromAgora(()=>{
this.isLoadAgoraRecordInfo=true;
//解析录制的rec数据
if(this.isLoadTxRecordInfo&&this.isLoadAgoraRecordInfo){
this.parseArrayBuf();
}
});
}
}
//获取媒体录制信息
getMediaRecrodInfo(){
getMediaRecrodInfoFromAgora(_callback){
//获取课堂录制信息 get localhost:3000/recordInfo/getRecordInfo/7d72365eb9834353397e3e3f9d460bdda
//localhost:3000/recordInfo/getRecordInfo/ 后面直接添加课堂号 channel
if(!GlobalConfig.getRecordInfoInterfaces){
loger.log("获取媒体录制信息->失败->接口地址无效");
loger.log("AG-获取媒体录制信息->失败->接口地址无效");
if(_callback){
_callback();
}
return ;
}
let url = `${GlobalConfig.locationProtocol+GlobalConfig.getRecordInfoInterfaces}/${GlobalConfig.channelId}`;
loger.log('获取媒体录制信息.', url);
loger.log('AG-获取媒体录制信息.', url);
fetch(url, {
timeout: 15000
})
... ... @@ -316,25 +335,118 @@ class RecordPlayBackParse extends Emiter {
if (ret.ok) {
return ret.json();
} else {
loger.error(`获取媒体录制信息-网络异常.状态码:${ret}`);
loger.error(`AG-获取媒体录制信息-网络异常.状态码:${ret}`);
if(_callback){
_callback();
}
throw '';
}
})
.then(ret => {
loger.log('获取媒体录制信息-完成',ret);
/* if (ret.errorCode == 0) {
loger.log('获取媒体录制信息-完成');
loger.log('AG-获取媒体录制信息-完成',ret);
if(_callback){
_callback();
}
})
.catch(err => {
loger.error(`AG-获取媒体录制信息-异常.状态码:${err}`);
if(_callback){
_callback();
}
});
}
//获取媒体录制信息
getMediaRecrodInfoFromTx(_callback){
if(!GlobalConfig.getTxRecordInfoInterfaces){
loger.log("TX-获取媒体录制信息->失败->接口地址无效");
if(_callback){
_callback();
}
return ;
}
let url = `${GlobalConfig.locationProtocol+GlobalConfig.getTxRecordInfoInterfaces}`;
loger.log('TX-获取媒体录制信息.', url);
//接口中用的是GET
fetch(encodeURI(url), {
method: 'POST',
headers: {
"Content-Type": "application/x-www-form-urlencoded; charset=utf-8"
},
body: `channelId=${ GlobalConfig.channelId}`,
timeout: 5000
})
.then(ret => {
if (ret.ok) {
return ret.json();
} else {
loger.warn('获取媒体录制信息-失败.');
loger.error(`TX-获取媒体录制信息-网络异常.状态码:${ret}`);
if(_callback){
_callback();
}
throw '';
}
})
.then(ret => {
loger.log('TX-获取媒体录制信息-完成',ret);
/* {"code": 200,
"returnData":{
"data":[
{
"id": "0zo9GALQdmkwrJVYxqgaE6xM7j8yvOZN",
"channelId": "marketflashtest_1966232762",
"recordInfo": "",
"createTime": ""
}]
}
}*/
if(ret&&ret.code==200){
if(ret.returnData&&ret.returnData.data){
this.parseAndSavaStreamInfoFromTx(ret.returnData.data);
}
}
if(_callback){
_callback();
}
})
.catch(err => {
loger.error(`获取媒体录制信息-异常.状态码:${err}`);
if(_callback){
_callback();
}
});
}
//解析数据
//解析腾讯云录制的视频数据,数组[]
parseAndSavaStreamInfoFromTx(_dataArr){
/* "id": "0zo9GALQdmkwrJVYxqgaE6xM7j8yvOZN",
"channelId": "marketflashtest_1966232762",
"recordInfo":"xxxxx",
"createTime":"xxxx"*/
if(!_dataArr){
loger.log("TX-外部录制的视频数据无效");
return;
}
let item;
let itemJson;
loger.log("TX-外部录制的视频数据-length="+_dataArr.length)
for(let i=0;i<_dataArr.length;i++){
item=_dataArr[i];
if(item){
itemJson=item.recordInfo;
try{
itemJson=JSON.parse(itemJson);
}catch (err){
}
if(itemJson&&itemJson.video_url){
MediaModule.streams[itemJson.stream_id]=itemJson.video_url;
}
}
}
console.log(" MediaModule.streams", MediaModule.streams);
}
//解析录制的rec数据
parseArrayBuf() {
//this._messages = {};
let byteLength = parseBuffer.offset;
... ... @@ -717,7 +829,7 @@ class RecordPlayBackParse extends Emiter {
let object_id = regUpdatedItem.objId;
let user_data = regUpdatedItem.userData;
console.log('RCRegistryUpdateObjPdu',regUpdatedItem)
//console.log('RCRegistryUpdateObjPdu',regUpdatedItem)
switch (sub_type) {
case pdu.RCPDU_REG_ROSTER_INSERT_PDU:
... ...
... ... @@ -101,6 +101,14 @@ class MediaModule {
loger.warn('获取录制回放时点播的地址->参数错误', _param);
return {"code": ApeConsts.RETURN_FAILED, "data": ""};
}
//如果是外部的流地址,不需要拼接,直接使用就可以,是完整的地址
let streamPlayUrl=MediaModule.streams[_param.streamId]
if(streamPlayUrl){
loger.log("使用外部的流地址->",streamPlayUrl);
return {"code": ApeConsts.RETURN_SUCCESS, "data": "", "playUrl":streamPlayUrl};
}
//M3U8 http://123.56.73.119:6001/live/h5dev_2106728010_8ab3b0ed5a3a9220015a3a958f0d0003_983041_1489113860/total.m3u8
let port = (GlobalConfig.RS_RECORD_PLAY_PORT == "" || GlobalConfig.RS_RECORD_PLAY_PORT == null) ? "" : ":" + GlobalConfig.RS_RECORD_PLAY_PORT;
//let path = "http://" + GlobalConfig.RS_RECORD_PLAY_IP
... ... @@ -411,5 +419,6 @@ class MediaModule {
}
}
MediaModule.allMediaChannelsList = {};//当前已经创建的所有音视频通道(包含释放或未未释放的)
MediaModule.streams={};//记录流名称对应的数据
export default MediaModule;
... ...