import Event from "../utils/event"; import User from "../model/user"; import Stream from "../model/stream"; import { EVENT } from "../common/constants"; const TAG_NAME = 'UserController'; /** * 通讯成员管理 */ class UserController { constructor(componentContext) { // userMap 用于存储完整的数据结构 this.userMap = new Map(); // userList 用于存储简化的用户数据 Object,包括 {userID hasMainAudio hasMainVideo hasAuxAudio hasAuxVideo} this.userList = []; // streamList 存储steam 对象列表,用于trtc-room 渲染 player this.streamList = []; this._emitter = new Event(); this.componentContext = componentContext; this.isNewVersion = componentContext.isNewVersion; } userEventHandler(event) { const code = event.detail.code; let data; if (event.detail.message && typeof event.detail.message === 'string') { try { data = JSON.parse(event.detail.message); } catch (exception) { console.warn(TAG_NAME, 'userEventHandler 数据格式错误', exception); return false; } } else { console.warn(TAG_NAME, 'userEventHandler 数据格式错误'); return false; } switch (code) { case 1020: // console.log(TAG_NAME, '远端用户全量列表更新:', code) if (!this.isNewVersion) {// TODO 旧版SDK处理逻辑,返回全量的用户列表,需要对userList 进行前后对比,筛选出新增用户,暂不实现 } break; case 1031: // console.log(TAG_NAME, '远端用户进房通知:', code) // 1031 有新用户 // { // "userlist":[ // { // "userid":"webrtc11" // } // ] // } this.addUser(data); break; case 1032: // console.log(TAG_NAME, '远端用户退房通知:', code) // 1032 有用户退出 this.removeUser(data); break; case 1033: // console.log(TAG_NAME, '远端用户视频状态位变化通知:', code) // 1033 用户视频状态变化,新增stream或者更新stream 状态 // { // "userlist":[ // { // "userid":"webrtc11", // "playurl":" room://rtc.tencent.com?userid=xxx&streamtype=main", // "streamtype":"main", // "hasvideo":true // } // ] // } this.updateUserVideo(data); break; case 1034: // console.log(TAG_NAME, '远端用户音频状态位变化通知:', code) // 1034 用户音频状态变化 // { // "userlist":[ // { // "userid":"webrtc11", // "playurl":" room://rtc.tencent.com?userid=xxx&streamtype=main", // "hasaudio":false // } // ] // } this.updateUserAudio(data); break; } } /** * 处理用户进房事件 * @param {Object} data pusher 下发的数据 */ addUser(data) { // console.log(TAG_NAME, 'addUser', data) const incomingUserList = data.userlist; const userMap = this.userMap; if (Array.isArray(incomingUserList) && incomingUserList.length > 0) { incomingUserList.forEach(item => { const userID = item.userid; // 已经在 map 中的用户 let user = this.getUser(userID); if (!user) { // 新增用户 user = new User({ userID: userID }); this.userList.push({ userID: userID }); } userMap.set(userID, user); this._emitter.emit(EVENT.REMOTE_USER_JOIN, { userID: userID, userList: this.userList }); // console.log(TAG_NAME, 'addUser', item, userMap.get(userID), this.userMap) }); } } /** * 处理用户退房事件 * @param {Object} data pusher 下发的数据 {userlist} */ removeUser(data) { // console.log(TAG_NAME, 'removeUser', data) const incomingUserList = data.userlist; if (Array.isArray(incomingUserList) && incomingUserList.length > 0) { incomingUserList.forEach(item => { const userID = item.userid; let user = this.getUser(userID); // 从userList 里删除指定的用户和 stream this._removeUserAndStream(userID); // 重置 user.streams['main'] && user.streams['main'].reset(); user.streams['aux'] && user.streams['aux'].reset(); // 用户退出,释放引用,外部调用该 user 所有stream 的 playerContext.stop() 方法停止播放 // TODO 触发时机提前了,方便外部用户做出处理,时机仍需进一步验证 this._emitter.emit(EVENT.REMOTE_USER_LEAVE, { userID: userID, userList: this.userList, streamList: this.streamList }); user = undefined; this.userMap.delete(userID); // console.log(TAG_NAME, 'removeUser', this.userMap) }); } } /** * 处理用户视频通知事件 * @param {Object} data pusher 下发的数据 {userlist} */ updateUserVideo(data) { // console.log(TAG_NAME, 'updateUserVideo', data) const incomingUserList = data.userlist; if (Array.isArray(incomingUserList) && incomingUserList.length > 0) { incomingUserList.forEach(item => { // 修改现有用户属性 const userID = item.userid; const streamType = item.streamtype; const hasVideo = item.hasvideo; const src = item.playurl; const user = this.getUser(userID); // 如果找到该用户,则更新用户的信息和相关的stream 信息 if (user) { let stream = user.streams[streamType]; // console.log(TAG_NAME, 'updateUserVideo', user, streamType, stream) // 更新指定的stream if (!stream) { user.streams[streamType] = stream = new Stream({ streamType: streamType }); // 更新streamList this.streamList.push(stream); } if (hasVideo && streamType === 'aux') { stream.objectFit = 'contain'; } if (!hasVideo && streamType === 'aux') { // TODO 如果是辅流可能要移除该 stream this._removeStream(stream); } else { stream.userID = userID; stream.streamID = userID + '_' + streamType; stream.hasVideo = hasVideo; stream.src = src; } // 更新所属user 的 hasXxx 值 this.userList.find(item => { if (item.userID === userID) { item[`has${streamType.replace(/^\S/, s => s.toUpperCase())}Video`] = hasVideo; return true; } }); const eventName = hasVideo ? EVENT.REMOTE_VIDEO_ADD : EVENT.REMOTE_VIDEO_REMOVE; this._emitter.emit(eventName, { stream: stream, streamList: this.streamList, userList: this.userList }); // console.log(TAG_NAME, 'updateUserVideo', user, stream, this.userMap) } }); } } /** * 处理用户音频通知事件 * @param {Object} data pusher 下发的数据 {userlist} */ updateUserAudio(data) { // console.log(TAG_NAME, 'updateUserAudio', data) const incomingUserList = data.userlist; if (Array.isArray(incomingUserList) && incomingUserList.length > 0) { incomingUserList.forEach(item => { const userID = item.userid; // 音频只跟着 stream main ,这里只修改 main const streamType = 'main'; const hasAudio = item.hasaudio; const src = item.playurl; const user = this.getUser(userID); if (user) { let stream = user.streams[streamType]; if (!stream) { user.streams[streamType] = stream = new Stream({ streamType: streamType }); // 更新streamList this.streamList.push(stream); } stream.userID = userID; stream.streamID = userID + '_' + streamType; stream.hasAudio = hasAudio; stream.src = src; // 更新所属 user 的 hasXxx 值 this.userList.find(item => { if (item.userID === userID) { item[`has${streamType.replace(/^\S/, s => s.toUpperCase())}Audio`] = hasAudio; return true; } }); const eventName = hasAudio ? EVENT.REMOTE_AUDIO_ADD : EVENT.REMOTE_AUDIO_REMOVE; this._emitter.emit(eventName, { stream: stream, streamList: this.streamList, userList: this.userList }); // console.log(TAG_NAME, 'updateUserAudio', user, stream, this.userMap) } }); } } /** * * @param {String} userID 用户ID * @returns {Object} */ getUser(userID) { return this.userMap.get(userID); } getStream({ userID, streamType }) { const user = this.userMap.get(userID); if (user) { return user.streams[streamType]; } return undefined; } getUserList() { return this.userList; } getStreamList() { return this.streamList; } /** * 重置所有user 和 steam * @returns {Object} */ reset() { this.streamList.forEach(item => { item.reset(); }); this.streamList = []; this.userList = []; this.userMap.clear(); return { userList: this.userList, streamList: this.streamList }; } on(eventCode, handler, context) { this._emitter.on(eventCode, handler, context); } off(eventCode, handler) { this._emitter.off(eventCode, handler); } /** * 删除用户和所有的 stream * @param {String} userID 用户ID */ _removeUserAndStream(userID) { this.streamList = this.streamList.filter(item => { return item.userID !== userID && item.userID !== ''; }); this.userList = this.userList.filter(item => { return item.userID !== userID; }); } _removeStream(stream) { this.streamList = this.streamList.filter(item => { if (item.userID === stream.userID && item.streamType === stream.streamType) { return false; } return true; }); } } export default UserController;