shop-live/static/js/mlvbliveroomcore.js

1597 lines
42 KiB
JavaScript
Raw Normal View History

2023-10-28 16:37:57 +08:00
/**
* @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
}