@xmaxai/web-sdk 1.0.4 → 1.0.5

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.
@@ -12,9 +12,16 @@ var API_BASE_URLS = {
12
12
  ["china" /* china */]: "https://cloud.xmax.22duck.cn/open/api/v1",
13
13
  ["global" /* global */]: "https://api.xmax.cloud/open/api/v1"
14
14
  };
15
+ var NON_REALTIME_API_BASE_URLS = {
16
+ ["china" /* china */]: "https://api.xmaxai.com/open/api/v1",
17
+ ["global" /* global */]: "https://api.xmax.ai/open/api/v1"
18
+ };
15
19
  function apiBaseURL(environment) {
16
20
  return API_BASE_URLS[environment];
17
21
  }
22
+ function nonRealtimeApiBaseURL(environment) {
23
+ return NON_REALTIME_API_BASE_URLS[environment];
24
+ }
18
25
 
19
26
  // src/Foundation/Logging/XmaxLogger.ts
20
27
  var XmaxLoggerOption = /* @__PURE__ */ ((XmaxLoggerOption2) => {
@@ -198,7 +205,7 @@ var XmaxError = class _XmaxError extends Error {
198
205
  };
199
206
 
200
207
  // src/Foundation/Runtime/RuntimeInfo.ts
201
- var XMAX_SDK_VERSION = "1.0.4";
208
+ var XMAX_SDK_VERSION = "1.0.5";
202
209
  var _RuntimeInfo = class _RuntimeInfo {
203
210
  /**
204
211
  * 序列化为接口传输使用的 snake_case 字段。
@@ -268,8 +275,24 @@ var _ApiService = class _ApiService {
268
275
  */
269
276
  async request(method, path, body, options) {
270
277
  this.validateConfiguration();
278
+ if (_optionalChain([options, 'optionalAccess', _4 => _4.signal, 'optionalAccess', _5 => _5.aborted])) {
279
+ throw new XmaxError("CANCELLED" /* cancelled */, "API request was cancelled");
280
+ }
271
281
  const request = this.makeRequest(method, path, body, options);
272
282
  const startedAt = Date.now();
283
+ const timeoutSignal = request.init.signal;
284
+ const controller = new AbortController();
285
+ const abortTimeout = () => controller.abort(timeoutSignal.reason);
286
+ const abortCaller = () => controller.abort(new DOMException("Cancelled", "AbortError"));
287
+ timeoutSignal.addEventListener("abort", abortTimeout, { once: true });
288
+ _optionalChain([options, 'optionalAccess', _6 => _6.signal, 'optionalAccess', _7 => _7.addEventListener, 'call', _8 => _8("abort", abortCaller, { once: true })]);
289
+ request.init.signal = controller.signal;
290
+ if (timeoutSignal.aborted) {
291
+ abortTimeout();
292
+ }
293
+ if (_optionalChain([options, 'optionalAccess', _9 => _9.signal, 'optionalAccess', _10 => _10.aborted])) {
294
+ abortCaller();
295
+ }
273
296
  let status;
274
297
  let text;
275
298
  try {
@@ -280,6 +303,9 @@ var _ApiService = class _ApiService {
280
303
  const mapped = _ApiService.transportError(error);
281
304
  this.logFailure(method, path, mapped, startedAt);
282
305
  throw mapped;
306
+ } finally {
307
+ timeoutSignal.removeEventListener("abort", abortTimeout);
308
+ _optionalChain([options, 'optionalAccess', _11 => _11.signal, 'optionalAccess', _12 => _12.removeEventListener, 'call', _13 => _13("abort", abortCaller)]);
283
309
  }
284
310
  try {
285
311
  const value = _ApiService.parseResponse(text, status);
@@ -297,8 +323,8 @@ var _ApiService = class _ApiService {
297
323
  /**
298
324
  * 发送 GET 请求。
299
325
  */
300
- get(path) {
301
- return this.request("GET" /* get */, path);
326
+ get(path, options) {
327
+ return this.request("GET" /* get */, path, void 0, options);
302
328
  }
303
329
  /**
304
330
  * 发送 POST 请求。
@@ -388,7 +414,7 @@ var _ApiService = class _ApiService {
388
414
  },
389
415
  body: encodedBody,
390
416
  signal: AbortSignal.timeout(this.timeoutMs),
391
- ..._optionalChain([options, 'optionalAccess', _4 => _4.keepalive]) ? { keepalive: true } : {}
417
+ ..._optionalChain([options, 'optionalAccess', _14 => _14.keepalive]) ? { keepalive: true } : {}
392
418
  }
393
419
  };
394
420
  }
@@ -405,8 +431,9 @@ var _ApiService = class _ApiService {
405
431
  });
406
432
  }
407
433
  const ok = status >= 200 && status < 300;
408
- if (!ok || envelope.success !== true) {
409
- const message = _optionalChain([envelope, 'access', _5 => _5.message, 'optionalAccess', _6 => _6.trim, 'call', _7 => _7()]);
434
+ const succeeded = envelope.success === true || envelope.success === void 0 && envelope.code === 200;
435
+ if (!ok || !succeeded) {
436
+ const message = _optionalChain([envelope, 'access', _15 => _15.message, 'optionalAccess', _16 => _16.trim, 'call', _17 => _17()]);
410
437
  throw new XmaxError(
411
438
  "API_ERROR" /* apiError */,
412
439
  message || "Xmax API request failed",
@@ -561,6 +588,14 @@ var RealtimeVideoFormat = class _RealtimeVideoFormat {
561
588
  }
562
589
  };
563
590
 
591
+ // src/Foundation/RTC/RtcProvider.ts
592
+ var RtcProvider = /* @__PURE__ */ ((RtcProvider2) => {
593
+ RtcProvider2["trtc"] = "trtc";
594
+ RtcProvider2["agora"] = "agora";
595
+ RtcProvider2["vertc"] = "vertc";
596
+ return RtcProvider2;
597
+ })(RtcProvider || {});
598
+
564
599
  // src/Service/Realtime/RealtimeModel.ts
565
600
  var RealtimeModel = /* @__PURE__ */ ((RealtimeModel2) => {
566
601
  RealtimeModel2["x2_0"] = "x2.0";
@@ -569,12 +604,28 @@ var RealtimeModel = /* @__PURE__ */ ((RealtimeModel2) => {
569
604
  RealtimeModel2["x2_1_preview"] = "x2.1-preview";
570
605
  return RealtimeModel2;
571
606
  })(RealtimeModel || {});
607
+ function isKnownModel(model) {
608
+ if (typeof model !== "string" || !model.trim()) {
609
+ throw new XmaxError("INVALID_CONFIGURATION" /* invalidConfiguration */, "Realtime model must be a non-empty string");
610
+ }
611
+ return Object.values(RealtimeModel).includes(model);
612
+ }
613
+ function modelDefaults(model) {
614
+ return isKnownModel(model) ? model : "x2.1-preview" /* x2_1_preview */;
615
+ }
616
+ var MODEL_DISPLAY_NAMES = {
617
+ ["x2.0" /* x2_0 */]: "X2.0",
618
+ ["x2.0-trtc" /* x2_0_trtc */]: "X2.1-preview-trtc",
619
+ ["x2.0-agora" /* x2_0_agora */]: "X2.1-preview-agora",
620
+ ["x2.1-preview" /* x2_1_preview */]: "X2.1-preview-vertc"
621
+ };
572
622
  var MODEL_BASE_URLS = {
573
623
  ["x2.0-agora" /* x2_0_agora */]: "https://dev.xmaxai.com/open/api/v1",
574
624
  ["x2.1-preview" /* x2_1_preview */]: "https://dev.xmaxai.com/open/api/v1"
575
625
  };
576
626
  var RESOLUTION_BUCKETS = {
577
- ["x2.1-preview" /* x2_1_preview */]: [
627
+ ["x2.0" /* x2_0 */]: [],
628
+ ["x2.0-trtc" /* x2_0_trtc */]: [
578
629
  { width: 1024, height: 1920 },
579
630
  { width: 1920, height: 1024 }
580
631
  ],
@@ -582,29 +633,31 @@ var RESOLUTION_BUCKETS = {
582
633
  { width: 1024, height: 1920 },
583
634
  { width: 1920, height: 1024 }
584
635
  ],
585
- ["x2.0" /* x2_0 */]: [],
586
- ["x2.0-trtc" /* x2_0_trtc */]: [
636
+ ["x2.1-preview" /* x2_1_preview */]: [
587
637
  { width: 1024, height: 1920 },
588
638
  { width: 1920, height: 1024 }
589
639
  ]
590
640
  };
591
641
  var MAXIMUM_INPUT_PIXELS = {
592
- ["x2.1-preview" /* x2_1_preview */]: 21e5,
593
- ["x2.0-agora" /* x2_0_agora */]: 21e5,
594
642
  ["x2.0" /* x2_0 */]: 128e4,
595
- ["x2.0-trtc" /* x2_0_trtc */]: 21e5
643
+ ["x2.0-trtc" /* x2_0_trtc */]: 21e5,
644
+ ["x2.0-agora" /* x2_0_agora */]: 21e5,
645
+ ["x2.1-preview" /* x2_1_preview */]: 21e5
596
646
  };
647
+ function modelDisplayName(model) {
648
+ return isKnownModel(model) ? MODEL_DISPLAY_NAMES[model] : model;
649
+ }
597
650
  function modelBaseURL(model) {
598
- return MODEL_BASE_URLS[model];
651
+ return MODEL_BASE_URLS[modelDefaults(model)];
599
652
  }
600
653
  function resolutionBuckets(model) {
601
- return RESOLUTION_BUCKETS[model];
654
+ return RESOLUTION_BUCKETS[modelDefaults(model)];
602
655
  }
603
656
  function minimumInputPixels(_model) {
604
657
  return 6e5;
605
658
  }
606
659
  function maximumInputPixels(model) {
607
- return MAXIMUM_INPUT_PIXELS[model];
660
+ return MAXIMUM_INPUT_PIXELS[modelDefaults(model)];
608
661
  }
609
662
  function inputSizeAlignment(_model) {
610
663
  return 32;
@@ -613,11 +666,24 @@ function defaultFrameRate(_model) {
613
666
  return 30;
614
667
  }
615
668
  function defaultCameraVideoFormat(model) {
616
- if (model === "x2.0" /* x2_0 */) {
669
+ if (modelDefaults(model) === "x2.0" /* x2_0 */) {
617
670
  return new RealtimeVideoFormat({ width: 832, height: 1472, fps: 30 });
618
671
  }
619
672
  return new RealtimeVideoFormat({ width: 1024, height: 1920, fps: 30 });
620
673
  }
674
+ var MODEL_RTC_PROVIDERS = {
675
+ ["x2.0" /* x2_0 */]: Object.freeze(["vertc" /* vertc */]),
676
+ ["x2.0-trtc" /* x2_0_trtc */]: Object.freeze(["trtc" /* trtc */]),
677
+ ["x2.0-agora" /* x2_0_agora */]: Object.freeze(["agora" /* agora */]),
678
+ ["x2.1-preview" /* x2_1_preview */]: Object.freeze(["vertc" /* vertc */])
679
+ };
680
+ function supportedRtcProviders(model) {
681
+ return isKnownModel(model) ? MODEL_RTC_PROVIDERS[model] : CUSTOM_MODEL_RTC_PROVIDERS;
682
+ }
683
+ var CUSTOM_MODEL_RTC_PROVIDERS = Object.freeze([
684
+ ...MODEL_RTC_PROVIDERS["x2.1-preview" /* x2_1_preview */],
685
+ ...Object.values(RtcProvider).filter((provider) => !MODEL_RTC_PROVIDERS["x2.1-preview" /* x2_1_preview */].includes(provider))
686
+ ]);
621
687
 
622
688
  // src/Foundation/Media/Video/FrameInterpolationSupport.ts
623
689
  async function frameInterpolationAdapter(size) {
@@ -625,7 +691,7 @@ async function frameInterpolationAdapter(size) {
625
691
  if (size && (!Number.isSafeInteger(size.width) || !Number.isSafeInteger(size.height) || size.width <= 0 || size.height <= 0)) return null;
626
692
  try {
627
693
  const adapter = await navigator.gpu.requestAdapter();
628
- if (!_optionalChain([adapter, 'optionalAccess', _8 => _8.features, 'access', _9 => _9.has, 'call', _10 => _10("shader-f16")])) return null;
694
+ if (!_optionalChain([adapter, 'optionalAccess', _18 => _18.features, 'access', _19 => _19.has, 'call', _20 => _20("shader-f16")])) return null;
629
695
  if (size && (Math.ceil(size.width / 16) * 16 > adapter.limits.maxTextureDimension2D || Math.ceil(size.height / 16) * 16 > adapter.limits.maxTextureDimension2D)) return null;
630
696
  return adapter;
631
697
  } catch (e5) {
@@ -802,7 +868,14 @@ var CONTENT_TYPES = {
802
868
  gif: "image/gif",
803
869
  bmp: "image/bmp",
804
870
  heic: "image/heic",
805
- heif: "image/heif"
871
+ heif: "image/heif",
872
+ mp4: "video/mp4",
873
+ m4v: "video/mp4",
874
+ mov: "video/quicktime",
875
+ webm: "video/webm",
876
+ avi: "video/x-msvideo",
877
+ mkv: "video/x-matroska",
878
+ ogv: "video/ogg"
806
879
  };
807
880
  var StorageService = class _StorageService {
808
881
  /**
@@ -836,15 +909,90 @@ var StorageService = class _StorageService {
836
909
  * 上传图片并返回访问地址。
837
910
  */
838
911
  async uploadImage(options) {
912
+ return this.upload(options, "Image");
913
+ }
914
+ /**
915
+ * 普通上传原始视频并返回访问地址,不进行转码、压缩或分片。
916
+ */
917
+ async uploadVideo(options) {
918
+ return this.upload(options, "Video");
919
+ }
920
+ /**
921
+ * 下载原始视频文件并交给浏览器保存,支持取消网络下载。
922
+ */
923
+ async downloadVideo(options) {
924
+ const fileName = _StorageService.validateFileName(options.fileName);
925
+ let url;
926
+ try {
927
+ url = new URL(options.url.trim());
928
+ } catch (e6) {
929
+ throw new XmaxError("INVALID_CONFIGURATION" /* invalidConfiguration */, "Invalid video download URL");
930
+ }
931
+ if (!["http:", "https:"].includes(url.protocol) || url.username || url.password) {
932
+ throw new XmaxError("INVALID_CONFIGURATION" /* invalidConfiguration */, "Video download requires an HTTP(S) URL without embedded credentials");
933
+ }
934
+ if (typeof document === "undefined" || !document.body) {
935
+ throw new XmaxError("INVALID_CONFIGURATION" /* invalidConfiguration */, "Video saving requires a browser document");
936
+ }
937
+ let objectURL;
938
+ let link;
939
+ let saveRequested = false;
940
+ try {
941
+ if (_optionalChain([options, 'access', _21 => _21.signal, 'optionalAccess', _22 => _22.aborted])) {
942
+ throw new XmaxError("CANCELLED" /* cancelled */, "Video download was cancelled");
943
+ }
944
+ const response = await fetch(options.url.trim(), { credentials: "omit", signal: options.signal });
945
+ if (!response.ok) {
946
+ throw new XmaxError("DOWNLOAD_ERROR" /* downloadError */, "Video download failed", { httpStatus: response.status });
947
+ }
948
+ const data = await response.blob();
949
+ if (_optionalChain([options, 'access', _23 => _23.signal, 'optionalAccess', _24 => _24.aborted])) {
950
+ throw new XmaxError("CANCELLED" /* cancelled */, "Video download was cancelled");
951
+ }
952
+ if (data.size === 0) {
953
+ throw new XmaxError("DOWNLOAD_ERROR" /* downloadError */, "Downloaded video is empty");
954
+ }
955
+ objectURL = URL.createObjectURL(data);
956
+ link = document.createElement("a");
957
+ link.href = objectURL;
958
+ link.download = fileName;
959
+ link.hidden = true;
960
+ document.body.appendChild(link);
961
+ link.click();
962
+ saveRequested = true;
963
+ } catch (error) {
964
+ if (_optionalChain([options, 'access', _25 => _25.signal, 'optionalAccess', _26 => _26.aborted])) {
965
+ throw new XmaxError("CANCELLED" /* cancelled */, "Video download was cancelled");
966
+ }
967
+ if (error instanceof XmaxError) {
968
+ throw error;
969
+ }
970
+ throw new XmaxError("DOWNLOAD_ERROR" /* downloadError */, error instanceof Error ? error.message : String(error));
971
+ } finally {
972
+ _optionalChain([link, 'optionalAccess', _27 => _27.remove, 'call', _28 => _28()]);
973
+ if (objectURL) {
974
+ const resourceURL = objectURL;
975
+ if (saveRequested) {
976
+ setTimeout(() => URL.revokeObjectURL(resourceURL), 6e4);
977
+ } else {
978
+ URL.revokeObjectURL(resourceURL);
979
+ }
980
+ }
981
+ }
982
+ }
983
+ /**
984
+ * 获取临时凭证并通过 COS putObject 上传文件,统一处理进度、结果和错误。
985
+ */
986
+ async upload(options, mediaType) {
839
987
  const startedAt = Date.now();
840
988
  try {
841
989
  const safeName = _StorageService.validateFileName(options.fileName);
842
- const contentType = _optionalChain([options, 'access', _11 => _11.contentType, 'optionalAccess', _12 => _12.trim, 'call', _13 => _13()]) || _StorageService.inferContentType(safeName);
990
+ const contentType = _optionalChain([options, 'access', _29 => _29.contentType, 'optionalAccess', _30 => _30.trim, 'call', _31 => _31()]) || _StorageService.inferContentType(safeName);
843
991
  const body = _StorageService.makeBody(options.data, contentType);
844
992
  if (body.size === 0) {
845
993
  throw new XmaxError(
846
994
  "INVALID_CONFIGURATION" /* invalidConfiguration */,
847
- "Image data cannot be empty"
995
+ `${mediaType} data cannot be empty`
848
996
  );
849
997
  }
850
998
  const temporary = await this.fetchStorageConfiguration();
@@ -861,7 +1009,7 @@ var StorageService = class _StorageService {
861
1009
  Key: objectKey,
862
1010
  Body: body,
863
1011
  ContentType: contentType,
864
- onProgress: options.onProgress ? (info) => _optionalChain([options, 'access', _14 => _14.onProgress, 'optionalCall', _15 => _15(info.loaded, info.total)]) : void 0
1012
+ onProgress: options.onProgress ? (info) => _optionalChain([options, 'access', _32 => _32.onProgress, 'optionalCall', _33 => _33(info.loaded, info.total)]) : void 0
865
1013
  });
866
1014
  const url = _StorageService.resolveObjectURL(
867
1015
  uploaded.Location,
@@ -893,9 +1041,9 @@ var StorageService = class _StorageService {
893
1041
  const bucket = _StorageService.nonEmpty(payload.bucket);
894
1042
  const region = _StorageService.nonEmpty(payload.region);
895
1043
  const prefix = _StorageService.nonEmpty(payload.prefix);
896
- const accessKeyID = _StorageService.nonEmpty(_optionalChain([payload, 'access', _16 => _16.credentials, 'optionalAccess', _17 => _17.accessKeyId]));
897
- const secretAccessKey = _StorageService.nonEmpty(_optionalChain([payload, 'access', _18 => _18.credentials, 'optionalAccess', _19 => _19.secretAccessKey]));
898
- const sessionToken = _StorageService.nonEmpty(_optionalChain([payload, 'access', _20 => _20.credentials, 'optionalAccess', _21 => _21.sessionToken]));
1044
+ const accessKeyID = _StorageService.nonEmpty(_optionalChain([payload, 'access', _34 => _34.credentials, 'optionalAccess', _35 => _35.accessKeyId]));
1045
+ const secretAccessKey = _StorageService.nonEmpty(_optionalChain([payload, 'access', _36 => _36.credentials, 'optionalAccess', _37 => _37.secretAccessKey]));
1046
+ const sessionToken = _StorageService.nonEmpty(_optionalChain([payload, 'access', _38 => _38.credentials, 'optionalAccess', _39 => _39.sessionToken]));
899
1047
  if (!bucket || !region || !prefix || !accessKeyID || !secretAccessKey || !sessionToken) {
900
1048
  throw new XmaxError("API_ERROR" /* apiError */, message);
901
1049
  }
@@ -936,7 +1084,7 @@ var StorageService = class _StorageService {
936
1084
  reject(
937
1085
  new XmaxError(
938
1086
  "UPLOAD_ERROR" /* uploadError */,
939
- _optionalChain([error, 'access', _22 => _22.message, 'optionalAccess', _23 => _23.trim, 'call', _24 => _24()]) || "Storage upload failed"
1087
+ _optionalChain([error, 'access', _40 => _40.message, 'optionalAccess', _41 => _41.trim, 'call', _42 => _42()]) || "Storage upload failed"
940
1088
  )
941
1089
  );
942
1090
  return;
@@ -950,14 +1098,14 @@ var StorageService = class _StorageService {
950
1098
  * 其次拼接自定义 endpoint,最后回退到默认 COS 域名。
951
1099
  */
952
1100
  static resolveObjectURL(location, configuration, objectKey) {
953
- const candidate = _nullishCoalesce(_optionalChain([location, 'optionalAccess', _25 => _25.trim, 'call', _26 => _26()]), () => ( ""));
1101
+ const candidate = _nullishCoalesce(_optionalChain([location, 'optionalAccess', _43 => _43.trim, 'call', _44 => _44()]), () => ( ""));
954
1102
  if (candidate.startsWith("//")) {
955
1103
  return `https:${candidate}`;
956
1104
  }
957
1105
  if (/^https?:\/\//i.test(candidate)) {
958
1106
  return candidate;
959
1107
  }
960
- const endpoint = _optionalChain([configuration, 'access', _27 => _27.endpoint, 'optionalAccess', _28 => _28.trim, 'call', _29 => _29()]);
1108
+ const endpoint = _optionalChain([configuration, 'access', _45 => _45.endpoint, 'optionalAccess', _46 => _46.trim, 'call', _47 => _47()]);
961
1109
  if (endpoint) {
962
1110
  const normalized = /^https?:\/\//i.test(endpoint) ? endpoint : `https://${endpoint}`;
963
1111
  return `${normalized.replace(/\/+$/, "")}/${objectKey}`;
@@ -982,7 +1130,7 @@ var StorageService = class _StorageService {
982
1130
  if (!safeName || safeName.includes("/") || safeName.includes("\\")) {
983
1131
  throw new XmaxError(
984
1132
  "INVALID_CONFIGURATION" /* invalidConfiguration */,
985
- "Invalid file name for storage upload"
1133
+ "Invalid file name for storage operation"
986
1134
  );
987
1135
  }
988
1136
  return safeName;
@@ -991,7 +1139,7 @@ var StorageService = class _StorageService {
991
1139
  * 按文件名后缀推断内容类型。
992
1140
  */
993
1141
  static inferContentType(fileName) {
994
- const extension = _nullishCoalesce(_optionalChain([fileName, 'access', _30 => _30.split, 'call', _31 => _31("."), 'access', _32 => _32.pop, 'call', _33 => _33(), 'optionalAccess', _34 => _34.toLowerCase, 'call', _35 => _35()]), () => ( ""));
1142
+ const extension = _nullishCoalesce(_optionalChain([fileName, 'access', _48 => _48.split, 'call', _49 => _49("."), 'access', _50 => _50.pop, 'call', _51 => _51(), 'optionalAccess', _52 => _52.toLowerCase, 'call', _53 => _53()]), () => ( ""));
995
1143
  return _nullishCoalesce(CONTENT_TYPES[extension], () => ( "application/octet-stream"));
996
1144
  }
997
1145
  /**
@@ -1010,7 +1158,7 @@ var StorageService = class _StorageService {
1010
1158
  * 归一化可选字符串:去除首尾空白后为空时返回 `undefined`。
1011
1159
  */
1012
1160
  static nonEmpty(value) {
1013
- const normalized = _optionalChain([value, 'optionalAccess', _36 => _36.trim, 'call', _37 => _37()]);
1161
+ const normalized = _optionalChain([value, 'optionalAccess', _54 => _54.trim, 'call', _55 => _55()]);
1014
1162
  return normalized || void 0;
1015
1163
  }
1016
1164
  };
@@ -1025,7 +1173,7 @@ var CameraPosition = /* @__PURE__ */ ((CameraPosition3) => {
1025
1173
  // src/Foundation/Permissions/MediaPermissionWatch.ts
1026
1174
  var PERMISSION_POLL_INTERVAL_MS = 100;
1027
1175
  async function watchMediaPermissionPrompt(includeMicrophone) {
1028
- if (typeof navigator === "undefined" || !_optionalChain([navigator, 'access', _38 => _38.permissions, 'optionalAccess', _39 => _39.query])) {
1176
+ if (typeof navigator === "undefined" || !_optionalChain([navigator, 'access', _56 => _56.permissions, 'optionalAccess', _57 => _57.query])) {
1029
1177
  return void 0;
1030
1178
  }
1031
1179
  const names = ["camera", ...includeMicrophone ? ["microphone"] : []];
@@ -1044,7 +1192,7 @@ async function watchMediaPermissionPrompt(includeMicrophone) {
1044
1192
  name
1045
1193
  });
1046
1194
  return status.state;
1047
- } catch (e6) {
1195
+ } catch (e7) {
1048
1196
  return "prompt";
1049
1197
  }
1050
1198
  })
@@ -1052,7 +1200,7 @@ async function watchMediaPermissionPrompt(includeMicrophone) {
1052
1200
  let states;
1053
1201
  try {
1054
1202
  states = await queryOnce();
1055
- } catch (e7) {
1203
+ } catch (e8) {
1056
1204
  return void 0;
1057
1205
  }
1058
1206
  if (states.every((state2) => state2 === "granted")) {
@@ -1108,12 +1256,12 @@ var AgoraRtcStatistics = class _AgoraRtcStatistics {
1108
1256
  frameRate: valid(stats.receiveFrameRate),
1109
1257
  bitrateKbps: valid(stats.receiveBitrate / 1e3),
1110
1258
  rttMs: valid(rtc.RTT),
1111
- uplinkLossPercent: _optionalChain([localVideo, 'optionalAccess', _40 => _40.uplinkLossPercent]),
1259
+ uplinkLossPercent: _optionalChain([localVideo, 'optionalAccess', _58 => _58.uplinkLossPercent]),
1112
1260
  downlinkLossPercent: loss(stats.currentPacketLossRate),
1113
1261
  endToEndDelayMs: valid(stats.end2EndDelay)
1114
1262
  }));
1115
- _optionalChain([listener, 'optionalAccess', _41 => _41.onLocalVideoStatistics, 'optionalCall', _42 => _42(localVideo && Object.freeze(localVideo))]);
1116
- _optionalChain([listener, 'optionalAccess', _43 => _43.onRemoteVideoStatistics, 'optionalCall', _44 => _44(Object.freeze(remote.map((item) => Object.freeze(item))))]);
1263
+ _optionalChain([listener, 'optionalAccess', _59 => _59.onLocalVideoStatistics, 'optionalCall', _60 => _60(localVideo && Object.freeze(localVideo))]);
1264
+ _optionalChain([listener, 'optionalAccess', _61 => _61.onRemoteVideoStatistics, 'optionalCall', _62 => _62(Object.freeze(remote.map((item) => Object.freeze(item))))]);
1117
1265
  XmaxLogger.rtc.info(() => `Agora \u8FD0\u884C\u7EDF\u8BA1 (Agora RTC Statistics)
1118
1266
  ${JSON.stringify({
1119
1267
  rttMs: valid(rtc.RTT),
@@ -1131,7 +1279,7 @@ ${JSON.stringify({
1131
1279
  static network(stats, listener) {
1132
1280
  const quality = (value) => Number.isInteger(value) && value >= 0 && value <= 6 ? value : void 0;
1133
1281
  const result = Object.freeze({ uplinkQuality: quality(stats.uplinkNetworkQuality), downlinkQuality: quality(stats.downlinkNetworkQuality) });
1134
- _optionalChain([listener, 'optionalAccess', _45 => _45.onNetworkStatistics, 'optionalCall', _46 => _46(result)]);
1282
+ _optionalChain([listener, 'optionalAccess', _63 => _63.onNetworkStatistics, 'optionalCall', _64 => _64(result)]);
1135
1283
  XmaxLogger.rtc.info(() => `Agora \u7F51\u7EDC\u8D28\u91CF (Agora Network Quality)
1136
1284
  ${JSON.stringify(result, null, 2)}`, 2 /* performance */);
1137
1285
  }
@@ -1151,17 +1299,17 @@ ${JSON.stringify(result, null, 2)}`, 2 /* performance */);
1151
1299
 
1152
1300
  // src/Foundation/RTC/RtcVideoOrientation.ts
1153
1301
  function shouldTransposeRtcVideoSize(environment) {
1154
- const userAgent = _nullishCoalesce(_optionalChain([environment, 'optionalAccess', _47 => _47.userAgent]), () => ( (typeof navigator === "undefined" ? "" : navigator.userAgent)));
1155
- const maxTouchPoints = _nullishCoalesce(_optionalChain([environment, 'optionalAccess', _48 => _48.maxTouchPoints]), () => ( (typeof navigator === "undefined" ? 0 : _nullishCoalesce(navigator.maxTouchPoints, () => ( 0)))));
1302
+ const userAgent = _nullishCoalesce(_optionalChain([environment, 'optionalAccess', _65 => _65.userAgent]), () => ( (typeof navigator === "undefined" ? "" : navigator.userAgent)));
1303
+ const maxTouchPoints = _nullishCoalesce(_optionalChain([environment, 'optionalAccess', _66 => _66.maxTouchPoints]), () => ( (typeof navigator === "undefined" ? 0 : _nullishCoalesce(navigator.maxTouchPoints, () => ( 0)))));
1156
1304
  const mobile = /Android|webOS|iPhone|iPad|iPod|Mobile/i.test(userAgent) || /Macintosh/i.test(userAgent) && maxTouchPoints > 1;
1157
1305
  if (!mobile) {
1158
1306
  return false;
1159
1307
  }
1160
- const orientationType = _nullishCoalesce(_optionalChain([environment, 'optionalAccess', _49 => _49.orientationType]), () => ( (typeof window === "undefined" ? void 0 : _optionalChain([window, 'access', _50 => _50.screen, 'optionalAccess', _51 => _51.orientation, 'optionalAccess', _52 => _52.type]))));
1308
+ const orientationType = _nullishCoalesce(_optionalChain([environment, 'optionalAccess', _67 => _67.orientationType]), () => ( (typeof window === "undefined" ? void 0 : _optionalChain([window, 'access', _68 => _68.screen, 'optionalAccess', _69 => _69.orientation, 'optionalAccess', _70 => _70.type]))));
1161
1309
  if (orientationType !== void 0) {
1162
1310
  return orientationType.includes("portrait");
1163
1311
  }
1164
- const degrees = _nullishCoalesce(_optionalChain([environment, 'optionalAccess', _53 => _53.orientationDegrees]), () => ( (typeof window === "undefined" ? void 0 : window.orientation)));
1312
+ const degrees = _nullishCoalesce(_optionalChain([environment, 'optionalAccess', _71 => _71.orientationDegrees]), () => ( (typeof window === "undefined" ? void 0 : window.orientation)));
1165
1313
  return degrees === 0 || degrees === 180;
1166
1314
  }
1167
1315
  function rtcOrientedVideoSize(size, environment) {
@@ -1185,6 +1333,8 @@ var _AgoraRtcManager = class _AgoraRtcManager {
1185
1333
  __publicField(this, "lifecycle", 0);
1186
1334
  __publicField(this, "camera");
1187
1335
  __publicField(this, "microphone");
1336
+ __publicField(this, "externalVideo");
1337
+ __publicField(this, "externalAudio");
1188
1338
  __publicField(this, "connection");
1189
1339
  __publicField(this, "roomVersion", 0);
1190
1340
  /**
@@ -1256,7 +1406,7 @@ var _AgoraRtcManager = class _AgoraRtcManager {
1256
1406
  if (typeof client.sendStreamMessage !== "function") {
1257
1407
  throw new XmaxError("RTC_ERROR" /* rtcError */, "Agora DataStream is unavailable in this SDK version");
1258
1408
  }
1259
- _AgoraRtcManager.regions.set(sdk, { environment: this.environment, count: (_nullishCoalesce(_optionalChain([region, 'optionalAccess', _54 => _54.count]), () => ( 0))) + 1 });
1409
+ _AgoraRtcManager.regions.set(sdk, { environment: this.environment, count: (_nullishCoalesce(_optionalChain([region, 'optionalAccess', _72 => _72.count]), () => ( 0))) + 1 });
1260
1410
  this.sdk = sdk;
1261
1411
  this.client = client;
1262
1412
  this.registerEvents(client);
@@ -1267,19 +1417,22 @@ var _AgoraRtcManager = class _AgoraRtcManager {
1267
1417
  async destroy() {
1268
1418
  this.lifecycle++;
1269
1419
  const client = this.client;
1270
- this.listener = void 0;
1271
1420
  this.clearRoom();
1272
1421
  this.client = void 0;
1273
- _optionalChain([client, 'optionalAccess', _55 => _55.removeAllListeners, 'call', _56 => _56()]);
1274
- _optionalChain([this, 'access', _57 => _57.camera, 'optionalAccess', _58 => _58.close, 'call', _59 => _59()]);
1275
- _optionalChain([this, 'access', _60 => _60.microphone, 'optionalAccess', _61 => _61.close, 'call', _62 => _62()]);
1422
+ _optionalChain([client, 'optionalAccess', _73 => _73.removeAllListeners, 'call', _74 => _74()]);
1423
+ _optionalChain([this, 'access', _75 => _75.camera, 'optionalAccess', _76 => _76.close, 'call', _77 => _77()]);
1424
+ _optionalChain([this, 'access', _78 => _78.microphone, 'optionalAccess', _79 => _79.close, 'call', _80 => _80()]);
1425
+ _optionalChain([this, 'access', _81 => _81.externalVideo, 'optionalAccess', _82 => _82.close, 'call', _83 => _83()]);
1426
+ _optionalChain([this, 'access', _84 => _84.externalAudio, 'optionalAccess', _85 => _85.close, 'call', _86 => _86()]);
1427
+ this.externalVideo = void 0;
1428
+ this.externalAudio = void 0;
1276
1429
  this.camera = void 0;
1277
1430
  this.microphone = void 0;
1278
1431
  const sdk = this.sdk;
1279
1432
  this.sdk = void 0;
1280
1433
  try {
1281
- await _optionalChain([client, 'optionalAccess', _63 => _63.leave, 'call', _64 => _64()]);
1282
- } catch (e8) {
1434
+ await _optionalChain([client, 'optionalAccess', _87 => _87.leave, 'call', _88 => _88()]);
1435
+ } catch (e9) {
1283
1436
  XmaxLogger.rtc.warning(() => "Agora \u9000\u623F\u5931\u8D25\uFF0C\u5DF2\u91CA\u653E\u672C\u5730\u8D44\u6E90 (Agora Leave Failed)");
1284
1437
  } finally {
1285
1438
  const region = sdk && _AgoraRtcManager.regions.get(sdk);
@@ -1291,7 +1444,9 @@ var _AgoraRtcManager = class _AgoraRtcManager {
1291
1444
  */
1292
1445
  async startCameraCapture(options) {
1293
1446
  const client = this.requireClient();
1294
- if (this.camera) throw new XmaxError("INVALID_CONFIGURATION" /* invalidConfiguration */, "Camera capture is already running");
1447
+ if (this.camera || this.externalVideo) {
1448
+ throw new XmaxError("INVALID_CONFIGURATION" /* invalidConfiguration */, "A local video source is already active");
1449
+ }
1295
1450
  try {
1296
1451
  const captureSize = rtcOrientedVideoSize(options);
1297
1452
  const camera = await this.sdk.createCameraVideoTrack({
@@ -1323,14 +1478,45 @@ var _AgoraRtcManager = class _AgoraRtcManager {
1323
1478
  async stopCameraCapture() {
1324
1479
  const camera = this.camera;
1325
1480
  this.camera = void 0;
1326
- _optionalChain([camera, 'optionalAccess', _65 => _65.close, 'call', _66 => _66()]);
1481
+ _optionalChain([camera, 'optionalAccess', _89 => _89.close, 'call', _90 => _90()]);
1327
1482
  }
1328
1483
  /**
1329
- * 应用公共编码配置,补偿移动端竖屏的宽高转置。
1484
+ * 将外部原生轨包装为 Agora 本地轨,不申请设备权限;失败时释放本次克隆。
1485
+ */
1486
+ async setExternalMediaTracks(tracks) {
1487
+ this.requireClient();
1488
+ if (this.camera || this.microphone || this.externalVideo) {
1489
+ throw new XmaxError("INVALID_CONFIGURATION" /* invalidConfiguration */, "Release the current local media before attaching external tracks");
1490
+ }
1491
+ if (tracks.videoTrack.kind !== "video" || tracks.videoTrack.readyState !== "live" || tracks.audioTrack && (tracks.audioTrack.kind !== "audio" || tracks.audioTrack.readyState !== "live")) {
1492
+ throw new XmaxError("INVALID_CONFIGURATION" /* invalidConfiguration */, "External media requires live video and optional audio tracks");
1493
+ }
1494
+ const video = tracks.videoTrack.clone();
1495
+ let audio;
1496
+ let wrappedVideo;
1497
+ let wrappedAudio;
1498
+ try {
1499
+ audio = _optionalChain([tracks, 'access', _91 => _91.audioTrack, 'optionalAccess', _92 => _92.clone, 'call', _93 => _93()]);
1500
+ wrappedVideo = this.sdk.createCustomVideoTrack({ mediaStreamTrack: video });
1501
+ if (audio) {
1502
+ wrappedAudio = this.sdk.createCustomAudioTrack({ mediaStreamTrack: audio, encoderConfig: "music_standard" });
1503
+ }
1504
+ this.externalVideo = wrappedVideo;
1505
+ this.externalAudio = wrappedAudio;
1506
+ } catch (error) {
1507
+ _optionalChain([wrappedVideo, 'optionalAccess', _94 => _94.close, 'call', _95 => _95()]);
1508
+ _optionalChain([wrappedAudio, 'optionalAccess', _96 => _96.close, 'call', _97 => _97()]);
1509
+ video.stop();
1510
+ _optionalChain([audio, 'optionalAccess', _98 => _98.stop, 'call', _99 => _99()]);
1511
+ throw this.mapError(error);
1512
+ }
1513
+ }
1514
+ /**
1515
+ * 应用公共编码配置;仅相机源补偿移动端转置,Canvas 外部源使用原始方向。
1330
1516
  */
1331
1517
  async configureVideoEncoding(config) {
1332
- const camera = this.requireCamera();
1333
- const encodingSize = rtcOrientedVideoSize(config);
1518
+ const camera = this.requireLocalVideo();
1519
+ const encodingSize = this.externalVideo ? config : rtcOrientedVideoSize(config);
1334
1520
  await this.run(async () => {
1335
1521
  await camera.setEncoderConfiguration({
1336
1522
  width: encodingSize.width,
@@ -1346,10 +1532,10 @@ var _AgoraRtcManager = class _AgoraRtcManager {
1346
1532
  * 使用会话下发的字符串频道与 UID 入房;不采信后端区域覆盖。
1347
1533
  */
1348
1534
  async joinRoom(configuration) {
1349
- await _optionalChain([this, 'access', _67 => _67.rejoinTask, 'optionalAccess', _68 => _68.catch, 'call', _69 => _69(() => {
1535
+ await _optionalChain([this, 'access', _100 => _100.rejoinTask, 'optionalAccess', _101 => _101.catch, 'call', _102 => _102(() => {
1350
1536
  })]);
1351
1537
  const client = this.requireClient();
1352
- if (configuration.provider !== "agora" || this.connection) {
1538
+ if (configuration.provider !== "agora" /* agora */ || this.connection) {
1353
1539
  throw new XmaxError("INVALID_CONFIGURATION" /* invalidConfiguration */, "Agora credentials and an idle room are required");
1354
1540
  }
1355
1541
  const version = ++this.roomVersion;
@@ -1360,13 +1546,13 @@ var _AgoraRtcManager = class _AgoraRtcManager {
1360
1546
  }
1361
1547
  this.connection = configuration;
1362
1548
  for (const user of client.remoteUsers) {
1363
- if (user.hasVideo) _optionalChain([this, 'access', _70 => _70.listener, 'optionalAccess', _71 => _71.onRemoteVideoPublished, 'call', _72 => _72(String(user.uid), true)]);
1549
+ if (user.hasVideo) _optionalChain([this, 'access', _103 => _103.listener, 'optionalAccess', _104 => _104.onRemoteVideoPublished, 'call', _105 => _105(String(user.uid), true)]);
1364
1550
  }
1365
1551
  this.statisticsTimer = setInterval(() => {
1366
1552
  if (this.client !== client || !this.connection || this.tokenExpired || this.rejoinOperation) return;
1367
1553
  try {
1368
1554
  AgoraRtcStatistics.collect(client, this.listener);
1369
- } catch (e9) {
1555
+ } catch (e10) {
1370
1556
  XmaxLogger.rtc.warning(() => "Agora \u7EDF\u8BA1\u91C7\u6837\u5931\u8D25 (Agora Statistics Failed)");
1371
1557
  }
1372
1558
  }, 2e3);
@@ -1382,10 +1568,10 @@ var _AgoraRtcManager = class _AgoraRtcManager {
1382
1568
  * 应用同一频道的新 Token;即将过期时续签,已经过期时重新进房并恢复媒体。
1383
1569
  */
1384
1570
  async updateCredentials(config, signal) {
1385
- if (_optionalChain([signal, 'optionalAccess', _73 => _73.aborted])) throw this.cancelled();
1571
+ if (_optionalChain([signal, 'optionalAccess', _106 => _106.aborted])) throw this.cancelled();
1386
1572
  const current = this.connection;
1387
1573
  const client = this.requireClient();
1388
- if (!current || config.provider !== "agora" || config.appID !== current.appID || config.roomID !== current.roomID || config.userID !== current.userID) {
1574
+ if (!current || config.provider !== "agora" /* agora */ || config.appID !== current.appID || config.roomID !== current.roomID || config.userID !== current.userID) {
1389
1575
  throw new XmaxError("SESSION_ERROR" /* sessionError */, "Agora session binding changed");
1390
1576
  }
1391
1577
  const version = this.roomVersion;
@@ -1398,7 +1584,7 @@ var _AgoraRtcManager = class _AgoraRtcManager {
1398
1584
  }
1399
1585
  }
1400
1586
  this.ensureRoomCurrent(client, version);
1401
- if (_optionalChain([signal, 'optionalAccess', _74 => _74.aborted])) throw this.cancelled();
1587
+ if (_optionalChain([signal, 'optionalAccess', _107 => _107.aborted])) throw this.cancelled();
1402
1588
  if (this.tokenExpired) await this.rejoinRoom(client, config, signal);
1403
1589
  else this.connection = config;
1404
1590
  }
@@ -1421,21 +1607,21 @@ var _AgoraRtcManager = class _AgoraRtcManager {
1421
1607
  reject(this.cancelled());
1422
1608
  };
1423
1609
  this.cancelRejoin = cancel;
1424
- _optionalChain([signal, 'optionalAccess', _75 => _75.addEventListener, 'call', _76 => _76("abort", cancel, { once: true })]);
1610
+ _optionalChain([signal, 'optionalAccess', _108 => _108.addEventListener, 'call', _109 => _109("abort", cancel, { once: true })]);
1425
1611
  task.then(resolve, reject);
1426
- if (_optionalChain([signal, 'optionalAccess', _77 => _77.aborted])) cancel();
1612
+ if (_optionalChain([signal, 'optionalAccess', _110 => _110.aborted])) cancel();
1427
1613
  });
1428
1614
  this.rejoinOperation = operation;
1429
1615
  try {
1430
1616
  await operation;
1431
1617
  } finally {
1432
- _optionalChain([signal, 'optionalAccess', _78 => _78.removeEventListener, 'call', _79 => _79("abort", cancel)]);
1618
+ _optionalChain([signal, 'optionalAccess', _111 => _111.removeEventListener, 'call', _112 => _112("abort", cancel)]);
1433
1619
  if (this.cancelRejoin === cancel) this.cancelRejoin = void 0;
1434
1620
  if (this.rejoinOperation === operation) this.rejoinOperation = void 0;
1435
1621
  }
1436
1622
  this.ensureRoomCurrent(client, version);
1437
1623
  for (const user of client.remoteUsers) {
1438
- if (user.hasVideo) _optionalChain([this, 'access', _80 => _80.listener, 'optionalAccess', _81 => _81.onRemoteVideoPublished, 'call', _82 => _82(String(user.uid), true)]);
1624
+ if (user.hasVideo) _optionalChain([this, 'access', _113 => _113.listener, 'optionalAccess', _114 => _114.onRemoteVideoPublished, 'call', _115 => _115(String(user.uid), true)]);
1439
1625
  }
1440
1626
  }
1441
1627
  /**
@@ -1443,10 +1629,10 @@ var _AgoraRtcManager = class _AgoraRtcManager {
1443
1629
  */
1444
1630
  async performRejoin(client, config, version) {
1445
1631
  this.ensureRoomCurrent(client, version);
1446
- _optionalChain([this, 'access', _83 => _83.cancelSendDelay, 'optionalCall', _84 => _84()]);
1447
- _optionalChain([this, 'access', _85 => _85.cancelActiveSend, 'optionalCall', _86 => _86()]);
1448
- _optionalChain([this, 'access', _87 => _87.listener, 'optionalAccess', _88 => _88.onRoomRejoining, 'optionalCall', _89 => _89()]);
1449
- for (const user of client.remoteUsers) _optionalChain([user, 'access', _90 => _90.audioTrack, 'optionalAccess', _91 => _91.stop, 'call', _92 => _92()]);
1632
+ _optionalChain([this, 'access', _116 => _116.cancelSendDelay, 'optionalCall', _117 => _117()]);
1633
+ _optionalChain([this, 'access', _118 => _118.cancelActiveSend, 'optionalCall', _119 => _119()]);
1634
+ _optionalChain([this, 'access', _120 => _120.listener, 'optionalAccess', _121 => _121.onRoomRejoining, 'optionalCall', _122 => _122()]);
1635
+ for (const user of client.remoteUsers) _optionalChain([user, 'access', _123 => _123.audioTrack, 'optionalAccess', _124 => _124.stop, 'call', _125 => _125()]);
1450
1636
  await this.run(() => client.leave());
1451
1637
  this.ensureRoomCurrent(client, version);
1452
1638
  await this.run(() => client.join(config.appID, config.roomID, config.roomToken, config.userID));
@@ -1454,12 +1640,14 @@ var _AgoraRtcManager = class _AgoraRtcManager {
1454
1640
  await this.run(() => client.leave());
1455
1641
  throw this.cancelled();
1456
1642
  }
1457
- if (this.videoPublishRequested && this.camera) {
1458
- await this.run(() => client.publish(this.camera));
1643
+ const video = _nullishCoalesce(this.externalVideo, () => ( this.camera));
1644
+ const audio = this.externalVideo ? this.externalAudio : this.microphone;
1645
+ if (this.videoPublishRequested && video) {
1646
+ await this.run(() => client.publish(video));
1459
1647
  this.ensureRoomCurrent(client, version);
1460
1648
  }
1461
- if (this.audioPublishRequested && this.microphone) {
1462
- await this.run(() => client.publish(this.microphone));
1649
+ if (this.audioPublishRequested && audio) {
1650
+ await this.run(() => client.publish(audio));
1463
1651
  this.ensureRoomCurrent(client, version);
1464
1652
  }
1465
1653
  for (const user of client.remoteUsers) {
@@ -1480,7 +1668,7 @@ var _AgoraRtcManager = class _AgoraRtcManager {
1480
1668
  */
1481
1669
  async publishLocalVideo() {
1482
1670
  const client = this.requireClient();
1483
- const camera = this.requireCamera();
1671
+ const camera = this.requireLocalVideo();
1484
1672
  this.videoPublishRequested = true;
1485
1673
  await this.rejoinOperation;
1486
1674
  if (client !== this.client || !this.videoPublishRequested) throw this.cancelled();
@@ -1493,10 +1681,13 @@ var _AgoraRtcManager = class _AgoraRtcManager {
1493
1681
  async unpublishLocalVideo() {
1494
1682
  this.videoPublishRequested = false;
1495
1683
  if (this.rejoinTask || this.tokenExpired) return;
1496
- if (this.client && this.camera) await this.run(() => this.client.unpublish(this.camera));
1684
+ const video = _nullishCoalesce(this.externalVideo, () => ( this.camera));
1685
+ if (this.client && video) {
1686
+ await this.run(() => this.client.unpublish(video));
1687
+ }
1497
1688
  }
1498
1689
  /**
1499
- * 首次发布时申请麦克风;取消后的迟到采集立即释放。
1690
+ * 外部源只发布提供的音轨;相机源首次发布时申请麦克风,迟到采集立即释放。
1500
1691
  */
1501
1692
  async publishLocalAudio() {
1502
1693
  const client = this.requireClient();
@@ -1504,6 +1695,13 @@ var _AgoraRtcManager = class _AgoraRtcManager {
1504
1695
  await this.rejoinOperation;
1505
1696
  const version = this.roomVersion;
1506
1697
  if (client !== this.client || !this.audioPublishRequested) throw this.cancelled();
1698
+ if (this.externalVideo) {
1699
+ const audio = this.externalAudio;
1700
+ if (audio && !client.localTracks.includes(audio)) {
1701
+ await this.run(() => client.publish(audio));
1702
+ }
1703
+ return;
1704
+ }
1507
1705
  try {
1508
1706
  if (!this.microphone) {
1509
1707
  const track = await this.sdk.createMicrophoneAudioTrack();
@@ -1524,7 +1722,10 @@ var _AgoraRtcManager = class _AgoraRtcManager {
1524
1722
  async unpublishLocalAudio() {
1525
1723
  this.audioPublishRequested = false;
1526
1724
  if (this.rejoinTask || this.tokenExpired) return;
1527
- if (this.client && this.microphone) await this.run(() => this.client.unpublish(this.microphone));
1725
+ const audio = this.externalVideo ? this.externalAudio : this.microphone;
1726
+ if (this.client && audio) {
1727
+ await this.run(() => this.client.unpublish(audio));
1728
+ }
1528
1729
  }
1529
1730
  /**
1530
1731
  * 订阅远端主视频并返回原生轨道,不创建额外播放视图。
@@ -1553,8 +1754,8 @@ var _AgoraRtcManager = class _AgoraRtcManager {
1553
1754
  const user = client.remoteUsers.find((user2) => String(user2.uid) === userID);
1554
1755
  if (!subscribe) {
1555
1756
  this.requestedAudio.delete(userID);
1556
- _optionalChain([user, 'optionalAccess', _93 => _93.audioTrack, 'optionalAccess', _94 => _94.stop, 'call', _95 => _95()]);
1557
- if (_optionalChain([user, 'optionalAccess', _96 => _96.audioTrack]) && !this.rejoinTask) await this.run(() => client.unsubscribe(user, "audio"));
1757
+ _optionalChain([user, 'optionalAccess', _126 => _126.audioTrack, 'optionalAccess', _127 => _127.stop, 'call', _128 => _128()]);
1758
+ if (_optionalChain([user, 'optionalAccess', _129 => _129.audioTrack]) && !this.rejoinTask) await this.run(() => client.unsubscribe(user, "audio"));
1558
1759
  return;
1559
1760
  }
1560
1761
  this.requestedAudio.add(userID);
@@ -1565,7 +1766,7 @@ var _AgoraRtcManager = class _AgoraRtcManager {
1565
1766
  if (this.requestedAudio.has(userID)) await this.subscribeRemoteAudio(userID, true);
1566
1767
  return;
1567
1768
  }
1568
- if (!_optionalChain([user, 'optionalAccess', _97 => _97.hasAudio])) return;
1769
+ if (!_optionalChain([user, 'optionalAccess', _130 => _130.hasAudio])) return;
1569
1770
  const version = this.roomVersion;
1570
1771
  const track = await this.run(() => client.subscribe(user, "audio"));
1571
1772
  if (client !== this.client || version !== this.roomVersion || !this.requestedAudio.has(userID)) {
@@ -1579,9 +1780,9 @@ var _AgoraRtcManager = class _AgoraRtcManager {
1579
1780
  */
1580
1781
  setRemoteAudioVolume(volume, userID) {
1581
1782
  this.audioVolumes.set(userID, Math.max(0, Math.min(100, Number.isFinite(volume) ? Math.round(volume) : 0)));
1582
- const user = _optionalChain([this, 'access', _98 => _98.client, 'optionalAccess', _99 => _99.remoteUsers, 'access', _100 => _100.find, 'call', _101 => _101((user2) => String(user2.uid) === userID)]);
1783
+ const user = _optionalChain([this, 'access', _131 => _131.client, 'optionalAccess', _132 => _132.remoteUsers, 'access', _133 => _133.find, 'call', _134 => _134((user2) => String(user2.uid) === userID)]);
1583
1784
  if (this.rejoinTask || this.tokenExpired) {
1584
- _optionalChain([user, 'optionalAccess', _102 => _102.audioTrack, 'optionalAccess', _103 => _103.stop, 'call', _104 => _104()]);
1785
+ _optionalChain([user, 'optionalAccess', _135 => _135.audioTrack, 'optionalAccess', _136 => _136.stop, 'call', _137 => _137()]);
1585
1786
  return;
1586
1787
  }
1587
1788
  if (user) this.applyAudioVolume(user);
@@ -1670,33 +1871,33 @@ var _AgoraRtcManager = class _AgoraRtcManager {
1670
1871
  const handleExpired = () => {
1671
1872
  if (!active() || this.tokenExpired) return;
1672
1873
  this.tokenExpired = true;
1673
- _optionalChain([this, 'access', _105 => _105.listener, 'optionalAccess', _106 => _106.onTokenExpired, 'optionalCall', _107 => _107()]);
1874
+ _optionalChain([this, 'access', _138 => _138.listener, 'optionalAccess', _139 => _139.onTokenExpired, 'optionalCall', _140 => _140()]);
1674
1875
  };
1675
1876
  client.on("user-published", (user, kind) => {
1676
1877
  if (!mediaActive()) return;
1677
- if (kind === "video") _optionalChain([this, 'access', _108 => _108.listener, 'optionalAccess', _109 => _109.onRemoteVideoPublished, 'call', _110 => _110(String(user.uid), true)]);
1878
+ if (kind === "video") _optionalChain([this, 'access', _141 => _141.listener, 'optionalAccess', _142 => _142.onRemoteVideoPublished, 'call', _143 => _143(String(user.uid), true)]);
1678
1879
  if (kind === "audio" && this.requestedAudio.has(String(user.uid))) {
1679
1880
  void this.subscribeRemoteAudio(String(user.uid), true).catch((error) => {
1680
- if (active()) _optionalChain([this, 'access', _111 => _111.listener, 'optionalAccess', _112 => _112.onError, 'optionalCall', _113 => _113(this.mapError(error))]);
1881
+ if (active()) _optionalChain([this, 'access', _144 => _144.listener, 'optionalAccess', _145 => _145.onError, 'optionalCall', _146 => _146(this.mapError(error))]);
1681
1882
  });
1682
1883
  }
1683
1884
  });
1684
1885
  client.on("user-unpublished", (user, kind) => {
1685
1886
  if (!mediaActive()) return;
1686
- if (kind === "video") _optionalChain([this, 'access', _114 => _114.listener, 'optionalAccess', _115 => _115.onRemoteVideoPublished, 'call', _116 => _116(String(user.uid), false)]);
1687
- if (kind === "audio") _optionalChain([user, 'access', _117 => _117.audioTrack, 'optionalAccess', _118 => _118.stop, 'call', _119 => _119()]);
1887
+ if (kind === "video") _optionalChain([this, 'access', _147 => _147.listener, 'optionalAccess', _148 => _148.onRemoteVideoPublished, 'call', _149 => _149(String(user.uid), false)]);
1888
+ if (kind === "audio") _optionalChain([user, 'access', _150 => _150.audioTrack, 'optionalAccess', _151 => _151.stop, 'call', _152 => _152()]);
1688
1889
  });
1689
1890
  client.on("user-left", (user) => {
1690
- if (mediaActive()) _optionalChain([this, 'access', _120 => _120.listener, 'optionalAccess', _121 => _121.onRemoteVideoPublished, 'call', _122 => _122(String(user.uid), false)]);
1891
+ if (mediaActive()) _optionalChain([this, 'access', _153 => _153.listener, 'optionalAccess', _154 => _154.onRemoteVideoPublished, 'call', _155 => _155(String(user.uid), false)]);
1691
1892
  });
1692
1893
  client.on("stream-message", (uid, data) => {
1693
- if (mediaActive()) _optionalChain([this, 'access', _123 => _123.listener, 'optionalAccess', _124 => _124.onCustomMessageReceived, 'call', _125 => _125(String(uid), typeof data === "string" ? data : new TextDecoder().decode(data))]);
1894
+ if (mediaActive()) _optionalChain([this, 'access', _156 => _156.listener, 'optionalAccess', _157 => _157.onCustomMessageReceived, 'call', _158 => _158(String(uid), typeof data === "string" ? data : new TextDecoder().decode(data))]);
1694
1895
  });
1695
1896
  client.on("network-quality", (quality) => {
1696
1897
  if (mediaActive()) AgoraRtcStatistics.network(quality, this.listener);
1697
1898
  });
1698
1899
  client.on("token-privilege-will-expire", () => {
1699
- if (active() && !this.tokenExpired) _optionalChain([this, 'access', _126 => _126.listener, 'optionalAccess', _127 => _127.onTokenWillExpire, 'optionalCall', _128 => _128()]);
1900
+ if (active() && !this.tokenExpired) _optionalChain([this, 'access', _159 => _159.listener, 'optionalAccess', _160 => _160.onTokenWillExpire, 'optionalCall', _161 => _161()]);
1700
1901
  });
1701
1902
  client.on("token-privilege-did-expire", handleExpired);
1702
1903
  client.on("connection-state-change", (state2, previous, reason) => {
@@ -1705,7 +1906,7 @@ var _AgoraRtcManager = class _AgoraRtcManager {
1705
1906
  return;
1706
1907
  }
1707
1908
  if (active() && !this.tokenExpired && state2 === "DISCONNECTED" && previous !== "DISCONNECTED") {
1708
- _optionalChain([this, 'access', _129 => _129.listener, 'optionalAccess', _130 => _130.onError, 'optionalCall', _131 => _131(new XmaxError("RTC_ERROR" /* rtcError */, `Agora connection disconnected: ${_nullishCoalesce(reason, () => ( "unknown"))}`))]);
1909
+ _optionalChain([this, 'access', _162 => _162.listener, 'optionalAccess', _163 => _163.onError, 'optionalCall', _164 => _164(new XmaxError("RTC_ERROR" /* rtcError */, `Agora connection disconnected: ${_nullishCoalesce(reason, () => ( "unknown"))}`))]);
1709
1910
  }
1710
1911
  });
1711
1912
  }
@@ -1714,20 +1915,20 @@ var _AgoraRtcManager = class _AgoraRtcManager {
1714
1915
  */
1715
1916
  clearRoom() {
1716
1917
  this.roomVersion++;
1717
- _optionalChain([this, 'access', _132 => _132.cancelRejoin, 'optionalCall', _133 => _133()]);
1918
+ _optionalChain([this, 'access', _165 => _165.cancelRejoin, 'optionalCall', _166 => _166()]);
1718
1919
  this.connection = void 0;
1719
1920
  this.tokenExpired = false;
1720
1921
  this.videoPublishRequested = false;
1721
1922
  this.audioPublishRequested = false;
1722
1923
  clearInterval(this.statisticsTimer);
1723
1924
  this.statisticsTimer = void 0;
1724
- _optionalChain([this, 'access', _134 => _134.cancelSendDelay, 'optionalCall', _135 => _135()]);
1725
- _optionalChain([this, 'access', _136 => _136.cancelActiveSend, 'optionalCall', _137 => _137()]);
1925
+ _optionalChain([this, 'access', _167 => _167.cancelSendDelay, 'optionalCall', _168 => _168()]);
1926
+ _optionalChain([this, 'access', _169 => _169.cancelActiveSend, 'optionalCall', _170 => _170()]);
1726
1927
  this.sentPackets = [];
1727
1928
  this.sendTail = Promise.resolve();
1728
1929
  this.requestedAudio.clear();
1729
1930
  this.audioVolumes.clear();
1730
- for (const user of _nullishCoalesce(_optionalChain([this, 'access', _138 => _138.client, 'optionalAccess', _139 => _139.remoteUsers]), () => ( []))) _optionalChain([user, 'access', _140 => _140.audioTrack, 'optionalAccess', _141 => _141.stop, 'call', _142 => _142()]);
1931
+ for (const user of _nullishCoalesce(_optionalChain([this, 'access', _171 => _171.client, 'optionalAccess', _172 => _172.remoteUsers]), () => ( []))) _optionalChain([user, 'access', _173 => _173.audioTrack, 'optionalAccess', _174 => _174.stop, 'call', _175 => _175()]);
1731
1932
  }
1732
1933
  /**
1733
1934
  * 判断异步恢复工作是否仍属于当前房间生命周期。
@@ -1755,6 +1956,16 @@ var _AgoraRtcManager = class _AgoraRtcManager {
1755
1956
  if (!this.camera) throw new XmaxError("INVALID_CONFIGURATION" /* invalidConfiguration */, "Camera capture is not running");
1756
1957
  return this.camera;
1757
1958
  }
1959
+ /**
1960
+ * 获取当前可编码、发布的视频轨,不要求来源为摄像头。
1961
+ */
1962
+ requireLocalVideo() {
1963
+ const video = _nullishCoalesce(this.externalVideo, () => ( this.camera));
1964
+ if (!video) {
1965
+ throw new XmaxError("INVALID_CONFIGURATION" /* invalidConfiguration */, "Local video source is not prepared");
1966
+ }
1967
+ return video;
1968
+ }
1758
1969
  /**
1759
1970
  * 执行声网操作并统一错误类型。
1760
1971
  */
@@ -1787,14 +1998,6 @@ var _AgoraRtcManager = class _AgoraRtcManager {
1787
1998
  __publicField(_AgoraRtcManager, "regions", /* @__PURE__ */ new Map());
1788
1999
  var AgoraRtcManager = _AgoraRtcManager;
1789
2000
 
1790
- // src/Foundation/RTC/RtcProvider.ts
1791
- var RtcProvider = /* @__PURE__ */ ((RtcProvider2) => {
1792
- RtcProvider2["trtc"] = "trtc";
1793
- RtcProvider2["agora"] = "agora";
1794
- RtcProvider2["vertc"] = "vertc";
1795
- return RtcProvider2;
1796
- })(RtcProvider || {});
1797
-
1798
2001
  // src/Foundation/RTC/TRTC/RtcEngineManager.ts
1799
2002
  var RtcEngineLease = class {
1800
2003
  /**
@@ -1863,13 +2066,13 @@ var _RtcEngineManager = class _RtcEngineManager {
1863
2066
  * 释放有效租约、销毁 Engine,并把独占权交给下一个等待者。
1864
2067
  */
1865
2068
  release(lease) {
1866
- if (_optionalChain([this, 'access', _143 => _143.activeLease, 'optionalAccess', _144 => _144.id]) !== lease.id) {
2069
+ if (_optionalChain([this, 'access', _176 => _176.activeLease, 'optionalAccess', _177 => _177.id]) !== lease.id) {
1867
2070
  return;
1868
2071
  }
1869
2072
  this.activeLease = void 0;
1870
2073
  try {
1871
2074
  lease.engine.destroy();
1872
- } catch (e10) {
2075
+ } catch (e11) {
1873
2076
  }
1874
2077
  void this.fulfillNextRequest();
1875
2078
  }
@@ -1937,10 +2140,10 @@ var RtcStatsLogger = class {
1937
2140
  `${XmaxLogger.localized("\u7D2F\u8BA1\u63A5\u6536\uFF1A", "Bytes Received: ")}${this.value(stats.bytesReceived, "bytes")}`
1938
2141
  ])];
1939
2142
  const local = stats.localStatistics;
1940
- if (_optionalChain([local, 'optionalAccess', _145 => _145.audio])) {
2143
+ if (_optionalChain([local, 'optionalAccess', _178 => _178.audio])) {
1941
2144
  blocks.push(this.audioBlock("\u672C\u5730\u97F3\u9891\u53D1\u9001 (Local Audio Uplink)", local.audio, false));
1942
2145
  }
1943
- for (const video of _nullishCoalesce(_optionalChain([local, 'optionalAccess', _146 => _146.video]), () => ( []))) {
2146
+ for (const video of _nullishCoalesce(_optionalChain([local, 'optionalAccess', _179 => _179.video]), () => ( []))) {
1944
2147
  blocks.push(this.videoBlock("\u672C\u5730\u89C6\u9891\u53D1\u9001 (Local Video Uplink)", video, false));
1945
2148
  }
1946
2149
  for (const remote of _nullishCoalesce(stats.remoteStatistics, () => ( []))) {
@@ -2030,8 +2233,8 @@ var RtcStatsLogger = class {
2030
2233
 
2031
2234
  // src/Foundation/RTC/TRTC/TrtcAudioVolumeSupport.ts
2032
2235
  function canControlTrtcRemoteAudioVolume(environment) {
2033
- const userAgent = _nullishCoalesce(_optionalChain([environment, 'optionalAccess', _147 => _147.userAgent]), () => ( (typeof navigator === "undefined" ? "" : navigator.userAgent)));
2034
- const maxTouchPoints = _nullishCoalesce(_optionalChain([environment, 'optionalAccess', _148 => _148.maxTouchPoints]), () => ( (typeof navigator === "undefined" ? 0 : _nullishCoalesce(navigator.maxTouchPoints, () => ( 0)))));
2236
+ const userAgent = _nullishCoalesce(_optionalChain([environment, 'optionalAccess', _180 => _180.userAgent]), () => ( (typeof navigator === "undefined" ? "" : navigator.userAgent)));
2237
+ const maxTouchPoints = _nullishCoalesce(_optionalChain([environment, 'optionalAccess', _181 => _181.maxTouchPoints]), () => ( (typeof navigator === "undefined" ? 0 : _nullishCoalesce(navigator.maxTouchPoints, () => ( 0)))));
2035
2238
  const ios = /iPhone|iPad|iPod/i.test(userAgent) || /Macintosh/i.test(userAgent) && maxTouchPoints > 1;
2036
2239
  return !ios;
2037
2240
  }
@@ -2072,6 +2275,11 @@ var TrtcRtcManager = class {
2072
2275
  */
2073
2276
  __publicField(this, "isCapturing", false);
2074
2277
  __publicField(this, "isAudioCapturing", false);
2278
+ /**
2279
+ * 外部媒体的克隆轨;原始轨道由媒体源管理。
2280
+ */
2281
+ __publicField(this, "externalVideo");
2282
+ __publicField(this, "externalAudio");
2075
2283
  /**
2076
2284
  * 房间状态
2077
2285
  */
@@ -2106,25 +2314,78 @@ var TrtcRtcManager = class {
2106
2314
  async destroy() {
2107
2315
  const lease = this.lease;
2108
2316
  this.lease = void 0;
2109
- this.eventListener = void 0;
2317
+ const externalVideo = this.externalVideo;
2318
+ const externalAudio = this.externalAudio;
2319
+ this.externalVideo = void 0;
2320
+ this.externalAudio = void 0;
2110
2321
  this.isCapturing = false;
2111
2322
  this.isAudioCapturing = false;
2112
2323
  this.isInRoom = false;
2113
- _optionalChain([this, 'access', _149 => _149.removeStatsListeners, 'optionalCall', _150 => _150()]);
2324
+ _optionalChain([this, 'access', _182 => _182.removeStatsListeners, 'optionalCall', _183 => _183()]);
2114
2325
  this.removeStatsListeners = void 0;
2115
2326
  if (!lease) {
2327
+ _optionalChain([externalVideo, 'optionalAccess', _184 => _184.stop, 'call', _185 => _185()]);
2328
+ _optionalChain([externalAudio, 'optionalAccess', _186 => _186.stop, 'call', _187 => _187()]);
2116
2329
  return;
2117
2330
  }
2118
2331
  try {
2119
2332
  await lease.engine.stopLocalAudio();
2120
- } catch (e11) {
2333
+ } catch (e12) {
2121
2334
  }
2122
2335
  try {
2123
2336
  await lease.engine.stopLocalVideo();
2124
- } catch (e12) {
2337
+ } catch (e13) {
2125
2338
  }
2339
+ _optionalChain([externalVideo, 'optionalAccess', _188 => _188.stop, 'call', _189 => _189()]);
2340
+ _optionalChain([externalAudio, 'optionalAccess', _190 => _190.stop, 'call', _191 => _191()]);
2126
2341
  this.engineManager.release(lease);
2127
2342
  }
2343
+ /**
2344
+ * 准备外部音视频轨但不发布;失败时释放克隆轨,不影响原始媒体源。
2345
+ */
2346
+ async setExternalMediaTracks(tracks) {
2347
+ const engine = this.requireEngine();
2348
+ if (this.isCapturing || this.isAudioCapturing || this.externalVideo) {
2349
+ throw new XmaxError("INVALID_CONFIGURATION" /* invalidConfiguration */, "Release the current local media before attaching external tracks");
2350
+ }
2351
+ if (tracks.videoTrack.kind !== "video" || tracks.videoTrack.readyState !== "live" || tracks.audioTrack && (tracks.audioTrack.kind !== "audio" || tracks.audioTrack.readyState !== "live")) {
2352
+ throw new XmaxError("INVALID_CONFIGURATION" /* invalidConfiguration */, "External media tracks must be live video/audio tracks");
2353
+ }
2354
+ const video = tracks.videoTrack.clone();
2355
+ this.externalVideo = video;
2356
+ let audio;
2357
+ try {
2358
+ audio = _optionalChain([tracks, 'access', _192 => _192.audioTrack, 'optionalAccess', _193 => _193.clone, 'call', _194 => _194()]);
2359
+ this.externalAudio = audio;
2360
+ await engine.startLocalVideo({ publish: false, option: { videoTrack: video } });
2361
+ if (_optionalChain([this, 'access', _195 => _195.lease, 'optionalAccess', _196 => _196.engine]) !== engine) {
2362
+ throw new XmaxError("CANCELLED" /* cancelled */, "External media preparation was cancelled");
2363
+ }
2364
+ if (audio) {
2365
+ await engine.startLocalAudio({
2366
+ publish: false,
2367
+ option: { audioTrack: audio, echoCancellation: false, autoGainControl: false, noiseSuppression: false }
2368
+ });
2369
+ if (_optionalChain([this, 'access', _197 => _197.lease, 'optionalAccess', _198 => _198.engine]) !== engine) {
2370
+ throw new XmaxError("CANCELLED" /* cancelled */, "External media preparation was cancelled");
2371
+ }
2372
+ }
2373
+ } catch (error) {
2374
+ if (_optionalChain([this, 'access', _199 => _199.lease, 'optionalAccess', _200 => _200.engine]) === engine) {
2375
+ await engine.stopLocalAudio().catch(() => {
2376
+ });
2377
+ await engine.stopLocalVideo().catch(() => {
2378
+ });
2379
+ }
2380
+ if (this.externalVideo === video) {
2381
+ this.externalVideo = void 0;
2382
+ this.externalAudio = void 0;
2383
+ }
2384
+ video.stop();
2385
+ _optionalChain([audio, 'optionalAccess', _201 => _201.stop, 'call', _202 => _202()]);
2386
+ throw this.mapError(error);
2387
+ }
2388
+ }
2128
2389
  /**
2129
2390
  * 启动摄像头采集(不发布)。
2130
2391
  *
@@ -2133,7 +2394,7 @@ var TrtcRtcManager = class {
2133
2394
  */
2134
2395
  async startCameraCapture(options) {
2135
2396
  const engine = this.requireEngine();
2136
- if (this.isCapturing) {
2397
+ if (this.isCapturing || this.externalVideo) {
2137
2398
  throw new XmaxError(
2138
2399
  "INVALID_CONFIGURATION" /* invalidConfiguration */,
2139
2400
  "Camera capture is already running"
@@ -2203,7 +2464,10 @@ var TrtcRtcManager = class {
2203
2464
  * 停止摄像头采集;引擎未初始化或采集未启动时不产生效果。
2204
2465
  */
2205
2466
  async stopCameraCapture() {
2206
- const engine = _optionalChain([this, 'access', _151 => _151.lease, 'optionalAccess', _152 => _152.engine]);
2467
+ if (this.externalVideo) {
2468
+ return;
2469
+ }
2470
+ const engine = _optionalChain([this, 'access', _203 => _203.lease, 'optionalAccess', _204 => _204.engine]);
2207
2471
  this.isCapturing = false;
2208
2472
  if (!engine) {
2209
2473
  return;
@@ -2220,7 +2484,7 @@ var TrtcRtcManager = class {
2220
2484
  * @throws 引擎未初始化、进房参数无效或进房失败时抛出错误。
2221
2485
  */
2222
2486
  async joinRoom(configuration) {
2223
- if (configuration.provider !== "trtc") {
2487
+ if (configuration.provider !== "trtc" /* trtc */) {
2224
2488
  throw new XmaxError("INVALID_CONFIGURATION" /* invalidConfiguration */, "TRTC credentials required");
2225
2489
  }
2226
2490
  const engine = this.requireEngine();
@@ -2256,7 +2520,7 @@ var TrtcRtcManager = class {
2256
2520
  * 离开当前 RTC 房间;未在房间中或引擎未初始化时不产生效果,退房失败仅记录日志。
2257
2521
  */
2258
2522
  async leaveRoom() {
2259
- const engine = _optionalChain([this, 'access', _153 => _153.lease, 'optionalAccess', _154 => _154.engine]);
2523
+ const engine = _optionalChain([this, 'access', _205 => _205.lease, 'optionalAccess', _206 => _206.engine]);
2260
2524
  if (!engine || !this.isInRoom) {
2261
2525
  return;
2262
2526
  }
@@ -2276,18 +2540,18 @@ var TrtcRtcManager = class {
2276
2540
  * TRTC 只接受单一目标码率,以 `minimumBitrate`(流畅优先档)作为目标码率;
2277
2541
  * 编码策略偏好映射为 TRTC 弱网偏好。
2278
2542
  *
2279
- * @throws 摄像头采集未启动或编码参数配置失败时抛出错误。
2543
+ * @throws 本地视频未准备或编码参数配置失败时抛出错误。
2280
2544
  */
2281
2545
  async configureVideoEncoding(configuration) {
2282
2546
  const engine = this.requireEngine();
2283
- if (!this.isCapturing) {
2547
+ if (!this.isCapturing && !this.externalVideo) {
2284
2548
  throw new XmaxError(
2285
2549
  "INVALID_CONFIGURATION" /* invalidConfiguration */,
2286
- "Camera capture is not running"
2550
+ "Local video is not prepared"
2287
2551
  );
2288
2552
  }
2289
2553
  try {
2290
- const encodingSize = rtcOrientedVideoSize({
2554
+ const encodingSize = this.externalVideo ? configuration : rtcOrientedVideoSize({
2291
2555
  width: configuration.width,
2292
2556
  height: configuration.height
2293
2557
  });
@@ -2309,14 +2573,14 @@ var TrtcRtcManager = class {
2309
2573
  /**
2310
2574
  * 发布本地视频流。
2311
2575
  *
2312
- * @throws 摄像头采集未启动或发布失败时抛出错误。
2576
+ * @throws 本地视频未准备或发布失败时抛出错误。
2313
2577
  */
2314
2578
  async publishLocalVideo() {
2315
2579
  const engine = this.requireEngine();
2316
- if (!this.isCapturing) {
2580
+ if (!this.isCapturing && !this.externalVideo) {
2317
2581
  throw new XmaxError(
2318
2582
  "INVALID_CONFIGURATION" /* invalidConfiguration */,
2319
- "Camera capture is not running"
2583
+ "Local video is not prepared"
2320
2584
  );
2321
2585
  }
2322
2586
  try {
@@ -2329,8 +2593,8 @@ var TrtcRtcManager = class {
2329
2593
  * 停止发布本地视频流;采集保持运行,本地预览不受影响。
2330
2594
  */
2331
2595
  async unpublishLocalVideo() {
2332
- const engine = _optionalChain([this, 'access', _155 => _155.lease, 'optionalAccess', _156 => _156.engine]);
2333
- if (!engine || !this.isCapturing) {
2596
+ const engine = _optionalChain([this, 'access', _207 => _207.lease, 'optionalAccess', _208 => _208.engine]);
2597
+ if (!engine || !this.isCapturing && !this.externalVideo) {
2334
2598
  return;
2335
2599
  }
2336
2600
  try {
@@ -2340,14 +2604,18 @@ var TrtcRtcManager = class {
2340
2604
  }
2341
2605
  }
2342
2606
  /**
2343
- * 发布本地音频流;首次调用时启动麦克风采集。
2607
+ * 发布文件音频或麦克风;无文件音轨时不启动麦克风。
2344
2608
  *
2345
2609
  * @throws 引擎未初始化、麦克风权限被拒绝或发布失败时抛出错误。
2346
2610
  */
2347
2611
  async publishLocalAudio() {
2348
2612
  const engine = this.requireEngine();
2349
2613
  try {
2350
- if (!this.isAudioCapturing) {
2614
+ if (this.externalVideo) {
2615
+ if (this.externalAudio) {
2616
+ await engine.updateLocalAudio({ publish: true });
2617
+ }
2618
+ } else if (!this.isAudioCapturing) {
2351
2619
  await engine.startLocalAudio();
2352
2620
  this.isAudioCapturing = true;
2353
2621
  } else {
@@ -2361,8 +2629,8 @@ var TrtcRtcManager = class {
2361
2629
  * 停止发布本地音频流;麦克风采集保持运行。
2362
2630
  */
2363
2631
  async unpublishLocalAudio() {
2364
- const engine = _optionalChain([this, 'access', _157 => _157.lease, 'optionalAccess', _158 => _158.engine]);
2365
- if (!engine || !this.isAudioCapturing) {
2632
+ const engine = _optionalChain([this, 'access', _209 => _209.lease, 'optionalAccess', _210 => _210.engine]);
2633
+ if (!engine || !this.isAudioCapturing && !this.externalAudio) {
2366
2634
  return;
2367
2635
  }
2368
2636
  try {
@@ -2487,19 +2755,19 @@ var TrtcRtcManager = class {
2487
2755
  registerEventBridge(engine) {
2488
2756
  const source = engine;
2489
2757
  const onStatistics = (stats) => {
2490
- if (this.isInRoom && _optionalChain([this, 'access', _159 => _159.lease, 'optionalAccess', _160 => _160.engine]) === engine) {
2758
+ if (this.isInRoom && _optionalChain([this, 'access', _211 => _211.lease, 'optionalAccess', _212 => _212.engine]) === engine) {
2491
2759
  RtcStatsLogger.logStatistics(stats);
2492
- const video = _optionalChain([stats, 'access', _161 => _161.localStatistics, 'optionalAccess', _162 => _162.video, 'optionalAccess', _163 => _163.find, 'call', _164 => _164((item) => item.videoType === "big")]);
2760
+ const video = _optionalChain([stats, 'access', _213 => _213.localStatistics, 'optionalAccess', _214 => _214.video, 'optionalAccess', _215 => _215.find, 'call', _216 => _216((item) => item.videoType === "big")]);
2493
2761
  const valid = (value, minimum) => typeof value === "number" && Number.isFinite(value) && value >= minimum ? value : void 0;
2494
- _optionalChain([this, 'access', _165 => _165.eventListener, 'optionalAccess', _166 => _166.onLocalVideoStatistics, 'optionalCall', _167 => _167(video ? Object.freeze({
2762
+ _optionalChain([this, 'access', _217 => _217.eventListener, 'optionalAccess', _218 => _218.onLocalVideoStatistics, 'optionalCall', _219 => _219(video ? Object.freeze({
2495
2763
  width: valid(video.width, 1),
2496
2764
  height: valid(video.height, 1),
2497
2765
  frameRate: valid(video.frameRate, 0),
2498
2766
  bitrateKbps: valid(video.bitrate, 0)
2499
2767
  }) : void 0)]);
2500
- _optionalChain([this, 'access', _168 => _168.eventListener, 'optionalAccess', _169 => _169.onRemoteVideoStatistics, 'optionalCall', _170 => _170(Object.freeze(
2768
+ _optionalChain([this, 'access', _220 => _220.eventListener, 'optionalAccess', _221 => _221.onRemoteVideoStatistics, 'optionalCall', _222 => _222(Object.freeze(
2501
2769
  (_nullishCoalesce(stats.remoteStatistics, () => ( []))).flatMap((remote) => {
2502
- const video2 = _optionalChain([remote, 'access', _171 => _171.video, 'optionalAccess', _172 => _172.find, 'call', _173 => _173((item) => item.videoType === "big")]);
2770
+ const video2 = _optionalChain([remote, 'access', _223 => _223.video, 'optionalAccess', _224 => _224.find, 'call', _225 => _225((item) => item.videoType === "big")]);
2503
2771
  if (!video2) {
2504
2772
  return [];
2505
2773
  }
@@ -2521,11 +2789,11 @@ var TrtcRtcManager = class {
2521
2789
  }
2522
2790
  };
2523
2791
  const onNetworkQuality = (stats) => {
2524
- if (this.isInRoom && _optionalChain([this, 'access', _174 => _174.lease, 'optionalAccess', _175 => _175.engine]) === engine) {
2792
+ if (this.isInRoom && _optionalChain([this, 'access', _226 => _226.lease, 'optionalAccess', _227 => _227.engine]) === engine) {
2525
2793
  RtcStatsLogger.logNetworkQuality(stats);
2526
2794
  const quality = (value) => Number.isInteger(value) && value >= 0 && value <= 6 ? value : void 0;
2527
2795
  const rtt = (value) => Number.isFinite(value) && value >= 0 ? value : void 0;
2528
- _optionalChain([this, 'access', _176 => _176.eventListener, 'optionalAccess', _177 => _177.onNetworkStatistics, 'optionalCall', _178 => _178(Object.freeze({
2796
+ _optionalChain([this, 'access', _228 => _228.eventListener, 'optionalAccess', _229 => _229.onNetworkStatistics, 'optionalCall', _230 => _230(Object.freeze({
2529
2797
  uplinkQuality: quality(stats.uplinkNetworkQuality),
2530
2798
  downlinkQuality: quality(stats.downlinkNetworkQuality),
2531
2799
  uplinkRttMs: rtt(stats.uplinkRTT),
@@ -2540,36 +2808,36 @@ var TrtcRtcManager = class {
2540
2808
  source.off(RTC_EVENT.networkQuality, onNetworkQuality);
2541
2809
  };
2542
2810
  source.on(RTC_EVENT.remoteVideoAvailable, (event) => {
2543
- if (event.streamType !== STREAM_TYPE_MAIN) {
2811
+ if (_optionalChain([this, 'access', _231 => _231.lease, 'optionalAccess', _232 => _232.engine]) !== engine || event.streamType !== STREAM_TYPE_MAIN) {
2544
2812
  return;
2545
2813
  }
2546
- _optionalChain([this, 'access', _179 => _179.eventListener, 'optionalAccess', _180 => _180.onRemoteVideoPublished, 'call', _181 => _181(event.userId, true)]);
2814
+ _optionalChain([this, 'access', _233 => _233.eventListener, 'optionalAccess', _234 => _234.onRemoteVideoPublished, 'call', _235 => _235(event.userId, true)]);
2547
2815
  });
2548
2816
  source.on(RTC_EVENT.remoteVideoUnavailable, (event) => {
2549
- if (event.streamType !== STREAM_TYPE_MAIN) {
2817
+ if (_optionalChain([this, 'access', _236 => _236.lease, 'optionalAccess', _237 => _237.engine]) !== engine || event.streamType !== STREAM_TYPE_MAIN) {
2550
2818
  return;
2551
2819
  }
2552
- _optionalChain([this, 'access', _182 => _182.eventListener, 'optionalAccess', _183 => _183.onRemoteVideoPublished, 'call', _184 => _184(event.userId, false)]);
2820
+ _optionalChain([this, 'access', _238 => _238.eventListener, 'optionalAccess', _239 => _239.onRemoteVideoPublished, 'call', _240 => _240(event.userId, false)]);
2553
2821
  });
2554
2822
  source.on(RTC_EVENT.customMessage, (event) => {
2555
- if (event.cmdId !== CUSTOM_MESSAGE_CMD_ID) {
2823
+ if (_optionalChain([this, 'access', _241 => _241.lease, 'optionalAccess', _242 => _242.engine]) !== engine || event.cmdId !== CUSTOM_MESSAGE_CMD_ID) {
2556
2824
  return;
2557
2825
  }
2558
2826
  let message;
2559
2827
  try {
2560
2828
  message = new TextDecoder().decode(event.data);
2561
- } catch (e13) {
2829
+ } catch (e14) {
2562
2830
  XmaxLogger.rtc.warning(() => "\u81EA\u5B9A\u4E49\u6D88\u606F\u89E3\u7801\u5931\u8D25 (Failed to Decode Custom Message)");
2563
2831
  return;
2564
2832
  }
2565
- _optionalChain([this, 'access', _185 => _185.eventListener, 'optionalAccess', _186 => _186.onCustomMessageReceived, 'call', _187 => _187(event.userId, message)]);
2833
+ _optionalChain([this, 'access', _243 => _243.eventListener, 'optionalAccess', _244 => _244.onCustomMessageReceived, 'call', _245 => _245(event.userId, message)]);
2566
2834
  });
2567
2835
  }
2568
2836
  /**
2569
2837
  * 返回当前引擎实例;未初始化时抛出错误。
2570
2838
  */
2571
2839
  requireEngine() {
2572
- const engine = _optionalChain([this, 'access', _188 => _188.lease, 'optionalAccess', _189 => _189.engine]);
2840
+ const engine = _optionalChain([this, 'access', _246 => _246.lease, 'optionalAccess', _247 => _247.engine]);
2573
2841
  if (!engine) {
2574
2842
  throw new XmaxError(
2575
2843
  "RTC_ERROR" /* rtcError */,
@@ -2586,8 +2854,8 @@ var TrtcRtcManager = class {
2586
2854
  return error;
2587
2855
  }
2588
2856
  const rtcError = error;
2589
- const originName = _nullishCoalesce(_optionalChain([rtcError, 'optionalAccess', _190 => _190.originError, 'optionalAccess', _191 => _191.name]), () => ( ""));
2590
- const message = typeof _optionalChain([rtcError, 'optionalAccess', _192 => _192.message]) === "string" && rtcError.message.trim().length > 0 ? rtcError.message.trim() : String(error);
2857
+ const originName = _nullishCoalesce(_optionalChain([rtcError, 'optionalAccess', _248 => _248.originError, 'optionalAccess', _249 => _249.name]), () => ( ""));
2858
+ const message = typeof _optionalChain([rtcError, 'optionalAccess', _250 => _250.message]) === "string" && rtcError.message.trim().length > 0 ? rtcError.message.trim() : String(error);
2591
2859
  if (originName === "NotAllowedError" || originName === "SecurityError" || /NotAllowed/i.test(message)) {
2592
2860
  return new XmaxError("CAMERA_PERMISSION_DENIED" /* cameraPermissionDenied */, message);
2593
2861
  }
@@ -2645,6 +2913,11 @@ var VeRtcManager = class {
2645
2913
  __publicField(this, "captureVersion", 0);
2646
2914
  __publicField(this, "camera");
2647
2915
  __publicField(this, "microphone", false);
2916
+ /**
2917
+ * 外部媒体的克隆轨;原始轨道由媒体源管理。
2918
+ */
2919
+ __publicField(this, "externalVideo");
2920
+ __publicField(this, "externalAudio");
2648
2921
  __publicField(this, "connection");
2649
2922
  __publicField(this, "joined", false);
2650
2923
  __publicField(this, "roomVersion", 0);
@@ -2700,18 +2973,76 @@ var VeRtcManager = class {
2700
2973
  this.captureVersion++;
2701
2974
  const engine = this.engine;
2702
2975
  const sdk = this.sdk;
2703
- this.listener = void 0;
2704
2976
  this.clearRoom();
2705
2977
  this.engine = void 0;
2706
2978
  this.sdk = void 0;
2707
2979
  this.camera = void 0;
2708
2980
  this.microphone = false;
2709
- _optionalChain([engine, 'optionalAccess', _193 => _193.removeAllListeners, 'call', _194 => _194()]);
2710
- if (engine && sdk) sdk.default.destroyEngine(engine);
2981
+ _optionalChain([engine, 'optionalAccess', _251 => _251.removeAllListeners, 'call', _252 => _252()]);
2982
+ try {
2983
+ if (engine && sdk) sdk.default.destroyEngine(engine);
2984
+ } finally {
2985
+ _optionalChain([this, 'access', _253 => _253.externalVideo, 'optionalAccess', _254 => _254.stop, 'call', _255 => _255()]);
2986
+ _optionalChain([this, 'access', _256 => _256.externalAudio, 'optionalAccess', _257 => _257.stop, 'call', _258 => _258()]);
2987
+ this.externalVideo = void 0;
2988
+ this.externalAudio = void 0;
2989
+ }
2990
+ }
2991
+ /**
2992
+ * 将克隆的文件音视频轨绑定到主流,不采集设备、不自动发布。
2993
+ */
2994
+ async setExternalMediaTracks(tracks) {
2995
+ const engine = this.requireEngine();
2996
+ const sdk = this.sdk;
2997
+ if (this.camera || this.microphone || this.externalVideo) {
2998
+ throw new XmaxError("INVALID_CONFIGURATION" /* invalidConfiguration */, "Release the current local media before attaching external tracks");
2999
+ }
3000
+ if (tracks.videoTrack.kind !== "video" || tracks.videoTrack.readyState !== "live" || tracks.audioTrack && (tracks.audioTrack.kind !== "audio" || tracks.audioTrack.readyState !== "live")) {
3001
+ throw new XmaxError("INVALID_CONFIGURATION" /* invalidConfiguration */, "External media tracks must be live video/audio tracks");
3002
+ }
3003
+ const video = tracks.videoTrack.clone();
3004
+ this.externalVideo = video;
3005
+ let audio;
3006
+ const index = sdk.StreamIndex.STREAM_INDEX_MAIN;
3007
+ const ensureCurrent = () => {
3008
+ if (this.engine !== engine || this.externalVideo !== video) {
3009
+ throw this.cancelled();
3010
+ }
3011
+ };
3012
+ try {
3013
+ audio = _optionalChain([tracks, 'access', _259 => _259.audioTrack, 'optionalAccess', _260 => _260.clone, 'call', _261 => _261()]);
3014
+ this.externalAudio = audio;
3015
+ await engine.setVideoSourceType(index, sdk.VideoSourceType.VIDEO_SOURCE_TYPE_EXTERNAL);
3016
+ ensureCurrent();
3017
+ await engine.setExternalVideoTrack(index, video);
3018
+ ensureCurrent();
3019
+ if (audio) {
3020
+ await engine.setAudioSourceType(index, sdk.AudioSourceType.AUDIO_SOURCE_TYPE_EXTERNAL);
3021
+ ensureCurrent();
3022
+ await engine.setExternalAudioTrack(index, audio);
3023
+ ensureCurrent();
3024
+ }
3025
+ } catch (error) {
3026
+ if (this.engine === engine) {
3027
+ await engine.setAudioSourceType(index, sdk.AudioSourceType.AUDIO_SOURCE_TYPE_INTERNAL).catch(() => {
3028
+ });
3029
+ await engine.setVideoSourceType(index, sdk.VideoSourceType.VIDEO_SOURCE_TYPE_INTERNAL).catch(() => {
3030
+ });
3031
+ }
3032
+ if (this.externalVideo === video) {
3033
+ this.externalVideo = void 0;
3034
+ this.externalAudio = void 0;
3035
+ }
3036
+ video.stop();
3037
+ _optionalChain([audio, 'optionalAccess', _262 => _262.stop, 'call', _263 => _263()]);
3038
+ throw this.mapError(error);
3039
+ }
2711
3040
  }
2712
3041
  async startCameraCapture(options) {
2713
3042
  const engine = this.requireEngine();
2714
- if (this.camera) throw new XmaxError("INVALID_CONFIGURATION" /* invalidConfiguration */, "Camera capture is already running");
3043
+ if (this.camera || this.externalVideo) {
3044
+ throw new XmaxError("INVALID_CONFIGURATION" /* invalidConfiguration */, "Local video is already prepared");
3045
+ }
2715
3046
  const version = ++this.captureVersion;
2716
3047
  await this.run(() => engine.setVideoEncoderConfig({
2717
3048
  ...rtcOrientedVideoSize({ width: options.width, height: options.height }),
@@ -2751,15 +3082,20 @@ var VeRtcManager = class {
2751
3082
  return track;
2752
3083
  }
2753
3084
  async stopCameraCapture() {
3085
+ if (this.externalVideo) {
3086
+ return;
3087
+ }
2754
3088
  this.captureVersion++;
2755
3089
  this.camera = void 0;
2756
3090
  if (this.engine) await this.run(() => this.engine.stopVideoCapture());
2757
3091
  }
2758
3092
  async configureVideoEncoding(config) {
2759
3093
  const engine = this.requireEngine();
2760
- this.requireCamera();
3094
+ this.requireLocalVideo();
3095
+ const size = this.externalVideo ? config : rtcOrientedVideoSize(config);
2761
3096
  await this.run(() => engine.setVideoEncoderConfig({
2762
- ...rtcOrientedVideoSize({ width: config.width, height: config.height }),
3097
+ width: size.width,
3098
+ height: size.height,
2763
3099
  frameRate: config.frameRate,
2764
3100
  maxKbps: config.maximumBitrate,
2765
3101
  contentHint: config.encoderPreference === "maintainFramerate" /* maintainFramerate */ ? "motion" : "detail",
@@ -2767,12 +3103,12 @@ var VeRtcManager = class {
2767
3103
  }));
2768
3104
  }
2769
3105
  async joinRoom(config) {
2770
- await _optionalChain([this, 'access', _195 => _195.joining, 'optionalAccess', _196 => _196.catch, 'call', _197 => _197(() => {
3106
+ await _optionalChain([this, 'access', _264 => _264.joining, 'optionalAccess', _265 => _265.catch, 'call', _266 => _266(() => {
2771
3107
  })]);
2772
- await _optionalChain([this, 'access', _198 => _198.recovery, 'optionalAccess', _199 => _199.catch, 'call', _200 => _200(() => {
3108
+ await _optionalChain([this, 'access', _267 => _267.recovery, 'optionalAccess', _268 => _268.catch, 'call', _269 => _269(() => {
2773
3109
  })]);
2774
3110
  const engine = this.requireEngine();
2775
- if (config.provider !== "vertc" || config.appID !== VERTC_APP_ID || this.connection) {
3111
+ if (config.provider !== "vertc" /* vertc */ || config.appID !== VERTC_APP_ID || this.connection) {
2776
3112
  throw new XmaxError("INVALID_CONFIGURATION" /* invalidConfiguration */, "VeRTC credentials for the configured AppID and an idle room are required");
2777
3113
  }
2778
3114
  this.clearRoom();
@@ -2818,8 +3154,8 @@ var VeRtcManager = class {
2818
3154
  async updateCredentials(config, signal) {
2819
3155
  const engine = this.requireEngine();
2820
3156
  const current = this.connection;
2821
- if (_optionalChain([signal, 'optionalAccess', _201 => _201.aborted])) throw this.cancelled();
2822
- if (!current || config.provider !== "vertc" || config.appID !== current.appID || config.roomID !== current.roomID || config.userID !== current.userID) {
3157
+ if (_optionalChain([signal, 'optionalAccess', _270 => _270.aborted])) throw this.cancelled();
3158
+ if (!current || config.provider !== "vertc" /* vertc */ || config.appID !== current.appID || config.roomID !== current.roomID || config.userID !== current.userID) {
2823
3159
  throw new XmaxError("SESSION_ERROR" /* sessionError */, "VeRTC session binding changed");
2824
3160
  }
2825
3161
  const version = this.roomVersion;
@@ -2846,7 +3182,7 @@ var VeRtcManager = class {
2846
3182
  const restore = this.tokenExpired || this.publishPrivilegeExpired || this.subscribePrivilegeExpired;
2847
3183
  if (restore) {
2848
3184
  this.joined = false;
2849
- _optionalChain([this, 'access', _202 => _202.listener, 'optionalAccess', _203 => _203.onRoomRejoining, 'optionalCall', _204 => _204()]);
3185
+ _optionalChain([this, 'access', _271 => _271.listener, 'optionalAccess', _272 => _272.onRoomRejoining, 'optionalCall', _273 => _273()]);
2850
3186
  this.clearPlayers();
2851
3187
  this.clearStatistics();
2852
3188
  if (this.tokenExpired) {
@@ -2861,7 +3197,7 @@ var VeRtcManager = class {
2861
3197
  throw this.cancelled();
2862
3198
  }
2863
3199
  }
2864
- if ((this.tokenExpired || this.publishPrivilegeExpired) && this.videoRequested && this.camera) {
3200
+ if ((this.tokenExpired || this.publishPrivilegeExpired) && this.videoRequested && (this.camera || this.externalVideo)) {
2865
3201
  await this.run(() => engine.publishStream(this.sdk.MediaType.VIDEO));
2866
3202
  this.ensureCurrent(engine, version);
2867
3203
  if (!this.videoRequested) {
@@ -2869,7 +3205,7 @@ var VeRtcManager = class {
2869
3205
  this.ensureCurrent(engine, version);
2870
3206
  }
2871
3207
  }
2872
- if ((this.tokenExpired || this.publishPrivilegeExpired) && this.audioRequested && this.microphone) {
3208
+ if ((this.tokenExpired || this.publishPrivilegeExpired) && this.audioRequested && (this.microphone || this.externalAudio)) {
2873
3209
  await this.run(() => engine.publishStream(this.sdk.MediaType.AUDIO));
2874
3210
  this.ensureCurrent(engine, version);
2875
3211
  if (!this.audioRequested) {
@@ -2893,7 +3229,7 @@ var VeRtcManager = class {
2893
3229
  }
2894
3230
  async publishLocalVideo() {
2895
3231
  const engine = this.requireEngine();
2896
- this.requireCamera();
3232
+ this.requireLocalVideo();
2897
3233
  this.videoRequested = true;
2898
3234
  const version = this.roomVersion;
2899
3235
  await this.recovery;
@@ -2907,12 +3243,15 @@ var VeRtcManager = class {
2907
3243
  }
2908
3244
  async publishLocalAudio() {
2909
3245
  const engine = this.requireEngine();
3246
+ if (this.externalVideo && !this.externalAudio) {
3247
+ return;
3248
+ }
2910
3249
  this.audioRequested = true;
2911
3250
  const version = this.roomVersion;
2912
3251
  await this.recovery;
2913
3252
  this.ensureCurrent(engine, version);
2914
3253
  try {
2915
- if (!this.microphone) {
3254
+ if (!this.externalVideo && !this.microphone) {
2916
3255
  await engine.startAudioCapture();
2917
3256
  if (!this.isCurrent(engine, version)) {
2918
3257
  await engine.stopAudioCapture().catch(() => {
@@ -2924,7 +3263,7 @@ var VeRtcManager = class {
2924
3263
  if (!this.audioRequested) throw this.cancelled();
2925
3264
  await engine.publishStream(this.sdk.MediaType.AUDIO);
2926
3265
  } catch (error) {
2927
- throw this.mapError(error, "MICROPHONE_PERMISSION_DENIED" /* microphonePermissionDenied */);
3266
+ throw this.mapError(error, this.externalVideo ? "RTC_ERROR" /* rtcError */ : "MICROPHONE_PERMISSION_DENIED" /* microphonePermissionDenied */);
2928
3267
  }
2929
3268
  }
2930
3269
  async unpublishLocalAudio() {
@@ -3014,13 +3353,13 @@ var VeRtcManager = class {
3014
3353
  if (!active()) return;
3015
3354
  if (video(mediaType)) {
3016
3355
  this.publishedVideo.add(userId);
3017
- if (mediaActive()) _optionalChain([this, 'access', _205 => _205.listener, 'optionalAccess', _206 => _206.onRemoteVideoPublished, 'call', _207 => _207(userId, true)]);
3356
+ if (mediaActive()) _optionalChain([this, 'access', _274 => _274.listener, 'optionalAccess', _275 => _275.onRemoteVideoPublished, 'call', _276 => _276(userId, true)]);
3018
3357
  }
3019
3358
  if (audio(mediaType)) {
3020
3359
  this.publishedAudio.add(userId);
3021
3360
  if (mediaActive() && this.requestedAudio.has(userId)) {
3022
3361
  void this.subscribeRemoteAudio(userId, true).catch((error) => {
3023
- if (mediaActive()) _optionalChain([this, 'access', _208 => _208.listener, 'optionalAccess', _209 => _209.onError, 'optionalCall', _210 => _210(this.mapError(error))]);
3362
+ if (mediaActive()) _optionalChain([this, 'access', _277 => _277.listener, 'optionalAccess', _278 => _278.onError, 'optionalCall', _279 => _279(this.mapError(error))]);
3024
3363
  });
3025
3364
  }
3026
3365
  }
@@ -3040,10 +3379,10 @@ var VeRtcManager = class {
3040
3379
  this.clearPlayer(userInfo.userId);
3041
3380
  });
3042
3381
  engine.on(events.onRoomMessageReceived, ({ userId, message }) => {
3043
- if (mediaActive() && typeof message === "string") _optionalChain([this, 'access', _211 => _211.listener, 'optionalAccess', _212 => _212.onCustomMessageReceived, 'call', _213 => _213(userId, message)]);
3382
+ if (mediaActive() && typeof message === "string") _optionalChain([this, 'access', _280 => _280.listener, 'optionalAccess', _281 => _281.onCustomMessageReceived, 'call', _282 => _282(userId, message)]);
3044
3383
  });
3045
3384
  const willExpire = () => {
3046
- if (active()) _optionalChain([this, 'access', _214 => _214.listener, 'optionalAccess', _215 => _215.onTokenWillExpire, 'optionalCall', _216 => _216()]);
3385
+ if (active()) _optionalChain([this, 'access', _283 => _283.listener, 'optionalAccess', _284 => _284.onTokenWillExpire, 'optionalCall', _285 => _285()]);
3047
3386
  };
3048
3387
  engine.on(events.onTokenWillExpire, willExpire);
3049
3388
  engine.on(events.onTokenPublishPrivilegeWillExpire, willExpire);
@@ -3051,13 +3390,13 @@ var VeRtcManager = class {
3051
3390
  engine.on(events.onTokenPublishPrivilegeDidExpired, () => {
3052
3391
  if (!active() || this.publishPrivilegeExpired) return;
3053
3392
  this.publishPrivilegeExpired = true;
3054
- _optionalChain([this, 'access', _217 => _217.listener, 'optionalAccess', _218 => _218.onTokenExpired, 'optionalCall', _219 => _219()]);
3393
+ _optionalChain([this, 'access', _286 => _286.listener, 'optionalAccess', _287 => _287.onTokenExpired, 'optionalCall', _288 => _288()]);
3055
3394
  });
3056
3395
  engine.on(events.onTokenSubscribePrivilegeDidExpired, () => {
3057
3396
  if (!active() || this.subscribePrivilegeExpired) return;
3058
3397
  this.subscribePrivilegeExpired = true;
3059
3398
  this.clearPlayers();
3060
- _optionalChain([this, 'access', _220 => _220.listener, 'optionalAccess', _221 => _221.onTokenExpired, 'optionalCall', _222 => _222()]);
3399
+ _optionalChain([this, 'access', _289 => _289.listener, 'optionalAccess', _290 => _290.onTokenExpired, 'optionalCall', _291 => _291()]);
3061
3400
  });
3062
3401
  engine.on(events.onError, ({ errorCode }) => {
3063
3402
  if (!active()) return;
@@ -3065,23 +3404,23 @@ var VeRtcManager = class {
3065
3404
  if (this.tokenExpired) return;
3066
3405
  this.tokenExpired = true;
3067
3406
  this.clearPlayers();
3068
- _optionalChain([this, 'access', _223 => _223.listener, 'optionalAccess', _224 => _224.onTokenExpired, 'optionalCall', _225 => _225()]);
3069
- } else _optionalChain([this, 'access', _226 => _226.listener, 'optionalAccess', _227 => _227.onError, 'optionalCall', _228 => _228(this.mapError({ code: errorCode }))]);
3407
+ _optionalChain([this, 'access', _292 => _292.listener, 'optionalAccess', _293 => _293.onTokenExpired, 'optionalCall', _294 => _294()]);
3408
+ } else _optionalChain([this, 'access', _295 => _295.listener, 'optionalAccess', _296 => _296.onError, 'optionalCall', _297 => _297(this.mapError({ code: errorCode }))]);
3070
3409
  });
3071
3410
  engine.on(events.onConnectionStateChanged, ({ state: state2 }) => {
3072
3411
  if (!mediaActive() || state2 !== this.sdk.ConnectionState.CONNECTION_STATE_RECONNECTED) return;
3073
- _optionalChain([this, 'access', _229 => _229.listener, 'optionalAccess', _230 => _230.onRoomRejoining, 'optionalCall', _231 => _231()]);
3412
+ _optionalChain([this, 'access', _298 => _298.listener, 'optionalAccess', _299 => _299.onRoomRejoining, 'optionalCall', _300 => _300()]);
3074
3413
  this.clearPlayers();
3075
3414
  this.clearStatistics();
3076
3415
  this.replayVideo();
3077
3416
  for (const userID of this.requestedAudio) {
3078
3417
  void this.subscribeRemoteAudio(userID, true).catch((error) => {
3079
- if (mediaActive()) _optionalChain([this, 'access', _232 => _232.listener, 'optionalAccess', _233 => _233.onError, 'optionalCall', _234 => _234(this.mapError(error))]);
3418
+ if (mediaActive()) _optionalChain([this, 'access', _301 => _301.listener, 'optionalAccess', _302 => _302.onError, 'optionalCall', _303 => _303(this.mapError(error))]);
3080
3419
  });
3081
3420
  }
3082
3421
  });
3083
3422
  engine.on(events.onNetworkQuality, (up, down) => {
3084
- if (mediaActive()) _optionalChain([this, 'access', _235 => _235.listener, 'optionalAccess', _236 => _236.onNetworkStatistics, 'optionalCall', _237 => _237(Object.freeze({
3423
+ if (mediaActive()) _optionalChain([this, 'access', _304 => _304.listener, 'optionalAccess', _305 => _305.onNetworkStatistics, 'optionalCall', _306 => _306(Object.freeze({
3085
3424
  uplinkQuality: VeRtcStatistics.quality(up),
3086
3425
  downlinkQuality: VeRtcStatistics.quality(down)
3087
3426
  }))]);
@@ -3090,7 +3429,7 @@ var VeRtcManager = class {
3090
3429
  if (!mediaActive() || stats.isScreen || !this.videoRequested) return;
3091
3430
  const result = VeRtcStatistics.local(stats.videoStats);
3092
3431
  this.uplinkLossPercent = result.uplinkLossPercent;
3093
- _optionalChain([this, 'access', _238 => _238.listener, 'optionalAccess', _239 => _239.onLocalVideoStatistics, 'optionalCall', _240 => _240(result)]);
3432
+ _optionalChain([this, 'access', _307 => _307.listener, 'optionalAccess', _308 => _308.onLocalVideoStatistics, 'optionalCall', _309 => _309(result)]);
3094
3433
  XmaxLogger.rtc.info(() => `VeRTC local video statistics: ${JSON.stringify(result)}`, 2 /* performance */);
3095
3434
  });
3096
3435
  engine.on(events.onRemoteStreamStats, (stats) => {
@@ -3100,21 +3439,21 @@ var VeRtcManager = class {
3100
3439
  });
3101
3440
  }
3102
3441
  replayVideo() {
3103
- for (const userID of this.publishedVideo) _optionalChain([this, 'access', _241 => _241.listener, 'optionalAccess', _242 => _242.onRemoteVideoPublished, 'call', _243 => _243(userID, true)]);
3442
+ for (const userID of this.publishedVideo) _optionalChain([this, 'access', _310 => _310.listener, 'optionalAccess', _311 => _311.onRemoteVideoPublished, 'call', _312 => _312(userID, true)]);
3104
3443
  }
3105
3444
  removeVideo(userID) {
3106
3445
  this.publishedVideo.delete(userID);
3107
3446
  this.remoteStatistics.delete(userID);
3108
3447
  this.emitRemoteStatistics();
3109
- if (this.joined && !this.tokenExpired) _optionalChain([this, 'access', _244 => _244.listener, 'optionalAccess', _245 => _245.onRemoteVideoPublished, 'call', _246 => _246(userID, false)]);
3448
+ if (this.joined && !this.tokenExpired) _optionalChain([this, 'access', _313 => _313.listener, 'optionalAccess', _314 => _314.onRemoteVideoPublished, 'call', _315 => _315(userID, false)]);
3110
3449
  }
3111
3450
  emitRemoteStatistics() {
3112
- _optionalChain([this, 'access', _247 => _247.listener, 'optionalAccess', _248 => _248.onRemoteVideoStatistics, 'optionalCall', _249 => _249(Object.freeze([...this.remoteStatistics.values()]))]);
3451
+ _optionalChain([this, 'access', _316 => _316.listener, 'optionalAccess', _317 => _317.onRemoteVideoStatistics, 'optionalCall', _318 => _318(Object.freeze([...this.remoteStatistics.values()]))]);
3113
3452
  }
3114
3453
  clearStatistics() {
3115
3454
  this.remoteStatistics.clear();
3116
3455
  this.uplinkLossPercent = void 0;
3117
- _optionalChain([this, 'access', _250 => _250.listener, 'optionalAccess', _251 => _251.onLocalVideoStatistics, 'optionalCall', _252 => _252(void 0)]);
3456
+ _optionalChain([this, 'access', _319 => _319.listener, 'optionalAccess', _320 => _320.onLocalVideoStatistics, 'optionalCall', _321 => _321(void 0)]);
3118
3457
  this.emitRemoteStatistics();
3119
3458
  }
3120
3459
  clearPlayer(userID) {
@@ -3160,15 +3499,15 @@ var VeRtcManager = class {
3160
3499
  const cleanup = () => {
3161
3500
  clearTimeout(timer);
3162
3501
  roomSignal.removeEventListener("abort", cancel);
3163
- _optionalChain([signal, 'optionalAccess', _253 => _253.removeEventListener, 'call', _254 => _254("abort", cancel)]);
3502
+ _optionalChain([signal, 'optionalAccess', _322 => _322.removeEventListener, 'call', _323 => _323("abort", cancel)]);
3164
3503
  };
3165
3504
  const cancel = () => {
3166
- if (_optionalChain([signal, 'optionalAccess', _255 => _255.aborted]) && !roomSignal.aborted) this.roomVersion++;
3505
+ if (_optionalChain([signal, 'optionalAccess', _324 => _324.aborted]) && !roomSignal.aborted) this.roomVersion++;
3167
3506
  cleanup();
3168
3507
  reject(this.cancelled());
3169
3508
  };
3170
3509
  roomSignal.addEventListener("abort", cancel, { once: true });
3171
- _optionalChain([signal, 'optionalAccess', _256 => _256.addEventListener, 'call', _257 => _257("abort", cancel, { once: true })]);
3510
+ _optionalChain([signal, 'optionalAccess', _325 => _325.addEventListener, 'call', _326 => _326("abort", cancel, { once: true })]);
3172
3511
  task.then((value) => {
3173
3512
  cleanup();
3174
3513
  resolve(value);
@@ -3176,7 +3515,7 @@ var VeRtcManager = class {
3176
3515
  cleanup();
3177
3516
  reject(error);
3178
3517
  });
3179
- if (roomSignal.aborted || _optionalChain([signal, 'optionalAccess', _258 => _258.aborted])) cancel();
3518
+ if (roomSignal.aborted || _optionalChain([signal, 'optionalAccess', _327 => _327.aborted])) cancel();
3180
3519
  });
3181
3520
  }
3182
3521
  isCurrent(engine, version) {
@@ -3189,6 +3528,15 @@ var VeRtcManager = class {
3189
3528
  if (!this.engine) throw new XmaxError("RTC_ERROR" /* rtcError */, "VeRTC is not initialized");
3190
3529
  return this.engine;
3191
3530
  }
3531
+ /**
3532
+ * 获取已准备的文件或摄像头视频轨。
3533
+ */
3534
+ requireLocalVideo() {
3535
+ if (this.externalVideo) {
3536
+ return this.externalVideo;
3537
+ }
3538
+ return this.requireCamera();
3539
+ }
3192
3540
  requireCamera() {
3193
3541
  if (!this.camera) throw new XmaxError("INVALID_CONFIGURATION" /* invalidConfiguration */, "Camera capture is not running");
3194
3542
  return this.camera;
@@ -3203,8 +3551,8 @@ var VeRtcManager = class {
3203
3551
  mapError(error, permissionCode) {
3204
3552
  if (error instanceof XmaxError) return error;
3205
3553
  const value = error;
3206
- const code = typeof _optionalChain([value, 'optionalAccess', _259 => _259.code]) === "string" && /^[A-Z_0-9-]{1,64}$/.test(value.code) ? value.code : "UNKNOWN";
3207
- const denied = _optionalChain([value, 'optionalAccess', _260 => _260.name]) === "NotAllowedError" || code === "PERMISSION_DENIED" || code === "DEVICE_PERMISSION_DENIED";
3554
+ const code = typeof _optionalChain([value, 'optionalAccess', _328 => _328.code]) === "string" && /^[A-Z_0-9-]{1,64}$/.test(value.code) ? value.code : "UNKNOWN";
3555
+ const denied = _optionalChain([value, 'optionalAccess', _329 => _329.name]) === "NotAllowedError" || code === "PERMISSION_DENIED" || code === "DEVICE_PERMISSION_DENIED";
3208
3556
  return new XmaxError(permissionCode && denied ? permissionCode : "RTC_ERROR" /* rtcError */, `VeRTC operation failed (${code})`);
3209
3557
  }
3210
3558
  cancelled() {
@@ -3215,12 +3563,14 @@ var VeRtcManager = class {
3215
3563
  // src/Foundation/RTC/RtcFactory.ts
3216
3564
  function createRtcManager(provider, environment) {
3217
3565
  switch (provider) {
3566
+ case "trtc" /* trtc */:
3567
+ return new TrtcRtcManager();
3218
3568
  case "agora" /* agora */:
3219
3569
  return new AgoraRtcManager({ environment });
3220
3570
  case "vertc" /* vertc */:
3221
3571
  return new VeRtcManager();
3222
3572
  default:
3223
- return new TrtcRtcManager();
3573
+ throw new XmaxError("INVALID_CONFIGURATION" /* invalidConfiguration */, "Unsupported RTC provider");
3224
3574
  }
3225
3575
  }
3226
3576
 
@@ -3284,7 +3634,7 @@ var PermissionManager = class {
3284
3634
  * @param deniedCode 权限被拒绝时抛出的错误码。
3285
3635
  */
3286
3636
  async ensurePermission(name, deniedCode) {
3287
- if (typeof navigator === "undefined" || !_optionalChain([navigator, 'access', _261 => _261.mediaDevices, 'optionalAccess', _262 => _262.getUserMedia])) {
3637
+ if (typeof navigator === "undefined" || !_optionalChain([navigator, 'access', _330 => _330.mediaDevices, 'optionalAccess', _331 => _331.getUserMedia])) {
3288
3638
  throw new XmaxError(
3289
3639
  deniedCode,
3290
3640
  "This browser does not support media capture (getUserMedia)"
@@ -3292,7 +3642,7 @@ var PermissionManager = class {
3292
3642
  }
3293
3643
  try {
3294
3644
  const permissions = navigator.permissions;
3295
- if (!_optionalChain([permissions, 'optionalAccess', _263 => _263.query])) {
3645
+ if (!_optionalChain([permissions, 'optionalAccess', _332 => _332.query])) {
3296
3646
  return;
3297
3647
  }
3298
3648
  const status = await permissions.query({
@@ -3472,10 +3822,10 @@ var _CameraController = class _CameraController {
3472
3822
  */
3473
3823
  __publicField(this, "storedUseMicrophone", false);
3474
3824
  __publicField(this, "storedFrameValidationEnabled", true);
3475
- this.permissionManager = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _264 => _264.permissionManager]), () => ( new PermissionManager()));
3476
- this.rtcManager = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _265 => _265.rtcManager]), () => ( new TrtcRtcManager()));
3477
- this.mediaService = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _266 => _266.mediaService]), () => ( new MediaService()));
3478
- this.errorListener = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _267 => _267.errorListener]), () => ( (() => {
3825
+ this.permissionManager = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _333 => _333.permissionManager]), () => ( new PermissionManager()));
3826
+ this.rtcManager = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _334 => _334.rtcManager]), () => ( new TrtcRtcManager()));
3827
+ this.mediaService = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _335 => _335.mediaService]), () => ( new MediaService()));
3828
+ this.errorListener = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _336 => _336.errorListener]), () => ( (() => {
3479
3829
  })));
3480
3830
  }
3481
3831
  /**
@@ -3501,7 +3851,7 @@ var _CameraController = class _CameraController {
3501
3851
  */
3502
3852
  async waitForValidCameraFrame(signal) {
3503
3853
  const track = this.activeTrack;
3504
- const mediaTrack = _optionalChain([track, 'optionalAccess', _268 => _268.mediaStreamTrack]);
3854
+ const mediaTrack = _optionalChain([track, 'optionalAccess', _337 => _337.mediaStreamTrack]);
3505
3855
  if (!mediaTrack) throw new XmaxError("MEDIA_ERROR" /* mediaError */, "Camera capture is not running");
3506
3856
  if (signal.aborted) throw new XmaxError("CANCELLED" /* cancelled */, "Camera frame validation cancelled");
3507
3857
  if (!this.isFrameValidationEnabled) return;
@@ -3601,7 +3951,7 @@ var _CameraController = class _CameraController {
3601
3951
  */
3602
3952
  async switchCamera() {
3603
3953
  const track = this.activeTrack;
3604
- const position = _optionalChain([track, 'optionalAccess', _269 => _269.position]);
3954
+ const position = _optionalChain([track, 'optionalAccess', _338 => _338.position]);
3605
3955
  if (!track || !position) {
3606
3956
  throw new XmaxError(
3607
3957
  "RTC_ERROR" /* rtcError */,
@@ -3619,7 +3969,7 @@ var _CameraController = class _CameraController {
3619
3969
  }
3620
3970
  this.previewStream.addTrack(mediaTrack);
3621
3971
  }
3622
- _optionalChain([this, 'access', _270 => _270.previewMirrorApplier, 'optionalCall', _271 => _271(nextPosition === "front" /* front */)]);
3972
+ _optionalChain([this, 'access', _339 => _339.previewMirrorApplier, 'optionalCall', _340 => _340(nextPosition === "front" /* front */)]);
3623
3973
  this.observeMediaTrack(track, mediaTrack);
3624
3974
  return new RealtimeMediaStream({ id: "stream0" /* local */, videoTrack: track });
3625
3975
  }
@@ -3715,6 +4065,463 @@ var _CameraController = class _CameraController {
3715
4065
  __publicField(_CameraController, "localVideoTrackID", "video0");
3716
4066
  var CameraController = _CameraController;
3717
4067
 
4068
+ // src/Service/Realtime/RealtimeReferenceVideo.ts
4069
+ var RealtimeVideoSampleMethod = /* @__PURE__ */ ((RealtimeVideoSampleMethod2) => {
4070
+ RealtimeVideoSampleMethod2["time"] = "time";
4071
+ RealtimeVideoSampleMethod2["fps"] = "fps";
4072
+ return RealtimeVideoSampleMethod2;
4073
+ })(RealtimeVideoSampleMethod || {});
4074
+
4075
+ // src/Media/Video/NetworkVideoController.ts
4076
+ var NetworkVideoController = class {
4077
+ constructor() {
4078
+ /**
4079
+ * 当前视频源与完成通知
4080
+ */
4081
+ __publicField(this, "currentTrack");
4082
+ __publicField(this, "reference");
4083
+ __publicField(this, "onFinish");
4084
+ /**
4085
+ * 预览进度与视图资源
4086
+ */
4087
+ __publicField(this, "position", 0);
4088
+ __publicField(this, "ended", false);
4089
+ __publicField(this, "bindings", /* @__PURE__ */ new Map());
4090
+ }
4091
+ /**
4092
+ * 创建网络视频源,校验地址、采样方式和模型支持的尺寸;不请求视频下载。
4093
+ */
4094
+ create(options, mediaService) {
4095
+ let url;
4096
+ try {
4097
+ url = new URL(options.url);
4098
+ } catch (e15) {
4099
+ throw new XmaxError("INVALID_CONFIGURATION" /* invalidConfiguration */, "Network video requires an absolute HTTP or HTTPS URL");
4100
+ }
4101
+ if (!["http:", "https:"].includes(url.protocol) || !url.hostname || url.username || url.password) {
4102
+ throw new XmaxError("INVALID_CONFIGURATION" /* invalidConfiguration */, "Network video requires an HTTP or HTTPS URL without embedded credentials");
4103
+ }
4104
+ const sampleMethod = _nullishCoalesce(options.sampleMethod, () => ( "time")) /* time */;
4105
+ if (!Object.values(RealtimeVideoSampleMethod).includes(sampleMethod)) {
4106
+ throw new XmaxError("INVALID_CONFIGURATION" /* invalidConfiguration */, "Unsupported video sample method");
4107
+ }
4108
+ if (this.currentTrack) {
4109
+ throw new XmaxError("INVALID_CONFIGURATION" /* invalidConfiguration */, "Close the current network video stream before creating another one");
4110
+ }
4111
+ options.videoFormat.validate();
4112
+ const size = mediaService.resolveModelInputSize(options.videoFormat);
4113
+ const track = new RealtimeVideoTrack({
4114
+ id: "network-video",
4115
+ videoFormat: options.videoFormat.resized(size.width, size.height)
4116
+ });
4117
+ this.currentTrack = track;
4118
+ this.reference = Object.freeze({ path: options.url.trim(), sampleMethod });
4119
+ this.onFinish = options.onFinish;
4120
+ VideoRenderRegistry.register(track, {
4121
+ attachHandler: (view) => this.attach(view),
4122
+ detachHandler: (view) => this.detach(view)
4123
+ });
4124
+ return new RealtimeMediaStream({ id: "stream0" /* local */, videoTrack: track });
4125
+ }
4126
+ /**
4127
+ * 释放全部预览绑定及源信息,不影响其他媒体源。
4128
+ */
4129
+ stop() {
4130
+ for (const view of this.bindings.keys()) {
4131
+ this.detach(view);
4132
+ }
4133
+ if (this.currentTrack) {
4134
+ VideoRenderRegistry.unregister(this.currentTrack);
4135
+ }
4136
+ this.currentTrack = void 0;
4137
+ this.reference = void 0;
4138
+ this.onFinish = void 0;
4139
+ this.position = 0;
4140
+ this.ended = false;
4141
+ }
4142
+ /**
4143
+ * 绑定播放器,恢复预览位置;本地播放错误仅记录,不中断服务端生成。
4144
+ */
4145
+ attach(view) {
4146
+ if (!this.reference || !view.videoElement || !view.setVideoURL) {
4147
+ throw new XmaxError("MEDIA_ERROR" /* mediaError */, "This render target does not support network video preview");
4148
+ }
4149
+ this.detach(view);
4150
+ const video = view.videoElement;
4151
+ video.autoplay = !this.ended;
4152
+ const restore = () => {
4153
+ if (this.position > 0) {
4154
+ video.currentTime = Math.min(this.position, Math.max(0, video.duration - 1e-3) || this.position);
4155
+ }
4156
+ if (this.ended) {
4157
+ video.pause();
4158
+ }
4159
+ };
4160
+ const fail = () => {
4161
+ XmaxLogger.render.warning(() => "\u7F51\u7EDC\u89C6\u9891\u9884\u89C8\u5931\u8D25 (Network Video Preview Failed)");
4162
+ };
4163
+ video.addEventListener("loadedmetadata", restore);
4164
+ video.addEventListener("error", fail);
4165
+ this.bindings.set(view, () => {
4166
+ this.position = video.currentTime;
4167
+ this.ended = video.ended || this.ended;
4168
+ video.removeEventListener("loadedmetadata", restore);
4169
+ video.removeEventListener("error", fail);
4170
+ _optionalChain([view, 'access', _341 => _341.setVideoURL, 'optionalCall', _342 => _342(null)]);
4171
+ });
4172
+ view.isMirrored = false;
4173
+ view.setVideoURL(this.reference.path);
4174
+ }
4175
+ /**
4176
+ * 保存预览进度并释放该视图的地址和事件监听。
4177
+ */
4178
+ detach(view) {
4179
+ _optionalChain([this, 'access', _343 => _343.bindings, 'access', _344 => _344.get, 'call', _345 => _345(view), 'optionalCall', _346 => _346()]);
4180
+ this.bindings.delete(view);
4181
+ }
4182
+ };
4183
+
4184
+ // src/Media/Video/LocalVideoController.ts
4185
+ var LocalVideoController = class {
4186
+ /**
4187
+ * 保存媒体错误处理器,不创建浏览器资源。
4188
+ */
4189
+ constructor(onError = () => {
4190
+ }) {
4191
+ /**
4192
+ * 当前文件资源与运行期错误通知
4193
+ */
4194
+ __publicField(this, "source");
4195
+ __publicField(this, "onError");
4196
+ this.onError = onError;
4197
+ }
4198
+ /**
4199
+ * 是否持有文件资源,包含尚未完成准备的播放器。
4200
+ */
4201
+ get isActive() {
4202
+ return this.source !== void 0;
4203
+ }
4204
+ /**
4205
+ * 准备完成的本地视频轨。
4206
+ */
4207
+ get currentTrack() {
4208
+ return _optionalChain([this, 'access', _347 => _347.source, 'optionalAccess', _348 => _348.track]);
4209
+ }
4210
+ /**
4211
+ * 文件音频输出;禁用音频时为空,无音轨文件的 Web Audio 输出为静音。
4212
+ */
4213
+ get audioTrack() {
4214
+ return _optionalChain([this, 'access', _349 => _349.source, 'optionalAccess', _350 => _350.audioDestination, 'optionalAccess', _351 => _351.stream, 'access', _352 => _352.getAudioTracks, 'call', _353 => _353(), 'access', _354 => _354[0]]);
4215
+ }
4216
+ /**
4217
+ * 校验文件和格式,准备首帧与音频输出;应从用户点击事件直接调用以解锁播放。
4218
+ */
4219
+ async create(options, mediaService, signal) {
4220
+ if (this.source) {
4221
+ throw new XmaxError("INVALID_CONFIGURATION" /* invalidConfiguration */, "Close the current local video before creating another one");
4222
+ }
4223
+ if (!(options.file instanceof Blob) || options.file.size === 0) {
4224
+ throw new XmaxError("INVALID_CONFIGURATION" /* invalidConfiguration */, "Local video requires a non-empty File or Blob");
4225
+ }
4226
+ const format = this.resolveVideoFormat(options.videoFormat, mediaService);
4227
+ if (signal.aborted) {
4228
+ throw this.cancelled();
4229
+ }
4230
+ if (typeof document === "undefined") {
4231
+ throw new XmaxError("MEDIA_ERROR" /* mediaError */, "Local video requires a browser");
4232
+ }
4233
+ const video = document.createElement("video");
4234
+ const canvas = document.createElement("canvas");
4235
+ const context = canvas.getContext("2d");
4236
+ if (!context || typeof canvas.captureStream !== "function") {
4237
+ throw new XmaxError("MEDIA_ERROR" /* mediaError */, "Canvas video capture is unavailable");
4238
+ }
4239
+ canvas.width = format.width;
4240
+ canvas.height = format.height;
4241
+ const source = {
4242
+ video,
4243
+ canvas,
4244
+ context,
4245
+ url: URL.createObjectURL(options.file),
4246
+ abort: new AbortController(),
4247
+ targets: /* @__PURE__ */ new Set()
4248
+ };
4249
+ this.source = source;
4250
+ try {
4251
+ video.playsInline = true;
4252
+ video.loop = _nullishCoalesce(options.loop, () => ( true));
4253
+ video.preload = "auto";
4254
+ video.volume = 1;
4255
+ video.muted = options.includeAudio === false;
4256
+ if (options.includeAudio !== false) {
4257
+ if (typeof AudioContext === "undefined") {
4258
+ throw new XmaxError("MEDIA_ERROR" /* mediaError */, "Web Audio is unavailable; disable file audio or use a supported browser");
4259
+ }
4260
+ const audio = new AudioContext();
4261
+ source.audioContext = audio;
4262
+ source.audioSource = audio.createMediaElementSource(video);
4263
+ source.audioDestination = audio.createMediaStreamDestination();
4264
+ source.audioSource.connect(source.audioDestination);
4265
+ }
4266
+ video.src = source.url;
4267
+ const resume = _optionalChain([source, 'access', _355 => _355.audioContext, 'optionalAccess', _356 => _356.resume, 'call', _357 => _357()]);
4268
+ const playing = this.play(source, signal);
4269
+ await this.wait(source, Promise.all([resume, playing]), signal);
4270
+ video.pause();
4271
+ if (!video.videoWidth || !video.videoHeight || !Number.isFinite(video.duration) || video.duration <= 0) {
4272
+ throw new XmaxError("MEDIA_ERROR" /* mediaError */, "The file has no playable finite video stream");
4273
+ }
4274
+ await this.rewind(source, signal);
4275
+ this.draw(source);
4276
+ source.stream = canvas.captureStream(format.fps);
4277
+ const nativeTrack = source.stream.getVideoTracks()[0];
4278
+ if (!nativeTrack) {
4279
+ throw new XmaxError("MEDIA_ERROR" /* mediaError */, "Canvas did not provide a video track");
4280
+ }
4281
+ const track = new RealtimeVideoTrack({ id: "local-video", videoFormat: format });
4282
+ track.mediaStreamTrack = nativeTrack;
4283
+ source.track = track;
4284
+ this.draw(source);
4285
+ source.drawTimer = setInterval(() => {
4286
+ if (this.source !== source) {
4287
+ return;
4288
+ }
4289
+ try {
4290
+ this.draw(source);
4291
+ } catch (e16) {
4292
+ this.handleError(source);
4293
+ }
4294
+ }, 1e3 / format.fps);
4295
+ source.onError = () => this.handleError(source);
4296
+ video.addEventListener("error", source.onError);
4297
+ VideoRenderRegistry.register(track, {
4298
+ attachHandler: (view) => {
4299
+ view.isMirrored = false;
4300
+ view.setMediaStream(source.stream);
4301
+ source.targets.add(view);
4302
+ },
4303
+ detachHandler: (view) => {
4304
+ source.targets.delete(view);
4305
+ view.setMediaStream(null);
4306
+ }
4307
+ });
4308
+ return new RealtimeMediaStream({ id: "stream0" /* local */, videoTrack: track });
4309
+ } catch (error) {
4310
+ await this.stop();
4311
+ throw XmaxError.from(error);
4312
+ }
4313
+ }
4314
+ /**
4315
+ * 开始信令发送成功后,从文件开头同步启动音视频;不等待远端首帧。
4316
+ */
4317
+ async start(signal) {
4318
+ const source = this.source;
4319
+ if (!_optionalChain([source, 'optionalAccess', _358 => _358.track])) {
4320
+ throw new XmaxError("INVALID_CONFIGURATION" /* invalidConfiguration */, "Local video is not prepared");
4321
+ }
4322
+ if (signal.aborted) {
4323
+ throw this.cancelled();
4324
+ }
4325
+ try {
4326
+ await this.rewind(source, signal);
4327
+ await this.wait(source, Promise.all([_optionalChain([source, 'access', _359 => _359.audioContext, 'optionalAccess', _360 => _360.resume, 'call', _361 => _361()]), this.play(source, signal)]), signal);
4328
+ } catch (error) {
4329
+ source.video.pause();
4330
+ throw error;
4331
+ }
4332
+ }
4333
+ /**
4334
+ * 暂停文件音视频,保留当前预览;重新开始生成时回到文件开头。
4335
+ */
4336
+ pause() {
4337
+ _optionalChain([this, 'access', _362 => _362.source, 'optionalAccess', _363 => _363.video, 'access', _364 => _364.pause, 'call', _365 => _365()]);
4338
+ }
4339
+ /**
4340
+ * 释放播放器、预览、定时器、轨道、音频上下文和对象 URL。
4341
+ */
4342
+ async stop() {
4343
+ const source = this.source;
4344
+ this.source = void 0;
4345
+ if (!source) {
4346
+ return;
4347
+ }
4348
+ source.abort.abort();
4349
+ clearInterval(source.drawTimer);
4350
+ if (source.onError) {
4351
+ source.video.removeEventListener("error", source.onError);
4352
+ }
4353
+ if (source.track) {
4354
+ VideoRenderRegistry.unregister(source.track);
4355
+ source.track.mediaStreamTrack = void 0;
4356
+ }
4357
+ for (const view of source.targets) {
4358
+ try {
4359
+ view.setMediaStream(null);
4360
+ } catch (e17) {
4361
+ }
4362
+ }
4363
+ source.targets.clear();
4364
+ source.video.pause();
4365
+ source.video.removeAttribute("src");
4366
+ source.video.load();
4367
+ _optionalChain([source, 'access', _366 => _366.stream, 'optionalAccess', _367 => _367.getTracks, 'call', _368 => _368(), 'access', _369 => _369.forEach, 'call', _370 => _370((track) => track.stop())]);
4368
+ _optionalChain([source, 'access', _371 => _371.audioDestination, 'optionalAccess', _372 => _372.stream, 'access', _373 => _373.getTracks, 'call', _374 => _374(), 'access', _375 => _375.forEach, 'call', _376 => _376((track) => track.stop())]);
4369
+ _optionalChain([source, 'access', _377 => _377.audioSource, 'optionalAccess', _378 => _378.disconnect, 'call', _379 => _379()]);
4370
+ _optionalChain([source, 'access', _380 => _380.audioDestination, 'optionalAccess', _381 => _381.disconnect, 'call', _382 => _382()]);
4371
+ URL.revokeObjectURL(source.url);
4372
+ await _optionalChain([source, 'access', _383 => _383.audioContext, 'optionalAccess', _384 => _384.close, 'call', _385 => _385(), 'access', _386 => _386.catch, 'call', _387 => _387(() => {
4373
+ })]);
4374
+ }
4375
+ /**
4376
+ * 播放被浏览器拒绝时给出可操作错误;关闭后的迟到播放立即暂停。
4377
+ */
4378
+ play(source, signal) {
4379
+ return source.video.play().then(() => {
4380
+ if (this.source !== source || source.abort.signal.aborted || signal.aborted) {
4381
+ source.video.pause();
4382
+ throw this.cancelled();
4383
+ }
4384
+ }).catch((error) => {
4385
+ if (error instanceof XmaxError) {
4386
+ throw error;
4387
+ }
4388
+ throw new XmaxError("MEDIA_ERROR" /* mediaError */, "Cannot play local video; call from a user click and use a browser-supported video codec");
4389
+ });
4390
+ }
4391
+ /**
4392
+ * 等待 seek 完成,确保重新生成从首帧开始,而非使用旧缓存画面。
4393
+ */
4394
+ async rewind(source, signal) {
4395
+ if (source.video.currentTime === 0) {
4396
+ return;
4397
+ }
4398
+ let seeked;
4399
+ const ready = new Promise((resolve) => {
4400
+ seeked = resolve;
4401
+ });
4402
+ source.video.addEventListener("seeked", seeked, { once: true });
4403
+ try {
4404
+ source.video.currentTime = 0;
4405
+ await this.wait(source, ready, signal);
4406
+ } finally {
4407
+ source.video.removeEventListener("seeked", seeked);
4408
+ }
4409
+ }
4410
+ /**
4411
+ * 给浏览器准备和播放操作设置 10 秒上限,同时响应关闭、取消和解码错误。
4412
+ */
4413
+ wait(source, operation, signal) {
4414
+ return new Promise((resolve, reject) => {
4415
+ const finish = (error) => {
4416
+ clearTimeout(timer);
4417
+ signal.removeEventListener("abort", abort);
4418
+ source.abort.signal.removeEventListener("abort", abort);
4419
+ source.video.removeEventListener("error", failed);
4420
+ if (error) {
4421
+ reject(error);
4422
+ } else {
4423
+ resolve();
4424
+ }
4425
+ };
4426
+ const abort = () => finish(this.cancelled());
4427
+ const failed = () => finish(new XmaxError("MEDIA_ERROR" /* mediaError */, "The browser cannot decode this video file"));
4428
+ const timer = setTimeout(() => finish(new XmaxError("TIMEOUT" /* timeout */, "Local video preparation timed out; start from a user click and check the video codec")), 1e4);
4429
+ signal.addEventListener("abort", abort, { once: true });
4430
+ source.abort.signal.addEventListener("abort", abort, { once: true });
4431
+ source.video.addEventListener("error", failed, { once: true });
4432
+ operation.then(() => finish(), finish);
4433
+ if (signal.aborted || source.abort.signal.aborted) {
4434
+ abort();
4435
+ }
4436
+ });
4437
+ }
4438
+ /**
4439
+ * 等比缩放并居中补黑边,保留完整画面,不按设备方向转置文件尺寸。
4440
+ */
4441
+ draw(source) {
4442
+ const { video, canvas, context } = source;
4443
+ if (video.readyState < 2) {
4444
+ return;
4445
+ }
4446
+ const scale = Math.min(canvas.width / video.videoWidth, canvas.height / video.videoHeight);
4447
+ const width = video.videoWidth * scale;
4448
+ const height = video.videoHeight * scale;
4449
+ context.fillStyle = "black";
4450
+ context.fillRect(0, 0, canvas.width, canvas.height);
4451
+ context.drawImage(video, (canvas.width - width) / 2, (canvas.height - height) / 2, width, height);
4452
+ }
4453
+ /**
4454
+ * 仅为本地文件选择最接近宽高比的模型档位;比例距离相同时优先横屏。
4455
+ * 无固定档位时保留公共尺寸解析规则,帧率和显式编码配置不变。
4456
+ */
4457
+ resolveVideoFormat(format, mediaService) {
4458
+ format.validate();
4459
+ const buckets = resolutionBuckets(mediaService.model);
4460
+ let target = buckets.find((size2) => size2.width === format.width && size2.height === format.height);
4461
+ if (!target && buckets.length > 0) {
4462
+ const ratio = format.width / format.height;
4463
+ let bestDistance = Number.POSITIVE_INFINITY;
4464
+ for (const bucket of buckets) {
4465
+ const distance = Math.abs(Math.log(bucket.width / bucket.height / ratio));
4466
+ const tied = Math.abs(distance - bestDistance) < 1e-9;
4467
+ if (distance < bestDistance - 1e-9 || tied && bucket.width >= bucket.height && target && target.width < target.height) {
4468
+ target = bucket;
4469
+ bestDistance = distance;
4470
+ }
4471
+ }
4472
+ }
4473
+ const size = mediaService.resolveModelInputSize(_nullishCoalesce(target, () => ( format)));
4474
+ return format.resized(size.width, size.height);
4475
+ }
4476
+ /**
4477
+ * 解码或绘制失败时停止媒体推进,并报告一次运行期错误。
4478
+ */
4479
+ handleError(source) {
4480
+ if (this.source !== source || source.drawTimer === void 0) {
4481
+ return;
4482
+ }
4483
+ clearInterval(source.drawTimer);
4484
+ source.drawTimer = void 0;
4485
+ source.video.pause();
4486
+ XmaxLogger.realtime.warning(() => "\u672C\u5730\u89C6\u9891\u89E3\u7801\u5931\u8D25 (Local Video Decode Failed)");
4487
+ this.onError(new XmaxError("MEDIA_ERROR" /* mediaError */, "Local video decoding failed"));
4488
+ }
4489
+ /**
4490
+ * 构造统一的媒体操作取消错误。
4491
+ */
4492
+ cancelled() {
4493
+ return new XmaxError("CANCELLED" /* cancelled */, "Local video operation was cancelled");
4494
+ }
4495
+ };
4496
+
4497
+ // src/Service/Realtime/RealtimeContext.ts
4498
+ var RealtimeContext = class {
4499
+ /**
4500
+ * 创建实时生成条件并规范化文本和参考路径。
4501
+ */
4502
+ constructor(init) {
4503
+ /**
4504
+ * 服务端直接读取的网络视频。
4505
+ */
4506
+ __publicField(this, "referenceVideo");
4507
+ /**
4508
+ * 生成条件
4509
+ */
4510
+ /**
4511
+ * 实时生成使用的文本条件。
4512
+ */
4513
+ __publicField(this, "prompt");
4514
+ /**
4515
+ * 已上传参考图片的可选远端路径。
4516
+ */
4517
+ __publicField(this, "referencePath");
4518
+ this.referenceVideo = init.referenceVideo && Object.freeze({ ...init.referenceVideo });
4519
+ this.prompt = init.prompt.trim();
4520
+ const normalizedReferencePath = _optionalChain([init, 'access', _388 => _388.referencePath, 'optionalAccess', _389 => _389.trim, 'call', _390 => _390()]);
4521
+ this.referencePath = normalizedReferencePath || void 0;
4522
+ }
4523
+ };
4524
+
3718
4525
  // src/Service/Realtime/RealtimeSession.ts
3719
4526
  var RealtimeSession = class {
3720
4527
  /**
@@ -3779,7 +4586,7 @@ var RoomJoinConfiguration = class {
3779
4586
  /**
3780
4587
  * RTC 提供方。
3781
4588
  */
3782
- __publicField(this, "provider", "trtc");
4589
+ __publicField(this, "provider", "trtc" /* trtc */);
3783
4590
  /**
3784
4591
  * 进房参数
3785
4592
  */
@@ -3872,10 +4679,10 @@ var RealtimeSessionConnection = class {
3872
4679
  }
3873
4680
  };
3874
4681
  function toRoomJoinConfiguration(connection) {
3875
- return connection.provider === "trtc" ? new RoomJoinConfiguration(connection) : connection;
4682
+ return connection.provider === "trtc" /* trtc */ ? new RoomJoinConfiguration(connection) : connection;
3876
4683
  }
3877
4684
  function connectionAppID(connection) {
3878
- return connection.provider === "trtc" ? connection.sdkAppID : connection.appID;
4685
+ return connection.provider === "trtc" /* trtc */ ? connection.sdkAppID : connection.appID;
3879
4686
  }
3880
4687
 
3881
4688
  // src/Service/Realtime/RealtimeSessionService.ts
@@ -3978,7 +4785,7 @@ var RealtimeSessionService = class _RealtimeSessionService {
3978
4785
  return;
3979
4786
  }
3980
4787
  _RealtimeSessionService.ensureSessionActive(session);
3981
- await _optionalChain([handlers, 'access', _272 => _272.onRefresh, 'optionalCall', _273 => _273(session)]);
4788
+ await _optionalChain([handlers, 'access', _391 => _391.onRefresh, 'optionalCall', _392 => _392(session)]);
3982
4789
  } catch (error) {
3983
4790
  if (this.heartbeatVersion !== version) {
3984
4791
  return;
@@ -4090,7 +4897,7 @@ var RealtimeSessionService = class _RealtimeSessionService {
4090
4897
  return void 0;
4091
4898
  }
4092
4899
  return new RealtimeSessionConnection({
4093
- provider: "trtc",
4900
+ provider: "trtc" /* trtc */,
4094
4901
  roomID,
4095
4902
  sdkAppID,
4096
4903
  userID,
@@ -4106,7 +4913,7 @@ var RealtimeSessionService = class _RealtimeSessionService {
4106
4913
  if (typeof modelExtra === "string") {
4107
4914
  try {
4108
4915
  return _RealtimeSessionService.decodeConnectionPayload(JSON.parse(modelExtra));
4109
- } catch (e14) {
4916
+ } catch (e18) {
4110
4917
  return void 0;
4111
4918
  }
4112
4919
  }
@@ -4191,7 +4998,7 @@ var RealtimeState = class {
4191
4998
  * @param other 待比较的状态。
4192
4999
  */
4193
5000
  equals(other) {
4194
- return this.connectionState === other.connectionState && this.sessionID === other.sessionID && this.taskID === other.taskID && _optionalChain([this, 'access', _274 => _274.reason, 'optionalAccess', _275 => _275.kind]) === _optionalChain([other, 'access', _276 => _276.reason, 'optionalAccess', _277 => _277.kind]);
5001
+ return this.connectionState === other.connectionState && this.sessionID === other.sessionID && this.taskID === other.taskID && _optionalChain([this, 'access', _393 => _393.reason, 'optionalAccess', _394 => _394.kind]) === _optionalChain([other, 'access', _395 => _395.reason, 'optionalAccess', _396 => _396.kind]);
4195
5002
  }
4196
5003
  };
4197
5004
 
@@ -4234,9 +5041,20 @@ var RoomEvent = class _RoomEvent {
4234
5041
  static start(options) {
4235
5042
  return _RoomEvent.encode({
4236
5043
  event: "start",
4237
- params: _RoomEvent.generationParameters(options),
5044
+ params: {
5045
+ ..._RoomEvent.generationParameters(options),
5046
+ ...options.context.referenceVideo && {
5047
+ ref_video_path: options.context.referenceVideo.path,
5048
+ sample_method: options.context.referenceVideo.sampleMethod,
5049
+ target_size: [
5050
+ _nullishCoalesce(_optionalChain([options, 'access', _397 => _397.targetSize, 'optionalAccess', _398 => _398.width]), () => ( options.videoFormat.width)),
5051
+ _nullishCoalesce(_optionalChain([options, 'access', _399 => _399.targetSize, 'optionalAccess', _400 => _400.height]), () => ( options.videoFormat.height))
5052
+ ]
5053
+ }
5054
+ },
4238
5055
  user_id: options.userID,
4239
- uid: options.taskID
5056
+ uid: options.taskID,
5057
+ ...options.context.referenceVideo && { session_uid: options.taskID }
4240
5058
  });
4241
5059
  }
4242
5060
  /**
@@ -4317,7 +5135,7 @@ var RoomEvent = class _RoomEvent {
4317
5135
  static encode(payload) {
4318
5136
  try {
4319
5137
  return JSON.stringify({ ...payload, runtime: RuntimeInfo.toJSON() });
4320
- } catch (e15) {
5138
+ } catch (e19) {
4321
5139
  throw new XmaxError(
4322
5140
  "INTERNAL_ERROR" /* internalError */,
4323
5141
  "Failed to encode RTC room event"
@@ -4443,7 +5261,7 @@ var _RoomMessageCodec = class _RoomMessageCodec {
4443
5261
  let parsed;
4444
5262
  try {
4445
5263
  parsed = JSON.parse(message);
4446
- } catch (e16) {
5264
+ } catch (e20) {
4447
5265
  XmaxLogger.room.warning(() => "\u623F\u95F4\u6D88\u606F\u4E0D\u662F\u5408\u6CD5 JSON (Room Message Is Not Valid JSON)");
4448
5266
  return void 0;
4449
5267
  }
@@ -4452,20 +5270,20 @@ var _RoomMessageCodec = class _RoomMessageCodec {
4452
5270
  return parsed;
4453
5271
  }
4454
5272
  const key = `${senderUserID}:${chunk.eventId}`;
4455
- let record = this.records.get(key);
4456
- if (!record) {
4457
- record = { count: chunk.count, parts: /* @__PURE__ */ new Map() };
4458
- this.records.set(key, record);
5273
+ let record2 = this.records.get(key);
5274
+ if (!record2) {
5275
+ record2 = { count: chunk.count, parts: /* @__PURE__ */ new Map() };
5276
+ this.records.set(key, record2);
4459
5277
  this.evictOverflow();
4460
5278
  }
4461
- record.parts.set(chunk.index, chunk.data);
4462
- if (record.parts.size < record.count) {
5279
+ record2.parts.set(chunk.index, chunk.data);
5280
+ if (record2.parts.size < record2.count) {
4463
5281
  return void 0;
4464
5282
  }
4465
5283
  this.records.delete(key);
4466
5284
  const parts = [];
4467
- for (let index = 0; index < record.count; index += 1) {
4468
- const part = record.parts.get(index);
5285
+ for (let index = 0; index < record2.count; index += 1) {
5286
+ const part = record2.parts.get(index);
4469
5287
  if (part === void 0) {
4470
5288
  return void 0;
4471
5289
  }
@@ -4474,7 +5292,7 @@ var _RoomMessageCodec = class _RoomMessageCodec {
4474
5292
  const joined = parts.join("");
4475
5293
  try {
4476
5294
  return JSON.parse(joined);
4477
- } catch (e17) {
5295
+ } catch (e21) {
4478
5296
  XmaxLogger.room.warning(
4479
5297
  () => `\u623F\u95F4\u6D88\u606F\u7EC4\u5305\u540E\u4E0D\u662F\u5408\u6CD5 JSON (Reassembled Message Is Not Valid JSON)
4480
5298
  \u2514\u2500 eventId: ${chunk.eventId}`
@@ -4495,16 +5313,16 @@ var _RoomMessageCodec = class _RoomMessageCodec {
4495
5313
  if (this.chunkEvent === null || typeof parsed !== "object" || parsed === null) {
4496
5314
  return void 0;
4497
5315
  }
4498
- const record = parsed;
4499
- if (record.event !== this.chunkEvent || typeof record.eventId !== "string" || record.eventId.length === 0 || !Number.isInteger(record.index) || !Number.isInteger(record.count) || typeof record.data !== "string") {
5316
+ const record2 = parsed;
5317
+ if (record2.event !== this.chunkEvent || typeof record2.eventId !== "string" || record2.eventId.length === 0 || !Number.isInteger(record2.index) || !Number.isInteger(record2.count) || typeof record2.data !== "string") {
4500
5318
  return void 0;
4501
5319
  }
4502
- const index = record.index;
4503
- const count = record.count;
5320
+ const index = record2.index;
5321
+ const count = record2.count;
4504
5322
  if (count < 1 || index < 0 || index >= count) {
4505
5323
  return void 0;
4506
5324
  }
4507
- return { eventId: record.eventId, index, count, data: record.data };
5325
+ return { eventId: record2.eventId, index, count, data: record2.data };
4508
5326
  }
4509
5327
  /**
4510
5328
  * 淘汰最旧的未完成记录,保持缓存不超过上限。
@@ -4616,9 +5434,9 @@ var RoomController = class {
4616
5434
  *
4617
5435
  * @throws 已有房间未离开、操作失效或进房失败时抛出错误。
4618
5436
  */
4619
- async join(connection, ensureActive) {
5437
+ async join(connection, ensureActive2) {
4620
5438
  try {
4621
- ensureActive();
5439
+ ensureActive2();
4622
5440
  } catch (error) {
4623
5441
  throw XmaxError.from(error);
4624
5442
  }
@@ -4633,10 +5451,10 @@ var RoomController = class {
4633
5451
  this.heartbeat.stop();
4634
5452
  try {
4635
5453
  if (!this.customCodec) this.codec = new RoomMessageCodec(
4636
- connection.provider === "vertc" ? null : connection.provider === "agora" ? "__agora_chunk__" : "__trtc_chunk__"
5454
+ connection.provider === "vertc" /* vertc */ ? null : connection.provider === "agora" /* agora */ ? "__agora_chunk__" : "__trtc_chunk__"
4637
5455
  );
4638
5456
  await this.rtcManager.joinRoom(toRoomJoinConfiguration(connection));
4639
- ensureActive();
5457
+ ensureActive2();
4640
5458
  if (this.state.kind !== "joining" || this.state.operationID !== operationID || this.leaveOperation) {
4641
5459
  throw new XmaxError("CANCELLED" /* cancelled */, "RTC room join was cancelled");
4642
5460
  }
@@ -4774,7 +5592,7 @@ var RoomController = class {
4774
5592
  if (targetUserID && ownUserID && targetUserID !== ownUserID) {
4775
5593
  return;
4776
5594
  }
4777
- _optionalChain([this, 'access', _278 => _278.listener, 'optionalAccess', _279 => _279.onRoomMessage, 'call', _280 => _280(senderUserID, message)]);
5595
+ _optionalChain([this, 'access', _401 => _401.listener, 'optionalAccess', _402 => _402.onRoomMessage, 'call', _403 => _403(senderUserID, message)]);
4778
5596
  }
4779
5597
  /**
4780
5598
  * 发送房间信令并输出日志。
@@ -5019,6 +5837,7 @@ var StreamController = class {
5019
5837
  * 运行状态
5020
5838
  */
5021
5839
  __publicField(this, "state", makeInitialState());
5840
+ __publicField(this, "roomListener");
5022
5841
  this.rtcManager = options.rtcManager;
5023
5842
  this.roomController = _nullishCoalesce(options.roomController, () => ( new RoomController({ rtcManager: options.rtcManager })));
5024
5843
  this.encodingController = _nullishCoalesce(options.encodingController, () => ( new EncodingController({ rtcManager: options.rtcManager })));
@@ -5027,25 +5846,31 @@ var StreamController = class {
5027
5846
  this.remoteStreamListener = _nullishCoalesce(options.remoteStreamListener, () => ( (() => {
5028
5847
  })));
5029
5848
  this.generationTimeoutMs = _nullishCoalesce(options.generationTimeoutMs, () => ( 3e4));
5849
+ this.roomController.setListener({
5850
+ onRoomMessage: (sender, message) => {
5851
+ this.handleNetworkVideoCompletion(sender, message);
5852
+ _optionalChain([this, 'access', _404 => _404.roomListener, 'optionalAccess', _405 => _405.onRoomMessage, 'call', _406 => _406(sender, message)]);
5853
+ }
5854
+ });
5030
5855
  this.rtcManager.setEventListener({
5031
5856
  onTokenWillExpire: options.onCredentialsRequired,
5032
5857
  onTokenExpired: options.onCredentialsRequired,
5033
5858
  onRoomRejoining: () => this.handleRoomRejoining(),
5034
5859
  onError: (error) => this.errorListener(error),
5035
5860
  onNetworkStatistics: (statistics) => {
5036
- if (this.state.localVideoPublished) {
5037
- _optionalChain([this, 'access', _281 => _281.networkStatisticsListener, 'optionalCall', _282 => _282(statistics)]);
5861
+ if (this.state.roomID) {
5862
+ _optionalChain([this, 'access', _407 => _407.networkStatisticsListener, 'optionalCall', _408 => _408(statistics)]);
5038
5863
  }
5039
5864
  },
5040
5865
  onRemoteVideoStatistics: (statistics) => {
5041
- const userID = _optionalChain([this, 'access', _283 => _283.state, 'access', _284 => _284.activeRemoteStream, 'optionalAccess', _285 => _285.userID]);
5042
- _optionalChain([this, 'access', _286 => _286.remoteVideoStatisticsListener, 'optionalCall', _287 => _287(
5866
+ const userID = _optionalChain([this, 'access', _409 => _409.state, 'access', _410 => _410.activeRemoteStream, 'optionalAccess', _411 => _411.userID]);
5867
+ _optionalChain([this, 'access', _412 => _412.remoteVideoStatisticsListener, 'optionalCall', _413 => _413(
5043
5868
  userID ? statistics.find((item) => item.userID === userID) : void 0
5044
5869
  )]);
5045
5870
  },
5046
5871
  onLocalVideoStatistics: (statistics) => {
5047
5872
  if (this.state.localVideoPublished) {
5048
- _optionalChain([this, 'access', _288 => _288.localVideoStatisticsListener, 'optionalCall', _289 => _289(statistics)]);
5873
+ _optionalChain([this, 'access', _414 => _414.localVideoStatisticsListener, 'optionalCall', _415 => _415(statistics)]);
5049
5874
  }
5050
5875
  },
5051
5876
  onCustomMessageReceived: (senderUserID, message) => {
@@ -5109,17 +5934,19 @@ var StreamController = class {
5109
5934
  *
5110
5935
  * @throws 连接已取消,或 RTC 进房、房间配置与本地流发布失败时抛出错误。
5111
5936
  */
5112
- async connect(connection, includeLocalAudio, ensureActive, beforePublish) {
5937
+ async connect(connection, includeLocalAudio, ensureActive2, beforePublish, publishLocalMedia = true) {
5113
5938
  this.configureRoom({ roomID: connection.roomID, botID: connection.botID });
5114
- await this.roomController.join(connection, ensureActive);
5115
- ensureActive();
5939
+ await this.roomController.join(connection, ensureActive2);
5940
+ ensureActive2();
5116
5941
  XmaxLogger.stream.info(
5117
5942
  () => `RTC \u623F\u95F4\u5DF2\u914D\u7F6E (RTC Room Configured)
5118
5943
  \u2514\u2500 roomID: ${connection.roomID}, botID: ${_nullishCoalesce(connection.botID, () => ( "(\u672A\u8BBE\u7F6E)"))}`
5119
5944
  );
5120
- await _optionalChain([beforePublish, 'optionalCall', _290 => _290()]);
5121
- ensureActive();
5122
- await this.publishLocalStream(includeLocalAudio);
5945
+ if (publishLocalMedia) {
5946
+ await _optionalChain([beforePublish, 'optionalCall', _416 => _416()]);
5947
+ ensureActive2();
5948
+ await this.publishLocalStream(includeLocalAudio);
5949
+ }
5123
5950
  }
5124
5951
  /**
5125
5952
  * 清理生成状态、本地发布和远端订阅,并离开当前 RTC 房间。
@@ -5180,6 +6007,7 @@ var StreamController = class {
5180
6007
  }, this.generationTimeoutMs)
5181
6008
  };
5182
6009
  this.state.generationTaskID = taskID;
6010
+ this.state.networkVideoCompletion = options.context.referenceVideo ? { taskID, finished: false, scheduled: false } : void 0;
5183
6011
  this.state.generationWaiter = waiter;
5184
6012
  void confirmation.catch(() => {
5185
6013
  });
@@ -5191,7 +6019,9 @@ var StreamController = class {
5191
6019
  context: options.context
5192
6020
  });
5193
6021
  const state2 = this.state;
5194
- const sent = Promise.resolve(sending).then(() => {
6022
+ const sent = Promise.resolve(sending).then(async () => {
6023
+ if (this.state !== state2 || state2.generationTaskID !== taskID) return;
6024
+ await _optionalChain([options, 'access', _417 => _417.onStartSent, 'optionalCall', _418 => _418()]);
5195
6025
  if (this.state !== state2 || state2.generationTaskID !== taskID) return;
5196
6026
  for (const userID of state2.publishedRemoteUserIDs) {
5197
6027
  void this.handleRemoteVideoPublished(userID, true);
@@ -5215,7 +6045,7 @@ var StreamController = class {
5215
6045
  try {
5216
6046
  void this.roomController.stopGeneration(taskID).catch(() => {
5217
6047
  });
5218
- } catch (e18) {
6048
+ } catch (e22) {
5219
6049
  }
5220
6050
  throw mapped;
5221
6051
  }
@@ -5257,9 +6087,9 @@ var StreamController = class {
5257
6087
  *
5258
6088
  * @throws 操作已取消、房间未就绪或信令发送失败时抛出错误。
5259
6089
  */
5260
- async changeTargetSize(taskID, targetSize, ensureActive) {
6090
+ async changeTargetSize(taskID, targetSize, ensureActive2) {
5261
6091
  try {
5262
- await this.roomController.changeTargetSize({ taskID, targetSize, ensureActive });
6092
+ await this.roomController.changeTargetSize({ taskID, targetSize, ensureActive: ensureActive2 });
5263
6093
  } catch (error) {
5264
6094
  throw XmaxError.from(error);
5265
6095
  }
@@ -5296,14 +6126,57 @@ var StreamController = class {
5296
6126
  * 设置房间业务消息监听器,传入空值时清除监听器。
5297
6127
  */
5298
6128
  setRoomListener(listener) {
5299
- this.roomController.setListener(listener);
6129
+ this.roomListener = listener;
6130
+ }
6131
+ /**
6132
+ * 生成状态已提交后登记完成通知,避免快速任务在启动返回前交付完成事件。
6133
+ */
6134
+ activateNetworkVideoCompletion(onFinish) {
6135
+ const completion = this.state.networkVideoCompletion;
6136
+ if (completion) {
6137
+ completion.onFinish = onFinish;
6138
+ this.deliverNetworkVideoCompletion();
6139
+ }
6140
+ }
6141
+ /**
6142
+ * 仅接受当前任务和目标机器人的完成消息;不因完成事件立即退房。
6143
+ */
6144
+ handleNetworkVideoCompletion(sender, message) {
6145
+ const state2 = this.state;
6146
+ const completion = state2.networkVideoCompletion;
6147
+ if (!state2.roomID || !completion || completion.taskID !== state2.generationTaskID || message.event !== "video_stopped" || message.uid !== completion.taskID || !sender || state2.botID && sender !== state2.botID) {
6148
+ return;
6149
+ }
6150
+ completion.finished = true;
6151
+ this.deliverNetworkVideoCompletion();
6152
+ }
6153
+ /**
6154
+ * 异步交付一次完成通知;执行前复核任务,隔离用户回调异常。
6155
+ */
6156
+ deliverNetworkVideoCompletion() {
6157
+ const state2 = this.state;
6158
+ const completion = state2.networkVideoCompletion;
6159
+ if (!_optionalChain([completion, 'optionalAccess', _419 => _419.finished]) || !completion.onFinish || completion.scheduled) {
6160
+ return;
6161
+ }
6162
+ completion.scheduled = true;
6163
+ queueMicrotask(() => {
6164
+ if (this.state !== state2 || state2.generationTaskID !== completion.taskID) {
6165
+ return;
6166
+ }
6167
+ try {
6168
+ void Promise.resolve(_optionalChain([completion, 'access', _420 => _420.onFinish, 'optionalCall', _421 => _421()])).catch(() => {
6169
+ });
6170
+ } catch (e23) {
6171
+ }
6172
+ });
5300
6173
  }
5301
6174
  /**
5302
6175
  * 配置当前房间;已有发布、订阅或生成任务时拒绝重新配置。
5303
6176
  */
5304
6177
  configureRoom(options) {
5305
6178
  const roomID = options.roomID.trim();
5306
- const botID = _nullishCoalesce(_optionalChain([options, 'access', _291 => _291.botID, 'optionalAccess', _292 => _292.trim, 'call', _293 => _293()]), () => ( ""));
6179
+ const botID = _nullishCoalesce(_optionalChain([options, 'access', _422 => _422.botID, 'optionalAccess', _423 => _423.trim, 'call', _424 => _424()]), () => ( ""));
5307
6180
  if (!roomID) {
5308
6181
  throw new XmaxError(
5309
6182
  "INVALID_CONFIGURATION" /* invalidConfiguration */,
@@ -5386,7 +6259,7 @@ var StreamController = class {
5386
6259
  return;
5387
6260
  }
5388
6261
  this.state.subscribedRemoteUserIDs.delete(trimmedUserID);
5389
- if (_optionalChain([this, 'access', _294 => _294.state, 'access', _295 => _295.activeRemoteStream, 'optionalAccess', _296 => _296.userID]) === trimmedUserID) {
6262
+ if (_optionalChain([this, 'access', _425 => _425.state, 'access', _426 => _426.activeRemoteStream, 'optionalAccess', _427 => _427.userID]) === trimmedUserID) {
5390
6263
  await this.unsubscribeRemoteAudio(trimmedUserID);
5391
6264
  this.state.activeRemoteStream = void 0;
5392
6265
  this.clearRemoteStream();
@@ -5527,6 +6400,7 @@ var StreamController = class {
5527
6400
  const remoteAudioUserIDs = [...this.state.subscribedRemoteAudioUserIDs];
5528
6401
  this.state.generationTaskID = void 0;
5529
6402
  this.state.generationWaiter = void 0;
6403
+ this.state.networkVideoCompletion = void 0;
5530
6404
  this.state.activeRemoteStream = void 0;
5531
6405
  this.state.subscribedRemoteAudioUserIDs.clear();
5532
6406
  if (waiter) {
@@ -5575,7 +6449,7 @@ var StreamController = class {
5575
6449
  */
5576
6450
  resolveGenerationStart(taskID) {
5577
6451
  const waiter = this.state.generationWaiter;
5578
- if (this.state.generationTaskID !== taskID || _optionalChain([waiter, 'optionalAccess', _297 => _297.taskID]) !== taskID) {
6452
+ if (this.state.generationTaskID !== taskID || _optionalChain([waiter, 'optionalAccess', _428 => _428.taskID]) !== taskID) {
5579
6453
  return;
5580
6454
  }
5581
6455
  this.state.generationWaiter = void 0;
@@ -5587,7 +6461,7 @@ var StreamController = class {
5587
6461
  */
5588
6462
  rejectGenerationStart(taskID, error) {
5589
6463
  const waiter = this.state.generationWaiter;
5590
- if (this.state.generationTaskID !== taskID || _optionalChain([waiter, 'optionalAccess', _298 => _298.taskID]) !== taskID) {
6464
+ if (this.state.generationTaskID !== taskID || _optionalChain([waiter, 'optionalAccess', _429 => _429.taskID]) !== taskID) {
5591
6465
  return;
5592
6466
  }
5593
6467
  this.state.generationWaiter = void 0;
@@ -5598,7 +6472,7 @@ var StreamController = class {
5598
6472
  * 通知渲染层清理远端生成流;失败仅记录日志。
5599
6473
  */
5600
6474
  clearRemoteStream() {
5601
- _optionalChain([this, 'access', _299 => _299.remoteVideoStatisticsListener, 'optionalCall', _300 => _300(void 0)]);
6475
+ _optionalChain([this, 'access', _430 => _430.remoteVideoStatisticsListener, 'optionalCall', _431 => _431(void 0)]);
5602
6476
  try {
5603
6477
  this.remoteStreamListener(null);
5604
6478
  } catch (error) {
@@ -5869,7 +6743,7 @@ var RealtimeCoordinator = class _RealtimeCoordinator {
5869
6743
  }
5870
6744
  throw _RealtimeCoordinator.cancelledError();
5871
6745
  }
5872
- const terminationTask = await _asyncNullishCoalesce(await _asyncNullishCoalesce(operation.terminationTask, async () => ( _optionalChain([this, 'access', _301 => _301.termination, 'optionalAccess', _302 => _302.task]))), async () => ( await this.requestTermination(
6746
+ const terminationTask = await _asyncNullishCoalesce(await _asyncNullishCoalesce(operation.terminationTask, async () => ( _optionalChain([this, 'access', _432 => _432.termination, 'optionalAccess', _433 => _433.task]))), async () => ( await this.requestTermination(
5873
6747
  _nullishCoalesce(operation.lease.failureScope, () => ( 0)) /* connection */,
5874
6748
  void 0,
5875
6749
  RealtimeReason.normal,
@@ -5998,7 +6872,7 @@ var RealtimeCoordinator = class _RealtimeCoordinator {
5998
6872
  * 结束指定操作,释放操作准入场。
5999
6873
  */
6000
6874
  finish(operation) {
6001
- if (_optionalChain([this, 'access', _303 => _303.activeOperation, 'optionalAccess', _304 => _304.id]) === operation.id) {
6875
+ if (_optionalChain([this, 'access', _434 => _434.activeOperation, 'optionalAccess', _435 => _435.id]) === operation.id) {
6002
6876
  this.activeOperation = void 0;
6003
6877
  }
6004
6878
  }
@@ -6010,7 +6884,7 @@ var RealtimeCoordinator = class _RealtimeCoordinator {
6010
6884
  return;
6011
6885
  }
6012
6886
  this.state = nextState;
6013
- _optionalChain([this, 'access', _305 => _305.stateListener, 'optionalCall', _306 => _306(nextState)]);
6887
+ _optionalChain([this, 'access', _436 => _436.stateListener, 'optionalCall', _437 => _437(nextState)]);
6014
6888
  }
6015
6889
  /**
6016
6890
  * 构造统一的操作取消错误。
@@ -6078,13 +6952,18 @@ var RealtimeLaunchTimer = class {
6078
6952
  * 开始新的启动周期并清空旧快照,返回记录相机创建完成的一次性回调。
6079
6953
  */
6080
6954
  startCamera() {
6955
+ this.start();
6956
+ return this.measure("cameraMs", this.startedAt);
6957
+ }
6958
+ /**
6959
+ * 开始新的启动周期;非摄像头源不产生相机和发布耗时。
6960
+ */
6961
+ start() {
6081
6962
  this.sequence += 1;
6082
6963
  this.active = true;
6083
6964
  this.startedAt = performance.now();
6084
6965
  this.snapshot = Object.freeze({});
6085
- const complete = this.measure("cameraMs", this.startedAt);
6086
6966
  this.notify();
6087
- return complete;
6088
6967
  }
6089
6968
  /**
6090
6969
  * 开始等待用户授权系统媒体权限,返回授权决定时调用的记录函数。
@@ -6142,9 +7021,9 @@ var RealtimeLaunchTimer = class {
6142
7021
  */
6143
7022
  notify() {
6144
7023
  try {
6145
- void Promise.resolve(_optionalChain([this, 'access', _307 => _307.listener, 'optionalCall', _308 => _308(this.snapshot)])).catch(() => {
7024
+ void Promise.resolve(_optionalChain([this, 'access', _438 => _438.listener, 'optionalCall', _439 => _439(this.snapshot)])).catch(() => {
6146
7025
  });
6147
- } catch (e19) {
7026
+ } catch (e24) {
6148
7027
  }
6149
7028
  }
6150
7029
  };
@@ -6174,7 +7053,7 @@ var XmaxRealtimeConnectionManager = class {
6174
7053
  * 当前活动会话标识;尚未连接或清理完成后为空。
6175
7054
  */
6176
7055
  get currentSessionID() {
6177
- return _optionalChain([this, 'access', _309 => _309.activeSession, 'optionalAccess', _310 => _310.id]);
7056
+ return _optionalChain([this, 'access', _440 => _440.activeSession, 'optionalAccess', _441 => _441.id]);
6178
7057
  }
6179
7058
  /**
6180
7059
  * 未配置 API 服务时在连接操作开始前报错,不影响本地预览。
@@ -6215,19 +7094,23 @@ var XmaxRealtimeConnectionManager = class {
6215
7094
  "Local video stream has no video format"
6216
7095
  );
6217
7096
  }
6218
- await streamController.setVideoEncoderConfig(videoFormat);
7097
+ const publishLocalMedia = _nullishCoalesce(options.publishLocalMedia, () => ( true));
7098
+ if (publishLocalMedia) {
7099
+ await streamController.setVideoEncoderConfig(videoFormat);
7100
+ }
6219
7101
  options.ensureCurrent();
6220
7102
  let completePublish;
6221
7103
  await streamController.connect(connection, options.includeLocalAudio, () => {
6222
7104
  options.ensureCurrent();
6223
7105
  }, async () => {
6224
7106
  options.ensureCurrent();
6225
- await _optionalChain([options, 'access', _311 => _311.beforePublish, 'optionalCall', _312 => _312()]);
7107
+ await _optionalChain([options, 'access', _442 => _442.beforePublish, 'optionalCall', _443 => _443()]);
6226
7108
  completeConnection();
6227
7109
  completePublish = this.dependencies.timing.startPublish();
6228
- });
7110
+ }, publishLocalMedia);
6229
7111
  options.ensureCurrent();
6230
- _optionalChain([completePublish, 'optionalCall', _313 => _313()]);
7112
+ completeConnection();
7113
+ _optionalChain([completePublish, 'optionalCall', _444 => _444()]);
6231
7114
  options.onPublished();
6232
7115
  sessionService.startHeartbeat(session.id, {
6233
7116
  onFailure: (sessionID, error) => {
@@ -6250,24 +7133,24 @@ var XmaxRealtimeConnectionManager = class {
6250
7133
  * 更新已绑定远端视图的插帧配置;传 undefined 停用插帧。
6251
7134
  */
6252
7135
  setFrameInterpolation(options) {
6253
- _optionalChain([this, 'access', _314 => _314.remoteTarget, 'optionalAccess', _315 => _315.setFrameInterpolation, 'optionalCall', _316 => _316(options)]);
7136
+ _optionalChain([this, 'access', _445 => _445.remoteTarget, 'optionalAccess', _446 => _446.setFrameInterpolation, 'optionalCall', _447 => _447(options)]);
6254
7137
  }
6255
7138
  /**
6256
7139
  * 清空远端视图的媒体流,保留轨道与视图绑定以供统一清理。
6257
7140
  */
6258
7141
  clearRemoteMedia() {
6259
- _optionalChain([this, 'access', _317 => _317.remoteTarget, 'optionalAccess', _318 => _318.setMediaStream, 'call', _319 => _319(null)]);
7142
+ _optionalChain([this, 'access', _448 => _448.remoteTarget, 'optionalAccess', _449 => _449.setMediaStream, 'call', _450 => _450(null)]);
6260
7143
  }
6261
7144
  /**
6262
7145
  * 停止会话心跳;未配置会话服务时不执行操作。
6263
7146
  */
6264
7147
  stopHeartbeat() {
6265
7148
  this.refreshVersion++;
6266
- _optionalChain([this, 'access', _320 => _320.credentialController, 'optionalAccess', _321 => _321.abort, 'call', _322 => _322()]);
7149
+ _optionalChain([this, 'access', _451 => _451.credentialController, 'optionalAccess', _452 => _452.abort, 'call', _453 => _453()]);
6267
7150
  this.credentialController = void 0;
6268
7151
  this.credentialRefresh = void 0;
6269
7152
  this.credentialApplication = Promise.resolve();
6270
- _optionalChain([this, 'access', _323 => _323.dependencies, 'access', _324 => _324.sessionService, 'optionalAccess', _325 => _325.stopHeartbeat, 'call', _326 => _326()]);
7153
+ _optionalChain([this, 'access', _454 => _454.dependencies, 'access', _455 => _455.sessionService, 'optionalAccess', _456 => _456.stopHeartbeat, 'call', _457 => _457()]);
6271
7154
  }
6272
7155
  /**
6273
7156
  * RTC Token 过期时复用会话心跳获取新凭据,并合并同一实例的并发请求。
@@ -6275,14 +7158,14 @@ var XmaxRealtimeConnectionManager = class {
6275
7158
  async refreshCredentials() {
6276
7159
  if (this.credentialRefresh) return this.credentialRefresh;
6277
7160
  const current = this.activeSession;
6278
- if (!_optionalChain([current, 'optionalAccess', _327 => _327.connection]) || current.connection.provider === "trtc") return;
7161
+ if (!_optionalChain([current, 'optionalAccess', _458 => _458.connection]) || current.connection.provider === "trtc" /* trtc */) return;
6279
7162
  const version = this.refreshVersion;
6280
7163
  const operation = (async () => {
6281
7164
  try {
6282
7165
  const session = await this.requireSessionService().heartbeatSession(current.id);
6283
7166
  await this.handleSessionRefresh(session, version, true);
6284
7167
  } catch (error) {
6285
- if (version === this.refreshVersion && _optionalChain([this, 'access', _328 => _328.activeSession, 'optionalAccess', _329 => _329.id]) === current.id) {
7168
+ if (version === this.refreshVersion && _optionalChain([this, 'access', _459 => _459.activeSession, 'optionalAccess', _460 => _460.id]) === current.id) {
6286
7169
  this.dependencies.onHeartbeatFailure(current.id, XmaxError.from(error));
6287
7170
  }
6288
7171
  }
@@ -6299,7 +7182,7 @@ var XmaxRealtimeConnectionManager = class {
6299
7182
  */
6300
7183
  async waitUntilRemoteTrackReady(timeoutMs, signal) {
6301
7184
  if (signal.aborted) throw RealtimeCoordinator.cancelledError();
6302
- const mediaTrack = _optionalChain([this, 'access', _330 => _330.activeRemoteTrack, 'optionalAccess', _331 => _331.mediaStreamTrack]);
7185
+ const mediaTrack = _optionalChain([this, 'access', _461 => _461.activeRemoteTrack, 'optionalAccess', _462 => _462.mediaStreamTrack]);
6303
7186
  if (!mediaTrack || !mediaTrack.muted) return;
6304
7187
  await new Promise((resolve, reject) => {
6305
7188
  const cleanup = () => {
@@ -6336,7 +7219,7 @@ var XmaxRealtimeConnectionManager = class {
6336
7219
  if (track) {
6337
7220
  track.mediaStreamTrack = void 0;
6338
7221
  }
6339
- _optionalChain([this, 'access', _332 => _332.remoteTarget, 'optionalAccess', _333 => _333.setMediaStream, 'call', _334 => _334(null)]);
7222
+ _optionalChain([this, 'access', _463 => _463.remoteTarget, 'optionalAccess', _464 => _464.setMediaStream, 'call', _465 => _465(null)]);
6340
7223
  return;
6341
7224
  }
6342
7225
  if (!track) {
@@ -6346,7 +7229,7 @@ var XmaxRealtimeConnectionManager = class {
6346
7229
  );
6347
7230
  }
6348
7231
  track.mediaStreamTrack = binding.videoTrack;
6349
- _optionalChain([this, 'access', _335 => _335.remoteTarget, 'optionalAccess', _336 => _336.setMediaStream, 'call', _337 => _337(new MediaStream([binding.videoTrack]))]);
7232
+ _optionalChain([this, 'access', _466 => _466.remoteTarget, 'optionalAccess', _467 => _467.setMediaStream, 'call', _468 => _468(new MediaStream([binding.videoTrack]))]);
6350
7233
  }
6351
7234
  /**
6352
7235
  * 为远端轨道注册渲染绑定:attach 时挂流占位,媒体轨到达后送入画面。
@@ -6359,7 +7242,7 @@ var XmaxRealtimeConnectionManager = class {
6359
7242
  }
6360
7243
  },
6361
7244
  attachHandler: (view) => {
6362
- _optionalChain([this, 'access', _338 => _338.remoteTarget, 'optionalAccess', _339 => _339.setFrameInterpolation, 'optionalCall', _340 => _340(void 0)]);
7245
+ _optionalChain([this, 'access', _469 => _469.remoteTarget, 'optionalAccess', _470 => _470.setFrameInterpolation, 'optionalCall', _471 => _471(void 0)]);
6363
7246
  view.isMirrored = this.dependencies.isMirrored();
6364
7247
  this.remoteTarget = view;
6365
7248
  this.dependencies.onRenderAttached();
@@ -6371,7 +7254,7 @@ var XmaxRealtimeConnectionManager = class {
6371
7254
  this.dependencies.onRenderDetached();
6372
7255
  this.remoteTarget = void 0;
6373
7256
  }
6374
- _optionalChain([view, 'access', _341 => _341.setFrameInterpolation, 'optionalCall', _342 => _342(void 0)]);
7257
+ _optionalChain([view, 'access', _472 => _472.setFrameInterpolation, 'optionalCall', _473 => _473(void 0)]);
6375
7258
  view.setMediaStream(null);
6376
7259
  }
6377
7260
  });
@@ -6428,8 +7311,8 @@ var XmaxRealtimeConnectionManager = class {
6428
7311
  );
6429
7312
  return;
6430
7313
  }
6431
- if (next.provider !== "trtc" && (force || !previous || previous.provider === "trtc" || next.roomToken !== previous.roomToken)) {
6432
- await this.dependencies.streamController.updateCredentials(next, _optionalChain([this, 'access', _343 => _343.credentialController, 'optionalAccess', _344 => _344.signal]));
7314
+ if (next.provider !== "trtc" /* trtc */ && (force || !previous || previous.provider === "trtc" /* trtc */ || next.roomToken !== previous.roomToken)) {
7315
+ await this.dependencies.streamController.updateCredentials(next, _optionalChain([this, 'access', _474 => _474.credentialController, 'optionalAccess', _475 => _475.signal]));
6433
7316
  if (version !== this.refreshVersion || this.activeSession !== current) return;
6434
7317
  }
6435
7318
  this.activeSession = new RealtimeSession({
@@ -6444,7 +7327,7 @@ var XmaxRealtimeConnectionManager = class {
6444
7327
  * 尽力清理全部连接资源,即使退房失败也关闭服务端会话。
6445
7328
  */
6446
7329
  async disconnect() {
6447
- const sessionID = _optionalChain([this, 'access', _345 => _345.activeSession, 'optionalAccess', _346 => _346.id]);
7330
+ const sessionID = _optionalChain([this, 'access', _476 => _476.activeSession, 'optionalAccess', _477 => _477.id]);
6448
7331
  this.stopHeartbeat();
6449
7332
  this.clearRemoteMedia();
6450
7333
  try {
@@ -6526,7 +7409,8 @@ var XmaxRealtimeGenerationManager = class _XmaxRealtimeGenerationManager {
6526
7409
  const confirmation = this.streamController.beginGeneration({
6527
7410
  taskID,
6528
7411
  videoFormat: options.videoFormat,
6529
- context
7412
+ context,
7413
+ ...options.onStartSent && { onStartSent: options.onStartSent }
6530
7414
  });
6531
7415
  await this.awaitConfirmation(confirmation, options.signal);
6532
7416
  options.ensureCurrent();
@@ -6578,7 +7462,7 @@ var XmaxRealtimeGenerationManager = class _XmaxRealtimeGenerationManager {
6578
7462
  try {
6579
7463
  await completion;
6580
7464
  } finally {
6581
- if (_optionalChain([this, 'access', _347 => _347.pendingStop, 'optionalAccess', _348 => _348.completion]) === completion) this.pendingStop = void 0;
7465
+ if (_optionalChain([this, 'access', _478 => _478.pendingStop, 'optionalAccess', _479 => _479.completion]) === completion) this.pendingStop = void 0;
6582
7466
  }
6583
7467
  }
6584
7468
  /**
@@ -6659,6 +7543,11 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
6659
7543
  __publicField(this, "coordinator");
6660
7544
  __publicField(this, "errorHandler");
6661
7545
  __publicField(this, "cameraController");
7546
+ __publicField(this, "networkVideoController", new NetworkVideoController());
7547
+ __publicField(this, "localVideoController", new LocalVideoController((error) => {
7548
+ void this.coordinator.terminateWithError(error, 0 /* connection */);
7549
+ }));
7550
+ __publicField(this, "rtcManager");
6662
7551
  /**
6663
7552
  * 启动计时与首帧通知
6664
7553
  */
@@ -6692,18 +7581,19 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
6692
7581
  this.options = options;
6693
7582
  this.mediaService = new MediaService(options.model);
6694
7583
  this.interpolationRequested = options.isFrameInterpolationEnabled;
6695
- this.supportsInterpolation = _nullishCoalesce(_optionalChain([dependencies, 'optionalAccess', _349 => _349.supportsFrameInterpolation]), () => ( (async (size) => await frameInterpolationAdapter(size) !== null)));
7584
+ this.supportsInterpolation = _nullishCoalesce(_optionalChain([dependencies, 'optionalAccess', _480 => _480.supportsFrameInterpolation]), () => ( (async (size) => await frameInterpolationAdapter(size) !== null)));
6696
7585
  this.errorHandler = new RealtimeErrorHandler();
6697
- const rtcManager = _nullishCoalesce(_optionalChain([dependencies, 'optionalAccess', _350 => _350.rtcManager]), () => ( createRtcManager(options.provider, _nullishCoalesce(_optionalChain([dependencies, 'optionalAccess', _351 => _351.environment]), () => ( "china")) /* china */)));
6698
- this.cameraController = _nullishCoalesce(_optionalChain([dependencies, 'optionalAccess', _352 => _352.cameraController]), () => ( new CameraController({
7586
+ const rtcManager = _nullishCoalesce(_optionalChain([dependencies, 'optionalAccess', _481 => _481.rtcManager]), () => ( createRtcManager(options.provider, _nullishCoalesce(_optionalChain([dependencies, 'optionalAccess', _482 => _482.environment]), () => ( "china")) /* china */)));
7587
+ this.rtcManager = rtcManager;
7588
+ this.cameraController = _nullishCoalesce(_optionalChain([dependencies, 'optionalAccess', _483 => _483.cameraController]), () => ( new CameraController({
6699
7589
  rtcManager,
6700
7590
  mediaService: this.mediaService,
6701
7591
  errorListener: (error) => {
6702
7592
  void this.errorHandler.report(error);
6703
7593
  }
6704
7594
  })));
6705
- const sessionService = _nullishCoalesce(_optionalChain([dependencies, 'optionalAccess', _353 => _353.sessionService]), () => ( (_optionalChain([dependencies, 'optionalAccess', _354 => _354.apiService]) ? new RealtimeSessionService({ apiService: dependencies.apiService, provider: options.provider }) : void 0)));
6706
- this.streamController = _nullishCoalesce(_optionalChain([dependencies, 'optionalAccess', _355 => _355.streamController]), () => ( new StreamController({
7595
+ const sessionService = _nullishCoalesce(_optionalChain([dependencies, 'optionalAccess', _484 => _484.sessionService]), () => ( (_optionalChain([dependencies, 'optionalAccess', _485 => _485.apiService]) ? new RealtimeSessionService({ apiService: dependencies.apiService, provider: options.provider }) : void 0)));
7596
+ this.streamController = _nullishCoalesce(_optionalChain([dependencies, 'optionalAccess', _486 => _486.streamController]), () => ( new StreamController({
6707
7597
  rtcManager,
6708
7598
  onCredentialsRequired: () => {
6709
7599
  void this.connectionManager.refreshCredentials();
@@ -6721,12 +7611,12 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
6721
7611
  sessionService,
6722
7612
  streamController: this.streamController,
6723
7613
  timing: this.launchTimer,
6724
- isMirrored: () => _optionalChain([this, 'access', _356 => _356.cameraController, 'access', _357 => _357.currentTrack, 'optionalAccess', _358 => _358.position]) === "front" /* front */,
7614
+ isMirrored: () => _optionalChain([this, 'access', _487 => _487.cameraController, 'access', _488 => _488.currentTrack, 'optionalAccess', _489 => _489.position]) === "front" /* front */,
6725
7615
  remoteAudioVolume: () => this.storedRemoteAudioVolume,
6726
7616
  onHeartbeatFailure: (sessionID, error) => {
6727
7617
  void this.handleHeartbeatFailure(sessionID, error);
6728
7618
  },
6729
- onFrameDisplayed: () => _optionalChain([this, 'access', _359 => _359.remoteFrameDisplayHandler, 'optionalCall', _360 => _360()]),
7619
+ onFrameDisplayed: () => _optionalChain([this, 'access', _490 => _490.remoteFrameDisplayHandler, 'optionalCall', _491 => _491()]),
6730
7620
  onRenderAttached: () => this.applyRemoteInterpolation(),
6731
7621
  onRenderDetached: () => {
6732
7622
  this.interpolationRevision++;
@@ -6773,7 +7663,7 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
6773
7663
  */
6774
7664
  async setFrameInterpolationEnabled(enabled) {
6775
7665
  await this.coordinator.run("configuration" /* configuration */, void 0, async (token) => {
6776
- const format = _nullishCoalesce(this.generationVideoFormat, () => ( _optionalChain([this, 'access', _361 => _361.cameraController, 'access', _362 => _362.currentTrack, 'optionalAccess', _363 => _363.videoFormat])));
7666
+ const format = _nullishCoalesce(this.generationVideoFormat, () => ( _optionalChain([this, 'access', _492 => _492.currentLocalTrack, 'optionalAccess', _493 => _493.videoFormat])));
6777
7667
  const size = format && enabled ? this.mediaService.resolveFrameInterpolationSize(format) : void 0;
6778
7668
  if (enabled && !await this.checkInterpolationSupport(size, token)) {
6779
7669
  token.ensureCurrent();
@@ -6838,7 +7728,6 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
6838
7728
  const size = this.interpolationSize;
6839
7729
  this.connectionManager.setFrameInterpolation(size ? {
6840
7730
  size,
6841
- targetFrameRate: this.options.frameInterpolation.targetFrameRate,
6842
7731
  onFailure: (error) => {
6843
7732
  if (revision === this.interpolationRevision) this.handleInterpolationFailure(error);
6844
7733
  }
@@ -6899,9 +7788,9 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
6899
7788
  */
6900
7789
  notifyRemoteVideoStatistics() {
6901
7790
  try {
6902
- void Promise.resolve(_optionalChain([this, 'access', _364 => _364.remoteVideoStatisticsListener, 'optionalCall', _365 => _365(this.remoteVideoStatistics)])).catch(() => {
7791
+ void Promise.resolve(_optionalChain([this, 'access', _494 => _494.remoteVideoStatisticsListener, 'optionalCall', _495 => _495(this.remoteVideoStatistics)])).catch(() => {
6903
7792
  });
6904
- } catch (e20) {
7793
+ } catch (e25) {
6905
7794
  }
6906
7795
  }
6907
7796
  /**
@@ -6916,9 +7805,9 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
6916
7805
  */
6917
7806
  notifyNetworkStatistics() {
6918
7807
  try {
6919
- void Promise.resolve(_optionalChain([this, 'access', _366 => _366.networkStatisticsListener, 'optionalCall', _367 => _367(this.networkStatistics)])).catch(() => {
7808
+ void Promise.resolve(_optionalChain([this, 'access', _496 => _496.networkStatisticsListener, 'optionalCall', _497 => _497(this.networkStatistics)])).catch(() => {
6920
7809
  });
6921
- } catch (e21) {
7810
+ } catch (e26) {
6922
7811
  }
6923
7812
  }
6924
7813
  /**
@@ -6936,9 +7825,9 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
6936
7825
  */
6937
7826
  notifyLocalVideoStatistics() {
6938
7827
  try {
6939
- void Promise.resolve(_optionalChain([this, 'access', _368 => _368.localVideoStatisticsListener, 'optionalCall', _369 => _369(this.localVideoStatistics)])).catch(() => {
7828
+ void Promise.resolve(_optionalChain([this, 'access', _498 => _498.localVideoStatisticsListener, 'optionalCall', _499 => _499(this.localVideoStatistics)])).catch(() => {
6940
7829
  });
6941
- } catch (e22) {
7830
+ } catch (e27) {
6942
7831
  }
6943
7832
  }
6944
7833
  /**
@@ -6999,6 +7888,7 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
6999
7888
  "media" /* media */,
7000
7889
  void 0,
7001
7890
  async (token) => {
7891
+ this.ensureNoLocalSource();
7002
7892
  await this.coordinator.commit(
7003
7893
  new RealtimeState({
7004
7894
  connectionState: "Preparing" /* preparing */
@@ -7009,7 +7899,7 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
7009
7899
  const completeCamera = this.launchTimer.startCamera();
7010
7900
  this.clearVideoStatistics();
7011
7901
  this.disposePermissionWatch();
7012
- const permissionWatch = await watchMediaPermissionPrompt(_nullishCoalesce(_optionalChain([options, 'optionalAccess', _370 => _370.useMicrophone]), () => ( false)));
7902
+ const permissionWatch = await watchMediaPermissionPrompt(_nullishCoalesce(_optionalChain([options, 'optionalAccess', _500 => _500.useMicrophone]), () => ( false)));
7013
7903
  if (permissionWatch) {
7014
7904
  const completePermission = this.launchTimer.startPermission();
7015
7905
  void permissionWatch.decided.then(() => completePermission()).catch(() => {
@@ -7026,7 +7916,7 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
7026
7916
  throw error;
7027
7917
  }
7028
7918
  token.ensureCurrent();
7029
- this.generationVideoFormat = _optionalChain([stream, 'access', _371 => _371.videoTrack, 'optionalAccess', _372 => _372.videoFormat]);
7919
+ this.generationVideoFormat = _optionalChain([stream, 'access', _501 => _501.videoTrack, 'optionalAccess', _502 => _502.videoFormat]);
7030
7920
  completeCamera();
7031
7921
  this.cameraController.setPreviewReadyHandler((isCurrent) => {
7032
7922
  void this.coordinator.localPreviewDidBecomeReady(isCurrent);
@@ -7035,25 +7925,82 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
7035
7925
  }
7036
7926
  );
7037
7927
  }
7928
+ /**
7929
+ * 创建网络视频源并初始化接收端 RTC;预览是否加载成功不影响服务端读取视频。
7930
+ */
7931
+ async createNetworkVideoStream(options) {
7932
+ return this.coordinator.run("media" /* media */, void 0, async (token) => {
7933
+ this.ensureNoLocalSource();
7934
+ const stream = this.networkVideoController.create(options, this.mediaService);
7935
+ token.setFailureScope(1 /* all */);
7936
+ this.launchTimer.start();
7937
+ this.clearVideoStatistics();
7938
+ await this.rtcManager.initialize();
7939
+ token.ensureCurrent();
7940
+ this.generationVideoFormat = _optionalChain([stream, 'access', _503 => _503.videoTrack, 'optionalAccess', _504 => _504.videoFormat]);
7941
+ await this.coordinator.commit(new RealtimeState({ connectionState: "Ready" /* ready */ }), token);
7942
+ return stream;
7943
+ });
7944
+ }
7945
+ /**
7946
+ * 创建文件音视频源并准备外部轨道;准备失败时统一释放本次媒体和 RTC 资源。
7947
+ */
7948
+ async createLocalVideoStream(options) {
7949
+ return this.coordinator.run("media" /* media */, void 0, async (token) => {
7950
+ this.ensureNoLocalSource();
7951
+ if (!this.rtcManager.setExternalMediaTracks) {
7952
+ throw new XmaxError("INVALID_CONFIGURATION" /* invalidConfiguration */, "The RTC provider does not support local video streams");
7953
+ }
7954
+ token.setFailureScope(1 /* all */);
7955
+ this.launchTimer.start();
7956
+ this.clearVideoStatistics();
7957
+ const stream = await this.localVideoController.create(options, this.mediaService, token.signal);
7958
+ token.ensureCurrent();
7959
+ await this.rtcManager.initialize();
7960
+ token.ensureCurrent();
7961
+ await this.rtcManager.setExternalMediaTracks({
7962
+ videoTrack: stream.videoTrack.mediaStreamTrack,
7963
+ audioTrack: this.localVideoController.audioTrack
7964
+ });
7965
+ token.ensureCurrent();
7966
+ this.generationVideoFormat = _optionalChain([stream, 'access', _505 => _505.videoTrack, 'optionalAccess', _506 => _506.videoFormat]);
7967
+ await this.coordinator.commit(new RealtimeState({ connectionState: "Ready" /* ready */ }), token);
7968
+ return stream;
7969
+ });
7970
+ }
7971
+ /**
7972
+ * 当前 Manager 拥有的媒体源轨道。
7973
+ */
7974
+ get currentLocalTrack() {
7975
+ return _nullishCoalesce(_nullishCoalesce(this.localVideoController.currentTrack, () => ( this.networkVideoController.currentTrack)), () => ( this.cameraController.currentTrack));
7976
+ }
7977
+ /**
7978
+ * 创建前拒绝覆盖活动源,避免旧预览或连接资源被静默遗留。
7979
+ */
7980
+ ensureNoLocalSource() {
7981
+ if (this.currentLocalTrack || this.localVideoController.isActive) {
7982
+ throw new XmaxError("INVALID_CONFIGURATION" /* invalidConfiguration */, "Close the current local stream before creating another one");
7983
+ }
7984
+ }
7038
7985
  /**
7039
7986
  * 移除媒体权限弹窗观察;授权决定已记录或不再需要观察时调用。
7040
7987
  */
7041
7988
  disposePermissionWatch() {
7042
- _optionalChain([this, 'access', _373 => _373.permissionWatchDispose, 'optionalCall', _374 => _374()]);
7989
+ _optionalChain([this, 'access', _507 => _507.permissionWatchDispose, 'optionalCall', _508 => _508()]);
7043
7990
  this.permissionWatchDispose = void 0;
7044
7991
  }
7045
7992
  /**
7046
7993
  * 应用完整的上行视频格式,成功后更新本地轨道元数据,不改变生成格式或生命周期。
7047
7994
  * 复用配置操作准入,避免与建连、切换摄像头或其他配置更新并发。
7048
7995
  *
7049
- * @throws 无本地相机流、参数无效、操作冲突或底层编码更新失败时抛错。
7996
+ * @throws 无本地相机或文件视频流、参数无效、操作冲突或底层编码更新失败时抛错。
7050
7997
  */
7051
7998
  async updateVideoFormat(videoFormat) {
7052
7999
  await this.coordinator.run("configuration" /* configuration */, void 0, async (token) => {
7053
- const track = this.cameraController.currentTrack;
7054
- const previousFormat = _optionalChain([track, 'optionalAccess', _375 => _375.videoFormat]);
8000
+ const track = _nullishCoalesce(this.localVideoController.currentTrack, () => ( this.cameraController.currentTrack));
8001
+ const previousFormat = _optionalChain([track, 'optionalAccess', _509 => _509.videoFormat]);
7055
8002
  if (!track || !previousFormat) {
7056
- throw new XmaxError("INVALID_CONFIGURATION" /* invalidConfiguration */, "Local camera stream is not started");
8003
+ throw new XmaxError("INVALID_CONFIGURATION" /* invalidConfiguration */, "Local camera or file video stream is not started");
7057
8004
  }
7058
8005
  videoFormat.validate();
7059
8006
  await this.streamController.setVideoEncoderConfig(videoFormat);
@@ -7066,6 +8013,9 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
7066
8013
  * 停止本地相机流并释放本地预览与 RTC 资源。
7067
8014
  */
7068
8015
  async stopLocalCameraStream() {
8016
+ if (this.networkVideoController.currentTrack || this.localVideoController.isActive) {
8017
+ return;
8018
+ }
7069
8019
  await this.coordinator.run(
7070
8020
  "media" /* media */,
7071
8021
  void 0,
@@ -7106,11 +8056,11 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
7106
8056
  /**
7107
8057
  * 使用当前 Manager 创建的本地流建立实时连接。
7108
8058
  *
7109
- * 创建实时会话、加入 RTC 房间并发布本地流,成功后启动会话心跳。
8059
+ * 创建实时会话、加入 RTC 房间并启动会话心跳;摄像头源发布本地流,网络视频源仅接收。
7110
8060
  * 启用帧检测时,发布前并行等待相机预热(固定 200ms),避免把黑帧推给 RTC。
7111
8061
  * 返回的远端媒体流在生成开始后承载远端生成画面。
7112
8062
  *
7113
- * @param localStream 由 `createLocalCameraStream` 创建的本地媒体流。
8063
+ * @param localStream 由当前 Manager 创建的摄像头、本地文件或网络视频源。
7114
8064
  * @returns 远端生成结果占位的媒体流。
7115
8065
  * @throws 本地流不属于当前 Manager、已有活动连接、会话创建或进房
7116
8066
  * 发布失败时抛出错误;失败时自动释放连接资源并恢复本地预览。
@@ -7137,7 +8087,7 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
7137
8087
  * 尚未连接时先建立实时连接;已在生成时仅更新生成条件,不重启生成。
7138
8088
  * 首次生成必须提供条件上下文,之后缺省时复用最近一次缓存的上下文。
7139
8089
  *
7140
- * @param options.localStream 由 `createLocalCameraStream` 创建的本地媒体流。
8090
+ * @param options.localStream 由当前 Manager 创建的摄像头、本地文件或网络视频源。
7141
8091
  * @param options.context 本次生成使用的条件上下文;缺省时复用缓存。
7142
8092
  * @returns 承载远端生成画面的媒体流。
7143
8093
  * @throws 本地流不属于当前 Manager、缺少可用的条件上下文、信令发送
@@ -7149,7 +8099,7 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
7149
8099
  void 0,
7150
8100
  async (token) => {
7151
8101
  const localTrack = options.localStream.videoTrack;
7152
- if (!localTrack || localTrack !== this.cameraController.currentTrack) {
8102
+ if (!localTrack || localTrack !== this.currentLocalTrack) {
7153
8103
  throw new XmaxError(
7154
8104
  "INVALID_CONFIGURATION" /* invalidConfiguration */,
7155
8105
  "The local stream must be created and started by this realtime manager"
@@ -7171,7 +8121,12 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
7171
8121
  token.ensureCurrent();
7172
8122
  return this.connectionManager.makeRemoteStream();
7173
8123
  }
7174
- const context = this.generationManager.validateContext(options.context);
8124
+ const supplied = this.generationManager.validateContext(options.context);
8125
+ const referenceVideo = this.networkVideoController.reference;
8126
+ if (!referenceVideo && supplied.referenceVideo) {
8127
+ throw new XmaxError("INVALID_CONFIGURATION" /* invalidConfiguration */, "Create a network video stream before using a reference video");
8128
+ }
8129
+ const context = referenceVideo ? new RealtimeContext({ ...supplied, referenceVideo }) : supplied;
7175
8130
  if (currentState.connectionState !== "Connected" /* connected */) {
7176
8131
  await this.performConnect(options.localStream, token);
7177
8132
  }
@@ -7183,6 +8138,7 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
7183
8138
  * 断开实时连接并保留当前本地媒体预览。
7184
8139
  */
7185
8140
  async disconnect() {
8141
+ this.localVideoController.pause();
7186
8142
  this.launchTimer.cancel();
7187
8143
  this.clearVideoStatistics();
7188
8144
  await this.coordinator.disconnect();
@@ -7192,6 +8148,7 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
7192
8148
  * 关闭期间重复调用会等待同一个释放任务;关闭完成后仍可重新创建本地流。
7193
8149
  */
7194
8150
  async close() {
8151
+ this.localVideoController.pause();
7195
8152
  this.launchTimer.cancel();
7196
8153
  this.clearVideoStatistics();
7197
8154
  await this.coordinator.terminate(1 /* all */);
@@ -7205,7 +8162,7 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
7205
8162
  */
7206
8163
  async performConnect(localStream, token) {
7207
8164
  const localTrack = localStream.videoTrack;
7208
- if (!localTrack || localTrack !== this.cameraController.currentTrack) {
8165
+ if (!localTrack || localTrack !== this.currentLocalTrack) {
7209
8166
  throw new XmaxError(
7210
8167
  "INVALID_CONFIGURATION" /* invalidConfiguration */,
7211
8168
  "The local stream must be created and started by this realtime manager"
@@ -7223,7 +8180,7 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
7223
8180
  if (token.signal.aborted) abortWarmup();
7224
8181
  try {
7225
8182
  let beforePublish;
7226
- if (this.cameraController.isFrameValidationEnabled) {
8183
+ if (!this.localVideoController.isActive && !this.networkVideoController.currentTrack && this.cameraController.isFrameValidationEnabled) {
7227
8184
  const cameraReady = this.cameraController.waitForValidCameraFrame(warmupController.signal).then(() => {
7228
8185
  token.ensureCurrent();
7229
8186
  });
@@ -7235,7 +8192,8 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
7235
8192
  localTrack,
7236
8193
  remoteVideoFormat: this.generationVideoFormat,
7237
8194
  model: this.options.model,
7238
- includeLocalAudio: this.cameraController.useMicrophone,
8195
+ includeLocalAudio: this.localVideoController.isActive ? this.localVideoController.audioTrack !== void 0 : !this.networkVideoController.currentTrack && this.cameraController.useMicrophone,
8196
+ publishLocalMedia: !this.networkVideoController.currentTrack,
7239
8197
  ensureCurrent: () => token.ensureCurrent(),
7240
8198
  onPublished: () => {
7241
8199
  this.acceptsVideoStatistics = true;
@@ -7270,6 +8228,11 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
7270
8228
  context,
7271
8229
  signal: token.signal,
7272
8230
  ensureCurrent: () => token.ensureCurrent(),
8231
+ onStartSent: this.localVideoController.isActive ? async () => {
8232
+ token.ensureCurrent();
8233
+ await this.localVideoController.start(token.signal);
8234
+ token.ensureCurrent();
8235
+ } : void 0,
7273
8236
  waitUntilRemoteReady: () => this.connectionManager.waitUntilRemoteTrackReady(
7274
8237
  REMOTE_FIRST_FRAME_TIMEOUT_MS,
7275
8238
  token.signal
@@ -7283,6 +8246,14 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
7283
8246
  new RealtimeState({ connectionState: "Generating" /* generating */, sessionID, taskID }),
7284
8247
  token
7285
8248
  );
8249
+ const onFinish = this.networkVideoController.onFinish;
8250
+ if (this.networkVideoController.currentTrack) {
8251
+ this.streamController.activateNetworkVideoCompletion(onFinish && (() => {
8252
+ if (this.currentState.taskID === taskID && this.currentState.connectionState === "Generating" /* generating */) {
8253
+ return onFinish();
8254
+ }
8255
+ }));
8256
+ }
7286
8257
  return this.connectionManager.makeRemoteStream();
7287
8258
  }
7288
8259
  /**
@@ -7299,6 +8270,7 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
7299
8270
  * 统一清理顺序:停止心跳与生成,再断开连接;all 额外释放本地媒体。
7300
8271
  */
7301
8272
  async performCleanup(scope, taskID) {
8273
+ this.localVideoController.pause();
7302
8274
  this.launchTimer.cancel();
7303
8275
  this.remoteFrameDisplayHandler = void 0;
7304
8276
  this.clearVideoStatistics();
@@ -7309,20 +8281,30 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
7309
8281
  await this.generationManager.reset(taskID);
7310
8282
  const sessionID = await this.connectionManager.disconnect();
7311
8283
  if (scope === 1 /* all */) {
8284
+ const hadFileSource = this.networkVideoController.currentTrack !== void 0 || this.localVideoController.isActive;
8285
+ this.networkVideoController.stop();
7312
8286
  this.generationVideoFormat = void 0;
7313
8287
  this.disposePermissionWatch();
7314
8288
  try {
7315
- await this.cameraController.stopLocalCameraStream();
8289
+ if (hadFileSource) {
8290
+ await this.rtcManager.destroy();
8291
+ } else {
8292
+ await this.cameraController.stopLocalCameraStream();
8293
+ }
7316
8294
  } catch (error) {
7317
8295
  XmaxLogger.realtime.error(
7318
- () => `\u505C\u6B62\u672C\u5730\u76F8\u673A\u6D41\u5931\u8D25 (Failed to Stop Local Camera Stream)
8296
+ () => `\u91CA\u653E\u672C\u5730\u5A92\u4F53\u5931\u8D25 (Failed to Release Local Media)
7319
8297
  \u2514\u2500 ${XmaxLogger.localized("\u539F\u56E0\uFF1A", "Reason: ")}${XmaxError.from(error).message}`
7320
8298
  );
8299
+ } finally {
8300
+ if (this.localVideoController.isActive) {
8301
+ await this.localVideoController.stop();
8302
+ }
7321
8303
  }
7322
8304
  }
7323
8305
  return {
7324
8306
  sessionID,
7325
- hasLocalMedia: this.cameraController.currentTrack !== void 0
8307
+ hasLocalMedia: this.currentLocalTrack !== void 0
7326
8308
  };
7327
8309
  }
7328
8310
  /**
@@ -7384,6 +8366,405 @@ var XmaxConfiguration = class {
7384
8366
  }
7385
8367
  };
7386
8368
 
8369
+ // src/Service/NonRealtime/NonRealtimeTask.ts
8370
+ var NonRealtimeQuality = /* @__PURE__ */ ((NonRealtimeQuality2) => {
8371
+ NonRealtimeQuality2["sd"] = "sd";
8372
+ NonRealtimeQuality2["hd"] = "hd";
8373
+ return NonRealtimeQuality2;
8374
+ })(NonRealtimeQuality || {});
8375
+ var NonRealtimeTaskStatus = /* @__PURE__ */ ((NonRealtimeTaskStatus2) => {
8376
+ NonRealtimeTaskStatus2["submitted"] = "submitted";
8377
+ NonRealtimeTaskStatus2["processing"] = "processing";
8378
+ NonRealtimeTaskStatus2["completed"] = "completed";
8379
+ NonRealtimeTaskStatus2["error"] = "error";
8380
+ return NonRealtimeTaskStatus2;
8381
+ })(NonRealtimeTaskStatus || {});
8382
+
8383
+ // src/Service/NonRealtime/NonRealtimeTaskService.ts
8384
+ var SUPPORTED_FPS = /* @__PURE__ */ new Set([8, 10, 12, 15, 16, 18, 20, 22, 24, 25, 30, 45, 48, 50, 60, 72, 90, 100, 120]);
8385
+ var NonRealtimeTaskService = class {
8386
+ /**
8387
+ * 注入非实时任务使用的 API 服务。
8388
+ */
8389
+ constructor(api) {
8390
+ /**
8391
+ * 网络依赖
8392
+ */
8393
+ __publicField(this, "api");
8394
+ this.api = api;
8395
+ }
8396
+ /**
8397
+ * 提交一次任务;请求结果不确定时也不自动重试,以免重复扣费。
8398
+ */
8399
+ async submitTask(options) {
8400
+ const model = _optionalChain([options, 'access', _510 => _510.model, 'optionalAccess', _511 => _511.trim, 'call', _512 => _512()]);
8401
+ if (!model) {
8402
+ invalid("Model must not be empty");
8403
+ }
8404
+ const prompt = options.prompt.trim();
8405
+ if (!prompt || [...prompt].length > 1024) {
8406
+ invalid("Prompt must contain 1\u20131024 characters");
8407
+ }
8408
+ validateURL(options.videoPath);
8409
+ if (options.referencePath !== void 0) {
8410
+ validateURL(options.referencePath);
8411
+ }
8412
+ if (!Object.values(NonRealtimeQuality).includes(options.quality)) {
8413
+ invalid("Quality must be sd or hd");
8414
+ }
8415
+ if (options.fps !== void 0 && !SUPPORTED_FPS.has(options.fps)) {
8416
+ invalid("Unsupported processing frame rate");
8417
+ }
8418
+ const payload = await this.api.post("/offline-task", {
8419
+ model,
8420
+ prompt,
8421
+ refVideoPath: options.videoPath.trim(),
8422
+ ...options.referencePath === void 0 ? {} : { refImagePath: options.referencePath.trim() },
8423
+ quality: options.quality,
8424
+ ...options.fps === void 0 ? {} : { fps: options.fps }
8425
+ });
8426
+ return parseTask(payload);
8427
+ }
8428
+ /**
8429
+ * 查询任务,校验返回 ID;signal 只取消本次 HTTP 查询,不取消服务端任务。
8430
+ */
8431
+ async getTask(taskUid, signal) {
8432
+ const uid = validateTaskUid(taskUid);
8433
+ const task = parseTask(await this.api.get(`/offline-task/${encodeURIComponent(uid)}`, { signal }));
8434
+ if (task.uid !== uid) {
8435
+ malformed("Task UID does not match the request");
8436
+ }
8437
+ return task;
8438
+ }
8439
+ /**
8440
+ * 查询最多 100 个 ID,按首次出现顺序去重;任一任务不可用时整体失败。
8441
+ */
8442
+ async getTasks(taskUids) {
8443
+ if (taskUids.length < 1 || taskUids.length > 100) {
8444
+ invalid("Provide 1\u2013100 task UIDs");
8445
+ }
8446
+ const uids = [...new Set(taskUids.map(validateTaskUid))];
8447
+ const payload = record(await this.api.post("/offline-task/batch-query", { taskUids: uids }));
8448
+ const tasks = parseList(payload.list);
8449
+ if (tasks.length !== uids.length || tasks.some((task, index) => task.uid !== uids[index])) {
8450
+ malformed("Batch response does not match the requested task UIDs");
8451
+ }
8452
+ return tasks;
8453
+ }
8454
+ /**
8455
+ * 分页查询任务,不隐式加载其他页。
8456
+ */
8457
+ async listTasks(options = {}) {
8458
+ const pageNumber = _nullishCoalesce(options.pageNumber, () => ( 1));
8459
+ const pageSize = _nullishCoalesce(options.pageSize, () => ( 10));
8460
+ if (!Number.isSafeInteger(pageNumber) || pageNumber < 1) {
8461
+ invalid("Page number must be a positive integer");
8462
+ }
8463
+ if (!Number.isInteger(pageSize) || pageSize < 1 || pageSize > 100) {
8464
+ invalid("Page size must be between 1 and 100");
8465
+ }
8466
+ if (options.status !== void 0 && !Object.values(NonRealtimeTaskStatus).includes(options.status)) {
8467
+ invalid("Invalid task status");
8468
+ }
8469
+ const query = new URLSearchParams({ pageNumber: String(pageNumber), pageSize: String(pageSize) });
8470
+ if (options.status !== void 0) {
8471
+ query.set("status", options.status);
8472
+ }
8473
+ const payload = record(await this.api.get(`/offline-task/page?${query}`));
8474
+ return Object.freeze({
8475
+ pageNumber: pageInteger(payload, "pageNumber", 1),
8476
+ pageSize: pageInteger(payload, "pageSize", 1),
8477
+ total: pageInteger(payload, "total", 0),
8478
+ list: parseList(payload.list)
8479
+ });
8480
+ }
8481
+ };
8482
+ function validateTaskUid(value) {
8483
+ const uid = value.trim();
8484
+ if (!uid || uid === "." || uid === "..") {
8485
+ invalid("Task UID must not be empty or a relative path segment");
8486
+ }
8487
+ return uid;
8488
+ }
8489
+ function validateURL(value) {
8490
+ let url;
8491
+ try {
8492
+ url = new URL(value.trim());
8493
+ } catch (e28) {
8494
+ return invalid("Resource must be an uploaded HTTP(S) URL");
8495
+ }
8496
+ if (!["https:", "http:"].includes(url.protocol) || url.username || url.password) {
8497
+ invalid("Resource must be an uploaded HTTP(S) URL");
8498
+ }
8499
+ }
8500
+ function invalid(message) {
8501
+ throw new XmaxError("INVALID_CONFIGURATION" /* invalidConfiguration */, message);
8502
+ }
8503
+ function malformed(message = "Invalid non-realtime response data") {
8504
+ throw new XmaxError("API_ERROR" /* apiError */, message);
8505
+ }
8506
+ function record(value) {
8507
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
8508
+ malformed();
8509
+ }
8510
+ return value;
8511
+ }
8512
+ function field(data, key, type) {
8513
+ const value = data[key];
8514
+ if (value === void 0 || value === null) {
8515
+ return void 0;
8516
+ }
8517
+ if (typeof value !== type || typeof value === "number" && (!Number.isFinite(value) || value < 0)) {
8518
+ malformed(`Invalid field: ${key}`);
8519
+ }
8520
+ return value;
8521
+ }
8522
+ function pageInteger(data, key, minimum) {
8523
+ const value = field(data, key, "number");
8524
+ if (value === void 0 || !Number.isSafeInteger(value) || value < minimum) {
8525
+ malformed(`Invalid field: ${key}`);
8526
+ }
8527
+ return value;
8528
+ }
8529
+ function parseList(value) {
8530
+ if (!Array.isArray(value)) {
8531
+ malformed("Missing task list");
8532
+ }
8533
+ return Object.freeze(value.map(parseTask));
8534
+ }
8535
+ function parseTask(value) {
8536
+ const data = record(value);
8537
+ const uid = field(data, "uid", "string");
8538
+ const status = field(data, "status", "string");
8539
+ const quality = field(data, "quality", "string");
8540
+ if (!_optionalChain([uid, 'optionalAccess', _513 => _513.trim, 'call', _514 => _514()]) || !Object.values(NonRealtimeTaskStatus).includes(status)) {
8541
+ malformed("Missing task UID or invalid status");
8542
+ }
8543
+ if (quality !== void 0 && !Object.values(NonRealtimeQuality).includes(quality)) {
8544
+ malformed("Invalid task quality");
8545
+ }
8546
+ return Object.freeze({
8547
+ uid,
8548
+ status,
8549
+ quality,
8550
+ userUid: field(data, "userUid", "string"),
8551
+ prompt: field(data, "prompt", "string"),
8552
+ videoPath: field(data, "refVideoPath", "string"),
8553
+ referencePath: field(data, "refImagePath", "string"),
8554
+ processorId: field(data, "processorId", "string"),
8555
+ requestedFps: field(data, "requestedFps", "number"),
8556
+ resolvedFps: field(data, "resolvedFps", "number"),
8557
+ retryCount: field(data, "retryCount", "number"),
8558
+ videoDurationSeconds: field(data, "videoDurationSeconds", "number"),
8559
+ billableDurationSeconds: field(data, "billableDurationSeconds", "number"),
8560
+ chargePoints: field(data, "chargePoints", "number"),
8561
+ result: data.result == null ? void 0 : parseResult(data.result),
8562
+ processStartTime: field(data, "processStartTime", "string"),
8563
+ createTimestamp: field(data, "createTimestamp", "string"),
8564
+ updateTimestamp: field(data, "updateTimestamp", "string")
8565
+ });
8566
+ }
8567
+ function parseResult(value) {
8568
+ const data = record(value);
8569
+ return Object.freeze({
8570
+ url: field(data, "result_url", "string"),
8571
+ uploadError: field(data, "upload_error", "string"),
8572
+ uploadStatus: field(data, "upload_status", "string"),
8573
+ finalizeDurationMs: field(data, "finalize_duration_ms", "number"),
8574
+ compressionLevel: field(data, "compression_level", "string"),
8575
+ frameCount: field(data, "frame_count", "number"),
8576
+ fps: field(data, "fps", "number"),
8577
+ durationMs: field(data, "result_duration_ms", "number"),
8578
+ fileSizeBytes: field(data, "file_size_bytes", "number"),
8579
+ droppedFrameCount: field(data, "dropped_frame_count", "number"),
8580
+ originalFileSizeBytes: field(data, "original_file_size_bytes", "number")
8581
+ });
8582
+ }
8583
+
8584
+ // src/Service/NonRealtime/NonRealtimeTaskError.ts
8585
+ var NonRealtimeTaskError = class extends XmaxError {
8586
+ /**
8587
+ * 使用 API 错误分类,不虚构服务端未返回的业务错误码。
8588
+ */
8589
+ constructor(task, message) {
8590
+ super("API_ERROR" /* apiError */, message);
8591
+ /**
8592
+ * 失败上下文
8593
+ */
8594
+ __publicField(this, "task");
8595
+ this.name = "NonRealtimeTaskError";
8596
+ this.task = task;
8597
+ }
8598
+ };
8599
+
8600
+ // src/Core/NonRealtime/XmaxNonRealtimeManager.ts
8601
+ var XmaxNonRealtimeManager = class {
8602
+ /**
8603
+ * 注入非实时任务服务。
8604
+ */
8605
+ constructor(tasks) {
8606
+ /**
8607
+ * 服务层组件
8608
+ */
8609
+ __publicField(this, "tasks");
8610
+ this.tasks = tasks;
8611
+ }
8612
+ /**
8613
+ * 单次提交任务,不自动重试可能已扣费的请求。
8614
+ */
8615
+ submitTask(options) {
8616
+ return this.tasks.submitTask(options);
8617
+ }
8618
+ /**
8619
+ * 取得任务快照,不把服务端失败终态当成查询失败。
8620
+ */
8621
+ getTask(taskUid, options) {
8622
+ return this.tasks.getTask(taskUid, _optionalChain([options, 'optionalAccess', _515 => _515.signal]));
8623
+ }
8624
+ /**
8625
+ * 批量取得任务快照。
8626
+ */
8627
+ getTasks(taskUids) {
8628
+ return this.tasks.getTasks(taskUids);
8629
+ }
8630
+ /**
8631
+ * 分页取得任务列表。
8632
+ */
8633
+ listTasks(options) {
8634
+ return this.tasks.listTasks(options);
8635
+ }
8636
+ /**
8637
+ * 等待任务完成;查询串行执行,瞬时错误有限退避,取消与总超时覆盖请求及等待阶段。
8638
+ */
8639
+ async waitForCompletion(taskUid, options = {}) {
8640
+ const uid = validateTaskUid(taskUid);
8641
+ const interval = _nullishCoalesce(options.intervalMs, () => ( 2e3));
8642
+ const retries = _nullishCoalesce(options.maxConsecutiveRetries, () => ( 3));
8643
+ validateDelay(interval);
8644
+ if (options.timeoutMs !== void 0) {
8645
+ validateDelay(options.timeoutMs);
8646
+ }
8647
+ if (!Number.isSafeInteger(retries) || retries < 0) {
8648
+ throw new XmaxError(
8649
+ "INVALID_CONFIGURATION" /* invalidConfiguration */,
8650
+ "Retry count must be a non-negative integer"
8651
+ );
8652
+ }
8653
+ const controller = new AbortController();
8654
+ let timedOut = false;
8655
+ const abort = () => controller.abort();
8656
+ _optionalChain([options, 'access', _516 => _516.signal, 'optionalAccess', _517 => _517.addEventListener, 'call', _518 => _518("abort", abort, { once: true })]);
8657
+ if (_optionalChain([options, 'access', _519 => _519.signal, 'optionalAccess', _520 => _520.aborted])) {
8658
+ abort();
8659
+ }
8660
+ const timer = options.timeoutMs === void 0 ? void 0 : setTimeout(() => {
8661
+ timedOut = true;
8662
+ abort();
8663
+ }, options.timeoutMs);
8664
+ let failures = 0;
8665
+ try {
8666
+ for (; ; ) {
8667
+ ensureActive(controller.signal);
8668
+ let task;
8669
+ try {
8670
+ task = await this.tasks.getTask(uid, controller.signal);
8671
+ } catch (error) {
8672
+ ensureActive(controller.signal);
8673
+ if (!isTransient(error) || failures >= retries) {
8674
+ throw error;
8675
+ }
8676
+ failures++;
8677
+ await delay(Math.min(interval * 2 ** (failures - 1), 3e4), controller.signal);
8678
+ continue;
8679
+ }
8680
+ ensureActive(controller.signal);
8681
+ failures = 0;
8682
+ try {
8683
+ void Promise.resolve(_optionalChain([options, 'access', _521 => _521.onTaskUpdated, 'optionalCall', _522 => _522(task)])).catch(() => {
8684
+ });
8685
+ } catch (e29) {
8686
+ }
8687
+ ensureActive(controller.signal);
8688
+ if (task.status === "error" /* error */) {
8689
+ throw new NonRealtimeTaskError(task, `Non-realtime task ${uid} failed`);
8690
+ }
8691
+ if (task.status === "completed" /* completed */) {
8692
+ if (!isResultAvailable(task)) {
8693
+ throw new NonRealtimeTaskError(
8694
+ task,
8695
+ `Non-realtime task ${uid} completed without a usable result`
8696
+ );
8697
+ }
8698
+ return task;
8699
+ }
8700
+ await delay(interval, controller.signal);
8701
+ }
8702
+ } catch (error) {
8703
+ if (timedOut) {
8704
+ throw new XmaxError(
8705
+ "TIMEOUT" /* timeout */,
8706
+ `Waiting for task ${uid} timed out; the server task was not cancelled`
8707
+ );
8708
+ }
8709
+ ensureActive(controller.signal);
8710
+ throw error;
8711
+ } finally {
8712
+ clearTimeout(timer);
8713
+ _optionalChain([options, 'access', _523 => _523.signal, 'optionalAccess', _524 => _524.removeEventListener, 'call', _525 => _525("abort", abort)]);
8714
+ }
8715
+ }
8716
+ };
8717
+ function validateDelay(value) {
8718
+ if (!Number.isFinite(value) || value <= 0 || value > 2147483647) {
8719
+ throw new XmaxError("INVALID_CONFIGURATION" /* invalidConfiguration */, "Polling durations must be positive and within the timer range");
8720
+ }
8721
+ }
8722
+ function ensureActive(signal) {
8723
+ if (signal.aborted) {
8724
+ throw new XmaxError(
8725
+ "CANCELLED" /* cancelled */,
8726
+ "Task wait was cancelled locally; the server task was not cancelled"
8727
+ );
8728
+ }
8729
+ }
8730
+ function delay(ms, signal) {
8731
+ return new Promise((resolve, reject) => {
8732
+ const finish = () => {
8733
+ clearTimeout(timer);
8734
+ signal.removeEventListener("abort", finish);
8735
+ try {
8736
+ ensureActive(signal);
8737
+ resolve();
8738
+ } catch (error) {
8739
+ reject(error);
8740
+ }
8741
+ };
8742
+ const timer = setTimeout(finish, ms);
8743
+ signal.addEventListener("abort", finish, { once: true });
8744
+ if (signal.aborted) {
8745
+ finish();
8746
+ }
8747
+ });
8748
+ }
8749
+ function isTransient(error) {
8750
+ if (!(error instanceof XmaxError)) {
8751
+ return false;
8752
+ }
8753
+ return error.code === "NETWORK_ERROR" /* networkError */ || error.code === "TIMEOUT" /* timeout */ || error.code === "API_ERROR" /* apiError */ && (_nullishCoalesce(error.httpStatus, () => ( 0))) >= 500 && (error.apiCode === void 0 || error.apiCode === error.httpStatus);
8754
+ }
8755
+ function isResultAvailable(task) {
8756
+ const result = task.result;
8757
+ if (!_optionalChain([result, 'optionalAccess', _526 => _526.url, 'optionalAccess', _527 => _527.trim, 'call', _528 => _528()]) || _optionalChain([result, 'access', _529 => _529.uploadError, 'optionalAccess', _530 => _530.trim, 'call', _531 => _531()]) || result.uploadStatus !== void 0 && result.uploadStatus !== "success") {
8758
+ return false;
8759
+ }
8760
+ try {
8761
+ const url = new URL(result.url);
8762
+ return ["http:", "https:"].includes(url.protocol) && !url.username && !url.password;
8763
+ } catch (e30) {
8764
+ return false;
8765
+ }
8766
+ }
8767
+
7387
8768
  // src/Core/XmaxClient.ts
7388
8769
  var XmaxClient = class {
7389
8770
  /**
@@ -7429,6 +8810,17 @@ var XmaxClient = class {
7429
8810
  environment: this.configuration.environment
7430
8811
  });
7431
8812
  }
8813
+ /**
8814
+ * 创建非实时视频任务 Manager,使用全局 API Key 和当前环境的非实时任务服务地址。
8815
+ * 本方法不发起网络请求或加载 RTC,任务由服务端持久化,可按 ID 恢复查询。
8816
+ */
8817
+ createNonRealtimeManager() {
8818
+ const apiService = new ApiService({
8819
+ apiKey: this.configuration.apiKey,
8820
+ baseURL: nonRealtimeApiBaseURL(this.configuration.environment)
8821
+ });
8822
+ return new XmaxNonRealtimeManager(new NonRealtimeTaskService(apiService));
8823
+ }
7432
8824
  /**
7433
8825
  * 创建媒体处理与能力查询 Service。
7434
8826
  *
@@ -7440,7 +8832,7 @@ var XmaxClient = class {
7440
8832
  /**
7441
8833
  * 创建文件存储 Service。
7442
8834
  *
7443
- * @returns 可上传图片到对象存储的存储 Service,返回地址可用作参考图路径。
8835
+ * @returns 可上传图片和视频到对象存储的存储 Service。
7444
8836
  */
7445
8837
  createStorageService() {
7446
8838
  return new StorageService({ apiService: this.apiService });
@@ -7452,45 +8844,36 @@ var RealtimeConfiguration = class {
7452
8844
  /**
7453
8845
  * 创建实时业务配置。
7454
8846
  *
7455
- * @param init.model 实时生成业务使用的模型。
7456
- * @param init.provider RTC 提供方,默认 TRTC;创建后不可切换。
7457
- * @param init.isFrameInterpolationEnabled 是否默认开启远端生成画面的插帧。
7458
- * @param init.frameInterpolation 插帧开关和显示帧率上限的详细配置。
8847
+ * @param init.model 实时生成业务使用的模型;未知名称原样透传,默认配置同 x2.1-preview。
8848
+ * @param init.provider RTC 提供方,默认按模型定义选择;必须受模型支持,创建后不可切换。
8849
+ * @param init.isFrameInterpolationEnabled 是否默认开启远端生成画面的插帧,默认 false。
7459
8850
  */
7460
8851
  constructor(init) {
7461
- /**
7462
- * RTC 配置
7463
- */
7464
- __publicField(this, "provider");
7465
- /**
7466
- * 模型配置
7467
- */
7468
8852
  /**
7469
8853
  * 实时生成业务使用的模型。
7470
8854
  */
7471
8855
  __publicField(this, "model");
7472
8856
  /**
7473
- * 远端插帧配置
8857
+ * 实时连接使用的 RTC 提供方。
7474
8858
  */
8859
+ __publicField(this, "provider");
7475
8860
  /**
7476
8861
  * 是否默认请求开启远端生成画面的插帧。
7477
8862
  */
7478
8863
  __publicField(this, "isFrameInterpolationEnabled");
7479
- /**
7480
- * 已补全默认值的只读插帧配置。
7481
- */
7482
- __publicField(this, "frameInterpolation");
7483
- this.provider = _nullishCoalesce(init.provider, () => ( "trtc")) /* trtc */;
8864
+ const providers = supportedRtcProviders(init.model);
8865
+ this.provider = _nullishCoalesce(init.provider, () => ( providers[0]));
7484
8866
  if (!Object.values(RtcProvider).includes(this.provider)) {
7485
8867
  throw new XmaxError("INVALID_CONFIGURATION" /* invalidConfiguration */, "Unsupported RTC provider");
7486
8868
  }
7487
- this.model = init.model;
7488
- const targetFrameRate = _nullishCoalesce(_optionalChain([init, 'access', _376 => _376.frameInterpolation, 'optionalAccess', _377 => _377.targetFrameRate]), () => ( 60));
7489
- if (!Number.isFinite(targetFrameRate) || targetFrameRate < 1 || targetFrameRate > 60) {
7490
- throw new XmaxError("INVALID_CONFIGURATION" /* invalidConfiguration */, "Interpolation targetFrameRate must be between 1 and 60");
8869
+ if (!providers.includes(this.provider)) {
8870
+ throw new XmaxError(
8871
+ "INVALID_CONFIGURATION" /* invalidConfiguration */,
8872
+ `Model ${init.model} does not support RTC provider ${this.provider}`
8873
+ );
7491
8874
  }
7492
- this.isFrameInterpolationEnabled = _nullishCoalesce(_nullishCoalesce(_optionalChain([init, 'access', _378 => _378.frameInterpolation, 'optionalAccess', _379 => _379.enabled]), () => ( init.isFrameInterpolationEnabled)), () => ( false));
7493
- this.frameInterpolation = Object.freeze({ enabled: this.isFrameInterpolationEnabled, targetFrameRate });
8875
+ this.model = init.model;
8876
+ this.isFrameInterpolationEnabled = _nullishCoalesce(init.isFrameInterpolationEnabled, () => ( false));
7494
8877
  }
7495
8878
  };
7496
8879
 
@@ -7501,29 +8884,6 @@ var VideoContentMode = /* @__PURE__ */ ((VideoContentMode2) => {
7501
8884
  return VideoContentMode2;
7502
8885
  })(VideoContentMode || {});
7503
8886
 
7504
- // src/Service/Realtime/RealtimeContext.ts
7505
- var RealtimeContext = class {
7506
- /**
7507
- * 创建实时生成条件并规范化文本和参考路径。
7508
- */
7509
- constructor(init) {
7510
- /**
7511
- * 生成条件
7512
- */
7513
- /**
7514
- * 实时生成使用的文本条件。
7515
- */
7516
- __publicField(this, "prompt");
7517
- /**
7518
- * 已上传参考图片的可选远端路径。
7519
- */
7520
- __publicField(this, "referencePath");
7521
- this.prompt = init.prompt.trim();
7522
- const normalizedReferencePath = _optionalChain([init, 'access', _380 => _380.referencePath, 'optionalAccess', _381 => _381.trim, 'call', _382 => _382()]);
7523
- this.referencePath = normalizedReferencePath || void 0;
7524
- }
7525
- };
7526
-
7527
8887
  // ../../node_modules/.pnpm/framegen@1.4.0/node_modules/framegen/rt.js
7528
8888
  var WG = 8;
7529
8889
  var texBgSeq = 0;
@@ -9516,11 +10876,11 @@ var _FrameInterpolationManager = class _FrameInterpolationManager {
9516
10876
  });
9517
10877
  }
9518
10878
  static async create(canvas, size, onFailure, signal) {
9519
- const ensureActive = () => {
10879
+ const ensureActive2 = () => {
9520
10880
  if (signal.aborted) throw new XmaxError("CANCELLED" /* cancelled */, "Interpolation initialization cancelled");
9521
10881
  };
9522
10882
  const adapter = await frameInterpolationAdapter(size);
9523
- ensureActive();
10883
+ ensureActive2();
9524
10884
  if (!adapter) throw new XmaxError("FRAME_INTERPOLATION_UNSUPPORTED" /* frameInterpolationUnsupported */, "WebGPU shader-f16 is unavailable");
9525
10885
  const device = await adapter.requestDevice({ requiredFeatures: ["shader-f16"] });
9526
10886
  const manager = new _FrameInterpolationManager(device, canvas, size, onFailure);
@@ -9531,9 +10891,9 @@ var _FrameInterpolationManager = class _FrameInterpolationManager {
9531
10891
  if (!manager.destroyed) onFailure(new Error(`Interpolation GPU device lost: ${info.message}`));
9532
10892
  });
9533
10893
  try {
9534
- ensureActive();
10894
+ ensureActive2();
9535
10895
  await manager.initialize();
9536
- ensureActive();
10896
+ ensureActive2();
9537
10897
  return manager;
9538
10898
  } catch (error) {
9539
10899
  manager.destroy();
@@ -9646,11 +11006,11 @@ var _FrameInterpolationManager = class _FrameInterpolationManager {
9646
11006
  if (this.destroyed) return;
9647
11007
  this.destroyed = true;
9648
11008
  this.device.removeEventListener("uncapturederror", this.onGPUError);
9649
- _optionalChain([this, 'access', _383 => _383.runtime, 'optionalAccess', _384 => _384.destroy, 'call', _385 => _385()]);
11009
+ _optionalChain([this, 'access', _532 => _532.runtime, 'optionalAccess', _533 => _533.destroy, 'call', _534 => _534()]);
9650
11010
  this.runtime = void 0;
9651
11011
  this.textures.forEach((texture) => texture.destroy());
9652
11012
  this.textures = [];
9653
- _optionalChain([this, 'access', _386 => _386.context, 'optionalAccess', _387 => _387.unconfigure, 'call', _388 => _388()]);
11013
+ _optionalChain([this, 'access', _535 => _535.context, 'optionalAccess', _536 => _536.unconfigure, 'call', _537 => _537()]);
9654
11014
  this.device.destroy();
9655
11015
  }
9656
11016
  };
@@ -9659,6 +11019,7 @@ __publicField(_FrameInterpolationManager, "originalSlots", 3);
9659
11019
  var FrameInterpolationManager = _FrameInterpolationManager;
9660
11020
 
9661
11021
  // src/Render/Video/RemoteVideoFramePipeline.ts
11022
+ var INTERPOLATION_FRAME_RATE = 60;
9662
11023
  var RemoteVideoFramePipeline = class {
9663
11024
  constructor(video, canvas, options, createProcessor = FrameInterpolationManager.create) {
9664
11025
  __publicField(this, "video", video);
@@ -9753,7 +11114,9 @@ var RemoteVideoFramePipeline = class {
9753
11114
  this.delay = interval;
9754
11115
  this.enqueue({ slot, time });
9755
11116
  this.startTick();
9756
- if (!consecutive || interval < 2e3 / this.options.targetFrameRate - 1 || previousSlot === void 0 || this.busy) return;
11117
+ if (!consecutive || interval < 2e3 / INTERPOLATION_FRAME_RATE - 1 || previousSlot === void 0 || this.busy) {
11118
+ return;
11119
+ }
9757
11120
  this.interpolate(previousSlot, slot, previous, time, interval);
9758
11121
  }
9759
11122
  /** 帧按显示时间升序入队;同一时间的内容后到先出。 */
@@ -9829,7 +11192,7 @@ var RemoteVideoFramePipeline = class {
9829
11192
  setActive(active) {
9830
11193
  if (active === this.active) return;
9831
11194
  this.active = active;
9832
- _optionalChain([this, 'access', _389 => _389.options, 'access', _390 => _390.onActiveChange, 'optionalCall', _391 => _391(active)]);
11195
+ _optionalChain([this, 'access', _538 => _538.options, 'access', _539 => _539.onActiveChange, 'optionalCall', _540 => _540(active)]);
9833
11196
  }
9834
11197
  resetTimeline() {
9835
11198
  this.generation++;
@@ -9860,7 +11223,7 @@ var RemoteVideoFramePipeline = class {
9860
11223
  this.callback = void 0;
9861
11224
  clearTimeout(this.initializationTimer);
9862
11225
  this.resetTimeline();
9863
- _optionalChain([this, 'access', _392 => _392.processor, 'optionalAccess', _393 => _393.destroy, 'call', _394 => _394()]);
11226
+ _optionalChain([this, 'access', _541 => _541.processor, 'optionalAccess', _542 => _542.destroy, 'call', _543 => _543()]);
9864
11227
  this.processor = void 0;
9865
11228
  }
9866
11229
  };
@@ -9918,7 +11281,7 @@ var XmaxVideoView = class {
9918
11281
  * DOM 挂载状态
9919
11282
  */
9920
11283
  __publicField(this, "detached", false);
9921
- this.contentMode = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _395 => _395.videoContentMode]), () => ( "fill")) /* fill */;
11284
+ this.contentMode = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _544 => _544.videoContentMode]), () => ( "fill")) /* fill */;
9922
11285
  this.element = document.createElement("div");
9923
11286
  this.element.style.position = "relative";
9924
11287
  this.element.style.overflow = "hidden";
@@ -9992,14 +11355,14 @@ var XmaxVideoView = class {
9992
11355
  attach(to) {
9993
11356
  this.detached = false;
9994
11357
  to.appendChild(this.element);
9995
- if (this.videoElement.srcObject && this.videoElement.paused && typeof this.videoElement.play === "function") {
11358
+ if ((this.videoElement.srcObject || this.videoElement.getAttribute("src")) && this.videoElement.autoplay && !this.videoElement.ended && this.videoElement.paused && typeof this.videoElement.play === "function") {
9996
11359
  void this.videoElement.play().catch((error) => {
9997
11360
  XmaxLogger.render.warning(() => `\u6062\u590D\u89C6\u9891\u64AD\u653E\u5931\u8D25 (Failed to Resume Video Playback)
9998
11361
  \u2514\u2500 ${String(error)}`);
9999
11362
  });
10000
11363
  }
10001
11364
  this.startInterpolation();
10002
- if (this.videoElement.srcObject && !this.hasNotifiedFrameDisplay) {
11365
+ if ((this.videoElement.srcObject || this.videoElement.getAttribute("src")) && !this.hasNotifiedFrameDisplay) {
10003
11366
  this.armFrameDisplayNotification();
10004
11367
  }
10005
11368
  }
@@ -10010,8 +11373,29 @@ var XmaxVideoView = class {
10010
11373
  this.detached = true;
10011
11374
  this.stopInterpolation();
10012
11375
  this.cancelFrameDisplayNotification();
11376
+ if (this.videoElement.getAttribute("src")) {
11377
+ this.videoElement.pause();
11378
+ }
10013
11379
  this.element.remove();
10014
11380
  }
11381
+ /**
11382
+ * 设置网络视频预览;静音播放一次,不捕获或发布媒体轨道。 @internal
11383
+ */
11384
+ setVideoURL(url) {
11385
+ this.stopInterpolation();
11386
+ this.cancelFrameDisplayNotification();
11387
+ this.hasNotifiedFrameDisplay = false;
11388
+ this.videoElement.pause();
11389
+ this.videoElement.srcObject = null;
11390
+ this.videoElement.removeAttribute("src");
11391
+ this.videoElement.muted = true;
11392
+ this.videoElement.loop = false;
11393
+ if (url) {
11394
+ this.videoElement.src = url;
11395
+ this.armFrameDisplayNotification();
11396
+ }
11397
+ this.videoElement.load();
11398
+ }
10015
11399
  /**
10016
11400
  * 直接设置渲染用的媒体流(绑定管线和直接渲染共用)。 @internal
10017
11401
  */
@@ -10024,6 +11408,7 @@ var XmaxVideoView = class {
10024
11408
  this.hasNotifiedFrameDisplay = false;
10025
11409
  this.videoElement.srcObject = stream;
10026
11410
  if (stream) {
11411
+ this.videoElement.autoplay = true;
10027
11412
  this.armFrameDisplayNotification();
10028
11413
  this.startInterpolation();
10029
11414
  }
@@ -10065,8 +11450,8 @@ var XmaxVideoView = class {
10065
11450
  stopInterpolation() {
10066
11451
  const pipeline = this.interpolationPipeline;
10067
11452
  this.interpolationPipeline = void 0;
10068
- _optionalChain([pipeline, 'optionalAccess', _396 => _396.stop, 'call', _397 => _397()]);
10069
- _optionalChain([this, 'access', _398 => _398.interpolationCanvas, 'optionalAccess', _399 => _399.remove, 'call', _400 => _400()]);
11453
+ _optionalChain([pipeline, 'optionalAccess', _545 => _545.stop, 'call', _546 => _546()]);
11454
+ _optionalChain([this, 'access', _547 => _547.interpolationCanvas, 'optionalAccess', _548 => _548.remove, 'call', _549 => _549()]);
10070
11455
  this.interpolationCanvas = void 0;
10071
11456
  }
10072
11457
  /**
@@ -10092,11 +11477,14 @@ var XmaxVideoView = class {
10092
11477
  detachCurrentTrack() {
10093
11478
  const track = this.currentTrack;
10094
11479
  if (track) {
10095
- _optionalChain([VideoRenderRegistry, 'access', _401 => _401.binding, 'call', _402 => _402(track), 'optionalAccess', _403 => _403.detachHandler, 'call', _404 => _404(this)]);
11480
+ _optionalChain([VideoRenderRegistry, 'access', _550 => _550.binding, 'call', _551 => _551(track), 'optionalAccess', _552 => _552.detachHandler, 'call', _553 => _553(this)]);
10096
11481
  }
10097
11482
  this.setFrameInterpolation(void 0);
10098
11483
  this.cancelFrameDisplayNotification();
10099
11484
  this.videoElement.srcObject = null;
11485
+ if (this.videoElement.getAttribute("src")) {
11486
+ this.setVideoURL(null);
11487
+ }
10100
11488
  }
10101
11489
  /**
10102
11490
  * 首帧提交合成器时通知;旧浏览器回退到 playing 近似判断。
@@ -10112,8 +11500,8 @@ var XmaxVideoView = class {
10112
11500
  }
10113
11501
  this.hasNotifiedFrameDisplay = true;
10114
11502
  this.cancelFrameDisplayNotification();
10115
- _optionalChain([binding, 'optionalAccess', _405 => _405.frameDisplayHandler, 'optionalCall', _406 => _406()]);
10116
- _optionalChain([this, 'access', _407 => _407.frameDisplayHandler, 'optionalCall', _408 => _408()]);
11503
+ _optionalChain([binding, 'optionalAccess', _554 => _554.frameDisplayHandler, 'optionalCall', _555 => _555()]);
11504
+ _optionalChain([this, 'access', _556 => _556.frameDisplayHandler, 'optionalCall', _557 => _557()]);
10117
11505
  };
10118
11506
  if (typeof this.videoElement.requestVideoFrameCallback === "function") {
10119
11507
  this.videoFrameCallbackID = this.videoElement.requestVideoFrameCallback(notify);
@@ -10183,9 +11571,9 @@ var XmaxRealtimeVideoView = class {
10183
11571
  __publicField(this, "currentLocalTrack");
10184
11572
  __publicField(this, "currentRemoteTrack");
10185
11573
  __publicField(this, "contentMode");
10186
- this.contentMode = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _409 => _409.videoContentMode]), () => ( "fill")) /* fill */;
10187
- this.currentLocalTrack = _optionalChain([options, 'optionalAccess', _410 => _410.localTrack]);
10188
- this.currentRemoteTrack = _optionalChain([options, 'optionalAccess', _411 => _411.remoteTrack]);
11574
+ this.contentMode = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _558 => _558.videoContentMode]), () => ( "fill")) /* fill */;
11575
+ this.currentLocalTrack = _optionalChain([options, 'optionalAccess', _559 => _559.localTrack]);
11576
+ this.currentRemoteTrack = _optionalChain([options, 'optionalAccess', _560 => _560.remoteTrack]);
10189
11577
  this.element = document.createElement("div");
10190
11578
  this.element.style.position = "relative";
10191
11579
  this.element.style.overflow = "hidden";
@@ -10344,5 +11732,11 @@ var XmaxRealtimeVideoView = class {
10344
11732
 
10345
11733
 
10346
11734
 
10347
- exports.XmaxEnvironment = XmaxEnvironment; exports.apiBaseURL = apiBaseURL; exports.XmaxLoggerOption = XmaxLoggerOption; exports.XmaxLogger = XmaxLogger; exports.XmaxErrorCode = XmaxErrorCode; exports.XmaxError = XmaxError; exports.RealtimeVideoEncoderPreference = RealtimeVideoEncoderPreference; exports.RealtimeVideoFormat = RealtimeVideoFormat; exports.RealtimeModel = RealtimeModel; exports.defaultFrameRate = defaultFrameRate; exports.defaultCameraVideoFormat = defaultCameraVideoFormat; exports.MediaService = MediaService; exports.StoredFile = StoredFile; exports.StorageService = StorageService; exports.CameraPosition = CameraPosition; exports.RtcProvider = RtcProvider; exports.RealtimeVideoTrack = RealtimeVideoTrack; exports.RealtimeMediaStream = RealtimeMediaStream; exports.RealtimeConnectionState = RealtimeConnectionState; exports.RealtimeReason = RealtimeReason; exports.RealtimeState = RealtimeState; exports.XmaxConfiguration = XmaxConfiguration; exports.XmaxClient = XmaxClient; exports.RealtimeConfiguration = RealtimeConfiguration; exports.VideoContentMode = VideoContentMode; exports.RealtimeContext = RealtimeContext; exports.XmaxVideoView = XmaxVideoView; exports.XmaxRealtimeVideoView = XmaxRealtimeVideoView;
10348
- //# sourceMappingURL=chunk-FEL5MJMJ.cjs.map
11735
+
11736
+
11737
+
11738
+
11739
+
11740
+
11741
+ exports.XmaxEnvironment = XmaxEnvironment; exports.apiBaseURL = apiBaseURL; exports.XmaxLoggerOption = XmaxLoggerOption; exports.XmaxLogger = XmaxLogger; exports.XmaxErrorCode = XmaxErrorCode; exports.XmaxError = XmaxError; exports.RealtimeVideoEncoderPreference = RealtimeVideoEncoderPreference; exports.RealtimeVideoFormat = RealtimeVideoFormat; exports.RtcProvider = RtcProvider; exports.RealtimeModel = RealtimeModel; exports.modelDisplayName = modelDisplayName; exports.defaultFrameRate = defaultFrameRate; exports.defaultCameraVideoFormat = defaultCameraVideoFormat; exports.supportedRtcProviders = supportedRtcProviders; exports.MediaService = MediaService; exports.StoredFile = StoredFile; exports.StorageService = StorageService; exports.CameraPosition = CameraPosition; exports.RealtimeVideoTrack = RealtimeVideoTrack; exports.RealtimeMediaStream = RealtimeMediaStream; exports.RealtimeVideoSampleMethod = RealtimeVideoSampleMethod; exports.RealtimeContext = RealtimeContext; exports.RealtimeConnectionState = RealtimeConnectionState; exports.RealtimeReason = RealtimeReason; exports.RealtimeState = RealtimeState; exports.XmaxConfiguration = XmaxConfiguration; exports.NonRealtimeQuality = NonRealtimeQuality; exports.NonRealtimeTaskStatus = NonRealtimeTaskStatus; exports.NonRealtimeTaskError = NonRealtimeTaskError; exports.XmaxClient = XmaxClient; exports.RealtimeConfiguration = RealtimeConfiguration; exports.VideoContentMode = VideoContentMode; exports.XmaxVideoView = XmaxVideoView; exports.XmaxRealtimeVideoView = XmaxRealtimeVideoView;
11742
+ //# sourceMappingURL=chunk-JN6HURTT.cjs.map