fcr-core
Version:
Core APIs for building online scenes
232 lines (216 loc) • 8.86 kB
JavaScript
;
require("core-js/modules/es.array.push.js");
require("core-js/modules/esnext.iterator.filter.js");
require("core-js/modules/esnext.iterator.for-each.js");
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.FcrJoinHelper = void 0;
exports.handleJoinRetryFailure = handleJoinRetryFailure;
require("core-js/modules/es.regexp.exec.js");
require("core-js/modules/esnext.iterator.constructor.js");
require("core-js/modules/esnext.iterator.map.js");
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _imports = require("../imports");
var _type = require("../type");
var _retryHelpers = require("./retry-helpers");
var _error = require("./error");
var _constants = require("../room-control/helpers/constants");
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } /**
* 房间加入辅助工具
*
* 提供房间控制相关的加入逻辑和参数构建,统一处理 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) {
let logPrefix = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : 'join room';
onAborted === null || onAborted === void 0 || onAborted();
if (!canRetryFn(error)) {
throw error;
}
// 记录重试信息
console.warn("Retry attempt ".concat(attemptCount + 1, " to ").concat(logPrefix, ": ").concat(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) {
var _options$platform, _options$createStream;
return _objectSpread(_objectSpread({
userName: options.userName,
userId,
userRole: _type.FcrUserRoleToStringMap[options.userRole],
userProperties: options.userProperties,
roomId,
platform: (_options$platform = options.platform) !== null && _options$platform !== void 0 ? _options$platform : (0, _imports.getPlatform)(),
streams: (_options$createStream = options.createStreamConfigs) === null || _options$createStream === void 0 ? void 0 : _options$createStream.map(s => _objectSpread(_objectSpread({
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 === null || extraParams === void 0 ? void 0 : extraParams.bypass) !== undefined && {
bypass: extraParams.bypass
}), (extraParams === null || extraParams === void 0 ? void 0 : 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) {
let useInternalApi = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 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 _ref => {
let {
error,
timeFn: waitBeforeRetry,
currentRetry: attemptCount
} = _ref;
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;