fcr-core
Version:
Core APIs for building online scenes
225 lines (208 loc) • 7.19 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.FcrJoinHelper = void 0;
exports.handleJoinRetryFailure = handleJoinRetryFailure;
require("core-js/modules/esnext.iterator.constructor.js");
require("core-js/modules/esnext.iterator.map.js");
var _imports = require("../imports");
var _type = require("../type");
var _retryHelpers = require("./retry-helpers");
var _error = require("./error");
var _constants = require("../room-control/helpers/constants");
/**
* 房间加入辅助工具
*
* 提供房间控制相关的加入逻辑和参数构建,统一处理 room-control 和 room-router 中的公共逻辑
*/
/**
* 通用的加入房间重试失败处理器
* @param error 错误对象
* @param waitBeforeRetry 等待重试函数
* @param attemptCount 当前重试次数
* @param canRetryFn 判断是否可以重试的函数
* @param onAborted 中止回调函数
* @param logPrefix 日志前缀
* @returns 是否继续重试
*/
async function handleJoinRetryFailure(error, waitBeforeRetry, attemptCount, canRetryFn, onAborted, logPrefix = 'join room') {
onAborted?.();
if (!canRetryFn(error)) {
throw error;
}
// 记录重试信息
console.warn(`Retry attempt ${attemptCount + 1} to ${logPrefix}: ${error.message}`);
await waitBeforeRetry();
return true;
}
/**
* 房间加入辅助工具类
*
* 封装了房间加入的公共逻辑,包括:
* - 快照方式加入房间
* - CheckIn 流程处理
* - 重试机制
* - 错误处理
*/
class FcrJoinHelper {
constructor(_userId, _scene, _api) {
this._userId = _userId;
this._scene = _scene;
this._api = _api;
}
/**
* 构建CheckIn请求参数(统一的参数构建方法)
* @static
* @param options 加入选项
* @param userId 用户ID
* @param roomId 房间ID
* @param extraParams 额外参数(bypass, avatar等)
* @returns CheckIn参数对象
*/
static buildCheckInParams(options, userId, roomId, extraParams) {
return {
userName: options.userName,
userId,
userRole: _type.FcrUserRoleToStringMap[options.userRole],
userProperties: options.userProperties,
roomId,
platform: options.platform ?? (0, _imports.getPlatform)(),
streams: options.createStreamConfigs?.map(s => ({
videoSourceUuid: s.videoSourceId,
audioSourceUuid: s.audioSourceId,
streamName: s.streamName,
...(0, _imports.convertStreamTypeToPublishState)(s.streamType),
videoSourceType: s.videoSourceType,
audioSourceType: s.audioSourceType,
audioSourceState: s.audioSourceState,
videoSourceState: s.videoSourceState
})),
version: (0, _imports.getVersion)(),
password: options.password,
// 只有在提供了额外参数时才添加
...(extraParams?.bypass !== undefined && {
bypass: extraParams.bypass
}),
...(extraParams?.avatar !== undefined && {
avatar: extraParams.avatar
})
};
}
/**
* 构建CheckIn请求参数的便捷方法(使用当前实例的配置)
* @private
* @param options 加入选项
* @param extraParams 额外参数(bypass, avatar等)
* @returns CheckIn参数对象
*/
_buildCheckInParams(options, extraParams) {
const userId = this._userId;
const roomId = this._scene.sceneId;
// 使用静态方法,但参数来自实例
return FcrJoinHelper.buildCheckInParams(options, userId, roomId, extraParams);
}
/**
*
* @param options 加入选项
* @param extraParams 额外参数(bypass, avatar等)
* @param useInternalApi 是否使用 checkInInternal API(默认 true)
* @returns CheckIn响应数据
*/
async performCheckIn(options, extraParams, useInternalApi = true) {
const checkInParams = this._buildCheckInParams(options, extraParams);
const apiCall = useInternalApi ? () => this._api.checkInInternal(checkInParams) : () => this._api.checkIn(checkInParams);
const errorMessage = useInternalApi ? 'check in internal failed' : 'check in failed';
const res = await (0, _error.handleRequestError)(apiCall, _error.FcrErrorModuleCode.ROOM, errorMessage);
return res;
}
/**
* 通过 CheckIn 流程加入房间(包含重试逻辑)
*
* 重试逻辑说明:
* - 重试范围:整个 "CheckIn + Join" 流程
* - 重试原因:CheckIn 和 Join 是关联操作,如果 Join 失败需要重新 CheckIn
* - 重试判断:使用 canRetryJoinError 判断错误是否可重试
* - 最大重试次数:ROOM_CONTROL_CONSTANTS.MAX_JOIN_ATTEMPTS
*
* @param options 加入选项
* @param onJoinAborted 加入中止检查函数
* @returns Promise<void>
*/
async joinWithCheckIn(options, onJoinAborted) {
await (0, _imports.retryAttempt)(async () => {
// 1. 执行 CheckIn(单次调用,无内部重试)
const {
data,
ts
} = await this.performCheckIn(options);
// 2. 检查是否中止
if (onJoinAborted) {
onJoinAborted();
}
// 3. 使用 CheckIn 结果加入房间
await this._joinScene(options.streamLatency, options.streamEncryptionConfig, data, ts);
// 4. 再次检查是否中止
if (onJoinAborted) {
onJoinAborted();
}
}, [], {
retriesMax: _constants.ROOM_CONTROL_CONSTANTS.MAX_JOIN_ATTEMPTS
}).fail(async ({
error,
timeFn: waitBeforeRetry,
currentRetry: attemptCount
}) => {
return handleJoinRetryFailure(error, waitBeforeRetry, attemptCount, _retryHelpers.canRetryJoinError, onJoinAborted, 'join room via CheckIn');
}).exec();
}
/**
* 使用快照数据加入房间的核心方法
* @private
*/
async _joinScene(streamLatency, streamEncryptionConfig, snapshot, timestamp, onJoinAborted) {
await this._scene.join({
streamLatency: streamLatency,
streamEncryptionConfig,
snapshot,
timestamp
});
// 执行中止检查(如果提供了回调)
if (onJoinAborted) {
onJoinAborted();
}
}
/**
* 统一的房间加入处理
*
* 根据选项类型自动选择合适的加入方式:
* - 如果有 snapshot 属性:直接使用快照数据加入
* - 否则:执行 CheckIn 流程后加入
*
* @param options 加入选项(支持普通加入和快照加入)
* @param onJoinAborted 加入中止检查函数
* @returns Promise<void>
*/
async join(options, onJoinAborted) {
if (this._isSnapshotJoin(options)) {
await this._joinWithSnapshot(options, onJoinAborted);
} else {
await this.joinWithCheckIn(options, onJoinAborted);
}
}
/**
* 判断是否为快照加入模式
* @private
*/
_isSnapshotJoin(options) {
return 'snapshot' in options;
}
/**
* 快照加入模式:直接使用快照数据加入房间
* @private
*/
async _joinWithSnapshot(options, onJoinAborted) {
await this._joinScene(options.streamLatency || _type.FcrStreamLatencyLevel.ULTRA_LOW, options.streamEncryptionConfig, options.snapshot, options.timestamp, onJoinAborted);
}
}
exports.FcrJoinHelper = FcrJoinHelper;