@xmaxai/web-sdk 1.0.3 → 1.0.4

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.
@@ -3,10 +3,10 @@ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { en
3
3
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
4
 
5
5
  // src/Foundation/Runtime/XmaxEnvironment.ts
6
- var XmaxEnvironment = /* @__PURE__ */ ((XmaxEnvironment2) => {
7
- XmaxEnvironment2["china"] = "china";
8
- XmaxEnvironment2["global"] = "global";
9
- return XmaxEnvironment2;
6
+ var XmaxEnvironment = /* @__PURE__ */ ((XmaxEnvironment3) => {
7
+ XmaxEnvironment3["china"] = "china";
8
+ XmaxEnvironment3["global"] = "global";
9
+ return XmaxEnvironment3;
10
10
  })(XmaxEnvironment || {});
11
11
  var API_BASE_URLS = {
12
12
  ["china" /* china */]: "https://cloud.xmax.22duck.cn/open/api/v1",
@@ -198,7 +198,7 @@ var XmaxError = class _XmaxError extends Error {
198
198
  };
199
199
 
200
200
  // src/Foundation/Runtime/RuntimeInfo.ts
201
- var XMAX_SDK_VERSION = "1.0.3";
201
+ var XMAX_SDK_VERSION = "1.0.4";
202
202
  var _RuntimeInfo = class _RuntimeInfo {
203
203
  /**
204
204
  * 序列化为接口传输使用的 snake_case 字段。
@@ -521,22 +521,22 @@ var RealtimeVideoFormat = class _RealtimeVideoFormat {
521
521
  * @throws 尺寸、帧率或码率配置无效时抛出错误。
522
522
  */
523
523
  validate() {
524
- if (this.width <= 0 || this.height <= 0 || this.fps <= 0 || this.width % 2 !== 0 || this.height % 2 !== 0) {
524
+ if (this.width <= 0 || this.height <= 0 || !Number.isFinite(this.fps) || this.fps <= 0 || this.width % 2 !== 0 || this.height % 2 !== 0) {
525
525
  throw new XmaxError(
526
526
  "INVALID_CONFIGURATION" /* invalidConfiguration */,
527
527
  "Realtime video width and height must be positive even numbers, and fps must be greater than zero"
528
528
  );
529
529
  }
530
- if (this.minimumBitrate !== void 0 && this.minimumBitrate < 0) {
530
+ if (this.minimumBitrate !== void 0 && (!Number.isFinite(this.minimumBitrate) || this.minimumBitrate < 0)) {
531
531
  throw new XmaxError(
532
532
  "INVALID_CONFIGURATION" /* invalidConfiguration */,
533
- "Minimum bitrate must not be negative"
533
+ "Minimum bitrate must be finite and not negative"
534
534
  );
535
535
  }
536
- if (this.maximumBitrate !== void 0 && this.maximumBitrate <= 0) {
536
+ if (this.maximumBitrate !== void 0 && (!Number.isFinite(this.maximumBitrate) || this.maximumBitrate <= 0)) {
537
537
  throw new XmaxError(
538
538
  "INVALID_CONFIGURATION" /* invalidConfiguration */,
539
- "Maximum bitrate must be greater than zero"
539
+ "Maximum bitrate must be finite and greater than zero"
540
540
  );
541
541
  }
542
542
  if (this.minimumBitrate !== void 0 && this.maximumBitrate !== void 0 && this.minimumBitrate > this.maximumBitrate) {
@@ -564,26 +564,39 @@ var RealtimeVideoFormat = class _RealtimeVideoFormat {
564
564
  // src/Service/Realtime/RealtimeModel.ts
565
565
  var RealtimeModel = /* @__PURE__ */ ((RealtimeModel2) => {
566
566
  RealtimeModel2["x2_0"] = "x2.0";
567
- RealtimeModel2["x2_0_pro"] = "x2.0-pro";
568
567
  RealtimeModel2["x2_0_trtc"] = "x2.0-trtc";
568
+ RealtimeModel2["x2_0_agora"] = "x2.0-agora";
569
+ RealtimeModel2["x2_1_preview"] = "x2.1-preview";
569
570
  return RealtimeModel2;
570
571
  })(RealtimeModel || {});
572
+ var MODEL_BASE_URLS = {
573
+ ["x2.0-agora" /* x2_0_agora */]: "https://dev.xmaxai.com/open/api/v1",
574
+ ["x2.1-preview" /* x2_1_preview */]: "https://dev.xmaxai.com/open/api/v1"
575
+ };
571
576
  var RESOLUTION_BUCKETS = {
572
- ["x2.0" /* x2_0 */]: [],
573
- ["x2.0-pro" /* x2_0_pro */]: [
577
+ ["x2.1-preview" /* x2_1_preview */]: [
574
578
  { width: 1024, height: 1920 },
575
579
  { width: 1920, height: 1024 }
576
580
  ],
581
+ ["x2.0-agora" /* x2_0_agora */]: [
582
+ { width: 1024, height: 1920 },
583
+ { width: 1920, height: 1024 }
584
+ ],
585
+ ["x2.0" /* x2_0 */]: [],
577
586
  ["x2.0-trtc" /* x2_0_trtc */]: [
578
587
  { width: 1024, height: 1920 },
579
588
  { width: 1920, height: 1024 }
580
589
  ]
581
590
  };
582
591
  var MAXIMUM_INPUT_PIXELS = {
592
+ ["x2.1-preview" /* x2_1_preview */]: 21e5,
593
+ ["x2.0-agora" /* x2_0_agora */]: 21e5,
583
594
  ["x2.0" /* x2_0 */]: 128e4,
584
- ["x2.0-pro" /* x2_0_pro */]: 21e5,
585
595
  ["x2.0-trtc" /* x2_0_trtc */]: 21e5
586
596
  };
597
+ function modelBaseURL(model) {
598
+ return MODEL_BASE_URLS[model];
599
+ }
587
600
  function resolutionBuckets(model) {
588
601
  return RESOLUTION_BUCKETS[model];
589
602
  }
@@ -1071,7 +1084,718 @@ async function watchMediaPermissionPrompt(includeMicrophone) {
1071
1084
  return { decided, dispose };
1072
1085
  }
1073
1086
 
1074
- // src/Foundation/RTC/RtcEngineManager.ts
1087
+ // src/Foundation/RTC/Agora/AgoraRtcStatistics.ts
1088
+ var AgoraRtcStatistics = class _AgoraRtcStatistics {
1089
+ /**
1090
+ * 采集已发布的视频和已订阅的结果流统计,性能日志不控制回调采集。
1091
+ */
1092
+ static collect(client, listener) {
1093
+ const rtc = client.getRTCStats();
1094
+ const local = client.getLocalVideoStats();
1095
+ const valid = _AgoraRtcStatistics.valid;
1096
+ const loss = _AgoraRtcStatistics.loss;
1097
+ const localVideo = client.localTracks.some((track) => track.trackMediaType === "video") ? {
1098
+ width: valid(local.sendResolutionWidth, 1),
1099
+ height: valid(local.sendResolutionHeight, 1),
1100
+ frameRate: valid(local.sendFrameRate),
1101
+ bitrateKbps: valid(local.sendBitrate / 1e3),
1102
+ uplinkLossPercent: loss(local.currentPacketLossRate)
1103
+ } : void 0;
1104
+ const remote = Object.entries(client.getRemoteVideoStats()).map(([userID, stats]) => ({
1105
+ userID,
1106
+ width: valid(stats.receiveResolutionWidth, 1),
1107
+ height: valid(stats.receiveResolutionHeight, 1),
1108
+ frameRate: valid(stats.receiveFrameRate),
1109
+ bitrateKbps: valid(stats.receiveBitrate / 1e3),
1110
+ rttMs: valid(rtc.RTT),
1111
+ uplinkLossPercent: _optionalChain([localVideo, 'optionalAccess', _40 => _40.uplinkLossPercent]),
1112
+ downlinkLossPercent: loss(stats.currentPacketLossRate),
1113
+ endToEndDelayMs: valid(stats.end2EndDelay)
1114
+ }));
1115
+ _optionalChain([listener, 'optionalAccess', _41 => _41.onLocalVideoStatistics, 'optionalCall', _42 => _42(localVideo && Object.freeze(localVideo))]);
1116
+ _optionalChain([listener, 'optionalAccess', _43 => _43.onRemoteVideoStatistics, 'optionalCall', _44 => _44(Object.freeze(remote.map((item) => Object.freeze(item))))]);
1117
+ XmaxLogger.rtc.info(() => `Agora \u8FD0\u884C\u7EDF\u8BA1 (Agora RTC Statistics)
1118
+ ${JSON.stringify({
1119
+ rttMs: valid(rtc.RTT),
1120
+ sentBytes: valid(rtc.SendBytes),
1121
+ receivedBytes: valid(rtc.RecvBytes),
1122
+ localVideo,
1123
+ remoteVideo: remote,
1124
+ localAudio: client.getLocalAudioStats(),
1125
+ remoteAudio: client.getRemoteAudioStats()
1126
+ }, null, 2)}`, 2 /* performance */);
1127
+ }
1128
+ /**
1129
+ * 网络质量等级范围与公共枚举一致;不将整体 RTT 冒充上下行独立 RTT。
1130
+ */
1131
+ static network(stats, listener) {
1132
+ const quality = (value) => Number.isInteger(value) && value >= 0 && value <= 6 ? value : void 0;
1133
+ const result = Object.freeze({ uplinkQuality: quality(stats.uplinkNetworkQuality), downlinkQuality: quality(stats.downlinkNetworkQuality) });
1134
+ _optionalChain([listener, 'optionalAccess', _45 => _45.onNetworkStatistics, 'optionalCall', _46 => _46(result)]);
1135
+ XmaxLogger.rtc.info(() => `Agora \u7F51\u7EDC\u8D28\u91CF (Agora Network Quality)
1136
+ ${JSON.stringify(result, null, 2)}`, 2 /* performance */);
1137
+ }
1138
+ /**
1139
+ * 过滤缺失、非有限或不合理的统计值。
1140
+ */
1141
+ static valid(value, minimum = 0) {
1142
+ return typeof value === "number" && Number.isFinite(value) && value >= minimum ? value : void 0;
1143
+ }
1144
+ /**
1145
+ * 声网丢包率为 0–1 比例,转换为公共百分比。
1146
+ */
1147
+ static loss(value) {
1148
+ return Number.isFinite(value) && value >= 0 && value <= 1 ? value * 100 : void 0;
1149
+ }
1150
+ };
1151
+
1152
+ // src/Foundation/RTC/RtcVideoOrientation.ts
1153
+ function shouldTransposeRtcVideoSize(environment) {
1154
+ const userAgent = _nullishCoalesce(_optionalChain([environment, 'optionalAccess', _47 => _47.userAgent]), () => ( (typeof navigator === "undefined" ? "" : navigator.userAgent)));
1155
+ const maxTouchPoints = _nullishCoalesce(_optionalChain([environment, 'optionalAccess', _48 => _48.maxTouchPoints]), () => ( (typeof navigator === "undefined" ? 0 : _nullishCoalesce(navigator.maxTouchPoints, () => ( 0)))));
1156
+ const mobile = /Android|webOS|iPhone|iPad|iPod|Mobile/i.test(userAgent) || /Macintosh/i.test(userAgent) && maxTouchPoints > 1;
1157
+ if (!mobile) {
1158
+ return false;
1159
+ }
1160
+ const orientationType = _nullishCoalesce(_optionalChain([environment, 'optionalAccess', _49 => _49.orientationType]), () => ( (typeof window === "undefined" ? void 0 : _optionalChain([window, 'access', _50 => _50.screen, 'optionalAccess', _51 => _51.orientation, 'optionalAccess', _52 => _52.type]))));
1161
+ if (orientationType !== void 0) {
1162
+ return orientationType.includes("portrait");
1163
+ }
1164
+ const degrees = _nullishCoalesce(_optionalChain([environment, 'optionalAccess', _53 => _53.orientationDegrees]), () => ( (typeof window === "undefined" ? void 0 : window.orientation)));
1165
+ return degrees === 0 || degrees === 180;
1166
+ }
1167
+ function rtcOrientedVideoSize(size, environment) {
1168
+ return shouldTransposeRtcVideoSize(environment) ? { width: size.height, height: size.width } : size;
1169
+ }
1170
+
1171
+ // src/Foundation/RTC/Agora/AgoraRtcManager.ts
1172
+ var _AgoraRtcManager = class _AgoraRtcManager {
1173
+ /**
1174
+ * 保存服务环境与加载器,不触发浏览器依赖或设备权限请求。
1175
+ */
1176
+ constructor(options) {
1177
+ /**
1178
+ * 配置与 RTC 资源
1179
+ */
1180
+ __publicField(this, "environment");
1181
+ __publicField(this, "loadSDK");
1182
+ __publicField(this, "sdk");
1183
+ __publicField(this, "client");
1184
+ __publicField(this, "initialization");
1185
+ __publicField(this, "lifecycle", 0);
1186
+ __publicField(this, "camera");
1187
+ __publicField(this, "microphone");
1188
+ __publicField(this, "connection");
1189
+ __publicField(this, "roomVersion", 0);
1190
+ /**
1191
+ * 凭据恢复与发布意图;RTC 自动断开可能清空 localTracks,因此单独保留发布意图。
1192
+ */
1193
+ __publicField(this, "tokenExpired", false);
1194
+ __publicField(this, "rejoinOperation");
1195
+ __publicField(this, "rejoinTask");
1196
+ __publicField(this, "cancelRejoin");
1197
+ __publicField(this, "videoPublishRequested", false);
1198
+ __publicField(this, "audioPublishRequested", false);
1199
+ /**
1200
+ * 事件、统计与音频播放状态
1201
+ */
1202
+ __publicField(this, "listener");
1203
+ __publicField(this, "statisticsTimer");
1204
+ __publicField(this, "audioVolumes", /* @__PURE__ */ new Map());
1205
+ __publicField(this, "requestedAudio", /* @__PURE__ */ new Set());
1206
+ /**
1207
+ * DataStream 串行发送与限速状态;等待中的消息通过房间版本取消。
1208
+ */
1209
+ __publicField(this, "sendTail", Promise.resolve());
1210
+ __publicField(this, "sentPackets", []);
1211
+ __publicField(this, "cancelSendDelay");
1212
+ __publicField(this, "cancelActiveSend");
1213
+ this.environment = options.environment;
1214
+ this.loadSDK = _nullishCoalesce(options.loadSDK, () => ( (async () => (await Promise.resolve().then(() => _interopRequireWildcard(require("agora-rtc-sdk-ng")))).default)));
1215
+ }
1216
+ /**
1217
+ * 当前实例是否已创建声网 client。
1218
+ */
1219
+ get isInitialized() {
1220
+ return this.client !== void 0;
1221
+ }
1222
+ /**
1223
+ * 声网音频轨支持播放前设置音量;零音量额外停止播放以防浏览器差异。
1224
+ */
1225
+ get supportsRemoteAudioVolumeControl() {
1226
+ return true;
1227
+ }
1228
+ /**
1229
+ * 共享本实例的初始化任务,区域完全由接入方环境决定。
1230
+ */
1231
+ async initialize() {
1232
+ if (this.client) return;
1233
+ if (this.initialization) return this.initialization;
1234
+ const version = this.lifecycle;
1235
+ const operation = this.performInitialize(version);
1236
+ this.initialization = operation;
1237
+ try {
1238
+ await operation;
1239
+ } finally {
1240
+ if (this.initialization === operation) this.initialization = void 0;
1241
+ }
1242
+ }
1243
+ /**
1244
+ * 加载模块、锁定区域并注册事件;迟到的初始化不能复活已销毁实例。
1245
+ */
1246
+ async performInitialize(version) {
1247
+ const sdk = await this.loadSDK();
1248
+ if (version !== this.lifecycle) throw this.cancelled();
1249
+ const region = _AgoraRtcManager.regions.get(sdk);
1250
+ if (region && region.environment !== this.environment) {
1251
+ throw new XmaxError("INVALID_CONFIGURATION" /* invalidConfiguration */, "Concurrent Agora clients must use the same XmaxEnvironment");
1252
+ }
1253
+ sdk.setLogLevel(4);
1254
+ if (!region) sdk.setArea([this.environment === "china" /* china */ ? "CHINA" : "GLOBAL"]);
1255
+ const client = sdk.createClient({ mode: "rtc", codec: "h264" });
1256
+ if (typeof client.sendStreamMessage !== "function") {
1257
+ throw new XmaxError("RTC_ERROR" /* rtcError */, "Agora DataStream is unavailable in this SDK version");
1258
+ }
1259
+ _AgoraRtcManager.regions.set(sdk, { environment: this.environment, count: (_nullishCoalesce(_optionalChain([region, 'optionalAccess', _54 => _54.count]), () => ( 0))) + 1 });
1260
+ this.sdk = sdk;
1261
+ this.client = client;
1262
+ this.registerEvents(client);
1263
+ }
1264
+ /**
1265
+ * 先失效全部异步工作,再释放房间、采集、监听和区域引用。
1266
+ */
1267
+ async destroy() {
1268
+ this.lifecycle++;
1269
+ const client = this.client;
1270
+ this.listener = void 0;
1271
+ this.clearRoom();
1272
+ this.client = void 0;
1273
+ _optionalChain([client, 'optionalAccess', _55 => _55.removeAllListeners, 'call', _56 => _56()]);
1274
+ _optionalChain([this, 'access', _57 => _57.camera, 'optionalAccess', _58 => _58.close, 'call', _59 => _59()]);
1275
+ _optionalChain([this, 'access', _60 => _60.microphone, 'optionalAccess', _61 => _61.close, 'call', _62 => _62()]);
1276
+ this.camera = void 0;
1277
+ this.microphone = void 0;
1278
+ const sdk = this.sdk;
1279
+ this.sdk = void 0;
1280
+ try {
1281
+ await _optionalChain([client, 'optionalAccess', _63 => _63.leave, 'call', _64 => _64()]);
1282
+ } catch (e8) {
1283
+ XmaxLogger.rtc.warning(() => "Agora \u9000\u623F\u5931\u8D25\uFF0C\u5DF2\u91CA\u653E\u672C\u5730\u8D44\u6E90 (Agora Leave Failed)");
1284
+ } finally {
1285
+ const region = sdk && _AgoraRtcManager.regions.get(sdk);
1286
+ if (sdk && region && --region.count === 0) _AgoraRtcManager.regions.delete(sdk);
1287
+ }
1288
+ }
1289
+ /**
1290
+ * 通过声网采集相机但不发布,返回原生轨道供公共渲染层使用。
1291
+ */
1292
+ async startCameraCapture(options) {
1293
+ const client = this.requireClient();
1294
+ if (this.camera) throw new XmaxError("INVALID_CONFIGURATION" /* invalidConfiguration */, "Camera capture is already running");
1295
+ try {
1296
+ const captureSize = rtcOrientedVideoSize(options);
1297
+ const camera = await this.sdk.createCameraVideoTrack({
1298
+ facingMode: options.position === "front" /* front */ ? "user" : "environment",
1299
+ encoderConfig: { width: captureSize.width, height: captureSize.height, frameRate: options.frameRate }
1300
+ });
1301
+ if (this.client !== client) {
1302
+ camera.close();
1303
+ throw this.cancelled();
1304
+ }
1305
+ this.camera = camera;
1306
+ return camera.getMediaStreamTrack();
1307
+ } catch (error) {
1308
+ throw this.mapError(error, "CAMERA_PERMISSION_DENIED" /* cameraPermissionDenied */);
1309
+ }
1310
+ }
1311
+ /**
1312
+ * 切换声网相机设备并返回切换后的原生轨道。
1313
+ */
1314
+ async switchCameraCapture(to) {
1315
+ const camera = this.requireCamera();
1316
+ await this.run(() => camera.setDevice({ facingMode: to === "front" /* front */ ? "user" : "environment" }));
1317
+ if (this.camera !== camera) throw this.cancelled();
1318
+ return camera.getMediaStreamTrack();
1319
+ }
1320
+ /**
1321
+ * 停止采集并释放摄像头。
1322
+ */
1323
+ async stopCameraCapture() {
1324
+ const camera = this.camera;
1325
+ this.camera = void 0;
1326
+ _optionalChain([camera, 'optionalAccess', _65 => _65.close, 'call', _66 => _66()]);
1327
+ }
1328
+ /**
1329
+ * 应用公共编码配置,补偿移动端竖屏的宽高转置。
1330
+ */
1331
+ async configureVideoEncoding(config) {
1332
+ const camera = this.requireCamera();
1333
+ const encodingSize = rtcOrientedVideoSize(config);
1334
+ await this.run(async () => {
1335
+ await camera.setEncoderConfiguration({
1336
+ width: encodingSize.width,
1337
+ height: encodingSize.height,
1338
+ frameRate: config.frameRate,
1339
+ bitrateMin: config.minimumBitrate,
1340
+ bitrateMax: config.maximumBitrate
1341
+ });
1342
+ await camera.setOptimizationMode(config.encoderPreference === "maintainFramerate" /* maintainFramerate */ ? "motion" : "detail");
1343
+ });
1344
+ }
1345
+ /**
1346
+ * 使用会话下发的字符串频道与 UID 入房;不采信后端区域覆盖。
1347
+ */
1348
+ async joinRoom(configuration) {
1349
+ await _optionalChain([this, 'access', _67 => _67.rejoinTask, 'optionalAccess', _68 => _68.catch, 'call', _69 => _69(() => {
1350
+ })]);
1351
+ const client = this.requireClient();
1352
+ if (configuration.provider !== "agora" || this.connection) {
1353
+ throw new XmaxError("INVALID_CONFIGURATION" /* invalidConfiguration */, "Agora credentials and an idle room are required");
1354
+ }
1355
+ const version = ++this.roomVersion;
1356
+ await this.run(() => client.join(configuration.appID, configuration.roomID, configuration.roomToken, configuration.userID));
1357
+ if (client !== this.client || version !== this.roomVersion) {
1358
+ await client.leave();
1359
+ throw this.cancelled();
1360
+ }
1361
+ this.connection = configuration;
1362
+ for (const user of client.remoteUsers) {
1363
+ if (user.hasVideo) _optionalChain([this, 'access', _70 => _70.listener, 'optionalAccess', _71 => _71.onRemoteVideoPublished, 'call', _72 => _72(String(user.uid), true)]);
1364
+ }
1365
+ this.statisticsTimer = setInterval(() => {
1366
+ if (this.client !== client || !this.connection || this.tokenExpired || this.rejoinOperation) return;
1367
+ try {
1368
+ AgoraRtcStatistics.collect(client, this.listener);
1369
+ } catch (e9) {
1370
+ XmaxLogger.rtc.warning(() => "Agora \u7EDF\u8BA1\u91C7\u6837\u5931\u8D25 (Agora Statistics Failed)");
1371
+ }
1372
+ }, 2e3);
1373
+ }
1374
+ /**
1375
+ * 退房前取消排队消息、统计与音频播放;本地相机预览保持运行。
1376
+ */
1377
+ async leaveRoom() {
1378
+ this.clearRoom();
1379
+ if (this.client) await this.run(() => this.client.leave());
1380
+ }
1381
+ /**
1382
+ * 应用同一频道的新 Token;即将过期时续签,已经过期时重新进房并恢复媒体。
1383
+ */
1384
+ async updateCredentials(config, signal) {
1385
+ if (_optionalChain([signal, 'optionalAccess', _73 => _73.aborted])) throw this.cancelled();
1386
+ const current = this.connection;
1387
+ const client = this.requireClient();
1388
+ if (!current || config.provider !== "agora" || config.appID !== current.appID || config.roomID !== current.roomID || config.userID !== current.userID) {
1389
+ throw new XmaxError("SESSION_ERROR" /* sessionError */, "Agora session binding changed");
1390
+ }
1391
+ const version = this.roomVersion;
1392
+ if (!this.tokenExpired) {
1393
+ try {
1394
+ await this.run(() => client.renewToken(config.roomToken));
1395
+ } catch (error) {
1396
+ this.ensureRoomCurrent(client, version);
1397
+ if (!this.tokenExpired) throw error;
1398
+ }
1399
+ }
1400
+ this.ensureRoomCurrent(client, version);
1401
+ if (_optionalChain([signal, 'optionalAccess', _74 => _74.aborted])) throw this.cancelled();
1402
+ if (this.tokenExpired) await this.rejoinRoom(client, config, signal);
1403
+ else this.connection = config;
1404
+ }
1405
+ /**
1406
+ * 串行恢复同一 RTC 房间,不创建业务会话或重新发送生成开始指令。
1407
+ */
1408
+ async rejoinRoom(client, config, signal) {
1409
+ if (this.rejoinOperation) return this.rejoinOperation;
1410
+ const version = ++this.roomVersion;
1411
+ const task = Promise.resolve().then(() => this.performRejoin(client, config, version));
1412
+ this.rejoinTask = task;
1413
+ void task.finally(() => {
1414
+ if (this.rejoinTask === task) this.rejoinTask = void 0;
1415
+ }).catch(() => {
1416
+ });
1417
+ let cancel;
1418
+ const operation = new Promise((resolve, reject) => {
1419
+ cancel = () => {
1420
+ if (this.isRoomCurrent(client, version)) this.roomVersion++;
1421
+ reject(this.cancelled());
1422
+ };
1423
+ this.cancelRejoin = cancel;
1424
+ _optionalChain([signal, 'optionalAccess', _75 => _75.addEventListener, 'call', _76 => _76("abort", cancel, { once: true })]);
1425
+ task.then(resolve, reject);
1426
+ if (_optionalChain([signal, 'optionalAccess', _77 => _77.aborted])) cancel();
1427
+ });
1428
+ this.rejoinOperation = operation;
1429
+ try {
1430
+ await operation;
1431
+ } finally {
1432
+ _optionalChain([signal, 'optionalAccess', _78 => _78.removeEventListener, 'call', _79 => _79("abort", cancel)]);
1433
+ if (this.cancelRejoin === cancel) this.cancelRejoin = void 0;
1434
+ if (this.rejoinOperation === operation) this.rejoinOperation = void 0;
1435
+ }
1436
+ this.ensureRoomCurrent(client, version);
1437
+ for (const user of client.remoteUsers) {
1438
+ if (user.hasVideo) _optionalChain([this, 'access', _80 => _80.listener, 'optionalAccess', _81 => _81.onRemoteVideoPublished, 'call', _82 => _82(String(user.uid), true)]);
1439
+ }
1440
+ }
1441
+ /**
1442
+ * 使用新 Token 重建 RTC 连接,复用采集轨道并恢复远端音频订阅。
1443
+ */
1444
+ async performRejoin(client, config, version) {
1445
+ this.ensureRoomCurrent(client, version);
1446
+ _optionalChain([this, 'access', _83 => _83.cancelSendDelay, 'optionalCall', _84 => _84()]);
1447
+ _optionalChain([this, 'access', _85 => _85.cancelActiveSend, 'optionalCall', _86 => _86()]);
1448
+ _optionalChain([this, 'access', _87 => _87.listener, 'optionalAccess', _88 => _88.onRoomRejoining, 'optionalCall', _89 => _89()]);
1449
+ for (const user of client.remoteUsers) _optionalChain([user, 'access', _90 => _90.audioTrack, 'optionalAccess', _91 => _91.stop, 'call', _92 => _92()]);
1450
+ await this.run(() => client.leave());
1451
+ this.ensureRoomCurrent(client, version);
1452
+ await this.run(() => client.join(config.appID, config.roomID, config.roomToken, config.userID));
1453
+ if (!this.isRoomCurrent(client, version)) {
1454
+ await this.run(() => client.leave());
1455
+ throw this.cancelled();
1456
+ }
1457
+ if (this.videoPublishRequested && this.camera) {
1458
+ await this.run(() => client.publish(this.camera));
1459
+ this.ensureRoomCurrent(client, version);
1460
+ }
1461
+ if (this.audioPublishRequested && this.microphone) {
1462
+ await this.run(() => client.publish(this.microphone));
1463
+ this.ensureRoomCurrent(client, version);
1464
+ }
1465
+ for (const user of client.remoteUsers) {
1466
+ if (!user.hasAudio || !this.requestedAudio.has(String(user.uid))) continue;
1467
+ const track = await this.run(() => client.subscribe(user, "audio"));
1468
+ if (!this.isRoomCurrent(client, version)) {
1469
+ track.stop();
1470
+ throw this.cancelled();
1471
+ }
1472
+ this.applyAudioVolume(user);
1473
+ }
1474
+ this.connection = config;
1475
+ this.tokenExpired = false;
1476
+ this.sentPackets = [];
1477
+ }
1478
+ /**
1479
+ * 发布已采集的视频轨道。
1480
+ */
1481
+ async publishLocalVideo() {
1482
+ const client = this.requireClient();
1483
+ const camera = this.requireCamera();
1484
+ this.videoPublishRequested = true;
1485
+ await this.rejoinOperation;
1486
+ if (client !== this.client || !this.videoPublishRequested) throw this.cancelled();
1487
+ if (client.localTracks.includes(camera)) return;
1488
+ await this.run(() => client.publish(camera));
1489
+ }
1490
+ /**
1491
+ * 停止视频发布但保留采集与预览。
1492
+ */
1493
+ async unpublishLocalVideo() {
1494
+ this.videoPublishRequested = false;
1495
+ if (this.rejoinTask || this.tokenExpired) return;
1496
+ if (this.client && this.camera) await this.run(() => this.client.unpublish(this.camera));
1497
+ }
1498
+ /**
1499
+ * 首次发布时申请麦克风;取消后的迟到采集立即释放。
1500
+ */
1501
+ async publishLocalAudio() {
1502
+ const client = this.requireClient();
1503
+ this.audioPublishRequested = true;
1504
+ await this.rejoinOperation;
1505
+ const version = this.roomVersion;
1506
+ if (client !== this.client || !this.audioPublishRequested) throw this.cancelled();
1507
+ try {
1508
+ if (!this.microphone) {
1509
+ const track = await this.sdk.createMicrophoneAudioTrack();
1510
+ if (client !== this.client || version !== this.roomVersion) {
1511
+ track.close();
1512
+ throw this.cancelled();
1513
+ }
1514
+ this.microphone = track;
1515
+ }
1516
+ if (!client.localTracks.includes(this.microphone)) await client.publish(this.microphone);
1517
+ } catch (error) {
1518
+ throw this.mapError(error, "MICROPHONE_PERMISSION_DENIED" /* microphonePermissionDenied */);
1519
+ }
1520
+ }
1521
+ /**
1522
+ * 停止音频发布,采集交给关闭流程释放。
1523
+ */
1524
+ async unpublishLocalAudio() {
1525
+ this.audioPublishRequested = false;
1526
+ if (this.rejoinTask || this.tokenExpired) return;
1527
+ if (this.client && this.microphone) await this.run(() => this.client.unpublish(this.microphone));
1528
+ }
1529
+ /**
1530
+ * 订阅远端主视频并返回原生轨道,不创建额外播放视图。
1531
+ */
1532
+ async subscribeRemoteVideo(userID, subscribe) {
1533
+ if (!subscribe && this.rejoinTask) return;
1534
+ await this.rejoinOperation;
1535
+ const client = this.requireClient();
1536
+ const user = client.remoteUsers.find((user2) => String(user2.uid) === userID);
1537
+ if (!user && !subscribe) return;
1538
+ if (!user) throw new XmaxError("RTC_ERROR" /* rtcError */, "Agora remote video user is unavailable");
1539
+ if (!subscribe) {
1540
+ await this.run(() => client.unsubscribe(user, "video"));
1541
+ return;
1542
+ }
1543
+ const version = this.roomVersion;
1544
+ const track = await this.run(() => client.subscribe(user, "video"));
1545
+ if (client !== this.client || version !== this.roomVersion) throw this.cancelled();
1546
+ return track.getMediaStreamTrack();
1547
+ }
1548
+ /**
1549
+ * 保存音频订阅意图;音频稍后发布时自动订阅,播放前应用音量。
1550
+ */
1551
+ async subscribeRemoteAudio(userID, subscribe) {
1552
+ const client = this.requireClient();
1553
+ const user = client.remoteUsers.find((user2) => String(user2.uid) === userID);
1554
+ if (!subscribe) {
1555
+ this.requestedAudio.delete(userID);
1556
+ _optionalChain([user, 'optionalAccess', _93 => _93.audioTrack, 'optionalAccess', _94 => _94.stop, 'call', _95 => _95()]);
1557
+ if (_optionalChain([user, 'optionalAccess', _96 => _96.audioTrack]) && !this.rejoinTask) await this.run(() => client.unsubscribe(user, "audio"));
1558
+ return;
1559
+ }
1560
+ this.requestedAudio.add(userID);
1561
+ if (this.rejoinOperation) {
1562
+ const version2 = this.roomVersion;
1563
+ await this.rejoinOperation;
1564
+ this.ensureRoomCurrent(client, version2);
1565
+ if (this.requestedAudio.has(userID)) await this.subscribeRemoteAudio(userID, true);
1566
+ return;
1567
+ }
1568
+ if (!_optionalChain([user, 'optionalAccess', _97 => _97.hasAudio])) return;
1569
+ const version = this.roomVersion;
1570
+ const track = await this.run(() => client.subscribe(user, "audio"));
1571
+ if (client !== this.client || version !== this.roomVersion || !this.requestedAudio.has(userID)) {
1572
+ track.stop();
1573
+ return;
1574
+ }
1575
+ this.applyAudioVolume(user);
1576
+ }
1577
+ /**
1578
+ * 缓存音量,订阅前可调用;零音量不调用 play,防止初始短暂漏声。
1579
+ */
1580
+ setRemoteAudioVolume(volume, userID) {
1581
+ this.audioVolumes.set(userID, Math.max(0, Math.min(100, Number.isFinite(volume) ? Math.round(volume) : 0)));
1582
+ const user = _optionalChain([this, 'access', _98 => _98.client, 'optionalAccess', _99 => _99.remoteUsers, 'access', _100 => _100.find, 'call', _101 => _101((user2) => String(user2.uid) === userID)]);
1583
+ if (this.rejoinTask || this.tokenExpired) {
1584
+ _optionalChain([user, 'optionalAccess', _102 => _102.audioTrack, 'optionalAccess', _103 => _103.stop, 'call', _104 => _104()]);
1585
+ return;
1586
+ }
1587
+ if (user) this.applyAudioVolume(user);
1588
+ }
1589
+ /**
1590
+ * 应用当前用户的播放状态,保留声网对浏览器自动播放限制的处理。
1591
+ */
1592
+ applyAudioVolume(user) {
1593
+ const track = user.audioTrack;
1594
+ if (!track) return;
1595
+ const volume = _nullishCoalesce(this.audioVolumes.get(String(user.uid)), () => ( 0));
1596
+ track.setVolume(volume);
1597
+ if (volume > 0 && this.requestedAudio.has(String(user.uid))) track.play();
1598
+ else track.stop();
1599
+ }
1600
+ /**
1601
+ * 按房间顺序发送消息,限制每秒包数与总字节;失败返回给原业务调用。
1602
+ */
1603
+ sendRoomMessage(message) {
1604
+ const version = this.roomVersion;
1605
+ const client = this.client;
1606
+ const bytes = new TextEncoder().encode(message).byteLength;
1607
+ const operation = this.sendTail.then(async () => {
1608
+ await this.rejoinOperation;
1609
+ if (!client || client !== this.client || !this.connection || version !== this.roomVersion || this.tokenExpired) throw this.cancelled();
1610
+ if (bytes > 1e3) throw new XmaxError("INVALID_CONFIGURATION" /* invalidConfiguration */, "Room message exceeds the 1000-byte limit");
1611
+ await this.waitForSendBudget(bytes, version);
1612
+ await this.rejoinOperation;
1613
+ if (client !== this.client || version !== this.roomVersion || !this.connection || this.tokenExpired) throw this.cancelled();
1614
+ this.sentPackets.push({ time: Date.now(), bytes });
1615
+ await this.sendWithTimeout(client, message);
1616
+ });
1617
+ this.sendTail = operation.catch(() => {
1618
+ });
1619
+ return operation;
1620
+ }
1621
+ /**
1622
+ * 发送超过五秒视为失败;退房立即取消等待,底层迟到结果不影响新房间。
1623
+ */
1624
+ async sendWithTimeout(client, message) {
1625
+ await new Promise((resolve, reject) => {
1626
+ const finish = (error) => {
1627
+ clearTimeout(timer);
1628
+ if (this.cancelActiveSend === cancel) this.cancelActiveSend = void 0;
1629
+ if (error) reject(this.mapError(error));
1630
+ else resolve();
1631
+ };
1632
+ const cancel = () => finish(this.cancelled());
1633
+ const timer = setTimeout(() => finish(new XmaxError("TIMEOUT" /* timeout */, "Agora room message send timed out")), 5e3);
1634
+ this.cancelActiveSend = cancel;
1635
+ void this.run(() => client.sendStreamMessage(message, false)).then(() => finish(), finish);
1636
+ });
1637
+ }
1638
+ /**
1639
+ * 按滑动时间窗限速,退房立即唤醒等待并取消,不遗留计时器。
1640
+ */
1641
+ async waitForSendBudget(bytes, version) {
1642
+ while (version === this.roomVersion) {
1643
+ const now = Date.now();
1644
+ this.sentPackets = this.sentPackets.filter((packet) => now - packet.time < 1e3);
1645
+ if (this.sentPackets.length < 30 && this.sentPackets.reduce((sum, packet) => sum + packet.bytes, bytes) <= 6e3) return;
1646
+ await new Promise((resolve) => {
1647
+ const finish = () => {
1648
+ clearTimeout(timer);
1649
+ this.cancelSendDelay = void 0;
1650
+ resolve();
1651
+ };
1652
+ const timer = setTimeout(finish, Math.max(1, this.sentPackets[0].time + 1e3 - now));
1653
+ this.cancelSendDelay = finish;
1654
+ });
1655
+ }
1656
+ throw this.cancelled();
1657
+ }
1658
+ /**
1659
+ * 设置公共 RTC 监听器。
1660
+ */
1661
+ setEventListener(listener) {
1662
+ this.listener = listener;
1663
+ }
1664
+ /**
1665
+ * 将声网发布、DataStream、网络质量与 Token 事件桥接到公共传输层。
1666
+ */
1667
+ registerEvents(client) {
1668
+ const active = () => this.client === client && this.connection !== void 0 && !this.rejoinTask;
1669
+ const mediaActive = () => active() && !this.tokenExpired;
1670
+ const handleExpired = () => {
1671
+ if (!active() || this.tokenExpired) return;
1672
+ this.tokenExpired = true;
1673
+ _optionalChain([this, 'access', _105 => _105.listener, 'optionalAccess', _106 => _106.onTokenExpired, 'optionalCall', _107 => _107()]);
1674
+ };
1675
+ client.on("user-published", (user, kind) => {
1676
+ if (!mediaActive()) return;
1677
+ if (kind === "video") _optionalChain([this, 'access', _108 => _108.listener, 'optionalAccess', _109 => _109.onRemoteVideoPublished, 'call', _110 => _110(String(user.uid), true)]);
1678
+ if (kind === "audio" && this.requestedAudio.has(String(user.uid))) {
1679
+ void this.subscribeRemoteAudio(String(user.uid), true).catch((error) => {
1680
+ if (active()) _optionalChain([this, 'access', _111 => _111.listener, 'optionalAccess', _112 => _112.onError, 'optionalCall', _113 => _113(this.mapError(error))]);
1681
+ });
1682
+ }
1683
+ });
1684
+ client.on("user-unpublished", (user, kind) => {
1685
+ if (!mediaActive()) return;
1686
+ if (kind === "video") _optionalChain([this, 'access', _114 => _114.listener, 'optionalAccess', _115 => _115.onRemoteVideoPublished, 'call', _116 => _116(String(user.uid), false)]);
1687
+ if (kind === "audio") _optionalChain([user, 'access', _117 => _117.audioTrack, 'optionalAccess', _118 => _118.stop, 'call', _119 => _119()]);
1688
+ });
1689
+ client.on("user-left", (user) => {
1690
+ if (mediaActive()) _optionalChain([this, 'access', _120 => _120.listener, 'optionalAccess', _121 => _121.onRemoteVideoPublished, 'call', _122 => _122(String(user.uid), false)]);
1691
+ });
1692
+ client.on("stream-message", (uid, data) => {
1693
+ if (mediaActive()) _optionalChain([this, 'access', _123 => _123.listener, 'optionalAccess', _124 => _124.onCustomMessageReceived, 'call', _125 => _125(String(uid), typeof data === "string" ? data : new TextDecoder().decode(data))]);
1694
+ });
1695
+ client.on("network-quality", (quality) => {
1696
+ if (mediaActive()) AgoraRtcStatistics.network(quality, this.listener);
1697
+ });
1698
+ client.on("token-privilege-will-expire", () => {
1699
+ if (active() && !this.tokenExpired) _optionalChain([this, 'access', _126 => _126.listener, 'optionalAccess', _127 => _127.onTokenWillExpire, 'optionalCall', _128 => _128()]);
1700
+ });
1701
+ client.on("token-privilege-did-expire", handleExpired);
1702
+ client.on("connection-state-change", (state2, previous, reason) => {
1703
+ if (state2 === "DISCONNECTED" && reason === "TOKEN_EXPIRE") {
1704
+ handleExpired();
1705
+ return;
1706
+ }
1707
+ if (active() && !this.tokenExpired && state2 === "DISCONNECTED" && previous !== "DISCONNECTED") {
1708
+ _optionalChain([this, 'access', _129 => _129.listener, 'optionalAccess', _130 => _130.onError, 'optionalCall', _131 => _131(new XmaxError("RTC_ERROR" /* rtcError */, `Agora connection disconnected: ${_nullishCoalesce(reason, () => ( "unknown"))}`))]);
1709
+ }
1710
+ });
1711
+ }
1712
+ /**
1713
+ * 清理房间状态、播放和发送队列,不释放本地视频采集。
1714
+ */
1715
+ clearRoom() {
1716
+ this.roomVersion++;
1717
+ _optionalChain([this, 'access', _132 => _132.cancelRejoin, 'optionalCall', _133 => _133()]);
1718
+ this.connection = void 0;
1719
+ this.tokenExpired = false;
1720
+ this.videoPublishRequested = false;
1721
+ this.audioPublishRequested = false;
1722
+ clearInterval(this.statisticsTimer);
1723
+ this.statisticsTimer = void 0;
1724
+ _optionalChain([this, 'access', _134 => _134.cancelSendDelay, 'optionalCall', _135 => _135()]);
1725
+ _optionalChain([this, 'access', _136 => _136.cancelActiveSend, 'optionalCall', _137 => _137()]);
1726
+ this.sentPackets = [];
1727
+ this.sendTail = Promise.resolve();
1728
+ this.requestedAudio.clear();
1729
+ this.audioVolumes.clear();
1730
+ for (const user of _nullishCoalesce(_optionalChain([this, 'access', _138 => _138.client, 'optionalAccess', _139 => _139.remoteUsers]), () => ( []))) _optionalChain([user, 'access', _140 => _140.audioTrack, 'optionalAccess', _141 => _141.stop, 'call', _142 => _142()]);
1731
+ }
1732
+ /**
1733
+ * 判断异步恢复工作是否仍属于当前房间生命周期。
1734
+ */
1735
+ isRoomCurrent(client, version) {
1736
+ return this.client === client && this.connection !== void 0 && this.roomVersion === version;
1737
+ }
1738
+ /**
1739
+ * 退房、销毁或换房后停止后续恢复步骤。
1740
+ */
1741
+ ensureRoomCurrent(client, version) {
1742
+ if (!this.isRoomCurrent(client, version)) throw this.cancelled();
1743
+ }
1744
+ /**
1745
+ * 获取已初始化的 client。
1746
+ */
1747
+ requireClient() {
1748
+ if (!this.client) throw new XmaxError("RTC_ERROR" /* rtcError */, "Agora is not initialized");
1749
+ return this.client;
1750
+ }
1751
+ /**
1752
+ * 获取已启动的相机采集。
1753
+ */
1754
+ requireCamera() {
1755
+ if (!this.camera) throw new XmaxError("INVALID_CONFIGURATION" /* invalidConfiguration */, "Camera capture is not running");
1756
+ return this.camera;
1757
+ }
1758
+ /**
1759
+ * 执行声网操作并统一错误类型。
1760
+ */
1761
+ async run(operation) {
1762
+ try {
1763
+ return await operation();
1764
+ } catch (error) {
1765
+ throw this.mapError(error);
1766
+ }
1767
+ }
1768
+ /**
1769
+ * 只传递错误码,不回显可能包含 Token 的厂商原始错误消息。
1770
+ */
1771
+ mapError(error, permissionCode) {
1772
+ if (error instanceof XmaxError) return error;
1773
+ const code = typeof error === "object" && error !== null && "code" in error ? String(error.code) : "UNKNOWN";
1774
+ const safeCode = /^[A-Z_0-9-]+$/.test(code) ? code : "UNKNOWN";
1775
+ return new XmaxError(permissionCode && safeCode === "PERMISSION_DENIED" ? permissionCode : "RTC_ERROR" /* rtcError */, `Agora operation failed (${safeCode})`);
1776
+ }
1777
+ /**
1778
+ * 构造异步操作失效错误。
1779
+ */
1780
+ cancelled() {
1781
+ return new XmaxError("CANCELLED" /* cancelled */, "Agora operation was cancelled");
1782
+ }
1783
+ };
1784
+ /**
1785
+ * 模块级区域约束:setArea 是全局设置,拒绝同时运行不同区域,避免静默覆盖。
1786
+ */
1787
+ __publicField(_AgoraRtcManager, "regions", /* @__PURE__ */ new Map());
1788
+ var AgoraRtcManager = _AgoraRtcManager;
1789
+
1790
+ // src/Foundation/RTC/RtcProvider.ts
1791
+ var RtcProvider = /* @__PURE__ */ ((RtcProvider2) => {
1792
+ RtcProvider2["trtc"] = "trtc";
1793
+ RtcProvider2["agora"] = "agora";
1794
+ RtcProvider2["vertc"] = "vertc";
1795
+ return RtcProvider2;
1796
+ })(RtcProvider || {});
1797
+
1798
+ // src/Foundation/RTC/TRTC/RtcEngineManager.ts
1075
1799
  var RtcEngineLease = class {
1076
1800
  /**
1077
1801
  * 为已创建的引擎生成独占租约标识,供引擎管理器核对释放请求。
@@ -1139,13 +1863,13 @@ var _RtcEngineManager = class _RtcEngineManager {
1139
1863
  * 释放有效租约、销毁 Engine,并把独占权交给下一个等待者。
1140
1864
  */
1141
1865
  release(lease) {
1142
- if (_optionalChain([this, 'access', _40 => _40.activeLease, 'optionalAccess', _41 => _41.id]) !== lease.id) {
1866
+ if (_optionalChain([this, 'access', _143 => _143.activeLease, 'optionalAccess', _144 => _144.id]) !== lease.id) {
1143
1867
  return;
1144
1868
  }
1145
1869
  this.activeLease = void 0;
1146
1870
  try {
1147
1871
  lease.engine.destroy();
1148
- } catch (e8) {
1872
+ } catch (e10) {
1149
1873
  }
1150
1874
  void this.fulfillNextRequest();
1151
1875
  }
@@ -1213,10 +1937,10 @@ var RtcStatsLogger = class {
1213
1937
  `${XmaxLogger.localized("\u7D2F\u8BA1\u63A5\u6536\uFF1A", "Bytes Received: ")}${this.value(stats.bytesReceived, "bytes")}`
1214
1938
  ])];
1215
1939
  const local = stats.localStatistics;
1216
- if (_optionalChain([local, 'optionalAccess', _42 => _42.audio])) {
1940
+ if (_optionalChain([local, 'optionalAccess', _145 => _145.audio])) {
1217
1941
  blocks.push(this.audioBlock("\u672C\u5730\u97F3\u9891\u53D1\u9001 (Local Audio Uplink)", local.audio, false));
1218
1942
  }
1219
- for (const video of _nullishCoalesce(_optionalChain([local, 'optionalAccess', _43 => _43.video]), () => ( []))) {
1943
+ for (const video of _nullishCoalesce(_optionalChain([local, 'optionalAccess', _146 => _146.video]), () => ( []))) {
1220
1944
  blocks.push(this.videoBlock("\u672C\u5730\u89C6\u9891\u53D1\u9001 (Local Video Uplink)", video, false));
1221
1945
  }
1222
1946
  for (const remote of _nullishCoalesce(stats.remoteStatistics, () => ( []))) {
@@ -1304,34 +2028,15 @@ var RtcStatsLogger = class {
1304
2028
  }
1305
2029
  };
1306
2030
 
1307
- // src/Foundation/RTC/RtcVideoOrientation.ts
1308
- function shouldTransposeRtcVideoSize(environment) {
1309
- const userAgent = _nullishCoalesce(_optionalChain([environment, 'optionalAccess', _44 => _44.userAgent]), () => ( (typeof navigator === "undefined" ? "" : navigator.userAgent)));
1310
- const maxTouchPoints = _nullishCoalesce(_optionalChain([environment, 'optionalAccess', _45 => _45.maxTouchPoints]), () => ( (typeof navigator === "undefined" ? 0 : _nullishCoalesce(navigator.maxTouchPoints, () => ( 0)))));
1311
- const mobile = /Android|webOS|iPhone|iPad|iPod|Mobile/i.test(userAgent) || /Macintosh/i.test(userAgent) && maxTouchPoints > 1;
1312
- if (!mobile) {
1313
- return false;
1314
- }
1315
- const orientationType = _nullishCoalesce(_optionalChain([environment, 'optionalAccess', _46 => _46.orientationType]), () => ( (typeof window === "undefined" ? void 0 : _optionalChain([window, 'access', _47 => _47.screen, 'optionalAccess', _48 => _48.orientation, 'optionalAccess', _49 => _49.type]))));
1316
- if (orientationType !== void 0) {
1317
- return orientationType.includes("portrait");
1318
- }
1319
- const degrees = _nullishCoalesce(_optionalChain([environment, 'optionalAccess', _50 => _50.orientationDegrees]), () => ( (typeof window === "undefined" ? void 0 : window.orientation)));
1320
- return degrees === 0 || degrees === 180;
1321
- }
1322
- function rtcOrientedVideoSize(size, environment) {
1323
- return shouldTransposeRtcVideoSize(environment) ? { width: size.height, height: size.width } : size;
1324
- }
1325
-
1326
- // src/Foundation/RTC/RemoteAudioVolumeControl.ts
1327
- function canControlRemoteAudioVolume(environment) {
1328
- const userAgent = _nullishCoalesce(_optionalChain([environment, 'optionalAccess', _51 => _51.userAgent]), () => ( (typeof navigator === "undefined" ? "" : navigator.userAgent)));
1329
- const maxTouchPoints = _nullishCoalesce(_optionalChain([environment, 'optionalAccess', _52 => _52.maxTouchPoints]), () => ( (typeof navigator === "undefined" ? 0 : _nullishCoalesce(navigator.maxTouchPoints, () => ( 0)))));
2031
+ // src/Foundation/RTC/TRTC/TrtcAudioVolumeSupport.ts
2032
+ function canControlTrtcRemoteAudioVolume(environment) {
2033
+ const userAgent = _nullishCoalesce(_optionalChain([environment, 'optionalAccess', _147 => _147.userAgent]), () => ( (typeof navigator === "undefined" ? "" : navigator.userAgent)));
2034
+ const maxTouchPoints = _nullishCoalesce(_optionalChain([environment, 'optionalAccess', _148 => _148.maxTouchPoints]), () => ( (typeof navigator === "undefined" ? 0 : _nullishCoalesce(navigator.maxTouchPoints, () => ( 0)))));
1330
2035
  const ios = /iPhone|iPad|iPod/i.test(userAgent) || /Macintosh/i.test(userAgent) && maxTouchPoints > 1;
1331
2036
  return !ios;
1332
2037
  }
1333
2038
 
1334
- // src/Foundation/RTC/RtcManager.ts
2039
+ // src/Foundation/RTC/TRTC/TrtcRtcManager.ts
1335
2040
  var RTC_EVENT = {
1336
2041
  remoteVideoAvailable: "remote-video-available",
1337
2042
  remoteVideoUnavailable: "remote-video-unavailable",
@@ -1347,7 +2052,7 @@ var QOS_PREFERENCE_MAP = {
1347
2052
  ["maintainFramerate" /* maintainFramerate */]: "smooth",
1348
2053
  ["maintainQuality" /* maintainQuality */]: "clear"
1349
2054
  };
1350
- var RtcManager = class {
2055
+ var TrtcRtcManager = class {
1351
2056
  /**
1352
2057
  * 创建 RTC 管理器。
1353
2058
  *
@@ -1405,18 +2110,18 @@ var RtcManager = class {
1405
2110
  this.isCapturing = false;
1406
2111
  this.isAudioCapturing = false;
1407
2112
  this.isInRoom = false;
1408
- _optionalChain([this, 'access', _53 => _53.removeStatsListeners, 'optionalCall', _54 => _54()]);
2113
+ _optionalChain([this, 'access', _149 => _149.removeStatsListeners, 'optionalCall', _150 => _150()]);
1409
2114
  this.removeStatsListeners = void 0;
1410
2115
  if (!lease) {
1411
2116
  return;
1412
2117
  }
1413
2118
  try {
1414
2119
  await lease.engine.stopLocalAudio();
1415
- } catch (e9) {
2120
+ } catch (e11) {
1416
2121
  }
1417
2122
  try {
1418
2123
  await lease.engine.stopLocalVideo();
1419
- } catch (e10) {
2124
+ } catch (e12) {
1420
2125
  }
1421
2126
  this.engineManager.release(lease);
1422
2127
  }
@@ -1498,7 +2203,7 @@ var RtcManager = class {
1498
2203
  * 停止摄像头采集;引擎未初始化或采集未启动时不产生效果。
1499
2204
  */
1500
2205
  async stopCameraCapture() {
1501
- const engine = _optionalChain([this, 'access', _55 => _55.lease, 'optionalAccess', _56 => _56.engine]);
2206
+ const engine = _optionalChain([this, 'access', _151 => _151.lease, 'optionalAccess', _152 => _152.engine]);
1502
2207
  this.isCapturing = false;
1503
2208
  if (!engine) {
1504
2209
  return;
@@ -1515,6 +2220,9 @@ var RtcManager = class {
1515
2220
  * @throws 引擎未初始化、进房参数无效或进房失败时抛出错误。
1516
2221
  */
1517
2222
  async joinRoom(configuration) {
2223
+ if (configuration.provider !== "trtc") {
2224
+ throw new XmaxError("INVALID_CONFIGURATION" /* invalidConfiguration */, "TRTC credentials required");
2225
+ }
1518
2226
  const engine = this.requireEngine();
1519
2227
  const sdkAppId = Number(configuration.sdkAppID);
1520
2228
  if (!Number.isInteger(sdkAppId) || sdkAppId <= 0) {
@@ -1548,7 +2256,7 @@ var RtcManager = class {
1548
2256
  * 离开当前 RTC 房间;未在房间中或引擎未初始化时不产生效果,退房失败仅记录日志。
1549
2257
  */
1550
2258
  async leaveRoom() {
1551
- const engine = _optionalChain([this, 'access', _57 => _57.lease, 'optionalAccess', _58 => _58.engine]);
2259
+ const engine = _optionalChain([this, 'access', _153 => _153.lease, 'optionalAccess', _154 => _154.engine]);
1552
2260
  if (!engine || !this.isInRoom) {
1553
2261
  return;
1554
2262
  }
@@ -1621,7 +2329,7 @@ var RtcManager = class {
1621
2329
  * 停止发布本地视频流;采集保持运行,本地预览不受影响。
1622
2330
  */
1623
2331
  async unpublishLocalVideo() {
1624
- const engine = _optionalChain([this, 'access', _59 => _59.lease, 'optionalAccess', _60 => _60.engine]);
2332
+ const engine = _optionalChain([this, 'access', _155 => _155.lease, 'optionalAccess', _156 => _156.engine]);
1625
2333
  if (!engine || !this.isCapturing) {
1626
2334
  return;
1627
2335
  }
@@ -1653,7 +2361,7 @@ var RtcManager = class {
1653
2361
  * 停止发布本地音频流;麦克风采集保持运行。
1654
2362
  */
1655
2363
  async unpublishLocalAudio() {
1656
- const engine = _optionalChain([this, 'access', _61 => _61.lease, 'optionalAccess', _62 => _62.engine]);
2364
+ const engine = _optionalChain([this, 'access', _157 => _157.lease, 'optionalAccess', _158 => _158.engine]);
1657
2365
  if (!engine || !this.isAudioCapturing) {
1658
2366
  return;
1659
2367
  }
@@ -1711,7 +2419,7 @@ var RtcManager = class {
1711
2419
  * 当前平台是否支持远端音量控制(iOS Safari/WKWebView 不支持)。
1712
2420
  */
1713
2421
  get supportsRemoteAudioVolumeControl() {
1714
- return canControlRemoteAudioVolume();
2422
+ return canControlTrtcRemoteAudioVolume();
1715
2423
  }
1716
2424
  /**
1717
2425
  * 设置指定远端用户的音频播放音量。
@@ -1726,7 +2434,7 @@ var RtcManager = class {
1726
2434
  setRemoteAudioVolume(volume, userID) {
1727
2435
  const engine = this.requireEngine();
1728
2436
  const normalized = Math.min(Math.max(Math.round(volume), 0), 100);
1729
- if (!canControlRemoteAudioVolume()) {
2437
+ if (!canControlTrtcRemoteAudioVolume()) {
1730
2438
  void engine.muteRemoteAudio(userID, normalized === 0).catch((error) => {
1731
2439
  XmaxLogger.rtc.warning(
1732
2440
  () => `\u8FDC\u7AEF\u97F3\u9891\u64AD\u653E\u72B6\u6001\u5207\u6362\u5931\u8D25 (Failed to Toggle Remote Audio Playback)
@@ -1742,7 +2450,7 @@ var RtcManager = class {
1742
2450
  *
1743
2451
  * @throws 未在房间中或消息编码后超过 1000 字节时抛出错误。
1744
2452
  */
1745
- sendRoomMessage(message) {
2453
+ async sendRoomMessage(message) {
1746
2454
  const engine = this.requireEngine();
1747
2455
  if (!this.isInRoom) {
1748
2456
  throw new XmaxError(
@@ -1762,6 +2470,11 @@ var RtcManager = class {
1762
2470
  data: encoded.buffer
1763
2471
  });
1764
2472
  }
2473
+ /**
2474
+ * TRTC 凭据由会话层缓存,保持现有进房鉴权行为。
2475
+ */
2476
+ async updateCredentials(_configuration) {
2477
+ }
1765
2478
  /**
1766
2479
  * 设置 RTC 事件监听器,传入空值时清除监听器。
1767
2480
  */
@@ -1774,19 +2487,19 @@ var RtcManager = class {
1774
2487
  registerEventBridge(engine) {
1775
2488
  const source = engine;
1776
2489
  const onStatistics = (stats) => {
1777
- if (this.isInRoom && _optionalChain([this, 'access', _63 => _63.lease, 'optionalAccess', _64 => _64.engine]) === engine) {
2490
+ if (this.isInRoom && _optionalChain([this, 'access', _159 => _159.lease, 'optionalAccess', _160 => _160.engine]) === engine) {
1778
2491
  RtcStatsLogger.logStatistics(stats);
1779
- const video = _optionalChain([stats, 'access', _65 => _65.localStatistics, 'optionalAccess', _66 => _66.video, 'optionalAccess', _67 => _67.find, 'call', _68 => _68((item) => item.videoType === "big")]);
2492
+ const video = _optionalChain([stats, 'access', _161 => _161.localStatistics, 'optionalAccess', _162 => _162.video, 'optionalAccess', _163 => _163.find, 'call', _164 => _164((item) => item.videoType === "big")]);
1780
2493
  const valid = (value, minimum) => typeof value === "number" && Number.isFinite(value) && value >= minimum ? value : void 0;
1781
- _optionalChain([this, 'access', _69 => _69.eventListener, 'optionalAccess', _70 => _70.onLocalVideoStatistics, 'optionalCall', _71 => _71(video ? Object.freeze({
2494
+ _optionalChain([this, 'access', _165 => _165.eventListener, 'optionalAccess', _166 => _166.onLocalVideoStatistics, 'optionalCall', _167 => _167(video ? Object.freeze({
1782
2495
  width: valid(video.width, 1),
1783
2496
  height: valid(video.height, 1),
1784
2497
  frameRate: valid(video.frameRate, 0),
1785
2498
  bitrateKbps: valid(video.bitrate, 0)
1786
2499
  }) : void 0)]);
1787
- _optionalChain([this, 'access', _72 => _72.eventListener, 'optionalAccess', _73 => _73.onRemoteVideoStatistics, 'optionalCall', _74 => _74(Object.freeze(
2500
+ _optionalChain([this, 'access', _168 => _168.eventListener, 'optionalAccess', _169 => _169.onRemoteVideoStatistics, 'optionalCall', _170 => _170(Object.freeze(
1788
2501
  (_nullishCoalesce(stats.remoteStatistics, () => ( []))).flatMap((remote) => {
1789
- const video2 = _optionalChain([remote, 'access', _75 => _75.video, 'optionalAccess', _76 => _76.find, 'call', _77 => _77((item) => item.videoType === "big")]);
2502
+ const video2 = _optionalChain([remote, 'access', _171 => _171.video, 'optionalAccess', _172 => _172.find, 'call', _173 => _173((item) => item.videoType === "big")]);
1790
2503
  if (!video2) {
1791
2504
  return [];
1792
2505
  }
@@ -1808,11 +2521,11 @@ var RtcManager = class {
1808
2521
  }
1809
2522
  };
1810
2523
  const onNetworkQuality = (stats) => {
1811
- if (this.isInRoom && _optionalChain([this, 'access', _78 => _78.lease, 'optionalAccess', _79 => _79.engine]) === engine) {
2524
+ if (this.isInRoom && _optionalChain([this, 'access', _174 => _174.lease, 'optionalAccess', _175 => _175.engine]) === engine) {
1812
2525
  RtcStatsLogger.logNetworkQuality(stats);
1813
2526
  const quality = (value) => Number.isInteger(value) && value >= 0 && value <= 6 ? value : void 0;
1814
2527
  const rtt = (value) => Number.isFinite(value) && value >= 0 ? value : void 0;
1815
- _optionalChain([this, 'access', _80 => _80.eventListener, 'optionalAccess', _81 => _81.onNetworkStatistics, 'optionalCall', _82 => _82(Object.freeze({
2528
+ _optionalChain([this, 'access', _176 => _176.eventListener, 'optionalAccess', _177 => _177.onNetworkStatistics, 'optionalCall', _178 => _178(Object.freeze({
1816
2529
  uplinkQuality: quality(stats.uplinkNetworkQuality),
1817
2530
  downlinkQuality: quality(stats.downlinkNetworkQuality),
1818
2531
  uplinkRttMs: rtt(stats.uplinkRTT),
@@ -1830,13 +2543,13 @@ var RtcManager = class {
1830
2543
  if (event.streamType !== STREAM_TYPE_MAIN) {
1831
2544
  return;
1832
2545
  }
1833
- _optionalChain([this, 'access', _83 => _83.eventListener, 'optionalAccess', _84 => _84.onRemoteVideoPublished, 'call', _85 => _85(event.userId, true)]);
2546
+ _optionalChain([this, 'access', _179 => _179.eventListener, 'optionalAccess', _180 => _180.onRemoteVideoPublished, 'call', _181 => _181(event.userId, true)]);
1834
2547
  });
1835
2548
  source.on(RTC_EVENT.remoteVideoUnavailable, (event) => {
1836
2549
  if (event.streamType !== STREAM_TYPE_MAIN) {
1837
2550
  return;
1838
2551
  }
1839
- _optionalChain([this, 'access', _86 => _86.eventListener, 'optionalAccess', _87 => _87.onRemoteVideoPublished, 'call', _88 => _88(event.userId, false)]);
2552
+ _optionalChain([this, 'access', _182 => _182.eventListener, 'optionalAccess', _183 => _183.onRemoteVideoPublished, 'call', _184 => _184(event.userId, false)]);
1840
2553
  });
1841
2554
  source.on(RTC_EVENT.customMessage, (event) => {
1842
2555
  if (event.cmdId !== CUSTOM_MESSAGE_CMD_ID) {
@@ -1845,46 +2558,672 @@ var RtcManager = class {
1845
2558
  let message;
1846
2559
  try {
1847
2560
  message = new TextDecoder().decode(event.data);
1848
- } catch (e11) {
2561
+ } catch (e13) {
1849
2562
  XmaxLogger.rtc.warning(() => "\u81EA\u5B9A\u4E49\u6D88\u606F\u89E3\u7801\u5931\u8D25 (Failed to Decode Custom Message)");
1850
2563
  return;
1851
2564
  }
1852
- _optionalChain([this, 'access', _89 => _89.eventListener, 'optionalAccess', _90 => _90.onCustomMessageReceived, 'call', _91 => _91(event.userId, message)]);
2565
+ _optionalChain([this, 'access', _185 => _185.eventListener, 'optionalAccess', _186 => _186.onCustomMessageReceived, 'call', _187 => _187(event.userId, message)]);
2566
+ });
2567
+ }
2568
+ /**
2569
+ * 返回当前引擎实例;未初始化时抛出错误。
2570
+ */
2571
+ requireEngine() {
2572
+ const engine = _optionalChain([this, 'access', _188 => _188.lease, 'optionalAccess', _189 => _189.engine]);
2573
+ if (!engine) {
2574
+ throw new XmaxError(
2575
+ "RTC_ERROR" /* rtcError */,
2576
+ "RTC engine is not initialized"
2577
+ );
2578
+ }
2579
+ return engine;
2580
+ }
2581
+ /**
2582
+ * 将 TRTC 错误映射为 SDK 统一错误。
2583
+ */
2584
+ mapError(error) {
2585
+ if (error instanceof XmaxError) {
2586
+ return error;
2587
+ }
2588
+ const rtcError = error;
2589
+ const originName = _nullishCoalesce(_optionalChain([rtcError, 'optionalAccess', _190 => _190.originError, 'optionalAccess', _191 => _191.name]), () => ( ""));
2590
+ const message = typeof _optionalChain([rtcError, 'optionalAccess', _192 => _192.message]) === "string" && rtcError.message.trim().length > 0 ? rtcError.message.trim() : String(error);
2591
+ if (originName === "NotAllowedError" || originName === "SecurityError" || /NotAllowed/i.test(message)) {
2592
+ return new XmaxError("CAMERA_PERMISSION_DENIED" /* cameraPermissionDenied */, message);
2593
+ }
2594
+ if (originName) {
2595
+ return new XmaxError("MEDIA_ERROR" /* mediaError */, message);
2596
+ }
2597
+ return new XmaxError("RTC_ERROR" /* rtcError */, message);
2598
+ }
2599
+ };
2600
+
2601
+ // src/Foundation/RTC/VeRTC/VeRtcStatistics.ts
2602
+ var VeRtcStatistics = class {
2603
+ static local(stats) {
2604
+ return Object.freeze({
2605
+ width: this.valid(stats.encodedFrameWidth, 1),
2606
+ height: this.valid(stats.encodedFrameHeight, 1),
2607
+ frameRate: this.valid(stats.sentFrameRate),
2608
+ bitrateKbps: this.valid(stats.sentKBitrate),
2609
+ uplinkLossPercent: this.loss(stats.videoLossRate)
2610
+ });
2611
+ }
2612
+ static remote(userID, stats, uplinkLossPercent) {
2613
+ return Object.freeze({
2614
+ userID,
2615
+ width: this.valid(stats.width, 1),
2616
+ height: this.valid(stats.height, 1),
2617
+ frameRate: this.valid(stats.decoderOutputFrameRate),
2618
+ bitrateKbps: this.valid(stats.receivedKBitrate),
2619
+ rttMs: this.valid(stats.rtt),
2620
+ downlinkLossPercent: this.loss(stats.videoLossRate),
2621
+ endToEndDelayMs: this.valid(stats.e2eDelay),
2622
+ uplinkLossPercent
2623
+ });
2624
+ }
2625
+ static quality(value) {
2626
+ return Number.isInteger(value) && value >= 0 && value <= 6 ? value : void 0;
2627
+ }
2628
+ static valid(value, minimum = 0) {
2629
+ return typeof value === "number" && Number.isFinite(value) && value >= minimum ? value : void 0;
2630
+ }
2631
+ static loss(value) {
2632
+ return Number.isFinite(value) && value >= 0 && value <= 1 ? value * 100 : void 0;
2633
+ }
2634
+ };
2635
+
2636
+ // src/Foundation/RTC/VeRTC/VeRtcManager.ts
2637
+ var VERTC_APP_ID = "69a177e226e9b90176a86b96";
2638
+ var VeRtcManager = class {
2639
+ constructor(options = {}) {
2640
+ __publicField(this, "loadSDK");
2641
+ __publicField(this, "sdk");
2642
+ __publicField(this, "engine");
2643
+ __publicField(this, "initialization");
2644
+ __publicField(this, "lifecycle", 0);
2645
+ __publicField(this, "captureVersion", 0);
2646
+ __publicField(this, "camera");
2647
+ __publicField(this, "microphone", false);
2648
+ __publicField(this, "connection");
2649
+ __publicField(this, "joined", false);
2650
+ __publicField(this, "roomVersion", 0);
2651
+ __publicField(this, "roomAbort", new AbortController());
2652
+ __publicField(this, "recovery");
2653
+ __publicField(this, "joining");
2654
+ __publicField(this, "tokenExpired", false);
2655
+ __publicField(this, "publishPrivilegeExpired", false);
2656
+ __publicField(this, "subscribePrivilegeExpired", false);
2657
+ __publicField(this, "videoRequested", false);
2658
+ __publicField(this, "audioRequested", false);
2659
+ __publicField(this, "listener");
2660
+ __publicField(this, "publishedVideo", /* @__PURE__ */ new Set());
2661
+ __publicField(this, "publishedAudio", /* @__PURE__ */ new Set());
2662
+ __publicField(this, "requestedVideo", /* @__PURE__ */ new Set());
2663
+ __publicField(this, "requestedAudio", /* @__PURE__ */ new Set());
2664
+ __publicField(this, "audioVolumes", /* @__PURE__ */ new Map());
2665
+ __publicField(this, "audioPlayers", /* @__PURE__ */ new Map());
2666
+ __publicField(this, "audioSubscriptions", /* @__PURE__ */ new Map());
2667
+ __publicField(this, "remoteStatistics", /* @__PURE__ */ new Map());
2668
+ __publicField(this, "uplinkLossPercent");
2669
+ this.loadSDK = _nullishCoalesce(options.loadSDK, () => ( (() => Promise.resolve().then(() => _interopRequireWildcard(require("@volcengine/rtc"))))));
2670
+ }
2671
+ get isInitialized() {
2672
+ return this.engine !== void 0;
2673
+ }
2674
+ get supportsRemoteAudioVolumeControl() {
2675
+ if (typeof navigator === "undefined") return true;
2676
+ return !(/iPad|iPhone|iPod/.test(navigator.userAgent) || /Macintosh/.test(navigator.userAgent) && navigator.maxTouchPoints > 1);
2677
+ }
2678
+ async initialize() {
2679
+ if (this.engine) return;
2680
+ if (this.initialization) return this.initialization;
2681
+ const version = this.lifecycle;
2682
+ const operation = (async () => {
2683
+ const sdk = await this.loadSDK();
2684
+ if (version !== this.lifecycle) throw this.cancelled();
2685
+ sdk.default.setLogConfig({ logLevel: "none" });
2686
+ const engine = sdk.default.createEngine(VERTC_APP_ID, { autoPlayPolicy: sdk.RTCAutoPlayPolicy.PLAY_MANUALLY });
2687
+ this.sdk = sdk;
2688
+ this.engine = engine;
2689
+ this.registerEvents(engine);
2690
+ })();
2691
+ this.initialization = operation;
2692
+ try {
2693
+ await operation;
2694
+ } finally {
2695
+ if (this.initialization === operation) this.initialization = void 0;
2696
+ }
2697
+ }
2698
+ async destroy() {
2699
+ this.lifecycle++;
2700
+ this.captureVersion++;
2701
+ const engine = this.engine;
2702
+ const sdk = this.sdk;
2703
+ this.listener = void 0;
2704
+ this.clearRoom();
2705
+ this.engine = void 0;
2706
+ this.sdk = void 0;
2707
+ this.camera = void 0;
2708
+ this.microphone = false;
2709
+ _optionalChain([engine, 'optionalAccess', _193 => _193.removeAllListeners, 'call', _194 => _194()]);
2710
+ if (engine && sdk) sdk.default.destroyEngine(engine);
2711
+ }
2712
+ async startCameraCapture(options) {
2713
+ const engine = this.requireEngine();
2714
+ if (this.camera) throw new XmaxError("INVALID_CONFIGURATION" /* invalidConfiguration */, "Camera capture is already running");
2715
+ const version = ++this.captureVersion;
2716
+ await this.run(() => engine.setVideoEncoderConfig({
2717
+ ...rtcOrientedVideoSize({ width: options.width, height: options.height }),
2718
+ frameRate: options.frameRate,
2719
+ maxKbps: 1e3,
2720
+ preferCodecName: this.sdk.VideoCodecType.H264,
2721
+ contentHint: "motion"
2722
+ }));
2723
+ if (engine !== this.engine || version !== this.captureVersion) throw this.cancelled();
2724
+ try {
2725
+ await engine.startVideoCapture(options.position === "front" /* front */ ? "user" : "environment");
2726
+ if (engine !== this.engine || version !== this.captureVersion) {
2727
+ await engine.stopVideoCapture().catch(() => {
2728
+ });
2729
+ throw this.cancelled();
2730
+ }
2731
+ const track = engine.getLocalStreamTrack(this.sdk.StreamIndex.STREAM_INDEX_MAIN, "video");
2732
+ if (!track) {
2733
+ await engine.stopVideoCapture();
2734
+ throw new XmaxError("RTC_ERROR" /* rtcError */, "VeRTC camera track is unavailable");
2735
+ }
2736
+ this.camera = track;
2737
+ return track;
2738
+ } catch (error) {
2739
+ throw this.mapError(error, "CAMERA_PERMISSION_DENIED" /* cameraPermissionDenied */);
2740
+ }
2741
+ }
2742
+ async switchCameraCapture(to) {
2743
+ const engine = this.requireEngine();
2744
+ this.requireCamera();
2745
+ const version = this.captureVersion;
2746
+ await this.run(() => engine.setVideoCaptureDevice(to === "front" /* front */ ? "user" : "environment"));
2747
+ if (engine !== this.engine || version !== this.captureVersion) throw this.cancelled();
2748
+ const track = engine.getLocalStreamTrack(this.sdk.StreamIndex.STREAM_INDEX_MAIN, "video");
2749
+ if (!track) throw new XmaxError("RTC_ERROR" /* rtcError */, "VeRTC camera track is unavailable");
2750
+ this.camera = track;
2751
+ return track;
2752
+ }
2753
+ async stopCameraCapture() {
2754
+ this.captureVersion++;
2755
+ this.camera = void 0;
2756
+ if (this.engine) await this.run(() => this.engine.stopVideoCapture());
2757
+ }
2758
+ async configureVideoEncoding(config) {
2759
+ const engine = this.requireEngine();
2760
+ this.requireCamera();
2761
+ await this.run(() => engine.setVideoEncoderConfig({
2762
+ ...rtcOrientedVideoSize({ width: config.width, height: config.height }),
2763
+ frameRate: config.frameRate,
2764
+ maxKbps: config.maximumBitrate,
2765
+ contentHint: config.encoderPreference === "maintainFramerate" /* maintainFramerate */ ? "motion" : "detail",
2766
+ preferCodecName: this.sdk.VideoCodecType.H264
2767
+ }));
2768
+ }
2769
+ async joinRoom(config) {
2770
+ await _optionalChain([this, 'access', _195 => _195.joining, 'optionalAccess', _196 => _196.catch, 'call', _197 => _197(() => {
2771
+ })]);
2772
+ await _optionalChain([this, 'access', _198 => _198.recovery, 'optionalAccess', _199 => _199.catch, 'call', _200 => _200(() => {
2773
+ })]);
2774
+ const engine = this.requireEngine();
2775
+ if (config.provider !== "vertc" || config.appID !== VERTC_APP_ID || this.connection) {
2776
+ throw new XmaxError("INVALID_CONFIGURATION" /* invalidConfiguration */, "VeRTC credentials for the configured AppID and an idle room are required");
2777
+ }
2778
+ this.clearRoom();
2779
+ this.connection = config;
2780
+ const version = this.roomVersion;
2781
+ const task = this.performJoin(engine, config, version);
2782
+ this.joining = task;
2783
+ try {
2784
+ await task;
2785
+ } finally {
2786
+ if (this.joining === task) this.joining = void 0;
2787
+ }
2788
+ }
2789
+ async performJoin(engine, config, version) {
2790
+ try {
2791
+ await this.enter(engine, config);
2792
+ if (!this.isCurrent(engine, version)) {
2793
+ await engine.leaveRoom().catch(() => {
2794
+ });
2795
+ throw this.cancelled();
2796
+ }
2797
+ this.joined = true;
2798
+ this.replayVideo();
2799
+ } catch (error) {
2800
+ if (this.isCurrent(engine, version)) this.clearRoom();
2801
+ throw this.mapError(error);
2802
+ }
2803
+ }
2804
+ enter(engine, config) {
2805
+ return this.run(() => engine.joinRoom(config.roomToken, config.roomID, { userId: config.userID }, {
2806
+ isAutoPublish: false,
2807
+ isAutoSubscribeAudio: false,
2808
+ isAutoSubscribeVideo: false,
2809
+ roomProfileType: this.sdk.RoomProfileType.communication
2810
+ }));
2811
+ }
2812
+ async leaveRoom() {
2813
+ const engine = this.engine;
2814
+ this.clearRoom();
2815
+ if (engine) await this.run(() => engine.leaveRoom());
2816
+ }
2817
+ /** 正常续签仅 updateToken;被移出房间后重进,恢复发布与订阅意图。 */
2818
+ async updateCredentials(config, signal) {
2819
+ const engine = this.requireEngine();
2820
+ const current = this.connection;
2821
+ if (_optionalChain([signal, 'optionalAccess', _201 => _201.aborted])) throw this.cancelled();
2822
+ if (!current || config.provider !== "vertc" || config.appID !== current.appID || config.roomID !== current.roomID || config.userID !== current.userID) {
2823
+ throw new XmaxError("SESSION_ERROR" /* sessionError */, "VeRTC session binding changed");
2824
+ }
2825
+ const version = this.roomVersion;
2826
+ if (this.recovery) await this.cancellable(this.recovery, signal);
2827
+ if (!this.isCurrent(engine, version)) throw this.cancelled();
2828
+ const task = this.renew(engine, config, version);
2829
+ this.recovery = task;
2830
+ void task.finally(() => {
2831
+ if (this.recovery === task) this.recovery = void 0;
2832
+ }).catch(() => {
2833
+ });
2834
+ await this.cancellable(task, signal);
2835
+ }
2836
+ async renew(engine, config, version) {
2837
+ if (!this.tokenExpired) {
2838
+ try {
2839
+ await this.run(() => engine.updateToken(config.roomToken));
2840
+ } catch (error) {
2841
+ this.ensureCurrent(engine, version);
2842
+ if (!this.tokenExpired) throw error;
2843
+ }
2844
+ }
2845
+ this.ensureCurrent(engine, version);
2846
+ const restore = this.tokenExpired || this.publishPrivilegeExpired || this.subscribePrivilegeExpired;
2847
+ if (restore) {
2848
+ this.joined = false;
2849
+ _optionalChain([this, 'access', _202 => _202.listener, 'optionalAccess', _203 => _203.onRoomRejoining, 'optionalCall', _204 => _204()]);
2850
+ this.clearPlayers();
2851
+ this.clearStatistics();
2852
+ if (this.tokenExpired) {
2853
+ this.publishedVideo.clear();
2854
+ this.publishedAudio.clear();
2855
+ await this.run(() => engine.leaveRoom());
2856
+ this.ensureCurrent(engine, version);
2857
+ await this.enter(engine, config);
2858
+ if (!this.isCurrent(engine, version)) {
2859
+ await engine.leaveRoom().catch(() => {
2860
+ });
2861
+ throw this.cancelled();
2862
+ }
2863
+ }
2864
+ if ((this.tokenExpired || this.publishPrivilegeExpired) && this.videoRequested && this.camera) {
2865
+ await this.run(() => engine.publishStream(this.sdk.MediaType.VIDEO));
2866
+ this.ensureCurrent(engine, version);
2867
+ if (!this.videoRequested) {
2868
+ await this.run(() => engine.unpublishStream(this.sdk.MediaType.VIDEO));
2869
+ this.ensureCurrent(engine, version);
2870
+ }
2871
+ }
2872
+ if ((this.tokenExpired || this.publishPrivilegeExpired) && this.audioRequested && this.microphone) {
2873
+ await this.run(() => engine.publishStream(this.sdk.MediaType.AUDIO));
2874
+ this.ensureCurrent(engine, version);
2875
+ if (!this.audioRequested) {
2876
+ await this.run(() => engine.unpublishStream(this.sdk.MediaType.AUDIO));
2877
+ this.ensureCurrent(engine, version);
2878
+ }
2879
+ }
2880
+ }
2881
+ this.connection = config;
2882
+ this.tokenExpired = false;
2883
+ this.publishPrivilegeExpired = false;
2884
+ this.subscribePrivilegeExpired = false;
2885
+ this.joined = true;
2886
+ if (restore) {
2887
+ this.replayVideo();
2888
+ for (const userID of this.requestedAudio) {
2889
+ if (this.publishedAudio.has(userID)) await this.subscribeRemoteAudio(userID, true);
2890
+ this.ensureCurrent(engine, version);
2891
+ }
2892
+ }
2893
+ }
2894
+ async publishLocalVideo() {
2895
+ const engine = this.requireEngine();
2896
+ this.requireCamera();
2897
+ this.videoRequested = true;
2898
+ const version = this.roomVersion;
2899
+ await this.recovery;
2900
+ this.ensureCurrent(engine, version);
2901
+ if (!this.videoRequested) throw this.cancelled();
2902
+ await this.run(() => engine.publishStream(this.sdk.MediaType.VIDEO));
2903
+ }
2904
+ async unpublishLocalVideo() {
2905
+ this.videoRequested = false;
2906
+ if (this.joined && !this.tokenExpired) await this.run(() => this.requireEngine().unpublishStream(this.sdk.MediaType.VIDEO));
2907
+ }
2908
+ async publishLocalAudio() {
2909
+ const engine = this.requireEngine();
2910
+ this.audioRequested = true;
2911
+ const version = this.roomVersion;
2912
+ await this.recovery;
2913
+ this.ensureCurrent(engine, version);
2914
+ try {
2915
+ if (!this.microphone) {
2916
+ await engine.startAudioCapture();
2917
+ if (!this.isCurrent(engine, version)) {
2918
+ await engine.stopAudioCapture().catch(() => {
2919
+ });
2920
+ throw this.cancelled();
2921
+ }
2922
+ this.microphone = true;
2923
+ }
2924
+ if (!this.audioRequested) throw this.cancelled();
2925
+ await engine.publishStream(this.sdk.MediaType.AUDIO);
2926
+ } catch (error) {
2927
+ throw this.mapError(error, "MICROPHONE_PERMISSION_DENIED" /* microphonePermissionDenied */);
2928
+ }
2929
+ }
2930
+ async unpublishLocalAudio() {
2931
+ this.audioRequested = false;
2932
+ if (this.joined && !this.tokenExpired) await this.run(() => this.requireEngine().unpublishStream(this.sdk.MediaType.AUDIO));
2933
+ }
2934
+ async subscribeRemoteVideo(userID, subscribe) {
2935
+ const engine = this.requireEngine();
2936
+ const version = this.roomVersion;
2937
+ if (!subscribe) {
2938
+ this.requestedVideo.delete(userID);
2939
+ this.remoteStatistics.delete(userID);
2940
+ this.emitRemoteStatistics();
2941
+ if (this.joined && !this.tokenExpired) await this.run(() => engine.unsubscribeStream(userID, this.sdk.MediaType.VIDEO));
2942
+ return;
2943
+ }
2944
+ this.requestedVideo.add(userID);
2945
+ await this.run(() => engine.subscribeStream(userID, this.sdk.MediaType.VIDEO));
2946
+ this.ensureCurrent(engine, version);
2947
+ if (!this.requestedVideo.has(userID)) return;
2948
+ const track = engine.getRemoteStreamTrack(userID, this.sdk.StreamIndex.STREAM_INDEX_MAIN, "video");
2949
+ if (!track) throw new XmaxError("RTC_ERROR" /* rtcError */, "VeRTC remote video track is unavailable");
2950
+ return track;
2951
+ }
2952
+ async subscribeRemoteAudio(userID, subscribe) {
2953
+ const engine = this.requireEngine();
2954
+ const version = this.roomVersion;
2955
+ if (!subscribe) {
2956
+ this.requestedAudio.delete(userID);
2957
+ this.clearPlayer(userID);
2958
+ if (this.joined && !this.tokenExpired && this.publishedAudio.has(userID)) await this.run(() => engine.unsubscribeStream(userID, this.sdk.MediaType.AUDIO));
2959
+ return;
2960
+ }
2961
+ this.requestedAudio.add(userID);
2962
+ if (!this.joined || this.tokenExpired || !this.publishedAudio.has(userID)) return;
2963
+ const subscription = {};
2964
+ this.audioSubscriptions.set(userID, subscription);
2965
+ await this.run(() => engine.subscribeStream(userID, this.sdk.MediaType.AUDIO));
2966
+ if (!this.isCurrent(engine, version) || !this.joined || this.tokenExpired || this.audioSubscriptions.get(userID) !== subscription || !this.requestedAudio.has(userID) || !this.publishedAudio.has(userID)) return;
2967
+ const track = engine.getRemoteStreamTrack(userID, this.sdk.StreamIndex.STREAM_INDEX_MAIN, "audio");
2968
+ if (!track) return;
2969
+ this.clearPlayer(userID);
2970
+ const player = document.createElement("audio");
2971
+ player.srcObject = new MediaStream([track]);
2972
+ this.audioPlayers.set(userID, player);
2973
+ this.applyAudioVolume(userID);
2974
+ }
2975
+ setRemoteAudioVolume(volume, userID) {
2976
+ this.audioVolumes.set(userID, Number.isFinite(volume) ? Math.max(0, Math.min(100, Math.round(volume))) : 0);
2977
+ this.applyAudioVolume(userID);
2978
+ }
2979
+ applyAudioVolume(userID) {
2980
+ const player = this.audioPlayers.get(userID);
2981
+ if (!player) return;
2982
+ const volume = _nullishCoalesce(this.audioVolumes.get(userID), () => ( 0));
2983
+ player.muted = volume === 0 || !this.requestedAudio.has(userID);
2984
+ if (this.supportsRemoteAudioVolumeControl) player.volume = volume / 100;
2985
+ if (player.muted) player.pause();
2986
+ else void player.play().catch(() => {
2987
+ XmaxLogger.rtc.warning(() => "VeRTC audio playback requires a user interaction");
2988
+ });
2989
+ }
2990
+ async sendRoomMessage(message) {
2991
+ const engine = this.requireEngine();
2992
+ const version = this.roomVersion;
2993
+ if (!this.joined || this.tokenExpired) throw this.cancelled();
2994
+ if (new TextEncoder().encode(message).byteLength > 64 * 1024) {
2995
+ throw new XmaxError("INVALID_CONFIGURATION" /* invalidConfiguration */, "VeRTC room message exceeds the 64-KiB limit");
2996
+ }
2997
+ await this.cancellable(this.run(async () => {
2998
+ const sent = engine.sendRoomMessage(message);
2999
+ if (!sent) throw new XmaxError("RTC_ERROR" /* rtcError */, "VeRTC room message was not sent");
3000
+ await sent;
3001
+ }), void 0, 5e3);
3002
+ this.ensureCurrent(engine, version);
3003
+ }
3004
+ setEventListener(listener) {
3005
+ this.listener = listener;
3006
+ }
3007
+ registerEvents(engine) {
3008
+ const events = this.sdk.default.events;
3009
+ const active = () => engine === this.engine && this.connection !== void 0;
3010
+ const mediaActive = () => active() && this.joined && !this.tokenExpired;
3011
+ const video = (mediaType) => (mediaType & this.sdk.MediaType.VIDEO) !== 0;
3012
+ const audio = (mediaType) => (mediaType & this.sdk.MediaType.AUDIO) !== 0;
3013
+ engine.on(events.onUserPublishStream, ({ userId, mediaType }) => {
3014
+ if (!active()) return;
3015
+ if (video(mediaType)) {
3016
+ this.publishedVideo.add(userId);
3017
+ if (mediaActive()) _optionalChain([this, 'access', _205 => _205.listener, 'optionalAccess', _206 => _206.onRemoteVideoPublished, 'call', _207 => _207(userId, true)]);
3018
+ }
3019
+ if (audio(mediaType)) {
3020
+ this.publishedAudio.add(userId);
3021
+ if (mediaActive() && this.requestedAudio.has(userId)) {
3022
+ void this.subscribeRemoteAudio(userId, true).catch((error) => {
3023
+ if (mediaActive()) _optionalChain([this, 'access', _208 => _208.listener, 'optionalAccess', _209 => _209.onError, 'optionalCall', _210 => _210(this.mapError(error))]);
3024
+ });
3025
+ }
3026
+ }
3027
+ });
3028
+ engine.on(events.onUserUnpublishStream, ({ userId, mediaType }) => {
3029
+ if (!active()) return;
3030
+ if (video(mediaType)) this.removeVideo(userId);
3031
+ if (audio(mediaType)) {
3032
+ this.publishedAudio.delete(userId);
3033
+ this.clearPlayer(userId);
3034
+ }
3035
+ });
3036
+ engine.on(events.onUserLeave, ({ userInfo }) => {
3037
+ if (!active()) return;
3038
+ this.removeVideo(userInfo.userId);
3039
+ this.publishedAudio.delete(userInfo.userId);
3040
+ this.clearPlayer(userInfo.userId);
3041
+ });
3042
+ engine.on(events.onRoomMessageReceived, ({ userId, message }) => {
3043
+ if (mediaActive() && typeof message === "string") _optionalChain([this, 'access', _211 => _211.listener, 'optionalAccess', _212 => _212.onCustomMessageReceived, 'call', _213 => _213(userId, message)]);
3044
+ });
3045
+ const willExpire = () => {
3046
+ if (active()) _optionalChain([this, 'access', _214 => _214.listener, 'optionalAccess', _215 => _215.onTokenWillExpire, 'optionalCall', _216 => _216()]);
3047
+ };
3048
+ engine.on(events.onTokenWillExpire, willExpire);
3049
+ engine.on(events.onTokenPublishPrivilegeWillExpire, willExpire);
3050
+ engine.on(events.onTokenSubscribePrivilegeWillExpire, willExpire);
3051
+ engine.on(events.onTokenPublishPrivilegeDidExpired, () => {
3052
+ if (!active() || this.publishPrivilegeExpired) return;
3053
+ this.publishPrivilegeExpired = true;
3054
+ _optionalChain([this, 'access', _217 => _217.listener, 'optionalAccess', _218 => _218.onTokenExpired, 'optionalCall', _219 => _219()]);
3055
+ });
3056
+ engine.on(events.onTokenSubscribePrivilegeDidExpired, () => {
3057
+ if (!active() || this.subscribePrivilegeExpired) return;
3058
+ this.subscribePrivilegeExpired = true;
3059
+ this.clearPlayers();
3060
+ _optionalChain([this, 'access', _220 => _220.listener, 'optionalAccess', _221 => _221.onTokenExpired, 'optionalCall', _222 => _222()]);
3061
+ });
3062
+ engine.on(events.onError, ({ errorCode }) => {
3063
+ if (!active()) return;
3064
+ if (errorCode === "TOKEN_EXPIRED") {
3065
+ if (this.tokenExpired) return;
3066
+ this.tokenExpired = true;
3067
+ this.clearPlayers();
3068
+ _optionalChain([this, 'access', _223 => _223.listener, 'optionalAccess', _224 => _224.onTokenExpired, 'optionalCall', _225 => _225()]);
3069
+ } else _optionalChain([this, 'access', _226 => _226.listener, 'optionalAccess', _227 => _227.onError, 'optionalCall', _228 => _228(this.mapError({ code: errorCode }))]);
3070
+ });
3071
+ engine.on(events.onConnectionStateChanged, ({ state: state2 }) => {
3072
+ if (!mediaActive() || state2 !== this.sdk.ConnectionState.CONNECTION_STATE_RECONNECTED) return;
3073
+ _optionalChain([this, 'access', _229 => _229.listener, 'optionalAccess', _230 => _230.onRoomRejoining, 'optionalCall', _231 => _231()]);
3074
+ this.clearPlayers();
3075
+ this.clearStatistics();
3076
+ this.replayVideo();
3077
+ for (const userID of this.requestedAudio) {
3078
+ void this.subscribeRemoteAudio(userID, true).catch((error) => {
3079
+ if (mediaActive()) _optionalChain([this, 'access', _232 => _232.listener, 'optionalAccess', _233 => _233.onError, 'optionalCall', _234 => _234(this.mapError(error))]);
3080
+ });
3081
+ }
3082
+ });
3083
+ engine.on(events.onNetworkQuality, (up, down) => {
3084
+ if (mediaActive()) _optionalChain([this, 'access', _235 => _235.listener, 'optionalAccess', _236 => _236.onNetworkStatistics, 'optionalCall', _237 => _237(Object.freeze({
3085
+ uplinkQuality: VeRtcStatistics.quality(up),
3086
+ downlinkQuality: VeRtcStatistics.quality(down)
3087
+ }))]);
3088
+ });
3089
+ engine.on(events.onLocalStreamStats, (stats) => {
3090
+ if (!mediaActive() || stats.isScreen || !this.videoRequested) return;
3091
+ const result = VeRtcStatistics.local(stats.videoStats);
3092
+ this.uplinkLossPercent = result.uplinkLossPercent;
3093
+ _optionalChain([this, 'access', _238 => _238.listener, 'optionalAccess', _239 => _239.onLocalVideoStatistics, 'optionalCall', _240 => _240(result)]);
3094
+ XmaxLogger.rtc.info(() => `VeRTC local video statistics: ${JSON.stringify(result)}`, 2 /* performance */);
3095
+ });
3096
+ engine.on(events.onRemoteStreamStats, (stats) => {
3097
+ if (!mediaActive() || stats.isScreen || !this.requestedVideo.has(stats.userId)) return;
3098
+ this.remoteStatistics.set(stats.userId, VeRtcStatistics.remote(stats.userId, stats.videoStats, this.uplinkLossPercent));
3099
+ this.emitRemoteStatistics();
1853
3100
  });
1854
3101
  }
1855
- /**
1856
- * 返回当前引擎实例;未初始化时抛出错误。
1857
- */
3102
+ replayVideo() {
3103
+ for (const userID of this.publishedVideo) _optionalChain([this, 'access', _241 => _241.listener, 'optionalAccess', _242 => _242.onRemoteVideoPublished, 'call', _243 => _243(userID, true)]);
3104
+ }
3105
+ removeVideo(userID) {
3106
+ this.publishedVideo.delete(userID);
3107
+ this.remoteStatistics.delete(userID);
3108
+ this.emitRemoteStatistics();
3109
+ if (this.joined && !this.tokenExpired) _optionalChain([this, 'access', _244 => _244.listener, 'optionalAccess', _245 => _245.onRemoteVideoPublished, 'call', _246 => _246(userID, false)]);
3110
+ }
3111
+ emitRemoteStatistics() {
3112
+ _optionalChain([this, 'access', _247 => _247.listener, 'optionalAccess', _248 => _248.onRemoteVideoStatistics, 'optionalCall', _249 => _249(Object.freeze([...this.remoteStatistics.values()]))]);
3113
+ }
3114
+ clearStatistics() {
3115
+ this.remoteStatistics.clear();
3116
+ this.uplinkLossPercent = void 0;
3117
+ _optionalChain([this, 'access', _250 => _250.listener, 'optionalAccess', _251 => _251.onLocalVideoStatistics, 'optionalCall', _252 => _252(void 0)]);
3118
+ this.emitRemoteStatistics();
3119
+ }
3120
+ clearPlayer(userID) {
3121
+ this.audioSubscriptions.delete(userID);
3122
+ const player = this.audioPlayers.get(userID);
3123
+ if (player) {
3124
+ player.pause();
3125
+ player.srcObject = null;
3126
+ }
3127
+ this.audioPlayers.delete(userID);
3128
+ }
3129
+ clearPlayers() {
3130
+ this.audioSubscriptions.clear();
3131
+ for (const userID of this.audioPlayers.keys()) this.clearPlayer(userID);
3132
+ }
3133
+ clearRoom() {
3134
+ this.roomVersion++;
3135
+ this.roomAbort.abort();
3136
+ this.roomAbort = new AbortController();
3137
+ this.connection = void 0;
3138
+ this.joined = false;
3139
+ this.tokenExpired = false;
3140
+ this.publishPrivilegeExpired = false;
3141
+ this.subscribePrivilegeExpired = false;
3142
+ this.videoRequested = false;
3143
+ this.audioRequested = false;
3144
+ this.publishedVideo.clear();
3145
+ this.publishedAudio.clear();
3146
+ this.requestedVideo.clear();
3147
+ this.requestedAudio.clear();
3148
+ this.audioVolumes.clear();
3149
+ this.clearPlayers();
3150
+ this.clearStatistics();
3151
+ }
3152
+ /** 业务取消立即结束等待;底层迟到结果由版本检查清理。 */
3153
+ async cancellable(task, signal, timeoutMs) {
3154
+ const roomSignal = this.roomAbort.signal;
3155
+ return new Promise((resolve, reject) => {
3156
+ const timer = timeoutMs === void 0 ? void 0 : setTimeout(() => {
3157
+ cleanup();
3158
+ reject(new XmaxError("TIMEOUT" /* timeout */, "VeRTC room message send timed out"));
3159
+ }, timeoutMs);
3160
+ const cleanup = () => {
3161
+ clearTimeout(timer);
3162
+ roomSignal.removeEventListener("abort", cancel);
3163
+ _optionalChain([signal, 'optionalAccess', _253 => _253.removeEventListener, 'call', _254 => _254("abort", cancel)]);
3164
+ };
3165
+ const cancel = () => {
3166
+ if (_optionalChain([signal, 'optionalAccess', _255 => _255.aborted]) && !roomSignal.aborted) this.roomVersion++;
3167
+ cleanup();
3168
+ reject(this.cancelled());
3169
+ };
3170
+ roomSignal.addEventListener("abort", cancel, { once: true });
3171
+ _optionalChain([signal, 'optionalAccess', _256 => _256.addEventListener, 'call', _257 => _257("abort", cancel, { once: true })]);
3172
+ task.then((value) => {
3173
+ cleanup();
3174
+ resolve(value);
3175
+ }, (error) => {
3176
+ cleanup();
3177
+ reject(error);
3178
+ });
3179
+ if (roomSignal.aborted || _optionalChain([signal, 'optionalAccess', _258 => _258.aborted])) cancel();
3180
+ });
3181
+ }
3182
+ isCurrent(engine, version) {
3183
+ return this.engine === engine && this.connection !== void 0 && this.roomVersion === version;
3184
+ }
3185
+ ensureCurrent(engine, version) {
3186
+ if (!this.isCurrent(engine, version)) throw this.cancelled();
3187
+ }
1858
3188
  requireEngine() {
1859
- const engine = _optionalChain([this, 'access', _92 => _92.lease, 'optionalAccess', _93 => _93.engine]);
1860
- if (!engine) {
1861
- throw new XmaxError(
1862
- "RTC_ERROR" /* rtcError */,
1863
- "RTC engine is not initialized"
1864
- );
1865
- }
1866
- return engine;
3189
+ if (!this.engine) throw new XmaxError("RTC_ERROR" /* rtcError */, "VeRTC is not initialized");
3190
+ return this.engine;
1867
3191
  }
1868
- /**
1869
- * 将 TRTC 错误映射为 SDK 统一错误。
1870
- */
1871
- mapError(error) {
1872
- if (error instanceof XmaxError) {
1873
- return error;
1874
- }
1875
- const rtcError = error;
1876
- const originName = _nullishCoalesce(_optionalChain([rtcError, 'optionalAccess', _94 => _94.originError, 'optionalAccess', _95 => _95.name]), () => ( ""));
1877
- const message = typeof _optionalChain([rtcError, 'optionalAccess', _96 => _96.message]) === "string" && rtcError.message.trim().length > 0 ? rtcError.message.trim() : String(error);
1878
- if (originName === "NotAllowedError" || originName === "SecurityError" || /NotAllowed/i.test(message)) {
1879
- return new XmaxError("CAMERA_PERMISSION_DENIED" /* cameraPermissionDenied */, message);
1880
- }
1881
- if (originName) {
1882
- return new XmaxError("MEDIA_ERROR" /* mediaError */, message);
3192
+ requireCamera() {
3193
+ if (!this.camera) throw new XmaxError("INVALID_CONFIGURATION" /* invalidConfiguration */, "Camera capture is not running");
3194
+ return this.camera;
3195
+ }
3196
+ async run(operation) {
3197
+ try {
3198
+ return await operation();
3199
+ } catch (error) {
3200
+ throw this.mapError(error);
1883
3201
  }
1884
- return new XmaxError("RTC_ERROR" /* rtcError */, message);
3202
+ }
3203
+ mapError(error, permissionCode) {
3204
+ if (error instanceof XmaxError) return error;
3205
+ const value = error;
3206
+ const code = typeof _optionalChain([value, 'optionalAccess', _259 => _259.code]) === "string" && /^[A-Z_0-9-]{1,64}$/.test(value.code) ? value.code : "UNKNOWN";
3207
+ const denied = _optionalChain([value, 'optionalAccess', _260 => _260.name]) === "NotAllowedError" || code === "PERMISSION_DENIED" || code === "DEVICE_PERMISSION_DENIED";
3208
+ return new XmaxError(permissionCode && denied ? permissionCode : "RTC_ERROR" /* rtcError */, `VeRTC operation failed (${code})`);
3209
+ }
3210
+ cancelled() {
3211
+ return new XmaxError("CANCELLED" /* cancelled */, "VeRTC operation was cancelled");
1885
3212
  }
1886
3213
  };
1887
3214
 
3215
+ // src/Foundation/RTC/RtcFactory.ts
3216
+ function createRtcManager(provider, environment) {
3217
+ switch (provider) {
3218
+ case "agora" /* agora */:
3219
+ return new AgoraRtcManager({ environment });
3220
+ case "vertc" /* vertc */:
3221
+ return new VeRtcManager();
3222
+ default:
3223
+ return new TrtcRtcManager();
3224
+ }
3225
+ }
3226
+
1888
3227
  // src/Foundation/Media/Camera/CameraFrameValidator.ts
1889
3228
  var CAMERA_WARMUP_MS = 200;
1890
3229
  function waitForValidCameraFrame(track, signal) {
@@ -1945,7 +3284,7 @@ var PermissionManager = class {
1945
3284
  * @param deniedCode 权限被拒绝时抛出的错误码。
1946
3285
  */
1947
3286
  async ensurePermission(name, deniedCode) {
1948
- if (typeof navigator === "undefined" || !_optionalChain([navigator, 'access', _97 => _97.mediaDevices, 'optionalAccess', _98 => _98.getUserMedia])) {
3287
+ if (typeof navigator === "undefined" || !_optionalChain([navigator, 'access', _261 => _261.mediaDevices, 'optionalAccess', _262 => _262.getUserMedia])) {
1949
3288
  throw new XmaxError(
1950
3289
  deniedCode,
1951
3290
  "This browser does not support media capture (getUserMedia)"
@@ -1953,7 +3292,7 @@ var PermissionManager = class {
1953
3292
  }
1954
3293
  try {
1955
3294
  const permissions = navigator.permissions;
1956
- if (!_optionalChain([permissions, 'optionalAccess', _99 => _99.query])) {
3295
+ if (!_optionalChain([permissions, 'optionalAccess', _263 => _263.query])) {
1957
3296
  return;
1958
3297
  }
1959
3298
  const status = await permissions.query({
@@ -2133,10 +3472,10 @@ var _CameraController = class _CameraController {
2133
3472
  */
2134
3473
  __publicField(this, "storedUseMicrophone", false);
2135
3474
  __publicField(this, "storedFrameValidationEnabled", true);
2136
- this.permissionManager = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _100 => _100.permissionManager]), () => ( new PermissionManager()));
2137
- this.rtcManager = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _101 => _101.rtcManager]), () => ( new RtcManager()));
2138
- this.mediaService = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _102 => _102.mediaService]), () => ( new MediaService()));
2139
- this.errorListener = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _103 => _103.errorListener]), () => ( (() => {
3475
+ this.permissionManager = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _264 => _264.permissionManager]), () => ( new PermissionManager()));
3476
+ this.rtcManager = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _265 => _265.rtcManager]), () => ( new TrtcRtcManager()));
3477
+ this.mediaService = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _266 => _266.mediaService]), () => ( new MediaService()));
3478
+ this.errorListener = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _267 => _267.errorListener]), () => ( (() => {
2140
3479
  })));
2141
3480
  }
2142
3481
  /**
@@ -2162,7 +3501,7 @@ var _CameraController = class _CameraController {
2162
3501
  */
2163
3502
  async waitForValidCameraFrame(signal) {
2164
3503
  const track = this.activeTrack;
2165
- const mediaTrack = _optionalChain([track, 'optionalAccess', _104 => _104.mediaStreamTrack]);
3504
+ const mediaTrack = _optionalChain([track, 'optionalAccess', _268 => _268.mediaStreamTrack]);
2166
3505
  if (!mediaTrack) throw new XmaxError("MEDIA_ERROR" /* mediaError */, "Camera capture is not running");
2167
3506
  if (signal.aborted) throw new XmaxError("CANCELLED" /* cancelled */, "Camera frame validation cancelled");
2168
3507
  if (!this.isFrameValidationEnabled) return;
@@ -2262,7 +3601,7 @@ var _CameraController = class _CameraController {
2262
3601
  */
2263
3602
  async switchCamera() {
2264
3603
  const track = this.activeTrack;
2265
- const position = _optionalChain([track, 'optionalAccess', _105 => _105.position]);
3604
+ const position = _optionalChain([track, 'optionalAccess', _269 => _269.position]);
2266
3605
  if (!track || !position) {
2267
3606
  throw new XmaxError(
2268
3607
  "RTC_ERROR" /* rtcError */,
@@ -2280,7 +3619,7 @@ var _CameraController = class _CameraController {
2280
3619
  }
2281
3620
  this.previewStream.addTrack(mediaTrack);
2282
3621
  }
2283
- _optionalChain([this, 'access', _106 => _106.previewMirrorApplier, 'optionalCall', _107 => _107(nextPosition === "front" /* front */)]);
3622
+ _optionalChain([this, 'access', _270 => _270.previewMirrorApplier, 'optionalCall', _271 => _271(nextPosition === "front" /* front */)]);
2284
3623
  this.observeMediaTrack(track, mediaTrack);
2285
3624
  return new RealtimeMediaStream({ id: "stream0" /* local */, videoTrack: track });
2286
3625
  }
@@ -2425,6 +3764,53 @@ var RealtimeSession = class {
2425
3764
  }
2426
3765
  };
2427
3766
 
3767
+ // src/Foundation/RTC/RoomJoinConfiguration.ts
3768
+ var RoomJoinConfiguration = class {
3769
+ /**
3770
+ * 创建房间加入参数。
3771
+ *
3772
+ * @param init.roomID TRTC 房间号。
3773
+ * @param init.userID TRTC 登录身份。
3774
+ * @param init.sdkAppID TRTC SDKAppID(字符串形式)。
3775
+ * @param init.userSig TRTC 登录签名。
3776
+ * @param init.privateMapKey TRTC 进房鉴权字段。
3777
+ */
3778
+ constructor(init) {
3779
+ /**
3780
+ * RTC 提供方。
3781
+ */
3782
+ __publicField(this, "provider", "trtc");
3783
+ /**
3784
+ * 进房参数
3785
+ */
3786
+ /**
3787
+ * TRTC 房间号(字符串房间号)。
3788
+ */
3789
+ __publicField(this, "roomID");
3790
+ /**
3791
+ * TRTC 登录身份(`rtc_user_id`)。
3792
+ */
3793
+ __publicField(this, "userID");
3794
+ /**
3795
+ * TRTC SDKAppID(字符串形式,进房时转换为数值)。
3796
+ */
3797
+ __publicField(this, "sdkAppID");
3798
+ /**
3799
+ * TRTC 登录签名。
3800
+ */
3801
+ __publicField(this, "userSig");
3802
+ /**
3803
+ * TRTC 进房鉴权字段(字符串房间号对应的 privateMapKey)。
3804
+ */
3805
+ __publicField(this, "privateMapKey");
3806
+ this.roomID = init.roomID;
3807
+ this.userID = init.userID;
3808
+ this.sdkAppID = init.sdkAppID;
3809
+ this.userSig = init.userSig;
3810
+ this.privateMapKey = init.privateMapKey;
3811
+ }
3812
+ };
3813
+
2428
3814
  // src/Service/Realtime/RealtimeSessionConnection.ts
2429
3815
  var RealtimeSessionConnection = class {
2430
3816
  /**
@@ -2443,7 +3829,7 @@ var RealtimeSessionConnection = class {
2443
3829
  * RTC 提供方
2444
3830
  */
2445
3831
  /**
2446
- * RTC 提供方标识(当前仅支持 `trtc`)。
3832
+ * TRTC 凭据的提供方判别字段。
2447
3833
  */
2448
3834
  __publicField(this, "provider");
2449
3835
  /**
@@ -2485,6 +3871,12 @@ var RealtimeSessionConnection = class {
2485
3871
  this.botID = init.botID;
2486
3872
  }
2487
3873
  };
3874
+ function toRoomJoinConfiguration(connection) {
3875
+ return connection.provider === "trtc" ? new RoomJoinConfiguration(connection) : connection;
3876
+ }
3877
+ function connectionAppID(connection) {
3878
+ return connection.provider === "trtc" ? connection.sdkAppID : connection.appID;
3879
+ }
2488
3880
 
2489
3881
  // src/Service/Realtime/RealtimeSessionService.ts
2490
3882
  var RealtimeSessionService = class _RealtimeSessionService {
@@ -2500,6 +3892,7 @@ var RealtimeSessionService = class _RealtimeSessionService {
2500
3892
  * 服务层组件
2501
3893
  */
2502
3894
  __publicField(this, "apiService");
3895
+ __publicField(this, "provider");
2503
3896
  /**
2504
3897
  * 心跳配置
2505
3898
  */
@@ -2509,7 +3902,16 @@ var RealtimeSessionService = class _RealtimeSessionService {
2509
3902
  * 运行状态
2510
3903
  */
2511
3904
  __publicField(this, "heartbeatVersion", 0);
3905
+ /**
3906
+ * 会话凭证缓存,支持心跳仅返回变更字段;关闭后清除。
3907
+ */
3908
+ __publicField(this, "credentials", /* @__PURE__ */ new Map());
3909
+ __publicField(this, "refreshes", /* @__PURE__ */ new Map());
2512
3910
  this.apiService = options.apiService;
3911
+ this.provider = _nullishCoalesce(options.provider, () => ( "trtc")) /* trtc */;
3912
+ if (!Object.values(RtcProvider).includes(this.provider)) {
3913
+ throw new XmaxError("INVALID_CONFIGURATION" /* invalidConfiguration */, "Unsupported RTC provider");
3914
+ }
2513
3915
  this.heartbeatIntervalMs = _nullishCoalesce(options.heartbeatIntervalMs, () => ( 1e4));
2514
3916
  this.sleep = _nullishCoalesce(options.sleep, () => ( ((ms) => new Promise((resolve) => setTimeout(resolve, ms)))));
2515
3917
  }
@@ -2520,7 +3922,18 @@ var RealtimeSessionService = class _RealtimeSessionService {
2520
3922
  const payload = await this.apiService.post("/session", {
2521
3923
  model
2522
3924
  });
2523
- return _RealtimeSessionService.makeSession(payload, true);
3925
+ try {
3926
+ const session = this.makeSession(payload, true);
3927
+ _RealtimeSessionService.ensureSessionActive(session);
3928
+ const credentials = _RealtimeSessionService.decodeConnectionPayload(payload.modelExtra);
3929
+ this.credentials.set(session.id, this.provider === "vertc" /* vertc */ ? { ...credentials, user_id: session.connection.userID } : credentials);
3930
+ return session;
3931
+ } catch (error) {
3932
+ const sessionID = _RealtimeSessionService.nonEmpty(payload.sessionUid);
3933
+ if (sessionID) await this.closeSession(sessionID).catch(() => {
3934
+ });
3935
+ throw error;
3936
+ }
2524
3937
  }
2525
3938
  /**
2526
3939
  * 启动指定会话的周期心跳;重复启动会替换当前心跳。
@@ -2539,6 +3952,8 @@ var RealtimeSessionService = class _RealtimeSessionService {
2539
3952
  * 关闭指定实时会话;请求带 keepalive,页面卸载后仍能发出。
2540
3953
  */
2541
3954
  async closeSession(sessionID) {
3955
+ this.credentials.delete(sessionID);
3956
+ this.refreshes.delete(sessionID);
2542
3957
  try {
2543
3958
  await this.apiService.delete(
2544
3959
  `/session/${sessionID}`,
@@ -2563,7 +3978,7 @@ var RealtimeSessionService = class _RealtimeSessionService {
2563
3978
  return;
2564
3979
  }
2565
3980
  _RealtimeSessionService.ensureSessionActive(session);
2566
- _optionalChain([handlers, 'access', _108 => _108.onRefresh, 'optionalCall', _109 => _109(session)]);
3981
+ await _optionalChain([handlers, 'access', _272 => _272.onRefresh, 'optionalCall', _273 => _273(session)]);
2567
3982
  } catch (error) {
2568
3983
  if (this.heartbeatVersion !== version) {
2569
3984
  return;
@@ -2577,11 +3992,35 @@ var RealtimeSessionService = class _RealtimeSessionService {
2577
3992
  /**
2578
3993
  * 发送一次心跳请求并解析会话数据(不要求携带完整连接参数)。
2579
3994
  */
2580
- async heartbeatSession(sessionID) {
3995
+ heartbeatSession(sessionID) {
3996
+ const pending = this.refreshes.get(sessionID);
3997
+ if (pending) return pending;
3998
+ const operation = this.performHeartbeat(sessionID);
3999
+ this.refreshes.set(sessionID, operation);
4000
+ void operation.finally(() => {
4001
+ if (this.refreshes.get(sessionID) === operation) this.refreshes.delete(sessionID);
4002
+ }).catch(() => {
4003
+ });
4004
+ return operation;
4005
+ }
4006
+ /**
4007
+ * 合并心跳部分凭据;已关闭会话的迟到响应不重建缓存。
4008
+ */
4009
+ async performHeartbeat(sessionID) {
4010
+ const previous = this.credentials.get(sessionID);
2581
4011
  const payload = await this.apiService.put(
2582
4012
  `/session/${sessionID}/heartbeat`
2583
4013
  );
2584
- return _RealtimeSessionService.makeSession(payload, false);
4014
+ const incoming = _RealtimeSessionService.decodeConnectionPayload(payload.modelExtra);
4015
+ if (payload.modelExtra != null && !incoming) {
4016
+ throw new XmaxError("SESSION_ERROR" /* sessionError */, "Invalid heartbeat RTC credentials");
4017
+ }
4018
+ const merged = incoming ? { ...previous, ...incoming } : previous;
4019
+ const session = this.makeSession({ ...payload, modelExtra: merged }, merged !== void 0);
4020
+ if (session.id !== sessionID) throw new XmaxError("SESSION_ERROR" /* sessionError */, "Heartbeat session identity changed");
4021
+ _RealtimeSessionService.ensureSessionActive(session);
4022
+ if (previous && this.credentials.get(sessionID) === previous && merged) this.credentials.set(sessionID, merged);
4023
+ return session;
2585
4024
  }
2586
4025
  /**
2587
4026
  * 校验会话仍处于活跃状态;状态明确非 ACTIVE 时抛出会话错误。
@@ -2598,12 +4037,12 @@ var RealtimeSessionService = class _RealtimeSessionService {
2598
4037
  /**
2599
4038
  * 由会话接口数据构造会话模型;`requiresConnection` 要求携带完整 RTC 连接参数。
2600
4039
  */
2601
- static makeSession(payload, requiresConnection) {
4040
+ makeSession(payload, requiresConnection) {
2602
4041
  const sessionID = _RealtimeSessionService.nonEmpty(payload.sessionUid);
2603
4042
  if (!sessionID) {
2604
4043
  throw new XmaxError("SESSION_ERROR" /* sessionError */, "Invalid session response");
2605
4044
  }
2606
- const connection = _RealtimeSessionService.makeConnection(payload.modelExtra);
4045
+ const connection = this.makeConnection(payload.modelExtra, payload.userUid);
2607
4046
  if (requiresConnection && !connection) {
2608
4047
  throw new XmaxError(
2609
4048
  "SESSION_ERROR" /* sessionError */,
@@ -2619,19 +4058,30 @@ var RealtimeSessionService = class _RealtimeSessionService {
2619
4058
  });
2620
4059
  }
2621
4060
  /**
2622
- * 解析 `modelExtra` 中的 TRTC 连接参数;字段不完整或提供方不支持时返回空。
4061
+ * 解析 `modelExtra` 中的 RTC 连接参数;字段不完整或提供方不支持时返回空。
2623
4062
  */
2624
- static makeConnection(modelExtra) {
4063
+ makeConnection(modelExtra, fallbackUserID) {
2625
4064
  const payload = _RealtimeSessionService.decodeConnectionPayload(modelExtra);
2626
4065
  if (!payload) {
2627
4066
  return void 0;
2628
4067
  }
2629
- if (_RealtimeSessionService.nonEmpty(payload.provider) !== "trtc") {
2630
- return void 0;
2631
- }
4068
+ const provider = this.provider;
2632
4069
  const roomID = _RealtimeSessionService.nonEmpty(payload.room_id);
2633
4070
  const sdkAppID = _RealtimeSessionService.nonEmpty(payload.rtc_app_id);
2634
- const userID = _RealtimeSessionService.nonEmpty(payload.rtc_user_id);
4071
+ const userID = provider === "vertc" /* vertc */ ? _nullishCoalesce(_RealtimeSessionService.nonEmpty(payload.user_id), () => ( _RealtimeSessionService.nonEmpty(fallbackUserID))) : _RealtimeSessionService.nonEmpty(payload.rtc_user_id);
4072
+ if (!roomID || !sdkAppID || !userID) return void 0;
4073
+ if (provider === "agora" /* agora */ || provider === "vertc" /* vertc */) {
4074
+ const roomToken = _RealtimeSessionService.nonEmpty(payload.room_token);
4075
+ if (!roomToken) return void 0;
4076
+ return Object.freeze({
4077
+ provider,
4078
+ roomID,
4079
+ appID: sdkAppID,
4080
+ userID,
4081
+ roomToken,
4082
+ botID: _RealtimeSessionService.nonEmpty(provider === "vertc" /* vertc */ ? payload.bot_name : payload.rtc_bot_id)
4083
+ });
4084
+ }
2635
4085
  const userSig = _RealtimeSessionService.nonEmpty(payload.user_sig);
2636
4086
  const privateMapKey = _RealtimeSessionService.nonEmpty(
2637
4087
  payload.private_map_key_with_string_room_id
@@ -2655,12 +4105,12 @@ var RealtimeSessionService = class _RealtimeSessionService {
2655
4105
  static decodeConnectionPayload(modelExtra) {
2656
4106
  if (typeof modelExtra === "string") {
2657
4107
  try {
2658
- return JSON.parse(modelExtra);
2659
- } catch (e12) {
4108
+ return _RealtimeSessionService.decodeConnectionPayload(JSON.parse(modelExtra));
4109
+ } catch (e14) {
2660
4110
  return void 0;
2661
4111
  }
2662
4112
  }
2663
- if (typeof modelExtra === "object" && modelExtra !== null) {
4113
+ if (typeof modelExtra === "object" && modelExtra !== null && !Array.isArray(modelExtra)) {
2664
4114
  return modelExtra;
2665
4115
  }
2666
4116
  return void 0;
@@ -2669,7 +4119,7 @@ var RealtimeSessionService = class _RealtimeSessionService {
2669
4119
  * 归一化可选字符串:去除首尾空白后为空时返回 `undefined`。
2670
4120
  */
2671
4121
  static nonEmpty(value) {
2672
- const normalized = _optionalChain([value, 'optionalAccess', _110 => _110.trim, 'call', _111 => _111()]);
4122
+ const normalized = typeof value === "string" ? value.trim() : void 0;
2673
4123
  return normalized || void 0;
2674
4124
  }
2675
4125
  };
@@ -2741,7 +4191,7 @@ var RealtimeState = class {
2741
4191
  * @param other 待比较的状态。
2742
4192
  */
2743
4193
  equals(other) {
2744
- return this.connectionState === other.connectionState && this.sessionID === other.sessionID && this.taskID === other.taskID && _optionalChain([this, 'access', _112 => _112.reason, 'optionalAccess', _113 => _113.kind]) === _optionalChain([other, 'access', _114 => _114.reason, 'optionalAccess', _115 => _115.kind]);
4194
+ return this.connectionState === other.connectionState && this.sessionID === other.sessionID && this.taskID === other.taskID && _optionalChain([this, 'access', _274 => _274.reason, 'optionalAccess', _275 => _275.kind]) === _optionalChain([other, 'access', _276 => _276.reason, 'optionalAccess', _277 => _277.kind]);
2745
4195
  }
2746
4196
  };
2747
4197
 
@@ -2776,49 +4226,6 @@ var RemoteStream = class {
2776
4226
  }
2777
4227
  };
2778
4228
 
2779
- // src/Foundation/RTC/RoomJoinConfiguration.ts
2780
- var RoomJoinConfiguration = class {
2781
- /**
2782
- * 创建房间加入参数。
2783
- *
2784
- * @param init.roomID TRTC 房间号。
2785
- * @param init.userID TRTC 登录身份。
2786
- * @param init.sdkAppID TRTC SDKAppID(字符串形式)。
2787
- * @param init.userSig TRTC 登录签名。
2788
- * @param init.privateMapKey TRTC 进房鉴权字段。
2789
- */
2790
- constructor(init) {
2791
- /**
2792
- * 进房参数
2793
- */
2794
- /**
2795
- * TRTC 房间号(字符串房间号)。
2796
- */
2797
- __publicField(this, "roomID");
2798
- /**
2799
- * TRTC 登录身份(`rtc_user_id`)。
2800
- */
2801
- __publicField(this, "userID");
2802
- /**
2803
- * TRTC SDKAppID(字符串形式,进房时转换为数值)。
2804
- */
2805
- __publicField(this, "sdkAppID");
2806
- /**
2807
- * TRTC 登录签名。
2808
- */
2809
- __publicField(this, "userSig");
2810
- /**
2811
- * TRTC 进房鉴权字段(字符串房间号对应的 privateMapKey)。
2812
- */
2813
- __publicField(this, "privateMapKey");
2814
- this.roomID = init.roomID;
2815
- this.userID = init.userID;
2816
- this.sdkAppID = init.sdkAppID;
2817
- this.userSig = init.userSig;
2818
- this.privateMapKey = init.privateMapKey;
2819
- }
2820
- };
2821
-
2822
4229
  // src/Stream/Room/RoomEvent.ts
2823
4230
  var RoomEvent = class _RoomEvent {
2824
4231
  /**
@@ -2910,7 +4317,7 @@ var RoomEvent = class _RoomEvent {
2910
4317
  static encode(payload) {
2911
4318
  try {
2912
4319
  return JSON.stringify({ ...payload, runtime: RuntimeInfo.toJSON() });
2913
- } catch (e13) {
4320
+ } catch (e15) {
2914
4321
  throw new XmaxError(
2915
4322
  "INTERNAL_ERROR" /* internalError */,
2916
4323
  "Failed to encode RTC room event"
@@ -2969,7 +4376,7 @@ var RoomHeartbeat = class {
2969
4376
  return;
2970
4377
  }
2971
4378
  try {
2972
- this.rtcManager.sendRoomMessage(RoomEvent.heartbeat({ userID }));
4379
+ await this.rtcManager.sendRoomMessage(RoomEvent.heartbeat({ userID }));
2973
4380
  } catch (error) {
2974
4381
  if (this.cycleVersion !== version) {
2975
4382
  return;
@@ -2986,7 +4393,11 @@ var RoomHeartbeat = class {
2986
4393
  // src/Stream/Room/RoomMessageCodec.ts
2987
4394
  var CHUNK_EVENT = "__trtc_chunk__";
2988
4395
  var _RoomMessageCodec = class _RoomMessageCodec {
2989
- constructor() {
4396
+ /**
4397
+ * 创建厂商协议编解码器;TRTC 保持原事件名,Agora 使用 __agora_chunk__;null 表示不拆包。
4398
+ */
4399
+ constructor(chunkEvent = CHUNK_EVENT) {
4400
+ __publicField(this, "chunkEvent", chunkEvent);
2990
4401
  /**
2991
4402
  * 接收缓存
2992
4403
  */
@@ -2996,10 +4407,10 @@ var _RoomMessageCodec = class _RoomMessageCodec {
2996
4407
  * 编码出站消息。
2997
4408
  *
2998
4409
  * @returns 不超过阈值时返回仅含原消息的单元素数组;
2999
- * 否则返回按 `__trtc_chunk__` 协议包装的分片 JSON 数组。
4410
+ * 否则返回按当前提供方分片协议包装的 JSON 数组。
3000
4411
  */
3001
4412
  encodeOutgoing(message) {
3002
- if (_RoomMessageCodec.byteLength(message) <= _RoomMessageCodec.chunkThresholdBytes) {
4413
+ if (this.chunkEvent === null || _RoomMessageCodec.byteLength(message) <= _RoomMessageCodec.chunkThresholdBytes) {
3003
4414
  return [message];
3004
4415
  }
3005
4416
  const eventId = _RoomMessageCodec.makeEventId();
@@ -3014,7 +4425,7 @@ var _RoomMessageCodec = class _RoomMessageCodec {
3014
4425
  );
3015
4426
  return slices.map(
3016
4427
  (data, index) => JSON.stringify({
3017
- event: CHUNK_EVENT,
4428
+ event: this.chunkEvent,
3018
4429
  eventId,
3019
4430
  index,
3020
4431
  count,
@@ -3032,11 +4443,11 @@ var _RoomMessageCodec = class _RoomMessageCodec {
3032
4443
  let parsed;
3033
4444
  try {
3034
4445
  parsed = JSON.parse(message);
3035
- } catch (e14) {
4446
+ } catch (e16) {
3036
4447
  XmaxLogger.room.warning(() => "\u623F\u95F4\u6D88\u606F\u4E0D\u662F\u5408\u6CD5 JSON (Room Message Is Not Valid JSON)");
3037
4448
  return void 0;
3038
4449
  }
3039
- const chunk = _RoomMessageCodec.parseChunk(parsed);
4450
+ const chunk = this.parseChunk(parsed);
3040
4451
  if (!chunk) {
3041
4452
  return parsed;
3042
4453
  }
@@ -3063,7 +4474,7 @@ var _RoomMessageCodec = class _RoomMessageCodec {
3063
4474
  const joined = parts.join("");
3064
4475
  try {
3065
4476
  return JSON.parse(joined);
3066
- } catch (e15) {
4477
+ } catch (e17) {
3067
4478
  XmaxLogger.room.warning(
3068
4479
  () => `\u623F\u95F4\u6D88\u606F\u7EC4\u5305\u540E\u4E0D\u662F\u5408\u6CD5 JSON (Reassembled Message Is Not Valid JSON)
3069
4480
  \u2514\u2500 eventId: ${chunk.eventId}`
@@ -3080,12 +4491,12 @@ var _RoomMessageCodec = class _RoomMessageCodec {
3080
4491
  /**
3081
4492
  * 从已解析的值识别分片消息;字段不完整时按普通业务消息处理。
3082
4493
  */
3083
- static parseChunk(parsed) {
3084
- if (typeof parsed !== "object" || parsed === null) {
4494
+ parseChunk(parsed) {
4495
+ if (this.chunkEvent === null || typeof parsed !== "object" || parsed === null) {
3085
4496
  return void 0;
3086
4497
  }
3087
4498
  const record = parsed;
3088
- if (record.event !== CHUNK_EVENT || typeof record.eventId !== "string" || !Number.isInteger(record.index) || !Number.isInteger(record.count) || typeof record.data !== "string") {
4499
+ if (record.event !== this.chunkEvent || typeof record.eventId !== "string" || record.eventId.length === 0 || !Number.isInteger(record.index) || !Number.isInteger(record.count) || typeof record.data !== "string") {
3089
4500
  return void 0;
3090
4501
  }
3091
4502
  const index = record.index;
@@ -3115,7 +4526,7 @@ var _RoomMessageCodec = class _RoomMessageCodec {
3115
4526
  let current = "";
3116
4527
  let currentBytes = 0;
3117
4528
  for (const char of message) {
3118
- const bytes = _RoomMessageCodec.byteLength(char);
4529
+ const bytes = _RoomMessageCodec.byteLength(JSON.stringify(char)) - 2;
3119
4530
  if (currentBytes + bytes > budget && current.length > 0) {
3120
4531
  slices.push(current);
3121
4532
  current = "";
@@ -3184,6 +4595,8 @@ var RoomController = class {
3184
4595
  */
3185
4596
  __publicField(this, "heartbeat");
3186
4597
  __publicField(this, "codec");
4598
+ __publicField(this, "customCodec");
4599
+ __publicField(this, "sendTail", Promise.resolve());
3187
4600
  /**
3188
4601
  * 事件监听
3189
4602
  */
@@ -3196,6 +4609,7 @@ var RoomController = class {
3196
4609
  this.rtcManager = options.rtcManager;
3197
4610
  this.heartbeat = _nullishCoalesce(options.heartbeat, () => ( new RoomHeartbeat({ rtcManager: options.rtcManager })));
3198
4611
  this.codec = _nullishCoalesce(options.codec, () => ( new RoomMessageCodec()));
4612
+ this.customCodec = options.codec !== void 0;
3199
4613
  }
3200
4614
  /**
3201
4615
  * 加入实时房间,并在异步边界前后确认连接操作仍然有效。
@@ -3218,15 +4632,10 @@ var RoomController = class {
3218
4632
  this.state = { kind: "joining", operationID };
3219
4633
  this.heartbeat.stop();
3220
4634
  try {
3221
- await this.rtcManager.joinRoom(
3222
- new RoomJoinConfiguration({
3223
- roomID: connection.roomID,
3224
- userID: connection.userID,
3225
- sdkAppID: connection.sdkAppID,
3226
- userSig: connection.userSig,
3227
- privateMapKey: connection.privateMapKey
3228
- })
4635
+ if (!this.customCodec) this.codec = new RoomMessageCodec(
4636
+ connection.provider === "vertc" ? null : connection.provider === "agora" ? "__agora_chunk__" : "__trtc_chunk__"
3229
4637
  );
4638
+ await this.rtcManager.joinRoom(toRoomJoinConfiguration(connection));
3230
4639
  ensureActive();
3231
4640
  if (this.state.kind !== "joining" || this.state.operationID !== operationID || this.leaveOperation) {
3232
4641
  throw new XmaxError("CANCELLED" /* cancelled */, "RTC room join was cancelled");
@@ -3266,7 +4675,7 @@ var RoomController = class {
3266
4675
  * @throws 房间未就绪或信令发送失败时抛出错误。
3267
4676
  */
3268
4677
  startGeneration(options) {
3269
- this.send(
4678
+ return this.send(
3270
4679
  RoomEvent.start({
3271
4680
  userID: this.requireUserID(),
3272
4681
  taskID: options.taskID,
@@ -3282,7 +4691,7 @@ var RoomController = class {
3282
4691
  * @throws 房间未就绪或信令发送失败时抛出错误。
3283
4692
  */
3284
4693
  changeGenerationCondition(options) {
3285
- this.send(
4694
+ return this.send(
3286
4695
  RoomEvent.changeCondition({
3287
4696
  userID: this.requireUserID(),
3288
4697
  taskID: options.taskID,
@@ -3299,7 +4708,7 @@ var RoomController = class {
3299
4708
  */
3300
4709
  changeTargetSize(options) {
3301
4710
  options.ensureActive();
3302
- this.send(
4711
+ return this.send(
3303
4712
  RoomEvent.changeTargetSize({
3304
4713
  userID: this.requireUserID(),
3305
4714
  taskID: options.taskID,
@@ -3312,18 +4721,18 @@ var RoomController = class {
3312
4721
  */
3313
4722
  stopGeneration(taskID) {
3314
4723
  if (!taskID || this.state.kind !== "joined") {
3315
- return;
4724
+ return Promise.resolve();
3316
4725
  }
3317
- this.send(RoomEvent.stop({ userID: this.state.userID, taskID }));
4726
+ return this.send(RoomEvent.stop({ userID: this.state.userID, taskID }));
3318
4727
  }
3319
4728
  /**
3320
4729
  * 发送生成任务的交互轨迹;任务标识或轨迹为空时忽略。
3321
4730
  */
3322
4731
  sendTracks(taskID, points) {
3323
4732
  if (!taskID || points.length === 0) {
3324
- return;
4733
+ return Promise.resolve();
3325
4734
  }
3326
- this.send(
4735
+ return this.send(
3327
4736
  RoomEvent.tracks({
3328
4737
  userID: this.requireUserID(),
3329
4738
  taskID,
@@ -3344,8 +4753,12 @@ var RoomController = class {
3344
4753
  this.state = { kind: "idle" };
3345
4754
  this.heartbeat.stop();
3346
4755
  this.codec.reset();
3347
- await this.rtcManager.leaveRoom();
3348
- this.leaveOperation = void 0;
4756
+ this.sendTail = Promise.resolve();
4757
+ try {
4758
+ await this.rtcManager.leaveRoom();
4759
+ } finally {
4760
+ this.leaveOperation = void 0;
4761
+ }
3349
4762
  }
3350
4763
  /**
3351
4764
  * 组包入站消息并按目标用户过滤后分发完整业务消息。
@@ -3361,24 +4774,32 @@ var RoomController = class {
3361
4774
  if (targetUserID && ownUserID && targetUserID !== ownUserID) {
3362
4775
  return;
3363
4776
  }
3364
- _optionalChain([this, 'access', _116 => _116.listener, 'optionalAccess', _117 => _117.onRoomMessage, 'call', _118 => _118(senderUserID, message)]);
4777
+ _optionalChain([this, 'access', _278 => _278.listener, 'optionalAccess', _279 => _279.onRoomMessage, 'call', _280 => _280(senderUserID, message)]);
3365
4778
  }
3366
4779
  /**
3367
4780
  * 发送房间信令并输出日志。
3368
4781
  */
3369
4782
  send(message) {
3370
- for (const packet of this.codec.encodeOutgoing(message)) {
3371
- try {
3372
- this.rtcManager.sendRoomMessage(packet);
3373
- } catch (error) {
3374
- throw XmaxError.from(error);
4783
+ const state2 = this.state;
4784
+ const packets = this.codec.encodeOutgoing(message);
4785
+ const operation = this.sendTail.then(async () => {
4786
+ for (const packet of packets) {
4787
+ if (state2 !== this.state || state2.kind !== "joined") {
4788
+ throw new XmaxError("CANCELLED" /* cancelled */, "Room message was cancelled");
4789
+ }
4790
+ await this.rtcManager.sendRoomMessage(packet);
3375
4791
  }
3376
- }
3377
- XmaxLogger.room.info(
3378
- () => `\u53D1\u9001\u623F\u95F4\u4FE1\u4EE4 (Outbound Room Signaling)
4792
+ XmaxLogger.room.info(
4793
+ () => `\u53D1\u9001\u623F\u95F4\u4FE1\u4EE4 (Outbound Room Signaling)
3379
4794
  \u2514\u2500 ${XmaxLogger.localized("\u5185\u5BB9\uFF1A", "Content:")}
3380
4795
  ${JSON.stringify(JSON.parse(message), null, 2)}`
3381
- );
4796
+ );
4797
+ }).catch((error) => {
4798
+ throw XmaxError.from(error);
4799
+ });
4800
+ this.sendTail = operation.catch(() => {
4801
+ });
4802
+ return operation;
3382
4803
  }
3383
4804
  /**
3384
4805
  * 返回当前房间中的用户标识;未进房时抛出错误。
@@ -3551,6 +4972,9 @@ function makeInitialState() {
3551
4972
  localVideoPublished: false,
3552
4973
  localAudioPublished: false,
3553
4974
  subscribedRemoteUserIDs: /* @__PURE__ */ new Set(),
4975
+ publishedRemoteUserIDs: /* @__PURE__ */ new Set(),
4976
+ remoteVideoTracks: /* @__PURE__ */ new Map(),
4977
+ remoteVideoSubscriptions: /* @__PURE__ */ new Map(),
3554
4978
  subscribedRemoteAudioUserIDs: /* @__PURE__ */ new Set()
3555
4979
  };
3556
4980
  }
@@ -3604,20 +5028,24 @@ var StreamController = class {
3604
5028
  })));
3605
5029
  this.generationTimeoutMs = _nullishCoalesce(options.generationTimeoutMs, () => ( 3e4));
3606
5030
  this.rtcManager.setEventListener({
5031
+ onTokenWillExpire: options.onCredentialsRequired,
5032
+ onTokenExpired: options.onCredentialsRequired,
5033
+ onRoomRejoining: () => this.handleRoomRejoining(),
5034
+ onError: (error) => this.errorListener(error),
3607
5035
  onNetworkStatistics: (statistics) => {
3608
5036
  if (this.state.localVideoPublished) {
3609
- _optionalChain([this, 'access', _119 => _119.networkStatisticsListener, 'optionalCall', _120 => _120(statistics)]);
5037
+ _optionalChain([this, 'access', _281 => _281.networkStatisticsListener, 'optionalCall', _282 => _282(statistics)]);
3610
5038
  }
3611
5039
  },
3612
5040
  onRemoteVideoStatistics: (statistics) => {
3613
- const userID = _optionalChain([this, 'access', _121 => _121.state, 'access', _122 => _122.activeRemoteStream, 'optionalAccess', _123 => _123.userID]);
3614
- _optionalChain([this, 'access', _124 => _124.remoteVideoStatisticsListener, 'optionalCall', _125 => _125(
5041
+ const userID = _optionalChain([this, 'access', _283 => _283.state, 'access', _284 => _284.activeRemoteStream, 'optionalAccess', _285 => _285.userID]);
5042
+ _optionalChain([this, 'access', _286 => _286.remoteVideoStatisticsListener, 'optionalCall', _287 => _287(
3615
5043
  userID ? statistics.find((item) => item.userID === userID) : void 0
3616
5044
  )]);
3617
5045
  },
3618
5046
  onLocalVideoStatistics: (statistics) => {
3619
5047
  if (this.state.localVideoPublished) {
3620
- _optionalChain([this, 'access', _126 => _126.localVideoStatisticsListener, 'optionalCall', _127 => _127(statistics)]);
5048
+ _optionalChain([this, 'access', _288 => _288.localVideoStatisticsListener, 'optionalCall', _289 => _289(statistics)]);
3621
5049
  }
3622
5050
  },
3623
5051
  onCustomMessageReceived: (senderUserID, message) => {
@@ -3682,14 +5110,14 @@ var StreamController = class {
3682
5110
  * @throws 连接已取消,或 RTC 进房、房间配置与本地流发布失败时抛出错误。
3683
5111
  */
3684
5112
  async connect(connection, includeLocalAudio, ensureActive, beforePublish) {
5113
+ this.configureRoom({ roomID: connection.roomID, botID: connection.botID });
3685
5114
  await this.roomController.join(connection, ensureActive);
3686
5115
  ensureActive();
3687
- this.configureRoom({ roomID: connection.roomID, botID: connection.botID });
3688
5116
  XmaxLogger.stream.info(
3689
5117
  () => `RTC \u623F\u95F4\u5DF2\u914D\u7F6E (RTC Room Configured)
3690
5118
  \u2514\u2500 roomID: ${connection.roomID}, botID: ${_nullishCoalesce(connection.botID, () => ( "(\u672A\u8BBE\u7F6E)"))}`
3691
5119
  );
3692
- await _optionalChain([beforePublish, 'optionalCall', _128 => _128()]);
5120
+ await _optionalChain([beforePublish, 'optionalCall', _290 => _290()]);
3693
5121
  ensureActive();
3694
5122
  await this.publishLocalStream(includeLocalAudio);
3695
5123
  }
@@ -3700,12 +5128,18 @@ var StreamController = class {
3700
5128
  await this.resetStream();
3701
5129
  await this.roomController.leave();
3702
5130
  }
5131
+ /**
5132
+ * 将会话刷新后的凭据应用到当前 RTC 连接。
5133
+ */
5134
+ async updateCredentials(connection, signal) {
5135
+ await this.rtcManager.updateCredentials(toRoomJoinConfiguration(connection), signal);
5136
+ }
3703
5137
  /**
3704
5138
  * 建立生成任务并发送开始信令。
3705
5139
  *
3706
5140
  * @returns 等待远端结果流确认的 Promise。
3707
- * @throws 任务标识无效、RTC 房间未就绪、已有生成任务,或开始信令
3708
- * 发送失败时同步抛出错误。
5141
+ * @throws 任务标识无效、RTC 房间未就绪或已有生成任务时同步抛错;
5142
+ * 发送失败或确认超时通过返回的 Promise 拒绝。
3709
5143
  */
3710
5144
  beginGeneration(options) {
3711
5145
  const taskID = options.taskID.trim();
@@ -3747,24 +5181,44 @@ var StreamController = class {
3747
5181
  };
3748
5182
  this.state.generationTaskID = taskID;
3749
5183
  this.state.generationWaiter = waiter;
5184
+ void confirmation.catch(() => {
5185
+ });
3750
5186
  try {
3751
- this.roomController.startGeneration({
5187
+ const sending = this.roomController.startGeneration({
3752
5188
  taskID,
3753
5189
  videoFormat: options.videoFormat,
3754
5190
  targetSize: options.targetSize,
3755
5191
  context: options.context
3756
5192
  });
5193
+ const state2 = this.state;
5194
+ const sent = Promise.resolve(sending).then(() => {
5195
+ if (this.state !== state2 || state2.generationTaskID !== taskID) return;
5196
+ for (const userID of state2.publishedRemoteUserIDs) {
5197
+ void this.handleRemoteVideoPublished(userID, true);
5198
+ }
5199
+ }).catch((error) => {
5200
+ const mapped = XmaxError.from(error);
5201
+ this.rejectGenerationStart(taskID, mapped);
5202
+ if (this.state.generationTaskID === taskID) {
5203
+ this.state.generationTaskID = void 0;
5204
+ void this.roomController.stopGeneration(taskID).catch(() => {
5205
+ });
5206
+ }
5207
+ throw mapped;
5208
+ });
5209
+ return Promise.all([sent, confirmation]).then(() => {
5210
+ });
3757
5211
  } catch (error) {
3758
5212
  const mapped = XmaxError.from(error);
3759
- this.state.generationTaskID = void 0;
3760
5213
  this.rejectGenerationStart(taskID, mapped);
5214
+ this.state.generationTaskID = void 0;
3761
5215
  try {
3762
- this.roomController.stopGeneration(taskID);
3763
- } catch (e16) {
5216
+ void this.roomController.stopGeneration(taskID).catch(() => {
5217
+ });
5218
+ } catch (e18) {
3764
5219
  }
3765
5220
  throw mapped;
3766
5221
  }
3767
- return confirmation;
3768
5222
  }
3769
5223
  /**
3770
5224
  * 在远端首帧已经可显示后订阅当前生成流的音频。
@@ -3786,9 +5240,9 @@ var StreamController = class {
3786
5240
  *
3787
5241
  * @throws RTC 房间未就绪或条件变更信令发送失败时抛出错误。
3788
5242
  */
3789
- updateGeneration(options) {
5243
+ async updateGeneration(options) {
3790
5244
  try {
3791
- this.roomController.changeGenerationCondition({
5245
+ await this.roomController.changeGenerationCondition({
3792
5246
  taskID: options.taskID,
3793
5247
  videoFormat: options.videoFormat,
3794
5248
  targetSize: options.targetSize,
@@ -3803,9 +5257,9 @@ var StreamController = class {
3803
5257
  *
3804
5258
  * @throws 操作已取消、房间未就绪或信令发送失败时抛出错误。
3805
5259
  */
3806
- changeTargetSize(taskID, targetSize, ensureActive) {
5260
+ async changeTargetSize(taskID, targetSize, ensureActive) {
3807
5261
  try {
3808
- this.roomController.changeTargetSize({ taskID, targetSize, ensureActive });
5262
+ await this.roomController.changeTargetSize({ taskID, targetSize, ensureActive });
3809
5263
  } catch (error) {
3810
5264
  throw XmaxError.from(error);
3811
5265
  }
@@ -3821,7 +5275,7 @@ var StreamController = class {
3821
5275
  return;
3822
5276
  }
3823
5277
  try {
3824
- this.roomController.stopGeneration(stoppedTaskID);
5278
+ await this.roomController.stopGeneration(stoppedTaskID);
3825
5279
  } catch (error) {
3826
5280
  throw XmaxError.from(error);
3827
5281
  }
@@ -3831,9 +5285,9 @@ var StreamController = class {
3831
5285
  *
3832
5286
  * @throws RTC 房间未就绪或轨迹信令发送失败时抛出错误。
3833
5287
  */
3834
- sendTracks(taskID, points) {
5288
+ async sendTracks(taskID, points) {
3835
5289
  try {
3836
- this.roomController.sendTracks(taskID, points);
5290
+ await this.roomController.sendTracks(taskID, points);
3837
5291
  } catch (error) {
3838
5292
  throw XmaxError.from(error);
3839
5293
  }
@@ -3849,7 +5303,7 @@ var StreamController = class {
3849
5303
  */
3850
5304
  configureRoom(options) {
3851
5305
  const roomID = options.roomID.trim();
3852
- const botID = _nullishCoalesce(_optionalChain([options, 'access', _129 => _129.botID, 'optionalAccess', _130 => _130.trim, 'call', _131 => _131()]), () => ( ""));
5306
+ const botID = _nullishCoalesce(_optionalChain([options, 'access', _291 => _291.botID, 'optionalAccess', _292 => _292.trim, 'call', _293 => _293()]), () => ( ""));
3853
5307
  if (!roomID) {
3854
5308
  throw new XmaxError(
3855
5309
  "INVALID_CONFIGURATION" /* invalidConfiguration */,
@@ -3908,6 +5362,12 @@ var StreamController = class {
3908
5362
  if (!this.state.roomID) {
3909
5363
  return;
3910
5364
  }
5365
+ if (published) this.state.publishedRemoteUserIDs.add(trimmedUserID);
5366
+ else {
5367
+ this.state.publishedRemoteUserIDs.delete(trimmedUserID);
5368
+ this.state.remoteVideoSubscriptions.delete(trimmedUserID);
5369
+ this.state.remoteVideoTracks.delete(trimmedUserID);
5370
+ }
3911
5371
  if (this.state.botID && this.state.botID !== trimmedUserID) {
3912
5372
  if (this.state.generationTaskID === void 0) {
3913
5373
  XmaxLogger.stream.warning(
@@ -3926,37 +5386,55 @@ var StreamController = class {
3926
5386
  return;
3927
5387
  }
3928
5388
  this.state.subscribedRemoteUserIDs.delete(trimmedUserID);
3929
- if (_optionalChain([this, 'access', _132 => _132.state, 'access', _133 => _133.activeRemoteStream, 'optionalAccess', _134 => _134.userID]) === trimmedUserID) {
5389
+ if (_optionalChain([this, 'access', _294 => _294.state, 'access', _295 => _295.activeRemoteStream, 'optionalAccess', _296 => _296.userID]) === trimmedUserID) {
3930
5390
  await this.unsubscribeRemoteAudio(trimmedUserID);
3931
5391
  this.state.activeRemoteStream = void 0;
3932
5392
  this.clearRemoteStream();
3933
5393
  }
3934
5394
  }
5395
+ /**
5396
+ * RTC 重进房时废弃旧媒体绑定;业务任务、音频订阅意图和生成确认等待器继续保留。
5397
+ */
5398
+ handleRoomRejoining() {
5399
+ this.state.publishedRemoteUserIDs.clear();
5400
+ this.state.subscribedRemoteUserIDs.clear();
5401
+ this.state.remoteVideoSubscriptions.clear();
5402
+ this.state.remoteVideoTracks.clear();
5403
+ this.state.activeRemoteStream = void 0;
5404
+ this.clearRemoteStream();
5405
+ }
3935
5406
  /**
3936
5407
  * 订阅远端视频并在成功后就绪或确认生成任务。
3937
5408
  */
3938
5409
  async subscribeRemoteVideo(userID) {
3939
- if (this.state.subscribedRemoteUserIDs.has(userID)) {
5410
+ const state2 = this.state;
5411
+ const existing = state2.remoteVideoTracks.get(userID);
5412
+ if (existing) {
5413
+ if (state2.generationWaiter) this.confirmOrUpdateRemoteStream(userID, existing);
3940
5414
  return;
3941
5415
  }
5416
+ if (state2.remoteVideoSubscriptions.has(userID)) return;
5417
+ const subscription = {};
5418
+ state2.remoteVideoSubscriptions.set(userID, subscription);
5419
+ const isCurrent = () => this.state === state2 && state2.remoteVideoSubscriptions.get(userID) === subscription;
3942
5420
  try {
3943
5421
  const track = await this.rtcManager.subscribeRemoteVideo(userID, true);
5422
+ if (!isCurrent()) return;
3944
5423
  if (!track) {
3945
5424
  throw new XmaxError(
3946
5425
  "MEDIA_ERROR" /* mediaError */,
3947
5426
  "Remote video track is not available"
3948
5427
  );
3949
5428
  }
3950
- if (!this.state.roomID) {
3951
- return;
3952
- }
3953
- this.state.subscribedRemoteUserIDs.add(userID);
5429
+ state2.subscribedRemoteUserIDs.add(userID);
5430
+ state2.remoteVideoTracks.set(userID, track);
3954
5431
  XmaxLogger.stream.info(
3955
5432
  () => `\u8FDC\u7AEF\u89C6\u9891\u8BA2\u9605\u6210\u529F (Remote Video Subscribed)
3956
5433
  \u2514\u2500 userID: ${userID}`
3957
5434
  );
3958
5435
  this.confirmOrUpdateRemoteStream(userID, track);
3959
5436
  } catch (error) {
5437
+ if (!isCurrent()) return;
3960
5438
  const mapped = XmaxError.from(error);
3961
5439
  const pendingTaskID = this.state.generationWaiter ? this.state.generationTaskID : void 0;
3962
5440
  if (pendingTaskID) {
@@ -3964,6 +5442,8 @@ var StreamController = class {
3964
5442
  } else {
3965
5443
  this.errorListener(mapped);
3966
5444
  }
5445
+ } finally {
5446
+ if (isCurrent()) state2.remoteVideoSubscriptions.delete(userID);
3967
5447
  }
3968
5448
  }
3969
5449
  /**
@@ -4095,7 +5575,7 @@ var StreamController = class {
4095
5575
  */
4096
5576
  resolveGenerationStart(taskID) {
4097
5577
  const waiter = this.state.generationWaiter;
4098
- if (this.state.generationTaskID !== taskID || _optionalChain([waiter, 'optionalAccess', _135 => _135.taskID]) !== taskID) {
5578
+ if (this.state.generationTaskID !== taskID || _optionalChain([waiter, 'optionalAccess', _297 => _297.taskID]) !== taskID) {
4099
5579
  return;
4100
5580
  }
4101
5581
  this.state.generationWaiter = void 0;
@@ -4107,7 +5587,7 @@ var StreamController = class {
4107
5587
  */
4108
5588
  rejectGenerationStart(taskID, error) {
4109
5589
  const waiter = this.state.generationWaiter;
4110
- if (this.state.generationTaskID !== taskID || _optionalChain([waiter, 'optionalAccess', _136 => _136.taskID]) !== taskID) {
5590
+ if (this.state.generationTaskID !== taskID || _optionalChain([waiter, 'optionalAccess', _298 => _298.taskID]) !== taskID) {
4111
5591
  return;
4112
5592
  }
4113
5593
  this.state.generationWaiter = void 0;
@@ -4118,7 +5598,7 @@ var StreamController = class {
4118
5598
  * 通知渲染层清理远端生成流;失败仅记录日志。
4119
5599
  */
4120
5600
  clearRemoteStream() {
4121
- _optionalChain([this, 'access', _137 => _137.remoteVideoStatisticsListener, 'optionalCall', _138 => _138(void 0)]);
5601
+ _optionalChain([this, 'access', _299 => _299.remoteVideoStatisticsListener, 'optionalCall', _300 => _300(void 0)]);
4122
5602
  try {
4123
5603
  this.remoteStreamListener(null);
4124
5604
  } catch (error) {
@@ -4389,7 +5869,7 @@ var RealtimeCoordinator = class _RealtimeCoordinator {
4389
5869
  }
4390
5870
  throw _RealtimeCoordinator.cancelledError();
4391
5871
  }
4392
- const terminationTask = await _asyncNullishCoalesce(await _asyncNullishCoalesce(operation.terminationTask, async () => ( _optionalChain([this, 'access', _139 => _139.termination, 'optionalAccess', _140 => _140.task]))), async () => ( await this.requestTermination(
5872
+ const terminationTask = await _asyncNullishCoalesce(await _asyncNullishCoalesce(operation.terminationTask, async () => ( _optionalChain([this, 'access', _301 => _301.termination, 'optionalAccess', _302 => _302.task]))), async () => ( await this.requestTermination(
4393
5873
  _nullishCoalesce(operation.lease.failureScope, () => ( 0)) /* connection */,
4394
5874
  void 0,
4395
5875
  RealtimeReason.normal,
@@ -4518,7 +5998,7 @@ var RealtimeCoordinator = class _RealtimeCoordinator {
4518
5998
  * 结束指定操作,释放操作准入场。
4519
5999
  */
4520
6000
  finish(operation) {
4521
- if (_optionalChain([this, 'access', _141 => _141.activeOperation, 'optionalAccess', _142 => _142.id]) === operation.id) {
6001
+ if (_optionalChain([this, 'access', _303 => _303.activeOperation, 'optionalAccess', _304 => _304.id]) === operation.id) {
4522
6002
  this.activeOperation = void 0;
4523
6003
  }
4524
6004
  }
@@ -4530,7 +6010,7 @@ var RealtimeCoordinator = class _RealtimeCoordinator {
4530
6010
  return;
4531
6011
  }
4532
6012
  this.state = nextState;
4533
- _optionalChain([this, 'access', _143 => _143.stateListener, 'optionalCall', _144 => _144(nextState)]);
6013
+ _optionalChain([this, 'access', _305 => _305.stateListener, 'optionalCall', _306 => _306(nextState)]);
4534
6014
  }
4535
6015
  /**
4536
6016
  * 构造统一的操作取消错误。
@@ -4662,9 +6142,9 @@ var RealtimeLaunchTimer = class {
4662
6142
  */
4663
6143
  notify() {
4664
6144
  try {
4665
- void Promise.resolve(_optionalChain([this, 'access', _145 => _145.listener, 'optionalCall', _146 => _146(this.snapshot)])).catch(() => {
6145
+ void Promise.resolve(_optionalChain([this, 'access', _307 => _307.listener, 'optionalCall', _308 => _308(this.snapshot)])).catch(() => {
4666
6146
  });
4667
- } catch (e17) {
6147
+ } catch (e19) {
4668
6148
  }
4669
6149
  }
4670
6150
  };
@@ -4680,6 +6160,10 @@ var XmaxRealtimeConnectionManager = class {
4680
6160
  * 会话资源
4681
6161
  */
4682
6162
  __publicField(this, "activeSession");
6163
+ __publicField(this, "refreshVersion", 0);
6164
+ __publicField(this, "credentialRefresh");
6165
+ __publicField(this, "credentialApplication", Promise.resolve());
6166
+ __publicField(this, "credentialController");
4683
6167
  /**
4684
6168
  * 远端轨道与渲染绑定
4685
6169
  */
@@ -4690,7 +6174,7 @@ var XmaxRealtimeConnectionManager = class {
4690
6174
  * 当前活动会话标识;尚未连接或清理完成后为空。
4691
6175
  */
4692
6176
  get currentSessionID() {
4693
- return _optionalChain([this, 'access', _147 => _147.activeSession, 'optionalAccess', _148 => _148.id]);
6177
+ return _optionalChain([this, 'access', _309 => _309.activeSession, 'optionalAccess', _310 => _310.id]);
4694
6178
  }
4695
6179
  /**
4696
6180
  * 未配置 API 服务时在连接操作开始前报错,不影响本地预览。
@@ -4719,6 +6203,11 @@ var XmaxRealtimeConnectionManager = class {
4719
6203
  "Session does not contain RTC join information"
4720
6204
  );
4721
6205
  }
6206
+ if (connection.provider !== (_nullishCoalesce(this.dependencies.provider, () => ( "trtc")) /* trtc */)) {
6207
+ throw new XmaxError("SESSION_ERROR" /* sessionError */, "RTC adapter configuration does not match the session service");
6208
+ }
6209
+ const refreshVersion = ++this.refreshVersion;
6210
+ this.credentialController = new AbortController();
4722
6211
  const videoFormat = options.localTrack.videoFormat;
4723
6212
  if (!videoFormat) {
4724
6213
  throw new XmaxError(
@@ -4733,24 +6222,24 @@ var XmaxRealtimeConnectionManager = class {
4733
6222
  options.ensureCurrent();
4734
6223
  }, async () => {
4735
6224
  options.ensureCurrent();
4736
- await _optionalChain([options, 'access', _149 => _149.beforePublish, 'optionalCall', _150 => _150()]);
6225
+ await _optionalChain([options, 'access', _311 => _311.beforePublish, 'optionalCall', _312 => _312()]);
4737
6226
  completeConnection();
4738
6227
  completePublish = this.dependencies.timing.startPublish();
4739
6228
  });
4740
6229
  options.ensureCurrent();
4741
- _optionalChain([completePublish, 'optionalCall', _151 => _151()]);
6230
+ _optionalChain([completePublish, 'optionalCall', _313 => _313()]);
4742
6231
  options.onPublished();
4743
6232
  sessionService.startHeartbeat(session.id, {
4744
6233
  onFailure: (sessionID, error) => {
4745
6234
  this.dependencies.onHeartbeatFailure(sessionID, error);
4746
6235
  },
4747
6236
  onRefresh: (refreshed) => {
4748
- this.handleSessionRefresh(refreshed);
6237
+ return this.handleSessionRefresh(refreshed, refreshVersion);
4749
6238
  }
4750
6239
  });
4751
6240
  const remoteTrack = new RealtimeVideoTrack({
4752
6241
  id: _nullishCoalesce(connection.botID, () => ( "video-remote")),
4753
- videoFormat: options.localTrack.videoFormat
6242
+ videoFormat: _nullishCoalesce(options.remoteVideoFormat, () => ( options.localTrack.videoFormat))
4754
6243
  });
4755
6244
  this.activeRemoteTrack = remoteTrack;
4756
6245
  this.registerRemoteBinding(remoteTrack);
@@ -4761,26 +6250,56 @@ var XmaxRealtimeConnectionManager = class {
4761
6250
  * 更新已绑定远端视图的插帧配置;传 undefined 停用插帧。
4762
6251
  */
4763
6252
  setFrameInterpolation(options) {
4764
- _optionalChain([this, 'access', _152 => _152.remoteTarget, 'optionalAccess', _153 => _153.setFrameInterpolation, 'optionalCall', _154 => _154(options)]);
6253
+ _optionalChain([this, 'access', _314 => _314.remoteTarget, 'optionalAccess', _315 => _315.setFrameInterpolation, 'optionalCall', _316 => _316(options)]);
4765
6254
  }
4766
6255
  /**
4767
6256
  * 清空远端视图的媒体流,保留轨道与视图绑定以供统一清理。
4768
6257
  */
4769
6258
  clearRemoteMedia() {
4770
- _optionalChain([this, 'access', _155 => _155.remoteTarget, 'optionalAccess', _156 => _156.setMediaStream, 'call', _157 => _157(null)]);
6259
+ _optionalChain([this, 'access', _317 => _317.remoteTarget, 'optionalAccess', _318 => _318.setMediaStream, 'call', _319 => _319(null)]);
4771
6260
  }
4772
6261
  /**
4773
6262
  * 停止会话心跳;未配置会话服务时不执行操作。
4774
6263
  */
4775
6264
  stopHeartbeat() {
4776
- _optionalChain([this, 'access', _158 => _158.dependencies, 'access', _159 => _159.sessionService, 'optionalAccess', _160 => _160.stopHeartbeat, 'call', _161 => _161()]);
6265
+ this.refreshVersion++;
6266
+ _optionalChain([this, 'access', _320 => _320.credentialController, 'optionalAccess', _321 => _321.abort, 'call', _322 => _322()]);
6267
+ this.credentialController = void 0;
6268
+ this.credentialRefresh = void 0;
6269
+ this.credentialApplication = Promise.resolve();
6270
+ _optionalChain([this, 'access', _323 => _323.dependencies, 'access', _324 => _324.sessionService, 'optionalAccess', _325 => _325.stopHeartbeat, 'call', _326 => _326()]);
6271
+ }
6272
+ /**
6273
+ * RTC Token 过期时复用会话心跳获取新凭据,并合并同一实例的并发请求。
6274
+ */
6275
+ async refreshCredentials() {
6276
+ if (this.credentialRefresh) return this.credentialRefresh;
6277
+ const current = this.activeSession;
6278
+ if (!_optionalChain([current, 'optionalAccess', _327 => _327.connection]) || current.connection.provider === "trtc") return;
6279
+ const version = this.refreshVersion;
6280
+ const operation = (async () => {
6281
+ try {
6282
+ const session = await this.requireSessionService().heartbeatSession(current.id);
6283
+ await this.handleSessionRefresh(session, version, true);
6284
+ } catch (error) {
6285
+ if (version === this.refreshVersion && _optionalChain([this, 'access', _328 => _328.activeSession, 'optionalAccess', _329 => _329.id]) === current.id) {
6286
+ this.dependencies.onHeartbeatFailure(current.id, XmaxError.from(error));
6287
+ }
6288
+ }
6289
+ })();
6290
+ this.credentialRefresh = operation;
6291
+ try {
6292
+ await operation;
6293
+ } finally {
6294
+ if (this.credentialRefresh === operation) this.credentialRefresh = void 0;
6295
+ }
4777
6296
  }
4778
6297
  /**
4779
6298
  * 等待接收轨解除静音;超时只警告,取消必须立即退出并移除监听。
4780
6299
  */
4781
6300
  async waitUntilRemoteTrackReady(timeoutMs, signal) {
4782
6301
  if (signal.aborted) throw RealtimeCoordinator.cancelledError();
4783
- const mediaTrack = _optionalChain([this, 'access', _162 => _162.activeRemoteTrack, 'optionalAccess', _163 => _163.mediaStreamTrack]);
6302
+ const mediaTrack = _optionalChain([this, 'access', _330 => _330.activeRemoteTrack, 'optionalAccess', _331 => _331.mediaStreamTrack]);
4784
6303
  if (!mediaTrack || !mediaTrack.muted) return;
4785
6304
  await new Promise((resolve, reject) => {
4786
6305
  const cleanup = () => {
@@ -4817,7 +6336,7 @@ var XmaxRealtimeConnectionManager = class {
4817
6336
  if (track) {
4818
6337
  track.mediaStreamTrack = void 0;
4819
6338
  }
4820
- _optionalChain([this, 'access', _164 => _164.remoteTarget, 'optionalAccess', _165 => _165.setMediaStream, 'call', _166 => _166(null)]);
6339
+ _optionalChain([this, 'access', _332 => _332.remoteTarget, 'optionalAccess', _333 => _333.setMediaStream, 'call', _334 => _334(null)]);
4821
6340
  return;
4822
6341
  }
4823
6342
  if (!track) {
@@ -4827,7 +6346,7 @@ var XmaxRealtimeConnectionManager = class {
4827
6346
  );
4828
6347
  }
4829
6348
  track.mediaStreamTrack = binding.videoTrack;
4830
- _optionalChain([this, 'access', _167 => _167.remoteTarget, 'optionalAccess', _168 => _168.setMediaStream, 'call', _169 => _169(new MediaStream([binding.videoTrack]))]);
6349
+ _optionalChain([this, 'access', _335 => _335.remoteTarget, 'optionalAccess', _336 => _336.setMediaStream, 'call', _337 => _337(new MediaStream([binding.videoTrack]))]);
4831
6350
  }
4832
6351
  /**
4833
6352
  * 为远端轨道注册渲染绑定:attach 时挂流占位,媒体轨到达后送入画面。
@@ -4840,7 +6359,7 @@ var XmaxRealtimeConnectionManager = class {
4840
6359
  }
4841
6360
  },
4842
6361
  attachHandler: (view) => {
4843
- _optionalChain([this, 'access', _170 => _170.remoteTarget, 'optionalAccess', _171 => _171.setFrameInterpolation, 'optionalCall', _172 => _172(void 0)]);
6362
+ _optionalChain([this, 'access', _338 => _338.remoteTarget, 'optionalAccess', _339 => _339.setFrameInterpolation, 'optionalCall', _340 => _340(void 0)]);
4844
6363
  view.isMirrored = this.dependencies.isMirrored();
4845
6364
  this.remoteTarget = view;
4846
6365
  this.dependencies.onRenderAttached();
@@ -4852,7 +6371,7 @@ var XmaxRealtimeConnectionManager = class {
4852
6371
  this.dependencies.onRenderDetached();
4853
6372
  this.remoteTarget = void 0;
4854
6373
  }
4855
- _optionalChain([view, 'access', _173 => _173.setFrameInterpolation, 'optionalCall', _174 => _174(void 0)]);
6374
+ _optionalChain([view, 'access', _341 => _341.setFrameInterpolation, 'optionalCall', _342 => _342(void 0)]);
4856
6375
  view.setMediaStream(null);
4857
6376
  }
4858
6377
  });
@@ -4882,9 +6401,18 @@ var XmaxRealtimeConnectionManager = class {
4882
6401
  * 心跳成功后的凭据刷新:仅凭据变化时覆盖本地缓存;房间绑定信息
4883
6402
  * (提供方、房间、应用、登录身份)变化时当前连接失效,按失败结束。
4884
6403
  */
4885
- handleSessionRefresh(session) {
6404
+ handleSessionRefresh(session, version, force = false) {
6405
+ const operation = this.credentialApplication.then(() => this.applySessionRefresh(session, version, force));
6406
+ this.credentialApplication = operation.catch(() => {
6407
+ });
6408
+ return operation;
6409
+ }
6410
+ /**
6411
+ * 校验绑定并应用最新凭据,异步刷新期间失效的房间不得恢复缓存。
6412
+ */
6413
+ async applySessionRefresh(session, version, force) {
4886
6414
  const current = this.activeSession;
4887
- if (!current || current.id !== session.id) {
6415
+ if (version !== this.refreshVersion || !current || current.id !== session.id) {
4888
6416
  return;
4889
6417
  }
4890
6418
  const next = session.connection;
@@ -4892,7 +6420,7 @@ var XmaxRealtimeConnectionManager = class {
4892
6420
  return;
4893
6421
  }
4894
6422
  const previous = current.connection;
4895
- const bindingChanged = previous !== void 0 && (next.provider !== previous.provider || next.roomID !== previous.roomID || next.sdkAppID !== previous.sdkAppID || next.userID !== previous.userID);
6423
+ const bindingChanged = previous !== void 0 && (next.provider !== previous.provider || next.roomID !== previous.roomID || connectionAppID(next) !== connectionAppID(previous) || next.botID !== previous.botID || next.userID !== previous.userID);
4896
6424
  if (bindingChanged) {
4897
6425
  this.dependencies.onHeartbeatFailure(
4898
6426
  session.id,
@@ -4900,6 +6428,10 @@ var XmaxRealtimeConnectionManager = class {
4900
6428
  );
4901
6429
  return;
4902
6430
  }
6431
+ if (next.provider !== "trtc" && (force || !previous || previous.provider === "trtc" || next.roomToken !== previous.roomToken)) {
6432
+ await this.dependencies.streamController.updateCredentials(next, _optionalChain([this, 'access', _343 => _343.credentialController, 'optionalAccess', _344 => _344.signal]));
6433
+ if (version !== this.refreshVersion || this.activeSession !== current) return;
6434
+ }
4903
6435
  this.activeSession = new RealtimeSession({
4904
6436
  id: session.id,
4905
6437
  userID: _nullishCoalesce(session.userID, () => ( current.userID)),
@@ -4912,7 +6444,7 @@ var XmaxRealtimeConnectionManager = class {
4912
6444
  * 尽力清理全部连接资源,即使退房失败也关闭服务端会话。
4913
6445
  */
4914
6446
  async disconnect() {
4915
- const sessionID = _optionalChain([this, 'access', _175 => _175.activeSession, 'optionalAccess', _176 => _176.id]);
6447
+ const sessionID = _optionalChain([this, 'access', _345 => _345.activeSession, 'optionalAccess', _346 => _346.id]);
4916
6448
  this.stopHeartbeat();
4917
6449
  this.clearRemoteMedia();
4918
6450
  try {
@@ -5012,16 +6544,17 @@ var XmaxRealtimeGenerationManager = class _XmaxRealtimeGenerationManager {
5012
6544
  /**
5013
6545
  * 更新活动任务的生成条件;信令发送成功后才覆盖缓存,无任务或上下文时抛错。
5014
6546
  */
5015
- update(taskID, options) {
6547
+ async update(taskID, options) {
5016
6548
  const context = _nullishCoalesce(options.context, () => ( this.currentContext));
5017
6549
  if (!taskID || !context) {
5018
6550
  throw new XmaxError("INVALID_CONFIGURATION" /* invalidConfiguration */, "A realtime context is required to update the current generation");
5019
6551
  }
5020
- this.streamController.updateGeneration({
6552
+ await this.streamController.updateGeneration({
5021
6553
  taskID,
5022
6554
  videoFormat: options.videoFormat,
5023
6555
  context
5024
6556
  });
6557
+ if (this.activeTaskID !== taskID) throw RealtimeCoordinator.cancelledError();
5025
6558
  this.currentContext = context;
5026
6559
  }
5027
6560
  /**
@@ -5045,7 +6578,7 @@ var XmaxRealtimeGenerationManager = class _XmaxRealtimeGenerationManager {
5045
6578
  try {
5046
6579
  await completion;
5047
6580
  } finally {
5048
- if (_optionalChain([this, 'access', _177 => _177.pendingStop, 'optionalAccess', _178 => _178.completion]) === completion) this.pendingStop = void 0;
6581
+ if (_optionalChain([this, 'access', _347 => _347.pendingStop, 'optionalAccess', _348 => _348.completion]) === completion) this.pendingStop = void 0;
5049
6582
  }
5050
6583
  }
5051
6584
  /**
@@ -5109,6 +6642,10 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
5109
6642
  */
5110
6643
  __publicField(this, "options");
5111
6644
  __publicField(this, "mediaService");
6645
+ /**
6646
+ * 本地流创建时的生成格式;上行编码调整不改变模型输入配置与远端插帧尺寸。
6647
+ */
6648
+ __publicField(this, "generationVideoFormat");
5112
6649
  /**
5113
6650
  * 插帧能力、用户配置与渲染版本
5114
6651
  */
@@ -5155,19 +6692,22 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
5155
6692
  this.options = options;
5156
6693
  this.mediaService = new MediaService(options.model);
5157
6694
  this.interpolationRequested = options.isFrameInterpolationEnabled;
5158
- this.supportsInterpolation = _nullishCoalesce(_optionalChain([dependencies, 'optionalAccess', _179 => _179.supportsFrameInterpolation]), () => ( (async (size) => await frameInterpolationAdapter(size) !== null)));
6695
+ this.supportsInterpolation = _nullishCoalesce(_optionalChain([dependencies, 'optionalAccess', _349 => _349.supportsFrameInterpolation]), () => ( (async (size) => await frameInterpolationAdapter(size) !== null)));
5159
6696
  this.errorHandler = new RealtimeErrorHandler();
5160
- const rtcManager = _nullishCoalesce(_optionalChain([dependencies, 'optionalAccess', _180 => _180.rtcManager]), () => ( new RtcManager()));
5161
- this.cameraController = _nullishCoalesce(_optionalChain([dependencies, 'optionalAccess', _181 => _181.cameraController]), () => ( new CameraController({
6697
+ const rtcManager = _nullishCoalesce(_optionalChain([dependencies, 'optionalAccess', _350 => _350.rtcManager]), () => ( createRtcManager(options.provider, _nullishCoalesce(_optionalChain([dependencies, 'optionalAccess', _351 => _351.environment]), () => ( "china")) /* china */)));
6698
+ this.cameraController = _nullishCoalesce(_optionalChain([dependencies, 'optionalAccess', _352 => _352.cameraController]), () => ( new CameraController({
5162
6699
  rtcManager,
5163
6700
  mediaService: this.mediaService,
5164
6701
  errorListener: (error) => {
5165
6702
  void this.errorHandler.report(error);
5166
6703
  }
5167
6704
  })));
5168
- const sessionService = _nullishCoalesce(_optionalChain([dependencies, 'optionalAccess', _182 => _182.sessionService]), () => ( (_optionalChain([dependencies, 'optionalAccess', _183 => _183.apiService]) ? new RealtimeSessionService({ apiService: dependencies.apiService }) : void 0)));
5169
- this.streamController = _nullishCoalesce(_optionalChain([dependencies, 'optionalAccess', _184 => _184.streamController]), () => ( new StreamController({
6705
+ const sessionService = _nullishCoalesce(_optionalChain([dependencies, 'optionalAccess', _353 => _353.sessionService]), () => ( (_optionalChain([dependencies, 'optionalAccess', _354 => _354.apiService]) ? new RealtimeSessionService({ apiService: dependencies.apiService, provider: options.provider }) : void 0)));
6706
+ this.streamController = _nullishCoalesce(_optionalChain([dependencies, 'optionalAccess', _355 => _355.streamController]), () => ( new StreamController({
5170
6707
  rtcManager,
6708
+ onCredentialsRequired: () => {
6709
+ void this.connectionManager.refreshCredentials();
6710
+ },
5171
6711
  errorListener: (error) => {
5172
6712
  void this.errorHandler.report(error);
5173
6713
  },
@@ -5177,15 +6717,16 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
5177
6717
  }
5178
6718
  })));
5179
6719
  this.connectionManager = new XmaxRealtimeConnectionManager({
6720
+ provider: options.provider,
5180
6721
  sessionService,
5181
6722
  streamController: this.streamController,
5182
6723
  timing: this.launchTimer,
5183
- isMirrored: () => _optionalChain([this, 'access', _185 => _185.cameraController, 'access', _186 => _186.currentTrack, 'optionalAccess', _187 => _187.position]) === "front" /* front */,
6724
+ isMirrored: () => _optionalChain([this, 'access', _356 => _356.cameraController, 'access', _357 => _357.currentTrack, 'optionalAccess', _358 => _358.position]) === "front" /* front */,
5184
6725
  remoteAudioVolume: () => this.storedRemoteAudioVolume,
5185
6726
  onHeartbeatFailure: (sessionID, error) => {
5186
6727
  void this.handleHeartbeatFailure(sessionID, error);
5187
6728
  },
5188
- onFrameDisplayed: () => _optionalChain([this, 'access', _188 => _188.remoteFrameDisplayHandler, 'optionalCall', _189 => _189()]),
6729
+ onFrameDisplayed: () => _optionalChain([this, 'access', _359 => _359.remoteFrameDisplayHandler, 'optionalCall', _360 => _360()]),
5189
6730
  onRenderAttached: () => this.applyRemoteInterpolation(),
5190
6731
  onRenderDetached: () => {
5191
6732
  this.interpolationRevision++;
@@ -5232,7 +6773,7 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
5232
6773
  */
5233
6774
  async setFrameInterpolationEnabled(enabled) {
5234
6775
  await this.coordinator.run("configuration" /* configuration */, void 0, async (token) => {
5235
- const format = _optionalChain([this, 'access', _190 => _190.cameraController, 'access', _191 => _191.currentTrack, 'optionalAccess', _192 => _192.videoFormat]);
6776
+ const format = _nullishCoalesce(this.generationVideoFormat, () => ( _optionalChain([this, 'access', _361 => _361.cameraController, 'access', _362 => _362.currentTrack, 'optionalAccess', _363 => _363.videoFormat])));
5236
6777
  const size = format && enabled ? this.mediaService.resolveFrameInterpolationSize(format) : void 0;
5237
6778
  if (enabled && !await this.checkInterpolationSupport(size, token)) {
5238
6779
  token.ensureCurrent();
@@ -5358,9 +6899,9 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
5358
6899
  */
5359
6900
  notifyRemoteVideoStatistics() {
5360
6901
  try {
5361
- void Promise.resolve(_optionalChain([this, 'access', _193 => _193.remoteVideoStatisticsListener, 'optionalCall', _194 => _194(this.remoteVideoStatistics)])).catch(() => {
6902
+ void Promise.resolve(_optionalChain([this, 'access', _364 => _364.remoteVideoStatisticsListener, 'optionalCall', _365 => _365(this.remoteVideoStatistics)])).catch(() => {
5362
6903
  });
5363
- } catch (e18) {
6904
+ } catch (e20) {
5364
6905
  }
5365
6906
  }
5366
6907
  /**
@@ -5375,9 +6916,9 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
5375
6916
  */
5376
6917
  notifyNetworkStatistics() {
5377
6918
  try {
5378
- void Promise.resolve(_optionalChain([this, 'access', _195 => _195.networkStatisticsListener, 'optionalCall', _196 => _196(this.networkStatistics)])).catch(() => {
6919
+ void Promise.resolve(_optionalChain([this, 'access', _366 => _366.networkStatisticsListener, 'optionalCall', _367 => _367(this.networkStatistics)])).catch(() => {
5379
6920
  });
5380
- } catch (e19) {
6921
+ } catch (e21) {
5381
6922
  }
5382
6923
  }
5383
6924
  /**
@@ -5395,9 +6936,9 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
5395
6936
  */
5396
6937
  notifyLocalVideoStatistics() {
5397
6938
  try {
5398
- void Promise.resolve(_optionalChain([this, 'access', _197 => _197.localVideoStatisticsListener, 'optionalCall', _198 => _198(this.localVideoStatistics)])).catch(() => {
6939
+ void Promise.resolve(_optionalChain([this, 'access', _368 => _368.localVideoStatisticsListener, 'optionalCall', _369 => _369(this.localVideoStatistics)])).catch(() => {
5399
6940
  });
5400
- } catch (e20) {
6941
+ } catch (e22) {
5401
6942
  }
5402
6943
  }
5403
6944
  /**
@@ -5468,7 +7009,7 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
5468
7009
  const completeCamera = this.launchTimer.startCamera();
5469
7010
  this.clearVideoStatistics();
5470
7011
  this.disposePermissionWatch();
5471
- const permissionWatch = await watchMediaPermissionPrompt(_nullishCoalesce(_optionalChain([options, 'optionalAccess', _199 => _199.useMicrophone]), () => ( false)));
7012
+ const permissionWatch = await watchMediaPermissionPrompt(_nullishCoalesce(_optionalChain([options, 'optionalAccess', _370 => _370.useMicrophone]), () => ( false)));
5472
7013
  if (permissionWatch) {
5473
7014
  const completePermission = this.launchTimer.startPermission();
5474
7015
  void permissionWatch.decided.then(() => completePermission()).catch(() => {
@@ -5485,6 +7026,7 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
5485
7026
  throw error;
5486
7027
  }
5487
7028
  token.ensureCurrent();
7029
+ this.generationVideoFormat = _optionalChain([stream, 'access', _371 => _371.videoTrack, 'optionalAccess', _372 => _372.videoFormat]);
5488
7030
  completeCamera();
5489
7031
  this.cameraController.setPreviewReadyHandler((isCurrent) => {
5490
7032
  void this.coordinator.localPreviewDidBecomeReady(isCurrent);
@@ -5497,9 +7039,29 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
5497
7039
  * 移除媒体权限弹窗观察;授权决定已记录或不再需要观察时调用。
5498
7040
  */
5499
7041
  disposePermissionWatch() {
5500
- _optionalChain([this, 'access', _200 => _200.permissionWatchDispose, 'optionalCall', _201 => _201()]);
7042
+ _optionalChain([this, 'access', _373 => _373.permissionWatchDispose, 'optionalCall', _374 => _374()]);
5501
7043
  this.permissionWatchDispose = void 0;
5502
7044
  }
7045
+ /**
7046
+ * 应用完整的上行视频格式,成功后更新本地轨道元数据,不改变生成格式或生命周期。
7047
+ * 复用配置操作准入,避免与建连、切换摄像头或其他配置更新并发。
7048
+ *
7049
+ * @throws 无本地相机流、参数无效、操作冲突或底层编码更新失败时抛错。
7050
+ */
7051
+ async updateVideoFormat(videoFormat) {
7052
+ await this.coordinator.run("configuration" /* configuration */, void 0, async (token) => {
7053
+ const track = this.cameraController.currentTrack;
7054
+ const previousFormat = _optionalChain([track, 'optionalAccess', _375 => _375.videoFormat]);
7055
+ if (!track || !previousFormat) {
7056
+ throw new XmaxError("INVALID_CONFIGURATION" /* invalidConfiguration */, "Local camera stream is not started");
7057
+ }
7058
+ videoFormat.validate();
7059
+ await this.streamController.setVideoEncoderConfig(videoFormat);
7060
+ token.ensureCurrent();
7061
+ _nullishCoalesce(this.generationVideoFormat, () => ( (this.generationVideoFormat = previousFormat)));
7062
+ track.updateVideoFormat(videoFormat);
7063
+ });
7064
+ }
5503
7065
  /**
5504
7066
  * 停止本地相机流并释放本地预览与 RTC 资源。
5505
7067
  */
@@ -5513,6 +7075,7 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
5513
7075
  this.disposePermissionWatch();
5514
7076
  await this.cameraController.stopLocalCameraStream();
5515
7077
  token.ensureCurrent();
7078
+ this.generationVideoFormat = void 0;
5516
7079
  await this.coordinator.commit(
5517
7080
  new RealtimeState({ connectionState: "Idle" /* idle */ }),
5518
7081
  token
@@ -5592,7 +7155,7 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
5592
7155
  "The local stream must be created and started by this realtime manager"
5593
7156
  );
5594
7157
  }
5595
- const videoFormat = localTrack.videoFormat;
7158
+ const videoFormat = _nullishCoalesce(this.generationVideoFormat, () => ( localTrack.videoFormat));
5596
7159
  if (!videoFormat) {
5597
7160
  throw new XmaxError(
5598
7161
  "INVALID_CONFIGURATION" /* invalidConfiguration */,
@@ -5601,10 +7164,11 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
5601
7164
  }
5602
7165
  const currentState = this.coordinator.currentState;
5603
7166
  if (currentState.connectionState === "Generating" /* generating */) {
5604
- this.generationManager.update(currentState.taskID, {
7167
+ await this.generationManager.update(currentState.taskID, {
5605
7168
  videoFormat,
5606
7169
  context: options.context
5607
7170
  });
7171
+ token.ensureCurrent();
5608
7172
  return this.connectionManager.makeRemoteStream();
5609
7173
  }
5610
7174
  const context = this.generationManager.validateContext(options.context);
@@ -5669,6 +7233,7 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
5669
7233
  }
5670
7234
  const remote = await this.connectionManager.connect({
5671
7235
  localTrack,
7236
+ remoteVideoFormat: this.generationVideoFormat,
5672
7237
  model: this.options.model,
5673
7238
  includeLocalAudio: this.cameraController.useMicrophone,
5674
7239
  ensureCurrent: () => token.ensureCurrent(),
@@ -5744,6 +7309,7 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
5744
7309
  await this.generationManager.reset(taskID);
5745
7310
  const sessionID = await this.connectionManager.disconnect();
5746
7311
  if (scope === 1 /* all */) {
7312
+ this.generationVideoFormat = void 0;
5747
7313
  this.disposePermissionWatch();
5748
7314
  try {
5749
7315
  await this.cameraController.stopLocalCameraStream();
@@ -5850,12 +7416,18 @@ var XmaxClient = class {
5850
7416
  }
5851
7417
  /**
5852
7418
  * 创建实时媒体 Manager。
7419
+ * 模型专用地址优先于环境默认地址,并用于该 Manager 的整个会话生命周期。
5853
7420
  *
5854
7421
  * @param options 实时生成模型等业务配置。
5855
7422
  * @returns 可用于本地相机预览、实时连接与生成的实时 Manager。
5856
7423
  */
5857
7424
  createRealtimeManager(options) {
5858
- return new XmaxRealtimeManager(options, { apiService: this.apiService });
7425
+ const baseURL = modelBaseURL(options.model);
7426
+ const apiService = baseURL ? new ApiService({ apiKey: this.configuration.apiKey, baseURL }) : this.apiService;
7427
+ return new XmaxRealtimeManager(options, {
7428
+ apiService,
7429
+ environment: this.configuration.environment
7430
+ });
5859
7431
  }
5860
7432
  /**
5861
7433
  * 创建媒体处理与能力查询 Service。
@@ -5881,10 +7453,15 @@ var RealtimeConfiguration = class {
5881
7453
  * 创建实时业务配置。
5882
7454
  *
5883
7455
  * @param init.model 实时生成业务使用的模型。
7456
+ * @param init.provider RTC 提供方,默认 TRTC;创建后不可切换。
5884
7457
  * @param init.isFrameInterpolationEnabled 是否默认开启远端生成画面的插帧。
5885
7458
  * @param init.frameInterpolation 插帧开关和显示帧率上限的详细配置。
5886
7459
  */
5887
7460
  constructor(init) {
7461
+ /**
7462
+ * RTC 配置
7463
+ */
7464
+ __publicField(this, "provider");
5888
7465
  /**
5889
7466
  * 模型配置
5890
7467
  */
@@ -5903,12 +7480,16 @@ var RealtimeConfiguration = class {
5903
7480
  * 已补全默认值的只读插帧配置。
5904
7481
  */
5905
7482
  __publicField(this, "frameInterpolation");
7483
+ this.provider = _nullishCoalesce(init.provider, () => ( "trtc")) /* trtc */;
7484
+ if (!Object.values(RtcProvider).includes(this.provider)) {
7485
+ throw new XmaxError("INVALID_CONFIGURATION" /* invalidConfiguration */, "Unsupported RTC provider");
7486
+ }
5906
7487
  this.model = init.model;
5907
- const targetFrameRate = _nullishCoalesce(_optionalChain([init, 'access', _202 => _202.frameInterpolation, 'optionalAccess', _203 => _203.targetFrameRate]), () => ( 60));
7488
+ const targetFrameRate = _nullishCoalesce(_optionalChain([init, 'access', _376 => _376.frameInterpolation, 'optionalAccess', _377 => _377.targetFrameRate]), () => ( 60));
5908
7489
  if (!Number.isFinite(targetFrameRate) || targetFrameRate < 1 || targetFrameRate > 60) {
5909
7490
  throw new XmaxError("INVALID_CONFIGURATION" /* invalidConfiguration */, "Interpolation targetFrameRate must be between 1 and 60");
5910
7491
  }
5911
- this.isFrameInterpolationEnabled = _nullishCoalesce(_nullishCoalesce(_optionalChain([init, 'access', _204 => _204.frameInterpolation, 'optionalAccess', _205 => _205.enabled]), () => ( init.isFrameInterpolationEnabled)), () => ( false));
7492
+ this.isFrameInterpolationEnabled = _nullishCoalesce(_nullishCoalesce(_optionalChain([init, 'access', _378 => _378.frameInterpolation, 'optionalAccess', _379 => _379.enabled]), () => ( init.isFrameInterpolationEnabled)), () => ( false));
5912
7493
  this.frameInterpolation = Object.freeze({ enabled: this.isFrameInterpolationEnabled, targetFrameRate });
5913
7494
  }
5914
7495
  };
@@ -5938,7 +7519,7 @@ var RealtimeContext = class {
5938
7519
  */
5939
7520
  __publicField(this, "referencePath");
5940
7521
  this.prompt = init.prompt.trim();
5941
- const normalizedReferencePath = _optionalChain([init, 'access', _206 => _206.referencePath, 'optionalAccess', _207 => _207.trim, 'call', _208 => _208()]);
7522
+ const normalizedReferencePath = _optionalChain([init, 'access', _380 => _380.referencePath, 'optionalAccess', _381 => _381.trim, 'call', _382 => _382()]);
5942
7523
  this.referencePath = normalizedReferencePath || void 0;
5943
7524
  }
5944
7525
  };
@@ -8065,11 +9646,11 @@ var _FrameInterpolationManager = class _FrameInterpolationManager {
8065
9646
  if (this.destroyed) return;
8066
9647
  this.destroyed = true;
8067
9648
  this.device.removeEventListener("uncapturederror", this.onGPUError);
8068
- _optionalChain([this, 'access', _209 => _209.runtime, 'optionalAccess', _210 => _210.destroy, 'call', _211 => _211()]);
9649
+ _optionalChain([this, 'access', _383 => _383.runtime, 'optionalAccess', _384 => _384.destroy, 'call', _385 => _385()]);
8069
9650
  this.runtime = void 0;
8070
9651
  this.textures.forEach((texture) => texture.destroy());
8071
9652
  this.textures = [];
8072
- _optionalChain([this, 'access', _212 => _212.context, 'optionalAccess', _213 => _213.unconfigure, 'call', _214 => _214()]);
9653
+ _optionalChain([this, 'access', _386 => _386.context, 'optionalAccess', _387 => _387.unconfigure, 'call', _388 => _388()]);
8073
9654
  this.device.destroy();
8074
9655
  }
8075
9656
  };
@@ -8248,7 +9829,7 @@ var RemoteVideoFramePipeline = class {
8248
9829
  setActive(active) {
8249
9830
  if (active === this.active) return;
8250
9831
  this.active = active;
8251
- _optionalChain([this, 'access', _215 => _215.options, 'access', _216 => _216.onActiveChange, 'optionalCall', _217 => _217(active)]);
9832
+ _optionalChain([this, 'access', _389 => _389.options, 'access', _390 => _390.onActiveChange, 'optionalCall', _391 => _391(active)]);
8252
9833
  }
8253
9834
  resetTimeline() {
8254
9835
  this.generation++;
@@ -8279,7 +9860,7 @@ var RemoteVideoFramePipeline = class {
8279
9860
  this.callback = void 0;
8280
9861
  clearTimeout(this.initializationTimer);
8281
9862
  this.resetTimeline();
8282
- _optionalChain([this, 'access', _218 => _218.processor, 'optionalAccess', _219 => _219.destroy, 'call', _220 => _220()]);
9863
+ _optionalChain([this, 'access', _392 => _392.processor, 'optionalAccess', _393 => _393.destroy, 'call', _394 => _394()]);
8283
9864
  this.processor = void 0;
8284
9865
  }
8285
9866
  };
@@ -8337,7 +9918,7 @@ var XmaxVideoView = class {
8337
9918
  * DOM 挂载状态
8338
9919
  */
8339
9920
  __publicField(this, "detached", false);
8340
- this.contentMode = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _221 => _221.videoContentMode]), () => ( "fill")) /* fill */;
9921
+ this.contentMode = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _395 => _395.videoContentMode]), () => ( "fill")) /* fill */;
8341
9922
  this.element = document.createElement("div");
8342
9923
  this.element.style.position = "relative";
8343
9924
  this.element.style.overflow = "hidden";
@@ -8484,8 +10065,8 @@ var XmaxVideoView = class {
8484
10065
  stopInterpolation() {
8485
10066
  const pipeline = this.interpolationPipeline;
8486
10067
  this.interpolationPipeline = void 0;
8487
- _optionalChain([pipeline, 'optionalAccess', _222 => _222.stop, 'call', _223 => _223()]);
8488
- _optionalChain([this, 'access', _224 => _224.interpolationCanvas, 'optionalAccess', _225 => _225.remove, 'call', _226 => _226()]);
10068
+ _optionalChain([pipeline, 'optionalAccess', _396 => _396.stop, 'call', _397 => _397()]);
10069
+ _optionalChain([this, 'access', _398 => _398.interpolationCanvas, 'optionalAccess', _399 => _399.remove, 'call', _400 => _400()]);
8489
10070
  this.interpolationCanvas = void 0;
8490
10071
  }
8491
10072
  /**
@@ -8511,7 +10092,7 @@ var XmaxVideoView = class {
8511
10092
  detachCurrentTrack() {
8512
10093
  const track = this.currentTrack;
8513
10094
  if (track) {
8514
- _optionalChain([VideoRenderRegistry, 'access', _227 => _227.binding, 'call', _228 => _228(track), 'optionalAccess', _229 => _229.detachHandler, 'call', _230 => _230(this)]);
10095
+ _optionalChain([VideoRenderRegistry, 'access', _401 => _401.binding, 'call', _402 => _402(track), 'optionalAccess', _403 => _403.detachHandler, 'call', _404 => _404(this)]);
8515
10096
  }
8516
10097
  this.setFrameInterpolation(void 0);
8517
10098
  this.cancelFrameDisplayNotification();
@@ -8531,8 +10112,8 @@ var XmaxVideoView = class {
8531
10112
  }
8532
10113
  this.hasNotifiedFrameDisplay = true;
8533
10114
  this.cancelFrameDisplayNotification();
8534
- _optionalChain([binding, 'optionalAccess', _231 => _231.frameDisplayHandler, 'optionalCall', _232 => _232()]);
8535
- _optionalChain([this, 'access', _233 => _233.frameDisplayHandler, 'optionalCall', _234 => _234()]);
10115
+ _optionalChain([binding, 'optionalAccess', _405 => _405.frameDisplayHandler, 'optionalCall', _406 => _406()]);
10116
+ _optionalChain([this, 'access', _407 => _407.frameDisplayHandler, 'optionalCall', _408 => _408()]);
8536
10117
  };
8537
10118
  if (typeof this.videoElement.requestVideoFrameCallback === "function") {
8538
10119
  this.videoFrameCallbackID = this.videoElement.requestVideoFrameCallback(notify);
@@ -8602,9 +10183,9 @@ var XmaxRealtimeVideoView = class {
8602
10183
  __publicField(this, "currentLocalTrack");
8603
10184
  __publicField(this, "currentRemoteTrack");
8604
10185
  __publicField(this, "contentMode");
8605
- this.contentMode = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _235 => _235.videoContentMode]), () => ( "fill")) /* fill */;
8606
- this.currentLocalTrack = _optionalChain([options, 'optionalAccess', _236 => _236.localTrack]);
8607
- this.currentRemoteTrack = _optionalChain([options, 'optionalAccess', _237 => _237.remoteTrack]);
10186
+ this.contentMode = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _409 => _409.videoContentMode]), () => ( "fill")) /* fill */;
10187
+ this.currentLocalTrack = _optionalChain([options, 'optionalAccess', _410 => _410.localTrack]);
10188
+ this.currentRemoteTrack = _optionalChain([options, 'optionalAccess', _411 => _411.remoteTrack]);
8608
10189
  this.element = document.createElement("div");
8609
10190
  this.element.style.position = "relative";
8610
10191
  this.element.style.overflow = "hidden";
@@ -8762,5 +10343,6 @@ var XmaxRealtimeVideoView = class {
8762
10343
 
8763
10344
 
8764
10345
 
8765
- exports.XmaxEnvironment = XmaxEnvironment; exports.apiBaseURL = apiBaseURL; exports.XmaxLoggerOption = XmaxLoggerOption; exports.XmaxLogger = XmaxLogger; exports.XmaxErrorCode = XmaxErrorCode; exports.XmaxError = XmaxError; exports.RealtimeVideoEncoderPreference = RealtimeVideoEncoderPreference; exports.RealtimeVideoFormat = RealtimeVideoFormat; exports.RealtimeModel = RealtimeModel; exports.defaultFrameRate = defaultFrameRate; exports.defaultCameraVideoFormat = defaultCameraVideoFormat; exports.MediaService = MediaService; exports.StoredFile = StoredFile; exports.StorageService = StorageService; exports.CameraPosition = CameraPosition; exports.RealtimeVideoTrack = RealtimeVideoTrack; exports.RealtimeMediaStream = RealtimeMediaStream; exports.RealtimeConnectionState = RealtimeConnectionState; exports.RealtimeReason = RealtimeReason; exports.RealtimeState = RealtimeState; exports.XmaxConfiguration = XmaxConfiguration; exports.XmaxClient = XmaxClient; exports.RealtimeConfiguration = RealtimeConfiguration; exports.VideoContentMode = VideoContentMode; exports.RealtimeContext = RealtimeContext; exports.XmaxVideoView = XmaxVideoView; exports.XmaxRealtimeVideoView = XmaxRealtimeVideoView;
8766
- //# sourceMappingURL=chunk-JWAAS6P4.cjs.map
10346
+
10347
+ exports.XmaxEnvironment = XmaxEnvironment; exports.apiBaseURL = apiBaseURL; exports.XmaxLoggerOption = XmaxLoggerOption; exports.XmaxLogger = XmaxLogger; exports.XmaxErrorCode = XmaxErrorCode; exports.XmaxError = XmaxError; exports.RealtimeVideoEncoderPreference = RealtimeVideoEncoderPreference; exports.RealtimeVideoFormat = RealtimeVideoFormat; exports.RealtimeModel = RealtimeModel; exports.defaultFrameRate = defaultFrameRate; exports.defaultCameraVideoFormat = defaultCameraVideoFormat; exports.MediaService = MediaService; exports.StoredFile = StoredFile; exports.StorageService = StorageService; exports.CameraPosition = CameraPosition; exports.RtcProvider = RtcProvider; exports.RealtimeVideoTrack = RealtimeVideoTrack; exports.RealtimeMediaStream = RealtimeMediaStream; exports.RealtimeConnectionState = RealtimeConnectionState; exports.RealtimeReason = RealtimeReason; exports.RealtimeState = RealtimeState; exports.XmaxConfiguration = XmaxConfiguration; exports.XmaxClient = XmaxClient; exports.RealtimeConfiguration = RealtimeConfiguration; exports.VideoContentMode = VideoContentMode; exports.RealtimeContext = RealtimeContext; exports.XmaxVideoView = XmaxVideoView; exports.XmaxRealtimeVideoView = XmaxRealtimeVideoView;
10348
+ //# sourceMappingURL=chunk-FEL5MJMJ.cjs.map