@xmaxai/web-sdk 1.0.9 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/Foundation/Runtime/XmaxEnvironment.ts","../src/Foundation/Logging/XmaxLogger.ts","../src/Foundation/Errors/XmaxError.ts","../src/Foundation/Runtime/RuntimeInfo.ts","../src/Service/Network/ApiService.ts","../src/Service/Realtime/RealtimeVideoFormat.ts","../src/Foundation/RTC/RtcProvider.ts","../src/Service/Realtime/RealtimeModel.ts","../src/Foundation/Media/Video/VideoSize.ts","../src/Service/Media/MediaService.ts","../src/Service/Storage/StoredFile.ts","../src/Service/Storage/StorageService.ts","../src/Foundation/Media/Camera/CameraPosition.ts","../src/Foundation/Permissions/MediaPermissionWatch.ts","../src/Foundation/RTC/NetworkStatistics.ts","../src/Foundation/RTC/RtcStatsLogger.ts","../src/Foundation/RTC/Agora/AgoraRtcStatistics.ts","../src/Foundation/RTC/RtcVideoOrientation.ts","../src/Foundation/RTC/Agora/AgoraVideoSender.ts","../src/Foundation/Media/Camera/CameraRotation.ts","../src/Foundation/Media/Camera/CameraRotationProcessor.ts","../src/Foundation/RTC/Agora/AgoraRtcManager.ts","../src/Foundation/RTC/TRTC/RtcEngineManager.ts","../src/Foundation/RTC/TRTC/TrtcAudioVolumeSupport.ts","../src/Foundation/RTC/TRTC/TrtcRtcManager.ts","../src/Foundation/RTC/VeRTC/VeRtcStatistics.ts","../src/Foundation/RTC/VeRTC/VeRtcManager.ts","../src/Foundation/RTC/RtcFactory.ts","../src/Service/Realtime/RealtimeExtraParams.ts","../src/Foundation/Media/Camera/CameraFrameValidator.ts","../src/Foundation/Permissions/PermissionManager.ts","../src/Service/Realtime/VideoRenderBinding.ts","../src/Service/Realtime/RealtimeVideoTrack.ts","../src/Service/Realtime/RealtimeMediaStream.ts","../src/Media/Camera/CameraController.ts","../src/Media/Video/LocalVideoController.ts","../src/Media/Video/ExternalVideoController.ts","../src/Foundation/Media/MediaOperation.ts","../src/Foundation/Media/Audio/ExternalAudioWorklet.ts","../src/Foundation/Media/Audio/ExternalAudioFrameSource.ts","../src/Media/External/ExternalFrameSink.ts","../src/Media/External/ExternalFrameSourceController.ts","../src/Media/Image/LocalImageController.ts","../src/Foundation/Media/Video/VideoContentMode.ts","../src/Service/Realtime/RealtimePoint.ts","../src/Media/Interaction/InteractionCoordinateMapper.ts","../src/Media/Interaction/InteractionController.ts","../src/Media/MediaController.ts","../src/Foundation/Media/Audio/RemoteAudioWorklet.ts","../src/Foundation/Media/Audio/RemoteAudioFrameSource.ts","../src/Foundation/Media/Video/RemoteVideoFrameSource.ts","../src/Render/Video/RealtimeMediaFrameDispatcher.ts","../src/Render/Video/RemoteMediaFrameController.ts","../src/Render/Trajectory/TrajectoryBinding.ts","../src/Render/Trajectory/TrajectoryRegistry.ts","../src/Render/RenderController.ts","../src/Service/Realtime/RealtimeContext.ts","../src/Service/Realtime/RealtimeSession.ts","../src/Foundation/RTC/RoomJoinConfiguration.ts","../src/Service/Realtime/RealtimeSessionConnection.ts","../src/Service/Realtime/RealtimeSessionService.ts","../src/Service/Realtime/RealtimeState.ts","../src/Foundation/RTC/RemoteStream.ts","../src/Stream/Room/RoomEvent.ts","../src/Stream/Encoding/EncodingBitratePolicy.ts","../src/Stream/Room/RoomHeartbeat.ts","../src/Stream/Room/RoomMessageCodec.ts","../src/Stream/Room/RoomController.ts","../src/Stream/Encoding/EncodingController.ts","../src/Stream/StreamController.ts","../src/Stream/Quality/QualityTiers.ts","../src/Stream/Quality/QualityPolicy.ts","../src/Stream/Quality/QualityController.ts","../src/Stream/Quality/QualityVideoFormat.ts","../src/Stream/Quality/QualityHistory.ts","../src/Core/Realtime/RealtimeCoordinator.ts","../src/Core/Realtime/RealtimeErrorHandler.ts","../src/Core/Realtime/RealtimeTiming.ts","../src/Core/Realtime/RealtimeCameraPreset.ts","../src/Core/Realtime/RealtimeQualityLevel.ts","../src/Core/Realtime/XmaxRealtimeConnectionManager.ts","../src/Core/Realtime/XmaxRealtimeGenerationManager.ts","../src/Core/Realtime/XmaxRealtimeManager.ts","../src/Core/XmaxConfiguration.ts","../src/Service/NonRealtime/NonRealtimeTask.ts","../src/Service/NonRealtime/NonRealtimeTaskService.ts","../src/Service/NonRealtime/NonRealtimeTaskError.ts","../src/Core/NonRealtime/XmaxNonRealtimeManager.ts","../src/Core/XmaxClient.ts","../src/Core/Realtime/RealtimeConfiguration.ts","../src/Render/Video/RenderStatisticsController.ts","../src/Render/Video/RenderWarmupController.ts","../src/Render/Trajectory/TrajectoryOverlayView.ts","../src/Render/Trajectory/DefaultTrajectoryEffectRenderer.ts","../src/Render/Video/XmaxRealtimeVideoView.ts","../src/Render/Video/XmaxRealtimeDualVideoView.ts"],"sourcesContent":["//\n// XmaxEnvironment.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\n/**\n * Xmax 服务环境。\n */\nexport enum XmaxEnvironment {\n\n /**\n * 国内环境。\n */\n china = \"china\",\n\n /**\n * 海外环境。\n */\n global = \"global\",\n}\n\n/**\n * 通用 API 与存储服务使用的环境地址。\n */\nconst API_BASE_URLS: Record<XmaxEnvironment, string> = {\n [XmaxEnvironment.china]: \"https://cloud.xmax.22duck.cn/open/api/v1\",\n [XmaxEnvironment.global]: \"https://api.xmax.cloud/open/api/v1\",\n};\n\n/**\n * 实时会话服务的环境地址。\n */\nconst REALTIME_API_BASE_URLS: Record<XmaxEnvironment, string> = {\n [XmaxEnvironment.china]: \"https://cloud.xmax.22duck.cn/open/api/v1\",\n [XmaxEnvironment.global]: \"https://api.xmax.ai/open/api/v1\",\n};\n\n/**\n * 非实时任务服务的环境地址。\n */\nconst NON_REALTIME_API_BASE_URLS: Record<XmaxEnvironment, string> = {\n [XmaxEnvironment.china]: \"https://api.xmaxai.com/open/api/v1\",\n [XmaxEnvironment.global]: \"https://api.xmax.ai/open/api/v1\",\n};\n\n/**\n * 环境对应的 Xmax API Base URL。\n */\nexport function apiBaseURL(\n environment: XmaxEnvironment\n): string {\n return API_BASE_URLS[environment];\n}\n\n/**\n * 环境对应的实时会话 API 地址。\n */\nexport function realtimeApiBaseURL(\n environment: XmaxEnvironment\n): string {\n return REALTIME_API_BASE_URLS[environment];\n}\n\n/**\n * 环境对应的非实时任务 API Base URL。\n */\nexport function nonRealtimeApiBaseURL(\n environment: XmaxEnvironment\n): string {\n return NON_REALTIME_API_BASE_URLS[environment];\n}\n","//\n// XmaxLogger.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\nimport { XmaxEnvironment } from \"../Runtime/XmaxEnvironment\";\n\n/**\n * 控制 XmaxSDK 输出的日志类型(位掩码,可组合)。\n */\nexport enum XmaxLoggerOption {\n\n /**\n * 关闭全部 SDK 日志输出。\n */\n none = 0,\n\n /**\n * Room、API、Realtime、Storage 等业务运行日志。\n */\n business = 1 << 0,\n\n /**\n * 启动耗时、RTC 网络质量和音视频运行统计,输出到 console.info。\n */\n performance = 1 << 1,\n\n /**\n * 输出全部 XmaxSDK 日志。\n */\n all = business | performance,\n}\n\n/**\n * SDK 日志的严重程度。\n */\ntype XmaxLogLevel = \"debug\" | \"info\" | \"warning\" | \"error\";\n\n/**\n * 各日志模块共用的输出配置。\n */\ninterface XmaxLoggerState {\n\n /**\n * 允许输出的日志类别。\n */\n options: XmaxLoggerOption;\n\n /**\n * 日志内容使用的服务环境和语言。\n */\n environment: XmaxEnvironment;\n}\n\n/**\n * 各日志模块共享的输出类别与语言配置。\n */\nconst state: XmaxLoggerState = {\n options: XmaxLoggerOption.none,\n environment: XmaxEnvironment.china,\n};\n\n/**\n * 日志级别对应的浏览器控制台输出方法。\n */\nconst CONSOLE_METHOD: Record<XmaxLogLevel, \"debug\" | \"info\" | \"warn\" | \"error\"> = {\n debug: \"debug\",\n info: \"info\",\n warning: \"warn\",\n error: \"error\",\n};\n\n/**\n * Xmax 品牌标签:蓝底白字。\n */\nconst BRAND_BADGE_STYLE = \"background:#2563eb;color:#fff;border-radius:3px;padding:1px 4px;font-weight:600;\";\n\n/**\n * 业务域标签:使用深灰蓝,与 Xmax 品牌标签明确区分。\n */\nconst CATEGORY_BADGE_STYLE = \"background:#475569;color:#fff;border-radius:3px;padding:1px 4px;font-weight:600;\";\n\n/**\n * 统一输出带 Xmax 前缀和类别的控制台日志。\n *\n * 调用方不得传入 API Key、Token、Secret、Authorization 或完整敏感响应。\n */\nexport class XmaxLogger {\n\n /**\n * 分类日志\n */\n static readonly realtime = new XmaxLogger(\"Realtime\");\n\n /**\n * RTC 引擎、进退房和媒体传输日志。\n */\n static readonly rtc = new XmaxLogger(\"RTC\");\n\n /**\n * 本地媒体准备与释放日志。\n */\n static readonly media = new XmaxLogger(\"Media\");\n\n /**\n * API 请求及响应日志。\n */\n static readonly api = new XmaxLogger(\"API\");\n\n /**\n * 文件上传与下载日志。\n */\n static readonly storage = new XmaxLogger(\"Storage\");\n\n /**\n * 房间消息和心跳日志。\n */\n static readonly room = new XmaxLogger(\"Room\");\n\n /**\n * 流发布、订阅及生成控制日志。\n */\n static readonly stream = new XmaxLogger(\"Stream\");\n\n /**\n * 视频视图与渲染管线日志。\n */\n static readonly render = new XmaxLogger(\"Render\");\n\n /**\n * 触点采样和轨迹交互日志。\n */\n static readonly interaction = new XmaxLogger(\"Interaction\");\n\n /**\n * 摄像头和麦克风权限日志。\n */\n static readonly permission = new XmaxLogger(\"Permission\");\n\n /**\n * 日志类别\n */\n private readonly category: string;\n\n /**\n * 创建固定业务分类的日志入口,共享全局日志选项和语言配置。\n */\n private constructor(\n category: string\n ) {\n this.category = category;\n }\n\n /**\n * 更新 SDK 全局日志选项和细项语言,后一次配置覆盖前一次。\n *\n * @param options 输出的日志类型位掩码。\n * @param environment 服务环境,决定日志细项语言(国内中文,海外英文)。\n */\n static configure(\n options: XmaxLoggerOption,\n environment: XmaxEnvironment = XmaxEnvironment.china,\n ): void {\n state.options = options;\n state.environment = environment;\n }\n\n /**\n * 选择日志细项文案;日志标题保持原有中英双语。\n */\n static localized(\n chinese: string,\n english: string\n ): string {\n return state.environment === XmaxEnvironment.china ? chinese : english;\n }\n\n /**\n * 判断指定日志类型是否已开启。\n */\n static isEnabled(\n option: XmaxLoggerOption\n ): boolean {\n return (state.options & option) !== 0;\n }\n\n /**\n * 输出调试日志。\n */\n debug(\n message: () => string,\n option: XmaxLoggerOption = XmaxLoggerOption.business\n ): void {\n this.log(\"debug\", message, option);\n }\n\n /**\n * 输出信息日志。\n */\n info(\n message: () => string,\n option: XmaxLoggerOption = XmaxLoggerOption.business\n ): void {\n this.log(\"info\", message, option);\n }\n\n /**\n * 输出警告日志。\n */\n warning(\n message: () => string,\n option: XmaxLoggerOption = XmaxLoggerOption.business\n ): void {\n this.log(\"warning\", message, option);\n }\n\n /**\n * 输出错误日志。\n */\n error(\n message: () => string,\n option: XmaxLoggerOption = XmaxLoggerOption.business\n ): void {\n this.log(\"error\", message, option);\n }\n\n /**\n * 按日志类型开关输出到控制台;未开启时不求值日志内容。\n */\n private log(\n level: XmaxLogLevel,\n message: () => string,\n option: XmaxLoggerOption,\n ): void {\n if (!XmaxLogger.isEnabled(option)) {\n return;\n }\n\n const text = message();\n if (typeof window !== \"undefined\" && typeof document !== \"undefined\") {\n // 正文通过 %s 传入,避免其中的 %c / %s 等内容被当作控制台格式指令。\n console[CONSOLE_METHOD[level]](\n `%c[Xmax]%c %c[${this.category}]%c\\n%s`,\n BRAND_BADGE_STYLE,\n \"\",\n CATEGORY_BADGE_STYLE,\n \"\",\n text,\n );\n return;\n }\n\n // SSR、Node 和其他非页面环境使用纯文本前缀。\n console[CONSOLE_METHOD[level]](`[Xmax][${this.category}] ${text}`);\n }\n}\n","//\n// XmaxError.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\n/**\n * SDK 向接入方暴露的统一错误码。\n */\nexport enum XmaxErrorCode {\n\n /**\n * API Key 未配置或为空。\n */\n invalidAPIKey = \"INVALID_API_KEY\",\n\n /**\n * 调用参数、配置或当前生命周期状态不符合操作要求。\n */\n invalidConfiguration = \"INVALID_CONFIGURATION\",\n\n /**\n * SDK 内部发生无法归类的非预期错误。\n */\n internalError = \"INTERNAL_ERROR\",\n\n /**\n * 网络不可用、连接失败或 HTTP 请求无法完成。\n */\n networkError = \"NETWORK_ERROR\",\n\n /**\n * Xmax API 返回业务失败,或者响应数据无法正确解析。\n */\n apiError = \"API_ERROR\",\n\n /**\n * 实时生成会话数据缺失、会话关闭或心跳状态异常。\n */\n sessionError = \"SESSION_ERROR\",\n\n /**\n * RTC Engine、房间、媒体传输或渲染绑定发生错误。\n */\n rtcError = \"RTC_ERROR\",\n\n /**\n * 本地媒体读取、解码、同步或帧处理发生错误。\n */\n mediaError = \"MEDIA_ERROR\",\n\n /**\n * 接入方未授予相机权限。\n */\n cameraPermissionDenied = \"CAMERA_PERMISSION_DENIED\",\n\n /**\n * @deprecated 为兼容保留;相机预热改为固定等待,不再抛出此错误。\n */\n cameraExposureTimeout = \"CAMERA_EXPOSURE_TIMEOUT\",\n\n /**\n * 接入方未授予麦克风权限。\n */\n microphonePermissionDenied = \"MICROPHONE_PERMISSION_DENIED\",\n\n /**\n * 文件或二进制数据上传失败。\n */\n uploadError = \"UPLOAD_ERROR\",\n\n /**\n * 远端文件下载失败。\n */\n downloadError = \"DOWNLOAD_ERROR\",\n\n /**\n * 图片未通过内容安全检查。\n */\n unsafeImage = \"UNSAFE_IMAGE\",\n\n /**\n * 操作因生命周期切换或接入方主动终止而正常取消。\n */\n cancelled = \"CANCELLED\",\n\n /**\n * 等待连接、房间事件或生成结果超过规定时间。\n */\n timeout = \"TIMEOUT\",\n}\n\n/**\n * SDK 内部组件上报统一错误时使用的监听器。\n */\nexport type XmaxErrorListener = (\n error: XmaxError\n) => void;\n\n/**\n * 表示 SDK 抛出或回调给接入方的统一错误。\n */\nexport class XmaxError extends Error {\n\n /**\n * SDK 统一错误码。\n */\n readonly code: XmaxErrorCode;\n\n /**\n * Xmax API 返回的业务错误码;非 API 业务错误时为空。\n */\n readonly apiCode?: number;\n\n /**\n * HTTP 响应状态码;请求未获得响应时为空。\n */\n readonly httpStatus?: number;\n\n /**\n * 创建 SDK 错误。\n *\n * @param code SDK 统一错误码。\n * @param message 面向接入方的可读错误说明。\n * @param options Xmax API 业务错误码与 HTTP 状态码(可选)。\n */\n constructor(\n code: XmaxErrorCode,\n message: string,\n options?: {\n apiCode?: number;\n httpStatus?: number\n },\n ) {\n super(message);\n this.name = \"XmaxError\";\n this.code = code;\n this.apiCode = options?.apiCode;\n this.httpStatus = options?.httpStatus;\n }\n\n /**\n * 将未知错误转换为 SDK 内部错误,已有 `XmaxError` 原样返回。\n *\n * @param error 待转换的错误。\n * @returns 可向接入方抛出或回调的统一错误。\n */\n static from(\n error: unknown\n ): XmaxError {\n if (error instanceof XmaxError) {\n return error;\n }\n const message =\n error instanceof Error && error.message.trim().length > 0\n ? error.message.trim()\n : String(error);\n return new XmaxError(XmaxErrorCode.internalError, message);\n }\n}\n","//\n// RuntimeInfo.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\n/**\n * SDK 当前版本号。\n */\nexport const XMAX_SDK_VERSION = \"1.1.0\";\n\n/**\n * 随业务请求上报的 SDK 与浏览器环境信息。\n */\nexport interface RuntimeInfoSnapshot {\n\n /**\n * 运行平台标识。\n */\n platform: string;\n\n /**\n * 操作系统或浏览器版本描述。\n */\n osVersion: string;\n\n /**\n * SDK 版本号。\n */\n sdkVersion: string;\n\n /**\n * 设备或浏览器型号描述。\n */\n deviceModel: string;\n}\n\n/**\n * 描述 SDK 当前运行环境。\n */\nexport class RuntimeInfo {\n\n /**\n * 当前运行环境快照。\n */\n static readonly current: RuntimeInfoSnapshot = {\n platform: \"web\",\n osVersion: RuntimeInfo.currentOSVersion(),\n sdkVersion: XMAX_SDK_VERSION,\n deviceModel: \"browser\",\n };\n\n /**\n * 序列化为接口传输使用的 snake_case 字段。\n */\n static toJSON(\n snapshot: RuntimeInfoSnapshot = RuntimeInfo.current\n ): Record<string, string> {\n return {\n platform: snapshot.platform,\n os_version: snapshot.osVersion,\n sdk_version: snapshot.sdkVersion,\n device_model: snapshot.deviceModel,\n };\n }\n\n /**\n * 读取浏览器 User-Agent;非浏览器环境返回 unknown。\n */\n private static currentOSVersion(): string {\n if (typeof navigator !== \"undefined\" && navigator.userAgent) {\n return navigator.userAgent;\n }\n return \"unknown\";\n }\n}\n","//\n// ApiService.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\nimport { XmaxError, XmaxErrorCode } from \"../../Foundation/Errors/XmaxError\";\nimport { XmaxLogger } from \"../../Foundation/Logging/XmaxLogger\";\nimport { XMAX_SDK_VERSION } from \"../../Foundation/Runtime/RuntimeInfo\";\nimport { ApiMethod, type ApiServicing, type ApiRequestOptions } from \"./ApiServicing\";\n\n/**\n * Xmax API 统一响应信封中的公共元数据。\n */\ninterface EnvelopeMetadata {\n\n /**\n * 服务端响应中的业务成功标记。\n */\n success?: boolean;\n\n /**\n * 服务端返回的业务状态码。\n */\n code?: number;\n\n /**\n * 服务端返回的状态说明。\n */\n message?: string;\n}\n\n/**\n * 请求 fetch 时使用的签名,便于测试替换。\n */\nexport type ApiFetch = (\n url: string,\n init: {\n method: string;\n headers: Record<string, string>;\n body?: string;\n signal?: AbortSignal;\n keepalive?: boolean;\n },\n) => Promise<{\n status: number;\n /**\n * 读取响应正文文本,供统一响应解析使用。\n */\n text(): Promise<string>;\n}>;\n\n/**\n * API 请求身份、地址和传输选项。\n */\nexport interface ApiServiceOptions {\n\n /**\n * 调用 Xmax 服务使用的 API Key。\n */\n apiKey: string;\n\n /**\n * Xmax API Base URL。\n */\n baseURL: string;\n\n /**\n * 请求超时时间(毫秒);默认 15 秒。\n */\n timeoutMs?: number;\n\n /**\n * fetch 实现(可替换,测试用)。\n */\n fetchImpl?: ApiFetch;\n}\n\n/**\n * 负责发送 Xmax API 请求并统一处理响应、日志和错误。\n */\nexport class ApiService implements ApiServicing {\n\n /**\n * 默认请求超时时间(毫秒)。\n */\n static readonly defaultTimeoutMs = 15_000;\n\n /**\n * 请求身份、地址与超时配置\n */\n private readonly apiKey: string;\n\n /**\n * API 请求使用的基础地址。\n */\n private readonly baseURL: string;\n\n /**\n * 单次请求的超时上限,单位为毫秒。\n */\n private readonly timeoutMs: number;\n\n /**\n * 网络传输依赖\n */\n private readonly fetchImpl: ApiFetch;\n\n /**\n * 创建 API Service。\n *\n * @param options.apiKey 调用 Xmax 服务使用的 API Key。\n * @param options.baseURL Xmax API Base URL。\n * @param options.timeoutMs 请求超时时间(毫秒);默认 15 秒。\n * @param options.fetchImpl fetch 实现(可替换,测试用)。\n */\n constructor(\n options: ApiServiceOptions\n ) {\n this.apiKey = options.apiKey.trim();\n this.baseURL = options.baseURL;\n this.timeoutMs = options.timeoutMs ?? ApiService.defaultTimeoutMs;\n\n this.fetchImpl = options.fetchImpl ?? ((url, init) => fetch(url, init));\n }\n\n /**\n * 发送请求并返回统一响应中的数据。\n *\n * @param options.keepalive 为 true 时请求可存活于页面卸载之后。\n */\n async request<T>(\n method: ApiMethod,\n path: string,\n body?: unknown,\n options?: ApiRequestOptions\n ): Promise<T> {\n this.validateConfiguration();\n if (options?.signal?.aborted) {\n throw new XmaxError(XmaxErrorCode.cancelled, \"API request was cancelled\");\n }\n\n const request = this.makeRequest(method, path, body, options);\n const startedAt = Date.now();\n\n // 同时保留单次请求超时与调用方取消,不依赖较新的 AbortSignal.any。\n const timeoutSignal = request.init.signal!;\n const controller = new AbortController();\n const abortTimeout = () => controller.abort(timeoutSignal.reason);\n const abortCaller = () => controller.abort(new DOMException(\"Cancelled\", \"AbortError\"));\n timeoutSignal.addEventListener(\"abort\", abortTimeout, { once: true });\n options?.signal?.addEventListener(\"abort\", abortCaller, { once: true });\n request.init.signal = controller.signal;\n if (timeoutSignal.aborted) {\n abortTimeout();\n }\n if (options?.signal?.aborted) {\n abortCaller();\n }\n\n let status: number;\n let text: string;\n try {\n const response = await this.fetchImpl(request.url, request.init);\n status = response.status;\n text = await response.text();\n } catch (error) {\n const mapped = ApiService.transportError(error);\n this.logFailure(method, path, mapped, startedAt);\n throw mapped;\n } finally {\n timeoutSignal.removeEventListener(\"abort\", abortTimeout);\n options?.signal?.removeEventListener(\"abort\", abortCaller);\n }\n\n try {\n const value = ApiService.parseResponse<T>(text, status);\n XmaxLogger.api.debug(\n () =>\n `API 请求成功 (API Request Succeeded)\\n` +\n `└─ ${method} ${path} · ${status} · ${Date.now() - startedAt}ms`,\n );\n return value;\n } catch (error) {\n const mapped = XmaxError.from(error);\n this.logFailure(method, path, mapped, startedAt);\n throw mapped;\n }\n }\n\n /**\n * 发送 GET 请求。\n */\n get<T>(\n path: string,\n options?: ApiRequestOptions\n ): Promise<T> {\n return this.request<T>(ApiMethod.get, path, undefined, options);\n }\n\n /**\n * 发送 POST 请求。\n */\n post<T>(\n path: string,\n body?: unknown\n ): Promise<T> {\n return this.request<T>(ApiMethod.post, path, body);\n }\n\n /**\n * 发送 PUT 请求。\n */\n put<T>(\n path: string,\n body?: unknown\n ): Promise<T> {\n return this.request<T>(ApiMethod.put, path, body);\n }\n\n /**\n * 发送 DELETE 请求。\n */\n delete<T>(\n path: string,\n options?: {\n keepalive?: boolean\n }\n ): Promise<T> {\n return this.request<T>(ApiMethod.delete, path, undefined, options);\n }\n\n /**\n * 校验 API Key 与 Base URL 配置。\n */\n private validateConfiguration(): void {\n if (this.apiKey.length === 0) {\n throw new XmaxError(XmaxErrorCode.invalidAPIKey, \"API key cannot be empty\");\n }\n\n let url: URL;\n try {\n url = new URL(this.baseURL);\n } catch {\n throw new XmaxError(\n XmaxErrorCode.invalidConfiguration,\n \"API service configuration is invalid\",\n );\n }\n\n const isSecure = url.protocol === \"https:\";\n const isLocalhost =\n url.protocol === \"http:\" &&\n (url.hostname === \"localhost\" || url.hostname === \"127.0.0.1\");\n if ((!isSecure && !isLocalhost) || this.timeoutMs <= 0) {\n throw new XmaxError(\n XmaxErrorCode.invalidConfiguration,\n \"API service configuration is invalid\",\n );\n }\n }\n\n /**\n * 构造请求地址、请求头与请求体;路径必须相对且与 Base URL 同源。\n */\n private makeRequest(\n method: ApiMethod,\n path: string,\n body?: unknown,\n options?: {\n keepalive?: boolean\n },\n ): {\n url: string;\n init: {\n method: string;\n headers: Record<string, string>;\n body?: string;\n signal?: AbortSignal;\n keepalive?: boolean;\n };\n } {\n const normalizedPath = path.trim();\n if (\n normalizedPath.length === 0 ||\n normalizedPath.includes(\"://\") ||\n normalizedPath.startsWith(\"//\")\n ) {\n throw new XmaxError(\n XmaxErrorCode.invalidConfiguration,\n \"API request path is invalid\",\n );\n }\n\n const base = this.baseURL.endsWith(\"/\") ? this.baseURL.slice(0, -1) : this.baseURL;\n const relativePath = normalizedPath.startsWith(\"/\")\n ? normalizedPath\n : `/${normalizedPath}`;\n const url = new URL(base + relativePath);\n const baseURLObj = new URL(base);\n if (\n url.protocol.toLowerCase() !== baseURLObj.protocol.toLowerCase() ||\n url.host.toLowerCase() !== baseURLObj.host.toLowerCase()\n ) {\n throw new XmaxError(\n XmaxErrorCode.invalidConfiguration,\n \"API request path is invalid\",\n );\n }\n\n let encodedBody: string | undefined;\n if (body !== undefined) {\n try {\n encodedBody = JSON.stringify(body);\n } catch {\n throw new XmaxError(\n XmaxErrorCode.apiError,\n \"Failed to encode API request body\",\n );\n }\n }\n\n return {\n url: url.toString(),\n init: {\n method,\n headers: {\n Accept: \"application/json\",\n \"Content-Type\": \"application/json\",\n \"X-Api-Key\": this.apiKey,\n \"X-Platform\": \"web\",\n \"X-SDK-Version\": XMAX_SDK_VERSION,\n },\n body: encodedBody,\n signal: AbortSignal.timeout(this.timeoutMs),\n ...(options?.keepalive ? { keepalive: true } : {}),\n },\n };\n }\n\n /**\n * 解析统一响应信封并返回数据;业务失败或数据缺失时抛出错误。\n */\n private static parseResponse<T>(\n text: string,\n status: number\n ): T {\n let envelope: EnvelopeMetadata & {\n data?: T\n };\n try {\n envelope = JSON.parse(text);\n } catch {\n throw new XmaxError(XmaxErrorCode.apiError, \"Server returned invalid JSON\", {\n httpStatus: status,\n });\n }\n\n const ok = status >= 200 && status < 300;\n const succeeded = envelope.success === true || (envelope.success === undefined && envelope.code === 200);\n if (!ok || !succeeded) {\n const message = envelope.message?.trim();\n throw new XmaxError(\n XmaxErrorCode.apiError,\n message || \"Xmax API request failed\",\n { apiCode: envelope.code, httpStatus: status },\n );\n }\n\n if (envelope.data === undefined || envelope.data === null) {\n throw new XmaxError(\n XmaxErrorCode.apiError,\n \"Server returned invalid response data\",\n { httpStatus: status },\n );\n }\n\n return envelope.data;\n }\n\n /**\n * 将传输层异常映射为 SDK 统一错误。\n */\n private static transportError(\n error: unknown\n ): XmaxError {\n if (error instanceof XmaxError) {\n return error;\n }\n if (error instanceof DOMException && error.name === \"TimeoutError\") {\n return new XmaxError(XmaxErrorCode.timeout, \"API request timed out\");\n }\n if (error instanceof DOMException && error.name === \"AbortError\") {\n return new XmaxError(XmaxErrorCode.cancelled, \"API request was cancelled\");\n }\n\n const message =\n error instanceof Error && error.message.trim().length > 0\n ? error.message.trim()\n : String(error);\n return new XmaxError(\n XmaxErrorCode.networkError,\n `HTTP request failed: ${message}`,\n );\n }\n\n /**\n * 输出失败日志。\n */\n private logFailure(\n method: ApiMethod,\n path: string,\n error: XmaxError,\n startedAt: number,\n ): void {\n XmaxLogger.api.error(\n () =>\n `API 请求失败 (API Request Failed)\\n` +\n `└─ ${method} ${path} · ${Date.now() - startedAt}ms\\n` +\n `└─ ${XmaxLogger.localized(\"原因:\", \"Reason: \")}${error.message}`,\n );\n }\n}\n","//\n// RealtimeVideoFormat.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\nimport { XmaxError, XmaxErrorCode } from \"../../Foundation/Errors/XmaxError\";\n\n/**\n * 实时视频的编码策略偏好。\n */\nexport enum RealtimeVideoEncoderPreference {\n\n /**\n * 优先保障帧率(弱网降分辨率)。\n */\n maintainFramerate = \"maintainFramerate\",\n\n /**\n * 优先保障分辨率(弱网降帧率)。\n */\n maintainQuality = \"maintainQuality\",\n}\n\n/**\n * 视频尺寸、帧率和上传编码选项。\n */\nexport interface RealtimeVideoFormatInit {\n\n /**\n * 视频宽度,单位为像素。\n */\n width: number;\n\n /**\n * 视频高度,单位为像素。\n */\n height: number;\n\n /**\n * 视频帧率,必须大于 0。\n */\n fps: number;\n\n /**\n * 最低上传码率,单位为 kbps;缺省取 500 与最高码率的较小值,0 表示不设下限。\n */\n minimumBitrate?: number;\n\n /**\n * 最高上传码率,单位为 kbps;缺省使用 SDK 默认值,指定时必须大于 0。\n */\n maximumBitrate?: number;\n\n /**\n * 上传编码策略偏好,默认优先保障帧率。\n */\n encoderPreference?: RealtimeVideoEncoderPreference;\n}\n\n/**\n * 实时视频的尺寸、帧率和上传编码配置。\n */\nexport class RealtimeVideoFormat {\n\n /**\n * 视频宽度,单位为像素。\n */\n readonly width: number;\n\n /**\n * 视频高度,单位为像素。\n */\n readonly height: number;\n\n /**\n * 视频帧率,单位为 fps。\n */\n readonly fps: number;\n\n /**\n * 最低上传码率,单位为 kbps;缺省取 500 与最高码率的较小值,0 表示不设下限;仅支持该参数的 RTC 链路生效。\n */\n readonly minimumBitrate?: number;\n\n /**\n * 最高上传码率,单位为 kbps;未指定时由 SDK 计算。\n */\n readonly maximumBitrate?: number;\n\n /**\n * 弱网下优先保障帧率或分辨率的编码策略。\n */\n readonly encoderPreference: RealtimeVideoEncoderPreference;\n\n /**\n * 创建实时视频格式。\n */\n constructor(\n init: RealtimeVideoFormatInit\n ) {\n this.width = init.width;\n this.height = init.height;\n this.fps = init.fps;\n\n this.minimumBitrate = init.minimumBitrate;\n this.maximumBitrate = init.maximumBitrate;\n this.encoderPreference =\n init.encoderPreference ?? RealtimeVideoEncoderPreference.maintainFramerate;\n }\n\n /**\n * 校验尺寸、帧率和显式指定的码率范围。\n * @throws 尺寸、帧率或码率配置无效时抛出错误。\n */\n validate(): void {\n if (\n this.width <= 0 ||\n this.height <= 0 ||\n !Number.isFinite(this.fps) ||\n this.fps <= 0 ||\n this.width % 2 !== 0 ||\n this.height % 2 !== 0\n ) {\n throw new XmaxError(\n XmaxErrorCode.invalidConfiguration,\n \"Realtime video width and height must be positive \" +\n \"even numbers, and fps must be greater than zero\",\n );\n }\n\n if (this.minimumBitrate !== undefined && (!Number.isFinite(this.minimumBitrate) || this.minimumBitrate < 0)) {\n throw new XmaxError(\n XmaxErrorCode.invalidConfiguration,\n \"Minimum bitrate must be finite and not negative\",\n );\n }\n if (this.maximumBitrate !== undefined && (!Number.isFinite(this.maximumBitrate) || this.maximumBitrate <= 0)) {\n throw new XmaxError(\n XmaxErrorCode.invalidConfiguration,\n \"Maximum bitrate must be finite and greater than zero\",\n );\n }\n\n if (\n this.minimumBitrate !== undefined &&\n this.maximumBitrate !== undefined &&\n this.minimumBitrate > this.maximumBitrate\n ) {\n throw new XmaxError(\n XmaxErrorCode.invalidConfiguration,\n \"Minimum bitrate must not exceed maximum bitrate\",\n );\n }\n }\n\n /**\n * 调整尺寸,保留帧率和上传编码配置。\n */\n resized(\n width: number,\n height: number\n ): RealtimeVideoFormat {\n return new RealtimeVideoFormat({\n width,\n height,\n fps: this.fps,\n minimumBitrate: this.minimumBitrate,\n maximumBitrate: this.maximumBitrate,\n encoderPreference: this.encoderPreference,\n });\n }\n}\n","//\n// RtcProvider.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\n/**\n * 实时媒体使用的 RTC 提供方;一个 Manager 生命周期内保持不变。\n */\nexport enum RtcProvider {\n\n /**\n * 腾讯实时音视频。\n */\n trtc = \"trtc\",\n\n /**\n * 声网实时音视频。\n */\n agora = \"agora\",\n\n /**\n * 火山引擎实时音视频。\n */\n vertc = \"vertc\",\n}\n","//\n// RealtimeModel.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\nimport { RealtimeVideoFormat } from \"./RealtimeVideoFormat\";\nimport { RtcProvider } from \"../../Foundation/RTC/RtcProvider\";\nimport { XmaxError, XmaxErrorCode } from \"../../Foundation/Errors/XmaxError\";\n\n/**\n * SDK 当前支持的实时生成模型。\n */\nexport enum RealtimeModel {\n\n /**\n * 使用 VeRTC 的 X2.0 实时生成模型。\n */\n x2_0 = \"x2.0\",\n\n /**\n * 使用 Agora 的 X2.1 实时生成模型。\n */\n x2_1 = \"x2.1\",\n}\n\n/**\n * 内置模型或服务端新增的模型标识;自定义名称原样传给服务端。\n */\nexport type RealtimeModelName = RealtimeModel | (string & {});\n\n/**\n * 校验模型名称非空,并判断是否为 SDK 内置模型。\n */\nfunction isKnownModel(\n model: RealtimeModelName\n): model is RealtimeModel {\n if (typeof model !== \"string\" || !model.trim()) {\n throw new XmaxError(XmaxErrorCode.invalidConfiguration, \"Realtime model must be a non-empty string\");\n }\n return Object.values(RealtimeModel).includes(model as RealtimeModel);\n}\n\n/**\n * 只解析本地默认配置,不替换请求中的模型标识;避免原型属性参与查表。\n */\nfunction modelDefaults(\n model: RealtimeModelName\n): RealtimeModel {\n return isKnownModel(model) ? model : RealtimeModel.x2_1;\n}\n\n/**\n * 模型在界面中展示的名称,与接口使用的模型标识分开维护。\n */\nconst MODEL_DISPLAY_NAMES: Readonly<Record<RealtimeModel, string>> = {\n [RealtimeModel.x2_0]: \"X2.0\",\n [RealtimeModel.x2_1]: \"X2.1\",\n};\n\n/**\n * 模型输入画面的像素尺寸。\n */\nexport interface ModelSize {\n\n /**\n * 视频宽度,单位为像素。\n */\n width: number;\n\n /**\n * 视频高度,单位为像素。\n */\n height: number;\n}\n\n/**\n * 各模型支持的输入分辨率桶;空数组表示按像素面积上下限和对齐规则计算尺寸。\n */\nconst RESOLUTION_BUCKETS: Record<RealtimeModel, ModelSize[]> = {\n [RealtimeModel.x2_0]: [],\n [RealtimeModel.x2_1]: [\n { width: 1920, height: 1080 },\n { width: 1080, height: 1920 },\n { width: 1408, height: 1408 },\n { width: 1152, height: 1536 },\n { width: 1536, height: 1152 },\n ],\n};\n\n/**\n * 本地媒体帧的最大像素面积;无固定桶时也用于限制生成画布。\n */\nconst MAXIMUM_INPUT_PIXELS: Record<RealtimeModel, number> = {\n [RealtimeModel.x2_0]: 1280000,\n [RealtimeModel.x2_1]: 2100000,\n};\n\n/**\n * 获取模型的显示名称。\n */\nexport function modelDisplayName(\n model: RealtimeModelName\n): string {\n return isKnownModel(model) ? MODEL_DISPLAY_NAMES[model] : model;\n}\n\n/**\n * 模型专用的会话 API Base URL;未知模型使用测试环境地址。\n * 已知模型返回 undefined,由客户端按环境选择实时会话地址。\n */\nexport function modelBaseURL(\n model: RealtimeModelName\n): string | undefined {\n return isKnownModel(model) ? undefined : \"https://dev.22duck.cn/open/api/v1\";\n}\n\n/**\n * 模型支持的固定视频规格;空列表表示按像素预算解析,帧率为推荐默认值。\n */\nexport function supportedVideoFormats(\n model: RealtimeModelName\n): RealtimeVideoFormat[] {\n return resolutionBuckets(model).map(size => new RealtimeVideoFormat({ ...size, fps: defaultFrameRate(model) }));\n}\n\n/**\n * 模型支持的输入分辨率桶;空数组表示按像素面积上下限和对齐规则计算输入尺寸。\n */\nexport function resolutionBuckets(\n model: RealtimeModelName\n): ModelSize[] {\n return RESOLUTION_BUCKETS[modelDefaults(model)];\n}\n\n/**\n * 输入分辨率的最小总像素面积;仅在分辨率桶为空时参与尺寸计算。\n */\nexport function minimumInputPixels(\n _model: RealtimeModelName\n): number {\n return 600000;\n}\n\n/**\n * 输入分辨率的最大总像素面积;仅在分辨率桶为空时参与尺寸计算。\n */\nexport function maximumInputPixels(\n model: RealtimeModelName\n): number {\n return MAXIMUM_INPUT_PIXELS[modelDefaults(model)];\n}\n\n/**\n * 输入宽度和高度分别需要对齐的像素倍数;仅在分辨率桶为空时参与尺寸计算。\n */\nexport function inputSizeAlignment(\n _model: RealtimeModelName\n): number {\n return 32;\n}\n\n/**\n * 未指定视频规格时,各媒体来源使用的默认帧率。\n */\nexport function defaultFrameRate(\n _model: RealtimeModelName\n): number {\n return 30;\n}\n\n/**\n * 摄像头采集使用的默认视频规格。\n */\nexport function defaultCameraVideoFormat(\n model: RealtimeModelName\n): RealtimeVideoFormat {\n const size = modelDefaults(model) === RealtimeModel.x2_0\n ? { width: 832, height: 1472 }\n : { width: 1080, height: 1920 };\n return new RealtimeVideoFormat({ ...size, fps: defaultFrameRate(model) });\n}\n\n/**\n * 模型支持的 RTC 提供方;第一项为默认值,只读列表防止接入方修改全局能力定义。\n */\nconst MODEL_RTC_PROVIDERS: Readonly<Record<RealtimeModel, readonly RtcProvider[]>> = {\n [RealtimeModel.x2_0]: Object.freeze([RtcProvider.vertc]),\n [RealtimeModel.x2_1]: Object.freeze([RtcProvider.agora]),\n};\n\n/**\n * 查询 SDK 允许配置的 RTC 提供方,第一项为默认值。\n * 未知模型默认使用 Agora,也允许显式选择其他已接入的 RTC;\n * 是否被该模型的服务端支持,由服务端决定。\n */\nexport function supportedRtcProviders(\n model: RealtimeModelName\n): readonly RtcProvider[] {\n return isKnownModel(model) ? MODEL_RTC_PROVIDERS[model] : CUSTOM_MODEL_RTC_PROVIDERS;\n}\n\n/**\n * 自定义模型允许选择的 RTC 类型,首项为默认值。\n */\nconst CUSTOM_MODEL_RTC_PROVIDERS: readonly RtcProvider[] = Object.freeze([\n ...MODEL_RTC_PROVIDERS[RealtimeModel.x2_1],\n ...Object.values(RtcProvider).filter((provider) =>\n !MODEL_RTC_PROVIDERS[RealtimeModel.x2_1].includes(provider)),\n]);\n","//\n// VideoSize.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\nimport { XmaxError, XmaxErrorCode } from \"../../Errors/XmaxError\";\n\n/**\n * 在缩放上限内计算对齐尺寸,优先保持精确宽高比,不放大源画面。\n */\nexport function alignedVideoSize(\n size: {\n width: number;\n height: number\n },\n scale: number,\n alignment: number,\n): {\n width: number;\n height: number\n} {\n const { width, height } = size;\n if (![width, height, alignment].every(value => Number.isSafeInteger(value) && value > 0) ||\n !Number.isFinite(scale) || scale <= 0 || scale > 1) {\n throw new XmaxError(XmaxErrorCode.invalidConfiguration, \"Invalid video size or alignment\");\n }\n\n let divisor = width;\n let remainder = height;\n while (remainder !== 0) {\n [divisor, remainder] = [remainder, divisor % remainder];\n }\n\n // 最简比例的公共倍数同时满足像素对齐,避免宽高分别取整改变比例。\n const units = Math.floor(divisor * scale / alignment) * alignment;\n if (units >= alignment) {\n return { width: width / divisor * units, height: height / divisor * units };\n }\n\n const alignedWidth = Math.floor(width * scale / alignment) * alignment;\n const alignedHeight = Math.floor(height * scale / alignment) * alignment;\n if (alignedWidth < alignment || alignedHeight < alignment) {\n throw new XmaxError(XmaxErrorCode.invalidConfiguration, \"No aligned video size is available within the size limits\");\n }\n return { width: alignedWidth, height: alignedHeight };\n}\n","//\n// MediaService.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\nimport {\n inputSizeAlignment,\n maximumInputPixels,\n minimumInputPixels,\n RealtimeModel,\n resolutionBuckets,\n type ModelSize,\n type RealtimeModelName,\n} from \"../Realtime/RealtimeModel\";\nimport { XmaxError, XmaxErrorCode } from \"../../Foundation/Errors/XmaxError\";\nimport type { MediaServicing } from \"./MediaServicing\";\nimport { alignedVideoSize } from \"../../Foundation/Media/Video/VideoSize\";\n\n/**\n * 统一计算本地媒体、生成画布及回传尺寸,并查询平台媒体能力。\n */\nexport class MediaService implements MediaServicing {\n\n /**\n * 模型约束\n */\n readonly model: RealtimeModelName;\n\n /**\n * 创建媒体服务。\n */\n constructor(\n model: RealtimeModelName = RealtimeModel.x2_1\n ) {\n this.model = model;\n }\n\n /**\n * 解析固定媒体基准:保留完整内容、不放大、限制面积并按 4 对齐。\n * 摄像头请求尺寸须先匹配模型桶;文件媒体和外部轨道不受模型桶比例限制。\n *\n * @param sourceMediaSize 原始媒体尺寸。\n * @param source 媒体来源,仅摄像头需要校验采集分辨率桶。\n * @returns 用于固定媒体基准和初始上行编码的尺寸。\n */\n resolveMediaSize(\n sourceMediaSize: ModelSize,\n source: \"camera\" | \"image\" | \"video\" | \"external\"\n ): ModelSize {\n this.validateIntegralSize(sourceMediaSize);\n const buckets = resolutionBuckets(this.model);\n if (source === \"camera\" && buckets.length > 0) {\n this.resolveModelInputSize(sourceMediaSize);\n }\n\n const pixels = sourceMediaSize.width * sourceMediaSize.height;\n const scale = Math.min(1, Math.sqrt(maximumInputPixels(this.model) / pixels));\n return alignedVideoSize(sourceMediaSize, scale, 4);\n }\n\n /**\n * 选择比例最接近的模型生成画布;无固定桶时按模型像素预算和对齐规则计算。\n */\n resolveGenerationSize(\n resolvedMediaSize: ModelSize\n ): ModelSize {\n this.validateIntegralSize(resolvedMediaSize);\n const buckets = resolutionBuckets(this.model);\n if (buckets.length === 0) {\n return this.resolveModelInputSize(resolvedMediaSize);\n }\n\n const aspect = resolvedMediaSize.width / resolvedMediaSize.height;\n let best = buckets[0]!;\n let distance = Math.abs(Math.log(aspect / (best.width / best.height)));\n for (const bucket of buckets.slice(1)) {\n const candidate = Math.abs(Math.log(aspect / (bucket.width / bucket.height)));\n if (candidate < distance) {\n best = bucket;\n distance = candidate;\n }\n }\n return { width: best.width, height: best.height };\n }\n\n /**\n * 综合下行宽高上限计算回传尺寸,不放大、优先保持比例并按 4 对齐。\n */\n resolveTargetSize(\n resolvedMediaSize: ModelSize,\n maximumSize: ModelSize\n ): ModelSize {\n this.validateIntegralSize(resolvedMediaSize);\n if (![maximumSize.width, maximumSize.height].every(value => Number.isFinite(value) && value > 0)) {\n throw new XmaxError(XmaxErrorCode.invalidConfiguration, \"Target bounds must be positive and finite\");\n }\n const scale = Math.min(1, maximumSize.width / resolvedMediaSize.width, maximumSize.height / resolvedMediaSize.height);\n return alignedVideoSize(resolvedMediaSize, scale, 4);\n }\n\n /**\n * 拒绝无效或不能安全参与面积计算的媒体像素尺寸。\n */\n private validateIntegralSize(\n size: ModelSize\n ): void {\n if (![size.width, size.height].every(value => Number.isSafeInteger(value) && value > 0) ||\n !Number.isSafeInteger(size.width * size.height)) {\n throw new XmaxError(XmaxErrorCode.invalidConfiguration, \"Media dimensions must be positive safe integers\");\n }\n }\n\n\n /**\n * 按模型分辨率档位或像素预算解析输入尺寸,保持宽高比例并对齐;不支持的规格抛错。\n */\n resolveModelInputSize(\n size: ModelSize\n ): ModelSize {\n const buckets = resolutionBuckets(this.model);\n if (buckets.length > 0) {\n const matched = buckets.some(\n (bucket) => bucket.width === size.width && bucket.height === size.height,\n );\n if (!matched) {\n const supportedSizes = buckets\n .map((bucket) => `${bucket.width}×${bucket.height}`)\n .join(\", \");\n throw new XmaxError(\n XmaxErrorCode.invalidConfiguration,\n `Model ${this.model} does not support input resolution ` +\n `${size.width}×${size.height}. Supported resolutions: ${supportedSizes}`,\n );\n }\n return { width: size.width, height: size.height };\n }\n\n const validated = this.validatedSize(size);\n const pixels = validated.width * validated.height;\n const minimumPixels = minimumInputPixels(this.model);\n const maximumPixels = maximumInputPixels(this.model);\n\n let scale: number;\n let rounding: (\n value: number\n ) => number;\n if (pixels < minimumPixels) {\n scale = Math.sqrt(minimumPixels / pixels);\n rounding = Math.ceil;\n } else if (pixels > maximumPixels) {\n scale = Math.sqrt(maximumPixels / pixels);\n rounding = Math.floor;\n } else {\n scale = 1;\n rounding = Math.round;\n }\n\n const alignment = inputSizeAlignment(this.model);\n const width = Math.max(\n rounding((validated.width * scale) / alignment) * alignment,\n alignment,\n );\n const height = Math.max(\n rounding((validated.height * scale) / alignment) * alignment,\n alignment,\n );\n\n const alignedPixels = width * height;\n if (alignedPixels >= minimumPixels && alignedPixels <= maximumPixels) {\n return { width, height };\n }\n\n // 对齐可能使边界附近的尺寸越界,选择满足面积限制且最接近目标的尺寸。\n return this.boundedAlignedSize(\n validated.width * scale,\n validated.height * scale,\n );\n }\n\n /**\n * 在对齐网格上选择满足面积限制且最接近目标的尺寸。\n */\n private boundedAlignedSize(\n width: number,\n height: number\n ): ModelSize {\n const alignment = inputSizeAlignment(this.model);\n const unitPixels = alignment * alignment;\n const minimumUnits = Math.ceil(minimumInputPixels(this.model) / unitPixels);\n const maximumUnits = Math.floor(maximumInputPixels(this.model) / unitPixels);\n\n let bestSize: ModelSize = { width: 0, height: 0 };\n let bestDistance = Number.POSITIVE_INFINITY;\n\n for (let widthUnits = 1; widthUnits <= maximumUnits; widthUnits += 1) {\n const minimumHeight = Math.ceil(minimumUnits / widthUnits);\n const maximumHeight = Math.floor(maximumUnits / widthUnits);\n if (minimumHeight > maximumHeight) {\n continue;\n }\n const heightUnits = Math.min(\n Math.max(Math.round(height / alignment), minimumHeight),\n maximumHeight,\n );\n const candidateWidth = widthUnits * alignment;\n const candidateHeight = heightUnits * alignment;\n const distance =\n ((candidateWidth - width) / width) ** 2 +\n ((candidateHeight - height) / height) ** 2;\n if (distance < bestDistance) {\n bestDistance = distance;\n bestSize = { width: candidateWidth, height: candidateHeight };\n }\n }\n\n return bestSize;\n }\n\n /**\n * 校验尺寸为有限正数,并归一化为不小于 1 的整数。\n */\n private validatedSize(\n size: ModelSize\n ): ModelSize {\n if (\n !Number.isFinite(size.width) ||\n !Number.isFinite(size.height) ||\n size.width <= 0 ||\n size.height <= 0\n ) {\n throw new XmaxError(\n XmaxErrorCode.invalidConfiguration,\n \"Image width and height must be finite numbers greater than zero\",\n );\n }\n return {\n width: Math.max(Math.round(size.width), 1),\n height: Math.max(Math.round(size.height), 1),\n };\n }\n}\n","//\n// StoredFile.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\n/**\n * 上传完成后返回的已存储文件信息。\n */\nexport class StoredFile {\n\n /**\n * 文件的访问地址。\n */\n readonly url: string;\n\n /**\n * 对象存储中的对象键。\n */\n readonly objectKey: string;\n\n /**\n * 对象存储返回的 ETag;未返回时为空。\n */\n readonly etag?: string;\n\n /**\n * 创建已存储文件信息。\n */\n constructor(\n init: {\n url: string;\n objectKey: string;\n etag?: string\n }\n ) {\n this.url = init.url;\n this.objectKey = init.objectKey;\n this.etag = init.etag;\n }\n}\n","//\n// StorageService.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\nimport { XmaxError, XmaxErrorCode } from \"../../Foundation/Errors/XmaxError\";\nimport { XmaxLogger } from \"../../Foundation/Logging/XmaxLogger\";\nimport type { ApiServicing } from \"../Network/ApiServicing\";\nimport { StoredFile } from \"./StoredFile\";\nimport type { StorageServicing, StorageUploadOptions } from \"./StorageServicing\";\n\n/**\n * `/cos/sts` 返回的临时存储配置。\n */\ninterface TemporaryStoragePayload {\n\n /**\n * 对象存储桶名称。\n */\n bucket?: string;\n\n /**\n * 对象存储桶所在区域。\n */\n region?: string;\n\n /**\n * 对象存储访问域名。\n */\n endpoint?: string;\n\n /**\n * 上传对象的键名前缀。\n */\n prefix?: string;\n\n /**\n * 对象存储临时鉴权凭据。\n */\n credentials?: {\n accessKeyId?: string;\n secretAccessKey?: string;\n sessionToken?: string;\n };\n}\n\n/**\n * 解析后的临时存储配置。\n */\ninterface TemporaryStorageConfiguration {\n\n /**\n * 对象存储桶名称。\n */\n bucket: string;\n\n /**\n * 对象存储桶所在区域。\n */\n region: string;\n\n /**\n * 对象存储访问域名。\n */\n endpoint?: string;\n\n /**\n * 上传对象的键名前缀。\n */\n prefix: string;\n\n /**\n * 对象存储临时鉴权凭据。\n */\n credentials: {\n accessKeyID: string;\n secretAccessKey: string;\n sessionToken: string;\n };\n}\n\n/**\n * 对象存储直传客户端需要的最小接口。\n */\nexport interface StorageObjectClient {\n\n /**\n * 将文件直传到指定桶和对象键,通过回调报告错误或访问地址,并可上报上传进度。\n */\n putObject(\n params: {\n Bucket: string;\n Region: string;\n Key: string;\n Body: Blob;\n ContentType: string;\n onProgress?: (\n info: {\n loaded: number;\n total: number\n }\n ) => void;\n },\n callback: (\n error: {\n message?: string\n } | null,\n data?: {\n Location?: string;\n ETag?: string\n },\n ) => void,\n ): void;\n}\n\n/**\n * 对象存储客户端工厂;生产实现按临时凭证创建 COS 客户端。\n */\nexport type StorageObjectClientFactory = (\n configuration: TemporaryStorageConfiguration,\n) => Promise<StorageObjectClient>;\n\n/**\n * 对象存储上传所需的接口与客户端依赖。\n */\nexport interface StorageServiceOptions {\n\n /**\n * Xmax API 请求组件。\n */\n apiService: ApiServicing;\n\n /**\n * 对象存储客户端工厂(可替换,测试用)。\n */\n clientFactory?: StorageObjectClientFactory;\n\n /**\n * 时间来源(可替换,测试用)。\n */\n dateGenerator?: () => Date;\n\n /**\n * 标识来源(可替换,测试用)。\n */\n identifierGenerator?: () => string;\n}\n\n/**\n * 文件后缀到内容类型的映射。\n */\nconst CONTENT_TYPES: Record<string, string> = {\n jpg: \"image/jpeg\",\n jpeg: \"image/jpeg\",\n png: \"image/png\",\n webp: \"image/webp\",\n gif: \"image/gif\",\n bmp: \"image/bmp\",\n heic: \"image/heic\",\n heif: \"image/heif\",\n mp4: \"video/mp4\",\n m4v: \"video/mp4\",\n mov: \"video/quicktime\",\n webm: \"video/webm\",\n avi: \"video/x-msvideo\",\n mkv: \"video/x-matroska\",\n ogv: \"video/ogg\",\n};\n\n/**\n * 管理文件上传和视频下载。\n *\n * 上传直传不经过 Xmax 服务中转:先通过 `GET /cos/sts` 获取临时凭证,\n * 再用临时凭证创建 COS 客户端直传;访问地址按 `Location`、自定义\n * endpoint 或默认 COS 域名依次解析。\n */\nexport class StorageService implements StorageServicing {\n\n /**\n * 服务层组件\n */\n private readonly apiService: ApiServicing;\n\n /**\n * 平台资源\n */\n private readonly clientFactory: StorageObjectClientFactory;\n\n /**\n * 标识生成\n */\n private readonly dateGenerator: () => Date;\n\n /**\n * 生成上传对象键名中的唯一标识。\n */\n private readonly identifierGenerator: () => string;\n\n /**\n * 创建存储 Service。\n *\n * @param options.apiService Xmax API 请求组件。\n * @param options.clientFactory 对象存储客户端工厂(可替换,测试用)。\n * @param options.dateGenerator 时间来源(可替换,测试用)。\n * @param options.identifierGenerator 标识来源(可替换,测试用)。\n */\n constructor(\n options: StorageServiceOptions\n ) {\n this.apiService = options.apiService;\n this.clientFactory = options.clientFactory ?? StorageService.cosClientFactory;\n this.dateGenerator = options.dateGenerator ?? (() => new Date());\n this.identifierGenerator =\n options.identifierGenerator ?? (() => crypto.randomUUID().toLowerCase());\n }\n\n /**\n * 上传图片并返回访问地址。\n */\n async uploadImage(\n options: StorageUploadOptions\n ): Promise<StoredFile> {\n return this.upload(options, \"Image\");\n }\n\n /**\n * 普通上传原始视频并返回访问地址,不进行转码、压缩或分片。\n */\n async uploadVideo(\n options: StorageUploadOptions\n ): Promise<StoredFile> {\n return this.upload(options, \"Video\");\n }\n\n /**\n * 下载原始视频文件并交给浏览器保存,支持取消网络下载。\n */\n async downloadVideo(\n options: Parameters<StorageServicing[\"downloadVideo\"]>[0]\n ): Promise<void> {\n const fileName = StorageService.validateFileName(options.fileName);\n let url: URL;\n try {\n url = new URL(options.url.trim());\n } catch {\n throw new XmaxError(XmaxErrorCode.invalidConfiguration, \"Invalid video download URL\");\n }\n if (![\"http:\", \"https:\"].includes(url.protocol) || url.username || url.password) {\n throw new XmaxError(XmaxErrorCode.invalidConfiguration, \"Video download requires an HTTP(S) URL without embedded credentials\");\n }\n if (typeof document === \"undefined\" || !document.body) {\n throw new XmaxError(XmaxErrorCode.invalidConfiguration, \"Video saving requires a browser document\");\n }\n\n let objectURL: string | undefined;\n let link: HTMLAnchorElement | undefined;\n let saveRequested = false;\n try {\n if (options.signal?.aborted) {\n throw new XmaxError(XmaxErrorCode.cancelled, \"Video download was cancelled\");\n }\n\n // 直接读取资源,不向文件地址发送 SDK API Key 或 Cookie。\n const response = await fetch(options.url.trim(), { credentials: \"omit\", signal: options.signal });\n if (!response.ok) {\n throw new XmaxError(XmaxErrorCode.downloadError, \"Video download failed\", { httpStatus: response.status });\n }\n const data = await response.blob();\n if (options.signal?.aborted) {\n throw new XmaxError(XmaxErrorCode.cancelled, \"Video download was cancelled\");\n }\n if (data.size === 0) {\n throw new XmaxError(XmaxErrorCode.downloadError, \"Downloaded video is empty\");\n }\n\n // 使用本地 Blob 地址,让浏览器按指定文件名保存跨域资源。\n objectURL = URL.createObjectURL(data);\n link = document.createElement(\"a\");\n link.href = objectURL;\n link.download = fileName;\n link.hidden = true;\n document.body.appendChild(link);\n link.click();\n saveRequested = true;\n } catch (error) {\n if (options.signal?.aborted) {\n throw new XmaxError(XmaxErrorCode.cancelled, \"Video download was cancelled\");\n }\n if (error instanceof XmaxError) {\n throw error;\n }\n throw new XmaxError(XmaxErrorCode.downloadError, error instanceof Error ? error.message : String(error));\n } finally {\n link?.remove();\n if (objectURL) {\n const resourceURL = objectURL;\n if (saveRequested) {\n // 给浏览器时间接管下载,再释放 Blob 地址。\n setTimeout(() => URL.revokeObjectURL(resourceURL), 60_000);\n } else {\n URL.revokeObjectURL(resourceURL);\n }\n }\n }\n }\n\n /**\n * 获取临时凭证并通过 COS putObject 上传文件,统一处理进度、结果和错误。\n */\n private async upload(\n options: StorageUploadOptions,\n mediaType: \"Image\" | \"Video\"\n ): Promise<StoredFile> {\n const startedAt = Date.now();\n try {\n const safeName = StorageService.validateFileName(options.fileName);\n const contentType =\n options.contentType?.trim() || StorageService.inferContentType(safeName);\n const body = StorageService.makeBody(options.data, contentType);\n if (body.size === 0) {\n throw new XmaxError(\n XmaxErrorCode.invalidConfiguration,\n `${mediaType} data cannot be empty`,\n );\n }\n\n const temporary = await this.fetchStorageConfiguration();\n const objectKey = this.makeObjectKey(temporary.prefix, safeName);\n const client = await this.clientFactory(temporary);\n\n XmaxLogger.storage.info(\n () =>\n `开始上传 (Upload Started)\\n` +\n `├─ ${XmaxLogger.localized(\"文件:\", \"File: \")}${safeName}\\n` +\n `└─ ${XmaxLogger.localized(\"大小:\", \"Size: \")}${StorageService.formatByteCount(body.size)}`,\n );\n\n const uploaded = await StorageService.putObject(client, {\n Bucket: temporary.bucket,\n Region: temporary.region,\n Key: objectKey,\n Body: body,\n ContentType: contentType,\n onProgress: options.onProgress\n ? (info) => options.onProgress?.(info.loaded, info.total)\n : undefined,\n });\n\n const url = StorageService.resolveObjectURL(\n uploaded.Location,\n temporary,\n objectKey,\n );\n XmaxLogger.storage.info(\n () =>\n `上传完成 (Upload Completed)\\n` +\n `├─ ${XmaxLogger.localized(\"地址:\", \"URL: \")}${url}\\n` +\n `└─ ${XmaxLogger.localized(\"耗时:\", \"Duration: \")}${Date.now() - startedAt}ms`,\n );\n return new StoredFile({ url, objectKey, etag: uploaded.ETag });\n } catch (error) {\n if (error instanceof XmaxError) {\n throw error;\n }\n throw new XmaxError(\n XmaxErrorCode.uploadError,\n error instanceof Error && error.message.trim().length > 0\n ? error.message.trim()\n : String(error),\n );\n }\n }\n\n /**\n * 获取临时存储配置;字段不完整时抛出 API 错误。\n */\n private async fetchStorageConfiguration(): Promise<TemporaryStorageConfiguration> {\n const payload = await this.apiService.get<TemporaryStoragePayload>(\"/cos/sts\");\n\n const message = \"Invalid storage credential payload\";\n const bucket = StorageService.nonEmpty(payload.bucket);\n const region = StorageService.nonEmpty(payload.region);\n const prefix = StorageService.nonEmpty(payload.prefix);\n const accessKeyID = StorageService.nonEmpty(payload.credentials?.accessKeyId);\n const secretAccessKey = StorageService.nonEmpty(payload.credentials?.secretAccessKey);\n const sessionToken = StorageService.nonEmpty(payload.credentials?.sessionToken);\n if (!bucket || !region || !prefix || !accessKeyID || !secretAccessKey || !sessionToken) {\n throw new XmaxError(XmaxErrorCode.apiError, message);\n }\n\n return {\n bucket,\n region,\n endpoint: StorageService.nonEmpty(payload.endpoint),\n prefix,\n credentials: { accessKeyID, secretAccessKey, sessionToken },\n };\n }\n\n /**\n * 生成对象键:`${prefix}${毫秒时间戳}_${标识}_${文件名}`。\n */\n private makeObjectKey(\n prefix: string,\n fileName: string\n ): string {\n const milliseconds = Math.round(this.dateGenerator().getTime());\n return `${prefix}${milliseconds}_${this.identifierGenerator()}_${fileName}`;\n }\n\n /**\n * 生产环境 COS 客户端工厂:动态加载 cos-js-sdk-v5 并按临时凭证创建客户端。\n */\n private static async cosClientFactory(\n configuration: TemporaryStorageConfiguration,\n ): Promise<StorageObjectClient> {\n const module = await import(\"cos-js-sdk-v5\");\n const COS = module.default;\n return new COS({\n SecretId: configuration.credentials.accessKeyID,\n SecretKey: configuration.credentials.secretAccessKey,\n SecurityToken: configuration.credentials.sessionToken,\n }) as unknown as StorageObjectClient;\n }\n\n /**\n * 调用对象存储上传并包装为 Promise。\n */\n private static putObject(\n client: StorageObjectClient,\n params: Parameters<StorageObjectClient[\"putObject\"]>[0],\n ): Promise<{\n Location?: string;\n ETag?: string\n }> {\n return new Promise((resolve, reject) => {\n client.putObject(params, (error, data) => {\n if (error) {\n reject(\n new XmaxError(\n XmaxErrorCode.uploadError,\n error.message?.trim() || \"Storage upload failed\",\n ),\n );\n return;\n }\n resolve(data ?? {});\n });\n });\n }\n\n /**\n * 解析对象访问地址:优先使用上传返回的 `Location`(补全协议),\n * 其次拼接自定义 endpoint,最后回退到默认 COS 域名。\n */\n private static resolveObjectURL(\n location: string | undefined,\n configuration: TemporaryStorageConfiguration,\n objectKey: string,\n ): string {\n const candidate = location?.trim() ?? \"\";\n if (candidate.startsWith(\"//\")) {\n return `https:${candidate}`;\n }\n if (/^https?:\\/\\//i.test(candidate)) {\n return candidate;\n }\n\n const endpoint = configuration.endpoint?.trim();\n if (endpoint) {\n const normalized = /^https?:\\/\\//i.test(endpoint)\n ? endpoint\n : `https://${endpoint}`;\n return `${normalized.replace(/\\/+$/, \"\")}/${objectKey}`;\n }\n\n return `https://${configuration.bucket}.cos.${configuration.region}.myqcloud.com/${objectKey}`;\n }\n\n /**\n * 统一构造上传体。\n */\n private static makeBody(\n data: StorageUploadOptions[\"data\"],\n contentType: string,\n ): Blob {\n if (data instanceof Blob) {\n return data;\n }\n const bytes = data instanceof Uint8Array ? data : new Uint8Array(data);\n return new Blob([bytes as unknown as BlobPart], { type: contentType });\n }\n\n /**\n * 校验文件名非空且不包含路径分隔符。\n */\n private static validateFileName(\n fileName: string\n ): string {\n const safeName = fileName.trim();\n if (!safeName || safeName.includes(\"/\") || safeName.includes(\"\\\\\")) {\n throw new XmaxError(\n XmaxErrorCode.invalidConfiguration,\n \"Invalid file name for storage operation\",\n );\n }\n return safeName;\n }\n\n /**\n * 按文件名后缀推断内容类型。\n */\n private static inferContentType(\n fileName: string\n ): string {\n const extension = fileName.split(\".\").pop()?.toLowerCase() ?? \"\";\n return CONTENT_TYPES[extension] ?? \"application/octet-stream\";\n }\n\n /**\n * 格式化字节数用于日志输出。\n */\n private static formatByteCount(\n bytes: number\n ): string {\n if (bytes < 1024) {\n return `${bytes} B`;\n }\n if (bytes < 1024 * 1024) {\n return `${(bytes / 1024).toFixed(1)} KB`;\n }\n return `${(bytes / 1024 / 1024).toFixed(2)} MB`;\n }\n\n /**\n * 归一化可选字符串:去除首尾空白后为空时返回 `undefined`。\n */\n private static nonEmpty(\n value?: string\n ): string | undefined {\n const normalized = value?.trim();\n return normalized || undefined;\n }\n}\n","//\n// CameraPosition.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\n/**\n * 相机采集使用的镜头方向。\n *\n * 采集时映射为 RTC 层的前后置选择;桌面设备通常只有前置等价\n * 摄像头,back 会回退到可用设备。\n */\nexport enum CameraPosition {\n\n /**\n * 前置摄像头(面向用户)。\n */\n front = \"front\",\n\n /**\n * 后置摄像头(面向环境)。\n */\n back = \"back\",\n}\n","//\n// MediaPermissionWatch.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\n/**\n * 媒体权限弹窗观察器。\n */\nexport interface MediaPermissionWatch {\n\n /**\n * 用户完成授权决定(所有观察的权限离开 prompt 状态)时兑现。\n */\n readonly decided: Promise<void>;\n\n /**\n * 移除授权变化监听;调用后 decided 不再兑现。\n */\n dispose(): void;\n}\n\n/**\n * 授权状态轮询间隔(毫秒)。\n *\n * Safari 的 PermissionStatus.onchange 不会触发(状态变化只能重新查询\n * 发现),在 onchange 之外用轮询兜底;间隔决定授权耗时的最大误差。\n */\nconst PERMISSION_POLL_INTERVAL_MS = 100;\n\n/**\n * 观察相机(可选麦克风)的系统授权状态。\n *\n * 权限均已授权时返回立即兑现的观察器(授权耗时接近 0);任一权限处于\n * prompt 状态时返回观察器,在用户完成授权决定时兑现;存在已拒绝权限或\n * 浏览器不支持该查询(如部分 Safari 版本)时返回 undefined,不记录耗时。\n */\nexport async function watchMediaPermissionPrompt(\n includeMicrophone: boolean,\n): Promise<MediaPermissionWatch | undefined> {\n if (typeof navigator === \"undefined\" || !navigator.permissions?.query) {\n return undefined;\n }\n\n // TS 标准库未包含 camera/microphone 描述名,按运行时能力调用。\n const names = [\"camera\", ...(includeMicrophone ? [\"microphone\"] : [])];\n const queryOnce = (): Promise<PermissionState[]> =>\n Promise.all(\n names.map(async (name) => {\n const status = await navigator.permissions.query({\n name: name as PermissionName,\n });\n return status.state;\n }),\n );\n const queryStates = (): Promise<PermissionState[]> =>\n Promise.all(\n names.map(async (name) => {\n try {\n const status = await navigator.permissions.query({\n name: name as PermissionName,\n });\n return status.state;\n } catch {\n // 轮询期间的查询失败按未决定处理,等待下次轮询。\n return \"prompt\" as PermissionState;\n }\n }),\n );\n\n let states: PermissionState[];\n try {\n states = await queryOnce();\n } catch {\n // 浏览器不支持该查询(如部分 Safari 版本),无法观察授权耗时。\n return undefined;\n }\n\n // 已全部授权时立即兑现(授权耗时接近 0);存在 denied 时无法观察,不记录。\n if (states.every((state) => state === \"granted\")) {\n return { decided: Promise.resolve(), dispose() { } };\n }\n if (!states.includes(\"prompt\")) {\n return undefined;\n }\n\n let disposed = false;\n let resolveDecided!: () => void;\n const decided = new Promise<void>((resolve) => {\n resolveDecided = resolve;\n });\n\n const finishIfDecided = (current: PermissionState[]): void => {\n if (disposed || current.includes(\"prompt\")) {\n return;\n }\n dispose();\n resolveDecided();\n };\n\n const poll = setInterval(() => {\n void queryStates().then(finishIfDecided);\n }, PERMISSION_POLL_INTERVAL_MS);\n\n // 结束授权轮询,后续权限变化不再通知当前观察器。\n function dispose(): void {\n disposed = true;\n clearInterval(poll);\n }\n\n return { decided, dispose };\n}\n","//\n// NetworkStatistics.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\n/**\n * 实时连接的网络质量等级。\n */\nexport enum RealtimeNetworkQualityLevel {\n\n /**\n * 尚无有效的质量评估。\n */\n unknown = \"Unknown\",\n\n /**\n * 网络质量极好。\n */\n excellent = \"Excellent\",\n\n /**\n * 网络质量良好。\n */\n good = \"Good\",\n\n /**\n * 网络质量一般,可能影响实时播放。\n */\n poor = \"Poor\",\n\n /**\n * 网络质量差,影响实时播放。\n */\n bad = \"Bad\",\n\n /**\n * 网络质量极差,可能严重中断媒体传输。\n */\n veryBad = \"VeryBad\",\n\n /**\n * 网络连接已断开。\n */\n down = \"Down\",\n}\n\n/**\n * 本端 RTC 连接的网络统计,不依赖远端生成流是否已经到达。\n */\nexport interface RealtimeNetworkQuality {\n\n /**\n * 上行网络质量等级,沿用 RTC 厂商的评估结果。\n */\n readonly uplink: RealtimeNetworkQualityLevel;\n\n /**\n * 所有下行连接的平均网络质量。\n */\n readonly downlink: RealtimeNetworkQualityLevel;\n}\n\n/**\n * undefined 表示尚无采样或连接已清理。\n */\nexport type RealtimeNetworkQualityListener = (\n statistics: RealtimeNetworkQuality | undefined\n) => void;\n\n/**\n * RTC 厂商使用的 0~6 质量等级映射;无效等级统一为 unknown。\n */\nconst RTC_QUALITY_LEVELS: readonly RealtimeNetworkQualityLevel[] = Object.values(RealtimeNetworkQualityLevel);\n\n/**\n * 将 RTC 数值等级转换为 SDK 网络质量枚举,不把无效样本当作好网络。\n */\nexport function networkQualityLevel(\n value: number | undefined\n): RealtimeNetworkQualityLevel {\n return value !== undefined && Number.isInteger(value)\n ? RTC_QUALITY_LEVELS[value] ?? RealtimeNetworkQualityLevel.unknown\n : RealtimeNetworkQualityLevel.unknown;\n}\n","//\n// RtcStatsLogger.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\nimport { XmaxLogger, XmaxLoggerOption } from \"../Logging/XmaxLogger\";\nimport { RealtimeNetworkQualityLevel, type RealtimeNetworkQuality } from \"./NetworkStatistics\";\nimport type { DownlinkVideoStatistics, UplinkVideoStatistics } from \"./VideoStatistics\";\n\n/**\n * RTC 连接层的通用诊断指标,独立于视频统计回调。\n */\ninterface ConnectionStatistics {\n\n /**\n * 本端与 RTC 服务的往返时延,单位 ms。\n */\n rttMs?: number;\n\n /**\n * 连接级上下行丢包率,单位 %。\n */\n uplinkLossPercent?: number;\n downlinkLossPercent?: number;\n\n /**\n * 当前连接累计收发的字节数。\n */\n sentBytes?: number;\n receivedBytes?: number;\n}\n\n/**\n * 网络质量日志可附带厂商实际提供的独立方向指标,不由整体 RTT 推算。\n */\ninterface NetworkQualityStatistics extends RealtimeNetworkQuality {\n\n /**\n * 独立上下行往返时延,单位 ms。\n */\n uplinkRTT?: number;\n downlinkRTT?: number;\n\n /**\n * 独立上下行丢包率,单位 %。\n */\n uplinkLoss?: number;\n downlinkLoss?: number;\n}\n\n/**\n * 使用公共统计对象输出厂商无关的性能日志,视频字段顺序和单位与 iOS 一致。 @internal\n */\nexport class RtcStatsLogger {\n\n /**\n * 输出上行视频统计;保持 info 级别,关闭性能日志时不格式化。\n */\n static logUplinkVideoStatistics(\n video: UplinkVideoStatistics,\n ): void {\n XmaxLogger.rtc.info(() => this.uplinkVideoStatisticsMessage(video), XmaxLoggerOption.performance);\n }\n\n /**\n * 格式化上行视频指标,不以发送帧率填补缺失的编码帧率。\n */\n static uplinkVideoStatisticsMessage(\n video: UplinkVideoStatistics,\n ): string {\n return this.block(\"视频上行统计 (Video Uplink Statistics)\", [\n `${XmaxLogger.localized(\"分辨率:\", \"Resolution: \")}${this.resolution(video.width, video.height)}`,\n `${XmaxLogger.localized(\"发送码率:\", \"Send Bitrate: \")}${this.value(video.bitrateKbps, \"kbps\")}`,\n `${XmaxLogger.localized(\"采集帧率:\", \"Capture Frame Rate: \")}${this.value(video.captureFrameRate, \"fps\")}`,\n `${XmaxLogger.localized(\"编码帧率:\", \"Encode Frame Rate: \")}${this.value(video.encodeFrameRate, \"fps\")}`,\n `${XmaxLogger.localized(\"发送帧率:\", \"Send Frame Rate: \")}${this.value(video.sendFrameRate, \"fps\")}`,\n `${XmaxLogger.localized(\"视频丢包率:\", \"Video Packet Loss: \")}${this.value(video.packetLossPercent, \"%\")}`,\n ]);\n }\n\n /**\n * 输出下行视频统计,采用与统计回调相同的对象。\n */\n static logDownlinkVideoStatistics(\n video: DownlinkVideoStatistics,\n ): void {\n XmaxLogger.rtc.info(() => this.downlinkVideoStatisticsMessage(video), XmaxLoggerOption.performance);\n }\n\n /**\n * 格式化下行视频指标;不输出用户标识或未经白名单筛选的厂商字段。\n */\n static downlinkVideoStatisticsMessage(\n video: DownlinkVideoStatistics,\n ): string {\n return this.block(\"视频下行统计 (Video Downlink Statistics)\", [\n `${XmaxLogger.localized(\"分辨率:\", \"Resolution: \")}${this.resolution(video.width, video.height)}`,\n `${XmaxLogger.localized(\"接收码率:\", \"Receive Bitrate: \")}${this.value(video.bitrateKbps, \"kbps\")}`,\n `${XmaxLogger.localized(\"解码帧率:\", \"Decode Frame Rate: \")}${this.value(video.decodeFrameRate, \"fps\")}`,\n `${XmaxLogger.localized(\"渲染帧率:\", \"Render Frame Rate: \")}${this.value(video.renderFrameRate, \"fps\")}`,\n `${XmaxLogger.localized(\"视频丢包率:\", \"Video Packet Loss: \")}${this.value(video.packetLossPercent, \"%\")}`,\n `${XmaxLogger.localized(\"端到端时延:\", \"End-to-End Delay: \")}${this.value(video.endToEndDelayMs, \"ms\")}`,\n ]);\n }\n\n /**\n * 独立保留连接级 RTT、丢包率和累计流量,不混入视频指标。\n */\n static logConnectionStatistics(\n stats: ConnectionStatistics,\n ): void {\n XmaxLogger.rtc.info(() => this.block(\"RTC 运行统计 (RTC Statistics)\", [\n `RTT: ${this.value(stats.rttMs, \"ms\")}`,\n `${XmaxLogger.localized(\"上行丢包率:\", \"Uplink Packet Loss: \")}${this.value(stats.uplinkLossPercent, \"%\")}`,\n `${XmaxLogger.localized(\"下行丢包率:\", \"Downlink Packet Loss: \")}${this.value(stats.downlinkLossPercent, \"%\")}`,\n `${XmaxLogger.localized(\"累计发送:\", \"Bytes Sent: \")}${this.value(stats.sentBytes, \"bytes\")}`,\n `${XmaxLogger.localized(\"累计接收:\", \"Bytes Received: \")}${this.value(stats.receivedBytes, \"bytes\")}`,\n ]), XmaxLoggerOption.performance);\n }\n\n /**\n * 输出统一网络质量;仅附带厂商实际提供的独立方向时延和丢包率。\n */\n static logNetworkQuality(\n stats: NetworkQualityStatistics,\n ): void {\n XmaxLogger.rtc.info(() => {\n const details = (rtt?: number, loss?: number) => rtt === undefined && loss === undefined\n ? \"\"\n : `, RTT ${this.value(rtt, \"ms\")}, ${XmaxLogger.localized(\"丢包率 \", \"Loss \")}${this.value(loss, \"%\")}`;\n return this.block(\"网络质量 (Network Quality Metrics)\", [\n `${XmaxLogger.localized(\"本地上行:\", \"Local Uplink: \")}${this.quality(stats.uplink)}${details(stats.uplinkRTT, stats.uplinkLoss)}`,\n `${XmaxLogger.localized(\"本地下行(平均):\", \"Local Downlink (Average): \")}${this.quality(stats.downlink)}${details(stats.downlinkRTT, stats.downlinkLoss)}`,\n ]);\n }, XmaxLoggerOption.performance);\n }\n\n /**\n * 宽高必须同时有效,任意一项缺失时整个分辨率显示占位符。\n */\n private static resolution(\n width?: number,\n height?: number,\n ): string {\n if (typeof width !== \"number\" || typeof height !== \"number\" || !Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0) {\n return \"—\";\n }\n return `${width} × ${height}`;\n }\n\n /**\n * 为指标列表添加标题和树形行前缀,生成一个控制台日志分组。\n */\n private static block(\n title: string,\n lines: string[]\n ): string {\n return [title, ...lines.map((line, index) => `${index === lines.length - 1 ? \"└─\" : \"├─\"} ${line}`)].join(\"\\n\");\n }\n\n /**\n * 不改变公共单位,保留有效零值,Web 小数最多显示两位。\n */\n private static value(\n value: number | undefined,\n unit = \"\"\n ): string {\n if (typeof value !== \"number\" || !Number.isFinite(value) || value < 0) {\n return \"—\";\n }\n return `${Number(value.toFixed(2))}${unit ? ` ${unit}` : \"\"}`;\n }\n\n /**\n * 将公共网络质量枚举转换为本地化文案。\n */\n private static quality(\n value: RealtimeNetworkQualityLevel,\n ): string {\n const names: Record<RealtimeNetworkQualityLevel, readonly [string, string]> = {\n [RealtimeNetworkQualityLevel.unknown]: [\"未知\", \"Unknown\"],\n [RealtimeNetworkQualityLevel.excellent]: [\"极好\", \"Excellent\"],\n [RealtimeNetworkQualityLevel.good]: [\"良好\", \"Good\"],\n [RealtimeNetworkQualityLevel.poor]: [\"一般\", \"Poor\"],\n [RealtimeNetworkQualityLevel.bad]: [\"差\", \"Bad\"],\n [RealtimeNetworkQualityLevel.veryBad]: [\"极差\", \"Very Bad\"],\n [RealtimeNetworkQualityLevel.down]: [\"断网\", \"Disconnected\"],\n };\n const name = names[value] ?? names[RealtimeNetworkQualityLevel.unknown];\n return XmaxLogger.localized(name[0], name[1]);\n }\n}\n","//\n// AgoraRtcStatistics.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\nimport type { IAgoraRTCClient, NetworkQuality } from \"agora-rtc-sdk-ng\";\nimport type { RtcEventListener } from \"../RtcEventListener\";\nimport { networkQualityLevel } from \"../NetworkStatistics\";\nimport { RtcStatsLogger } from \"../RtcStatsLogger\";\nimport type { DownlinkVideoStatistics, UplinkVideoStatistics } from \"../VideoStatistics\";\n\n/**\n * 将声网统计转换为 SDK 公共单位,保留厂商缺失指标,不推算播放缓冲延迟。\n */\nexport class AgoraRtcStatistics {\n\n /**\n * 采集已发布的视频和已订阅的结果流统计,性能日志不控制回调采集。\n */\n static collect(\n client: IAgoraRTCClient,\n listener?: RtcEventListener\n ): void {\n const rtc = client.getRTCStats();\n const local = client.getLocalVideoStats();\n const valid = AgoraRtcStatistics.valid;\n const loss = AgoraRtcStatistics.loss;\n const localVideo: UplinkVideoStatistics | undefined = client.localTracks.some(track => track.trackMediaType === \"video\") ? Object.freeze({\n width: valid(local.sendResolutionWidth, 1), height: valid(local.sendResolutionHeight, 1),\n captureFrameRate: valid(local.captureFrameRate), sendFrameRate: valid(local.sendFrameRate),\n bitrateKbps: valid(local.sendBitrate / 1000), packetLossPercent: loss(local.currentPacketLossRate),\n }) : undefined;\n const remote: readonly DownlinkVideoStatistics[] = Object.freeze(Object.entries(client.getRemoteVideoStats()).map(([userID, stats]) => Object.freeze({\n userID, width: valid(stats.receiveResolutionWidth, 1), height: valid(stats.receiveResolutionHeight, 1),\n decodeFrameRate: valid(stats.decodeFrameRate), renderFrameRate: valid(stats.renderFrameRate),\n bitrateKbps: valid(stats.receiveBitrate / 1000),\n packetLossPercent: loss(stats.currentPacketLossRate), endToEndDelayMs: valid(stats.end2EndDelay),\n })));\n listener?.onLocalVideoStatistics?.(localVideo);\n listener?.onRemoteVideoStatistics?.(remote);\n\n RtcStatsLogger.logConnectionStatistics({\n rttMs: valid(rtc.RTT), sentBytes: valid(rtc.SendBytes), receivedBytes: valid(rtc.RecvBytes),\n });\n if (localVideo) {\n RtcStatsLogger.logUplinkVideoStatistics(localVideo);\n }\n for (const video of remote) {\n RtcStatsLogger.logDownlinkVideoStatistics(video);\n }\n }\n\n /**\n * 网络质量等级范围与公共枚举一致;不将整体 RTT 冒充上下行独立 RTT。\n */\n static network(\n stats: NetworkQuality,\n listener?: RtcEventListener\n ): void {\n const result = Object.freeze({\n uplink: networkQualityLevel(stats.uplinkNetworkQuality),\n downlink: networkQualityLevel(stats.downlinkNetworkQuality),\n });\n listener?.onNetworkStatistics?.(result);\n RtcStatsLogger.logNetworkQuality(result);\n }\n\n /**\n * 过滤缺失、非有限或不合理的统计值。\n */\n private static valid(\n value: number | undefined,\n minimum = 0\n ): number | undefined {\n return typeof value === \"number\" && Number.isFinite(value) && value >= minimum ? value : undefined;\n }\n\n /**\n * 声网丢包率为 0–1 比例,转换为公共百分比。\n */\n private static loss(\n value: number\n ): number | undefined {\n return Number.isFinite(value) && value >= 0 && value <= 1 ? value * 100 : undefined;\n }\n}\n","//\n// RtcVideoOrientation.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\n/**\n * RTC 视频方向环境参数;缺省时读取浏览器全局对象(可注入,测试用)。\n */\nexport interface RtcOrientationEnvironment {\n\n /**\n * 浏览器报告的用户代理字符串。\n */\n userAgent?: string;\n\n /**\n * 设备支持的最大同时触点数。\n */\n maxTouchPoints?: number;\n\n /**\n * `screen.orientation.type`,如 `\"portrait-primary\"`。\n */\n orientationType?: string;\n\n /**\n * 旧式 `window.orientation` 角度;无 `orientationType` 时使用。\n */\n orientationDegrees?: number;\n}\n\n/**\n * 判断当前是否为移动端竖屏。\n *\n * TRTC、Agora 与 VeRTC 的移动端竖屏采集、编码路径存在宽高转置行为。\n * 为让上行方向与请求一致,传给厂商 SDK 的尺寸需反向转置;\n * 业务请求尺寸和实际视频统计保持原样。\n */\nexport function shouldTransposeRtcVideoSize(\n environment?: RtcOrientationEnvironment,\n): boolean {\n const userAgent =\n environment?.userAgent ??\n (typeof navigator === \"undefined\" ? \"\" : navigator.userAgent);\n const maxTouchPoints =\n environment?.maxTouchPoints ??\n (typeof navigator === \"undefined\" ? 0 : navigator.maxTouchPoints ?? 0);\n\n // 桌面模式 UA 的 iPad 通过触控点数识别。\n const mobile =\n /Android|webOS|iPhone|iPad|iPod|Mobile/i.test(userAgent) ||\n (/Macintosh/i.test(userAgent) && maxTouchPoints > 1);\n if (!mobile) {\n return false;\n }\n\n const orientationType =\n environment?.orientationType ??\n (typeof window === \"undefined\" ? undefined : window.screen?.orientation?.type);\n if (orientationType !== undefined) {\n return orientationType.includes(\"portrait\");\n }\n\n const degrees =\n environment?.orientationDegrees ??\n (typeof window === \"undefined\"\n ? undefined\n : (window as unknown as {\n orientation?: number\n }).orientation);\n return degrees === 0 || degrees === 180;\n}\n\n/**\n * 移动端竖屏时返回转置后的宽高,其余情况原样返回。\n */\nexport function rtcOrientedVideoSize(\n size: {\n width: number;\n height: number\n },\n environment?: RtcOrientationEnvironment,\n): {\n width: number;\n height: number\n} {\n return shouldTransposeRtcVideoSize(environment)\n ? { width: size.height, height: size.width }\n : size;\n}\n","//\n// AgoraVideoSender.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\nimport type { ILocalVideoTrack } from \"agora-rtc-sdk-ng\";\nimport { XmaxError, XmaxErrorCode } from \"../../Errors/XmaxError\";\nimport { RtcVideoEncoderPreference, type VideoEncodingConfiguration } from \"../VideoEncodingConfiguration\";\n\n/**\n * 实验性摄像头发送配置:不修改采集约束,不调用 Agora 的编码配置接口。\n */\nexport class AgoraVideoSender {\n\n /**\n * 将码率和帧率约束应用到浏览器视频发送器。\n */\n static async apply(\n track: ILocalVideoTrack,\n config: VideoEncodingConfiguration,\n ensureActive: () => void\n ): Promise<void> {\n ensureActive();\n const sender = track.getRTCRtpTransceiver()?.sender;\n if (!sender)\n throw new XmaxError(XmaxErrorCode.rtcError, \"WebRTC video sender is unavailable\");\n const capture = sender.track?.getSettings();\n if (!capture?.width || !capture.height) {\n throw new XmaxError(XmaxErrorCode.rtcError, \"WebRTC capture dimensions are unavailable\");\n }\n // 按实际采集规格计算,不能用上一次发送规格,否则连续降档会重复缩放。\n const scale = Math.max(1, capture.width / config.width, capture.height / config.height);\n const parameters = sender.getParameters();\n if (parameters.encodings?.length !== 1) {\n throw new XmaxError(XmaxErrorCode.rtcError, \"Sender-only configuration requires one video encoding\");\n }\n const encoding = parameters.encodings[0]!;\n const previous = { ...encoding };\n const previousPreference = parameters.degradationPreference;\n encoding.scaleResolutionDownBy = scale;\n encoding.maxFramerate = config.frameRate;\n encoding.maxBitrate = Math.round(config.maximumBitrate * 1000);\n parameters.degradationPreference = config.encoderPreference === RtcVideoEncoderPreference.maintainFramerate\n ? \"maintain-framerate\" : \"maintain-resolution\";\n await sender.setParameters(parameters);\n ensureActive();\n if (track.getRTCRtpTransceiver()?.sender !== sender) {\n throw new XmaxError(XmaxErrorCode.cancelled, \"WebRTC video sender changed during configuration\");\n }\n const applied = sender.getParameters();\n const actual = applied.encodings?.[0];\n if (!actual || Math.abs((actual.scaleResolutionDownBy ?? 1) - scale) > 0.0001 ||\n actual.maxFramerate !== encoding.maxFramerate || actual.maxBitrate !== encoding.maxBitrate) {\n // 某些浏览器会静默忽略字段;尽力恢复旧发送参数,绝不回退到修改摄像头约束。\n if (actual && applied.encodings.length === 1) {\n for (const key of [\"scaleResolutionDownBy\", \"maxFramerate\", \"maxBitrate\"] as const) {\n if (previous[key] === undefined)\n delete actual[key];\n else\n actual[key] = previous[key];\n }\n if (previousPreference === undefined)\n delete applied.degradationPreference;\n else\n applied.degradationPreference = previousPreference;\n await sender.setParameters(applied);\n }\n throw new XmaxError(XmaxErrorCode.rtcError, \"Browser did not retain WebRTC video sender parameters\");\n }\n }\n}\n","//\n// CameraRotation.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\nimport { XmaxError, XmaxErrorCode } from \"../../Errors/XmaxError\";\n\n/**\n * 输入帧顺时针旋转角度;与前置摄像头的水平镜像独立。\n */\nexport type CameraRotation = 0 | 90 | 180 | 270;\n\n/**\n * 校验摄像头旋转角度,仅接受 0、90、180 或 270 度。\n */\nexport function validateCameraRotation(\n rotation: number\n): asserts rotation is CameraRotation {\n if (![0, 90, 180, 270].includes(rotation)) {\n throw new XmaxError(XmaxErrorCode.invalidConfiguration, \"Camera rotation must be 0, 90, 180 or 270\");\n }\n}\n","//\n// CameraRotationProcessor.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\nimport { XmaxError, XmaxErrorCode } from \"../../Errors/XmaxError\";\nimport type { CameraRotation } from \"./CameraRotation\";\n\n/**\n * 将摄像头原始帧旋转为固定目标尺寸,供预览和 RTC 共用。\n * 只持有输入轨引用,不关闭摄像头;拥有输出轨和帧调度资源。\n */\nexport class CameraRotationProcessor {\n\n /**\n * 用于读取或预览源画面的视频元素。\n */\n private readonly video = document.createElement(\"video\");\n\n /**\n * 承载媒体帧的画布。\n */\n private readonly canvas = document.createElement(\"canvas\");\n\n /**\n * 画布绘制上下文。\n */\n private readonly context: CanvasRenderingContext2D;\n\n /**\n * 由当前媒体源输出的浏览器媒体流。\n */\n private stream?: MediaStream;\n\n /**\n * 旋转处理前的摄像头原始轨,不由本组件关闭。\n */\n private source?: MediaStreamTrack;\n\n /**\n * 等待下一帧的视频回调句柄。\n */\n private frameCallback?: number;\n\n /**\n * 当前等待或定时任务的取消句柄。\n */\n private timer?: ReturnType<typeof setTimeout>;\n\n /**\n * 终止尚未完成的首帧等待。\n */\n private cancelPending?: () => void;\n\n /**\n * 是否已停止调度并释放当前管线。\n */\n private stopped = false;\n\n /**\n * 标识当前媒体操作,过滤旧操作的迟到结果。\n */\n private version = 0;\n\n /**\n * 摄像头原始轨结束时停止旋转处理。\n */\n private readonly sourceEnded = () => {\n this.fail(new XmaxError(XmaxErrorCode.mediaError, \"Camera source ended during rotation\"));\n };\n\n /**\n * 准备旋转画布与播放器,浏览器不支持画布采集时抛出错误。\n */\n constructor(\n private readonly options: {\n width: number;\n height: number;\n frameRate: number;\n rotation: Exclude<CameraRotation, 0>;\n onError: (\n error: XmaxError\n ) => void;\n }\n ) {\n this.canvas.width = options.width;\n this.canvas.height = options.height;\n const context = this.canvas.getContext(\"2d\", { alpha: false });\n if (!context || typeof this.canvas.captureStream !== \"function\") {\n throw new XmaxError(XmaxErrorCode.mediaError, \"Canvas camera rotation is not supported by this browser\");\n }\n this.context = context;\n this.video.muted = true;\n this.video.autoplay = true;\n this.video.playsInline = true;\n }\n\n /**\n * 等待第一帧实际绘制成功;换摄像头时复用输出轨,不重建 sender。\n */\n async setSource(\n source: MediaStreamTrack\n ): Promise<MediaStreamTrack> {\n if (this.stopped)\n throw new XmaxError(XmaxErrorCode.cancelled, \"Camera rotation has stopped\");\n if (source.readyState === \"ended\") {\n this.stop();\n throw new XmaxError(XmaxErrorCode.mediaError, \"Camera rotation requires a live source\");\n }\n this.cancelPending?.();\n this.cancelFrame();\n this.source?.removeEventListener(\"ended\", this.sourceEnded);\n const version = ++this.version;\n this.source = source;\n source.addEventListener(\"ended\", this.sourceEnded);\n this.video.srcObject = new MediaStream([source]);\n\n try {\n await new Promise<void>((resolve, reject) => {\n let settled = false;\n const finish = (error?: unknown) => {\n if (settled)\n return;\n settled = true;\n clearTimeout(timeout);\n this.cancelPending = undefined;\n if (error)\n reject(error);\n else\n resolve();\n };\n const timeout = setTimeout(() => finish(new XmaxError(XmaxErrorCode.timeout, \"Timed out waiting for a camera rotation frame\")), 10000);\n this.cancelPending = () => finish(new XmaxError(XmaxErrorCode.cancelled, \"Camera rotation source changed\"));\n // play() 的异步完成也可能晚于停止/切换,不能复活旧的绘制循环。\n void this.video.play().then(() => {\n if (settled || this.stopped || version !== this.version)\n return;\n const firstFrame = () => {\n if (settled || this.stopped || version !== this.version)\n return;\n try {\n if (!this.draw()) {\n this.schedule(firstFrame);\n return;\n }\n this.stream ??= this.canvas.captureStream(this.options.frameRate);\n if (!this.stream.getVideoTracks()[0])\n throw new Error(\"Canvas did not produce a video track\");\n this.requestFrame();\n finish();\n this.schedule(() => this.render(version));\n } catch (error) {\n finish(error);\n }\n };\n this.schedule(firstFrame);\n }, finish);\n });\n if (this.stopped || version !== this.version) {\n throw new XmaxError(XmaxErrorCode.cancelled, \"Camera rotation source changed\");\n }\n return this.stream!.getVideoTracks()[0]!;\n } catch (error) {\n if (version === this.version)\n this.stop();\n throw XmaxError.from(error);\n }\n }\n\n /**\n * 换设备前解除旧原始轨监听,保留 Canvas 最后一帧及输出轨。\n */\n pauseSource(): void {\n ++this.version;\n this.cancelPending?.();\n this.cancelFrame();\n this.source?.removeEventListener(\"ended\", this.sourceEnded);\n this.source = undefined;\n this.video.pause();\n this.video.srcObject = null;\n }\n\n /**\n * 更新旋转画布与输出帧率,复用当前输出轨道和源帧调度。\n */\n async updateFormat(\n width: number,\n height: number,\n frameRate: number\n ): Promise<void> {\n if (this.stopped) {\n throw new XmaxError(XmaxErrorCode.cancelled, \"Camera rotation has stopped\");\n }\n const version = this.version;\n const output = this.stream?.getVideoTracks()[0];\n await output?.applyConstraints({ frameRate });\n if (this.stopped || this.version !== version) {\n throw new XmaxError(XmaxErrorCode.cancelled, \"Camera rotation source changed\");\n }\n Object.assign(this.options, { width, height, frameRate });\n this.canvas.width = width;\n this.canvas.height = height;\n if (this.draw()) {\n this.requestFrame();\n }\n }\n\n /**\n * 停止旋转调度并释放输出轨,保留原始摄像头轨的所有权。\n */\n stop(): void {\n if (this.stopped)\n return;\n this.stopped = true;\n ++this.version;\n this.cancelPending?.();\n this.cancelFrame();\n this.source?.removeEventListener(\"ended\", this.sourceEnded);\n this.source = undefined;\n this.video.pause();\n this.video.srcObject = null;\n this.stream?.getTracks().forEach(track => track.stop());\n this.stream = undefined;\n this.canvas.width = this.canvas.height = 0;\n }\n\n /**\n * 按旋转后的目标尺寸居中裁剪并绘制当前源帧。\n */\n private draw(): boolean {\n const { videoWidth: width, videoHeight: height } = this.video;\n if (this.video.readyState < 2 || !width || !height)\n return false;\n const { width: outWidth, height: outHeight, rotation } = this.options;\n const quarterTurn = rotation === 90 || rotation === 270;\n const targetWidth = quarterTurn ? outHeight : outWidth;\n const targetHeight = quarterTurn ? outWidth : outHeight;\n // 设备不一定满足请求宽高;按真实帧尺寸等比居中裁剪,绝不拉伸。\n const scale = Math.max(targetWidth / width, targetHeight / height);\n const cropWidth = targetWidth / scale, cropHeight = targetHeight / scale;\n this.context.setTransform(1, 0, 0, 1, outWidth / 2, outHeight / 2);\n this.context.rotate(rotation * Math.PI / 180);\n this.context.drawImage(this.video, (width - cropWidth) / 2, (height - cropHeight) / 2,\n cropWidth, cropHeight, -targetWidth / 2, -targetHeight / 2, targetWidth, targetHeight);\n return true;\n }\n\n /**\n * 绘制当前源帧并继续调度;媒体源已切换或停止时结束调度。\n */\n private render(\n version: number\n ): void {\n if (this.stopped || version !== this.version)\n return;\n try {\n if (this.draw())\n this.requestFrame();\n this.schedule(() => this.render(version));\n } catch (error) {\n this.fail(XmaxError.from(error));\n }\n }\n\n /**\n * 通知画布输出轨发送刚绘制的帧。\n */\n private requestFrame(): void {\n const track = this.stream?.getVideoTracks()[0] as CanvasCaptureMediaStreamTrack | undefined;\n track?.requestFrame?.();\n }\n\n /**\n * 优先等待原生视频帧回调,不支持时按目标帧率定时绘制。\n */\n private schedule(\n callback: () => void\n ): void {\n if (typeof this.video.requestVideoFrameCallback === \"function\") {\n this.frameCallback = this.video.requestVideoFrameCallback(callback);\n } else {\n this.timer = setTimeout(callback, 1000 / this.options.frameRate);\n }\n }\n\n /**\n * 取消下一次视频帧回调和定时绘制任务。\n */\n private cancelFrame(): void {\n if (this.frameCallback !== undefined)\n this.video.cancelVideoFrameCallback(this.frameCallback);\n clearTimeout(this.timer);\n this.frameCallback = undefined;\n this.timer = undefined;\n }\n\n /**\n * 释放旋转资源并向上层报告媒体错误。\n */\n private fail(\n error: XmaxError\n ): void {\n this.stop();\n this.options.onError(error);\n }\n}\n","//\n// AgoraRtcManager.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\nimport { RtcProvider } from \"../RtcProvider\";\nimport type { AREAS, IAgoraRTC, IAgoraRTCClient, IAgoraRTCRemoteUser, ICameraVideoTrack, IMicrophoneAudioTrack, ILocalVideoTrack, ILocalAudioTrack } from \"agora-rtc-sdk-ng\";\nimport { XmaxError, XmaxErrorCode } from \"../../Errors/XmaxError\";\nimport { XmaxLogger } from \"../../Logging/XmaxLogger\";\nimport { CameraPosition } from \"../../Media/Camera/CameraPosition\";\nimport { XmaxEnvironment } from \"../../Runtime/XmaxEnvironment\";\nimport type { RtcManaging, RtcCameraCaptureOptions, RtcRoomMessageDelivery } from \"../RtcManaging\";\nimport type { RtcEventListener } from \"../RtcEventListener\";\nimport type { RtcRoomJoinConfiguration, AgoraRoomJoinConfiguration } from \"../RoomJoinConfiguration\";\nimport { RtcVideoEncoderPreference, type VideoEncodingConfiguration } from \"../VideoEncodingConfiguration\";\nimport { AgoraRtcStatistics } from \"./AgoraRtcStatistics\";\nimport { rtcOrientedVideoSize } from \"../RtcVideoOrientation\";\nimport { AgoraVideoSender } from \"./AgoraVideoSender\";\nimport { validateCameraRotation } from \"../../Media/Camera/CameraRotation\";\nimport { CameraRotationProcessor } from \"../../Media/Camera/CameraRotationProcessor\";\n\n/**\n * 固定版本 SDK 的 DataStream 扩展;官方公共类型未暴露发送方法。\n */\nexport interface AgoraDataStreamClient extends IAgoraRTCClient {\n\n /**\n * 发送 UTF-8 房间消息;关闭底层重试,错误交给业务操作处理。\n */\n sendStreamMessage(\n message: string,\n needRetry: boolean\n ): Promise<void>;\n}\n\n/**\n * 绑定房间生命周期的待发消息;控制消息附带完成通知,快照仅保留最新一条。\n */\ninterface PendingRoomMessage {\n /**\n * 创建消息时的客户端与房间版本。\n */\n client: AgoraDataStreamClient;\n version: number;\n\n /**\n * 文本正文。\n */\n message: string;\n\n /**\n * 控制信令的发送结果;快照入队后即返回,不等待网关确认。\n */\n completion?: {\n resolve: () => void;\n reject: (error: unknown) => void;\n };\n}\n\n/**\n * 可替换的声网模块加载器,仅在初始化时加载浏览器依赖。\n */\nexport interface AgoraRtcManagerOptions {\n\n /**\n * 服务环境,同时用于选择厂商接入区域。\n */\n environment: XmaxEnvironment;\n\n /**\n * 按需加载 RTC SDK,首次初始化时调用。\n */\n loadSDK?: () => Promise<IAgoraRTC>;\n}\n\n/**\n * 声网媒体、房间与 DataStream 适配器;每个实例拥有独立 client 和媒体轨道。\n */\nexport class AgoraRtcManager implements RtcManaging {\n\n /**\n * 模块级区域约束:setArea 是全局设置,拒绝同时运行不同区域,避免静默覆盖。\n */\n private static readonly regions = new Map<IAgoraRTC, {\n environment: XmaxEnvironment;\n count: number\n }>();\n\n /**\n * 配置与 RTC 资源\n */\n private readonly environment: XmaxEnvironment;\n\n /**\n * 按需加载 RTC SDK,首次初始化时调用。\n */\n private readonly loadSDK: () => Promise<IAgoraRTC>;\n\n /**\n * 按需加载的 RTC SDK 模块。\n */\n private sdk?: IAgoraRTC;\n\n /**\n * 当前 Agora 客户端。\n */\n private client?: AgoraDataStreamClient;\n\n /**\n * 共享的初始化任务,避免并发创建引擎。\n */\n private initialization?: Promise<void>;\n\n /**\n * 生命周期标识,使释放后的异步结果失效。\n */\n private lifecycle = 0;\n\n /**\n * 当前摄像头视频轨。\n */\n private camera?: ILocalVideoTrack;\n\n /**\n * 旋转处理前的摄像头原始轨。\n */\n private cameraSource?: ICameraVideoTrack;\n\n /**\n * 当前摄像头旋转处理组件。\n */\n private cameraRotation?: CameraRotationProcessor;\n\n /**\n * 摄像头采集操作标识,过滤切换或停止后的迟到结果。\n */\n private captureVersion = 0;\n\n /**\n * 是否已启动麦克风采集。\n */\n private microphone?: IMicrophoneAudioTrack;\n\n /**\n * 由媒体源提供、适配器持有的外部视频克隆轨。\n */\n private externalVideo?: ILocalVideoTrack;\n\n /**\n * 由媒体源提供、适配器持有的外部音频克隆轨。\n */\n private externalAudio?: ILocalAudioTrack;\n\n /**\n * 已经应用编码偏好的 Agora 本地视频轨。\n */\n private readonly appliedOptimizationModes = new WeakMap<ILocalVideoTrack, RtcVideoEncoderPreference>();\n\n /**\n * 摄像头当前需要应用的发送编码参数。\n */\n private cameraSenderConfiguration?: VideoEncodingConfiguration;\n\n /**\n * 串行执行发送端参数更新的任务链。\n */\n private senderUpdateTail: Promise<void> = Promise.resolve();\n\n /**\n * 发送器替换后重新应用编码参数的监听器。\n */\n private cameraTransceiverListener?: () => void;\n\n /**\n * 当前房间使用的身份和鉴权配置。\n */\n private connection?: AgoraRoomJoinConfiguration;\n\n /**\n * 当前房间操作标识,退房后旧结果不再生效。\n */\n private roomVersion = 0;\n\n /**\n * 凭据恢复与发布意图;RTC 自动断开可能清空 localTracks,因此单独保留发布意图。\n */\n private tokenExpired = false;\n\n /**\n * 当前重新进房流程的共享任务。\n */\n private rejoinOperation?: Promise<void>;\n\n /**\n * 当前重新进房的共享任务。\n */\n private rejoinTask?: Promise<void>;\n\n /**\n * 终止正在等待的重新进房流程。\n */\n private cancelRejoin?: () => void;\n\n /**\n * 业务当前是否要求发布视频。\n */\n private videoPublishRequested = false;\n\n /**\n * 业务当前是否要求发布音频。\n */\n private audioPublishRequested = false;\n\n /**\n * 事件、统计与音频播放状态\n */\n private listener?: RtcEventListener;\n\n /**\n * 周期采集运行统计的定时器。\n */\n private statisticsTimer?: ReturnType<typeof setInterval>;\n\n /**\n * 重连或退出期间是否暂停网络质量采样。\n */\n private networkQualitySuspended = false;\n\n /**\n * 各远端用户的播放音量,范围为 0–100。\n */\n private readonly audioVolumes = new Map<string, number>();\n\n /**\n * 业务当前要求订阅音频的远端用户。\n */\n private readonly requestedAudio = new Set<string>();\n\n /**\n * DataStream 控制消息与快照调度;控制消息优先,快照不积压历史位置。\n */\n private readonly orderedMessages: PendingRoomMessage[] = [];\n\n /**\n * 尚未提交到声网的最新消息快照。\n */\n private latestMessage?: PendingRoomMessage;\n\n /**\n * 当前调度循环的身份,隔离退房后迟到的异步工作。\n */\n private messageDrain?: object;\n\n /**\n * 取消所有正在等待网关响应的消息;快照最多允许三十条在途。\n */\n private readonly activeSends = new Set<() => void>();\n\n /**\n * 保存服务环境与加载器,不触发浏览器依赖或设备权限请求。\n */\n constructor(\n options: AgoraRtcManagerOptions\n ) {\n this.environment = options.environment;\n this.loadSDK = options.loadSDK ?? (async () => (await import(\"agora-rtc-sdk-ng\")).default);\n }\n\n /**\n * 当前实例是否已创建声网 client。\n */\n get isInitialized(): boolean {\n return this.client !== undefined;\n }\n\n /**\n * 声网音频轨支持播放前设置音量;零音量额外停止播放以防浏览器差异。\n */\n get supportsRemoteAudioVolumeControl(): boolean {\n return true;\n }\n\n /**\n * 共享本实例的初始化任务,区域完全由接入方环境决定。\n */\n async initialize(): Promise<void> {\n if (this.client)\n return;\n if (this.initialization)\n return this.initialization;\n\n const version = this.lifecycle;\n const operation = this.performInitialize(version);\n this.initialization = operation;\n try {\n await operation;\n } finally {\n if (this.initialization === operation)\n this.initialization = undefined;\n }\n }\n\n /**\n * 加载模块、锁定区域并注册事件;迟到的初始化不能复活已销毁实例。\n */\n private async performInitialize(\n version: number\n ): Promise<void> {\n const sdk = await this.loadSDK();\n if (version !== this.lifecycle)\n throw this.cancelled();\n\n const region = AgoraRtcManager.regions.get(sdk);\n if (region && region.environment !== this.environment) {\n throw new XmaxError(XmaxErrorCode.invalidConfiguration, \"Concurrent Agora clients must use the same XmaxEnvironment\");\n }\n sdk.setLogLevel(4);\n if (!region)\n sdk.setArea([(this.environment === XmaxEnvironment.china ? \"CHINA\" : \"GLOBAL\") as AREAS]);\n\n const client = sdk.createClient({ mode: \"rtc\", codec: \"h264\" }) as AgoraDataStreamClient;\n if (typeof client.sendStreamMessage !== \"function\") {\n throw new XmaxError(XmaxErrorCode.rtcError, \"Agora DataStream is unavailable in this SDK version\");\n }\n AgoraRtcManager.regions.set(sdk, { environment: this.environment, count: (region?.count ?? 0) + 1 });\n this.sdk = sdk;\n this.client = client;\n this.registerEvents(client);\n }\n\n /**\n * 先失效全部异步工作,再释放房间、采集、监听和区域引用。\n */\n async destroy(): Promise<void> {\n this.lifecycle++;\n const client = this.client;\n // 保留上层事件接收者,允许同一个 Manager close 后重新创建媒体源;旧 client 的监听单独移除。\n this.clearRoom();\n this.client = undefined;\n client?.removeAllListeners();\n this.releaseCameraCapture();\n this.microphone?.close();\n this.externalVideo?.close();\n this.externalAudio?.close();\n this.externalVideo = undefined;\n this.externalAudio = undefined;\n this.camera = undefined;\n this.microphone = undefined;\n\n const sdk = this.sdk;\n this.sdk = undefined;\n try {\n await client?.leave();\n } catch {\n XmaxLogger.rtc.warning(() => \"Agora 退房失败,已释放本地资源 (Agora Leave Failed)\");\n } finally {\n const region = sdk && AgoraRtcManager.regions.get(sdk);\n if (sdk && region && --region.count === 0)\n AgoraRtcManager.regions.delete(sdk);\n }\n }\n\n /**\n * 通过声网采集相机但不发布,返回原生轨道供公共渲染层使用。\n */\n async startCameraCapture(\n options: RtcCameraCaptureOptions\n ): Promise<MediaStreamTrack> {\n const client = this.requireClient();\n if (this.camera || this.cameraSource || this.externalVideo) {\n throw new XmaxError(XmaxErrorCode.invalidConfiguration, \"A local video source is already active\");\n }\n\n const rotation = options.rotation ?? 0;\n validateCameraRotation(rotation);\n const version = ++this.captureVersion;\n let source: ICameraVideoTrack | undefined;\n try {\n const captureSize = rtcOrientedVideoSize(rotation === 90 || rotation === 270\n ? { width: options.height, height: options.width } : options);\n source = await this.sdk!.createCameraVideoTrack({\n facingMode: options.position === CameraPosition.front ? \"user\" : \"environment\",\n encoderConfig: { width: captureSize.width, height: captureSize.height, frameRate: options.frameRate },\n });\n if (this.client !== client || version !== this.captureVersion) {\n source.close();\n throw this.cancelled();\n }\n this.cameraSource = source;\n let camera: ILocalVideoTrack = source;\n if (rotation !== 0) {\n const processor = new CameraRotationProcessor({\n ...options, rotation,\n onError: error => this.listener?.onError?.(error),\n });\n this.cameraRotation = processor;\n const output = await processor.setSource(source.getMediaStreamTrack());\n if (this.client !== client || version !== this.captureVersion)\n throw this.cancelled();\n camera = this.sdk!.createCustomVideoTrack({\n mediaStreamTrack: output,\n width: options.width, height: options.height, frameRate: options.frameRate,\n });\n }\n this.camera = camera;\n this.cameraTransceiverListener = () => {\n // 初次发布和显式重进房由发布流程负责;这里只处理 SDK 自动重建 sender。\n if (this.client === client && client.localTracks.includes(camera) && this.videoPublishRequested && !this.rejoinTask) {\n void this.restoreCameraSender(camera).catch(() => {\n if (this.camera === camera && this.videoPublishRequested) {\n XmaxLogger.rtc.warning(() => \"Agora 发送参数恢复失败 (Agora Sender Parameters Restore Failed)\");\n }\n });\n }\n };\n camera.on(\"transceiver-updated\", this.cameraTransceiverListener);\n return camera.getMediaStreamTrack();\n } catch (error) {\n if (version === this.captureVersion)\n this.releaseCameraCapture();\n throw this.mapError(error, XmaxErrorCode.cameraPermissionDenied);\n }\n }\n\n /**\n * 更新未发布的摄像头采集规格,保留本地轨道及旋转输出。\n */\n async updateCameraCapture(\n options: RtcCameraCaptureOptions\n ): Promise<MediaStreamTrack> {\n const source = this.requireCamera();\n const camera = this.camera!;\n const client = this.requireClient();\n const version = this.captureVersion;\n if (this.connection || this.videoPublishRequested) {\n throw new XmaxError(XmaxErrorCode.invalidConfiguration, \"Disconnect Agora before updating camera capture\");\n }\n const rotation = options.rotation ?? 0;\n validateCameraRotation(rotation);\n if ((rotation !== 0) !== !!this.cameraRotation) {\n throw new XmaxError(XmaxErrorCode.invalidConfiguration, \"Camera format updates must preserve rotation\");\n }\n const ensureCurrent = () => {\n if (this.camera !== camera || this.cameraSource !== source || this.client !== client || version !== this.captureVersion) {\n throw this.cancelled();\n }\n };\n const size = rtcOrientedVideoSize(rotation === 90 || rotation === 270\n ? { width: options.height, height: options.width } : options);\n await this.run(() => source.setEncoderConfiguration({ width: size.width, height: size.height, frameRate: options.frameRate }));\n ensureCurrent();\n if (this.cameraRotation) {\n await this.cameraRotation.updateFormat(options.width, options.height, options.frameRate);\n ensureCurrent();\n }\n return camera.getMediaStreamTrack();\n }\n\n /**\n * 切换声网相机设备并返回切换后的原生轨道。\n */\n async switchCameraCapture(\n to: CameraPosition\n ): Promise<MediaStreamTrack> {\n const source = this.requireCamera();\n const camera = this.camera!;\n // setDevice 可能结束旧原始轨;切换期间保留最后一帧和同一输出轨。\n this.cameraRotation?.pauseSource();\n try {\n await this.run(() => source.setDevice({ facingMode: to === CameraPosition.front ? \"user\" : \"environment\" }));\n } catch (error) {\n if (this.camera === camera && this.cameraRotation) {\n await this.cameraRotation.setSource(source.getMediaStreamTrack()).catch(restoreError => {\n this.listener?.onError?.(XmaxError.from(restoreError));\n });\n }\n throw error;\n }\n if (this.camera !== camera || this.cameraSource !== source)\n throw this.cancelled();\n if (this.cameraRotation) {\n await this.cameraRotation.setSource(source.getMediaStreamTrack());\n if (this.camera !== camera)\n throw this.cancelled();\n }\n if (this.videoPublishRequested)\n await this.restoreCameraSender(camera);\n return camera.getMediaStreamTrack();\n }\n\n /**\n * 停止采集并释放摄像头。\n */\n async stopCameraCapture(): Promise<void> {\n this.releaseCameraCapture();\n }\n\n /**\n * 停止摄像头采集并清理旋转输出与发送器状态。\n */\n private releaseCameraCapture(): void {\n ++this.captureVersion;\n const camera = this.camera;\n this.releaseCameraSender();\n this.camera = undefined;\n camera?.close();\n this.cameraRotation?.stop();\n this.cameraRotation = undefined;\n if (this.cameraSource !== camera)\n this.cameraSource?.close();\n this.cameraSource = undefined;\n }\n\n /**\n * 将外部原生轨包装为 Agora 本地轨,不申请设备权限;失败时释放本次克隆。\n */\n async setExternalMediaTracks(\n tracks: {\n videoTrack: MediaStreamTrack;\n audioTrack?: MediaStreamTrack\n }\n ): Promise<void> {\n this.requireClient();\n if (this.camera || this.cameraSource || this.microphone || this.externalVideo) {\n throw new XmaxError(XmaxErrorCode.invalidConfiguration, \"Release the current local media before attaching external tracks\");\n }\n if (tracks.videoTrack.kind !== \"video\" || tracks.videoTrack.readyState !== \"live\" ||\n (tracks.audioTrack && (tracks.audioTrack.kind !== \"audio\" || tracks.audioTrack.readyState !== \"live\"))) {\n throw new XmaxError(XmaxErrorCode.invalidConfiguration, \"External media requires live video and optional audio tracks\");\n }\n\n const video = tracks.videoTrack.clone();\n let audio: MediaStreamTrack | undefined;\n let wrappedVideo: ILocalVideoTrack | undefined;\n let wrappedAudio: ILocalAudioTrack | undefined;\n try {\n audio = tracks.audioTrack?.clone();\n wrappedVideo = this.sdk!.createCustomVideoTrack({ mediaStreamTrack: video });\n if (audio) {\n wrappedAudio = this.sdk!.createCustomAudioTrack({ mediaStreamTrack: audio, encoderConfig: \"music_standard\" });\n }\n this.externalVideo = wrappedVideo;\n this.externalAudio = wrappedAudio;\n } catch (error) {\n wrappedVideo?.close();\n wrappedAudio?.close();\n video.stop();\n audio?.stop();\n throw this.mapError(error);\n }\n }\n\n /**\n * 摄像头实验走原生 sender(发布前暂存);外部视频仍走 Agora 编码配置。\n */\n async configureVideoEncoding(\n config: VideoEncodingConfiguration\n ): Promise<void> {\n const camera = this.requireLocalVideo();\n if (camera === this.camera) {\n const client = this.requireClient(), version = this.roomVersion;\n const published = this.videoPublishRequested;\n await this.run(() => this.queueSenderUpdate(async () => {\n const ensureActive = () => {\n if (this.camera !== camera || this.client !== client || this.roomVersion !== version ||\n (published && !this.videoPublishRequested))\n throw this.cancelled();\n };\n ensureActive();\n if (this.videoPublishRequested) {\n await AgoraVideoSender.apply(camera, this.cameraEncodingConfiguration(config), ensureActive);\n }\n // 发布前只有目标配置,sender 在 publish 返回后才可使用。\n this.cameraSenderConfiguration = { ...config };\n }));\n return;\n }\n const encodingSize = config;\n await this.run(async () => {\n await camera.setEncoderConfiguration({\n width: encodingSize.width,\n height: encodingSize.height,\n frameRate: config.frameRate,\n bitrateMin: config.minimumBitrate,\n bitrateMax: config.maximumBitrate,\n });\n if (this.appliedOptimizationModes.get(camera) !== config.encoderPreference) {\n await camera.setOptimizationMode(config.encoderPreference === RtcVideoEncoderPreference.maintainFramerate ? \"motion\" : \"detail\");\n this.appliedOptimizationModes.set(camera, config.encoderPreference);\n }\n });\n }\n\n /**\n * 串行应用摄像头发送参数,防止更新顺序交错。\n */\n private queueSenderUpdate(\n operation: () => Promise<void>\n ): Promise<void> {\n const next = this.senderUpdateTail.then(operation);\n this.senderUpdateTail = next.catch(() => { });\n return next;\n }\n\n /**\n * 发布/重建 sender 后恢复最近成功的目标配置;排队时不捕获过期的参数对象。\n */\n private restoreCameraSender(\n camera: ILocalVideoTrack\n ): Promise<void> {\n const client = this.client, version = this.roomVersion;\n return this.queueSenderUpdate(async () => {\n const ensureActive = () => {\n if (this.camera !== camera || this.client !== client || this.roomVersion !== version || !this.videoPublishRequested) {\n throw this.cancelled();\n }\n };\n ensureActive();\n const config = this.cameraSenderConfiguration;\n if (config)\n await AgoraVideoSender.apply(camera, this.cameraEncodingConfiguration(config), ensureActive);\n });\n }\n\n /**\n * 根据当前画面方向解析摄像头发送编码参数。\n */\n private cameraEncodingConfiguration(\n config: VideoEncodingConfiguration\n ): VideoEncodingConfiguration {\n // Canvas 已经输出目标方向,不能再套用移动端原生采集的宽高转置。\n return this.cameraRotation ? config : { ...config, ...rtcOrientedVideoSize(config) };\n }\n\n /**\n * 解除摄像头发送器变化监听并清空编码缓存。\n */\n private releaseCameraSender(): void {\n if (this.cameraTransceiverListener)\n this.camera?.off(\"transceiver-updated\", this.cameraTransceiverListener);\n this.cameraTransceiverListener = undefined;\n this.cameraSenderConfiguration = undefined;\n }\n\n /**\n * 使用会话下发的字符串频道与 UID 入房;不采信后端区域覆盖。\n */\n async joinRoom(\n configuration: RtcRoomJoinConfiguration\n ): Promise<void> {\n // 旧重进房操作完全结束后才允许新进房,避免迟到的 leave 影响新连接。\n await this.rejoinTask?.catch(() => { });\n const client = this.requireClient();\n if (configuration.provider !== RtcProvider.agora || this.connection) {\n throw new XmaxError(XmaxErrorCode.invalidConfiguration, \"Agora credentials and an idle room are required\");\n }\n const version = ++this.roomVersion;\n await this.run(() => client.join(configuration.appID, configuration.roomID, configuration.roomToken, configuration.userID));\n if (client !== this.client || version !== this.roomVersion) {\n await client.leave();\n throw this.cancelled();\n }\n\n this.connection = configuration;\n // 入房期间可能已经收到发布事件;从 SDK 当前状态补齐一次,避免遗漏现有机器人。\n for (const user of client.remoteUsers) {\n if (user.hasVideo)\n this.listener?.onRemoteVideoPublished(String(user.uid), true);\n }\n this.statisticsTimer = setInterval(() => {\n if (this.client !== client || !this.connection || this.tokenExpired || this.rejoinOperation)\n return;\n try {\n AgoraRtcStatistics.collect(client, this.listener);\n }\n catch {\n XmaxLogger.rtc.warning(() => \"Agora 统计采样失败 (Agora Statistics Failed)\");\n }\n }, 2000);\n }\n\n /**\n * 退房前取消排队消息、统计与音频播放;本地相机预览保持运行。\n */\n async leaveRoom(): Promise<void> {\n this.clearRoom();\n if (this.client)\n await this.run(() => this.client!.leave());\n }\n\n /**\n * 应用同一频道的新 Token;即将过期时续签,已经过期时重新进房并恢复媒体。\n */\n async updateCredentials(\n config: RtcRoomJoinConfiguration,\n signal?: AbortSignal\n ): Promise<void> {\n if (signal?.aborted)\n throw this.cancelled();\n const current = this.connection;\n const client = this.requireClient();\n if (!current || config.provider !== RtcProvider.agora || config.appID !== current.appID || config.roomID !== current.roomID || config.userID !== current.userID) {\n throw new XmaxError(XmaxErrorCode.sessionError, \"Agora session binding changed\");\n }\n const version = this.roomVersion;\n if (!this.tokenExpired) {\n try {\n await this.run(() => client.renewToken(config.roomToken));\n } catch (error) {\n // 续签过程中可能已跨过过期时间;此时升级为重进房,不再重复 renewToken。\n this.ensureRoomCurrent(client, version);\n if (!this.tokenExpired)\n throw error;\n }\n }\n this.ensureRoomCurrent(client, version);\n if (signal?.aborted)\n throw this.cancelled();\n\n if (this.tokenExpired)\n await this.rejoinRoom(client, config, signal);\n else\n this.connection = config;\n }\n\n /**\n * 串行恢复同一 RTC 房间,不创建业务会话或重新发送生成开始指令。\n */\n private async rejoinRoom(\n client: AgoraDataStreamClient,\n config: AgoraRoomJoinConfiguration,\n signal?: AbortSignal\n ): Promise<void> {\n if (this.rejoinOperation)\n return this.rejoinOperation;\n\n const version = ++this.roomVersion;\n const task = Promise.resolve().then(() => this.performRejoin(client, config, version));\n this.rejoinTask = task;\n void task.finally(() => {\n if (this.rejoinTask === task)\n this.rejoinTask = undefined;\n }).catch(() => { });\n\n // 取消立即释放业务等待;底层 SDK 的迟到 join 继续由 task 负责退房收尾。\n let cancel!: () => void;\n const operation = new Promise<void>((resolve, reject) => {\n cancel = () => {\n if (this.isRoomCurrent(client, version))\n this.roomVersion++;\n reject(this.cancelled());\n };\n this.cancelRejoin = cancel;\n signal?.addEventListener(\"abort\", cancel, { once: true });\n task.then(resolve, reject);\n if (signal?.aborted)\n cancel();\n });\n this.rejoinOperation = operation;\n try {\n await operation;\n } finally {\n signal?.removeEventListener(\"abort\", cancel);\n if (this.cancelRejoin === cancel)\n this.cancelRejoin = undefined;\n if (this.rejoinOperation === operation)\n this.rejoinOperation = undefined;\n }\n\n this.ensureRoomCurrent(client, version);\n for (const user of client.remoteUsers) {\n if (user.hasVideo)\n this.listener?.onRemoteVideoPublished(String(user.uid), true);\n }\n }\n\n /**\n * 使用新 Token 重建 RTC 连接,复用采集轨道并恢复远端音频订阅。\n */\n private async performRejoin(\n client: AgoraDataStreamClient,\n config: AgoraRoomJoinConfiguration,\n version: number\n ): Promise<void> {\n this.ensureRoomCurrent(client, version);\n for (const cancel of this.activeSends) {\n cancel();\n }\n this.listener?.onRoomRejoining?.();\n for (const user of client.remoteUsers) user.audioTrack?.stop();\n\n await this.run(() => client.leave());\n this.ensureRoomCurrent(client, version);\n await this.run(() => client.join(config.appID, config.roomID, config.roomToken, config.userID));\n if (!this.isRoomCurrent(client, version)) {\n // 退房或销毁期间迟到的 join 仍可能成功,必须释放刚建立的连接。\n await this.run(() => client.leave());\n throw this.cancelled();\n }\n\n const video = this.externalVideo ?? this.camera;\n const audio = this.externalVideo ? this.externalAudio : this.microphone;\n if (this.videoPublishRequested && video) {\n await this.run(() => client.publish(video));\n this.ensureRoomCurrent(client, version);\n if (video === this.camera)\n await this.restoreCameraSender(this.camera);\n }\n if (this.audioPublishRequested && audio) {\n await this.run(() => client.publish(audio));\n this.ensureRoomCurrent(client, version);\n }\n\n for (const user of client.remoteUsers) {\n if (!user.hasAudio || !this.requestedAudio.has(String(user.uid)))\n continue;\n const track = await this.run(() => client.subscribe(user, \"audio\"));\n if (!this.isRoomCurrent(client, version)) {\n track.stop();\n throw this.cancelled();\n }\n // 恢复期间用户仍可静音或停止生成,使用最新音量与订阅意图。\n this.applyAudioVolume(user);\n }\n\n this.connection = config;\n this.tokenExpired = false;\n }\n\n /**\n * 发布已采集的视频轨道。\n */\n async publishLocalVideo(): Promise<void> {\n const client = this.requireClient();\n const camera = this.requireLocalVideo();\n this.videoPublishRequested = true;\n await this.rejoinOperation;\n if (client !== this.client || !this.videoPublishRequested)\n throw this.cancelled();\n if (client.localTracks.includes(camera))\n return;\n await this.run(() => client.publish(camera));\n if (camera === this.camera)\n await this.run(() => this.restoreCameraSender(this.camera!));\n }\n\n /**\n * 停止视频发布但保留采集与预览。\n */\n async unpublishLocalVideo(): Promise<void> {\n this.videoPublishRequested = false;\n if (this.rejoinTask || this.tokenExpired)\n return;\n const video = this.externalVideo ?? this.camera;\n if (this.client && video) {\n await this.run(() => this.client!.unpublish(video));\n }\n }\n\n /**\n * 外部源只发布提供的音轨;相机源首次发布时申请麦克风,迟到采集立即释放。\n */\n async publishLocalAudio(): Promise<void> {\n const client = this.requireClient();\n this.audioPublishRequested = true;\n await this.rejoinOperation;\n const version = this.roomVersion;\n if (client !== this.client || !this.audioPublishRequested)\n throw this.cancelled();\n if (this.externalVideo) {\n const audio = this.externalAudio;\n if (audio && !client.localTracks.includes(audio)) {\n await this.run(() => client.publish(audio));\n }\n return;\n }\n try {\n if (!this.microphone) {\n const track = await this.sdk!.createMicrophoneAudioTrack();\n if (client !== this.client || version !== this.roomVersion) {\n track.close();\n throw this.cancelled();\n }\n this.microphone = track;\n }\n if (!client.localTracks.includes(this.microphone))\n await client.publish(this.microphone);\n } catch (error) {\n throw this.mapError(error, XmaxErrorCode.microphonePermissionDenied);\n }\n }\n\n /**\n * 停止音频发布,采集交给关闭流程释放。\n */\n async unpublishLocalAudio(): Promise<void> {\n this.audioPublishRequested = false;\n if (this.rejoinTask || this.tokenExpired)\n return;\n const audio = this.externalVideo ? this.externalAudio : this.microphone;\n if (this.client && audio) {\n await this.run(() => this.client!.unpublish(audio));\n }\n }\n\n /**\n * 订阅远端主视频并返回原生轨道,不创建额外播放视图。\n */\n async subscribeRemoteVideo(\n userID: string,\n subscribe: boolean\n ): Promise<MediaStreamTrack | undefined> {\n if (!subscribe && this.rejoinTask)\n return;\n await this.rejoinOperation;\n const client = this.requireClient();\n const user = client.remoteUsers.find(user => String(user.uid) === userID);\n if (!user && !subscribe)\n return;\n if (!user)\n throw new XmaxError(XmaxErrorCode.rtcError, \"Agora remote video user is unavailable\");\n if (!subscribe) {\n await this.run(() => client.unsubscribe(user, \"video\"));\n return;\n }\n\n const version = this.roomVersion;\n const track = await this.run(() => client.subscribe(user, \"video\"));\n if (client !== this.client || version !== this.roomVersion)\n throw this.cancelled();\n return track.getMediaStreamTrack();\n }\n\n /**\n * 保存音频订阅意图;音频稍后发布时自动订阅,播放前应用音量。\n */\n async subscribeRemoteAudio(\n userID: string,\n subscribe: boolean\n ): Promise<void> {\n const client = this.requireClient();\n const user = client.remoteUsers.find(user => String(user.uid) === userID);\n if (!subscribe) {\n this.requestedAudio.delete(userID);\n user?.audioTrack?.stop();\n if (user?.audioTrack && !this.rejoinTask)\n await this.run(() => client.unsubscribe(user, \"audio\"));\n return;\n }\n this.requestedAudio.add(userID);\n if (this.rejoinOperation) {\n const version = this.roomVersion;\n await this.rejoinOperation;\n this.ensureRoomCurrent(client, version);\n if (this.requestedAudio.has(userID))\n await this.subscribeRemoteAudio(userID, true);\n return;\n }\n if (!user?.hasAudio)\n return;\n\n const version = this.roomVersion;\n const track = await this.run(() => client.subscribe(user, \"audio\"));\n if (client !== this.client || version !== this.roomVersion || !this.requestedAudio.has(userID)) {\n track.stop();\n return;\n }\n this.applyAudioVolume(user);\n this.listener?.onRemoteAudioTrackChanged?.(userID);\n }\n\n /**\n * 缓存音量,订阅前可调用;零音量不调用 play,防止初始短暂漏声。\n */\n setRemoteAudioVolume(\n volume: number,\n userID: string\n ): void {\n this.audioVolumes.set(userID, Math.max(0, Math.min(100, Number.isFinite(volume) ? Math.round(volume) : 0)));\n const user = this.client?.remoteUsers.find(user => String(user.uid) === userID);\n if (this.rejoinTask || this.tokenExpired) {\n user?.audioTrack?.stop();\n return;\n }\n if (user)\n this.applyAudioVolume(user);\n }\n\n /**\n * 应用当前用户的播放状态,保留声网对浏览器自动播放限制的处理。\n */\n private applyAudioVolume(\n user: IAgoraRTCRemoteUser\n ): void {\n const track = user.audioTrack;\n if (!track)\n return;\n const volume = this.audioVolumes.get(String(user.uid)) ?? 0;\n track.setVolume(volume);\n if (volume > 0 && this.requestedAudio.has(String(user.uid)))\n track.play();\n else\n track.stop();\n }\n\n /**\n * 获取原始远端音轨,不依赖声网音频播放器是否处于播放状态。\n */\n getRemoteAudioTrack(\n userID: string\n ): MediaStreamTrack | undefined {\n const user = this.client?.remoteUsers.find(user => String(user.uid) === userID);\n return user?.hasAudio ? user.audioTrack?.getMediaStreamTrack() : undefined;\n }\n\n /**\n * 控制消息按序等待结果;快照只保留最新待发值,不逐包等待网关响应。\n */\n async sendRoomMessage(\n message: string,\n delivery: RtcRoomMessageDelivery = \"ordered\",\n ): Promise<void> {\n const version = this.roomVersion;\n const client = this.client;\n const bytes = new TextEncoder().encode(message).byteLength;\n if (!client || !this.connection || (this.tokenExpired && !this.rejoinOperation)) {\n throw this.cancelled();\n }\n if (bytes > 1000) {\n throw new XmaxError(XmaxErrorCode.invalidConfiguration, \"Room message exceeds the 1000-byte limit\");\n }\n\n const pending: PendingRoomMessage = { client, version, message };\n if (delivery === \"latest\") {\n this.latestMessage = pending;\n this.scheduleRoomMessages();\n return;\n }\n\n // 任务控制构成屏障;普通有序消息(例如心跳)不丢弃最后一个交互位置。\n if (delivery === \"barrier\") {\n this.latestMessage = undefined;\n }\n return new Promise<void>((resolve, reject) => {\n pending.completion = { resolve, reject };\n this.orderedMessages.push(pending);\n this.scheduleRoomMessages();\n });\n }\n\n /**\n * 每个房间只运行一个消息调度循环,保持控制顺序并合并尚未发出的快照。\n */\n private scheduleRoomMessages(): void {\n if (this.messageDrain || (!this.orderedMessages.length && !this.latestMessage)) {\n return;\n }\n const drain = {};\n this.messageDrain = drain;\n void this.drainRoomMessages(drain);\n }\n\n /**\n * 控制消息串行确认;快照不等待上一包响应,并限制在途请求数。\n */\n private async drainRoomMessages(\n drain: object,\n ): Promise<void> {\n try {\n while (this.messageDrain === drain) {\n const pending = this.orderedMessages[0] ?? this.latestMessage;\n if (!pending || (!pending.completion && this.activeSends.size >= 30)) {\n return;\n }\n\n try {\n await this.rejoinOperation;\n if (this.messageDrain !== drain) {\n return;\n }\n // 恢复等待期间可以到达更新的快照或控制消息,不发送旧位置。\n if (pending !== (this.orderedMessages[0] ?? this.latestMessage)) {\n continue;\n }\n this.ensureRoomCurrent(pending.client, pending.version);\n if (this.tokenExpired) {\n throw this.cancelled();\n }\n\n if (pending.completion) {\n this.orderedMessages.shift();\n } else {\n this.latestMessage = undefined;\n }\n const sending = this.sendWithTimeout(pending.client, pending.message);\n if (pending.completion) {\n await sending;\n pending.completion.resolve();\n } else {\n void sending.catch(error => this.reportLatestMessageFailure(pending, error)).finally(() => this.scheduleRoomMessages());\n }\n } catch (error) {\n if (this.orderedMessages[0] === pending) {\n this.orderedMessages.shift();\n }\n if (this.latestMessage === pending) {\n this.latestMessage = undefined;\n }\n if (pending.completion) {\n pending.completion.reject(this.mapError(error));\n } else {\n this.reportLatestMessageFailure(pending, error);\n }\n }\n }\n } finally {\n if (this.messageDrain === drain) {\n this.messageDrain = undefined;\n }\n }\n }\n\n /**\n * 快照失败不终止生成;退房和恢复期间取消的旧请求不再输出警告。\n */\n private reportLatestMessageFailure(\n pending: PendingRoomMessage,\n error: unknown,\n ): void {\n if (this.isRoomCurrent(pending.client, pending.version) && this.mapError(error).code !== XmaxErrorCode.cancelled) {\n XmaxLogger.rtc.warning(() => \"发送实时房间快照失败,已丢弃当前采样帧 (Latest Room Message Dropped)\");\n }\n }\n\n /**\n * 发送超过五秒视为失败;退房立即取消等待,底层迟到结果不影响新房间。\n */\n private async sendWithTimeout(\n client: AgoraDataStreamClient,\n message: string\n ): Promise<void> {\n await new Promise<void>((resolve, reject) => {\n let settled = false;\n const finish = (error?: unknown) => {\n if (settled) {\n return;\n }\n settled = true;\n clearTimeout(timer);\n this.activeSends.delete(cancel);\n if (error)\n reject(this.mapError(error));\n else\n resolve();\n };\n const cancel = () => finish(this.cancelled());\n const timer = setTimeout(() => finish(new XmaxError(XmaxErrorCode.timeout, \"Agora room message send timed out\")), 5000);\n this.activeSends.add(cancel);\n\n // run 同时接住同步异常和异步拒绝;即使取消或超时,底层结果仍有处理器。\n void this.run(() => client.sendStreamMessage(message, false)).then(() => finish(), finish);\n });\n }\n\n /**\n * 设置公共 RTC 监听器。\n */\n setEventListener(\n listener?: RtcEventListener\n ): void {\n this.listener = listener;\n }\n\n /**\n * 将声网发布、DataStream、网络质量与 Token 事件桥接到公共传输层。\n */\n private registerEvents(\n client: AgoraDataStreamClient\n ): void {\n const active = () => this.client === client && this.connection !== undefined && !this.rejoinTask;\n const mediaActive = () => active() && !this.tokenExpired;\n const handleExpired = () => {\n if (!active() || this.tokenExpired)\n return;\n this.tokenExpired = true;\n this.listener?.onNetworkStatistics?.(undefined);\n this.listener?.onTokenExpired?.();\n };\n client.on(\"user-published\", (user, kind) => {\n if (!mediaActive())\n return;\n if (kind === \"video\")\n this.listener?.onRemoteVideoPublished(String(user.uid), true);\n if (kind === \"audio\" && this.requestedAudio.has(String(user.uid))) {\n void this.subscribeRemoteAudio(String(user.uid), true).catch(error => {\n if (active())\n this.listener?.onError?.(this.mapError(error));\n });\n }\n });\n client.on(\"user-unpublished\", (user, kind) => {\n if (!mediaActive())\n return;\n if (kind === \"video\")\n this.listener?.onRemoteVideoPublished(String(user.uid), false);\n if (kind === \"audio\") {\n user.audioTrack?.stop();\n this.listener?.onRemoteAudioTrackChanged?.(String(user.uid));\n }\n });\n client.on(\"user-left\", user => {\n if (mediaActive())\n this.listener?.onRemoteVideoPublished(String(user.uid), false);\n });\n client.on(\"stream-message\", (uid, data) => {\n if (mediaActive())\n this.listener?.onCustomMessageReceived(String(uid), typeof data === \"string\" ? data : new TextDecoder().decode(data));\n });\n client.on(\"network-quality\", quality => {\n if (mediaActive() && !this.networkQualitySuspended)\n AgoraRtcStatistics.network(quality, this.listener);\n });\n client.on(\"token-privilege-will-expire\", () => {\n if (active() && !this.tokenExpired)\n this.listener?.onTokenWillExpire?.();\n });\n client.on(\"token-privilege-did-expire\", handleExpired);\n client.on(\"connection-state-change\", (state, previous, reason) => {\n if (this.client === client)\n this.networkQualitySuspended = state !== \"CONNECTED\";\n if (active() && state !== \"CONNECTED\")\n this.listener?.onNetworkStatistics?.(undefined);\n // SDK 可能先报告因 Token 过期断开,再发 did-expire;合并成同一次恢复请求。\n if (state === \"DISCONNECTED\" && reason === \"TOKEN_EXPIRE\") {\n handleExpired();\n return;\n }\n if (active() && !this.tokenExpired && state === \"DISCONNECTED\" && previous !== \"DISCONNECTED\") {\n this.listener?.onError?.(new XmaxError(XmaxErrorCode.rtcError, `Agora connection disconnected: ${reason ?? \"unknown\"}`));\n }\n });\n }\n\n /**\n * 清理房间状态、播放和发送队列,不释放本地视频采集。\n */\n private clearRoom(): void {\n this.roomVersion++;\n this.cancelRejoin?.();\n this.connection = undefined;\n this.tokenExpired = false;\n this.networkQualitySuspended = false;\n this.videoPublishRequested = false;\n this.audioPublishRequested = false;\n clearInterval(this.statisticsTimer);\n this.statisticsTimer = undefined;\n this.messageDrain = undefined;\n this.latestMessage = undefined;\n for (const pending of this.orderedMessages.splice(0)) {\n pending.completion?.reject(this.cancelled());\n }\n for (const cancel of this.activeSends) {\n cancel();\n }\n this.requestedAudio.clear();\n this.audioVolumes.clear();\n for (const user of this.client?.remoteUsers ?? []) user.audioTrack?.stop();\n }\n\n /**\n * 判断异步恢复工作是否仍属于当前房间生命周期。\n */\n private isRoomCurrent(\n client: AgoraDataStreamClient,\n version: number\n ): boolean {\n return this.client === client && this.connection !== undefined && this.roomVersion === version;\n }\n\n /**\n * 退房、销毁或换房后停止后续恢复步骤。\n */\n private ensureRoomCurrent(\n client: AgoraDataStreamClient,\n version: number\n ): void {\n if (!this.isRoomCurrent(client, version))\n throw this.cancelled();\n }\n\n /**\n * 获取已初始化的 client。\n */\n private requireClient(): AgoraDataStreamClient {\n if (!this.client)\n throw new XmaxError(XmaxErrorCode.rtcError, \"Agora is not initialized\");\n return this.client;\n }\n\n /**\n * 获取已启动的相机采集。\n */\n private requireCamera(): ICameraVideoTrack {\n if (!this.cameraSource || !this.camera)\n throw new XmaxError(XmaxErrorCode.invalidConfiguration, \"Camera capture is not running\");\n return this.cameraSource;\n }\n\n /**\n * 获取当前可编码、发布的视频轨,不要求来源为摄像头。\n */\n private requireLocalVideo(): ILocalVideoTrack {\n const video = this.externalVideo ?? this.camera;\n if (!video) {\n throw new XmaxError(XmaxErrorCode.invalidConfiguration, \"Local video source is not prepared\");\n }\n return video;\n }\n\n /**\n * 执行声网操作并统一错误类型。\n */\n private async run<T>(\n operation: () => Promise<T>\n ): Promise<T> {\n try {\n return await operation();\n }\n catch (error) {\n throw this.mapError(error);\n }\n }\n\n /**\n * 只传递错误码,不回显可能包含 Token 的厂商原始错误消息。\n */\n private mapError(\n error: unknown,\n permissionCode?: XmaxErrorCode\n ): XmaxError {\n if (error instanceof XmaxError)\n return error;\n const code = typeof error === \"object\" && error !== null && \"code\" in error ? String(error.code) : \"UNKNOWN\";\n const safeCode = /^[A-Z_0-9-]+$/.test(code) ? code : \"UNKNOWN\";\n return new XmaxError(permissionCode && safeCode === \"PERMISSION_DENIED\" ? permissionCode : XmaxErrorCode.rtcError, `Agora operation failed (${safeCode})`);\n }\n\n /**\n * 构造异步操作失效错误。\n */\n private cancelled(): XmaxError {\n return new XmaxError(XmaxErrorCode.cancelled, \"Agora operation was cancelled\");\n }\n}\n","//\n// RtcEngineManager.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\nimport type TRTC from \"trtc-sdk-v5\";\nimport { XmaxError, XmaxErrorCode } from \"../../Errors/XmaxError\";\n\n/**\n * TRTC 引擎实例类型。\n */\nexport type RtcEngine = TRTC;\n\n/**\n * 表示对共享 TRTC Engine 的独占使用权。\n */\nexport class RtcEngineLease {\n\n /**\n * 租约持有的 TRTC 引擎实例。\n */\n readonly engine: RtcEngine;\n\n /**\n * 租约标识。\n */\n readonly id: string;\n\n /**\n * 为已创建的引擎生成独占租约标识,供引擎管理器核对释放请求。\n */\n constructor(\n engine: RtcEngine\n ) {\n this.engine = engine;\n this.id = `lease-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;\n }\n}\n\n/**\n * 等待独占租用 TRTC 引擎的请求。\n */\ninterface EngineRequest {\n\n /**\n * 当前引擎租用请求的唯一标识。\n */\n id: string;\n\n /**\n * 结束当前等待并交付结果。\n */\n resolve: (\n lease: RtcEngineLease\n ) => void;\n\n /**\n * 结束当前等待并传递错误。\n */\n reject: (\n error: unknown\n ) => void;\n}\n\n/**\n * 管理共享 TRTC Engine 的独占创建、排队租用和销毁。\n *\n * `TRTC.create()` 不需要任何凭证(sdkAppId 在进房时才使用),\n * 因此引擎可以在本地预览阶段提前创建,不依赖会话接口返回。\n *\n * TRTC SDK 在模块加载时会访问浏览器环境,这里通过动态导入\n * 延迟到首次创建引擎时才加载,保证 SDK 包本身可以在非浏览器\n * 环境(如 SSR、测试)中正常导入。\n */\nexport class RtcEngineManager {\n\n /**\n * 共享实例。\n */\n static readonly shared = new RtcEngineManager();\n\n /**\n * 依赖\n */\n private readonly makeEngine: () => Promise<RtcEngine>;\n\n /**\n * RTC 资源\n */\n private activeLease?: RtcEngineLease;\n\n /**\n * 运行状态\n */\n private requests: EngineRequest[] = [];\n\n /**\n * 创建引擎管理器。\n */\n constructor(\n makeEngine: () => Promise<RtcEngine> = RtcEngineManager.defaultMakeEngine,\n ) {\n this.makeEngine = makeEngine;\n }\n\n /**\n * 等待并获取一份独占 RTC Engine 租约。\n *\n * @returns 独占引擎租约;使用完毕必须调用 `release`。\n */\n async acquire(): Promise<RtcEngineLease> {\n if (!this.activeLease && this.requests.length === 0) {\n const lease = await this.createLease();\n this.activeLease = lease;\n return lease;\n }\n return new Promise<RtcEngineLease>((resolve, reject) => {\n this.requests.push({\n id: `req-${Date.now()}-${this.requests.length}`,\n resolve,\n reject,\n });\n });\n }\n\n /**\n * 释放有效租约、销毁 Engine,并把独占权交给下一个等待者。\n */\n release(\n lease: RtcEngineLease\n ): void {\n if (this.activeLease?.id !== lease.id) {\n return;\n }\n this.activeLease = undefined;\n try {\n lease.engine.destroy();\n } catch {\n // 引擎销毁失败不影响租约释放(如退房前调用 destroy)。\n }\n void this.fulfillNextRequest();\n }\n\n /**\n * 动态加载 TRTC SDK 并创建引擎实例。\n */\n private static async defaultMakeEngine(): Promise<RtcEngine> {\n const module = await import(\"trtc-sdk-v5\");\n // NONE:关闭 TRTC 自身的 console 输出;保留默认的诊断日志上传。\n // 在 create 之前设置,避免实例创建和进房日志淹没 XmaxSDK 日志。\n module.default.setLogLevel(5);\n return module.default.create();\n }\n\n /**\n * 创建新引擎租约。\n */\n private async createLease(): Promise<RtcEngineLease> {\n const engine = await this.makeEngine();\n if (!engine) {\n throw new XmaxError(\n XmaxErrorCode.rtcError,\n \"Failed to create RTC Engine\",\n );\n }\n return new RtcEngineLease(engine);\n }\n\n /**\n * 满足下一个排队请求;创建失败时拒绝该请求并继续处理后续请求。\n */\n private async fulfillNextRequest(): Promise<void> {\n while (!this.activeLease && this.requests.length > 0) {\n const request = this.requests.shift();\n if (!request) {\n return;\n }\n try {\n const lease = await this.createLease();\n this.activeLease = lease;\n request.resolve(lease);\n } catch (error) {\n request.reject(error);\n }\n }\n }\n}\n","//\n// TrtcAudioVolumeSupport.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\n/**\n * TRTC 远端音量控制能力检测的环境参数;缺省时读取浏览器全局对象(可注入,测试用)。\n */\nexport interface TrtcAudioVolumeEnvironment {\n\n /**\n * 浏览器报告的用户代理字符串。\n */\n userAgent?: string;\n\n /**\n * 设备支持的最大同时触点数。\n */\n maxTouchPoints?: number;\n}\n\n/**\n * 判断当前平台是否支持 TRTC 的 `setRemoteAudioVolume` 远端音量控制。\n *\n * iOS Safari 与 WKWebView 不允许 JS 修改媒体元素音量\n * (`HTMLMediaElement.volume` 只读),TRTC 官方亦标注该接口不支持\n * iOS Safari;桌面模式 UA 的 iPad 通过触控点数识别。\n */\nexport function canControlTrtcRemoteAudioVolume(\n environment?: TrtcAudioVolumeEnvironment,\n): boolean {\n const userAgent =\n environment?.userAgent ??\n (typeof navigator === \"undefined\" ? \"\" : navigator.userAgent);\n const maxTouchPoints =\n environment?.maxTouchPoints ??\n (typeof navigator === \"undefined\" ? 0 : navigator.maxTouchPoints ?? 0);\n\n const ios =\n /iPhone|iPad|iPod/i.test(userAgent) ||\n (/Macintosh/i.test(userAgent) && maxTouchPoints > 1);\n return !ios;\n}\n","//\n// TrtcRtcManager.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\nimport { RtcProvider } from \"../RtcProvider\";\nimport { XmaxError, XmaxErrorCode } from \"../../Errors/XmaxError\";\nimport type { NetworkQuality, TRTCStatistics } from \"trtc-sdk-v5\";\nimport { XmaxLogger } from \"../../Logging/XmaxLogger\";\nimport { CameraPosition } from \"../../Media/Camera/CameraPosition\";\nimport {\n RtcEngineManager,\n type RtcEngine,\n type RtcEngineLease,\n} from \"./RtcEngineManager\";\nimport type { RtcEventListener } from \"../RtcEventListener\";\nimport { networkQualityLevel } from \"../NetworkStatistics\";\nimport type { RtcRoomJoinConfiguration } from \"../RoomJoinConfiguration\";\nimport type { RtcCameraCaptureOptions, RtcManaging } from \"../RtcManaging\";\nimport { RtcStatsLogger } from \"../RtcStatsLogger\";\nimport type { UplinkVideoStatistics, DownlinkVideoStatistics } from \"../VideoStatistics\";\nimport { rtcOrientedVideoSize } from \"../RtcVideoOrientation\";\nimport { canControlTrtcRemoteAudioVolume } from \"./TrtcAudioVolumeSupport\";\nimport {\n RtcVideoEncoderPreference,\n type VideoEncodingConfiguration,\n} from \"../VideoEncodingConfiguration\";\n\n/**\n * TRTC 事件名(字符串字面量,避免在非浏览器环境引用 TRTC 运行时常量)。\n */\nconst RTC_EVENT = {\n remoteVideoAvailable: \"remote-video-available\",\n remoteVideoUnavailable: \"remote-video-unavailable\",\n remoteAudioAvailable: \"remote-audio-available\",\n remoteAudioUnavailable: \"remote-audio-unavailable\",\n customMessage: \"custom-message\",\n statistics: \"statistics\",\n networkQuality: \"network-quality\",\n} as const;\n\n/**\n * TRTC 主流类型标识。\n */\nconst STREAM_TYPE_MAIN = \"main\";\n\n/**\n * 房间自定义消息使用的 cmdId。\n */\nconst CUSTOM_MESSAGE_CMD_ID = 1;\n\n/**\n * 房间自定义消息编码后的字节上限。\n */\nconst ROOM_MESSAGE_MAX_BYTES = 1000;\n\n/**\n * 采集启动时的占位码率(kbps)。\n *\n * 采集阶段不发布,该值不会生效到发送端;发布前由编码控制器按\n * 参考表计算的码率覆盖。\n */\nconst CAPTURE_PLACEHOLDER_BITRATE = 1000;\n\n/**\n * 编码策略偏好到 TRTC 弱网偏好的映射。\n */\nconst QOS_PREFERENCE_MAP: Record<RtcVideoEncoderPreference, \"smooth\" | \"clear\"> = {\n [RtcVideoEncoderPreference.maintainFramerate]: \"smooth\",\n [RtcVideoEncoderPreference.maintainQuality]: \"clear\",\n};\n\n/**\n * 引擎事件订阅需要的最小接口,用于隔离 TRTC 事件枚举类型。\n */\ninterface RtcEventSource {\n\n /**\n * 为指定 RTC 事件注册回调。\n */\n on(\n event: string,\n handler: (\n event: never\n ) => void\n ): void;\n\n /**\n * 移除指定 RTC 事件的对应回调。\n */\n off(\n event: string,\n handler: (\n event: never\n ) => void\n ): void;\n}\n\n/**\n * TRTC 远端视频事件的负载。\n */\ninterface RtcRemoteVideoEvent {\n\n /**\n * RTC 事件对应的用户标识。\n */\n userId: string;\n\n /**\n * TRTC 视频流类型,用于区分主流与辅流。\n */\n streamType: string;\n}\n\n/**\n * TRTC 自定义消息事件的负载。\n */\ninterface RtcCustomMessageEvent {\n\n /**\n * RTC 事件对应的用户标识。\n */\n userId: string;\n\n /**\n * TRTC 自定义消息的命令标识。\n */\n cmdId: number;\n\n /**\n * RTC 自定义消息的原始内容。\n */\n data: ArrayBuffer;\n}\n\n/**\n * 基于 TRTC 的 RTC 能力管理器。\n *\n * 采集由 TRTC 内部完成:`startLocalVideo({ publish: false })` 只采不发,\n * 浏览器兼容性差异交由 TRTC 适配;视频轨通过 `getVideoTrack()` 取出后\n * 交给 SDK 自己的渲染层预览。房间信令走自定义消息通道(`cmdId = 1`)。\n */\nexport class TrtcRtcManager implements RtcManaging {\n\n /**\n * 依赖\n */\n private readonly engineManager: RtcEngineManager;\n\n /**\n * RTC 资源\n */\n private lease?: RtcEngineLease;\n\n /**\n * 采集状态\n */\n private isCapturing = false;\n\n /**\n * 是否已启动本地音频采集。\n */\n private isAudioCapturing = false;\n\n /**\n * 外部媒体的克隆轨;原始轨道由媒体源管理。\n */\n private externalVideo?: MediaStreamTrack;\n\n /**\n * 由媒体源提供、适配器持有的外部音频克隆轨。\n */\n private externalAudio?: MediaStreamTrack;\n\n /**\n * 房间状态\n */\n private isInRoom = false;\n\n /**\n * 事件监听\n */\n private eventListener?: RtcEventListener;\n\n /**\n * 解除 RTC 统计事件监听。\n */\n private removeStatsListeners?: () => void;\n\n /**\n * 创建 RTC 管理器。\n */\n constructor(\n engineManager: RtcEngineManager = RtcEngineManager.shared\n ) {\n this.engineManager = engineManager;\n }\n\n /**\n * RTC 引擎是否已初始化。\n */\n get isInitialized(): boolean {\n return this.lease !== undefined;\n }\n\n /**\n * 初始化 RTC 引擎(获取独占租约)并注册事件桥接;已初始化时重复调用不产生效果。\n */\n async initialize(): Promise<void> {\n if (this.lease) {\n return;\n }\n const lease = await this.engineManager.acquire();\n this.registerEventBridge(lease.engine);\n this.lease = lease;\n }\n\n /**\n * 停止采集与发布、销毁 RTC 引擎并释放租约;未初始化时重复调用不产生效果。\n */\n async destroy(): Promise<void> {\n const lease = this.lease;\n this.lease = undefined;\n const externalVideo = this.externalVideo;\n const externalAudio = this.externalAudio;\n this.externalVideo = undefined;\n this.externalAudio = undefined;\n this.isCapturing = false;\n this.isAudioCapturing = false;\n this.isInRoom = false;\n\n this.removeStatsListeners?.();\n this.removeStatsListeners = undefined;\n\n if (!lease) {\n externalVideo?.stop();\n externalAudio?.stop();\n return;\n }\n\n try {\n await lease.engine.stopLocalAudio();\n } catch {\n // 引擎销毁前停止音频失败不影响租约释放。\n }\n\n try {\n await lease.engine.stopLocalVideo();\n } catch {\n // 引擎销毁前停止采集失败不影响租约释放。\n }\n\n externalVideo?.stop();\n externalAudio?.stop();\n this.engineManager.release(lease);\n }\n\n /**\n * 准备外部音视频轨但不发布;失败时释放克隆轨,不影响原始媒体源。\n */\n async setExternalMediaTracks(\n tracks: {\n videoTrack: MediaStreamTrack;\n audioTrack?: MediaStreamTrack\n }\n ): Promise<void> {\n const engine = this.requireEngine();\n if (this.isCapturing || this.isAudioCapturing || this.externalVideo) {\n throw new XmaxError(XmaxErrorCode.invalidConfiguration, \"Release the current local media before attaching external tracks\");\n }\n if (tracks.videoTrack.kind !== \"video\" || tracks.videoTrack.readyState !== \"live\" ||\n (tracks.audioTrack && (tracks.audioTrack.kind !== \"audio\" || tracks.audioTrack.readyState !== \"live\"))) {\n throw new XmaxError(XmaxErrorCode.invalidConfiguration, \"External media tracks must be live video/audio tracks\");\n }\n\n const video = tracks.videoTrack.clone();\n this.externalVideo = video;\n let audio: MediaStreamTrack | undefined;\n try {\n audio = tracks.audioTrack?.clone();\n this.externalAudio = audio;\n await engine.startLocalVideo({ publish: false, option: { videoTrack: video } });\n if (this.lease?.engine !== engine) {\n throw new XmaxError(XmaxErrorCode.cancelled, \"External media preparation was cancelled\");\n }\n if (audio) {\n await engine.startLocalAudio({\n publish: false,\n option: { audioTrack: audio, echoCancellation: false, autoGainControl: false, noiseSuppression: false },\n });\n if (this.lease?.engine !== engine) {\n throw new XmaxError(XmaxErrorCode.cancelled, \"External media preparation was cancelled\");\n }\n }\n } catch (error) {\n if (this.lease?.engine === engine) {\n await engine.stopLocalAudio().catch(() => { });\n await engine.stopLocalVideo().catch(() => { });\n }\n if (this.externalVideo === video) {\n this.externalVideo = undefined;\n this.externalAudio = undefined;\n }\n video.stop();\n audio?.stop();\n throw this.mapError(error);\n }\n }\n\n /**\n * 更新未连接的摄像头采集尺寸与帧率,保留当前设备和采集生命周期。\n */\n async updateCameraCapture(\n options: RtcCameraCaptureOptions\n ): Promise<MediaStreamTrack> {\n const engine = this.requireEngine();\n if (!this.isCapturing || this.externalVideo || this.isInRoom) {\n throw new XmaxError(XmaxErrorCode.invalidConfiguration, \"A disconnected camera preview is required\");\n }\n const size = rtcOrientedVideoSize(options);\n try {\n await engine.updateLocalVideo({\n option: {\n profile: {\n width: size.width, height: size.height, frameRate: options.frameRate, bitrate: CAPTURE_PLACEHOLDER_BITRATE,\n }\n }\n });\n if (this.lease?.engine !== engine || !this.isCapturing) {\n throw new XmaxError(XmaxErrorCode.cancelled, \"Camera capture stopped during format update\");\n }\n const track = engine.getVideoTrack();\n if (!track) {\n throw new XmaxError(XmaxErrorCode.mediaError, \"RTC engine did not produce a camera video track\");\n }\n return track;\n } catch (error) {\n throw this.mapError(error);\n }\n }\n\n /**\n * 启动摄像头采集(不发布)。\n *\n * @returns 采集到的视频轨。\n * @throws 引擎未初始化、已在采集、设备不可用或权限被拒绝时抛出错误。\n */\n async startCameraCapture(\n options: RtcCameraCaptureOptions,\n ): Promise<MediaStreamTrack> {\n if (options.rotation !== undefined && options.rotation !== 0) {\n throw new XmaxError(XmaxErrorCode.invalidConfiguration, \"Camera rotation currently requires Agora\");\n }\n const engine = this.requireEngine();\n if (this.isCapturing || this.externalVideo) {\n throw new XmaxError(\n XmaxErrorCode.invalidConfiguration,\n \"Camera capture is already running\",\n );\n }\n try {\n // 移动端竖屏时 TRTC 会转置输出宽高,这里反向转置使上行方向与请求一致。\n const captureSize = rtcOrientedVideoSize({\n width: options.width,\n height: options.height,\n });\n await engine.startLocalVideo({\n publish: false,\n option: {\n useFrontCamera: options.position === CameraPosition.front,\n profile: {\n width: captureSize.width,\n height: captureSize.height,\n frameRate: options.frameRate,\n bitrate: CAPTURE_PLACEHOLDER_BITRATE,\n },\n },\n });\n const track = engine.getVideoTrack();\n if (!track) {\n throw new XmaxError(\n XmaxErrorCode.mediaError,\n \"RTC engine did not produce a camera video track\",\n );\n }\n this.isCapturing = true;\n return track;\n } catch (error) {\n throw this.mapError(error);\n }\n }\n\n /**\n * 在前置和后置摄像头之间切换。\n *\n * @returns 切换后的视频轨。\n * @throws 引擎未初始化、采集未启动或设备切换失败时抛出错误。\n */\n async switchCameraCapture(\n to: CameraPosition\n ): Promise<MediaStreamTrack> {\n const engine = this.requireEngine();\n if (!this.isCapturing) {\n throw new XmaxError(\n XmaxErrorCode.invalidConfiguration,\n \"Camera capture is not running\",\n );\n }\n try {\n await engine.updateLocalVideo({\n option: { useFrontCamera: to === CameraPosition.front },\n });\n const track = engine.getVideoTrack();\n if (!track) {\n throw new XmaxError(\n XmaxErrorCode.mediaError,\n \"RTC engine did not produce a camera video track after switching\",\n );\n }\n return track;\n } catch (error) {\n throw this.mapError(error);\n }\n }\n\n /**\n * 停止摄像头采集;引擎未初始化或采集未启动时不产生效果。\n */\n async stopCameraCapture(): Promise<void> {\n if (this.externalVideo) {\n return;\n }\n const engine = this.lease?.engine;\n this.isCapturing = false;\n if (!engine) {\n return;\n }\n try {\n await engine.stopLocalVideo();\n } catch (error) {\n throw this.mapError(error);\n }\n }\n\n /**\n * 加入 RTC 房间。\n *\n * @throws 引擎未初始化、进房参数无效或进房失败时抛出错误。\n */\n async joinRoom(\n configuration: RtcRoomJoinConfiguration\n ): Promise<void> {\n if (configuration.provider !== RtcProvider.trtc) {\n throw new XmaxError(XmaxErrorCode.invalidConfiguration, \"TRTC credentials required\");\n }\n const engine = this.requireEngine();\n const sdkAppId = Number(configuration.sdkAppID);\n if (!Number.isInteger(sdkAppId) || sdkAppId <= 0) {\n throw new XmaxError(\n XmaxErrorCode.invalidConfiguration,\n \"RTC sdkAppID is invalid\",\n );\n }\n if (this.isInRoom) {\n throw new XmaxError(\n XmaxErrorCode.invalidConfiguration,\n \"Already in an RTC room\",\n );\n }\n\n try {\n await engine.enterRoom({\n sdkAppId,\n userId: configuration.userID,\n userSig: configuration.userSig,\n strRoomId: configuration.roomID,\n privateMapKey: configuration.privateMapKey,\n // 由传输层先设置音量再显式订阅,避免首帧前按 TRTC 默认音量播放。\n autoReceiveAudio: false,\n });\n this.isInRoom = true;\n } catch (error) {\n throw this.mapError(error);\n }\n }\n\n /**\n * 离开当前 RTC 房间;未在房间中或引擎未初始化时不产生效果,退房失败仅记录日志。\n */\n async leaveRoom(): Promise<void> {\n const engine = this.lease?.engine;\n if (!engine || !this.isInRoom) {\n return;\n }\n\n this.isInRoom = false;\n try {\n await engine.exitRoom();\n } catch (error) {\n XmaxLogger.rtc.warning(\n () =>\n `退出 RTC 房间失败 (Failed to Leave RTC Room)\\n` +\n `└─ ${XmaxLogger.localized(\"原因:\", \"Reason: \")}${XmaxError.from(error).message}`,\n );\n }\n }\n\n /**\n * 配置本地视频编码参数。\n *\n * TRTC 只接受单一目标码率,以 `minimumBitrate`(流畅优先档)作为目标码率;\n * 编码策略偏好映射为 TRTC 弱网偏好。\n *\n * @throws 本地视频未准备或编码参数配置失败时抛出错误。\n */\n async configureVideoEncoding(\n configuration: VideoEncodingConfiguration,\n ): Promise<void> {\n const engine = this.requireEngine();\n if (!this.isCapturing && !this.externalVideo) {\n throw new XmaxError(\n XmaxErrorCode.invalidConfiguration,\n \"Local video is not prepared\",\n );\n }\n try {\n // 外部画布方向已确定;只有摄像头需要移动端反向转置。\n const encodingSize = this.externalVideo ? configuration : rtcOrientedVideoSize({\n width: configuration.width,\n height: configuration.height,\n });\n await engine.updateLocalVideo({\n option: {\n profile: {\n width: encodingSize.width,\n height: encodingSize.height,\n frameRate: configuration.frameRate,\n bitrate: configuration.minimumBitrate,\n },\n qosPreference: QOS_PREFERENCE_MAP[configuration.encoderPreference],\n },\n });\n } catch (error) {\n throw this.mapError(error);\n }\n }\n\n /**\n * 发布本地视频流。\n *\n * @throws 本地视频未准备或发布失败时抛出错误。\n */\n async publishLocalVideo(): Promise<void> {\n const engine = this.requireEngine();\n if (!this.isCapturing && !this.externalVideo) {\n throw new XmaxError(\n XmaxErrorCode.invalidConfiguration,\n \"Local video is not prepared\",\n );\n }\n try {\n await engine.updateLocalVideo({ publish: true });\n } catch (error) {\n throw this.mapError(error);\n }\n }\n\n /**\n * 停止发布本地视频流;采集保持运行,本地预览不受影响。\n */\n async unpublishLocalVideo(): Promise<void> {\n const engine = this.lease?.engine;\n if (!engine || (!this.isCapturing && !this.externalVideo)) {\n return;\n }\n try {\n await engine.updateLocalVideo({ publish: false });\n } catch (error) {\n throw this.mapError(error);\n }\n }\n\n /**\n * 发布文件音频或麦克风;无文件音轨时不启动麦克风。\n *\n * @throws 引擎未初始化、麦克风权限被拒绝或发布失败时抛出错误。\n */\n async publishLocalAudio(): Promise<void> {\n const engine = this.requireEngine();\n try {\n if (this.externalVideo) {\n if (this.externalAudio) {\n await engine.updateLocalAudio({ publish: true });\n }\n } else if (!this.isAudioCapturing) {\n await engine.startLocalAudio();\n this.isAudioCapturing = true;\n } else {\n await engine.updateLocalAudio({ publish: true });\n }\n } catch (error) {\n throw this.mapError(error);\n }\n }\n\n /**\n * 停止发布本地音频流;麦克风采集保持运行。\n */\n async unpublishLocalAudio(): Promise<void> {\n const engine = this.lease?.engine;\n if (!engine || (!this.isAudioCapturing && !this.externalAudio)) {\n return;\n }\n try {\n await engine.updateLocalAudio({ publish: false });\n } catch (error) {\n throw this.mapError(error);\n }\n }\n\n /**\n * 更新远端视频主流订阅状态;订阅成功返回远端视频轨供渲染层绑定。\n *\n * @throws 引擎未初始化、订阅或停止订阅失败时抛出错误。\n */\n async subscribeRemoteVideo(\n userID: string,\n subscribe: boolean,\n ): Promise<MediaStreamTrack | undefined> {\n const engine = this.requireEngine();\n try {\n if (subscribe) {\n await engine.startRemoteVideo({\n userId: userID,\n streamType: STREAM_TYPE_MAIN as never,\n });\n const track = engine.getVideoTrack({ userId: userID });\n if (!track) {\n throw new XmaxError(\n XmaxErrorCode.mediaError,\n \"Remote video track is not available\",\n );\n }\n return track;\n }\n await engine.stopRemoteVideo({\n userId: userID,\n streamType: STREAM_TYPE_MAIN as never,\n });\n return undefined;\n } catch (error) {\n throw this.mapError(error);\n }\n }\n\n /**\n * 更新远端音频订阅状态。\n *\n * @throws 引擎未初始化或操作失败时抛出错误。\n */\n async subscribeRemoteAudio(\n userID: string,\n subscribe: boolean\n ): Promise<void> {\n const engine = this.requireEngine();\n try {\n await engine.muteRemoteAudio(userID, !subscribe);\n } catch (error) {\n throw this.mapError(error);\n }\n }\n\n /**\n * 当前平台是否支持远端音量控制(iOS Safari/WKWebView 不支持)。\n */\n get supportsRemoteAudioVolumeControl(): boolean {\n return canControlTrtcRemoteAudioVolume();\n }\n\n /**\n * 获取原始远端音轨;不支持独立播放音量的平台暂不开放媒体观察。\n */\n getRemoteAudioTrack(\n userID: string\n ): MediaStreamTrack | undefined {\n if (!canControlTrtcRemoteAudioVolume()) {\n throw new XmaxError(XmaxErrorCode.mediaError, \"TRTC cannot provide remote audio frames independently of playback on this platform\");\n }\n return this.requireEngine().getAudioTrack(userID) ?? undefined;\n }\n\n /**\n * 设置指定远端用户的音频播放音量。\n *\n * iOS Safari/WKWebView 不允许 JS 修改媒体元素音量(TRTC 官方标注\n * `setRemoteAudioVolume` 不支持 iOS Safari),该平台上降级为\n * 播放/静音二值控制:音量为 0 时停止播放该路音频,非 0 时恢复播放;\n * 恢复播放需要重新拉流,会有短暂缓冲。\n *\n * @param volume 音量,取值范围为 `0...100`,越界时收敛到边界。\n */\n setRemoteAudioVolume(\n volume: number,\n userID: string\n ): void {\n const engine = this.requireEngine();\n const normalized = Math.min(Math.max(Math.round(volume), 0), 100);\n if (!canControlTrtcRemoteAudioVolume()) {\n void engine.muteRemoteAudio(userID, normalized === 0).catch((error: unknown) => {\n XmaxLogger.rtc.warning(\n () =>\n `远端音频播放状态切换失败 (Failed to Toggle Remote Audio Playback)\\n` +\n `└─ ${XmaxLogger.localized(\"原因:\", \"Reason: \")}${XmaxError.from(error).message}`,\n );\n });\n return;\n }\n engine.setRemoteAudioVolume(userID, normalized);\n }\n\n /**\n * 向当前 RTC 房间发送自定义消息(`cmdId = 1`)。\n *\n * @throws 未在房间中或消息编码后超过 1000 字节时抛出错误。\n */\n async sendRoomMessage(\n message: string\n ): Promise<void> {\n const engine = this.requireEngine();\n if (!this.isInRoom) {\n throw new XmaxError(\n XmaxErrorCode.invalidConfiguration,\n \"Not in an RTC room\",\n );\n }\n\n const encoded = new TextEncoder().encode(message);\n if (encoded.byteLength > ROOM_MESSAGE_MAX_BYTES) {\n throw new XmaxError(\n XmaxErrorCode.invalidConfiguration,\n \"Room message exceeds the 1000-byte limit\",\n );\n }\n\n engine.sendCustomMessage({\n cmdId: CUSTOM_MESSAGE_CMD_ID,\n data: encoded.buffer as ArrayBuffer,\n });\n }\n\n /**\n * TRTC 凭据由会话层缓存,保持现有进房鉴权行为。\n */\n async updateCredentials(\n _configuration: RtcRoomJoinConfiguration\n ): Promise<void> { }\n\n /**\n * 设置 RTC 事件监听器,传入空值时清除监听器。\n */\n setEventListener(\n listener?: RtcEventListener\n ): void {\n this.eventListener = listener;\n }\n\n /**\n * 注册引擎事件桥接:媒体、消息及性能日志。\n */\n private registerEventBridge(\n engine: RtcEngine\n ): void {\n const source = engine as unknown as RtcEventSource;\n\n // 直接使用 TRTC 的统计周期,不创建定时器;退房和旧引擎的迟到事件不输出。\n const onStatistics = (stats: TRTCStatistics) => {\n if (this.isInRoom && this.lease?.engine === engine) {\n RtcStatsLogger.logConnectionStatistics({\n rttMs: stats.rtt, uplinkLossPercent: stats.upLoss, downlinkLossPercent: stats.downLoss,\n sentBytes: stats.bytesSent, receivedBytes: stats.bytesReceived,\n });\n const video = stats.localStatistics?.video?.find((item) => item.videoType === \"big\");\n const valid = (value: number | undefined, minimum: number) =>\n typeof value === \"number\" && Number.isFinite(value) && value >= minimum ? value : undefined;\n // TRTC 的上行 frameRate 来自编码帧率,不作为采集或发送帧率重复输出。\n const localVideo: UplinkVideoStatistics | undefined = video ? Object.freeze({\n width: valid(video.width, 1),\n height: valid(video.height, 1),\n encodeFrameRate: valid(video.frameRate, 0),\n bitrateKbps: valid(video.bitrate, 0),\n packetLossPercent: stats.upLoss <= 100 ? valid(stats.upLoss, 0) : undefined,\n }) : undefined;\n this.eventListener?.onLocalVideoStatistics?.(localVideo);\n if (localVideo) {\n RtcStatsLogger.logUplinkVideoStatistics(localVideo);\n }\n\n const remoteVideo: readonly DownlinkVideoStatistics[] = Object.freeze(\n (stats.remoteStatistics ?? []).flatMap((remote) => {\n const video = remote.video?.find((item) => item.videoType === \"big\");\n if (!video) {\n return [];\n }\n // 当前 TRTC 类型声明漏了这个可选运行时字段,缺失时不以缓冲延迟替代。\n const playback = video as typeof video & {\n point2pointDelay?: number;\n };\n return [Object.freeze({\n userID: remote.userId,\n width: valid(video.width, 1),\n height: valid(video.height, 1),\n decodeFrameRate: valid(video.frameRate, 0),\n bitrateKbps: valid(video.bitrate, 0),\n packetLossPercent: stats.downLoss <= 100 ? valid(stats.downLoss, 0) : undefined,\n endToEndDelayMs: valid(playback.point2pointDelay, 0),\n })];\n }),\n );\n this.eventListener?.onRemoteVideoStatistics?.(remoteVideo);\n for (const video of remoteVideo) {\n RtcStatsLogger.logDownlinkVideoStatistics(video);\n }\n }\n };\n\n const onNetworkQuality = (stats: NetworkQuality) => {\n if (this.isInRoom && this.lease?.engine === engine) {\n const quality = Object.freeze({\n uplink: networkQualityLevel(stats.uplinkNetworkQuality),\n downlink: networkQualityLevel(stats.downlinkNetworkQuality),\n });\n RtcStatsLogger.logNetworkQuality({\n ...quality,\n uplinkRTT: stats.uplinkRTT, downlinkRTT: stats.downlinkRTT,\n uplinkLoss: stats.uplinkLoss, downlinkLoss: stats.downlinkLoss,\n });\n this.eventListener?.onNetworkStatistics?.(quality);\n }\n };\n\n source.on(RTC_EVENT.statistics, onStatistics);\n source.on(RTC_EVENT.networkQuality, onNetworkQuality);\n const onAudioChanged = (event: RtcRemoteVideoEvent) => {\n if (this.isInRoom && this.lease?.engine === engine) this.eventListener?.onRemoteAudioTrackChanged?.(event.userId);\n };\n source.on(RTC_EVENT.remoteAudioAvailable, onAudioChanged);\n source.on(RTC_EVENT.remoteAudioUnavailable, onAudioChanged);\n this.removeStatsListeners = () => {\n source.off(RTC_EVENT.statistics, onStatistics);\n source.off(RTC_EVENT.networkQuality, onNetworkQuality);\n source.off(RTC_EVENT.remoteAudioAvailable, onAudioChanged);\n source.off(RTC_EVENT.remoteAudioUnavailable, onAudioChanged);\n };\n\n source.on(RTC_EVENT.remoteVideoAvailable, (event: RtcRemoteVideoEvent) => {\n if (this.lease?.engine !== engine || event.streamType !== STREAM_TYPE_MAIN) {\n return;\n }\n this.eventListener?.onRemoteVideoPublished(event.userId, true);\n });\n\n source.on(RTC_EVENT.remoteVideoUnavailable, (event: RtcRemoteVideoEvent) => {\n if (this.lease?.engine !== engine || event.streamType !== STREAM_TYPE_MAIN) {\n return;\n }\n this.eventListener?.onRemoteVideoPublished(event.userId, false);\n });\n\n source.on(RTC_EVENT.customMessage, (event: RtcCustomMessageEvent) => {\n if (this.lease?.engine !== engine || event.cmdId !== CUSTOM_MESSAGE_CMD_ID) {\n return;\n }\n let message: string;\n try {\n message = new TextDecoder().decode(event.data);\n } catch {\n XmaxLogger.rtc.warning(() => \"自定义消息解码失败 (Failed to Decode Custom Message)\");\n return;\n }\n this.eventListener?.onCustomMessageReceived(event.userId, message);\n });\n }\n\n /**\n * 返回当前引擎实例;未初始化时抛出错误。\n */\n private requireEngine(): RtcEngine {\n const engine = this.lease?.engine;\n if (!engine) {\n throw new XmaxError(\n XmaxErrorCode.rtcError,\n \"RTC engine is not initialized\",\n );\n }\n return engine;\n }\n\n /**\n * 将 TRTC 错误映射为 SDK 统一错误。\n */\n private mapError(\n error: unknown\n ): XmaxError {\n if (error instanceof XmaxError) {\n return error;\n }\n\n const rtcError = error as {\n message?: unknown;\n originError?: {\n name?: string\n };\n };\n const originName = rtcError?.originError?.name ?? \"\";\n const message =\n typeof rtcError?.message === \"string\" && rtcError.message.trim().length > 0\n ? rtcError.message.trim()\n : String(error);\n\n // 浏览器权限拒绝(getUserMedia 的 NotAllowedError 会被 TRTC 包装后抛出)。\n if (\n originName === \"NotAllowedError\" ||\n originName === \"SecurityError\" ||\n /NotAllowed/i.test(message)\n ) {\n return new XmaxError(XmaxErrorCode.cameraPermissionDenied, message);\n }\n // 其他设备层错误:无可用设备、设备被占用、采集参数不支持等。\n if (originName) {\n return new XmaxError(XmaxErrorCode.mediaError, message);\n }\n\n return new XmaxError(XmaxErrorCode.rtcError, message);\n }\n}\n","//\n// VeRtcStatistics.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\nimport type { LocalVideoStats, RemoteVideoStats } from \"@volcengine/rtc\";\nimport type { UplinkVideoStatistics, DownlinkVideoStatistics } from \"../VideoStatistics\";\n\n/**\n * VeRTC 的码率已经是 kbps,丢包率为 0–1;缺失指标不推算。\n */\nexport class VeRtcStatistics {\n\n /**\n * 转换 VeRTC 上行视频统计,统一帧率、码率和丢包率单位。\n */\n static local(\n stats: LocalVideoStats\n ): UplinkVideoStatistics {\n return Object.freeze({\n width: this.valid(stats.encodedFrameWidth, 1), height: this.valid(stats.encodedFrameHeight, 1),\n captureFrameRate: this.valid(stats.inputFrameRate), encodeFrameRate: this.valid(stats.encoderOutputFrameRate),\n sendFrameRate: this.valid(stats.sentFrameRate), bitrateKbps: this.valid(stats.sentKBitrate),\n packetLossPercent: this.loss(stats.videoLossRate),\n });\n }\n\n /**\n * 转换指定远端用户的视频接收统计,保留厂商未提供的指标。\n */\n static remote(\n userID: string,\n stats: RemoteVideoStats\n ): DownlinkVideoStatistics {\n return Object.freeze({\n userID, width: this.valid(stats.width, 1), height: this.valid(stats.height, 1),\n decodeFrameRate: this.valid(stats.decoderOutputFrameRate), renderFrameRate: this.valid(stats.rendererOutputFrameRate),\n bitrateKbps: this.valid(stats.receivedKBitrate), packetLossPercent: this.loss(stats.videoLossRate),\n endToEndDelayMs: this.valid(stats.e2eDelay),\n });\n }\n\n /**\n * 过滤缺失、非有限或不符合取值范围的统计值。\n */\n private static valid(\n value: number | undefined,\n minimum = 0\n ): number | undefined {\n return typeof value === \"number\" && Number.isFinite(value) && value >= minimum ? value : undefined;\n }\n\n /**\n * 将厂商提供的丢包比例转换为百分比。\n */\n private static loss(\n value: number\n ): number | undefined {\n return Number.isFinite(value) && value >= 0 && value <= 1 ? value * 100 : undefined;\n }\n}\n","//\n// VeRtcManager.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\nimport { RtcProvider } from \"../RtcProvider\";\nimport type { IRTCEngine } from \"@volcengine/rtc\";\nimport { XmaxError, XmaxErrorCode } from \"../../Errors/XmaxError\";\nimport { XmaxLogger } from \"../../Logging/XmaxLogger\";\nimport { CameraPosition } from \"../../Media/Camera/CameraPosition\";\nimport type { RtcManaging, RtcCameraCaptureOptions } from \"../RtcManaging\";\nimport type { RtcEventListener } from \"../RtcEventListener\";\nimport type { RtcRoomJoinConfiguration, VeRtcRoomJoinConfiguration } from \"../RoomJoinConfiguration\";\nimport { RtcVideoEncoderPreference, type VideoEncodingConfiguration } from \"../VideoEncodingConfiguration\";\nimport { rtcOrientedVideoSize } from \"../RtcVideoOrientation\";\nimport type { DownlinkVideoStatistics } from \"../VideoStatistics\";\nimport { VeRtcStatistics } from \"./VeRtcStatistics\";\nimport { networkQualityLevel } from \"../NetworkStatistics\";\nimport { RtcStatsLogger } from \"../RtcStatsLogger\";\n\n/**\n * 动态加载的 VeRTC Web SDK。\n */\ntype VeRtcSDK = typeof import(\"@volcengine/rtc\");\n/**\n * 创建 VeRTC 引擎使用的应用标识。\n */\nexport const VERTC_APP_ID = \"69a177e226e9b90176a86b96\";\n\n/**\n * VeRTC 适配器的加载依赖。\n */\nexport interface VeRtcManagerOptions {\n\n /**\n * 按需加载 RTC SDK,首次初始化时调用。\n */\n loadSDK?: () => Promise<VeRtcSDK>;\n}\n\n/**\n * SDK 内部采集、房间文本信令;原生媒体轨道复用公共渲染层。\n */\nexport class VeRtcManager implements RtcManaging {\n\n /**\n * 按需加载 RTC SDK,首次初始化时调用。\n */\n private readonly loadSDK: () => Promise<VeRtcSDK>;\n\n /**\n * 按需加载的 RTC SDK 模块。\n */\n private sdk?: VeRtcSDK;\n\n /**\n * 当前 RTC 引擎实例。\n */\n private engine?: IRTCEngine;\n\n /**\n * 共享的初始化任务,避免并发创建引擎。\n */\n private initialization?: Promise<void>;\n\n /**\n * 生命周期标识,使释放后的异步结果失效。\n */\n private lifecycle = 0;\n\n /**\n * 摄像头采集操作标识,过滤切换或停止后的迟到结果。\n */\n private captureVersion = 0;\n\n /**\n * 当前摄像头视频轨。\n */\n private camera?: MediaStreamTrack;\n\n /**\n * 是否已启动麦克风采集。\n */\n private microphone = false;\n\n /**\n * 外部媒体的克隆轨;原始轨道由媒体源管理。\n */\n private externalVideo?: MediaStreamTrack;\n\n /**\n * 由媒体源提供、适配器持有的外部音频克隆轨。\n */\n private externalAudio?: MediaStreamTrack;\n\n /**\n * 当前房间使用的身份和鉴权配置。\n */\n private connection?: VeRtcRoomJoinConfiguration;\n\n /**\n * 是否已经完成进房。\n */\n private joined = false;\n\n /**\n * 当前房间操作标识,退房后旧结果不再生效。\n */\n private roomVersion = 0;\n\n /**\n * 退房时取消当前房间的异步等待。\n */\n private roomAbort = new AbortController();\n\n /**\n * 共享的凭据恢复任务。\n */\n private recovery?: Promise<void>;\n\n /**\n * 共享的进房任务。\n */\n private joining?: Promise<void>;\n\n /**\n * 是否需要在凭据更新后重新进房。\n */\n private tokenExpired = false;\n\n /**\n * 发布权限是否已失效。\n */\n private publishPrivilegeExpired = false;\n\n /**\n * 订阅权限是否已失效。\n */\n private subscribePrivilegeExpired = false;\n\n /**\n * 业务当前是否要求发布本地视频。\n */\n private videoRequested = false;\n\n /**\n * 业务当前是否要求发布本地音频。\n */\n private audioRequested = false;\n\n /**\n * 当前事件监听器。\n */\n private listener?: RtcEventListener;\n\n /**\n * 已获知发布主视频流的远端用户。\n */\n private readonly publishedVideo = new Set<string>();\n\n /**\n * 已获知发布音频流的远端用户。\n */\n private readonly publishedAudio = new Set<string>();\n\n /**\n * 业务当前要求订阅视频的远端用户。\n */\n private readonly requestedVideo = new Set<string>();\n\n /**\n * 业务当前要求订阅音频的远端用户。\n */\n private readonly requestedAudio = new Set<string>();\n\n /**\n * 各远端用户的播放音量,范围为 0–100。\n */\n private readonly audioVolumes = new Map<string, number>();\n\n /**\n * 按远端用户维护的音频播放器。\n */\n private readonly audioPlayers = new Map<string, HTMLAudioElement>();\n\n /**\n * 音频订阅操作标识,用于丢弃迟到结果。\n */\n private readonly audioSubscriptions = new Map<string, object>();\n\n /**\n * 按远端用户缓存的最新视频统计。\n */\n private readonly remoteStatistics = new Map<string, DownlinkVideoStatistics>();\n\n /**\n * 设置 VeRTC 模块加载方式,初始化时再创建引擎。\n */\n constructor(\n options: VeRtcManagerOptions = {}\n ) {\n this.loadSDK = options.loadSDK ?? (() => import(\"@volcengine/rtc\"));\n }\n\n /**\n * 当前 VeRTC 引擎是否可用。\n */\n get isInitialized(): boolean {\n return this.engine !== undefined;\n }\n\n /**\n * 当前浏览器是否支持连续音量控制;移动 Safari 降级为播放或静音。\n */\n get supportsRemoteAudioVolumeControl(): boolean {\n if (typeof navigator === \"undefined\")\n return true;\n return !(/iPad|iPhone|iPod/.test(navigator.userAgent) ||\n (/Macintosh/.test(navigator.userAgent) && navigator.maxTouchPoints > 1));\n }\n\n /**\n * 按需加载 VeRTC 并创建手动播放模式的引擎,合并并发初始化请求。\n */\n async initialize(): Promise<void> {\n if (this.engine)\n return;\n if (this.initialization)\n return this.initialization;\n const version = this.lifecycle;\n const operation = (async () => {\n const sdk = await this.loadSDK();\n if (version !== this.lifecycle)\n throw this.cancelled();\n sdk.default.setLogConfig({ logLevel: \"none\" });\n // 禁用厂商自动播放;视频交给公共 View,音频按当前音量显式播放,避免初始漏声。\n const engine = sdk.default.createEngine(VERTC_APP_ID, { autoPlayPolicy: sdk.RTCAutoPlayPolicy.PLAY_MANUALLY });\n this.sdk = sdk;\n this.engine = engine;\n this.registerEvents(engine);\n })();\n this.initialization = operation;\n try {\n await operation;\n }\n finally {\n if (this.initialization === operation)\n this.initialization = undefined;\n }\n }\n\n /**\n * 销毁 VeRTC 引擎并释放适配器持有的媒体轨和播放器。\n */\n async destroy(): Promise<void> {\n this.lifecycle++;\n this.captureVersion++;\n const engine = this.engine;\n const sdk = this.sdk;\n this.clearRoom();\n this.engine = undefined;\n this.sdk = undefined;\n this.camera = undefined;\n this.microphone = false;\n engine?.removeAllListeners();\n try {\n if (engine && sdk)\n sdk.default.destroyEngine(engine);\n } finally {\n this.externalVideo?.stop();\n this.externalAudio?.stop();\n this.externalVideo = undefined;\n this.externalAudio = undefined;\n }\n }\n\n /**\n * 将克隆的文件音视频轨绑定到主流,不采集设备、不自动发布。\n */\n async setExternalMediaTracks(\n tracks: {\n videoTrack: MediaStreamTrack;\n audioTrack?: MediaStreamTrack\n }\n ): Promise<void> {\n const engine = this.requireEngine();\n const sdk = this.sdk!;\n if (this.camera || this.microphone || this.externalVideo) {\n throw new XmaxError(XmaxErrorCode.invalidConfiguration, \"Release the current local media before attaching external tracks\");\n }\n if (tracks.videoTrack.kind !== \"video\" || tracks.videoTrack.readyState !== \"live\" ||\n (tracks.audioTrack && (tracks.audioTrack.kind !== \"audio\" || tracks.audioTrack.readyState !== \"live\"))) {\n throw new XmaxError(XmaxErrorCode.invalidConfiguration, \"External media tracks must be live video/audio tracks\");\n }\n\n const video = tracks.videoTrack.clone();\n this.externalVideo = video;\n let audio: MediaStreamTrack | undefined;\n const index = sdk.StreamIndex.STREAM_INDEX_MAIN;\n const ensureCurrent = () => {\n if (this.engine !== engine || this.externalVideo !== video) {\n throw this.cancelled();\n }\n };\n try {\n audio = tracks.audioTrack?.clone();\n this.externalAudio = audio;\n await engine.setVideoSourceType(index, sdk.VideoSourceType.VIDEO_SOURCE_TYPE_EXTERNAL);\n ensureCurrent();\n await engine.setExternalVideoTrack(index, video);\n ensureCurrent();\n if (audio) {\n await engine.setAudioSourceType(index, sdk.AudioSourceType.AUDIO_SOURCE_TYPE_EXTERNAL);\n ensureCurrent();\n await engine.setExternalAudioTrack(index, audio);\n ensureCurrent();\n }\n } catch (error) {\n // 还原采集模式但不启动设备;原引擎已销毁时只释放克隆轨。\n if (this.engine === engine) {\n await engine.setAudioSourceType(index, sdk.AudioSourceType.AUDIO_SOURCE_TYPE_INTERNAL).catch(() => { });\n await engine.setVideoSourceType(index, sdk.VideoSourceType.VIDEO_SOURCE_TYPE_INTERNAL).catch(() => { });\n }\n if (this.externalVideo === video) {\n this.externalVideo = undefined;\n this.externalAudio = undefined;\n }\n video.stop();\n audio?.stop();\n throw this.mapError(error);\n }\n }\n\n /**\n * 启动摄像头采集,不自动发布视频;不支持非零旋转角度。\n */\n async startCameraCapture(\n options: RtcCameraCaptureOptions\n ): Promise<MediaStreamTrack> {\n if (options.rotation !== undefined && options.rotation !== 0) {\n throw new XmaxError(XmaxErrorCode.invalidConfiguration, \"Camera rotation currently requires Agora\");\n }\n const engine = this.requireEngine();\n if (this.camera || this.externalVideo) {\n throw new XmaxError(XmaxErrorCode.invalidConfiguration, \"Local video is already prepared\");\n }\n const version = ++this.captureVersion;\n await this.run(() => engine.setVideoEncoderConfig({\n ...rtcOrientedVideoSize({ width: options.width, height: options.height }), frameRate: options.frameRate, maxKbps: 1000,\n preferCodecName: this.sdk!.VideoCodecType.H264, contentHint: \"motion\",\n }));\n if (engine !== this.engine || version !== this.captureVersion)\n throw this.cancelled();\n try {\n await engine.startVideoCapture(options.position === CameraPosition.front ? \"user\" : \"environment\");\n if (engine !== this.engine || version !== this.captureVersion) {\n await engine.stopVideoCapture().catch(() => { });\n throw this.cancelled();\n }\n const track = engine.getLocalStreamTrack(this.sdk!.StreamIndex.STREAM_INDEX_MAIN, \"video\");\n if (!track) {\n await engine.stopVideoCapture();\n throw new XmaxError(XmaxErrorCode.rtcError, \"VeRTC camera track is unavailable\");\n }\n this.camera = track;\n return track;\n } catch (error) {\n throw this.mapError(error, XmaxErrorCode.cameraPermissionDenied);\n }\n }\n\n /**\n * 更新未连接的摄像头采集规格,复用引擎和当前设备。\n */\n async updateCameraCapture(\n options: RtcCameraCaptureOptions\n ): Promise<MediaStreamTrack> {\n const engine = this.requireEngine();\n this.requireCamera();\n if (this.connection) {\n throw new XmaxError(XmaxErrorCode.invalidConfiguration, \"Disconnect VeRTC before updating camera capture\");\n }\n const version = this.captureVersion;\n const size = rtcOrientedVideoSize(options);\n await this.run(() => engine.setVideoEncoderConfig({\n width: size.width, height: size.height, frameRate: options.frameRate, maxKbps: 1000,\n preferCodecName: this.sdk!.VideoCodecType.H264, contentHint: \"motion\",\n }));\n if (engine !== this.engine || version !== this.captureVersion) {\n throw this.cancelled();\n }\n const track = engine.getLocalStreamTrack(this.sdk!.StreamIndex.STREAM_INDEX_MAIN, \"video\");\n if (!track) {\n throw new XmaxError(XmaxErrorCode.rtcError, \"VeRTC camera track is unavailable\");\n }\n this.camera = track;\n return track;\n }\n\n /**\n * 切换前后置摄像头,返回切换后的视频轨。\n */\n async switchCameraCapture(\n to: CameraPosition\n ): Promise<MediaStreamTrack> {\n const engine = this.requireEngine();\n this.requireCamera();\n const version = this.captureVersion;\n await this.run(() => engine.setVideoCaptureDevice(to === CameraPosition.front ? \"user\" : \"environment\"));\n if (engine !== this.engine || version !== this.captureVersion)\n throw this.cancelled();\n const track = engine.getLocalStreamTrack(this.sdk!.StreamIndex.STREAM_INDEX_MAIN, \"video\");\n if (!track)\n throw new XmaxError(XmaxErrorCode.rtcError, \"VeRTC camera track is unavailable\");\n this.camera = track;\n return track;\n }\n\n /**\n * 停止摄像头采集并清除当前轨道引用。\n */\n async stopCameraCapture(): Promise<void> {\n if (this.externalVideo) {\n return;\n }\n this.captureVersion++;\n this.camera = undefined;\n if (this.engine)\n await this.run(() => this.engine!.stopVideoCapture());\n }\n\n /**\n * 将当前方向下的视频尺寸、帧率和码率约束应用到编码器。\n */\n async configureVideoEncoding(\n config: VideoEncodingConfiguration\n ): Promise<void> {\n const engine = this.requireEngine();\n this.requireLocalVideo();\n // 外部画布无需摄像头的移动端方向补偿。\n const size = this.externalVideo ? config : rtcOrientedVideoSize(config);\n await this.run(() => engine.setVideoEncoderConfig({\n width: size.width, height: size.height, frameRate: config.frameRate, maxKbps: config.maximumBitrate,\n contentHint: config.encoderPreference === RtcVideoEncoderPreference.maintainFramerate ? \"motion\" : \"detail\",\n preferCodecName: this.sdk!.VideoCodecType.H264,\n }));\n }\n\n /**\n * 使用当前会话凭据加入 VeRTC 房间,并合并并发进房操作。\n */\n async joinRoom(\n config: RtcRoomJoinConfiguration\n ): Promise<void> {\n await this.joining?.catch(() => { });\n await this.recovery?.catch(() => { });\n const engine = this.requireEngine();\n if (config.provider !== RtcProvider.vertc || config.appID !== VERTC_APP_ID || this.connection) {\n throw new XmaxError(XmaxErrorCode.invalidConfiguration, \"VeRTC credentials for the configured AppID and an idle room are required\");\n }\n this.clearRoom();\n this.connection = config;\n const version = this.roomVersion;\n const task = this.performJoin(engine, config, version);\n this.joining = task;\n try {\n await task;\n }\n finally {\n if (this.joining === task)\n this.joining = undefined;\n }\n }\n\n /**\n * 完成当前进房操作,取消后退出迟到创建的房间。\n */\n private async performJoin(\n engine: IRTCEngine,\n config: VeRtcRoomJoinConfiguration,\n version: number\n ): Promise<void> {\n try {\n await this.enter(engine, config);\n if (!this.isCurrent(engine, version)) {\n await engine.leaveRoom().catch(() => { });\n throw this.cancelled();\n }\n this.joined = true;\n this.replayVideo();\n } catch (error) {\n if (this.isCurrent(engine, version))\n this.clearRoom();\n throw this.mapError(error);\n }\n }\n\n /**\n * 调用 VeRTC 进房接口,关闭自动发布和自动订阅。\n */\n private enter(\n engine: IRTCEngine,\n config: VeRtcRoomJoinConfiguration\n ): Promise<void> {\n return this.run(() => engine.joinRoom(config.roomToken, config.roomID, { userId: config.userID }, {\n isAutoPublish: false, isAutoSubscribeAudio: false, isAutoSubscribeVideo: false,\n roomProfileType: this.sdk!.RoomProfileType.communication,\n }));\n }\n\n /**\n * 退出当前房间并清理订阅、播放器与统计。\n */\n async leaveRoom(): Promise<void> {\n const engine = this.engine;\n this.clearRoom();\n if (engine)\n await this.run(() => engine.leaveRoom());\n }\n\n /**\n * 正常续签仅 updateToken;被移出房间后重进,恢复发布与订阅意图。\n */\n async updateCredentials(\n config: RtcRoomJoinConfiguration,\n signal?: AbortSignal\n ): Promise<void> {\n const engine = this.requireEngine();\n const current = this.connection;\n if (signal?.aborted)\n throw this.cancelled();\n if (!current || config.provider !== RtcProvider.vertc || config.appID !== current.appID || config.roomID !== current.roomID || config.userID !== current.userID) {\n throw new XmaxError(XmaxErrorCode.sessionError, \"VeRTC session binding changed\");\n }\n const version = this.roomVersion;\n if (this.recovery)\n await this.cancellable(this.recovery, signal);\n if (!this.isCurrent(engine, version))\n throw this.cancelled();\n const task = this.renew(engine, config, version);\n this.recovery = task;\n void task.finally(() => {\n if (this.recovery === task)\n this.recovery = undefined;\n }).catch(() => { });\n await this.cancellable(task, signal);\n }\n\n /**\n * 更新令牌,必要时重新进房并恢复原有发布和订阅意图。\n */\n private async renew(\n engine: IRTCEngine,\n config: VeRtcRoomJoinConfiguration,\n version: number\n ): Promise<void> {\n if (!this.tokenExpired) {\n try {\n await this.run(() => engine.updateToken(config.roomToken));\n }\n catch (error) {\n this.ensureCurrent(engine, version);\n if (!this.tokenExpired)\n throw error;\n }\n }\n this.ensureCurrent(engine, version);\n const restore = this.tokenExpired || this.publishPrivilegeExpired || this.subscribePrivilegeExpired;\n if (restore) {\n this.joined = false;\n this.listener?.onRoomRejoining?.();\n this.clearPlayers();\n this.clearStatistics();\n if (this.tokenExpired) {\n this.publishedVideo.clear();\n this.publishedAudio.clear();\n await this.run(() => engine.leaveRoom());\n this.ensureCurrent(engine, version);\n await this.enter(engine, config);\n if (!this.isCurrent(engine, version)) {\n await engine.leaveRoom().catch(() => { });\n throw this.cancelled();\n }\n }\n if ((this.tokenExpired || this.publishPrivilegeExpired) && this.videoRequested && (this.camera || this.externalVideo)) {\n await this.run(() => engine.publishStream(this.sdk!.MediaType.VIDEO));\n this.ensureCurrent(engine, version);\n if (!this.videoRequested) {\n await this.run(() => engine.unpublishStream(this.sdk!.MediaType.VIDEO));\n this.ensureCurrent(engine, version);\n }\n }\n if ((this.tokenExpired || this.publishPrivilegeExpired) && this.audioRequested && (this.microphone || this.externalAudio)) {\n await this.run(() => engine.publishStream(this.sdk!.MediaType.AUDIO));\n this.ensureCurrent(engine, version);\n if (!this.audioRequested) {\n await this.run(() => engine.unpublishStream(this.sdk!.MediaType.AUDIO));\n this.ensureCurrent(engine, version);\n }\n }\n }\n this.connection = config;\n this.tokenExpired = false;\n this.publishPrivilegeExpired = false;\n this.subscribePrivilegeExpired = false;\n this.joined = true;\n if (restore) {\n this.replayVideo();\n for (const userID of this.requestedAudio) {\n if (this.publishedAudio.has(userID))\n await this.subscribeRemoteAudio(userID, true);\n this.ensureCurrent(engine, version);\n }\n }\n }\n\n /**\n * 发布已准备的本地视频轨。\n */\n async publishLocalVideo(): Promise<void> {\n const engine = this.requireEngine();\n this.requireLocalVideo();\n this.videoRequested = true;\n const version = this.roomVersion;\n await this.recovery;\n this.ensureCurrent(engine, version);\n if (!this.videoRequested)\n throw this.cancelled();\n await this.run(() => engine.publishStream(this.sdk!.MediaType.VIDEO));\n }\n\n /**\n * 停止发布视频,保留本地媒体源。\n */\n async unpublishLocalVideo(): Promise<void> {\n this.videoRequested = false;\n if (this.joined && !this.tokenExpired)\n await this.run(() => this.requireEngine().unpublishStream(this.sdk!.MediaType.VIDEO));\n }\n\n /**\n * 发布文件音频,或在设备采集模式下启动并发布麦克风音频。\n */\n async publishLocalAudio(): Promise<void> {\n const engine = this.requireEngine();\n if (this.externalVideo && !this.externalAudio) {\n return;\n }\n this.audioRequested = true;\n const version = this.roomVersion;\n await this.recovery;\n this.ensureCurrent(engine, version);\n try {\n if (!this.externalVideo && !this.microphone) {\n await engine.startAudioCapture();\n if (!this.isCurrent(engine, version)) {\n await engine.stopAudioCapture().catch(() => { });\n throw this.cancelled();\n }\n this.microphone = true;\n }\n if (!this.audioRequested)\n throw this.cancelled();\n await engine.publishStream(this.sdk!.MediaType.AUDIO);\n } catch (error) {\n throw this.mapError(error, this.externalVideo ? XmaxErrorCode.rtcError : XmaxErrorCode.microphonePermissionDenied);\n }\n }\n\n /**\n * 停止发布音频,保留已经准备的媒体资源。\n */\n async unpublishLocalAudio(): Promise<void> {\n this.audioRequested = false;\n if (this.joined && !this.tokenExpired)\n await this.run(() => this.requireEngine().unpublishStream(this.sdk!.MediaType.AUDIO));\n }\n\n /**\n * 更新指定用户的视频订阅,订阅成功后返回其主视频轨。\n */\n async subscribeRemoteVideo(\n userID: string,\n subscribe: boolean\n ): Promise<MediaStreamTrack | undefined> {\n const engine = this.requireEngine();\n const version = this.roomVersion;\n if (!subscribe) {\n this.requestedVideo.delete(userID);\n this.remoteStatistics.delete(userID);\n this.emitRemoteStatistics();\n if (this.joined && !this.tokenExpired)\n await this.run(() => engine.unsubscribeStream(userID, this.sdk!.MediaType.VIDEO));\n return;\n }\n this.requestedVideo.add(userID);\n await this.run(() => engine.subscribeStream(userID, this.sdk!.MediaType.VIDEO));\n this.ensureCurrent(engine, version);\n if (!this.requestedVideo.has(userID))\n return;\n const track = engine.getRemoteStreamTrack(userID, this.sdk!.StreamIndex.STREAM_INDEX_MAIN, \"video\");\n if (!track)\n throw new XmaxError(XmaxErrorCode.rtcError, \"VeRTC remote video track is unavailable\");\n return track;\n }\n\n /**\n * 更新指定用户的音频订阅,并按当前音量绑定播放器。\n */\n async subscribeRemoteAudio(\n userID: string,\n subscribe: boolean\n ): Promise<void> {\n const engine = this.requireEngine();\n const version = this.roomVersion;\n if (!subscribe) {\n this.requestedAudio.delete(userID);\n this.clearPlayer(userID);\n if (this.joined && !this.tokenExpired && this.publishedAudio.has(userID))\n await this.run(() => engine.unsubscribeStream(userID, this.sdk!.MediaType.AUDIO));\n return;\n }\n this.requestedAudio.add(userID);\n if (!this.joined || this.tokenExpired || !this.publishedAudio.has(userID))\n return;\n const subscription = {};\n this.audioSubscriptions.set(userID, subscription);\n await this.run(() => engine.subscribeStream(userID, this.sdk!.MediaType.AUDIO));\n if (!this.isCurrent(engine, version) || !this.joined || this.tokenExpired ||\n this.audioSubscriptions.get(userID) !== subscription ||\n !this.requestedAudio.has(userID) || !this.publishedAudio.has(userID))\n return;\n const track = engine.getRemoteStreamTrack(userID, this.sdk!.StreamIndex.STREAM_INDEX_MAIN, \"audio\");\n if (!track)\n return;\n this.clearPlayer(userID);\n const player = document.createElement(\"audio\");\n player.srcObject = new MediaStream([track]);\n this.audioPlayers.set(userID, player);\n this.applyAudioVolume(userID);\n this.listener?.onRemoteAudioTrackChanged?.(userID);\n }\n\n /**\n * 获取原始远端音轨,独立于 audio 元素的静音和播放音量。\n */\n getRemoteAudioTrack(\n userID: string\n ): MediaStreamTrack | undefined {\n if (!this.publishedAudio.has(userID)) return undefined;\n return this.requireEngine().getRemoteStreamTrack(userID, this.sdk!.StreamIndex.STREAM_INDEX_MAIN, \"audio\");\n }\n\n /**\n * 设置指定远端用户的播放音量,范围为 0–100。\n */\n setRemoteAudioVolume(\n volume: number,\n userID: string\n ): void {\n this.audioVolumes.set(userID, Number.isFinite(volume) ? Math.max(0, Math.min(100, Math.round(volume))) : 0);\n this.applyAudioVolume(userID);\n }\n\n /**\n * 将当前音量应用到远端播放器;不支持连续音量时切换播放或静音。\n */\n private applyAudioVolume(\n userID: string\n ): void {\n const player = this.audioPlayers.get(userID);\n if (!player)\n return;\n const volume = this.audioVolumes.get(userID) ?? 0;\n player.muted = volume === 0 || !this.requestedAudio.has(userID);\n if (this.supportsRemoteAudioVolumeControl)\n player.volume = volume / 100;\n if (player.muted)\n player.pause();\n else\n void player.play().catch(() => {\n XmaxLogger.rtc.warning(() => \"VeRTC audio playback requires a user interaction\");\n });\n }\n\n /**\n * 在当前房间发送文本消息,校验字节数并限制等待时长。\n */\n async sendRoomMessage(\n message: string\n ): Promise<void> {\n const engine = this.requireEngine();\n const version = this.roomVersion;\n if (!this.joined || this.tokenExpired)\n throw this.cancelled();\n if (new TextEncoder().encode(message).byteLength > 64 * 1024) {\n throw new XmaxError(XmaxErrorCode.invalidConfiguration, \"VeRTC room message exceeds the 64-KiB limit\");\n }\n // 不添加包装、不拆包;保留公共 RoomEvent 原始 JSON。\n await this.cancellable(this.run(async () => {\n const sent = engine.sendRoomMessage(message);\n if (!sent)\n throw new XmaxError(XmaxErrorCode.rtcError, \"VeRTC room message was not sent\");\n await sent;\n }), undefined, 5000);\n this.ensureCurrent(engine, version);\n }\n\n /**\n * 替换 RTC 事件监听器。\n */\n setEventListener(\n listener?: RtcEventListener\n ): void {\n this.listener = listener;\n }\n\n /**\n * 将 VeRTC 事件转换为公共发布、订阅和统计通知。\n */\n private registerEvents(\n engine: IRTCEngine\n ): void {\n const events = this.sdk!.default.events;\n const active = () => engine === this.engine && this.connection !== undefined;\n const mediaActive = () => active() && this.joined && !this.tokenExpired;\n const video = (mediaType: number) => (mediaType & this.sdk!.MediaType.VIDEO) !== 0;\n const audio = (mediaType: number) => (mediaType & this.sdk!.MediaType.AUDIO) !== 0;\n engine.on(events.onUserPublishStream, ({ userId, mediaType }) => {\n if (!active())\n return;\n if (video(mediaType)) {\n this.publishedVideo.add(userId);\n if (mediaActive())\n this.listener?.onRemoteVideoPublished(userId, true);\n }\n if (audio(mediaType)) {\n this.publishedAudio.add(userId);\n if (mediaActive() && this.requestedAudio.has(userId)) {\n void this.subscribeRemoteAudio(userId, true).catch(error => {\n if (mediaActive())\n this.listener?.onError?.(this.mapError(error));\n });\n }\n }\n });\n engine.on(events.onUserUnpublishStream, ({ userId, mediaType }) => {\n if (!active())\n return;\n if (video(mediaType))\n this.removeVideo(userId);\n if (audio(mediaType)) {\n this.publishedAudio.delete(userId);\n this.clearPlayer(userId);\n this.listener?.onRemoteAudioTrackChanged?.(userId);\n }\n });\n engine.on(events.onUserLeave, ({ userInfo }) => {\n if (!active())\n return;\n this.removeVideo(userInfo.userId);\n this.publishedAudio.delete(userInfo.userId);\n this.clearPlayer(userInfo.userId);\n });\n engine.on(events.onRoomMessageReceived, ({ userId, message }) => {\n if (mediaActive() && typeof message === \"string\")\n this.listener?.onCustomMessageReceived(userId, message);\n });\n const willExpire = () => {\n if (active())\n this.listener?.onTokenWillExpire?.();\n };\n engine.on(events.onTokenWillExpire, willExpire);\n engine.on(events.onTokenPublishPrivilegeWillExpire, willExpire);\n engine.on(events.onTokenSubscribePrivilegeWillExpire, willExpire);\n engine.on(events.onTokenPublishPrivilegeDidExpired, () => {\n if (!active() || this.publishPrivilegeExpired)\n return;\n this.publishPrivilegeExpired = true;\n this.listener?.onTokenExpired?.();\n });\n engine.on(events.onTokenSubscribePrivilegeDidExpired, () => {\n if (!active() || this.subscribePrivilegeExpired)\n return;\n this.subscribePrivilegeExpired = true;\n this.clearPlayers();\n this.listener?.onTokenExpired?.();\n });\n engine.on(events.onError, ({ errorCode }) => {\n if (!active())\n return;\n if (errorCode === \"TOKEN_EXPIRED\") {\n if (this.tokenExpired)\n return;\n this.tokenExpired = true;\n this.clearPlayers();\n this.listener?.onTokenExpired?.();\n } else\n this.listener?.onError?.(this.mapError({ code: errorCode }));\n });\n // 网络断线由厂商自动恢复,避免自行重复 join。恢复后重新绑定可能替换的轨道。\n engine.on(events.onConnectionStateChanged, ({ state }) => {\n if (!mediaActive() || state !== this.sdk!.ConnectionState.CONNECTION_STATE_RECONNECTED)\n return;\n this.listener?.onRoomRejoining?.();\n this.clearPlayers();\n this.clearStatistics();\n this.replayVideo();\n for (const userID of this.requestedAudio) {\n void this.subscribeRemoteAudio(userID, true).catch(error => {\n if (mediaActive())\n this.listener?.onError?.(this.mapError(error));\n });\n }\n });\n engine.on(events.onNetworkQuality, (up, down) => {\n if (mediaActive()) {\n const quality = Object.freeze({\n uplink: networkQualityLevel(up), downlink: networkQualityLevel(down),\n });\n this.listener?.onNetworkStatistics?.(quality);\n RtcStatsLogger.logNetworkQuality(quality);\n }\n });\n engine.on(events.onLocalStreamStats, stats => {\n if (!mediaActive() || stats.isScreen || !this.videoRequested)\n return;\n const result = VeRtcStatistics.local(stats.videoStats);\n this.listener?.onLocalVideoStatistics?.(result);\n RtcStatsLogger.logUplinkVideoStatistics(result);\n });\n engine.on(events.onRemoteStreamStats, stats => {\n if (!mediaActive() || stats.isScreen || !this.requestedVideo.has(stats.userId))\n return;\n const result = VeRtcStatistics.remote(stats.userId, stats.videoStats);\n this.remoteStatistics.set(stats.userId, result);\n RtcStatsLogger.logDownlinkVideoStatistics(result);\n this.emitRemoteStatistics();\n });\n }\n\n /**\n * 向监听器重新报告已知的远端视频发布状态。\n */\n private replayVideo(): void {\n for (const userID of this.publishedVideo) this.listener?.onRemoteVideoPublished(userID, true);\n }\n\n /**\n * 移除远端用户的视频发布记录并清除其统计。\n */\n private removeVideo(\n userID: string\n ): void {\n this.publishedVideo.delete(userID);\n this.remoteStatistics.delete(userID);\n this.emitRemoteStatistics();\n if (this.joined && !this.tokenExpired)\n this.listener?.onRemoteVideoPublished(userID, false);\n }\n\n /**\n * 交付当前远端视频统计的只读快照。\n */\n private emitRemoteStatistics(): void {\n this.listener?.onRemoteVideoStatistics?.(Object.freeze([...this.remoteStatistics.values()]));\n }\n\n /**\n * 清除视频统计并通知当前没有可用样本。\n */\n private clearStatistics(): void {\n this.remoteStatistics.clear();\n this.listener?.onLocalVideoStatistics?.(undefined);\n this.emitRemoteStatistics();\n }\n\n /**\n * 释放指定远端用户的音频播放器。\n */\n private clearPlayer(\n userID: string\n ): void {\n this.audioSubscriptions.delete(userID);\n const player = this.audioPlayers.get(userID);\n if (player) {\n player.pause();\n player.srcObject = null;\n }\n this.audioPlayers.delete(userID);\n }\n\n /**\n * 释放全部远端音频播放器和订阅标识。\n */\n private clearPlayers(): void {\n this.audioSubscriptions.clear();\n for (const userID of this.audioPlayers.keys()) this.clearPlayer(userID);\n }\n\n /**\n * 使当前房间操作失效并清理鉴权、媒体意图和订阅缓存。\n */\n private clearRoom(): void {\n this.roomVersion++;\n this.roomAbort.abort();\n this.roomAbort = new AbortController();\n this.connection = undefined;\n this.joined = false;\n this.tokenExpired = false;\n this.publishPrivilegeExpired = false;\n this.subscribePrivilegeExpired = false;\n this.videoRequested = false;\n this.audioRequested = false;\n this.publishedVideo.clear();\n this.publishedAudio.clear();\n this.requestedVideo.clear();\n this.requestedAudio.clear();\n this.audioVolumes.clear();\n this.clearPlayers();\n this.clearStatistics();\n }\n\n /**\n * 业务取消立即结束等待;底层迟到结果由版本检查清理。\n */\n private async cancellable<T>(\n task: Promise<T>,\n signal?: AbortSignal,\n timeoutMs?: number\n ): Promise<T> {\n const roomSignal = this.roomAbort.signal;\n return new Promise<T>((resolve, reject) => {\n const timer = timeoutMs === undefined ? undefined : setTimeout(() => {\n cleanup();\n reject(new XmaxError(XmaxErrorCode.timeout, \"VeRTC room message send timed out\"));\n }, timeoutMs);\n const cleanup = () => {\n clearTimeout(timer);\n roomSignal.removeEventListener(\"abort\", cancel);\n signal?.removeEventListener(\"abort\", cancel);\n };\n const cancel = () => {\n // 外部取消使续签后续步骤失效,但由正常退房流程释放所有资源。\n if (signal?.aborted && !roomSignal.aborted)\n this.roomVersion++;\n cleanup();\n reject(this.cancelled());\n };\n roomSignal.addEventListener(\"abort\", cancel, { once: true });\n signal?.addEventListener(\"abort\", cancel, { once: true });\n task.then(value => {\n cleanup();\n resolve(value);\n }, error => {\n cleanup();\n reject(error);\n });\n if (roomSignal.aborted || signal?.aborted)\n cancel();\n });\n }\n\n /**\n * 检查引擎、房间和操作版本是否仍然有效。\n */\n private isCurrent(\n engine: IRTCEngine,\n version: number\n ): boolean {\n return this.engine === engine && this.connection !== undefined && this.roomVersion === version;\n }\n\n /**\n * 当前房间操作已失效时抛出取消错误。\n */\n private ensureCurrent(\n engine: IRTCEngine,\n version: number\n ): void {\n if (!this.isCurrent(engine, version))\n throw this.cancelled();\n }\n\n /**\n * 获取已初始化引擎,尚未初始化时抛出 RTC 错误。\n */\n private requireEngine(): IRTCEngine {\n if (!this.engine)\n throw new XmaxError(XmaxErrorCode.rtcError, \"VeRTC is not initialized\");\n return this.engine;\n }\n\n /**\n * 获取已准备的文件或摄像头视频轨。\n */\n private requireLocalVideo(): MediaStreamTrack {\n if (this.externalVideo) {\n return this.externalVideo;\n }\n return this.requireCamera();\n }\n\n /**\n * 获取正在采集的摄像头轨,未采集时抛出配置错误。\n */\n private requireCamera(): MediaStreamTrack {\n if (!this.camera)\n throw new XmaxError(XmaxErrorCode.invalidConfiguration, \"Camera capture is not running\");\n return this.camera;\n }\n\n /**\n * 执行 VeRTC 异步操作并统一转换厂商错误。\n */\n private async run<T>(\n operation: () => Promise<T>\n ): Promise<T> {\n try {\n return await operation();\n } catch (error) {\n throw this.mapError(error);\n }\n }\n\n /**\n * 转换厂商错误,按需要区分设备权限和 RTC 失败。\n */\n private mapError(\n error: unknown,\n permissionCode?: XmaxErrorCode\n ): XmaxError {\n if (error instanceof XmaxError)\n return error;\n const value = error as {\n code?: unknown;\n name?: unknown\n } | null;\n const code = typeof value?.code === \"string\" && /^[A-Z_0-9-]{1,64}$/.test(value.code) ? value.code : \"UNKNOWN\";\n const denied = value?.name === \"NotAllowedError\" || code === \"PERMISSION_DENIED\" || code === \"DEVICE_PERMISSION_DENIED\";\n return new XmaxError(permissionCode && denied ? permissionCode : XmaxErrorCode.rtcError, `VeRTC operation failed (${code})`);\n }\n\n /**\n * 构造当前 RTC 操作已取消的业务错误。\n */\n private cancelled(): XmaxError {\n return new XmaxError(XmaxErrorCode.cancelled, \"VeRTC operation was cancelled\");\n }\n}\n","//\n// RtcFactory.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\nimport { XmaxEnvironment } from \"../Runtime/XmaxEnvironment\";\nimport { AgoraRtcManager } from \"./Agora/AgoraRtcManager\";\nimport { RtcProvider } from \"./RtcProvider\";\nimport { TrtcRtcManager } from \"./TRTC/TrtcRtcManager\";\nimport { VeRtcManager } from \"./VeRTC/VeRtcManager\";\nimport type { RtcManaging } from \"./RtcManaging\";\nimport { XmaxError, XmaxErrorCode } from \"../Errors/XmaxError\";\n\n/**\n * 按固定的接入配置创建 RTC 实现;构造阶段不加载厂商运行时。\n */\nexport function createRtcManager(\n provider: RtcProvider,\n environment: XmaxEnvironment\n): RtcManaging {\n switch (provider) {\n case RtcProvider.trtc:\n return new TrtcRtcManager();\n case RtcProvider.agora:\n return new AgoraRtcManager({ environment });\n case RtcProvider.vertc:\n return new VeRtcManager();\n default:\n throw new XmaxError(XmaxErrorCode.invalidConfiguration, \"Unsupported RTC provider\");\n }\n}\n","//\n// RealtimeExtraParams.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\nimport { XmaxError, XmaxErrorCode } from \"../../Foundation/Errors/XmaxError\";\n\n/**\n * 可通过生成信令传递的 JSON 数据。\n */\nexport type JsonValue = string | number | boolean | null | JsonValue[] | {\n [key: string]: JsonValue\n};\n\n/**\n * 合并到本次 start 或 change_condition 的 params;不缓存,不允许覆盖 SDK 字段。\n */\nexport type RealtimeExtraParams = Record<string, JsonValue>;\n\n/**\n * 由 SDK 管理的生成信令字段,扩展参数不得覆盖。\n */\nconst RESERVED_PARAMS = new Set([\n \"model\", \"face_replace\", \"size\", \"target_size\", \"prompt\", \"ref_image_path\",\n \"mirror\", \"min_bitrate\", \"max_bitrate\", \"padding\",\n]);\n\n/**\n * 校验并复制本次参数;不保存到生成上下文。\n *\n * @param params 本次调用的扩展参数,只接受 JSON 数据。\n * @returns 独立的 JSON 副本;未传参数时返回 undefined。\n * @throws 参数包含非 JSON 数据、循环引用或 SDK 保留字段时抛出 invalidConfiguration。\n */\nexport function snapshotExtraParams(\n params?: RealtimeExtraParams\n): RealtimeExtraParams | undefined {\n if (params === undefined)\n return undefined;\n const invalid = (message: string): never => {\n throw new XmaxError(XmaxErrorCode.invalidConfiguration, message);\n };\n if (!params || typeof params !== \"object\" || Array.isArray(params)) {\n return invalid(\"extraParams must be a JSON object\");\n }\n for (const key of Object.keys(params)) {\n if (RESERVED_PARAMS.has(key))\n return invalid(`extraParams conflicts with SDK parameter: ${key}`);\n }\n const ancestors = new Set<object>();\n const validate = (value: unknown): void => {\n if (value === null || typeof value === \"string\" || typeof value === \"boolean\")\n return;\n if (typeof value === \"number\" && Number.isFinite(value))\n return;\n if (typeof value !== \"object\" || !value)\n return invalid(\"extraParams must contain only JSON values\");\n if (ancestors.has(value))\n return invalid(\"extraParams must not contain circular references\");\n const prototype = Object.getPrototypeOf(value);\n if (!Array.isArray(value) && prototype !== Object.prototype && prototype !== null) {\n return invalid(\"extraParams must contain only JSON objects and arrays\");\n }\n if (Object.getOwnPropertySymbols(value).length)\n return invalid(\"extraParams must not contain symbol keys\");\n ancestors.add(value);\n for (const child of Array.isArray(value) ? value : Object.values(value)) validate(child);\n ancestors.delete(value);\n };\n validate(params);\n return JSON.parse(JSON.stringify(params)) as RealtimeExtraParams;\n}\n","//\n// CameraFrameValidator.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\nimport { XmaxError, XmaxErrorCode } from \"../../Errors/XmaxError\";\n\n/**\n * 摄像头开启后的固定等待时长:覆盖硬件出帧与曝光爬坡,避免把黑帧推给 RTC。\n */\nconst CAMERA_WARMUP_MS = 200;\n\n/**\n * 等待摄像头进入出帧状态:固定延时,不检测亮度、不保存图像、不停止原始轨道。\n * 取消或轨道结束时立即拒绝,结束后清理全部监听与定时器。\n */\nexport function waitForValidCameraFrame(\n track: MediaStreamTrack,\n signal: AbortSignal\n): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n let timer: ReturnType<typeof setTimeout> | undefined;\n let settled = false;\n\n const finish = (error?: XmaxError) => {\n if (settled)\n return;\n settled = true;\n clearTimeout(timer);\n signal.removeEventListener(\"abort\", abort);\n track.removeEventListener(\"ended\", ended);\n if (error) {\n reject(error);\n } else {\n resolve();\n }\n };\n const abort = () => finish(new XmaxError(XmaxErrorCode.cancelled, \"Camera warmup cancelled\"));\n const ended = () => finish(new XmaxError(XmaxErrorCode.mediaError, \"Camera track ended before warmup completed\"));\n\n signal.addEventListener(\"abort\", abort, { once: true });\n track.addEventListener(\"ended\", ended, { once: true });\n if (signal.aborted) {\n abort();\n return;\n }\n if (track.readyState === \"ended\") {\n ended();\n return;\n }\n\n timer = setTimeout(() => finish(), CAMERA_WARMUP_MS);\n });\n}\n","//\n// PermissionManager.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\nimport { XmaxError, XmaxErrorCode } from \"../Errors/XmaxError\";\nimport { XmaxLogger } from \"../Logging/XmaxLogger\";\nimport type { PermissionManaging } from \"./PermissionManaging\";\n\n/**\n * Web 权限管理。\n *\n * Web 上权限通过 getUserMedia 隐式申请;这里用 Permissions API 做预检\n * (Safari 对 camera/microphone 查询支持不完整,查询不可用时跳过预检,\n * 由采集阶段的 DOMException 映射兜底)。\n */\nexport class PermissionManager implements PermissionManaging {\n\n /**\n * 确保相机权限可用。\n * @throws 权限被拒绝或浏览器不支持媒体采集时抛出 `cameraPermissionDenied`。\n */\n async ensureCameraPermission(): Promise<void> {\n await this.ensurePermission(\"camera\", XmaxErrorCode.cameraPermissionDenied);\n }\n\n /**\n * 确保麦克风权限可用。\n * @throws 权限被拒绝或浏览器不支持媒体采集时抛出 `microphonePermissionDenied`。\n */\n async ensureMicrophonePermission(): Promise<void> {\n await this.ensurePermission(\n \"microphone\",\n XmaxErrorCode.microphonePermissionDenied,\n );\n }\n\n /**\n * 通过 Permissions API 预检指定设备权限。\n *\n * @param name 设备权限名称。\n * @param deniedCode 权限被拒绝时抛出的错误码。\n */\n private async ensurePermission(\n name: \"camera\" | \"microphone\",\n deniedCode: XmaxErrorCode,\n ): Promise<void> {\n if (typeof navigator === \"undefined\" || !navigator.mediaDevices?.getUserMedia) {\n throw new XmaxError(\n deniedCode,\n \"This browser does not support media capture (getUserMedia)\",\n );\n }\n\n try {\n const permissions = navigator.permissions;\n if (!permissions?.query) {\n return;\n }\n // TS 标准库未包含 camera/microphone 描述名,按运行时能力调用。\n const status = await permissions.query({\n name: name as PermissionName,\n });\n if (status.state === \"denied\") {\n throw new XmaxError(\n deniedCode,\n `${name === \"camera\" ? \"Camera\" : \"Microphone\"} permission was denied`,\n );\n }\n } catch (error) {\n if (error instanceof XmaxError) {\n throw error;\n }\n // 浏览器不支持该权限查询(如部分 Safari 版本),跳过预检。\n XmaxLogger.permission.debug(\n () => `Permission query for ${name} is unavailable; skip pre-check`,\n );\n }\n }\n}\n","//\n// VideoRenderBinding.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\nimport type { VideoContentMode } from \"../../Foundation/Media/Video/VideoContentMode\";\nimport type { RealtimeVideoTrack } from \"./RealtimeVideoTrack\";\n\n/**\n * 轨道渲染目标。\n *\n * 由渲染层视图实现;绑定管线只通过该接口向视图送流和调整镜像,\n * 不感知具体视图类型。\n */\nexport interface VideoRenderTarget {\n\n /**\n * 为新生成任务重新等待首帧及渲染预热,不重建媒体流。\n */\n prepareForGeneration?: () => void;\n\n /**\n * 视图内部播放器;自定义目标可以不提供。\n */\n readonly videoElement?: HTMLVideoElement;\n\n /**\n * 画面是否镜像显示(仅影响显示,不影响发布流)。\n */\n isMirrored: boolean;\n\n /**\n * 设置渲染用的媒体流;传 null 清空画面。\n */\n setMediaStream: (\n stream: MediaStream | null\n ) => void;\n}\n\n/**\n * 轨道与渲染视图之间的绑定行为。\n */\nexport interface VideoRenderBinding {\n\n /**\n * 远端首帧就绪后的展示预热时长;本地预览不配置。\n */\n renderWarmupMs?: number;\n\n /**\n * 预热结束、开始渐入时通知轨道拥有者。\n */\n renderWarmupCompletedHandler?: () => void;\n\n /**\n * 视图首帧呈现时通知轨道拥有者,独立于视图自身的渐入回调。\n */\n frameDisplayHandler?: () => void;\n\n /**\n * 视图绑定轨道时调用;负责把画面接入视图。\n */\n attachHandler: (\n target: VideoRenderTarget,\n contentMode: VideoContentMode\n ) => void;\n\n /**\n * 视图解绑轨道时调用;负责释放画面资源。\n */\n detachHandler: (\n target: VideoRenderTarget\n ) => void;\n}\n\n/**\n * 轨道渲染绑定注册表。\n *\n * 摄像头等需要自定义渲染管线的轨道在创建时注册绑定;\n * 未注册绑定的轨道由视图按 `mediaStreamTrack` 直接渲染。\n */\nexport class VideoRenderRegistry {\n\n /**\n * 轨道渲染绑定索引\n */\n private static bindings = new WeakMap<RealtimeVideoTrack, VideoRenderBinding>();\n\n /**\n * 注册轨道的自定义渲染绑定,覆盖该轨道已有的绑定。\n * @internal\n */\n static register(\n track: RealtimeVideoTrack,\n binding: VideoRenderBinding\n ): void {\n VideoRenderRegistry.bindings.set(track, binding);\n }\n\n /**\n * 移除轨道的渲染绑定,不执行视图解绑或资源释放。\n * @internal\n */\n static unregister(\n track: RealtimeVideoTrack\n ): void {\n VideoRenderRegistry.bindings.delete(track);\n }\n\n /**\n * 查询轨道的自定义渲染绑定;未注册时返回 undefined。\n * @internal\n */\n static binding(\n forTrack: RealtimeVideoTrack\n ): VideoRenderBinding | undefined {\n return VideoRenderRegistry.bindings.get(forTrack);\n }\n}\n","//\n// RealtimeVideoTrack.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\nimport { CameraPosition } from \"../../Foundation/Media/Camera/CameraPosition\";\nimport { RealtimeVideoFormat } from \"./RealtimeVideoFormat\";\n\n/**\n * 视频轨道身份及初始媒体信息。\n */\nexport interface RealtimeVideoTrackInit {\n\n /**\n * 视频轨道标识。\n */\n id: string;\n\n /**\n * 轨道当前使用的视频格式。\n */\n videoFormat?: RealtimeVideoFormat;\n\n /**\n * 本地相机轨道的摄像头位置。\n */\n position?: CameraPosition;\n}\n\n/**\n * 实时视频轨道及其动态元数据。\n *\n * Web 版本额外持有 `MediaStreamTrack` 句柄(SDK 内部使用),\n * 渲染层通过它把画面绑定到 `<video>` 元素。\n */\nexport class RealtimeVideoTrack {\n\n /**\n * 视频轨道标识。\n */\n readonly id: string;\n\n /**\n * 动态元数据\n */\n private metadata: {\n videoFormat?: RealtimeVideoFormat;\n position?: CameraPosition;\n };\n\n /**\n * SDK 内部:轨道对应的 Web 媒体轨。\n * 本地轨道在采集启动后可用;远端轨道在订阅成功后可用。\n */\n mediaStreamTrack?: MediaStreamTrack;\n\n /**\n * 创建实时视频轨道。\n */\n constructor(\n init: RealtimeVideoTrackInit\n ) {\n this.id = init.id;\n this.metadata = {\n videoFormat: init.videoFormat,\n position: init.position,\n };\n }\n\n /**\n * 当前视频轨道使用的视频格式。\n */\n get videoFormat(): RealtimeVideoFormat | undefined {\n return this.metadata.videoFormat;\n }\n\n /**\n * 本地相机轨道当前使用的摄像头位置。\n */\n get position(): CameraPosition | undefined {\n return this.metadata.position;\n }\n\n /**\n * 更新轨道视频格式。 @internal\n */\n updateVideoFormat(\n videoFormat: RealtimeVideoFormat\n ): void {\n this.metadata.videoFormat = videoFormat;\n }\n\n /**\n * 更新轨道摄像头位置。 @internal\n */\n updatePosition(\n position: CameraPosition\n ): void {\n this.metadata.position = position;\n }\n}\n","//\n// RealtimeMediaStream.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\nimport { RealtimeVideoTrack } from \"./RealtimeVideoTrack\";\n\n/**\n * 实时生成输入/输出的媒体流。\n */\nexport class RealtimeMediaStream {\n\n /**\n * 媒体流标识。\n */\n readonly id: string;\n\n /**\n * 媒体流包含的视频轨道。\n */\n readonly videoTrack?: RealtimeVideoTrack;\n\n /**\n * 创建实时媒体流。\n */\n constructor(\n init: {\n id: string;\n videoTrack?: RealtimeVideoTrack\n }\n ) {\n this.id = init.id;\n this.videoTrack = init.videoTrack;\n }\n}\n","//\n// CameraController.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\nimport { CameraPosition } from \"../../Foundation/Media/Camera/CameraPosition\";\nimport { validateCameraRotation, type CameraRotation } from \"../../Foundation/Media/Camera/CameraRotation\";\nimport { waitForValidCameraFrame } from \"../../Foundation/Media/Camera/CameraFrameValidator\";\nimport { XmaxError, XmaxErrorCode, type XmaxErrorListener } from \"../../Foundation/Errors/XmaxError\";\nimport type { PermissionManaging } from \"../../Foundation/Permissions/PermissionManaging\";\nimport { PermissionManager } from \"../../Foundation/Permissions/PermissionManager\";\nimport type { RtcManaging } from \"../../Foundation/RTC/RtcManaging\";\nimport { TrtcRtcManager } from \"../../Foundation/RTC/TRTC/TrtcRtcManager\";\nimport { VideoRenderRegistry } from \"../../Service/Realtime/VideoRenderBinding\";\nimport type { MediaServicing } from \"../../Service/Media/MediaServicing\";\nimport { MediaService } from \"../../Service/Media/MediaService\";\nimport { RealtimeMediaStream } from \"../../Service/Realtime/RealtimeMediaStream\";\nimport type { RealtimeVideoFormat } from \"../../Service/Realtime/RealtimeVideoFormat\";\nimport { RealtimeVideoTrack } from \"../../Service/Realtime/RealtimeVideoTrack\";\nimport { StreamID } from \"../../Service/Realtime/StreamID\";\nimport type { CameraControlling, CameraPreviewReadyHandler } from \"./CameraControlling\";\n\n/**\n * 协调摄像头采集和 SDK 本地预览。\n *\n * 采集由 RTC 层完成(只采不发),控制器把采集视频轨装入\n * `MediaStream` 后挂到预览视图;切换摄像头时原位替换流内视频轨,\n * 已挂载的视图无需重新绑定。麦克风在实时连接建立时由 RTC 层启动,\n * 本控制器维护配置与权限。\n */\nexport class CameraController implements CameraControlling {\n\n /**\n * 轨道标识\n */\n private static readonly localVideoTrackID = \"video0\";\n\n /**\n * 基础层组件\n */\n private readonly permissionManager: PermissionManaging;\n\n /**\n * 当前 RTC 提供方的采集与传输适配器。\n */\n private readonly rtcManager: RtcManaging;\n\n /**\n * 服务层组件\n */\n private readonly mediaService: MediaServicing;\n\n /**\n * 事件监听\n */\n private readonly errorListener: XmaxErrorListener;\n\n /**\n * 预览绑定且有效帧就绪后的通知入口。\n */\n private previewReadyHandler?: CameraPreviewReadyHandler;\n\n /**\n * 本地资源\n */\n private activeTrack?: RealtimeVideoTrack;\n\n /**\n * 本地摄像头预览使用的媒体流。\n */\n private previewStream?: MediaStream;\n\n /**\n * 更新本地预览镜像显示的回调。\n */\n private previewMirrorApplier?: (\n mirrored: boolean\n ) => void;\n\n /**\n * 预览状态\n */\n private hasCapturedFrame = false;\n\n /**\n * 摄像头预览是否已经挂载到视图。\n */\n private isPreviewAttached = false;\n\n /**\n * 当前相机流配置\n */\n private storedUseMicrophone = false;\n\n /**\n * 创建当前摄像头流时选择的亮度检查开关。\n */\n private storedFrameValidationEnabled = true;\n\n /**\n * 当前摄像头输出的顺时针旋转角度,更新规格时保留。\n */\n private storedRotation: CameraRotation = 0;\n\n /**\n * 正在更新规格的轨道,期间忽略底层替换旧原生轨产生的结束事件。\n */\n private formatUpdatingTrack?: RealtimeVideoTrack;\n\n /**\n * 创建相机控制器。\n *\n * @param options.permissionManager 权限管理组件(可替换,测试用)。\n * @param options.rtcManager RTC 引擎与采集组件(可替换,测试用)。\n * @param options.mediaService 模型输入尺寸规则组件(可替换,测试用)。\n * @param options.errorListener 运行期错误回调。\n */\n constructor(\n options?: {\n permissionManager?: PermissionManaging;\n rtcManager?: RtcManaging;\n mediaService?: MediaServicing;\n errorListener?: XmaxErrorListener;\n }\n ) {\n this.permissionManager = options?.permissionManager ?? new PermissionManager();\n this.rtcManager = options?.rtcManager ?? new TrtcRtcManager();\n this.mediaService = options?.mediaService ?? new MediaService();\n this.errorListener = options?.errorListener ?? (() => { });\n }\n\n /**\n * 当前活动的本地相机视频轨道;尚未创建时为空。\n */\n get currentTrack(): RealtimeVideoTrack | undefined {\n return this.activeTrack;\n }\n\n /**\n * 当前相机流是否配置为使用麦克风。\n */\n get useMicrophone(): boolean {\n return this.activeTrack !== undefined && this.storedUseMicrophone;\n }\n\n /**\n * 当前活动相机流是否启用发布前预热等待;没有活动流时为 false。\n */\n get isFrameValidationEnabled(): boolean {\n return this.activeTrack !== undefined && this.storedFrameValidationEnabled;\n }\n\n /**\n * 等待当前轨道预热完成(固定 200ms);禁用时跳过,取消或轨道被替换时拒绝。\n */\n async waitForValidCameraFrame(\n signal: AbortSignal\n ): Promise<void> {\n const track = this.activeTrack;\n const mediaTrack = track?.mediaStreamTrack;\n if (!mediaTrack)\n throw new XmaxError(XmaxErrorCode.mediaError, \"Camera capture is not running\");\n if (signal.aborted)\n throw new XmaxError(XmaxErrorCode.cancelled, \"Camera frame validation cancelled\");\n if (!this.isFrameValidationEnabled)\n return;\n\n await waitForValidCameraFrame(mediaTrack, signal);\n\n if (this.activeTrack !== track || track.mediaStreamTrack !== mediaTrack) {\n throw new XmaxError(XmaxErrorCode.cancelled, \"Camera track changed during warmup\");\n }\n }\n\n /**\n * 设置当前相机流的一次性内部就绪处理;条件为已收到有效帧且预览已绑定。\n */\n setPreviewReadyHandler(\n handler?: CameraPreviewReadyHandler\n ): void {\n this.previewReadyHandler = handler;\n const track = this.activeTrack;\n if (track) {\n this.notifyPreviewReady(track);\n }\n }\n\n /**\n * 创建并启动本地相机流。\n *\n * @param options.videoFormat 期望的输出尺寸、帧率和编码配置;尺寸按模型规则调整。\n * @param options.position 首次启动时使用的摄像头位置。\n * @param options.rotation 输入帧顺时针旋转角度,默认 0;videoFormat 为旋转后的规格,目前仅 Agora 支持非零角度。\n * @param options.useMicrophone 是否申请麦克风权限并允许实时连接时启动音频采集。\n * @param options.enableFrameValidation 是否在发布前等待相机预热(固定 200ms),默认 true;false 跳过等待。\n * @returns 包含本地相机视频轨道的媒体流。\n * @throws 已有活动相机流、格式无效、权限不足或采集启动失败时抛出错误。\n */\n async createLocalCameraStream(\n options: {\n videoFormat: RealtimeVideoFormat;\n position: CameraPosition;\n useMicrophone: boolean;\n rotation?: CameraRotation;\n enableFrameValidation?: boolean;\n }\n ): Promise<RealtimeMediaStream> {\n if (this.activeTrack) {\n throw new XmaxError(\n XmaxErrorCode.invalidConfiguration,\n \"Stop the current local camera stream before creating another one\",\n );\n }\n\n validateCameraRotation(options.rotation ?? 0);\n const resolvedFormat = this.resolveVideoFormat(options.videoFormat);\n const track = new RealtimeVideoTrack({\n id: CameraController.localVideoTrackID,\n videoFormat: resolvedFormat,\n position: options.position,\n });\n\n try {\n await this.permissionManager.ensureCameraPermission();\n if (options.useMicrophone) {\n await this.permissionManager.ensureMicrophonePermission();\n }\n\n await this.rtcManager.initialize();\n\n this.activeTrack = track;\n this.storedUseMicrophone = options.useMicrophone;\n this.storedFrameValidationEnabled = options.enableFrameValidation ?? true;\n this.storedRotation = options.rotation ?? 0;\n this.hasCapturedFrame = false;\n this.isPreviewAttached = false;\n this.registerPreviewBinding(track);\n\n const mediaTrack = await this.rtcManager.startCameraCapture({\n width: resolvedFormat.width,\n height: resolvedFormat.height,\n frameRate: resolvedFormat.fps,\n position: options.position,\n ...(options.rotation !== undefined ? { rotation: options.rotation } : {}),\n });\n track.mediaStreamTrack = mediaTrack;\n this.previewStream = new MediaStream([mediaTrack]);\n this.observeMediaTrack(track, mediaTrack);\n\n return new RealtimeMediaStream({ id: StreamID.local, videoTrack: track });\n } catch (error) {\n await this.stopLocalCameraStream();\n throw XmaxError.from(error);\n }\n }\n\n /**\n * 停止相机采集并销毁 RTC 引擎,释放当前轨道及本地预览资源。\n */\n async stopLocalCameraStream(): Promise<void> {\n const track = this.activeTrack;\n this.activeTrack = undefined;\n this.previewReadyHandler = undefined;\n this.previewStream = undefined;\n this.previewMirrorApplier = undefined;\n\n this.storedUseMicrophone = false;\n this.storedFrameValidationEnabled = true;\n this.storedRotation = 0;\n this.hasCapturedFrame = false;\n this.isPreviewAttached = false;\n\n await this.rtcManager.stopCameraCapture();\n await this.rtcManager.destroy();\n\n if (track) {\n VideoRenderRegistry.unregister(track);\n track.mediaStreamTrack = undefined;\n }\n }\n\n /**\n * 更新采集规格,底层成功后才替换预览轨和元数据;失败保留原有绑定。\n */\n async updateVideoFormat(\n videoFormat: RealtimeVideoFormat\n ): Promise<void> {\n const track = this.activeTrack;\n const position = track?.position;\n if (!track || !position || !track.mediaStreamTrack) {\n throw new XmaxError(XmaxErrorCode.invalidConfiguration, \"Camera preview is not started\");\n }\n const resolved = this.resolveVideoFormat(videoFormat);\n if (!this.rtcManager.updateCameraCapture) {\n throw new XmaxError(XmaxErrorCode.invalidConfiguration, \"The RTC provider does not support camera format updates\");\n }\n\n const previous = track.mediaStreamTrack;\n this.formatUpdatingTrack = track;\n try {\n const mediaTrack = await this.rtcManager.updateCameraCapture({\n width: resolved.width,\n height: resolved.height,\n frameRate: resolved.fps,\n position,\n rotation: this.storedRotation,\n });\n if (this.activeTrack !== track) {\n throw new XmaxError(XmaxErrorCode.cancelled, \"Camera stream changed during format update\");\n }\n\n track.updateVideoFormat(resolved);\n track.mediaStreamTrack = mediaTrack;\n if (previous !== mediaTrack) {\n this.previewStream?.removeTrack(previous);\n this.previewStream?.addTrack(mediaTrack);\n this.observeMediaTrack(track, mediaTrack);\n }\n } finally {\n if (this.formatUpdatingTrack === track) {\n this.formatUpdatingTrack = undefined;\n }\n if (this.activeTrack === track && track.mediaStreamTrack?.readyState === \"ended\") {\n this.errorListener(new XmaxError(XmaxErrorCode.mediaError, \"Camera capture ended during format update\"));\n }\n }\n }\n\n /**\n * 在前置和后置摄像头之间切换,保留当前视频轨道。\n *\n * 新视频轨原位替换预览流中的旧轨(旧轨由 RTC 层释放,此处不\n * 主动停止),已挂载的预览视图无需重新绑定。\n *\n * @returns 包含更新后相机轨道的媒体流。\n * @throws 相机流尚未启动或设备切换失败时抛出错误。\n */\n async switchCamera(): Promise<RealtimeMediaStream> {\n const track = this.activeTrack;\n const position = track?.position;\n if (!track || !position) {\n throw new XmaxError(\n XmaxErrorCode.rtcError,\n \"Local camera preview is not started\",\n );\n }\n\n const nextPosition =\n position === CameraPosition.front ? CameraPosition.back : CameraPosition.front;\n\n // 采集切换失败时旧设备保持不变。\n const mediaTrack = await this.rtcManager.switchCameraCapture(nextPosition);\n\n const previousTrack = track.mediaStreamTrack;\n track.updatePosition(nextPosition);\n track.mediaStreamTrack = mediaTrack;\n\n if (this.previewStream) {\n if (previousTrack) {\n this.previewStream.removeTrack(previousTrack);\n }\n this.previewStream.addTrack(mediaTrack);\n }\n this.previewMirrorApplier?.(nextPosition === CameraPosition.front);\n this.observeMediaTrack(track, mediaTrack);\n\n return new RealtimeMediaStream({ id: StreamID.local, videoTrack: track });\n }\n\n /**\n * 为相机轨道注册预览渲染绑定:attach 时挂流、镜像并标记预览已绑定。\n */\n private registerPreviewBinding(\n track: RealtimeVideoTrack\n ): void {\n VideoRenderRegistry.register(track, {\n attachHandler: (view) => {\n const stream = this.previewStream;\n if (!stream) {\n throw new XmaxError(\n XmaxErrorCode.mediaError,\n \"Camera capture is not running\",\n );\n }\n\n view.isMirrored = track.position === CameraPosition.front;\n view.setMediaStream(stream);\n this.previewMirrorApplier = (mirrored) => {\n view.isMirrored = mirrored;\n };\n\n this.isPreviewAttached = true;\n this.notifyPreviewReady(track);\n },\n detachHandler: (view) => {\n view.setMediaStream(null);\n this.previewMirrorApplier = undefined;\n this.isPreviewAttached = false;\n },\n });\n }\n\n /**\n * 监听媒体轨首帧与意外结束:首帧推进预览就绪,意外结束上报错误。\n */\n private observeMediaTrack(\n track: RealtimeVideoTrack,\n mediaTrack: MediaStreamTrack,\n ): void {\n const isCurrent = () =>\n this.activeTrack === track && track.mediaStreamTrack === mediaTrack;\n if (mediaTrack.muted) {\n mediaTrack.addEventListener(\n \"unmute\",\n () => {\n if (!isCurrent()) {\n return;\n }\n this.hasCapturedFrame = true;\n this.notifyPreviewReady(track);\n },\n { once: true },\n );\n } else {\n this.hasCapturedFrame = true;\n this.notifyPreviewReady(track);\n }\n mediaTrack.addEventListener(\"ended\", () => {\n if (!isCurrent() || this.formatUpdatingTrack === track) {\n return;\n }\n this.errorListener(\n new XmaxError(\n XmaxErrorCode.mediaError,\n \"Camera video track ended unexpectedly\",\n ),\n );\n });\n }\n\n /**\n * 满足\"已收到有效帧且预览已绑定\"时触发一次性就绪回调。\n */\n private notifyPreviewReady(\n track: RealtimeVideoTrack\n ): void {\n if (\n this.activeTrack !== track ||\n !this.hasCapturedFrame ||\n !this.isPreviewAttached ||\n !this.previewReadyHandler\n ) {\n return;\n }\n const handler = this.previewReadyHandler;\n this.previewReadyHandler = undefined;\n handler(() => this.activeTrack === track);\n }\n\n /**\n * 按模型输入规则校验并解析目标视频规格。\n */\n private resolveVideoFormat(\n videoFormat: RealtimeVideoFormat\n ): RealtimeVideoFormat {\n videoFormat.validate();\n const targetSize = this.mediaService.resolveMediaSize({\n width: videoFormat.width,\n height: videoFormat.height,\n }, \"camera\");\n const resolvedFormat = videoFormat.resized(targetSize.width, targetSize.height);\n resolvedFormat.validate();\n return resolvedFormat;\n }\n}\n","//\n// LocalVideoController.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\nimport { XmaxError, XmaxErrorCode } from \"../../Foundation/Errors/XmaxError\";\nimport { XmaxLogger } from \"../../Foundation/Logging/XmaxLogger\";\nimport type { MediaServicing } from \"../../Service/Media/MediaServicing\";\nimport { RealtimeMediaStream } from \"../../Service/Realtime/RealtimeMediaStream\";\nimport type { RealtimeVideoFormat } from \"../../Service/Realtime/RealtimeVideoFormat\";\nimport { RealtimeVideoTrack } from \"../../Service/Realtime/RealtimeVideoTrack\";\nimport { StreamID } from \"../../Service/Realtime/StreamID\";\nimport { VideoRenderRegistry, type VideoRenderTarget } from \"../../Service/Realtime/VideoRenderBinding\";\n\n/**\n * 单个文件播放器及其音视频输出资源。\n */\ninterface LocalVideoSource {\n\n /**\n * 用于读取或预览源画面的视频元素。\n */\n video: HTMLVideoElement;\n\n /**\n * 承载媒体帧的画布。\n */\n canvas: HTMLCanvasElement;\n\n /**\n * 画布绘制上下文。\n */\n context: CanvasRenderingContext2D;\n\n /**\n * 源视频的浏览器对象 URL,释放源时一并撤销。\n */\n url: string;\n\n /**\n * 取消当前媒体操作及相关等待。\n */\n abort: AbortController;\n\n /**\n * 当前绑定该媒体源的渲染目标。\n */\n targets: Set<VideoRenderTarget>;\n\n /**\n * 当前媒体源对应的 SDK 视频轨道。\n */\n track?: RealtimeVideoTrack;\n\n /**\n * 由当前媒体源输出的浏览器媒体流。\n */\n stream?: MediaStream;\n\n /**\n * 路由媒体音频使用的 Web Audio 上下文。\n */\n audioContext?: AudioContext;\n\n /**\n * 读取源音轨的 Web Audio 节点。\n */\n audioSource?: MediaElementAudioSourceNode;\n\n /**\n * 将 Web Audio 输出转换为媒体音轨的目标节点。\n */\n audioDestination?: MediaStreamAudioDestinationNode;\n\n /**\n * 下一次文件视频绘制任务的句柄。\n */\n drawTimer?: ReturnType<typeof setInterval>;\n\n /**\n * 媒体处理失败时通知上层。\n */\n onError?: () => void;\n}\n\n/**\n * 将本地文件解码为 Canvas 视频轨和 Web Audio 音频轨,不负责 RTC 发布。\n * 同一个播放器驱动音视频,预览始终静音;准备后停在首帧,生成时按配置播放。\n */\nexport class LocalVideoController {\n\n /**\n * 当前文件资源与运行期错误通知\n */\n private source?: LocalVideoSource;\n\n /**\n * 媒体处理失败时通知上层。\n */\n private readonly onError: (\n error: XmaxError\n ) => void;\n\n /**\n * 保存媒体错误处理器,不创建浏览器资源。\n */\n constructor(\n onError: (\n error: XmaxError\n ) => void = () => { }\n ) {\n this.onError = onError;\n }\n\n /**\n * 是否持有文件资源,包含尚未完成准备的播放器。\n */\n get isActive(): boolean {\n return this.source !== undefined;\n }\n\n /**\n * 准备完成的本地视频轨。\n */\n get currentTrack(): RealtimeVideoTrack | undefined {\n return this.source?.track;\n }\n\n /**\n * 文件音频输出;禁用音频时为空,无音轨文件的 Web Audio 输出为静音。\n */\n get audioTrack(): MediaStreamTrack | undefined {\n return this.source?.audioDestination?.stream.getAudioTracks()[0];\n }\n\n /**\n * 校验文件和格式,准备首帧与音频输出;应从用户点击事件直接调用以解锁播放。\n */\n async create(\n options: {\n file: Blob;\n videoFormat: RealtimeVideoFormat;\n loop?: boolean;\n includeAudio?: boolean;\n },\n mediaService: MediaServicing,\n signal: AbortSignal\n ): Promise<RealtimeMediaStream> {\n if (this.source) {\n throw new XmaxError(XmaxErrorCode.invalidConfiguration, \"Close the current local video before creating another one\");\n }\n if (!(options.file instanceof Blob) || options.file.size === 0) {\n throw new XmaxError(XmaxErrorCode.invalidConfiguration, \"Local video requires a non-empty File or Blob\");\n }\n options.videoFormat.validate();\n if (signal.aborted) {\n throw this.cancelled();\n }\n if (typeof document === \"undefined\") {\n throw new XmaxError(XmaxErrorCode.mediaError, \"Local video requires a browser\");\n }\n\n const video = document.createElement(\"video\");\n const canvas = document.createElement(\"canvas\");\n const context = canvas.getContext(\"2d\");\n if (!context || typeof canvas.captureStream !== \"function\") {\n throw new XmaxError(XmaxErrorCode.mediaError, \"Canvas video capture is unavailable\");\n }\n const source: LocalVideoSource = {\n video, canvas, context, url: URL.createObjectURL(options.file),\n abort: new AbortController(), targets: new Set(),\n };\n this.source = source;\n\n try {\n video.playsInline = true;\n video.loop = options.loop ?? true;\n video.preload = \"auto\";\n video.volume = 1;\n video.muted = options.includeAudio === false;\n if (options.includeAudio !== false) {\n if (typeof AudioContext === \"undefined\") {\n throw new XmaxError(XmaxErrorCode.mediaError, \"Web Audio is unavailable; disable file audio or use a supported browser\");\n }\n const audio = new AudioContext();\n source.audioContext = audio;\n source.audioSource = audio.createMediaElementSource(video);\n source.audioDestination = audio.createMediaStreamDestination();\n source.audioSource.connect(source.audioDestination);\n // 不连接扬声器节点;静音预览不会改变送往 RTC 的原始音频。\n }\n video.src = source.url;\n\n // 在调用方点击的同一轮事件内解锁播放器和音频上下文,然后回到首帧等待生成。\n const resume = source.audioContext?.resume();\n const playing = this.play(source, signal);\n await this.wait(source, Promise.all([resume, playing]), signal);\n video.pause();\n if (!video.videoWidth || !video.videoHeight || !Number.isFinite(video.duration) || video.duration <= 0) {\n throw new XmaxError(XmaxErrorCode.mediaError, \"The file has no playable finite video stream\");\n }\n const size = mediaService.resolveMediaSize({ width: video.videoWidth, height: video.videoHeight }, \"video\");\n const format = options.videoFormat.resized(size.width, size.height);\n canvas.width = size.width;\n canvas.height = size.height;\n await this.rewind(source, signal);\n\n this.draw(source);\n source.stream = canvas.captureStream(format.fps);\n const nativeTrack = source.stream.getVideoTracks()[0];\n if (!nativeTrack) {\n throw new XmaxError(XmaxErrorCode.mediaError, \"Canvas did not provide a video track\");\n }\n const track = new RealtimeVideoTrack({ id: \"local-video\", videoFormat: format });\n track.mediaStreamTrack = nativeTrack;\n source.track = track;\n this.draw(source);\n // 暂停和播放结束后继续输出静止画面,避免发布后必须等待文件播放才能协商视频。\n source.drawTimer = setInterval(() => {\n if (this.source !== source) {\n return;\n }\n try {\n this.draw(source);\n } catch {\n this.handleError(source);\n }\n }, 1000 / format.fps);\n source.onError = () => this.handleError(source);\n video.addEventListener(\"error\", source.onError);\n\n VideoRenderRegistry.register(track, {\n attachHandler: (view) => {\n view.isMirrored = false;\n view.setMediaStream(source.stream!);\n source.targets.add(view);\n },\n detachHandler: (view) => {\n source.targets.delete(view);\n view.setMediaStream(null);\n },\n });\n return new RealtimeMediaStream({ id: StreamID.local, videoTrack: track });\n } catch (error) {\n await this.stop();\n throw XmaxError.from(error);\n }\n }\n\n /**\n * 开始信令发送成功后,从文件开头同步启动音视频;不等待远端首帧。\n */\n async start(\n signal: AbortSignal\n ): Promise<void> {\n const source = this.source;\n if (!source?.track) {\n throw new XmaxError(XmaxErrorCode.invalidConfiguration, \"Local video is not prepared\");\n }\n if (signal.aborted) {\n throw this.cancelled();\n }\n try {\n await this.rewind(source, signal);\n await this.wait(source, Promise.all([source.audioContext?.resume(), this.play(source, signal)]), signal);\n } catch (error) {\n source.video.pause();\n throw error;\n }\n }\n\n /**\n * 暂停文件音视频,保留当前预览;重新开始生成时回到文件开头。\n */\n pause(): void {\n this.source?.video.pause();\n }\n\n /**\n * 释放播放器、预览、定时器、轨道、音频上下文和对象 URL。\n */\n async stop(): Promise<void> {\n const source = this.source;\n this.source = undefined;\n if (!source) {\n return;\n }\n source.abort.abort();\n clearInterval(source.drawTimer);\n if (source.onError) {\n source.video.removeEventListener(\"error\", source.onError);\n }\n if (source.track) {\n VideoRenderRegistry.unregister(source.track);\n source.track.mediaStreamTrack = undefined;\n }\n for (const view of source.targets) {\n try {\n view.setMediaStream(null);\n } catch {\n // 单个视图解绑失败不阻断其他资源释放。\n }\n }\n source.targets.clear();\n source.video.pause();\n source.video.removeAttribute(\"src\");\n source.video.load();\n source.stream?.getTracks().forEach(track => track.stop());\n source.audioDestination?.stream.getTracks().forEach(track => track.stop());\n source.audioSource?.disconnect();\n source.audioDestination?.disconnect();\n URL.revokeObjectURL(source.url);\n await source.audioContext?.close().catch(() => { });\n }\n\n /**\n * 播放被浏览器拒绝时给出可操作错误;关闭后的迟到播放立即暂停。\n */\n private play(\n source: LocalVideoSource,\n signal: AbortSignal\n ): Promise<void> {\n return source.video.play().then(() => {\n if (this.source !== source || source.abort.signal.aborted || signal.aborted) {\n source.video.pause();\n throw this.cancelled();\n }\n }).catch(error => {\n if (error instanceof XmaxError) {\n throw error;\n }\n throw new XmaxError(XmaxErrorCode.mediaError, \"Cannot play local video; call from a user click and use a browser-supported video codec\");\n });\n }\n\n /**\n * 等待 seek 完成,确保重新生成从首帧开始,而非使用旧缓存画面。\n */\n private async rewind(\n source: LocalVideoSource,\n signal: AbortSignal\n ): Promise<void> {\n if (source.video.currentTime === 0) {\n return;\n }\n let seeked!: () => void;\n const ready = new Promise<void>(resolve => {\n seeked = resolve;\n });\n source.video.addEventListener(\"seeked\", seeked, { once: true });\n try {\n source.video.currentTime = 0;\n await this.wait(source, ready, signal);\n } finally {\n source.video.removeEventListener(\"seeked\", seeked);\n }\n }\n\n /**\n * 给浏览器准备和播放操作设置 10 秒上限,同时响应关闭、取消和解码错误。\n */\n private wait(\n source: LocalVideoSource,\n operation: Promise<unknown>,\n signal: AbortSignal\n ): Promise<void> {\n return new Promise((resolve, reject) => {\n const finish = (error?: unknown) => {\n clearTimeout(timer);\n signal.removeEventListener(\"abort\", abort);\n source.abort.signal.removeEventListener(\"abort\", abort);\n source.video.removeEventListener(\"error\", failed);\n if (error) {\n reject(error);\n } else {\n resolve();\n }\n };\n const abort = () => finish(this.cancelled());\n const failed = () => finish(new XmaxError(XmaxErrorCode.mediaError, \"The browser cannot decode this video file\"));\n const timer = setTimeout(() => finish(new XmaxError(XmaxErrorCode.timeout, \"Local video preparation timed out; start from a user click and check the video codec\")), 10_000);\n signal.addEventListener(\"abort\", abort, { once: true });\n source.abort.signal.addEventListener(\"abort\", abort, { once: true });\n source.video.addEventListener(\"error\", failed, { once: true });\n operation.then(() => finish(), finish);\n if (signal.aborted || source.abort.signal.aborted) {\n abort();\n }\n });\n }\n\n /**\n * 将完整画面缩放到已解析的媒体尺寸,不补模型画布、不按设备方向转置文件尺寸。\n */\n private draw(\n source: LocalVideoSource\n ): void {\n const { video, canvas, context } = source;\n if (video.readyState < 2) {\n return;\n }\n context.fillStyle = \"black\";\n context.fillRect(0, 0, canvas.width, canvas.height);\n context.drawImage(video, 0, 0, canvas.width, canvas.height);\n }\n\n /**\n * 解码或绘制失败时停止媒体推进,并报告一次运行期错误。\n */\n private handleError(\n source: LocalVideoSource\n ): void {\n if (this.source !== source || source.drawTimer === undefined) {\n return;\n }\n clearInterval(source.drawTimer);\n source.drawTimer = undefined;\n source.video.pause();\n XmaxLogger.realtime.warning(() => \"本地视频解码失败 (Local Video Decode Failed)\");\n this.onError(new XmaxError(XmaxErrorCode.mediaError, \"Local video decoding failed\"));\n }\n\n /**\n * 构造统一的媒体操作取消错误。\n */\n private cancelled(): XmaxError {\n return new XmaxError(XmaxErrorCode.cancelled, \"Local video operation was cancelled\");\n }\n}\n","//\n// ExternalVideoController.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\nimport { XmaxError, XmaxErrorCode } from \"../../Foundation/Errors/XmaxError\";\nimport type { MediaServicing } from \"../../Service/Media/MediaServicing\";\nimport { RealtimeMediaStream } from \"../../Service/Realtime/RealtimeMediaStream\";\nimport type { RealtimeVideoFormat } from \"../../Service/Realtime/RealtimeVideoFormat\";\nimport { RealtimeVideoTrack } from \"../../Service/Realtime/RealtimeVideoTrack\";\nimport { StreamID } from \"../../Service/Realtime/StreamID\";\n\n/**\n * 仅持有外部视频轨的副本;不采集设备、不处理画面、不创建预览。\n */\nexport class ExternalVideoController {\n\n /**\n * 当前媒体源对应的 SDK 视频轨道。\n */\n private track?: RealtimeVideoTrack;\n\n /**\n * 媒体源结束时释放资源并通知上层。\n */\n private readonly onEnded = () => {\n this.onError(new XmaxError(XmaxErrorCode.mediaError, \"External video source ended\"));\n };\n\n /**\n * 设置外部视频轨结束时的错误通知。\n */\n constructor(\n private readonly onError: (\n error: XmaxError\n ) => void\n ) { }\n\n /**\n * 当前由 SDK 持有的外部视频克隆轨。\n */\n get currentTrack(): RealtimeVideoTrack | undefined {\n return this.track;\n }\n\n /**\n * 校验并克隆外部视频轨,按统一媒体规则解析上行基准,不修改原始轨道像素。\n *\n * @param options 外部 live 视频轨及原始媒体规格。\n * @param mediaService 当前模型的媒体尺寸解析服务。\n * @returns 持有轨道副本和解析后固定媒体规格的本地流。\n */\n create(\n options: {\n videoTrack: MediaStreamTrack;\n videoFormat: RealtimeVideoFormat\n },\n mediaService: MediaServicing\n ): RealtimeMediaStream {\n if (this.track)\n throw new XmaxError(XmaxErrorCode.invalidConfiguration, \"Close the current external video first\");\n const { videoTrack, videoFormat } = options;\n if (!videoTrack || videoTrack.kind !== \"video\" || videoTrack.readyState !== \"live\" || typeof videoTrack.clone !== \"function\") {\n throw new XmaxError(XmaxErrorCode.invalidConfiguration, \"External video requires a live video MediaStreamTrack\");\n }\n const size = mediaService.resolveMediaSize(videoFormat, \"external\");\n const format = videoFormat.resized(size.width, size.height);\n format.validate();\n const nativeTrack = videoTrack.clone();\n if (nativeTrack.readyState !== \"live\") {\n nativeTrack.stop();\n throw new XmaxError(XmaxErrorCode.mediaError, \"External video source ended during setup\");\n }\n const track = new RealtimeVideoTrack({ id: \"external-video\", videoFormat: format });\n track.mediaStreamTrack = nativeTrack;\n nativeTrack.addEventListener(\"ended\", this.onEnded);\n this.track = track;\n return new RealtimeMediaStream({ id: StreamID.local, videoTrack: track });\n }\n\n /**\n * 解除结束事件监听并释放 SDK 持有的克隆轨。\n */\n stop(): void {\n const nativeTrack = this.track?.mediaStreamTrack;\n this.track = undefined;\n nativeTrack?.removeEventListener(\"ended\", this.onEnded);\n nativeTrack?.stop();\n }\n}\n","//\n// MediaOperation.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\nimport { XmaxError, XmaxErrorCode } from \"../Errors/XmaxError\";\n\n/**\n * 有界等待媒体准备或接入方回调;取消后仍接住迟到的拒绝,清理所有监听和计时器。\n */\nexport async function waitForMediaOperation<T>(\n operation: Promise<T>, signal: AbortSignal, timeoutMs: number, description: string,\n): Promise<T> {\n let timer: ReturnType<typeof setTimeout> | undefined;\n let abort: (() => void) | undefined;\n try {\n const interrupted = new Promise<never>((_, reject) => {\n abort = () => reject(new XmaxError(XmaxErrorCode.cancelled, `${description} cancelled`));\n timer = setTimeout(() => reject(new XmaxError(XmaxErrorCode.timeout, `${description} timed out`)), timeoutMs);\n signal.addEventListener(\"abort\", abort, { once: true });\n if (signal.aborted) {\n abort();\n }\n });\n return await Promise.race([operation, interrupted]);\n } finally {\n clearTimeout(timer);\n if (abort) {\n signal.removeEventListener(\"abort\", abort);\n }\n }\n}\n","//\n// ExternalAudioWorklet.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\n/**\n * 按 AudioContext 采样时钟调度外部单声道 PCM;线性重采样,间断输出静音。\n * 主线程限制在途包预算,线程内释放消费完的包并反馈预算,不连接扬声器。\n */\nexport const EXTERNAL_AUDIO_WORKLET = `\nclass XmaxExternalAudioProcessor extends AudioWorkletProcessor {\n constructor() {\n super();\n this.queue = [];\n this.epoch = 0;\n this.port.onmessage = ({ data }) => {\n if (data.type === \"reset\") {\n this.queue = [];\n this.epoch = data.epoch;\n } else if (data.epoch === this.epoch) {\n this.queue.push(data);\n }\n };\n }\n\n process(_inputs, outputs) {\n const output = outputs[0][0];\n output.fill(0);\n for (let i = 0; i < output.length; i++) {\n const time = currentFrame + i;\n while (this.queue.length && time >= this.queue[0].end) {\n const packet = this.queue.shift();\n this.port.postMessage({ id: packet.id, epoch: packet.epoch });\n }\n const packet = this.queue[0];\n if (!packet || time < packet.start) continue;\n const position = (time - packet.start) * packet.rate / sampleRate;\n const left = Math.floor(position);\n const a = packet.samples[Math.min(left, packet.samples.length - 1)];\n const b = packet.samples[Math.min(left + 1, packet.samples.length - 1)];\n output[i] = a + (b - a) * (position - left);\n }\n return true;\n }\n}\nregisterProcessor(\"xmax-external-audio\", XmaxExternalAudioProcessor);\n`;\n","//\n// ExternalAudioFrameSource.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\nimport { XmaxError, XmaxErrorCode } from \"../../Errors/XmaxError\";\nimport { waitForMediaOperation } from \"../MediaOperation\";\nimport { EXTERNAL_AUDIO_WORKLET } from \"./ExternalAudioWorklet\";\nimport type { RealtimeExternalAudioFrame } from \"./RealtimeExternalAudioFrame\";\n\n/**\n * 将外部 PCM 混成单声道并生成 RTC 音轨,不采集设备或连接扬声器。\n * 浏览器可能将输出轨声明为双声道,此时两路内容相同。\n */\nexport class ExternalAudioFrameSource {\n\n /**\n * 浏览器音频图与输出轨道\n */\n private context?: AudioContext;\n private processor?: AudioWorkletNode;\n private destination?: MediaStreamAudioDestinationNode;\n\n /**\n * 输出轨意外结束时通知源绑定;主动释放前移除监听。\n */\n private onEnded?: () => void;\n\n /**\n * 固定输入格式、时间连续性及发布状态\n */\n private format?: string;\n private nextInputTime?: number;\n private published = false;\n\n /**\n * 有界在途包预算;epoch 使清空前的端口反馈失效\n */\n private sequence = 0;\n private epoch = 0;\n private pendingSeconds = 0;\n private readonly pending = new Map<number, number>();\n\n /**\n * 音频采样时钟,单位为微秒,用于和视频共用源时间映射。\n */\n get nowUs(): number {\n return (this.context?.currentTime ?? 0) * 1_000_000;\n }\n\n /**\n * 由 SDK 拥有的 RTC 输入轨。\n */\n get track(): MediaStreamTrack | undefined {\n return this.destination?.stream.getAudioTracks()[0];\n }\n\n /**\n * 从用户手势准备音频图;加载或自动播放策略失败时拒绝并释放资源。\n */\n async prepare(signal: AbortSignal, onError: (error: XmaxError) => void): Promise<void> {\n if (typeof AudioContext === \"undefined\" || typeof AudioWorkletNode === \"undefined\") {\n throw new XmaxError(XmaxErrorCode.mediaError, \"External PCM input requires AudioWorklet support\");\n }\n const context = new AudioContext({ sampleRate: 48000 });\n this.context = context;\n let url: string | undefined;\n try {\n if (!context.audioWorklet) {\n throw new XmaxError(XmaxErrorCode.mediaError, \"External PCM input requires a secure context with AudioWorklet\");\n }\n url = URL.createObjectURL(new Blob([EXTERNAL_AUDIO_WORKLET], { type: \"text/javascript\" }));\n await waitForMediaOperation(Promise.all([context.resume(), context.audioWorklet.addModule(url)]), signal, 3000,\n \"External audio initialization; start from a user gesture\");\n if (context.state !== \"running\") {\n throw new XmaxError(XmaxErrorCode.mediaError, \"External audio must be initialized from a user gesture\");\n }\n\n const destination = new MediaStreamAudioDestinationNode(context, { channelCount: 1, channelCountMode: \"explicit\" });\n this.destination = destination;\n const processor = new AudioWorkletNode(context, \"xmax-external-audio\", {\n numberOfInputs: 0, numberOfOutputs: 1, outputChannelCount: [1],\n });\n this.processor = processor;\n processor.port.onmessage = ({ data }) => {\n if (this.processor !== processor || data?.epoch !== this.epoch) {\n return;\n }\n this.pendingSeconds = Math.max(0, this.pendingSeconds - (this.pending.get(data.id) ?? 0));\n this.pending.delete(data.id);\n };\n processor.onprocessorerror = () => onError(new XmaxError(XmaxErrorCode.mediaError, \"External audio processor failed\"));\n processor.connect(destination);\n if (!this.track || this.track.readyState !== \"live\") {\n throw new XmaxError(XmaxErrorCode.mediaError, \"External audio did not produce a live track\");\n }\n this.onEnded = () => onError(new XmaxError(XmaxErrorCode.mediaError, \"External audio output ended\"));\n this.track.addEventListener(\"ended\", this.onEnded);\n } catch (error) {\n await this.close();\n throw error;\n } finally {\n if (url) {\n URL.revokeObjectURL(url);\n }\n }\n }\n\n /**\n * 只在实际发布期间消费输入;切换状态立即丢弃旧音频,不为重连积压。\n */\n setPublished(published: boolean): void {\n if (this.published === published) {\n return;\n }\n this.published = published;\n this.reset();\n }\n\n /**\n * 清空线程队列和在途预算,不改变输入格式或发布状态。\n */\n reset(): void {\n this.pending.clear();\n this.pendingSeconds = 0;\n this.processor?.port.postMessage({ type: \"reset\", epoch: ++this.epoch });\n }\n\n /**\n * 同步复制 PCM、混声道并交给采样线程;无效时间或重叠包忽略,格式变化抛错。\n */\n push(frame: RealtimeExternalAudioFrame, mapTime: (timestamp: number) => number): void {\n const timestamp = frame.timestamp;\n if (!Number.isSafeInteger(timestamp) || timestamp < 0) {\n return;\n }\n const { sampleRate: rate, numberOfChannels: channels, numberOfFrames: frames, format } = frame;\n if (!format || !Number.isInteger(rate) || rate < 8000 || rate > 192000 ||\n !Number.isInteger(channels) || channels < 1 || channels > 8 || !Number.isInteger(frames) || frames < 1 || frames > rate) {\n throw new XmaxError(XmaxErrorCode.mediaError, \"External audio requires live PCM at 8–192 kHz, 1–8 channels, at most one second per packet\");\n }\n const identity = `${format}/${rate}/${channels}`;\n if (this.format && this.format !== identity) {\n throw new XmaxError(XmaxErrorCode.mediaError, \"External audio format must remain unchanged during a source binding\");\n }\n this.format = identity;\n if (this.nextInputTime !== undefined && timestamp < this.nextInputTime - 500_000 / rate) {\n return;\n }\n const seconds = frames / rate;\n this.nextInputTime = timestamp + seconds * 1_000_000;\n const context = this.context;\n const processor = this.processor;\n if (!this.published || !context || !processor) {\n return;\n }\n if (context.state !== \"running\") {\n throw new XmaxError(XmaxErrorCode.mediaError, \"External audio context is suspended\");\n }\n const time = mapTime(timestamp);\n if (time + seconds * 1_000_000 <= this.nowUs) {\n return;\n }\n if (time > this.nowUs + 2_000_000 || this.pendingSeconds + seconds > 2 || this.pending.size >= 256) {\n throw new XmaxError(XmaxErrorCode.mediaError, \"External audio must be paced in real time; pending audio exceeds two seconds or 256 packets\");\n }\n\n const samples = new Float32Array(frames);\n const plane = new Float32Array(frames);\n for (let channel = 0; channel < channels; channel++) {\n frame.copyTo(plane, { planeIndex: channel, format: \"f32-planar\" });\n for (let i = 0; i < frames; i++) {\n const value = Number.isFinite(plane[i]) ? plane[i]! : 0;\n samples[i]! += Math.max(-1, Math.min(1, value)) / channels;\n }\n }\n const id = ++this.sequence;\n const start = Math.round(time * context.sampleRate / 1_000_000);\n this.pending.set(id, seconds);\n this.pendingSeconds += seconds;\n processor.port.postMessage({ type: \"packet\", id, epoch: this.epoch, samples, rate,\n start, end: start + Math.round(seconds * context.sampleRate) }, [samples.buffer]);\n }\n\n /**\n * 停止 SDK 音轨、处理器和上下文;不触碰接入方原始音频资源。\n */\n async close(): Promise<void> {\n this.published = false;\n const context = this.context;\n this.context = undefined;\n if (this.onEnded) {\n this.track?.removeEventListener(\"ended\", this.onEnded);\n this.onEnded = undefined;\n }\n this.destination?.stream.getTracks().forEach(track => track.stop());\n this.destination?.disconnect();\n this.destination = undefined;\n if (this.processor) {\n this.processor.onprocessorerror = null;\n this.processor.port.onmessage = null;\n this.processor.port.close();\n this.processor.disconnect();\n this.processor = undefined;\n }\n this.pending.clear();\n this.pendingSeconds = 0;\n if (context && context.state !== \"closed\") {\n await waitForMediaOperation(context.close(), new AbortController().signal, 2000, \"External audio close\");\n }\n }\n}\n","//\n// ExternalFrameSink.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\nimport { XmaxError, XmaxErrorCode } from \"../../Foundation/Errors/XmaxError\";\nimport type { ExternalAudioFrameSource } from \"../../Foundation/Media/Audio/ExternalAudioFrameSource\";\nimport type { RealtimeExternalAudioFrame } from \"../../Foundation/Media/Audio/RealtimeExternalAudioFrame\";\nimport type { RealtimeVideoFormat } from \"../../Service/Realtime/RealtimeVideoFormat\";\nimport type { RealtimeExternalFrameSink } from \"./RealtimeExternalVideoSource\";\n\n/**\n * 校验源时间戳,按共享采样时钟调度帧;解绑后旧 sink 永久失效。\n */\nexport class ExternalFrameSink implements RealtimeExternalFrameSink {\n\n /**\n * 绑定状态、连续性和源时间到浏览器时钟的映射\n */\n private active = true;\n private lastVideoTime = -1;\n private anchor?: { source: number; output: number };\n\n /**\n * 保留下一张待呈现帧和最新后续帧;不能用新帧反复推迟已安排的呈现。\n */\n private pending?: VideoFrame;\n private next?: { frame: VideoFrame; time: number };\n private timer?: ReturnType<typeof setTimeout>;\n\n /**\n * 注入固定输入和媒体规格、画布输出、可选音频及不可恢复错误通知。\n */\n constructor(\n readonly videoFormat: RealtimeVideoFormat,\n private readonly sourceFormat: RealtimeVideoFormat,\n private readonly context: CanvasRenderingContext2D,\n private readonly track: CanvasCaptureMediaStreamTrack,\n private readonly audio: ExternalAudioFrameSource | undefined,\n private readonly onError: (error: XmaxError) => void,\n ) {}\n\n /**\n * 断开时继续接受预览帧;释放或源失败后拒绝后续输入。\n */\n get isRequestingFrames(): boolean {\n return this.active;\n }\n\n /**\n * 同步克隆视频,提交方可在返回后释放原帧;时间戳不合法时忽略。\n */\n pushVideo(frame: VideoFrame): void {\n if (!this.active || !Number.isSafeInteger(frame.timestamp) || frame.timestamp < 0 || frame.timestamp <= this.lastVideoTime) {\n return;\n }\n try {\n if (frame.displayWidth !== this.sourceFormat.width || frame.displayHeight !== this.sourceFormat.height) {\n throw new XmaxError(XmaxErrorCode.mediaError, \"External video frame dimensions must match source.videoFormat\");\n }\n const time = this.mapTime(frame.timestamp);\n const copy = frame.clone();\n this.lastVideoTime = frame.timestamp;\n if (this.pending) {\n this.next?.frame.close();\n this.next = { frame: copy, time };\n } else {\n this.scheduleVideo(copy, time);\n }\n } catch (error) {\n this.reportError(XmaxError.from(error));\n }\n }\n\n /**\n * 到时呈现当前帧,再安排最新后续帧;最多持有两个副本。\n */\n private scheduleVideo(copy: VideoFrame, time: number): void {\n this.pending = copy;\n this.timer = setTimeout(() => {\n this.pending = undefined;\n this.timer = undefined;\n try {\n if (this.active) {\n this.context.drawImage(copy, 0, 0, this.videoFormat.width, this.videoFormat.height);\n this.track.requestFrame?.();\n }\n } catch (error) {\n this.reportError(XmaxError.from(error));\n } finally {\n copy.close();\n }\n\n const next = this.next;\n this.next = undefined;\n if (this.active && next) {\n this.scheduleVideo(next.frame, next.time);\n }\n }, Math.max(0, (time - this.nowUs()) / 1000));\n }\n\n /**\n * 复制音频并使用与视频相同的时间映射;未声明音频时忽略。\n */\n pushAudio(frame: RealtimeExternalAudioFrame): void {\n if (!this.active || !this.audio) {\n return;\n }\n try {\n this.audio.push(frame, timestamp => this.mapTime(timestamp));\n } catch (error) {\n this.reportError(XmaxError.from(error));\n }\n }\n\n /**\n * 首次失败立即使 sink 失效,再通知上层释放连接和本地媒体。\n */\n reportError(error: Error): void {\n if (!this.active) {\n return;\n }\n this.invalidate();\n this.onError(new XmaxError(XmaxErrorCode.mediaError, `External source failed: ${error.message}`));\n }\n\n /**\n * 释放未呈现副本并停止音频排队,不接管原帧或接入方设备。\n */\n invalidate(): void {\n this.active = false;\n this.clearVideo();\n this.audio?.setPublished(false);\n }\n\n /**\n * 清理当前等待呈现的副本。\n */\n private clearVideo(): void {\n clearTimeout(this.timer);\n this.timer = undefined;\n this.pending?.close();\n this.pending = undefined;\n this.next?.frame.close();\n this.next = undefined;\n }\n\n /**\n * 有音频时以采样时钟为基准,纯视频使用浏览器单调时钟。\n */\n private nowUs(): number {\n return this.audio?.nowUs ?? performance.now() * 1000;\n }\n\n /**\n * 共用 20ms 调度余量;源暂停或线程长时间阻塞后重新锚定两轨,不重放旧队列。\n */\n private mapTime(timestamp: number): number {\n const now = this.nowUs();\n let time = this.anchor ? this.anchor.output + timestamp - this.anchor.source : now + 20_000;\n if (!this.anchor || time < now - 250_000) {\n this.clearVideo();\n this.audio?.reset();\n this.anchor = { source: timestamp, output: now + 20_000 };\n time = this.anchor.output;\n }\n if (time > now + 2_000_000) {\n throw new XmaxError(XmaxErrorCode.mediaError, \"External frames must use a shared monotonic timeline and be paced in real time\");\n }\n return time;\n }\n}\n","//\n// ExternalFrameSourceController.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\nimport { XmaxError, XmaxErrorCode } from \"../../Foundation/Errors/XmaxError\";\nimport { XmaxLogger } from \"../../Foundation/Logging/XmaxLogger\";\nimport { ExternalAudioFrameSource } from \"../../Foundation/Media/Audio/ExternalAudioFrameSource\";\nimport { waitForMediaOperation } from \"../../Foundation/Media/MediaOperation\";\nimport type { MediaServicing } from \"../../Service/Media/MediaServicing\";\nimport { RealtimeMediaStream } from \"../../Service/Realtime/RealtimeMediaStream\";\nimport { RealtimeVideoTrack } from \"../../Service/Realtime/RealtimeVideoTrack\";\nimport { StreamID } from \"../../Service/Realtime/StreamID\";\nimport { VideoRenderRegistry, type VideoRenderTarget } from \"../../Service/Realtime/VideoRenderBinding\";\nimport { ExternalFrameSink } from \"./ExternalFrameSink\";\nimport type { RealtimeExternalVideoSource } from \"./RealtimeExternalVideoSource\";\n\n/**\n * 一次外部源绑定及 SDK 创建的媒体资源。\n */\ninterface ExternalBinding {\n\n /**\n * 接入方源与生命周期取消。\n */\n source: RealtimeExternalVideoSource;\n abort: AbortController;\n\n /**\n * SDK 拥有的视频输出、可选音频和输入端。\n */\n canvas: HTMLCanvasElement;\n stream: MediaStream;\n track: RealtimeVideoTrack;\n audio?: ExternalAudioFrameSource;\n sink?: ExternalFrameSink;\n\n /**\n * 预览目标和轨道结束通知。\n */\n targets: Set<VideoRenderTarget>;\n onEnded?: () => void;\n}\n\n/**\n * 外部帧源的绑定、预览与释放;不拥有接入方采集设备和输入帧。\n */\nexport class ExternalFrameSourceController {\n\n /**\n * 包含仍在准备的源,防止并发覆盖。\n */\n private binding?: ExternalBinding;\n\n /**\n * 注入不可恢复错误的生命周期通知。\n */\n constructor(private readonly onError: (error: XmaxError) => void) {}\n\n /**\n * 是否持有准备中或已绑定的源。\n */\n get isActive(): boolean {\n return this.binding !== undefined;\n }\n\n /**\n * 可预览、发布的视频轨。\n */\n get currentTrack(): RealtimeVideoTrack | undefined {\n return this.binding?.track;\n }\n\n /**\n * 源声明音频时创建的可发布音轨。\n */\n get audioTrack(): MediaStreamTrack | undefined {\n return this.binding?.audio?.track;\n }\n\n /**\n * 准备媒体输出后调用 source.start;不等待首帧,失败或取消时调用 stop 解绑。\n */\n async create(source: RealtimeExternalVideoSource, media: MediaServicing, signal: AbortSignal): Promise<RealtimeMediaStream> {\n if (this.binding) {\n throw new XmaxError(XmaxErrorCode.invalidConfiguration, \"Close the current external frame source first\");\n }\n if (!source || typeof source.start !== \"function\" || typeof source.stop !== \"function\" ||\n (source.hasAudio !== undefined && typeof source.hasAudio !== \"boolean\")) {\n throw new XmaxError(XmaxErrorCode.invalidConfiguration, \"A valid external video source is required\");\n }\n source.videoFormat.validate();\n const size = media.resolveMediaSize(source.videoFormat, \"external\");\n const sourceFormat = source.videoFormat.resized(source.videoFormat.width, source.videoFormat.height);\n const format = sourceFormat.resized(size.width, size.height);\n if (signal.aborted) {\n throw new XmaxError(XmaxErrorCode.cancelled, \"External source creation cancelled\");\n }\n if (typeof document === \"undefined\" || typeof VideoFrame === \"undefined\") {\n throw new XmaxError(XmaxErrorCode.mediaError, \"External frames require a browser with VideoFrame support\");\n }\n const canvas = document.createElement(\"canvas\");\n canvas.width = size.width;\n canvas.height = size.height;\n const context = canvas.getContext(\"2d\", { alpha: false });\n if (!context || typeof canvas.captureStream !== \"function\") {\n throw new XmaxError(XmaxErrorCode.mediaError, \"Canvas video capture is unavailable\");\n }\n const stream = canvas.captureStream(format.fps);\n const native = stream.getVideoTracks()[0] as CanvasCaptureMediaStreamTrack | undefined;\n if (!native || native.readyState !== \"live\") {\n stream.getTracks().forEach(track => track.stop());\n throw new XmaxError(XmaxErrorCode.mediaError, \"Canvas did not provide a live external video track\");\n }\n const track = new RealtimeVideoTrack({ id: \"external-frames\", videoFormat: format });\n track.mediaStreamTrack = native;\n const binding: ExternalBinding = { source, canvas, stream, track, abort: new AbortController(), targets: new Set() };\n this.binding = binding;\n const abort = () => binding.abort.abort();\n signal.addEventListener(\"abort\", abort, { once: true });\n if (signal.aborted) {\n abort();\n }\n try {\n if (source.hasAudio) {\n binding.audio = new ExternalAudioFrameSource();\n await binding.audio.prepare(binding.abort.signal, error => {\n if (binding.sink) {\n binding.sink.reportError(error);\n } else {\n binding.abort.abort();\n if (this.binding === binding) {\n this.onError(error);\n }\n }\n });\n }\n const sink = new ExternalFrameSink(format, sourceFormat, context, native, binding.audio, error => {\n if (this.binding === binding) {\n this.onError(error);\n }\n });\n binding.sink = sink;\n binding.onEnded = () => sink.reportError(new XmaxError(XmaxErrorCode.mediaError, \"External video output ended\"));\n native.addEventListener(\"ended\", binding.onEnded);\n VideoRenderRegistry.register(track, {\n attachHandler: view => {\n if (this.binding !== binding) {\n return;\n }\n view.isMirrored = false;\n view.setMediaStream(stream);\n binding.targets.add(view);\n },\n detachHandler: view => {\n binding.targets.delete(view);\n view.setMediaStream(null);\n },\n });\n await waitForMediaOperation(Promise.resolve(source.start(sink)), binding.abort.signal, 10_000, \"External source startup\");\n if (this.binding !== binding || binding.abort.signal.aborted || !sink.isRequestingFrames) {\n throw new XmaxError(XmaxErrorCode.cancelled, \"External source is no longer attached\");\n }\n return new RealtimeMediaStream({ id: StreamID.local, videoTrack: track });\n } catch (error) {\n if (this.binding === binding) {\n await this.stop();\n }\n throw XmaxError.from(error);\n } finally {\n signal.removeEventListener(\"abort\", abort);\n }\n }\n\n /**\n * 跟随 RTC 实际发布状态,不发布时丢弃音频。\n */\n setAudioPublished(published: boolean): void {\n this.binding?.audio?.setPublished(published && this.binding.sink?.isRequestingFrames === true);\n }\n\n /**\n * 释放连接或本地媒体前同步拒绝后续帧。\n */\n invalidate(): void {\n this.binding?.sink?.invalidate();\n this.binding?.abort.abort();\n }\n\n /**\n * 先使 sink 失效并释放 SDK 输出,再有界等待接入方停止回调。\n */\n async stop(): Promise<void> {\n const binding = this.binding;\n if (!binding) {\n return;\n }\n this.binding = undefined;\n binding.abort.abort();\n binding.sink?.invalidate();\n const native = binding.track.mediaStreamTrack;\n if (binding.onEnded) {\n native?.removeEventListener(\"ended\", binding.onEnded);\n }\n VideoRenderRegistry.unregister(binding.track);\n binding.track.mediaStreamTrack = undefined;\n for (const view of binding.targets) {\n try {\n view.setMediaStream(null);\n } catch {\n // 单个视图失败不阻断其他预览和媒体资源清理。\n }\n }\n binding.targets.clear();\n binding.stream.getTracks().forEach(track => track.stop());\n binding.canvas.width = 0;\n binding.canvas.height = 0;\n const results = await Promise.allSettled([\n binding.audio?.close(),\n waitForMediaOperation(Promise.resolve().then(() => binding.source.stop()), new AbortController().signal, 2000, \"External source stop\"),\n ]);\n for (const result of results) {\n if (result.status === \"rejected\") {\n XmaxLogger.media.warning(() => `External source cleanup failed: ${XmaxError.from(result.reason).message}`);\n }\n }\n }\n}\n","//\n// LocalImageController.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\nimport { XmaxError, XmaxErrorCode } from \"../../Foundation/Errors/XmaxError\";\nimport type { MediaServicing } from \"../../Service/Media/MediaServicing\";\nimport { RealtimeMediaStream } from \"../../Service/Realtime/RealtimeMediaStream\";\nimport type { RealtimeVideoFormat } from \"../../Service/Realtime/RealtimeVideoFormat\";\nimport { RealtimeVideoTrack } from \"../../Service/Realtime/RealtimeVideoTrack\";\nimport { StreamID } from \"../../Service/Realtime/StreamID\";\nimport { VideoRenderRegistry, type VideoRenderTarget } from \"../../Service/Realtime/VideoRenderBinding\";\n\n/**\n * 图片解码、静止画面输出及预览资源。\n */\ninterface LocalImageSource {\n\n /**\n * 用于解码源图片的图像元素。\n */\n image: HTMLImageElement;\n\n /**\n * 承载媒体帧的画布。\n */\n canvas: HTMLCanvasElement;\n\n /**\n * 源图片的浏览器对象 URL,释放源时一并撤销。\n */\n url: string;\n\n /**\n * 取消当前媒体操作及相关等待。\n */\n abort: AbortController;\n\n /**\n * 当前绑定该媒体源的渲染目标。\n */\n targets: Set<VideoRenderTarget>;\n\n /**\n * 由当前媒体源输出的浏览器媒体流。\n */\n stream?: MediaStream;\n\n /**\n * 当前媒体源对应的 SDK 视频轨道。\n */\n track?: RealtimeVideoTrack;\n\n /**\n * 当前等待或定时任务的取消句柄。\n */\n timer?: ReturnType<typeof setInterval>;\n\n /**\n * 媒体源结束时释放资源并通知上层。\n */\n onEnded?: () => void;\n}\n\n/**\n * 将本地图片等比缩放为静止视频流,不上传文件、不采集设备、不生成音频。\n */\nexport class LocalImageController {\n\n /**\n * 当前图片源,包含尚未完成的解码操作。\n */\n private source?: LocalImageSource;\n\n /**\n * 注入运行期错误通知,不创建浏览器资源。\n */\n constructor(\n private readonly onError: (\n error: XmaxError\n ) => void\n ) { }\n\n /**\n * 是否持有正在准备或可用的图片源。\n */\n get isActive(): boolean {\n return this.source !== undefined;\n }\n\n /**\n * 已准备的本地图片视频轨。\n */\n get currentTrack(): RealtimeVideoTrack | undefined {\n return this.source?.track;\n }\n\n /**\n * 解码图片、准备静止画面并持续输出帧;取消或失败时释放本次资源。\n */\n async create(\n options: {\n file: Blob;\n videoFormat: RealtimeVideoFormat;\n },\n mediaService: MediaServicing,\n signal: AbortSignal\n ): Promise<RealtimeMediaStream> {\n if (this.source) {\n throw new XmaxError(XmaxErrorCode.invalidConfiguration, \"Close the current local image before creating another one\");\n }\n if (!(options.file instanceof Blob) || options.file.size === 0) {\n throw new XmaxError(XmaxErrorCode.invalidConfiguration, \"Local image requires a non-empty File or Blob\");\n }\n options.videoFormat.validate();\n if (signal.aborted) {\n throw this.cancelled();\n }\n if (typeof document === \"undefined\") {\n throw new XmaxError(XmaxErrorCode.mediaError, \"Local image requires a browser\");\n }\n\n const image = document.createElement(\"img\");\n const canvas = document.createElement(\"canvas\");\n const context = canvas.getContext(\"2d\");\n if (!context || typeof canvas.captureStream !== \"function\") {\n throw new XmaxError(XmaxErrorCode.mediaError, \"Canvas video capture is unavailable\");\n }\n const source: LocalImageSource = {\n image, canvas, url: URL.createObjectURL(options.file),\n abort: new AbortController(), targets: new Set(),\n };\n this.source = source;\n\n try {\n await this.load(source, signal);\n if (signal.aborted || this.source !== source) {\n throw this.cancelled();\n }\n if (!image.naturalWidth || !image.naturalHeight) {\n throw new XmaxError(XmaxErrorCode.mediaError, \"The image has no valid dimensions\");\n }\n\n const size = mediaService.resolveMediaSize({ width: image.naturalWidth, height: image.naturalHeight }, \"image\");\n const format = options.videoFormat.resized(size.width, size.height);\n canvas.width = size.width;\n canvas.height = size.height;\n\n // 仅在解码完成时绘制一次图片;后续发送同一快照,动图也不会继续播放。\n context.fillStyle = \"black\";\n context.fillRect(0, 0, canvas.width, canvas.height);\n context.drawImage(image, 0, 0, canvas.width, canvas.height);\n source.stream = canvas.captureStream(format.fps);\n const nativeTrack = source.stream.getVideoTracks()[0] as CanvasCaptureMediaStreamTrack | undefined;\n if (!nativeTrack || nativeTrack.readyState !== \"live\") {\n throw new XmaxError(XmaxErrorCode.mediaError, \"Canvas did not provide a live video track\");\n }\n const track = new RealtimeVideoTrack({ id: \"local-image\", videoFormat: format });\n track.mediaStreamTrack = nativeTrack;\n source.track = track;\n\n const refresh = () => {\n // 重绘快照使 Canvas 产生新帧;不重复绘制原图,避免动图内容变化。\n context.drawImage(canvas, 0, 0);\n if (typeof nativeTrack.requestFrame === \"function\") {\n nativeTrack.requestFrame();\n }\n };\n refresh();\n source.timer = setInterval(() => {\n if (this.source !== source) {\n return;\n }\n try {\n refresh();\n } catch {\n this.handleError(source);\n }\n }, 1000 / format.fps);\n source.onEnded = () => this.handleError(source);\n nativeTrack.addEventListener(\"ended\", source.onEnded);\n\n VideoRenderRegistry.register(track, {\n attachHandler: view => {\n if (this.source !== source) {\n return;\n }\n view.isMirrored = false;\n view.setMediaStream(source.stream!);\n source.targets.add(view);\n },\n detachHandler: view => {\n source.targets.delete(view);\n view.setMediaStream(null);\n },\n });\n return new RealtimeMediaStream({ id: StreamID.local, videoTrack: track });\n } catch (error) {\n if (this.source === source) {\n this.stop();\n }\n throw XmaxError.from(error);\n }\n }\n\n /**\n * 取消解码、清空预览并释放视频轨、定时器和对象 URL。\n */\n stop(): void {\n const source = this.source;\n this.source = undefined;\n if (!source) {\n return;\n }\n\n source.abort.abort();\n clearInterval(source.timer);\n if (source.track) {\n VideoRenderRegistry.unregister(source.track);\n if (source.onEnded) {\n source.track.mediaStreamTrack?.removeEventListener(\"ended\", source.onEnded);\n }\n source.track.mediaStreamTrack = undefined;\n }\n for (const view of source.targets) {\n try {\n view.setMediaStream(null);\n } catch {\n // 单个视图异常不能阻断媒体资源清理。\n }\n }\n source.targets.clear();\n source.stream?.getTracks().forEach(track => track.stop());\n source.image.removeAttribute(\"src\");\n URL.revokeObjectURL(source.url);\n source.canvas.width = 0;\n source.canvas.height = 0;\n }\n\n /**\n * 等待浏览器完成图片解码,支持主动停止、操作取消和超时。\n */\n private load(\n source: LocalImageSource,\n signal: AbortSignal\n ): Promise<void> {\n return new Promise((resolve, reject) => {\n const finish = (error?: XmaxError) => {\n clearTimeout(timer);\n source.image.removeEventListener(\"load\", loaded);\n source.image.removeEventListener(\"error\", failed);\n signal.removeEventListener(\"abort\", aborted);\n source.abort.signal.removeEventListener(\"abort\", aborted);\n if (error) {\n reject(error);\n } else {\n resolve();\n }\n };\n const loaded = () => finish();\n const failed = () => finish(new XmaxError(XmaxErrorCode.mediaError, \"The browser cannot decode this image file\"));\n const aborted = () => finish(this.cancelled());\n const timer = setTimeout(() => finish(new XmaxError(XmaxErrorCode.timeout, \"Local image preparation timed out\")), 10_000);\n source.image.addEventListener(\"load\", loaded, { once: true });\n source.image.addEventListener(\"error\", failed, { once: true });\n signal.addEventListener(\"abort\", aborted, { once: true });\n source.abort.signal.addEventListener(\"abort\", aborted, { once: true });\n if (signal.aborted || source.abort.signal.aborted) {\n aborted();\n } else {\n source.image.src = source.url;\n }\n });\n }\n\n /**\n * 输出失效时停止刷新并仅报告一次,资源由业务生命周期统一释放。\n */\n private handleError(\n source: LocalImageSource\n ): void {\n if (this.source !== source || source.timer === undefined) {\n return;\n }\n clearInterval(source.timer);\n source.timer = undefined;\n this.onError(new XmaxError(XmaxErrorCode.mediaError, \"Local image video output failed\"));\n }\n\n /**\n * 构造统一的图片操作取消错误。\n */\n private cancelled(): XmaxError {\n return new XmaxError(XmaxErrorCode.cancelled, \"Local image operation was cancelled\");\n }\n}\n","//\n// VideoContentMode.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\n/**\n * 视频内容在渲染容器中的显示模式。\n */\nexport enum VideoContentMode {\n\n /**\n * 等比完整显示,允许容器留空。\n */\n fit = \"fit\",\n\n /**\n * 等比铺满容器,允许裁剪画面。\n */\n fill = \"fill\",\n}\n","//\n// RealtimePoint.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\n/**\n * 媒体坐标系中的横纵坐标。\n */\nexport interface RealtimePointInit {\n\n /**\n * 媒体横坐标;交互轨迹使用 [0, 1] 归一化值。\n */\n x: number;\n\n /**\n * 媒体纵坐标;交互轨迹使用 [0, 1] 归一化值。\n */\n y: number;\n}\n\n/**\n * 媒体坐标系中的二维点;交互轨迹使用归一化坐标。\n */\nexport class RealtimePoint {\n\n /**\n * 媒体横坐标。\n */\n readonly x: number;\n\n /**\n * 媒体纵坐标。\n */\n readonly y: number;\n\n /**\n * 创建媒体坐标点。\n */\n constructor(\n init: RealtimePointInit\n ) {\n this.x = init.x;\n this.y = init.y;\n }\n}\n","//\n// InteractionCoordinateMapper.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\nimport { VideoContentMode } from \"../../Foundation/Media/Video/VideoContentMode\";\nimport { RealtimePoint } from \"../../Service/Realtime/RealtimePoint\";\nimport type { InteractionFrame } from \"./InteractionFrame\";\n\n/**\n * 将视口坐标映射到固定媒体基准;不依赖 DOM、设备像素比或 RTC。\n */\nexport class InteractionCoordinateMapper {\n\n /**\n * 计算居中显示的视频矩形;fill 允许矩形超出视口,fit 留出黑边。\n */\n static displayedFrame(\n viewport: {\n width: number;\n height: number\n },\n video: {\n width: number;\n height: number\n },\n mode: VideoContentMode\n ) {\n if (![viewport.width, viewport.height, video.width, video.height].every(value => Number.isFinite(value) && value > 0)) {\n return undefined;\n }\n\n const scale = (mode === VideoContentMode.fill ? Math.max : Math.min)(viewport.width / video.width, viewport.height / video.height);\n const width = video.width * scale;\n const height = video.height * scale;\n return { x: (viewport.width - width) / 2, y: (viewport.height - height) / 2, width, height };\n }\n\n /**\n * 忽略黑边和无效点,撤销显示镜像后返回归一化坐标。\n * 可选 pixels 模式返回媒体像素坐标。\n */\n static map(\n point: {\n x: number;\n y: number\n },\n frame: InteractionFrame,\n video: {\n width: number;\n height: number\n },\n coordinateSpace: \"normalized\" | \"pixels\" = \"normalized\",\n ): RealtimePoint | undefined {\n const rect = this.displayedFrame(frame.viewportSize, video, frame.contentMode);\n if (!rect || !Number.isFinite(point.x) || !Number.isFinite(point.y)) {\n return undefined;\n }\n\n if (frame.contentMode === VideoContentMode.fit &&\n (point.x < rect.x || point.x > rect.x + rect.width || point.y < rect.y || point.y > rect.y + rect.height)) {\n return undefined;\n }\n\n const displayedX = (point.x - rect.x) / rect.width;\n const x = frame.mirrored ? 1 - displayedX : displayedX;\n const y = (point.y - rect.y) / rect.height;\n if (coordinateSpace === \"pixels\") {\n return new RealtimePoint({\n x: Math.min(video.width - 1, Math.max(0, Math.round(x * video.width))),\n y: Math.min(video.height - 1, Math.max(0, Math.round(y * video.height))),\n });\n }\n\n return new RealtimePoint({\n x: Math.min(1, Math.max(0, x)),\n y: Math.min(1, Math.max(0, y)),\n });\n }\n}\n","//\n// InteractionController.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\nimport { XmaxLogger } from \"../../Foundation/Logging/XmaxLogger\";\nimport type { RealtimePoint } from \"../../Service/Realtime/RealtimePoint\";\nimport type { RealtimeVideoFormat } from \"../../Service/Realtime/RealtimeVideoFormat\";\nimport { InteractionCoordinateMapper } from \"./InteractionCoordinateMapper\";\nimport type { InteractionFrame } from \"./InteractionFrame\";\nimport type { InteractionControlling, InteractionListener } from \"./InteractionControlling\";\n\n/**\n * 将视口采样转换为媒体坐标,串行提交且只保留最新待发送帧。\n */\nexport class InteractionController implements InteractionControlling {\n\n /**\n * 活动任务与待发送采样;上下文对象身份隔离任务切换。\n */\n private active?: {\n taskID: string;\n videoFormat: RealtimeVideoFormat;\n coordinateSpace: \"normalized\" | \"pixels\";\n };\n\n /**\n * 发送忙时保留的最新交互采样。\n */\n private pending?: {\n context: NonNullable<InteractionController[\"active\"]>;\n points: RealtimePoint[]\n };\n\n /**\n * 当前正在串行发送采样的交互上下文。\n */\n private draining?: NonNullable<InteractionController[\"active\"]>;\n\n /**\n * 注入发送能力,不持有房间或具体 RTC。\n */\n constructor(\n private readonly listener: InteractionListener\n ) { }\n\n /**\n * 激活或更新任务输入格式,清除旧采样。\n */\n startInteraction(\n taskID: string,\n videoFormat: RealtimeVideoFormat,\n coordinateSpace: \"normalized\" | \"pixels\" = \"normalized\",\n ): void {\n this.stopInteraction();\n if (taskID) {\n this.active = { taskID, videoFormat, coordinateSpace };\n }\n }\n\n /**\n * 立即使交互上下文失效;已提交给 RTC 的消息无法撤回。\n */\n stopInteraction(): void {\n this.active = undefined;\n this.pending = undefined;\n this.draining = undefined;\n }\n\n /**\n * 同步转换数据,发送忙时用最新采样覆盖旧采样,避免弱网积压。\n */\n submitInteraction(\n frame: InteractionFrame\n ): void {\n const context = this.active;\n if (!context) {\n return;\n }\n\n const points = frame.points.flatMap(point => {\n const mapped = InteractionCoordinateMapper.map(point, frame, context.videoFormat, context.coordinateSpace);\n return mapped ? [mapped] : [];\n });\n if (!points.length) {\n return;\n }\n\n this.pending = { context, points };\n if (this.draining !== context) {\n void this.drain(context);\n }\n }\n\n /**\n * 顺序发送;失败只丢弃当前帧,不终止生成或阻止后续交互。\n */\n private async drain(\n context: NonNullable<InteractionController[\"active\"]>\n ): Promise<void> {\n this.draining = context;\n try {\n while (this.active === context && this.pending?.context === context) {\n const { points } = this.pending;\n this.pending = undefined;\n try {\n await this.listener(context.taskID, points);\n } catch {\n if (context === this.active) {\n XmaxLogger.interaction.warning(() => \"发送交互轨迹失败,已丢弃当前采样帧 (Interaction Sample Dropped)\");\n }\n }\n }\n } finally {\n if (this.draining === context) {\n this.draining = undefined;\n }\n }\n }\n}\n","//\n// MediaController.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\nimport { XmaxError, XmaxErrorCode } from \"../Foundation/Errors/XmaxError\";\nimport type { RtcManaging } from \"../Foundation/RTC/RtcManaging\";\nimport type { MediaServicing } from \"../Service/Media/MediaServicing\";\nimport type { RealtimeVideoFormat } from \"../Service/Realtime/RealtimeVideoFormat\";\nimport { CameraController } from \"./Camera/CameraController\";\nimport type { CameraControlling, CameraPreviewReadyHandler } from \"./Camera/CameraControlling\";\nimport { LocalVideoController } from \"./Video/LocalVideoController\";\nimport { ExternalVideoController } from \"./Video/ExternalVideoController\";\nimport { ExternalFrameSourceController } from \"./External/ExternalFrameSourceController\";\nimport { LocalImageController } from \"./Image/LocalImageController\";\nimport { InteractionController } from \"./Interaction/InteractionController\";\nimport type { InteractionListener } from \"./Interaction/InteractionControlling\";\nimport type { InteractionFrame } from \"./Interaction/InteractionFrame\";\nimport type { MediaControlling } from \"./MediaControlling\";\n\n/**\n * 统一持有本地媒体及交互组件;Core 负责串行业务操作,本层管理源和 RTC 资源。\n */\nexport class MediaController implements MediaControlling {\n\n /**\n * 本地媒体与交互组件\n */\n private readonly camera: CameraControlling;\n\n /**\n * 本地文件视频的播放与音视频输出组件。\n */\n private readonly video: LocalVideoController;\n\n /**\n * 本地静止图片的视频输出组件。\n */\n private readonly image: LocalImageController;\n\n /**\n * 外部视频轨的所有权和结束事件管理组件。\n */\n private readonly external: ExternalVideoController;\n\n /**\n * 外部帧源的绑定、可选音频和预览组件。\n */\n private readonly externalFrames: ExternalFrameSourceController;\n\n /**\n * 交互坐标转换与串行发送组件。\n */\n private readonly interaction: InteractionController;\n\n /**\n * 注入基础能力与错误、交互输出,不自行创建跨层业务组件。\n */\n constructor(\n private readonly dependencies: {\n rtcManager: RtcManaging;\n mediaService: MediaServicing;\n cameraController?: CameraControlling;\n onCameraError: (\n error: XmaxError\n ) => void;\n onVideoError: (\n error: XmaxError\n ) => void;\n onExternalError: (\n error: XmaxError\n ) => void;\n onImageError: (\n error: XmaxError\n ) => void;\n interactionListener: InteractionListener;\n }\n ) {\n this.camera = dependencies.cameraController ?? new CameraController({\n rtcManager: dependencies.rtcManager,\n mediaService: dependencies.mediaService,\n errorListener: dependencies.onCameraError,\n });\n this.video = new LocalVideoController(dependencies.onVideoError);\n this.image = new LocalImageController(dependencies.onImageError);\n this.external = new ExternalVideoController(dependencies.onExternalError);\n this.externalFrames = new ExternalFrameSourceController(dependencies.onExternalError);\n this.interaction = new InteractionController(dependencies.interactionListener);\n }\n\n /**\n * 当前可发布的轨道。\n */\n get currentTrack() {\n return this.externalFrames.currentTrack ?? this.external.currentTrack ?? this.video.currentTrack ?? this.image.currentTrack ?? this.camera.currentTrack;\n }\n\n /**\n * 从实际资源推导来源,不维护另一份媒体状态。\n */\n get source(): MediaControlling[\"source\"] {\n if (this.externalFrames.isActive || this.external.currentTrack) {\n return \"externalVideo\";\n }\n if (this.video.isActive) {\n return \"localVideo\";\n }\n if (this.image.isActive) {\n return \"localImage\";\n }\n if (this.camera.currentTrack) {\n return \"camera\";\n }\n\n return undefined;\n }\n\n /**\n * 当前源需要发布的音频能力。\n */\n get hasAudio() {\n if (this.externalFrames.isActive) {\n return !!this.externalFrames.audioTrack;\n }\n return this.video.isActive ? !!this.video.audioTrack : this.source === \"camera\" && this.camera.useMicrophone;\n }\n\n /**\n * 非相机源不等待相机预热。\n */\n get isFrameValidationEnabled() {\n return this.source === \"camera\" && this.camera.isFrameValidationEnabled;\n }\n\n /**\n * 创建前拒绝覆盖活动源或尚未完成的文件准备。\n */\n ensureNoLocalSource(): void {\n if (this.currentTrack || this.externalFrames.isActive || this.video.isActive || this.image.isActive) {\n throw new XmaxError(XmaxErrorCode.invalidConfiguration, \"Close the current local stream before creating another one\");\n }\n }\n\n /**\n * 保持现有外部轨道能力校验与错误语义。\n */\n validateExternalMediaSupport(\n kind: \"local\" | \"external\" | \"image\"\n ): void {\n if (!this.dependencies.rtcManager.setExternalMediaTracks) {\n throw new XmaxError(XmaxErrorCode.invalidConfiguration, `The RTC provider does not support ${kind} video streams`);\n }\n }\n\n /**\n * 创建相机流,由相机组件管理 RTC 和预览。\n */\n createLocalCameraStream(\n options: Parameters<MediaControlling[\"createLocalCameraStream\"]>[0]\n ) {\n this.ensureNoLocalSource();\n return this.camera.createLocalCameraStream(options);\n }\n\n /**\n * 更新活动摄像头规格,保留预览与其他媒体资源。\n */\n updateCameraFormat(\n videoFormat: RealtimeVideoFormat\n ): Promise<void> {\n if (this.source !== \"camera\") {\n throw new XmaxError(XmaxErrorCode.invalidConfiguration, \"Camera preview is not started\");\n }\n return this.camera.updateVideoFormat(videoFormat);\n }\n\n /**\n * 先解锁用户触发的播放,再由 prepareSource 初始化 RTC。\n */\n createLocalVideoStream(\n options: Parameters<MediaControlling[\"createLocalVideoStream\"]>[0],\n signal: AbortSignal\n ) {\n this.ensureNoLocalSource();\n return this.video.create(options, this.dependencies.mediaService, signal);\n }\n\n /**\n * 解码本地图片并准备静止视频轨,不申请设备权限。\n */\n createLocalImageStream(\n options: Parameters<MediaControlling[\"createLocalImageStream\"]>[0],\n signal: AbortSignal\n ) {\n this.ensureNoLocalSource();\n return this.image.create(options, this.dependencies.mediaService, signal);\n }\n\n /**\n * 绑定帧源或接管兼容视频轨副本,不停止调用方原始采集。\n */\n async createExternalVideoStream(\n options: Parameters<MediaControlling[\"createExternalVideoStream\"]>[0],\n signal: AbortSignal\n ) {\n this.ensureNoLocalSource();\n if (options.source !== undefined) {\n if (options.videoTrack !== undefined || options.videoFormat !== undefined) {\n throw new XmaxError(XmaxErrorCode.invalidConfiguration, \"External source and video track options cannot be combined\");\n }\n return this.externalFrames.create(options.source, this.dependencies.mediaService, signal);\n }\n return this.external.create(options, this.dependencies.mediaService);\n }\n\n /**\n * 将连接发布状态传给外部音频,不影响相机或文件源。\n */\n setExternalAudioPublished(published: boolean): void {\n this.externalFrames.setAudioPublished(published);\n }\n\n /**\n * 初始化 RTC 并绑定非相机轨道;取消后不继续安装轨道。\n */\n async prepareSource(\n ensureCurrent: () => void\n ): Promise<void> {\n const rtc = this.dependencies.rtcManager;\n await rtc.initialize();\n ensureCurrent();\n\n const track = this.externalFrames.currentTrack ?? this.external.currentTrack ?? this.video.currentTrack ?? this.image.currentTrack;\n if (track?.mediaStreamTrack) {\n await rtc.setExternalMediaTracks!({ videoTrack: track.mediaStreamTrack,\n audioTrack: this.externalFrames.isActive ? this.externalFrames.audioTrack : this.video.audioTrack });\n ensureCurrent();\n }\n }\n\n /**\n * 设置相机预览就绪通知。\n */\n setCameraPreviewReadyHandler(\n handler?: CameraPreviewReadyHandler\n ): void {\n this.camera.setPreviewReadyHandler(handler);\n }\n\n /**\n * 等待相机预热。\n */\n waitForValidCameraFrame(\n signal: AbortSignal\n ): Promise<void> {\n return this.camera.waitForValidCameraFrame(signal);\n }\n\n /**\n * 切换相机;外部采集不由 SDK 控制。\n */\n switchCamera() {\n if (this.externalFrames.isActive || this.external.currentTrack) {\n throw new XmaxError(XmaxErrorCode.invalidConfiguration, \"External video capture is managed by the caller\");\n }\n return this.camera.switchCamera();\n }\n\n /**\n * 停止相机资源。\n */\n stopLocalCameraStream(): Promise<void> {\n return this.camera.stopLocalCameraStream();\n }\n\n /**\n * 生成开始后启动文件播放。\n */\n startLocalVideo(\n signal: AbortSignal\n ): Promise<void> {\n return this.video.start(signal);\n }\n\n /**\n * 断开时保留文件预览资源但暂停时间线。\n */\n pauseLocalVideo(): void {\n this.video.pause();\n }\n\n /**\n * 释放当前源,RTC 销毁失败也释放外部轨和解码器。\n */\n async stopLocalStream(): Promise<void> {\n this.stopInteraction();\n const external = this.source !== undefined && this.source !== \"camera\";\n this.externalFrames.invalidate();\n\n try {\n if (external) {\n await this.dependencies.rtcManager.destroy();\n } else {\n await this.camera.stopLocalCameraStream();\n }\n } finally {\n this.external.stop();\n await this.externalFrames.stop();\n if (this.image.isActive) {\n this.image.stop();\n }\n if (this.video.isActive) {\n await this.video.stop();\n }\n }\n }\n\n /**\n * 使用固定媒体基准启用交互,外部视频轨也发送归一化坐标。\n * 不读取调档后的编码规格或生成画布。\n */\n startInteraction(\n taskID: string,\n format: RealtimeVideoFormat\n ): void {\n this.interaction.startInteraction(taskID, format);\n }\n\n /**\n * 结束任务时立即清除待发采样。\n */\n stopInteraction(): void {\n this.interaction.stopInteraction();\n }\n\n /**\n * 接收渲染层快照并交给坐标与发送队列处理。\n */\n submitInteraction(\n frame: InteractionFrame\n ): void {\n this.interaction.submitInteraction(frame);\n }\n}\n","//\n// RemoteAudioWorklet.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\n/**\n * 独立音频线程将输入复制成约 20 ms 的 S16LE PCM 包,不输出可听声音。\n * 时间使用音频上下文的采样时钟,不使用主线程收到消息的时间。\n */\nexport const REMOTE_AUDIO_WORKLET = `\nclass XmaxRemoteAudioProcessor extends AudioWorkletProcessor {\n constructor() {\n super();\n this.channels = 0;\n this.filled = 0;\n this.startFrame = 0;\n this.nextFrame = undefined;\n this.discontinuous = true;\n this.packetSamples = Math.max(1, Math.round(sampleRate * 0.02));\n }\n\n process(inputs) {\n const input = inputs[0];\n if (!input || !input.length || !input[0].length) {\n this.filled = 0;\n this.nextFrame = undefined;\n this.discontinuous = true;\n return true;\n }\n const channels = Math.min(2, input.length);\n if (channels !== this.channels || this.nextFrame !== currentFrame) {\n this.channels = channels;\n this.filled = 0;\n this.bytes = new ArrayBuffer(this.packetSamples * channels * 2);\n this.data = new DataView(this.bytes);\n this.discontinuous = true;\n }\n for (let i = 0; i < input[0].length; i++) {\n if (this.filled === 0) this.startFrame = currentFrame + i;\n for (let channel = 0; channel < channels; channel++) {\n const value = Math.max(-1, Math.min(1, input[channel][i] || 0));\n this.data.setInt16((this.filled * channels + channel) * 2,\n Math.round(value < 0 ? value * 32768 : value * 32767), true);\n }\n if (++this.filled === this.packetSamples) {\n this.port.postMessage({ data: this.bytes, sampleRate, channels,\n samplesPerChannel: this.packetSamples, frameIndex: this.startFrame,\n discontinuous: this.discontinuous }, [this.bytes]);\n this.bytes = new ArrayBuffer(this.packetSamples * channels * 2);\n this.data = new DataView(this.bytes);\n this.filled = 0;\n this.discontinuous = false;\n }\n }\n this.nextFrame = currentFrame + input[0].length;\n return true;\n }\n}\nregisterProcessor(\"xmax-remote-audio\", XmaxRemoteAudioProcessor);\n`;\n","//\n// RemoteAudioFrameSource.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\nimport { XmaxError, XmaxErrorCode } from \"../../Errors/XmaxError\";\nimport { REMOTE_AUDIO_WORKLET } from \"./RemoteAudioWorklet\";\n\n/**\n * 音频线程复制出的 PCM 包及其接收端呈现时间。\n */\nexport interface RemoteAudioPacket {\n\n /**\n * S16LE 交错音频字节及格式。\n */\n data: Uint8Array;\n sampleRate: number;\n channels: number;\n samplesPerChannel: number;\n\n /**\n * 共享 performance 时间轴上的微秒时间。\n */\n presentationTimeStamp: number;\n\n /**\n * 音频输入、格式或采样时钟发生变化。\n */\n discontinuous: boolean;\n}\n\n/**\n * 以独立静音音频图读取远端原始音轨,不修改 RTC 播放器的音量或订阅。\n */\nexport class RemoteAudioFrameSource {\n\n /**\n * 音频上下文与当前输入图\n */\n private context?: AudioContext;\n\n /**\n * 当前输入轨的音频源。\n */\n private source?: MediaStreamAudioSourceNode;\n\n /**\n * PCM 复制处理器。\n */\n private processor?: AudioWorkletNode;\n\n /**\n * 保持音频图活动但不发声的输出节点。\n */\n private silence?: GainNode;\n\n /**\n * 借用轨道及其结束通知,释放时仅移除监听。\n */\n private track?: MediaStreamTrack;\n\n /**\n * 当前音轨结束后停止复制 PCM。\n */\n private onEnded?: () => void;\n\n /**\n * 连续性时钟与格式\n */\n private nextPTS?: number;\n\n /**\n * 上一包的采样率和声道数。\n */\n private format?: string;\n\n /**\n * 从用户手势调用链准备音频线程;失败或取消时释放已分配的资源。\n */\n async prepare(signal: AbortSignal): Promise<void> {\n if (typeof AudioContext === \"undefined\" || typeof AudioWorkletNode === \"undefined\") {\n throw new XmaxError(XmaxErrorCode.mediaError, \"Remote PCM frames require AudioWorklet support\");\n }\n const context = new AudioContext();\n this.context = context;\n let url: string | undefined;\n try {\n if (!context.audioWorklet) {\n throw new XmaxError(XmaxErrorCode.mediaError, \"Remote PCM frames require a secure context with AudioWorklet\");\n }\n const resumed = context.resume();\n url = URL.createObjectURL(new Blob([REMOTE_AUDIO_WORKLET], { type: \"text/javascript\" }));\n await this.wait(Promise.all([resumed, context.audioWorklet.addModule(url)]).then(() => {}), signal);\n if (context.state !== \"running\") {\n throw new XmaxError(XmaxErrorCode.mediaError, \"Start remote media observation from a user gesture to unlock audio\");\n }\n } catch (error) {\n await this.close();\n throw error;\n } finally {\n if (url) URL.revokeObjectURL(url);\n }\n }\n\n /**\n * 切换原始音轨,旧消息通过节点身份失效;没有音轨时不制造静音 PCM。\n */\n setTrack(track: MediaStreamTrack | undefined, listener: (packet: RemoteAudioPacket) => void): void {\n this.disconnect();\n const context = this.context;\n if (!context || !track || track.readyState === \"ended\") return;\n\n this.track = track;\n this.onEnded = () => this.disconnect();\n track.addEventListener(\"ended\", this.onEnded, { once: true });\n\n const source = context.createMediaStreamSource(new MediaStream([track]));\n this.source = source;\n const processor = new AudioWorkletNode(context, \"xmax-remote-audio\", {\n numberOfInputs: 1, numberOfOutputs: 1, outputChannelCount: [1],\n channelCount: 2, channelCountMode: \"max\",\n });\n this.processor = processor;\n const silence = context.createGain();\n this.silence = silence;\n silence.gain.value = 0;\n processor.port.onmessage = event => {\n if (this.processor !== processor || context.state !== \"running\") return;\n const packet = this.convert(event.data, context);\n if (packet) listener(packet);\n };\n source.connect(processor);\n processor.connect(silence);\n silence.connect(context.destination);\n }\n\n /**\n * 释放当前音频图和共享上下文,不停止借用的 RTC 音轨。\n */\n async close(): Promise<void> {\n this.disconnect();\n const context = this.context;\n this.context = undefined;\n if (context && context.state !== \"closed\") await context.close();\n }\n\n /**\n * 取消输入和旧端口消息,并重新武装音频时间线。\n */\n private disconnect(): void {\n if (this.track && this.onEnded) this.track.removeEventListener(\"ended\", this.onEnded);\n this.track = undefined;\n this.onEnded = undefined;\n const processor = this.processor;\n this.processor = undefined;\n if (processor) {\n processor.port.onmessage = null;\n processor.port.close();\n processor.disconnect();\n }\n this.source?.disconnect();\n this.silence?.disconnect();\n this.source = undefined;\n this.silence = undefined;\n this.nextPTS = undefined;\n this.format = undefined;\n }\n\n /**\n * 校验音频线程消息,以采样数推进连续时间;时钟跳变超过 100 ms 时重新锚定。\n */\n private convert(value: unknown, context: AudioContext): RemoteAudioPacket | undefined {\n const raw = value as {\n data?: ArrayBuffer; sampleRate?: number; channels?: number;\n samplesPerChannel?: number; frameIndex?: number; discontinuous?: boolean;\n } | null;\n const { data, sampleRate, channels, samplesPerChannel, frameIndex } = raw ?? {};\n if (!(data instanceof ArrayBuffer) || !sampleRate || sampleRate < 8000 || sampleRate > 192000 ||\n !Number.isInteger(sampleRate) || !channels || ![1, 2].includes(channels) ||\n !samplesPerChannel || !Number.isInteger(samplesPerChannel) || samplesPerChannel > sampleRate ||\n frameIndex === undefined || !Number.isSafeInteger(frameIndex) || frameIndex < 0 ||\n data.byteLength !== samplesPerChannel * channels * 2) {\n this.nextPTS = undefined;\n return undefined;\n }\n\n const output = context.getOutputTimestamp?.();\n const validClock = output && Number.isFinite(output.contextTime) &&\n Number.isFinite(output.performanceTime) && output.performanceTime! > 0;\n const contextTime = validClock ? output.contextTime! : context.currentTime;\n const performanceTime = validClock ? output.performanceTime! : performance.now();\n const nativePTS = Math.round(performanceTime * 1000 + (frameIndex / sampleRate - contextTime) * 1_000_000);\n if (!Number.isFinite(nativePTS)) {\n this.nextPTS = undefined;\n return undefined;\n }\n const format = `${sampleRate}/${channels}`;\n const discontinuous = raw?.discontinuous === true || this.nextPTS === undefined || this.format !== format ||\n Math.abs(nativePTS - this.nextPTS) > 100_000;\n const pts = discontinuous ? nativePTS : this.nextPTS!;\n this.nextPTS = pts + samplesPerChannel * 1_000_000 / sampleRate;\n this.format = format;\n return { data: new Uint8Array(data), sampleRate, channels, samplesPerChannel,\n presentationTimeStamp: pts, discontinuous };\n }\n\n /**\n * 为自动播放阻塞和模块加载设置有界等待,并响应监听取消。\n */\n private wait(promise: Promise<void>, signal: AbortSignal): Promise<void> {\n return new Promise((resolve, reject) => {\n const timer = setTimeout(() => finish(new XmaxError(XmaxErrorCode.timeout,\n \"Remote audio initialization timed out; call setRemoteMediaFrameListener from a user gesture\")), 3000);\n const abort = () => finish(new XmaxError(XmaxErrorCode.cancelled, \"Remote media observation cancelled\"));\n const finish = (error?: unknown) => {\n clearTimeout(timer);\n signal.removeEventListener(\"abort\", abort);\n if (error) reject(error); else resolve();\n };\n promise.then(() => finish(), finish);\n signal.addEventListener(\"abort\", abort, { once: true });\n if (signal.aborted) abort();\n });\n }\n}\n","//\n// RemoteVideoFrameSource.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\nimport { XmaxError, XmaxErrorCode } from \"../../Errors/XmaxError\";\n\n/**\n * 原始解码视频帧及其时间连续性。\n */\nexport interface RemoteVideoPacket {\n\n /**\n * 拷贝出的帧,所有权交给调用方。\n */\n frame: VideoFrame;\n\n /**\n * 帧回调或时钟存在跳变。\n */\n discontinuous: boolean;\n}\n\n/**\n * 通过独立的静音 video 观察原始轨道,不依赖 SDK 视图。\n * 帧按浏览器呈现回调获取,不保证包含全部网络接收/解码帧。\n */\nexport class RemoteVideoFrameSource {\n\n /**\n * 媒体元素和回调资源\n */\n private video?: HTMLVideoElement;\n\n /**\n * 当前视频帧观察句柄。\n */\n private callback?: number;\n\n /**\n * 借用轨道及其结束通知。\n */\n private track?: MediaStreamTrack;\n\n /**\n * 当前视频轨结束后取消观察。\n */\n private onEnded?: () => void;\n\n /**\n * 开始观察原始视频;失败交给上层隔离处理,不影响主播放器。\n */\n start(track: MediaStreamTrack, listener: (packet: RemoteVideoPacket) => void, onError: (error: unknown) => void): void {\n this.stop();\n if (typeof VideoFrame === \"undefined\" || typeof HTMLVideoElement === \"undefined\" ||\n typeof HTMLVideoElement.prototype.requestVideoFrameCallback !== \"function\") {\n throw new XmaxError(XmaxErrorCode.mediaError, \"Remote video frames require VideoFrame and requestVideoFrameCallback\");\n }\n const video = document.createElement(\"video\");\n this.video = video;\n this.track = track;\n this.onEnded = () => this.stop();\n track.addEventListener(\"ended\", this.onEnded, { once: true });\n video.muted = true;\n video.playsInline = true;\n video.autoplay = true;\n video.srcObject = new MediaStream([track]);\n Object.assign(video.style, { position: \"fixed\", top: \"0\", left: \"0\", width: \"1px\", height: \"1px\", opacity: \"0\", pointerEvents: \"none\" });\n video.setAttribute(\"aria-hidden\", \"true\");\n document.body.appendChild(video);\n let previousPTS: number | undefined;\n let previousMediaTime: number | undefined;\n let previousFrames: number | undefined;\n let previousSize: string | undefined;\n const next = () => {\n this.callback = video.requestVideoFrameCallback((now, metadata) => {\n if (this.video !== video) return;\n let frame: VideoFrame | undefined;\n\n try {\n const pts = Math.round((Number.isFinite(metadata.expectedDisplayTime) ? metadata.expectedDisplayTime : now) * 1000);\n const size = `${video.videoWidth}/${video.videoHeight}`;\n const discontinuous = previousPTS === undefined || pts <= previousPTS || size !== previousSize ||\n (previousMediaTime !== undefined && metadata.mediaTime < previousMediaTime) ||\n (previousFrames !== undefined && metadata.presentedFrames > previousFrames + 1);\n const duration = previousPTS !== undefined && pts > previousPTS ? pts - previousPTS : undefined;\n frame = new VideoFrame(video, { timestamp: pts, ...(duration && { duration }) });\n previousPTS = pts;\n previousMediaTime = metadata.mediaTime;\n previousFrames = metadata.presentedFrames;\n previousSize = size;\n listener({ frame, discontinuous });\n } catch (error) {\n frame?.close();\n onError(error);\n }\n if (this.video === video) next();\n });\n };\n next();\n void video.play().catch(error => {\n if (this.video === video) onError(error);\n });\n }\n\n /**\n * 停止观察并释放媒体元素,不停止借用的 RTC 视频轨。\n */\n stop(): void {\n if (this.track && this.onEnded) this.track.removeEventListener(\"ended\", this.onEnded);\n this.track = undefined;\n this.onEnded = undefined;\n const video = this.video;\n this.video = undefined;\n if (!video) return;\n if (this.callback !== undefined) video.cancelVideoFrameCallback(this.callback);\n this.callback = undefined;\n video.pause();\n video.srcObject = null;\n video.remove();\n }\n}\n","//\n// RealtimeMediaFrameDispatcher.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\nimport { XmaxLogger } from \"../../Foundation/Logging/XmaxLogger\";\nimport type { RealtimeMediaFrame, RealtimeMediaFrameListener } from \"../../Service/Realtime/RealtimeMediaFrame\";\n\n/**\n * 有界媒体交付队列;不阻塞生产者,不保存已交给接入方的帧。\n */\nexport class RealtimeMediaFrameDispatcher {\n\n /**\n * 回调及队列资源\n */\n private listener?: RealtimeMediaFrameListener;\n\n /**\n * 待交付媒体及估算内存。\n */\n private queue: Array<{ packet: RealtimeMediaFrame; bytes: number }> = [];\n\n /**\n * 队列占用总字节数。\n */\n private bytes = 0;\n\n /**\n * 异步交付的调度句柄。\n */\n private timer?: ReturnType<typeof setTimeout>;\n\n /**\n * 各轨下一包需要标记不连续。\n */\n private readonly discontinuous = new Set<\"audio\" | \"video\">([\"audio\", \"video\"]);\n\n /**\n * 当前监听周期是否已报告接入方异常。\n */\n private reportedError = false;\n\n /**\n * 使用帧数和字节数双重限制,避免接入方处理变慢时无限积压。\n */\n constructor(\n private readonly maximumPackets = 256,\n private readonly maximumBytes = 32 * 1024 * 1024,\n ) {}\n\n /**\n * 替换或清除监听器,并丢弃旧监听器的待交付媒体。\n */\n setListener(listener?: RealtimeMediaFrameListener): void {\n this.reset();\n this.listener = listener;\n this.reportedError = false;\n }\n\n /**\n * 清空旧绑定的媒体,保留监听器,不停止借用的 RTC 轨道。\n */\n reset(): void {\n clearTimeout(this.timer);\n this.timer = undefined;\n for (const item of this.queue) this.release(item.packet);\n this.queue = [];\n this.bytes = 0;\n this.discontinuous.add(\"audio\");\n this.discontinuous.add(\"video\");\n }\n\n /**\n * 接收一包媒体;超限时释放未交付的视频,下一包标记不连续,不压缩时间戳。\n */\n dispatch(packet: RealtimeMediaFrame): void {\n const type = packet.content.type;\n const bytes = packet.content.type === \"audio\" ? packet.content.frame.data.byteLength\n : packet.content.frame.frame.displayWidth * packet.content.frame.frame.displayHeight * 4;\n if (!this.listener || this.queue.length >= this.maximumPackets || bytes > this.maximumBytes - this.bytes) {\n this.release(packet);\n this.discontinuous.add(type);\n return;\n }\n this.queue.push({\n packet: Object.freeze({ ...packet, isDiscontinuous: packet.isDiscontinuous || this.discontinuous.has(type) }), bytes,\n });\n this.discontinuous.delete(type);\n this.bytes += bytes;\n this.timer ??= setTimeout(() => this.deliver(), 0);\n }\n\n /**\n * 分批交付,监听器内部替换或移除自身时不再调用旧回调。\n */\n private deliver(): void {\n this.timer = undefined;\n for (let count = 0; count < 16 && this.queue.length; count++) {\n const item = this.queue.shift()!;\n this.bytes -= item.bytes;\n const listener = this.listener;\n if (!listener) {\n this.release(item.packet);\n continue;\n }\n try {\n void Promise.resolve(listener(item.packet)).catch(error => {\n this.release(item.packet);\n this.report(error);\n });\n } catch (error) {\n this.release(item.packet);\n this.report(error);\n }\n }\n if (this.queue.length) this.timer ??= setTimeout(() => this.deliver(), 0);\n }\n\n /**\n * 仅释放尚未成功交付的媒体,正常返回的帧由接入方释放。\n */\n private release(packet: RealtimeMediaFrame): void {\n if (packet.content.type === \"video\") packet.content.frame.frame.close();\n }\n\n /**\n * 隔离观察者异常,同一监听周期只输出一次日志。\n */\n private report(error: unknown): void {\n if (this.reportedError) return;\n this.reportedError = true;\n XmaxLogger.render.warning(() => `Remote media frame listener failed: ${String(error)}`);\n }\n}\n","//\n// RemoteMediaFrameController.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\nimport { XmaxError, XmaxErrorCode } from \"../../Foundation/Errors/XmaxError\";\nimport { XmaxLogger } from \"../../Foundation/Logging/XmaxLogger\";\nimport { RemoteAudioFrameSource } from \"../../Foundation/Media/Audio/RemoteAudioFrameSource\";\nimport { RemoteVideoFrameSource } from \"../../Foundation/Media/Video/RemoteVideoFrameSource\";\nimport type { RemoteMediaTracks } from \"../../Foundation/Media/RemoteMediaTracks\";\nimport type { RealtimeMediaFrameListener } from \"../../Service/Realtime/RealtimeMediaFrame\";\nimport { RealtimeMediaFrameDispatcher } from \"./RealtimeMediaFrameDispatcher\";\n\n/**\n * 编排原始远端音视频观察与有界交付,不持有录制器、文件或时长限制。\n */\nexport class RemoteMediaFrameController {\n\n /**\n * 监听器与准备操作\n */\n private listener?: RealtimeMediaFrameListener;\n\n /**\n * 取消尚未完成的音频初始化。\n */\n private setup?: AbortController;\n\n /**\n * 已准备的音频线程。\n */\n private audio?: RemoteAudioFrameSource;\n\n /**\n * 原始视频观察器与交付队列\n */\n private readonly video = new RemoteVideoFrameSource();\n\n /**\n * 限制尚未交付的媒体占用。\n */\n private readonly dispatcher = new RealtimeMediaFrameDispatcher();\n\n /**\n * RTC 当前结果流与已经安装的绑定。\n */\n private source?: RemoteMediaTracks;\n\n /**\n * 当前取帧绑定。\n */\n private bound?: RemoteMediaTracks;\n\n /**\n * 同一结果流的音视频共享标识。\n */\n private streamID?: string;\n\n /**\n * 同一绑定只报告一次运行期取帧错误。\n */\n private reportedError = false;\n\n /**\n * 替换或取消观察;准备失败仅拒绝本次设置,不终止实时生成。\n */\n async setListener(listener?: RealtimeMediaFrameListener): Promise<void> {\n this.setup?.abort();\n const setup = new AbortController();\n this.setup = setup;\n this.listener = undefined;\n this.stopBinding();\n this.dispatcher.setListener(undefined);\n const previous = this.audio;\n this.audio = undefined;\n await previous?.close();\n if (setup.signal.aborted) throw new XmaxError(XmaxErrorCode.cancelled, \"Remote media listener replaced\");\n if (!listener) return;\n\n if (typeof VideoFrame === \"undefined\" || typeof HTMLVideoElement === \"undefined\" ||\n typeof HTMLVideoElement.prototype.requestVideoFrameCallback !== \"function\") {\n throw new XmaxError(XmaxErrorCode.mediaError, \"This browser cannot provide remote video frames\");\n }\n const audio = new RemoteAudioFrameSource();\n this.audio = audio;\n try {\n await audio.prepare(setup.signal);\n if (setup.signal.aborted) throw new XmaxError(XmaxErrorCode.cancelled, \"Remote media listener replaced\");\n this.listener = listener;\n this.dispatcher.setListener(listener);\n this.bind(true);\n } catch (error) {\n if (this.audio === audio) this.audio = undefined;\n await audio.close();\n throw error;\n }\n }\n\n /**\n * 结果流、任务或原始音轨变化时更新取帧;未设置监听器时只记录绑定,不分配资源。\n */\n setSource(source?: RemoteMediaTracks): void {\n this.source = source;\n if (!this.listener || !this.audio) return;\n this.bind();\n }\n\n /**\n * 安装当前结果流;只换音轨时保持视频和共享标识。\n */\n private bind(strict = false): void {\n const source = this.source;\n const previous = this.bound;\n if (!source) {\n this.stopBinding();\n return;\n }\n const sameStream = previous?.bindingID === source.bindingID && previous.userID === source.userID &&\n previous.videoTrack === source.videoTrack;\n if (sameStream && previous.audioTrack === source.audioTrack) return;\n if (!sameStream) {\n this.stopBinding();\n this.streamID = crypto.randomUUID();\n this.reportedError = false;\n }\n this.bound = source;\n const streamID = this.streamID!;\n try {\n if (!sameStream) {\n this.video.start(source.videoTrack, packet => {\n if (this.streamID !== streamID) { packet.frame.close(); return; }\n const frame = Object.freeze({ frame: packet.frame, presentationTimeStamp: packet.frame.timestamp,\n duration: packet.frame.duration ?? undefined });\n this.dispatcher.dispatch({ streamID, isDiscontinuous: packet.discontinuous,\n content: { type: \"video\", frame }, presentationTimeStamp: frame.presentationTimeStamp, duration: frame.duration });\n }, error => this.report(error));\n }\n this.audio!.setTrack(source.audioTrack, packet => {\n if (this.streamID !== streamID) return;\n const frame = Object.freeze({ data: packet.data, sampleRate: packet.sampleRate, channels: packet.channels,\n samplesPerChannel: packet.samplesPerChannel, presentationTimeStamp: packet.presentationTimeStamp,\n duration: packet.samplesPerChannel * 1_000_000 / packet.sampleRate });\n this.dispatcher.dispatch({ streamID, isDiscontinuous: packet.discontinuous,\n content: { type: \"audio\", frame }, presentationTimeStamp: frame.presentationTimeStamp, duration: frame.duration });\n });\n } catch (error) {\n this.stopBinding();\n if (strict) throw error;\n this.report(error);\n }\n }\n\n /**\n * 停止旧绑定和待交付帧,借用的 RTC 轨道保持原状。\n */\n private stopBinding(): void {\n this.streamID = undefined;\n this.bound = undefined;\n this.video.stop();\n this.audio?.setTrack(undefined, () => {});\n this.dispatcher.reset();\n }\n\n /**\n * 运行期观察失败不终止媒体播放或生成;记录明确的诊断原因。\n */\n private report(error: unknown): void {\n if (this.reportedError) return;\n this.reportedError = true;\n XmaxLogger.render.warning(() => `Remote media frame observation failed: ${XmaxError.from(error).message}`);\n }\n}\n","//\n// TrajectoryBinding.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\nimport type { InteractionFrame } from \"../../Media/Interaction/InteractionFrame\";\nimport type { RealtimeVideoFormat } from \"../../Service/Realtime/RealtimeVideoFormat\";\nimport type { TrajectoryOverlayView } from \"./TrajectoryOverlayView\";\n\n/**\n * 远端轨道的交互绑定,向视图传递格式和任务可用性,不持有任务 ID。\n */\nexport class TrajectoryBinding {\n\n /**\n * 绑定视图与可用性\n */\n private overlay?: TrajectoryOverlayView;\n\n /**\n * 当前绑定是否允许提交交互采样。\n */\n private enabled = false;\n\n /**\n * 当前绑定是否仍属于有效轨道。\n */\n private valid = true;\n\n /**\n * 注入采样回调及模型输入格式,不引用 Media Controller 实例。\n */\n constructor(\n private readonly listener: (\n frame: InteractionFrame\n ) => void,\n private format: RealtimeVideoFormat\n ) { }\n\n /**\n * 一个远端绑定只由一个交互视图持有,迁移时取消旧视图的触点。\n */\n attach(\n overlay: TrajectoryOverlayView\n ): void {\n if (!this.valid) {\n return;\n }\n if (this.overlay !== overlay) {\n this.overlay?.setBinding(undefined);\n }\n this.overlay = overlay;\n overlay.setBinding(this, this.format, this.enabled);\n }\n\n /**\n * 只解除当前所有者,旧视图迟到的 detach 不影响新视图。\n */\n detach(\n overlay: TrajectoryOverlayView\n ): void {\n if (this.overlay === overlay) {\n this.overlay = undefined;\n overlay.setBinding(undefined);\n }\n }\n\n /**\n * 开始、更新或结束任务时同步交互能力,并取消旧手势。\n */\n update(\n enabled: boolean,\n format = this.format\n ): void {\n this.enabled = enabled;\n this.format = format;\n this.overlay?.setBinding(this, format, enabled);\n }\n\n /**\n * 转发当前视图采样;注销或停用后忽略。\n */\n submit(\n frame: InteractionFrame\n ): void {\n if (this.valid && this.enabled) {\n this.listener(frame);\n }\n }\n\n /**\n * 注销绑定并释放视图交互资源,不允许旧引用重新挂载。\n */\n invalidate(): void {\n this.valid = false;\n this.overlay?.setBinding(undefined);\n this.overlay = undefined;\n }\n}\n","//\n// TrajectoryRegistry.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\nimport type { RealtimeVideoTrack } from \"../../Service/Realtime/RealtimeVideoTrack\";\nimport type { TrajectoryBinding } from \"./TrajectoryBinding\";\n\n/**\n * 按 Track 对象身份查找交互能力;不以可能重复的轨道字符串 ID 为索引。\n */\nexport class TrajectoryRegistry {\n\n /**\n * 媒体轨道与渲染视图的绑定索引。\n */\n private static readonly bindings = new WeakMap<RealtimeVideoTrack, TrajectoryBinding>();\n\n /**\n * 替换绑定前先使旧绑定失效。\n */\n static register(\n track: RealtimeVideoTrack,\n binding: TrajectoryBinding\n ): void {\n this.unregister(track);\n this.bindings.set(track, binding);\n }\n\n /**\n * 查询交互能力,本地轨道不注册该能力。\n */\n static binding(\n track: RealtimeVideoTrack\n ): TrajectoryBinding | undefined {\n return this.bindings.get(track);\n }\n\n /**\n * 主动清理视图资源,不能仅依赖 WeakMap 垃圾回收。\n */\n static unregister(\n track: RealtimeVideoTrack\n ): void {\n this.bindings.get(track)?.invalidate();\n this.bindings.delete(track);\n }\n}\n","//\n// RenderController.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\nimport type { InteractionFrame } from \"../Media/Interaction/InteractionFrame\";\nimport type { RealtimeVideoTrack } from \"../Service/Realtime/RealtimeVideoTrack\";\nimport type { RealtimeVideoFormat } from \"../Service/Realtime/RealtimeVideoFormat\";\nimport { VideoRenderRegistry, type VideoRenderTarget } from \"../Service/Realtime/VideoRenderBinding\";\nimport type { RenderControlling } from \"./RenderControlling\";\nimport { TrajectoryBinding } from \"./Trajectory/TrajectoryBinding\";\nimport { TrajectoryRegistry } from \"./Trajectory/TrajectoryRegistry\";\n\n/**\n * 统一管理远端轨道的画面、展示就绪状态与交互绑定,不管理生成任务。\n */\nexport class RenderController implements RenderControlling {\n\n /**\n * 当前轨道与唯一渲染目标\n */\n private track?: RealtimeVideoTrack;\n\n /**\n * 当前远端轨道的渲染绑定目标。\n */\n private target?: VideoRenderTarget;\n\n /**\n * 当前生成任务是否允许轨迹交互。\n */\n private interactionEnabled = false;\n\n /**\n * 当前远端视图是否已完成渲染预热。\n */\n private ready = false;\n\n /**\n * 注入显示生命周期通知,不持有 Core 对象。\n */\n constructor(\n private readonly callbacks: {\n onFrameDisplayed?: () => void;\n onRenderWarmupCompleted?: () => void;\n } = {}\n ) { }\n\n /**\n * 当前远端视图是否已完成渲染预热。\n */\n get isReady(): boolean {\n return this.ready;\n }\n\n /**\n * 新任务重新等待首帧和预热,避免复用上一次任务的展示就绪状态。\n */\n prepareForGeneration(): void {\n this.ready = false;\n this.target?.prepareForGeneration?.();\n }\n\n /**\n * 注册新轨道并使旧绑定失效;本地轨道不经过该入口。\n */\n registerRemoteTrack(\n track: RealtimeVideoTrack,\n listener: (\n frame: InteractionFrame\n ) => void\n ): void {\n this.resetRemoteTrack();\n this.track = track;\n if (track.videoFormat) {\n TrajectoryRegistry.register(track, new TrajectoryBinding(listener, track.videoFormat));\n }\n\n VideoRenderRegistry.register(track, {\n // 首帧后继续播放 750 ms 完成渲染预热,再开始渐入。\n renderWarmupMs: 750,\n renderWarmupCompletedHandler: () => {\n if (this.track === track) {\n this.ready = true;\n this.callbacks.onRenderWarmupCompleted?.();\n }\n },\n frameDisplayHandler: () => {\n if (this.track === track) {\n this.callbacks.onFrameDisplayed?.();\n }\n },\n attachHandler: view => {\n if (this.track !== track) {\n return;\n }\n this.ready = false;\n // 生成结果不再次镜像;显示镜像仍可由视图的公开属性设置。\n view.isMirrored = false;\n this.target = view;\n view.setMediaStream(track.mediaStreamTrack ? new MediaStream([track.mediaStreamTrack]) : null);\n },\n detachHandler: view => {\n if (this.target === view) {\n this.ready = false;\n this.target = undefined;\n }\n view.setMediaStream(null);\n },\n });\n }\n\n /**\n * 更新媒体画面;暂停显示时同时禁用交互。\n */\n setRemoteMedia(\n track: MediaStreamTrack | null\n ): void {\n this.ready = false;\n if (this.track) {\n TrajectoryRegistry.binding(this.track)?.update(!!track && this.interactionEnabled);\n }\n this.target?.setMediaStream(track ? new MediaStream([track]) : null);\n }\n\n /**\n * 更新交互绑定的输入格式和可用性。\n */\n setInteractionEnabled(\n enabled: boolean,\n format?: RealtimeVideoFormat\n ): void {\n this.interactionEnabled = enabled;\n if (this.track) {\n TrajectoryRegistry.binding(this.track)?.update(enabled, format);\n }\n }\n\n /**\n * 先使能力失效再清空画面,迟到首帧和旧手势不能恢复已结束任务。\n */\n resetRemoteTrack(): void {\n this.ready = false;\n const track = this.track;\n this.track = undefined;\n this.interactionEnabled = false;\n if (track) {\n TrajectoryRegistry.unregister(track);\n VideoRenderRegistry.unregister(track);\n }\n this.target?.setMediaStream(null);\n this.target = undefined;\n }\n}\n","//\n// RealtimeContext.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\n/**\n * 实时生成指令和参考资源。\n */\nexport interface RealtimeContextInit {\n\n /**\n * 实时生成使用的文本条件。\n */\n prompt: string;\n\n /**\n * 已上传参考图片的可选远端路径。\n */\n referencePath?: string;\n}\n\n/**\n * 单次实时生成任务的文本和参考资源上下文。\n */\nexport class RealtimeContext {\n\n /**\n * 实时生成使用的文本条件。\n */\n readonly prompt: string;\n\n /**\n * 已上传参考图片的可选远端路径。\n */\n readonly referencePath?: string;\n\n /**\n * 创建实时生成条件并规范化文本和参考路径。\n */\n constructor(\n init: RealtimeContextInit\n ) {\n this.prompt = init.prompt.trim();\n const normalizedReferencePath = init.referencePath?.trim();\n this.referencePath = normalizedReferencePath || undefined;\n }\n}\n","//\n// RealtimeSession.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\nimport type { RtcSessionConnection } from \"./RealtimeSessionConnection\";\n\n/**\n * 实时会话身份、状态和连接凭据。\n */\nexport interface RealtimeSessionInit {\n\n /**\n * 会话标识(sessionUid)。\n */\n id: string;\n\n /**\n * 业务用户标识。\n */\n userID?: string;\n\n /**\n * 会话状态(如 ACTIVE)。\n */\n status?: string;\n\n /**\n * RTC 连接参数。\n */\n connection?: RtcSessionConnection;\n\n /**\n * 会话关闭原因。\n */\n closeReason?: string;\n}\n\n/**\n * Xmax 实时生成会话。\n */\nexport class RealtimeSession {\n\n /**\n * 会话标识(sessionUid)。\n */\n readonly id: string;\n\n /**\n * 业务用户标识。\n */\n readonly userID?: string;\n\n /**\n * 会话状态(如 ACTIVE);响应未携带时为空。\n */\n readonly status?: string;\n\n /**\n * 会话关闭原因。\n */\n readonly closeReason?: string;\n\n /**\n * RTC 连接参数;心跳等响应未携带完整参数时为空。\n */\n readonly connection?: RtcSessionConnection;\n\n /**\n * 创建实时会话。\n */\n constructor(\n init: RealtimeSessionInit\n ) {\n this.id = init.id;\n this.userID = init.userID;\n this.status = init.status;\n this.connection = init.connection;\n this.closeReason = init.closeReason;\n }\n}\n","//\n// RoomJoinConfiguration.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\nimport { RtcProvider } from \"./RtcProvider\";\n\n/**\n * TRTC 进房身份和鉴权参数。\n */\nexport interface RoomJoinConfigurationInit {\n\n /**\n * TRTC 房间号(字符串房间号)。\n */\n roomID: string;\n\n /**\n * TRTC 登录身份(`rtc_user_id`)。\n */\n userID: string;\n\n /**\n * TRTC SDKAppID(字符串形式,进房时转换为数值)。\n */\n sdkAppID: string;\n\n /**\n * TRTC 登录签名。\n */\n userSig: string;\n\n /**\n * TRTC 进房鉴权字段(字符串房间号对应的 privateMapKey)。\n */\n privateMapKey: string;\n}\n\n/**\n * RTC 房间加入参数。\n */\nexport class RoomJoinConfiguration {\n\n /**\n * RTC 提供方。\n */\n readonly provider = RtcProvider.trtc;\n\n /**\n * TRTC 房间号(字符串房间号)。\n */\n readonly roomID: string;\n\n /**\n * TRTC 登录身份(`rtc_user_id`)。\n */\n readonly userID: string;\n\n /**\n * TRTC SDKAppID(字符串形式,进房时转换为数值)。\n */\n readonly sdkAppID: string;\n\n /**\n * TRTC 登录签名。\n */\n readonly userSig: string;\n\n /**\n * TRTC 进房鉴权字段(字符串房间号对应的 privateMapKey)。\n */\n readonly privateMapKey: string;\n\n /**\n * 创建房间加入参数。\n */\n constructor(\n init: RoomJoinConfigurationInit\n ) {\n this.roomID = init.roomID;\n this.userID = init.userID;\n this.sdkAppID = init.sdkAppID;\n this.userSig = init.userSig;\n this.privateMapKey = init.privateMapKey;\n }\n}\n\n/**\n * 声网进房参数;App ID、频道、用户和 Token 必须来自同一会话。\n */\nexport interface AgoraRoomJoinConfiguration {\n\n /**\n * RTC 提供方,决定进房参数和凭据的解析方式。\n */\n readonly provider: RtcProvider.agora;\n\n /**\n * RTC 房间标识。\n */\n readonly roomID: string;\n\n /**\n * RTC 用户标识。\n */\n readonly userID: string;\n\n /**\n * RTC 应用标识。\n */\n readonly appID: string;\n\n /**\n * 进房及续期使用的鉴权令牌。\n */\n readonly roomToken: string;\n}\n\n/**\n * 火山引擎进房参数,不混用不同厂商的鉴权字段。\n */\nexport interface VeRtcRoomJoinConfiguration {\n\n /**\n * RTC 提供方,决定进房参数和凭据的解析方式。\n */\n readonly provider: RtcProvider.vertc;\n\n /**\n * RTC 房间标识。\n */\n readonly roomID: string;\n\n /**\n * RTC 用户标识。\n */\n readonly userID: string;\n\n /**\n * RTC 应用标识。\n */\n readonly appID: string;\n\n /**\n * 进房及续期使用的鉴权令牌。\n */\n readonly roomToken: string;\n}\n\n/**\n * 按 RTC 提供方区分的进房参数。\n */\nexport type RtcRoomJoinConfiguration = RoomJoinConfiguration | AgoraRoomJoinConfiguration | VeRtcRoomJoinConfiguration;\n","//\n// RealtimeSessionConnection.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\nimport { RtcProvider } from \"../../Foundation/RTC/RtcProvider\";\nimport type { XmaxError } from \"../../Foundation/Errors/XmaxError\";\nimport type { RealtimeSession } from \"./RealtimeSession\";\nimport { RoomJoinConfiguration, type RtcRoomJoinConfiguration, type VeRtcRoomJoinConfiguration } from \"../../Foundation/RTC/RoomJoinConfiguration\";\n\n/**\n * TRTC 会话对应的房间身份和鉴权凭据。\n */\nexport interface RealtimeSessionConnectionInit {\n\n /**\n * TRTC 凭据的提供方判别字段。\n */\n provider: RtcProvider.trtc;\n\n /**\n * TRTC 房间号(字符串房间号)。\n */\n roomID: string;\n\n /**\n * TRTC SDKAppID(按字符串处理)。\n */\n sdkAppID: string;\n\n /**\n * TRTC 登录身份(`rtc_user_id`,不是业务 `user_id`)。\n */\n userID: string;\n\n /**\n * TRTC 登录签名。\n */\n userSig: string;\n\n /**\n * TRTC 进房鉴权字段(字符串房间号对应的 privateMapKey)。\n */\n privateMapKey: string;\n\n /**\n * 房间内生成 Bot 的用户标识,用于匹配远端结果流。\n */\n botID?: string;\n}\n\n/**\n * 实时会话对应的 RTC 连接参数(TRTC)。\n *\n * 凭据来自会话接口 `data.modelExtra`,provider 由接入配置决定;心跳成功后服务端可能\n * 下发新的 `userSig` / `privateMapKey`,使用方应覆盖本地缓存。\n */\nexport class RealtimeSessionConnection {\n\n /**\n * TRTC 凭据的提供方判别字段。\n */\n readonly provider: RtcProvider.trtc;\n\n /**\n * TRTC 房间号(字符串房间号)。\n */\n readonly roomID: string;\n\n /**\n * TRTC SDKAppID(按字符串处理)。\n */\n readonly sdkAppID: string;\n\n /**\n * TRTC 登录身份(`rtc_user_id`,不是业务 `user_id`)。\n */\n readonly userID: string;\n\n /**\n * TRTC 登录签名。\n */\n readonly userSig: string;\n\n /**\n * TRTC 进房鉴权字段(字符串房间号对应的 privateMapKey)。\n */\n readonly privateMapKey: string;\n\n /**\n * 房间内生成 Bot 的用户标识,用于匹配远端结果流。\n */\n readonly botID?: string;\n\n /**\n * 创建 RTC 连接参数。\n */\n constructor(\n init: RealtimeSessionConnectionInit\n ) {\n this.provider = init.provider;\n this.roomID = init.roomID;\n this.sdkAppID = init.sdkAppID;\n this.userID = init.userID;\n this.userSig = init.userSig;\n this.privateMapKey = init.privateMapKey;\n this.botID = init.botID;\n }\n}\n\n/**\n * 实时会话心跳失败回调。\n */\nexport type RealtimeSessionHeartbeatFailureHandler = (\n sessionID: string,\n error: XmaxError,\n) => void | Promise<void>;\n\n/**\n * 实时会话心跳刷新回调;心跳成功且会话仍活跃时携带最新会话数据。\n */\nexport type RealtimeSessionHeartbeatRefreshHandler = (\n session: RealtimeSession,\n) => void | Promise<void>;\n\n/**\n * 声网会话凭证;频道与 UID 原样用于 RTC 入房和房间信令。\n */\nexport interface AgoraSessionConnection {\n\n /**\n * 提供方与房间身份\n */\n readonly provider: RtcProvider.agora;\n\n /**\n * RTC 房间标识。\n */\n readonly roomID: string;\n\n /**\n * RTC 应用标识。\n */\n readonly appID: string;\n\n /**\n * RTC 用户标识。\n */\n readonly userID: string;\n\n /**\n * 生成结果流对应的房间用户标识。\n */\n readonly botID?: string;\n\n /**\n * RTC 入房与续期凭证,不写入日志。\n */\n readonly roomToken: string;\n}\n\n/**\n * RTC 会话连接参数,按 provider 区分厂商凭证。\n */\nexport interface VeRtcSessionConnection extends VeRtcRoomJoinConfiguration {\n\n /**\n * 生成结果流对应的房间用户标识。\n */\n readonly botID?: string;\n}\n\n/**\n * 按 RTC 提供方区分的会话连接凭据。\n */\nexport type RtcSessionConnection = RealtimeSessionConnection | AgoraSessionConnection | VeRtcSessionConnection;\n\n/**\n * provider 由接入配置赋值,只在 SDK 内部区分凭据类型。\n */\nexport function toRoomJoinConfiguration(\n connection: RtcSessionConnection\n): RtcRoomJoinConfiguration {\n return connection.provider === RtcProvider.trtc ? new RoomJoinConfiguration(connection) : connection;\n}\n\n/**\n * 从对应 RTC 类型的会话凭据中读取应用标识。\n */\nexport function connectionAppID(\n connection: RtcSessionConnection\n): string {\n return connection.provider === RtcProvider.trtc ? connection.sdkAppID : connection.appID;\n}\n","//\n// RealtimeSessionService.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\nimport { XmaxError, XmaxErrorCode } from \"../../Foundation/Errors/XmaxError\";\nimport type { RealtimeModelName } from \"./RealtimeModel\";\nimport { RtcProvider } from \"../../Foundation/RTC/RtcProvider\";\nimport type { ApiServicing } from \"../Network/ApiServicing\";\nimport { RealtimeSession } from \"./RealtimeSession\";\nimport { RealtimeSessionConnection, type RtcSessionConnection } from \"./RealtimeSessionConnection\";\nimport type {\n RealtimeSessionHeartbeatHandlers,\n RealtimeSessionServicing,\n} from \"./RealtimeSessionServicing\";\n\n/**\n * 会话接口返回的统一数据。\n */\ninterface SessionPayload {\n\n /**\n * 服务端会话标识。\n */\n sessionUid?: string;\n\n /**\n * 业务用户标识。\n */\n userUid?: string;\n\n /**\n * 服务端返回的运行状态。\n */\n status?: string;\n\n /**\n * 服务端返回的 RTC 凭据,按接入配置解析。\n */\n modelExtra?: unknown;\n\n /**\n * 服务端返回的会话关闭原因。\n */\n closeReason?: string;\n}\n\n/**\n * `modelExtra` 中的 RTC 凭据;厂商始终由接入配置决定。\n */\ninterface ConnectionPayload {\n\n /**\n * 服务端返回的 RTC 房间标识。\n */\n room_id?: string;\n\n /**\n * VeRTC 会话使用的业务用户标识。\n */\n user_id?: string;\n\n /**\n * VeRTC 结果流的用户标识。\n */\n bot_name?: string;\n\n /**\n * RTC 应用标识。\n */\n rtc_app_id?: string;\n\n /**\n * TRTC 或 Agora 进房使用的用户标识。\n */\n rtc_user_id?: string;\n\n /**\n * TRTC 用户登录签名。\n */\n user_sig?: string;\n\n /**\n * TRTC 字符串房间号对应的进房鉴权凭据。\n */\n private_map_key_with_string_room_id?: string;\n\n /**\n * 生成结果流的 RTC 用户标识。\n */\n rtc_bot_id?: string;\n\n /**\n * Agora 或 VeRTC 的进房令牌。\n */\n room_token?: string;\n}\n\n/**\n * 会话接口、RTC 类型和心跳策略。\n */\nexport interface RealtimeSessionServiceOptions {\n\n /**\n * Xmax API 请求组件。\n */\n apiService: ApiServicing;\n\n /**\n * 接入方选择的 RTC 厂商;不读取后端 provider,不根据模型推断。\n */\n provider?: RtcProvider;\n\n /**\n * 心跳间隔(毫秒);默认 10 秒。\n */\n heartbeatIntervalMs?: number;\n\n /**\n * 心跳等待实现(可替换,测试用)。\n */\n sleep?: (\n ms: number\n ) => Promise<void>;\n}\n\n/**\n * 管理实时生成会话的创建、心跳维持和关闭。\n *\n * 心跳成功后会把携带最新 RTC 凭据的会话通过 `onRefresh` 回调给\n * 使用方覆盖本地缓存;心跳使用版本号使已停止心跳的迟到结果失效。\n */\nexport class RealtimeSessionService implements RealtimeSessionServicing {\n\n /**\n * 服务层组件\n */\n private readonly apiService: ApiServicing;\n\n /**\n * RTC 提供方,决定进房参数和凭据的解析方式。\n */\n private readonly provider: RtcProvider;\n\n /**\n * 心跳配置\n */\n private readonly heartbeatIntervalMs: number;\n\n /**\n * 等待指定毫秒数后继续执行的调度函数。\n */\n private readonly sleep: (\n ms: number\n ) => Promise<void>;\n\n /**\n * 运行状态\n */\n private heartbeatVersion = 0;\n\n /**\n * 会话凭证缓存,支持心跳仅返回变更字段;关闭后清除。\n */\n private readonly credentials = new Map<string, ConnectionPayload>();\n\n /**\n * 按会话标识维护的凭据刷新操作。\n */\n private readonly refreshes = new Map<string, Promise<RealtimeSession>>();\n\n /**\n * 创建实时生成会话 Service。\n *\n * @param options.apiService Xmax API 请求组件。\n * @param options.heartbeatIntervalMs 心跳间隔(毫秒);默认 10 秒。\n * @param options.sleep 心跳等待实现(可替换,测试用)。\n */\n constructor(\n options: RealtimeSessionServiceOptions\n ) {\n this.apiService = options.apiService;\n this.provider = options.provider ?? RtcProvider.trtc;\n if (!Object.values(RtcProvider).includes(this.provider)) {\n throw new XmaxError(XmaxErrorCode.invalidConfiguration, \"Unsupported RTC provider\");\n }\n this.heartbeatIntervalMs = options.heartbeatIntervalMs ?? 10_000;\n this.sleep =\n options.sleep ??\n ((ms) => new Promise<void>((resolve) => setTimeout(resolve, ms)));\n }\n\n /**\n * 创建实时会话并返回 RTC 连接信息。\n */\n async createSession(\n model: RealtimeModelName\n ): Promise<RealtimeSession> {\n const payload = await this.apiService.post<SessionPayload>(\"/session\", {\n model,\n });\n try {\n const session = this.makeSession(payload, true);\n RealtimeSessionService.ensureSessionActive(session);\n const credentials = RealtimeSessionService.decodeConnectionPayload(payload.modelExtra)!;\n this.credentials.set(session.id, this.provider === RtcProvider.vertc\n ? { ...credentials, user_id: session.connection!.userID } : credentials);\n return session;\n } catch (error) {\n // 后端已创建会话但凭据无效时,也必须尽力关闭,避免留下计费会话。\n const sessionID = RealtimeSessionService.nonEmpty(payload.sessionUid);\n if (sessionID)\n await this.closeSession(sessionID).catch(() => { });\n throw error;\n }\n }\n\n /**\n * 启动指定会话的周期心跳;重复启动会替换当前心跳。\n */\n startHeartbeat(\n sessionID: string,\n handlers: RealtimeSessionHeartbeatHandlers,\n ): void {\n const version = ++this.heartbeatVersion;\n void this.runHeartbeat(version, sessionID, handlers);\n }\n\n /**\n * 停止当前心跳;已经失效的迟到结果不会再触发回调。\n */\n stopHeartbeat(): void {\n this.heartbeatVersion += 1;\n }\n\n /**\n * 关闭指定实时会话;请求带 keepalive,页面卸载后仍能发出。\n */\n async closeSession(\n sessionID: string\n ): Promise<void> {\n this.credentials.delete(sessionID);\n this.refreshes.delete(sessionID);\n try {\n await this.apiService.delete<Record<string, never>>(\n `/session/${sessionID}`,\n { keepalive: true },\n );\n } catch (error) {\n throw XmaxError.from(error);\n }\n }\n\n /**\n * 周期心跳:等待 → 请求 → 校验会话活跃并回调刷新;失败时失效版本并回调。\n */\n private async runHeartbeat(\n version: number,\n sessionID: string,\n handlers: RealtimeSessionHeartbeatHandlers,\n ): Promise<void> {\n while (this.heartbeatVersion === version) {\n await this.sleep(this.heartbeatIntervalMs);\n if (this.heartbeatVersion !== version) {\n return;\n }\n try {\n const session = await this.heartbeatSession(sessionID);\n if (this.heartbeatVersion !== version) {\n return;\n }\n RealtimeSessionService.ensureSessionActive(session);\n await handlers.onRefresh?.(session);\n } catch (error) {\n if (this.heartbeatVersion !== version) {\n return;\n }\n this.heartbeatVersion += 1;\n await handlers.onFailure(sessionID, XmaxError.from(error));\n return;\n }\n }\n }\n\n /**\n * 发送一次心跳请求并解析会话数据(不要求携带完整连接参数)。\n */\n heartbeatSession(\n sessionID: string\n ): Promise<RealtimeSession> {\n const pending = this.refreshes.get(sessionID);\n if (pending)\n return pending;\n const operation = this.performHeartbeat(sessionID);\n this.refreshes.set(sessionID, operation);\n void operation.finally(() => {\n if (this.refreshes.get(sessionID) === operation)\n this.refreshes.delete(sessionID);\n }).catch(() => { });\n return operation;\n }\n\n /**\n * 合并心跳部分凭据;已关闭会话的迟到响应不重建缓存。\n */\n private async performHeartbeat(\n sessionID: string\n ): Promise<RealtimeSession> {\n const previous = this.credentials.get(sessionID);\n const payload = await this.apiService.put<SessionPayload>(\n `/session/${sessionID}/heartbeat`,\n );\n const incoming = RealtimeSessionService.decodeConnectionPayload(payload.modelExtra);\n if (payload.modelExtra != null && !incoming) {\n throw new XmaxError(XmaxErrorCode.sessionError, \"Invalid heartbeat RTC credentials\");\n }\n const merged = incoming ? { ...previous, ...incoming } : previous;\n const session = this.makeSession({ ...payload, modelExtra: merged }, merged !== undefined);\n if (session.id !== sessionID)\n throw new XmaxError(XmaxErrorCode.sessionError, \"Heartbeat session identity changed\");\n RealtimeSessionService.ensureSessionActive(session);\n if (previous && this.credentials.get(sessionID) === previous && merged)\n this.credentials.set(sessionID, merged);\n return session;\n }\n\n /**\n * 校验会话仍处于活跃状态;状态明确非 ACTIVE 时抛出会话错误。\n */\n private static ensureSessionActive(\n session: RealtimeSession\n ): void {\n if (!session.status || session.status === \"ACTIVE\") {\n return;\n }\n throw new XmaxError(\n XmaxErrorCode.sessionError,\n session.closeReason ?? `Session is no longer active: ${session.status}`,\n );\n }\n\n /**\n * 由会话接口数据构造会话模型;`requiresConnection` 要求携带完整 RTC 连接参数。\n */\n private makeSession(\n payload: SessionPayload,\n requiresConnection: boolean,\n ): RealtimeSession {\n const sessionID = RealtimeSessionService.nonEmpty(payload.sessionUid);\n if (!sessionID) {\n throw new XmaxError(XmaxErrorCode.sessionError, \"Invalid session response\");\n }\n\n const connection = this.makeConnection(payload.modelExtra, payload.userUid);\n if (requiresConnection && !connection) {\n throw new XmaxError(\n XmaxErrorCode.sessionError,\n \"Session does not contain complete RTC join information\",\n );\n }\n\n return new RealtimeSession({\n id: sessionID,\n userID: RealtimeSessionService.nonEmpty(payload.userUid),\n status: RealtimeSessionService.nonEmpty(payload.status),\n connection,\n closeReason: RealtimeSessionService.nonEmpty(payload.closeReason),\n });\n }\n\n /**\n * 解析 `modelExtra` 中的 RTC 连接参数;字段不完整或提供方不支持时返回空。\n */\n private makeConnection(\n modelExtra: unknown,\n fallbackUserID?: string,\n ): RtcSessionConnection | undefined {\n const payload = RealtimeSessionService.decodeConnectionPayload(modelExtra);\n if (!payload) {\n return undefined;\n }\n\n const provider = this.provider;\n\n const roomID = RealtimeSessionService.nonEmpty(payload.room_id);\n const sdkAppID = RealtimeSessionService.nonEmpty(payload.rtc_app_id);\n const userID = provider === RtcProvider.vertc\n ? RealtimeSessionService.nonEmpty(payload.user_id) ?? RealtimeSessionService.nonEmpty(fallbackUserID)\n : RealtimeSessionService.nonEmpty(payload.rtc_user_id);\n if (!roomID || !sdkAppID || !userID)\n return undefined;\n if (provider === RtcProvider.agora || provider === RtcProvider.vertc) {\n const roomToken = RealtimeSessionService.nonEmpty(payload.room_token);\n if (!roomToken)\n return undefined;\n return Object.freeze({\n provider, roomID, appID: sdkAppID, userID, roomToken,\n botID: RealtimeSessionService.nonEmpty(provider === RtcProvider.vertc ? payload.bot_name : payload.rtc_bot_id)\n });\n }\n const userSig = RealtimeSessionService.nonEmpty(payload.user_sig);\n const privateMapKey = RealtimeSessionService.nonEmpty(\n payload.private_map_key_with_string_room_id,\n );\n if (!roomID || !sdkAppID || !userID || !userSig || !privateMapKey) {\n return undefined;\n }\n\n return new RealtimeSessionConnection({\n provider: RtcProvider.trtc,\n roomID,\n sdkAppID,\n userID,\n userSig,\n privateMapKey,\n botID: RealtimeSessionService.nonEmpty(payload.rtc_bot_id),\n });\n }\n\n /**\n * `modelExtra` 可能为对象或 JSON 字符串,统一解析为连接参数字段。\n */\n private static decodeConnectionPayload(\n modelExtra: unknown,\n ): ConnectionPayload | undefined {\n if (typeof modelExtra === \"string\") {\n try {\n return RealtimeSessionService.decodeConnectionPayload(JSON.parse(modelExtra));\n } catch {\n return undefined;\n }\n }\n if (typeof modelExtra === \"object\" && modelExtra !== null && !Array.isArray(modelExtra)) {\n return modelExtra as ConnectionPayload;\n }\n return undefined;\n }\n\n /**\n * 归一化可选字符串:去除首尾空白后为空时返回 `undefined`。\n */\n private static nonEmpty(\n value?: string\n ): string | undefined {\n const normalized = typeof value === \"string\" ? value.trim() : undefined;\n return normalized || undefined;\n }\n}\n","//\n// RealtimeState.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\nimport { XmaxError } from \"../../Foundation/Errors/XmaxError\";\n\n/**\n * 实时业务连接状态。\n */\nexport enum RealtimeConnectionState {\n\n /**\n * 没有可用的本地媒体流。\n */\n idle = \"Idle\",\n\n /**\n * 正在准备本地媒体流;摄像头还需等待有效帧和预览视图绑定。\n */\n preparing = \"Preparing\",\n\n /**\n * 本地媒体流已就绪,可以预览、连接和生成。\n */\n ready = \"Ready\",\n\n /**\n * 正在创建 Session、加入 Room 并发布本地流。\n */\n connecting = \"Connecting\",\n\n /**\n * 实时连接已建立,当前没有生成任务。\n */\n connected = \"Connected\",\n\n /**\n * 正在启动生成或等待 SDK 视频视图完成首帧及渲染预热。\n * startGeneration 返回远端流后仍可能保持此状态,接入方需挂载视频视图。\n */\n starting = \"Starting\",\n\n /**\n * 生成任务正在运行,SDK 视频视图已完成渲染预热并开始展示画面。\n */\n generating = \"Generating\",\n\n /**\n * 正在清理生成、Room 和 Session 资源。\n */\n disconnecting = \"Disconnecting\",\n}\n\n/**\n * 进入当前实时状态的原因。\n */\nexport type RealtimeReason =\n | {\n kind: \"normal\"\n }\n | {\n kind: \"orientationChanged\"\n }\n | {\n kind: \"failure\";\n error: XmaxError\n };\n\n/**\n * 正常操作、画面方向变化和错误对应的状态原因。\n */\nexport const RealtimeReason = {\n normal: { kind: \"normal\" } as RealtimeReason,\n orientationChanged: { kind: \"orientationChanged\" } as RealtimeReason,\n /**\n * 将 SDK 错误包装为状态切换原因,保留错误码和原始说明。\n */\n failure(\n error: XmaxError\n ): RealtimeReason {\n return { kind: \"failure\", error };\n },\n};\n\n/**\n * 实时状态快照的生命周期、业务标识和变化原因。\n */\nexport interface RealtimeStateInit {\n\n /**\n * 当前实时连接生命周期状态。\n */\n connectionState: RealtimeConnectionState;\n\n /**\n * 业务会话标识。\n */\n sessionID?: string;\n\n /**\n * 当前生成任务标识。\n */\n taskID?: string;\n\n /**\n * 当前操作的结束或变更原因。\n */\n reason?: RealtimeReason;\n}\n\n/**\n * 实时业务当前状态快照。\n */\nexport class RealtimeState {\n\n /**\n * 当前连接生命周期状态。\n */\n readonly connectionState: RealtimeConnectionState;\n\n /**\n * 当前或最近一次实时 Session 标识。\n */\n readonly sessionID?: string;\n\n /**\n * 当前生成任务标识。\n */\n readonly taskID?: string;\n\n /**\n * 进入当前状态的原因;正常开始新的操作时清空。\n */\n readonly reason?: RealtimeReason;\n\n /**\n * 创建实时状态快照。\n */\n constructor(\n init: RealtimeStateInit\n ) {\n this.connectionState = init.connectionState;\n this.sessionID = init.sessionID;\n this.taskID = init.taskID;\n this.reason = init.reason;\n }\n\n /**\n * 判断当前状态与另一状态是否等价:连接状态、会话、任务与原因种类一致。\n *\n * @param other 待比较的状态。\n */\n equals(\n other: RealtimeState\n ): boolean {\n return (\n this.connectionState === other.connectionState &&\n this.sessionID === other.sessionID &&\n this.taskID === other.taskID &&\n this.reason?.kind === other.reason?.kind\n );\n }\n}\n\n/**\n * 实时状态监听器。\n */\nexport type RealtimeStateListener = (\n state: RealtimeState\n) => void;\n","//\n// RemoteStream.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\n/**\n * 远端视频流的身份和轨道信息。\n */\nexport interface RemoteStreamInit {\n\n /**\n * 远端流所在房间号。\n */\n roomID: string;\n\n /**\n * 远端流所属用户标识。\n */\n userID: string;\n}\n\n/**\n * 标识 RTC 房间中的一条远端主流。\n */\nexport class RemoteStream {\n\n /**\n * 远端流所在房间号。\n */\n readonly roomID: string;\n\n /**\n * 远端流所属用户标识。\n */\n readonly userID: string;\n\n /**\n * 创建远端流标识。\n */\n constructor(\n init: RemoteStreamInit\n ) {\n this.roomID = init.roomID;\n this.userID = init.userID;\n }\n\n /**\n * 跨房间唯一的远端流键。\n */\n get key(): string {\n return `${this.roomID}:${this.userID}`;\n }\n}\n","//\n// RoomEvent.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\nimport { XmaxError, XmaxErrorCode } from \"../../Foundation/Errors/XmaxError\";\nimport { RuntimeInfo } from \"../../Foundation/Runtime/RuntimeInfo\";\nimport type { RealtimeContext } from \"../../Service/Realtime/RealtimeContext\";\nimport type { RealtimeExtraParams } from \"../../Service/Realtime/RealtimeExtraParams\";\nimport type { RealtimePoint } from \"../../Service/Realtime/RealtimePoint\";\nimport type { RealtimeVideoFormat } from \"../../Service/Realtime/RealtimeVideoFormat\";\n\n/**\n * 生成或回传使用的整数像素尺寸。\n */\nexport interface RoomEventTargetSize {\n\n /**\n * 视频宽度,单位为像素。\n */\n width: number;\n\n /**\n * 视频高度,单位为像素。\n */\n height: number;\n}\n\n/**\n * 生成条件类信令的公共参数。\n */\nexport interface RoomEventGenerationOptions {\n\n /**\n * 业务用户标识。\n */\n userID: string;\n\n /**\n * 当前生成任务标识。\n */\n taskID: string;\n\n /**\n * 模型生成使用的视频规格。\n */\n videoFormat: RealtimeVideoFormat;\n\n /**\n * 当前生成条件。\n */\n context: RealtimeContext;\n\n /**\n * 服务端生成后的回传尺寸,缺省时不发送。\n */\n targetSize?: RoomEventTargetSize;\n\n /**\n * 是否回贴原始人脸,默认 false。\n */\n faceRestoration?: boolean;\n\n /**\n * 是否由服务端保留媒体比例并填充生成画布。\n */\n paddingContain?: boolean;\n\n /**\n * 合并到本次生成信令的 params,不缓存。\n */\n extraParams?: RealtimeExtraParams;\n}\n\n/**\n * 生成 SDK 与 RTC 房间之间传输的结构化事件消息。\n *\n * 控制消息附带 `runtime` 运行环境信息,轨迹快照仅发送交互字段;\n * 编码失败时抛出 `internalError`。\n */\nexport class RoomEvent {\n\n /**\n * 生成开始信令。\n *\n * @param options.userID 业务用户标识。\n * @param options.taskID 当前生成任务标识。\n * @param options.videoFormat 模型生成使用的视频规格。\n * @param options.context 本次生成条件。\n * @param options.targetSize 回传尺寸,缺省时不发送。\n * @param options.minimumBitrate 服务端回传的最低码率,单位为 kbps。\n * @param options.maximumBitrate 服务端回传的最高码率,单位为 kbps。\n * @param options.mirror 是否由后端镜像输入画面,默认 false。\n * @param options.faceRestoration 是否回贴原始人脸,默认 false。\n * @param options.extraParams 已校验的本次扩展参数,合并到 params。\n * @returns 序列化后的 start 房间消息。\n */\n static start(\n options: {\n userID: string;\n taskID: string;\n videoFormat: RealtimeVideoFormat;\n context: RealtimeContext;\n targetSize?: RoomEventTargetSize;\n minimumBitrate: number;\n maximumBitrate: number;\n mirror?: boolean;\n faceRestoration?: boolean;\n paddingContain?: boolean;\n extraParams?: RealtimeExtraParams;\n }\n ): string {\n return RoomEvent.encode({\n event: \"start\",\n params: {\n ...RoomEvent.generationParameters(options),\n min_bitrate: options.minimumBitrate,\n max_bitrate: options.maximumBitrate,\n mirror: options.mirror ?? false,\n },\n user_id: options.userID,\n uid: options.taskID,\n });\n }\n\n /**\n * 生成条件变更信令。\n *\n * @param options.userID 业务用户标识。\n * @param options.taskID 当前生成任务标识。\n * @param options.videoFormat 模型生成使用的视频规格。\n * @param options.context 本次生成条件。\n * @param options.targetSize 回传尺寸,缺省时不发送。\n * @param options.faceRestoration 是否回贴原始人脸,默认 false。\n * @param options.extraParams 已校验的本次扩展参数,合并到 params。\n * @returns 序列化后的 change_condition 房间消息。\n */\n static changeCondition(\n options: RoomEventGenerationOptions\n ): string {\n return RoomEvent.encode({\n event: \"change_condition\",\n params: RoomEvent.generationParameters(options),\n user_id: options.userID,\n uid: options.taskID,\n });\n }\n\n /**\n * 回传尺寸和码率范围调整信令,码率单位为 kbps。\n */\n static changeTargetSize(\n options: {\n userID: string;\n taskID: string;\n targetSize: RoomEventTargetSize;\n minimumBitrate: number;\n maximumBitrate: number;\n }\n ): string {\n return RoomEvent.encode({\n event: \"change_target_size\",\n params: {\n min_bitrate: options.minimumBitrate,\n max_bitrate: options.maximumBitrate,\n target_size: [\n Math.round(options.targetSize.width),\n Math.round(options.targetSize.height),\n ],\n },\n user_id: options.userID,\n uid: options.taskID,\n });\n }\n\n /**\n * 生成停止信令。\n */\n static stop(\n options: {\n userID: string;\n taskID: string\n }\n ): string {\n return RoomEvent.encode({\n event: \"stop\",\n user_id: options.userID,\n uid: options.taskID,\n });\n }\n\n /**\n * 交互轨迹信令。\n */\n static tracks(\n options: {\n userID: string;\n taskID: string;\n points: RealtimePoint[];\n }\n ): string {\n return RoomEvent.encode({\n event: \"tracks\",\n tracks: options.points.map((point) => [point.x, point.y]),\n user_id: options.userID,\n uid: options.taskID,\n }, false);\n }\n\n /**\n * 房间心跳信令。\n */\n static heartbeat(\n options: {\n userID: string\n }\n ): string {\n return RoomEvent.encode({\n event: \"heartbeat\",\n user_id: options.userID,\n });\n }\n\n /**\n * 组装生成条件参数,包含人脸回贴配置及本次扩展参数。\n *\n * @param options 本次生成条件类信令的参数。\n * @returns start 与 change_condition 共用的 params 字段。\n */\n private static generationParameters(\n options: RoomEventGenerationOptions,\n ): Record<string, unknown> {\n return {\n model: \"default\",\n face_replace: options.faceRestoration ?? false,\n size: [options.videoFormat.width, options.videoFormat.height],\n ...(options.paddingContain && { padding: \"contain\" }),\n target_size: options.targetSize\n ? [\n Math.round(options.targetSize.width),\n Math.round(options.targetSize.height),\n ]\n : undefined,\n prompt: options.context.prompt,\n ref_image_path: options.context.referencePath,\n ...options.extraParams,\n };\n }\n\n /**\n * 序列化业务字段;高频轨迹不重复附带运行环境信息。\n */\n private static encode(\n payload: Record<string, unknown>,\n includeRuntime = true,\n ): string {\n try {\n return JSON.stringify(includeRuntime ? { ...payload, runtime: RuntimeInfo.toJSON() } : payload);\n } catch {\n throw new XmaxError(\n XmaxErrorCode.internalError,\n \"Failed to encode RTC room event\",\n );\n }\n }\n}\n","//\n// EncodingBitratePolicy.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\nimport { XmaxError, XmaxErrorCode } from \"../../Foundation/Errors/XmaxError\";\nimport { RealtimeVideoFormat } from \"../../Service/Realtime/RealtimeVideoFormat\";\n\n/**\n * 像素面积或帧率对应的码率参考点,单位为 kbps。\n */\ninterface BitratePoint {\n\n /**\n * 用于查找默认码率的像素或帧率边界。\n */\n value: number;\n\n /**\n * 对应边界的默认码率,单位为 kbps。\n */\n bitrate: number;\n}\n\n/**\n * 上下行共用的默认码率预算;显式上传码率由编码控制器优先处理。\n */\nexport class EncodingBitratePolicy {\n\n /**\n * 最高码率相对参考值的预算系数。\n */\n private static readonly bitrateScale = 0.6;\n\n /**\n * 最低码率固定为 100 kbps,最高码率按尺寸和帧率计算并应用预算系数。\n */\n static resolve(\n width: number,\n height: number,\n fps: number\n ): {\n minimum: number;\n maximum: number\n } {\n new RealtimeVideoFormat({ width, height, fps }).validate();\n const maximum = Math.round(\n this.resolveMaximumBitrate(width * height, fps) * this.bitrateScale,\n );\n if (!Number.isSafeInteger(maximum)) {\n throw new XmaxError(\n XmaxErrorCode.invalidConfiguration,\n \"Realtime video format exceeds the supported bitrate range\",\n );\n }\n\n return { minimum: 100, maximum: Math.max(100, maximum) };\n }\n\n /**\n * 按像素面积和帧率插值计算最高码率参考值。\n */\n private static resolveMaximumBitrate(\n pixels: number,\n fps: number\n ): number {\n const bitrate15 = EncodingBitratePolicy.interpolate(\n pixels,\n EncodingBitratePolicy.referenceBitratesAt15FPS,\n );\n\n const first30 = EncodingBitratePolicy.referenceBitratesAt30FPS[0]!;\n let bitrate30: number;\n if (pixels < first30.value) {\n // 小尺寸沿用 15fps 的尺寸曲线,衔接 30fps 的首个参考点。\n const reference15 = EncodingBitratePolicy.interpolate(\n first30.value,\n EncodingBitratePolicy.referenceBitratesAt15FPS,\n );\n bitrate30 = bitrate15 * (first30.bitrate / reference15);\n } else {\n bitrate30 = EncodingBitratePolicy.interpolate(\n pixels,\n EncodingBitratePolicy.referenceBitratesAt30FPS,\n );\n }\n\n // 10fps 以 640×480 的 400kbps 为参考,沿用 15fps 的尺寸曲线。\n const bitrate10 = bitrate15 * (400 / 500);\n\n // 60fps 沿用 30fps 的尺寸曲线,匹配 1080p 的画质优先参考值。\n const maximum60 = bitrate30 * (6500 / 3150);\n\n return EncodingBitratePolicy.interpolate(fps, [\n { value: 10, bitrate: bitrate10 * 2 },\n { value: 15, bitrate: bitrate15 * 2 },\n { value: 30, bitrate: bitrate30 * 2 },\n { value: 60, bitrate: maximum60 },\n ]);\n }\n\n /**\n * 在相邻参考点间线性插值,表外按最近端点的比例外推。\n */\n private static interpolate(\n value: number,\n points: BitratePoint[]\n ): number {\n const first = points[0]!;\n if (value <= first.value) {\n return first.bitrate * (value / first.value);\n }\n\n for (let index = 1; index < points.length; index += 1) {\n const upper = points[index]!;\n if (value <= upper.value) {\n const lower = points[index - 1]!;\n const ratio = (value - lower.value) / (upper.value - lower.value);\n return lower.bitrate + (upper.bitrate - lower.bitrate) * ratio;\n }\n }\n\n const last = points[points.length - 1]!;\n return last.bitrate * (value / last.value);\n }\n\n /**\n * 流畅优先码率参考,按像素面积升序排列,单位为 kbps。\n */\n private static readonly referenceBitratesAt15FPS: BitratePoint[] = [\n { value: 120 * 120, bitrate: 50 },\n { value: 160 * 120, bitrate: 65 },\n { value: 180 * 180, bitrate: 100 },\n { value: 240 * 180, bitrate: 120 },\n { value: 320 * 180, bitrate: 140 },\n { value: 320 * 240, bitrate: 200 },\n { value: 424 * 240, bitrate: 220 },\n { value: 360 * 360, bitrate: 260 },\n { value: 480 * 360, bitrate: 320 },\n { value: 640 * 360, bitrate: 400 },\n { value: 640 * 480, bitrate: 500 },\n { value: 848 * 480, bitrate: 610 },\n { value: 960 * 720, bitrate: 910 },\n { value: 1280 * 720, bitrate: 1130 },\n { value: 1920 * 1080, bitrate: 2080 },\n ];\n\n /**\n * 30 fps 的像素面积与码率参考点,单位为 kbps。\n */\n private static readonly referenceBitratesAt30FPS: BitratePoint[] = [\n { value: 360 * 360, bitrate: 400 },\n { value: 480 * 360, bitrate: 490 },\n { value: 640 * 360, bitrate: 600 },\n { value: 640 * 480, bitrate: 750 },\n { value: 848 * 480, bitrate: 930 },\n { value: 960 * 720, bitrate: 1380 },\n { value: 1280 * 720, bitrate: 1710 },\n { value: 1920 * 1080, bitrate: 3150 },\n ];\n}\n","//\n// RoomHeartbeat.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\nimport { XmaxError } from \"../../Foundation/Errors/XmaxError\";\nimport { XmaxLogger } from \"../../Foundation/Logging/XmaxLogger\";\nimport type { RtcManaging } from \"../../Foundation/RTC/RtcManaging\";\nimport { RoomEvent } from \"./RoomEvent\";\n\n/**\n * 房间心跳发送间隔与等待策略。\n */\nexport interface RoomHeartbeatOptions {\n\n /**\n * RTC 引擎与消息能力组件。\n */\n rtcManager: RtcManaging;\n\n /**\n * 心跳间隔(毫秒);默认 10 秒。\n */\n intervalMs?: number;\n\n /**\n * 心跳等待实现(可替换,测试用)。\n */\n sleep?: (\n ms: number\n ) => Promise<void>;\n}\n\n/**\n * 周期发送 RTC 房间心跳,并用版本号隔离已停止的旧周期。\n */\nexport class RoomHeartbeat {\n\n /**\n * 基础层组件\n */\n private readonly rtcManager: RtcManaging;\n\n /**\n * 心跳配置\n */\n private readonly intervalMs: number;\n\n /**\n * 等待指定毫秒数后继续执行的调度函数。\n */\n private readonly sleep: (\n ms: number\n ) => Promise<void>;\n\n /**\n * 运行状态\n */\n private cycleVersion = 0;\n\n /**\n * 创建房间心跳。\n *\n * @param options.rtcManager RTC 引擎与消息能力组件。\n * @param options.intervalMs 心跳间隔(毫秒);默认 10 秒。\n * @param options.sleep 心跳等待实现(可替换,测试用)。\n */\n constructor(\n options: RoomHeartbeatOptions\n ) {\n this.rtcManager = options.rtcManager;\n this.intervalMs = options.intervalMs ?? 10_000;\n this.sleep =\n options.sleep ??\n ((ms) => new Promise<void>((resolve) => setTimeout(resolve, ms)));\n }\n\n /**\n * 启动周期心跳;重复启动会替换当前周期。\n */\n start(\n userID: string\n ): void {\n const version = ++this.cycleVersion;\n void this.run(version, userID);\n }\n\n /**\n * 停止当前心跳周期;迟到结果不会再发送。\n */\n stop(): void {\n this.cycleVersion += 1;\n }\n\n /**\n * 周期循环:等待后发送心跳;发送失败仅记录日志,周期继续。\n */\n private async run(\n version: number,\n userID: string\n ): Promise<void> {\n while (this.cycleVersion === version) {\n await this.sleep(this.intervalMs);\n if (this.cycleVersion !== version) {\n return;\n }\n try {\n await this.rtcManager.sendRoomMessage(RoomEvent.heartbeat({ userID }));\n } catch (error) {\n if (this.cycleVersion !== version) {\n return;\n }\n XmaxLogger.room.error(\n () =>\n `发送 RTC 房间心跳失败 (Failed to Send RTC Room Heartbeat)\\n` +\n `└─ ${XmaxLogger.localized(\"原因:\", \"Reason: \")}${XmaxError.from(error).message}`,\n );\n }\n }\n }\n}\n","//\n// RoomMessageCodec.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\nimport { XmaxLogger } from \"../../Foundation/Logging/XmaxLogger\";\n\n/**\n * 分片消息的固定事件名。\n */\nconst CHUNK_EVENT = \"__trtc_chunk__\";\n\n/**\n * 未完成的组包记录。\n */\ninterface ChunkRecord {\n\n /**\n * 当前分片消息预期接收的总片数。\n */\n count: number;\n\n /**\n * 按分片序号保存的消息片段。\n */\n parts: Map<number, string>;\n}\n\n/**\n * 房间文本协议编解码器;TRTC/Agora 启用拆包,VeRTC 原样传输。\n *\n * 出站:启用拆包时,消息 UTF-8 字节长度超过 800 后按分片 JSON 拆包;\n * 入站:识别分片消息并按 `(发送方, eventId)` 乱序组包,\n * 未收齐时返回 `undefined`,收齐后返回解析后的完整业务消息。\n */\nexport class RoomMessageCodec {\n\n /**\n * 触发拆包的消息字节阈值。\n */\n static readonly chunkThresholdBytes = 800;\n\n /**\n * 单个分片正文的字节预算,保证分片 JSON 自身不超过阈值。\n */\n static readonly chunkDataBudgetBytes = 600;\n\n /**\n * 未完成组包记录的缓存上限(LRU)。\n */\n static readonly cacheLimit = 1000;\n\n /**\n * 接收缓存\n */\n private readonly records = new Map<string, ChunkRecord>();\n\n /**\n * 创建厂商协议编解码器;TRTC 保持原事件名,Agora 使用 __agora_chunk__;null 表示不拆包。\n */\n constructor(\n private readonly chunkEvent: string | null = CHUNK_EVENT\n ) { }\n\n /**\n * 编码出站消息。\n *\n * @returns 不超过阈值时返回仅含原消息的单元素数组;\n * 否则返回按当前提供方分片协议包装的 JSON 数组。\n */\n encodeOutgoing(\n message: string\n ): string[] {\n if (this.chunkEvent === null || RoomMessageCodec.byteLength(message) <= RoomMessageCodec.chunkThresholdBytes) {\n return [message];\n }\n\n const eventId = RoomMessageCodec.makeEventId();\n const slices = RoomMessageCodec.sliceByByteBudget(\n message,\n RoomMessageCodec.chunkDataBudgetBytes,\n );\n const count = slices.length;\n\n XmaxLogger.room.debug(\n () =>\n `房间消息拆包 (Chunking Room Message)\\n` +\n `└─ eventId: ${eventId} · count: ${count} · bytes: ${RoomMessageCodec.byteLength(message)}`,\n );\n\n return slices.map((data, index) =>\n JSON.stringify({\n event: this.chunkEvent,\n eventId,\n index,\n count,\n data,\n }),\n );\n }\n\n /**\n * 处理入站消息。\n *\n * @returns 返回解析后的业务消息;分片未收齐或 JSON 非法时返回\n * `undefined`;分片收齐后从缓存移除并解析完整消息。\n */\n processIncoming(\n senderUserID: string,\n message: string\n ): unknown {\n let parsed: unknown;\n try {\n parsed = JSON.parse(message);\n } catch {\n XmaxLogger.room.warning(() => \"房间消息不是合法 JSON (Room Message Is Not Valid JSON)\");\n return undefined;\n }\n\n const chunk = this.parseChunk(parsed);\n if (!chunk) {\n return parsed;\n }\n\n const key = `${senderUserID}:${chunk.eventId}`;\n let record = this.records.get(key);\n if (!record) {\n record = { count: chunk.count, parts: new Map() };\n this.records.set(key, record);\n this.evictOverflow();\n }\n\n record.parts.set(chunk.index, chunk.data);\n\n if (record.parts.size < record.count) {\n return undefined;\n }\n\n this.records.delete(key);\n const parts: string[] = [];\n for (let index = 0; index < record.count; index += 1) {\n const part = record.parts.get(index);\n if (part === undefined) {\n return undefined;\n }\n parts.push(part);\n }\n\n const joined = parts.join(\"\");\n try {\n return JSON.parse(joined) as unknown;\n } catch {\n XmaxLogger.room.warning(\n () => `房间消息组包后不是合法 JSON (Reassembled Message Is Not Valid JSON)\\n└─ eventId: ${chunk.eventId}`,\n );\n return undefined;\n }\n }\n\n /**\n * 清空未完成的组包缓存。\n */\n reset(): void {\n this.records.clear();\n }\n\n /**\n * 从已解析的值识别分片消息;字段不完整时按普通业务消息处理。\n */\n private parseChunk(\n parsed: unknown,\n ): {\n eventId: string;\n index: number;\n count: number;\n data: string\n } | undefined {\n if (this.chunkEvent === null || typeof parsed !== \"object\" || parsed === null) {\n return undefined;\n }\n\n const record = parsed as Record<string, unknown>;\n if (\n record.event !== this.chunkEvent ||\n typeof record.eventId !== \"string\" ||\n record.eventId.length === 0 ||\n !Number.isInteger(record.index) ||\n !Number.isInteger(record.count) ||\n typeof record.data !== \"string\"\n ) {\n return undefined;\n }\n\n const index = record.index as number;\n const count = record.count as number;\n if (count < 1 || index < 0 || index >= count) {\n return undefined;\n }\n\n return { eventId: record.eventId, index, count, data: record.data };\n }\n\n /**\n * 淘汰最旧的未完成记录,保持缓存不超过上限。\n */\n private evictOverflow(): void {\n while (this.records.size > RoomMessageCodec.cacheLimit) {\n const oldest = this.records.keys().next().value;\n if (oldest === undefined) {\n return;\n }\n this.records.delete(oldest);\n }\n }\n\n /**\n * 按 UTF-8 字节预算切分字符串,不拆分多字节字符。\n */\n private static sliceByByteBudget(\n message: string,\n budget: number\n ): string[] {\n const slices: string[] = [];\n let current = \"\";\n let currentBytes = 0;\n\n for (const char of message) {\n // data 是 JSON 字符串,必须计入引号、反斜杠与控制字符的转义开销。\n const bytes = RoomMessageCodec.byteLength(JSON.stringify(char)) - 2;\n if (currentBytes + bytes > budget && current.length > 0) {\n slices.push(current);\n current = \"\";\n currentBytes = 0;\n }\n current += char;\n currentBytes += bytes;\n }\n\n if (current.length > 0) {\n slices.push(current);\n }\n\n return slices;\n }\n\n /**\n * 计算字符串的 UTF-8 字节长度。\n */\n private static byteLength(\n value: string\n ): number {\n return new TextEncoder().encode(value).byteLength;\n }\n\n /**\n * 生成拆包事件标识(32 位十六进制)。\n */\n private static makeEventId(): string {\n if (typeof crypto !== \"undefined\" && crypto.randomUUID) {\n return crypto.randomUUID().replace(/-/g, \"\");\n }\n return Array.from({ length: 32 }, () =>\n Math.floor(Math.random() * 16).toString(16),\n ).join(\"\");\n }\n}\n","//\n// RoomController.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\nimport { RtcProvider } from \"../../Foundation/RTC/RtcProvider\";\nimport { XmaxError, XmaxErrorCode } from \"../../Foundation/Errors/XmaxError\";\nimport { XmaxLogger } from \"../../Foundation/Logging/XmaxLogger\";\nimport { toRoomJoinConfiguration } from \"../../Service/Realtime/RealtimeSessionConnection\";\nimport type { RtcManaging } from \"../../Foundation/RTC/RtcManaging\";\nimport type { RealtimeContext } from \"../../Service/Realtime/RealtimeContext\";\nimport type { RealtimeExtraParams } from \"../../Service/Realtime/RealtimeExtraParams\";\nimport type { RealtimePoint } from \"../../Service/Realtime/RealtimePoint\";\nimport type { RtcSessionConnection } from \"../../Service/Realtime/RealtimeSessionConnection\";\nimport type { RealtimeVideoFormat } from \"../../Service/Realtime/RealtimeVideoFormat\";\nimport type { RoomControlling, RoomListener } from \"./RoomControlling\";\nimport { RoomEvent, type RoomEventTargetSize } from \"./RoomEvent\";\nimport { EncodingBitratePolicy } from \"../Encoding/EncodingBitratePolicy\";\nimport { RoomHeartbeat } from \"./RoomHeartbeat\";\nimport { RoomMessageCodec } from \"./RoomMessageCodec\";\n\n/**\n * 房间生命周期状态。\n */\ntype RoomState =\n | {\n kind: \"idle\"\n }\n | {\n kind: \"joining\";\n operationID: string\n }\n | {\n kind: \"joined\";\n userID: string\n }\n | {\n kind: \"leaving\"\n };\n\n/**\n * 房间传输、分片编解码和心跳选项。\n */\nexport interface RoomControllerOptions {\n\n /**\n * RTC 引擎与房间能力组件。\n */\n rtcManager: RtcManaging;\n\n /**\n * 房间心跳组件(可替换,测试用)。\n */\n heartbeat?: RoomHeartbeat;\n\n /**\n * 房间消息编解码组件(可替换,测试用)。\n */\n codec?: RoomMessageCodec;\n}\n\n/**\n * 管理 RTC 房间生命周期、心跳和实时生成信令。\n *\n * 入站消息经 `RoomMessageCodec` 组包后按 `user_id` 目标字段过滤\n * (RTC 消息通道使用广播语义),再分发给监听器。\n */\nexport class RoomController implements RoomControlling {\n\n /**\n * 基础层组件\n */\n private readonly rtcManager: RtcManaging;\n\n /**\n * 传输层组件\n */\n private readonly heartbeat: RoomHeartbeat;\n\n /**\n * 当前房间使用的消息分片编解码器。\n */\n private codec: RoomMessageCodec;\n\n /**\n * 接入方注入的消息分片编解码器。\n */\n private readonly customCodec: boolean;\n\n /**\n * 串行发送房间消息的任务链。\n */\n private sendTail: Promise<void> = Promise.resolve();\n\n /**\n * 事件监听\n */\n private listener?: RoomListener;\n\n /**\n * 房间资源\n */\n private state: RoomState = { kind: \"idle\" };\n\n /**\n * 共享的退房及资源清理任务。\n */\n private leaveOperation?: Promise<void>;\n\n /**\n * 创建房间控制器。\n *\n * @param options.rtcManager RTC 引擎与房间能力组件。\n * @param options.heartbeat 房间心跳组件(可替换,测试用)。\n * @param options.codec 房间消息编解码组件(可替换,测试用)。\n */\n constructor(\n options: RoomControllerOptions\n ) {\n this.rtcManager = options.rtcManager;\n this.heartbeat =\n options.heartbeat ?? new RoomHeartbeat({ rtcManager: options.rtcManager });\n this.codec = options.codec ?? new RoomMessageCodec();\n this.customCodec = options.codec !== undefined;\n }\n\n /**\n * 加入实时房间,并在异步边界前后确认连接操作仍然有效。\n *\n * @throws 已有房间未离开、操作失效或进房失败时抛出错误。\n */\n async join(\n connection: RtcSessionConnection,\n ensureActive: () => void,\n measureRtcJoin?: () => () => void,\n ): Promise<void> {\n try {\n ensureActive();\n } catch (error) {\n throw XmaxError.from(error);\n }\n if (this.state.kind !== \"idle\" || this.leaveOperation) {\n throw new XmaxError(\n XmaxErrorCode.invalidConfiguration,\n \"Leave the current RTC room before joining another one\",\n );\n }\n\n const operationID = `join-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;\n this.state = { kind: \"joining\", operationID };\n this.heartbeat.stop();\n\n try {\n if (!this.customCodec)\n this.codec = new RoomMessageCodec(\n connection.provider === RtcProvider.vertc ? null : connection.provider === RtcProvider.agora ? \"__agora_chunk__\" : \"__trtc_chunk__\",\n );\n const configuration = toRoomJoinConfiguration(connection);\n const completeRtcJoin = measureRtcJoin?.();\n await this.rtcManager.joinRoom(configuration);\n ensureActive();\n if (\n this.state.kind !== \"joining\" ||\n this.state.operationID !== operationID ||\n this.leaveOperation\n ) {\n throw new XmaxError(XmaxErrorCode.cancelled, \"RTC room join was cancelled\");\n }\n\n completeRtcJoin?.();\n\n this.state = { kind: \"joined\", userID: connection.userID };\n this.heartbeat.start(connection.userID);\n } catch (error) {\n if (\n this.state.kind === \"joining\" &&\n this.state.operationID === operationID &&\n !this.leaveOperation\n ) {\n this.state = { kind: \"leaving\" };\n this.heartbeat.stop();\n await this.rtcManager.leaveRoom();\n if (this.state.kind === \"leaving\") {\n this.state = { kind: \"idle\" };\n }\n }\n throw XmaxError.from(error);\n }\n }\n\n /**\n * 停止房间心跳并离开当前 RTC 房间;并发的离开操作共享同一个任务。\n */\n async leave(): Promise<void> {\n if (this.leaveOperation) {\n await this.leaveOperation;\n return;\n }\n if (this.state.kind === \"idle\" || this.state.kind === \"leaving\") {\n return;\n }\n const operation = this.performLeave();\n this.leaveOperation = operation;\n await operation;\n }\n\n /**\n * 发送生成开始信令。\n *\n * @param options.taskID 当前生成任务标识。\n * @param options.videoFormat 模型生成使用的视频规格。\n * @param options.context 本次生成条件。\n * @param options.targetSize 回传尺寸,缺省时不发送。\n * @param options.mirror 是否由后端镜像输入画面,默认 false。\n * @param options.faceRestoration 是否回贴原始人脸,默认 false。\n * @param options.extraParams 合并到本次 start 的 params,不缓存。\n * @throws 房间未就绪或信令发送失败时抛出错误。\n */\n startGeneration(\n options: {\n taskID: string;\n videoFormat: RealtimeVideoFormat;\n context: RealtimeContext;\n targetSize?: RoomEventTargetSize;\n mirror?: boolean;\n faceRestoration?: boolean;\n paddingContain?: boolean;\n extraParams?: RealtimeExtraParams;\n }\n ): Promise<void> {\n return this.send(\n RoomEvent.start({\n userID: this.requireUserID(),\n taskID: options.taskID,\n videoFormat: options.videoFormat,\n context: options.context,\n targetSize: options.targetSize,\n mirror: options.mirror ?? false,\n faceRestoration: options.faceRestoration ?? false,\n paddingContain: options.paddingContain,\n extraParams: options.extraParams,\n ...this.downlinkBitrates(options.targetSize ?? options.videoFormat),\n }),\n );\n }\n\n /**\n * 发送生成条件变更信令。\n *\n * @param options.taskID 当前生成任务标识。\n * @param options.videoFormat 模型生成使用的视频规格。\n * @param options.context 本次生成条件。\n * @param options.targetSize 服务端生成后的回传尺寸,缺省时不发送。\n * @param options.faceRestoration 是否回贴原始人脸,默认 false。\n * @param options.extraParams 合并到本次 change_condition 的 params,不缓存。\n * @throws 房间未就绪或信令发送失败时抛出错误。\n */\n changeGenerationCondition(\n options: {\n taskID: string;\n videoFormat: RealtimeVideoFormat;\n context: RealtimeContext;\n targetSize?: RoomEventTargetSize;\n faceRestoration?: boolean;\n paddingContain?: boolean;\n extraParams?: RealtimeExtraParams;\n }\n ): Promise<void> {\n return this.send(\n RoomEvent.changeCondition({\n userID: this.requireUserID(),\n taskID: options.taskID,\n videoFormat: options.videoFormat,\n context: options.context,\n targetSize: options.targetSize,\n faceRestoration: options.faceRestoration ?? false,\n paddingContain: options.paddingContain,\n extraParams: options.extraParams,\n }),\n );\n }\n\n /**\n * 调整当前生成任务的回传尺寸。\n *\n * @throws 操作已取消、房间未就绪或信令发送失败时抛出错误。\n */\n changeTargetSize(\n options: {\n taskID: string;\n targetSize: RoomEventTargetSize;\n ensureActive: () => void;\n }\n ): Promise<void> {\n options.ensureActive();\n return this.send(\n RoomEvent.changeTargetSize({\n userID: this.requireUserID(),\n taskID: options.taskID,\n targetSize: options.targetSize,\n ...this.downlinkBitrates(options.targetSize),\n }),\n );\n }\n\n /**\n * 按回传尺寸和 24 fps 估算下行码率,最高值限制在 100~10000 kbps。\n * 估算不设置服务端帧率,也不沿用显式上传码率。\n */\n private downlinkBitrates(\n size: RoomEventTargetSize\n ): {\n minimumBitrate: number;\n maximumBitrate: number\n } {\n const { maximum } = EncodingBitratePolicy.resolve(\n Math.round(size.width), Math.round(size.height), 24,\n );\n return { minimumBitrate: 100, maximumBitrate: Math.min(10000, maximum) };\n }\n\n /**\n * 尝试发送生成停止信令;未进房或任务标识为空时忽略。\n */\n stopGeneration(\n taskID: string\n ): Promise<void> {\n if (!taskID || this.state.kind !== \"joined\") {\n return Promise.resolve();\n }\n return this.send(RoomEvent.stop({ userID: this.state.userID, taskID }));\n }\n\n /**\n * 发送生成任务的交互轨迹;任务标识或轨迹为空时忽略。\n */\n sendTracks(\n taskID: string,\n points: RealtimePoint[]\n ): Promise<void> {\n if (!taskID || points.length === 0) {\n return Promise.resolve();\n }\n return this.send(\n RoomEvent.tracks({\n userID: this.requireUserID(),\n taskID,\n points,\n }),\n \"latest\",\n );\n }\n\n /**\n * 设置房间事件监听器,传入空值时清除监听器。\n */\n setListener(\n listener?: RoomListener\n ): void {\n this.listener = listener;\n }\n\n /**\n * 执行离开:复位状态、停止心跳、清空组包缓存并退房。\n */\n private async performLeave(): Promise<void> {\n this.state = { kind: \"idle\" };\n this.heartbeat.stop();\n this.codec.reset();\n this.sendTail = Promise.resolve();\n try {\n await this.rtcManager.leaveRoom();\n }\n finally {\n this.leaveOperation = undefined;\n }\n }\n\n /**\n * 组包入站消息并按目标用户过滤后分发完整业务消息。\n */\n handleIncomingMessage(\n senderUserID: string,\n raw: string\n ): void {\n const parsed = this.codec.processIncoming(senderUserID, raw);\n if (typeof parsed !== \"object\" || parsed === null) {\n return;\n }\n\n const message = parsed as Record<string, unknown>;\n\n // RTC 消息使用广播语义,按 payload 中的 user_id 过滤目标。\n const targetUserID =\n typeof message.user_id === \"string\" ? message.user_id : undefined;\n const ownUserID = this.state.kind === \"joined\" ? this.state.userID : undefined;\n if (targetUserID && ownUserID && targetUserID !== ownUserID) {\n return;\n }\n\n this.listener?.onRoomMessage(senderUserID, message);\n }\n\n /**\n * 控制信令串行确认;快照等待此前控制信令,但不阻塞后续控制信令。\n */\n private send(\n message: string,\n delivery: \"ordered\" | \"latest\" = \"ordered\",\n ): Promise<void> {\n const state = this.state;\n const packets = this.codec.encodeOutgoing(message);\n // 多片消息仍逐片确认,避免快照覆盖导致接收端无法完整组包。\n const mode = packets.length > 1 ? \"ordered\" : delivery;\n const operation = this.sendTail.then(async () => {\n for (const packet of packets) {\n if (state !== this.state || state.kind !== \"joined\") {\n throw new XmaxError(XmaxErrorCode.cancelled, \"Room message was cancelled\");\n }\n if (mode === \"latest\") {\n await this.rtcManager.sendRoomMessage(packet, \"latest\");\n } else {\n await this.rtcManager.sendRoomMessage(packet, \"barrier\");\n }\n }\n XmaxLogger.room.info(\n () =>\n `发送房间信令 (Outbound Room Signaling)\\n` +\n `└─ ${XmaxLogger.localized(\"内容:\", \"Content:\")}\\n${JSON.stringify(JSON.parse(message), null, 2)}`,\n );\n }).catch(error => {\n throw XmaxError.from(error);\n });\n if (mode === \"ordered\") {\n this.sendTail = operation.catch(() => { });\n }\n return operation;\n }\n\n /**\n * 返回当前房间中的用户标识;未进房时抛出错误。\n */\n private requireUserID(): string {\n if (this.state.kind !== \"joined\") {\n throw new XmaxError(XmaxErrorCode.rtcError, \"RTC room is not joined\");\n }\n return this.state.userID;\n }\n}\n","//\n// EncodingController.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\nimport { XmaxError, XmaxErrorCode } from \"../../Foundation/Errors/XmaxError\";\nimport type { RtcManaging } from \"../../Foundation/RTC/RtcManaging\";\nimport {\n RtcVideoEncoderPreference,\n type VideoEncodingConfiguration,\n} from \"../../Foundation/RTC/VideoEncodingConfiguration\";\nimport {\n RealtimeVideoEncoderPreference,\n RealtimeVideoFormat,\n} from \"../../Service/Realtime/RealtimeVideoFormat\";\nimport { EncodingBitratePolicy } from \"./EncodingBitratePolicy\";\nimport type { EncodingControlling } from \"./EncodingControlling\";\n\n/**\n * 业务编码偏好与 RTC 编码策略的对应关系。\n */\nconst ENCODER_PREFERENCE_MAP: Record<\n RealtimeVideoEncoderPreference,\n RtcVideoEncoderPreference\n> = {\n [RealtimeVideoEncoderPreference.maintainFramerate]:\n RtcVideoEncoderPreference.maintainFramerate,\n [RealtimeVideoEncoderPreference.maintainQuality]:\n RtcVideoEncoderPreference.maintainQuality,\n};\n\n/**\n * 根据实时视频格式配置 RTC 视频编码参数。\n *\n * 默认最低码率为 500 kbps,且不超过当前最高码率;最高码率由共享预算策略计算。\n * 格式显式指定码率时以显式值为准。\n */\nexport class EncodingController implements EncodingControlling {\n\n /**\n * 基础层组件\n */\n private readonly rtcManager: RtcManaging;\n\n /**\n * 最近成功应用到 RTC 的编码配置。\n */\n private lastAppliedConfiguration?: VideoEncodingConfiguration;\n\n /**\n * 创建编码控制器。\n */\n constructor(\n options: {\n rtcManager: RtcManaging\n }\n ) {\n this.rtcManager = options.rtcManager;\n }\n\n /**\n * 校验视频格式,解析显式或推荐码率范围后配置 RTC 编码器;参数或配置失败时抛错。\n */\n async configure(\n videoFormat: RealtimeVideoFormat\n ): Promise<void> {\n videoFormat.validate();\n\n let minimum: number;\n let maximum: number;\n if (\n videoFormat.minimumBitrate !== undefined &&\n videoFormat.maximumBitrate !== undefined\n ) {\n minimum = videoFormat.minimumBitrate;\n maximum = videoFormat.maximumBitrate;\n } else {\n const defaults = EncodingBitratePolicy.resolve(\n videoFormat.width, videoFormat.height, videoFormat.fps,\n );\n maximum = videoFormat.maximumBitrate ?? defaults.maximum;\n minimum = videoFormat.minimumBitrate ?? Math.min(500, maximum);\n }\n\n if (minimum > maximum) {\n throw new XmaxError(\n XmaxErrorCode.invalidConfiguration,\n \"Minimum bitrate must not exceed maximum bitrate after applying SDK defaults\",\n );\n }\n\n const configuration: VideoEncodingConfiguration = {\n width: videoFormat.width,\n height: videoFormat.height,\n frameRate: videoFormat.fps,\n minimumBitrate: minimum,\n maximumBitrate: maximum,\n encoderPreference: ENCODER_PREFERENCE_MAP[videoFormat.encoderPreference],\n };\n await this.rtcManager.configureVideoEncoding(configuration);\n this.lastAppliedConfiguration = { ...configuration };\n }\n\n /**\n * 只替换目标帧率,保留最近成功应用的尺寸、已解析码率区间和偏好。\n */\n async updateFrameRate(\n fps: number\n ): Promise<RealtimeVideoFormat> {\n const previous = this.lastAppliedConfiguration;\n if (!previous) {\n throw new XmaxError(XmaxErrorCode.invalidConfiguration, \"Apply an encoder configuration before updating frame rate\");\n }\n const format = new RealtimeVideoFormat({\n width: previous.width, height: previous.height, fps,\n minimumBitrate: previous.minimumBitrate, maximumBitrate: previous.maximumBitrate,\n encoderPreference: previous.encoderPreference === RtcVideoEncoderPreference.maintainFramerate\n ? RealtimeVideoEncoderPreference.maintainFramerate : RealtimeVideoEncoderPreference.maintainQuality,\n });\n format.validate();\n if (fps !== previous.frameRate) {\n const next = { ...previous, frameRate: fps };\n await this.rtcManager.configureVideoEncoding(next);\n this.lastAppliedConfiguration = next;\n }\n return format;\n }\n}\n","//\n// StreamController.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\nimport { XmaxError, XmaxErrorCode, type XmaxErrorListener } from \"../Foundation/Errors/XmaxError\";\nimport { XmaxLogger } from \"../Foundation/Logging/XmaxLogger\";\nimport { RemoteStream } from \"../Foundation/RTC/RemoteStream\";\nimport { toRoomJoinConfiguration } from \"../Service/Realtime/RealtimeSessionConnection\";\nimport type { RtcManaging } from \"../Foundation/RTC/RtcManaging\";\nimport type { DownlinkVideoStatisticsListener, UplinkVideoStatisticsListener } from \"../Foundation/RTC/VideoStatistics\";\nimport type { RealtimeNetworkQualityListener } from \"../Foundation/RTC/NetworkStatistics\";\nimport type { RealtimePoint } from \"../Service/Realtime/RealtimePoint\";\nimport type { RtcSessionConnection } from \"../Service/Realtime/RealtimeSessionConnection\";\nimport type { RealtimeVideoFormat } from \"../Service/Realtime/RealtimeVideoFormat\";\nimport type { RoomListener, RoomControlling } from \"./Room/RoomControlling\";\nimport { RoomController } from \"./Room/RoomController\";\nimport type { ModelSize } from \"../Service/Realtime/RealtimeModel\";\nimport type { EncodingControlling } from \"./Encoding/EncodingControlling\";\nimport { EncodingController } from \"./Encoding/EncodingController\";\nimport type { RemoteMediaTracks } from \"../Foundation/Media/RemoteMediaTracks\";\nimport type {\n RemoteStreamListener,\n StreamControlling,\n StreamGenerationOptions,\n} from \"./StreamControlling\";\n\n/**\n * 生成确认等待器。\n */\ninterface GenerationWaiter {\n\n /**\n * 当前生成任务标识。\n */\n taskID: string;\n\n /**\n * 结束当前等待并交付结果。\n */\n resolve: () => void;\n\n /**\n * 结束当前等待并传递错误。\n */\n reject: (\n error: XmaxError\n ) => void;\n\n /**\n * 限制生成确认等待时间的定时器。\n */\n timeoutTimer: ReturnType<typeof setTimeout>;\n\n /**\n * 是否仍在等待生成确认。\n */\n confirmationPending: boolean;\n}\n\n/**\n * 传输层运行状态。\n */\ninterface StreamState {\n\n /**\n * RTC 房间标识。\n */\n roomID: string;\n\n /**\n * 生成结果流对应的房间用户标识。\n */\n botID: string;\n\n /**\n * 本地视频是否已经发布。\n */\n localVideoPublished: boolean;\n\n /**\n * 本地音频是否已经发布。\n */\n localAudioPublished: boolean;\n\n /**\n * 已完成主视频订阅的远端用户。\n */\n subscribedRemoteUserIDs: Set<string>;\n\n /**\n * 已发布主视频的远端用户。\n */\n publishedRemoteUserIDs: Set<string>;\n\n /**\n * 按用户维护的远端视频轨。\n */\n remoteVideoTracks: Map<string, MediaStreamTrack>;\n\n /**\n * 按用户维护的视频订阅操作。\n */\n remoteVideoSubscriptions: Map<string, object>;\n\n /**\n * 已订阅音频的远端用户。\n */\n subscribedRemoteAudioUserIDs: Set<string>;\n\n /**\n * 当前生成任务标识。\n */\n generationTaskID?: string;\n\n /**\n * 当前生成任务的确认等待。\n */\n generationWaiter?: GenerationWaiter;\n\n /**\n * 当前承载生成结果的远端流。\n */\n activeRemoteStream?: RemoteStream;\n\n}\n\n/**\n * 创建独立的空传输状态,每次重置使用新的订阅集合,避免旧房间资源残留。\n */\nfunction makeInitialState(): StreamState {\n return {\n roomID: \"\",\n botID: \"\",\n localVideoPublished: false,\n localAudioPublished: false,\n subscribedRemoteUserIDs: new Set(),\n publishedRemoteUserIDs: new Set(),\n remoteVideoTracks: new Map(),\n remoteVideoSubscriptions: new Map(),\n subscribedRemoteAudioUserIDs: new Set(),\n };\n}\n\n/**\n * 媒体传输、房间信令及生成确认所需的组件与回调。\n */\nexport interface StreamControllerOptions {\n\n /**\n * 凭据即将过期或已过期时,转交会话层获取新凭据。\n */\n onCredentialsRequired?: () => void;\n\n /**\n * RTC 引擎与媒体传输组件。\n */\n rtcManager: RtcManaging;\n\n /**\n * 房间生命周期与信令组件(可替换,测试用)。\n */\n roomController?: RoomControlling;\n\n /**\n * 视频编码参数配置组件(可替换,测试用)。\n */\n encodingController?: EncodingControlling;\n\n /**\n * 运行期错误回调。\n */\n errorListener?: XmaxErrorListener;\n\n /**\n * 远端生成流就绪与清理回调。\n */\n remoteStreamListener?: RemoteStreamListener;\n\n /**\n * 生成开始确认超时时间(毫秒);默认 30 秒。\n */\n generationTimeoutMs?: number;\n}\n\n/**\n * 统一协调 RTC 房间、媒体流发布订阅和生成任务确认。\n *\n * 生成确认不使用 SEI:以目标机器人发布远端视频(并完成订阅、\n * 渲染绑定被接受)作为生成开始成功的确认信号。\n */\nexport class StreamController implements StreamControlling {\n\n /**\n * 基础层组件\n */\n private readonly rtcManager: RtcManaging;\n\n /**\n * 传输层组件\n */\n private readonly roomController: RoomControlling;\n\n /**\n * 本地上行编码参数的应用组件。\n */\n private readonly encodingController: EncodingControlling;\n\n /**\n * 事件监听\n */\n private readonly errorListener: XmaxErrorListener;\n\n /**\n * 结果流变化的业务监听器。\n */\n private readonly remoteStreamListener: RemoteStreamListener;\n\n /**\n * 原始结果流的观察者;未设置时不读取额外音轨。\n */\n private remoteMediaListener?: (tracks?: RemoteMediaTracks) => void;\n\n /**\n * 本地视频统计的业务监听器。\n */\n private localVideoStatisticsListener?: UplinkVideoStatisticsListener;\n\n /**\n * 网络质量统计的业务监听器。\n */\n private networkStatisticsListener?: RealtimeNetworkQualityListener;\n\n /**\n * 远端视频统计的业务监听器。\n */\n private remoteVideoStatisticsListener?: DownlinkVideoStatisticsListener;\n\n /**\n * 生成配置\n */\n private readonly generationTimeoutMs: number;\n\n /**\n * 音频配置\n */\n private remoteAudioVolumePercentage = 100;\n\n /**\n * 运行状态\n */\n private state: StreamState = makeInitialState();\n\n /**\n * 当前房间事件的处理入口。\n */\n private roomListener?: RoomListener;\n\n /**\n * 创建传输层控制器。\n *\n * @param options.rtcManager RTC 引擎与媒体传输组件。\n * @param options.roomController 房间生命周期与信令组件(可替换,测试用)。\n * @param options.encodingController 视频编码参数配置组件(可替换,测试用)。\n * @param options.errorListener 运行期错误回调。\n * @param options.remoteStreamListener 远端生成流就绪与清理回调。\n * @param options.generationTimeoutMs 生成开始确认超时时间(毫秒);默认 30 秒。\n */\n constructor(\n options: StreamControllerOptions\n ) {\n this.rtcManager = options.rtcManager;\n this.roomController =\n options.roomController ??\n new RoomController({ rtcManager: options.rtcManager });\n this.encodingController =\n options.encodingController ??\n new EncodingController({ rtcManager: options.rtcManager });\n\n this.errorListener = options.errorListener ?? (() => { });\n this.remoteStreamListener = options.remoteStreamListener ?? (() => { });\n this.generationTimeoutMs = options.generationTimeoutMs ?? 30_000;\n\n this.roomController.setListener({\n onRoomMessage: (sender, message) => {\n this.roomListener?.onRoomMessage(sender, message);\n },\n });\n\n // RTC 事件监听权归传输层:房间消息交给房间控制器组包分发。\n this.rtcManager.setEventListener({\n onTokenWillExpire: options.onCredentialsRequired,\n onTokenExpired: options.onCredentialsRequired,\n onRoomRejoining: () => this.handleRoomRejoining(),\n onError: (error) => this.errorListener(error),\n onNetworkStatistics: (statistics) => {\n if (this.state.roomID) {\n this.networkStatisticsListener?.(statistics);\n }\n },\n onRemoteVideoStatistics: (statistics) => {\n const userID = this.state.activeRemoteStream?.userID;\n this.remoteVideoStatisticsListener?.(\n userID ? statistics.find((item) => item.userID === userID) : undefined,\n );\n },\n onLocalVideoStatistics: (statistics) => {\n if (this.state.localVideoPublished) {\n this.localVideoStatisticsListener?.(statistics);\n }\n },\n onCustomMessageReceived: (senderUserID, message) => {\n this.roomController.handleIncomingMessage(senderUserID, message);\n },\n onRemoteVideoPublished: (userID, published) => {\n void this.handleRemoteVideoPublished(userID, published);\n },\n onRemoteAudioTrackChanged: userID => {\n if (this.state.activeRemoteStream?.userID === userID) this.notifyRemoteMedia();\n },\n });\n }\n\n /**\n * 当前是否存在正在启动或已经运行的生成任务。\n */\n get hasGenerationTask(): boolean {\n return this.state.generationTaskID !== undefined;\n }\n\n /**\n * 替换原始媒体观察者并立即回放当前绑定,不改变 RTC 事件监听权。\n */\n setRemoteMediaListener(listener?: (tracks?: RemoteMediaTracks) => void): void {\n this.remoteMediaListener = listener;\n this.notifyRemoteMedia();\n }\n\n /**\n * 仅向当前观察者交付实际结果用户的音视频轨,隔离取轨失败。\n */\n private notifyRemoteMedia(): void {\n if (!this.remoteMediaListener) return;\n const userID = this.state.activeRemoteStream?.userID;\n const videoTrack = userID && this.state.remoteVideoTracks.get(userID);\n const bindingID = this.state.generationTaskID;\n try {\n this.remoteMediaListener(userID && videoTrack && bindingID ? {\n userID, bindingID, videoTrack,\n audioTrack: this.rtcManager.getRemoteAudioTrack(userID),\n } : undefined);\n } catch (error) {\n XmaxLogger.stream.warning(() => `Unable to observe remote media tracks: ${XmaxError.from(error).message}`);\n }\n }\n\n /**\n * 设置本地主视频流统计监听器,不改变 RTC 事件监听权。\n */\n setUplinkVideoStatisticsListener(\n listener?: UplinkVideoStatisticsListener\n ): void {\n this.localVideoStatisticsListener = listener;\n }\n\n /**\n * 网络统计不按远端用户过滤,连接发布完成后即可回调。\n */\n setNetworkQualityListener(\n listener?: RealtimeNetworkQualityListener\n ): void {\n this.networkStatisticsListener = listener;\n }\n\n /**\n * 监听当前实际生成结果流的统计,不按会话下发的 botID 猜测结果流身份。\n */\n setDownlinkVideoStatisticsListener(\n listener?: DownlinkVideoStatisticsListener\n ): void {\n this.remoteVideoStatisticsListener = listener;\n }\n\n /**\n * 当前远端生成音频播放音量,取值范围为 `0...1`。\n */\n get remoteAudioVolume(): number {\n return this.remoteAudioVolumePercentage / 100;\n }\n\n /**\n * 设置远端生成音频播放音量,并应用到所有已订阅的远端音频。\n */\n setRemoteAudioVolume(\n volume: number\n ): void {\n const rtcVolume = Math.min(Math.max(Math.round(volume * 100), 0), 100);\n for (const userID of [...this.state.subscribedRemoteAudioUserIDs].sort()) {\n this.rtcManager.setRemoteAudioVolume(rtcVolume, userID);\n }\n this.remoteAudioVolumePercentage = rtcVolume;\n }\n\n /**\n * 按视频格式配置本地视频编码参数(码率区间与编码策略)。\n *\n * @throws 格式无效、码率区间无效或 RTC 配置失败时抛出错误。\n */\n async updateUplinkVideoFormat(\n videoFormat: RealtimeVideoFormat\n ): Promise<void> {\n await this.encodingController.configure(videoFormat);\n }\n\n /**\n * 不根据新帧率重新推算码率区间。\n */\n async updateUplinkFrameRate(\n fps: number\n ): Promise<RealtimeVideoFormat> {\n return this.encodingController.updateFrameRate(fps);\n }\n\n /**\n * 加入 RTC 房间并发布本地媒体流。\n *\n * @throws 连接已取消,或 RTC 进房、房间配置与本地流发布失败时抛出错误。\n */\n async connect(\n connection: RtcSessionConnection,\n includeLocalAudio: boolean,\n ensureActive: () => void,\n beforePublish?: () => Promise<void>,\n measureRtcJoin?: () => () => void,\n ): Promise<void> {\n this.configureRoom({ roomID: connection.roomID, botID: connection.botID });\n await this.roomController.join(connection, ensureActive, measureRtcJoin);\n ensureActive();\n\n XmaxLogger.stream.info(\n () =>\n `RTC 房间已配置 (RTC Room Configured)\\n` +\n `└─ roomID: ${connection.roomID}, botID: ${connection.botID ?? \"(未设置)\"}`,\n );\n\n await beforePublish?.();\n ensureActive();\n await this.publishLocalStream(includeLocalAudio);\n }\n\n /**\n * 清理生成状态、本地发布和远端订阅,并离开当前 RTC 房间。\n */\n async disconnect(): Promise<void> {\n await this.resetStream();\n await this.roomController.leave();\n }\n\n /**\n * 将会话刷新后的凭据应用到当前 RTC 连接。\n */\n async updateCredentials(\n connection: RtcSessionConnection,\n signal?: AbortSignal\n ): Promise<void> {\n await this.rtcManager.updateCredentials(toRoomJoinConfiguration(connection), signal);\n }\n\n /**\n * 建立生成任务并发送开始信令。\n *\n * @param options.taskID 当前生成任务标识。\n * @param options.videoFormat 模型生成使用的视频规格。\n * @param options.context 本次生成条件。\n * @param options.restart 是否保留连接并替换当前生成任务,默认 false;不预先发送 stop。\n * @param options.targetSize 回传尺寸,缺省时不发送。\n * @param options.mirror 是否由后端镜像输入画面,默认 false。\n * @param options.faceRestoration 是否回贴原始人脸,默认 false。\n * @param options.onStartSent start 发送成功后的回调,不等待生成确认。\n * @param options.extraParams 合并到本次 start 的 params,不缓存。\n * @returns 等待远端结果流确认的 Promise。\n * @throws 任务标识无效、RTC 房间未就绪、旧任务仍在启动或未指定 restart 且已有生成任务时同步抛错;\n * 发送失败或确认超时通过返回的 Promise 拒绝。\n */\n beginGeneration(\n options: StreamGenerationOptions\n ): Promise<void> {\n const taskID = options.taskID.trim();\n if (!taskID) {\n throw new XmaxError(\n XmaxErrorCode.invalidConfiguration,\n \"Realtime generation task ID cannot be empty\",\n );\n }\n if (!this.state.roomID) {\n throw new XmaxError(XmaxErrorCode.rtcError, \"RTC room is not configured\");\n }\n if (this.state.generationTaskID && (!options.restart || this.state.generationWaiter)) {\n throw new XmaxError(\n XmaxErrorCode.rtcError,\n \"Realtime generation is already active\",\n );\n }\n\n let resolve!: () => void;\n let reject!: (\n error: XmaxError\n ) => void;\n const confirmation = new Promise<void>((res, rej) => {\n resolve = res;\n reject = rej;\n });\n const waiter: GenerationWaiter = {\n taskID,\n resolve,\n reject,\n confirmationPending: false,\n timeoutTimer: setTimeout(() => {\n this.rejectGenerationStart(\n taskID,\n new XmaxError(\n XmaxErrorCode.timeout,\n \"Realtime generation start timed out\",\n ),\n );\n }, this.generationTimeoutMs),\n };\n\n this.state.generationTaskID = taskID;\n this.state.generationWaiter = waiter;\n // 发送可能先失败,提前接住确认 Promise,避免产生无人处理的拒绝。\n void confirmation.catch(() => { });\n\n try {\n const sending = this.roomController.startGeneration({\n taskID,\n videoFormat: options.videoFormat,\n context: options.context,\n targetSize: options.targetSize,\n mirror: options.mirror ?? false,\n faceRestoration: options.faceRestoration ?? false,\n paddingContain: options.paddingContain,\n extraParams: options.extraParams,\n });\n const state = this.state;\n const sent = Promise.resolve(sending).then(async () => {\n if (this.state !== state || state.generationTaskID !== taskID)\n return;\n await options.onStartSent?.();\n if (this.state !== state || state.generationTaskID !== taskID)\n return;\n // 机器人可能在 start 之前已发布;复用已订阅轨道,不等待第二次发布事件。\n for (const userID of state.publishedRemoteUserIDs) {\n void this.handleRemoteVideoPublished(userID, true);\n }\n }).catch(error => {\n const mapped = XmaxError.from(error);\n this.rejectGenerationStart(taskID, mapped);\n if (this.state.generationTaskID === taskID) {\n this.state.generationTaskID = undefined;\n void this.roomController.stopGeneration(taskID).catch(() => { });\n }\n throw mapped;\n });\n return Promise.all([sent, confirmation]).then(() => { });\n } catch (error) {\n const mapped = XmaxError.from(error);\n this.rejectGenerationStart(taskID, mapped);\n this.state.generationTaskID = undefined;\n try {\n void this.roomController.stopGeneration(taskID).catch(() => { });\n } catch {\n // 开始信令发送失败后的停止信令失败不影响错误返回。\n }\n throw mapped;\n }\n }\n\n /**\n * 在远端首帧已经可显示后订阅当前生成流的音频。\n *\n * @throws 当前生成流尚未确认,或 RTC 音量和订阅配置失败时抛出错误。\n */\n async activateRemoteAudio(): Promise<void> {\n const remoteStream = this.state.generationTaskID\n ? this.state.activeRemoteStream\n : undefined;\n if (!remoteStream) {\n throw new XmaxError(\n XmaxErrorCode.rtcError,\n \"Remote generation audio stream is unavailable\",\n );\n }\n await this.subscribeRemoteAudio(remoteStream.userID);\n }\n\n /**\n * 发送生成条件变更信令。\n *\n * @param options.taskID 当前生成任务标识。\n * @param options.videoFormat 模型生成使用的视频规格。\n * @param options.context 本次生成条件。\n * @param options.restart 本次条件更新不使用该字段。\n * @param options.targetSize 回传尺寸,缺省时不发送。\n * @param options.mirror 本次条件更新不使用该字段。\n * @param options.faceRestoration 是否回贴原始人脸,默认 false。\n * @param options.onStartSent 本次条件更新不调用该回调。\n * @param options.extraParams 合并到本次 change_condition 的 params,不缓存。\n * @throws RTC 房间未就绪或条件变更信令发送失败时抛出错误。\n */\n async updateGeneration(\n options: StreamGenerationOptions\n ): Promise<void> {\n try {\n await this.roomController.changeGenerationCondition({\n taskID: options.taskID,\n videoFormat: options.videoFormat,\n context: options.context,\n targetSize: options.targetSize,\n faceRestoration: options.faceRestoration ?? false,\n paddingContain: options.paddingContain,\n extraParams: options.extraParams,\n });\n } catch (error) {\n throw XmaxError.from(error);\n }\n }\n\n /**\n * 调整当前生成任务的回传尺寸,不重启生成。\n *\n * @throws 操作已取消、房间未就绪或信令发送失败时抛出错误。\n */\n async updateDownlinkVideoResolution(\n taskID: string,\n targetSize: ModelSize,\n ensureActive: () => void,\n ): Promise<void> {\n try {\n await this.roomController.changeTargetSize({ taskID, targetSize, ensureActive });\n } catch (error) {\n throw XmaxError.from(error);\n }\n }\n\n /**\n * 停止生成任务并清理远端结果流;任务标识为空字符串时停止当前任务。\n *\n * @throws RTC 停止信令发送失败时抛出错误。\n */\n async stopGeneration(\n taskID: string\n ): Promise<void> {\n const stoppedTaskID = await this.stopStreamGeneration(taskID);\n if (taskID && !stoppedTaskID) {\n return;\n }\n try {\n await this.roomController.stopGeneration(stoppedTaskID);\n } catch (error) {\n throw XmaxError.from(error);\n }\n }\n\n /**\n * 发送生成任务的交互轨迹;空数组会被忽略。\n *\n * @throws RTC 房间未就绪或轨迹信令发送失败时抛出错误。\n */\n async sendTracks(\n taskID: string,\n points: RealtimePoint[]\n ): Promise<void> {\n try {\n await this.roomController.sendTracks(taskID, points);\n } catch (error) {\n throw XmaxError.from(error);\n }\n }\n\n /**\n * 设置房间业务消息监听器,传入空值时清除监听器。\n */\n setRoomListener(\n listener?: RoomListener\n ): void {\n this.roomListener = listener;\n }\n\n /**\n * 配置当前房间;已有发布、订阅或生成任务时拒绝重新配置。\n */\n private configureRoom(\n options: {\n roomID: string;\n botID?: string\n }\n ): void {\n const roomID = options.roomID.trim();\n const botID = options.botID?.trim() ?? \"\";\n if (!roomID) {\n throw new XmaxError(\n XmaxErrorCode.invalidConfiguration,\n \"RTC room ID cannot be empty\",\n );\n }\n if (\n this.state.localVideoPublished ||\n this.state.localAudioPublished ||\n this.state.subscribedRemoteUserIDs.size > 0 ||\n this.state.generationTaskID\n ) {\n throw new XmaxError(\n XmaxErrorCode.invalidConfiguration,\n \"Reset the current RTC room before configuring another one\",\n );\n }\n\n this.state.roomID = roomID;\n this.state.botID = botID;\n }\n\n /**\n * 发布本地视频,按需发布本地音频;音频发布失败时回滚视频发布。\n */\n private async publishLocalStream(\n includeAudio: boolean\n ): Promise<void> {\n if (!this.state.roomID) {\n throw new XmaxError(\n XmaxErrorCode.invalidConfiguration,\n \"Configure an RTC room before publishing the local stream\",\n );\n }\n let publishedVideoInOperation = false;\n try {\n if (!this.state.localVideoPublished) {\n await this.rtcManager.publishLocalVideo();\n this.state.localVideoPublished = true;\n publishedVideoInOperation = true;\n }\n if (includeAudio && !this.state.localAudioPublished) {\n await this.rtcManager.publishLocalAudio();\n this.state.localAudioPublished = true;\n }\n } catch (error) {\n if (publishedVideoInOperation) {\n await this.rollbackLocalVideoPublication();\n }\n throw XmaxError.from(error);\n }\n }\n\n /**\n * 处理远端视频发布状态变化:按机器人标识过滤后订阅或清理。\n */\n private async handleRemoteVideoPublished(\n userID: string,\n published: boolean,\n ): Promise<void> {\n const trimmedUserID = userID.trim();\n if (!trimmedUserID) {\n return;\n }\n\n XmaxLogger.stream.info(\n () =>\n `远端视频发布状态变化 (Remote Video Publication Changed)\\n` +\n `└─ userID: ${trimmedUserID}, published: ${published}`,\n );\n\n if (!this.state.roomID) {\n return;\n }\n if (published)\n this.state.publishedRemoteUserIDs.add(trimmedUserID);\n else {\n this.state.publishedRemoteUserIDs.delete(trimmedUserID);\n this.state.remoteVideoSubscriptions.delete(trimmedUserID);\n this.state.remoteVideoTracks.delete(trimmedUserID);\n }\n if (this.state.botID && this.state.botID !== trimmedUserID) {\n // 会话下发的机器人标识与实际发布者不一致时,若当前有生成任务在\n // 等待确认或运行中,仍接受该发布者(房间为会话独占,除本端外\n // 只有生成机器人会推流)。\n if (this.state.generationTaskID === undefined) {\n XmaxLogger.stream.warning(\n () =>\n `忽略非目标机器人的远端视频 (Ignored Remote Video from Non-Bot User)\\n` +\n `└─ userID: ${trimmedUserID}, botID: ${this.state.botID}`,\n );\n return;\n }\n XmaxLogger.stream.warning(\n () =>\n `远端视频发布者与会话下发的机器人标识不一致,按生成结果流接受 (Accepted Remote Video Despite Bot ID Mismatch)\\n` +\n `└─ userID: ${trimmedUserID}, botID: ${this.state.botID}`,\n );\n }\n\n if (published) {\n await this.subscribeRemoteVideo(trimmedUserID);\n return;\n }\n\n this.state.subscribedRemoteUserIDs.delete(trimmedUserID);\n if (this.state.activeRemoteStream?.userID === trimmedUserID) {\n await this.unsubscribeRemoteAudio(trimmedUserID);\n this.state.activeRemoteStream = undefined;\n this.clearRemoteStream();\n }\n }\n\n /**\n * RTC 重进房时废弃旧媒体绑定;业务任务、音频订阅意图和生成确认等待器继续保留。\n */\n private handleRoomRejoining(): void {\n this.state.publishedRemoteUserIDs.clear();\n this.state.subscribedRemoteUserIDs.clear();\n this.state.remoteVideoSubscriptions.clear();\n this.state.remoteVideoTracks.clear();\n this.state.activeRemoteStream = undefined;\n this.clearRemoteStream();\n }\n\n /**\n * 订阅远端视频并在成功后就绪或确认生成任务。\n */\n private async subscribeRemoteVideo(\n userID: string\n ): Promise<void> {\n const state = this.state;\n const existing = state.remoteVideoTracks.get(userID);\n if (existing) {\n if (state.generationWaiter)\n this.confirmOrUpdateRemoteStream(userID, existing);\n return;\n }\n if (state.remoteVideoSubscriptions.has(userID))\n return;\n const subscription = {};\n state.remoteVideoSubscriptions.set(userID, subscription);\n const isCurrent = () => this.state === state && state.remoteVideoSubscriptions.get(userID) === subscription;\n\n try {\n const track = await this.rtcManager.subscribeRemoteVideo(userID, true);\n if (!isCurrent())\n return;\n if (!track) {\n throw new XmaxError(\n XmaxErrorCode.mediaError,\n \"Remote video track is not available\",\n );\n }\n\n state.subscribedRemoteUserIDs.add(userID);\n state.remoteVideoTracks.set(userID, track);\n XmaxLogger.stream.info(\n () =>\n `远端视频订阅成功 (Remote Video Subscribed)\\n` +\n `└─ userID: ${userID}`,\n );\n this.confirmOrUpdateRemoteStream(userID, track);\n } catch (error) {\n if (!isCurrent())\n return;\n const mapped = XmaxError.from(error);\n const pendingTaskID = this.state.generationWaiter\n ? this.state.generationTaskID\n : undefined;\n if (pendingTaskID) {\n this.rejectGenerationStart(pendingTaskID, mapped);\n } else {\n this.errorListener(mapped);\n }\n } finally {\n if (isCurrent())\n state.remoteVideoSubscriptions.delete(userID);\n }\n }\n\n /**\n * 订阅成功后就绪远端流:有待确认的生成任务时确认它,否则更新当前绑定。\n */\n private confirmOrUpdateRemoteStream(\n userID: string,\n track: MediaStreamTrack,\n ): void {\n const waiter = this.state.generationWaiter;\n const taskID = this.state.generationTaskID;\n const stream = new RemoteStream({ roomID: this.state.roomID, userID });\n\n if (waiter && taskID) {\n if (waiter.confirmationPending) {\n return;\n }\n waiter.confirmationPending = true;\n try {\n this.remoteStreamListener({ stream, videoTrack: track });\n if (this.state.generationTaskID === taskID) {\n this.state.activeRemoteStream = stream;\n this.notifyRemoteMedia();\n }\n } catch (error) {\n this.rejectGenerationStart(taskID, XmaxError.from(error));\n return;\n }\n this.resolveGenerationStart(taskID);\n return;\n }\n\n // 生成已确认后机器人重新发布:更新绑定与当前远端流。\n if (taskID) {\n try {\n this.remoteStreamListener({ stream, videoTrack: track });\n if (this.state.generationTaskID === taskID) {\n this.state.activeRemoteStream = stream;\n this.notifyRemoteMedia();\n }\n } catch (error) {\n this.errorListener(XmaxError.from(error));\n }\n }\n }\n\n /**\n * 订阅远端音频并按平台能力应用当前音量。\n *\n * 支持音量控制的平台先设音量再订阅,避免以默认音量短暂出声;\n * iOS 上音量降级为播放/静音二值控制,必须先完成订阅(恢复播放)\n * 再按需静音,否则订阅的恢复播放会覆盖音量 0 的静音状态。\n */\n private async subscribeRemoteAudio(\n userID: string\n ): Promise<void> {\n if (this.state.subscribedRemoteAudioUserIDs.has(userID)) {\n return;\n }\n if (this.rtcManager.supportsRemoteAudioVolumeControl) {\n this.rtcManager.setRemoteAudioVolume(this.remoteAudioVolumePercentage, userID);\n await this.rtcManager.subscribeRemoteAudio(userID, true);\n } else {\n await this.rtcManager.subscribeRemoteAudio(userID, true);\n this.rtcManager.setRemoteAudioVolume(this.remoteAudioVolumePercentage, userID);\n }\n this.state.subscribedRemoteAudioUserIDs.add(userID);\n this.notifyRemoteMedia();\n }\n\n /**\n * 取消订阅远端音频;失败仅记录日志。\n */\n private async unsubscribeRemoteAudio(\n userID: string\n ): Promise<void> {\n if (!this.state.subscribedRemoteAudioUserIDs.delete(userID)) {\n return;\n }\n await this.performCleanup(\n \"取消订阅 RTC 远端音频失败 (Failed to Unsubscribe from RTC Remote Audio)\",\n () => this.rtcManager.subscribeRemoteAudio(userID, false),\n );\n }\n\n /**\n * 停止当前生成任务:拒绝等待器、清理远端流与音频订阅。\n *\n * @returns 被停止的任务标识;无匹配任务时返回空字符串。\n */\n private async stopStreamGeneration(\n taskID: string,\n reason = \"Realtime generation start cancelled\",\n ): Promise<string> {\n const currentTaskID = this.state.generationTaskID ?? \"\";\n if (taskID && taskID !== currentTaskID) {\n return \"\";\n }\n\n const waiter = this.state.generationWaiter;\n const remoteAudioUserIDs = [...this.state.subscribedRemoteAudioUserIDs];\n this.state.generationTaskID = undefined;\n this.state.generationWaiter = undefined;\n this.state.activeRemoteStream = undefined;\n this.state.subscribedRemoteAudioUserIDs.clear();\n\n if (waiter) {\n clearTimeout(waiter.timeoutTimer);\n waiter.reject(new XmaxError(XmaxErrorCode.cancelled, reason));\n }\n\n for (const userID of remoteAudioUserIDs.sort()) {\n await this.performCleanup(\n \"取消订阅 RTC 远端音频失败 (Failed to Unsubscribe from RTC Remote Audio)\",\n () => this.rtcManager.subscribeRemoteAudio(userID, false),\n );\n }\n\n this.clearRemoteStream();\n\n return currentTaskID;\n }\n\n /**\n * 复位传输层:停止生成、取消全部远端订阅、取消本地发布。\n */\n private async resetStream(): Promise<void> {\n await this.stopStreamGeneration(\"\");\n\n const previousState = this.state;\n this.state = makeInitialState();\n\n for (const userID of [...previousState.subscribedRemoteUserIDs].sort()) {\n await this.performCleanup(\n \"取消订阅 RTC 远端视频失败 (Failed to Unsubscribe from RTC Remote Video)\",\n async () => {\n await this.rtcManager.subscribeRemoteVideo(userID, false);\n },\n );\n }\n\n if (previousState.localAudioPublished) {\n await this.performCleanup(\n \"取消发布 RTC 本地音频失败 (Failed to Unpublish RTC Local Audio)\",\n () => this.rtcManager.unpublishLocalAudio(),\n );\n }\n\n if (previousState.localVideoPublished) {\n await this.performCleanup(\n \"取消发布 RTC 本地视频失败 (Failed to Unpublish RTC Local Video)\",\n () => this.rtcManager.unpublishLocalVideo(),\n );\n }\n }\n\n /**\n * 确认生成开始:清理等待器并兑现确认 Promise。\n */\n private resolveGenerationStart(\n taskID: string\n ): void {\n const waiter = this.state.generationWaiter;\n if (this.state.generationTaskID !== taskID || waiter?.taskID !== taskID) {\n return;\n }\n this.state.generationWaiter = undefined;\n clearTimeout(waiter.timeoutTimer);\n waiter.resolve();\n }\n\n /**\n * 拒绝生成开始:清理等待器并拒绝确认 Promise。\n */\n private rejectGenerationStart(\n taskID: string,\n error: XmaxError\n ): void {\n const waiter = this.state.generationWaiter;\n if (this.state.generationTaskID !== taskID || waiter?.taskID !== taskID) {\n return;\n }\n this.state.generationWaiter = undefined;\n clearTimeout(waiter.timeoutTimer);\n waiter.reject(error);\n }\n\n /**\n * 通知渲染层清理远端生成流;失败仅记录日志。\n */\n private clearRemoteStream(): void {\n this.remoteMediaListener?.(undefined);\n this.remoteVideoStatisticsListener?.(undefined);\n try {\n this.remoteStreamListener(null);\n } catch (error) {\n XmaxLogger.stream.error(\n () =>\n `清理 RTC 远端生成流失败 (Failed to Clean Up RTC Remote Generation Stream)\\n` +\n `└─ ${XmaxLogger.localized(\"原因:\", \"Reason: \")}${XmaxError.from(error).message}`,\n );\n }\n }\n\n /**\n * 回滚本地视频发布;失败仅记录日志。\n */\n private async rollbackLocalVideoPublication(): Promise<void> {\n await this.performCleanup(\n \"回滚 RTC 本地视频发布失败 (Failed to Roll Back RTC Local Video Publication)\",\n async () => {\n await this.rtcManager.unpublishLocalVideo();\n this.state.localVideoPublished = false;\n },\n );\n }\n\n /**\n * 执行清理动作;失败仅记录日志。\n */\n private async performCleanup(\n title: string,\n action: () => Promise<void> | void,\n ): Promise<void> {\n try {\n await action();\n } catch (error) {\n XmaxLogger.stream.error(\n () =>\n `${title}\\n` +\n `└─ ${XmaxLogger.localized(\"原因:\", \"Reason: \")}${XmaxError.from(error).message}`,\n );\n }\n }\n}\n","//\n// QualityTiers.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\n/**\n * 支持自动调档的 RTC 新轨道/新生成任务的默认启动档位;不改变 L1 恢复上限。\n */\nexport const INITIAL_QUALITY_LEVEL = 2;\n\n/**\n * 自动缩放的短边下限;原始短边更小时不放大。\n */\nexport const MINIMUM_QUALITY_SHORT_SIDE = 240;\n\n/**\n * 上下行共用的五档缩放比例与上行帧率上限;具体尺寸由媒体基准计算。\n * 网络质量等级不与档位直接映射。\n */\nexport const QUALITY_TIERS = {\n L1: { scale: 1, frameRate: 30 },\n L2: { scale: 0.9, frameRate: 24 },\n L3: { scale: 0.75, frameRate: 24 },\n L4: { scale: 0.6, frameRate: 20 },\n L5: { scale: 0.5, frameRate: 16 },\n} as const;\n","//\n// QualityPolicy.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\n/**\n * 自动调档的样本阈值与时间窗口;样本数须为正整数,时间单位均为毫秒。\n */\nexport interface QualityPolicy {\n\n /**\n * 连续差网络样本(bad、veryBad)达到此次数时降一档,默认 3 次。\n */\n poorSamples: number;\n\n /**\n * 连续好网络样本(excellent、good)达到此次数时尝试升一档,默认 5 次。\n */\n goodSamples: number;\n\n /**\n * 切档成功或应用失败后的稳定等待时间,期间不累计质量样本,默认 4000 ms。\n */\n stabilizationMs: number;\n\n /**\n * 升档成功后的观察窗口;窗口内触发降档则判定试升失败,默认 20000 ms。\n */\n probeMs: number;\n\n /**\n * 试升失败并回退成功后禁止再次升档的时长,期间仍允许降档,默认 60000 ms。\n */\n upgradeCooldownMs: number;\n\n /**\n * 样本最大有效时长及相邻样本最大间隔;超时样本不计入,间隔超限则重新累计,默认 5000 ms。\n */\n sampleTimeoutMs: number;\n}\n\n/**\n * 快降慢升的默认策略,控制器可按需覆盖各项参数。\n */\nexport const DEFAULT_POLICY: QualityPolicy = {\n poorSamples: 3,\n goodSamples: 5,\n stabilizationMs: 4000,\n probeMs: 20000,\n upgradeCooldownMs: 60000,\n sampleTimeoutMs: 5000,\n};\n","//\n// QualityController.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\nimport { QUALITY_TIERS } from \"./QualityTiers\";\nimport { DEFAULT_POLICY, type QualityPolicy } from \"./QualityPolicy\";\nimport { RealtimeNetworkQualityLevel } from \"../../Foundation/RTC/NetworkStatistics\";\n\n/**\n * 按 L1~L5 顺序供状态机逐级切换,规格只在共享定义中维护。\n */\nconst ORDERED_QUALITY_TIERS = Object.values(QUALITY_TIERS);\n\n/**\n * 网络质量触发的档位变更及其原因。\n */\nexport interface QualityChange {\n\n /**\n * 切档前后的档位编号,范围 1~5;不能仅靠帧率区分档位。\n */\n from: number;\n\n /**\n * 切档后的档位编号,范围为 1–5。\n */\n to: number;\n\n /**\n * 相对固定媒体基准的缩放比例,实际尺寸由调用方解析。\n */\n scale: number;\n\n /**\n * 当前档位的上行帧率上限,单位为 fps。\n */\n frameRate: number;\n\n /**\n * 降档、尝试升档或试升失败回退的触发原因。\n */\n reason: \"poorNetwork\" | \"probeUpgrade\" | \"failedProbe\";\n}\n\n/**\n * 每个方向各自持有的质量状态机,默认停止,不持有 RTC 或定时器。\n * 调用方须在生成成功后 start,仅传入新到达的质量样本,重连/换流/结束时 stop。\n * apply 必须遵循 AbortSignal 及会话操作租约。下行无生效 ACK 时,档位仅表示已发送目标。\n */\nexport class QualityController {\n\n /**\n * 网络质量样本阈值与升降档时间窗口。\n */\n private readonly policy: QualityPolicy;\n\n /**\n * 当前功能是否处于活动状态。\n */\n private active = false;\n\n /**\n * 当前档位在共享档位表中的索引。\n */\n private index = 0;\n\n /**\n * 连续好网络样本数。\n */\n private good = 0;\n\n /**\n * 连续差网络样本数。\n */\n private poor = 0;\n\n /**\n * 最近一次有效网络样本的单调时间,单位为毫秒。\n */\n private lastSampleAt?: number;\n\n /**\n * 切档后恢复样本累计的最早时间,单位为单调时钟毫秒。\n */\n private stableUntil = 0;\n\n /**\n * 允许再次尝试升档的最早时间,单位为单调时钟毫秒。\n */\n private upgradeAfter = 0;\n\n /**\n * 当前升档观察窗口的截止时间,单位为单调时钟毫秒。\n */\n private probeUntil = 0;\n\n /**\n * 是否正在应用档位参数。\n */\n private switching = false;\n\n /**\n * 生命周期标识,使释放后的异步结果失效。\n */\n private lifecycle = new AbortController();\n\n /**\n * 创建调档状态机;候选档位不满足编码约束时保持当前档位,不跨档尝试。\n */\n constructor(\n private readonly apply: (\n change: QualityChange,\n signal: AbortSignal\n ) => Promise<void>,\n private readonly now: () => number = () => performance.now(),\n policy: Partial<QualityPolicy> = {},\n private readonly canApply: (\n level: number\n ) => boolean = () => true,\n ) {\n this.policy = { ...DEFAULT_POLICY, ...policy };\n for (const value of Object.values(this.policy)) {\n if (!Number.isFinite(value) || value < 0)\n throw new Error(\"Invalid quality policy\");\n }\n for (const value of [this.policy.poorSamples, this.policy.goodSamples]) {\n if (!Number.isInteger(value) || value < 1)\n throw new Error(\"Sample counts must be positive integers\");\n }\n }\n\n /**\n * 当前档位、活动状态和升档等待时间的只读快照。\n */\n get snapshot() {\n return Object.freeze({\n active: this.active, level: this.index + 1, ...ORDERED_QUALITY_TIERS[this.index]!,\n switching: this.switching, upgradeAfter: this.upgradeAfter,\n });\n }\n\n /**\n * 传入调用方保存的档位,不在 start 中擅自改写编码配置。\n */\n start(\n level = 1\n ): void {\n if (!Number.isInteger(level) || level < 1 || level > ORDERED_QUALITY_TIERS.length) {\n throw new Error(\"Unsupported quality level\");\n }\n this.stop();\n this.lifecycle = new AbortController();\n this.index = level - 1;\n this.active = true;\n this.stableUntil = this.upgradeAfter = this.probeUntil = 0;\n }\n\n /**\n * 取消旧会话的迟到结果;底层尚未完成时仍保持串行限制。\n */\n stop(): void {\n this.active = false;\n this.lifecycle.abort();\n this.resetSamples();\n }\n\n /**\n * 前台操作期间丢弃累计样本,但保留试升失败后的冷却时间。\n */\n resetSamples(): void {\n this.resetCounts();\n this.lastSampleAt = undefined;\n }\n\n /**\n * 只接收质量事件,不得用定时器重复提交同一份缓存统计。异常由调用方记录。\n */\n async observe(\n quality: RealtimeNetworkQualityLevel | undefined,\n sampleAt = this.now()\n ): Promise<QualityChange | undefined> {\n if (!this.active || this.switching || !Number.isFinite(sampleAt))\n return;\n const now = this.now();\n if (sampleAt > now)\n return;\n if (this.lastSampleAt !== undefined) {\n if (sampleAt <= this.lastSampleAt)\n return;\n if (sampleAt - this.lastSampleAt > this.policy.sampleTimeoutMs)\n this.resetCounts();\n }\n this.lastSampleAt = sampleAt;\n if (now - sampleAt > this.policy.sampleTimeoutMs || sampleAt < this.stableUntil) {\n this.resetCounts();\n return;\n }\n if (quality === RealtimeNetworkQualityLevel.bad || quality === RealtimeNetworkQualityLevel.veryBad) {\n this.good = 0;\n this.poor++;\n if (this.poor >= this.policy.poorSamples && this.index < ORDERED_QUALITY_TIERS.length - 1) {\n return this.change(this.index + 1, now < this.probeUntil ? \"failedProbe\" : \"poorNetwork\");\n }\n } else if (quality === RealtimeNetworkQualityLevel.excellent || quality === RealtimeNetworkQualityLevel.good) {\n this.poor = 0;\n if (sampleAt < this.upgradeAfter) {\n this.good = 0;\n return;\n }\n this.good++;\n if (this.good >= this.policy.goodSamples && this.index > 0) {\n return this.change(this.index - 1, \"probeUpgrade\");\n }\n } else {\n // 一般、未知、断连和无效等级均打断连续计数,不推测可用带宽。\n this.resetCounts();\n }\n return;\n }\n\n /**\n * 清除连续好网络与差网络样本计数。\n */\n private resetCounts(): void {\n this.good = this.poor = 0;\n }\n\n /**\n * 应用候选档位,成功后更新状态及升降档时间窗口。\n */\n private async change(\n index: number,\n reason: QualityChange[\"reason\"]\n ): Promise<QualityChange | undefined> {\n this.resetCounts();\n if (!this.canApply(index + 1)) {\n return;\n }\n\n const lifecycle = this.lifecycle;\n const change: QualityChange = { from: this.index + 1, to: index + 1, ...ORDERED_QUALITY_TIERS[index]!, reason };\n this.switching = true;\n try {\n await this.apply(change, lifecycle.signal);\n if (lifecycle.signal.aborted)\n return;\n this.index = index;\n const now = this.now();\n this.stableUntil = now + this.policy.stabilizationMs;\n this.probeUntil = reason === \"probeUpgrade\" ? now + this.policy.probeMs : 0;\n if (reason === \"failedProbe\")\n this.upgradeAfter = now + this.policy.upgradeCooldownMs;\n return change;\n } catch (error) {\n if (!lifecycle.signal.aborted) {\n this.stableUntil = this.now() + this.policy.stabilizationMs;\n throw error;\n }\n return;\n } finally {\n this.switching = false;\n }\n }\n}\n","//\n// QualityVideoFormat.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\nimport { RealtimeVideoFormat } from \"../../Service/Realtime/RealtimeVideoFormat\";\nimport { EncodingBitratePolicy } from \"../Encoding/EncodingBitratePolicy\";\nimport { MINIMUM_QUALITY_SHORT_SIDE, QUALITY_TIERS } from \"./QualityTiers\";\nimport { alignedVideoSize } from \"../../Foundation/Media/Video/VideoSize\";\nimport { XmaxError, XmaxErrorCode } from \"../../Foundation/Errors/XmaxError\";\n\n/**\n * 计算档位的浮点尺寸上限;最终对齐由媒体或编码尺寸规则完成。\n */\nexport function qualityVideoMaximumSize(\n baseline: RealtimeVideoFormat,\n level: number\n): {\n width: number;\n height: number\n} {\n const tier = Object.values(QUALITY_TIERS)[level - 1];\n if (!Number.isInteger(level) || !tier) {\n throw new Error(\"Unsupported quality level\");\n }\n baseline.validate();\n const shortSide = Math.min(baseline.width, baseline.height);\n const minimum = Math.min(shortSide, MINIMUM_QUALITY_SHORT_SIDE);\n const scale = Math.max(tier.scale, minimum / shortSide);\n return { width: baseline.width * scale, height: baseline.height * scale };\n}\n\n/**\n * 计算上行 4 对齐尺寸;优先保持精确比例,兼容按宽高分别向下取整。\n */\nexport function qualityVideoSize(\n baseline: RealtimeVideoFormat,\n level: number,\n preserveAspectRatio = true,\n): {\n width: number;\n height: number\n} {\n const maximum = qualityVideoMaximumSize(baseline, level);\n if (preserveAspectRatio) {\n return alignedVideoSize(baseline, Math.min(1, maximum.width / baseline.width, maximum.height / baseline.height), 4);\n }\n\n const width = Math.floor(maximum.width / 4 + 1e-9) * 4;\n const height = Math.floor(maximum.height / 4 + 1e-9) * 4;\n if (width < 4 || height < 4) {\n throw new XmaxError(XmaxErrorCode.invalidConfiguration, \"No aligned video size is available within the size limits\");\n }\n return { width, height };\n}\n\n/**\n * 检查目标规格的默认码率与显式上传码率约束是否兼容。\n */\nexport function supportsQualityEncoding(\n format: RealtimeVideoFormat\n): boolean {\n const defaults = EncodingBitratePolicy.resolve(format.width, format.height, format.fps);\n const maximum = format.maximumBitrate ?? defaults.maximum;\n return (format.minimumBitrate ?? Math.min(500, maximum)) <= maximum;\n}\n\n/**\n * 以用户初始/手动配置为 L1,按统一比例缩小并对齐。\n * 不读取 RTC 实际发送统计,避免自动降档后丢失恢复上限。\n * 显式码率沿用用户约束;缺省码率保持 undefined,由编码层按目标尺寸和帧率解析。\n */\nexport function qualityVideoFormat(\n baseline: RealtimeVideoFormat,\n level: number,\n preserveAspectRatio = true\n): RealtimeVideoFormat {\n const size = qualityVideoSize(baseline, level, preserveAspectRatio);\n const tier = Object.values(QUALITY_TIERS)[level - 1]!;\n return new RealtimeVideoFormat({\n ...baseline,\n ...size,\n fps: Math.min(baseline.fps, tier.frameRate),\n });\n}\n","//\n// QualityHistory.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\nimport { INITIAL_QUALITY_LEVEL, QUALITY_TIERS } from \"./QualityTiers\";\nimport type { RtcProvider } from \"../../Foundation/RTC/RtcProvider\";\n\n/**\n * 自动调档历史记录的传输方向。\n */\ntype QualityDirection = \"uplink\" | \"downlink\";\n\n/**\n * 一小时内成功应用的档位;按 RTC 提供方、模型、方向隔离,不保存媒体或凭据。\n */\nexport class QualityHistory {\n\n /**\n * 历史档位有效期,单位为毫秒。\n */\n private static readonly expirationMs = 60 * 60 * 1000;\n\n /**\n * 创建按模型与 RTC 提供方隔离的档位存储。\n */\n constructor(\n private readonly model: string,\n private readonly provider: RtcProvider,\n private readonly now: () => number = Date.now,\n ) { }\n\n /**\n * 无记录、过期、损坏、时钟回退或存储不可用时使用默认 L2。\n */\n read(\n direction: QualityDirection\n ): number {\n try {\n const entry = JSON.parse(globalThis.localStorage.getItem(this.key(direction)) ?? \"null\");\n const now = this.now();\n if (entry && this.isValid(entry.level) && Number.isFinite(entry.savedAt) &&\n entry.savedAt > 0 && now >= entry.savedAt && now - entry.savedAt < QualityHistory.expirationMs) {\n return entry.level;\n }\n } catch { /* 存储不可用不影响生成。 */ }\n return INITIAL_QUALITY_LEVEL;\n }\n\n /**\n * 应用成功后保存档位并刷新一小时有效期;下行成功仅表示信令已发送。\n */\n write(\n direction: QualityDirection,\n level: number\n ): void {\n if (!this.isValid(level)) {\n return;\n }\n try {\n globalThis.localStorage.setItem(this.key(direction), JSON.stringify({ level, savedAt: this.now() }));\n } catch { /* 配额或权限错误不影响自动调档。 */ }\n }\n\n /**\n * 生成当前方向的存储键;无时间戳的旧记录按无效记录处理。\n */\n private key(\n direction: QualityDirection\n ): string {\n return `xmax:quality:v1:${this.provider}:${encodeURIComponent(this.model)}:${direction}`;\n }\n\n /**\n * 校验档位编号。\n */\n private isValid(\n level: number\n ): boolean {\n return Number.isInteger(level) && level >= 1 && level <= Object.keys(QUALITY_TIERS).length;\n }\n}\n","//\n// RealtimeCoordinator.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\nimport { XmaxError, XmaxErrorCode } from \"../../Foundation/Errors/XmaxError\";\nimport {\n RealtimeConnectionState,\n RealtimeReason,\n RealtimeState,\n type RealtimeStateListener,\n} from \"../../Service/Realtime/RealtimeState\";\nimport type { RealtimeErrorHandler } from \"./RealtimeErrorHandler\";\n\n/**\n * 实时操作类型;同一时刻只允许一个操作。\n */\nexport enum RealtimeOperationKind {\n\n /**\n * 准备或释放本地媒体源。\n */\n media = \"media\",\n\n /**\n * 建立实时会话和 RTC 连接。\n */\n connection = \"connection\",\n\n /**\n * 启动或更新视频生成任务。\n */\n generation = \"generation\",\n\n /**\n * 保留连接并替换生成任务;失败按生成生命周期清理。\n */\n generationRestart = \"generationRestart\",\n\n /**\n * 切换当前摄像头。\n */\n cameraSwitch = \"cameraSwitch\",\n\n /**\n * 更新生成条件或回传尺寸,不改变生成生命周期。\n */\n configuration = \"configuration\",\n}\n\n/**\n * 资源清理范围;值越大范围越广,并发终止请求合并为最大范围。\n */\nexport enum RealtimeTerminationScope {\n\n /**\n * 结束生成并断开连接,保留本地媒体。\n */\n connection = 0,\n\n /**\n * 结束生成、断开连接并释放本地媒体。\n */\n all = 1,\n}\n\n/**\n * 判断终止范围是否影响指定类型的操作。\n */\nfunction scopeAffects(\n scope: RealtimeTerminationScope,\n kind: RealtimeOperationKind,\n): boolean {\n if (scope === RealtimeTerminationScope.all) {\n return true;\n }\n return kind !== RealtimeOperationKind.media;\n}\n\n/**\n * 清理完成后的残留资源信息。\n */\nexport interface RealtimeCleanupResult {\n\n /**\n * 业务会话标识。\n */\n sessionID?: string;\n\n /**\n * 资源清理后是否仍保留可用的本地媒体。\n */\n hasLocalMedia: boolean;\n}\n\n/**\n * 分级资源清理的执行体,由 Manager 提供。\n */\nexport type RealtimeCleanupHandler = (\n scope: RealtimeTerminationScope,\n taskID: string,\n) => Promise<RealtimeCleanupResult>;\n\n/**\n * 操作租约:更新失败清理范围、校验操作仍拥有实时生命周期。\n */\nexport interface RealtimeOperationToken {\n\n /**\n * 更新当前操作失败或被调用方取消时需要释放的资源范围。\n */\n setFailureScope(\n scope: RealtimeTerminationScope\n ): void;\n\n /**\n * 在异步边界后校验当前操作仍拥有实时生命周期。\n */\n ensureCurrent(): void;\n\n /**\n * 供必须同步返回的底层有效性回调读取操作状态。\n */\n readonly isCurrent: boolean;\n\n /**\n * 操作被取消时触发中止(供 fetch 等可中断调用使用)。\n */\n readonly signal: AbortSignal;\n}\n\n/**\n * 单次操作的有效性凭证,记录失败范围并向异步任务传播取消信号。\n */\nclass OperationLease implements RealtimeOperationToken {\n\n /**\n * 并发状态\n */\n private valid = true;\n\n /**\n * 使当前操作终止的错误。\n */\n private terminalError?: XmaxError;\n\n /**\n * 当前操作失败时需要清理的资源范围。\n */\n private storedFailureScope?: RealtimeTerminationScope;\n\n /**\n * 取消当前业务操作及相关异步等待。\n */\n private readonly abortController = new AbortController();\n\n /**\n * 创建操作租约;未指定失败范围时不自动扩大资源清理范围。\n */\n constructor(\n failureScope?: RealtimeTerminationScope\n ) {\n this.storedFailureScope = failureScope;\n }\n\n /**\n * 租约是否仍然有效,可用于异步结果提交前的快速判断。\n */\n get isCurrent(): boolean {\n return this.valid;\n }\n\n /**\n * 首次使租约失效的错误;主动取消且未指定原因时为空。\n */\n get failure(): XmaxError | undefined {\n return this.terminalError;\n }\n\n /**\n * 当前操作失败或取消时需要清理的资源范围。\n */\n get failureScope(): RealtimeTerminationScope | undefined {\n return this.storedFailureScope;\n }\n\n /**\n * 租约失效时同步触发的取消信号。\n */\n get signal(): AbortSignal {\n return this.abortController.signal;\n }\n\n /**\n * 更新当前操作失败或被调用方取消时需要释放的资源范围。\n */\n setFailureScope(\n scope: RealtimeTerminationScope\n ): void {\n this.storedFailureScope = scope;\n }\n\n /**\n * 作废租约并触发中止信号;首个终止原因被保留。\n */\n invalidate(\n error?: XmaxError\n ): void {\n this.valid = false;\n if (!this.terminalError) {\n this.terminalError = error;\n }\n this.abortController.abort();\n }\n\n /**\n * 在异步边界后校验当前操作仍拥有实时生命周期。\n */\n ensureCurrent(): void {\n if (!this.valid) {\n throw this.terminalError ?? RealtimeCoordinator.cancelledError();\n }\n }\n}\n\n/**\n * 正在执行的业务操作及其取消、清理进度。\n */\ninterface Operation {\n\n /**\n * 当前业务操作的唯一标识。\n */\n id: string;\n\n /**\n * 当前操作的业务类型。\n */\n kind: RealtimeOperationKind;\n\n /**\n * 当前操作持有的生命周期租约。\n */\n lease: OperationLease;\n\n /**\n * 当前操作完成或清理结束时兑现的任务。\n */\n completion: Promise<void>;\n\n /**\n * 等待当前操作收尾的终止任务。\n */\n terminationTask?: Promise<void>;\n}\n\n/**\n * 一次终止操作需要释放的范围及等待任务。\n */\ninterface Termination {\n\n /**\n * 本次终止流程的唯一标识。\n */\n id: string;\n\n /**\n * 本次需要释放的资源范围。\n */\n target: RealtimeTerminationScope;\n\n /**\n * 当前操作的结束或变更原因。\n */\n reason: RealtimeReason;\n\n /**\n * 导致当前操作失败的 SDK 错误。\n */\n error?: XmaxError;\n\n /**\n * 业务会话标识。\n */\n sessionID?: string;\n\n /**\n * 资源清理前需要等待的在途操作。\n */\n waitForOperations: Array<() => Promise<void>>;\n\n /**\n * 当前终止流程的共享任务。\n */\n task: Promise<void>;\n}\n\n/**\n * 为实时操作和终止流程分配递增标识。\n */\nlet operationSequence = 0;\n\n/**\n * 统一管理实时操作准入、状态提交和分级资源清理。\n * 在 JS 单线程事件循环下以 Promise 链和操作租约实现串行化语义。\n */\nexport class RealtimeCoordinator {\n\n /**\n * 业务组件\n */\n private readonly errorHandler: RealtimeErrorHandler;\n\n /**\n * 按终止范围释放业务资源的回调。\n */\n private readonly cleanup: RealtimeCleanupHandler;\n\n /**\n * 状态管理\n */\n private state = new RealtimeState({\n connectionState: RealtimeConnectionState.idle,\n });\n\n /**\n * 实时状态变化的业务监听器。\n */\n private stateListener?: RealtimeStateListener;\n\n /**\n * 操作管理\n */\n private activeOperation?: Operation;\n\n /**\n * 当前正在执行的资源终止流程。\n */\n private termination?: Termination;\n\n /**\n * 注入错误分发与资源清理逻辑,初始状态为 idle。\n */\n constructor(\n options: {\n errorHandler: RealtimeErrorHandler;\n cleanup: RealtimeCleanupHandler;\n }\n ) {\n this.errorHandler = options.errorHandler;\n this.cleanup = options.cleanup;\n }\n\n /**\n * 当前实时状态快照。\n */\n get currentState(): RealtimeState {\n return this.state;\n }\n\n /**\n * 后台自动调档仅在没有前台操作或清理时申请操作租约。\n */\n get isBusy(): boolean {\n return this.activeOperation !== undefined || this.termination !== undefined;\n }\n\n /**\n * 设置实时状态监听器;设置后立即回放当前状态。\n *\n * @param listener 实时状态回调;传入 `undefined` 时清除监听器。\n */\n async setStateListener(\n listener?: RealtimeStateListener\n ): Promise<void> {\n this.stateListener = listener;\n if (listener) {\n listener(this.state);\n }\n }\n\n /**\n * 接纳并执行一个实时操作,同一时刻只允许一个。生命周期操作取消时先完成\n * 对应范围的资源清理;仅取消参数更新不会停止当前生成。\n *\n * @param kind 操作类型。\n * @param failureScope 操作失败时需要自动释放的资源范围;不传则失败仅上报。\n * @param body 操作执行体,通过租约校验异步边界后的有效性。\n * @returns 操作执行结果。\n * @throws 已有活跃操作或终止进行中时抛出 `invalidConfiguration`。\n */\n async run<Value>(\n kind: RealtimeOperationKind,\n failureScope: RealtimeTerminationScope | undefined,\n body: (\n token: RealtimeOperationToken\n ) => Promise<Value>,\n ): Promise<Value> {\n if (this.termination || this.activeOperation) {\n const error = new XmaxError(\n XmaxErrorCode.invalidConfiguration,\n \"Another realtime operation is in progress; wait for it to finish\",\n );\n await this.errorHandler.report(error);\n throw error;\n }\n\n // 普通生成调用在运行中只更新条件;强制重启保持独立的生命周期操作类型。\n const operationKind =\n kind === RealtimeOperationKind.generation &&\n (this.state.connectionState === RealtimeConnectionState.generating ||\n (this.state.connectionState === RealtimeConnectionState.starting && !!this.state.taskID))\n ? RealtimeOperationKind.configuration\n : kind;\n\n const lease = new OperationLease(failureScope);\n const resultPromise = (async () => body(lease))();\n const operation: Operation = {\n id: `op-${(operationSequence += 1)}`,\n kind: operationKind,\n lease,\n completion: resultPromise.then(\n () => undefined,\n () => undefined,\n ),\n };\n this.activeOperation = operation;\n\n try {\n const value = await resultPromise;\n lease.ensureCurrent();\n this.finish(operation);\n return value;\n } catch (error) {\n return this.handleFailure(error, operation);\n }\n }\n\n /**\n * 仅当前操作可以提交新的公开状态。\n */\n async commit(\n nextState: RealtimeState,\n token: RealtimeOperationToken,\n ): Promise<void> {\n token.ensureCurrent();\n await this.setState(nextState);\n }\n\n /**\n * 当前本地预览满足就绪条件后,结束媒体准备状态。\n */\n async localPreviewDidBecomeReady(\n isCurrent: () => boolean\n ): Promise<void> {\n if (\n this.state.connectionState !== RealtimeConnectionState.preparing ||\n !isCurrent()\n ) {\n return;\n }\n await this.setState(\n new RealtimeState({ connectionState: RealtimeConnectionState.ready }),\n );\n }\n\n /**\n * 当前生成任务和远端展示均就绪后进入 generating;迟到通知不恢复已结束任务。\n */\n async remotePreviewDidBecomeReady(\n sessionID: string,\n taskID: string,\n isCurrent: () => boolean,\n ): Promise<void> {\n if (this.termination || this.state.connectionState !== RealtimeConnectionState.starting ||\n this.state.sessionID !== sessionID || this.state.taskID !== taskID || !isCurrent()) {\n return;\n }\n\n await this.setState(new RealtimeState({\n connectionState: RealtimeConnectionState.generating, sessionID, taskID,\n }));\n }\n\n /**\n * 断开实时连接;尚未提交连接状态的活跃操作也会被取消。\n */\n async disconnect(\n reason: RealtimeReason = RealtimeReason.normal\n ): Promise<void> {\n const task = await this.beginDisconnect(reason);\n await task;\n }\n\n /**\n * 发起断开并返回收尾任务,使本地媒体调整不必等待网络资源释放。\n */\n async beginDisconnect(\n reason: RealtimeReason,\n ): Promise<Promise<void> | undefined> {\n const hasConnectionOperation = this.activeOperation\n ? scopeAffects(RealtimeTerminationScope.connection, this.activeOperation.kind)\n : false;\n const state = this.state.connectionState;\n const needsDisconnect =\n hasConnectionOperation ||\n this.termination !== undefined ||\n (state !== RealtimeConnectionState.idle &&\n state !== RealtimeConnectionState.preparing &&\n state !== RealtimeConnectionState.ready);\n if (!needsDisconnect) {\n return undefined;\n }\n\n return this.requestTermination(\n RealtimeTerminationScope.connection,\n undefined,\n reason,\n );\n }\n\n /**\n * 终止指定范围并等待资源清理完成;并发终止请求会合并为最大范围。\n */\n async terminate(\n target: RealtimeTerminationScope,\n reason: RealtimeReason = RealtimeReason.normal,\n ): Promise<void> {\n const task = await this.requestTermination(target, undefined, reason);\n await task;\n }\n\n /**\n * 清理仍属于当前生命周期的后台故障,并通过最终状态提供原因。\n */\n async terminateWithError(\n error: XmaxError,\n target: RealtimeTerminationScope,\n isCurrent: () => boolean = () => true,\n ): Promise<void> {\n if (!isCurrent()) {\n return;\n }\n // 清理期间的远端迟到错误不覆盖主动结束;本地媒体终止仍需扩大释放范围。\n if (this.termination && target !== RealtimeTerminationScope.all) {\n return;\n }\n const task = await this.requestTermination(\n target,\n error,\n RealtimeReason.failure(error),\n );\n await task;\n }\n\n /**\n * 操作失败处理:取消走终止流程,错误按失败范围清理或直接上报。\n */\n private async handleFailure<Value>(\n error: unknown,\n operation: Operation,\n ): Promise<Value> {\n const operationWasCancelled =\n (error instanceof XmaxError && error.code === XmaxErrorCode.cancelled) ||\n (error instanceof DOMException && error.name === \"AbortError\") ||\n !operation.lease.isCurrent;\n\n if (operationWasCancelled) {\n // 取消配置请求本身不停止生成;外部断开或关闭仍按已有终止流程等待收尾。\n if (\n (operation.kind === RealtimeOperationKind.configuration ||\n operation.lease.failureScope === undefined) &&\n operation.terminationTask === undefined &&\n this.termination === undefined\n ) {\n this.finish(operation);\n if (\n operation.kind === RealtimeOperationKind.media &&\n this.state.connectionState === RealtimeConnectionState.preparing\n ) {\n await this.setState(\n new RealtimeState({ connectionState: RealtimeConnectionState.idle }),\n );\n }\n throw RealtimeCoordinator.cancelledError();\n }\n const terminationTask =\n operation.terminationTask ??\n this.termination?.task ??\n (await this.requestTermination(\n operation.lease.failureScope ?? RealtimeTerminationScope.connection,\n undefined,\n RealtimeReason.normal,\n operation,\n ));\n await terminationTask;\n this.finish(operation);\n throw operation.lease.failure ?? RealtimeCoordinator.cancelledError();\n }\n\n const resolvedError = XmaxError.from(error);\n if (\n operation.kind !== RealtimeOperationKind.configuration &&\n operation.lease.failureScope !== undefined\n ) {\n const terminationTask = await this.requestTermination(\n operation.lease.failureScope,\n resolvedError,\n RealtimeReason.failure(resolvedError),\n operation,\n );\n await terminationTask;\n } else {\n this.finish(operation);\n if (\n operation.kind === RealtimeOperationKind.media &&\n this.state.connectionState === RealtimeConnectionState.preparing\n ) {\n await this.setState(\n new RealtimeState({ connectionState: RealtimeConnectionState.idle }),\n );\n }\n await this.errorHandler.report(resolvedError);\n throw resolvedError;\n }\n\n this.finish(operation);\n throw resolvedError;\n }\n\n /**\n * 发起或合并终止请求,作废旧操作并进入断开中状态。\n */\n private async requestTermination(\n target: RealtimeTerminationScope,\n error: XmaxError | undefined,\n reason: RealtimeReason,\n origin?: Operation,\n ): Promise<Promise<void>> {\n let pending = this.termination;\n if (pending) {\n if (target > pending.target) {\n pending.target = target;\n }\n if (!pending.error) {\n pending.error = error;\n }\n if (error && pending.error === error) {\n pending.reason = RealtimeReason.failure(error);\n }\n } else {\n pending = {\n id: `term-${(operationSequence += 1)}`,\n target,\n reason,\n error,\n waitForOperations: [],\n task: Promise.resolve(),\n };\n this.termination = pending;\n pending.task = this.performTermination(pending.id);\n }\n\n const active = this.activeOperation;\n if (active && active !== origin && scopeAffects(pending.target, active.kind)) {\n active.lease.invalidate(pending.error);\n active.terminationTask = pending.task;\n pending.waitForOperations.push(() => active.completion);\n } else if (origin) {\n origin.lease.invalidate(error);\n origin.terminationTask = pending.task;\n pending.waitForOperations.push(() => origin.completion);\n }\n\n await this.setState(\n new RealtimeState({\n connectionState: RealtimeConnectionState.disconnecting,\n sessionID: this.state.sessionID,\n }),\n );\n\n return pending.task;\n }\n\n /**\n * 执行终止:等待旧操作收尾,分级清理资源,提交最终状态。\n */\n private async performTermination(\n id: string\n ): Promise<void> {\n const pending = this.termination;\n if (!pending || pending.id !== id) {\n return;\n }\n\n for (; ;) {\n const waiters = pending.waitForOperations;\n pending.waitForOperations = [];\n for (const waiter of waiters) {\n await waiter();\n }\n\n const requestedTarget = pending.target;\n const result = await this.cleanup(\n requestedTarget,\n this.state.taskID ?? \"\",\n );\n if (result.sessionID) {\n pending.sessionID = result.sessionID;\n }\n\n if (pending.target !== requestedTarget || pending.waitForOperations.length > 0) {\n continue;\n }\n\n const connectionState =\n requestedTarget === RealtimeTerminationScope.all\n ? RealtimeConnectionState.idle\n : result.hasLocalMedia\n ? RealtimeConnectionState.ready\n : RealtimeConnectionState.idle;\n const finalState = new RealtimeState({\n connectionState,\n sessionID: pending.sessionID ?? this.state.sessionID,\n reason: pending.reason,\n });\n // 先完成内部收尾,再通知监听器,避免旧任务覆盖重入的关闭请求。\n const active = this.activeOperation;\n if (active && scopeAffects(requestedTarget, active.kind)) {\n this.finish(active);\n }\n const listener = !this.state.equals(finalState)\n ? this.stateListener\n : undefined;\n this.state = finalState;\n this.termination = undefined;\n this.errorHandler.invalidatePendingFailures(requestedTarget);\n if (listener) {\n listener(finalState);\n }\n if (pending.error) {\n await this.errorHandler.report(pending.error);\n }\n return;\n }\n }\n\n /**\n * 结束指定操作,释放操作准入场。\n */\n private finish(\n operation: Operation\n ): void {\n if (this.activeOperation?.id === operation.id) {\n this.activeOperation = undefined;\n }\n }\n\n /**\n * 提交新状态并通知监听器;状态未变化时不通知。\n */\n private async setState(\n nextState: RealtimeState\n ): Promise<void> {\n if (this.state.equals(nextState)) {\n return;\n }\n this.state = nextState;\n this.stateListener?.(nextState);\n }\n\n /**\n * 构造统一的操作取消错误。\n */\n static cancelledError(): XmaxError {\n return new XmaxError(\n XmaxErrorCode.cancelled,\n \"Realtime operation was cancelled\",\n );\n }\n}\n","//\n// RealtimeErrorHandler.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\nimport { XmaxError, XmaxErrorCode } from \"../../Foundation/Errors/XmaxError\";\nimport { XmaxLogger } from \"../../Foundation/Logging/XmaxLogger\";\nimport { RealtimeTerminationScope } from \"./RealtimeCoordinator\";\n\n/**\n * 实时业务异步错误的统一出口。\n * 当前为简化实现:记录日志并保留待上报错误。\n */\nexport class RealtimeErrorHandler {\n\n /**\n * 运行状态\n */\n private pendingFailures: XmaxError[] = [];\n\n /**\n * 上报错误;当前通过日志输出,后续里程碑接入状态原因上报。\n */\n async report(\n error: XmaxError\n ): Promise<void> {\n if (error.code === XmaxErrorCode.cancelled) {\n return;\n }\n this.pendingFailures.push(error);\n XmaxLogger.realtime.error(\n () =>\n `实时业务错误 (Realtime Error)\\n└─ ${XmaxLogger.localized(\"原因:\", \"Reason: \")}` +\n `${error.code} ${error.message}`,\n );\n }\n\n /**\n * 清理仍属于已终止生命周期的后台故障。\n */\n invalidatePendingFailures(\n _target: RealtimeTerminationScope\n ): void {\n this.pendingFailures = [];\n }\n}\n","//\n// RealtimeTiming.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\nimport type {\n RealtimeTiming as RealtimeTimingSnapshot,\n RealtimeTimingListener,\n} from \"../../Service/Realtime/RealtimeTiming\";\nimport { XmaxLogger, XmaxLoggerOption } from \"../../Foundation/Logging/XmaxLogger\";\n\n/**\n * @internal 观察媒体源首轮启动耗时,提供快照并在预热完成后输出性能日志。\n * 不参与生成流程的状态提交或等待。\n */\nexport class RealtimeTiming {\n\n /**\n * 统计快照与观察者\n */\n private snapshot: RealtimeTimingSnapshot = Object.freeze({});\n\n /**\n * 启动耗时快照的业务监听器。\n */\n private listener?: RealtimeTimingListener;\n\n /**\n * 当前启动周期与计时起点\n */\n private sequence = 0;\n\n /**\n * 当前启动周期是否仍在记录耗时。\n */\n private active = false;\n\n /**\n * 当前计时起点,使用单调时钟的毫秒值。\n */\n private startedAt = 0;\n\n /**\n * 设置统计监听器并立即回放当前快照;传 undefined 取消监听。\n */\n setListener(\n listener?: RealtimeTimingListener\n ): void {\n this.listener = listener;\n this.notify();\n }\n\n /**\n * 开始新的启动周期并清空旧快照,返回记录相机创建完成的一次性回调。\n */\n startCamera(): () => void {\n this.start();\n return this.measure(\"cameraMs\", this.startedAt);\n }\n\n /**\n * 开始新的启动周期;非摄像头源不产生相机耗时。\n */\n start(): void {\n this.sequence += 1;\n this.active = true;\n this.startedAt = performance.now();\n this.snapshot = Object.freeze({});\n\n this.notify();\n }\n\n /**\n * 开始等待用户授权系统媒体权限,返回授权决定时调用的记录函数。\n * 仅在系统弹出授权请求时使用;已授权或未弹窗不记录该阶段。\n */\n startPermission(): () => void {\n return this.measure(\"permissionMs\", performance.now());\n }\n\n /**\n * 开始创建业务会话的计时。\n */\n startSession(): () => void {\n return this.measure(\"sessionMs\", performance.now());\n }\n\n /**\n * 开始 RTC 适配器进房调用的计时。\n */\n startRtcJoin(): () => void {\n return this.measure(\"rtcJoinMs\", performance.now());\n }\n\n /**\n * 开始发布本地流计时,返回发布完成时调用的记录函数。\n */\n startPublish(): () => void {\n return this.measure(\"publishMs\", performance.now());\n }\n\n /**\n * 开始等待远端首帧提交合成器,不包含后续渲染预热。\n */\n startFirstFrame(): () => void {\n return this.measure(\"firstFrameMs\", performance.now());\n }\n\n /**\n * 首帧就绪后开始渲染预热;结束时记录预热及实际总耗时,不含渐入动画。\n */\n startRenderWarmup(): () => void {\n return this.measure(\"renderWarmupMs\", performance.now());\n }\n\n /**\n * 冻结本轮结果,令尚未完成的回调失效;下一轮创建媒体源时重置。\n */\n cancel(): void {\n this.active = false;\n this.sequence += 1;\n }\n\n /**\n * 创建当前周期的一次性计时回调;周期失效或该阶段已记录时忽略调用。\n */\n private measure(\n key: Exclude<keyof RealtimeTimingSnapshot, \"totalMs\">,\n startedAt: number,\n ): () => void {\n const sequence = this.sequence;\n return () => {\n if (!this.active || sequence !== this.sequence || this.snapshot[key] !== undefined) {\n return;\n }\n\n const now = performance.now();\n this.snapshot = Object.freeze({\n ...this.snapshot,\n [key]: now - startedAt,\n ...(key === \"renderWarmupMs\" ? { totalMs: now - this.startedAt } : {}),\n });\n\n if (key === \"renderWarmupMs\") {\n const snapshot = this.snapshot;\n XmaxLogger.realtime.info(() => this.format(snapshot), XmaxLoggerOption.performance);\n }\n\n this.notify();\n };\n }\n\n /**\n * 按快照口径格式化启动汇总,未完成或未启用的阶段显示占位符。\n */\n private format(\n snapshot: RealtimeTimingSnapshot\n ): string {\n const stages = [\n [XmaxLogger.localized(\"摄像头启动:\", \"Camera Startup: \"), snapshot.cameraMs],\n [XmaxLogger.localized(\"媒体授权:\", \"Media Permission: \"), snapshot.permissionMs],\n [XmaxLogger.localized(\"创建会话:\", \"Session Creation: \"), snapshot.sessionMs],\n [XmaxLogger.localized(\"RTC 进房:\", \"RTC Room Join: \"), snapshot.rtcJoinMs],\n [XmaxLogger.localized(\"发布本地流:\", \"Local Stream Publication: \"), snapshot.publishMs],\n [XmaxLogger.localized(\"首帧到达:\", \"First Frame Arrival: \"), snapshot.firstFrameMs],\n [XmaxLogger.localized(\"渲染预热:\", \"Render Warmup: \"), snapshot.renderWarmupMs],\n ] as const;\n\n return [\n \"实时生成启动耗时 (Realtime Generation Startup Timing)\",\n `└─ ${XmaxLogger.localized(\"总耗时:\", \"Total Duration: \")}${this.duration(snapshot.totalMs)}`,\n ...stages.map(([label, duration], index) =>\n ` ${index === stages.length - 1 ? \"└─\" : \"├─\"} ${label}${this.duration(duration)}`,\n ),\n ].join(\"\\n\");\n }\n\n /**\n * 毫秒值保留一位小数,缺失或无效值显示占位符。\n */\n private duration(\n milliseconds?: number\n ): string {\n if (milliseconds === undefined || !Number.isFinite(milliseconds) || milliseconds < 0) {\n return \"—\";\n }\n\n return `${milliseconds.toFixed(1)} ms`;\n }\n\n /**\n * 分发不可变快照;隔离同步和异步观察者异常,避免影响媒体生命周期。\n */\n private notify(): void {\n try {\n // 异步监听器也允许使用;观察者异常不得影响媒体生命周期。\n void Promise.resolve(this.listener?.(this.snapshot)).catch(() => { });\n } catch {\n // 同步观察者异常同样隔离。\n }\n }\n}\n","//\n// RealtimeCameraPreset.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\nimport type { ModelSize } from \"../../Service/Realtime/RealtimeModel\";\nimport { XmaxError, XmaxErrorCode } from \"../../Foundation/Errors/XmaxError\";\n\n/**\n * 摄像头分辨率快捷选项;实际支持范围由模型决定。\n */\nexport enum RealtimeCameraPreset {\n\n /**\n * 16:9 横向画面。\n */\n resolution1920x1080 = \"1920x1080\",\n\n /**\n * 9:16 竖向画面。\n */\n resolution1080x1920 = \"1080x1920\",\n\n /**\n * 1:1 正方形画面。\n */\n resolution1408x1408 = \"1408x1408\",\n\n /**\n * 3:4 竖向画面。\n */\n resolution1152x1536 = \"1152x1536\",\n\n /**\n * 4:3 横向画面。\n */\n resolution1536x1152 = \"1536x1152\",\n}\n\n/**\n * 读取预设的像素宽高,不包含帧率和码率配置。\n */\nexport function cameraPresetSize(\n preset: RealtimeCameraPreset\n): ModelSize {\n if (!Object.values(RealtimeCameraPreset).includes(preset)) {\n throw new XmaxError(XmaxErrorCode.invalidConfiguration, \"Unsupported camera preset\");\n }\n const [width, height] = preset.split(\"x\").map(Number);\n return { width: width!, height: height! };\n}\n","//\n// RealtimeQualityLevel.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\n/**\n * 实时传输档位;L1 质量最高,L5 最节省带宽,与网络质量评级无关。\n */\nexport enum RealtimeQualityLevel {\n\n /**\n * 保留媒体基准尺寸。\n */\n l1 = 1,\n\n /**\n * 目标宽高为媒体基准的 90%。\n */\n l2 = 2,\n\n /**\n * 目标宽高为媒体基准的 75%。\n */\n l3 = 3,\n\n /**\n * 目标宽高为媒体基准的 60%。\n */\n l4 = 4,\n\n /**\n * 目标宽高为媒体基准的 50%,上行帧率上限为 16 fps。\n */\n l5 = 5,\n}\n","//\n// XmaxRealtimeConnectionManager.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\nimport { XmaxError, XmaxErrorCode } from \"../../Foundation/Errors/XmaxError\";\nimport { XmaxLogger } from \"../../Foundation/Logging/XmaxLogger\";\nimport type { RenderControlling } from \"../../Render/RenderControlling\";\nimport type { InteractionFrame } from \"../../Media/Interaction/InteractionFrame\";\nimport { RealtimeMediaStream } from \"../../Service/Realtime/RealtimeMediaStream\";\nimport type { RealtimeModelName } from \"../../Service/Realtime/RealtimeModel\";\nimport { connectionAppID } from \"../../Service/Realtime/RealtimeSessionConnection\";\nimport { RealtimeSession } from \"../../Service/Realtime/RealtimeSession\";\nimport type { RealtimeSessionServicing } from \"../../Service/Realtime/RealtimeSessionServicing\";\nimport { RealtimeVideoTrack } from \"../../Service/Realtime/RealtimeVideoTrack\";\nimport { StreamID } from \"../../Service/Realtime/StreamID\";\nimport type { RemoteStreamBinding, StreamControlling } from \"../../Stream/StreamControlling\";\nimport type { RealtimeTiming } from \"./RealtimeTiming\";\nimport { RealtimeCoordinator } from \"./RealtimeCoordinator\";\nimport { RtcProvider } from \"../../Foundation/RTC/RtcProvider\";\n\n/**\n * 内部连接管理器:拥有会话、心跳、RTC 连接及远端轨道;不提交公开状态。\n */\nexport class XmaxRealtimeConnectionManager {\n\n /**\n * 会话资源\n */\n private activeSession?: RealtimeSession;\n\n /**\n * 会话凭据刷新版本,过滤旧生命周期的更新。\n */\n private refreshVersion = 0;\n\n /**\n * 当前会话凭据刷新的共享任务。\n */\n private credentialRefresh?: Promise<void>;\n\n /**\n * 当前正在应用的凭据更新任务。\n */\n private credentialApplication: Promise<void> = Promise.resolve();\n\n /**\n * 取消当前凭据刷新及恢复流程。\n */\n private credentialController?: AbortController;\n\n /**\n * 远端轨道与渲染绑定\n */\n private activeRemoteTrack?: RealtimeVideoTrack;\n\n /**\n * 注入会话、传输、计时和渲染回调依赖;连接资源在 connect 时创建。\n */\n constructor(\n /**\n * 会话、传输、计时与渲染依赖\n */\n private readonly dependencies: {\n provider?: RtcProvider;\n sessionService?: RealtimeSessionServicing;\n streamController: StreamControlling;\n timing: RealtimeTiming;\n remoteAudioVolume: () => number;\n onHeartbeatFailure: (\n sessionID: string,\n error: XmaxError\n ) => void;\n renderController: RenderControlling;\n onInteraction: (\n frame: InteractionFrame\n ) => void;\n }\n ) { }\n\n /**\n * 当前活动会话标识;尚未连接或清理完成后为空。\n */\n get currentSessionID(): string | undefined {\n return this.activeSession?.id;\n }\n\n /**\n * 未配置 API 服务时在连接操作开始前报错,不影响本地预览。\n */\n validateConfiguration(): void {\n this.requireSessionService();\n }\n\n /**\n * 创建会话、配置编码、进房并发布本地媒体,启动心跳后返回远端占位流。\n * 会话创建、RTC 进房和本地流发布分别计时;可选 beforePublish 检查阻塞发布,\n * 其等待只反映在实际完整启动耗时中。\n * @param options.localTrack 当前本地媒体轨道。\n * @param options.remoteVideoFormat 原始生成及远端画面规格,缺省使用本地轨道规格。\n * @param options.localVideoFormat 发布前实际应用的上行编码格式;与原始生成及远端格式分开。\n * @param options.onVideoEncoderConfigured 上行编码配置成功后的回调。\n * @param options.model 创建业务会话使用的模型名称。\n * @param options.includeLocalAudio 是否发布本地音频。\n * @param options.ensureCurrent 校验当前操作仍有效。\n * @param options.onPublished 本地发布完成后的回调。\n * @param options.beforePublish 发布前的可选异步检查。\n * @throws 会话、进房、发布失败或操作租约已失效时抛出错误,由上层统一清理。\n */\n async connect(\n options: {\n localTrack: RealtimeVideoTrack;\n remoteVideoFormat?: RealtimeVideoTrack[\"videoFormat\"];\n localVideoFormat?: RealtimeVideoTrack[\"videoFormat\"];\n onVideoEncoderConfigured?: () => void;\n model: RealtimeModelName;\n includeLocalAudio: boolean;\n ensureCurrent: () => void;\n onPublished: () => void;\n beforePublish?: () => Promise<void>;\n }\n ): Promise<RealtimeMediaStream> {\n const sessionService = this.requireSessionService();\n const streamController = this.dependencies.streamController;\n\n // 会话创建成功后立即登记,供失败清理时关闭会话。\n options.ensureCurrent();\n const completeSession = this.dependencies.timing.startSession();\n const session = await sessionService.createSession(options.model);\n this.activeSession = session;\n options.ensureCurrent();\n completeSession();\n\n const connection = session.connection;\n if (!connection) {\n throw new XmaxError(\n XmaxErrorCode.sessionError,\n \"Session does not contain RTC join information\",\n );\n }\n // 内部服务与适配器必须使用同一接入配置;connection.provider 不来自后端。\n if (connection.provider !== (this.dependencies.provider ?? RtcProvider.trtc)) {\n throw new XmaxError(XmaxErrorCode.sessionError, \"RTC adapter configuration does not match the session service\");\n }\n const refreshVersion = ++this.refreshVersion;\n this.credentialController = new AbortController();\n\n // 发布本地流之前配置编码参数:采集阶段不发布,此处配置即可生效到发送端。\n const videoFormat = options.localVideoFormat ?? options.localTrack.videoFormat;\n if (!videoFormat) {\n throw new XmaxError(\n XmaxErrorCode.internalError,\n \"Local video stream has no video format\",\n );\n }\n await streamController.updateUplinkVideoFormat(videoFormat);\n options.ensureCurrent();\n options.onVideoEncoderConfigured?.();\n options.ensureCurrent();\n\n let completePublish: (() => void) | undefined;\n await streamController.connect(connection, options.includeLocalAudio, () => {\n options.ensureCurrent();\n }, async () => {\n options.ensureCurrent();\n\n // 预热与建连并行;剩余等待不计入进房或发布耗时。\n await options.beforePublish?.();\n options.ensureCurrent();\n completePublish = this.dependencies.timing.startPublish();\n }, () => this.dependencies.timing.startRtcJoin());\n options.ensureCurrent();\n completePublish?.();\n options.onPublished();\n\n sessionService.startHeartbeat(session.id, {\n onFailure: (sessionID, error) => {\n this.dependencies.onHeartbeatFailure(sessionID, error);\n },\n onRefresh: (refreshed) => {\n return this.handleSessionRefresh(refreshed, refreshVersion);\n },\n });\n\n const remoteTrack = new RealtimeVideoTrack({\n id: connection.botID ?? \"video-remote\",\n videoFormat: options.remoteVideoFormat ?? options.localTrack.videoFormat,\n });\n this.activeRemoteTrack = remoteTrack;\n this.dependencies.renderController.registerRemoteTrack(remoteTrack, this.dependencies.onInteraction);\n streamController.setRemoteAudioVolume(this.dependencies.remoteAudioVolume());\n\n return new RealtimeMediaStream({ id: StreamID.remote, videoTrack: remoteTrack });\n }\n\n /**\n * 清空远端视图的媒体流,保留轨道与视图绑定以供统一清理。\n */\n clearRemoteMedia(): void {\n this.dependencies.renderController.setRemoteMedia(null);\n }\n\n /**\n * 停止会话心跳;未配置会话服务时不执行操作。\n */\n stopHeartbeat(): void {\n this.refreshVersion++;\n this.credentialController?.abort();\n this.credentialController = undefined;\n this.credentialRefresh = undefined;\n this.credentialApplication = Promise.resolve();\n this.dependencies.sessionService?.stopHeartbeat();\n }\n\n /**\n * RTC Token 过期时复用会话心跳获取新凭据,并合并同一实例的并发请求。\n */\n async refreshCredentials(): Promise<void> {\n if (this.credentialRefresh)\n return this.credentialRefresh;\n const current = this.activeSession;\n if (!current?.connection || current.connection.provider === RtcProvider.trtc)\n return;\n const version = this.refreshVersion;\n const operation = (async () => {\n try {\n const session = await this.requireSessionService().heartbeatSession(current.id);\n await this.handleSessionRefresh(session, version, true);\n } catch (error) {\n if (version === this.refreshVersion && this.activeSession?.id === current.id) {\n this.dependencies.onHeartbeatFailure(current.id, XmaxError.from(error));\n }\n }\n })();\n this.credentialRefresh = operation;\n try {\n await operation;\n }\n finally {\n if (this.credentialRefresh === operation)\n this.credentialRefresh = undefined;\n }\n }\n\n /**\n * 等待接收轨解除静音;超时只警告,取消必须立即退出并移除监听。\n */\n async waitUntilRemoteTrackReady(\n timeoutMs: number,\n signal: AbortSignal\n ): Promise<void> {\n if (signal.aborted)\n throw RealtimeCoordinator.cancelledError();\n\n const mediaTrack = this.activeRemoteTrack?.mediaStreamTrack;\n if (!mediaTrack || !mediaTrack.muted)\n return;\n\n await new Promise<void>((resolve, reject) => {\n const cleanup = () => {\n clearTimeout(timer);\n mediaTrack.removeEventListener(\"unmute\", finish);\n signal.removeEventListener(\"abort\", abort);\n };\n const finish = () => {\n cleanup();\n resolve();\n };\n const abort = () => {\n cleanup();\n reject(RealtimeCoordinator.cancelledError());\n };\n\n const timer = setTimeout(() => {\n XmaxLogger.realtime.warning(() => \"等待远端生成流首帧超时 (Timed Out Waiting for the First Remote Frame)\");\n finish();\n }, timeoutMs);\n\n mediaTrack.addEventListener(\"unmute\", finish);\n signal.addEventListener(\"abort\", abort, { once: true });\n if (signal.aborted)\n abort();\n });\n }\n\n /**\n * 传输层远端生成流就绪或清理:更新远端轨道媒体轨并同步到渲染视图。\n *\n * @param binding 远端流及其视频轨;传入空值表示清理。\n * @throws 远端流到达但当前没有活动连接时抛出错误,使生成确认失败。\n */\n handleRemoteStreamBinding(\n binding: RemoteStreamBinding | null\n ): void {\n const track = this.activeRemoteTrack;\n if (!binding) {\n if (track) {\n track.mediaStreamTrack = undefined;\n }\n this.dependencies.renderController.setRemoteMedia(null);\n return;\n }\n\n if (!track) {\n throw new XmaxError(\n XmaxErrorCode.rtcError,\n \"Remote generation stream arrived without an active realtime connection\",\n );\n }\n\n track.mediaStreamTrack = binding.videoTrack;\n this.dependencies.renderController.setRemoteMedia(binding.videoTrack);\n }\n\n /**\n * 构造当前远端生成结果媒体流。\n */\n makeRemoteStream(): RealtimeMediaStream {\n const track = this.activeRemoteTrack;\n if (!track) {\n throw new XmaxError(\n XmaxErrorCode.rtcError,\n \"The remote generation stream is unavailable\",\n );\n }\n return new RealtimeMediaStream({ id: StreamID.remote, videoTrack: track });\n }\n\n /**\n * 心跳成功后的凭据刷新:仅凭据变化时覆盖本地缓存;房间绑定信息\n * (提供方、房间、应用、登录身份)变化时当前连接失效,按失败结束。\n */\n private handleSessionRefresh(\n session: RealtimeSession,\n version: number,\n force = false\n ): Promise<void> {\n // 周期心跳和 Token 到期通知可能并发;按响应顺序应用,避免较旧 Token 迟到覆盖。\n const operation = this.credentialApplication.then(() => this.applySessionRefresh(session, version, force));\n this.credentialApplication = operation.catch(() => { });\n return operation;\n }\n\n /**\n * 校验绑定并应用最新凭据,异步刷新期间失效的房间不得恢复缓存。\n */\n private async applySessionRefresh(\n session: RealtimeSession,\n version: number,\n force: boolean\n ): Promise<void> {\n const current = this.activeSession;\n if (version !== this.refreshVersion || !current || current.id !== session.id) {\n return;\n }\n\n const next = session.connection;\n if (!next) {\n return;\n }\n\n const previous = current.connection;\n const bindingChanged =\n previous !== undefined &&\n (next.provider !== previous.provider ||\n next.roomID !== previous.roomID ||\n connectionAppID(next) !== connectionAppID(previous) ||\n next.botID !== previous.botID ||\n next.userID !== previous.userID);\n if (bindingChanged) {\n this.dependencies.onHeartbeatFailure(\n session.id,\n new XmaxError(XmaxErrorCode.sessionError, \"RTC session binding changed during heartbeat\"),\n );\n return;\n }\n\n if (next.provider !== RtcProvider.trtc && (force || !previous || previous.provider === RtcProvider.trtc || next.roomToken !== previous.roomToken)) {\n await this.dependencies.streamController.updateCredentials(next, this.credentialController?.signal);\n if (version !== this.refreshVersion || this.activeSession !== current)\n return;\n }\n\n this.activeSession = new RealtimeSession({\n id: session.id,\n userID: session.userID ?? current.userID,\n status: session.status ?? current.status,\n connection: next,\n closeReason: session.closeReason,\n });\n }\n\n /**\n * 尽力清理全部连接资源,即使退房失败也关闭服务端会话。\n */\n async disconnect(): Promise<string | undefined> {\n const sessionID = this.activeSession?.id;\n\n this.stopHeartbeat();\n this.clearRemoteMedia();\n\n try {\n await this.dependencies.streamController.disconnect();\n } catch (error) {\n XmaxLogger.realtime.error(() => `断开 RTC 连接失败 (Failed to Disconnect RTC)\\n└─ ${XmaxError.from(error).message}`);\n }\n\n const track = this.activeRemoteTrack;\n if (track) {\n this.dependencies.renderController.resetRemoteTrack();\n track.mediaStreamTrack = undefined;\n }\n\n this.activeSession = undefined;\n this.activeRemoteTrack = undefined;\n\n if (sessionID && this.dependencies.sessionService) {\n try {\n await this.dependencies.sessionService.closeSession(sessionID);\n } catch (error) {\n XmaxLogger.realtime.error(() => `关闭实时会话失败 (Failed to Close Realtime Session)\\n└─ ${XmaxError.from(error).message}`);\n }\n }\n\n return sessionID;\n }\n\n /**\n * 获取会话 Service;未配置 API 服务时抛出配置错误。\n */\n private requireSessionService(): RealtimeSessionServicing {\n if (!this.dependencies.sessionService) {\n throw new XmaxError(\n XmaxErrorCode.invalidConfiguration,\n \"The realtime manager is not configured with an API service\",\n );\n }\n return this.dependencies.sessionService;\n }\n}\n","//\n// XmaxRealtimeGenerationManager.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\nimport { XmaxError, XmaxErrorCode } from \"../../Foundation/Errors/XmaxError\";\nimport { XmaxLogger } from \"../../Foundation/Logging/XmaxLogger\";\nimport type { RealtimeContext } from \"../../Service/Realtime/RealtimeContext\";\nimport type { RealtimeExtraParams } from \"../../Service/Realtime/RealtimeExtraParams\";\nimport type { RealtimeVideoFormat } from \"../../Service/Realtime/RealtimeVideoFormat\";\nimport type { StreamControlling } from \"../../Stream/StreamControlling\";\nimport type { RoomEventTargetSize } from \"../../Stream/Room/RoomEvent\";\nimport { RealtimeCoordinator } from \"./RealtimeCoordinator\";\nimport type { InteractionControlling } from \"../../Media/Interaction/InteractionControlling\";\nimport type { RenderControlling } from \"../../Render/RenderControlling\";\n\n/**\n * 生成画布、媒体基准和本次生成条件。\n */\ninterface GenerationOptions {\n\n /**\n * 固定本地媒体规格,用于交互坐标。\n */\n videoFormat: RealtimeVideoFormat;\n\n /**\n * 模型生成画布;缺省时与本地媒体规格一致。\n */\n generationVideoFormat?: RealtimeVideoFormat;\n\n /**\n * 是否由服务端按包含模式适配生成画布。\n */\n paddingContain?: boolean;\n\n /**\n * 生成条件,缺省复用最近缓存的上下文。\n */\n context?: RealtimeContext;\n\n /**\n * 服务端生成后的回传尺寸,缺省时不发送。\n */\n targetSize?: RoomEventTargetSize;\n\n /**\n * 是否回贴原始人脸,默认 false。\n */\n faceRestoration?: boolean;\n\n /**\n * 合并到本次生成信令的 params,不缓存。\n */\n extraParams?: RealtimeExtraParams;\n}\n\n/**\n * 内部生成管理器:拥有生成任务与上下文,管理信令、确认和取消;不持有会话或公开状态。\n */\nexport class XmaxRealtimeGenerationManager {\n\n /**\n * 当前生成条件与任务标识\n */\n private currentContext?: RealtimeContext;\n\n /**\n * 当前生成任务标识。\n */\n private activeTaskID?: string;\n\n /**\n * 并发停止操作的共享结果\n */\n private pendingStop?: {\n taskID: string;\n completion: Promise<void>\n };\n\n /**\n * 注入房间传输组件和任务标识生成器,不创建会话或启动生成。\n */\n constructor(\n private readonly streamController: StreamControlling,\n private readonly interactionController: InteractionControlling,\n private readonly renderController: RenderControlling,\n private readonly taskIDGenerator: () => string = XmaxRealtimeGenerationManager.createTaskID,\n ) { }\n\n /**\n * 首次请求在建立连接前校验条件;后续请求可复用缓存。\n */\n validateContext(\n context?: RealtimeContext\n ): RealtimeContext {\n const resolved = context ?? this.currentContext;\n if (!resolved) {\n throw new XmaxError(XmaxErrorCode.invalidConfiguration, \"A realtime context is required for the first generation\");\n }\n return resolved;\n }\n\n /**\n * 发送生成指令并等待确认、远端就绪和音频激活,成功后返回任务标识。\n * 条件上下文在生成确认后缓存;启动失败时尽力停止本次任务并保留原始错误。\n * @param options.videoFormat 固定本地媒体规格,用于交互坐标。\n * @param options.generationVideoFormat 模型生成画布,缺省沿用媒体规格。\n * @param options.paddingContain 是否由服务端按包含模式适配画布。\n * @param options.signal 取消本次生成启动的信号。\n * @param options.ensureCurrent 校验当前操作仍有效。\n * @param options.waitUntilRemoteReady 等待远端生成流就绪。\n * @param options.context 生成条件,缺省复用最近缓存的上下文。\n * @param options.restart 是否替换当前任务并重新发送 start,默认 false;不发送 stop。\n * @param options.targetSize 服务端生成后的回传尺寸,缺省时不发送。\n * @param options.mirror 是否由后端水平翻转输入,默认 false;仅用于 start 信令。\n * @param options.faceRestoration 是否回贴原始人脸,默认 false。\n * @param options.onStartSent start 信令发送成功后的可选异步回调。\n * @param options.extraParams 合并到本次 start 的 params,不缓存。\n */\n async start(\n options: {\n videoFormat: RealtimeVideoFormat;\n generationVideoFormat?: RealtimeVideoFormat;\n paddingContain?: boolean;\n signal: AbortSignal;\n ensureCurrent: () => void;\n waitUntilRemoteReady: () => Promise<void>;\n context?: RealtimeContext;\n restart?: boolean;\n targetSize?: RoomEventTargetSize;\n mirror?: boolean;\n faceRestoration?: boolean;\n onStartSent?: () => Promise<void>;\n extraParams?: RealtimeExtraParams;\n }\n ): Promise<string> {\n const context = this.validateContext(options.context);\n options.ensureCurrent();\n\n const taskID = this.taskIDGenerator();\n if (options.restart) {\n this.interactionController.stopInteraction();\n this.renderController.setInteractionEnabled(false);\n }\n this.activeTaskID = taskID;\n\n try {\n const confirmation = this.streamController.beginGeneration({\n taskID,\n videoFormat: options.generationVideoFormat ?? options.videoFormat,\n paddingContain: options.paddingContain,\n context,\n restart: options.restart,\n ...(options.targetSize && { targetSize: options.targetSize }),\n mirror: options.mirror ?? false,\n faceRestoration: options.faceRestoration ?? false,\n ...(options.onStartSent && { onStartSent: options.onStartSent }),\n extraParams: options.extraParams,\n });\n await this.awaitConfirmation(confirmation, options.signal);\n options.ensureCurrent();\n this.currentContext = context;\n\n // 静态触控任务确认后即可交互,让轨迹驱动首帧。\n if (options.extraParams?.static_generate !== true) {\n await options.waitUntilRemoteReady();\n }\n options.ensureCurrent();\n\n await this.streamController.activateRemoteAudio();\n options.ensureCurrent();\n\n this.interactionController.startInteraction(taskID, options.videoFormat);\n this.renderController.setInteractionEnabled(true, options.videoFormat);\n return taskID;\n } catch (error) {\n await this.stop(taskID);\n throw error;\n }\n }\n\n /**\n * 更新活动任务的生成条件;信令发送成功后才覆盖缓存,无任务或上下文时抛错。\n * @param taskID 当前活动任务标识。\n * @param options.videoFormat 固定本地媒体规格,用于交互坐标。\n * @param options.generationVideoFormat 模型生成画布,缺省沿用媒体规格。\n * @param options.paddingContain 是否由服务端按包含模式适配画布。\n * @param options.context 生成条件,缺省复用最近缓存的上下文。\n * @param options.targetSize 服务端生成后的回传尺寸,缺省时不发送。\n * @param options.faceRestoration 是否回贴原始人脸,默认 false。\n * @param options.extraParams 合并到本次 change_condition 的 params,不缓存。\n */\n async update(\n taskID: string | undefined,\n options: GenerationOptions\n ): Promise<void> {\n const context = options.context ?? this.currentContext;\n if (!taskID || !context) {\n throw new XmaxError(XmaxErrorCode.invalidConfiguration, \"A realtime context is required to update the current generation\");\n }\n\n await this.streamController.updateGeneration({\n taskID,\n videoFormat: options.generationVideoFormat ?? options.videoFormat,\n paddingContain: options.paddingContain,\n context,\n ...(options.targetSize && { targetSize: options.targetSize }),\n faceRestoration: options.faceRestoration ?? false,\n extraParams: options.extraParams,\n });\n if (this.activeTaskID !== taskID)\n throw RealtimeCoordinator.cancelledError();\n this.currentContext = context;\n this.interactionController.startInteraction(taskID, options.videoFormat);\n this.renderController.setInteractionEnabled(true, options.videoFormat);\n }\n\n /**\n * 幂等停止指定活动任务;并发调用共享结果,停止失败仅记录日志。\n */\n async stop(\n taskID = this.activeTaskID\n ): Promise<void> {\n // 取消与统一清理可能同时到达:共享停止结果,不重复发送,也不提前退房。\n if (this.pendingStop && (!taskID || this.pendingStop.taskID === taskID)) {\n return this.pendingStop.completion;\n }\n if (!taskID || this.activeTaskID !== taskID)\n return;\n\n this.activeTaskID = undefined;\n this.interactionController.stopInteraction();\n this.renderController.setInteractionEnabled(false);\n const completion = (async () => {\n try {\n await this.streamController.stopGeneration(taskID);\n } catch (error) {\n XmaxLogger.realtime.error(() => `停止生成任务失败 (Failed to Stop Generation Task)\\n└─ ${XmaxError.from(error).message}`);\n }\n })();\n this.pendingStop = { taskID, completion };\n\n try {\n await completion;\n } finally {\n if (this.pendingStop?.completion === completion)\n this.pendingStop = undefined;\n }\n }\n\n /**\n * 清空缓存的生成条件并等待活动任务停止,供断连和关闭流程使用。\n */\n async reset(\n taskID?: string\n ): Promise<void> {\n this.currentContext = undefined;\n this.interactionController.stopInteraction();\n this.renderController.setInteractionEnabled(false);\n // Coordinator 进入 disconnecting 后可能不再携带 taskID,仍须停止本类持有的任务。\n await this.stop(this.activeTaskID ?? (taskID || undefined));\n }\n\n /**\n * 等待生成确认;取消时立即拒绝,并在所有结束路径移除取消监听。\n */\n private async awaitConfirmation(\n confirmation: Promise<void>,\n signal: AbortSignal\n ): Promise<void> {\n await new Promise<void>((resolve, reject) => {\n const cleanup = () => signal.removeEventListener(\"abort\", abort);\n const abort = () => {\n cleanup();\n reject(RealtimeCoordinator.cancelledError());\n };\n\n confirmation.then(\n () => {\n cleanup();\n resolve();\n },\n (error) => {\n cleanup();\n reject(error);\n },\n );\n\n signal.addEventListener(\"abort\", abort, { once: true });\n if (signal.aborted)\n abort();\n });\n }\n\n /**\n * 生成当前生成任务的唯一标识。\n */\n private static createTaskID(): string {\n const hex = crypto.randomUUID().replace(/-/g, \"\");\n let binary = \"\";\n for (let index = 0; index < 16; index += 1) {\n binary += String.fromCharCode(parseInt(hex.slice(index * 2, index * 2 + 2), 16));\n }\n\n const base64url = btoa(binary)\n .replace(/\\+/g, \"-\")\n .replace(/\\//g, \"_\")\n .replace(/=+$/, \"\");\n\n return `task-${base64url}?os=web`;\n }\n}\n","//\n// XmaxRealtimeManager.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\nimport { XmaxError, XmaxErrorCode } from \"../../Foundation/Errors/XmaxError\";\nimport { XmaxLogger } from \"../../Foundation/Logging/XmaxLogger\";\nimport { CameraPosition } from \"../../Foundation/Media/Camera/CameraPosition\";\nimport { alignedVideoSize } from \"../../Foundation/Media/Video/VideoSize\";\nimport { watchMediaPermissionPrompt } from \"../../Foundation/Permissions/MediaPermissionWatch\";\nimport { createRtcManager } from \"../../Foundation/RTC/RtcFactory\";\nimport { XmaxEnvironment } from \"../../Foundation/Runtime/XmaxEnvironment\";\nimport type { RtcManaging } from \"../../Foundation/RTC/RtcManaging\";\nimport { RtcProvider } from \"../../Foundation/RTC/RtcProvider\";\nimport { snapshotExtraParams, type RealtimeExtraParams } from \"../../Service/Realtime/RealtimeExtraParams\";\nimport type { RealtimeNetworkQuality, RealtimeNetworkQualityListener } from \"../../Foundation/RTC/NetworkStatistics\";\nimport type { DownlinkVideoStatistics, DownlinkVideoStatisticsListener, UplinkVideoStatistics, UplinkVideoStatisticsListener } from \"../../Foundation/RTC/VideoStatistics\";\nimport { MediaController } from \"../../Media/MediaController\";\nimport { RemoteMediaFrameController } from \"../../Render/Video/RemoteMediaFrameController\";\nimport type { RealtimeMediaFrameListener } from \"../../Service/Realtime/RealtimeMediaFrame\";\nimport type { RemoteMediaTracks } from \"../../Foundation/Media/RemoteMediaTracks\";\nimport type { MediaControlling } from \"../../Media/MediaControlling\";\nimport { RenderController } from \"../../Render/RenderController\";\nimport type { RenderControlling } from \"../../Render/RenderControlling\";\nimport type { CameraControlling } from \"../../Media/Camera/CameraControlling\";\nimport { resolutionBuckets, supportedVideoFormats, type ModelSize } from \"../../Service/Realtime/RealtimeModel\";\nimport { MediaService } from \"../../Service/Media/MediaService\";\nimport type { ApiServicing } from \"../../Service/Network/ApiServicing\";\nimport { RealtimeContext } from \"../../Service/Realtime/RealtimeContext\";\nimport { RealtimeMediaStream } from \"../../Service/Realtime/RealtimeMediaStream\";\nimport type { RealtimeSessionServicing } from \"../../Service/Realtime/RealtimeSessionServicing\";\nimport { RealtimeSessionService } from \"../../Service/Realtime/RealtimeSessionService\";\nimport {\n RealtimeConnectionState,\n RealtimeState,\n type RealtimeStateListener,\n} from \"../../Service/Realtime/RealtimeState\";\nimport { RealtimeVideoTrack } from \"../../Service/Realtime/RealtimeVideoTrack\";\nimport type { RealtimeVideoFormat } from \"../../Service/Realtime/RealtimeVideoFormat\";\nimport { StreamController } from \"../../Stream/StreamController\";\nimport { QualityController } from \"../../Stream/Quality/QualityController\";\nimport { qualityVideoFormat, qualityVideoMaximumSize, supportsQualityEncoding } from \"../../Stream/Quality/QualityVideoFormat\";\nimport { QUALITY_TIERS } from \"../../Stream/Quality/QualityTiers\";\nimport { QualityHistory } from \"../../Stream/Quality/QualityHistory\";\nimport type { StreamControlling } from \"../../Stream/StreamControlling\";\nimport type { RealtimeConfiguration } from \"./RealtimeConfiguration\";\nimport {\n RealtimeCoordinator,\n RealtimeOperationKind,\n RealtimeTerminationScope,\n type RealtimeCleanupResult,\n type RealtimeOperationToken,\n} from \"./RealtimeCoordinator\";\nimport { RealtimeErrorHandler } from \"./RealtimeErrorHandler\";\nimport { RealtimeTiming } from \"./RealtimeTiming\";\nimport type { RealtimeTimingListener } from \"../../Service/Realtime/RealtimeTiming\";\nimport type { XmaxRealtimeManaging } from \"./XmaxRealtimeManaging\";\nimport { RealtimeCameraPreset, cameraPresetSize } from \"./RealtimeCameraPreset\";\nimport { RealtimeQualityLevel } from \"./RealtimeQualityLevel\";\nimport { XmaxRealtimeConnectionManager } from \"./XmaxRealtimeConnectionManager\";\nimport { XmaxRealtimeGenerationManager } from \"./XmaxRealtimeGenerationManager\";\n\n/**\n * 等待远端生成流首帧的时限(毫秒);超时仅记录日志,不影响生成流程。\n */\nconst REMOTE_FIRST_FRAME_TIMEOUT_MS = 5_000;\n\n/**\n * 保存原始 L1 基准与当前目标档位;自动调档不改写基准。\n */\ninterface QualityState {\n\n /**\n * 计算档位尺寸和帧率的固定基准。\n */\n readonly baseline: RealtimeVideoFormat;\n\n /**\n * 当前生效档位,取值为 1–5。\n */\n level: number;\n}\n\n/**\n * 下行状态绑定生成任务;尺寸是已发送目标,不代表实际接收规格。\n */\ninterface DownlinkQualityState extends QualityState {\n\n /**\n * 当前生成任务标识。\n */\n readonly taskID: string;\n\n /**\n * 当前视频画面的像素尺寸。\n */\n size: ModelSize;\n}\n\n/**\n * 实时能力门面:公开 API、本地媒体与连接/生成流程编排。\n *\n * 相机采集与房间传输共享同一个 RTC 引擎;会话由 Service 层创建并通过\n * 心跳维持。ConnectionManager 持有连接资源,GenerationManager 持有\n * 生成任务和上下文,Coordinator 统一管理公开状态、操作租约与取消。\n */\nexport class XmaxRealtimeManager implements XmaxRealtimeManaging {\n\n /**\n * 业务配置\n */\n readonly options: RealtimeConfiguration;\n\n /**\n * 媒体尺寸规则与平台能力查询服务。\n */\n private readonly mediaService: MediaService;\n\n /**\n * 按需观察原始远端音视频,不持有录制或文件封装能力。\n */\n private readonly remoteMediaFrames = new RemoteMediaFrameController();\n\n /** 独立于 WebCodecs 的原始轨道观察者。 */\n private remoteMediaTracksListener?: (tracks?: RemoteMediaTracks) => void;\n private remoteMediaFrameObservation = false;\n\n /**\n * 当前 RTC 平台能否独立于播放音量读取音频。\n */\n private readonly canReadRemoteAudio: boolean;\n\n /**\n * 本地流创建时解析的固定媒体基准;不被上行调档、生成画布或回传尺寸覆盖。\n */\n private resolvedMediaVideoFormat?: RealtimeVideoFormat;\n\n /**\n * 业务组件\n */\n private readonly coordinator: RealtimeCoordinator;\n\n /**\n * 处理实时业务错误并决定资源终止范围。\n */\n private readonly errorHandler: RealtimeErrorHandler;\n\n /**\n * 当前媒体源及交互采样的管理组件。\n */\n private readonly mediaController: MediaControlling;\n\n /**\n * 远端轨道、预热和交互视图的绑定组件。\n */\n private readonly renderController: RenderControlling;\n\n /**\n * 启动计时与首帧通知\n */\n private readonly timing = new RealtimeTiming();\n\n /**\n * 远端首帧提交显示时结束首帧计时。\n */\n private remoteFrameDisplayHandler?: () => void;\n\n /**\n * 远端预热结束时记录耗时并更新展示状态。\n */\n private remoteRenderWarmupCompletedHandler?: () => void;\n\n /**\n * 媒体权限弹窗观察\n */\n private permissionWatchDispose?: () => void;\n\n /**\n * 本地、远端和网络统计快照及观察者\n */\n private localVideoStatistics?: UplinkVideoStatistics;\n\n /**\n * 本地视频统计的业务监听器。\n */\n private localVideoStatisticsListener?: UplinkVideoStatisticsListener;\n\n /**\n * 当前生成结果流的视频统计。\n */\n private remoteVideoStatistics?: DownlinkVideoStatistics;\n\n /**\n * 远端视频统计的业务监听器。\n */\n private remoteVideoStatisticsListener?: DownlinkVideoStatisticsListener;\n\n /**\n * 是否已发布媒体并开始接收有效视频统计。\n */\n private acceptsVideoStatistics = false;\n\n /**\n * 最新的上下行网络质量统计。\n */\n private networkStatistics?: RealtimeNetworkQuality;\n\n /**\n * 网络质量统计的业务监听器。\n */\n private networkStatisticsListener?: RealtimeNetworkQualityListener;\n\n /**\n * 实时业务管理组件;状态所有权分别归连接和生成管理器。\n */\n private readonly connectionManager: XmaxRealtimeConnectionManager;\n\n /**\n * 生成信令、任务确认和上下文的管理组件。\n */\n private readonly generationManager: XmaxRealtimeGenerationManager;\n\n /**\n * 房间传输、发布订阅和生成信令的管理组件。\n */\n private readonly streamController: StreamControlling;\n\n /**\n * 上行网络质量调档状态机。\n */\n private readonly qualityController?: QualityController;\n\n /**\n * 上次成功档位的本地历史记录。\n */\n private readonly qualityHistory?: QualityHistory;\n\n /**\n * 各本地视频轨的调档基准与当前档位。\n */\n private readonly qualityStates = new WeakMap<RealtimeVideoTrack, QualityState>();\n\n /**\n * 下行网络质量调档状态机。\n */\n private readonly downlinkQualityController?: QualityController;\n\n /**\n * 无服务端生效 ACK;level/size 表示最后成功发送的目标,而非实际接收规格。\n */\n private downlinkQualityState?: DownlinkQualityState;\n\n /**\n * 等待处理的最新网络质量样本。\n */\n private adaptiveQualityPending = false;\n\n /**\n * 自动调档操作版本,阻止旧任务结果生效。\n */\n private adaptiveQualityRevision = 0;\n\n /**\n * 音量配置\n */\n private storedLocalAudioVolume = 1;\n\n /**\n * 当前远端播放音量,范围为 0–100。\n */\n private storedRemoteAudioVolume = 0;\n\n /**\n * 创建实时能力 Manager。\n *\n * @param options 实时生成模型等业务配置。\n * @param dependencies 可替换的内部组件(测试用);未注入时按生产配置创建,\n * 相机控制器与传输层共享同一个 RTC 引擎。\n */\n constructor(\n options: RealtimeConfiguration,\n dependencies?: {\n environment?: XmaxEnvironment;\n apiService?: ApiServicing;\n sessionService?: RealtimeSessionServicing;\n streamController?: StreamControlling;\n cameraController?: CameraControlling;\n rtcManager?: RtcManaging;\n }\n ) {\n this.options = options;\n this.mediaService = new MediaService(options.model);\n\n this.errorHandler = new RealtimeErrorHandler();\n\n const rtcManager = dependencies?.rtcManager ?? createRtcManager(options.provider, dependencies?.environment ?? XmaxEnvironment.china);\n this.canReadRemoteAudio = options.provider !== RtcProvider.trtc || rtcManager.supportsRemoteAudioVolumeControl;\n\n const sessionService =\n dependencies?.sessionService ??\n (dependencies?.apiService\n ? new RealtimeSessionService({ apiService: dependencies.apiService, provider: options.provider })\n : undefined);\n this.streamController = dependencies?.streamController ?? new StreamController({\n rtcManager,\n onCredentialsRequired: () => {\n void this.connectionManager.refreshCredentials();\n },\n errorListener: (error) => {\n void this.errorHandler.report(error);\n },\n remoteStreamListener: (binding) => {\n this.clearRemoteVideoStatistics();\n this.connectionManager.handleRemoteStreamBinding(binding);\n },\n });\n\n this.mediaController = new MediaController({\n rtcManager,\n mediaService: this.mediaService,\n cameraController: dependencies?.cameraController,\n onCameraError: error => {\n void this.errorHandler.report(error);\n },\n onVideoError: error => {\n void this.coordinator.terminateWithError(error, RealtimeTerminationScope.connection);\n },\n onExternalError: error => {\n void this.coordinator.terminateWithError(error, RealtimeTerminationScope.all);\n },\n onImageError: error => {\n void this.coordinator.terminateWithError(error, RealtimeTerminationScope.all);\n },\n interactionListener: (taskID, points) => this.streamController.sendTracks(taskID, points),\n });\n this.renderController = new RenderController({\n onFrameDisplayed: () => this.remoteFrameDisplayHandler?.(),\n onRenderWarmupCompleted: () => {\n this.remoteRenderWarmupCompletedHandler?.();\n void this.completeGenerationPresentation().catch(error => this.errorHandler.report(error));\n },\n });\n this.connectionManager = new XmaxRealtimeConnectionManager({\n provider: options.provider,\n sessionService,\n streamController: this.streamController,\n timing: this.timing,\n remoteAudioVolume: () => this.storedRemoteAudioVolume,\n onHeartbeatFailure: (sessionID, error) => {\n void this.handleHeartbeatFailure(sessionID, error);\n },\n renderController: this.renderController,\n onInteraction: frame => this.mediaController.submitInteraction(frame),\n });\n this.generationManager = new XmaxRealtimeGenerationManager(this.streamController, this.mediaController, this.renderController);\n\n this.coordinator = new RealtimeCoordinator({\n errorHandler: this.errorHandler,\n cleanup: (scope, taskID) => this.performCleanup(scope, taskID),\n });\n\n if (options.isAdaptiveQualityEnabled &&\n (options.provider === RtcProvider.agora || options.provider === RtcProvider.trtc)) {\n this.qualityHistory = new QualityHistory(options.model, options.provider);\n this.qualityController = new QualityController(async (change, signal) => {\n await this.coordinator.run(RealtimeOperationKind.configuration, undefined, async token => {\n await this.applyUplinkQuality(change.to, token, signal);\n });\n }, undefined, {}, level => {\n const track = this.currentLocalTrack;\n const state = track && this.qualityStates.get(track);\n return !!state && supportsQualityEncoding(this.uplinkQualityFormat(state.baseline, level));\n });\n this.downlinkQualityController = new QualityController(async (change, signal) => {\n await this.coordinator.run(RealtimeOperationKind.configuration, undefined, async token => {\n await this.applyDownlinkQuality(change.to, token, signal);\n });\n });\n }\n\n this.streamController.setUplinkVideoStatisticsListener((statistics) => {\n if (this.acceptsVideoStatistics) {\n this.localVideoStatistics = statistics ? Object.freeze({ ...statistics }) : undefined;\n this.notifyLocalVideoStatistics();\n }\n });\n\n this.streamController.setNetworkQualityListener((statistics) => {\n if (this.acceptsVideoStatistics) {\n this.networkStatistics = statistics ? Object.freeze({ ...statistics }) : undefined;\n this.handleAdaptiveQuality(statistics);\n this.notifyNetworkStatistics();\n }\n });\n\n this.streamController.setDownlinkVideoStatisticsListener((statistics) => {\n if (this.acceptsVideoStatistics) {\n this.remoteVideoStatistics = statistics ? Object.freeze({ ...statistics }) : undefined;\n this.notifyRemoteVideoStatistics();\n }\n });\n }\n\n /**\n * 当前实时连接与生成状态。\n */\n get currentState(): RealtimeState {\n return this.coordinator.currentState;\n }\n\n /**\n * 当前摄像头的固定媒体规格,不读取调档后的上行编码尺寸。\n */\n get currentCameraVideoFormat(): RealtimeVideoFormat | undefined {\n return this.mediaController.source === \"camera\"\n ? this.resolvedMediaVideoFormat ?? this.currentLocalTrack?.videoFormat\n : undefined;\n }\n\n /**\n * 按摄像头媒体宽高匹配预设,不受编码帧率和档位影响。\n */\n get currentCameraPreset(): RealtimeCameraPreset | undefined {\n const format = this.currentCameraVideoFormat;\n return format && Object.values(RealtimeCameraPreset).find(preset => {\n const size = cameraPresetSize(preset);\n return size.width === format.width && size.height === format.height;\n });\n }\n\n /**\n * 当前已连接会话成功应用的上行档位。\n */\n get uplinkQualityLevel(): RealtimeQualityLevel | undefined {\n const track = this.currentLocalTrack;\n const state = this.currentState.connectionState;\n return (state === RealtimeConnectionState.connected || state === RealtimeConnectionState.starting || state === RealtimeConnectionState.generating) && track\n ? this.qualityStates.get(track)?.level as RealtimeQualityLevel | undefined\n : undefined;\n }\n\n /**\n * 当前已连接会话成功提交的下行档位。\n */\n get downlinkQualityLevel(): RealtimeQualityLevel | undefined {\n const state = this.currentState.connectionState;\n return state === RealtimeConnectionState.connected || state === RealtimeConnectionState.starting || state === RealtimeConnectionState.generating\n ? this.downlinkQualityState?.level as RealtimeQualityLevel | undefined\n : undefined;\n }\n\n /**\n * 在媒体操作内更新未连接的摄像头规格,成功后重建调档基准。\n */\n async updateCameraFormat(\n format: RealtimeVideoFormat | RealtimeCameraPreset\n ): Promise<void> {\n await this.coordinator.run(RealtimeOperationKind.media, undefined, async token => {\n const current = this.currentCameraVideoFormat;\n if (this.currentState.connectionState !== RealtimeConnectionState.ready ||\n this.connectionManager.currentSessionID || !current) {\n throw new XmaxError(XmaxErrorCode.invalidConfiguration, \"Camera preview must be ready and disconnected before updating its format\");\n }\n\n const size = typeof format === \"string\" ? cameraPresetSize(format) : undefined;\n const requested = size ? current.resized(size.width, size.height) : format as RealtimeVideoFormat;\n if (!requested || typeof requested.validate !== \"function\") {\n throw new XmaxError(XmaxErrorCode.invalidConfiguration, \"A camera format or preset is required\");\n }\n token.ensureCurrent();\n await this.mediaController.updateCameraFormat(requested);\n token.ensureCurrent();\n const track = this.currentLocalTrack!;\n this.resolvedMediaVideoFormat = track.videoFormat;\n this.qualityStates.delete(track);\n });\n }\n\n /**\n * 指定上行档位,成功后自动调档继续从新档位判断。\n */\n setUplinkQualityLevel(\n level: RealtimeQualityLevel\n ): Promise<void> {\n return this.updateQualityLevel(level, false, true);\n }\n\n /**\n * 指定下行档位,沿用尺寸信令更新流程。\n */\n setDownlinkQualityLevel(\n level: RealtimeQualityLevel\n ): Promise<void> {\n return this.updateQualityLevel(level, true, true);\n }\n\n /**\n * 手动调整上行档位,保留原始基准,自动调档从新档位继续。\n * @param direction 升一档或降一档;到达 L1/L5 边界时不重复应用。\n */\n async adjustUplinkQuality(\n direction: \"upgrade\" | \"downgrade\"\n ): Promise<void> {\n return this.updateQualityLevel(direction, false);\n }\n\n /**\n * 应用上行档位参数,成功后更新轨道信息与历史档位。\n */\n private async applyUplinkQuality(\n level: number,\n token: RealtimeOperationToken,\n signal?: AbortSignal\n ): Promise<void> {\n const track = this.currentLocalTrack;\n const state = track && this.qualityStates.get(track);\n const ensureActive = () => {\n token.ensureCurrent();\n if (signal?.aborted || !track || !state || track !== this.currentLocalTrack ||\n state !== this.qualityStates.get(track) || this.currentState.connectionState !== RealtimeConnectionState.generating) {\n throw new XmaxError(XmaxErrorCode.cancelled, \"Adaptive video format update was cancelled\");\n }\n };\n\n ensureActive();\n if (!track || !state)\n return;\n\n const target = this.uplinkQualityFormat(state.baseline, level);\n let format: RealtimeVideoFormat;\n if (this.options.provider === RtcProvider.trtc) {\n // TRTC 调档只替换帧率,保留最近应用的尺寸、码率和编码偏好。\n format = await this.streamController.updateUplinkFrameRate(target.fps);\n } else {\n await this.streamController.updateUplinkVideoFormat(target);\n format = target;\n }\n ensureActive();\n\n // 上行调档不改变生成和下行尺寸,也不覆盖原始 L1 基准。\n this.resolvedMediaVideoFormat ??= state.baseline;\n track.updateVideoFormat(format);\n state.level = level;\n this.qualityHistory?.write(\"uplink\", level);\n }\n\n /**\n * 手动调试与自动调档使用同一尺寸信令路径,不修改生成或上行配置。\n */\n async adjustDownlinkQuality(\n direction: \"upgrade\" | \"downgrade\"\n ): Promise<void> {\n return this.updateQualityLevel(direction, true);\n }\n\n /**\n * 指定档位和逐级调档共用操作租约;失败时保留最近成功的档位。\n */\n private async updateQualityLevel(\n requested: RealtimeQualityLevel | \"upgrade\" | \"downgrade\",\n downlink: boolean,\n absolute = false\n ): Promise<void> {\n await this.coordinator.run(RealtimeOperationKind.configuration, undefined, async token => {\n const track = this.currentLocalTrack;\n const state = downlink ? this.downlinkQualityState : track && this.qualityStates.get(track);\n if (!state || this.currentState.connectionState !== RealtimeConnectionState.generating) {\n throw new XmaxError(XmaxErrorCode.invalidConfiguration, \"Quality adjustment requires an active generation\");\n }\n\n let level = NaN;\n if (absolute) {\n if (typeof requested === \"number\") {\n level = requested;\n }\n } else if (requested === \"upgrade\" || requested === \"downgrade\") {\n const step = requested === \"upgrade\" ? -1 : 1;\n level = Math.max(1, Math.min(Object.keys(QUALITY_TIERS).length, state.level + step));\n }\n if (!Number.isInteger(level) || level < 1 || level > Object.keys(QUALITY_TIERS).length) {\n throw new XmaxError(XmaxErrorCode.invalidConfiguration, \"Unsupported quality level or direction\");\n }\n if (level === state.level) {\n return;\n }\n if (!downlink && !supportsQualityEncoding(this.uplinkQualityFormat(state.baseline, level))) {\n throw new XmaxError(XmaxErrorCode.invalidConfiguration, \"Quality level conflicts with explicit bitrate constraints\");\n }\n\n this.adaptiveQualityRevision++;\n const controller = downlink ? this.downlinkQualityController : this.qualityController;\n controller?.resetSamples();\n if (downlink) {\n await this.applyDownlinkQuality(level, token);\n } else {\n await this.applyUplinkQuality(level, token);\n }\n controller?.start(level);\n });\n }\n\n /**\n * 发送下行目标尺寸,成功后更新当前档位。\n */\n private async applyDownlinkQuality(\n level: number,\n token: RealtimeOperationToken,\n signal?: AbortSignal\n ): Promise<void> {\n const state = this.downlinkQualityState;\n const ensureActive = () => {\n token.ensureCurrent();\n if (signal?.aborted || !state || state !== this.downlinkQualityState ||\n this.currentState.connectionState !== RealtimeConnectionState.generating || this.currentState.taskID !== state.taskID) {\n throw new XmaxError(XmaxErrorCode.cancelled, \"Target size update was cancelled\");\n }\n };\n ensureActive();\n if (!state)\n return;\n const size = this.resolveTargetSize(state.baseline, level);\n await this.streamController.updateDownlinkVideoResolution(state.taskID, size, ensureActive);\n ensureActive();\n state.level = level;\n state.size = size;\n this.qualityHistory?.write(\"downlink\", level);\n }\n\n /**\n * 当前本地媒体预览音量,取值范围为 `0...1`。\n */\n get localAudioVolume(): number {\n return this.storedLocalAudioVolume;\n }\n\n /**\n * 当前远端生成音频播放音量,取值范围为 `0...1`。\n */\n get remoteAudioVolume(): number {\n return this.storedRemoteAudioVolume;\n }\n\n /**\n * 设置实时状态监听器;设置后立即回放当前状态。\n *\n * @param listener 实时状态回调;传入 `undefined` 时清除监听器。\n */\n async setStateListener(\n listener?: RealtimeStateListener\n ): Promise<void> {\n await this.coordinator.setStateListener(listener);\n }\n\n /**\n * 监听启动耗时;设置后立即回放,传 undefined 取消监听。\n */\n async setTimingListener(\n listener?: RealtimeTimingListener\n ): Promise<void> {\n this.timing.setListener(listener);\n }\n\n /**\n * 监听本地主视频流的实际运行统计;立即回放最新快照。\n */\n async setUplinkVideoStatisticsListener(\n listener?: UplinkVideoStatisticsListener\n ): Promise<void> {\n this.localVideoStatisticsListener = listener;\n this.notifyLocalVideoStatistics();\n }\n\n /**\n * 监听当前生成结果流的实际运行统计;立即回放最新快照。\n */\n async setDownlinkVideoStatisticsListener(\n listener?: DownlinkVideoStatisticsListener\n ): Promise<void> {\n this.remoteVideoStatisticsListener = listener;\n this.notifyRemoteVideoStatistics();\n }\n\n /**\n * 回放当前远端视频统计,隔离观察者的同步和异步异常。\n */\n private notifyRemoteVideoStatistics(): void {\n try {\n void Promise.resolve(this.remoteVideoStatisticsListener?.(this.remoteVideoStatistics)).catch(() => { });\n } catch {\n // 统计观察者不得打断生成或资源清理。\n }\n }\n\n /**\n * 网络统计立即回放最新快照,无数据或断开后为 undefined。\n */\n async setNetworkQualityListener(\n listener?: RealtimeNetworkQualityListener\n ): Promise<void> {\n this.networkStatisticsListener = listener;\n this.notifyNetworkStatistics();\n }\n\n /**\n * 回放当前网络统计,隔离观察者的同步和异步异常。\n */\n private notifyNetworkStatistics(): void {\n try {\n void Promise.resolve(this.networkStatisticsListener?.(this.networkStatistics)).catch(() => { });\n } catch {\n // 统计观察者不得打断生成或资源清理。\n }\n }\n\n /**\n * 清除远端视频统计;原先存在数据时通知观察者数据已失效。\n */\n private clearRemoteVideoStatistics(): void {\n const hadStatistics = this.remoteVideoStatistics !== undefined;\n this.remoteVideoStatistics = undefined;\n if (hadStatistics) {\n this.notifyRemoteVideoStatistics();\n }\n }\n\n /**\n * 回放当前本地视频统计,隔离观察者的同步和异步异常。\n */\n private notifyLocalVideoStatistics(): void {\n try {\n void Promise.resolve(this.localVideoStatisticsListener?.(this.localVideoStatistics)).catch(() => { });\n } catch {\n // 统计观察者不得打断采集、生成或资源清理。\n }\n }\n\n /**\n * 停止接收媒体统计并清空各类快照,仅对曾有数据的统计发出清空通知。\n */\n private clearVideoStatistics(): void {\n this.qualityController?.stop();\n this.downlinkQualityController?.stop();\n this.downlinkQualityState = undefined;\n this.adaptiveQualityRevision++;\n this.acceptsVideoStatistics = false;\n\n const hadNetworkStatistics = this.networkStatistics !== undefined;\n this.networkStatistics = undefined;\n if (hadNetworkStatistics)\n this.notifyNetworkStatistics();\n\n this.clearRemoteVideoStatistics();\n\n const hadStatistics = this.localVideoStatistics !== undefined;\n this.localVideoStatistics = undefined;\n if (hadStatistics) {\n this.notifyLocalVideoStatistics();\n }\n }\n\n /**\n * 设置本地媒体预览音量。\n *\n * @param volume 本地预览音量,取值范围为 `0...1`。\n * @throws 音量超出有效范围时抛出错误。\n */\n async setLocalAudioVolume(\n volume: number\n ): Promise<void> {\n XmaxRealtimeManager.validateVolume(volume);\n this.storedLocalAudioVolume = volume;\n }\n\n /**\n * 设置远端生成音频播放音量。\n * 尚未连接或订阅远端流时保存配置,并在远端音频开始播放前应用。\n *\n * @param volume 远端播放音量,取值范围为 `0...1`。\n * @throws 音量超出有效范围或 RTC 音量配置失败时抛出错误。\n */\n async setRemoteAudioVolume(\n volume: number\n ): Promise<void> {\n XmaxRealtimeManager.validateVolume(volume);\n this.storedRemoteAudioVolume = volume;\n if (!this.connectionManager.currentSessionID) {\n return;\n }\n try {\n this.streamController.setRemoteAudioVolume(volume);\n } catch (error) {\n throw XmaxError.from(error);\n }\n }\n\n /**\n * 创建本地相机流并开始预览。\n *\n * 将返回的轨道绑定到预览视图;收到有效帧且视图已绑定后进入 `ready`。\n *\n * @param options.videoFormat 旋转后的目标规格,尺寸按模型规则解析。\n * @param options.preset 替代 videoFormat,使用模型支持的摄像头预设和推荐帧率;两者必须且只能提供一个。\n * @param options.position 前置或后置摄像头。\n * @param options.useMicrophone 是否采集并发布麦克风音频。\n * @param options.enableFrameValidation 是否在发布前等待相机预热(固定 200ms),默认 true;false 跳过等待。\n * @param options.rotation 输入帧顺时针旋转角度,默认 0;videoFormat 为旋转后的规格,目前仅 Agora 支持非零角度。\n * @returns 包含本地相机视频轨道的媒体流。\n * @throws 模型不支持相机输入、配置无效、权限或采集启动失败时抛出错误。\n */\n async createLocalCameraStream(\n options: Parameters<XmaxRealtimeManaging[\"createLocalCameraStream\"]>[0]\n ): Promise<RealtimeMediaStream> {\n return this.coordinator.run(\n RealtimeOperationKind.media,\n undefined,\n async (token) => {\n this.mediaController.ensureNoLocalSource();\n const { preset, videoFormat, ...captureOptions } = options;\n if ((preset === undefined) === (videoFormat === undefined)) {\n throw new XmaxError(XmaxErrorCode.invalidConfiguration, \"Provide either a camera preset or a video format, not both\");\n }\n\n let requestedFormat = videoFormat;\n if (preset !== undefined) {\n const size = cameraPresetSize(preset);\n requestedFormat = supportedVideoFormats(this.options.model).find(format =>\n format.width === size.width && format.height === size.height,\n );\n }\n if (!requestedFormat) {\n throw new XmaxError(XmaxErrorCode.invalidConfiguration, \"Model does not support the requested camera preset\");\n }\n const cameraOptions = { ...captureOptions, videoFormat: requestedFormat };\n\n await this.coordinator.commit(\n new RealtimeState({\n connectionState: RealtimeConnectionState.preparing,\n }),\n token,\n );\n token.ensureCurrent();\n\n const completeCamera = this.timing.startCamera();\n this.clearVideoStatistics();\n\n // 系统弹出授权请求时单独统计用户授权耗时;已授权或未弹窗不记录。\n // 麦克风可能在连接建立时才弹窗,观察器存活到相机流停止。\n this.disposePermissionWatch();\n const permissionWatch = await watchMediaPermissionPrompt(options?.useMicrophone ?? false);\n if (permissionWatch) {\n const completePermission = this.timing.startPermission();\n void permissionWatch.decided.then(() => completePermission()).catch(() => { });\n this.permissionWatchDispose = permissionWatch.dispose;\n }\n\n let stream: RealtimeMediaStream;\n try {\n token.ensureCurrent();\n stream = await this.mediaController.createLocalCameraStream(cameraOptions);\n } catch (error) {\n this.disposePermissionWatch();\n this.timing.cancel();\n throw error;\n }\n\n token.ensureCurrent();\n this.resolvedMediaVideoFormat = stream.videoTrack?.videoFormat;\n completeCamera();\n\n // 收到有效帧且预览视图绑定后进入 ready。\n this.mediaController.setCameraPreviewReadyHandler((isCurrent) => {\n void this.coordinator.localPreviewDidBecomeReady(isCurrent);\n });\n\n return stream;\n },\n );\n }\n\n /**\n * 创建文件音视频源并准备外部轨道;准备失败时统一释放本次媒体和 RTC 资源。\n * @param options.file 包含可解码视频的本地 File 或 Blob。\n * @param options.videoFormat 期望的视频规格;尺寸按宽高比匹配模型档位。\n * @param options.loop 是否循环播放,默认 true。\n * @param options.includeAudio 是否发送文件音频,默认 true。\n */\n async createLocalVideoStream(\n options: Parameters<XmaxRealtimeManaging[\"createLocalVideoStream\"]>[0]\n ): Promise<RealtimeMediaStream> {\n return this.coordinator.run(RealtimeOperationKind.media, undefined, async (token) => {\n this.mediaController.ensureNoLocalSource();\n this.mediaController.validateExternalMediaSupport(\"local\");\n token.setFailureScope(RealtimeTerminationScope.all);\n this.timing.start();\n this.clearVideoStatistics();\n\n // 不在浏览器播放解锁之前等待网络或 RTC 初始化,保留用户点击的激活状态。\n const stream = await this.mediaController.createLocalVideoStream(options, token.signal);\n token.ensureCurrent();\n await this.mediaController.prepareSource(() => token.ensureCurrent());\n\n this.resolvedMediaVideoFormat = stream.videoTrack?.videoFormat;\n await this.coordinator.commit(new RealtimeState({ connectionState: RealtimeConnectionState.ready }), token);\n return stream;\n });\n }\n\n /**\n * 创建本地图片静止视频源并准备 RTC,失败或取消时释放本次资源。\n */\n async createLocalImageStream(\n options: Parameters<XmaxRealtimeManaging[\"createLocalImageStream\"]>[0]\n ): Promise<RealtimeMediaStream> {\n return this.coordinator.run(RealtimeOperationKind.media, undefined, async token => {\n this.mediaController.ensureNoLocalSource();\n this.mediaController.validateExternalMediaSupport(\"image\");\n token.setFailureScope(RealtimeTerminationScope.all);\n this.timing.start();\n this.clearVideoStatistics();\n\n const stream = await this.mediaController.createLocalImageStream(options, token.signal);\n token.ensureCurrent();\n await this.mediaController.prepareSource(() => token.ensureCurrent());\n\n this.resolvedMediaVideoFormat = stream.videoTrack?.videoFormat;\n await this.coordinator.commit(new RealtimeState({ connectionState: RealtimeConnectionState.ready }), token);\n return stream;\n });\n }\n\n /**\n * 绑定外部帧源或克隆兼容视频轨;不采集设备,失败时释放本次资源。\n * @param options 外部 source 或兼容的 videoTrack/videoFormat,不能混用。\n * @returns 含解析后媒体规格的本地流;帧源还支持 SDK 预览及可选音频。\n */\n async createExternalVideoStream(\n options: Parameters<XmaxRealtimeManaging[\"createExternalVideoStream\"]>[0]\n ): Promise<RealtimeMediaStream> {\n return this.coordinator.run(RealtimeOperationKind.media, undefined, async token => {\n this.mediaController.ensureNoLocalSource();\n\n this.mediaController.validateExternalMediaSupport(\"external\");\n\n const stream = await this.mediaController.createExternalVideoStream(options, token.signal);\n token.setFailureScope(RealtimeTerminationScope.all);\n token.ensureCurrent();\n this.timing.start();\n this.clearVideoStatistics();\n\n await this.mediaController.prepareSource(() => token.ensureCurrent());\n\n this.resolvedMediaVideoFormat = stream.videoTrack?.videoFormat;\n await this.coordinator.commit(new RealtimeState({ connectionState: RealtimeConnectionState.ready }), token);\n\n return stream;\n });\n }\n\n /**\n * 未连接时解绑外部源,保留接入方设备所有权,清空本地媒体状态。\n */\n async stopExternalVideoStream(): Promise<void> {\n await this.coordinator.run(RealtimeOperationKind.media, undefined, async token => {\n if (this.mediaController.source !== \"externalVideo\") {\n return;\n }\n\n if (this.connectionManager.currentSessionID) {\n throw new XmaxError(XmaxErrorCode.invalidConfiguration, \"Disconnect before stopping the external video stream\");\n }\n\n token.setFailureScope(RealtimeTerminationScope.all);\n this.timing.cancel();\n this.clearVideoStatistics();\n await this.mediaController.stopLocalStream();\n token.ensureCurrent();\n\n this.resolvedMediaVideoFormat = undefined;\n await this.coordinator.commit(new RealtimeState({ connectionState: RealtimeConnectionState.idle }), token);\n });\n }\n\n /**\n * 当前 Manager 拥有的媒体源轨道。\n */\n private get currentLocalTrack(): RealtimeVideoTrack | undefined {\n return this.mediaController.currentTrack;\n }\n\n /**\n * 移除媒体权限弹窗观察;授权决定已记录或不再需要观察时调用。\n */\n private disposePermissionWatch(): void {\n this.permissionWatchDispose?.();\n this.permissionWatchDispose = undefined;\n }\n\n /**\n * 应用完整的上行视频格式,成功后更新本地轨道元数据,不改变生成格式或生命周期。\n * 尺寸向下做 4 对齐,优先保持精确比例;对齐后的格式作为新的上行 L1 基准。\n * 复用配置操作准入,避免与建连、切换摄像头或其他配置更新并发。\n *\n * @throws 无本地相机、外部或文件视频流、参数无效、操作冲突或底层编码更新失败时抛错。\n */\n async updateUplinkVideoFormat(\n videoFormat: RealtimeVideoFormat\n ): Promise<void> {\n await this.coordinator.run(RealtimeOperationKind.configuration, undefined, async (token) => {\n const track = this.currentLocalTrack;\n const previousFormat = track?.videoFormat;\n if (!track || !previousFormat) {\n throw new XmaxError(XmaxErrorCode.invalidConfiguration, \"Local camera, external or file video stream is not started\");\n }\n videoFormat.validate();\n const size = alignedVideoSize(videoFormat, 1, 4);\n const resolvedFormat = size.width === videoFormat.width && size.height === videoFormat.height\n ? videoFormat : videoFormat.resized(size.width, size.height);\n this.qualityController?.stop();\n\n // 上行尺寸不按模型分辨率桶吸附,也不发送生成条件或回传尺寸信令。\n await this.streamController.updateUplinkVideoFormat(resolvedFormat);\n token.ensureCurrent();\n\n this.resolvedMediaVideoFormat ??= previousFormat;\n track.updateVideoFormat(resolvedFormat);\n this.qualityStates.set(track, { baseline: resolvedFormat, level: 1 });\n this.qualityHistory?.write(\"uplink\", 1);\n });\n }\n\n /**\n * 已连接时仅替换帧率;与其他配置及生命周期操作共享同一操作租约。\n */\n async updateUplinkFrameRate(\n fps: number\n ): Promise<void> {\n await this.coordinator.run(RealtimeOperationKind.configuration, undefined, async (token) => {\n this.qualityController?.stop();\n await this.applyUplinkFrameRate(fps, token);\n const track = this.currentLocalTrack;\n if (track?.videoFormat) {\n this.qualityStates.set(track, { baseline: track.videoFormat, level: 1 });\n this.qualityHistory?.write(\"uplink\", 1);\n }\n });\n }\n\n /**\n * 手动仅调帧接口,成功后的当前格式将成为新的自适应 L1 基准。\n */\n private async applyUplinkFrameRate(\n fps: number,\n token: RealtimeOperationToken\n ): Promise<void> {\n const track = this.currentLocalTrack;\n const previous = track?.videoFormat;\n const state = this.currentState.connectionState;\n if (!track || !previous ||\n (state !== RealtimeConnectionState.connected && state !== RealtimeConnectionState.starting &&\n state !== RealtimeConnectionState.generating)) {\n throw new XmaxError(XmaxErrorCode.invalidConfiguration, \"Frame rate updates require a connected local camera, external or file stream\");\n }\n if (!Number.isFinite(fps) || fps <= 0) {\n throw new XmaxError(XmaxErrorCode.invalidConfiguration, \"Video frame rate must be greater than zero\");\n }\n const format = await this.streamController.updateUplinkFrameRate(fps);\n token.ensureCurrent();\n this.resolvedMediaVideoFormat ??= previous;\n track.updateVideoFormat(format);\n }\n\n /**\n * 上下行独立判断;串行应用同一原始事件,避免同时降档争抢配置租约。\n */\n private handleAdaptiveQuality(\n statistics?: RealtimeNetworkQuality\n ): void {\n const revision = ++this.adaptiveQualityRevision;\n const controller = this.qualityController;\n const downlink = this.downlinkQualityController;\n if (!controller || !downlink)\n return;\n const taskID = this.currentState.taskID;\n if (this.currentState.connectionState !== RealtimeConnectionState.generating || !taskID) {\n controller.stop();\n downlink.stop();\n return;\n }\n if (!statistics || this.coordinator.isBusy || this.adaptiveQualityPending) {\n controller.resetSamples();\n downlink.resetSamples();\n return;\n }\n const track = this.currentLocalTrack;\n if (!track) {\n controller.stop();\n } else if (!controller.snapshot.active) {\n const format = track.videoFormat;\n if (format) {\n const state = this.qualityStates.get(track) ?? { baseline: format, level: 1 };\n this.qualityStates.set(track, state);\n controller.start(state.level);\n }\n }\n const state = this.downlinkQualityState;\n if (state && !downlink.snapshot.active)\n downlink.start(state.level);\n const sampleAt = performance.now();\n this.adaptiveQualityPending = true;\n void (async () => {\n // 失败已由协调器上报;一个方向失败不阻断另一个方向的判断。\n await controller.observe(statistics.uplink, sampleAt).catch(() => { });\n if (revision !== this.adaptiveQualityRevision || this.currentState.taskID !== taskID || state !== this.downlinkQualityState ||\n this.currentState.connectionState !== RealtimeConnectionState.generating || this.coordinator.isBusy) {\n downlink.resetSamples();\n return;\n }\n await downlink.observe(statistics.downlink, sampleAt).catch(() => { });\n })().finally(() => {\n this.adaptiveQualityPending = false;\n });\n }\n\n /**\n * 停止本地相机流并释放本地预览与 RTC 资源。\n */\n async stopLocalCameraStream(): Promise<void> {\n if (this.mediaController.source && this.mediaController.source !== \"camera\") {\n return;\n }\n await this.coordinator.run(\n RealtimeOperationKind.media,\n undefined,\n async (token) => {\n this.timing.cancel();\n this.clearVideoStatistics();\n this.disposePermissionWatch();\n await this.mediaController.stopLocalCameraStream();\n token.ensureCurrent();\n this.resolvedMediaVideoFormat = undefined;\n await this.coordinator.commit(\n new RealtimeState({ connectionState: RealtimeConnectionState.idle }),\n token,\n );\n },\n );\n }\n\n /**\n * 切换前后置摄像头。\n *\n * 生成过程中调用时,SDK 会停止当前生成、切换摄像头,并使用缓存的生成\n * 条件恢复生成;RTC 连接保持不变。连接或生成正在启动时不可切换。\n *\n * @returns 复用原视频轨道并更新摄像头位置后的本地媒体流。\n */\n async switchCamera(): Promise<RealtimeMediaStream> {\n return this.coordinator.run(\n RealtimeOperationKind.cameraSwitch,\n undefined,\n async (token) => {\n if (this.mediaController.source === \"externalVideo\") {\n throw new XmaxError(XmaxErrorCode.invalidConfiguration, \"External video capture is managed by the caller\");\n }\n\n this.qualityController?.stop();\n const stream = await this.mediaController.switchCamera();\n token.ensureCurrent();\n return stream;\n },\n );\n }\n\n /**\n * 使用当前 Manager 创建的本地流建立实时连接。\n *\n * 创建实时会话、加入 RTC 房间、发布本地媒体并启动会话心跳。\n * 启用帧检测时,发布前并行等待相机预热(固定 200ms),避免把黑帧推给 RTC。\n * 返回的远端媒体流在生成开始后承载远端生成画面。\n *\n * @param localStream 由当前 Manager 创建的摄像头、外部视频轨或本地文件。\n * @returns 远端生成结果占位的媒体流。\n * @throws 本地流不属于当前 Manager、已有活动连接、会话创建或进房\n * 发布失败时抛出错误;失败时自动释放连接资源并恢复本地预览。\n */\n async connect(\n localStream: RealtimeMediaStream\n ): Promise<RealtimeMediaStream> {\n return this.coordinator.run(\n RealtimeOperationKind.connection,\n undefined,\n async (token) => {\n const connectionState = this.coordinator.currentState.connectionState;\n if (\n connectionState === RealtimeConnectionState.connecting ||\n connectionState === RealtimeConnectionState.connected ||\n connectionState === RealtimeConnectionState.starting ||\n connectionState === RealtimeConnectionState.generating\n ) {\n throw new XmaxError(\n XmaxErrorCode.invalidConfiguration,\n \"A realtime connection is already active\",\n );\n }\n return this.performConnect(localStream, token);\n },\n );\n }\n\n /**\n * 按需建立连接并开始生成。\n *\n * 尚未连接时先建立实时连接;已在生成时默认更新条件,restart 为 true 时重新开始生成。\n * 首次生成必须提供条件上下文,之后缺省时复用最近一次缓存的上下文。\n * 返回后请将远端流挂载到 SDK 视频视图;首帧及渲染预热完成后状态才进入 generating。\n *\n * @param options.localStream 由当前 Manager 创建的摄像头、外部视频轨或本地文件。\n * @param options.context 本次生成使用的条件上下文;缺省时复用缓存。\n * @param options.restart 是否保留 RTC 连接并创建新任务发送 start,默认 false;不预先发送 stop。\n * @param options.faceRestoration 是否启用原始人脸回贴,默认 false;正在生成时通过 change_condition 更新。\n * @param options.extraParams 合并到本次 start 或 change_condition 的 params;不缓存,自动恢复时不携带,不允许覆盖 SDK 字段。\n * @returns 可挂载的远端媒体流,不等待渲染预热;展示就绪以 generating 状态为准。\n * @throws extraParams 包含非 JSON 数据、循环引用或 SDK 保留字段时,在连接或更新前抛出 invalidConfiguration。\n * @throws 本地流不属于当前 Manager、缺少可用的条件上下文、信令发送\n * 或生成确认失败时抛出错误;失败时自动停止生成任务并释放连接资源。\n */\n async startGeneration(\n options: {\n localStream: RealtimeMediaStream;\n context?: RealtimeContext;\n restart?: boolean;\n faceRestoration?: boolean;\n extraParams?: RealtimeExtraParams;\n }\n ): Promise<RealtimeMediaStream> {\n const extraParams = snapshotExtraParams(options.extraParams);\n return this.coordinator.run(\n options.restart === true ? RealtimeOperationKind.generationRestart : RealtimeOperationKind.generation,\n undefined,\n async (token) => {\n const localTrack = options.localStream.videoTrack;\n if (!localTrack || localTrack !== this.currentLocalTrack) {\n throw new XmaxError(\n XmaxErrorCode.invalidConfiguration,\n \"The local stream must be created and started by this realtime manager\",\n );\n }\n const videoFormat = this.resolvedMediaVideoFormat ?? localTrack.videoFormat;\n if (!videoFormat) {\n throw new XmaxError(\n XmaxErrorCode.invalidConfiguration,\n \"The local video format is unavailable\",\n );\n }\n // 默认只更新条件;显式重启时沿用连接并创建新任务。\n const currentState = this.coordinator.currentState;\n const hasGeneration = currentState.connectionState === RealtimeConnectionState.generating ||\n (currentState.connectionState === RealtimeConnectionState.starting && !!currentState.taskID);\n if (hasGeneration && options.restart !== true) {\n await this.generationManager.update(currentState.taskID, {\n videoFormat,\n ...this.resolveGenerationFormat(videoFormat),\n context: options.context,\n targetSize: this.downlinkQualityState?.size,\n faceRestoration: options.faceRestoration ?? false,\n extraParams,\n });\n token.ensureCurrent();\n return this.connectionManager.makeRemoteStream();\n }\n const context = this.generationManager.validateContext(options.context);\n\n // 尚未连接时先建立实时连接。\n if (currentState.connectionState !== RealtimeConnectionState.connected && !hasGeneration) {\n await this.performConnect(options.localStream, token);\n }\n\n return this.performStartGeneration(\n token,\n videoFormat,\n context,\n options.restart ?? false,\n options.faceRestoration ?? false,\n extraParams,\n );\n },\n );\n }\n\n /**\n * 观察原始远端视频和 PCM 音频;可在生成前或生成中设置,传 undefined 释放观察资源。\n * 从用户手势调用以解锁音频;能力不足或初始化失败时拒绝,不影响当前播放和生成。\n */\n async setRemoteMediaFrameListener(\n listener?: RealtimeMediaFrameListener\n ): Promise<void> {\n if (listener && !this.canReadRemoteAudio) {\n throw new XmaxError(XmaxErrorCode.mediaError, \"This RTC platform cannot observe remote audio independently of playback\");\n }\n this.remoteMediaFrameObservation = false;\n this.remoteMediaFrames.setSource(undefined);\n this.updateRemoteMediaObservation();\n await this.remoteMediaFrames.setListener(listener);\n this.remoteMediaFrameObservation = Boolean(listener);\n this.updateRemoteMediaObservation();\n }\n\n /** 交付借用的远端轨道,不创建音频处理器或视频取帧器。 */\n setRemoteMediaTracksListener(listener?: (tracks?: RemoteMediaTracks) => void): void {\n this.remoteMediaTracksListener = listener;\n this.updateRemoteMediaObservation();\n }\n\n /** 两种观察方式共享 RTC 轨道通知,但各自独立订阅和清理。 */\n private updateRemoteMediaObservation(): void {\n this.streamController.setRemoteMediaListener(\n this.remoteMediaFrameObservation || this.remoteMediaTracksListener ? tracks => {\n if (this.remoteMediaFrameObservation) this.remoteMediaFrames.setSource(tracks);\n try {\n this.remoteMediaTracksListener?.(tracks);\n } catch (error) {\n XmaxLogger.render.warning(() => `Remote media track observer failed: ${XmaxError.from(error).message}`);\n }\n } : undefined,\n );\n }\n\n /**\n * 断开实时连接并保留当前本地媒体预览。\n */\n async disconnect(): Promise<void> {\n this.mediaController.pauseLocalVideo();\n this.timing.cancel();\n this.clearVideoStatistics();\n await this.coordinator.disconnect();\n }\n\n /**\n * 关闭当前实时生命周期并释放连接、本地媒体和 RTC Engine。\n * 关闭期间重复调用会等待同一个释放任务;关闭完成后仍可重新创建本地流。\n */\n async close(): Promise<void> {\n this.mediaController.pauseLocalVideo();\n this.timing.cancel();\n this.clearVideoStatistics();\n await this.coordinator.terminate(RealtimeTerminationScope.all);\n }\n\n /**\n * TRTC 按档位调整帧率;启动时保留原始分辨率。\n */\n private uplinkQualityFormat(\n baseline: RealtimeVideoFormat,\n level: number\n ): RealtimeVideoFormat {\n const format = qualityVideoFormat(baseline, level);\n return this.options.provider === RtcProvider.trtc\n ? format.resized(baseline.width, baseline.height)\n : format;\n }\n\n /**\n * 从固定媒体基准选择模型生成画布,媒体与画布比例不一致时为非相机源启用服务端补边。\n * 只生成信令配置,不改变上行轨道或其编码尺寸。\n *\n * @param videoFormat 固定媒体规格,不受上行调档影响。\n * @returns 模型支持的生成画布规格及服务端补边策略。\n */\n private resolveGenerationFormat(\n videoFormat: RealtimeVideoFormat\n ): {\n generationVideoFormat: RealtimeVideoFormat;\n paddingContain: boolean;\n } {\n if (this.mediaController.source === \"camera\" && resolutionBuckets(this.options.model).length > 0) {\n return { generationVideoFormat: videoFormat, paddingContain: false };\n }\n\n const size = this.mediaService.resolveGenerationSize(videoFormat);\n const paddingContain = this.mediaController.source !== \"camera\" &&\n videoFormat.width * size.height !== videoFormat.height * size.width;\n return {\n generationVideoFormat: size.width !== videoFormat.width || size.height !== videoFormat.height\n ? videoFormat.resized(size.width, size.height) : videoFormat,\n paddingContain,\n };\n }\n\n /**\n * 从固定媒体基准计算下行尺寸,所有来源共用保留比例和按 4 对齐的规则。\n *\n * @param videoFormat 固定媒体规格。\n * @param level 下行清晰度档位。\n * @returns 不放大且符合档位边界的回传尺寸。\n */\n private resolveTargetSize(\n videoFormat: RealtimeVideoFormat,\n level: number\n ): ModelSize {\n return this.mediaService.resolveTargetSize(videoFormat, qualityVideoMaximumSize(videoFormat, level));\n }\n\n /**\n * 选择本地上行初始档位;关闭自动调档时,每次连接均从原媒体基准的 L1 启动。\n */\n private resolveInitialUplinkQuality(\n track: RealtimeVideoTrack\n ): QualityState | undefined {\n const previous = this.qualityStates.get(track);\n if (previous && this.options.isAdaptiveQualityEnabled)\n return undefined;\n\n const baseline = previous?.baseline ?? track.videoFormat;\n if (!baseline)\n return undefined;\n\n let level = this.qualityHistory?.read(\"uplink\") ?? 1;\n while (level > 1 && !supportsQualityEncoding(this.uplinkQualityFormat(baseline, level))) {\n level--;\n }\n\n return { baseline, level };\n }\n\n /**\n * 建立实时连接:创建会话、进房发布、启动心跳并准备远端渲染轨道。\n *\n * @param localStream 当前 Manager 创建的本地媒体流。\n * @param token 当前操作租约。\n * @returns 远端生成结果占位的媒体流。\n */\n private async performConnect(\n localStream: RealtimeMediaStream,\n token: RealtimeOperationToken,\n ): Promise<RealtimeMediaStream> {\n // 校验本地流归属与连接配置,避免无效请求改变连接状态。\n const localTrack = localStream.videoTrack;\n if (!localTrack || localTrack !== this.currentLocalTrack) {\n throw new XmaxError(\n XmaxErrorCode.invalidConfiguration,\n \"The local stream must be created and started by this realtime manager\",\n );\n }\n this.connectionManager.validateConfiguration();\n\n token.setFailureScope(RealtimeTerminationScope.connection);\n await this.coordinator.commit(\n new RealtimeState({ connectionState: RealtimeConnectionState.connecting }),\n token,\n );\n\n // 将预热等待绑定到本次连接操作;取消或退出连接流程时停止等待。\n const warmupController = new AbortController();\n const abortWarmup = () => warmupController.abort();\n token.signal.addEventListener(\"abort\", abortWarmup, { once: true });\n if (token.signal.aborted)\n abortWarmup();\n\n try {\n let beforePublish: (() => Promise<void>) | undefined;\n if (this.mediaController.isFrameValidationEnabled) {\n // 预热与建连并行;进房前预热失败也要接住拒绝,发布屏障仍等待原始结果。\n const cameraReady = this.mediaController.waitForValidCameraFrame(warmupController.signal).then(() => {\n token.ensureCurrent();\n });\n\n void cameraReady.catch(() => { });\n beforePublish = () => cameraReady;\n }\n\n // 支持自动调档的新轨道读取历史档位(缺省 L2);保留 L1 基准,重连优先沿用内存状态。\n const initialQuality = this.resolveInitialUplinkQuality(localTrack);\n const initialFormat = initialQuality && this.qualityController\n ? this.uplinkQualityFormat(initialQuality.baseline, initialQuality.level)\n : initialQuality?.baseline;\n const remote = await this.connectionManager.connect({\n localTrack,\n remoteVideoFormat: this.resolvedMediaVideoFormat ?? localTrack.videoFormat,\n localVideoFormat: initialFormat,\n onVideoEncoderConfigured: () => {\n if (!initialQuality || !initialFormat)\n return;\n this.resolvedMediaVideoFormat ??= initialQuality.baseline;\n localTrack.updateVideoFormat(initialFormat);\n this.qualityStates.set(localTrack, initialQuality);\n this.qualityHistory?.write(\"uplink\", initialQuality.level);\n },\n model: this.options.model,\n includeLocalAudio: this.mediaController.hasAudio,\n ensureCurrent: () => token.ensureCurrent(),\n onPublished: () => {\n this.acceptsVideoStatistics = true;\n this.mediaController.setExternalAudioPublished(true);\n },\n beforePublish,\n });\n\n await this.coordinator.commit(\n new RealtimeState({\n connectionState: RealtimeConnectionState.connected,\n sessionID: this.connectionManager.currentSessionID,\n }),\n token,\n );\n\n return remote;\n } finally {\n token.signal.removeEventListener(\"abort\", abortWarmup);\n abortWarmup();\n }\n }\n\n /**\n * 编排首帧计时和生成状态,任务与确认由生成管理器负责。\n *\n * @param token 当前生成操作的取消与状态控制令牌。\n * @param videoFormat 固定本地媒体基准,用于派生生成画布、回传尺寸和交互坐标。\n * @param context 本次已解析的生成条件。\n * @param restart 是否替换当前生成任务并重新发送 start。\n * @param faceRestoration 是否回贴原始人脸。\n * @param extraParams 合并到本次 start 的 params,不缓存。\n * @returns 承载生成结果的远端媒体流。\n */\n private async performStartGeneration(\n token: RealtimeOperationToken,\n videoFormat: NonNullable<RealtimeVideoTrack[\"videoFormat\"]>,\n context: RealtimeContext,\n restart: boolean,\n faceRestoration: boolean,\n extraParams?: RealtimeExtraParams,\n ): Promise<RealtimeMediaStream> {\n token.setFailureScope(RealtimeTerminationScope.connection);\n const sessionID = this.connectionManager.currentSessionID;\n if (!sessionID) {\n throw new XmaxError(XmaxErrorCode.sessionError, \"Realtime session is unavailable\");\n }\n\n await this.coordinator.commit(\n new RealtimeState({ connectionState: RealtimeConnectionState.starting, sessionID }),\n token,\n );\n token.ensureCurrent();\n\n const initialLevel = this.qualityHistory?.read(\"downlink\") ?? 1;\n const targetSize = this.resolveTargetSize(videoFormat, initialLevel);\n\n const completeFirstFrame = this.timing.startFirstFrame();\n this.remoteRenderWarmupCompletedHandler = undefined;\n this.remoteFrameDisplayHandler = () => {\n if (!token.signal.aborted) {\n completeFirstFrame();\n this.remoteRenderWarmupCompletedHandler ??= this.timing.startRenderWarmup();\n }\n };\n this.renderController.prepareForGeneration();\n\n const taskID = await this.generationManager.start({\n videoFormat,\n ...this.resolveGenerationFormat(videoFormat),\n signal: token.signal,\n ensureCurrent: () => token.ensureCurrent(),\n waitUntilRemoteReady: () => this.connectionManager.waitUntilRemoteTrackReady(\n REMOTE_FIRST_FRAME_TIMEOUT_MS, token.signal,\n ),\n context,\n restart,\n targetSize,\n mirror: this.mediaController.source === \"camera\" &&\n this.currentLocalTrack?.position === CameraPosition.front,\n faceRestoration,\n onStartSent: this.mediaController.source === \"localVideo\" ? async () => {\n token.ensureCurrent();\n await this.mediaController.startLocalVideo(token.signal);\n token.ensureCurrent();\n } : undefined,\n extraParams,\n });\n\n token.ensureCurrent();\n\n this.downlinkQualityController?.stop();\n this.downlinkQualityState = {\n taskID, baseline: videoFormat, level: initialLevel,\n size: targetSize ?? { width: videoFormat.width, height: videoFormat.height },\n };\n await this.coordinator.commit(\n new RealtimeState({ connectionState: RealtimeConnectionState.starting, sessionID, taskID }),\n token,\n );\n token.ensureCurrent();\n // 已提前挂载的视图可能先完成预热;任务确认后再尝试提交 generating。\n await this.completeGenerationPresentation();\n token.ensureCurrent();\n this.qualityHistory?.write(\"downlink\", initialLevel);\n\n return this.connectionManager.makeRemoteStream();\n }\n\n /**\n * 任务启动成功且当前视图完成预热后提交展示就绪状态,不阻塞远端流返回。\n */\n private async completeGenerationPresentation(): Promise<void> {\n const { sessionID, taskID } = this.currentState;\n if (!sessionID || !taskID) {\n return;\n }\n\n await this.coordinator.remotePreviewDidBecomeReady(\n sessionID, taskID, () => this.renderController.isReady,\n );\n }\n\n /**\n * 心跳失败或会话失效:结束当前连接生命周期并通过最终状态给出原因。\n */\n private async handleHeartbeatFailure(\n sessionID: string,\n error: XmaxError,\n ): Promise<void> {\n await this.coordinator.terminateWithError(\n error,\n RealtimeTerminationScope.connection,\n () => this.connectionManager.currentSessionID === sessionID,\n );\n }\n\n /**\n * 统一清理顺序:停止心跳与生成,再断开连接;all 额外释放本地媒体。\n */\n private async performCleanup(\n scope: RealtimeTerminationScope,\n taskID: string,\n ): Promise<RealtimeCleanupResult> {\n this.remoteMediaFrames.setSource(undefined);\n this.mediaController.setExternalAudioPublished(false);\n this.mediaController.pauseLocalVideo();\n this.timing.cancel();\n this.remoteFrameDisplayHandler = undefined;\n this.remoteRenderWarmupCompletedHandler = undefined;\n this.clearVideoStatistics();\n\n this.connectionManager.clearRemoteMedia();\n\n this.connectionManager.stopHeartbeat();\n await this.generationManager.reset(taskID);\n const sessionID = await this.connectionManager.disconnect();\n\n if (scope === RealtimeTerminationScope.all) {\n this.setRemoteMediaTracksListener(undefined);\n await this.setRemoteMediaFrameListener(undefined);\n this.resolvedMediaVideoFormat = undefined;\n this.disposePermissionWatch();\n try {\n await this.mediaController.stopLocalStream();\n } catch (error) {\n XmaxLogger.realtime.error(\n () =>\n `释放本地媒体失败 (Failed to Release Local Media)\\n` +\n `└─ ${XmaxLogger.localized(\"原因:\", \"Reason: \")}${XmaxError.from(error).message}`,\n );\n }\n }\n\n return {\n sessionID,\n hasLocalMedia: this.currentLocalTrack !== undefined,\n };\n }\n\n /**\n * 校验音量取值范围为 `0...1`。\n */\n private static validateVolume(\n volume: number\n ): void {\n if (!Number.isFinite(volume) || volume < 0 || volume > 1) {\n throw new XmaxError(\n XmaxErrorCode.invalidConfiguration,\n \"Audio volume must be between 0 and 1\",\n );\n }\n }\n}\n","//\n// XmaxConfiguration.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\nimport { XmaxError, XmaxErrorCode } from \"../Foundation/Errors/XmaxError\";\nimport { XmaxLoggerOption } from \"../Foundation/Logging/XmaxLogger\";\nimport { XmaxEnvironment } from \"../Foundation/Runtime/XmaxEnvironment\";\n\n/**\n * SDK 的身份、环境和日志选项。\n */\nexport interface XmaxConfigurationInit {\n\n /**\n * 调用 Xmax 服务使用的 API Key。\n */\n apiKey: string;\n\n /**\n * SDK 连接的服务环境;默认为国内环境。\n */\n environment?: XmaxEnvironment;\n\n /**\n * SDK 输出的日志类型;默认为不输出日志。\n */\n loggerOptions?: XmaxLoggerOption;\n}\n\n/**\n * SDK 全局配置。\n */\nexport class XmaxConfiguration {\n\n /**\n * 调用 Xmax 服务使用的 API Key。\n */\n readonly apiKey: string;\n\n /**\n * SDK 连接的服务环境,同时决定日志细项语言:国内为中文,海外为英文。\n */\n readonly environment: XmaxEnvironment;\n\n /**\n * SDK 输出的日志类型;默认为不输出日志。\n */\n readonly loggerOptions: XmaxLoggerOption;\n\n /**\n * 清理 API Key 首尾空白并补全环境和日志默认值;有效性检查由 validate 执行。\n */\n constructor(\n init: XmaxConfigurationInit\n ) {\n this.apiKey = init.apiKey.trim();\n this.environment = init.environment ?? XmaxEnvironment.china;\n this.loggerOptions = init.loggerOptions ?? XmaxLoggerOption.none;\n }\n\n /**\n * 校验全局配置。\n * @throws API Key 为空时抛出 `XmaxError`。\n */\n validate(): void {\n if (this.apiKey.length === 0) {\n throw new XmaxError(\n XmaxErrorCode.invalidAPIKey,\n \"API key cannot be empty\",\n );\n }\n }\n}\n","//\n// NonRealtimeTask.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\n/**\n * 非实时视频的输出质量。\n */\nexport enum NonRealtimeQuality {\n\n /**\n * 标准清晰度输出。\n */\n sd = \"sd\",\n\n /**\n * 高清输出。\n */\n hd = \"hd\",\n}\n\n/**\n * 服务端任务状态;上传进度不属于任务状态。\n */\nexport enum NonRealtimeTaskStatus {\n\n /**\n * 任务已提交,等待服务端处理。\n */\n submitted = \"submitted\",\n\n /**\n * 服务端正在处理任务。\n */\n processing = \"processing\",\n\n /**\n * 任务处理完成。\n */\n completed = \"completed\",\n\n /**\n * 任务处理失败。\n */\n error = \"error\",\n}\n\n/**\n * 提交非实时任务的参数;资源必须来自 Xmax 上传流程,不能使用 blob URL 或本地路径。\n */\nexport interface NonRealtimeTaskOptions {\n\n /**\n * 模型标识。\n */\n model: string;\n\n /**\n * 已上传源视频的 URL。\n */\n videoPath: string;\n\n /**\n * 生成指令;去除首尾空白后为 1–1024 个字符。\n */\n prompt: string;\n\n /**\n * 已上传参考图片的 URL;省略表示不使用参考图。\n */\n referencePath?: string;\n\n /**\n * 输出质量。\n */\n quality: NonRealtimeQuality;\n\n /**\n * 可选的处理帧率;仅接受协议支持的整数,省略时沿用源视频帧率(含小数)。\n */\n fps?: number;\n}\n\n/**\n * 非实时生成结果;缺失或 null 的服务端字段规范化为 undefined。\n */\nexport interface NonRealtimeResult {\n\n /**\n * 生成结果文件的访问 URL。\n */\n readonly url?: string;\n\n /**\n * 服务端上传生成结果时返回的错误说明。\n */\n readonly uploadError?: string;\n\n /**\n * 生成结果的上传状态。\n */\n readonly uploadStatus?: string;\n\n /**\n * 服务端结果收尾耗时,单位为毫秒。\n */\n readonly finalizeDurationMs?: number;\n\n /**\n * 服务端使用的压缩等级。\n */\n readonly compressionLevel?: string;\n\n /**\n * 结果视频的帧数。\n */\n readonly frameCount?: number;\n\n /**\n * 结果视频帧率,单位为 fps。\n */\n readonly fps?: number;\n\n /**\n * 结果视频时长,单位为毫秒。\n */\n readonly durationMs?: number;\n\n /**\n * 结果文件大小,单位为字节。\n */\n readonly fileSizeBytes?: number;\n\n /**\n * 服务端处理时丢弃的帧数。\n */\n readonly droppedFrameCount?: number;\n\n /**\n * 结果压缩前的文件大小,单位为字节。\n */\n readonly originalFileSizeBytes?: number;\n}\n\n/**\n * 非实时任务快照;uid 和 status 为 SDK 查询、恢复及轮询所需字段。\n * 时间戳保持服务端原始字符串,不推断时区。\n */\nexport interface NonRealtimeTask {\n\n /**\n * 非实时任务标识,用于查询和恢复任务。\n */\n readonly uid: string;\n\n /**\n * 服务端返回的运行状态。\n */\n readonly status: NonRealtimeTaskStatus;\n\n /**\n * 业务用户标识。\n */\n readonly userUid?: string;\n\n /**\n * 本次视频生成使用的文字指令。\n */\n readonly prompt?: string;\n\n /**\n * 已上传源视频的 URL。\n */\n readonly videoPath?: string;\n\n /**\n * 已上传参考图片的 URL。\n */\n readonly referencePath?: string;\n\n /**\n * 服务端任务处理器标识。\n */\n readonly processorId?: string;\n\n /**\n * 生成结果使用的输出质量。\n */\n readonly quality?: NonRealtimeQuality;\n\n /**\n * 提交任务时指定的处理帧率,单位为 fps。\n */\n readonly requestedFps?: number;\n\n /**\n * 服务端最终使用的处理帧率,单位为 fps。\n */\n readonly resolvedFps?: number;\n\n /**\n * 服务端已执行的重试次数。\n */\n readonly retryCount?: number;\n\n /**\n * 源视频时长,单位为秒。\n */\n readonly videoDurationSeconds?: number;\n\n /**\n * 服务端报告的计费时长,单位为秒。\n */\n readonly billableDurationSeconds?: number;\n\n /**\n * 服务端报告的任务消耗点数。\n */\n readonly chargePoints?: number;\n\n /**\n * 生成结果的文件和处理信息。\n */\n readonly result?: NonRealtimeResult;\n\n /**\n * 服务端开始处理任务的时间。\n */\n readonly processStartTime?: string;\n\n /**\n * 任务创建时间。\n */\n readonly createTimestamp?: string;\n\n /**\n * 任务最近更新时间。\n */\n readonly updateTimestamp?: string;\n}\n\n/**\n * 分页查询选项。\n */\nexport interface NonRealtimeTaskListOptions {\n\n /**\n * 从 1 开始的页码,默认 1。\n */\n pageNumber?: number;\n\n /**\n * 每页数量,1–100,默认 10。\n */\n pageSize?: number;\n\n /**\n * 可选的任务状态筛选。\n */\n status?: NonRealtimeTaskStatus;\n}\n\n/**\n * 按创建时间从新到旧排列的任务页。\n */\nexport interface NonRealtimeTaskPage {\n\n /**\n * 当前页码,从 1 开始。\n */\n readonly pageNumber: number;\n\n /**\n * 当前页的容量。\n */\n readonly pageSize: number;\n\n /**\n * 符合查询条件的任务总数。\n */\n readonly total: number;\n\n /**\n * 当前页中的任务,按创建时间从新到旧排列。\n */\n readonly list: readonly NonRealtimeTask[];\n}\n","//\n// NonRealtimeTaskService.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\nimport { XmaxError, XmaxErrorCode } from \"../../Foundation/Errors/XmaxError\";\nimport type { ApiServicing } from \"../Network/ApiServicing\";\nimport {\n NonRealtimeQuality, NonRealtimeTaskStatus,\n type NonRealtimeResult, type NonRealtimeTask, type NonRealtimeTaskOptions,\n type NonRealtimeTaskListOptions, type NonRealtimeTaskPage,\n} from \"./NonRealtimeTask\";\n\n/**\n * 非实时任务接口允许显式指定的处理帧率。\n */\nconst SUPPORTED_FPS = new Set([8, 10, 12, 15, 16, 18, 20, 22, 24, 25, 30, 45, 48, 50, 60, 72, 90, 100, 120]);\n\n/**\n * 非实时任务 HTTP 服务;负责协议校验及字段映射,不执行提交重试或轮询。\n */\nexport class NonRealtimeTaskService {\n\n /**\n * 网络依赖\n */\n private readonly api: ApiServicing;\n\n /**\n * 注入非实时任务使用的 API 服务。\n */\n constructor(\n api: ApiServicing\n ) {\n this.api = api;\n }\n\n /**\n * 提交一次任务;请求结果不确定时也不自动重试,以免重复扣费。\n */\n async submitTask(\n options: NonRealtimeTaskOptions\n ): Promise<NonRealtimeTask> {\n const model = options.model?.trim();\n if (!model) {\n invalid(\"Model must not be empty\");\n }\n const prompt = options.prompt.trim();\n if (!prompt || [...prompt].length > 1024) {\n invalid(\"Prompt must contain 1–1024 characters\");\n }\n validateURL(options.videoPath);\n if (options.referencePath !== undefined) {\n validateURL(options.referencePath);\n }\n if (!Object.values(NonRealtimeQuality).includes(options.quality)) {\n invalid(\"Quality must be sd or hd\");\n }\n if (options.fps !== undefined && !SUPPORTED_FPS.has(options.fps)) {\n invalid(\"Unsupported processing frame rate\");\n }\n\n const payload = await this.api.post<unknown>(\"/offline-task\", {\n model,\n prompt,\n refVideoPath: options.videoPath.trim(),\n ...(options.referencePath === undefined ? {} : { refImagePath: options.referencePath.trim() }),\n quality: options.quality,\n ...(options.fps === undefined ? {} : { fps: options.fps }),\n });\n return parseTask(payload);\n }\n\n /**\n * 查询任务,校验返回 ID;signal 只取消本次 HTTP 查询,不取消服务端任务。\n */\n async getTask(\n taskUid: string,\n signal?: AbortSignal\n ): Promise<NonRealtimeTask> {\n const uid = validateTaskUid(taskUid);\n const task = parseTask(await this.api.get<unknown>(`/offline-task/${encodeURIComponent(uid)}`, { signal }));\n if (task.uid !== uid) {\n malformed(\"Task UID does not match the request\");\n }\n return task;\n }\n\n /**\n * 查询最多 100 个 ID,按首次出现顺序去重;任一任务不可用时整体失败。\n */\n async getTasks(\n taskUids: readonly string[]\n ): Promise<readonly NonRealtimeTask[]> {\n if (taskUids.length < 1 || taskUids.length > 100) {\n invalid(\"Provide 1–100 task UIDs\");\n }\n const uids = [...new Set(taskUids.map(validateTaskUid))];\n const payload = record(await this.api.post<unknown>(\"/offline-task/batch-query\", { taskUids: uids }));\n const tasks = parseList(payload.list);\n if (tasks.length !== uids.length || tasks.some((task, index) => task.uid !== uids[index])) {\n malformed(\"Batch response does not match the requested task UIDs\");\n }\n return tasks;\n }\n\n /**\n * 分页查询任务,不隐式加载其他页。\n */\n async listTasks(\n options: NonRealtimeTaskListOptions = {}\n ): Promise<NonRealtimeTaskPage> {\n const pageNumber = options.pageNumber ?? 1;\n const pageSize = options.pageSize ?? 10;\n if (!Number.isSafeInteger(pageNumber) || pageNumber < 1) {\n invalid(\"Page number must be a positive integer\");\n }\n if (!Number.isInteger(pageSize) || pageSize < 1 || pageSize > 100) {\n invalid(\"Page size must be between 1 and 100\");\n }\n if (options.status !== undefined && !Object.values(NonRealtimeTaskStatus).includes(options.status)) {\n invalid(\"Invalid task status\");\n }\n\n const query = new URLSearchParams({ pageNumber: String(pageNumber), pageSize: String(pageSize) });\n if (options.status !== undefined) {\n query.set(\"status\", options.status);\n }\n const payload = record(await this.api.get<unknown>(`/offline-task/page?${query}`));\n return Object.freeze({\n pageNumber: pageInteger(payload, \"pageNumber\", 1),\n pageSize: pageInteger(payload, \"pageSize\", 1),\n total: pageInteger(payload, \"total\", 0),\n list: parseList(payload.list),\n });\n }\n}\n\n/**\n * 校验任务 ID,拒绝相对路径段以避免 URL 规范化改变请求目标。\n */\nexport function validateTaskUid(\n value: string\n): string {\n const uid = value.trim();\n if (!uid || uid === \".\" || uid === \"..\") {\n invalid(\"Task UID must not be empty or a relative path segment\");\n }\n return uid;\n}\n\n/**\n * 仅校验网络 URL 格式;资源是否由 Xmax 上传必须由服务端核验。\n */\nfunction validateURL(\n value: string\n): void {\n let url: URL;\n try {\n url = new URL(value.trim());\n } catch {\n return invalid(\"Resource must be an uploaded HTTP(S) URL\");\n }\n if (![\"https:\", \"http:\"].includes(url.protocol) || url.username || url.password) {\n invalid(\"Resource must be an uploaded HTTP(S) URL\");\n }\n}\n\n/**\n * 将参数错误映射为 SDK 配置错误。\n */\nfunction invalid(\n message: string\n): never {\n throw new XmaxError(XmaxErrorCode.invalidConfiguration, message);\n}\n\n/**\n * 拒绝不可用的响应数据,避免把缺失状态当成永久处理中。\n */\nfunction malformed(\n message = \"Invalid non-realtime response data\"\n): never {\n throw new XmaxError(XmaxErrorCode.apiError, message);\n}\n\n/**\n * 解析响应对象。\n */\nfunction record(\n value: unknown\n): Record<string, unknown> {\n if (!value || typeof value !== \"object\" || Array.isArray(value)) {\n malformed();\n }\n return value as Record<string, unknown>;\n}\n\n/**\n * 读取可空字段,保留缺失与数值 0 的区别。\n */\nfunction field<T extends \"string\" | \"number\">(\n data: Record<string, unknown>,\n key: string,\n type: T\n): (T extends \"string\" ? string : number) | undefined {\n const value = data[key];\n if (value === undefined || value === null) {\n return undefined;\n }\n if (typeof value !== type || (typeof value === \"number\" && (!Number.isFinite(value) || value < 0))) {\n malformed(`Invalid field: ${key}`);\n }\n return value as T extends \"string\" ? string : number;\n}\n\n/**\n * 校验分页响应中必需的整数。\n */\nfunction pageInteger(\n data: Record<string, unknown>,\n key: string,\n minimum: number\n): number {\n const value = field(data, key, \"number\");\n if (value === undefined || !Number.isSafeInteger(value) || value < minimum) {\n malformed(`Invalid field: ${key}`);\n }\n return value;\n}\n\n/**\n * 将任务数组解析为不可变快照。\n */\nfunction parseList(\n value: unknown\n): readonly NonRealtimeTask[] {\n if (!Array.isArray(value)) {\n malformed(\"Missing task list\");\n }\n return Object.freeze(value.map(parseTask));\n}\n\n/**\n * 转换任务及结果字段,不补造服务端未提供的进度或失败原因。\n */\nfunction parseTask(\n value: unknown\n): NonRealtimeTask {\n const data = record(value);\n const uid = field(data, \"uid\", \"string\");\n const status = field(data, \"status\", \"string\") as NonRealtimeTaskStatus;\n const quality = field(data, \"quality\", \"string\") as NonRealtimeQuality | undefined;\n if (!uid?.trim() || !Object.values(NonRealtimeTaskStatus).includes(status)) {\n malformed(\"Missing task UID or invalid status\");\n }\n if (quality !== undefined && !Object.values(NonRealtimeQuality).includes(quality)) {\n malformed(\"Invalid task quality\");\n }\n\n return Object.freeze({\n uid, status, quality,\n userUid: field(data, \"userUid\", \"string\"),\n prompt: field(data, \"prompt\", \"string\"),\n videoPath: field(data, \"refVideoPath\", \"string\"),\n referencePath: field(data, \"refImagePath\", \"string\"),\n processorId: field(data, \"processorId\", \"string\"),\n requestedFps: field(data, \"requestedFps\", \"number\"),\n resolvedFps: field(data, \"resolvedFps\", \"number\"),\n retryCount: field(data, \"retryCount\", \"number\"),\n videoDurationSeconds: field(data, \"videoDurationSeconds\", \"number\"),\n billableDurationSeconds: field(data, \"billableDurationSeconds\", \"number\"),\n chargePoints: field(data, \"chargePoints\", \"number\"),\n result: data.result == null ? undefined : parseResult(data.result),\n processStartTime: field(data, \"processStartTime\", \"string\"),\n createTimestamp: field(data, \"createTimestamp\", \"string\"),\n updateTimestamp: field(data, \"updateTimestamp\", \"string\"),\n });\n}\n\n/**\n * 将服务端 snake_case 结果转换成 SDK camelCase 字段。\n */\nfunction parseResult(\n value: unknown\n): NonRealtimeResult {\n const data = record(value);\n return Object.freeze({\n url: field(data, \"result_url\", \"string\"),\n uploadError: field(data, \"upload_error\", \"string\"),\n uploadStatus: field(data, \"upload_status\", \"string\"),\n finalizeDurationMs: field(data, \"finalize_duration_ms\", \"number\"),\n compressionLevel: field(data, \"compression_level\", \"string\"),\n frameCount: field(data, \"frame_count\", \"number\"),\n fps: field(data, \"fps\", \"number\"),\n durationMs: field(data, \"result_duration_ms\", \"number\"),\n fileSizeBytes: field(data, \"file_size_bytes\", \"number\"),\n droppedFrameCount: field(data, \"dropped_frame_count\", \"number\"),\n originalFileSizeBytes: field(data, \"original_file_size_bytes\", \"number\"),\n });\n}\n","//\n// NonRealtimeTaskError.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\nimport { XmaxError, XmaxErrorCode } from \"../../Foundation/Errors/XmaxError\";\nimport type { NonRealtimeTask } from \"./NonRealtimeTask\";\n\n/**\n * 服务端任务失败或终态结果不可用;保留最后的任务快照供接入方展示及恢复查询。\n */\nexport class NonRealtimeTaskError extends XmaxError {\n\n /**\n * 失败上下文\n */\n readonly task: NonRealtimeTask;\n\n /**\n * 使用 API 错误分类,不虚构服务端未返回的业务错误码。\n */\n constructor(\n task: NonRealtimeTask,\n message: string\n ) {\n super(XmaxErrorCode.apiError, message);\n this.name = \"NonRealtimeTaskError\";\n this.task = task;\n }\n}\n","//\n// XmaxNonRealtimeManager.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\nimport { XmaxError, XmaxErrorCode } from \"../../Foundation/Errors/XmaxError\";\nimport { NonRealtimeTaskService, validateTaskUid } from \"../../Service/NonRealtime/NonRealtimeTaskService\";\nimport { NonRealtimeTaskError } from \"../../Service/NonRealtime/NonRealtimeTaskError\";\nimport { NonRealtimeTaskStatus, type NonRealtimeTask, type NonRealtimeTaskOptions, type NonRealtimeTaskListOptions } from \"../../Service/NonRealtime/NonRealtimeTask\";\nimport type { NonRealtimeWaitOptions, XmaxNonRealtimeManaging } from \"./XmaxNonRealtimeManaging\";\n\n/**\n * 非实时任务门面;复用 HTTP 服务,独立管理每一次等待,不创建 RTC 或实时会话。\n */\nexport class XmaxNonRealtimeManager implements XmaxNonRealtimeManaging {\n\n /**\n * 服务层组件\n */\n private readonly tasks: NonRealtimeTaskService;\n\n /**\n * 注入非实时任务服务。\n */\n constructor(\n tasks: NonRealtimeTaskService\n ) {\n this.tasks = tasks;\n }\n\n /**\n * 单次提交任务,不自动重试可能已扣费的请求。\n */\n submitTask(\n options: NonRealtimeTaskOptions\n ): Promise<NonRealtimeTask> {\n return this.tasks.submitTask(options);\n }\n\n /**\n * 取得任务快照,不把服务端失败终态当成查询失败。\n */\n getTask(\n taskUid: string,\n options?: {\n signal?: AbortSignal\n }\n ): Promise<NonRealtimeTask> {\n return this.tasks.getTask(taskUid, options?.signal);\n }\n\n /**\n * 批量取得任务快照。\n */\n getTasks(\n taskUids: readonly string[]\n ): Promise<readonly NonRealtimeTask[]> {\n return this.tasks.getTasks(taskUids);\n }\n\n /**\n * 分页取得任务列表。\n */\n listTasks(\n options?: NonRealtimeTaskListOptions\n ) {\n return this.tasks.listTasks(options);\n }\n\n /**\n * 等待任务完成;查询串行执行,瞬时错误有限退避,取消与总超时覆盖请求及等待阶段。\n */\n async waitForCompletion(\n taskUid: string,\n options: NonRealtimeWaitOptions = {}\n ): Promise<NonRealtimeTask> {\n const uid = validateTaskUid(taskUid);\n const interval = options.intervalMs ?? 2000;\n const retries = options.maxConsecutiveRetries ?? 3;\n validateDelay(interval);\n if (options.timeoutMs !== undefined) {\n validateDelay(options.timeoutMs);\n }\n if (!Number.isSafeInteger(retries) || retries < 0) {\n throw new XmaxError(\n XmaxErrorCode.invalidConfiguration,\n \"Retry count must be a non-negative integer\",\n );\n }\n\n const controller = new AbortController();\n let timedOut = false;\n const abort = () => controller.abort();\n options.signal?.addEventListener(\"abort\", abort, { once: true });\n if (options.signal?.aborted) {\n abort();\n }\n const timer = options.timeoutMs === undefined ? undefined : setTimeout(() => {\n timedOut = true;\n abort();\n }, options.timeoutMs);\n\n let failures = 0;\n try {\n for (; ;) {\n ensureActive(controller.signal);\n let task: NonRealtimeTask;\n try {\n task = await this.tasks.getTask(uid, controller.signal);\n } catch (error) {\n ensureActive(controller.signal);\n if (!isTransient(error) || failures >= retries) {\n throw error;\n }\n failures++;\n await delay(Math.min(interval * 2 ** (failures - 1), 30_000), controller.signal);\n continue;\n }\n\n ensureActive(controller.signal);\n failures = 0;\n // 观察回调异常不改变服务器任务状态,也不触发重复提交或查询重试。\n try {\n void Promise.resolve(options.onTaskUpdated?.(task)).catch(() => { });\n } catch {\n // 接入方负责处理观察回调错误。\n }\n ensureActive(controller.signal);\n\n if (task.status === NonRealtimeTaskStatus.error) {\n throw new NonRealtimeTaskError(task, `Non-realtime task ${uid} failed`);\n }\n if (task.status === NonRealtimeTaskStatus.completed) {\n if (!isResultAvailable(task)) {\n throw new NonRealtimeTaskError(\n task,\n `Non-realtime task ${uid} completed without a usable result`,\n );\n }\n return task;\n }\n await delay(interval, controller.signal);\n }\n } catch (error) {\n if (timedOut) {\n throw new XmaxError(\n XmaxErrorCode.timeout,\n `Waiting for task ${uid} timed out; the server task was not cancelled`,\n );\n }\n ensureActive(controller.signal);\n throw error;\n } finally {\n clearTimeout(timer);\n options.signal?.removeEventListener(\"abort\", abort);\n }\n }\n}\n\n/**\n * 校验浏览器 setTimeout 可表示的正数时长。\n */\nfunction validateDelay(\n value: number\n): void {\n if (!Number.isFinite(value) || value <= 0 || value > 2_147_483_647) {\n throw new XmaxError(XmaxErrorCode.invalidConfiguration, \"Polling durations must be positive and within the timer range\");\n }\n}\n\n/**\n * 统一取消错误,避免误表达为服务端任务取消。\n */\nfunction ensureActive(\n signal: AbortSignal\n): void {\n if (signal.aborted) {\n throw new XmaxError(\n XmaxErrorCode.cancelled,\n \"Task wait was cancelled locally; the server task was not cancelled\",\n );\n }\n}\n\n/**\n * 可取消的轮询间隔,完成后释放定时器及事件监听。\n */\nfunction delay(\n ms: number,\n signal: AbortSignal\n): Promise<void> {\n return new Promise((resolve, reject) => {\n const finish = () => {\n clearTimeout(timer);\n signal.removeEventListener(\"abort\", finish);\n try {\n ensureActive(signal);\n resolve();\n } catch (error) {\n reject(error);\n }\n };\n const timer = setTimeout(finish, ms);\n signal.addEventListener(\"abort\", finish, { once: true });\n if (signal.aborted) {\n finish();\n }\n });\n}\n\n/**\n * 只重试查询中的网络异常、请求超时和没有明确业务拒绝码的服务器故障。\n */\nfunction isTransient(\n error: unknown\n): boolean {\n if (!(error instanceof XmaxError)) {\n return false;\n }\n return error.code === XmaxErrorCode.networkError || error.code === XmaxErrorCode.timeout ||\n (error.code === XmaxErrorCode.apiError && (error.httpStatus ?? 0) >= 500 && (error.apiCode === undefined || error.apiCode === error.httpStatus));\n}\n\n/**\n * 完成必须有 HTTP(S) 结果地址,且没有明确的上传失败信息。\n */\nfunction isResultAvailable(\n task: NonRealtimeTask\n): boolean {\n const result = task.result;\n if (\n !result?.url?.trim() ||\n result.uploadError?.trim() ||\n (result.uploadStatus !== undefined && result.uploadStatus !== \"success\")\n ) {\n return false;\n }\n try {\n const url = new URL(result.url);\n return [\"http:\", \"https:\"].includes(url.protocol) && !url.username && !url.password;\n } catch {\n return false;\n }\n}\n","//\n// XmaxClient.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\nimport { XmaxLogger } from \"../Foundation/Logging/XmaxLogger\";\nimport type { ApiServicing } from \"../Service/Network/ApiServicing\";\nimport { ApiService } from \"../Service/Network/ApiService\";\nimport type { MediaServicing } from \"../Service/Media/MediaServicing\";\nimport { MediaService } from \"../Service/Media/MediaService\";\nimport type { StorageServicing } from \"../Service/Storage/StorageServicing\";\nimport { StorageService } from \"../Service/Storage/StorageService\";\nimport { RealtimeModel, modelBaseURL, type RealtimeModelName } from \"../Service/Realtime/RealtimeModel\";\nimport type { RealtimeConfiguration } from \"./Realtime/RealtimeConfiguration\";\nimport { XmaxRealtimeManager } from \"./Realtime/XmaxRealtimeManager\";\nimport type { XmaxRealtimeManaging } from \"./Realtime/XmaxRealtimeManaging\";\nimport { XmaxConfiguration } from \"./XmaxConfiguration\";\nimport { apiBaseURL, realtimeApiBaseURL, nonRealtimeApiBaseURL } from \"../Foundation/Runtime/XmaxEnvironment\";\nimport { NonRealtimeTaskService } from \"../Service/NonRealtime/NonRealtimeTaskService\";\nimport { XmaxNonRealtimeManager } from \"./NonRealtime/XmaxNonRealtimeManager\";\nimport type { XmaxNonRealtimeManaging } from \"./NonRealtime/XmaxNonRealtimeManaging\";\n\n/**\n * SDK 的统一入口,负责创建实时、非实时和媒体服务组件。\n */\nexport class XmaxClient {\n\n /**\n * 客户端使用的全局配置。\n */\n readonly configuration: XmaxConfiguration;\n\n /**\n * 服务层组件\n */\n private readonly apiService: ApiServicing;\n\n /**\n * 创建 SDK 客户端。\n *\n * 本地相机预览不依赖 API Key 校验;连接与生成等涉及服务端的操作\n * 在实际调用时校验 API Key。\n */\n constructor(\n configuration: XmaxConfiguration\n ) {\n this.configuration = configuration;\n XmaxLogger.configure(\n configuration.loggerOptions,\n configuration.environment,\n );\n this.apiService = new ApiService({\n apiKey: configuration.apiKey,\n baseURL: apiBaseURL(configuration.environment),\n });\n }\n\n /**\n * 创建实时媒体 Manager。\n * 已知模型按环境选择实时会话地址;自定义模型使用测试地址,覆盖整个会话生命周期。\n *\n * @param options 实时生成模型等业务配置。\n * @returns 可用于本地相机预览、实时连接与生成的实时 Manager。\n */\n createRealtimeManager(\n options: RealtimeConfiguration\n ): XmaxRealtimeManaging {\n const baseURL = modelBaseURL(options.model) ?? realtimeApiBaseURL(this.configuration.environment);\n const apiService = new ApiService({ apiKey: this.configuration.apiKey, baseURL });\n\n return new XmaxRealtimeManager(options, {\n apiService,\n environment: this.configuration.environment,\n });\n }\n\n /**\n * 创建非实时视频任务 Manager,使用全局 API Key 和当前环境的非实时任务服务地址。\n * 本方法不发起网络请求或加载 RTC,任务由服务端持久化,可按 ID 恢复查询。\n */\n createNonRealtimeManager(): XmaxNonRealtimeManaging {\n const apiService = new ApiService({\n apiKey: this.configuration.apiKey,\n baseURL: nonRealtimeApiBaseURL(this.configuration.environment),\n });\n\n return new XmaxNonRealtimeManager(new NonRealtimeTaskService(apiService));\n }\n\n /**\n * 创建媒体处理与能力查询 Service。\n *\n * @param model 媒体输入规则使用的模型,默认为 `x2.1`。\n */\n createMediaService(\n model: RealtimeModelName = RealtimeModel.x2_1\n ): MediaServicing {\n return new MediaService(model);\n }\n\n /**\n * 创建文件存储 Service。\n *\n * @returns 可上传图片和视频到对象存储的存储 Service。\n */\n createStorageService(): StorageServicing {\n return new StorageService({ apiService: this.apiService });\n }\n}\n","//\n// RealtimeConfiguration.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\nimport { supportedRtcProviders, type RealtimeModelName } from \"../../Service/Realtime/RealtimeModel\";\nimport { RtcProvider } from \"../../Foundation/RTC/RtcProvider\";\nimport { XmaxError, XmaxErrorCode } from \"../../Foundation/Errors/XmaxError\";\n\n/**\n * 创建实时 Manager 所需的业务配置。\n */\nexport class RealtimeConfiguration {\n\n /**\n * 实时生成使用的模型;未知名称原样透传到测试环境,媒体与 RTC 默认配置同 X2.1。\n */\n readonly model: RealtimeModelName;\n\n /**\n * 实时连接使用的 RTC 提供方,默认取模型支持列表的首项,创建后不可切换。\n */\n readonly provider: RtcProvider;\n\n /**\n * 是否自动调整上下行档位,默认 true。关闭时从 L1 启动且不读写历史档位,仍支持手动调档。\n */\n readonly isAdaptiveQualityEnabled: boolean;\n\n /**\n * 补全 RTC 和自动调档默认值,并校验模型名称与 RTC 提供方配置。\n */\n constructor(\n init: {\n model: RealtimeModelName;\n provider?: RtcProvider;\n isAdaptiveQualityEnabled?: boolean;\n }\n ) {\n const providers = supportedRtcProviders(init.model);\n this.provider = init.provider ?? providers[0]!;\n if (!Object.values(RtcProvider).includes(this.provider)) {\n throw new XmaxError(XmaxErrorCode.invalidConfiguration, \"Unsupported RTC provider\");\n }\n if (!providers.includes(this.provider)) {\n throw new XmaxError(\n XmaxErrorCode.invalidConfiguration,\n `Model ${init.model} does not support RTC provider ${this.provider}`,\n );\n }\n this.model = init.model;\n\n this.isAdaptiveQualityEnabled = init.isAdaptiveQualityEnabled ?? true;\n }\n}\n","//\n// RenderStatisticsController.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\nimport type { VideoRenderStatistics } from \"./VideoRenderStatistics\";\n\n/**\n * 单个视频视图的呈现统计采集器,管理帧观察、采样窗口和统计回调。\n * 不控制视频播放,仅观察浏览器呈现的视频帧。 @internal\n */\nexport class RenderStatisticsController {\n\n /**\n * 统计回调\n */\n handler?: (\n statistics?: VideoRenderStatistics\n ) => void;\n\n /**\n * 帧观察与采样生命周期\n */\n private timer?: ReturnType<typeof setInterval>;\n\n /**\n * 当前视频帧回调的取消句柄。\n */\n private callbackID?: number;\n\n /**\n * 当前统计周期标识,阻止旧回调继续累计。\n */\n private sequence = 0;\n\n /**\n * 采样窗口与输出方式\n */\n private startedAt = 0;\n\n /**\n * 当前统计窗口累计的原始帧数。\n */\n private originalFrames = 0;\n\n /**\n * 上一次观察到的浏览器累计呈现帧数。\n */\n private previousPresentedFrames?: number;\n\n /**\n * 绑定用于观察原视频帧的元素,调用 start 后才开始采集。\n */\n constructor(\n private readonly video: HTMLVideoElement\n ) { }\n\n /**\n * 独立于统计面板可见性采集;没有监听者时不占用定时器。\n */\n start(): void {\n if (this.timer !== undefined ||\n (!this.video.srcObject && !this.video.getAttribute(\"src\")) ||\n !this.handler ||\n typeof this.video.requestVideoFrameCallback !== \"function\") {\n return;\n }\n\n this.reset();\n const sequence = this.sequence;\n const observe = () => {\n this.callbackID = this.video.requestVideoFrameCallback((_now, metadata) => {\n if (sequence !== this.sequence) {\n return;\n }\n this.callbackID = undefined;\n if (!document.hidden) {\n this.recordVideoFrames(metadata.presentedFrames);\n } else {\n // 页面隐藏期间的视频帧不归入后续可见窗口。\n this.previousPresentedFrames = undefined;\n }\n observe();\n });\n };\n observe();\n this.timer = setInterval(() => this.sample(), 1_000);\n }\n\n /**\n * 按浏览器累计呈现帧数的增量统计,包含两次回调之间已呈现但未回调的帧。\n */\n private recordVideoFrames(\n presentedFrames: number\n ): void {\n if (!Number.isSafeInteger(presentedFrames) || presentedFrames < 0) {\n this.previousPresentedFrames = undefined;\n return;\n }\n\n const previous = this.previousPresentedFrames;\n this.previousPresentedFrames = presentedFrames;\n\n // 首次观察或计数回退时只计当前帧,不能把此前播放的累计帧数加进来。\n if (previous === undefined || presentedFrames < previous) {\n this.originalFrames += presentedFrames > 0 ? 1 : 0;\n return;\n }\n\n this.originalFrames += presentedFrames - previous;\n }\n\n /**\n * 取消采样及帧观察,忽略已排队的旧回调并清空对外统计。\n */\n stop(): void {\n this.sequence++;\n clearInterval(this.timer);\n this.timer = undefined;\n if (this.callbackID !== undefined) {\n this.video.cancelVideoFrameCallback(this.callbackID);\n }\n this.callbackID = undefined;\n this.reset();\n }\n\n /**\n * 清空采样计数,避免换流或切换输出方式时混入旧数据。\n */\n private reset(): void {\n this.startedAt = performance.now();\n this.originalFrames = 0;\n this.previousPresentedFrames = undefined;\n this.notify(undefined);\n }\n\n /**\n * 按实际经过的时间计算帧率;没有新呈现时输出 0,后台页面不报告帧率。\n */\n private sample(): void {\n const now = performance.now();\n const seconds = (now - this.startedAt) / 1000;\n if (document.hidden || seconds <= 0) {\n this.reset();\n return;\n }\n\n const statistics: VideoRenderStatistics = Object.freeze({\n source: \"video\",\n frameRate: this.originalFrames / seconds,\n originalFrameRate: this.originalFrames / seconds,\n });\n this.startedAt = now;\n this.originalFrames = 0;\n this.notify(statistics);\n }\n\n /**\n * 隔离接入方回调异常,避免影响播放和后续采样。\n */\n private notify(\n statistics?: VideoRenderStatistics\n ): void {\n try {\n this.handler?.(statistics);\n } catch { }\n }\n}\n","//\n// RenderWarmupController.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\nimport { XmaxError, XmaxErrorCode } from \"../../Foundation/Errors/XmaxError\";\n\n/**\n * 首帧后保持视频播放,等待预热结束再渐入;不缓存或改写媒体帧。\n */\nexport class RenderWarmupController {\n\n /**\n * 预热配置与生命周期\n */\n private durationMs?: number;\n\n /**\n * 当前等待或定时任务的取消句柄。\n */\n private timer?: ReturnType<typeof setTimeout>;\n\n /**\n * 是否已经开始渲染预热。\n */\n private started = false;\n\n /**\n * 是否已经完成本次渲染预热。\n */\n private completed = false;\n\n /**\n * 下一次渐入动画的时长,单位为毫秒。\n */\n private currentFadeInDurationMs = 300;\n\n /**\n * 创建视图预热控制器,渐入配置不改变首帧或预热完成的时机。\n *\n * @param element 需要控制预热显隐和渐入的媒体显示层。\n * @param fadeInDurationMs 预热完成后的渐入时长,单位为毫秒,默认 300;0 表示直接显示。\n * @throws 时长为负数或非有限数值时抛出 invalidConfiguration。\n */\n constructor(\n private readonly element: HTMLElement,\n fadeInDurationMs = 300\n ) {\n this.fadeInDurationMs = fadeInDurationMs;\n }\n\n /**\n * 当前配置的渐入时长,单位为毫秒。\n */\n get fadeInDurationMs(): number {\n return this.currentFadeInDurationMs;\n }\n\n /**\n * 更新下一次渐入时长,不修改正在播放的动画或首帧预热。\n */\n set fadeInDurationMs(\n fadeInDurationMs: number\n ) {\n if (!Number.isFinite(fadeInDurationMs) || fadeInDurationMs < 0) {\n throw new XmaxError(XmaxErrorCode.invalidConfiguration, \"fadeInDurationMs must be a finite non-negative number\");\n }\n this.currentFadeInDurationMs = fadeInDurationMs;\n }\n\n /**\n * 媒体内容是否仍在等待首帧或预热完成。\n */\n get isPending(): boolean {\n return this.durationMs !== undefined && !this.completed;\n }\n\n /**\n * 为新媒体隐藏画面;undefined 表示直接显示,不启用预热。\n */\n prepare(\n durationMs?: number\n ): void {\n this.reset();\n this.durationMs = durationMs;\n if (durationMs === undefined) {\n return;\n }\n\n this.element.style.transition = \"none\";\n this.element.style.opacity = \"0\";\n this.element.style.pointerEvents = \"none\";\n }\n\n /**\n * 首帧就绪后开始一次性预热,结束时通知并渐入画面。\n */\n start(\n onCompleted: () => void\n ): void {\n if (this.durationMs === undefined || this.started) {\n return;\n }\n\n this.started = true;\n this.timer = setTimeout(() => {\n this.timer = undefined;\n this.completed = true;\n const reduceMotion = globalThis.matchMedia?.(\"(prefers-reduced-motion: reduce)\").matches;\n this.element.style.transition = reduceMotion || this.fadeInDurationMs === 0\n ? \"none\" : `opacity ${this.fadeInDurationMs}ms ease-in-out`;\n this.element.style.opacity = \"1\";\n this.element.style.pointerEvents = \"\";\n onCompleted();\n }, this.durationMs);\n }\n\n /**\n * 换流或卸载时取消预热和渐入,防止旧任务显示新画面。\n */\n reset(): void {\n clearTimeout(this.timer);\n this.timer = undefined;\n this.started = false;\n this.completed = false;\n if (this.durationMs !== undefined) {\n this.element.style.transition = \"\";\n this.element.style.opacity = \"\";\n this.element.style.pointerEvents = \"\";\n }\n this.durationMs = undefined;\n }\n}\n","//\n// TrajectoryOverlayView.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\nimport { VideoContentMode } from \"../../Foundation/Media/Video/VideoContentMode\";\nimport { XmaxLogger } from \"../../Foundation/Logging/XmaxLogger\";\nimport { InteractionCoordinateMapper } from \"../../Media/Interaction/InteractionCoordinateMapper\";\nimport type { RealtimeVideoFormat } from \"../../Service/Realtime/RealtimeVideoFormat\";\nimport type { TrajectoryBinding } from \"./TrajectoryBinding\";\nimport type { TrajectoryEffectRendering } from \"./TrajectoryEffectRendering\";\nimport type { TrajectoryPoint } from \"./TrajectoryPoint\";\n\n/**\n * 采集鼠标、触屏和触控笔输入,驱动效果并以约 30Hz 提交视口采样。\n */\nexport class TrajectoryOverlayView {\n\n /**\n * 视图与显示配置\n */\n readonly element = document.createElement(\"div\");\n\n /**\n * 当前轨道提供的交互绑定。\n */\n private binding?: TrajectoryBinding;\n\n /**\n * 交互坐标换算使用的固定媒体规格。\n */\n private format?: RealtimeVideoFormat;\n\n /**\n * 画面在视口中的缩放与裁剪方式。\n */\n private contentMode = VideoContentMode.fill;\n\n /**\n * 是否对显示画面应用水平镜像。\n */\n private mirrored = false;\n\n /**\n * 接入方是否请求启用轨迹交互。\n */\n private requested = true;\n\n /**\n * 当前绑定是否允许提交交互采样。\n */\n private enabled = false;\n\n /**\n * 手势采样与生命周期资源\n */\n private readonly pointers = new Map<number, TrajectoryPoint>();\n\n /**\n * 下一条轨迹使用的标识。\n */\n private nextID = 0;\n\n /**\n * 下一次动画刷新任务的句柄。\n */\n private animation?: number;\n\n /**\n * 等待按显示节拍提交的最新触点快照。\n */\n private lastSample = 0;\n\n /**\n * 同步交互层视口尺寸的观察器。\n */\n private resizeObserver?: ResizeObserver;\n\n /**\n * 当前交互视口的 CSS 像素尺寸。\n */\n private viewport?: {\n width: number;\n height: number\n };\n\n /**\n * 页面隐藏时结束活动轨迹并清理采样。\n */\n private readonly visibilityChanged = () => {\n if (document.hidden) {\n this.cancel();\n }\n this.updateStyle();\n };\n\n /**\n * 处理触点按下并创建新轨迹。\n */\n private readonly onDown = (event: PointerEvent) => this.begin(event);\n\n /**\n * 处理触点移动并更新采样。\n */\n private readonly onMove = (event: PointerEvent) => this.move(event);\n\n /**\n * 结束触点及对应轨迹。\n */\n private readonly onEnd = (event: PointerEvent) => this.end(event.pointerId);\n\n /**\n * 安装透明交互层及独立 Renderer;只有任务就绪时接管指针。\n */\n constructor(\n private readonly renderer: TrajectoryEffectRendering\n ) {\n Object.assign(this.element.style, { position: \"absolute\", inset: \"0\", zIndex: \"2\", overflow: \"hidden\" });\n Object.assign(renderer.element.style, { position: \"absolute\", inset: \"0\", width: \"100%\", height: \"100%\", pointerEvents: \"none\" });\n this.element.appendChild(renderer.element);\n this.element.addEventListener(\"pointerdown\", this.onDown);\n this.element.addEventListener(\"pointermove\", this.onMove);\n this.element.addEventListener(\"pointerup\", this.onEnd);\n this.element.addEventListener(\"pointercancel\", this.onEnd);\n this.element.addEventListener(\"lostpointercapture\", this.onEnd);\n document.addEventListener(\"visibilitychange\", this.visibilityChanged);\n if (typeof ResizeObserver !== \"undefined\") {\n this.resizeObserver = new ResizeObserver(() => this.cancel());\n this.resizeObserver.observe(this.element);\n }\n this.updateStyle();\n }\n\n /**\n * 更新任务绑定,清除旧任务手势和采样。\n */\n setBinding(\n binding?: TrajectoryBinding,\n format?: RealtimeVideoFormat,\n enabled = false\n ): void {\n this.cancel();\n this.binding = binding;\n this.format = format;\n this.enabled = enabled;\n this.updateStyle();\n }\n\n /**\n * 同步显示配置;配置变化后旧坐标不再继续发送。\n */\n configure(\n mode: VideoContentMode,\n mirrored: boolean,\n enabled: boolean\n ): void {\n if (this.contentMode !== mode || this.mirrored !== mirrored || this.requested !== enabled) {\n this.cancel();\n }\n this.contentMode = mode;\n this.mirrored = mirrored;\n this.requested = enabled;\n this.updateStyle();\n }\n\n /**\n * 卸载时取消采样、动画、捕获和事件监听;Renderer 可以再次挂载。\n */\n dispose(): void {\n this.binding?.detach(this);\n this.cancel();\n this.resizeObserver?.disconnect();\n document.removeEventListener(\"visibilitychange\", this.visibilityChanged);\n this.element.removeEventListener(\"pointerdown\", this.onDown);\n this.element.removeEventListener(\"pointermove\", this.onMove);\n this.element.removeEventListener(\"pointerup\", this.onEnd);\n this.element.removeEventListener(\"pointercancel\", this.onEnd);\n this.element.removeEventListener(\"lostpointercapture\", this.onEnd);\n this.element.remove();\n }\n\n /**\n * 无任务或被禁用时不阻止页面滚动。\n */\n private updateStyle(): void {\n const active = this.requested && this.enabled && !!this.binding && !document.hidden;\n this.element.style.pointerEvents = active ? \"auto\" : \"none\";\n this.element.style.touchAction = active ? \"none\" : \"auto\";\n this.element.style.userSelect = active ? \"none\" : \"auto\";\n }\n\n /**\n * 根据实际显示区域拒绝黑边,移动到视口外时限制在可见视频边缘。\n */\n private point(\n event: PointerEvent,\n id: number,\n clamp: boolean\n ): TrajectoryPoint | undefined {\n const viewport = this.element.getBoundingClientRect();\n if (!this.format) {\n return undefined;\n }\n const rect = InteractionCoordinateMapper.displayedFrame(viewport, this.format, this.contentMode);\n if (!rect || !Number.isFinite(event.clientX) || !Number.isFinite(event.clientY)) {\n return undefined;\n }\n const minX = Math.max(0, rect.x);\n const minY = Math.max(0, rect.y);\n const maxX = Math.min(viewport.width, rect.x + rect.width);\n const maxY = Math.min(viewport.height, rect.y + rect.height);\n let x = event.clientX - viewport.left;\n let y = event.clientY - viewport.top;\n if (!clamp && (x < minX || x > maxX || y < minY || y > maxY)) {\n return undefined;\n }\n x = Math.max(minX, Math.min(maxX, x));\n y = Math.max(minY, Math.min(maxY, y));\n return Object.freeze({\n id,\n location: Object.freeze({ x, y }),\n normalizedLocation: Object.freeze({ x: (x - rect.x) / rect.width, y: (y - rect.y) / rect.height }),\n timestamp: performance.now(),\n });\n }\n\n /**\n * 按下立即采样,不等待定时窗口;只接受鼠标主键。\n */\n private begin(\n event: PointerEvent\n ): void {\n if (!this.requested || !this.enabled || !this.binding || document.hidden || event.button !== 0) {\n return;\n }\n const point = this.point(event, ++this.nextID, false);\n if (!point) {\n return;\n }\n event.preventDefault();\n this.pointers.set(event.pointerId, point);\n try {\n this.element.setPointerCapture(event.pointerId);\n } catch {\n // 指针可能已被浏览器取消;仍由 pointercancel 或视图卸载结束手势。\n }\n this.render(() => this.renderer.renderBegan([point]));\n this.sample(performance.now(), true);\n this.schedule();\n }\n\n /**\n * 高频指针事件只更新最新位置及视觉效果,不直接发送网络消息。\n */\n private move(\n event: PointerEvent\n ): void {\n const previous = this.pointers.get(event.pointerId);\n if (!previous) {\n return;\n }\n const point = this.point(event, previous.id, true);\n if (point) {\n this.pointers.set(event.pointerId, point);\n this.render(() => this.renderer.renderMoved([point]));\n }\n }\n\n /**\n * 释放单个指针,多指交互继续;最后一个指针释放后停止采样。\n */\n private end(\n pointerID: number\n ): void {\n const point = this.pointers.get(pointerID);\n if (!point) {\n return;\n }\n this.pointers.delete(pointerID);\n try {\n this.element.releasePointerCapture(pointerID);\n } catch {\n // 浏览器可能已在 pointerup 或取消事件中释放捕获。\n }\n this.render(() => this.renderer.renderEnded([point.id]));\n if (!this.pointers.size) {\n this.stopSampling();\n }\n }\n\n /**\n * 取消全部手势及视觉效果,不保留跨生命周期轨迹。\n */\n private cancel(): void {\n for (const id of [...this.pointers.keys()]) {\n this.end(id);\n }\n this.stopSampling();\n this.render(() => this.renderer.reset());\n }\n\n /**\n * 清理采样回调与视口快照。\n */\n private stopSampling(): void {\n if (this.animation !== undefined) {\n cancelAnimationFrame(this.animation);\n }\n this.animation = undefined;\n this.viewport = undefined;\n this.lastSample = 0;\n }\n\n /**\n * 只在存在活动触点时运行动画时钟。\n */\n private schedule(): void {\n if (this.animation === undefined && this.pointers.size) {\n this.animation = requestAnimationFrame(now => {\n this.animation = undefined;\n this.sample(now);\n this.schedule();\n });\n }\n }\n\n /**\n * 固定频率发送所有活动触点的快照,布局变化时取消而非发送旧坐标。\n */\n private sample(\n now: number,\n force = false\n ): void {\n if (document.hidden) {\n this.cancel();\n return;\n }\n const { width, height } = this.element.getBoundingClientRect();\n if (this.viewport && (width !== this.viewport.width || height !== this.viewport.height)) {\n this.cancel();\n return;\n }\n this.viewport = { width, height };\n if (!force && now - this.lastSample < 1000 / 30) {\n return;\n }\n this.lastSample = now;\n this.binding?.submit({\n points: [...this.pointers.values()].map(point => point.location),\n viewportSize: this.viewport,\n contentMode: this.contentMode,\n mirrored: this.mirrored,\n });\n }\n\n /**\n * 自定义效果异常不影响触点采集和网络发送。\n */\n private render(\n action: () => void\n ): void {\n try {\n action();\n } catch {\n XmaxLogger.render.warning(() => \"轨迹效果绘制失败 (Trajectory Rendering Failed)\");\n }\n }\n}\n","//\n// DefaultTrajectoryEffectRenderer.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\nimport type { TrajectoryEffectRendering } from \"./TrajectoryEffectRendering\";\nimport type { TrajectoryID, TrajectoryPoint } from \"./TrajectoryPoint\";\n\n/**\n * 轨迹外晕、中层、白色线芯及触点装饰的 CSS 像素规格。\n */\nconst VISUAL = {\n outerWidth: 18, outerBlur: 12, outerAlpha: 0.35,\n middleWidth: 10, middleBlur: 6, middleAlpha: 0.75,\n coreWidth: 3,\n headRadius: 5, headGlowRadius: 14, headGlowAlpha: 0.9,\n ringCount: 2, ringStartRadius: 10, ringSpacing: 14,\n ringLineWidth: 2, ringPulseSpeed: 1.2, ringMaxAlpha: 0.6,\n particleCount: 4, particleRadius: 3, particleOrbitRadius: 22,\n particleSpeed: 0.06, particleAlpha: 0.6,\n fadeAlpha: 0.05, idleFadeFrames: 64,\n} as const;\n\n/**\n * 绿色发光尾迹与触点装饰;仅负责绘制,不参与采样或信令发送。\n */\nexport class DefaultTrajectoryEffectRenderer implements TrajectoryEffectRendering {\n\n /**\n * 画布与动画资源;尾迹保留像素渐隐,触点装饰每帧重绘。\n */\n readonly element = document.createElement(\"div\");\n\n /**\n * 绘制轨迹线条的画布。\n */\n private readonly trailCanvas = document.createElement(\"canvas\");\n\n /**\n * 绘制触点光圈的画布。\n */\n private readonly effectCanvas = document.createElement(\"canvas\");\n\n /**\n * 下一次动画刷新任务的句柄。\n */\n private animation?: number;\n\n /**\n * 轨迹画布中是否仍有待淡出的像素。\n */\n private hasTrailPixels = false;\n\n /**\n * 无活动触点后已经执行的淡出帧数。\n */\n private idleFadeFrames = 0;\n\n /**\n * 活动触点和等待下一帧绘制的线段。\n */\n private readonly pointers = new Map<TrajectoryID, {\n point: TrajectoryPoint;\n startedAt: number\n }>();\n\n /**\n * 等待绘制的轨迹线段。\n */\n private readonly segments: Array<{\n from: TrajectoryPoint;\n to: TrajectoryPoint\n }> = [];\n\n /**\n * 安装独立视觉图层,不注册指针事件。\n */\n constructor() {\n for (const canvas of [this.trailCanvas, this.effectCanvas]) {\n Object.assign(canvas.style, { position: \"absolute\", inset: \"0\", width: \"100%\", height: \"100%\", pointerEvents: \"none\" });\n this.element.appendChild(canvas);\n }\n }\n\n /**\n * 开始触点装饰,脉冲动画以本次按下时间为起点。\n */\n renderBegan(\n points: readonly TrajectoryPoint[]\n ): void {\n for (const point of points) {\n this.pointers.set(point.id, { point, startedAt: point.timestamp });\n }\n this.idleFadeFrames = 0;\n this.schedule();\n }\n\n /**\n * 只绘制新线段,旧像素自然衰减,不反复加亮整条历史轨迹。\n */\n renderMoved(\n points: readonly TrajectoryPoint[]\n ): void {\n for (const point of points) {\n const pointer = this.pointers.get(point.id);\n if (pointer) {\n this.segments.push({ from: pointer.point, to: point });\n pointer.point = point;\n }\n }\n this.schedule();\n }\n\n /**\n * 释放触点后移除装饰,仅保留尾迹的渐隐动画。\n */\n renderEnded(\n identifiers: readonly TrajectoryID[]\n ): void {\n for (const id of identifiers) {\n this.pointers.delete(id);\n }\n this.schedule();\n }\n\n /**\n * 清除画面、待绘制数据及动画,允许同一 Renderer 再次挂载。\n */\n reset(): void {\n if (this.animation !== undefined) {\n cancelAnimationFrame(this.animation);\n }\n this.animation = undefined;\n this.pointers.clear();\n this.segments.length = 0;\n this.hasTrailPixels = false;\n this.idleFadeFrames = 0;\n for (const canvas of [this.trailCanvas, this.effectCanvas]) {\n canvas.getContext(\"2d\")?.clearRect(0, 0, canvas.width, canvas.height);\n }\n }\n\n /**\n * 仅在触点、待绘制线段或残留尾迹存在时申请下一帧。\n */\n private schedule(): void {\n if (this.animation === undefined && (this.pointers.size || this.segments.length || this.hasTrailPixels)) {\n this.animation = requestAnimationFrame(now => this.draw(now));\n }\n }\n\n /**\n * 同步画布分辨率,分别更新尾迹和触点视觉图层。\n */\n private draw(\n now: number\n ): void {\n this.animation = undefined;\n const trail = this.trailCanvas.getContext(\"2d\");\n const effects = this.effectCanvas.getContext(\"2d\");\n if (!trail || !effects) {\n this.reset();\n return;\n }\n\n const rect = this.element.getBoundingClientRect();\n const dpr = globalThis.devicePixelRatio || 1;\n const width = Math.max(1, Math.round(rect.width * dpr));\n const height = Math.max(1, Math.round(rect.height * dpr));\n for (const canvas of [this.trailCanvas, this.effectCanvas]) {\n if (canvas.width !== width || canvas.height !== height) {\n canvas.width = width;\n canvas.height = height;\n this.hasTrailPixels = false;\n }\n }\n\n if (this.hasTrailPixels) {\n trail.save();\n trail.globalCompositeOperation = \"destination-out\";\n trail.fillStyle = \"rgba(0,0,0,\" + VISUAL.fadeAlpha + \")\";\n trail.fillRect(0, 0, width, height);\n trail.restore();\n }\n if (this.segments.length) {\n this.drawSegments(trail, dpr);\n this.segments.length = 0;\n this.hasTrailPixels = true;\n this.idleFadeFrames = 0;\n }\n\n effects.clearRect(0, 0, width, height);\n effects.save();\n effects.globalCompositeOperation = \"lighter\";\n for (const { point, startedAt } of this.pointers.values()) {\n const x = point.location.x * dpr;\n const y = point.location.y * dpr;\n if (this.pointers.size === 1) {\n this.drawRings(effects, x, y, dpr, (now - startedAt) / 1000);\n this.drawParticles(effects, x, y, dpr, (now - startedAt) / 1000);\n }\n this.drawGlow(effects, x, y, VISUAL.headGlowRadius * dpr, VISUAL.headGlowAlpha, true);\n effects.fillStyle = \"rgba(255,255,255,1)\";\n effects.beginPath();\n effects.arc(x, y, VISUAL.headRadius * dpr, 0, Math.PI * 2);\n effects.fill();\n }\n effects.restore();\n\n if (this.pointers.size) {\n this.idleFadeFrames = 0;\n } else if (this.hasTrailPixels && ++this.idleFadeFrames > VISUAL.idleFadeFrames) {\n trail.clearRect(0, 0, width, height);\n this.hasTrailPixels = false;\n this.idleFadeFrames = 0;\n }\n this.schedule();\n }\n\n /**\n * 叠加绿色外晕、中层和白色线芯;多指时省略阴影降低绘制开销。\n */\n private drawSegments(\n context: CanvasRenderingContext2D,\n dpr: number\n ): void {\n const layers = [\n { width: VISUAL.outerWidth, blur: VISUAL.outerBlur, color: this.green(VISUAL.outerAlpha) },\n { width: VISUAL.middleWidth, blur: VISUAL.middleBlur, color: this.green(VISUAL.middleAlpha) },\n { width: VISUAL.coreWidth, blur: 0, color: \"rgba(255,255,255,1)\" },\n ];\n for (const layer of layers) {\n context.save();\n context.globalCompositeOperation = \"lighter\";\n context.lineCap = \"round\";\n context.lineJoin = \"round\";\n context.lineWidth = layer.width * dpr;\n context.strokeStyle = layer.color;\n context.shadowBlur = this.pointers.size > 1 ? 0 : layer.blur * dpr;\n context.shadowColor = context.shadowBlur ? layer.color : \"transparent\";\n context.beginPath();\n for (const { from, to } of this.segments) {\n context.moveTo(from.location.x * dpr, from.location.y * dpr);\n context.lineTo(to.location.x * dpr, to.location.y * dpr);\n }\n context.stroke();\n context.restore();\n }\n }\n\n /**\n * 绘制触点周围两层不同相位的脉冲圆环。\n */\n private drawRings(\n context: CanvasRenderingContext2D,\n x: number,\n y: number,\n dpr: number,\n elapsed: number\n ): void {\n context.lineWidth = VISUAL.ringLineWidth * dpr;\n for (let index = 0; index < VISUAL.ringCount; index++) {\n const pulse = (Math.sin((elapsed * VISUAL.ringPulseSpeed + index * 0.5) * Math.PI * 2) + 1) / 2;\n const radius = (VISUAL.ringStartRadius + index * VISUAL.ringSpacing + pulse * 6) * dpr;\n context.strokeStyle = this.green(VISUAL.ringMaxAlpha * (1 - index * 0.2) * (0.5 + pulse * 0.5));\n context.beginPath();\n context.arc(x, y, radius, 0, Math.PI * 2);\n context.stroke();\n }\n }\n\n /**\n * 绘制四个反向交错运动的柔和环绕粒子。\n */\n private drawParticles(\n context: CanvasRenderingContext2D,\n x: number,\n y: number,\n dpr: number,\n elapsed: number\n ): void {\n for (let index = 0; index < VISUAL.particleCount; index++) {\n const direction = index % 2 === 0 ? 1 : -1;\n const angle = index / VISUAL.particleCount * Math.PI * 2 + elapsed * VISUAL.particleSpeed * direction;\n const radius = VISUAL.particleOrbitRadius * dpr;\n const alpha = VISUAL.particleAlpha * (0.6 + Math.sin(elapsed * 3 + index) * 0.4);\n this.drawGlow(context, x + Math.cos(angle) * radius, y + Math.sin(angle) * radius, VISUAL.particleRadius * 2 * dpr, alpha);\n }\n }\n\n /**\n * 绘制径向绿色光晕,触点核心增加中间色阶。\n */\n private drawGlow(\n context: CanvasRenderingContext2D,\n x: number,\n y: number,\n radius: number,\n alpha: number,\n head = false\n ): void {\n const gradient = context.createRadialGradient(x, y, 0, x, y, radius);\n gradient.addColorStop(0, this.green(alpha));\n if (head) {\n gradient.addColorStop(0.4, this.green(alpha * 0.6));\n }\n gradient.addColorStop(1, this.green(0));\n context.fillStyle = gradient;\n context.beginPath();\n context.arc(x, y, radius, 0, Math.PI * 2);\n context.fill();\n }\n\n /**\n * 统一轨迹主题色,透明度由各视觉层指定。\n */\n private green(\n alpha: number\n ): string {\n return \"rgba(0,255,100,\" + alpha + \")\";\n }\n}\n","//\n// XmaxRealtimeVideoView.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\nimport { VideoContentMode } from \"../../Foundation/Media/Video/VideoContentMode\";\nimport { XmaxLogger } from \"../../Foundation/Logging/XmaxLogger\";\nimport type { VideoRenderStatistics } from \"./VideoRenderStatistics\";\nimport { RenderStatisticsController } from \"./RenderStatisticsController\";\nimport { RenderWarmupController } from \"./RenderWarmupController\";\nimport type { RealtimeVideoTrack } from \"../../Service/Realtime/RealtimeVideoTrack\";\nimport { VideoRenderRegistry } from \"../../Service/Realtime/VideoRenderBinding\";\nimport { TrajectoryRegistry } from \"../Trajectory/TrajectoryRegistry\";\nimport { TrajectoryOverlayView } from \"../Trajectory/TrajectoryOverlayView\";\nimport { DefaultTrajectoryEffectRenderer } from \"../Trajectory/DefaultTrajectoryEffectRenderer\";\nimport type { TrajectoryEffectRendering } from \"../Trajectory/TrajectoryEffectRendering\";\n\n/**\n * SDK 视频显示模式与浏览器缩放样式的对应关系。\n */\nconst OBJECT_FIT: Record<VideoContentMode, \"contain\" | \"cover\"> = {\n [VideoContentMode.fit]: \"contain\",\n [VideoContentMode.fill]: \"cover\",\n};\n\n/**\n * 显示一路本地或远端视频轨道的实时视频视图(框架无关)。\n *\n * 通过 `attach(to:)` 挂载到任意容器元素;设置 `track` 后自动渲染。\n * 轨道注册了渲染绑定时走绑定管线,否则按 `mediaStreamTrack` 直接渲染。\n * SDK 远端轨道在首帧后按绑定配置预热,再按视图配置渐入;本地预览不延迟。\n */\nexport class XmaxRealtimeVideoView {\n\n /**\n * 视图根元素(div,内含一个 video 元素)。\n */\n readonly element: HTMLDivElement;\n\n /**\n * @internal 渲染视频帧的元素,供绑定管线使用。\n */\n readonly videoElement: HTMLVideoElement;\n\n /**\n * 视频显示层,与触控轨迹层独立。\n */\n private readonly videoLayer: HTMLDivElement;\n\n /**\n * 首帧预热只影响媒体内容,不影响轨迹交互层。\n */\n private readonly warmupLayer: HTMLDivElement;\n\n /**\n * 视频画面是否可见。\n */\n private videoVisible = true;\n\n /**\n * 首帧实际提交显示时调用(一次性;换轨后重新武装)。\n */\n frameDisplayHandler?: () => void;\n\n /**\n * 渲染状态\n */\n private currentTrack?: RealtimeVideoTrack;\n\n /**\n * 画面在视口中的缩放与裁剪方式。\n */\n private contentMode: VideoContentMode;\n\n /**\n * 是否对显示画面应用水平镜像。\n */\n private mirrored = false;\n\n /**\n * 首帧显示通知与异步回调版本\n */\n private hasNotifiedFrameDisplay = false;\n\n /**\n * 视频开始播放时尝试通知首帧就绪。\n */\n private onPlaying?: () => void;\n\n /**\n * 首帧观察回调的取消句柄。\n */\n private videoFrameCallbackID?: number;\n\n /**\n * 首帧通知版本,避免旧流回调影响新流。\n */\n private frameNotificationSequence = 0;\n\n /**\n * 呈现统计组件\n */\n private readonly statisticsController: RenderStatisticsController;\n\n /**\n * 远端画面预热与渐入组件\n */\n private readonly warmupController: RenderWarmupController;\n\n /**\n * DOM 挂载状态\n */\n private detached = false;\n\n /**\n * 轨迹交互配置与懒加载视图\n */\n private interactionEnabled = true;\n\n /**\n * 接入方提供的轨迹视觉效果实现。\n */\n private customTrajectoryRenderer?: TrajectoryEffectRendering;\n\n /**\n * 视口触点采样与轨迹效果覆盖层。\n */\n private trajectoryOverlay?: TrajectoryOverlayView;\n\n /**\n * 创建单轨视频视图。\n *\n * @param options.videoContentMode 视频内容显示模式,默认 fill。\n * @param options.isInteractionEnabled 是否允许远端轨迹交互,默认 true。\n * @param options.fadeInDurationMs 远端画面预热完成后的渐入时长,单位为毫秒,默认 300;0 表示直接显示,不跳过预热。\n * @throws 渐入时长为负数或非有限数值时抛出 invalidConfiguration。\n */\n constructor(\n options?: {\n videoContentMode?: VideoContentMode;\n isInteractionEnabled?: boolean;\n /**\n * 远端画面的渐入时长,单位为毫秒,默认 300;0 表示预热完成后直接显示。\n */\n fadeInDurationMs?: number;\n }\n ) {\n this.contentMode = options?.videoContentMode ?? VideoContentMode.fill;\n this.interactionEnabled = options?.isInteractionEnabled ?? true;\n\n this.element = document.createElement(\"div\");\n this.element.style.position = \"relative\";\n this.element.style.overflow = \"hidden\";\n this.element.style.backgroundColor = \"black\";\n\n this.videoLayer = document.createElement(\"div\");\n Object.assign(this.videoLayer.style, {\n position: \"absolute\", inset: \"0\", pointerEvents: \"none\", opacity: \"1\",\n });\n this.element.appendChild(this.videoLayer);\n\n this.warmupLayer = document.createElement(\"div\");\n Object.assign(this.warmupLayer.style, { position: \"absolute\", inset: \"0\" });\n this.videoLayer.appendChild(this.warmupLayer);\n this.warmupController = new RenderWarmupController(this.warmupLayer, options?.fadeInDurationMs);\n\n this.videoElement = document.createElement(\"video\");\n this.statisticsController = new RenderStatisticsController(this.videoElement);\n this.videoElement.autoplay = true;\n this.videoElement.playsInline = true;\n this.videoElement.muted = true;\n\n this.videoElement.style.position = \"absolute\";\n this.videoElement.style.inset = \"0\";\n this.videoElement.style.width = \"100%\";\n this.videoElement.style.height = \"100%\";\n\n this.applyContentMode();\n\n this.warmupLayer.appendChild(this.videoElement);\n }\n\n /**\n * 远端画面预热完成后的渐入时长,单位为毫秒。\n */\n get fadeInDurationMs(): number {\n return this.warmupController.fadeInDurationMs;\n }\n\n /**\n * 更新下一次渐入时长,0 表示直接显示,不跳过渲染预热。\n */\n set fadeInDurationMs(\n durationMs: number\n ) {\n this.warmupController.fadeInDurationMs = durationMs;\n }\n\n /**\n * 是否显示视频画面,默认 true;隐藏时保留播放、解码和触控轨迹。\n */\n get isVideoVisible(): boolean {\n return this.videoVisible;\n }\n\n /**\n * 即时切换视频的显隐,不改变触控配置或媒体流。\n */\n set isVideoVisible(\n visible: boolean\n ) {\n if (this.videoVisible === visible) {\n return;\n }\n this.videoVisible = visible;\n this.videoLayer.style.opacity = visible ? \"1\" : \"0\";\n }\n\n /**\n * 是否允许远端轨迹交互;本地轨道没有交互绑定,不受此开关影响。\n */\n get isInteractionEnabled(): boolean {\n return this.interactionEnabled;\n }\n\n /**\n * 关闭时立即取消当前手势,不影响视频播放。\n */\n set isInteractionEnabled(\n enabled: boolean\n ) {\n this.interactionEnabled = enabled;\n this.trajectoryOverlay?.configure(this.contentMode, this.mirrored, enabled);\n }\n\n /**\n * 自定义轨迹视觉效果;undefined 表示使用 SDK 默认效果。\n */\n get trajectoryRenderer(): TrajectoryEffectRendering | undefined {\n return this.customTrajectoryRenderer;\n }\n\n /**\n * 替换效果前释放旧动画,采样及网络发送仍由 SDK 管理。\n */\n set trajectoryRenderer(\n renderer: TrajectoryEffectRendering | undefined\n ) {\n if (this.customTrajectoryRenderer === renderer) {\n return;\n }\n this.disposeTrajectory();\n this.customTrajectoryRenderer = renderer;\n this.attachTrajectory();\n }\n\n /**\n * 当前显示的视频轨道;切换时自动解绑旧轨道。\n */\n get track(): RealtimeVideoTrack | undefined {\n return this.currentTrack;\n }\n\n /**\n * 每秒接收当前视图的呈现统计;换流、卸载或不可统计时返回 undefined。\n */\n get renderStatisticsHandler(): ((\n statistics?: VideoRenderStatistics\n ) => void) | undefined {\n return this.statisticsController.handler;\n }\n\n /**\n * 更新统计回调,不影响播放;移除回调时停止采集。\n */\n set renderStatisticsHandler(\n handler: ((\n statistics?: VideoRenderStatistics\n ) => void) | undefined\n ) {\n this.statisticsController.handler = handler;\n this.statisticsController.stop();\n this.startRenderStatistics();\n }\n\n /**\n * 解绑旧轨道后接入新轨道,并为新内容重新启用一次性首帧通知。\n */\n set track(\n track: RealtimeVideoTrack | undefined\n ) {\n if (this.currentTrack === track) {\n return;\n }\n this.detachCurrentTrack();\n this.currentTrack = track;\n this.hasNotifiedFrameDisplay = false;\n if (track) {\n this.attachTrack(track);\n }\n }\n\n /**\n * 视频内容在容器中的显示模式。\n */\n get videoContentMode(): VideoContentMode {\n return this.contentMode;\n }\n\n /**\n * 更新视频的内容缩放方式;相同配置不重复应用。\n */\n set videoContentMode(\n mode: VideoContentMode\n ) {\n if (this.contentMode === mode) {\n return;\n }\n this.contentMode = mode;\n this.applyContentMode();\n }\n\n /**\n * 本地预览是否镜像显示(仅影响显示,不影响发布流)。\n */\n get isMirrored(): boolean {\n return this.mirrored;\n }\n\n /**\n * 更新视图镜像变换,仅改变呈现方式,不改动媒体流。\n */\n set isMirrored(\n mirrored: boolean\n ) {\n if (this.mirrored === mirrored) {\n return;\n }\n this.mirrored = mirrored;\n this.applyContentMode();\n }\n\n /**\n * 挂载到容器元素。\n */\n attach(\n to: HTMLElement\n ): void {\n this.detached = false;\n to.appendChild(this.element);\n this.attachTrajectory();\n\n // 浏览器可能在移出 DOM 时暂停 MediaStream video;重新挂载要恢复解码回调。\n if (this.videoElement.srcObject && this.videoElement.autoplay && !this.videoElement.ended && this.videoElement.paused && typeof this.videoElement.play === \"function\") {\n void this.videoElement.play().catch((error) => {\n XmaxLogger.render.warning(() => `恢复视频播放失败 (Failed to Resume Video Playback)\\n└─ ${String(error)}`);\n });\n }\n\n this.startRenderStatistics();\n if (this.videoElement.srcObject && !this.hasNotifiedFrameDisplay) {\n this.prepareRenderWarmup();\n this.armFrameDisplayNotification();\n }\n }\n\n /**\n * 从容器元素移除。\n */\n detach(): void {\n this.detached = true;\n this.disposeTrajectory();\n this.statisticsController.stop();\n this.cancelFrameDisplayNotification();\n this.warmupController.reset();\n this.hasNotifiedFrameDisplay = false;\n this.element.remove();\n }\n\n /**\n * 直接设置渲染用的媒体流(绑定管线和直接渲染共用)。 @internal\n */\n setMediaStream(\n stream: MediaStream | null\n ): void {\n if (this.videoElement.srcObject === stream) {\n return;\n }\n\n this.statisticsController.stop();\n this.cancelFrameDisplayNotification();\n this.hasNotifiedFrameDisplay = false;\n\n this.videoElement.srcObject = stream;\n this.warmupController.reset();\n if (stream) {\n this.videoElement.autoplay = true;\n this.prepareRenderWarmup();\n this.armFrameDisplayNotification();\n this.startRenderStatistics();\n }\n }\n\n /**\n * 新生成任务重新等待首帧和预热,不替换正在播放的媒体流。 @internal\n */\n prepareForGeneration(): void {\n this.cancelFrameDisplayNotification();\n this.warmupController.reset();\n this.hasNotifiedFrameDisplay = false;\n this.prepareRenderWarmup();\n if (!this.detached && this.videoElement.srcObject) {\n this.armFrameDisplayNotification();\n }\n }\n\n /**\n * 接入轨道画面:优先走注册的渲染绑定,否则按媒体轨直接渲染。\n */\n private attachTrack(\n track: RealtimeVideoTrack\n ): void {\n const binding = VideoRenderRegistry.binding(track);\n if (binding) {\n binding.attachHandler(this, this.contentMode);\n this.attachTrajectory();\n return;\n }\n\n if (track.mediaStreamTrack) {\n this.setMediaStream(new MediaStream([track.mediaStreamTrack]));\n return;\n }\n\n XmaxLogger.render.warning(\n () => `轨道 ${track.id} 既没有渲染绑定也没有媒体轨,无法显示`,\n );\n }\n\n /**\n * 解绑当前轨道并清空渲染内容。\n */\n private detachCurrentTrack(): void {\n this.warmupController.reset();\n this.disposeTrajectory();\n this.statisticsController.stop();\n const track = this.currentTrack;\n if (track) {\n VideoRenderRegistry.binding(track)?.detachHandler(this);\n }\n this.cancelFrameDisplayNotification();\n this.videoElement.srcObject = null;\n }\n\n /**\n * 首帧提交合成器时通知;旧浏览器回退到 playing 近似判断。\n */\n private armFrameDisplayNotification(): void {\n this.cancelFrameDisplayNotification();\n\n const sequence = this.frameNotificationSequence;\n const track = this.currentTrack;\n const binding = track ? VideoRenderRegistry.binding(track) : undefined;\n\n const notify = () => {\n if (sequence !== this.frameNotificationSequence || this.hasNotifiedFrameDisplay) {\n return;\n }\n this.hasNotifiedFrameDisplay = true;\n this.cancelFrameDisplayNotification();\n const notifiedSequence = this.frameNotificationSequence;\n binding?.frameDisplayHandler?.();\n this.frameDisplayHandler?.();\n if (notifiedSequence === this.frameNotificationSequence) {\n this.warmupController.start(() => {\n this.applyContentMode();\n binding?.renderWarmupCompletedHandler?.();\n });\n }\n };\n\n if (typeof this.videoElement.requestVideoFrameCallback === \"function\") {\n this.videoFrameCallbackID = this.videoElement.requestVideoFrameCallback(notify);\n return;\n }\n\n this.onPlaying = notify;\n this.videoElement.addEventListener(\"playing\", this.onPlaying, {\n once: true,\n });\n if (!this.videoElement.paused && this.videoElement.readyState >= 2) {\n queueMicrotask(notify);\n }\n }\n\n /**\n * 换流、解绑或卸载时取消观察,防止迟到的首帧归入下一条流。\n */\n private cancelFrameDisplayNotification(): void {\n this.frameNotificationSequence += 1;\n if (this.videoFrameCallbackID !== undefined) {\n this.videoElement.cancelVideoFrameCallback(this.videoFrameCallbackID);\n this.videoFrameCallbackID = undefined;\n }\n if (this.onPlaying) {\n this.videoElement.removeEventListener(\"playing\", this.onPlaying);\n this.onPlaying = undefined;\n }\n }\n\n /**\n * 仅远端渲染绑定启用预热,本地预览和普通媒体直接显示。\n */\n private prepareRenderWarmup(): void {\n const binding = this.currentTrack ? VideoRenderRegistry.binding(this.currentTrack) : undefined;\n this.warmupController.prepare(binding?.renderWarmupMs);\n this.applyContentMode();\n }\n\n /**\n * 应用显示模式与镜像样式。\n */\n private applyContentMode(): void {\n this.trajectoryOverlay?.configure(this.contentMode, this.mirrored, this.interactionEnabled);\n this.videoElement.style.objectFit = OBJECT_FIT[this.contentMode];\n this.videoElement.style.transform = this.mirrored ? \"scaleX(-1)\" : \"\";\n }\n\n /**\n * 视图未卸载时启动呈现统计,采样及媒体就绪检查由统计组件负责。\n */\n private startRenderStatistics(): void {\n if (!this.detached)\n this.statisticsController.start();\n }\n\n /**\n * 仅为具备交互能力的轨道创建覆盖层,普通视频和本地预览不分配 Canvas。\n */\n private attachTrajectory(): void {\n if (this.detached || this.trajectoryOverlay || !this.currentTrack) {\n return;\n }\n const binding = TrajectoryRegistry.binding(this.currentTrack);\n if (!binding) {\n return;\n }\n const overlay = new TrajectoryOverlayView(this.customTrajectoryRenderer ?? new DefaultTrajectoryEffectRenderer());\n this.trajectoryOverlay = overlay;\n overlay.configure(this.contentMode, this.mirrored, this.interactionEnabled);\n this.element.appendChild(overlay.element);\n binding.attach(overlay);\n }\n\n /**\n * 换轨或卸载时移除全部交互资源;再次挂载时重新建立绑定。\n */\n private disposeTrajectory(): void {\n this.trajectoryOverlay?.dispose();\n this.trajectoryOverlay = undefined;\n }\n}\n","//\n// XmaxRealtimeDualVideoView.ts\n// XmaxSDK\n//\n// Author: 翁跃庭 (Yueting Weng)\n// GitHub: https://github.com/Xmax-AI/XmaxSDK-Web\n//\n// Copyright (c) 2026 XMAX.AI PTE. LTD.\n//\n\nimport { VideoContentMode } from \"../../Foundation/Media/Video/VideoContentMode\";\nimport type { RealtimeVideoTrack } from \"../../Service/Realtime/RealtimeVideoTrack\";\nimport { XmaxRealtimeVideoView } from \"./XmaxRealtimeVideoView\";\nimport { VideoRenderRegistry } from \"../../Service/Realtime/VideoRenderBinding\";\nimport type { TrajectoryEffectRendering } from \"../Trajectory/TrajectoryEffectRendering\";\n\n/**\n * 双轨视频容器:在本地预览和远端生成画面之间自动切换(框架无关)。\n *\n * 设置 SDK 远端轨道后,等待首帧及渲染预热结束再渐入远端画面;\n * 远端轨道置空时立即恢复本地预览。\n */\nexport class XmaxRealtimeDualVideoView {\n\n /**\n * 视图根元素。\n */\n readonly element: HTMLDivElement;\n\n /**\n * 视频视图\n */\n private readonly localVideoView: XmaxRealtimeVideoView;\n\n /**\n * 展示生成结果流的远端视频视图。\n */\n private readonly remoteVideoView: XmaxRealtimeVideoView;\n\n /**\n * 公共配置\n */\n private currentLocalTrack?: RealtimeVideoTrack;\n\n /**\n * 当前绑定的远端视频轨。\n */\n private currentRemoteTrack?: RealtimeVideoTrack;\n\n /**\n * 画面在视口中的缩放与裁剪方式。\n */\n private contentMode: VideoContentMode;\n\n /**\n * 远端画面渐入时长,单位为秒。\n */\n private fadeInDuration = 0.3;\n\n /**\n * 创建实时视频容器。\n *\n * @param options.localTrack 需要持续预览的本地视频轨道。\n * @param options.remoteTrack 需要覆盖显示的远端生成视频轨道。\n * @param options.videoContentMode 本地与远端视频的内容显示模式,默认 fill。\n * @param options.remoteVideoFadeInDuration 远端渐入时长,单位为秒,默认 0.3;非正数或非有限值关闭动画。\n */\n constructor(\n options?: {\n localTrack?: RealtimeVideoTrack;\n remoteTrack?: RealtimeVideoTrack;\n videoContentMode?: VideoContentMode;\n remoteVideoFadeInDuration?: number;\n isInteractionEnabled?: boolean;\n }\n ) {\n this.contentMode = options?.videoContentMode ?? VideoContentMode.fill;\n this.currentLocalTrack = options?.localTrack;\n this.currentRemoteTrack = options?.remoteTrack;\n\n this.element = document.createElement(\"div\");\n this.element.style.position = \"relative\";\n this.element.style.overflow = \"hidden\";\n this.element.style.backgroundColor = \"black\";\n\n this.localVideoView = new XmaxRealtimeVideoView({ videoContentMode: this.contentMode, isInteractionEnabled: false });\n this.remoteVideoView = new XmaxRealtimeVideoView({ videoContentMode: this.contentMode, isInteractionEnabled: options?.isInteractionEnabled });\n this.remoteVideoFadeInDuration = options?.remoteVideoFadeInDuration ?? 0.3;\n for (const view of [this.localVideoView, this.remoteVideoView]) {\n view.element.style.position = \"absolute\";\n view.element.style.inset = \"0\";\n this.element.appendChild(view.element);\n }\n\n this.localVideoView.track = this.currentLocalTrack;\n this.updateRemoteTrack();\n }\n\n /**\n * 远端画面的渐入时长,单位为秒,默认 0.3。\n */\n get remoteVideoFadeInDuration(): number {\n return this.fadeInDuration;\n }\n\n /**\n * 配置下一次渐入;非正数或非有限值关闭动画,不跳过首帧等待及渲染预热。\n */\n set remoteVideoFadeInDuration(\n duration: number\n ) {\n this.fadeInDuration = duration;\n const milliseconds = Number.isFinite(duration) && duration > 0 ? duration * 1000 : 0;\n this.remoteVideoView.fadeInDurationMs = Number.isFinite(milliseconds) ? milliseconds : 0;\n }\n\n /**\n * 当前显示的本地视频轨道。\n */\n get localTrack(): RealtimeVideoTrack | undefined {\n return this.currentLocalTrack;\n }\n\n /**\n * 只控制远端视图的轨迹交互,本地预览始终不接管手势。\n */\n get isInteractionEnabled(): boolean {\n return this.remoteVideoView.isInteractionEnabled;\n }\n\n /**\n * 转交远端交互开关。\n */\n set isInteractionEnabled(\n enabled: boolean\n ) {\n this.remoteVideoView.isInteractionEnabled = enabled;\n }\n\n /**\n * 远端视图的自定义轨迹效果。\n */\n get trajectoryRenderer(): TrajectoryEffectRendering | undefined {\n return this.remoteVideoView.trajectoryRenderer;\n }\n\n /**\n * undefined 恢复默认效果,不改变轨迹发送逻辑。\n */\n set trajectoryRenderer(\n renderer: TrajectoryEffectRendering | undefined\n ) {\n this.remoteVideoView.trajectoryRenderer = renderer;\n }\n\n /**\n * 替换本地预览轨道;轨道未变化时保留当前绑定。\n */\n set localTrack(\n track: RealtimeVideoTrack | undefined\n ) {\n if (this.currentLocalTrack === track) {\n return;\n }\n this.currentLocalTrack = track;\n this.localVideoView.track = track;\n }\n\n /**\n * 当前显示的远端生成视频轨道。\n * SDK 远端轨道等待首帧及预热后渐入;置空时立即恢复本地预览。\n */\n get remoteTrack(): RealtimeVideoTrack | undefined {\n return this.currentRemoteTrack;\n }\n\n /**\n * 替换远端轨道并重新等待首帧渐入;清空轨道时恢复本地预览。\n */\n set remoteTrack(\n track: RealtimeVideoTrack | undefined\n ) {\n if (this.currentRemoteTrack === track) {\n return;\n }\n this.currentRemoteTrack = track;\n this.updateRemoteTrack();\n }\n\n /**\n * 视频内容在容器中的显示模式。\n */\n get videoContentMode(): VideoContentMode {\n return this.contentMode;\n }\n\n /**\n * 同步调整本地与远端视频在容器中的缩放和裁剪方式。\n */\n set videoContentMode(\n mode: VideoContentMode\n ) {\n if (this.contentMode === mode) {\n return;\n }\n this.contentMode = mode;\n this.localVideoView.videoContentMode = mode;\n this.remoteVideoView.videoContentMode = mode;\n }\n\n /**\n * 本地预览是否镜像显示(前置摄像头)。\n */\n get isLocalMirrored(): boolean {\n return this.localVideoView.isMirrored;\n }\n\n /**\n * 更新本地预览镜像;不修改采集帧或远端画面。\n */\n set isLocalMirrored(\n mirrored: boolean\n ) {\n this.localVideoView.isMirrored = mirrored;\n }\n\n /**\n * 挂载到容器元素。\n */\n attach(\n to: HTMLElement\n ): void {\n to.appendChild(this.element);\n this.localVideoView.attach(this.element);\n this.remoteVideoView.attach(this.element);\n }\n\n /**\n * 从容器元素移除。\n */\n detach(): void {\n this.localVideoView.detach();\n this.remoteVideoView.detach();\n this.element.remove();\n }\n\n /**\n * 更新远端轨道绑定;SDK 轨道共用单轨视图的预热,普通轨道等待首帧后渐入。\n */\n private updateRemoteTrack(): void {\n const remoteTrack = this.currentRemoteTrack;\n\n this.remoteVideoView.frameDisplayHandler = undefined;\n this.remoteVideoView.track = undefined;\n this.remoteVideoView.element.style.transition = \"\";\n this.remoteVideoView.element.style.opacity = \"0\";\n this.remoteVideoView.element.style.visibility = \"hidden\";\n this.remoteVideoView.element.style.backgroundColor = \"black\";\n\n if (!remoteTrack) {\n return;\n }\n\n // SDK 远端轨道由单轨视图统一预热和渐入,不再叠加容器动画。\n if (VideoRenderRegistry.binding(remoteTrack)?.renderWarmupMs !== undefined) {\n this.remoteVideoView.element.style.visibility = \"visible\";\n this.remoteVideoView.element.style.opacity = \"1\";\n this.remoteVideoView.element.style.backgroundColor = \"transparent\";\n this.remoteVideoView.track = remoteTrack;\n return;\n }\n\n this.remoteVideoView.frameDisplayHandler = () => {\n if (this.currentRemoteTrack !== remoteTrack) {\n return;\n }\n this.showRemoteVideo();\n };\n this.remoteVideoView.track = remoteTrack;\n }\n\n /**\n * 远端首帧已提交:渐入远端画面。\n */\n private showRemoteVideo(): void {\n const element = this.remoteVideoView.element;\n if (element.style.visibility === \"visible\" && element.style.opacity === \"1\") {\n return;\n }\n\n element.style.visibility = \"visible\";\n const durationMs = this.remoteVideoView.fadeInDurationMs;\n const reduceMotion = globalThis.matchMedia?.(\"(prefers-reduced-motion: reduce)\").matches;\n if (durationMs === 0 || reduceMotion) {\n element.style.transition = \"none\";\n element.style.opacity = \"1\";\n return;\n }\n element.style.transition = `opacity ${durationMs}ms ease-in-out`;\n\n // 强制一帧后再过渡,确保渐入生效。\n requestAnimationFrame(() => {\n element.style.opacity = \"1\";\n });\n }\n}\n"],"mappings":"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","names":["XmaxEnvironment","XmaxLoggerOption","XmaxErrorCode","RealtimeVideoEncoderPreference","RtcProvider","RealtimeModel","CameraPosition","state","RealtimeNetworkQualityLevel","ensureActive","ensureActive","user","version","state","video","state","invalid","track","VideoContentMode","RealtimeConnectionState","record","ensureActive","state","ensureActive","state","state","RealtimeCameraPreset","RealtimeQualityLevel","state","ensureActive","NonRealtimeQuality","NonRealtimeTaskStatus"]}