1597 lines
42 KiB
JavaScript
1597 lines
42 KiB
JavaScript
|
/**
|
|||
|
* @file liveroom.js 直播模式房间管理sdk
|
|||
|
* @author binniexu
|
|||
|
*/
|
|||
|
var webim = require('./webim_wx.js');
|
|||
|
var webimhandler = require('./webim_handler.js');
|
|||
|
|
|||
|
|
|||
|
//移动直播(<mlvb-live-room>)使用此地址实现房间服务和连麦功能
|
|||
|
var RoomServiceUrl = "https://liveroom.qcloud.com/weapp/live_room/",
|
|||
|
|
|||
|
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, // 是否已解散
|
|||
|
hasJoinAnchor: false,
|
|||
|
roomStatusCode: 0
|
|||
|
},
|
|||
|
// 事件
|
|||
|
event = {
|
|||
|
onAnchorEnter: function () {}, // 进房通知
|
|||
|
onAnchorExit: function () {}, // 退房通知
|
|||
|
onRoomDestroy: function() {}, // 群解散通知
|
|||
|
onRecvRoomTextMsg: function() {}, // 消息通知
|
|||
|
onRequestJoinAnchor: function() {}, //大主播收到小主播连麦请求通知
|
|||
|
onKickoutJoinAnchor: function() {}, //小主播被踢通知
|
|||
|
onRecvRoomCustomMsg: function() {}, //自定义消息通知
|
|||
|
onSketchpadData: function(){}
|
|||
|
};
|
|||
|
// 随机昵称
|
|||
|
var userName = ['林静晓', '陆杨', '江辰', '付小司', '陈小希', '吴柏松', '肖奈', '芦苇微微', '一笑奈何', '立夏'];
|
|||
|
// 请求数
|
|||
|
var requestNum = 0;
|
|||
|
var requestJoinCallback = null;
|
|||
|
var bigAnchorStreamID = '';
|
|||
|
var bigAnchorWidth = 360;
|
|||
|
var bigAnchorHeight = 640;
|
|||
|
var gTimeoutID = null;
|
|||
|
var mTimeDiff = 0;
|
|||
|
|
|||
|
/**
|
|||
|
* [request 封装request请求]
|
|||
|
* @param {options}
|
|||
|
* url: 请求接口url
|
|||
|
* data: 请求参数
|
|||
|
* success: 成功回调
|
|||
|
* fail: 失败回调
|
|||
|
* complete: 完成回调
|
|||
|
*/
|
|||
|
function request(options) {
|
|||
|
requestNum++;
|
|||
|
console.log('requestNum: ', requestNum);
|
|||
|
requestTask[requestSeq++] = wx.request({
|
|||
|
url: RoomServiceUrl + 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: {
|
|||
|
* userID: 用户ID
|
|||
|
* userSig: 用户sig
|
|||
|
* sdkAppID: IM应用ID
|
|||
|
* userName: 用户昵称
|
|||
|
* userAvatar: 用户头像地址
|
|||
|
* }
|
|||
|
* success: 成功回调
|
|||
|
* fail: 失败回调
|
|||
|
*
|
|||
|
* @return success
|
|||
|
* userName: 用户昵称
|
|||
|
*/
|
|||
|
function login(options) {
|
|||
|
if (!options || !options.data.sdkAppID || !options.data.userID || !options.data.userSig) {
|
|||
|
console.log('init参数错误',options);
|
|||
|
options.fail && options.fail({
|
|||
|
errCode: -9,
|
|||
|
errMsg: 'init参数错误'
|
|||
|
});
|
|||
|
return;
|
|||
|
}
|
|||
|
accountInfo.userID = options.data.userID;
|
|||
|
accountInfo.userSig = options.data.userSig;
|
|||
|
accountInfo.sdkAppID = options.data.sdkAppID;
|
|||
|
accountInfo.userName = options.data.userName || userName[Math.floor(Math.random()*10)] || accountInfo.userID;
|
|||
|
accountInfo.userAvatar = options.data.userAvatar || '123';
|
|||
|
|
|||
|
request({
|
|||
|
url: 'login',
|
|||
|
params: {
|
|||
|
accountType: '0',
|
|||
|
sdkAppID: accountInfo.sdkAppID,
|
|||
|
userSig: accountInfo.userSig,
|
|||
|
platform: "WeChat"
|
|||
|
},
|
|||
|
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;
|
|||
|
mTimeDiff = Math.round(Date.now()) - ret.data.timestamp;
|
|||
|
// // 登录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){}
|
|||
|
});
|
|||
|
accountInfo.userID = '';
|
|||
|
accountInfo.userSig = '';
|
|||
|
accountInfo.sdkAppID = '';
|
|||
|
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: '0',
|
|||
|
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': "0", //用户所属应用帐号类型,填0
|
|||
|
'identifier': accountInfo.userID, //当前用户ID,必须是否字符串类型,选填
|
|||
|
'identifierNick': accountInfo.userID, //当前用户昵称,选填
|
|||
|
'userSig': accountInfo.userSig, //当前用户身份凭证,必须是字符串类型,选填
|
|||
|
};
|
|||
|
//监听(多终端同步)群系统消息方法,方法都定义在demo_group_notice.js文件中
|
|||
|
var onGroupSystemNotifys = {
|
|||
|
// 群被解散(全员接收)
|
|||
|
"5": function (notify) {
|
|||
|
roomInfo.isDestory = true;
|
|||
|
event.onRoomDestroy();
|
|||
|
},
|
|||
|
"11": webimhandler.onRevokeGroupNotify, //群已被回收(全员接收)
|
|||
|
// 用户自定义通知(默认全员接收)
|
|||
|
"255": function (notify) {
|
|||
|
// console.error('收到系统通知:', notify.UserDefinedField);
|
|||
|
// var content = JSON.parse(notify.UserDefinedField);
|
|||
|
// if (content && content.cmd == 'notifyPusherChange') {
|
|||
|
// mergeAnchors();
|
|||
|
// }
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
//监听连接状态回调变化事件
|
|||
|
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 && options.errCode != 10025) {
|
|||
|
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) {
|
|||
|
console.log('消息内容为空',msg)
|
|||
|
return;
|
|||
|
}
|
|||
|
console.log('IM消息0: ',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,
|
|||
|
nature:0//1进群
|
|||
|
});
|
|||
|
} else {
|
|||
|
var contentObj,newContent;
|
|||
|
try {
|
|||
|
newContent = msg.content.split('}}');
|
|||
|
contentObj = JSON.parse(newContent[0] + '}}');
|
|||
|
} catch (e) {
|
|||
|
console.warn("IM消息解析异常,重新按json格式解析");
|
|||
|
newContent = new Array(1);
|
|||
|
newContent[0] = msg.content;
|
|||
|
contentObj = JSON.parse(msg.content);
|
|||
|
}
|
|||
|
console.log('contentObj',contentObj)
|
|||
|
if(contentObj.cmd == 'CustomTextMsg') {//自定义发送消息
|
|||
|
msg.userName = contentObj.data.nickname;
|
|||
|
msg.userAvatar = contentObj.data.avatarurl;
|
|||
|
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,
|
|||
|
nature:contentObj.data.nature,//2//发送消息
|
|||
|
userid:contentObj.data.userid,
|
|||
|
gift_name:contentObj.data.gift_name?contentObj.data.gift_name:'',
|
|||
|
gift_ico:contentObj.data.gift_ico?contentObj.data.gift_ico:'',
|
|||
|
number:contentObj.data.number?contentObj.data.number:''
|
|||
|
|
|||
|
});
|
|||
|
} 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,
|
|||
|
nature:4//未知
|
|||
|
});
|
|||
|
} else if (contentObj.cmd == 'notifyPusherChange') {
|
|||
|
mergeAnchors();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
};
|
|||
|
|
|||
|
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.onRequestJoinAnchor({
|
|||
|
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.onKickoutJoinAnchor && event.onKickoutJoinAnchor({
|
|||
|
roomID: contentObj.data.roomID
|
|||
|
});
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
function notifyPusherChange() {
|
|||
|
var customMsg = {
|
|||
|
cmd: "notifyPusherChange",
|
|||
|
data: {}
|
|||
|
}
|
|||
|
var strCustomMsg = JSON.stringify(customMsg);
|
|||
|
webimhandler.sendCustomMsg({data:strCustomMsg, text:"notify"}, null)
|
|||
|
}
|
|||
|
|
|||
|
function mergeAnchors() {
|
|||
|
if (!roomInfo.hasJoinAnchor) {
|
|||
|
return;
|
|||
|
}
|
|||
|
getAnchors({
|
|||
|
data: {
|
|||
|
roomID: roomInfo.roomID
|
|||
|
},
|
|||
|
success: function(ret) {
|
|||
|
ret = ret.data;
|
|||
|
|
|||
|
innerMergerAnchors(ret)
|
|||
|
},
|
|||
|
fail: function(ret) {
|
|||
|
// event.onRoomDestroy && event.onRoomDestroy({
|
|||
|
// errCode: ret.errCode,
|
|||
|
// errMsg: ret.errMsg
|
|||
|
// });
|
|||
|
}
|
|||
|
});
|
|||
|
};
|
|||
|
|
|||
|
function innerMergerAnchors(data) {
|
|||
|
/**
|
|||
|
* enterPushers:新进推流人员信息
|
|||
|
* leavePushers:退出推流人员信息
|
|||
|
* ishave:用于判断去重操作
|
|||
|
*/
|
|||
|
var enterPushers = [],leavePushers = [],ishave = 0;
|
|||
|
console.log('去重操作');
|
|||
|
console.log('旧', JSON.stringify(roomInfo.pushers));
|
|||
|
console.log('新',JSON.stringify(data.pushers));
|
|||
|
console.log('用户信息:', JSON.stringify(accountInfo));
|
|||
|
data.pushers && data.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;
|
|||
|
data.pushers && data.pushers.forEach(function(val2) {
|
|||
|
if(val1.userID == val2.userID) {
|
|||
|
ishave = 1;
|
|||
|
}
|
|||
|
});
|
|||
|
if(!ishave)
|
|||
|
leavePushers.push(val1);
|
|||
|
ishave = 0;
|
|||
|
});
|
|||
|
if (data.roomStatusCode) {
|
|||
|
roomInfo.roomStatusCode = data.roomStatusCode
|
|||
|
}
|
|||
|
// 重置roomInfo.pushers
|
|||
|
roomInfo.pushers = data.pushers;
|
|||
|
// 通知有人进入房间
|
|||
|
if(enterPushers.length) {
|
|||
|
console.log('进房:', JSON.stringify(enterPushers));
|
|||
|
event.onAnchorEnter && event.onAnchorEnter({
|
|||
|
pushers: enterPushers
|
|||
|
});
|
|||
|
//混流
|
|||
|
mergeStream(1);
|
|||
|
}
|
|||
|
// 通知有人退出房间
|
|||
|
if(leavePushers.length) {
|
|||
|
console.log('退房:', JSON.stringify(leavePushers));
|
|||
|
event.onAnchorExit && event.onAnchorExit({
|
|||
|
pushers: leavePushers
|
|||
|
});
|
|||
|
//混流
|
|||
|
mergeStream(1);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
function getAnchors(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_anchors',
|
|||
|
data: data,
|
|||
|
success: function (ret) {
|
|||
|
if (ret.data.code) {
|
|||
|
console.log('请求CGI:get_anchors失败', ret);
|
|||
|
object.fail && object.fail({errCode: ret.data.code, errMsg: '请求CGI:get_anchors失败:' + ret.data.message + + '[' + ret.data.code + ']'});
|
|||
|
return;
|
|||
|
}
|
|||
|
console.log("房间信息0:");
|
|||
|
console.log(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;
|
|||
|
}
|
|||
|
console.log('sendRoomTextMsg 发送文本消息',options)
|
|||
|
webimhandler.sendCustomMsg({
|
|||
|
data: '{"cmd":"CustomTextMsg","data":{"nickname":"'+options.data.nickname+'","avatarurl":"'+options.data.avatarurl+'","nature":"'+options.data.nature+'","gift_name":"'+options.data.gift_name+'","gift_ico":"'+options.data.gift_ico+'","number":"'+options.data.number+'","userid":"'+options.data.userid+'"}}',
|
|||
|
text: options.data.msg
|
|||
|
},function() {
|
|||
|
options.success && options.success();
|
|||
|
});
|
|||
|
}
|
|||
|
/*
|
|||
|
发送礼物
|
|||
|
*/
|
|||
|
function sendRoomGiftMsg(options) {
|
|||
|
if (!options || !options.data.msg || !options.data.msg.replace(/^\s*|\s*$/g, '')) {
|
|||
|
console.log('sendRoomGiftMsg参数错误',options);
|
|||
|
options.fail && options.fail({
|
|||
|
errCode: -9,
|
|||
|
errMsg: 'sendRoomGiftMsg参数错误'
|
|||
|
});
|
|||
|
return;
|
|||
|
}
|
|||
|
console.log('sendRoomGiftMsg 发送礼物消息',options)
|
|||
|
webimhandler.sendCustomMsg({
|
|||
|
data: '{"cmd":"CustomTextMsg","data":{"nickname":"'+options.data.nickname+'","avatarurl":"'+options.data.avatarurl+'","nature":"3"}}',
|
|||
|
text: '发送礼物'
|
|||
|
},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: 'anchor_heartbeat',
|
|||
|
data: {
|
|||
|
roomID: roomInfo.roomID,
|
|||
|
userID: accountInfo.userID,
|
|||
|
roomStatusCode: roomInfo.roomStatusCode
|
|||
|
},
|
|||
|
success: function (ret) {
|
|||
|
if (ret.data.code) {
|
|||
|
console.log('心跳失败:', ret);
|
|||
|
return;
|
|||
|
}
|
|||
|
if (ret.data.pushers) {
|
|||
|
innerMergerAnchors(ret.data);
|
|||
|
}
|
|||
|
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_anchor_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}
|
|||
|
* onRoomDestroy: 群解散通知
|
|||
|
* onRecvRoomTextMsg: 消息通知
|
|||
|
*/
|
|||
|
function setListener(options) {
|
|||
|
if (!options) { console.log('setListener参数错误',options); return; }
|
|||
|
event.onAnchorEnter = options.onAnchorEnter || function () {};
|
|||
|
event.onAnchorExit = options.onAnchorExit || function () {};
|
|||
|
event.onRoomDestroy = options.onRoomDestroy || function () {};
|
|||
|
event.onRecvRoomTextMsg = options.onRecvRoomTextMsg || function () {};
|
|||
|
event.onRequestJoinAnchor = options.onRequestJoinAnchor || function () {};
|
|||
|
event.onKickoutJoinAnchor = options.onKickoutJoinAnchor || function () {};
|
|||
|
event.onRecvRoomCustomMsg = options.onRecvRoomCustomMsg || function () {};
|
|||
|
event.onSketchpadData = options.onSketchpadData || 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) {
|
|||
|
var createRoomInfo = {
|
|||
|
userID: accountInfo.userID,
|
|||
|
roomInfo: roomInfo.roomInfo
|
|||
|
};
|
|||
|
if (options.data.roomID && options.data.roomID.length > 0) {
|
|||
|
createRoomInfo.roomID = options.data.roomID;
|
|||
|
}
|
|||
|
request({
|
|||
|
url: 'create_room',
|
|||
|
data: createRoomInfo,
|
|||
|
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群
|
|||
|
var createIMGroupInfo = {
|
|||
|
roomID: options.data.roomID,
|
|||
|
userID: accountInfo.userID,
|
|||
|
roomName: options.data.roomID
|
|||
|
}
|
|||
|
webimhandler.createBigGroup(createIMGroupInfo, afterJoinBigGroup, {
|
|||
|
success: function() {
|
|||
|
joinAnchor(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 || '创建房间失败'
|
|||
|
});
|
|||
|
}
|
|||
|
});
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* [joinAnchor 加入推流]
|
|||
|
* @param {options}
|
|||
|
* data: {
|
|||
|
* roomID: 房间ID
|
|||
|
* pushURL: 推流地址
|
|||
|
* }
|
|||
|
* success: 成功回调
|
|||
|
* fail: 失败回调
|
|||
|
*/
|
|||
|
function joinAnchor(options) {
|
|||
|
if(!options || !options.data.roomID || !options.data.pushURL) {
|
|||
|
console.log('joinAnchor参数错误',options);
|
|||
|
options.fail && options.fail({
|
|||
|
errCode: -9,
|
|||
|
errMsg: 'joinAnchor参数错误'
|
|||
|
});
|
|||
|
return;
|
|||
|
}
|
|||
|
roomInfo.roomID = options.data.roomID;
|
|||
|
roomInfo.isDestory = false;
|
|||
|
proto_joinAnchor(options);
|
|||
|
}
|
|||
|
function proto_joinAnchor(options) {
|
|||
|
request({
|
|||
|
url: 'add_anchor',
|
|||
|
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.hasJoinAnchor = true;
|
|||
|
mergeAnchors();
|
|||
|
console.log('加入推流成功');
|
|||
|
// 开始心跳
|
|||
|
heart = true;
|
|||
|
pusherHeartBeat(1);
|
|||
|
//通知房间内其他主播
|
|||
|
notifyPusherChange();
|
|||
|
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) {
|
|||
|
getAnchors({
|
|||
|
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();
|
|||
|
//通知房间内其他主播
|
|||
|
notifyPusherChange();
|
|||
|
// clearRequest();
|
|||
|
roomInfo.isJoinGroup && webimhandler.quitBigGroup();
|
|||
|
request({
|
|||
|
url: 'delete_anchor',
|
|||
|
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('退出推流成功');
|
|||
|
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.destroyGroup();
|
|||
|
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('关闭房间成功');
|
|||
|
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 quitJoinAnchor(options) {
|
|||
|
stopPusherHeartBeat();
|
|||
|
request({
|
|||
|
url: 'delete_anchor',
|
|||
|
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.pushers = [];
|
|||
|
//通知房间内其他主播
|
|||
|
notifyPusherChange();
|
|||
|
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.hasJoinAnchor = false;
|
|||
|
}
|
|||
|
|
|||
|
function requestJoinAnchor (object) {
|
|||
|
var body = {
|
|||
|
cmd: 'linkmic',
|
|||
|
data: {
|
|||
|
type: 'request',
|
|||
|
roomID: roomInfo.roomID,
|
|||
|
userID: accountInfo.userID,
|
|||
|
userName: accountInfo.userName,
|
|||
|
userAvatar: accountInfo.userAvatar,
|
|||
|
timestamp: Math.round(Date.now()) - mTimeDiff
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
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 acceptJoinAnchor (object) {
|
|||
|
var body = {
|
|||
|
cmd: 'linkmic',
|
|||
|
data: {
|
|||
|
type: 'response',
|
|||
|
result: 'accept',
|
|||
|
reason: '',
|
|||
|
roomID: roomInfo.roomID,
|
|||
|
timestamp: Math.round(Date.now()) - mTimeDiff
|
|||
|
}
|
|||
|
}
|
|||
|
console.log('要连麦房间信息')
|
|||
|
console.log(body)
|
|||
|
var msg = {
|
|||
|
data: JSON.stringify(body)
|
|||
|
}
|
|||
|
webimhandler.sendC2CCustomMsg(object.data.userID, msg, function (ret) {});
|
|||
|
}
|
|||
|
|
|||
|
function rejectJoinAnchor (object) {
|
|||
|
var body = {
|
|||
|
cmd: 'linkmic',
|
|||
|
data: {
|
|||
|
type: 'response',
|
|||
|
result: 'reject',
|
|||
|
reason: object.data.reason || '主播拒绝了您的连麦请求',
|
|||
|
roomID: roomInfo.roomID,
|
|||
|
timestamp: Math.round(Date.now()) - mTimeDiff
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
var msg = {
|
|||
|
data: JSON.stringify(body)
|
|||
|
}
|
|||
|
webimhandler.sendC2CCustomMsg(object.data.userID, msg, function (ret) {});
|
|||
|
}
|
|||
|
|
|||
|
function kickoutJoinAnchor (object) {
|
|||
|
var body = {
|
|||
|
cmd: 'linkmic',
|
|||
|
data: {
|
|||
|
type: 'kickout',
|
|||
|
roomID: roomInfo.roomID,
|
|||
|
timestamp: Math.round(Date.now()) - mTimeDiff
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
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.merge_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) {
|
|||
|
console.log('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) {
|
|||
|
console.log('观众进房时,向后台发送进房通知',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, // 发送文本消息
|
|||
|
sendRoomGiftMsg: sendRoomGiftMsg, // 发送文本消息
|
|||
|
setListener: setListener, // 设置监听事件
|
|||
|
joinAnchor: joinAnchor, //加入连麦
|
|||
|
quitJoinAnchor: quitJoinAnchor, //退出连麦
|
|||
|
requestJoinAnchor: requestJoinAnchor,
|
|||
|
acceptJoinAnchor: acceptJoinAnchor,
|
|||
|
rejectJoinAnchor: rejectJoinAnchor,
|
|||
|
kickoutJoinAnchor: kickoutJoinAnchor,
|
|||
|
getAccountInfo: getAccountInfo,
|
|||
|
setVideoRatio: setVideoRatio,
|
|||
|
sendC2CCustomMsg: sendC2CCustomMsg,
|
|||
|
getAnchors: getAnchors
|
|||
|
// addRemoteView: addRemoteView,
|
|||
|
// deleteRemoteView: deleteRemoteView
|
|||
|
}
|