/**
|
* @file liveroom.js 直播模式房间管理sdk
|
* @author binniexu
|
*/
|
var webim = require('webim_wx.js');
|
var webimhandler = require('webim_handler.js');
|
var tls = require('tls.js');
|
var encrypt = require('encrypt.js');
|
|
var serverDomain = '', // 后台域名
|
heart = '', // 判断心跳变量
|
requestSeq = 0, // 请求id
|
requestTask = [], // 请求task
|
// 用户信息
|
accountInfo = {
|
userID: '', // 用户ID
|
userName: '', // 用户昵称
|
userAvatar: '', // 用户头像URL
|
userSig: '', // IM登录凭证
|
sdkAppID: '', // IM应用ID
|
accountType: '', // 账号集成类型
|
accountMode: 0, //帐号模式,0-表示独立模式,1-表示托管模式
|
token: '' //登录RoomService后使用的票据
|
},
|
// 房间信息
|
roomInfo = {
|
roomID: '', // 视频位房间ID
|
roomInfo: '', // 房间名称
|
mixedPlayURL: '', // 混流地址
|
isCreator: false, // 是否为创建者
|
pushers: [], // 当前用户信息
|
isLoginIM: false, // 是否登录IM
|
isJoinGroup: false, // 是否加入群
|
isDestory: false, // 是否已解散
|
hasJoinPusher: false
|
},
|
// 事件
|
event = {
|
onGetPusherList: function () {}, // 初始化成员列表
|
onPusherJoin: function () {}, // 进房通知
|
onPusherQuit: function () {}, // 退房通知
|
onRoomClose: function() {}, // 群解散通知
|
onRecvRoomTextMsg: function() {}, // 消息通知
|
onRecvJoinPusherRequest: function() {}, //大主播收到小主播连麦请求通知
|
onKickOut: function() {}, //小主播被踢通知
|
onRecvRoomCustomMsg: function() {}, //自定义消息通知
|
onSketchpadData: function(){},
|
onPushersChange: function() {} //当推流成员有变更时回调,主播、连麦观众、普通观众都能收到此通知
|
};
|
// 随机昵称
|
var userName = ['林静晓', '陆杨', '江辰', '付小司', '陈小希', '吴柏松', '肖奈', '芦苇微微', '一笑奈何', '立夏'];
|
// 请求数
|
var requestNum = 0;
|
var requestJoinCallback = null;
|
var bigAnchorStreamID = '';
|
var bigAnchorWidth = 360;
|
var bigAnchorHeight = 640;
|
var gTimeoutID = null;
|
|
/**
|
* [request 封装request请求]
|
* @param {options}
|
* url: 请求接口url
|
* data: 请求参数
|
* success: 成功回调
|
* fail: 失败回调
|
* complete: 完成回调
|
*/
|
function request(options) {
|
if (!serverDomain) {
|
console.log('请求服务器域名为空,请将wxlite/config里面的url配置成你的服务器域名');
|
options.fail && options.fail({
|
errCode: -9,
|
errMsg: '请求服务器域名为空,请将wxlite/config里面的url配置成你的服务器域名'
|
});
|
return;
|
}
|
requestNum++;
|
console.log('requestNum: ', requestNum);
|
requestTask[requestSeq++] = wx.request({
|
url: serverDomain + options.url + (options.params?('?' + formatParams(options.params) + '&'):'?') + 'userID=' + accountInfo.userID + (accountInfo.token?'&token=' + accountInfo.token:""),
|
data: options.data || {},
|
method: 'POST',
|
header: {
|
'content-type': 'application/json' // 默认值
|
},
|
// dataType: 'json',
|
success: options.success || function() {},
|
fail: options.fail || function() {},
|
complete: options.complete || function() {
|
requestNum--;
|
// console.log('complete requestNum: ',requestNum);
|
}
|
});
|
}
|
|
//url encode编码
|
function formatParams(data) {
|
var arr = [];
|
for (var name in data) {
|
arr.push(encodeURIComponent(name) + "=" + encodeURIComponent(data[name]));
|
}
|
return arr.join("&");
|
}
|
|
/**
|
* [login 初始化登录信息]
|
* @param {options}
|
* data: {
|
* serverDomain: 请求域名
|
* }
|
* success: 成功回调
|
* fail: 失败回调
|
*
|
* @return success
|
* userName: 用户昵称
|
*/
|
function login(options) {
|
if(!options || !options.data.serverDomain) {
|
console.log('init参数错误',options);
|
options.fail && options.fail({
|
errCode: -9,
|
errMsg: 'init参数错误'
|
});
|
return;
|
}
|
serverDomain = options.data.serverDomain;
|
accountInfo.userID = options.data.userID;
|
accountInfo.userSig = options.data.userSig;
|
accountInfo.sdkAppID = options.data.sdkAppID;
|
accountInfo.accountType = options.data.accType;
|
accountInfo.userName = options.data.userName || userName[Math.floor(Math.random()*10)] || accountInfo.userID;
|
accountInfo.userAvatar = options.data.userAvatar || '123';
|
|
request({
|
url: 'login',
|
params: {
|
accountType: accountInfo.accountType,
|
sdkAppID: accountInfo.sdkAppID,
|
userSig: accountInfo.userSig
|
},
|
data: {},
|
success: function(ret) {
|
if (ret.data.code) {
|
console.error("登录到RoomService后台失败:", JSON.stringify(ret));
|
options.fail && options.fail({
|
errCode: ret.data.code,
|
errMsg: ret.data.message
|
});
|
return;
|
}
|
accountInfo.token = ret.data.token;
|
accountInfo.userID = ret.data.userID;
|
// 登录IM
|
loginIM({
|
success: function(ret) {
|
options.success && options.success({
|
userID: accountInfo.userID,
|
userName: accountInfo.userName
|
});
|
},
|
fail: function(ret) {
|
console.error("IM登录失败:", JSON.stringify(ret));
|
options.fail && options.fail({
|
errCode: -999,
|
errMsg: "IM登录失败"
|
});
|
}
|
});
|
},
|
fail: function(ret) {
|
console.error("登录到RoomService后台失败:", JSON.stringify(ret));
|
options.fail && options.fail(ret);
|
}
|
});
|
}
|
|
/**
|
* [logout 结束初始化信息]
|
*/
|
function logout() {
|
request({
|
url: "logout",
|
success: function(ret){},
|
fail: function(ret){}
|
});
|
serverDomain = '';
|
accountInfo.userID = '';
|
accountInfo.userSig = '';
|
accountInfo.sdkAppID = '';
|
accountInfo.accountType = '';
|
accountInfo.userName = '';
|
accountInfo.userAvatar = '';
|
accountInfo.token = '';
|
// 退出IM登录
|
webimhandler.logout();
|
}
|
|
/**
|
* [loginIM 登录IM]
|
* @param {options}
|
* data: {
|
* roomID: 房间ID
|
* }
|
* success: 成功回调
|
* fail: 失败回调
|
*/
|
function loginIM(options) {
|
// 初始化设置参数
|
webimhandler.init({
|
accountMode: accountInfo.accountMode,
|
accountType: accountInfo.accountType,
|
sdkAppID: accountInfo.sdkAppID,
|
avChatRoomId: options.roomID || 0,
|
selType: webim.SESSION_TYPE.GROUP,
|
selToID: options.roomID || 0,
|
selSess: null //当前聊天会话
|
});
|
//当前用户身份
|
var loginInfo = {
|
'sdkAppID': accountInfo.sdkAppID, //用户所属应用id,必填
|
'appIDAt3rd': accountInfo.sdkAppID, //用户所属应用id,必填
|
'accountType': accountInfo.accountType, //用户所属应用帐号类型,必填
|
'identifier': accountInfo.userID, //当前用户ID,必须是否字符串类型,选填
|
'identifierNick': accountInfo.userID, //当前用户昵称,选填
|
'userSig': accountInfo.userSig, //当前用户身份凭证,必须是字符串类型,选填
|
};
|
//监听(多终端同步)群系统消息方法,方法都定义在demo_group_notice.js文件中
|
var onGroupSystemNotifys = {
|
// 群被解散(全员接收)
|
"5": function (notify) {
|
roomInfo.isDestory = true;
|
event.onRoomClose();
|
},
|
"11": webimhandler.onRevokeGroupNotify, //群已被回收(全员接收)
|
// 用户自定义通知(默认全员接收)
|
"255": function (notify) {
|
// console.error('收到系统通知:', notify.UserDefinedField);
|
var content = JSON.parse(notify.UserDefinedField);
|
if (content && content.cmd == 'notifyPusherChange') {
|
event.onPushersChange && event.onPushersChange();
|
mergePushers();
|
}
|
}
|
};
|
|
//监听连接状态回调变化事件
|
var onConnNotify = function (resp) {
|
switch (resp.ErrorCode) {
|
case webim.CONNECTION_STATUS.ON:
|
//webim.Log.warn('连接状态正常...');
|
break;
|
case webim.CONNECTION_STATUS.OFF:
|
webim.Log.warn('连接已断开,无法收到新消息,请检查下你的网络是否正常');
|
break;
|
default:
|
webim.Log.error('未知连接状态,status=' + resp.ErrorCode);
|
break;
|
}
|
};
|
|
//监听事件
|
var listeners = {
|
"onConnNotify": webimhandler.onConnNotify, //选填
|
"onBigGroupMsgNotify": function (msg) {
|
webimhandler.onBigGroupMsgNotify(msg, function (msgs) {
|
receiveMsg(msgs);
|
}, function (datas) {
|
//收到白板数据
|
console.log("LiveRoom callback --> 收到白板数据")
|
onSketchpadData(datas);
|
})
|
// webimhandler.onBigGroupMsgNotify(msg, function (msgs) {
|
// receiveMsg(msgs);
|
// })
|
}, //监听新消息(大群)事件,必填
|
"onMsgNotify": function (newMsgList) { //监听新消息(私聊(包括普通消息和全员推送消息),普通群(非直播聊天室)消息)事件,必填
|
webimhandler.onMsgNotify(newMsgList, function(msg) {
|
recvC2CMsg(msg);
|
});
|
},
|
"onGroupSystemNotifys": onGroupSystemNotifys, //监听(多终端同步)群系统消息事件,必填
|
"onGroupInfoChangeNotify": webimhandler.onGroupInfoChangeNotify,
|
// 'onKickedEventCall': self.onKickedEventCall // 踢人操作
|
};
|
|
//其他对象,选填
|
var others = {
|
'isAccessFormalEnv': true, //是否访问正式环境,默认访问正式,选填
|
'isLogOn': false //是否开启控制台打印日志,默认开启,选填
|
};
|
|
if (accountInfo.accountMode == 1) { //托管模式
|
webimhandler.sdkLogin(loginInfo, listeners, others, 0, afterLoginIM, options);
|
} else { //独立模式
|
//sdk登录
|
webimhandler.sdkLogin(loginInfo, listeners, others, 0, afterLoginIM, options);
|
}
|
}
|
function afterLoginIM(options) {
|
if (options.errCode) {
|
// webim登录失败
|
console.log('webim登录失败:', options);
|
options.callback.fail && options.callback.fail({
|
errCode: -2,
|
errMsg: 'IM登录失败,如果你是在配置线上环境,请将IM域名[https://webim.tim.qq.com]配置到小程序request合法域名'
|
});
|
return;
|
}
|
// webim登录成功
|
console.log('webim登录成功');
|
roomInfo.isLoginIM = true;
|
options.callback.success && options.callback.success({
|
userName: accountInfo.userName
|
});
|
}
|
function afterJoinBigGroup(options) {
|
if (options.errCode) {
|
console.log('webim进群失败: ', options);
|
options.callback.fail && options.callback.fail({
|
errCode: -2,
|
errMsg: 'IM进群失败'
|
});
|
return;
|
}
|
roomInfo.isJoinGroup = true;
|
console.log('进入IM房间成功: ', roomInfo.roomID);
|
options.callback.success && options.callback.success({});
|
}
|
|
function onSketchpadData(data){
|
event.onSketchpadData(data);
|
}
|
|
/**
|
* [receiveMsg 接收消息处理]
|
* @param {options}
|
*
|
* @return event.onRecvRoomTextMsg
|
* roomID: 房间ID
|
* userID: 用户ID
|
* nickName: 用户昵称
|
* headPic: 用户头像
|
* textMsg: 文本消息
|
* time: 消息时间
|
*/
|
function receiveMsg(msg) {
|
if (!msg.content) { return; }
|
console.log('IM消息: ',JSON.stringify(msg));
|
var time = new Date();
|
var h = time.getHours()+'', m = time.getMinutes()+'', s = time.getSeconds()+'';
|
h.length == 1 ? (h='0'+h) : '';
|
m.length == 1 ? (m='0'+m) : '';
|
s.length == 1 ? (s='0'+s) : '';
|
time = h + ':' + m + ':' + s;
|
msg.time = time;
|
|
if(msg.fromAccountNick == '@TIM#SYSTEM') {
|
msg.fromAccountNick = '';
|
msg.content = msg.content.split(';');
|
msg.content = msg.content[0];
|
event.onRecvRoomTextMsg && event.onRecvRoomTextMsg({
|
roomID: roomInfo.roomID,
|
userID: msg.fromAccountNick,
|
userName: msg.userName,
|
userAvatar: msg.userAvatar,
|
message: msg.content,
|
time: msg.time
|
});
|
} else {
|
var contentObj,newContent;
|
newContent = msg.content.split('}}');
|
contentObj = JSON.parse(newContent[0] + '}}');
|
if(contentObj.cmd == 'CustomTextMsg') {
|
msg.userName = contentObj.data.nickName;
|
msg.userAvatar = contentObj.data.headPic;
|
var content = '';
|
for(var i = 1; i < newContent.length; i++) {
|
if(i == newContent.length - 1)
|
content += newContent[i];
|
else content += newContent[i] + '}}';
|
}
|
msg.content = content;
|
event.onRecvRoomTextMsg && event.onRecvRoomTextMsg({
|
roomID: roomInfo.roomID,
|
userID: msg.fromAccountNick,
|
userName: msg.userName,
|
userAvatar: msg.userAvatar,
|
message: msg.content,
|
time: msg.time
|
});
|
} else if (contentObj.cmd == 'CustomCmdMsg') {
|
msg.userName = contentObj.data.nickName;
|
msg.userAvatar = contentObj.data.headPic;
|
msg.cmd = contentObj.data.cmd;
|
var content = '';
|
for(var i = 1; i < newContent.length; i++) {
|
if(i == newContent.length - 1)
|
content += newContent[i];
|
else content += newContent[i] + '}}';
|
}
|
msg.content = content;
|
event.onRecvRoomCustomMsg && event.onRecvRoomCustomMsg({
|
roomID: roomInfo.roomID,
|
userID: msg.fromAccountNick,
|
userName: msg.userName,
|
userAvatar: msg.userAvatar,
|
cmd: msg.cmd,
|
message: msg.content,
|
time: msg.time
|
});
|
}
|
}
|
|
};
|
|
function recvC2CMsg(msg) {
|
console.log("收到C2C消息:", JSON.stringify(msg));
|
var contentObj = JSON.parse(msg.content);
|
if (contentObj) {
|
if (contentObj.cmd == 'linkmic') {
|
if (contentObj.data.type && contentObj.data.type == 'request') {
|
event.onRecvJoinPusherRequest({
|
userID: msg.fromAccountNick,
|
userName: contentObj.data.userName,
|
userAvatar: contentObj.data.userAvatar
|
})
|
} else if (contentObj.data.type && contentObj.data.type == 'response') {
|
if (contentObj.data.result == 'accept') {
|
requestJoinCallback && requestJoinCallback({
|
errCode: 0,
|
errMsg: ''
|
});
|
} else if (contentObj.data.result == 'reject') {
|
requestJoinCallback && requestJoinCallback({
|
errCode: -999,
|
errMsg: '主播拒绝了你的请求'
|
});
|
}
|
} else if (contentObj.data.type && contentObj.data.type == 'kickout') {
|
event.onKickOut && event.onKickOut({
|
roomID: contentObj.data.roomID
|
});
|
}
|
}
|
}
|
}
|
|
function mergePushers() {
|
if (!roomInfo.hasJoinPusher) {
|
return;
|
}
|
getPushers({
|
data: {
|
roomID: roomInfo.roomID
|
},
|
success: function(ret) {
|
ret = ret.data;
|
/**
|
* enterPushers:新进推流人员信息
|
* leavePushers:退出推流人员信息
|
* ishave:用于判断去重操作
|
*/
|
var enterPushers = [],leavePushers = [],ishave = 0;
|
console.log('去重操作');
|
console.log('旧', JSON.stringify(roomInfo.pushers));
|
console.log('新',JSON.stringify(ret.pushers));
|
console.log('用户信息:', JSON.stringify(accountInfo));
|
ret.pushers && ret.pushers.forEach(function(val1){
|
ishave = 0;
|
roomInfo.pushers && roomInfo.pushers.forEach(function(val2) {
|
if(val1.userID == val2.userID) {
|
ishave = 1;
|
}
|
});
|
if(!ishave && val1.userID != accountInfo.userID)
|
enterPushers.push(val1);
|
ishave = 0;
|
});
|
roomInfo.pushers && roomInfo.pushers.forEach(function(val1) {
|
ishave = 0;
|
ret.pushers && ret.pushers.forEach(function(val2) {
|
if(val1.userID == val2.userID) {
|
ishave = 1;
|
}
|
});
|
if(!ishave)
|
leavePushers.push(val1);
|
ishave = 0;
|
});
|
// 重置roomInfo.pushers
|
roomInfo.pushers = ret.pushers;
|
// 通知有人进入房间
|
if(enterPushers.length) {
|
console.log('进房:', JSON.stringify(enterPushers));
|
event.onPusherJoin && event.onPusherJoin({
|
pushers: enterPushers
|
});
|
//混流
|
mergeStream(1);
|
}
|
// 通知有人退出房间
|
if(leavePushers.length) {
|
console.log('退房:', JSON.stringify(leavePushers));
|
event.onPusherQuit && event.onPusherQuit({
|
pushers: leavePushers
|
});
|
//混流
|
mergeStream(1);
|
}
|
},
|
fail: function(ret) {
|
// event.onRoomClose && event.onRoomClose({
|
// errCode: ret.errCode,
|
// errMsg: ret.errMsg
|
// });
|
}
|
});
|
};
|
|
|
function getPushers(object) {
|
var data = {};
|
if (object.data && object.data.roomID) {
|
data.roomID = object.data.roomID;
|
} else if (roomInfo.roomID) {
|
data.roomID = roomInfo.roomID;
|
} else {
|
object.fail && object.fail({
|
errCode: -999,
|
errMsg: '无roomID'
|
})
|
return;
|
}
|
//获取房间信息
|
request({
|
url: 'get_pushers',
|
data: data,
|
success: function (ret) {
|
if (ret.data.code) {
|
console.log('请求CGI:get_pushers失败', ret);
|
object.fail && object.fail({errCode: ret.data.code, errMsg: '请求CGI:get_pushers失败:' + ret.data.message + + '[' + ret.data.code + ']'});
|
return;
|
}
|
console.log("房间信息:", JSON.stringify(ret));
|
object.success && object.success(ret);
|
},
|
fail: object.fail
|
});
|
}
|
|
/**
|
* [sendRoomTextMsg 发送文本消息]
|
* @param {options}
|
* data: {
|
* msg: 文本消息
|
* }
|
*/
|
function sendRoomTextMsg(options) {
|
if (!options || !options.data.msg || !options.data.msg.replace(/^\s*|\s*$/g, '')) {
|
console.log('sendRoomTextMsg参数错误',options);
|
options.fail && options.fail({
|
errCode: -9,
|
errMsg: 'sendRoomTextMsg参数错误'
|
});
|
return;
|
}
|
webimhandler.sendCustomMsg({
|
data: '{"cmd":"CustomTextMsg","data":{"nickName":"'+accountInfo.userName+'","headPic":"'+accountInfo.userAvatar+'"}}',
|
text: options.data.msg
|
},function() {
|
options.success && options.success();
|
});
|
}
|
|
/**
|
* [pusherHeartBeat 推流者心跳]
|
* @param {options}
|
*/
|
function pusherHeartBeat(options) {
|
if (options) {
|
setTimeout(function () {
|
proto_pusherHeartBeat();
|
}, 3000);
|
}
|
if (heart) {
|
setTimeout(function () {
|
proto_pusherHeartBeat();
|
pusherHeartBeat();
|
}, 7000);
|
}
|
}
|
function proto_pusherHeartBeat() {
|
console.log('心跳请求');
|
request({
|
url: 'pusher_heartbeat',
|
data: {
|
roomID: roomInfo.roomID,
|
userID: accountInfo.userID
|
},
|
success: function (ret) {
|
if (ret.data.code) {
|
console.log('心跳失败:', ret);
|
return;
|
}
|
console.log('心跳成功', ret);
|
},
|
fail: function (ret) {
|
console.log('心跳失败:', ret);
|
}
|
});
|
}
|
|
/**
|
* [stopPusherHeartBeat 停止推流者心跳]
|
* @param {options}
|
*/
|
function stopPusherHeartBeat() {
|
heart = false;
|
}
|
|
/**
|
* [getRoomList 获取房间列表]
|
* @param {options}
|
* data: {
|
* index: 获取的房间开始索引,从0开始计算
|
* cnt: 获取的房间个数
|
* }
|
* success: 成功回调
|
* fail: 失败回调
|
*
|
* @return success
|
* rooms: 房间列表信息
|
*/
|
function getRoomList(options) {
|
if (!options) {
|
console.log('getRoomList参数错误',options);
|
options.fail && options.fail({
|
errCode: -9,
|
errMsg: 'getRoomList参数错误'
|
});
|
return;
|
}
|
request({
|
url: 'get_room_list',
|
data: {
|
index: options.data.index || 0,
|
cnt: options.data.cnt || 20
|
},
|
success: function (ret) {
|
if (ret.data.code) {
|
console.error('获取房间列表失败: ', ret);
|
options.fail && options.fail({
|
errCode: ret.data.code,
|
errMsg: ret.data.message + '[' + ret.data.code + ']'
|
});
|
return;
|
}
|
console.log("房间列表信息:", ret);
|
options.success && options.success({
|
rooms: ret.data.rooms
|
});
|
},
|
fail: function (ret) {
|
console.log('获取房间列表失败: ', ret);
|
if(ret.errMsg == 'request:fail timeout') {
|
var errCode = -1;
|
var errMsg = '网络请求超时,请检查网络状态';
|
}
|
options.fail && options.fail({
|
errCode: errCode || -1,
|
errMsg: errMsg || '获取房间列表失败'
|
});
|
}
|
});
|
}
|
|
|
/**
|
* [getPushURL 获取推流地址]
|
* @param {options}
|
* success: 成功回调
|
* fail: 失败回调
|
*
|
* @return success
|
* pushURL: 推流地址
|
*/
|
function getPushURL(options) {
|
if (!options) {
|
console.log('getPushURL参数错误',options);
|
options.fail && options.fail({
|
errCode: -9,
|
errMsg: 'getPushURL参数错误'
|
});
|
return;
|
}
|
request({
|
url: 'get_push_url',
|
data: {
|
userID: accountInfo.userID
|
},
|
success: function (ret) {
|
if (ret.data.code) {
|
console.log('获取推流地址失败: ', ret);
|
options.fail && options.fail({
|
errCode: ret.data.code,
|
errMsg: ret.data.message + '[' + ret.data.code + ']'
|
});
|
return;
|
}
|
console.log('获取推流地址成功:', ret.data.pushURL);
|
options.success && options.success({
|
pushURL: ret.data.pushURL
|
});
|
},
|
fail: function (ret) {
|
if(ret.errMsg == 'request:fail timeout') {
|
var errCode = -1;
|
var errMsg = '网络请求超时,请检查网络状态';
|
}
|
options.fail && options.fail({
|
errCode: errCode || -1,
|
errMsg: errMsg || '获取推流地址失败'
|
});
|
}
|
});
|
};
|
|
|
/**
|
* [setListener 设置监听事件]
|
* @param {options}
|
* onRoomClose: 群解散通知
|
* onRecvRoomTextMsg: 消息通知
|
*/
|
function setListener(options) {
|
if (!options) { console.log('setListener参数错误',options); return; }
|
event.onGetPusherList = options.onGetPusherList || function () {};
|
event.onPusherJoin = options.onPusherJoin || function () {};
|
event.onPusherQuit = options.onPusherQuit || function () {};
|
event.onRoomClose = options.onRoomClose || function () {};
|
event.onRecvRoomTextMsg = options.onRecvRoomTextMsg || function () {};
|
event.onRecvJoinPusherRequest = options.onRecvJoinPusherRequest || function () {};
|
event.onKickOut = options.onKickOut || function () {};
|
event.onRecvRoomCustomMsg = options.onRecvRoomCustomMsg || function () {};
|
event.onSketchpadData = options.onSketchpadData || function(){};
|
event.onPushersChange = options.onPushersChange || function() {};
|
}
|
|
/**
|
* [createRoom 创建房间]
|
* @param {options}
|
* data: {
|
* roomInfo: 房间名称
|
* pushURL: 推流地址
|
* }
|
* success: 成功回调
|
* fail: 失败回调
|
*/
|
function createRoom(options) {
|
roomInfo.isCreator = true;
|
roomInfo.isDestory = false;
|
roomInfo.isJoinGroup = false;
|
if (!options || !options.data.roomInfo || !options.data.pushURL) {
|
console.log('createRoom参数错误',options);
|
options.fail && options.fail({
|
errCode: -9,
|
errMsg: 'createRoom参数错误'
|
});
|
return;
|
}
|
roomInfo.roomInfo = options.data.roomInfo;
|
proto_createRoom(options);
|
}
|
function proto_createRoom(options) {
|
request({
|
url: 'create_room',
|
data: {
|
userID: accountInfo.userID,
|
roomInfo: roomInfo.roomInfo
|
},
|
success: function (ret) {
|
if (ret.data.code) {
|
console.log('创建房间失败:', ret);
|
options.fail && options.fail({
|
errCode: ret.data.code,
|
errMsg: ret.data.message + '[' + ret.data.code + ']'
|
});
|
return;
|
}
|
console.log('--->创建房间成功:', ret);
|
roomInfo.roomID = ret.data.roomID;
|
roomInfo.roomCreator = accountInfo.userID;
|
if (roomInfo.isDestory) {
|
roomInfo.isDestory = false;
|
destoryRoom({});
|
return;
|
}
|
options.data.roomID = ret.data.roomID;
|
// 进入IM群
|
webimhandler.applyJoinBigGroup(roomInfo.roomID, afterJoinBigGroup, {
|
success: function() {
|
joinPusher(options);
|
},
|
fail: options.fail
|
});
|
},
|
fail: function (ret) {
|
console.log('创建后台房间失败:', ret);
|
if(ret.errMsg == 'request:fail timeout') {
|
var errCode = -1;
|
var errMsg = '网络请求超时,请检查网络状态';
|
}
|
options.fail && options.fail({
|
errCode: errCode || -3,
|
errMsg: errMsg || '创建房间失败'
|
});
|
}
|
});
|
}
|
|
/**
|
* [joinPusher 加入推流]
|
* @param {options}
|
* data: {
|
* roomID: 房间ID
|
* pushURL: 推流地址
|
* }
|
* success: 成功回调
|
* fail: 失败回调
|
*/
|
function joinPusher(options) {
|
if(!options || !options.data.roomID || !options.data.pushURL) {
|
console.log('joinPusher参数错误',options);
|
options.fail && options.fail({
|
errCode: -9,
|
errMsg: 'joinPusher参数错误'
|
});
|
return;
|
}
|
roomInfo.roomID = options.data.roomID;
|
roomInfo.isDestory = false;
|
proto_joinPusher(options);
|
}
|
function proto_joinPusher(options) {
|
request({
|
url: 'add_pusher',
|
data: {
|
roomID: roomInfo.roomID,
|
userID: accountInfo.userID,
|
userName: accountInfo.userName,
|
userAvatar: accountInfo.userAvatar,
|
pushURL: options.data.pushURL
|
},
|
success: function(ret) {
|
if(ret.data.code) {
|
console.log('进入房间失败:',ret);
|
options.fail && options.fail({
|
errCode: ret.data.code,
|
errMsg: ret.data.message + '[' + ret.data.code + ']'
|
});
|
return;
|
}
|
roomInfo.hasJoinPusher = true;
|
mergePushers();
|
console.log('加入推流成功');
|
// 开始心跳
|
heart = true;
|
pusherHeartBeat(1);
|
options.success && options.success({roomID: roomInfo.roomID});
|
},
|
fail: function(ret) {
|
console.log('进入房间失败:',ret);
|
if(ret.errMsg == 'request:fail timeout') {
|
var errCode = -1;
|
var errMsg = '网络请求超时,请检查网络状态';
|
}
|
options.fail && options.fail({
|
errCode: errCode || -4,
|
errMsg: errMsg || '进入房间失败'
|
});
|
}
|
});
|
}
|
|
/**
|
* [enterRoom 进入房间]
|
* @param {options}
|
* data: {
|
* roomID: 房间ID
|
* }
|
* success: 成功回调
|
* fail: 失败回调
|
*/
|
function enterRoom(options) {
|
roomInfo.isCreator = false;
|
roomInfo.isJoinGroup = false;
|
if (!options || !options.data.roomID) {
|
console.log('enterRoom参数错误',options);
|
options.fail && options.fail({
|
errCode: -9,
|
errMsg: 'enterRoom参数错误'
|
});
|
return;
|
}
|
roomInfo.roomID = options.data.roomID;
|
proto_enterRoom({
|
success: function(ret) {
|
options.success && options.success(ret);
|
var userInfo = {
|
userName: accountInfo.userName,
|
userAvatar: accountInfo.userAvatar
|
}
|
addAudience({
|
data: {
|
roomID: options.data.roomID,
|
userID: accountInfo.userID,
|
userInfo: JSON.stringify(userInfo)
|
}
|
})
|
},
|
fail: options.fail
|
});
|
}
|
function proto_enterRoom(options) {
|
console.log('开始IM: ', roomInfo.roomID);
|
webimhandler.applyJoinBigGroup(roomInfo.roomID, afterJoinBigGroup, {
|
success: function(ret) {
|
getPushers({
|
data: {
|
roomID: roomInfo.roomID
|
},
|
success: function(ret) {
|
roomInfo.roomID = ret.data.roomID;
|
roomInfo.roomInfo = ret.data.roomInfo;
|
roomInfo.roomCreator = ret.data.roomCreator;
|
roomInfo.mixedPlayURL = ret.data.mixedPlayURL;
|
options.success && options.success({
|
roomID: roomInfo.roomID,
|
roomCreator: roomInfo.roomCreator,
|
mixedPlayURL: roomInfo.mixedPlayURL,
|
pushers: ret.data.pushers
|
});
|
},
|
fail: function(ret) {
|
options.fail && options.fail({
|
errCode: ret.errCode,
|
errMsg: ret.errMsg || '拉取主播信息失败'
|
});
|
}
|
});
|
},
|
fail: options.fail
|
});
|
}
|
|
/**
|
* [clearRequest 中断请求]
|
* @param {options}
|
*/
|
function clearRequest() {
|
for(var i = 0; i < requestSeq; i++) {
|
requestTask[i].abort();
|
}
|
requestTask = [];
|
requestSeq = 0;
|
}
|
|
/**
|
* [exitRoom 退出房间]
|
* @param {options}
|
*/
|
function exitRoom(options) {
|
if (roomInfo.isCreator) {
|
destoryRoom(options);
|
} else {
|
leaveRoom(options);
|
}
|
roomInfo.isDestory = true;
|
roomInfo.roomID = '';
|
roomInfo.pushers = [];
|
roomInfo.mixedPlayURL = "";
|
roomInfo.roomInfo = "";
|
accountInfo.pushURL = "";
|
accountInfo.isCreator = false;
|
}
|
|
/**
|
* [leaveRoom 退出房间]
|
*/
|
function leaveRoom(options) {
|
// 停止心跳
|
stopPusherHeartBeat();
|
// clearRequest();
|
roomInfo.isJoinGroup && webimhandler.quitBigGroup();
|
request({
|
url: 'delete_pusher',
|
data: {
|
roomID: roomInfo.roomID,
|
userID: accountInfo.userID
|
},
|
success: function(ret) {
|
if(ret.data.code) {
|
console.log('退出推流失败:',ret);
|
console.error('退房信息: roomID:' + roomInfo.roomID + ", userID:" + accountInfo.userID);
|
options.fail && options.fail({
|
errCode: ret.data.code,
|
errMsg: ret.data.message + '[' + ret.data.code + ']'
|
});
|
return;
|
}
|
console.log('退出推流成功');
|
roomInfo.roomID = '';
|
options.success && options.success({});
|
},
|
fail: function(ret) {
|
console.log('退出推流失败:',ret);
|
var errCode = ret.errCode || -1;
|
var errMsg = ret.errMsg || '退出房间失败'
|
if(ret.errMsg == 'request:fail timeout') {
|
errCode = -1;
|
errMsg = '网络请求超时,请检查网络状态';
|
}
|
options.fail && options.fail({
|
errCode: errCode,
|
errMsg: errMsg
|
});
|
}
|
});
|
|
delAudience({
|
data: {
|
userID: accountInfo.userID,
|
roomID: roomInfo.roomID
|
}
|
})
|
}
|
|
/**
|
* [destoryRoom 销毁房间]
|
*/
|
function destoryRoom(options) {
|
// 停止心跳
|
stopPusherHeartBeat();
|
// clearRequest();
|
roomInfo.isJoinGroup && webimhandler.quitBigGroup();
|
if(roomInfo.isDestory) return;
|
request({
|
url: 'destroy_room',
|
data: {
|
roomID: roomInfo.roomID,
|
userID: accountInfo.userID
|
},
|
success: function(ret) {
|
if(ret.data.code) {
|
console.log('关闭房间失败:',ret);
|
console.error('关闭房间失败: roomID:' + roomInfo.roomID + ", userID:" + accountInfo.userID);
|
options.fail && options.fail({
|
errCode: ret.data.code,
|
errMsg: ret.data.message + '[' + ret.data.code + ']'
|
});
|
return;
|
}
|
console.log('关闭房间成功');
|
roomInfo.roomID = '';
|
options.success && options.success({});
|
},
|
fail: function(ret) {
|
console.log('关闭房间失败:',ret);
|
var errCode = ret.errCode || -1;
|
var errMsg = ret.errMsg || '关闭房间失败'
|
if(ret.errMsg == 'request:fail timeout') {
|
errCode = -1;
|
errMsg = '网络请求超时,请检查网络状态';
|
}
|
options.fail && options.fail({
|
errCode: errCode,
|
errMsg: errMsg
|
});
|
}
|
});
|
}
|
|
function quitPusher(options) {
|
stopPusherHeartBeat();
|
request({
|
url: 'delete_pusher',
|
data: {
|
roomID: roomInfo.roomID,
|
userID: accountInfo.userID
|
},
|
success: function(ret) {
|
if(ret.data.code) {
|
console.log('退出推流失败:',ret);
|
options.fail && options.fail({
|
errCode: ret.data.code,
|
errMsg: ret.data.message + '[' + ret.data.code + ']'
|
});
|
return;
|
}
|
console.log('退出推流成功');
|
roomInfo.roomID = '';
|
roomInfo.pushers = [];
|
options.success && options.success({});
|
},
|
fail: function(ret) {
|
console.log('退出推流失败:',ret);
|
if(ret.errMsg == 'request:fail timeout') {
|
var errCode = -1;
|
var errMsg = '网络请求超时,请检查网络状态';
|
}
|
options.fail && options.fail({
|
errCode: errCode || -1,
|
errMsg: errMsg || '退出房间失败'
|
});
|
}
|
});
|
roomInfo.hasJoinPusher = false;
|
}
|
|
function requestJoinPusher (object) {
|
var body = {
|
cmd: 'linkmic',
|
data: {
|
type: 'request',
|
roomID: roomInfo.roomID,
|
userID: accountInfo.userID,
|
userName: accountInfo.userName,
|
userAvatar: accountInfo.userAvatar
|
}
|
}
|
|
requestJoinCallback = function(ret) {
|
if (gTimeoutID) {
|
clearTimeout(gTimeoutID);
|
gTimeoutID = null;
|
}
|
if (ret.errCode) {
|
object.fail && object.fail(ret);
|
} else {
|
object.success && object.success(ret);
|
}
|
}
|
|
var isTimeout = false;
|
gTimeoutID = setTimeout(function () {
|
gTimeoutID = null;
|
console.error('申请连麦超时:', JSON.stringify(object.data));
|
isTimeout = true;
|
requestJoinCallback && requestJoinCallback({
|
errCode: -999,
|
errMsg: '申请加入连麦超时'
|
});
|
}, (object.data && object.data.timeout)? object.data.timeout : 30000);
|
|
var msg = {
|
data: JSON.stringify(body)
|
}
|
webimhandler.sendC2CCustomMsg(roomInfo.roomCreator, msg, function (ret) {
|
if (isTimeout) {
|
return;
|
}
|
if (ret && ret.errCode) {
|
console.log('请求连麦失败:', JSON.stringify(ret));
|
requestJoinCallback && requestJoinCallback(ret);
|
return;
|
}
|
});
|
}
|
|
function acceptJoinPusher (object) {
|
var body = {
|
cmd: 'linkmic',
|
data: {
|
type: 'response',
|
result: 'accept',
|
message: '',
|
roomID: roomInfo.roomID
|
}
|
}
|
|
var msg = {
|
data: JSON.stringify(body)
|
}
|
webimhandler.sendC2CCustomMsg(object.data.userID, msg, function (ret) {});
|
}
|
|
function rejectJoinPusher (object) {
|
var body = {
|
cmd: 'linkmic',
|
data: {
|
type: 'response',
|
result: 'reject',
|
message: object.data.reason || '',
|
roomID: roomInfo.roomID
|
}
|
}
|
|
var msg = {
|
data: JSON.stringify(body)
|
}
|
webimhandler.sendC2CCustomMsg(object.data.userID, msg, function (ret) {});
|
}
|
|
function kickoutSubPusher (object) {
|
var body = {
|
cmd: 'linkmic',
|
data: {
|
type: 'kickout',
|
roomID: roomInfo.roomID
|
}
|
}
|
|
var msg = {
|
data: JSON.stringify(body)
|
}
|
webimhandler.sendC2CCustomMsg(object.data.userID, msg, function (ret) {
|
if (ret && ret.errCode==0) {
|
object.success && object.success(ret);
|
} else {
|
object.fail && object.fail(ret);
|
}
|
});
|
}
|
|
function getAccountInfo() {
|
return accountInfo;
|
}
|
|
/**
|
*
|
* @param {Int} retryCount
|
*/
|
function mergeStream(retryCount) {
|
if (accountInfo.userID != roomInfo.roomCreator) {
|
//大主播才能混流
|
return;
|
}
|
var mergeStreams = [];
|
if (roomInfo.pushers && roomInfo.pushers.length > 0) {
|
roomInfo.pushers.forEach(function (val) {
|
if (val.userID != roomInfo.roomCreator) {
|
//获取流id
|
var streamID = getStreamIDByStreamUrl(val.accelerateURL);
|
if (streamID) {
|
mergeStreams.push({
|
userID: val.userID,
|
streamID: streamID,
|
width: val.width,
|
height: val.height
|
});
|
}
|
} else {
|
bigAnchorStreamID = getStreamIDByStreamUrl(val.accelerateURL);
|
}
|
});
|
}
|
console.log("混流信息:", JSON.stringify(mergeStreams));
|
|
sendStreamMergeRequest(retryCount, mergeStreams);
|
}
|
|
function getStreamIDByStreamUrl(streamUrl) {
|
if (!streamUrl) {
|
return null;
|
}
|
//推流地址格式: rtmp://8888.livepush.myqcloud.com/path/8888_test_12345?txSecret=aaa&txTime=bbb
|
//拉流地址格式: rtmp://8888.livepush.myqcloud.com/path/8888_test_12345
|
// http://8888.livepush.myqcloud.com/path/8888_test_12345.flv
|
// http://8888.livepush.myqcloud.com/path/8888_test_12345.m3u8
|
|
var subStr = streamUrl;
|
var index = subStr.indexOf('?');
|
if (index >= 0) {
|
subStr = subStr.substring(0, index);
|
}
|
if (!subStr) {
|
return null;
|
}
|
index = subStr.lastIndexOf('/');
|
if (index >= 0) {
|
subStr = subStr.substring(index + 1);
|
}
|
if (!subStr) {
|
return null;
|
}
|
index = subStr.indexOf('.');
|
if (index >= 0) {
|
subStr = subStr.substring(0, index);
|
}
|
if (!subStr) {
|
return null;
|
}
|
return subStr;
|
}
|
|
function sendStreamMergeRequest(retryCount, mergeStreams) {
|
if (retryCount < 0) {
|
return;
|
}
|
|
var mergeInfo = createMergeInfo(mergeStreams);
|
console.log('混流信息:', JSON.stringify(mergeInfo));
|
|
doMergeRequest(mergeInfo, function (ret) {
|
if (ret) {
|
console.log('混流成功');
|
} else {
|
console.log('混流失败');
|
setTimeout(() => {
|
retryCount--;
|
sendStreamMergeRequest(retryCount, mergeStreams);
|
}, 2000);
|
}
|
});
|
}
|
|
function doMergeRequest(mergeInfo, callback) {
|
request({
|
url: 'merge_stream',
|
data: {
|
userID: accountInfo.userID,
|
roomID: roomInfo.roomID,
|
mergeParams: JSON.stringify(mergeInfo)
|
},
|
success: function (ret) {
|
if (ret.data.code || ret.data.result.code) {
|
console.error('混流失败:', JSON.stringify(ret));
|
callback(false);
|
return;
|
}
|
callback(true);
|
},
|
fail: function (ret) {
|
callback(false);
|
}
|
})
|
}
|
|
function createMergeInfo(mergeStreams) {
|
console.log("混流原始信息:", JSON.stringify(mergeStreams));
|
|
var smallAnchorWidth = 160;
|
var smallAnchorHeight = 240;
|
var offsetHeight = 90;
|
if (bigAnchorWidth < 540 || bigAnchorHeight < 960) {
|
smallAnchorWidth = 120;
|
smallAnchorHeight = 180;
|
offsetHeight = 60;
|
}
|
|
//组装混流JSON结构体
|
var streamInfoArray = [];
|
if (mergeStreams && mergeStreams.length > 0) {
|
|
//大主播
|
var bigAnchorInfo = {
|
input_stream_id: bigAnchorStreamID || '',
|
layout_params: {
|
image_layer: 1
|
}
|
}
|
streamInfoArray.push(bigAnchorInfo);
|
|
//小主播
|
var subLocationX = bigAnchorWidth - smallAnchorWidth;
|
var subLocationY = bigAnchorHeight - smallAnchorHeight - offsetHeight;
|
if (mergeStreams && mergeStreams.length > 0) {
|
var layerIndex = 0
|
mergeStreams.forEach(function (val) {
|
//组装JSON
|
var smallAchorInfo = {
|
input_stream_id: val.streamID,
|
layout_params: {
|
image_layer: layerIndex + 2,
|
image_width: smallAnchorWidth,
|
image_height: smallAnchorHeight,
|
location_x: subLocationX,
|
location_y: subLocationY - layerIndex * smallAnchorHeight
|
}
|
}
|
streamInfoArray.push(smallAchorInfo);
|
layerIndex++;
|
});
|
}
|
} else {
|
var bigAnchorInfo = {
|
input_stream_id: bigAnchorStreamID || '',
|
layout_params: {
|
image_layer: 1
|
}
|
}
|
streamInfoArray.push(bigAnchorInfo);
|
}
|
|
var para = {
|
app_id: accountInfo.sdkAppID.toString(),
|
interface: 'mix_streamv2.start_mix_stream_advanced',
|
mix_stream_session_id: bigAnchorStreamID,
|
output_stream_id: bigAnchorStreamID,
|
input_stream_list: streamInfoArray
|
}
|
|
var interfaceObj = {
|
interfaceName: 'Mix_StreamV2',
|
para: para
|
}
|
|
var reqParam = {
|
timestamp: Math.round((Date.now() / 1000)),
|
eventId: Math.round((Date.now() / 1000)),
|
interface: interfaceObj
|
}
|
|
return reqParam;
|
}
|
|
function setVideoRatio(ratio) {
|
if (ratio == 1) {
|
//9:16
|
bigAnchorWidth = 360;
|
bigAnchorHeight = 640;
|
} else {
|
//3:4
|
bigAnchorWidth = 480;
|
bigAnchorHeight = 640;
|
}
|
}
|
|
function sendC2CCustomMsg(object) {
|
var body = {
|
cmd: object.cmd,
|
data: {
|
userID: accountInfo.userID,
|
userName: accountInfo.userName,
|
userAvatar: accountInfo.userAvatar,
|
msg: object.msg || ''
|
}
|
}
|
var msg = {
|
data: JSON.stringify(body)
|
}
|
webimhandler.sendC2CCustomMsg(object.toUserID?object.toUserID:roomInfo.roomCreator, msg, function (ret) {
|
if (ret && ret.errCode) {
|
console.log('请求连麦失败:', JSON.stringify(ret));
|
object.fail && object.fail(ret);
|
return;
|
}
|
object.success && object.success({});
|
});
|
}
|
|
//观众进房时,向后台发送进房通知
|
function addAudience(object) {
|
request({
|
url: 'add_audience',
|
data: {
|
userID: accountInfo.userID,
|
roomID: object.data.roomID,
|
userInfo: object.data.userInfo
|
},
|
success: function (ret) {
|
if (ret.data.code) {
|
console.log('增加观众请求失败', ret);
|
object.fail && object.fail({errCode: ret.data.code, errMsg: '增加观众请求失败:' + ret.data.message + + '[' + ret.data.code + ']'});
|
return;
|
}
|
object.success && object.success(ret);
|
},
|
fail: object.fail
|
});
|
}
|
|
//观众退房时,向后台发送退房通知
|
function delAudience(object) {
|
request({
|
url: 'delete_audience',
|
data: {
|
userID: object.data.userID,
|
roomID: object.data.roomID
|
},
|
success: function (ret) {
|
if (ret.data.code) {
|
console.log('减少观众请求失败', ret);
|
object.fail && object.fail({errCode: ret.data.code, errMsg: '减少观众请求失败:' + ret.data.message + + '[' + ret.data.code + ']'});
|
return;
|
}
|
object.success && object.success(ret);
|
},
|
fail: object.fail
|
});
|
}
|
|
/**
|
* 对外暴露函数
|
* @type {Object}
|
*/
|
module.exports = {
|
login: login, // 初始化
|
logout: logout, // 结束初始化
|
getRoomList: getRoomList, // 拉取房间列表
|
getPushURL: getPushURL, // 拉取推流地址
|
createRoom: createRoom, // 创建房间
|
enterRoom: enterRoom, // 加入房间
|
exitRoom: exitRoom, // 退出房间
|
sendRoomTextMsg: sendRoomTextMsg, // 发送文本消息
|
setListener: setListener, // 设置监听事件
|
joinPusher: joinPusher,
|
quitPusher: quitPusher,
|
requestJoinPusher: requestJoinPusher,
|
acceptJoinPusher: acceptJoinPusher,
|
rejectJoinPusher: rejectJoinPusher,
|
kickoutSubPusher: kickoutSubPusher,
|
getAccountInfo: getAccountInfo,
|
setVideoRatio: setVideoRatio,
|
sendC2CCustomMsg: sendC2CCustomMsg,
|
getPushers: getPushers
|
// addRemoteView: addRemoteView,
|
// deleteRemoteView: deleteRemoteView
|
}
|