shop-live/static/js/mlvbliveroomcore.js

1597 lines
42 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* @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
}