@xmaxai/web-sdk 1.0.0 → 1.0.2

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.
@@ -9,8 +9,7 @@ var XmaxEnvironment = /* @__PURE__ */ ((XmaxEnvironment2) => {
9
9
  return XmaxEnvironment2;
10
10
  })(XmaxEnvironment || {});
11
11
  var API_BASE_URLS = {
12
- // 临时联调环境。
13
- ["china" /* china */]: "https://dev.xmaxai.com/open/api/v1",
12
+ ["china" /* china */]: "https://cloud.xmax.22duck.cn/open/api/v1",
14
13
  ["global" /* global */]: "https://api.xmax.cloud/open/api/v1"
15
14
  };
16
15
  function apiBaseURL(environment) {
@@ -38,8 +37,13 @@ var CONSOLE_METHOD = {
38
37
  var BRAND_BADGE_STYLE = "background:#2563eb;color:#fff;border-radius:3px;padding:1px 4px;font-weight:600;";
39
38
  var CATEGORY_BADGE_STYLE = "background:#475569;color:#fff;border-radius:3px;padding:1px 4px;font-weight:600;";
40
39
  var _XmaxLogger = class _XmaxLogger {
40
+ /**
41
+ * 创建固定业务分类的日志入口,共享全局日志选项和语言配置。
42
+ */
41
43
  constructor(category) {
42
- // 日志类别
44
+ /**
45
+ * 日志类别
46
+ */
43
47
  __publicField(this, "category");
44
48
  this.category = category;
45
49
  }
@@ -53,31 +57,45 @@ var _XmaxLogger = class _XmaxLogger {
53
57
  state.options = options;
54
58
  state.environment = environment;
55
59
  }
56
- /** 选择日志细项文案;日志标题保持原有中英双语。 */
60
+ /**
61
+ * 选择日志细项文案;日志标题保持原有中英双语。
62
+ */
57
63
  static localized(chinese, english) {
58
64
  return state.environment === "china" /* china */ ? chinese : english;
59
65
  }
60
- /** 判断指定日志类型是否已开启。 */
66
+ /**
67
+ * 判断指定日志类型是否已开启。
68
+ */
61
69
  static isEnabled(option) {
62
70
  return (state.options & option) !== 0;
63
71
  }
64
- /** 输出调试日志。 */
72
+ /**
73
+ * 输出调试日志。
74
+ */
65
75
  debug(message, option = 1 /* business */) {
66
76
  this.log("debug", message, option);
67
77
  }
68
- /** 输出信息日志。 */
78
+ /**
79
+ * 输出信息日志。
80
+ */
69
81
  info(message, option = 1 /* business */) {
70
82
  this.log("info", message, option);
71
83
  }
72
- /** 输出警告日志。 */
84
+ /**
85
+ * 输出警告日志。
86
+ */
73
87
  warning(message, option = 1 /* business */) {
74
88
  this.log("warning", message, option);
75
89
  }
76
- /** 输出错误日志。 */
90
+ /**
91
+ * 输出错误日志。
92
+ */
77
93
  error(message, option = 1 /* business */) {
78
94
  this.log("error", message, option);
79
95
  }
80
- /** 按日志类型开关输出到控制台;未开启时不求值日志内容。 */
96
+ /**
97
+ * 按日志类型开关输出到控制台;未开启时不求值日志内容。
98
+ */
81
99
  log(level, message, option) {
82
100
  if (!_XmaxLogger.isEnabled(option)) {
83
101
  return;
@@ -98,7 +116,9 @@ var _XmaxLogger = class _XmaxLogger {
98
116
  console[CONSOLE_METHOD[level]](`[Xmax][${this.category}] ${text}`);
99
117
  }
100
118
  };
101
- // 分类日志
119
+ /**
120
+ * 分类日志
121
+ */
102
122
  __publicField(_XmaxLogger, "realtime", new _XmaxLogger("Realtime"));
103
123
  __publicField(_XmaxLogger, "rtc", new _XmaxLogger("RTC"));
104
124
  __publicField(_XmaxLogger, "media", new _XmaxLogger("Media"));
@@ -142,12 +162,20 @@ var XmaxError = class _XmaxError extends Error {
142
162
  */
143
163
  constructor(code, message, options) {
144
164
  super(message);
145
- // 错误信息
146
- /** SDK 统一错误码。 */
165
+ /**
166
+ * 错误信息
167
+ */
168
+ /**
169
+ * SDK 统一错误码。
170
+ */
147
171
  __publicField(this, "code");
148
- /** Xmax API 返回的业务错误码;非 API 业务错误时为空。 */
172
+ /**
173
+ * Xmax API 返回的业务错误码;非 API 业务错误时为空。
174
+ */
149
175
  __publicField(this, "apiCode");
150
- /** HTTP 响应状态码;请求未获得响应时为空。 */
176
+ /**
177
+ * HTTP 响应状态码;请求未获得响应时为空。
178
+ */
151
179
  __publicField(this, "httpStatus");
152
180
  this.name = "XmaxError";
153
181
  this.code = code;
@@ -170,9 +198,11 @@ var XmaxError = class _XmaxError extends Error {
170
198
  };
171
199
 
172
200
  // src/Foundation/Runtime/RuntimeInfo.ts
173
- var XMAX_SDK_VERSION = "1.0.0";
201
+ var XMAX_SDK_VERSION = "1.0.2";
174
202
  var _RuntimeInfo = class _RuntimeInfo {
175
- /** 序列化为接口传输使用的 snake_case 字段。 */
203
+ /**
204
+ * 序列化为接口传输使用的 snake_case 字段。
205
+ */
176
206
  static toJSON(snapshot = _RuntimeInfo.current) {
177
207
  return {
178
208
  platform: snapshot.platform,
@@ -181,7 +211,9 @@ var _RuntimeInfo = class _RuntimeInfo {
181
211
  device_model: snapshot.deviceModel
182
212
  };
183
213
  }
184
- /** 读取浏览器 User-Agent;非浏览器环境返回 unknown。 */
214
+ /**
215
+ * 读取浏览器 User-Agent;非浏览器环境返回 unknown。
216
+ */
185
217
  static currentOSVersion() {
186
218
  if (typeof navigator !== "undefined" && navigator.userAgent) {
187
219
  return navigator.userAgent;
@@ -189,7 +221,12 @@ var _RuntimeInfo = class _RuntimeInfo {
189
221
  return "unknown";
190
222
  }
191
223
  };
192
- /** 当前运行环境快照。 */
224
+ /**
225
+ * 运行环境快照
226
+ */
227
+ /**
228
+ * 当前运行环境快照。
229
+ */
193
230
  __publicField(_RuntimeInfo, "current", {
194
231
  platform: "web",
195
232
  osVersion: _RuntimeInfo.currentOSVersion(),
@@ -209,17 +246,24 @@ var _ApiService = class _ApiService {
209
246
  * @param options.fetchImpl fetch 实现(可替换,测试用)。
210
247
  */
211
248
  constructor(options) {
212
- // 平台资源
249
+ /**
250
+ * 请求身份、地址与超时配置
251
+ */
213
252
  __publicField(this, "apiKey");
214
253
  __publicField(this, "baseURL");
215
254
  __publicField(this, "timeoutMs");
255
+ /**
256
+ * 网络传输依赖
257
+ */
216
258
  __publicField(this, "fetchImpl");
217
259
  this.apiKey = options.apiKey.trim();
218
260
  this.baseURL = options.baseURL;
219
261
  this.timeoutMs = options.timeoutMs ?? _ApiService.defaultTimeoutMs;
220
262
  this.fetchImpl = options.fetchImpl ?? ((url, init) => fetch(url, init));
221
263
  }
222
- /** 发送请求并返回统一响应中的数据。 */
264
+ /**
265
+ * 发送请求并返回统一响应中的数据。
266
+ */
223
267
  async request(method, path, body) {
224
268
  this.validateConfiguration();
225
269
  const request = this.makeRequest(method, path, body);
@@ -248,23 +292,33 @@ var _ApiService = class _ApiService {
248
292
  throw mapped;
249
293
  }
250
294
  }
251
- /** 发送 GET 请求。 */
295
+ /**
296
+ * 发送 GET 请求。
297
+ */
252
298
  get(path) {
253
299
  return this.request("GET" /* get */, path);
254
300
  }
255
- /** 发送 POST 请求。 */
301
+ /**
302
+ * 发送 POST 请求。
303
+ */
256
304
  post(path, body) {
257
305
  return this.request("POST" /* post */, path, body);
258
306
  }
259
- /** 发送 PUT 请求。 */
307
+ /**
308
+ * 发送 PUT 请求。
309
+ */
260
310
  put(path, body) {
261
311
  return this.request("PUT" /* put */, path, body);
262
312
  }
263
- /** 发送 DELETE 请求。 */
313
+ /**
314
+ * 发送 DELETE 请求。
315
+ */
264
316
  delete(path) {
265
317
  return this.request("DELETE" /* delete */, path);
266
318
  }
267
- /** 校验 API Key 与 Base URL 配置。 */
319
+ /**
320
+ * 校验 API Key 与 Base URL 配置。
321
+ */
268
322
  validateConfiguration() {
269
323
  if (this.apiKey.length === 0) {
270
324
  throw new XmaxError("INVALID_API_KEY" /* invalidAPIKey */, "API key cannot be empty");
@@ -287,7 +341,9 @@ var _ApiService = class _ApiService {
287
341
  );
288
342
  }
289
343
  }
290
- /** 构造请求地址、请求头与请求体;路径必须相对且与 Base URL 同源。 */
344
+ /**
345
+ * 构造请求地址、请求头与请求体;路径必须相对且与 Base URL 同源。
346
+ */
291
347
  makeRequest(method, path, body) {
292
348
  const normalizedPath = path.trim();
293
349
  if (normalizedPath.length === 0 || normalizedPath.includes("://") || normalizedPath.startsWith("//")) {
@@ -333,7 +389,9 @@ var _ApiService = class _ApiService {
333
389
  }
334
390
  };
335
391
  }
336
- /** 解析统一响应信封并返回数据;业务失败或数据缺失时抛出错误。 */
392
+ /**
393
+ * 解析统一响应信封并返回数据;业务失败或数据缺失时抛出错误。
394
+ */
337
395
  static parseResponse(text, status) {
338
396
  let envelope;
339
397
  try {
@@ -348,7 +406,7 @@ var _ApiService = class _ApiService {
348
406
  const message = envelope.message?.trim();
349
407
  throw new XmaxError(
350
408
  "API_ERROR" /* apiError */,
351
- message && message.length > 0 ? message : "Xmax API request failed",
409
+ message || "Xmax API request failed",
352
410
  { apiCode: envelope.code, httpStatus: status }
353
411
  );
354
412
  }
@@ -361,7 +419,9 @@ var _ApiService = class _ApiService {
361
419
  }
362
420
  return envelope.data;
363
421
  }
364
- /** 将传输层异常映射为 SDK 统一错误。 */
422
+ /**
423
+ * 将传输层异常映射为 SDK 统一错误。
424
+ */
365
425
  static transportError(error) {
366
426
  if (error instanceof XmaxError) {
367
427
  return error;
@@ -378,7 +438,9 @@ var _ApiService = class _ApiService {
378
438
  `HTTP request failed: ${message}`
379
439
  );
380
440
  }
381
- /** 输出失败日志。 */
441
+ /**
442
+ * 输出失败日志。
443
+ */
382
444
  logFailure(method, path, error, startedAt) {
383
445
  XmaxLogger.api.error(
384
446
  () => `API \u8BF7\u6C42\u5931\u8D25 (API Request Failed)
@@ -387,8 +449,12 @@ var _ApiService = class _ApiService {
387
449
  );
388
450
  }
389
451
  };
390
- // API 配置
391
- /** 默认请求超时时间(毫秒)。 */
452
+ /**
453
+ * API 配置
454
+ */
455
+ /**
456
+ * 默认请求超时时间(毫秒)。
457
+ */
392
458
  __publicField(_ApiService, "defaultTimeoutMs", 15e3);
393
459
  var ApiService = _ApiService;
394
460
 
@@ -410,11 +476,35 @@ var RealtimeVideoFormat = class _RealtimeVideoFormat {
410
476
  * @param init.encoderPreference 上传编码策略偏好,默认值为 maintainFramerate。
411
477
  */
412
478
  constructor(init) {
479
+ /**
480
+ * 视频规格
481
+ */
482
+ /**
483
+ * 视频宽度,单位为像素。
484
+ */
413
485
  __publicField(this, "width");
486
+ /**
487
+ * 视频高度,单位为像素。
488
+ */
414
489
  __publicField(this, "height");
490
+ /**
491
+ * 视频帧率,单位为 fps。
492
+ */
415
493
  __publicField(this, "fps");
494
+ /**
495
+ * 上传编码配置
496
+ */
497
+ /**
498
+ * 最低上传码率,单位为 kbps;未指定时由 SDK 计算,0 表示不设最低码率。
499
+ */
416
500
  __publicField(this, "minimumBitrate");
501
+ /**
502
+ * 最高上传码率,单位为 kbps;未指定时由 SDK 计算。
503
+ */
417
504
  __publicField(this, "maximumBitrate");
505
+ /**
506
+ * 弱网下优先保障帧率或分辨率的编码策略。
507
+ */
418
508
  __publicField(this, "encoderPreference");
419
509
  this.width = init.width;
420
510
  this.height = init.height;
@@ -453,7 +543,9 @@ var RealtimeVideoFormat = class _RealtimeVideoFormat {
453
543
  );
454
544
  }
455
545
  }
456
- /** 调整尺寸,保留帧率和上传编码配置。 */
546
+ /**
547
+ * 调整尺寸,保留帧率和上传编码配置。
548
+ */
457
549
  resized(width, height) {
458
550
  return new _RealtimeVideoFormat({
459
551
  width,
@@ -468,17 +560,26 @@ var RealtimeVideoFormat = class _RealtimeVideoFormat {
468
560
 
469
561
  // src/Service/Realtime/RealtimeModel.ts
470
562
  var RealtimeModel = /* @__PURE__ */ ((RealtimeModel2) => {
471
- RealtimeModel2["x2_fast_1080p"] = "x2-fast-1080p";
563
+ RealtimeModel2["x2_0"] = "x2.0";
564
+ RealtimeModel2["x2_0_pro"] = "x2.0-pro";
565
+ RealtimeModel2["x2_0_trtc"] = "x2.0-trtc";
472
566
  return RealtimeModel2;
473
567
  })(RealtimeModel || {});
474
568
  var RESOLUTION_BUCKETS = {
475
- ["x2-fast-1080p" /* x2_fast_1080p */]: [
569
+ ["x2.0" /* x2_0 */]: [],
570
+ ["x2.0-pro" /* x2_0_pro */]: [
571
+ { width: 1024, height: 1920 },
572
+ { width: 1920, height: 1024 }
573
+ ],
574
+ ["x2.0-trtc" /* x2_0_trtc */]: [
476
575
  { width: 1024, height: 1920 },
477
576
  { width: 1920, height: 1024 }
478
577
  ]
479
578
  };
480
579
  var MAXIMUM_INPUT_PIXELS = {
481
- ["x2-fast-1080p" /* x2_fast_1080p */]: 21e5
580
+ ["x2.0" /* x2_0 */]: 128e4,
581
+ ["x2.0-pro" /* x2_0_pro */]: 21e5,
582
+ ["x2.0-trtc" /* x2_0_trtc */]: 21e5
482
583
  };
483
584
  function resolutionBuckets(model) {
484
585
  return RESOLUTION_BUCKETS[model];
@@ -495,7 +596,10 @@ function inputSizeAlignment(_model) {
495
596
  function defaultFrameRate(_model) {
496
597
  return 30;
497
598
  }
498
- function defaultCameraVideoFormat(_model) {
599
+ function defaultCameraVideoFormat(model) {
600
+ if (model === "x2.0" /* x2_0 */) {
601
+ return new RealtimeVideoFormat({ width: 832, height: 1472, fps: 30 });
602
+ }
499
603
  return new RealtimeVideoFormat({ width: 1024, height: 1920, fps: 30 });
500
604
  }
501
605
 
@@ -518,14 +622,18 @@ var MediaService = class {
518
622
  /**
519
623
  * 创建媒体服务。
520
624
  *
521
- * @param model 媒体输入规则使用的模型,默认为 `x2-fast-1080p`。
625
+ * @param model 媒体输入规则使用的模型,默认为 `x2.0-trtc`。
522
626
  */
523
- constructor(model = "x2-fast-1080p" /* x2_fast_1080p */) {
524
- // 模型约束
627
+ constructor(model = "x2.0-trtc" /* x2_0_trtc */) {
628
+ /**
629
+ * 模型约束
630
+ */
525
631
  __publicField(this, "model");
526
632
  this.model = model;
527
633
  }
528
- /** Web 插帧保留原始尺寸;/16 对齐由 GPU 内部补边处理,不缩小回传视频。 */
634
+ /**
635
+ * Web 插帧保留原始尺寸;/16 对齐由 GPU 内部补边处理,不缩小回传视频。
636
+ */
529
637
  resolveFrameInterpolationSize(size) {
530
638
  const { width, height } = size;
531
639
  if (!Number.isSafeInteger(width) || !Number.isSafeInteger(height) || width <= 0 || height <= 0) {
@@ -533,10 +641,15 @@ var MediaService = class {
533
641
  }
534
642
  return { width, height };
535
643
  }
536
- /** WebGPU 适配器探测是异步的;实际性能和运行状态由渲染管线判断。 */
644
+ /**
645
+ * WebGPU 适配器探测是异步的;实际性能和运行状态由渲染管线判断。
646
+ */
537
647
  async supportsFrameInterpolation(size) {
538
648
  return await frameInterpolationAdapter(size) !== null;
539
649
  }
650
+ /**
651
+ * 按模型分辨率档位或像素预算解析输入尺寸,保持宽高比例并对齐;不支持的规格抛错。
652
+ */
540
653
  resolveModelInputSize(size) {
541
654
  const buckets = resolutionBuckets(this.model);
542
655
  if (buckets.length > 0) {
@@ -586,7 +699,9 @@ var MediaService = class {
586
699
  validated.height * scale
587
700
  );
588
701
  }
589
- /** 在对齐网格上选择满足面积限制且最接近目标的尺寸。 */
702
+ /**
703
+ * 在对齐网格上选择满足面积限制且最接近目标的尺寸。
704
+ */
590
705
  boundedAlignedSize(width, height) {
591
706
  const alignment = inputSizeAlignment(this.model);
592
707
  const unitPixels = alignment * alignment;
@@ -614,7 +729,9 @@ var MediaService = class {
614
729
  }
615
730
  return bestSize;
616
731
  }
617
- /** 校验尺寸为有限正数,并归一化为不小于 1 的整数。 */
732
+ /**
733
+ * 校验尺寸为有限正数,并归一化为不小于 1 的整数。
734
+ */
618
735
  validatedSize(size) {
619
736
  if (!Number.isFinite(size.width) || !Number.isFinite(size.height) || size.width <= 0 || size.height <= 0) {
620
737
  throw new XmaxError(
@@ -639,11 +756,20 @@ var StoredFile = class {
639
756
  * @param init.etag 对象存储返回的 ETag。
640
757
  */
641
758
  constructor(init) {
642
- /** 文件的访问地址。 */
759
+ /**
760
+ * 存储结果
761
+ */
762
+ /**
763
+ * 文件的访问地址。
764
+ */
643
765
  __publicField(this, "url");
644
- /** 对象存储中的对象键。 */
766
+ /**
767
+ * 对象存储中的对象键。
768
+ */
645
769
  __publicField(this, "objectKey");
646
- /** 对象存储返回的 ETag;未返回时为空。 */
770
+ /**
771
+ * 对象存储返回的 ETag;未返回时为空。
772
+ */
647
773
  __publicField(this, "etag");
648
774
  this.url = init.url;
649
775
  this.objectKey = init.objectKey;
@@ -672,11 +798,17 @@ var StorageService = class _StorageService {
672
798
  * @param options.identifierGenerator 标识来源(可替换,测试用)。
673
799
  */
674
800
  constructor(options) {
675
- // 服务层组件
801
+ /**
802
+ * 服务层组件
803
+ */
676
804
  __publicField(this, "apiService");
677
- // 平台资源
805
+ /**
806
+ * 平台资源
807
+ */
678
808
  __publicField(this, "clientFactory");
679
- // 标识生成
809
+ /**
810
+ * 标识生成
811
+ */
680
812
  __publicField(this, "dateGenerator");
681
813
  __publicField(this, "identifierGenerator");
682
814
  this.apiService = options.apiService;
@@ -684,7 +816,9 @@ var StorageService = class _StorageService {
684
816
  this.dateGenerator = options.dateGenerator ?? (() => /* @__PURE__ */ new Date());
685
817
  this.identifierGenerator = options.identifierGenerator ?? (() => crypto.randomUUID().toLowerCase());
686
818
  }
687
- /** 上传图片并返回访问地址。 */
819
+ /**
820
+ * 上传图片并返回访问地址。
821
+ */
688
822
  async uploadImage(options) {
689
823
  const startedAt = Date.now();
690
824
  try {
@@ -734,7 +868,9 @@ var StorageService = class _StorageService {
734
868
  );
735
869
  }
736
870
  }
737
- /** 获取临时存储配置;字段不完整时抛出 API 错误。 */
871
+ /**
872
+ * 获取临时存储配置;字段不完整时抛出 API 错误。
873
+ */
738
874
  async fetchStorageConfiguration() {
739
875
  const payload = await this.apiService.get("/cos/sts");
740
876
  const message = "Invalid storage credential payload";
@@ -755,12 +891,16 @@ var StorageService = class _StorageService {
755
891
  credentials: { accessKeyID, secretAccessKey, sessionToken }
756
892
  };
757
893
  }
758
- /** 生成对象键:`${prefix}${毫秒时间戳}_${标识}_${文件名}`。 */
894
+ /**
895
+ * 生成对象键:`${prefix}${毫秒时间戳}_${标识}_${文件名}`。
896
+ */
759
897
  makeObjectKey(prefix, fileName) {
760
898
  const milliseconds = Math.round(this.dateGenerator().getTime());
761
899
  return `${prefix}${milliseconds}_${this.identifierGenerator()}_${fileName}`;
762
900
  }
763
- /** 生产环境 COS 客户端工厂:动态加载 cos-js-sdk-v5 并按临时凭证创建客户端。 */
901
+ /**
902
+ * 生产环境 COS 客户端工厂:动态加载 cos-js-sdk-v5 并按临时凭证创建客户端。
903
+ */
764
904
  static async cosClientFactory(configuration) {
765
905
  const module = await import("cos-js-sdk-v5");
766
906
  const COS = module.default;
@@ -770,7 +910,9 @@ var StorageService = class _StorageService {
770
910
  SecurityToken: configuration.credentials.sessionToken
771
911
  });
772
912
  }
773
- /** 调用对象存储上传并包装为 Promise。 */
913
+ /**
914
+ * 调用对象存储上传并包装为 Promise。
915
+ */
774
916
  static putObject(client, params) {
775
917
  return new Promise((resolve, reject) => {
776
918
  client.putObject(params, (error, data) => {
@@ -806,7 +948,9 @@ var StorageService = class _StorageService {
806
948
  }
807
949
  return `https://${configuration.bucket}.cos.${configuration.region}.myqcloud.com/${objectKey}`;
808
950
  }
809
- /** 统一构造上传体。 */
951
+ /**
952
+ * 统一构造上传体。
953
+ */
810
954
  static makeBody(data, contentType) {
811
955
  if (data instanceof Blob) {
812
956
  return data;
@@ -814,7 +958,9 @@ var StorageService = class _StorageService {
814
958
  const bytes = data instanceof Uint8Array ? data : new Uint8Array(data);
815
959
  return new Blob([bytes], { type: contentType });
816
960
  }
817
- /** 校验文件名非空且不包含路径分隔符。 */
961
+ /**
962
+ * 校验文件名非空且不包含路径分隔符。
963
+ */
818
964
  static validateFileName(fileName) {
819
965
  const safeName = fileName.trim();
820
966
  if (!safeName || safeName.includes("/") || safeName.includes("\\")) {
@@ -825,12 +971,16 @@ var StorageService = class _StorageService {
825
971
  }
826
972
  return safeName;
827
973
  }
828
- /** 按文件名后缀推断内容类型。 */
974
+ /**
975
+ * 按文件名后缀推断内容类型。
976
+ */
829
977
  static inferContentType(fileName) {
830
978
  const extension = fileName.split(".").pop()?.toLowerCase() ?? "";
831
979
  return CONTENT_TYPES[extension] ?? "application/octet-stream";
832
980
  }
833
- /** 格式化字节数用于日志输出。 */
981
+ /**
982
+ * 格式化字节数用于日志输出。
983
+ */
834
984
  static formatByteCount(bytes) {
835
985
  if (bytes < 1024) {
836
986
  return `${bytes} B`;
@@ -840,10 +990,12 @@ var StorageService = class _StorageService {
840
990
  }
841
991
  return `${(bytes / 1024 / 1024).toFixed(2)} MB`;
842
992
  }
843
- /** 归一化可选字符串:去除首尾空白后为空时返回 `undefined`。 */
993
+ /**
994
+ * 归一化可选字符串:去除首尾空白后为空时返回 `undefined`。
995
+ */
844
996
  static nonEmpty(value) {
845
997
  const normalized = value?.trim();
846
- return normalized && normalized.length > 0 ? normalized : void 0;
998
+ return normalized || void 0;
847
999
  }
848
1000
  };
849
1001
 
@@ -856,12 +1008,23 @@ var CameraPosition = /* @__PURE__ */ ((CameraPosition3) => {
856
1008
 
857
1009
  // src/Foundation/RTC/RtcEngineManager.ts
858
1010
  var RtcEngineLease = class {
1011
+ /**
1012
+ * 为已创建的引擎生成独占租约标识,供引擎管理器核对释放请求。
1013
+ */
859
1014
  constructor(engine) {
860
- // RTC 资源
861
- /** 租约持有的 TRTC 引擎实例。 */
1015
+ /**
1016
+ * RTC 资源
1017
+ */
1018
+ /**
1019
+ * 租约持有的 TRTC 引擎实例。
1020
+ */
862
1021
  __publicField(this, "engine");
863
- // 运行标识
864
- /** 租约标识。 */
1022
+ /**
1023
+ * 运行标识
1024
+ */
1025
+ /**
1026
+ * 租约标识。
1027
+ */
865
1028
  __publicField(this, "id");
866
1029
  this.engine = engine;
867
1030
  this.id = `lease-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
@@ -874,11 +1037,17 @@ var _RtcEngineManager = class _RtcEngineManager {
874
1037
  * @param makeEngine 引擎工厂(可替换,测试用)。
875
1038
  */
876
1039
  constructor(makeEngine = _RtcEngineManager.defaultMakeEngine) {
877
- // 依赖
1040
+ /**
1041
+ * 依赖
1042
+ */
878
1043
  __publicField(this, "makeEngine");
879
- // RTC 资源
1044
+ /**
1045
+ * RTC 资源
1046
+ */
880
1047
  __publicField(this, "activeLease");
881
- // 运行状态
1048
+ /**
1049
+ * 运行状态
1050
+ */
882
1051
  __publicField(this, "requests", []);
883
1052
  this.makeEngine = makeEngine;
884
1053
  }
@@ -901,7 +1070,9 @@ var _RtcEngineManager = class _RtcEngineManager {
901
1070
  });
902
1071
  });
903
1072
  }
904
- /** 释放有效租约、销毁 Engine,并把独占权交给下一个等待者。 */
1073
+ /**
1074
+ * 释放有效租约、销毁 Engine,并把独占权交给下一个等待者。
1075
+ */
905
1076
  release(lease) {
906
1077
  if (this.activeLease?.id !== lease.id) {
907
1078
  return;
@@ -913,13 +1084,17 @@ var _RtcEngineManager = class _RtcEngineManager {
913
1084
  }
914
1085
  void this.fulfillNextRequest();
915
1086
  }
916
- /** 动态加载 TRTC SDK 并创建引擎实例。 */
1087
+ /**
1088
+ * 动态加载 TRTC SDK 并创建引擎实例。
1089
+ */
917
1090
  static async defaultMakeEngine() {
918
1091
  const module = await import("trtc-sdk-v5");
919
1092
  module.default.setLogLevel(5);
920
1093
  return module.default.create();
921
1094
  }
922
- /** 创建新引擎租约。 */
1095
+ /**
1096
+ * 创建新引擎租约。
1097
+ */
923
1098
  async createLease() {
924
1099
  const engine = await this.makeEngine();
925
1100
  if (!engine) {
@@ -930,7 +1105,9 @@ var _RtcEngineManager = class _RtcEngineManager {
930
1105
  }
931
1106
  return new RtcEngineLease(engine);
932
1107
  }
933
- /** 满足下一个排队请求;创建失败时拒绝该请求并继续处理后续请求。 */
1108
+ /**
1109
+ * 满足下一个排队请求;创建失败时拒绝该请求并继续处理后续请求。
1110
+ */
934
1111
  async fulfillNextRequest() {
935
1112
  while (!this.activeLease && this.requests.length > 0) {
936
1113
  const request = this.requests.shift();
@@ -947,12 +1124,20 @@ var _RtcEngineManager = class _RtcEngineManager {
947
1124
  }
948
1125
  }
949
1126
  };
950
- /** 共享实例。 */
1127
+ /**
1128
+ * 共享管理入口
1129
+ */
1130
+ /**
1131
+ * 共享实例。
1132
+ */
951
1133
  __publicField(_RtcEngineManager, "shared", new _RtcEngineManager());
952
1134
  var RtcEngineManager = _RtcEngineManager;
953
1135
 
954
1136
  // src/Foundation/RTC/RtcStatsLogger.ts
955
1137
  var RtcStatsLogger = class {
1138
+ /**
1139
+ * 按上下行及音视频分组输出白名单统计指标,仅在 performance 日志启用时格式化。
1140
+ */
956
1141
  static logStatistics(stats) {
957
1142
  XmaxLogger.rtc.info(() => {
958
1143
  const blocks = [this.block("RTC \u8FD0\u884C\u7EDF\u8BA1 (RTC Statistics)", [
@@ -981,12 +1166,18 @@ var RtcStatsLogger = class {
981
1166
  return blocks.join("\n\n");
982
1167
  }, 2 /* performance */);
983
1168
  }
1169
+ /**
1170
+ * 输出上下行网络质量等级、RTT 和丢包率,遵循 performance 日志开关。
1171
+ */
984
1172
  static logNetworkQuality(stats) {
985
1173
  XmaxLogger.rtc.info(() => this.block("\u7F51\u7EDC\u8D28\u91CF (Network Quality Metrics)", [
986
1174
  `${XmaxLogger.localized("\u672C\u5730\u4E0A\u884C\uFF1A", "Local Uplink: ")}${this.quality(stats.uplinkNetworkQuality)}, RTT ${this.value(stats.uplinkRTT, "ms")}, ${XmaxLogger.localized("\u4E22\u5305\u7387 ", "Loss ")}${this.value(stats.uplinkLoss, "%")}`,
987
1175
  `${XmaxLogger.localized("\u672C\u5730\u4E0B\u884C\uFF08\u5E73\u5747\uFF09\uFF1A", "Local Downlink (Average): ")}${this.quality(stats.downlinkNetworkQuality)}, RTT ${this.value(stats.downlinkRTT, "ms")}, ${XmaxLogger.localized("\u4E22\u5305\u7387 ", "Loss ")}${this.value(stats.downlinkLoss, "%")}`
988
1176
  ]), 2 /* performance */);
989
1177
  }
1178
+ /**
1179
+ * 格式化音频码率和音量;远端音频额外包含播放延迟。
1180
+ */
990
1181
  static audioBlock(title, stats, remote) {
991
1182
  return this.block(title, [
992
1183
  `${XmaxLogger.localized("\u7801\u7387\uFF1A", "Bitrate: ")}${this.value(stats.bitrate, "kbps")}`,
@@ -994,6 +1185,9 @@ var RtcStatsLogger = class {
994
1185
  ...remote ? this.playbackLines(stats) : []
995
1186
  ]);
996
1187
  }
1188
+ /**
1189
+ * 格式化视频流类型、尺寸、帧率和码率;远端视频额外包含播放延迟。
1190
+ */
997
1191
  static videoBlock(title, stats, remote) {
998
1192
  return this.block(title, [
999
1193
  `${XmaxLogger.localized("\u6D41\u7C7B\u578B\uFF1A", "Stream Type: ")}${stats.videoType ?? "\u2014"}`,
@@ -1003,22 +1197,33 @@ var RtcStatsLogger = class {
1003
1197
  ...remote ? this.playbackLines(stats) : []
1004
1198
  ]);
1005
1199
  }
1200
+ /**
1201
+ * 格式化 TRTC 播放缓冲及估算端到端延迟,缺失或无效指标显示占位符。
1202
+ */
1006
1203
  static playbackLines(stats) {
1007
1204
  return [
1008
1205
  `${XmaxLogger.localized("\u64AD\u653E\u7F13\u51B2\u5EF6\u8FDF\uFF1A", "Jitter Buffer Delay: ")}${this.value(stats.jitterBufferDelay, "ms")}`,
1009
1206
  `${XmaxLogger.localized("RTC \u7AEF\u5230\u7AEF\u5EF6\u8FDF\uFF08\u4F30\u7B97\uFF09\uFF1A", "RTC End-to-End Delay (Estimated): ")}${this.value(stats.point2pointDelay, "ms")}`
1010
1207
  ];
1011
1208
  }
1209
+ /**
1210
+ * 为指标列表添加标题和树形行前缀,生成一个控制台日志分组。
1211
+ */
1012
1212
  static block(title, lines) {
1013
1213
  return [title, ...lines.map((line, index) => `${index === lines.length - 1 ? "\u2514\u2500" : "\u251C\u2500"} ${line}`)].join("\n");
1014
1214
  }
1015
- /** TRTC 丢包率已是百分数,无需再乘 100。 */
1215
+ /**
1216
+ * TRTC 丢包率已是百分数,无需再乘 100。
1217
+ */
1016
1218
  static value(value, unit = "") {
1017
1219
  if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
1018
1220
  return "\u2014";
1019
1221
  }
1020
1222
  return `${Number(value.toFixed(2))}${unit ? `${unit === "%" ? "" : " "}${unit}` : ""}`;
1021
1223
  }
1224
+ /**
1225
+ * 将 TRTC 质量等级映射为本地化文案;未知等级回退为“未知”。
1226
+ */
1022
1227
  static quality(value) {
1023
1228
  const names = [
1024
1229
  ["\u672A\u77E5", "Unknown"],
@@ -1057,25 +1262,39 @@ var RtcManager = class {
1057
1262
  * @param engineManager 引擎租约管理组件(可替换,测试用)。
1058
1263
  */
1059
1264
  constructor(engineManager = RtcEngineManager.shared) {
1060
- // 依赖
1265
+ /**
1266
+ * 依赖
1267
+ */
1061
1268
  __publicField(this, "engineManager");
1062
- // RTC 资源
1269
+ /**
1270
+ * RTC 资源
1271
+ */
1063
1272
  __publicField(this, "lease");
1064
- // 采集状态
1273
+ /**
1274
+ * 采集状态
1275
+ */
1065
1276
  __publicField(this, "isCapturing", false);
1066
1277
  __publicField(this, "isAudioCapturing", false);
1067
- // 房间状态
1278
+ /**
1279
+ * 房间状态
1280
+ */
1068
1281
  __publicField(this, "isInRoom", false);
1069
- // 事件监听
1282
+ /**
1283
+ * 事件监听
1284
+ */
1070
1285
  __publicField(this, "eventListener");
1071
1286
  __publicField(this, "removeStatsListeners");
1072
1287
  this.engineManager = engineManager;
1073
1288
  }
1074
- /** RTC 引擎是否已初始化。 */
1289
+ /**
1290
+ * RTC 引擎是否已初始化。
1291
+ */
1075
1292
  get isInitialized() {
1076
1293
  return this.lease !== void 0;
1077
1294
  }
1078
- /** 初始化 RTC 引擎(获取独占租约)并注册事件桥接;已初始化时重复调用不产生效果。 */
1295
+ /**
1296
+ * 初始化 RTC 引擎(获取独占租约)并注册事件桥接;已初始化时重复调用不产生效果。
1297
+ */
1079
1298
  async initialize() {
1080
1299
  if (this.lease) {
1081
1300
  return;
@@ -1084,7 +1303,9 @@ var RtcManager = class {
1084
1303
  this.registerEventBridge(lease.engine);
1085
1304
  this.lease = lease;
1086
1305
  }
1087
- /** 停止采集与发布、销毁 RTC 引擎并释放租约;未初始化时重复调用不产生效果。 */
1306
+ /**
1307
+ * 停止采集与发布、销毁 RTC 引擎并释放租约;未初始化时重复调用不产生效果。
1308
+ */
1088
1309
  async destroy() {
1089
1310
  const lease = this.lease;
1090
1311
  this.lease = void 0;
@@ -1177,7 +1398,9 @@ var RtcManager = class {
1177
1398
  throw this.mapError(error);
1178
1399
  }
1179
1400
  }
1180
- /** 停止摄像头采集;引擎未初始化或采集未启动时不产生效果。 */
1401
+ /**
1402
+ * 停止摄像头采集;引擎未初始化或采集未启动时不产生效果。
1403
+ */
1181
1404
  async stopCameraCapture() {
1182
1405
  const engine = this.lease?.engine;
1183
1406
  this.isCapturing = false;
@@ -1225,7 +1448,9 @@ var RtcManager = class {
1225
1448
  throw this.mapError(error);
1226
1449
  }
1227
1450
  }
1228
- /** 离开当前 RTC 房间;未在房间中或引擎未初始化时不产生效果,退房失败仅记录日志。 */
1451
+ /**
1452
+ * 离开当前 RTC 房间;未在房间中或引擎未初始化时不产生效果,退房失败仅记录日志。
1453
+ */
1229
1454
  async leaveRoom() {
1230
1455
  const engine = this.lease?.engine;
1231
1456
  if (!engine || !this.isInRoom) {
@@ -1292,7 +1517,9 @@ var RtcManager = class {
1292
1517
  throw this.mapError(error);
1293
1518
  }
1294
1519
  }
1295
- /** 停止发布本地视频流;采集保持运行,本地预览不受影响。 */
1520
+ /**
1521
+ * 停止发布本地视频流;采集保持运行,本地预览不受影响。
1522
+ */
1296
1523
  async unpublishLocalVideo() {
1297
1524
  const engine = this.lease?.engine;
1298
1525
  if (!engine || !this.isCapturing) {
@@ -1322,7 +1549,9 @@ var RtcManager = class {
1322
1549
  throw this.mapError(error);
1323
1550
  }
1324
1551
  }
1325
- /** 停止发布本地音频流;麦克风采集保持运行。 */
1552
+ /**
1553
+ * 停止发布本地音频流;麦克风采集保持运行。
1554
+ */
1326
1555
  async unpublishLocalAudio() {
1327
1556
  const engine = this.lease?.engine;
1328
1557
  if (!engine || !this.isAudioCapturing) {
@@ -1413,11 +1642,15 @@ var RtcManager = class {
1413
1642
  data: encoded.buffer
1414
1643
  });
1415
1644
  }
1416
- /** 设置 RTC 事件监听器,传入空值时清除监听器。 */
1645
+ /**
1646
+ * 设置 RTC 事件监听器,传入空值时清除监听器。
1647
+ */
1417
1648
  setEventListener(listener) {
1418
1649
  this.eventListener = listener;
1419
1650
  }
1420
- /** 注册引擎事件桥接:媒体、消息及性能日志。 */
1651
+ /**
1652
+ * 注册引擎事件桥接:媒体、消息及性能日志。
1653
+ */
1421
1654
  registerEventBridge(engine) {
1422
1655
  const source = engine;
1423
1656
  const onStatistics = (stats) => {
@@ -1499,7 +1732,9 @@ var RtcManager = class {
1499
1732
  this.eventListener?.onCustomMessageReceived(event.userId, message);
1500
1733
  });
1501
1734
  }
1502
- /** 返回当前引擎实例;未初始化时抛出错误。 */
1735
+ /**
1736
+ * 返回当前引擎实例;未初始化时抛出错误。
1737
+ */
1503
1738
  requireEngine() {
1504
1739
  const engine = this.lease?.engine;
1505
1740
  if (!engine) {
@@ -1510,7 +1745,9 @@ var RtcManager = class {
1510
1745
  }
1511
1746
  return engine;
1512
1747
  }
1513
- /** 将 TRTC 错误映射为 SDK 统一错误。 */
1748
+ /**
1749
+ * 将 TRTC 错误映射为 SDK 统一错误。
1750
+ */
1514
1751
  mapError(error) {
1515
1752
  if (error instanceof XmaxError) {
1516
1753
  return error;
@@ -1528,28 +1765,16 @@ var RtcManager = class {
1528
1765
  }
1529
1766
  };
1530
1767
 
1531
- // src/Foundation/Media/Camera/CameraExposureGate.ts
1532
- var SAMPLE_INTERVAL_MS = 80;
1533
- var STABLE_DURATION_MS = 400;
1534
- var TIMEOUT_MS = 5e3;
1535
- var CameraExposureGate = class {
1536
- constructor() {
1537
- __publicField(this, "baseline");
1538
- __publicField(this, "lastTime");
1539
- __publicField(this, "count", 0);
1540
- }
1541
- reset() {
1542
- this.baseline = void 0;
1543
- this.lastTime = void 0;
1544
- this.count = 0;
1545
- }
1546
- /** 只接受新的采样帧;中央区域需足够亮,并在至少 400 ms 内保持稳定。 */
1547
- accept(pixels, time) {
1768
+ // src/Foundation/Media/Camera/CameraFrameValidator.ts
1769
+ var FALLBACK_POLL_INTERVAL_MS = 16;
1770
+ var TIMEOUT_MS = 2e3;
1771
+ var CameraFrameValidator = class {
1772
+ /**
1773
+ * 单帧中央区域亮度达标即放行,不等待连续帧或曝光稳定。
1774
+ */
1775
+ accept(pixels) {
1548
1776
  const size = pixels.length / 4;
1549
- if (!size || !Number.isInteger(size)) {
1550
- this.reset();
1551
- return false;
1552
- }
1777
+ if (!size || !Number.isInteger(size)) return false;
1553
1778
  const histogram = new Uint32Array(256);
1554
1779
  let total = 0;
1555
1780
  let dark = 0;
@@ -1566,22 +1791,10 @@ var CameraExposureGate = class {
1566
1791
  if (accumulated >= size / 2) break;
1567
1792
  }
1568
1793
  const mean = total / size;
1569
- if (mean < 24 || median < 18 || dark / size > 0.75) {
1570
- this.reset();
1571
- return false;
1572
- }
1573
- const baseline = this.baseline;
1574
- const stable = baseline && this.lastTime !== void 0 && time > this.lastTime && time - this.lastTime <= 250 && Math.abs(mean - baseline.mean) <= Math.max(5, baseline.mean * 0.1) && Math.abs(median - baseline.median) <= Math.max(5, baseline.median * 0.1);
1575
- if (!stable) {
1576
- this.baseline = { mean, median, time };
1577
- this.count = 0;
1578
- }
1579
- this.lastTime = time;
1580
- this.count++;
1581
- return this.count >= 5 && time - this.baseline.time >= STABLE_DURATION_MS;
1794
+ return mean >= 24 && median >= 18 && dark / size <= 0.75;
1582
1795
  }
1583
1796
  };
1584
- function waitForCameraExposure(track, signal) {
1797
+ function waitForValidCameraFrame(track, signal) {
1585
1798
  return new Promise((resolve, reject) => {
1586
1799
  let video;
1587
1800
  let timeout;
@@ -1589,8 +1802,8 @@ function waitForCameraExposure(track, signal) {
1589
1802
  let frameCallback;
1590
1803
  let finished = false;
1591
1804
  const started = performance.now();
1592
- const gate = new CameraExposureGate();
1593
- const finish = (error) => {
1805
+ const gate = new CameraFrameValidator();
1806
+ const finish = (result = "valid") => {
1594
1807
  if (finished) return;
1595
1808
  finished = true;
1596
1809
  clearTimeout(timeout);
@@ -1603,12 +1816,19 @@ function waitForCameraExposure(track, signal) {
1603
1816
  }
1604
1817
  signal.removeEventListener("abort", abort);
1605
1818
  track.removeEventListener("ended", ended);
1606
- if (error) {
1607
- if (error.code !== "CANCELLED" /* cancelled */) XmaxLogger.media.warning(() => error.message);
1608
- reject(error);
1819
+ if (result instanceof XmaxError) {
1820
+ if (result.code !== "CANCELLED" /* cancelled */) XmaxLogger.media.warning(() => result.message);
1821
+ reject(result);
1609
1822
  } else {
1610
- XmaxLogger.media.info(() => `\u76F8\u673A\u66DD\u5149\u68C0\u67E5\u901A\u8FC7 (Camera Exposure Ready)
1823
+ if (result === "timeout") {
1824
+ XmaxLogger.media.warning(() => XmaxLogger.localized(
1825
+ "\u76F8\u673A\u4EAE\u5EA6\u68C0\u67E5\u8D85\u65F6\uFF0C\u6309\u5F53\u524D\u753B\u9762\u7EE7\u7EED\u751F\u6210 (Camera Frame Validation Timed Out; Continuing)\n\u2514\u2500 \u672A\u83B7\u5F97\u4EAE\u5EA6\u5408\u683C\u5E27\uFF0C\u4E0D\u518D\u7B49\u5F85\u3002",
1826
+ "Camera Frame Validation Timed Out; Continuing\n\u2514\u2500 No sufficiently bright frame received; proceeding without further waiting."
1827
+ ));
1828
+ } else {
1829
+ XmaxLogger.media.info(() => `\u76F8\u673A\u66DD\u5149\u68C0\u67E5\u901A\u8FC7 (Camera Exposure Ready)
1611
1830
  \u2514\u2500 ${Math.round(performance.now() - started)} ms`);
1831
+ }
1612
1832
  resolve();
1613
1833
  }
1614
1834
  };
@@ -1639,21 +1859,17 @@ function waitForCameraExposure(track, signal) {
1639
1859
  video.srcObject = new MediaStream([track]);
1640
1860
  document.body.appendChild(video);
1641
1861
  const source = video;
1642
- let lastSample = -Infinity;
1643
1862
  let lastFrameTime = -Infinity;
1644
- const sample = (now, mediaTime) => {
1645
- if (now - lastSample < SAMPLE_INTERVAL_MS) return;
1863
+ const sample = (mediaTime) => {
1646
1864
  if (source.readyState < 2 || !source.videoWidth || !source.videoHeight || track.muted || !track.enabled) {
1647
- gate.reset();
1648
1865
  return;
1649
1866
  }
1650
1867
  if (mediaTime === lastFrameTime) return;
1651
- lastSample = now;
1652
1868
  lastFrameTime = mediaTime;
1653
1869
  const width = source.videoWidth;
1654
1870
  const height = source.videoHeight;
1655
1871
  context.drawImage(source, width * 0.2, height * 0.2, width * 0.6, height * 0.6, 0, 0, 64, 48);
1656
- if (gate.accept(context.getImageData(0, 0, 64, 48).data, now)) finish();
1872
+ if (gate.accept(context.getImageData(0, 0, 64, 48).data)) finish();
1657
1873
  };
1658
1874
  const fail = (error) => finish(new XmaxError(
1659
1875
  "MEDIA_ERROR" /* mediaError */,
@@ -1662,10 +1878,10 @@ function waitForCameraExposure(track, signal) {
1662
1878
  const schedule = () => {
1663
1879
  if (finished) return;
1664
1880
  if (typeof source.requestVideoFrameCallback === "function") {
1665
- frameCallback = source.requestVideoFrameCallback((now, metadata) => {
1881
+ frameCallback = source.requestVideoFrameCallback((_now, metadata) => {
1666
1882
  frameCallback = void 0;
1667
1883
  try {
1668
- sample(now, metadata.mediaTime);
1884
+ sample(metadata.mediaTime);
1669
1885
  schedule();
1670
1886
  } catch (error) {
1671
1887
  fail(error);
@@ -1675,22 +1891,16 @@ function waitForCameraExposure(track, signal) {
1675
1891
  poll = setTimeout(() => {
1676
1892
  try {
1677
1893
  const frameTime = typeof source.getVideoPlaybackQuality === "function" ? source.getVideoPlaybackQuality().totalVideoFrames : source.currentTime;
1678
- sample(performance.now(), frameTime);
1894
+ sample(frameTime);
1679
1895
  schedule();
1680
1896
  } catch (error) {
1681
1897
  fail(error);
1682
1898
  }
1683
- }, SAMPLE_INTERVAL_MS);
1899
+ }, FALLBACK_POLL_INTERVAL_MS);
1684
1900
  }
1685
1901
  };
1686
- timeout = setTimeout(() => finish(new XmaxError(
1687
- "CAMERA_EXPOSURE_TIMEOUT" /* cameraExposureTimeout */,
1688
- XmaxLogger.localized(
1689
- "\u76F8\u673A\u753B\u9762\u8FC7\u6697\u6216\u5C1A\u672A\u7A33\u5B9A\uFF0C\u672A\u53D1\u5E03\u89C6\u9891\u3002\u8BF7\u6539\u5584\u5149\u7167\u540E\u91CD\u8BD5\u3002",
1690
- "Camera frames are too dark or not stable. Video was not published. Improve lighting and retry."
1691
- )
1692
- )), TIMEOUT_MS);
1693
- XmaxLogger.media.info(() => "\u7B49\u5F85\u76F8\u673A\u66DD\u5149\u7A33\u5B9A (Waiting for Camera Exposure)");
1902
+ timeout = setTimeout(() => finish("timeout"), TIMEOUT_MS);
1903
+ XmaxLogger.media.info(() => "\u7B49\u5F85\u9996\u5F20\u4EAE\u5EA6\u5408\u683C\u5E27 (Waiting for the First Sufficiently Bright Frame)");
1694
1904
  void source.play().then(schedule, fail);
1695
1905
  } catch (error) {
1696
1906
  finish(new XmaxError(
@@ -1760,19 +1970,31 @@ var PermissionManager = class {
1760
1970
 
1761
1971
  // src/Service/Realtime/VideoRenderBinding.ts
1762
1972
  var _VideoRenderRegistry = class _VideoRenderRegistry {
1763
- /** @internal */
1973
+ /**
1974
+ * 注册轨道的自定义渲染绑定,覆盖该轨道已有的绑定。
1975
+ * @internal
1976
+ */
1764
1977
  static register(track, binding) {
1765
1978
  _VideoRenderRegistry.bindings.set(track, binding);
1766
1979
  }
1767
- /** @internal */
1980
+ /**
1981
+ * 移除轨道的渲染绑定,不执行视图解绑或资源释放。
1982
+ * @internal
1983
+ */
1768
1984
  static unregister(track) {
1769
1985
  _VideoRenderRegistry.bindings.delete(track);
1770
1986
  }
1771
- /** @internal */
1987
+ /**
1988
+ * 查询轨道的自定义渲染绑定;未注册时返回 undefined。
1989
+ * @internal
1990
+ */
1772
1991
  static binding(forTrack) {
1773
1992
  return _VideoRenderRegistry.bindings.get(forTrack);
1774
1993
  }
1775
1994
  };
1995
+ /**
1996
+ * 轨道渲染绑定索引
1997
+ */
1776
1998
  __publicField(_VideoRenderRegistry, "bindings", /* @__PURE__ */ new WeakMap());
1777
1999
  var VideoRenderRegistry = _VideoRenderRegistry;
1778
2000
 
@@ -1786,12 +2008,20 @@ var RealtimeVideoTrack = class {
1786
2008
  * @param init.position 本地相机轨道的初始摄像头位置。
1787
2009
  */
1788
2010
  constructor(init) {
1789
- // 轨道信息
1790
- /** 视频轨道标识。 */
2011
+ /**
2012
+ * 轨道信息
2013
+ */
2014
+ /**
2015
+ * 视频轨道标识。
2016
+ */
1791
2017
  __publicField(this, "id");
1792
- // 动态元数据
2018
+ /**
2019
+ * 动态元数据
2020
+ */
1793
2021
  __publicField(this, "metadata");
1794
- // 平台资源
2022
+ /**
2023
+ * 平台资源
2024
+ */
1795
2025
  /**
1796
2026
  * SDK 内部:轨道对应的 Web 媒体轨。
1797
2027
  * 本地轨道在采集启动后可用;远端轨道在订阅成功后可用。
@@ -1803,19 +2033,27 @@ var RealtimeVideoTrack = class {
1803
2033
  position: init.position
1804
2034
  };
1805
2035
  }
1806
- /** 当前视频轨道使用的视频格式。 */
2036
+ /**
2037
+ * 当前视频轨道使用的视频格式。
2038
+ */
1807
2039
  get videoFormat() {
1808
2040
  return this.metadata.videoFormat;
1809
2041
  }
1810
- /** 本地相机轨道当前使用的摄像头位置。 */
2042
+ /**
2043
+ * 本地相机轨道当前使用的摄像头位置。
2044
+ */
1811
2045
  get position() {
1812
2046
  return this.metadata.position;
1813
2047
  }
1814
- /** 更新轨道视频格式。 @internal */
2048
+ /**
2049
+ * 更新轨道视频格式。 @internal
2050
+ */
1815
2051
  updateVideoFormat(videoFormat) {
1816
2052
  this.metadata.videoFormat = videoFormat;
1817
2053
  }
1818
- /** 更新轨道摄像头位置。 @internal */
2054
+ /**
2055
+ * 更新轨道摄像头位置。 @internal
2056
+ */
1819
2057
  updatePosition(position) {
1820
2058
  this.metadata.position = position;
1821
2059
  }
@@ -1830,9 +2068,16 @@ var RealtimeMediaStream = class {
1830
2068
  * @param init.videoTrack 媒体流包含的视频轨道。
1831
2069
  */
1832
2070
  constructor(init) {
1833
- /** 媒体流标识。 */
2071
+ /**
2072
+ * 流标识与轨道
2073
+ */
2074
+ /**
2075
+ * 媒体流标识。
2076
+ */
1834
2077
  __publicField(this, "id");
1835
- /** 媒体流包含的视频轨道。 */
2078
+ /**
2079
+ * 媒体流包含的视频轨道。
2080
+ */
1836
2081
  __publicField(this, "videoTrack");
1837
2082
  this.id = init.id;
1838
2083
  this.videoTrack = init.videoTrack;
@@ -1850,47 +2095,77 @@ var _CameraController = class _CameraController {
1850
2095
  * @param options.errorListener 运行期错误回调。
1851
2096
  */
1852
2097
  constructor(options) {
1853
- // 基础层组件
2098
+ /**
2099
+ * 基础层组件
2100
+ */
1854
2101
  __publicField(this, "permissionManager");
1855
2102
  __publicField(this, "rtcManager");
1856
- // 服务层组件
2103
+ /**
2104
+ * 服务层组件
2105
+ */
1857
2106
  __publicField(this, "mediaService");
1858
- // 事件监听
2107
+ /**
2108
+ * 事件监听
2109
+ */
1859
2110
  __publicField(this, "errorListener");
1860
2111
  __publicField(this, "previewReadyHandler");
1861
- // 本地资源
2112
+ /**
2113
+ * 本地资源
2114
+ */
1862
2115
  __publicField(this, "activeTrack");
1863
2116
  __publicField(this, "previewStream");
1864
2117
  __publicField(this, "previewMirrorApplier");
1865
- // 预览状态
2118
+ /**
2119
+ * 预览状态
2120
+ */
1866
2121
  __publicField(this, "hasCapturedFrame", false);
1867
2122
  __publicField(this, "isPreviewAttached", false);
1868
- // 麦克风配置
2123
+ /**
2124
+ * 当前相机流配置
2125
+ */
1869
2126
  __publicField(this, "storedUseMicrophone", false);
2127
+ __publicField(this, "storedFrameValidationEnabled", true);
1870
2128
  this.permissionManager = options?.permissionManager ?? new PermissionManager();
1871
2129
  this.rtcManager = options?.rtcManager ?? new RtcManager();
1872
2130
  this.mediaService = options?.mediaService ?? new MediaService();
1873
2131
  this.errorListener = options?.errorListener ?? (() => {
1874
2132
  });
1875
2133
  }
1876
- /** 当前活动的本地相机视频轨道;尚未创建时为空。 */
2134
+ /**
2135
+ * 当前活动的本地相机视频轨道;尚未创建时为空。
2136
+ */
1877
2137
  get currentTrack() {
1878
2138
  return this.activeTrack;
1879
2139
  }
1880
- /** 当前相机流是否配置为使用麦克风。 */
2140
+ /**
2141
+ * 当前相机流是否配置为使用麦克风。
2142
+ */
1881
2143
  get useMicrophone() {
1882
2144
  return this.activeTrack !== void 0 && this.storedUseMicrophone;
1883
2145
  }
1884
- async waitUntilExposureReady(signal) {
2146
+ /**
2147
+ * 当前活动相机流是否启用发布前亮度检测;没有活动流时为 false。
2148
+ */
2149
+ get isFrameValidationEnabled() {
2150
+ return this.activeTrack !== void 0 && this.storedFrameValidationEnabled;
2151
+ }
2152
+ /**
2153
+ * 等待当前轨道首张合格帧或超时放行;禁用时跳过,取消或轨道被替换时拒绝。
2154
+ */
2155
+ async waitForValidCameraFrame(signal) {
1885
2156
  const track = this.activeTrack;
1886
2157
  const mediaTrack = track?.mediaStreamTrack;
1887
2158
  if (!mediaTrack) throw new XmaxError("MEDIA_ERROR" /* mediaError */, "Camera capture is not running");
1888
- await waitForCameraExposure(mediaTrack, signal);
2159
+ if (signal.aborted) throw new XmaxError("CANCELLED" /* cancelled */, "Camera frame validation cancelled");
2160
+ if (!this.isFrameValidationEnabled) return;
2161
+ await waitForValidCameraFrame(mediaTrack, signal);
1889
2162
  if (this.activeTrack !== track || track.mediaStreamTrack !== mediaTrack) {
1890
2163
  throw new XmaxError("CANCELLED" /* cancelled */, "Camera track changed during exposure check");
1891
2164
  }
1892
2165
  }
1893
- /** 设置当前相机流的一次性内部就绪处理;条件为已收到有效帧且预览已绑定。 */
2166
+ /**
2167
+ * 设置当前相机流的一次性内部就绪处理;条件为已收到有效帧且预览已绑定。
2168
+ */
1894
2169
  setPreviewReadyHandler(handler) {
1895
2170
  this.previewReadyHandler = handler;
1896
2171
  const track = this.activeTrack;
@@ -1904,6 +2179,7 @@ var _CameraController = class _CameraController {
1904
2179
  * @param options.videoFormat 期望的输出尺寸、帧率和编码配置;尺寸按模型规则调整。
1905
2180
  * @param options.position 首次启动时使用的摄像头位置。
1906
2181
  * @param options.useMicrophone 是否申请麦克风权限并允许实时连接时启动音频采集。
2182
+ * @param options.enableFrameValidation 是否在发布前检测帧亮度,默认 true;false 跳过检测及其等待。
1907
2183
  * @returns 包含本地相机视频轨道的媒体流。
1908
2184
  * @throws 已有活动相机流、格式无效、权限不足或采集启动失败时抛出错误。
1909
2185
  */
@@ -1928,6 +2204,7 @@ var _CameraController = class _CameraController {
1928
2204
  await this.rtcManager.initialize();
1929
2205
  this.activeTrack = track;
1930
2206
  this.storedUseMicrophone = options.useMicrophone;
2207
+ this.storedFrameValidationEnabled = options.enableFrameValidation ?? true;
1931
2208
  this.hasCapturedFrame = false;
1932
2209
  this.isPreviewAttached = false;
1933
2210
  this.registerPreviewBinding(track);
@@ -1946,7 +2223,9 @@ var _CameraController = class _CameraController {
1946
2223
  throw XmaxError.from(error);
1947
2224
  }
1948
2225
  }
1949
- /** 停止相机采集并销毁 RTC 引擎,释放当前轨道及本地预览资源。 */
2226
+ /**
2227
+ * 停止相机采集并销毁 RTC 引擎,释放当前轨道及本地预览资源。
2228
+ */
1950
2229
  async stopLocalCameraStream() {
1951
2230
  const track = this.activeTrack;
1952
2231
  this.activeTrack = void 0;
@@ -1954,6 +2233,7 @@ var _CameraController = class _CameraController {
1954
2233
  this.previewStream = void 0;
1955
2234
  this.previewMirrorApplier = void 0;
1956
2235
  this.storedUseMicrophone = false;
2236
+ this.storedFrameValidationEnabled = true;
1957
2237
  this.hasCapturedFrame = false;
1958
2238
  this.isPreviewAttached = false;
1959
2239
  await this.rtcManager.stopCameraCapture();
@@ -1996,7 +2276,9 @@ var _CameraController = class _CameraController {
1996
2276
  this.observeMediaTrack(track, mediaTrack);
1997
2277
  return new RealtimeMediaStream({ id: "stream0" /* local */, videoTrack: track });
1998
2278
  }
1999
- /** 为相机轨道注册预览渲染绑定:attach 时挂流、镜像并标记预览已绑定。 */
2279
+ /**
2280
+ * 为相机轨道注册预览渲染绑定:attach 时挂流、镜像并标记预览已绑定。
2281
+ */
2000
2282
  registerPreviewBinding(track) {
2001
2283
  VideoRenderRegistry.register(track, {
2002
2284
  attachHandler: (view) => {
@@ -2022,7 +2304,9 @@ var _CameraController = class _CameraController {
2022
2304
  }
2023
2305
  });
2024
2306
  }
2025
- /** 监听媒体轨首帧与意外结束:首帧推进预览就绪,意外结束上报错误。 */
2307
+ /**
2308
+ * 监听媒体轨首帧与意外结束:首帧推进预览就绪,意外结束上报错误。
2309
+ */
2026
2310
  observeMediaTrack(track, mediaTrack) {
2027
2311
  const isCurrent = () => this.activeTrack === track && track.mediaStreamTrack === mediaTrack;
2028
2312
  if (mediaTrack.muted) {
@@ -2053,7 +2337,9 @@ var _CameraController = class _CameraController {
2053
2337
  );
2054
2338
  });
2055
2339
  }
2056
- /** 满足"已收到有效帧且预览已绑定"时触发一次性就绪回调。 */
2340
+ /**
2341
+ * 满足"已收到有效帧且预览已绑定"时触发一次性就绪回调。
2342
+ */
2057
2343
  notifyPreviewReady(track) {
2058
2344
  if (this.activeTrack !== track || !this.hasCapturedFrame || !this.isPreviewAttached || !this.previewReadyHandler) {
2059
2345
  return;
@@ -2062,7 +2348,9 @@ var _CameraController = class _CameraController {
2062
2348
  this.previewReadyHandler = void 0;
2063
2349
  handler(() => this.activeTrack === track);
2064
2350
  }
2065
- /** 按模型输入规则校验并解析目标视频规格。 */
2351
+ /**
2352
+ * 按模型输入规则校验并解析目标视频规格。
2353
+ */
2066
2354
  resolveVideoFormat(videoFormat) {
2067
2355
  videoFormat.validate();
2068
2356
  const targetSize = this.mediaService.resolveModelInputSize({
@@ -2074,7 +2362,9 @@ var _CameraController = class _CameraController {
2074
2362
  return resolvedFormat;
2075
2363
  }
2076
2364
  };
2077
- // 轨道标识
2365
+ /**
2366
+ * 轨道标识
2367
+ */
2078
2368
  __publicField(_CameraController, "localVideoTrackID", "video0");
2079
2369
  var CameraController = _CameraController;
2080
2370
 
@@ -2090,18 +2380,34 @@ var RealtimeSession = class {
2090
2380
  * @param init.closeReason 会话关闭原因。
2091
2381
  */
2092
2382
  constructor(init) {
2093
- // 会话信息
2094
- /** 会话标识(sessionUid)。 */
2383
+ /**
2384
+ * 会话信息
2385
+ */
2386
+ /**
2387
+ * 会话标识(sessionUid)。
2388
+ */
2095
2389
  __publicField(this, "id");
2096
- /** 业务用户标识。 */
2390
+ /**
2391
+ * 业务用户标识。
2392
+ */
2097
2393
  __publicField(this, "userID");
2098
- // 运行状态
2099
- /** 会话状态(如 ACTIVE);响应未携带时为空。 */
2394
+ /**
2395
+ * 运行状态
2396
+ */
2397
+ /**
2398
+ * 会话状态(如 ACTIVE);响应未携带时为空。
2399
+ */
2100
2400
  __publicField(this, "status");
2101
- /** 会话关闭原因。 */
2401
+ /**
2402
+ * 会话关闭原因。
2403
+ */
2102
2404
  __publicField(this, "closeReason");
2103
- // RTC 连接
2104
- /** RTC 连接参数;心跳等响应未携带完整参数时为空。 */
2405
+ /**
2406
+ * RTC 连接
2407
+ */
2408
+ /**
2409
+ * RTC 连接参数;心跳等响应未携带完整参数时为空。
2410
+ */
2105
2411
  __publicField(this, "connection");
2106
2412
  this.id = init.id;
2107
2413
  this.userID = init.userID;
@@ -2125,22 +2431,42 @@ var RealtimeSessionConnection = class {
2125
2431
  * @param init.botID 房间内生成 Bot 的用户标识。
2126
2432
  */
2127
2433
  constructor(init) {
2128
- // RTC 提供方
2129
- /** RTC 提供方标识(当前仅支持 `trtc`)。 */
2434
+ /**
2435
+ * RTC 提供方
2436
+ */
2437
+ /**
2438
+ * RTC 提供方标识(当前仅支持 `trtc`)。
2439
+ */
2130
2440
  __publicField(this, "provider");
2131
- // 进房参数
2132
- /** TRTC 房间号(字符串房间号)。 */
2441
+ /**
2442
+ * 进房参数
2443
+ */
2444
+ /**
2445
+ * TRTC 房间号(字符串房间号)。
2446
+ */
2133
2447
  __publicField(this, "roomID");
2134
- /** TRTC SDKAppID(按字符串处理)。 */
2448
+ /**
2449
+ * TRTC SDKAppID(按字符串处理)。
2450
+ */
2135
2451
  __publicField(this, "sdkAppID");
2136
- /** TRTC 登录身份(`rtc_user_id`,不是业务 `user_id`)。 */
2452
+ /**
2453
+ * TRTC 登录身份(`rtc_user_id`,不是业务 `user_id`)。
2454
+ */
2137
2455
  __publicField(this, "userID");
2138
- /** TRTC 登录签名。 */
2456
+ /**
2457
+ * TRTC 登录签名。
2458
+ */
2139
2459
  __publicField(this, "userSig");
2140
- /** TRTC 进房鉴权字段(字符串房间号对应的 privateMapKey)。 */
2460
+ /**
2461
+ * TRTC 进房鉴权字段(字符串房间号对应的 privateMapKey)。
2462
+ */
2141
2463
  __publicField(this, "privateMapKey");
2142
- // 房间成员
2143
- /** 房间内生成 Bot 的用户标识,用于匹配远端结果流。 */
2464
+ /**
2465
+ * 房间成员
2466
+ */
2467
+ /**
2468
+ * 房间内生成 Bot 的用户标识,用于匹配远端结果流。
2469
+ */
2144
2470
  __publicField(this, "botID");
2145
2471
  this.provider = init.provider;
2146
2472
  this.roomID = init.roomID;
@@ -2162,34 +2488,48 @@ var RealtimeSessionService = class _RealtimeSessionService {
2162
2488
  * @param options.sleep 心跳等待实现(可替换,测试用)。
2163
2489
  */
2164
2490
  constructor(options) {
2165
- // 服务层组件
2491
+ /**
2492
+ * 服务层组件
2493
+ */
2166
2494
  __publicField(this, "apiService");
2167
- // 心跳配置
2495
+ /**
2496
+ * 心跳配置
2497
+ */
2168
2498
  __publicField(this, "heartbeatIntervalMs");
2169
2499
  __publicField(this, "sleep");
2170
- // 运行状态
2500
+ /**
2501
+ * 运行状态
2502
+ */
2171
2503
  __publicField(this, "heartbeatVersion", 0);
2172
2504
  this.apiService = options.apiService;
2173
2505
  this.heartbeatIntervalMs = options.heartbeatIntervalMs ?? 1e4;
2174
2506
  this.sleep = options.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
2175
2507
  }
2176
- /** 创建实时会话并返回 RTC 连接信息。 */
2508
+ /**
2509
+ * 创建实时会话并返回 RTC 连接信息。
2510
+ */
2177
2511
  async createSession(model) {
2178
2512
  const payload = await this.apiService.post("/session", {
2179
2513
  model
2180
2514
  });
2181
2515
  return _RealtimeSessionService.makeSession(payload, true);
2182
2516
  }
2183
- /** 启动指定会话的周期心跳;重复启动会替换当前心跳。 */
2517
+ /**
2518
+ * 启动指定会话的周期心跳;重复启动会替换当前心跳。
2519
+ */
2184
2520
  startHeartbeat(sessionID, handlers) {
2185
2521
  const version = ++this.heartbeatVersion;
2186
2522
  void this.runHeartbeat(version, sessionID, handlers);
2187
2523
  }
2188
- /** 停止当前心跳;已经失效的迟到结果不会再触发回调。 */
2524
+ /**
2525
+ * 停止当前心跳;已经失效的迟到结果不会再触发回调。
2526
+ */
2189
2527
  stopHeartbeat() {
2190
2528
  this.heartbeatVersion += 1;
2191
2529
  }
2192
- /** 关闭指定实时会话。 */
2530
+ /**
2531
+ * 关闭指定实时会话。
2532
+ */
2193
2533
  async closeSession(sessionID) {
2194
2534
  try {
2195
2535
  await this.apiService.delete(
@@ -2199,7 +2539,9 @@ var RealtimeSessionService = class _RealtimeSessionService {
2199
2539
  throw XmaxError.from(error);
2200
2540
  }
2201
2541
  }
2202
- /** 周期心跳:等待 → 请求 → 校验会话活跃并回调刷新;失败时失效版本并回调。 */
2542
+ /**
2543
+ * 周期心跳:等待 → 请求 → 校验会话活跃并回调刷新;失败时失效版本并回调。
2544
+ */
2203
2545
  async runHeartbeat(version, sessionID, handlers) {
2204
2546
  while (this.heartbeatVersion === version) {
2205
2547
  await this.sleep(this.heartbeatIntervalMs);
@@ -2223,14 +2565,18 @@ var RealtimeSessionService = class _RealtimeSessionService {
2223
2565
  }
2224
2566
  }
2225
2567
  }
2226
- /** 发送一次心跳请求并解析会话数据(不要求携带完整连接参数)。 */
2568
+ /**
2569
+ * 发送一次心跳请求并解析会话数据(不要求携带完整连接参数)。
2570
+ */
2227
2571
  async heartbeatSession(sessionID) {
2228
2572
  const payload = await this.apiService.put(
2229
2573
  `/session/${sessionID}/heartbeat`
2230
2574
  );
2231
2575
  return _RealtimeSessionService.makeSession(payload, false);
2232
2576
  }
2233
- /** 校验会话仍处于活跃状态;状态明确非 ACTIVE 时抛出会话错误。 */
2577
+ /**
2578
+ * 校验会话仍处于活跃状态;状态明确非 ACTIVE 时抛出会话错误。
2579
+ */
2234
2580
  static ensureSessionActive(session) {
2235
2581
  if (!session.status || session.status === "ACTIVE") {
2236
2582
  return;
@@ -2240,7 +2586,9 @@ var RealtimeSessionService = class _RealtimeSessionService {
2240
2586
  session.closeReason ?? `Session is no longer active: ${session.status}`
2241
2587
  );
2242
2588
  }
2243
- /** 由会话接口数据构造会话模型;`requiresConnection` 要求携带完整 RTC 连接参数。 */
2589
+ /**
2590
+ * 由会话接口数据构造会话模型;`requiresConnection` 要求携带完整 RTC 连接参数。
2591
+ */
2244
2592
  static makeSession(payload, requiresConnection) {
2245
2593
  const sessionID = _RealtimeSessionService.nonEmpty(payload.sessionUid);
2246
2594
  if (!sessionID) {
@@ -2261,7 +2609,9 @@ var RealtimeSessionService = class _RealtimeSessionService {
2261
2609
  closeReason: _RealtimeSessionService.nonEmpty(payload.closeReason)
2262
2610
  });
2263
2611
  }
2264
- /** 解析 `modelExtra` 中的 TRTC 连接参数;字段不完整或提供方不支持时返回空。 */
2612
+ /**
2613
+ * 解析 `modelExtra` 中的 TRTC 连接参数;字段不完整或提供方不支持时返回空。
2614
+ */
2265
2615
  static makeConnection(modelExtra) {
2266
2616
  const payload = _RealtimeSessionService.decodeConnectionPayload(modelExtra);
2267
2617
  if (!payload) {
@@ -2290,7 +2640,9 @@ var RealtimeSessionService = class _RealtimeSessionService {
2290
2640
  botID: _RealtimeSessionService.nonEmpty(payload.rtc_bot_id)
2291
2641
  });
2292
2642
  }
2293
- /** `modelExtra` 可能为对象或 JSON 字符串,统一解析为连接参数字段。 */
2643
+ /**
2644
+ * `modelExtra` 可能为对象或 JSON 字符串,统一解析为连接参数字段。
2645
+ */
2294
2646
  static decodeConnectionPayload(modelExtra) {
2295
2647
  if (typeof modelExtra === "string") {
2296
2648
  try {
@@ -2304,10 +2656,12 @@ var RealtimeSessionService = class _RealtimeSessionService {
2304
2656
  }
2305
2657
  return void 0;
2306
2658
  }
2307
- /** 归一化可选字符串:去除首尾空白后为空时返回 `undefined`。 */
2659
+ /**
2660
+ * 归一化可选字符串:去除首尾空白后为空时返回 `undefined`。
2661
+ */
2308
2662
  static nonEmpty(value) {
2309
2663
  const normalized = value?.trim();
2310
- return normalized && normalized.length > 0 ? normalized : void 0;
2664
+ return normalized || void 0;
2311
2665
  }
2312
2666
  };
2313
2667
 
@@ -2325,6 +2679,9 @@ var RealtimeConnectionState = /* @__PURE__ */ ((RealtimeConnectionState2) => {
2325
2679
  var RealtimeReason = {
2326
2680
  normal: { kind: "normal" },
2327
2681
  orientationChanged: { kind: "orientationChanged" },
2682
+ /**
2683
+ * 将 SDK 错误包装为状态切换原因,保留错误码和原始说明。
2684
+ */
2328
2685
  failure(error) {
2329
2686
  return { kind: "failure", error };
2330
2687
  }
@@ -2339,13 +2696,30 @@ var RealtimeState = class {
2339
2696
  * @param init.reason 状态变化原因,默认无。
2340
2697
  */
2341
2698
  constructor(init) {
2342
- /** 当前连接生命周期状态。 */
2699
+ /**
2700
+ * 连接生命周期
2701
+ */
2702
+ /**
2703
+ * 当前连接生命周期状态。
2704
+ */
2343
2705
  __publicField(this, "connectionState");
2344
- /** 当前或最近一次实时 Session 标识。 */
2706
+ /**
2707
+ * 业务标识
2708
+ */
2709
+ /**
2710
+ * 当前或最近一次实时 Session 标识。
2711
+ */
2345
2712
  __publicField(this, "sessionID");
2346
- /** 当前生成任务标识。 */
2713
+ /**
2714
+ * 当前生成任务标识。
2715
+ */
2347
2716
  __publicField(this, "taskID");
2348
- /** 进入当前状态的原因;正常开始新的操作时清空。 */
2717
+ /**
2718
+ * 状态变化原因
2719
+ */
2720
+ /**
2721
+ * 进入当前状态的原因;正常开始新的操作时清空。
2722
+ */
2349
2723
  __publicField(this, "reason");
2350
2724
  this.connectionState = init.connectionState;
2351
2725
  this.sessionID = init.sessionID;
@@ -2371,15 +2745,23 @@ var RemoteStream = class {
2371
2745
  * @param init.userID 远端流所属用户标识。
2372
2746
  */
2373
2747
  constructor(init) {
2374
- // 流标识
2375
- /** 远端流所在房间号。 */
2748
+ /**
2749
+ * 流标识
2750
+ */
2751
+ /**
2752
+ * 远端流所在房间号。
2753
+ */
2376
2754
  __publicField(this, "roomID");
2377
- /** 远端流所属用户标识。 */
2755
+ /**
2756
+ * 远端流所属用户标识。
2757
+ */
2378
2758
  __publicField(this, "userID");
2379
2759
  this.roomID = init.roomID;
2380
2760
  this.userID = init.userID;
2381
2761
  }
2382
- /** 跨房间唯一的远端流键。 */
2762
+ /**
2763
+ * 跨房间唯一的远端流键。
2764
+ */
2383
2765
  get key() {
2384
2766
  return `${this.roomID}:${this.userID}`;
2385
2767
  }
@@ -2397,16 +2779,28 @@ var RoomJoinConfiguration = class {
2397
2779
  * @param init.privateMapKey TRTC 进房鉴权字段。
2398
2780
  */
2399
2781
  constructor(init) {
2400
- // 进房参数
2401
- /** TRTC 房间号(字符串房间号)。 */
2782
+ /**
2783
+ * 进房参数
2784
+ */
2785
+ /**
2786
+ * TRTC 房间号(字符串房间号)。
2787
+ */
2402
2788
  __publicField(this, "roomID");
2403
- /** TRTC 登录身份(`rtc_user_id`)。 */
2789
+ /**
2790
+ * TRTC 登录身份(`rtc_user_id`)。
2791
+ */
2404
2792
  __publicField(this, "userID");
2405
- /** TRTC SDKAppID(字符串形式,进房时转换为数值)。 */
2793
+ /**
2794
+ * TRTC SDKAppID(字符串形式,进房时转换为数值)。
2795
+ */
2406
2796
  __publicField(this, "sdkAppID");
2407
- /** TRTC 登录签名。 */
2797
+ /**
2798
+ * TRTC 登录签名。
2799
+ */
2408
2800
  __publicField(this, "userSig");
2409
- /** TRTC 进房鉴权字段(字符串房间号对应的 privateMapKey)。 */
2801
+ /**
2802
+ * TRTC 进房鉴权字段(字符串房间号对应的 privateMapKey)。
2803
+ */
2410
2804
  __publicField(this, "privateMapKey");
2411
2805
  this.roomID = init.roomID;
2412
2806
  this.userID = init.userID;
@@ -2418,7 +2812,9 @@ var RoomJoinConfiguration = class {
2418
2812
 
2419
2813
  // src/Stream/Room/RoomEvent.ts
2420
2814
  var RoomEvent = class _RoomEvent {
2421
- /** 生成开始信令。 */
2815
+ /**
2816
+ * 生成开始信令。
2817
+ */
2422
2818
  static start(options) {
2423
2819
  return _RoomEvent.encode({
2424
2820
  event: "start",
@@ -2427,7 +2823,9 @@ var RoomEvent = class _RoomEvent {
2427
2823
  uid: options.taskID
2428
2824
  });
2429
2825
  }
2430
- /** 生成条件变更信令。 */
2826
+ /**
2827
+ * 生成条件变更信令。
2828
+ */
2431
2829
  static changeCondition(options) {
2432
2830
  return _RoomEvent.encode({
2433
2831
  event: "change_condition",
@@ -2436,7 +2834,9 @@ var RoomEvent = class _RoomEvent {
2436
2834
  uid: options.taskID
2437
2835
  });
2438
2836
  }
2439
- /** 回传尺寸调整信令。 */
2837
+ /**
2838
+ * 回传尺寸调整信令。
2839
+ */
2440
2840
  static changeTargetSize(options) {
2441
2841
  return _RoomEvent.encode({
2442
2842
  event: "change_target_size",
@@ -2450,7 +2850,9 @@ var RoomEvent = class _RoomEvent {
2450
2850
  uid: options.taskID
2451
2851
  });
2452
2852
  }
2453
- /** 生成停止信令。 */
2853
+ /**
2854
+ * 生成停止信令。
2855
+ */
2454
2856
  static stop(options) {
2455
2857
  return _RoomEvent.encode({
2456
2858
  event: "stop",
@@ -2458,7 +2860,9 @@ var RoomEvent = class _RoomEvent {
2458
2860
  uid: options.taskID
2459
2861
  });
2460
2862
  }
2461
- /** 交互轨迹信令。 */
2863
+ /**
2864
+ * 交互轨迹信令。
2865
+ */
2462
2866
  static tracks(options) {
2463
2867
  return _RoomEvent.encode({
2464
2868
  event: "tracks",
@@ -2467,14 +2871,18 @@ var RoomEvent = class _RoomEvent {
2467
2871
  uid: options.taskID
2468
2872
  });
2469
2873
  }
2470
- /** 房间心跳信令。 */
2874
+ /**
2875
+ * 房间心跳信令。
2876
+ */
2471
2877
  static heartbeat(options) {
2472
2878
  return _RoomEvent.encode({
2473
2879
  event: "heartbeat",
2474
2880
  user_id: options.userID
2475
2881
  });
2476
2882
  }
2477
- /** 生成条件参数:模型、生成尺寸、回传尺寸、文本与参考图路径。 */
2883
+ /**
2884
+ * 生成条件参数:模型、生成尺寸、回传尺寸、文本与参考图路径。
2885
+ */
2478
2886
  static generationParameters(options) {
2479
2887
  return {
2480
2888
  model: "default",
@@ -2487,7 +2895,9 @@ var RoomEvent = class _RoomEvent {
2487
2895
  ref_image_path: options.context.referencePath
2488
2896
  };
2489
2897
  }
2490
- /** 业务字段外加 `runtime` 运行环境信息并序列化为 JSON 文本。 */
2898
+ /**
2899
+ * 业务字段外加 `runtime` 运行环境信息并序列化为 JSON 文本。
2900
+ */
2491
2901
  static encode(payload) {
2492
2902
  try {
2493
2903
  return JSON.stringify({ ...payload, runtime: RuntimeInfo.toJSON() });
@@ -2510,27 +2920,39 @@ var RoomHeartbeat = class {
2510
2920
  * @param options.sleep 心跳等待实现(可替换,测试用)。
2511
2921
  */
2512
2922
  constructor(options) {
2513
- // 基础层组件
2923
+ /**
2924
+ * 基础层组件
2925
+ */
2514
2926
  __publicField(this, "rtcManager");
2515
- // 心跳配置
2927
+ /**
2928
+ * 心跳配置
2929
+ */
2516
2930
  __publicField(this, "intervalMs");
2517
2931
  __publicField(this, "sleep");
2518
- // 运行状态
2932
+ /**
2933
+ * 运行状态
2934
+ */
2519
2935
  __publicField(this, "cycleVersion", 0);
2520
2936
  this.rtcManager = options.rtcManager;
2521
2937
  this.intervalMs = options.intervalMs ?? 1e4;
2522
2938
  this.sleep = options.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
2523
2939
  }
2524
- /** 启动周期心跳;重复启动会替换当前周期。 */
2940
+ /**
2941
+ * 启动周期心跳;重复启动会替换当前周期。
2942
+ */
2525
2943
  start(userID) {
2526
2944
  const version = ++this.cycleVersion;
2527
2945
  void this.run(version, userID);
2528
2946
  }
2529
- /** 停止当前心跳周期;迟到结果不会再发送。 */
2947
+ /**
2948
+ * 停止当前心跳周期;迟到结果不会再发送。
2949
+ */
2530
2950
  stop() {
2531
2951
  this.cycleVersion += 1;
2532
2952
  }
2533
- /** 周期循环:等待后发送心跳;发送失败仅记录日志,周期继续。 */
2953
+ /**
2954
+ * 周期循环:等待后发送心跳;发送失败仅记录日志,周期继续。
2955
+ */
2534
2956
  async run(version, userID) {
2535
2957
  while (this.cycleVersion === version) {
2536
2958
  await this.sleep(this.intervalMs);
@@ -2556,7 +2978,9 @@ var RoomHeartbeat = class {
2556
2978
  var CHUNK_EVENT = "__trtc_chunk__";
2557
2979
  var _RoomMessageCodec = class _RoomMessageCodec {
2558
2980
  constructor() {
2559
- // 接收缓存
2981
+ /**
2982
+ * 接收缓存
2983
+ */
2560
2984
  __publicField(this, "records", /* @__PURE__ */ new Map());
2561
2985
  }
2562
2986
  /**
@@ -2592,13 +3016,20 @@ var _RoomMessageCodec = class _RoomMessageCodec {
2592
3016
  /**
2593
3017
  * 处理入站消息。
2594
3018
  *
2595
- * @returns 普通业务消息原样返回;分片未收齐或组包结果不是合法
2596
- * JSON 时返回 `undefined`;分片收齐后返回完整消息并从缓存移除。
3019
+ * @returns 返回解析后的业务消息;分片未收齐或 JSON 非法时返回
3020
+ * `undefined`;分片收齐后从缓存移除并解析完整消息。
2597
3021
  */
2598
3022
  processIncoming(senderUserID, message) {
2599
- const chunk = _RoomMessageCodec.parseChunk(message);
3023
+ let parsed;
3024
+ try {
3025
+ parsed = JSON.parse(message);
3026
+ } catch {
3027
+ XmaxLogger.room.warning(() => "\u623F\u95F4\u6D88\u606F\u4E0D\u662F\u5408\u6CD5 JSON (Room Message Is Not Valid JSON)");
3028
+ return void 0;
3029
+ }
3030
+ const chunk = _RoomMessageCodec.parseChunk(parsed);
2600
3031
  if (!chunk) {
2601
- return message;
3032
+ return parsed;
2602
3033
  }
2603
3034
  const key = `${senderUserID}:${chunk.eventId}`;
2604
3035
  let record = this.records.get(key);
@@ -2622,7 +3053,7 @@ var _RoomMessageCodec = class _RoomMessageCodec {
2622
3053
  }
2623
3054
  const joined = parts.join("");
2624
3055
  try {
2625
- JSON.parse(joined);
3056
+ return JSON.parse(joined);
2626
3057
  } catch {
2627
3058
  XmaxLogger.room.warning(
2628
3059
  () => `\u623F\u95F4\u6D88\u606F\u7EC4\u5305\u540E\u4E0D\u662F\u5408\u6CD5 JSON (Reassembled Message Is Not Valid JSON)
@@ -2630,20 +3061,17 @@ var _RoomMessageCodec = class _RoomMessageCodec {
2630
3061
  );
2631
3062
  return void 0;
2632
3063
  }
2633
- return joined;
2634
3064
  }
2635
- /** 清空未完成的组包缓存。 */
3065
+ /**
3066
+ * 清空未完成的组包缓存。
3067
+ */
2636
3068
  reset() {
2637
3069
  this.records.clear();
2638
3070
  }
2639
- /** 识别分片消息;字段不完整时按普通业务消息处理。 */
2640
- static parseChunk(message) {
2641
- let parsed;
2642
- try {
2643
- parsed = JSON.parse(message);
2644
- } catch {
2645
- return void 0;
2646
- }
3071
+ /**
3072
+ * 从已解析的值识别分片消息;字段不完整时按普通业务消息处理。
3073
+ */
3074
+ static parseChunk(parsed) {
2647
3075
  if (typeof parsed !== "object" || parsed === null) {
2648
3076
  return void 0;
2649
3077
  }
@@ -2658,7 +3086,9 @@ var _RoomMessageCodec = class _RoomMessageCodec {
2658
3086
  }
2659
3087
  return { eventId: record.eventId, index, count, data: record.data };
2660
3088
  }
2661
- /** 淘汰最旧的未完成记录,保持缓存不超过上限。 */
3089
+ /**
3090
+ * 淘汰最旧的未完成记录,保持缓存不超过上限。
3091
+ */
2662
3092
  evictOverflow() {
2663
3093
  while (this.records.size > _RoomMessageCodec.cacheLimit) {
2664
3094
  const oldest = this.records.keys().next().value;
@@ -2668,7 +3098,9 @@ var _RoomMessageCodec = class _RoomMessageCodec {
2668
3098
  this.records.delete(oldest);
2669
3099
  }
2670
3100
  }
2671
- /** 按 UTF-8 字节预算切分字符串,不拆分多字节字符。 */
3101
+ /**
3102
+ * 按 UTF-8 字节预算切分字符串,不拆分多字节字符。
3103
+ */
2672
3104
  static sliceByByteBudget(message, budget) {
2673
3105
  const slices = [];
2674
3106
  let current = "";
@@ -2688,11 +3120,15 @@ var _RoomMessageCodec = class _RoomMessageCodec {
2688
3120
  }
2689
3121
  return slices;
2690
3122
  }
2691
- /** 计算字符串的 UTF-8 字节长度。 */
3123
+ /**
3124
+ * 计算字符串的 UTF-8 字节长度。
3125
+ */
2692
3126
  static byteLength(value) {
2693
3127
  return new TextEncoder().encode(value).byteLength;
2694
3128
  }
2695
- /** 生成拆包事件标识(32 位十六进制)。 */
3129
+ /**
3130
+ * 生成拆包事件标识(32 位十六进制)。
3131
+ */
2696
3132
  static makeEventId() {
2697
3133
  if (typeof crypto !== "undefined" && crypto.randomUUID) {
2698
3134
  return crypto.randomUUID().replace(/-/g, "");
@@ -2703,12 +3139,20 @@ var _RoomMessageCodec = class _RoomMessageCodec {
2703
3139
  ).join("");
2704
3140
  }
2705
3141
  };
2706
- // 协议常量
2707
- /** 触发拆包的消息字节阈值。 */
3142
+ /**
3143
+ * 协议常量
3144
+ */
3145
+ /**
3146
+ * 触发拆包的消息字节阈值。
3147
+ */
2708
3148
  __publicField(_RoomMessageCodec, "chunkThresholdBytes", 800);
2709
- /** 单个分片正文的字节预算,保证分片 JSON 自身不超过阈值。 */
3149
+ /**
3150
+ * 单个分片正文的字节预算,保证分片 JSON 自身不超过阈值。
3151
+ */
2710
3152
  __publicField(_RoomMessageCodec, "chunkDataBudgetBytes", 600);
2711
- /** 未完成组包记录的缓存上限(LRU)。 */
3153
+ /**
3154
+ * 未完成组包记录的缓存上限(LRU)。
3155
+ */
2712
3156
  __publicField(_RoomMessageCodec, "cacheLimit", 1e3);
2713
3157
  var RoomMessageCodec = _RoomMessageCodec;
2714
3158
 
@@ -2722,14 +3166,22 @@ var RoomController = class {
2722
3166
  * @param options.codec 房间消息编解码组件(可替换,测试用)。
2723
3167
  */
2724
3168
  constructor(options) {
2725
- // 基础层组件
3169
+ /**
3170
+ * 基础层组件
3171
+ */
2726
3172
  __publicField(this, "rtcManager");
2727
- // 传输层组件
3173
+ /**
3174
+ * 传输层组件
3175
+ */
2728
3176
  __publicField(this, "heartbeat");
2729
3177
  __publicField(this, "codec");
2730
- // 事件监听
3178
+ /**
3179
+ * 事件监听
3180
+ */
2731
3181
  __publicField(this, "listener");
2732
- // 房间资源
3182
+ /**
3183
+ * 房间资源
3184
+ */
2733
3185
  __publicField(this, "state", { kind: "idle" });
2734
3186
  __publicField(this, "leaveOperation");
2735
3187
  this.rtcManager = options.rtcManager;
@@ -2784,7 +3236,9 @@ var RoomController = class {
2784
3236
  throw XmaxError.from(error);
2785
3237
  }
2786
3238
  }
2787
- /** 停止房间心跳并离开当前 RTC 房间;并发的离开操作共享同一个任务。 */
3239
+ /**
3240
+ * 停止房间心跳并离开当前 RTC 房间;并发的离开操作共享同一个任务。
3241
+ */
2788
3242
  async leave() {
2789
3243
  if (this.leaveOperation) {
2790
3244
  await this.leaveOperation;
@@ -2844,14 +3298,18 @@ var RoomController = class {
2844
3298
  })
2845
3299
  );
2846
3300
  }
2847
- /** 尝试发送生成停止信令;未进房或任务标识为空时忽略。 */
3301
+ /**
3302
+ * 尝试发送生成停止信令;未进房或任务标识为空时忽略。
3303
+ */
2848
3304
  stopGeneration(taskID) {
2849
3305
  if (!taskID || this.state.kind !== "joined") {
2850
3306
  return;
2851
3307
  }
2852
3308
  this.send(RoomEvent.stop({ userID: this.state.userID, taskID }));
2853
3309
  }
2854
- /** 发送生成任务的交互轨迹;任务标识或轨迹为空时忽略。 */
3310
+ /**
3311
+ * 发送生成任务的交互轨迹;任务标识或轨迹为空时忽略。
3312
+ */
2855
3313
  sendTracks(taskID, points) {
2856
3314
  if (!taskID || points.length === 0) {
2857
3315
  return;
@@ -2864,11 +3322,15 @@ var RoomController = class {
2864
3322
  })
2865
3323
  );
2866
3324
  }
2867
- /** 设置房间事件监听器,传入空值时清除监听器。 */
3325
+ /**
3326
+ * 设置房间事件监听器,传入空值时清除监听器。
3327
+ */
2868
3328
  setListener(listener) {
2869
3329
  this.listener = listener;
2870
3330
  }
2871
- /** 执行离开:复位状态、停止心跳、清空组包缓存并退房。 */
3331
+ /**
3332
+ * 执行离开:复位状态、停止心跳、清空组包缓存并退房。
3333
+ */
2872
3334
  async performLeave() {
2873
3335
  this.state = { kind: "idle" };
2874
3336
  this.heartbeat.stop();
@@ -2876,19 +3338,11 @@ var RoomController = class {
2876
3338
  await this.rtcManager.leaveRoom();
2877
3339
  this.leaveOperation = void 0;
2878
3340
  }
2879
- /** 组包入站消息并按目标用户过滤后分发完整业务消息。 */
3341
+ /**
3342
+ * 组包入站消息并按目标用户过滤后分发完整业务消息。
3343
+ */
2880
3344
  handleIncomingMessage(senderUserID, raw) {
2881
- const complete = this.codec.processIncoming(senderUserID, raw);
2882
- if (complete === void 0) {
2883
- return;
2884
- }
2885
- let parsed;
2886
- try {
2887
- parsed = JSON.parse(complete);
2888
- } catch {
2889
- XmaxLogger.room.warning(() => "\u623F\u95F4\u6D88\u606F\u4E0D\u662F\u5408\u6CD5 JSON (Room Message Is Not Valid JSON)");
2890
- return;
2891
- }
3345
+ const parsed = this.codec.processIncoming(senderUserID, raw);
2892
3346
  if (typeof parsed !== "object" || parsed === null) {
2893
3347
  return;
2894
3348
  }
@@ -2900,7 +3354,9 @@ var RoomController = class {
2900
3354
  }
2901
3355
  this.listener?.onRoomMessage(senderUserID, message);
2902
3356
  }
2903
- /** 发送房间信令并输出日志。 */
3357
+ /**
3358
+ * 发送房间信令并输出日志。
3359
+ */
2904
3360
  send(message) {
2905
3361
  for (const packet of this.codec.encodeOutgoing(message)) {
2906
3362
  try {
@@ -2915,7 +3371,9 @@ var RoomController = class {
2915
3371
  ${JSON.stringify(JSON.parse(message), null, 2)}`
2916
3372
  );
2917
3373
  }
2918
- /** 返回当前房间中的用户标识;未进房时抛出错误。 */
3374
+ /**
3375
+ * 返回当前房间中的用户标识;未进房时抛出错误。
3376
+ */
2919
3377
  requireUserID() {
2920
3378
  if (this.state.kind !== "joined") {
2921
3379
  throw new XmaxError("RTC_ERROR" /* rtcError */, "RTC room is not joined");
@@ -2936,10 +3394,15 @@ var _EncodingController = class _EncodingController {
2936
3394
  * @param options.rtcManager RTC 引擎与媒体传输组件。
2937
3395
  */
2938
3396
  constructor(options) {
2939
- // 基础层组件
3397
+ /**
3398
+ * 基础层组件
3399
+ */
2940
3400
  __publicField(this, "rtcManager");
2941
3401
  this.rtcManager = options.rtcManager;
2942
3402
  }
3403
+ /**
3404
+ * 校验视频格式,解析显式或推荐码率范围后配置 RTC 编码器;参数或配置失败时抛错。
3405
+ */
2943
3406
  async configure(videoFormat) {
2944
3407
  videoFormat.validate();
2945
3408
  let minimum;
@@ -2959,8 +3422,8 @@ var _EncodingController = class _EncodingController {
2959
3422
  "Realtime video format exceeds the supported bitrate range"
2960
3423
  );
2961
3424
  }
2962
- const defaultMinimum = Math.max(1, Math.trunc(Math.round(bitrates.minimum)));
2963
- const defaultMaximum = Math.max(defaultMinimum + 1, Math.trunc(maximumBitrate));
3425
+ const defaultMinimum = Math.max(1, Math.round(bitrates.minimum));
3426
+ const defaultMaximum = Math.max(defaultMinimum + 1, maximumBitrate);
2964
3427
  minimum = videoFormat.minimumBitrate ?? defaultMinimum;
2965
3428
  maximum = videoFormat.maximumBitrate ?? defaultMaximum;
2966
3429
  }
@@ -2979,7 +3442,9 @@ var _EncodingController = class _EncodingController {
2979
3442
  encoderPreference: ENCODER_PREFERENCE_MAP[videoFormat.encoderPreference]
2980
3443
  });
2981
3444
  }
2982
- /** 按像素面积与帧率计算码率区间(kbps):流畅优先为最低,画质优先为最高。 */
3445
+ /**
3446
+ * 按像素面积与帧率计算码率区间(kbps):流畅优先为最低,画质优先为最高。
3447
+ */
2983
3448
  static resolveBitrates(pixels, fps) {
2984
3449
  const bitrate15 = _EncodingController.interpolate(
2985
3450
  pixels,
@@ -3017,7 +3482,9 @@ var _EncodingController = class _EncodingController {
3017
3482
  ])
3018
3483
  };
3019
3484
  }
3020
- /** 在相邻参考点间线性插值,表外按最近端点的比例外推。 */
3485
+ /**
3486
+ * 在相邻参考点间线性插值,表外按最近端点的比例外推。
3487
+ */
3021
3488
  static interpolate(value, points) {
3022
3489
  const first = points[0];
3023
3490
  if (value <= first.value) {
@@ -3035,7 +3502,9 @@ var _EncodingController = class _EncodingController {
3035
3502
  return last.bitrate * (value / last.value);
3036
3503
  }
3037
3504
  };
3038
- // 流畅优先码率参考,按像素面积升序排列,单位为 kbps。
3505
+ /**
3506
+ * 流畅优先码率参考,按像素面积升序排列,单位为 kbps。
3507
+ */
3039
3508
  __publicField(_EncodingController, "referenceBitratesAt15FPS", [
3040
3509
  { value: 120 * 120, bitrate: 50 },
3041
3510
  { value: 160 * 120, bitrate: 65 },
@@ -3088,22 +3557,34 @@ var StreamController = class {
3088
3557
  * @param options.generationTimeoutMs 生成开始确认超时时间(毫秒);默认 30 秒。
3089
3558
  */
3090
3559
  constructor(options) {
3091
- // 基础层组件
3560
+ /**
3561
+ * 基础层组件
3562
+ */
3092
3563
  __publicField(this, "rtcManager");
3093
- // 传输层组件
3564
+ /**
3565
+ * 传输层组件
3566
+ */
3094
3567
  __publicField(this, "roomController");
3095
3568
  __publicField(this, "encodingController");
3096
- // 事件监听
3569
+ /**
3570
+ * 事件监听
3571
+ */
3097
3572
  __publicField(this, "errorListener");
3098
3573
  __publicField(this, "remoteStreamListener");
3099
3574
  __publicField(this, "localVideoStatisticsListener");
3100
3575
  __publicField(this, "networkStatisticsListener");
3101
3576
  __publicField(this, "remoteVideoStatisticsListener");
3102
- // 生成配置
3577
+ /**
3578
+ * 生成配置
3579
+ */
3103
3580
  __publicField(this, "generationTimeoutMs");
3104
- // 音频配置
3581
+ /**
3582
+ * 音频配置
3583
+ */
3105
3584
  __publicField(this, "remoteAudioVolumePercentage", 100);
3106
- // 运行状态
3585
+ /**
3586
+ * 运行状态
3587
+ */
3107
3588
  __publicField(this, "state", makeInitialState());
3108
3589
  this.rtcManager = options.rtcManager;
3109
3590
  this.roomController = options.roomController ?? new RoomController({ rtcManager: options.rtcManager });
@@ -3138,27 +3619,39 @@ var StreamController = class {
3138
3619
  }
3139
3620
  });
3140
3621
  }
3141
- /** 当前是否存在正在启动或已经运行的生成任务。 */
3622
+ /**
3623
+ * 当前是否存在正在启动或已经运行的生成任务。
3624
+ */
3142
3625
  get hasGenerationTask() {
3143
3626
  return this.state.generationTaskID !== void 0;
3144
3627
  }
3145
- /** 设置本地主视频流统计监听器,不改变 RTC 事件监听权。 */
3628
+ /**
3629
+ * 设置本地主视频流统计监听器,不改变 RTC 事件监听权。
3630
+ */
3146
3631
  setLocalVideoStatisticsListener(listener) {
3147
3632
  this.localVideoStatisticsListener = listener;
3148
3633
  }
3149
- /** 网络统计不按远端用户过滤,连接发布完成后即可回调。 */
3634
+ /**
3635
+ * 网络统计不按远端用户过滤,连接发布完成后即可回调。
3636
+ */
3150
3637
  setNetworkStatisticsListener(listener) {
3151
3638
  this.networkStatisticsListener = listener;
3152
3639
  }
3153
- /** 监听当前实际生成结果流的统计,不按会话下发的 botID 猜测结果流身份。 */
3640
+ /**
3641
+ * 监听当前实际生成结果流的统计,不按会话下发的 botID 猜测结果流身份。
3642
+ */
3154
3643
  setRemoteVideoStatisticsListener(listener) {
3155
3644
  this.remoteVideoStatisticsListener = listener;
3156
3645
  }
3157
- /** 当前远端生成音频播放音量,取值范围为 `0...1`。 */
3646
+ /**
3647
+ * 当前远端生成音频播放音量,取值范围为 `0...1`。
3648
+ */
3158
3649
  get remoteAudioVolume() {
3159
3650
  return this.remoteAudioVolumePercentage / 100;
3160
3651
  }
3161
- /** 设置远端生成音频播放音量,并应用到所有已订阅的远端音频。 */
3652
+ /**
3653
+ * 设置远端生成音频播放音量,并应用到所有已订阅的远端音频。
3654
+ */
3162
3655
  setRemoteAudioVolume(volume) {
3163
3656
  const rtcVolume = Math.min(Math.max(Math.round(volume * 100), 0), 100);
3164
3657
  for (const userID of [...this.state.subscribedRemoteAudioUserIDs].sort()) {
@@ -3191,7 +3684,9 @@ var StreamController = class {
3191
3684
  ensureActive();
3192
3685
  await this.publishLocalStream(includeLocalAudio);
3193
3686
  }
3194
- /** 清理生成状态、本地发布和远端订阅,并离开当前 RTC 房间。 */
3687
+ /**
3688
+ * 清理生成状态、本地发布和远端订阅,并离开当前 RTC 房间。
3689
+ */
3195
3690
  async disconnect() {
3196
3691
  await this.resetStream();
3197
3692
  await this.roomController.leave();
@@ -3334,11 +3829,15 @@ var StreamController = class {
3334
3829
  throw XmaxError.from(error);
3335
3830
  }
3336
3831
  }
3337
- /** 设置房间业务消息监听器,传入空值时清除监听器。 */
3832
+ /**
3833
+ * 设置房间业务消息监听器,传入空值时清除监听器。
3834
+ */
3338
3835
  setRoomListener(listener) {
3339
3836
  this.roomController.setListener(listener);
3340
3837
  }
3341
- /** 配置当前房间;已有发布、订阅或生成任务时拒绝重新配置。 */
3838
+ /**
3839
+ * 配置当前房间;已有发布、订阅或生成任务时拒绝重新配置。
3840
+ */
3342
3841
  configureRoom(options) {
3343
3842
  const roomID = options.roomID.trim();
3344
3843
  const botID = options.botID?.trim() ?? "";
@@ -3357,7 +3856,9 @@ var StreamController = class {
3357
3856
  this.state.roomID = roomID;
3358
3857
  this.state.botID = botID;
3359
3858
  }
3360
- /** 发布本地视频,按需发布本地音频;音频发布失败时回滚视频发布。 */
3859
+ /**
3860
+ * 发布本地视频,按需发布本地音频;音频发布失败时回滚视频发布。
3861
+ */
3361
3862
  async publishLocalStream(includeAudio) {
3362
3863
  if (!this.state.roomID) {
3363
3864
  throw new XmaxError(
@@ -3383,7 +3884,9 @@ var StreamController = class {
3383
3884
  throw XmaxError.from(error);
3384
3885
  }
3385
3886
  }
3386
- /** 处理远端视频发布状态变化:按机器人标识过滤后订阅或清理。 */
3887
+ /**
3888
+ * 处理远端视频发布状态变化:按机器人标识过滤后订阅或清理。
3889
+ */
3387
3890
  async handleRemoteVideoPublished(userID, published) {
3388
3891
  const trimmedUserID = userID.trim();
3389
3892
  if (!trimmedUserID) {
@@ -3420,7 +3923,9 @@ var StreamController = class {
3420
3923
  this.clearRemoteStream();
3421
3924
  }
3422
3925
  }
3423
- /** 订阅远端视频并在成功后就绪或确认生成任务。 */
3926
+ /**
3927
+ * 订阅远端视频并在成功后就绪或确认生成任务。
3928
+ */
3424
3929
  async subscribeRemoteVideo(userID) {
3425
3930
  if (this.state.subscribedRemoteUserIDs.has(userID)) {
3426
3931
  return;
@@ -3452,7 +3957,9 @@ var StreamController = class {
3452
3957
  }
3453
3958
  }
3454
3959
  }
3455
- /** 订阅成功后就绪远端流:有待确认的生成任务时确认它,否则更新当前绑定。 */
3960
+ /**
3961
+ * 订阅成功后就绪远端流:有待确认的生成任务时确认它,否则更新当前绑定。
3962
+ */
3456
3963
  confirmOrUpdateRemoteStream(userID, track) {
3457
3964
  const waiter = this.state.generationWaiter;
3458
3965
  const taskID = this.state.generationTaskID;
@@ -3485,7 +3992,9 @@ var StreamController = class {
3485
3992
  }
3486
3993
  }
3487
3994
  }
3488
- /** 订阅远端音频:先应用当前音量再订阅。 */
3995
+ /**
3996
+ * 订阅远端音频:先应用当前音量再订阅。
3997
+ */
3489
3998
  async subscribeRemoteAudio(userID) {
3490
3999
  if (this.state.subscribedRemoteAudioUserIDs.has(userID)) {
3491
4000
  return;
@@ -3494,7 +4003,9 @@ var StreamController = class {
3494
4003
  await this.rtcManager.subscribeRemoteAudio(userID, true);
3495
4004
  this.state.subscribedRemoteAudioUserIDs.add(userID);
3496
4005
  }
3497
- /** 取消订阅远端音频;失败仅记录日志。 */
4006
+ /**
4007
+ * 取消订阅远端音频;失败仅记录日志。
4008
+ */
3498
4009
  async unsubscribeRemoteAudio(userID) {
3499
4010
  if (!this.state.subscribedRemoteAudioUserIDs.delete(userID)) {
3500
4011
  return;
@@ -3533,7 +4044,9 @@ var StreamController = class {
3533
4044
  this.clearRemoteStream();
3534
4045
  return currentTaskID;
3535
4046
  }
3536
- /** 复位传输层:停止生成、取消全部远端订阅、取消本地发布。 */
4047
+ /**
4048
+ * 复位传输层:停止生成、取消全部远端订阅、取消本地发布。
4049
+ */
3537
4050
  async resetStream() {
3538
4051
  await this.stopStreamGeneration("");
3539
4052
  const previousState = this.state;
@@ -3559,7 +4072,9 @@ var StreamController = class {
3559
4072
  );
3560
4073
  }
3561
4074
  }
3562
- /** 确认生成开始:清理等待器并兑现确认 Promise。 */
4075
+ /**
4076
+ * 确认生成开始:清理等待器并兑现确认 Promise。
4077
+ */
3563
4078
  resolveGenerationStart(taskID) {
3564
4079
  const waiter = this.state.generationWaiter;
3565
4080
  if (this.state.generationTaskID !== taskID || waiter?.taskID !== taskID) {
@@ -3569,7 +4084,9 @@ var StreamController = class {
3569
4084
  clearTimeout(waiter.timeoutTimer);
3570
4085
  waiter.resolve();
3571
4086
  }
3572
- /** 拒绝生成开始:清理等待器并拒绝确认 Promise。 */
4087
+ /**
4088
+ * 拒绝生成开始:清理等待器并拒绝确认 Promise。
4089
+ */
3573
4090
  rejectGenerationStart(taskID, error) {
3574
4091
  const waiter = this.state.generationWaiter;
3575
4092
  if (this.state.generationTaskID !== taskID || waiter?.taskID !== taskID) {
@@ -3579,7 +4096,9 @@ var StreamController = class {
3579
4096
  clearTimeout(waiter.timeoutTimer);
3580
4097
  waiter.reject(error);
3581
4098
  }
3582
- /** 通知渲染层清理远端生成流;失败仅记录日志。 */
4099
+ /**
4100
+ * 通知渲染层清理远端生成流;失败仅记录日志。
4101
+ */
3583
4102
  clearRemoteStream() {
3584
4103
  this.remoteVideoStatisticsListener?.(void 0);
3585
4104
  try {
@@ -3591,7 +4110,9 @@ var StreamController = class {
3591
4110
  );
3592
4111
  }
3593
4112
  }
3594
- /** 回滚本地视频发布;失败仅记录日志。 */
4113
+ /**
4114
+ * 回滚本地视频发布;失败仅记录日志。
4115
+ */
3595
4116
  async rollbackLocalVideoPublication() {
3596
4117
  await this.performCleanup(
3597
4118
  "\u56DE\u6EDA RTC \u672C\u5730\u89C6\u9891\u53D1\u5E03\u5931\u8D25 (Failed to Roll Back RTC Local Video Publication)",
@@ -3601,7 +4122,9 @@ var StreamController = class {
3601
4122
  }
3602
4123
  );
3603
4124
  }
3604
- /** 执行清理动作;失败仅记录日志。 */
4125
+ /**
4126
+ * 执行清理动作;失败仅记录日志。
4127
+ */
3605
4128
  async performCleanup(title, action) {
3606
4129
  try {
3607
4130
  await action();
@@ -3622,31 +4145,52 @@ function scopeAffects(scope, kind) {
3622
4145
  return kind !== "media" /* media */;
3623
4146
  }
3624
4147
  var OperationLease = class {
4148
+ /**
4149
+ * 创建操作租约;未指定失败范围时不自动扩大资源清理范围。
4150
+ */
3625
4151
  constructor(failureScope) {
3626
- // 并发状态
4152
+ /**
4153
+ * 并发状态
4154
+ */
3627
4155
  __publicField(this, "valid", true);
3628
4156
  __publicField(this, "terminalError");
3629
4157
  __publicField(this, "storedFailureScope");
3630
4158
  __publicField(this, "abortController", new AbortController());
3631
4159
  this.storedFailureScope = failureScope;
3632
4160
  }
4161
+ /**
4162
+ * 租约是否仍然有效,可用于异步结果提交前的快速判断。
4163
+ */
3633
4164
  get isCurrent() {
3634
4165
  return this.valid;
3635
4166
  }
4167
+ /**
4168
+ * 首次使租约失效的错误;主动取消且未指定原因时为空。
4169
+ */
3636
4170
  get failure() {
3637
4171
  return this.terminalError;
3638
4172
  }
4173
+ /**
4174
+ * 当前操作失败或取消时需要清理的资源范围。
4175
+ */
3639
4176
  get failureScope() {
3640
4177
  return this.storedFailureScope;
3641
4178
  }
4179
+ /**
4180
+ * 租约失效时同步触发的取消信号。
4181
+ */
3642
4182
  get signal() {
3643
4183
  return this.abortController.signal;
3644
4184
  }
3645
- /** 更新当前操作失败或被调用方取消时需要释放的资源范围。 */
4185
+ /**
4186
+ * 更新当前操作失败或被调用方取消时需要释放的资源范围。
4187
+ */
3646
4188
  setFailureScope(scope) {
3647
4189
  this.storedFailureScope = scope;
3648
4190
  }
3649
- /** 作废租约并触发中止信号;首个终止原因被保留。 */
4191
+ /**
4192
+ * 作废租约并触发中止信号;首个终止原因被保留。
4193
+ */
3650
4194
  invalidate(error) {
3651
4195
  this.valid = false;
3652
4196
  if (!this.terminalError) {
@@ -3654,7 +4198,9 @@ var OperationLease = class {
3654
4198
  }
3655
4199
  this.abortController.abort();
3656
4200
  }
3657
- /** 在异步边界后校验当前操作仍拥有实时生命周期。 */
4201
+ /**
4202
+ * 在异步边界后校验当前操作仍拥有实时生命周期。
4203
+ */
3658
4204
  ensureCurrent() {
3659
4205
  if (!this.valid) {
3660
4206
  throw this.terminalError ?? RealtimeCoordinator.cancelledError();
@@ -3663,22 +4209,33 @@ var OperationLease = class {
3663
4209
  };
3664
4210
  var operationSequence = 0;
3665
4211
  var RealtimeCoordinator = class _RealtimeCoordinator {
4212
+ /**
4213
+ * 注入错误分发与资源清理逻辑,初始状态为 idle。
4214
+ */
3666
4215
  constructor(options) {
3667
- // 业务组件
4216
+ /**
4217
+ * 业务组件
4218
+ */
3668
4219
  __publicField(this, "errorHandler");
3669
4220
  __publicField(this, "cleanup");
3670
- // 状态管理
4221
+ /**
4222
+ * 状态管理
4223
+ */
3671
4224
  __publicField(this, "state", new RealtimeState({
3672
4225
  connectionState: "Idle" /* idle */
3673
4226
  }));
3674
4227
  __publicField(this, "stateListener");
3675
- // 操作管理
4228
+ /**
4229
+ * 操作管理
4230
+ */
3676
4231
  __publicField(this, "activeOperation");
3677
4232
  __publicField(this, "termination");
3678
4233
  this.errorHandler = options.errorHandler;
3679
4234
  this.cleanup = options.cleanup;
3680
4235
  }
3681
- /** 当前实时状态快照。 */
4236
+ /**
4237
+ * 当前实时状态快照。
4238
+ */
3682
4239
  get currentState() {
3683
4240
  return this.state;
3684
4241
  }
@@ -3734,12 +4291,16 @@ var RealtimeCoordinator = class _RealtimeCoordinator {
3734
4291
  return this.handleFailure(error, operation);
3735
4292
  }
3736
4293
  }
3737
- /** 仅当前操作可以提交新的公开状态。 */
4294
+ /**
4295
+ * 仅当前操作可以提交新的公开状态。
4296
+ */
3738
4297
  async commit(nextState, token) {
3739
4298
  token.ensureCurrent();
3740
4299
  await this.setState(nextState);
3741
4300
  }
3742
- /** 当前本地预览满足就绪条件后,结束媒体准备状态。 */
4301
+ /**
4302
+ * 当前本地预览满足就绪条件后,结束媒体准备状态。
4303
+ */
3743
4304
  async localPreviewDidBecomeReady(isCurrent) {
3744
4305
  if (this.state.connectionState !== "Preparing" /* preparing */ || !isCurrent()) {
3745
4306
  return;
@@ -3748,12 +4309,16 @@ var RealtimeCoordinator = class _RealtimeCoordinator {
3748
4309
  new RealtimeState({ connectionState: "Ready" /* ready */ })
3749
4310
  );
3750
4311
  }
3751
- /** 断开实时连接;尚未提交连接状态的活跃操作也会被取消。 */
4312
+ /**
4313
+ * 断开实时连接;尚未提交连接状态的活跃操作也会被取消。
4314
+ */
3752
4315
  async disconnect(reason = RealtimeReason.normal) {
3753
4316
  const task = await this.beginDisconnect(reason);
3754
4317
  await task;
3755
4318
  }
3756
- /** 发起断开并返回收尾任务,使本地媒体调整不必等待网络资源释放。 */
4319
+ /**
4320
+ * 发起断开并返回收尾任务,使本地媒体调整不必等待网络资源释放。
4321
+ */
3757
4322
  async beginDisconnect(reason) {
3758
4323
  const hasConnectionOperation = this.activeOperation ? scopeAffects(0 /* connection */, this.activeOperation.kind) : false;
3759
4324
  const state2 = this.state.connectionState;
@@ -3767,12 +4332,16 @@ var RealtimeCoordinator = class _RealtimeCoordinator {
3767
4332
  reason
3768
4333
  );
3769
4334
  }
3770
- /** 终止指定范围并等待资源清理完成;并发终止请求会合并为最大范围。 */
4335
+ /**
4336
+ * 终止指定范围并等待资源清理完成;并发终止请求会合并为最大范围。
4337
+ */
3771
4338
  async terminate(target, reason = RealtimeReason.normal) {
3772
4339
  const task = await this.requestTermination(target, void 0, reason);
3773
4340
  await task;
3774
4341
  }
3775
- /** 清理仍属于当前生命周期的后台故障,并通过最终状态提供原因。 */
4342
+ /**
4343
+ * 清理仍属于当前生命周期的后台故障,并通过最终状态提供原因。
4344
+ */
3776
4345
  async terminateWithError(error, target, isCurrent = () => true) {
3777
4346
  if (!isCurrent()) {
3778
4347
  return;
@@ -3787,7 +4356,9 @@ var RealtimeCoordinator = class _RealtimeCoordinator {
3787
4356
  );
3788
4357
  await task;
3789
4358
  }
3790
- /** 操作失败处理:取消走终止流程,错误按失败范围清理或直接上报。 */
4359
+ /**
4360
+ * 操作失败处理:取消走终止流程,错误按失败范围清理或直接上报。
4361
+ */
3791
4362
  async handleFailure(error, operation) {
3792
4363
  const operationWasCancelled = error instanceof XmaxError && error.code === "CANCELLED" /* cancelled */ || error instanceof DOMException && error.name === "AbortError" || !operation.lease.isCurrent;
3793
4364
  if (operationWasCancelled) {
@@ -3832,7 +4403,9 @@ var RealtimeCoordinator = class _RealtimeCoordinator {
3832
4403
  this.finish(operation);
3833
4404
  throw resolvedError;
3834
4405
  }
3835
- /** 发起或合并终止请求,作废旧操作并进入断开中状态。 */
4406
+ /**
4407
+ * 发起或合并终止请求,作废旧操作并进入断开中状态。
4408
+ */
3836
4409
  async requestTermination(target, error, reason, origin) {
3837
4410
  let pending = this.termination;
3838
4411
  if (pending) {
@@ -3875,7 +4448,9 @@ var RealtimeCoordinator = class _RealtimeCoordinator {
3875
4448
  );
3876
4449
  return pending.task;
3877
4450
  }
3878
- /** 执行终止:等待旧操作收尾,分级清理资源,提交最终状态。 */
4451
+ /**
4452
+ * 执行终止:等待旧操作收尾,分级清理资源,提交最终状态。
4453
+ */
3879
4454
  async performTermination(id) {
3880
4455
  const pending = this.termination;
3881
4456
  if (!pending || pending.id !== id) {
@@ -3921,13 +4496,17 @@ var RealtimeCoordinator = class _RealtimeCoordinator {
3921
4496
  return;
3922
4497
  }
3923
4498
  }
3924
- /** 结束指定操作,释放操作准入场。 */
4499
+ /**
4500
+ * 结束指定操作,释放操作准入场。
4501
+ */
3925
4502
  finish(operation) {
3926
4503
  if (this.activeOperation?.id === operation.id) {
3927
4504
  this.activeOperation = void 0;
3928
4505
  }
3929
4506
  }
3930
- /** 提交新状态并通知监听器;状态未变化时不通知。 */
4507
+ /**
4508
+ * 提交新状态并通知监听器;状态未变化时不通知。
4509
+ */
3931
4510
  async setState(nextState) {
3932
4511
  if (this.state.equals(nextState)) {
3933
4512
  return;
@@ -3935,7 +4514,9 @@ var RealtimeCoordinator = class _RealtimeCoordinator {
3935
4514
  this.state = nextState;
3936
4515
  this.stateListener?.(nextState);
3937
4516
  }
3938
- /** 构造统一的操作取消错误。 */
4517
+ /**
4518
+ * 构造统一的操作取消错误。
4519
+ */
3939
4520
  static cancelledError() {
3940
4521
  return new XmaxError(
3941
4522
  "CANCELLED" /* cancelled */,
@@ -3947,10 +4528,14 @@ var RealtimeCoordinator = class _RealtimeCoordinator {
3947
4528
  // src/Core/Realtime/RealtimeErrorHandler.ts
3948
4529
  var RealtimeErrorHandler = class {
3949
4530
  constructor() {
3950
- // 运行状态
4531
+ /**
4532
+ * 运行状态
4533
+ */
3951
4534
  __publicField(this, "pendingFailures", []);
3952
4535
  }
3953
- /** 上报错误;当前通过日志输出,后续里程碑接入状态原因上报。 */
4536
+ /**
4537
+ * 上报错误;当前通过日志输出,后续里程碑接入状态原因上报。
4538
+ */
3954
4539
  async report(error) {
3955
4540
  if (error.code === "CANCELLED" /* cancelled */) {
3956
4541
  return;
@@ -3961,7 +4546,9 @@ var RealtimeErrorHandler = class {
3961
4546
  \u2514\u2500 ${XmaxLogger.localized("\u539F\u56E0\uFF1A", "Reason: ")}${error.code} ${error.message}`
3962
4547
  );
3963
4548
  }
3964
- /** 清理仍属于已终止生命周期的后台故障。 */
4549
+ /**
4550
+ * 清理仍属于已终止生命周期的后台故障。
4551
+ */
3965
4552
  invalidatePendingFailures(_target) {
3966
4553
  this.pendingFailures = [];
3967
4554
  }
@@ -3970,16 +4557,28 @@ var RealtimeErrorHandler = class {
3970
4557
  // src/Core/Realtime/RealtimeLaunchTimer.ts
3971
4558
  var RealtimeLaunchTimer = class {
3972
4559
  constructor() {
4560
+ /**
4561
+ * 统计快照与观察者
4562
+ */
3973
4563
  __publicField(this, "snapshot", Object.freeze({}));
3974
4564
  __publicField(this, "listener");
4565
+ /**
4566
+ * 当前启动周期与计时起点
4567
+ */
3975
4568
  __publicField(this, "sequence", 0);
3976
4569
  __publicField(this, "active", false);
3977
4570
  __publicField(this, "startedAt", 0);
3978
4571
  }
4572
+ /**
4573
+ * 设置统计监听器并立即回放当前快照;传 undefined 取消监听。
4574
+ */
3979
4575
  setListener(listener) {
3980
4576
  this.listener = listener;
3981
4577
  this.notify();
3982
4578
  }
4579
+ /**
4580
+ * 开始新的启动周期并清空旧快照,返回记录相机创建完成的一次性回调。
4581
+ */
3983
4582
  startCamera() {
3984
4583
  this.sequence += 1;
3985
4584
  this.active = true;
@@ -3989,17 +4588,34 @@ var RealtimeLaunchTimer = class {
3989
4588
  this.notify();
3990
4589
  return complete;
3991
4590
  }
4591
+ /**
4592
+ * 开始会话与进房计时,返回连接阶段完成时调用的记录函数。
4593
+ */
3992
4594
  startConnection() {
3993
4595
  return this.measure("connectionMs", performance.now());
3994
4596
  }
4597
+ /**
4598
+ * 开始亮度检测计时;合格帧到达或超时放行时调用返回的记录函数。
4599
+ */
4600
+ startFrameValidation() {
4601
+ return this.measure("frameValidationMs", performance.now());
4602
+ }
4603
+ /**
4604
+ * 开始等待远端显示首帧;完成回调同时记录首帧耗时和实际总耗时。
4605
+ */
3995
4606
  startFirstFrame() {
3996
4607
  return this.measure("firstFrameMs", performance.now());
3997
4608
  }
3998
- /** 冻结本轮结果,令尚未完成的回调失效;下一轮相机启动才重置。 */
4609
+ /**
4610
+ * 冻结本轮结果,令尚未完成的回调失效;下一轮相机启动才重置。
4611
+ */
3999
4612
  cancel() {
4000
4613
  this.active = false;
4001
4614
  this.sequence += 1;
4002
4615
  }
4616
+ /**
4617
+ * 创建当前周期的一次性计时回调;周期失效或该阶段已记录时忽略调用。
4618
+ */
4003
4619
  measure(key, startedAt) {
4004
4620
  const sequence = this.sequence;
4005
4621
  return () => {
@@ -4015,6 +4631,9 @@ var RealtimeLaunchTimer = class {
4015
4631
  this.notify();
4016
4632
  };
4017
4633
  }
4634
+ /**
4635
+ * 分发不可变快照;隔离同步和异步观察者异常,避免影响媒体生命周期。
4636
+ */
4018
4637
  notify() {
4019
4638
  try {
4020
4639
  void Promise.resolve(this.listener?.(this.snapshot)).catch(() => {
@@ -4026,23 +4645,38 @@ var RealtimeLaunchTimer = class {
4026
4645
 
4027
4646
  // src/Core/Realtime/XmaxRealtimeConnectionManager.ts
4028
4647
  var XmaxRealtimeConnectionManager = class {
4648
+ /**
4649
+ * 注入会话、传输、计时和渲染回调依赖;连接资源在 connect 时创建。
4650
+ */
4029
4651
  constructor(dependencies) {
4030
4652
  __publicField(this, "dependencies", dependencies);
4653
+ /**
4654
+ * 会话资源
4655
+ */
4031
4656
  __publicField(this, "activeSession");
4657
+ /**
4658
+ * 远端轨道与渲染绑定
4659
+ */
4032
4660
  __publicField(this, "activeRemoteTrack");
4033
- __publicField(this, "remoteBinding");
4034
- __publicField(this, "targetSize");
4661
+ __publicField(this, "remoteTarget");
4035
4662
  }
4663
+ /**
4664
+ * 当前活动会话标识;尚未连接或清理完成后为空。
4665
+ */
4036
4666
  get currentSessionID() {
4037
4667
  return this.activeSession?.id;
4038
4668
  }
4039
- get currentTargetSize() {
4040
- return this.targetSize;
4041
- }
4042
- /** 未配置 API 服务时在连接操作开始前报错,不影响本地预览。 */
4669
+ /**
4670
+ * 未配置 API 服务时在连接操作开始前报错,不影响本地预览。
4671
+ */
4043
4672
  validateConfiguration() {
4044
4673
  this.requireSessionService();
4045
4674
  }
4675
+ /**
4676
+ * 创建会话、配置编码、进房并发布本地媒体,启动心跳后返回远端占位流。
4677
+ * 进房结束即记录连接耗时;可选 beforePublish 检查只阻塞发布,不计入连接耗时。
4678
+ * @throws 会话、进房、发布失败或操作租约已失效时抛出错误,由上层统一清理。
4679
+ */
4046
4680
  async connect(options) {
4047
4681
  const sessionService = this.requireSessionService();
4048
4682
  const streamController = this.dependencies.streamController;
@@ -4069,9 +4703,12 @@ var XmaxRealtimeConnectionManager = class {
4069
4703
  options.ensureCurrent();
4070
4704
  await streamController.connect(connection, options.includeLocalAudio, () => {
4071
4705
  options.ensureCurrent();
4072
- }, options.beforePublish);
4073
- options.ensureCurrent();
4074
- completeConnection();
4706
+ }, async () => {
4707
+ options.ensureCurrent();
4708
+ completeConnection();
4709
+ options.ensureCurrent();
4710
+ await options.beforePublish?.();
4711
+ });
4075
4712
  options.ensureCurrent();
4076
4713
  options.onPublished();
4077
4714
  sessionService.startHeartbeat(session.id, {
@@ -4091,16 +4728,27 @@ var XmaxRealtimeConnectionManager = class {
4091
4728
  streamController.setRemoteAudioVolume(this.dependencies.remoteAudioVolume());
4092
4729
  return new RealtimeMediaStream({ id: "stream1" /* remote */, videoTrack: remoteTrack });
4093
4730
  }
4731
+ /**
4732
+ * 更新已绑定远端视图的插帧配置;传 undefined 停用插帧。
4733
+ */
4094
4734
  setFrameInterpolation(options) {
4095
- this.remoteBinding?.setFrameInterpolation(options);
4735
+ this.remoteTarget?.setFrameInterpolation?.(options);
4096
4736
  }
4737
+ /**
4738
+ * 清空远端视图的媒体流,保留轨道与视图绑定以供统一清理。
4739
+ */
4097
4740
  clearRemoteMedia() {
4098
- this.remoteBinding?.setMediaStream(null);
4741
+ this.remoteTarget?.setMediaStream(null);
4099
4742
  }
4743
+ /**
4744
+ * 停止会话心跳;未配置会话服务时不执行操作。
4745
+ */
4100
4746
  stopHeartbeat() {
4101
4747
  this.dependencies.sessionService?.stopHeartbeat();
4102
4748
  }
4103
- /** 等待接收轨解除静音;超时只警告,取消必须立即退出并移除监听。 */
4749
+ /**
4750
+ * 等待接收轨解除静音;超时只警告,取消必须立即退出并移除监听。
4751
+ */
4104
4752
  async waitUntilRemoteTrackReady(timeoutMs, signal) {
4105
4753
  if (signal.aborted) throw RealtimeCoordinator.cancelledError();
4106
4754
  const mediaTrack = this.activeRemoteTrack?.mediaStreamTrack;
@@ -4140,7 +4788,7 @@ var XmaxRealtimeConnectionManager = class {
4140
4788
  if (track) {
4141
4789
  track.mediaStreamTrack = void 0;
4142
4790
  }
4143
- this.remoteBinding?.setMediaStream(null);
4791
+ this.remoteTarget?.setMediaStream(null);
4144
4792
  return;
4145
4793
  }
4146
4794
  if (!track) {
@@ -4150,9 +4798,11 @@ var XmaxRealtimeConnectionManager = class {
4150
4798
  );
4151
4799
  }
4152
4800
  track.mediaStreamTrack = binding.videoTrack;
4153
- this.remoteBinding?.setMediaStream(new MediaStream([binding.videoTrack]));
4801
+ this.remoteTarget?.setMediaStream(new MediaStream([binding.videoTrack]));
4154
4802
  }
4155
- /** 为远端轨道注册渲染绑定:attach 时挂流占位,媒体轨到达后送入画面。 */
4803
+ /**
4804
+ * 为远端轨道注册渲染绑定:attach 时挂流占位,媒体轨到达后送入画面。
4805
+ */
4156
4806
  registerRemoteBinding(track) {
4157
4807
  VideoRenderRegistry.register(track, {
4158
4808
  frameDisplayHandler: () => {
@@ -4161,39 +4811,34 @@ var XmaxRealtimeConnectionManager = class {
4161
4811
  }
4162
4812
  },
4163
4813
  attachHandler: (view) => {
4164
- this.remoteBinding?.setFrameInterpolation(void 0);
4814
+ this.remoteTarget?.setFrameInterpolation?.(void 0);
4165
4815
  view.isMirrored = this.dependencies.isMirrored();
4166
- this.remoteBinding = {
4167
- target: view,
4168
- setFrameInterpolation: (options) => view.setFrameInterpolation?.(options),
4169
- setMediaStream: (stream) => {
4170
- view.setMediaStream(stream);
4171
- },
4172
- setMirrored: (mirrored) => {
4173
- view.isMirrored = mirrored;
4174
- }
4175
- };
4816
+ this.remoteTarget = view;
4176
4817
  this.dependencies.onRenderAttached();
4177
4818
  const mediaTrack = track.mediaStreamTrack;
4178
4819
  view.setMediaStream(mediaTrack ? new MediaStream([mediaTrack]) : null);
4179
4820
  },
4180
4821
  detachHandler: (view) => {
4181
- if (this.remoteBinding?.target === view) {
4822
+ if (this.remoteTarget === view) {
4182
4823
  this.dependencies.onRenderDetached();
4183
- this.remoteBinding = void 0;
4824
+ this.remoteTarget = void 0;
4184
4825
  }
4185
4826
  view.setFrameInterpolation?.(void 0);
4186
4827
  view.setMediaStream(null);
4187
4828
  }
4188
4829
  });
4189
4830
  }
4190
- /** 同步远端结果画面的镜像状态:与当前本地摄像头位置保持一致。 */
4831
+ /**
4832
+ * 同步远端结果画面的镜像状态:与当前本地摄像头位置保持一致。
4833
+ */
4191
4834
  updateRemoteMirror() {
4192
- this.remoteBinding?.setMirrored(
4193
- this.dependencies.isMirrored()
4194
- );
4835
+ if (this.remoteTarget) {
4836
+ this.remoteTarget.isMirrored = this.dependencies.isMirrored();
4837
+ }
4195
4838
  }
4196
- /** 构造当前远端生成结果媒体流。 */
4839
+ /**
4840
+ * 构造当前远端生成结果媒体流。
4841
+ */
4197
4842
  makeRemoteStream() {
4198
4843
  const track = this.activeRemoteTrack;
4199
4844
  if (!track) {
@@ -4234,7 +4879,9 @@ var XmaxRealtimeConnectionManager = class {
4234
4879
  closeReason: session.closeReason
4235
4880
  });
4236
4881
  }
4237
- /** 尽力清理全部连接资源,即使退房失败也关闭服务端会话。 */
4882
+ /**
4883
+ * 尽力清理全部连接资源,即使退房失败也关闭服务端会话。
4884
+ */
4238
4885
  async disconnect() {
4239
4886
  const sessionID = this.activeSession?.id;
4240
4887
  this.stopHeartbeat();
@@ -4252,8 +4899,7 @@ var XmaxRealtimeConnectionManager = class {
4252
4899
  }
4253
4900
  this.activeSession = void 0;
4254
4901
  this.activeRemoteTrack = void 0;
4255
- this.remoteBinding = void 0;
4256
- this.targetSize = void 0;
4902
+ this.remoteTarget = void 0;
4257
4903
  if (sessionID && this.dependencies.sessionService) {
4258
4904
  try {
4259
4905
  await this.dependencies.sessionService.closeSession(sessionID);
@@ -4264,7 +4910,9 @@ var XmaxRealtimeConnectionManager = class {
4264
4910
  }
4265
4911
  return sessionID;
4266
4912
  }
4267
- /** 获取会话 Service;未配置 API 服务时抛出配置错误。 */
4913
+ /**
4914
+ * 获取会话 Service;未配置 API 服务时抛出配置错误。
4915
+ */
4268
4916
  requireSessionService() {
4269
4917
  if (!this.dependencies.sessionService) {
4270
4918
  throw new XmaxError(
@@ -4278,14 +4926,25 @@ var XmaxRealtimeConnectionManager = class {
4278
4926
 
4279
4927
  // src/Core/Realtime/XmaxRealtimeGenerationManager.ts
4280
4928
  var XmaxRealtimeGenerationManager = class _XmaxRealtimeGenerationManager {
4929
+ /**
4930
+ * 注入房间传输组件和任务标识生成器,不创建会话或启动生成。
4931
+ */
4281
4932
  constructor(streamController, taskIDGenerator = _XmaxRealtimeGenerationManager.createTaskID) {
4282
4933
  __publicField(this, "streamController", streamController);
4283
4934
  __publicField(this, "taskIDGenerator", taskIDGenerator);
4935
+ /**
4936
+ * 当前生成条件与任务标识
4937
+ */
4284
4938
  __publicField(this, "currentContext");
4285
4939
  __publicField(this, "activeTaskID");
4940
+ /**
4941
+ * 并发停止操作的共享结果
4942
+ */
4286
4943
  __publicField(this, "pendingStop");
4287
4944
  }
4288
- /** 首次请求在建立连接前校验条件;后续请求可复用缓存。 */
4945
+ /**
4946
+ * 首次请求在建立连接前校验条件;后续请求可复用缓存。
4947
+ */
4289
4948
  validateContext(context) {
4290
4949
  const resolved = context ?? this.currentContext;
4291
4950
  if (!resolved) {
@@ -4293,6 +4952,10 @@ var XmaxRealtimeGenerationManager = class _XmaxRealtimeGenerationManager {
4293
4952
  }
4294
4953
  return resolved;
4295
4954
  }
4955
+ /**
4956
+ * 发送生成指令并等待确认、远端就绪和音频激活,成功后返回任务标识。
4957
+ * 条件上下文在生成确认后缓存;启动失败时尽力停止本次任务并保留原始错误。
4958
+ */
4296
4959
  async start(options) {
4297
4960
  const context = this.validateContext(options.context);
4298
4961
  options.ensureCurrent();
@@ -4302,7 +4965,6 @@ var XmaxRealtimeGenerationManager = class _XmaxRealtimeGenerationManager {
4302
4965
  const confirmation = this.streamController.beginGeneration({
4303
4966
  taskID,
4304
4967
  videoFormat: options.videoFormat,
4305
- targetSize: options.targetSize,
4306
4968
  context
4307
4969
  });
4308
4970
  await this.awaitConfirmation(confirmation, options.signal);
@@ -4318,6 +4980,9 @@ var XmaxRealtimeGenerationManager = class _XmaxRealtimeGenerationManager {
4318
4980
  throw error;
4319
4981
  }
4320
4982
  }
4983
+ /**
4984
+ * 更新活动任务的生成条件;信令发送成功后才覆盖缓存,无任务或上下文时抛错。
4985
+ */
4321
4986
  update(taskID, options) {
4322
4987
  const context = options.context ?? this.currentContext;
4323
4988
  if (!taskID || !context) {
@@ -4326,11 +4991,13 @@ var XmaxRealtimeGenerationManager = class _XmaxRealtimeGenerationManager {
4326
4991
  this.streamController.updateGeneration({
4327
4992
  taskID,
4328
4993
  videoFormat: options.videoFormat,
4329
- targetSize: options.targetSize,
4330
4994
  context
4331
4995
  });
4332
4996
  this.currentContext = context;
4333
4997
  }
4998
+ /**
4999
+ * 幂等停止指定活动任务;并发调用共享结果,停止失败仅记录日志。
5000
+ */
4334
5001
  async stop(taskID = this.activeTaskID) {
4335
5002
  if (this.pendingStop && (!taskID || this.pendingStop.taskID === taskID)) {
4336
5003
  return this.pendingStop.completion;
@@ -4352,10 +5019,16 @@ var XmaxRealtimeGenerationManager = class _XmaxRealtimeGenerationManager {
4352
5019
  if (this.pendingStop?.completion === completion) this.pendingStop = void 0;
4353
5020
  }
4354
5021
  }
5022
+ /**
5023
+ * 清空缓存的生成条件并等待活动任务停止,供断连和关闭流程使用。
5024
+ */
4355
5025
  async reset(taskID) {
4356
5026
  this.currentContext = void 0;
4357
5027
  await this.stop(this.activeTaskID ?? (taskID || void 0));
4358
5028
  }
5029
+ /**
5030
+ * 等待生成确认;取消时立即拒绝,并在所有结束路径移除取消监听。
5031
+ */
4359
5032
  async awaitConfirmation(confirmation, signal) {
4360
5033
  await new Promise((resolve, reject) => {
4361
5034
  const cleanup = () => signal.removeEventListener("abort", abort);
@@ -4377,7 +5050,9 @@ var XmaxRealtimeGenerationManager = class _XmaxRealtimeGenerationManager {
4377
5050
  if (signal.aborted) abort();
4378
5051
  });
4379
5052
  }
4380
- /** 生成当前生成任务的唯一标识。 */
5053
+ /**
5054
+ * 生成当前生成任务的唯一标识。
5055
+ */
4381
5056
  static createTaskID() {
4382
5057
  const hex = crypto.randomUUID().replace(/-/g, "");
4383
5058
  let binary = "";
@@ -4400,19 +5075,32 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
4400
5075
  * 相机控制器与传输层共享同一个 RTC 引擎。
4401
5076
  */
4402
5077
  constructor(options, dependencies) {
4403
- // 业务配置
5078
+ /**
5079
+ * 业务配置
5080
+ */
4404
5081
  __publicField(this, "options");
4405
5082
  __publicField(this, "mediaService");
5083
+ /**
5084
+ * 插帧能力、用户配置与渲染版本
5085
+ */
4406
5086
  __publicField(this, "supportsInterpolation");
4407
5087
  __publicField(this, "interpolationRequested");
4408
5088
  __publicField(this, "interpolationSize");
4409
5089
  __publicField(this, "interpolationRevision", 0);
4410
- // 业务组件
5090
+ /**
5091
+ * 业务组件
5092
+ */
4411
5093
  __publicField(this, "coordinator");
4412
5094
  __publicField(this, "errorHandler");
4413
5095
  __publicField(this, "cameraController");
5096
+ /**
5097
+ * 启动计时与首帧通知
5098
+ */
4414
5099
  __publicField(this, "launchTimer", new RealtimeLaunchTimer());
4415
5100
  __publicField(this, "remoteFrameDisplayHandler");
5101
+ /**
5102
+ * 本地、远端和网络统计快照及观察者
5103
+ */
4416
5104
  __publicField(this, "localVideoStatistics");
4417
5105
  __publicField(this, "localVideoStatisticsListener");
4418
5106
  __publicField(this, "remoteVideoStatistics");
@@ -4420,11 +5108,15 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
4420
5108
  __publicField(this, "acceptsVideoStatistics", false);
4421
5109
  __publicField(this, "networkStatistics");
4422
5110
  __publicField(this, "networkStatisticsListener");
4423
- // 实时业务管理组件;状态所有权分别归连接和生成管理器。
5111
+ /**
5112
+ * 实时业务管理组件;状态所有权分别归连接和生成管理器。
5113
+ */
4424
5114
  __publicField(this, "connectionManager");
4425
5115
  __publicField(this, "generationManager");
4426
5116
  __publicField(this, "streamController");
4427
- // 音量配置
5117
+ /**
5118
+ * 音量配置
5119
+ */
4428
5120
  __publicField(this, "storedLocalAudioVolume", 1);
4429
5121
  __publicField(this, "storedRemoteAudioVolume", 0);
4430
5122
  this.options = options;
@@ -4435,7 +5127,7 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
4435
5127
  const rtcManager = dependencies?.rtcManager ?? new RtcManager();
4436
5128
  this.cameraController = dependencies?.cameraController ?? new CameraController({
4437
5129
  rtcManager,
4438
- mediaService: new MediaService(options.model),
5130
+ mediaService: this.mediaService,
4439
5131
  errorListener: (error) => {
4440
5132
  void this.errorHandler.report(error);
4441
5133
  }
@@ -4490,13 +5182,21 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
4490
5182
  }
4491
5183
  });
4492
5184
  }
4493
- /** 当前实时连接与生成状态。 */
5185
+ /**
5186
+ * 当前实时连接与生成状态。
5187
+ */
4494
5188
  get currentState() {
4495
5189
  return this.coordinator.currentState;
4496
5190
  }
5191
+ /**
5192
+ * 当前是否请求启用插帧;能力探测或运行期降级后可能为 false。
5193
+ */
4497
5194
  get isFrameInterpolationEnabled() {
4498
5195
  return this.interpolationRequested;
4499
5196
  }
5197
+ /**
5198
+ * 串行更新插帧开关;开启前验证能力,不支持时抛错并保留原配置,不改变回传尺寸。
5199
+ */
4500
5200
  async setFrameInterpolationEnabled(enabled) {
4501
5201
  await this.coordinator.run("configuration" /* configuration */, void 0, async (token) => {
4502
5202
  const format = this.cameraController.currentTrack?.videoFormat;
@@ -4511,6 +5211,9 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
4511
5211
  this.applyRemoteInterpolation();
4512
5212
  });
4513
5213
  }
5214
+ /**
5215
+ * 根据当前视频尺寸准备插帧,能力不可用时降级为原视频,并在应用前校验租约。
5216
+ */
4514
5217
  async prepareFrameInterpolation(format, token) {
4515
5218
  let size;
4516
5219
  if (this.interpolationRequested) {
@@ -4527,6 +5230,9 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
4527
5230
  this.interpolationSize = size;
4528
5231
  this.applyRemoteInterpolation();
4529
5232
  }
5233
+ /**
5234
+ * 在 5 秒预算内探测插帧能力;超时或探测失败返回 false,主动取消则拒绝。
5235
+ */
4530
5236
  checkInterpolationSupport(size, token) {
4531
5237
  return new Promise((resolve, reject) => {
4532
5238
  const finish = (supported) => {
@@ -4550,6 +5256,9 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
4550
5256
  void this.supportsInterpolation(size).then(finish, () => finish(false));
4551
5257
  });
4552
5258
  }
5259
+ /**
5260
+ * 将当前插帧配置同步到远端绑定,并用版本号忽略旧管线的迟到失败。
5261
+ */
4553
5262
  applyRemoteInterpolation() {
4554
5263
  const revision = ++this.interpolationRevision;
4555
5264
  const size = this.interpolationSize;
@@ -4561,6 +5270,9 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
4561
5270
  }
4562
5271
  } : void 0);
4563
5272
  }
5273
+ /**
5274
+ * 关闭已失败的插帧配置并记录降级原因,保持原始视频渲染可用。
5275
+ */
4564
5276
  handleInterpolationFailure(error) {
4565
5277
  this.interpolationRequested = false;
4566
5278
  this.interpolationSize = void 0;
@@ -4568,11 +5280,15 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
4568
5280
  XmaxLogger.render.warning(() => `\u63D2\u5E27\u5DF2\u964D\u7EA7 (Frame Interpolation Disabled)
4569
5281
  \u2514\u2500 ${XmaxError.from(error).message}`);
4570
5282
  }
4571
- /** 当前本地媒体预览音量,取值范围为 `0...1`。 */
5283
+ /**
5284
+ * 当前本地媒体预览音量,取值范围为 `0...1`。
5285
+ */
4572
5286
  get localAudioVolume() {
4573
5287
  return this.storedLocalAudioVolume;
4574
5288
  }
4575
- /** 当前远端生成音频播放音量,取值范围为 `0...1`。 */
5289
+ /**
5290
+ * 当前远端生成音频播放音量,取值范围为 `0...1`。
5291
+ */
4576
5292
  get remoteAudioVolume() {
4577
5293
  return this.storedRemoteAudioVolume;
4578
5294
  }
@@ -4584,20 +5300,29 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
4584
5300
  async setStateListener(listener) {
4585
5301
  await this.coordinator.setStateListener(listener);
4586
5302
  }
4587
- /** 监听启动耗时;设置后立即回放,传 undefined 取消监听。 */
5303
+ /**
5304
+ * 监听启动耗时;设置后立即回放,传 undefined 取消监听。
5305
+ */
4588
5306
  async setLaunchTimingListener(listener) {
4589
5307
  this.launchTimer.setListener(listener);
4590
5308
  }
4591
- /** 监听本地主视频流的实际运行统计;立即回放最新快照。 */
5309
+ /**
5310
+ * 监听本地主视频流的实际运行统计;立即回放最新快照。
5311
+ */
4592
5312
  async setLocalVideoStatisticsListener(listener) {
4593
5313
  this.localVideoStatisticsListener = listener;
4594
5314
  this.notifyLocalVideoStatistics();
4595
5315
  }
4596
- /** 监听当前生成结果流的实际运行统计;立即回放最新快照。 */
5316
+ /**
5317
+ * 监听当前生成结果流的实际运行统计;立即回放最新快照。
5318
+ */
4597
5319
  async setRemoteVideoStatisticsListener(listener) {
4598
5320
  this.remoteVideoStatisticsListener = listener;
4599
5321
  this.notifyRemoteVideoStatistics();
4600
5322
  }
5323
+ /**
5324
+ * 回放当前远端视频统计,隔离观察者的同步和异步异常。
5325
+ */
4601
5326
  notifyRemoteVideoStatistics() {
4602
5327
  try {
4603
5328
  void Promise.resolve(this.remoteVideoStatisticsListener?.(this.remoteVideoStatistics)).catch(() => {
@@ -4605,11 +5330,16 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
4605
5330
  } catch {
4606
5331
  }
4607
5332
  }
4608
- /** 网络统计立即回放最新快照,无数据或断开后为 undefined。 */
5333
+ /**
5334
+ * 网络统计立即回放最新快照,无数据或断开后为 undefined。
5335
+ */
4609
5336
  async setNetworkStatisticsListener(listener) {
4610
5337
  this.networkStatisticsListener = listener;
4611
5338
  this.notifyNetworkStatistics();
4612
5339
  }
5340
+ /**
5341
+ * 回放当前网络统计,隔离观察者的同步和异步异常。
5342
+ */
4613
5343
  notifyNetworkStatistics() {
4614
5344
  try {
4615
5345
  void Promise.resolve(this.networkStatisticsListener?.(this.networkStatistics)).catch(() => {
@@ -4617,6 +5347,9 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
4617
5347
  } catch {
4618
5348
  }
4619
5349
  }
5350
+ /**
5351
+ * 清除远端视频统计;原先存在数据时通知观察者数据已失效。
5352
+ */
4620
5353
  clearRemoteVideoStatistics() {
4621
5354
  const hadStatistics = this.remoteVideoStatistics !== void 0;
4622
5355
  this.remoteVideoStatistics = void 0;
@@ -4624,6 +5357,9 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
4624
5357
  this.notifyRemoteVideoStatistics();
4625
5358
  }
4626
5359
  }
5360
+ /**
5361
+ * 回放当前本地视频统计,隔离观察者的同步和异步异常。
5362
+ */
4627
5363
  notifyLocalVideoStatistics() {
4628
5364
  try {
4629
5365
  void Promise.resolve(this.localVideoStatisticsListener?.(this.localVideoStatistics)).catch(() => {
@@ -4631,6 +5367,9 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
4631
5367
  } catch {
4632
5368
  }
4633
5369
  }
5370
+ /**
5371
+ * 停止接收媒体统计并清空各类快照,仅对曾有数据的统计发出清空通知。
5372
+ */
4634
5373
  clearVideoStatistics() {
4635
5374
  this.acceptsVideoStatistics = false;
4636
5375
  const hadNetworkStatistics = this.networkStatistics !== void 0;
@@ -4677,6 +5416,7 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
4677
5416
  *
4678
5417
  * 将返回的轨道绑定到预览视图;收到有效帧且视图已绑定后进入 `ready`。
4679
5418
  *
5419
+ * @param options.enableFrameValidation 是否在发布前检测帧亮度,默认 true;false 跳过检测及其等待。
4680
5420
  * @returns 包含本地相机视频轨道的媒体流。
4681
5421
  * @throws 模型不支持相机输入、配置无效、权限或采集启动失败时抛出错误。
4682
5422
  */
@@ -4711,7 +5451,9 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
4711
5451
  }
4712
5452
  );
4713
5453
  }
4714
- /** 停止本地相机流并释放本地预览与 RTC 资源。 */
5454
+ /**
5455
+ * 停止本地相机流并释放本地预览与 RTC 资源。
5456
+ */
4715
5457
  async stopLocalCameraStream() {
4716
5458
  await this.coordinator.run(
4717
5459
  "media" /* media */,
@@ -4752,8 +5494,8 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
4752
5494
  * 使用当前 Manager 创建的本地流建立实时连接。
4753
5495
  *
4754
5496
  * 创建实时会话、加入 RTC 房间并发布本地流,成功后启动会话心跳。
4755
- * 发布前并行检查相机亮度稳定性;5 秒内未通过检查时抛出
4756
- * `cameraExposureTimeout`,不发布媒体,接入方可改善光照后重试。
5497
+ * 启用帧检测时,发布前并行检查相机亮度,首张合格帧即放行;2 秒内未通过检查则
5498
+ * 记录警告并按当前画面继续发布,不因环境较暗而阻断连接。
4757
5499
  * 返回的远端媒体流在生成开始后承载远端生成画面。
4758
5500
  *
4759
5501
  * @param localStream 由 `createLocalCameraStream` 创建的本地媒体流。
@@ -4812,7 +5554,6 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
4812
5554
  if (currentState.connectionState === "Generating" /* generating */) {
4813
5555
  this.generationManager.update(currentState.taskID, {
4814
5556
  videoFormat,
4815
- targetSize: this.connectionManager.currentTargetSize,
4816
5557
  context: options.context
4817
5558
  });
4818
5559
  return this.connectionManager.makeRemoteStream();
@@ -4825,7 +5566,9 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
4825
5566
  }
4826
5567
  );
4827
5568
  }
4828
- /** 断开实时连接并保留当前本地媒体预览。 */
5569
+ /**
5570
+ * 断开实时连接并保留当前本地媒体预览。
5571
+ */
4829
5572
  async disconnect() {
4830
5573
  this.launchTimer.cancel();
4831
5574
  this.clearVideoStatistics();
@@ -4866,9 +5609,17 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
4866
5609
  token.signal.addEventListener("abort", abortExposure, { once: true });
4867
5610
  if (token.signal.aborted) abortExposure();
4868
5611
  try {
4869
- const cameraReady = this.cameraController.waitUntilExposureReady(exposureController.signal);
4870
- void cameraReady.catch(() => {
4871
- });
5612
+ let beforePublish;
5613
+ if (this.cameraController.isFrameValidationEnabled) {
5614
+ const completeFrameValidation = this.launchTimer.startFrameValidation();
5615
+ const cameraReady = this.cameraController.waitForValidCameraFrame(exposureController.signal).then(() => {
5616
+ token.ensureCurrent();
5617
+ completeFrameValidation();
5618
+ });
5619
+ void cameraReady.catch(() => {
5620
+ });
5621
+ beforePublish = () => cameraReady;
5622
+ }
4872
5623
  const remote = await this.connectionManager.connect({
4873
5624
  localTrack,
4874
5625
  model: this.options.model,
@@ -4877,7 +5628,7 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
4877
5628
  onPublished: () => {
4878
5629
  this.acceptsVideoStatistics = true;
4879
5630
  },
4880
- beforePublish: () => cameraReady
5631
+ beforePublish
4881
5632
  });
4882
5633
  await this.coordinator.commit(
4883
5634
  new RealtimeState({
@@ -4892,7 +5643,9 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
4892
5643
  abortExposure();
4893
5644
  }
4894
5645
  }
4895
- /** 编排插帧、首帧计时和生成状态,任务与确认由生成管理器负责。 */
5646
+ /**
5647
+ * 编排插帧、首帧计时和生成状态,任务与确认由生成管理器负责。
5648
+ */
4896
5649
  async performStartGeneration(token, videoFormat, context) {
4897
5650
  token.setFailureScope(0 /* connection */);
4898
5651
  await this.prepareFrameInterpolation(videoFormat, token);
@@ -4902,7 +5655,6 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
4902
5655
  };
4903
5656
  const taskID = await this.generationManager.start({
4904
5657
  videoFormat,
4905
- targetSize: this.connectionManager.currentTargetSize,
4906
5658
  context,
4907
5659
  signal: token.signal,
4908
5660
  ensureCurrent: () => token.ensureCurrent(),
@@ -4921,7 +5673,9 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
4921
5673
  );
4922
5674
  return this.connectionManager.makeRemoteStream();
4923
5675
  }
4924
- /** 心跳失败或会话失效:结束当前连接生命周期并通过最终状态给出原因。 */
5676
+ /**
5677
+ * 心跳失败或会话失效:结束当前连接生命周期并通过最终状态给出原因。
5678
+ */
4925
5679
  async handleHeartbeatFailure(sessionID, error) {
4926
5680
  await this.coordinator.terminateWithError(
4927
5681
  error,
@@ -4929,7 +5683,9 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
4929
5683
  () => this.connectionManager.currentSessionID === sessionID
4930
5684
  );
4931
5685
  }
4932
- /** 统一清理顺序:停止心跳与生成,再断开连接;all 额外释放本地媒体。 */
5686
+ /**
5687
+ * 统一清理顺序:停止心跳与生成,再断开连接;all 额外释放本地媒体。
5688
+ */
4933
5689
  async performCleanup(scope, taskID) {
4934
5690
  this.launchTimer.cancel();
4935
5691
  this.remoteFrameDisplayHandler = void 0;
@@ -4955,7 +5711,9 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
4955
5711
  hasLocalMedia: this.cameraController.currentTrack !== void 0
4956
5712
  };
4957
5713
  }
4958
- /** 校验音量取值范围为 `0...1`。 */
5714
+ /**
5715
+ * 校验音量取值范围为 `0...1`。
5716
+ */
4959
5717
  static validateVolume(volume) {
4960
5718
  if (!Number.isFinite(volume) || volume < 0 || volume > 1) {
4961
5719
  throw new XmaxError(
@@ -4976,11 +5734,23 @@ var XmaxConfiguration = class {
4976
5734
  * @param init.loggerOptions SDK 输出的日志类型;默认为空。
4977
5735
  */
4978
5736
  constructor(init) {
4979
- /** 调用 Xmax 服务使用的 API Key。 */
5737
+ /**
5738
+ * 服务身份与环境
5739
+ */
5740
+ /**
5741
+ * 调用 Xmax 服务使用的 API Key。
5742
+ */
4980
5743
  __publicField(this, "apiKey");
4981
- /** SDK 连接的服务环境,同时决定日志细项语言:国内为中文,海外为英文。 */
5744
+ /**
5745
+ * SDK 连接的服务环境,同时决定日志细项语言:国内为中文,海外为英文。
5746
+ */
4982
5747
  __publicField(this, "environment");
4983
- /** SDK 输出的日志类型;默认为不输出日志。 */
5748
+ /**
5749
+ * 日志配置
5750
+ */
5751
+ /**
5752
+ * SDK 输出的日志类型;默认为不输出日志。
5753
+ */
4984
5754
  __publicField(this, "loggerOptions");
4985
5755
  this.apiKey = init.apiKey.trim();
4986
5756
  this.environment = init.environment ?? "china" /* china */;
@@ -5009,9 +5779,16 @@ var XmaxClient = class {
5009
5779
  * 在实际调用时校验 API Key。
5010
5780
  */
5011
5781
  constructor(configuration) {
5012
- /** 客户端使用的全局配置。 */
5782
+ /**
5783
+ * 客户端配置
5784
+ */
5785
+ /**
5786
+ * 客户端使用的全局配置。
5787
+ */
5013
5788
  __publicField(this, "configuration");
5014
- // 服务层组件
5789
+ /**
5790
+ * 服务层组件
5791
+ */
5015
5792
  __publicField(this, "apiService");
5016
5793
  this.configuration = configuration;
5017
5794
  XmaxLogger.configure(
@@ -5035,9 +5812,9 @@ var XmaxClient = class {
5035
5812
  /**
5036
5813
  * 创建媒体处理与能力查询 Service。
5037
5814
  *
5038
- * @param model 媒体输入规则使用的模型,默认为 `x2-fast-1080p`。
5815
+ * @param model 媒体输入规则使用的模型,默认为 `x2.0-trtc`。
5039
5816
  */
5040
- createMediaService(model = "x2-fast-1080p" /* x2_fast_1080p */) {
5817
+ createMediaService(model = "x2.0-trtc" /* x2_0_trtc */) {
5041
5818
  return new MediaService(model);
5042
5819
  }
5043
5820
  /**
@@ -5057,19 +5834,33 @@ var RealtimeConfiguration = class {
5057
5834
  *
5058
5835
  * @param init.model 实时生成业务使用的模型。
5059
5836
  * @param init.isFrameInterpolationEnabled 是否默认开启远端生成画面的插帧。
5837
+ * @param init.frameInterpolation 插帧开关和显示帧率上限的详细配置。
5060
5838
  */
5061
5839
  constructor(init) {
5062
- /** 实时生成业务使用的模型。 */
5840
+ /**
5841
+ * 模型配置
5842
+ */
5843
+ /**
5844
+ * 实时生成业务使用的模型。
5845
+ */
5063
5846
  __publicField(this, "model");
5064
- /** 是否默认请求开启远端生成画面的插帧。 */
5847
+ /**
5848
+ * 远端插帧配置
5849
+ */
5850
+ /**
5851
+ * 是否默认请求开启远端生成画面的插帧。
5852
+ */
5065
5853
  __publicField(this, "isFrameInterpolationEnabled");
5854
+ /**
5855
+ * 已补全默认值的只读插帧配置。
5856
+ */
5066
5857
  __publicField(this, "frameInterpolation");
5067
5858
  this.model = init.model;
5068
5859
  const targetFrameRate = init.frameInterpolation?.targetFrameRate ?? 60;
5069
5860
  if (!Number.isFinite(targetFrameRate) || targetFrameRate < 1 || targetFrameRate > 60) {
5070
5861
  throw new XmaxError("INVALID_CONFIGURATION" /* invalidConfiguration */, "Interpolation targetFrameRate must be between 1 and 60");
5071
5862
  }
5072
- this.isFrameInterpolationEnabled = init.frameInterpolation?.enabled ?? init.isFrameInterpolationEnabled ?? true;
5863
+ this.isFrameInterpolationEnabled = init.frameInterpolation?.enabled ?? init.isFrameInterpolationEnabled ?? false;
5073
5864
  this.frameInterpolation = Object.freeze({ enabled: this.isFrameInterpolationEnabled, targetFrameRate });
5074
5865
  }
5075
5866
  };
@@ -5083,15 +5874,24 @@ var VideoContentMode = /* @__PURE__ */ ((VideoContentMode2) => {
5083
5874
 
5084
5875
  // src/Service/Realtime/RealtimeContext.ts
5085
5876
  var RealtimeContext = class {
5086
- /** 创建实时生成条件并规范化文本和参考路径。 */
5877
+ /**
5878
+ * 创建实时生成条件并规范化文本和参考路径。
5879
+ */
5087
5880
  constructor(init) {
5088
- /** 实时生成使用的文本条件。 */
5881
+ /**
5882
+ * 生成条件
5883
+ */
5884
+ /**
5885
+ * 实时生成使用的文本条件。
5886
+ */
5089
5887
  __publicField(this, "prompt");
5090
- /** 已上传参考图片的可选远端路径。 */
5888
+ /**
5889
+ * 已上传参考图片的可选远端路径。
5890
+ */
5091
5891
  __publicField(this, "referencePath");
5092
5892
  this.prompt = init.prompt.trim();
5093
5893
  const normalizedReferencePath = init.referencePath?.trim();
5094
- this.referencePath = normalizedReferencePath && normalizedReferencePath.length > 0 ? normalizedReferencePath : void 0;
5894
+ this.referencePath = normalizedReferencePath || void 0;
5095
5895
  }
5096
5896
  };
5097
5897
 
@@ -7067,7 +7867,7 @@ struct Output { @builtin(position) position: vec4f, @location(0) uv: vec2f }
7067
7867
  out.uv = vec2f((pos[i].x + 1) * 0.5, (1 - pos[i].y) * 0.5);
7068
7868
  return out;
7069
7869
  }`;
7070
- var FrameInterpolationManager = class _FrameInterpolationManager {
7870
+ var _FrameInterpolationManager = class _FrameInterpolationManager {
7071
7871
  constructor(device, canvas, size, onFailure) {
7072
7872
  __publicField(this, "device", device);
7073
7873
  __publicField(this, "canvas", canvas);
@@ -7075,7 +7875,7 @@ var FrameInterpolationManager = class _FrameInterpolationManager {
7075
7875
  __publicField(this, "onFailure", onFailure);
7076
7876
  __publicField(this, "runtime");
7077
7877
  __publicField(this, "textures", []);
7078
- __publicField(this, "current", 0);
7878
+ __publicField(this, "nextOriginal", 0);
7079
7879
  __publicField(this, "capturePipeline");
7080
7880
  __publicField(this, "displayPipeline");
7081
7881
  __publicField(this, "sampler");
@@ -7140,7 +7940,7 @@ var FrameInterpolationManager = class _FrameInterpolationManager {
7140
7940
  throw new Error("Interpolation initialization cancelled");
7141
7941
  }
7142
7942
  this.runtime = runtime;
7143
- for (let i = 0; i < 3; i++) {
7943
+ for (let i = 0; i < _FrameInterpolationManager.originalSlots + 1; i++) {
7144
7944
  this.textures.push(device.createTexture({
7145
7945
  size: [w, h],
7146
7946
  format: "rgba8unorm",
@@ -7183,21 +7983,21 @@ var FrameInterpolationManager = class _FrameInterpolationManager {
7183
7983
  }
7184
7984
  }
7185
7985
  capture(video) {
7186
- this.current = 1 - this.current;
7187
- this.draw(this.capturePipeline, this.device.importExternalTexture({ source: video }), this.textures[this.current].createView());
7188
- }
7189
- presentPrevious() {
7190
- this.present(this.textures[1 - this.current]);
7191
- }
7192
- async interpolate() {
7193
- this.runtime.prepPair(this.textures[1 - this.current], this.textures[this.current]);
7194
- this.runtime.runT(0.5, this.textures[2]);
7986
+ const slot = this.nextOriginal;
7987
+ this.nextOriginal = (this.nextOriginal + 1) % _FrameInterpolationManager.originalSlots;
7988
+ this.draw(this.capturePipeline, this.device.importExternalTexture({ source: video }), this.textures[slot].createView());
7989
+ return slot;
7990
+ }
7991
+ async interpolate(previous, current) {
7992
+ this.runtime.prepPair(this.textures[previous], this.textures[current]);
7993
+ this.runtime.runT(0.5, this.textures[_FrameInterpolationManager.originalSlots]);
7195
7994
  await this.device.queue.onSubmittedWorkDone();
7995
+ return _FrameInterpolationManager.originalSlots;
7196
7996
  }
7197
- presentInterpolated() {
7198
- this.present(this.textures[2]);
7997
+ present(slot) {
7998
+ this.presentTexture(this.textures[slot]);
7199
7999
  }
7200
- present(texture) {
8000
+ presentTexture(texture) {
7201
8001
  this.draw(this.displayPipeline, texture.createView(), this.context.getCurrentTexture().createView());
7202
8002
  }
7203
8003
  draw(pipeline, source, target) {
@@ -7225,6 +8025,9 @@ var FrameInterpolationManager = class _FrameInterpolationManager {
7225
8025
  this.device.destroy();
7226
8026
  }
7227
8027
  };
8028
+ /** 原帧环形缓冲的槽位数;插值结果固定在最后一个槽位。 */
8029
+ __publicField(_FrameInterpolationManager, "originalSlots", 3);
8030
+ var FrameInterpolationManager = _FrameInterpolationManager;
7228
8031
 
7229
8032
  // src/Render/Video/RemoteVideoFramePipeline.ts
7230
8033
  var RemoteVideoFramePipeline = class {
@@ -7236,18 +8039,35 @@ var RemoteVideoFramePipeline = class {
7236
8039
  __publicField(this, "processor");
7237
8040
  __publicField(this, "abort", new AbortController());
7238
8041
  __publicField(this, "callback");
7239
- __publicField(this, "midpointTimer");
8042
+ __publicField(this, "raf");
7240
8043
  __publicField(this, "stallTimer");
7241
8044
  __publicField(this, "initializationTimer");
7242
- __publicField(this, "animation");
7243
8045
  __publicField(this, "loading", false);
7244
8046
  __publicField(this, "busy", false);
7245
8047
  __publicField(this, "stopped", false);
7246
- __publicField(this, "pair", 0);
8048
+ __publicField(this, "generation", 0);
8049
+ /** 播放时钟锚点:最近一次输入帧的媒体时间与对应的 vsync 时刻。 */
8050
+ __publicField(this, "anchor");
8051
+ /** 输出延迟,取最近一次有效源帧间隔,为中间帧留出计算窗口。 */
8052
+ __publicField(this, "delay", 0);
8053
+ __publicField(this, "queue", []);
8054
+ __publicField(this, "displayedTime");
7247
8055
  __publicField(this, "previousTime");
8056
+ __publicField(this, "previousSlot");
7248
8057
  __publicField(this, "previousPresented");
7249
8058
  __publicField(this, "slowPairs", 0);
7250
8059
  __publicField(this, "active", false);
8060
+ /** vsync 显示节拍:弹出到点的帧并显示最新的一张。 */
8061
+ __publicField(this, "tick", (now) => {
8062
+ if (this.stopped) return;
8063
+ this.raf = requestAnimationFrame(this.tick);
8064
+ const position = this.position(now);
8065
+ let frame;
8066
+ while (this.queue.length > 0 && this.queue[0].time <= position) {
8067
+ frame = this.queue.shift();
8068
+ }
8069
+ if (frame) this.present(frame);
8070
+ });
7251
8071
  }
7252
8072
  start() {
7253
8073
  if (this.stopped || this.callback !== void 0) return;
@@ -7259,78 +8079,101 @@ var RemoteVideoFramePipeline = class {
7259
8079
  }
7260
8080
  observe() {
7261
8081
  if (this.stopped) return;
7262
- this.callback = this.video.requestVideoFrameCallback((_now, metadata) => {
8082
+ this.callback = this.video.requestVideoFrameCallback((now, metadata) => {
7263
8083
  this.callback = void 0;
7264
8084
  if (this.stopped) return;
7265
8085
  try {
7266
- this.frame(metadata);
8086
+ this.frame(now, metadata);
7267
8087
  } catch (error) {
7268
8088
  this.fail(error);
7269
8089
  }
7270
8090
  this.observe();
7271
8091
  });
7272
8092
  }
7273
- frame(metadata) {
8093
+ frame(now, metadata) {
7274
8094
  if (typeof document !== "undefined" && document.hidden) {
7275
- this.resetPair();
8095
+ this.resetTimeline();
7276
8096
  return;
7277
8097
  }
7278
8098
  if (metadata.width !== this.options.size.width || metadata.height !== this.options.size.height) {
7279
- this.resetPair();
8099
+ this.resetTimeline();
7280
8100
  return;
7281
8101
  }
7282
8102
  if (!this.processor) {
7283
8103
  if (!this.loading) void this.initialize();
7284
8104
  return;
7285
8105
  }
7286
- if (this.busy) return;
7287
- const interval = this.previousTime === void 0 ? 0 : (metadata.mediaTime - this.previousTime) * 1e3;
8106
+ const time = metadata.mediaTime * 1e3;
8107
+ if (!Number.isFinite(time) || this.previousTime !== void 0 && time <= this.previousTime) return;
8108
+ const previous = this.previousTime;
8109
+ const interval = previous === void 0 ? 0 : time - previous;
7288
8110
  const consecutive = this.previousPresented === void 0 || metadata.presentedFrames === this.previousPresented + 1;
7289
- this.clearPresentation();
7290
- const pair = ++this.pair;
7291
- this.processor.capture(this.video);
7292
- this.previousTime = metadata.mediaTime;
8111
+ const previousSlot = this.previousSlot;
8112
+ this.anchor = { media: time, at: now };
8113
+ this.previousTime = time;
7293
8114
  this.previousPresented = metadata.presentedFrames;
7294
- if (!consecutive || interval <= 0 || interval > 250 || interval < 2e3 / this.options.targetFrameRate - 1) {
7295
- this.setActive(false);
7296
- this.canvas.style.visibility = "hidden";
8115
+ const slot = this.processor.capture(this.video);
8116
+ this.previousSlot = slot;
8117
+ if (previous === void 0 || interval > 250) {
8118
+ this.queue = [];
8119
+ this.delay = 0;
8120
+ this.present({ slot, time });
8121
+ this.startTick();
7297
8122
  return;
7298
8123
  }
8124
+ this.delay = interval;
8125
+ this.enqueue({ slot, time });
8126
+ this.startTick();
8127
+ if (!consecutive || interval < 2e3 / this.options.targetFrameRate - 1 || previousSlot === void 0 || this.busy) return;
8128
+ this.interpolate(previousSlot, slot, previous, time, interval);
8129
+ }
8130
+ /** 帧按显示时间升序入队;同一时间的内容后到先出。 */
8131
+ enqueue(frame) {
8132
+ const index = this.queue.findIndex((queued) => queued.time >= frame.time);
8133
+ if (index < 0) this.queue.push(frame);
8134
+ else this.queue.splice(index, 0, frame);
8135
+ }
8136
+ interpolate(previousSlot, currentSlot, previousTime, currentTime, interval) {
8137
+ const generation = this.generation;
8138
+ const midpoint = (previousTime + currentTime) / 2;
7299
8139
  const startedAt = performance.now();
7300
- this.processor.presentPrevious();
7301
- this.canvas.style.visibility = "visible";
7302
8140
  this.busy = true;
7303
8141
  this.stallTimer = setTimeout(() => {
7304
8142
  if (this.busy) this.fail(new Error("Interpolation GPU submission timed out"));
7305
- else this.resetPair();
7306
8143
  }, Math.max(100, interval * 3));
7307
- void this.processor.interpolate().then(() => {
8144
+ void this.processor.interpolate(previousSlot, currentSlot).then((slot) => {
7308
8145
  if (this.stopped) return;
7309
8146
  this.busy = false;
7310
- if (pair !== this.pair) return;
8147
+ clearTimeout(this.stallTimer);
8148
+ this.stallTimer = void 0;
8149
+ if (generation !== this.generation) return;
7311
8150
  const elapsed = performance.now() - startedAt;
7312
8151
  this.slowPairs = elapsed > interval / 2 ? this.slowPairs + 1 : 0;
7313
8152
  if (this.slowPairs >= 5) {
7314
8153
  this.fail(new Error("Interpolation exceeded the GPU frame budget for 5 consecutive pairs"));
7315
8154
  return;
7316
8155
  }
7317
- if (elapsed >= interval) return;
7318
- this.midpointTimer = setTimeout(() => {
7319
- this.animation = requestAnimationFrame(() => {
7320
- this.animation = void 0;
7321
- if (this.stopped || pair !== this.pair || performance.now() - startedAt >= interval) return;
7322
- try {
7323
- this.processor.presentInterpolated();
7324
- this.setActive(true);
7325
- } catch (error) {
7326
- this.fail(error);
7327
- }
7328
- });
7329
- }, Math.max(0, interval / 2 - elapsed));
8156
+ if (this.position(performance.now()) >= midpoint) return;
8157
+ this.enqueue({ slot, time: midpoint });
7330
8158
  }).catch((error) => {
7331
8159
  if (!this.stopped) this.fail(error);
7332
8160
  });
7333
8161
  }
8162
+ /** 显示时钟位置:锚点媒体时间随墙钟推进,整体后移一个源帧间隔。 */
8163
+ position(now) {
8164
+ if (!this.anchor) return -Infinity;
8165
+ return this.anchor.media + (now - this.anchor.at) - this.delay;
8166
+ }
8167
+ startTick() {
8168
+ if (this.raf === void 0) this.raf = requestAnimationFrame(this.tick);
8169
+ }
8170
+ present(frame) {
8171
+ if (this.displayedTime === frame.time) return;
8172
+ this.processor.present(frame.slot);
8173
+ this.displayedTime = frame.time;
8174
+ this.canvas.style.visibility = "visible";
8175
+ this.setActive(true);
8176
+ }
7334
8177
  async initialize() {
7335
8178
  this.loading = true;
7336
8179
  this.initializationTimer = setTimeout(() => this.fail(new Error("Interpolation initialization timed out")), 15e3);
@@ -7359,16 +8202,18 @@ var RemoteVideoFramePipeline = class {
7359
8202
  this.active = active;
7360
8203
  this.options.onActiveChange?.(active);
7361
8204
  }
7362
- clearPresentation() {
7363
- clearTimeout(this.midpointTimer);
8205
+ resetTimeline() {
8206
+ this.generation++;
8207
+ this.queue = [];
7364
8208
  clearTimeout(this.stallTimer);
7365
- if (this.animation !== void 0) cancelAnimationFrame(this.animation);
7366
- this.animation = void 0;
7367
- }
7368
- resetPair() {
7369
- this.pair++;
7370
- this.clearPresentation();
8209
+ this.stallTimer = void 0;
8210
+ if (this.raf !== void 0) cancelAnimationFrame(this.raf);
8211
+ this.raf = void 0;
8212
+ this.anchor = void 0;
8213
+ this.delay = 0;
8214
+ this.displayedTime = void 0;
7371
8215
  this.previousTime = void 0;
8216
+ this.previousSlot = void 0;
7372
8217
  this.previousPresented = void 0;
7373
8218
  this.canvas.style.visibility = "hidden";
7374
8219
  this.setActive(false);
@@ -7385,7 +8230,7 @@ var RemoteVideoFramePipeline = class {
7385
8230
  if (this.callback !== void 0) this.video.cancelVideoFrameCallback(this.callback);
7386
8231
  this.callback = void 0;
7387
8232
  clearTimeout(this.initializationTimer);
7388
- this.resetPair();
8233
+ this.resetTimeline();
7389
8234
  this.processor?.destroy();
7390
8235
  this.processor = void 0;
7391
8236
  }
@@ -7403,25 +8248,46 @@ var XmaxVideoView = class {
7403
8248
  * @param options.videoContentMode 视频内容显示模式,默认 fill。
7404
8249
  */
7405
8250
  constructor(options) {
7406
- // 视图元素
7407
- /** 视图根元素(div,内含一个 video 元素)。 */
8251
+ /**
8252
+ * 视图元素
8253
+ */
8254
+ /**
8255
+ * 视图根元素(div,内含一个 video 元素)。
8256
+ */
7408
8257
  __publicField(this, "element");
7409
- /** @internal 渲染视频帧的元素,供绑定管线使用。 */
8258
+ /**
8259
+ * @internal 渲染视频帧的元素,供绑定管线使用。
8260
+ */
7410
8261
  __publicField(this, "videoElement");
7411
- // 公共配置
7412
- /** 首帧实际提交显示时调用(一次性;换轨后重新武装)。 */
8262
+ /**
8263
+ * 公共配置
8264
+ */
8265
+ /**
8266
+ * 首帧实际提交显示时调用(一次性;换轨后重新武装)。
8267
+ */
7413
8268
  __publicField(this, "frameDisplayHandler");
7414
- // 渲染状态
8269
+ /**
8270
+ * 渲染状态
8271
+ */
7415
8272
  __publicField(this, "currentTrack");
7416
8273
  __publicField(this, "contentMode");
7417
8274
  __publicField(this, "mirrored", false);
8275
+ /**
8276
+ * 首帧显示通知与异步回调版本
8277
+ */
7418
8278
  __publicField(this, "hasNotifiedFrameDisplay", false);
7419
8279
  __publicField(this, "onPlaying");
7420
8280
  __publicField(this, "videoFrameCallbackID");
7421
8281
  __publicField(this, "frameNotificationSequence", 0);
8282
+ /**
8283
+ * 远端插帧配置与渲染资源
8284
+ */
7422
8285
  __publicField(this, "interpolationOptions");
7423
8286
  __publicField(this, "interpolationPipeline");
7424
8287
  __publicField(this, "interpolationCanvas");
8288
+ /**
8289
+ * DOM 挂载状态
8290
+ */
7425
8291
  __publicField(this, "detached", false);
7426
8292
  this.contentMode = options?.videoContentMode ?? "fill" /* fill */;
7427
8293
  this.element = document.createElement("div");
@@ -7439,10 +8305,15 @@ var XmaxVideoView = class {
7439
8305
  this.applyContentMode();
7440
8306
  this.element.appendChild(this.videoElement);
7441
8307
  }
7442
- /** 当前显示的视频轨道;切换时自动解绑旧轨道。 */
8308
+ /**
8309
+ * 当前显示的视频轨道;切换时自动解绑旧轨道。
8310
+ */
7443
8311
  get track() {
7444
8312
  return this.currentTrack;
7445
8313
  }
8314
+ /**
8315
+ * 解绑旧轨道后接入新轨道,并为新内容重新启用一次性首帧通知。
8316
+ */
7446
8317
  set track(track) {
7447
8318
  if (this.currentTrack === track) {
7448
8319
  return;
@@ -7454,10 +8325,15 @@ var XmaxVideoView = class {
7454
8325
  this.attachTrack(track);
7455
8326
  }
7456
8327
  }
7457
- /** 视频内容在容器中的显示模式。 */
8328
+ /**
8329
+ * 视频内容在容器中的显示模式。
8330
+ */
7458
8331
  get videoContentMode() {
7459
8332
  return this.contentMode;
7460
8333
  }
8334
+ /**
8335
+ * 更新视频及插帧画布的内容缩放方式;相同配置不重复应用。
8336
+ */
7461
8337
  set videoContentMode(mode) {
7462
8338
  if (this.contentMode === mode) {
7463
8339
  return;
@@ -7465,10 +8341,15 @@ var XmaxVideoView = class {
7465
8341
  this.contentMode = mode;
7466
8342
  this.applyContentMode();
7467
8343
  }
7468
- /** 本地预览是否镜像显示(仅影响显示,不影响发布流)。 */
8344
+ /**
8345
+ * 本地预览是否镜像显示(仅影响显示,不影响发布流)。
8346
+ */
7469
8347
  get isMirrored() {
7470
8348
  return this.mirrored;
7471
8349
  }
8350
+ /**
8351
+ * 更新视图镜像变换,仅改变呈现方式,不改动媒体流。
8352
+ */
7472
8353
  set isMirrored(mirrored) {
7473
8354
  if (this.mirrored === mirrored) {
7474
8355
  return;
@@ -7476,7 +8357,9 @@ var XmaxVideoView = class {
7476
8357
  this.mirrored = mirrored;
7477
8358
  this.applyContentMode();
7478
8359
  }
7479
- /** 挂载到容器元素。 */
8360
+ /**
8361
+ * 挂载到容器元素。
8362
+ */
7480
8363
  attach(to) {
7481
8364
  this.detached = false;
7482
8365
  to.appendChild(this.element);
@@ -7491,14 +8374,18 @@ var XmaxVideoView = class {
7491
8374
  this.armFrameDisplayNotification();
7492
8375
  }
7493
8376
  }
7494
- /** 从容器元素移除。 */
8377
+ /**
8378
+ * 从容器元素移除。
8379
+ */
7495
8380
  detach() {
7496
8381
  this.detached = true;
7497
8382
  this.stopInterpolation();
7498
8383
  this.cancelFrameDisplayNotification();
7499
8384
  this.element.remove();
7500
8385
  }
7501
- /** 直接设置渲染用的媒体流(绑定管线和直接渲染共用)。 @internal */
8386
+ /**
8387
+ * 直接设置渲染用的媒体流(绑定管线和直接渲染共用)。 @internal
8388
+ */
7502
8389
  setMediaStream(stream) {
7503
8390
  if (this.videoElement.srcObject === stream) {
7504
8391
  return;
@@ -7512,13 +8399,18 @@ var XmaxVideoView = class {
7512
8399
  this.startInterpolation();
7513
8400
  }
7514
8401
  }
7515
- /** 由远端绑定注入配置;本地预览不走此管线。 @internal */
8402
+ /**
8403
+ * 由远端绑定注入配置;本地预览不走此管线。 @internal
8404
+ */
7516
8405
  setFrameInterpolation(options) {
7517
8406
  if (options === this.interpolationOptions) return;
7518
8407
  this.stopInterpolation();
7519
8408
  this.interpolationOptions = options;
7520
8409
  this.startInterpolation();
7521
8410
  }
8411
+ /**
8412
+ * 视图已挂载且媒体与配置就绪时创建插帧画布和管线,避免重复启动。
8413
+ */
7522
8414
  startInterpolation() {
7523
8415
  if (this.detached || this.interpolationPipeline || !this.interpolationOptions || !this.videoElement.srcObject) return;
7524
8416
  const canvas = document.createElement("canvas");
@@ -7538,6 +8430,9 @@ var XmaxVideoView = class {
7538
8430
  this.interpolationPipeline = pipeline;
7539
8431
  pipeline.start();
7540
8432
  }
8433
+ /**
8434
+ * 停止并释放插帧管线,移除辅助画布,保留原始视频元素。
8435
+ */
7541
8436
  stopInterpolation() {
7542
8437
  const pipeline = this.interpolationPipeline;
7543
8438
  this.interpolationPipeline = void 0;
@@ -7545,7 +8440,9 @@ var XmaxVideoView = class {
7545
8440
  this.interpolationCanvas?.remove();
7546
8441
  this.interpolationCanvas = void 0;
7547
8442
  }
7548
- /** 接入轨道画面:优先走注册的渲染绑定,否则按媒体轨直接渲染。 */
8443
+ /**
8444
+ * 接入轨道画面:优先走注册的渲染绑定,否则按媒体轨直接渲染。
8445
+ */
7549
8446
  attachTrack(track) {
7550
8447
  const binding = VideoRenderRegistry.binding(track);
7551
8448
  if (binding) {
@@ -7560,7 +8457,9 @@ var XmaxVideoView = class {
7560
8457
  () => `\u8F68\u9053 ${track.id} \u65E2\u6CA1\u6709\u6E32\u67D3\u7ED1\u5B9A\u4E5F\u6CA1\u6709\u5A92\u4F53\u8F68\uFF0C\u65E0\u6CD5\u663E\u793A`
7561
8458
  );
7562
8459
  }
7563
- /** 解绑当前轨道并清空渲染内容。 */
8460
+ /**
8461
+ * 解绑当前轨道并清空渲染内容。
8462
+ */
7564
8463
  detachCurrentTrack() {
7565
8464
  const track = this.currentTrack;
7566
8465
  if (track) {
@@ -7570,7 +8469,9 @@ var XmaxVideoView = class {
7570
8469
  this.cancelFrameDisplayNotification();
7571
8470
  this.videoElement.srcObject = null;
7572
8471
  }
7573
- /** 首帧提交合成器时通知;旧浏览器回退到 playing 近似判断。 */
8472
+ /**
8473
+ * 首帧提交合成器时通知;旧浏览器回退到 playing 近似判断。
8474
+ */
7574
8475
  armFrameDisplayNotification() {
7575
8476
  this.cancelFrameDisplayNotification();
7576
8477
  const sequence = this.frameNotificationSequence;
@@ -7597,7 +8498,9 @@ var XmaxVideoView = class {
7597
8498
  queueMicrotask(notify);
7598
8499
  }
7599
8500
  }
7600
- /** 换流、解绑或卸载时取消观察,防止迟到的首帧归入下一条流。 */
8501
+ /**
8502
+ * 换流、解绑或卸载时取消观察,防止迟到的首帧归入下一条流。
8503
+ */
7601
8504
  cancelFrameDisplayNotification() {
7602
8505
  this.frameNotificationSequence += 1;
7603
8506
  if (this.videoFrameCallbackID !== void 0) {
@@ -7609,7 +8512,9 @@ var XmaxVideoView = class {
7609
8512
  this.onPlaying = void 0;
7610
8513
  }
7611
8514
  }
7612
- /** 应用显示模式与镜像样式。 */
8515
+ /**
8516
+ * 应用显示模式与镜像样式。
8517
+ */
7613
8518
  applyContentMode() {
7614
8519
  this.videoElement.style.objectFit = OBJECT_FIT[this.contentMode];
7615
8520
  this.videoElement.style.transform = this.mirrored ? "scaleX(-1)" : "";
@@ -7631,13 +8536,21 @@ var XmaxRealtimeVideoView = class {
7631
8536
  * @param options.videoContentMode 本地与远端视频的内容显示模式,默认 fill。
7632
8537
  */
7633
8538
  constructor(options) {
7634
- // 视图元素
7635
- /** 视图根元素。 */
8539
+ /**
8540
+ * 视图元素
8541
+ */
8542
+ /**
8543
+ * 视图根元素。
8544
+ */
7636
8545
  __publicField(this, "element");
7637
- // 视频视图
8546
+ /**
8547
+ * 视频视图
8548
+ */
7638
8549
  __publicField(this, "localVideoView");
7639
8550
  __publicField(this, "remoteVideoView");
7640
- // 公共配置
8551
+ /**
8552
+ * 公共配置
8553
+ */
7641
8554
  __publicField(this, "currentLocalTrack");
7642
8555
  __publicField(this, "currentRemoteTrack");
7643
8556
  __publicField(this, "contentMode");
@@ -7658,10 +8571,15 @@ var XmaxRealtimeVideoView = class {
7658
8571
  this.localVideoView.track = this.currentLocalTrack;
7659
8572
  this.updateRemoteTrack();
7660
8573
  }
7661
- /** 当前显示的本地视频轨道。 */
8574
+ /**
8575
+ * 当前显示的本地视频轨道。
8576
+ */
7662
8577
  get localTrack() {
7663
8578
  return this.currentLocalTrack;
7664
8579
  }
8580
+ /**
8581
+ * 替换本地预览轨道;轨道未变化时保留当前绑定。
8582
+ */
7665
8583
  set localTrack(track) {
7666
8584
  if (this.currentLocalTrack === track) {
7667
8585
  return;
@@ -7676,6 +8594,9 @@ var XmaxRealtimeVideoView = class {
7676
8594
  get remoteTrack() {
7677
8595
  return this.currentRemoteTrack;
7678
8596
  }
8597
+ /**
8598
+ * 替换远端轨道并重新等待首帧渐入;清空轨道时恢复本地预览。
8599
+ */
7679
8600
  set remoteTrack(track) {
7680
8601
  if (this.currentRemoteTrack === track) {
7681
8602
  return;
@@ -7683,10 +8604,15 @@ var XmaxRealtimeVideoView = class {
7683
8604
  this.currentRemoteTrack = track;
7684
8605
  this.updateRemoteTrack();
7685
8606
  }
7686
- /** 视频内容在容器中的显示模式。 */
8607
+ /**
8608
+ * 视频内容在容器中的显示模式。
8609
+ */
7687
8610
  get videoContentMode() {
7688
8611
  return this.contentMode;
7689
8612
  }
8613
+ /**
8614
+ * 同步调整本地与远端视频在容器中的缩放和裁剪方式。
8615
+ */
7690
8616
  set videoContentMode(mode) {
7691
8617
  if (this.contentMode === mode) {
7692
8618
  return;
@@ -7695,26 +8621,37 @@ var XmaxRealtimeVideoView = class {
7695
8621
  this.localVideoView.videoContentMode = mode;
7696
8622
  this.remoteVideoView.videoContentMode = mode;
7697
8623
  }
7698
- /** 本地预览是否镜像显示(前置摄像头)。 */
8624
+ /**
8625
+ * 本地预览是否镜像显示(前置摄像头)。
8626
+ */
7699
8627
  get isLocalMirrored() {
7700
8628
  return this.localVideoView.isMirrored;
7701
8629
  }
8630
+ /**
8631
+ * 更新本地预览镜像;不修改采集帧或远端画面。
8632
+ */
7702
8633
  set isLocalMirrored(mirrored) {
7703
8634
  this.localVideoView.isMirrored = mirrored;
7704
8635
  }
7705
- /** 挂载到容器元素。 */
8636
+ /**
8637
+ * 挂载到容器元素。
8638
+ */
7706
8639
  attach(to) {
7707
8640
  to.appendChild(this.element);
7708
8641
  this.localVideoView.attach(this.element);
7709
8642
  this.remoteVideoView.attach(this.element);
7710
8643
  }
7711
- /** 从容器元素移除。 */
8644
+ /**
8645
+ * 从容器元素移除。
8646
+ */
7712
8647
  detach() {
7713
8648
  this.localVideoView.detach();
7714
8649
  this.remoteVideoView.detach();
7715
8650
  this.element.remove();
7716
8651
  }
7717
- /** 更新远端轨道绑定:重置渐入状态,等待首帧提交后再显示远端画面。 */
8652
+ /**
8653
+ * 更新远端轨道绑定:重置渐入状态,等待首帧提交后再显示远端画面。
8654
+ */
7718
8655
  updateRemoteTrack() {
7719
8656
  const remoteTrack = this.currentRemoteTrack;
7720
8657
  this.remoteVideoView.frameDisplayHandler = void 0;
@@ -7733,7 +8670,9 @@ var XmaxRealtimeVideoView = class {
7733
8670
  };
7734
8671
  this.remoteVideoView.track = remoteTrack;
7735
8672
  }
7736
- /** 远端首帧已提交:渐入远端画面。 */
8673
+ /**
8674
+ * 远端首帧已提交:渐入远端画面。
8675
+ */
7737
8676
  showRemoteVideo() {
7738
8677
  const element = this.remoteVideoView.element;
7739
8678
  if (element.style.visibility === "visible" && element.style.opacity === "1") {
@@ -7776,4 +8715,4 @@ export {
7776
8715
  XmaxVideoView,
7777
8716
  XmaxRealtimeVideoView
7778
8717
  };
7779
- //# sourceMappingURL=chunk-MJ72WWQX.js.map
8718
+ //# sourceMappingURL=chunk-6RIEDVRQ.js.map