Files
2026-01-12 17:52:15 +08:00

439 lines
15 KiB
TypeScript
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.
/**
* @module LiveSeatState
* @module_description
* 直播间麦位管理模块
* 核心功能:实现多人连麦场景下的座位控制,支持复杂的座位状态管理和音视频设备控制。
* 技术特点:基于音视频技术,支持多路音视频流管理,提供座位锁定、设备控制、权限管理等高级功能。
* 业务价值为多人互动直播提供核心技术支撑支持PK、连麦、多人游戏等丰富的互动场景。
* 应用场景多人连麦、主播PK、互动游戏、在线教育、会议直播等需要多人音视频互动的场景。
*/
import { ref } from "vue";
import {
TakeSeatOptions, LeaveSeatOptions, MuteMicrophoneOptions, UnmuteMicrophoneOptions, KickUserOutOfSeatOptions,
MoveUserToSeatOptions, UnlockSeatOptions, SeatUserInfoParam, LockSeatOptions,
OpenRemoteCameraOptions, CloseRemoteCameraOptions, OpenRemoteMicrophoneOptions, CloseRemoteMicrophoneOptions, ILiveListener,
} from "@/uni_modules/tuikit-atomic-x";
import { getRTCRoomEngineManager } from "./rtcRoomEngine";
import { callUTSFunction, safeJsonParse } from "../utils/utsUtils";
/**
* 区域信息参数类型定义
* @typedef {Object} RegionInfoParams
* @property {number} x X坐标位置
* @property {number} y Y坐标位置
* @property {number} w 宽度
* @property {number} h 高度
* @property {number} zorder 层级顺序
* @memberof module:LiveSeatState
*/
export type RegionInfoParams = {
x : number;
y : number;
w : number;
h : number;
zorder : number;
}
/**
* 直播画布参数类型定义
* @typedef {Object} LiveCanvasParams
* @property {number} w 画布宽度
* @property {number} h 画布高度
* @property {string} [background] 背景色(可选)
* @memberof module:LiveSeatState
*/
export type LiveCanvasParams = {
w : number;
h : number;
background ?: string;
}
/**
* 座位信息类型定义
* @typedef {Object} SeatInfo
* @property {number} index 座位索引
* @property {boolean} isLocked 是否锁定
* @property {SeatUserInfoParam} userInfo 座位上用户信息
* @property {RegionInfoParams} region 座位区域信息
* @memberof module:LiveSeatState
*/
export type SeatInfo = {
index : number;
isLocked : boolean;
userInfo : SeatUserInfoParam;
region : RegionInfoParams;
}
/**
* 座位列表
* @type {Ref<SeatInfo[]>}
* @memberof module:LiveSeatState
* @example
* import { useLiveSeatState } from '@/uni_modules/tuikit-atomic-x/state/LiveSeatState';
* const { seatList } = useLiveSeatState('your_live_id');
*
* // 监听座位列表变化
* watch(seatList, (newSeatList) => {
* if (newSeatList && newSeatList.length > 0) {
* console.log('座位列表更新:', newSeatList);
* newSeatList.forEach(seat => {
* console.log('座位索引:', seat.index);
* console.log('座位是否锁定:', seat.isLocked);
* if (seat.userInfo) {
* console.log('座位上用户ID:', seat.userInfo.userID);
* }
* });
* }
* });
*
* // 获取当前座位列表
* const seats = seatList.value;
* console.log('当前座位数:', seats.length);
*/
const seatList = ref<SeatInfo[]>([]);
/**
* 画布信息
* @type {Ref<LiveCanvasParams | null>}
* @memberof module:LiveSeatState
* @example
* import { useLiveSeatState } from '@/uni_modules/tuikit-atomic-x/state/LiveSeatState';
* const { canvas } = useLiveSeatState('your_live_id');
*
* // 监听画布信息变化
* watch(canvas, (newCanvas) => {
* if (newCanvas) {
* console.log('画布信息更新:', newCanvas);
* }
* });
*
* // 获取当前画布信息
* const currentCanvas = canvas.value;
* if (currentCanvas) {
* console.log('当前画布分辨率:', currentCanvas.w, 'x', currentCanvas.h);
* }
*/
const canvas = ref<LiveCanvasParams | null>(null);
/**
* 正在说话的用户列表
* @type {Ref<Map<string, number> | null>}
* @memberof module:LiveSeatState
* @example
* import { useLiveSeatState } from '@/uni_modules/tuikit-atomic-x/state/LiveSeatState';
* const { speakingUsers } = useLiveSeatState('your_live_id');
*
* // 监听正在说话的用户列表变化
* watch(speakingUsers, (newSpeakingUsers) => {
* if (newSpeakingUsers && newSpeakingUsers.size > 0) {
* console.log('正在说话的用户更新');
* newSpeakingUsers.forEach((volume, userID) => {
* console.log('用户ID:', userID);
* console.log('音量:', volume);
* });
* }
* });
*
* // 获取当前正在说话的用户数量
* const users = speakingUsers.value;
* if (users) {
* console.log('当前说话的用户数:', users.size);
* }
*/
const speakingUsers = ref<Map<string, number> | null>(null);
/**
* 用户上麦
* @param {TakeSeatOptions} params - 上麦参数
* @returns {void}
* @memberof module:LiveSeatState
* @example
* import { useLiveSeatState } from '@/uni_modules/tuikit-atomic-x/state/LiveSeatState';
* const { takeSeat } = useLiveSeatState('your_live_id');
* takeSeat({
* seatIndex: 1,
* onSuccess: () => console.log('上麦成功'),
* onError: (error) => console.error('上麦失败:', error)
* });
*/
function takeSeat(params : TakeSeatOptions) : void {
callUTSFunction("takeSeat", params);
}
/**
* 用户下麦
* @param {LeaveSeatOptions} params - 下麦参数
* @returns {void}
* @memberof module:LiveSeatState
* @example
* import { useLiveSeatState } from '@/uni_modules/tuikit-atomic-x/state/LiveSeatState';
* const { leaveSeat } = useLiveSeatState('your_live_id');
* leaveSeat({
* onSuccess: () => console.log('下麦成功'),
* onError: (error) => console.error('下麦失败:', error)
* });
*/
function leaveSeat(params : LeaveSeatOptions) : void {
callUTSFunction("leaveSeat", params);
}
/**
* 静音麦克风
* @param {MuteMicrophoneOptions} params - 静音参数
* @returns {void}
* @memberof module:LiveSeatState
* @example
* import { useLiveSeatState } from '@/uni_modules/tuikit-atomic-x/state/LiveSeatState';
* const { muteMicrophone } = useLiveSeatState('your_live_id');
* muteMicrophone({
* onSuccess: () => console.log('麦克风静音成功'),
* onError: (error) => console.error('麦克风静音失败:', error)
* });
*/
function muteMicrophone(params : MuteMicrophoneOptions) : void {
callUTSFunction("muteMicrophone", params);
}
/**
* 取消静音麦克风
* @param {UnmuteMicrophoneOptions} params - 取消静音参数
* @returns {void}
* @memberof module:LiveSeatState
* @example
* import { useLiveSeatState } from '@/uni_modules/tuikit-atomic-x/state/LiveSeatState';
* const { unmuteMicrophone } = useLiveSeatState('your_live_id');
* unmuteMicrophone({
* onSuccess: () => console.log('麦克风取消静音成功'),
* onError: (error) => console.error('麦克风取消静音失败:', error)
* });
*/
function unmuteMicrophone(params : UnmuteMicrophoneOptions) : void {
callUTSFunction("unmuteMicrophone", params);
}
/**
* 将用户踢出座位
* @param {KickUserOutOfSeatOptions} params - 踢出参数
* @returns {void}
* @memberof module:LiveSeatState
* @example
* import { useLiveSeatState } from '@/uni_modules/tuikit-atomic-x/state/LiveSeatState';
* const { kickUserOutOfSeat } = useLiveSeatState('your_live_id');
* kickUserOutOfSeat({
* seatIndex: 1,
* onSuccess: () => console.log('踢出用户成功'),
* onError: (error) => console.error('踢出用户失败:', error)
* });
*/
function kickUserOutOfSeat(params : KickUserOutOfSeatOptions) : void {
callUTSFunction("kickUserOutOfSeat", params);
}
/**
* 移动用户到指定座位
* @param {MoveUserToSeatOptions} params - 移动参数
* @returns {void}
* @memberof module:LiveSeatState
* @example
* import { useLiveSeatState } from '@/uni_modules/tuikit-atomic-x/state/LiveSeatState';
* const { moveUserToSeat } = useLiveSeatState('your_live_id');
* moveUserToSeat({
* fromSeatIndex: 1,
* toSeatIndex: 3,
* onSuccess: () => console.log('用户移动成功'),
* onError: (error) => console.error('用户移动失败:', error)
* });
*/
function moveUserToSeat(params : MoveUserToSeatOptions) : void {
callUTSFunction("moveUserToSeat", params);
}
/**
* 锁定座位
* @param {LockSeatOptions} params - 锁定参数
* @returns {void}
* @memberof module:LiveSeatState
* @example
* import { useLiveSeatState } from '@/uni_modules/tuikit-atomic-x/state/LiveSeatState';
* const { lockSeat } = useLiveSeatState('your_live_id');
* lockSeat({
* seatIndex: 2,
* onSuccess: () => console.log('座位锁定成功'),
* onError: (error) => console.error('座位锁定失败:', error)
* });
*/
function lockSeat(params : LockSeatOptions) : void {
callUTSFunction("lockSeat", params);
}
/**
* 解锁座位
* @param {UnlockSeatOptions} params - 解锁参数
* @returns {void}
* @memberof module:LiveSeatState
* @example
* import { useLiveSeatState } from '@/uni_modules/tuikit-atomic-x/state/LiveSeatState';
* const { unlockSeat } = useLiveSeatState('your_live_id');
* unlockSeat({
* seatIndex: 2,
* onSuccess: () => console.log('座位解锁成功'),
* onError: (error) => console.error('座位解锁失败:', error)
* });
*/
function unlockSeat(params : UnlockSeatOptions) : void {
callUTSFunction("unlockSeat", params);
}
/**
* 开启远程摄像头
* @param {OpenRemoteCameraOptions} params - 开启摄像头参数
* @returns {void}
* @memberof module:LiveSeatState
* @example
* import { useLiveSeatState } from '@/uni_modules/tuikit-atomic-x/state/LiveSeatState';
* const { openRemoteCamera } = useLiveSeatState('your_live_id');
* openRemoteCamera({
* seatIndex: 1,
* onSuccess: () => console.log('远程摄像头开启成功'),
* onError: (error) => console.error('远程摄像头开启失败:', error)
* });
*/
function openRemoteCamera(params : OpenRemoteCameraOptions) : void {
callUTSFunction("openRemoteCamera", params);
}
/**
* 关闭远程摄像头
* @param {CloseRemoteCameraOptions} params - 关闭摄像头参数
* @returns {void}
* @memberof module:LiveSeatState
* @example
* import { useLiveSeatState } from '@/uni_modules/tuikit-atomic-x/state/LiveSeatState';
* const { closeRemoteCamera } = useLiveSeatState('your_live_id');
* closeRemoteCamera({
* seatIndex: 1,
* onSuccess: () => console.log('远程摄像头关闭成功'),
* onError: (error) => console.error('远程摄像头关闭失败:', error)
* });
*/
function closeRemoteCamera(params : CloseRemoteCameraOptions) : void {
callUTSFunction("closeRemoteCamera", params);
}
/**
* 开启远程麦克风
* @param {OpenRemoteMicrophoneOptions} params - 开启麦克风参数
* @returns {void}
* @memberof module:LiveSeatState
* @example
* import { useLiveSeatState } from '@/uni_modules/tuikit-atomic-x/state/LiveSeatState';
* const { openRemoteMicrophone } = useLiveSeatState('your_live_id');
* openRemoteMicrophone({
* seatIndex: 1,
* onSuccess: () => console.log('远程麦克风开启成功'),
* onError: (error) => console.error('远程麦克风开启失败:', error)
* });
*/
function openRemoteMicrophone(params : OpenRemoteMicrophoneOptions) : void {
callUTSFunction("openRemoteMicrophone", params);
}
/**
* 关闭远程麦克风
* @param {CloseRemoteMicrophoneOptions} params - 关闭麦克风参数
* @returns {void}
* @memberof module:LiveSeatState
* @example
* import { useLiveSeatState } from '@/uni_modules/tuikit-atomic-x/state/LiveSeatState';
* const { closeRemoteMicrophone } = useLiveSeatState('your_live_id');
* closeRemoteMicrophone({
* seatIndex: 1,
* onSuccess: () => console.log('远程麦克风关闭成功'),
* onError: (error) => console.error('远程麦克风关闭失败:', error)
* });
*/
function closeRemoteMicrophone(params : CloseRemoteMicrophoneOptions) : void {
callUTSFunction("closeRemoteMicrophone", params);
}
/**
* 添加座位事件监听
* @param {string} liveID - 直播间ID
* @param {string} eventName - 事件名称,可选值: 'onLocalCameraOpenedByAdmin'(本地摄像头被管理员开启)<br>'onLocalCameraClosedByAdmin'(本地摄像头被管理员关闭)<br>'onLocalMicrophoneOpenedByAdmin'(本地麦克风被管理员开启)<br>'onLocalMicrophoneClosedByAdmin'(本地麦克风被管理员关闭)
* @param {ILiveListener} listener - 事件处理函数
* @returns {void}
* @memberof module:LiveSeatState
* @example
* import { useLiveSeatState } from '@/uni_modules/tuikit-atomic-x/state/LiveSeatState';
* const { addLiveSeatEventListener } = useLiveSeatState('your_live_id');
* addLiveSeatEventListener('your_live_id', 'onLocalCameraOpenedByAdmin', {
* callback: (params) => {
* console.log('result:', params);
* }
* });
*/
function addLiveSeatEventListener(liveID : string, eventName : string, listener : ILiveListener) : void {
getRTCRoomEngineManager().addLiveSeatEventListener(liveID, eventName, listener);
}
/**
* 移除座位事件监听
* @param {string} liveID - 直播间ID
* @param {string} eventName - 事件名称,可选值: 'onLocalCameraOpenedByAdmin'(本地摄像头被管理员开启)<br>'onLocalCameraClosedByAdmin'(本地摄像头被管理员关闭)<br>'onLocalMicrophoneOpenedByAdmin'(本地麦克风被管理员开启)<br>'onLocalMicrophoneClosedByAdmin'(本地麦克风被管理员关闭)
* @param {ILiveListener} listener - 事件处理函数
* @returns {void}
* @memberof module:LiveSeatState
* @example
* import { useLiveSeatState } from '@/uni_modules/tuikit-atomic-x/state/LiveSeatState';
* const { removeLiveSeatEventListener } = useLiveSeatState('your_live_id');
* removeLiveSeatEventListener('your_live_id', 'onLocalCameraOpenedByAdmin', seatListener);
*/
function removeLiveSeatEventListener(liveID : string, eventName : string, listener : ILiveListener) : void {
getRTCRoomEngineManager().removeLiveSeatEventListener(liveID, eventName, listener);
}
const onLiveSeatStoreChanged = (eventName : string, res : string) : void => {
try {
if (eventName === "seatList") {
seatList.value = safeJsonParse<SeatInfo[]>(res, []);
} else if (eventName === "canvas") {
canvas.value = safeJsonParse<LiveCanvasParams | null>(res, null);
} else if (eventName === "speakingUsers") {
speakingUsers.value = safeJsonParse<Map<string, number> | null>(res, null);
}
} catch (error) {
console.error("onLiveSeatStoreChanged error:", error);
}
};
function bindEvent(liveID : string) : void {
getRTCRoomEngineManager().on("liveSeatStoreChanged", onLiveSeatStoreChanged, liveID);
}
export function useLiveSeatState(liveID : string) {
bindEvent(liveID);
return {
seatList, // 座位列表
canvas, // 画布信息
speakingUsers, // 正在说话的用户列表
takeSeat, // 用户上麦
leaveSeat, // 用户下麦
muteMicrophone, // 静音麦克风
unmuteMicrophone, // 取消静音麦克风
kickUserOutOfSeat, // 将用户踢出座位
moveUserToSeat, // 移动用户到指定座位
lockSeat, // 锁定座位
unlockSeat, // 解锁座位
openRemoteCamera, // 开启远程摄像头
closeRemoteCamera, // 关闭远程摄像头
openRemoteMicrophone, // 开启远程麦克风
closeRemoteMicrophone, // 关闭远程麦克风
addLiveSeatEventListener, // 添加座位事件监听
removeLiveSeatEventListener, // 移除座位事件监听
};
}
export default useLiveSeatState;