@xmaxai/web-sdk 1.0.9 → 1.0.10

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.
@@ -205,7 +205,7 @@ var XmaxError = class _XmaxError extends Error {
205
205
  };
206
206
 
207
207
  // src/Foundation/Runtime/RuntimeInfo.ts
208
- var XMAX_SDK_VERSION = "1.0.9";
208
+ var XMAX_SDK_VERSION = "1.0.10";
209
209
  var _RuntimeInfo = class _RuntimeInfo {
210
210
  /**
211
211
  * 序列化为接口传输使用的 snake_case 字段。
@@ -501,7 +501,7 @@ var RealtimeVideoFormat = class _RealtimeVideoFormat {
501
501
  * @param init.width 视频宽度,单位为像素。
502
502
  * @param init.height 视频高度,单位为像素。
503
503
  * @param init.fps 视频帧率,必须大于 0。
504
- * @param init.minimumBitrate 最低上传码率,单位为 kbps;缺省按最终上传尺寸和帧率计算。
504
+ * @param init.minimumBitrate 最低上传码率,单位为 kbps;缺省为 100,仅支持该参数的 RTC 链路生效。
505
505
  * @param init.maximumBitrate 最高上传码率,单位为 kbps;缺省按最终上传尺寸和帧率计算。
506
506
  * @param init.encoderPreference 上传编码策略偏好,默认值为 maintainFramerate。
507
507
  */
@@ -525,7 +525,7 @@ var RealtimeVideoFormat = class _RealtimeVideoFormat {
525
525
  * 上传编码配置
526
526
  */
527
527
  /**
528
- * 最低上传码率,单位为 kbps;未指定时由 SDK 计算,0 表示不设最低码率。
528
+ * 最低上传码率,单位为 kbps;缺省为 100,0 表示不设最低码率;仅支持该参数的 RTC 链路生效。
529
529
  */
530
530
  _chunk4DE2IREAcjs.__publicField.call(void 0, this, "minimumBitrate");
531
531
  /**
@@ -598,10 +598,6 @@ var RtcProvider = /* @__PURE__ */ ((RtcProvider2) => {
598
598
 
599
599
  // src/Service/Realtime/RealtimeModel.ts
600
600
  var RealtimeModel = /* @__PURE__ */ ((RealtimeModel2) => {
601
- RealtimeModel2["x2_0"] = "x2.0";
602
- RealtimeModel2["x2_0_trtc"] = "x2.0-trtc";
603
- RealtimeModel2["x2_1_preview"] = "x2.1-preview";
604
- RealtimeModel2["x2_1_preview_1014"] = "x2.1-preview-1014";
605
601
  RealtimeModel2["x2_1_1_preview_cn"] = "x2.1.1-preview-cn";
606
602
  RealtimeModel2["x2_1_1_preview_us"] = "x2.1.1-preview-us";
607
603
  return RealtimeModel2;
@@ -613,36 +609,17 @@ function isKnownModel(model) {
613
609
  return Object.values(RealtimeModel).includes(model);
614
610
  }
615
611
  function modelDefaults(model) {
616
- return isKnownModel(model) ? model : "x2.1-preview" /* x2_1_preview */;
612
+ return isKnownModel(model) ? model : "x2.1.1-preview-cn" /* x2_1_1_preview_cn */;
617
613
  }
618
614
  var MODEL_DISPLAY_NAMES = {
619
- ["x2.0" /* x2_0 */]: "X2.0",
620
- ["x2.0-trtc" /* x2_0_trtc */]: "X2.1-preview-trtc",
621
- ["x2.1-preview" /* x2_1_preview */]: "X2.1-preview-agora",
622
- ["x2.1-preview-1014" /* x2_1_preview_1014 */]: "X2.1-preview-1014",
623
615
  ["x2.1.1-preview-cn" /* x2_1_1_preview_cn */]: "X2.1.1-preview-cn",
624
616
  ["x2.1.1-preview-us" /* x2_1_1_preview_us */]: "X2.1.1-preview-us"
625
617
  };
626
618
  var MODEL_BASE_URLS = {
627
- ["x2.1-preview" /* x2_1_preview */]: "https://cloud.xmax.22duck.cn/open/api/v1",
628
- ["x2.1-preview-1014" /* x2_1_preview_1014 */]: "https://dev.xmaxai.com/open/api/v1",
629
619
  ["x2.1.1-preview-cn" /* x2_1_1_preview_cn */]: "https://cloud.xmax.22duck.cn/open/api/v1",
630
620
  ["x2.1.1-preview-us" /* x2_1_1_preview_us */]: "https://api.xmax.ai/open/api/v1"
631
621
  };
632
622
  var RESOLUTION_BUCKETS = {
633
- ["x2.0" /* x2_0 */]: [],
634
- ["x2.0-trtc" /* x2_0_trtc */]: [
635
- { width: 1024, height: 1920 },
636
- { width: 1920, height: 1024 }
637
- ],
638
- ["x2.1-preview" /* x2_1_preview */]: [
639
- { width: 1024, height: 1920 },
640
- { width: 1920, height: 1024 }
641
- ],
642
- ["x2.1-preview-1014" /* x2_1_preview_1014 */]: [
643
- { width: 1024, height: 1920 },
644
- { width: 1920, height: 1024 }
645
- ],
646
623
  ["x2.1.1-preview-cn" /* x2_1_1_preview_cn */]: [
647
624
  { width: 1024, height: 1920 },
648
625
  { width: 1920, height: 1024 }
@@ -653,10 +630,6 @@ var RESOLUTION_BUCKETS = {
653
630
  ]
654
631
  };
655
632
  var MAXIMUM_INPUT_PIXELS = {
656
- ["x2.0" /* x2_0 */]: 128e4,
657
- ["x2.0-trtc" /* x2_0_trtc */]: 21e5,
658
- ["x2.1-preview" /* x2_1_preview */]: 21e5,
659
- ["x2.1-preview-1014" /* x2_1_preview_1014 */]: 21e5,
660
633
  ["x2.1.1-preview-cn" /* x2_1_1_preview_cn */]: 21e5,
661
634
  ["x2.1.1-preview-us" /* x2_1_1_preview_us */]: 21e5
662
635
  };
@@ -682,16 +655,10 @@ function defaultFrameRate(_model) {
682
655
  return 30;
683
656
  }
684
657
  function defaultCameraVideoFormat(model) {
685
- if (modelDefaults(model) === "x2.0" /* x2_0 */) {
686
- return new RealtimeVideoFormat({ width: 832, height: 1472, fps: 30 });
687
- }
658
+ modelDefaults(model);
688
659
  return new RealtimeVideoFormat({ width: 1024, height: 1920, fps: 30 });
689
660
  }
690
661
  var MODEL_RTC_PROVIDERS = {
691
- ["x2.0" /* x2_0 */]: Object.freeze(["vertc" /* vertc */]),
692
- ["x2.0-trtc" /* x2_0_trtc */]: Object.freeze(["trtc" /* trtc */]),
693
- ["x2.1-preview" /* x2_1_preview */]: Object.freeze(["agora" /* agora */]),
694
- ["x2.1-preview-1014" /* x2_1_preview_1014 */]: Object.freeze(["agora" /* agora */]),
695
662
  ["x2.1.1-preview-cn" /* x2_1_1_preview_cn */]: Object.freeze(["agora" /* agora */]),
696
663
  ["x2.1.1-preview-us" /* x2_1_1_preview_us */]: Object.freeze(["agora" /* agora */])
697
664
  };
@@ -699,8 +666,8 @@ function supportedRtcProviders(model) {
699
666
  return isKnownModel(model) ? MODEL_RTC_PROVIDERS[model] : CUSTOM_MODEL_RTC_PROVIDERS;
700
667
  }
701
668
  var CUSTOM_MODEL_RTC_PROVIDERS = Object.freeze([
702
- ...MODEL_RTC_PROVIDERS["x2.1-preview" /* x2_1_preview */],
703
- ...Object.values(RtcProvider).filter((provider) => !MODEL_RTC_PROVIDERS["x2.1-preview" /* x2_1_preview */].includes(provider))
669
+ ...MODEL_RTC_PROVIDERS["x2.1.1-preview-cn" /* x2_1_1_preview_cn */],
670
+ ...Object.values(RtcProvider).filter((provider) => !MODEL_RTC_PROVIDERS["x2.1.1-preview-cn" /* x2_1_1_preview_cn */].includes(provider))
704
671
  ]);
705
672
 
706
673
  // src/Foundation/Media/Video/FrameInterpolationSupport.ts
@@ -722,9 +689,9 @@ var MediaService = class {
722
689
  /**
723
690
  * 创建媒体服务。
724
691
  *
725
- * @param model 媒体输入规则使用的模型,默认为 `x2.0-trtc`。
692
+ * @param model 媒体输入规则使用的模型,默认为 `x2.1.1-preview-cn`。
726
693
  */
727
- constructor(model = "x2.0-trtc" /* x2_0_trtc */) {
694
+ constructor(model = "x2.1.1-preview-cn" /* x2_1_1_preview_cn */) {
728
695
  /**
729
696
  * 模型约束
730
697
  */
@@ -3968,7 +3935,9 @@ var RESERVED_PARAMS = /* @__PURE__ */ new Set([
3968
3935
  "ref_image_path",
3969
3936
  "mirror",
3970
3937
  "ref_video_path",
3971
- "sample_method"
3938
+ "sample_method",
3939
+ "min_bitrate",
3940
+ "max_bitrate"
3972
3941
  ]);
3973
3942
  function snapshotExtraParams(params) {
3974
3943
  if (params === void 0) return void 0;
@@ -5980,6 +5949,20 @@ var RenderController = class {
5980
5949
  _chunk4DE2IREAcjs.__publicField.call(void 0, this, "track");
5981
5950
  _chunk4DE2IREAcjs.__publicField.call(void 0, this, "target");
5982
5951
  _chunk4DE2IREAcjs.__publicField.call(void 0, this, "interactionEnabled", false);
5952
+ _chunk4DE2IREAcjs.__publicField.call(void 0, this, "ready", false);
5953
+ }
5954
+ /**
5955
+ * 当前远端视图是否已完成渲染预热。
5956
+ */
5957
+ get isReady() {
5958
+ return this.ready;
5959
+ }
5960
+ /**
5961
+ * 新任务重新等待首帧和预热,避免复用上一次任务的展示就绪状态。
5962
+ */
5963
+ prepareForGeneration() {
5964
+ this.ready = false;
5965
+ _optionalChain([this, 'access', _521 => _521.target, 'optionalAccess', _522 => _522.prepareForGeneration, 'optionalCall', _523 => _523()]);
5983
5966
  }
5984
5967
  /**
5985
5968
  * 注册新轨道并使旧绑定失效;本地轨道不经过该入口。
@@ -5991,27 +5974,37 @@ var RenderController = class {
5991
5974
  TrajectoryRegistry.register(track, new TrajectoryBinding(listener, track.videoFormat));
5992
5975
  }
5993
5976
  VideoRenderRegistry.register(track, {
5977
+ // 临时缓解生成开头卡顿:首帧后继续播放 1000 ms,再渐入。
5978
+ renderWarmupMs: 1e3,
5979
+ renderWarmupCompletedHandler: () => {
5980
+ if (this.track === track) {
5981
+ this.ready = true;
5982
+ _optionalChain([this, 'access', _524 => _524.callbacks, 'access', _525 => _525.onRenderWarmupCompleted, 'optionalCall', _526 => _526()]);
5983
+ }
5984
+ },
5994
5985
  frameDisplayHandler: () => {
5995
5986
  if (this.track === track) {
5996
- _optionalChain([this, 'access', _521 => _521.callbacks, 'access', _522 => _522.onFrameDisplayed, 'optionalCall', _523 => _523()]);
5987
+ _optionalChain([this, 'access', _527 => _527.callbacks, 'access', _528 => _528.onFrameDisplayed, 'optionalCall', _529 => _529()]);
5997
5988
  }
5998
5989
  },
5999
5990
  attachHandler: (view) => {
6000
5991
  if (this.track !== track) {
6001
5992
  return;
6002
5993
  }
6003
- _optionalChain([this, 'access', _524 => _524.target, 'optionalAccess', _525 => _525.setFrameInterpolation, 'optionalCall', _526 => _526(void 0)]);
5994
+ _optionalChain([this, 'access', _530 => _530.target, 'optionalAccess', _531 => _531.setFrameInterpolation, 'optionalCall', _532 => _532(void 0)]);
5995
+ this.ready = false;
6004
5996
  view.isMirrored = false;
6005
5997
  this.target = view;
6006
- _optionalChain([this, 'access', _527 => _527.callbacks, 'access', _528 => _528.onAttached, 'optionalCall', _529 => _529()]);
5998
+ _optionalChain([this, 'access', _533 => _533.callbacks, 'access', _534 => _534.onAttached, 'optionalCall', _535 => _535()]);
6007
5999
  view.setMediaStream(track.mediaStreamTrack ? new MediaStream([track.mediaStreamTrack]) : null);
6008
6000
  },
6009
6001
  detachHandler: (view) => {
6010
6002
  if (this.target === view) {
6011
- _optionalChain([this, 'access', _530 => _530.callbacks, 'access', _531 => _531.onDetached, 'optionalCall', _532 => _532()]);
6003
+ this.ready = false;
6004
+ _optionalChain([this, 'access', _536 => _536.callbacks, 'access', _537 => _537.onDetached, 'optionalCall', _538 => _538()]);
6012
6005
  this.target = void 0;
6013
6006
  }
6014
- _optionalChain([view, 'access', _533 => _533.setFrameInterpolation, 'optionalCall', _534 => _534(void 0)]);
6007
+ _optionalChain([view, 'access', _539 => _539.setFrameInterpolation, 'optionalCall', _540 => _540(void 0)]);
6015
6008
  view.setMediaStream(null);
6016
6009
  }
6017
6010
  });
@@ -6020,16 +6013,17 @@ var RenderController = class {
6020
6013
  * 更新媒体画面;暂停显示时同时禁用交互。
6021
6014
  */
6022
6015
  setRemoteMedia(track) {
6016
+ this.ready = false;
6023
6017
  if (this.track) {
6024
- _optionalChain([TrajectoryRegistry, 'access', _535 => _535.binding, 'call', _536 => _536(this.track), 'optionalAccess', _537 => _537.update, 'call', _538 => _538(!!track && this.interactionEnabled)]);
6018
+ _optionalChain([TrajectoryRegistry, 'access', _541 => _541.binding, 'call', _542 => _542(this.track), 'optionalAccess', _543 => _543.update, 'call', _544 => _544(!!track && this.interactionEnabled)]);
6025
6019
  }
6026
- _optionalChain([this, 'access', _539 => _539.target, 'optionalAccess', _540 => _540.setMediaStream, 'call', _541 => _541(track ? new MediaStream([track]) : null)]);
6020
+ _optionalChain([this, 'access', _545 => _545.target, 'optionalAccess', _546 => _546.setMediaStream, 'call', _547 => _547(track ? new MediaStream([track]) : null)]);
6027
6021
  }
6028
6022
  /**
6029
6023
  * 转交插帧配置,不重建交互视图。
6030
6024
  */
6031
6025
  setFrameInterpolation(options) {
6032
- _optionalChain([this, 'access', _542 => _542.target, 'optionalAccess', _543 => _543.setFrameInterpolation, 'optionalCall', _544 => _544(options)]);
6026
+ _optionalChain([this, 'access', _548 => _548.target, 'optionalAccess', _549 => _549.setFrameInterpolation, 'optionalCall', _550 => _550(options)]);
6033
6027
  }
6034
6028
  /**
6035
6029
  * 更新交互绑定的输入格式和可用性。
@@ -6037,13 +6031,14 @@ var RenderController = class {
6037
6031
  setInteractionEnabled(enabled, format) {
6038
6032
  this.interactionEnabled = enabled;
6039
6033
  if (this.track) {
6040
- _optionalChain([TrajectoryRegistry, 'access', _545 => _545.binding, 'call', _546 => _546(this.track), 'optionalAccess', _547 => _547.update, 'call', _548 => _548(enabled, format)]);
6034
+ _optionalChain([TrajectoryRegistry, 'access', _551 => _551.binding, 'call', _552 => _552(this.track), 'optionalAccess', _553 => _553.update, 'call', _554 => _554(enabled, format)]);
6041
6035
  }
6042
6036
  }
6043
6037
  /**
6044
6038
  * 先使能力失效再清空画面,迟到首帧和旧手势不能恢复已结束任务。
6045
6039
  */
6046
6040
  resetRemoteTrack() {
6041
+ this.ready = false;
6047
6042
  const track = this.track;
6048
6043
  this.track = void 0;
6049
6044
  this.interactionEnabled = false;
@@ -6051,8 +6046,8 @@ var RenderController = class {
6051
6046
  TrajectoryRegistry.unregister(track);
6052
6047
  VideoRenderRegistry.unregister(track);
6053
6048
  }
6054
- _optionalChain([this, 'access', _549 => _549.target, 'optionalAccess', _550 => _550.setFrameInterpolation, 'optionalCall', _551 => _551(void 0)]);
6055
- _optionalChain([this, 'access', _552 => _552.target, 'optionalAccess', _553 => _553.setMediaStream, 'call', _554 => _554(null)]);
6049
+ _optionalChain([this, 'access', _555 => _555.target, 'optionalAccess', _556 => _556.setFrameInterpolation, 'optionalCall', _557 => _557(void 0)]);
6050
+ _optionalChain([this, 'access', _558 => _558.target, 'optionalAccess', _559 => _559.setMediaStream, 'call', _560 => _560(null)]);
6056
6051
  this.target = void 0;
6057
6052
  }
6058
6053
  };
@@ -6080,7 +6075,7 @@ var RealtimeContext = class {
6080
6075
  _chunk4DE2IREAcjs.__publicField.call(void 0, this, "referencePath");
6081
6076
  this.referenceVideo = init.referenceVideo && Object.freeze({ ...init.referenceVideo });
6082
6077
  this.prompt = init.prompt.trim();
6083
- const normalizedReferencePath = _optionalChain([init, 'access', _555 => _555.referencePath, 'optionalAccess', _556 => _556.trim, 'call', _557 => _557()]);
6078
+ const normalizedReferencePath = _optionalChain([init, 'access', _561 => _561.referencePath, 'optionalAccess', _562 => _562.trim, 'call', _563 => _563()]);
6084
6079
  this.referencePath = normalizedReferencePath || void 0;
6085
6080
  }
6086
6081
  };
@@ -6348,7 +6343,7 @@ var RealtimeSessionService = class _RealtimeSessionService {
6348
6343
  return;
6349
6344
  }
6350
6345
  _RealtimeSessionService.ensureSessionActive(session);
6351
- await _optionalChain([handlers, 'access', _558 => _558.onRefresh, 'optionalCall', _559 => _559(session)]);
6346
+ await _optionalChain([handlers, 'access', _564 => _564.onRefresh, 'optionalCall', _565 => _565(session)]);
6352
6347
  } catch (error) {
6353
6348
  if (this.heartbeatVersion !== version) {
6354
6349
  return;
@@ -6501,6 +6496,7 @@ var RealtimeConnectionState = /* @__PURE__ */ ((RealtimeConnectionState2) => {
6501
6496
  RealtimeConnectionState2["ready"] = "Ready";
6502
6497
  RealtimeConnectionState2["connecting"] = "Connecting";
6503
6498
  RealtimeConnectionState2["connected"] = "Connected";
6499
+ RealtimeConnectionState2["starting"] = "Starting";
6504
6500
  RealtimeConnectionState2["generating"] = "Generating";
6505
6501
  RealtimeConnectionState2["disconnecting"] = "Disconnecting";
6506
6502
  return RealtimeConnectionState2;
@@ -6561,7 +6557,7 @@ var RealtimeState = class {
6561
6557
  * @param other 待比较的状态。
6562
6558
  */
6563
6559
  equals(other) {
6564
- return this.connectionState === other.connectionState && this.sessionID === other.sessionID && this.taskID === other.taskID && _optionalChain([this, 'access', _560 => _560.reason, 'optionalAccess', _561 => _561.kind]) === _optionalChain([other, 'access', _562 => _562.reason, 'optionalAccess', _563 => _563.kind]);
6560
+ return this.connectionState === other.connectionState && this.sessionID === other.sessionID && this.taskID === other.taskID && _optionalChain([this, 'access', _566 => _566.reason, 'optionalAccess', _567 => _567.kind]) === _optionalChain([other, 'access', _568 => _568.reason, 'optionalAccess', _569 => _569.kind]);
6565
6561
  }
6566
6562
  };
6567
6563
 
@@ -6606,6 +6602,8 @@ var RoomEvent = class _RoomEvent {
6606
6602
  * @param options.videoFormat 模型生成使用的视频规格。
6607
6603
  * @param options.context 本次生成条件。
6608
6604
  * @param options.targetSize 回传尺寸,缺省时不发送;网络视频默认使用生成尺寸。
6605
+ * @param options.minimumBitrate 服务端回传的最低码率,单位为 kbps。
6606
+ * @param options.maximumBitrate 服务端回传的最高码率,单位为 kbps。
6609
6607
  * @param options.mirror 是否由后端镜像输入画面,默认 false。
6610
6608
  * @param options.faceRestoration 是否回贴原始人脸,默认 false。
6611
6609
  * @param options.extraParams 已校验的本次扩展参数,合并到 params。
@@ -6616,13 +6614,15 @@ var RoomEvent = class _RoomEvent {
6616
6614
  event: "start",
6617
6615
  params: {
6618
6616
  ..._RoomEvent.generationParameters(options),
6617
+ min_bitrate: options.minimumBitrate,
6618
+ max_bitrate: options.maximumBitrate,
6619
6619
  mirror: _nullishCoalesce(options.mirror, () => ( false)),
6620
6620
  ...options.context.referenceVideo && {
6621
6621
  ref_video_path: options.context.referenceVideo.path,
6622
6622
  sample_method: options.context.referenceVideo.sampleMethod,
6623
6623
  target_size: [
6624
- _nullishCoalesce(_optionalChain([options, 'access', _564 => _564.targetSize, 'optionalAccess', _565 => _565.width]), () => ( options.videoFormat.width)),
6625
- _nullishCoalesce(_optionalChain([options, 'access', _566 => _566.targetSize, 'optionalAccess', _567 => _567.height]), () => ( options.videoFormat.height))
6624
+ _nullishCoalesce(_optionalChain([options, 'access', _570 => _570.targetSize, 'optionalAccess', _571 => _571.width]), () => ( options.videoFormat.width)),
6625
+ _nullishCoalesce(_optionalChain([options, 'access', _572 => _572.targetSize, 'optionalAccess', _573 => _573.height]), () => ( options.videoFormat.height))
6626
6626
  ]
6627
6627
  }
6628
6628
  },
@@ -6652,12 +6652,14 @@ var RoomEvent = class _RoomEvent {
6652
6652
  });
6653
6653
  }
6654
6654
  /**
6655
- * 回传尺寸调整信令。
6655
+ * 回传尺寸和码率范围调整信令,码率单位为 kbps。
6656
6656
  */
6657
6657
  static changeTargetSize(options) {
6658
6658
  return _RoomEvent.encode({
6659
6659
  event: "change_target_size",
6660
6660
  params: {
6661
+ min_bitrate: options.minimumBitrate,
6662
+ max_bitrate: options.maximumBitrate,
6661
6663
  target_size: [
6662
6664
  Math.round(options.targetSize.width),
6663
6665
  Math.round(options.targetSize.height)
@@ -6732,6 +6734,114 @@ var RoomEvent = class _RoomEvent {
6732
6734
  }
6733
6735
  };
6734
6736
 
6737
+ // src/Stream/Encoding/EncodingBitratePolicy.ts
6738
+ var _EncodingBitratePolicy = class _EncodingBitratePolicy {
6739
+ /**
6740
+ * 最低码率固定为 100 kbps,最高码率按尺寸和帧率计算并应用预算系数。
6741
+ */
6742
+ static resolve(width, height, fps) {
6743
+ new RealtimeVideoFormat({ width, height, fps }).validate();
6744
+ const maximum = Math.round(
6745
+ this.resolveMaximumBitrate(width * height, fps) * this.bitrateScale
6746
+ );
6747
+ if (!Number.isSafeInteger(maximum)) {
6748
+ throw new XmaxError(
6749
+ "INVALID_CONFIGURATION" /* invalidConfiguration */,
6750
+ "Realtime video format exceeds the supported bitrate range"
6751
+ );
6752
+ }
6753
+ return { minimum: 100, maximum: Math.max(100, maximum) };
6754
+ }
6755
+ /**
6756
+ * 按像素面积和帧率插值计算最高码率参考值。
6757
+ */
6758
+ static resolveMaximumBitrate(pixels, fps) {
6759
+ const bitrate15 = _EncodingBitratePolicy.interpolate(
6760
+ pixels,
6761
+ _EncodingBitratePolicy.referenceBitratesAt15FPS
6762
+ );
6763
+ const first30 = _EncodingBitratePolicy.referenceBitratesAt30FPS[0];
6764
+ let bitrate30;
6765
+ if (pixels < first30.value) {
6766
+ const reference15 = _EncodingBitratePolicy.interpolate(
6767
+ first30.value,
6768
+ _EncodingBitratePolicy.referenceBitratesAt15FPS
6769
+ );
6770
+ bitrate30 = bitrate15 * (first30.bitrate / reference15);
6771
+ } else {
6772
+ bitrate30 = _EncodingBitratePolicy.interpolate(
6773
+ pixels,
6774
+ _EncodingBitratePolicy.referenceBitratesAt30FPS
6775
+ );
6776
+ }
6777
+ const bitrate10 = bitrate15 * (400 / 500);
6778
+ const maximum60 = bitrate30 * (6500 / 3150);
6779
+ return _EncodingBitratePolicy.interpolate(fps, [
6780
+ { value: 10, bitrate: bitrate10 * 2 },
6781
+ { value: 15, bitrate: bitrate15 * 2 },
6782
+ { value: 30, bitrate: bitrate30 * 2 },
6783
+ { value: 60, bitrate: maximum60 }
6784
+ ]);
6785
+ }
6786
+ /**
6787
+ * 在相邻参考点间线性插值,表外按最近端点的比例外推。
6788
+ */
6789
+ static interpolate(value, points) {
6790
+ const first = points[0];
6791
+ if (value <= first.value) {
6792
+ return first.bitrate * (value / first.value);
6793
+ }
6794
+ for (let index = 1; index < points.length; index += 1) {
6795
+ const upper = points[index];
6796
+ if (value <= upper.value) {
6797
+ const lower = points[index - 1];
6798
+ const ratio = (value - lower.value) / (upper.value - lower.value);
6799
+ return lower.bitrate + (upper.bitrate - lower.bitrate) * ratio;
6800
+ }
6801
+ }
6802
+ const last = points[points.length - 1];
6803
+ return last.bitrate * (value / last.value);
6804
+ }
6805
+ };
6806
+ /**
6807
+ * 最高码率相对参考值的预算系数。
6808
+ */
6809
+ _chunk4DE2IREAcjs.__publicField.call(void 0, _EncodingBitratePolicy, "bitrateScale", 0.6);
6810
+ /**
6811
+ * 流畅优先码率参考,按像素面积升序排列,单位为 kbps。
6812
+ */
6813
+ _chunk4DE2IREAcjs.__publicField.call(void 0, _EncodingBitratePolicy, "referenceBitratesAt15FPS", [
6814
+ { value: 120 * 120, bitrate: 50 },
6815
+ { value: 160 * 120, bitrate: 65 },
6816
+ { value: 180 * 180, bitrate: 100 },
6817
+ { value: 240 * 180, bitrate: 120 },
6818
+ { value: 320 * 180, bitrate: 140 },
6819
+ { value: 320 * 240, bitrate: 200 },
6820
+ { value: 424 * 240, bitrate: 220 },
6821
+ { value: 360 * 360, bitrate: 260 },
6822
+ { value: 480 * 360, bitrate: 320 },
6823
+ { value: 640 * 360, bitrate: 400 },
6824
+ { value: 640 * 480, bitrate: 500 },
6825
+ { value: 848 * 480, bitrate: 610 },
6826
+ { value: 960 * 720, bitrate: 910 },
6827
+ { value: 1280 * 720, bitrate: 1130 },
6828
+ { value: 1920 * 1080, bitrate: 2080 }
6829
+ ]);
6830
+ /**
6831
+ * 30 fps 的像素面积与码率参考点,单位为 kbps。
6832
+ */
6833
+ _chunk4DE2IREAcjs.__publicField.call(void 0, _EncodingBitratePolicy, "referenceBitratesAt30FPS", [
6834
+ { value: 360 * 360, bitrate: 400 },
6835
+ { value: 480 * 360, bitrate: 490 },
6836
+ { value: 640 * 360, bitrate: 600 },
6837
+ { value: 640 * 480, bitrate: 750 },
6838
+ { value: 848 * 480, bitrate: 930 },
6839
+ { value: 960 * 720, bitrate: 1380 },
6840
+ { value: 1280 * 720, bitrate: 1710 },
6841
+ { value: 1920 * 1080, bitrate: 3150 }
6842
+ ]);
6843
+ var EncodingBitratePolicy = _EncodingBitratePolicy;
6844
+
6735
6845
  // src/Stream/Room/RoomHeartbeat.ts
6736
6846
  var RoomHeartbeat = class {
6737
6847
  /**
@@ -7022,7 +7132,7 @@ var RoomController = class {
7022
7132
  *
7023
7133
  * @throws 已有房间未离开、操作失效或进房失败时抛出错误。
7024
7134
  */
7025
- async join(connection, ensureActive2) {
7135
+ async join(connection, ensureActive2, measureRtcJoin) {
7026
7136
  try {
7027
7137
  ensureActive2();
7028
7138
  } catch (error) {
@@ -7041,11 +7151,14 @@ var RoomController = class {
7041
7151
  if (!this.customCodec) this.codec = new RoomMessageCodec(
7042
7152
  connection.provider === "vertc" /* vertc */ ? null : connection.provider === "agora" /* agora */ ? "__agora_chunk__" : "__trtc_chunk__"
7043
7153
  );
7044
- await this.rtcManager.joinRoom(toRoomJoinConfiguration(connection));
7154
+ const configuration = toRoomJoinConfiguration(connection);
7155
+ const completeRtcJoin = _optionalChain([measureRtcJoin, 'optionalCall', _574 => _574()]);
7156
+ await this.rtcManager.joinRoom(configuration);
7045
7157
  ensureActive2();
7046
7158
  if (this.state.kind !== "joining" || this.state.operationID !== operationID || this.leaveOperation) {
7047
7159
  throw new XmaxError("CANCELLED" /* cancelled */, "RTC room join was cancelled");
7048
7160
  }
7161
+ _optionalChain([completeRtcJoin, 'optionalCall', _575 => _575()]);
7049
7162
  this.state = { kind: "joined", userID: connection.userID };
7050
7163
  this.heartbeat.start(connection.userID);
7051
7164
  } catch (error) {
@@ -7097,7 +7210,8 @@ var RoomController = class {
7097
7210
  targetSize: options.targetSize,
7098
7211
  mirror: _nullishCoalesce(options.mirror, () => ( false)),
7099
7212
  faceRestoration: _nullishCoalesce(options.faceRestoration, () => ( false)),
7100
- extraParams: options.extraParams
7213
+ extraParams: options.extraParams,
7214
+ ...this.downlinkBitrates(_nullishCoalesce(options.targetSize, () => ( options.videoFormat)))
7101
7215
  })
7102
7216
  );
7103
7217
  }
@@ -7136,10 +7250,23 @@ var RoomController = class {
7136
7250
  RoomEvent.changeTargetSize({
7137
7251
  userID: this.requireUserID(),
7138
7252
  taskID: options.taskID,
7139
- targetSize: options.targetSize
7253
+ targetSize: options.targetSize,
7254
+ ...this.downlinkBitrates(options.targetSize)
7140
7255
  })
7141
7256
  );
7142
7257
  }
7258
+ /**
7259
+ * 按回传尺寸和 24 fps 估算下行码率,最高值限制在 100~10000 kbps。
7260
+ * 估算不设置服务端帧率,也不沿用显式上传码率。
7261
+ */
7262
+ downlinkBitrates(size) {
7263
+ const { maximum } = EncodingBitratePolicy.resolve(
7264
+ Math.round(size.width),
7265
+ Math.round(size.height),
7266
+ 24
7267
+ );
7268
+ return { minimumBitrate: 100, maximumBitrate: Math.min(1e4, maximum) };
7269
+ }
7143
7270
  /**
7144
7271
  * 尝试发送生成停止信令;未进房或任务标识为空时忽略。
7145
7272
  */
@@ -7198,7 +7325,7 @@ var RoomController = class {
7198
7325
  if (targetUserID && ownUserID && targetUserID !== ownUserID) {
7199
7326
  return;
7200
7327
  }
7201
- _optionalChain([this, 'access', _568 => _568.listener, 'optionalAccess', _569 => _569.onRoomMessage, 'call', _570 => _570(senderUserID, message)]);
7328
+ _optionalChain([this, 'access', _576 => _576.listener, 'optionalAccess', _577 => _577.onRoomMessage, 'call', _578 => _578(senderUserID, message)]);
7202
7329
  }
7203
7330
  /**
7204
7331
  * 发送房间信令并输出日志。
@@ -7241,7 +7368,7 @@ var ENCODER_PREFERENCE_MAP = {
7241
7368
  ["maintainFramerate" /* maintainFramerate */]: "maintainFramerate" /* maintainFramerate */,
7242
7369
  ["maintainQuality" /* maintainQuality */]: "maintainQuality" /* maintainQuality */
7243
7370
  };
7244
- var _EncodingController = class _EncodingController {
7371
+ var EncodingController = class {
7245
7372
  /**
7246
7373
  * 创建编码控制器。
7247
7374
  *
@@ -7266,21 +7393,13 @@ var _EncodingController = class _EncodingController {
7266
7393
  minimum = videoFormat.minimumBitrate;
7267
7394
  maximum = videoFormat.maximumBitrate;
7268
7395
  } else {
7269
- const bitrates = _EncodingController.resolveBitrates(
7270
- videoFormat.width * videoFormat.height,
7396
+ const defaults = EncodingBitratePolicy.resolve(
7397
+ videoFormat.width,
7398
+ videoFormat.height,
7271
7399
  videoFormat.fps
7272
7400
  );
7273
- const maximumBitrate = Math.round(bitrates.maximum);
7274
- if (!Number.isFinite(maximumBitrate)) {
7275
- throw new XmaxError(
7276
- "INVALID_CONFIGURATION" /* invalidConfiguration */,
7277
- "Realtime video format exceeds the supported bitrate range"
7278
- );
7279
- }
7280
- const defaultMinimum = Math.max(1, Math.round(bitrates.minimum));
7281
- const defaultMaximum = Math.max(defaultMinimum + 1, maximumBitrate);
7282
- minimum = _nullishCoalesce(videoFormat.minimumBitrate, () => ( defaultMinimum));
7283
- maximum = _nullishCoalesce(videoFormat.maximumBitrate, () => ( defaultMaximum));
7401
+ minimum = _nullishCoalesce(videoFormat.minimumBitrate, () => ( defaults.minimum));
7402
+ maximum = _nullishCoalesce(videoFormat.maximumBitrate, () => ( defaults.maximum));
7284
7403
  }
7285
7404
  if (minimum > maximum) {
7286
7405
  throw new XmaxError(
@@ -7321,97 +7440,7 @@ var _EncodingController = class _EncodingController {
7321
7440
  }
7322
7441
  return format;
7323
7442
  }
7324
- /**
7325
- * 按像素面积与帧率计算码率区间(kbps):流畅优先为最低,画质优先为最高。
7326
- */
7327
- static resolveBitrates(pixels, fps) {
7328
- const bitrate15 = _EncodingController.interpolate(
7329
- pixels,
7330
- _EncodingController.referenceBitratesAt15FPS
7331
- );
7332
- const first30 = _EncodingController.referenceBitratesAt30FPS[0];
7333
- let bitrate30;
7334
- if (pixels < first30.value) {
7335
- const reference15 = _EncodingController.interpolate(
7336
- first30.value,
7337
- _EncodingController.referenceBitratesAt15FPS
7338
- );
7339
- bitrate30 = bitrate15 * (first30.bitrate / reference15);
7340
- } else {
7341
- bitrate30 = _EncodingController.interpolate(
7342
- pixels,
7343
- _EncodingController.referenceBitratesAt30FPS
7344
- );
7345
- }
7346
- const bitrate10 = bitrate15 * (400 / 500);
7347
- const minimum60 = bitrate30 * (4780 / 3150);
7348
- const maximum60 = bitrate30 * (6500 / 3150);
7349
- return {
7350
- minimum: _EncodingController.interpolate(fps, [
7351
- { value: 10, bitrate: bitrate10 },
7352
- { value: 15, bitrate: bitrate15 },
7353
- { value: 30, bitrate: bitrate30 },
7354
- { value: 60, bitrate: minimum60 }
7355
- ]),
7356
- maximum: _EncodingController.interpolate(fps, [
7357
- { value: 10, bitrate: bitrate10 * 2 },
7358
- { value: 15, bitrate: bitrate15 * 2 },
7359
- { value: 30, bitrate: bitrate30 * 2 },
7360
- { value: 60, bitrate: maximum60 }
7361
- ])
7362
- };
7363
- }
7364
- /**
7365
- * 在相邻参考点间线性插值,表外按最近端点的比例外推。
7366
- */
7367
- static interpolate(value, points) {
7368
- const first = points[0];
7369
- if (value <= first.value) {
7370
- return first.bitrate * (value / first.value);
7371
- }
7372
- for (let index = 1; index < points.length; index += 1) {
7373
- const upper = points[index];
7374
- if (value <= upper.value) {
7375
- const lower = points[index - 1];
7376
- const ratio = (value - lower.value) / (upper.value - lower.value);
7377
- return lower.bitrate + (upper.bitrate - lower.bitrate) * ratio;
7378
- }
7379
- }
7380
- const last = points[points.length - 1];
7381
- return last.bitrate * (value / last.value);
7382
- }
7383
7443
  };
7384
- /**
7385
- * 流畅优先码率参考,按像素面积升序排列,单位为 kbps。
7386
- */
7387
- _chunk4DE2IREAcjs.__publicField.call(void 0, _EncodingController, "referenceBitratesAt15FPS", [
7388
- { value: 120 * 120, bitrate: 50 },
7389
- { value: 160 * 120, bitrate: 65 },
7390
- { value: 180 * 180, bitrate: 100 },
7391
- { value: 240 * 180, bitrate: 120 },
7392
- { value: 320 * 180, bitrate: 140 },
7393
- { value: 320 * 240, bitrate: 200 },
7394
- { value: 424 * 240, bitrate: 220 },
7395
- { value: 360 * 360, bitrate: 260 },
7396
- { value: 480 * 360, bitrate: 320 },
7397
- { value: 640 * 360, bitrate: 400 },
7398
- { value: 640 * 480, bitrate: 500 },
7399
- { value: 848 * 480, bitrate: 610 },
7400
- { value: 960 * 720, bitrate: 910 },
7401
- { value: 1280 * 720, bitrate: 1130 },
7402
- { value: 1920 * 1080, bitrate: 2080 }
7403
- ]);
7404
- _chunk4DE2IREAcjs.__publicField.call(void 0, _EncodingController, "referenceBitratesAt30FPS", [
7405
- { value: 360 * 360, bitrate: 400 },
7406
- { value: 480 * 360, bitrate: 490 },
7407
- { value: 640 * 360, bitrate: 600 },
7408
- { value: 640 * 480, bitrate: 750 },
7409
- { value: 848 * 480, bitrate: 930 },
7410
- { value: 960 * 720, bitrate: 1380 },
7411
- { value: 1280 * 720, bitrate: 1710 },
7412
- { value: 1920 * 1080, bitrate: 3150 }
7413
- ]);
7414
- var EncodingController = _EncodingController;
7415
7444
 
7416
7445
  // src/Stream/StreamController.ts
7417
7446
  function makeInitialState() {
@@ -7480,7 +7509,7 @@ var StreamController = class {
7480
7509
  this.roomController.setListener({
7481
7510
  onRoomMessage: (sender, message) => {
7482
7511
  this.handleNetworkVideoCompletion(sender, message);
7483
- _optionalChain([this, 'access', _571 => _571.roomListener, 'optionalAccess', _572 => _572.onRoomMessage, 'call', _573 => _573(sender, message)]);
7512
+ _optionalChain([this, 'access', _579 => _579.roomListener, 'optionalAccess', _580 => _580.onRoomMessage, 'call', _581 => _581(sender, message)]);
7484
7513
  }
7485
7514
  });
7486
7515
  this.rtcManager.setEventListener({
@@ -7490,18 +7519,18 @@ var StreamController = class {
7490
7519
  onError: (error) => this.errorListener(error),
7491
7520
  onNetworkStatistics: (statistics) => {
7492
7521
  if (this.state.roomID) {
7493
- _optionalChain([this, 'access', _574 => _574.networkStatisticsListener, 'optionalCall', _575 => _575(statistics)]);
7522
+ _optionalChain([this, 'access', _582 => _582.networkStatisticsListener, 'optionalCall', _583 => _583(statistics)]);
7494
7523
  }
7495
7524
  },
7496
7525
  onRemoteVideoStatistics: (statistics) => {
7497
- const userID = _optionalChain([this, 'access', _576 => _576.state, 'access', _577 => _577.activeRemoteStream, 'optionalAccess', _578 => _578.userID]);
7498
- _optionalChain([this, 'access', _579 => _579.remoteVideoStatisticsListener, 'optionalCall', _580 => _580(
7526
+ const userID = _optionalChain([this, 'access', _584 => _584.state, 'access', _585 => _585.activeRemoteStream, 'optionalAccess', _586 => _586.userID]);
7527
+ _optionalChain([this, 'access', _587 => _587.remoteVideoStatisticsListener, 'optionalCall', _588 => _588(
7499
7528
  userID ? statistics.find((item) => item.userID === userID) : void 0
7500
7529
  )]);
7501
7530
  },
7502
7531
  onLocalVideoStatistics: (statistics) => {
7503
7532
  if (this.state.localVideoPublished) {
7504
- _optionalChain([this, 'access', _581 => _581.localVideoStatisticsListener, 'optionalCall', _582 => _582(statistics)]);
7533
+ _optionalChain([this, 'access', _589 => _589.localVideoStatisticsListener, 'optionalCall', _590 => _590(statistics)]);
7505
7534
  }
7506
7535
  },
7507
7536
  onCustomMessageReceived: (senderUserID, message) => {
@@ -7522,7 +7551,7 @@ var StreamController = class {
7522
7551
  * 按活动结果流身份读取原始音视频,避免录制房间内其他用户的媒体。
7523
7552
  */
7524
7553
  getRemoteRecordingTracks() {
7525
- const userID = _optionalChain([this, 'access', _583 => _583.state, 'access', _584 => _584.activeRemoteStream, 'optionalAccess', _585 => _585.userID]);
7554
+ const userID = _optionalChain([this, 'access', _591 => _591.state, 'access', _592 => _592.activeRemoteStream, 'optionalAccess', _593 => _593.userID]);
7526
7555
  const videoTrack = userID && this.state.remoteVideoTracks.get(userID);
7527
7556
  if (!userID || !videoTrack) {
7528
7557
  return void 0;
@@ -7580,16 +7609,16 @@ var StreamController = class {
7580
7609
  *
7581
7610
  * @throws 连接已取消,或 RTC 进房、房间配置与本地流发布失败时抛出错误。
7582
7611
  */
7583
- async connect(connection, includeLocalAudio, ensureActive2, beforePublish, publishLocalMedia = true) {
7612
+ async connect(connection, includeLocalAudio, ensureActive2, beforePublish, publishLocalMedia = true, measureRtcJoin) {
7584
7613
  this.configureRoom({ roomID: connection.roomID, botID: connection.botID });
7585
- await this.roomController.join(connection, ensureActive2);
7614
+ await this.roomController.join(connection, ensureActive2, measureRtcJoin);
7586
7615
  ensureActive2();
7587
7616
  XmaxLogger.stream.info(
7588
7617
  () => `RTC \u623F\u95F4\u5DF2\u914D\u7F6E (RTC Room Configured)
7589
7618
  \u2514\u2500 roomID: ${connection.roomID}, botID: ${_nullishCoalesce(connection.botID, () => ( "(\u672A\u8BBE\u7F6E)"))}`
7590
7619
  );
7591
7620
  if (publishLocalMedia) {
7592
- await _optionalChain([beforePublish, 'optionalCall', _586 => _586()]);
7621
+ await _optionalChain([beforePublish, 'optionalCall', _594 => _594()]);
7593
7622
  ensureActive2();
7594
7623
  await this.publishLocalStream(includeLocalAudio);
7595
7624
  }
@@ -7679,7 +7708,7 @@ var StreamController = class {
7679
7708
  const state2 = this.state;
7680
7709
  const sent = Promise.resolve(sending).then(async () => {
7681
7710
  if (this.state !== state2 || state2.generationTaskID !== taskID) return;
7682
- await _optionalChain([options, 'access', _587 => _587.onStartSent, 'optionalCall', _588 => _588()]);
7711
+ await _optionalChain([options, 'access', _595 => _595.onStartSent, 'optionalCall', _596 => _596()]);
7683
7712
  if (this.state !== state2 || state2.generationTaskID !== taskID) return;
7684
7713
  for (const userID of state2.publishedRemoteUserIDs) {
7685
7714
  void this.handleRemoteVideoPublished(userID, true);
@@ -7825,7 +7854,7 @@ var StreamController = class {
7825
7854
  deliverNetworkVideoCompletion() {
7826
7855
  const state2 = this.state;
7827
7856
  const completion = state2.networkVideoCompletion;
7828
- if (!_optionalChain([completion, 'optionalAccess', _589 => _589.finished]) || !completion.onFinish || completion.scheduled) {
7857
+ if (!_optionalChain([completion, 'optionalAccess', _597 => _597.finished]) || !completion.onFinish || completion.scheduled) {
7829
7858
  return;
7830
7859
  }
7831
7860
  completion.scheduled = true;
@@ -7834,7 +7863,7 @@ var StreamController = class {
7834
7863
  return;
7835
7864
  }
7836
7865
  try {
7837
- void Promise.resolve(_optionalChain([completion, 'access', _590 => _590.onFinish, 'optionalCall', _591 => _591()])).catch(() => {
7866
+ void Promise.resolve(_optionalChain([completion, 'access', _598 => _598.onFinish, 'optionalCall', _599 => _599()])).catch(() => {
7838
7867
  });
7839
7868
  } catch (e27) {
7840
7869
  }
@@ -7845,7 +7874,7 @@ var StreamController = class {
7845
7874
  */
7846
7875
  configureRoom(options) {
7847
7876
  const roomID = options.roomID.trim();
7848
- const botID = _nullishCoalesce(_optionalChain([options, 'access', _592 => _592.botID, 'optionalAccess', _593 => _593.trim, 'call', _594 => _594()]), () => ( ""));
7877
+ const botID = _nullishCoalesce(_optionalChain([options, 'access', _600 => _600.botID, 'optionalAccess', _601 => _601.trim, 'call', _602 => _602()]), () => ( ""));
7849
7878
  if (!roomID) {
7850
7879
  throw new XmaxError(
7851
7880
  "INVALID_CONFIGURATION" /* invalidConfiguration */,
@@ -7928,7 +7957,7 @@ var StreamController = class {
7928
7957
  return;
7929
7958
  }
7930
7959
  this.state.subscribedRemoteUserIDs.delete(trimmedUserID);
7931
- if (_optionalChain([this, 'access', _595 => _595.state, 'access', _596 => _596.activeRemoteStream, 'optionalAccess', _597 => _597.userID]) === trimmedUserID) {
7960
+ if (_optionalChain([this, 'access', _603 => _603.state, 'access', _604 => _604.activeRemoteStream, 'optionalAccess', _605 => _605.userID]) === trimmedUserID) {
7932
7961
  await this.unsubscribeRemoteAudio(trimmedUserID);
7933
7962
  this.state.activeRemoteStream = void 0;
7934
7963
  this.clearRemoteStream();
@@ -8118,7 +8147,7 @@ var StreamController = class {
8118
8147
  */
8119
8148
  resolveGenerationStart(taskID) {
8120
8149
  const waiter = this.state.generationWaiter;
8121
- if (this.state.generationTaskID !== taskID || _optionalChain([waiter, 'optionalAccess', _598 => _598.taskID]) !== taskID) {
8150
+ if (this.state.generationTaskID !== taskID || _optionalChain([waiter, 'optionalAccess', _606 => _606.taskID]) !== taskID) {
8122
8151
  return;
8123
8152
  }
8124
8153
  this.state.generationWaiter = void 0;
@@ -8130,7 +8159,7 @@ var StreamController = class {
8130
8159
  */
8131
8160
  rejectGenerationStart(taskID, error) {
8132
8161
  const waiter = this.state.generationWaiter;
8133
- if (this.state.generationTaskID !== taskID || _optionalChain([waiter, 'optionalAccess', _599 => _599.taskID]) !== taskID) {
8162
+ if (this.state.generationTaskID !== taskID || _optionalChain([waiter, 'optionalAccess', _607 => _607.taskID]) !== taskID) {
8134
8163
  return;
8135
8164
  }
8136
8165
  this.state.generationWaiter = void 0;
@@ -8141,7 +8170,7 @@ var StreamController = class {
8141
8170
  * 通知渲染层清理远端生成流;失败仅记录日志。
8142
8171
  */
8143
8172
  clearRemoteStream() {
8144
- _optionalChain([this, 'access', _600 => _600.remoteVideoStatisticsListener, 'optionalCall', _601 => _601(void 0)]);
8173
+ _optionalChain([this, 'access', _608 => _608.remoteVideoStatisticsListener, 'optionalCall', _609 => _609(void 0)]);
8145
8174
  try {
8146
8175
  this.remoteStreamListener(null);
8147
8176
  } catch (error) {
@@ -8180,12 +8209,13 @@ var StreamController = class {
8180
8209
 
8181
8210
  // src/Stream/Quality/QualityTiers.ts
8182
8211
  var INITIAL_QUALITY_LEVEL = 2;
8212
+ var MINIMUM_QUALITY_SHORT_SIDE = 240;
8183
8213
  var QUALITY_TIERS = {
8184
- L1: { width: 1024, height: 1920, frameRate: 30 },
8185
- L2: { width: 768, height: 1440, frameRate: 24 },
8186
- L3: { width: 640, height: 1200, frameRate: 24 },
8187
- L4: { width: 512, height: 960, frameRate: 20 },
8188
- L5: { width: 384, height: 720, frameRate: 16 }
8214
+ L1: { scale: 1, width: 1024, height: 1920, frameRate: 30 },
8215
+ L2: { scale: 0.8, width: 818, height: 1536, frameRate: 24 },
8216
+ L3: { scale: 0.65, width: 664, height: 1248, frameRate: 24 },
8217
+ L4: { scale: 0.5, width: 512, height: 960, frameRate: 20 },
8218
+ L5: { scale: 0.4, width: 408, height: 768, frameRate: 16 }
8189
8219
  };
8190
8220
 
8191
8221
  // src/Stream/Quality/QualityPolicy.ts
@@ -8201,9 +8231,13 @@ var DEFAULT_POLICY = {
8201
8231
  // src/Stream/Quality/QualityController.ts
8202
8232
  var ORDERED_QUALITY_TIERS = Object.values(QUALITY_TIERS);
8203
8233
  var QualityController = class {
8204
- constructor(apply, now = () => performance.now(), policy = {}) {
8234
+ /**
8235
+ * 创建调档状态机;候选档位不满足编码约束时保持当前档位,不跨档尝试。
8236
+ */
8237
+ constructor(apply, now = () => performance.now(), policy = {}, canApply = () => true) {
8205
8238
  _chunk4DE2IREAcjs.__publicField.call(void 0, this, "apply", apply);
8206
8239
  _chunk4DE2IREAcjs.__publicField.call(void 0, this, "now", now);
8240
+ _chunk4DE2IREAcjs.__publicField.call(void 0, this, "canApply", canApply);
8207
8241
  _chunk4DE2IREAcjs.__publicField.call(void 0, this, "policy");
8208
8242
  _chunk4DE2IREAcjs.__publicField.call(void 0, this, "active", false);
8209
8243
  _chunk4DE2IREAcjs.__publicField.call(void 0, this, "index", 0);
@@ -8293,10 +8327,13 @@ var QualityController = class {
8293
8327
  this.good = this.poor = 0;
8294
8328
  }
8295
8329
  async change(index, reason) {
8330
+ this.resetCounts();
8331
+ if (!this.canApply(index + 1)) {
8332
+ return;
8333
+ }
8296
8334
  const lifecycle = this.lifecycle;
8297
8335
  const change = { from: this.index + 1, to: index + 1, ...ORDERED_QUALITY_TIERS[index], reason };
8298
8336
  this.switching = true;
8299
- this.resetCounts();
8300
8337
  try {
8301
8338
  await this.apply(change, lifecycle.signal);
8302
8339
  if (lifecycle.signal.aborted) return;
@@ -8321,12 +8358,20 @@ var QualityController = class {
8321
8358
  // src/Stream/Quality/QualityVideoFormat.ts
8322
8359
  function qualityVideoSize(baseline, level) {
8323
8360
  const tier = Object.values(QUALITY_TIERS)[level - 1];
8324
- if (!Number.isInteger(level) || !tier) throw new Error("Unsupported quality level");
8361
+ if (!Number.isInteger(level) || !tier) {
8362
+ throw new Error("Unsupported quality level");
8363
+ }
8325
8364
  baseline.validate();
8326
- const scale = tier.width / QUALITY_TIERS.L1.width;
8327
- const even = (value) => Math.max(2, Math.floor(value / 2) * 2);
8365
+ const shortSide = Math.min(baseline.width, baseline.height);
8366
+ const minimum = Math.min(shortSide, MINIMUM_QUALITY_SHORT_SIDE);
8367
+ const scale = Math.max(tier.scale, minimum / shortSide);
8368
+ const even = (value) => Math.max(2, Math.floor(value / 2 + 1e-9) * 2);
8328
8369
  return { width: even(baseline.width * scale), height: even(baseline.height * scale) };
8329
8370
  }
8371
+ function supportsQualityEncoding(format) {
8372
+ const defaults = EncodingBitratePolicy.resolve(format.width, format.height, format.fps);
8373
+ return (_nullishCoalesce(format.minimumBitrate, () => ( defaults.minimum))) <= (_nullishCoalesce(format.maximumBitrate, () => ( defaults.maximum)));
8374
+ }
8330
8375
  function qualityVideoFormat(baseline, level) {
8331
8376
  const size = qualityVideoSize(baseline, level);
8332
8377
  const tier = Object.values(QUALITY_TIERS)[level - 1];
@@ -8338,35 +8383,59 @@ function qualityVideoFormat(baseline, level) {
8338
8383
  }
8339
8384
 
8340
8385
  // src/Stream/Quality/QualityHistory.ts
8341
- var QualityHistory = class {
8342
- constructor(model, provider) {
8386
+ var _QualityHistory = class _QualityHistory {
8387
+ /**
8388
+ * 创建按模型与 RTC 提供方隔离的档位存储。
8389
+ */
8390
+ constructor(model, provider, now = Date.now) {
8343
8391
  _chunk4DE2IREAcjs.__publicField.call(void 0, this, "model", model);
8344
8392
  _chunk4DE2IREAcjs.__publicField.call(void 0, this, "provider", provider);
8393
+ _chunk4DE2IREAcjs.__publicField.call(void 0, this, "now", now);
8345
8394
  }
8346
- /** 无记录、损坏、SSR 或浏览器禁止存储时使用默认 L2。 */
8395
+ /**
8396
+ * 无记录、过期、损坏、时钟回退或存储不可用时使用默认 L2。
8397
+ */
8347
8398
  read(direction) {
8348
8399
  try {
8349
- const level = Number(globalThis.localStorage.getItem(this.key(direction)));
8350
- if (this.isValid(level)) return level;
8400
+ const entry = JSON.parse(_nullishCoalesce(globalThis.localStorage.getItem(this.key(direction)), () => ( "null")));
8401
+ const now = this.now();
8402
+ if (entry && this.isValid(entry.level) && Number.isFinite(entry.savedAt) && entry.savedAt > 0 && now >= entry.savedAt && now - entry.savedAt < _QualityHistory.expirationMs) {
8403
+ return entry.level;
8404
+ }
8351
8405
  } catch (e28) {
8352
8406
  }
8353
8407
  return INITIAL_QUALITY_LEVEL;
8354
8408
  }
8355
- /** 仅在应用成功后调用;下行成功表示信令已发送,不代表服务端已确认生效。 */
8409
+ /**
8410
+ * 应用成功后保存档位并刷新一小时有效期;下行成功仅表示信令已发送。
8411
+ */
8356
8412
  write(direction, level) {
8357
- if (!this.isValid(level)) return;
8413
+ if (!this.isValid(level)) {
8414
+ return;
8415
+ }
8358
8416
  try {
8359
- globalThis.localStorage.setItem(this.key(direction), String(level));
8417
+ globalThis.localStorage.setItem(this.key(direction), JSON.stringify({ level, savedAt: this.now() }));
8360
8418
  } catch (e29) {
8361
8419
  }
8362
8420
  }
8421
+ /**
8422
+ * 生成当前方向的存储键;无时间戳的旧记录按无效记录处理。
8423
+ */
8363
8424
  key(direction) {
8364
8425
  return `xmax:quality:v1:${this.provider}:${encodeURIComponent(this.model)}:${direction}`;
8365
8426
  }
8427
+ /**
8428
+ * 校验档位编号。
8429
+ */
8366
8430
  isValid(level) {
8367
8431
  return Number.isInteger(level) && level >= 1 && level <= Object.keys(QUALITY_TIERS).length;
8368
8432
  }
8369
8433
  };
8434
+ /**
8435
+ * 历史档位有效期,单位为毫秒。
8436
+ */
8437
+ _chunk4DE2IREAcjs.__publicField.call(void 0, _QualityHistory, "expirationMs", 60 * 60 * 1e3);
8438
+ var QualityHistory = _QualityHistory;
8370
8439
 
8371
8440
  // src/Core/Realtime/RealtimeCoordinator.ts
8372
8441
  function scopeAffects(scope, kind) {
@@ -8504,7 +8573,7 @@ var RealtimeCoordinator = class _RealtimeCoordinator {
8504
8573
  await this.errorHandler.report(error);
8505
8574
  throw error;
8506
8575
  }
8507
- const operationKind = kind === "generation" /* generation */ && this.state.connectionState === "Generating" /* generating */ ? "configuration" /* configuration */ : kind;
8576
+ const operationKind = kind === "generation" /* generation */ && (this.state.connectionState === "Generating" /* generating */ || this.state.connectionState === "Starting" /* starting */ && !!this.state.taskID) ? "configuration" /* configuration */ : kind;
8508
8577
  const lease = new OperationLease(failureScope);
8509
8578
  const resultPromise = (async () => body(lease))();
8510
8579
  const operation = {
@@ -8544,6 +8613,19 @@ var RealtimeCoordinator = class _RealtimeCoordinator {
8544
8613
  new RealtimeState({ connectionState: "Ready" /* ready */ })
8545
8614
  );
8546
8615
  }
8616
+ /**
8617
+ * 当前生成任务和远端展示均就绪后进入 generating;迟到通知不恢复已结束任务。
8618
+ */
8619
+ async remotePreviewDidBecomeReady(sessionID, taskID, isCurrent) {
8620
+ if (this.termination || this.state.connectionState !== "Starting" /* starting */ || this.state.sessionID !== sessionID || this.state.taskID !== taskID || !isCurrent()) {
8621
+ return;
8622
+ }
8623
+ await this.setState(new RealtimeState({
8624
+ connectionState: "Generating" /* generating */,
8625
+ sessionID,
8626
+ taskID
8627
+ }));
8628
+ }
8547
8629
  /**
8548
8630
  * 断开实时连接;尚未提交连接状态的活跃操作也会被取消。
8549
8631
  */
@@ -8606,7 +8688,7 @@ var RealtimeCoordinator = class _RealtimeCoordinator {
8606
8688
  }
8607
8689
  throw _RealtimeCoordinator.cancelledError();
8608
8690
  }
8609
- const terminationTask = await _asyncNullishCoalesce(await _asyncNullishCoalesce(operation.terminationTask, async () => ( _optionalChain([this, 'access', _602 => _602.termination, 'optionalAccess', _603 => _603.task]))), async () => ( await this.requestTermination(
8691
+ const terminationTask = await _asyncNullishCoalesce(await _asyncNullishCoalesce(operation.terminationTask, async () => ( _optionalChain([this, 'access', _610 => _610.termination, 'optionalAccess', _611 => _611.task]))), async () => ( await this.requestTermination(
8610
8692
  _nullishCoalesce(operation.lease.failureScope, () => ( 0)) /* connection */,
8611
8693
  void 0,
8612
8694
  RealtimeReason.normal,
@@ -8735,7 +8817,7 @@ var RealtimeCoordinator = class _RealtimeCoordinator {
8735
8817
  * 结束指定操作,释放操作准入场。
8736
8818
  */
8737
8819
  finish(operation) {
8738
- if (_optionalChain([this, 'access', _604 => _604.activeOperation, 'optionalAccess', _605 => _605.id]) === operation.id) {
8820
+ if (_optionalChain([this, 'access', _612 => _612.activeOperation, 'optionalAccess', _613 => _613.id]) === operation.id) {
8739
8821
  this.activeOperation = void 0;
8740
8822
  }
8741
8823
  }
@@ -8747,7 +8829,7 @@ var RealtimeCoordinator = class _RealtimeCoordinator {
8747
8829
  return;
8748
8830
  }
8749
8831
  this.state = nextState;
8750
- _optionalChain([this, 'access', _606 => _606.stateListener, 'optionalCall', _607 => _607(nextState)]);
8832
+ _optionalChain([this, 'access', _614 => _614.stateListener, 'optionalCall', _615 => _615(nextState)]);
8751
8833
  }
8752
8834
  /**
8753
8835
  * 构造统一的操作取消错误。
@@ -8836,11 +8918,16 @@ var RealtimeLaunchTimer = class {
8836
8918
  return this.measure("permissionMs", performance.now());
8837
8919
  }
8838
8920
  /**
8839
- * 开始会话与进房计时,返回连接阶段完成时调用的记录函数。
8840
- * 相机预热等待计入连接耗时。
8921
+ * 开始创建业务会话的计时。
8922
+ */
8923
+ startSession() {
8924
+ return this.measure("sessionMs", performance.now());
8925
+ }
8926
+ /**
8927
+ * 开始 RTC 适配器进房调用的计时。
8841
8928
  */
8842
- startConnection() {
8843
- return this.measure("connectionMs", performance.now());
8929
+ startRtcJoin() {
8930
+ return this.measure("rtcJoinMs", performance.now());
8844
8931
  }
8845
8932
  /**
8846
8933
  * 开始发布本地流计时,返回发布完成时调用的记录函数。
@@ -8849,11 +8936,17 @@ var RealtimeLaunchTimer = class {
8849
8936
  return this.measure("publishMs", performance.now());
8850
8937
  }
8851
8938
  /**
8852
- * 开始等待远端显示首帧;完成回调同时记录首帧耗时和实际总耗时。
8939
+ * 开始等待远端首帧提交合成器,不包含后续渲染预热。
8853
8940
  */
8854
8941
  startFirstFrame() {
8855
8942
  return this.measure("firstFrameMs", performance.now());
8856
8943
  }
8944
+ /**
8945
+ * 首帧就绪后开始渲染预热;结束时记录预热及实际总耗时,不含渐入动画。
8946
+ */
8947
+ startRenderWarmup() {
8948
+ return this.measure("renderWarmupMs", performance.now());
8949
+ }
8857
8950
  /**
8858
8951
  * 冻结本轮结果,令尚未完成的回调失效;下一轮相机启动才重置。
8859
8952
  */
@@ -8874,7 +8967,7 @@ var RealtimeLaunchTimer = class {
8874
8967
  this.snapshot = Object.freeze({
8875
8968
  ...this.snapshot,
8876
8969
  [key]: now - startedAt,
8877
- ...key === "firstFrameMs" ? { totalMs: now - this.startedAt } : {}
8970
+ ...key === "renderWarmupMs" ? { totalMs: now - this.startedAt } : {}
8878
8971
  });
8879
8972
  this.notify();
8880
8973
  };
@@ -8884,7 +8977,7 @@ var RealtimeLaunchTimer = class {
8884
8977
  */
8885
8978
  notify() {
8886
8979
  try {
8887
- void Promise.resolve(_optionalChain([this, 'access', _608 => _608.listener, 'optionalCall', _609 => _609(this.snapshot)])).catch(() => {
8980
+ void Promise.resolve(_optionalChain([this, 'access', _616 => _616.listener, 'optionalCall', _617 => _617(this.snapshot)])).catch(() => {
8888
8981
  });
8889
8982
  } catch (e30) {
8890
8983
  }
@@ -8915,7 +9008,7 @@ var XmaxRealtimeConnectionManager = class {
8915
9008
  * 当前活动会话标识;尚未连接或清理完成后为空。
8916
9009
  */
8917
9010
  get currentSessionID() {
8918
- return _optionalChain([this, 'access', _610 => _610.activeSession, 'optionalAccess', _611 => _611.id]);
9011
+ return _optionalChain([this, 'access', _618 => _618.activeSession, 'optionalAccess', _619 => _619.id]);
8919
9012
  }
8920
9013
  /**
8921
9014
  * 未配置 API 服务时在连接操作开始前报错,不影响本地预览。
@@ -8925,8 +9018,8 @@ var XmaxRealtimeConnectionManager = class {
8925
9018
  }
8926
9019
  /**
8927
9020
  * 创建会话、配置编码、进房并发布本地媒体,启动心跳后返回远端占位流。
8928
- * 进房结束即记录连接耗时;可选 beforePublish 检查阻塞发布,计入连接耗时;
8929
- * 发布本地流单独计时。
9021
+ * 会话创建、RTC 进房和本地流发布分别计时;可选 beforePublish 检查阻塞发布,
9022
+ * 其等待只反映在实际完整启动耗时中。
8930
9023
  * @param options.localTrack 当前本地媒体轨道。
8931
9024
  * @param options.remoteVideoFormat 原始生成及远端画面规格,缺省使用本地轨道规格。
8932
9025
  * @param options.localVideoFormat 发布前实际应用的上行编码格式;与原始生成及远端格式分开。
@@ -8943,10 +9036,11 @@ var XmaxRealtimeConnectionManager = class {
8943
9036
  const sessionService = this.requireSessionService();
8944
9037
  const streamController = this.dependencies.streamController;
8945
9038
  options.ensureCurrent();
8946
- const completeConnection = this.dependencies.timing.startConnection();
9039
+ const completeSession = this.dependencies.timing.startSession();
8947
9040
  const session = await sessionService.createSession(options.model);
8948
9041
  this.activeSession = session;
8949
9042
  options.ensureCurrent();
9043
+ completeSession();
8950
9044
  const connection = session.connection;
8951
9045
  if (!connection) {
8952
9046
  throw new XmaxError(
@@ -8970,7 +9064,7 @@ var XmaxRealtimeConnectionManager = class {
8970
9064
  if (publishLocalMedia) {
8971
9065
  await streamController.updateUplinkVideoFormat(videoFormat);
8972
9066
  options.ensureCurrent();
8973
- _optionalChain([options, 'access', _612 => _612.onVideoEncoderConfigured, 'optionalCall', _613 => _613()]);
9067
+ _optionalChain([options, 'access', _620 => _620.onVideoEncoderConfigured, 'optionalCall', _621 => _621()]);
8974
9068
  }
8975
9069
  options.ensureCurrent();
8976
9070
  let completePublish;
@@ -8978,13 +9072,12 @@ var XmaxRealtimeConnectionManager = class {
8978
9072
  options.ensureCurrent();
8979
9073
  }, async () => {
8980
9074
  options.ensureCurrent();
8981
- await _optionalChain([options, 'access', _614 => _614.beforePublish, 'optionalCall', _615 => _615()]);
8982
- completeConnection();
9075
+ await _optionalChain([options, 'access', _622 => _622.beforePublish, 'optionalCall', _623 => _623()]);
9076
+ options.ensureCurrent();
8983
9077
  completePublish = this.dependencies.timing.startPublish();
8984
- }, publishLocalMedia);
9078
+ }, publishLocalMedia, () => this.dependencies.timing.startRtcJoin());
8985
9079
  options.ensureCurrent();
8986
- completeConnection();
8987
- _optionalChain([completePublish, 'optionalCall', _616 => _616()]);
9080
+ _optionalChain([completePublish, 'optionalCall', _624 => _624()]);
8988
9081
  options.onPublished();
8989
9082
  sessionService.startHeartbeat(session.id, {
8990
9083
  onFailure: (sessionID, error) => {
@@ -9014,11 +9107,11 @@ var XmaxRealtimeConnectionManager = class {
9014
9107
  */
9015
9108
  stopHeartbeat() {
9016
9109
  this.refreshVersion++;
9017
- _optionalChain([this, 'access', _617 => _617.credentialController, 'optionalAccess', _618 => _618.abort, 'call', _619 => _619()]);
9110
+ _optionalChain([this, 'access', _625 => _625.credentialController, 'optionalAccess', _626 => _626.abort, 'call', _627 => _627()]);
9018
9111
  this.credentialController = void 0;
9019
9112
  this.credentialRefresh = void 0;
9020
9113
  this.credentialApplication = Promise.resolve();
9021
- _optionalChain([this, 'access', _620 => _620.dependencies, 'access', _621 => _621.sessionService, 'optionalAccess', _622 => _622.stopHeartbeat, 'call', _623 => _623()]);
9114
+ _optionalChain([this, 'access', _628 => _628.dependencies, 'access', _629 => _629.sessionService, 'optionalAccess', _630 => _630.stopHeartbeat, 'call', _631 => _631()]);
9022
9115
  }
9023
9116
  /**
9024
9117
  * RTC Token 过期时复用会话心跳获取新凭据,并合并同一实例的并发请求。
@@ -9026,14 +9119,14 @@ var XmaxRealtimeConnectionManager = class {
9026
9119
  async refreshCredentials() {
9027
9120
  if (this.credentialRefresh) return this.credentialRefresh;
9028
9121
  const current = this.activeSession;
9029
- if (!_optionalChain([current, 'optionalAccess', _624 => _624.connection]) || current.connection.provider === "trtc" /* trtc */) return;
9122
+ if (!_optionalChain([current, 'optionalAccess', _632 => _632.connection]) || current.connection.provider === "trtc" /* trtc */) return;
9030
9123
  const version = this.refreshVersion;
9031
9124
  const operation = (async () => {
9032
9125
  try {
9033
9126
  const session = await this.requireSessionService().heartbeatSession(current.id);
9034
9127
  await this.handleSessionRefresh(session, version, true);
9035
9128
  } catch (error) {
9036
- if (version === this.refreshVersion && _optionalChain([this, 'access', _625 => _625.activeSession, 'optionalAccess', _626 => _626.id]) === current.id) {
9129
+ if (version === this.refreshVersion && _optionalChain([this, 'access', _633 => _633.activeSession, 'optionalAccess', _634 => _634.id]) === current.id) {
9037
9130
  this.dependencies.onHeartbeatFailure(current.id, XmaxError.from(error));
9038
9131
  }
9039
9132
  }
@@ -9050,7 +9143,7 @@ var XmaxRealtimeConnectionManager = class {
9050
9143
  */
9051
9144
  async waitUntilRemoteTrackReady(timeoutMs, signal) {
9052
9145
  if (signal.aborted) throw RealtimeCoordinator.cancelledError();
9053
- const mediaTrack = _optionalChain([this, 'access', _627 => _627.activeRemoteTrack, 'optionalAccess', _628 => _628.mediaStreamTrack]);
9146
+ const mediaTrack = _optionalChain([this, 'access', _635 => _635.activeRemoteTrack, 'optionalAccess', _636 => _636.mediaStreamTrack]);
9054
9147
  if (!mediaTrack || !mediaTrack.muted) return;
9055
9148
  await new Promise((resolve, reject) => {
9056
9149
  const cleanup = () => {
@@ -9144,7 +9237,7 @@ var XmaxRealtimeConnectionManager = class {
9144
9237
  return;
9145
9238
  }
9146
9239
  if (next.provider !== "trtc" /* trtc */ && (force || !previous || previous.provider === "trtc" /* trtc */ || next.roomToken !== previous.roomToken)) {
9147
- await this.dependencies.streamController.updateCredentials(next, _optionalChain([this, 'access', _629 => _629.credentialController, 'optionalAccess', _630 => _630.signal]));
9240
+ await this.dependencies.streamController.updateCredentials(next, _optionalChain([this, 'access', _637 => _637.credentialController, 'optionalAccess', _638 => _638.signal]));
9148
9241
  if (version !== this.refreshVersion || this.activeSession !== current) return;
9149
9242
  }
9150
9243
  this.activeSession = new RealtimeSession({
@@ -9159,7 +9252,7 @@ var XmaxRealtimeConnectionManager = class {
9159
9252
  * 尽力清理全部连接资源,即使退房失败也关闭服务端会话。
9160
9253
  */
9161
9254
  async disconnect() {
9162
- const sessionID = _optionalChain([this, 'access', _631 => _631.activeSession, 'optionalAccess', _632 => _632.id]);
9255
+ const sessionID = _optionalChain([this, 'access', _639 => _639.activeSession, 'optionalAccess', _640 => _640.id]);
9163
9256
  this.stopHeartbeat();
9164
9257
  this.clearRemoteMedia();
9165
9258
  try {
@@ -9271,7 +9364,7 @@ var XmaxRealtimeGenerationManager = class _XmaxRealtimeGenerationManager {
9271
9364
  this.currentContext = context;
9272
9365
  await options.waitUntilRemoteReady();
9273
9366
  options.ensureCurrent();
9274
- _optionalChain([options, 'access', _633 => _633.onRemoteReady, 'optionalCall', _634 => _634(taskID)]);
9367
+ _optionalChain([options, 'access', _641 => _641.onRemoteReady, 'optionalCall', _642 => _642(taskID)]);
9275
9368
  options.ensureCurrent();
9276
9369
  await this.streamController.activateRemoteAudio();
9277
9370
  options.ensureCurrent();
@@ -9333,7 +9426,7 @@ var XmaxRealtimeGenerationManager = class _XmaxRealtimeGenerationManager {
9333
9426
  try {
9334
9427
  await completion;
9335
9428
  } finally {
9336
- if (_optionalChain([this, 'access', _635 => _635.pendingStop, 'optionalAccess', _636 => _636.completion]) === completion) this.pendingStop = void 0;
9429
+ if (_optionalChain([this, 'access', _643 => _643.pendingStop, 'optionalAccess', _644 => _644.completion]) === completion) this.pendingStop = void 0;
9337
9430
  }
9338
9431
  }
9339
9432
  /**
@@ -9426,6 +9519,7 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
9426
9519
  */
9427
9520
  _chunk4DE2IREAcjs.__publicField.call(void 0, this, "launchTimer", new RealtimeLaunchTimer());
9428
9521
  _chunk4DE2IREAcjs.__publicField.call(void 0, this, "remoteFrameDisplayHandler");
9522
+ _chunk4DE2IREAcjs.__publicField.call(void 0, this, "remoteRenderWarmupCompletedHandler");
9429
9523
  /**
9430
9524
  * 媒体权限弹窗观察
9431
9525
  */
@@ -9462,11 +9556,11 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
9462
9556
  this.options = options;
9463
9557
  this.mediaService = new MediaService(options.model);
9464
9558
  this.interpolationRequested = options.isFrameInterpolationEnabled;
9465
- this.supportsInterpolation = _nullishCoalesce(_optionalChain([dependencies, 'optionalAccess', _637 => _637.supportsFrameInterpolation]), () => ( (async (size) => await frameInterpolationAdapter(size) !== null)));
9559
+ this.supportsInterpolation = _nullishCoalesce(_optionalChain([dependencies, 'optionalAccess', _645 => _645.supportsFrameInterpolation]), () => ( (async (size) => await frameInterpolationAdapter(size) !== null)));
9466
9560
  this.errorHandler = new RealtimeErrorHandler();
9467
- const rtcManager = _nullishCoalesce(_optionalChain([dependencies, 'optionalAccess', _638 => _638.rtcManager]), () => ( createRtcManager(options.provider, _nullishCoalesce(_optionalChain([dependencies, 'optionalAccess', _639 => _639.environment]), () => ( "china")) /* china */)));
9468
- const sessionService = _nullishCoalesce(_optionalChain([dependencies, 'optionalAccess', _640 => _640.sessionService]), () => ( (_optionalChain([dependencies, 'optionalAccess', _641 => _641.apiService]) ? new RealtimeSessionService({ apiService: dependencies.apiService, provider: options.provider }) : void 0)));
9469
- this.streamController = _nullishCoalesce(_optionalChain([dependencies, 'optionalAccess', _642 => _642.streamController]), () => ( new StreamController({
9561
+ const rtcManager = _nullishCoalesce(_optionalChain([dependencies, 'optionalAccess', _646 => _646.rtcManager]), () => ( createRtcManager(options.provider, _nullishCoalesce(_optionalChain([dependencies, 'optionalAccess', _647 => _647.environment]), () => ( "china")) /* china */)));
9562
+ const sessionService = _nullishCoalesce(_optionalChain([dependencies, 'optionalAccess', _648 => _648.sessionService]), () => ( (_optionalChain([dependencies, 'optionalAccess', _649 => _649.apiService]) ? new RealtimeSessionService({ apiService: dependencies.apiService, provider: options.provider }) : void 0)));
9563
+ this.streamController = _nullishCoalesce(_optionalChain([dependencies, 'optionalAccess', _650 => _650.streamController]), () => ( new StreamController({
9470
9564
  rtcManager,
9471
9565
  onCredentialsRequired: () => {
9472
9566
  void this.connectionManager.refreshCredentials();
@@ -9485,7 +9579,7 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
9485
9579
  this.mediaController = new MediaController({
9486
9580
  rtcManager,
9487
9581
  mediaService: this.mediaService,
9488
- cameraController: _optionalChain([dependencies, 'optionalAccess', _643 => _643.cameraController]),
9582
+ cameraController: _optionalChain([dependencies, 'optionalAccess', _651 => _651.cameraController]),
9489
9583
  onCameraError: (error) => {
9490
9584
  void this.errorHandler.report(error);
9491
9585
  },
@@ -9501,7 +9595,11 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
9501
9595
  interactionListener: (taskID, points) => this.streamController.sendTracks(taskID, points)
9502
9596
  });
9503
9597
  this.renderController = new RenderController({
9504
- onFrameDisplayed: () => _optionalChain([this, 'access', _644 => _644.remoteFrameDisplayHandler, 'optionalCall', _645 => _645()]),
9598
+ onFrameDisplayed: () => _optionalChain([this, 'access', _652 => _652.remoteFrameDisplayHandler, 'optionalCall', _653 => _653()]),
9599
+ onRenderWarmupCompleted: () => {
9600
+ _optionalChain([this, 'access', _654 => _654.remoteRenderWarmupCompletedHandler, 'optionalCall', _655 => _655()]);
9601
+ void this.completeGenerationPresentation().catch((error) => this.errorHandler.report(error));
9602
+ },
9505
9603
  onAttached: () => this.applyRemoteInterpolation(),
9506
9604
  onDetached: () => {
9507
9605
  this.interpolationRevision++;
@@ -9530,6 +9628,10 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
9530
9628
  await this.coordinator.run("configuration" /* configuration */, void 0, async (token) => {
9531
9629
  await this.applyUplinkQuality(change.to, token, signal);
9532
9630
  });
9631
+ }, void 0, {}, (level) => {
9632
+ const track = this.currentUplinkTrack;
9633
+ const state2 = track && this.qualityStates.get(track);
9634
+ return !!state2 && supportsQualityEncoding(this.uplinkQualityFormat(state2.baseline, level));
9533
9635
  });
9534
9636
  this.downlinkQualityController = new QualityController(async (change, signal) => {
9535
9637
  await this.coordinator.run("configuration" /* configuration */, void 0, async (token) => {
@@ -9574,7 +9676,7 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
9574
9676
  */
9575
9677
  async setFrameInterpolationEnabled(enabled) {
9576
9678
  await this.coordinator.run("configuration" /* configuration */, void 0, async (token) => {
9577
- const format = _nullishCoalesce(_nullishCoalesce(_optionalChain([this, 'access', _646 => _646.downlinkQualityState, 'optionalAccess', _647 => _647.size]), () => ( this.generationVideoFormat)), () => ( _optionalChain([this, 'access', _648 => _648.currentLocalTrack, 'optionalAccess', _649 => _649.videoFormat])));
9679
+ const format = _nullishCoalesce(_nullishCoalesce(_optionalChain([this, 'access', _656 => _656.downlinkQualityState, 'optionalAccess', _657 => _657.size]), () => ( this.generationVideoFormat)), () => ( _optionalChain([this, 'access', _658 => _658.currentLocalTrack, 'optionalAccess', _659 => _659.videoFormat])));
9578
9680
  const size = format && enabled ? this.mediaService.resolveFrameInterpolationSize(format) : void 0;
9579
9681
  if (enabled && !await this.checkInterpolationSupport(size, token)) {
9580
9682
  token.ensureCurrent();
@@ -9600,6 +9702,9 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
9600
9702
  }
9601
9703
  const level = Math.max(1, Math.min(Object.keys(QUALITY_TIERS).length, state2.level + (direction === "upgrade" ? -1 : 1)));
9602
9704
  if (level === state2.level) return;
9705
+ if (!supportsQualityEncoding(this.uplinkQualityFormat(state2.baseline, level))) {
9706
+ throw new XmaxError("INVALID_CONFIGURATION" /* invalidConfiguration */, "Quality level conflicts with explicit bitrate constraints");
9707
+ }
9603
9708
  this.adaptiveQualityRevision++;
9604
9709
  controller.resetSamples();
9605
9710
  await this.applyUplinkQuality(level, token);
@@ -9611,7 +9716,7 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
9611
9716
  const state2 = track && this.qualityStates.get(track);
9612
9717
  const ensureActive2 = () => {
9613
9718
  token.ensureCurrent();
9614
- if (_optionalChain([signal, 'optionalAccess', _650 => _650.aborted]) || !track || !state2 || track !== this.currentUplinkTrack || state2 !== this.qualityStates.get(track) || this.currentState.connectionState !== "Generating" /* generating */) {
9719
+ if (_optionalChain([signal, 'optionalAccess', _660 => _660.aborted]) || !track || !state2 || track !== this.currentUplinkTrack || state2 !== this.qualityStates.get(track) || this.currentState.connectionState !== "Generating" /* generating */) {
9615
9720
  throw new XmaxError("CANCELLED" /* cancelled */, "Adaptive video format update was cancelled");
9616
9721
  }
9617
9722
  };
@@ -9629,7 +9734,7 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
9629
9734
  _nullishCoalesce(this.generationVideoFormat, () => ( (this.generationVideoFormat = state2.baseline)));
9630
9735
  track.updateVideoFormat(format);
9631
9736
  state2.level = level;
9632
- _optionalChain([this, 'access', _651 => _651.qualityHistory, 'optionalAccess', _652 => _652.write, 'call', _653 => _653("uplink", level)]);
9737
+ _optionalChain([this, 'access', _661 => _661.qualityHistory, 'optionalAccess', _662 => _662.write, 'call', _663 => _663("uplink", level)]);
9633
9738
  }
9634
9739
  /** 手动调试与自动调档使用同一尺寸信令路径,不修改生成或上行配置。 */
9635
9740
  async adjustDownlinkQuality(direction) {
@@ -9651,7 +9756,7 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
9651
9756
  const state2 = this.downlinkQualityState;
9652
9757
  const ensureActive2 = () => {
9653
9758
  token.ensureCurrent();
9654
- if (_optionalChain([signal, 'optionalAccess', _654 => _654.aborted]) || !state2 || state2 !== this.downlinkQualityState || this.currentState.connectionState !== "Generating" /* generating */ || this.currentState.taskID !== state2.taskID) {
9759
+ if (_optionalChain([signal, 'optionalAccess', _664 => _664.aborted]) || !state2 || state2 !== this.downlinkQualityState || this.currentState.connectionState !== "Generating" /* generating */ || this.currentState.taskID !== state2.taskID) {
9655
9760
  throw new XmaxError("CANCELLED" /* cancelled */, "Target size update was cancelled");
9656
9761
  }
9657
9762
  };
@@ -9662,7 +9767,7 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
9662
9767
  ensureActive2();
9663
9768
  state2.level = level;
9664
9769
  state2.size = size;
9665
- _optionalChain([this, 'access', _655 => _655.qualityHistory, 'optionalAccess', _656 => _656.write, 'call', _657 => _657("downlink", level)]);
9770
+ _optionalChain([this, 'access', _665 => _665.qualityHistory, 'optionalAccess', _666 => _666.write, 'call', _667 => _667("downlink", level)]);
9666
9771
  if (this.interpolationRequested) {
9667
9772
  this.interpolationSize = size;
9668
9773
  this.applyRemoteInterpolation();
@@ -9781,7 +9886,7 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
9781
9886
  */
9782
9887
  notifyRemoteVideoStatistics() {
9783
9888
  try {
9784
- void Promise.resolve(_optionalChain([this, 'access', _658 => _658.remoteVideoStatisticsListener, 'optionalCall', _659 => _659(this.remoteVideoStatistics)])).catch(() => {
9889
+ void Promise.resolve(_optionalChain([this, 'access', _668 => _668.remoteVideoStatisticsListener, 'optionalCall', _669 => _669(this.remoteVideoStatistics)])).catch(() => {
9785
9890
  });
9786
9891
  } catch (e31) {
9787
9892
  }
@@ -9798,7 +9903,7 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
9798
9903
  */
9799
9904
  notifyNetworkStatistics() {
9800
9905
  try {
9801
- void Promise.resolve(_optionalChain([this, 'access', _660 => _660.networkStatisticsListener, 'optionalCall', _661 => _661(this.networkStatistics)])).catch(() => {
9906
+ void Promise.resolve(_optionalChain([this, 'access', _670 => _670.networkStatisticsListener, 'optionalCall', _671 => _671(this.networkStatistics)])).catch(() => {
9802
9907
  });
9803
9908
  } catch (e32) {
9804
9909
  }
@@ -9818,7 +9923,7 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
9818
9923
  */
9819
9924
  notifyLocalVideoStatistics() {
9820
9925
  try {
9821
- void Promise.resolve(_optionalChain([this, 'access', _662 => _662.localVideoStatisticsListener, 'optionalCall', _663 => _663(this.localVideoStatistics)])).catch(() => {
9926
+ void Promise.resolve(_optionalChain([this, 'access', _672 => _672.localVideoStatisticsListener, 'optionalCall', _673 => _673(this.localVideoStatistics)])).catch(() => {
9822
9927
  });
9823
9928
  } catch (e33) {
9824
9929
  }
@@ -9827,8 +9932,8 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
9827
9932
  * 停止接收媒体统计并清空各类快照,仅对曾有数据的统计发出清空通知。
9828
9933
  */
9829
9934
  clearVideoStatistics() {
9830
- _optionalChain([this, 'access', _664 => _664.qualityController, 'optionalAccess', _665 => _665.stop, 'call', _666 => _666()]);
9831
- _optionalChain([this, 'access', _667 => _667.downlinkQualityController, 'optionalAccess', _668 => _668.stop, 'call', _669 => _669()]);
9935
+ _optionalChain([this, 'access', _674 => _674.qualityController, 'optionalAccess', _675 => _675.stop, 'call', _676 => _676()]);
9936
+ _optionalChain([this, 'access', _677 => _677.downlinkQualityController, 'optionalAccess', _678 => _678.stop, 'call', _679 => _679()]);
9832
9937
  this.downlinkQualityState = void 0;
9833
9938
  this.adaptiveQualityRevision++;
9834
9939
  this.acceptsVideoStatistics = false;
@@ -9900,7 +10005,7 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
9900
10005
  const completeCamera = this.launchTimer.startCamera();
9901
10006
  this.clearVideoStatistics();
9902
10007
  this.disposePermissionWatch();
9903
- const permissionWatch = await watchMediaPermissionPrompt(_nullishCoalesce(_optionalChain([options, 'optionalAccess', _670 => _670.useMicrophone]), () => ( false)));
10008
+ const permissionWatch = await watchMediaPermissionPrompt(_nullishCoalesce(_optionalChain([options, 'optionalAccess', _680 => _680.useMicrophone]), () => ( false)));
9904
10009
  if (permissionWatch) {
9905
10010
  const completePermission = this.launchTimer.startPermission();
9906
10011
  void permissionWatch.decided.then(() => completePermission()).catch(() => {
@@ -9917,7 +10022,7 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
9917
10022
  throw error;
9918
10023
  }
9919
10024
  token.ensureCurrent();
9920
- this.generationVideoFormat = _optionalChain([stream, 'access', _671 => _671.videoTrack, 'optionalAccess', _672 => _672.videoFormat]);
10025
+ this.generationVideoFormat = _optionalChain([stream, 'access', _681 => _681.videoTrack, 'optionalAccess', _682 => _682.videoFormat]);
9921
10026
  completeCamera();
9922
10027
  this.mediaController.setCameraPreviewReadyHandler((isCurrent) => {
9923
10028
  void this.coordinator.localPreviewDidBecomeReady(isCurrent);
@@ -9942,7 +10047,7 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
9942
10047
  this.clearVideoStatistics();
9943
10048
  await this.mediaController.prepareSource(() => token.ensureCurrent());
9944
10049
  token.ensureCurrent();
9945
- this.generationVideoFormat = _optionalChain([stream, 'access', _673 => _673.videoTrack, 'optionalAccess', _674 => _674.videoFormat]);
10050
+ this.generationVideoFormat = _optionalChain([stream, 'access', _683 => _683.videoTrack, 'optionalAccess', _684 => _684.videoFormat]);
9946
10051
  await this.coordinator.commit(new RealtimeState({ connectionState: "Ready" /* ready */ }), token);
9947
10052
  return stream;
9948
10053
  });
@@ -9964,7 +10069,7 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
9964
10069
  const stream = await this.mediaController.createLocalVideoStream(options, token.signal);
9965
10070
  token.ensureCurrent();
9966
10071
  await this.mediaController.prepareSource(() => token.ensureCurrent());
9967
- this.generationVideoFormat = _optionalChain([stream, 'access', _675 => _675.videoTrack, 'optionalAccess', _676 => _676.videoFormat]);
10072
+ this.generationVideoFormat = _optionalChain([stream, 'access', _685 => _685.videoTrack, 'optionalAccess', _686 => _686.videoFormat]);
9968
10073
  await this.coordinator.commit(new RealtimeState({ connectionState: "Ready" /* ready */ }), token);
9969
10074
  return stream;
9970
10075
  });
@@ -9982,7 +10087,7 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
9982
10087
  const stream = await this.mediaController.createLocalImageStream(options, token.signal);
9983
10088
  token.ensureCurrent();
9984
10089
  await this.mediaController.prepareSource(() => token.ensureCurrent());
9985
- this.generationVideoFormat = _optionalChain([stream, 'access', _677 => _677.videoTrack, 'optionalAccess', _678 => _678.videoFormat]);
10090
+ this.generationVideoFormat = _optionalChain([stream, 'access', _687 => _687.videoTrack, 'optionalAccess', _688 => _688.videoFormat]);
9986
10091
  await this.coordinator.commit(new RealtimeState({ connectionState: "Ready" /* ready */ }), token);
9987
10092
  return stream;
9988
10093
  });
@@ -10001,7 +10106,7 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
10001
10106
  this.launchTimer.start();
10002
10107
  this.clearVideoStatistics();
10003
10108
  await this.mediaController.prepareSource(() => token.ensureCurrent());
10004
- this.generationVideoFormat = _optionalChain([stream, 'access', _679 => _679.videoTrack, 'optionalAccess', _680 => _680.videoFormat]);
10109
+ this.generationVideoFormat = _optionalChain([stream, 'access', _689 => _689.videoTrack, 'optionalAccess', _690 => _690.videoFormat]);
10005
10110
  await this.coordinator.commit(new RealtimeState({ connectionState: "Ready" /* ready */ }), token);
10006
10111
  return stream;
10007
10112
  });
@@ -10018,7 +10123,7 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
10018
10123
  * 移除媒体权限弹窗观察;授权决定已记录或不再需要观察时调用。
10019
10124
  */
10020
10125
  disposePermissionWatch() {
10021
- _optionalChain([this, 'access', _681 => _681.permissionWatchDispose, 'optionalCall', _682 => _682()]);
10126
+ _optionalChain([this, 'access', _691 => _691.permissionWatchDispose, 'optionalCall', _692 => _692()]);
10022
10127
  this.permissionWatchDispose = void 0;
10023
10128
  }
10024
10129
  /**
@@ -10030,38 +10135,38 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
10030
10135
  async updateUplinkVideoFormat(videoFormat) {
10031
10136
  await this.coordinator.run("configuration" /* configuration */, void 0, async (token) => {
10032
10137
  const track = this.currentUplinkTrack;
10033
- const previousFormat = _optionalChain([track, 'optionalAccess', _683 => _683.videoFormat]);
10138
+ const previousFormat = _optionalChain([track, 'optionalAccess', _693 => _693.videoFormat]);
10034
10139
  if (!track || !previousFormat) {
10035
10140
  throw new XmaxError("INVALID_CONFIGURATION" /* invalidConfiguration */, "Local camera, external or file video stream is not started");
10036
10141
  }
10037
10142
  videoFormat.validate();
10038
- _optionalChain([this, 'access', _684 => _684.qualityController, 'optionalAccess', _685 => _685.stop, 'call', _686 => _686()]);
10143
+ _optionalChain([this, 'access', _694 => _694.qualityController, 'optionalAccess', _695 => _695.stop, 'call', _696 => _696()]);
10039
10144
  await this.streamController.updateUplinkVideoFormat(videoFormat);
10040
10145
  token.ensureCurrent();
10041
10146
  _nullishCoalesce(this.generationVideoFormat, () => ( (this.generationVideoFormat = previousFormat)));
10042
10147
  track.updateVideoFormat(videoFormat);
10043
10148
  this.qualityStates.set(track, { baseline: videoFormat, level: 1 });
10044
- _optionalChain([this, 'access', _687 => _687.qualityHistory, 'optionalAccess', _688 => _688.write, 'call', _689 => _689("uplink", 1)]);
10149
+ _optionalChain([this, 'access', _697 => _697.qualityHistory, 'optionalAccess', _698 => _698.write, 'call', _699 => _699("uplink", 1)]);
10045
10150
  });
10046
10151
  }
10047
10152
  /** 已连接时仅替换帧率;与其他配置及生命周期操作共享同一操作租约。 */
10048
10153
  async updateUplinkFrameRate(fps) {
10049
10154
  await this.coordinator.run("configuration" /* configuration */, void 0, async (token) => {
10050
- _optionalChain([this, 'access', _690 => _690.qualityController, 'optionalAccess', _691 => _691.stop, 'call', _692 => _692()]);
10155
+ _optionalChain([this, 'access', _700 => _700.qualityController, 'optionalAccess', _701 => _701.stop, 'call', _702 => _702()]);
10051
10156
  await this.applyUplinkFrameRate(fps, token);
10052
10157
  const track = this.currentLocalTrack;
10053
- if (_optionalChain([track, 'optionalAccess', _693 => _693.videoFormat])) {
10158
+ if (_optionalChain([track, 'optionalAccess', _703 => _703.videoFormat])) {
10054
10159
  this.qualityStates.set(track, { baseline: track.videoFormat, level: 1 });
10055
- _optionalChain([this, 'access', _694 => _694.qualityHistory, 'optionalAccess', _695 => _695.write, 'call', _696 => _696("uplink", 1)]);
10160
+ _optionalChain([this, 'access', _704 => _704.qualityHistory, 'optionalAccess', _705 => _705.write, 'call', _706 => _706("uplink", 1)]);
10056
10161
  }
10057
10162
  });
10058
10163
  }
10059
10164
  /** 手动仅调帧接口,成功后的当前格式将成为新的自适应 L1 基准。 */
10060
10165
  async applyUplinkFrameRate(fps, token) {
10061
10166
  const track = this.currentUplinkTrack;
10062
- const previous = _optionalChain([track, 'optionalAccess', _697 => _697.videoFormat]);
10167
+ const previous = _optionalChain([track, 'optionalAccess', _707 => _707.videoFormat]);
10063
10168
  const state2 = this.currentState.connectionState;
10064
- if (!track || !previous || state2 !== "Connected" /* connected */ && state2 !== "Generating" /* generating */) {
10169
+ if (!track || !previous || state2 !== "Connected" /* connected */ && state2 !== "Starting" /* starting */ && state2 !== "Generating" /* generating */) {
10065
10170
  throw new XmaxError("INVALID_CONFIGURATION" /* invalidConfiguration */, "Frame rate updates require a connected local camera, external or file stream");
10066
10171
  }
10067
10172
  if (!Number.isFinite(fps) || fps <= 0) {
@@ -10157,7 +10262,7 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
10157
10262
  if (this.mediaController.source === "externalVideo") {
10158
10263
  throw new XmaxError("INVALID_CONFIGURATION" /* invalidConfiguration */, "External video capture is managed by the caller");
10159
10264
  }
10160
- _optionalChain([this, 'access', _698 => _698.qualityController, 'optionalAccess', _699 => _699.stop, 'call', _700 => _700()]);
10265
+ _optionalChain([this, 'access', _708 => _708.qualityController, 'optionalAccess', _709 => _709.stop, 'call', _710 => _710()]);
10161
10266
  const stream = await this.mediaController.switchCamera();
10162
10267
  token.ensureCurrent();
10163
10268
  return stream;
@@ -10182,7 +10287,7 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
10182
10287
  void 0,
10183
10288
  async (token) => {
10184
10289
  const connectionState = this.coordinator.currentState.connectionState;
10185
- if (connectionState === "Connecting" /* connecting */ || connectionState === "Connected" /* connected */ || connectionState === "Generating" /* generating */) {
10290
+ if (connectionState === "Connecting" /* connecting */ || connectionState === "Connected" /* connected */ || connectionState === "Starting" /* starting */ || connectionState === "Generating" /* generating */) {
10186
10291
  throw new XmaxError(
10187
10292
  "INVALID_CONFIGURATION" /* invalidConfiguration */,
10188
10293
  "A realtime connection is already active"
@@ -10197,6 +10302,7 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
10197
10302
  *
10198
10303
  * 尚未连接时先建立实时连接;已在生成时默认更新条件,restart 为 true 时重新开始生成。
10199
10304
  * 首次生成必须提供条件上下文,之后缺省时复用最近一次缓存的上下文。
10305
+ * 返回后请将远端流挂载到 SDK 视频视图;首帧及渲染预热完成后状态才进入 generating。
10200
10306
  *
10201
10307
  * @param options.localStream 由当前 Manager 创建的摄像头、外部视频轨、本地文件或网络视频源。
10202
10308
  * @param options.context 本次生成使用的条件上下文;缺省时复用缓存。
@@ -10204,7 +10310,7 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
10204
10310
  * @param options.faceRestoration 是否启用原始人脸回贴,默认 false;正在生成时通过 change_condition 更新。
10205
10311
  * @param options.extraParams 合并到本次 start 或 change_condition 的 params;不缓存,自动恢复时不携带,不允许覆盖 SDK 字段。
10206
10312
  * @param options.record 是否录制远端原始音视频约 20 秒,默认 false;仅 start 有效,条件更新时忽略,重启录制暂不支持。
10207
- * @returns 承载远端生成画面的媒体流。
10313
+ * @returns 可挂载的远端媒体流,不等待渲染预热;展示就绪以 generating 状态为准。
10208
10314
  * @throws extraParams 包含非 JSON 数据、循环引用或 SDK 保留字段时,在连接或更新前抛出 invalidConfiguration。
10209
10315
  * @throws 本地流不属于当前 Manager、缺少可用的条件上下文、信令发送
10210
10316
  * 或生成确认失败时抛出错误;失败时自动停止生成任务并释放连接资源。
@@ -10230,12 +10336,12 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
10230
10336
  );
10231
10337
  }
10232
10338
  const currentState = this.coordinator.currentState;
10233
- const isGenerating = currentState.connectionState === "Generating" /* generating */;
10234
- if (isGenerating && options.restart !== true) {
10339
+ const hasGeneration = currentState.connectionState === "Generating" /* generating */ || currentState.connectionState === "Starting" /* starting */ && !!currentState.taskID;
10340
+ if (hasGeneration && options.restart !== true) {
10235
10341
  await this.generationManager.update(currentState.taskID, {
10236
10342
  videoFormat,
10237
10343
  context: options.context,
10238
- targetSize: this.downlinkQualityController ? _optionalChain([this, 'access', _701 => _701.downlinkQualityState, 'optionalAccess', _702 => _702.size]) : void 0,
10344
+ targetSize: this.downlinkQualityController ? _optionalChain([this, 'access', _711 => _711.downlinkQualityState, 'optionalAccess', _712 => _712.size]) : void 0,
10239
10345
  faceRestoration: _nullishCoalesce(options.faceRestoration, () => ( false)),
10240
10346
  extraParams
10241
10347
  });
@@ -10248,7 +10354,7 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
10248
10354
  throw new XmaxError("INVALID_CONFIGURATION" /* invalidConfiguration */, "Create a network video stream before using a reference video");
10249
10355
  }
10250
10356
  const context = referenceVideo ? new RealtimeContext({ ...supplied, referenceVideo }) : supplied;
10251
- if (currentState.connectionState !== "Connected" /* connected */ && !isGenerating) {
10357
+ if (currentState.connectionState !== "Connected" /* connected */ && !hasGeneration) {
10252
10358
  await this.performConnect(options.localStream, token);
10253
10359
  }
10254
10360
  return this.performStartGeneration(
@@ -10300,7 +10406,11 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
10300
10406
  if (this.qualityStates.has(track)) return void 0;
10301
10407
  const baseline = track.videoFormat;
10302
10408
  if (!baseline) return void 0;
10303
- return { baseline, level: this.qualityHistory.read("uplink") };
10409
+ let level = this.qualityHistory.read("uplink");
10410
+ while (level > 1 && !supportsQualityEncoding(this.uplinkQualityFormat(baseline, level))) {
10411
+ level--;
10412
+ }
10413
+ return { baseline, level };
10304
10414
  }
10305
10415
  /**
10306
10416
  * 建立实时连接:创建会话、进房发布、启动心跳并准备远端渲染轨道。
@@ -10348,7 +10458,7 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
10348
10458
  _nullishCoalesce(this.generationVideoFormat, () => ( (this.generationVideoFormat = initialQuality.baseline)));
10349
10459
  localTrack.updateVideoFormat(initialFormat);
10350
10460
  this.qualityStates.set(localTrack, initialQuality);
10351
- _optionalChain([this, 'access', _703 => _703.qualityHistory, 'optionalAccess', _704 => _704.write, 'call', _705 => _705("uplink", initialQuality.level)]);
10461
+ _optionalChain([this, 'access', _713 => _713.qualityHistory, 'optionalAccess', _714 => _714.write, 'call', _715 => _715("uplink", initialQuality.level)]);
10352
10462
  },
10353
10463
  model: this.options.model,
10354
10464
  includeLocalAudio: this.mediaController.hasAudio,
@@ -10386,16 +10496,30 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
10386
10496
  */
10387
10497
  async performStartGeneration(token, videoFormat, context, restart, faceRestoration, extraParams, record2 = false) {
10388
10498
  token.setFailureScope(0 /* connection */);
10499
+ const sessionID = this.connectionManager.currentSessionID;
10500
+ if (!sessionID) {
10501
+ throw new XmaxError("SESSION_ERROR" /* sessionError */, "Realtime session is unavailable");
10502
+ }
10503
+ await this.coordinator.commit(
10504
+ new RealtimeState({ connectionState: "Starting" /* starting */, sessionID }),
10505
+ token
10506
+ );
10507
+ token.ensureCurrent();
10389
10508
  if (restart) {
10390
10509
  void this.recordingController.stop("restarted" /* restarted */);
10391
10510
  }
10392
- const initialLevel = _nullishCoalesce(_optionalChain([this, 'access', _706 => _706.qualityHistory, 'optionalAccess', _707 => _707.read, 'call', _708 => _708("downlink")]), () => ( 1));
10511
+ const initialLevel = _nullishCoalesce(_optionalChain([this, 'access', _716 => _716.qualityHistory, 'optionalAccess', _717 => _717.read, 'call', _718 => _718("downlink")]), () => ( 1));
10393
10512
  const targetSize = this.downlinkQualityController ? qualityVideoSize(videoFormat, initialLevel) : void 0;
10394
10513
  await this.prepareFrameInterpolation(_nullishCoalesce(targetSize, () => ( videoFormat)), token);
10395
10514
  const completeFirstFrame = this.launchTimer.startFirstFrame();
10515
+ this.remoteRenderWarmupCompletedHandler = void 0;
10396
10516
  this.remoteFrameDisplayHandler = () => {
10397
- if (!token.signal.aborted) completeFirstFrame();
10517
+ if (!token.signal.aborted) {
10518
+ completeFirstFrame();
10519
+ _nullishCoalesce(this.remoteRenderWarmupCompletedHandler, () => ( (this.remoteRenderWarmupCompletedHandler = this.launchTimer.startRenderWarmup())));
10520
+ }
10398
10521
  };
10522
+ this.renderController.prepareForGeneration();
10399
10523
  const taskID = await this.generationManager.start({
10400
10524
  videoFormat,
10401
10525
  signal: token.signal,
@@ -10417,7 +10541,7 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
10417
10541
  }
10418
10542
  } : void 0,
10419
10543
  targetSize,
10420
- mirror: this.mediaController.source === "camera" && _optionalChain([this, 'access', _709 => _709.currentLocalTrack, 'optionalAccess', _710 => _710.position]) === "front" /* front */,
10544
+ mirror: this.mediaController.source === "camera" && _optionalChain([this, 'access', _719 => _719.currentLocalTrack, 'optionalAccess', _720 => _720.position]) === "front" /* front */,
10421
10545
  faceRestoration,
10422
10546
  onStartSent: this.mediaController.source === "localVideo" ? async () => {
10423
10547
  token.ensureCurrent();
@@ -10426,11 +10550,8 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
10426
10550
  } : void 0,
10427
10551
  extraParams
10428
10552
  });
10429
- const sessionID = this.connectionManager.currentSessionID;
10430
- if (!sessionID) {
10431
- throw new XmaxError("SESSION_ERROR" /* sessionError */, "Realtime session is unavailable");
10432
- }
10433
- _optionalChain([this, 'access', _711 => _711.downlinkQualityController, 'optionalAccess', _712 => _712.stop, 'call', _713 => _713()]);
10553
+ token.ensureCurrent();
10554
+ _optionalChain([this, 'access', _721 => _721.downlinkQualityController, 'optionalAccess', _722 => _722.stop, 'call', _723 => _723()]);
10434
10555
  this.downlinkQualityState = {
10435
10556
  taskID,
10436
10557
  baseline: videoFormat,
@@ -10438,21 +10559,38 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
10438
10559
  size: _nullishCoalesce(targetSize, () => ( { width: videoFormat.width, height: videoFormat.height }))
10439
10560
  };
10440
10561
  await this.coordinator.commit(
10441
- new RealtimeState({ connectionState: "Generating" /* generating */, sessionID, taskID }),
10562
+ new RealtimeState({ connectionState: "Starting" /* starting */, sessionID, taskID }),
10442
10563
  token
10443
10564
  );
10444
- _optionalChain([this, 'access', _714 => _714.qualityHistory, 'optionalAccess', _715 => _715.write, 'call', _716 => _716("downlink", initialLevel)]);
10565
+ token.ensureCurrent();
10566
+ await this.completeGenerationPresentation();
10567
+ token.ensureCurrent();
10568
+ _optionalChain([this, 'access', _724 => _724.qualityHistory, 'optionalAccess', _725 => _725.write, 'call', _726 => _726("downlink", initialLevel)]);
10445
10569
  const onFinish = this.mediaController.onNetworkVideoFinish;
10446
10570
  if (this.mediaController.source === "networkVideo") {
10447
10571
  this.streamController.activateNetworkVideoCompletion(() => {
10448
- if (this.currentState.taskID === taskID && this.currentState.connectionState === "Generating" /* generating */) {
10572
+ if (this.currentState.taskID === taskID && (this.currentState.connectionState === "Starting" /* starting */ || this.currentState.connectionState === "Generating" /* generating */)) {
10449
10573
  void this.recordingController.stop("generationEnded" /* generationEnded */);
10450
- return _optionalChain([onFinish, 'optionalCall', _717 => _717()]);
10574
+ return _optionalChain([onFinish, 'optionalCall', _727 => _727()]);
10451
10575
  }
10452
10576
  });
10453
10577
  }
10454
10578
  return this.connectionManager.makeRemoteStream();
10455
10579
  }
10580
+ /**
10581
+ * 任务启动成功且当前视图完成预热后提交展示就绪状态,不阻塞远端流返回。
10582
+ */
10583
+ async completeGenerationPresentation() {
10584
+ const { sessionID, taskID } = this.currentState;
10585
+ if (!sessionID || !taskID) {
10586
+ return;
10587
+ }
10588
+ await this.coordinator.remotePreviewDidBecomeReady(
10589
+ sessionID,
10590
+ taskID,
10591
+ () => this.renderController.isReady
10592
+ );
10593
+ }
10456
10594
  /**
10457
10595
  * 心跳失败或会话失效:结束当前连接生命周期并通过最终状态给出原因。
10458
10596
  */
@@ -10471,6 +10609,7 @@ var XmaxRealtimeManager = class _XmaxRealtimeManager {
10471
10609
  this.mediaController.pauseLocalVideo();
10472
10610
  this.launchTimer.cancel();
10473
10611
  this.remoteFrameDisplayHandler = void 0;
10612
+ this.remoteRenderWarmupCompletedHandler = void 0;
10474
10613
  this.clearVideoStatistics();
10475
10614
  this.interpolationSize = void 0;
10476
10615
  this.applyRemoteInterpolation();
@@ -10586,7 +10725,7 @@ var NonRealtimeTaskService = class {
10586
10725
  * 提交一次任务;请求结果不确定时也不自动重试,以免重复扣费。
10587
10726
  */
10588
10727
  async submitTask(options) {
10589
- const model = _optionalChain([options, 'access', _718 => _718.model, 'optionalAccess', _719 => _719.trim, 'call', _720 => _720()]);
10728
+ const model = _optionalChain([options, 'access', _728 => _728.model, 'optionalAccess', _729 => _729.trim, 'call', _730 => _730()]);
10590
10729
  if (!model) {
10591
10730
  invalid("Model must not be empty");
10592
10731
  }
@@ -10726,7 +10865,7 @@ function parseTask(value) {
10726
10865
  const uid = field(data, "uid", "string");
10727
10866
  const status = field(data, "status", "string");
10728
10867
  const quality = field(data, "quality", "string");
10729
- if (!_optionalChain([uid, 'optionalAccess', _721 => _721.trim, 'call', _722 => _722()]) || !Object.values(NonRealtimeTaskStatus).includes(status)) {
10868
+ if (!_optionalChain([uid, 'optionalAccess', _731 => _731.trim, 'call', _732 => _732()]) || !Object.values(NonRealtimeTaskStatus).includes(status)) {
10730
10869
  malformed("Missing task UID or invalid status");
10731
10870
  }
10732
10871
  if (quality !== void 0 && !Object.values(NonRealtimeQuality).includes(quality)) {
@@ -10808,7 +10947,7 @@ var XmaxNonRealtimeManager = class {
10808
10947
  * 取得任务快照,不把服务端失败终态当成查询失败。
10809
10948
  */
10810
10949
  getTask(taskUid, options) {
10811
- return this.tasks.getTask(taskUid, _optionalChain([options, 'optionalAccess', _723 => _723.signal]));
10950
+ return this.tasks.getTask(taskUid, _optionalChain([options, 'optionalAccess', _733 => _733.signal]));
10812
10951
  }
10813
10952
  /**
10814
10953
  * 批量取得任务快照。
@@ -10842,8 +10981,8 @@ var XmaxNonRealtimeManager = class {
10842
10981
  const controller = new AbortController();
10843
10982
  let timedOut = false;
10844
10983
  const abort = () => controller.abort();
10845
- _optionalChain([options, 'access', _724 => _724.signal, 'optionalAccess', _725 => _725.addEventListener, 'call', _726 => _726("abort", abort, { once: true })]);
10846
- if (_optionalChain([options, 'access', _727 => _727.signal, 'optionalAccess', _728 => _728.aborted])) {
10984
+ _optionalChain([options, 'access', _734 => _734.signal, 'optionalAccess', _735 => _735.addEventListener, 'call', _736 => _736("abort", abort, { once: true })]);
10985
+ if (_optionalChain([options, 'access', _737 => _737.signal, 'optionalAccess', _738 => _738.aborted])) {
10847
10986
  abort();
10848
10987
  }
10849
10988
  const timer = options.timeoutMs === void 0 ? void 0 : setTimeout(() => {
@@ -10869,7 +11008,7 @@ var XmaxNonRealtimeManager = class {
10869
11008
  ensureActive(controller.signal);
10870
11009
  failures = 0;
10871
11010
  try {
10872
- void Promise.resolve(_optionalChain([options, 'access', _729 => _729.onTaskUpdated, 'optionalCall', _730 => _730(task)])).catch(() => {
11011
+ void Promise.resolve(_optionalChain([options, 'access', _739 => _739.onTaskUpdated, 'optionalCall', _740 => _740(task)])).catch(() => {
10873
11012
  });
10874
11013
  } catch (e35) {
10875
11014
  }
@@ -10899,7 +11038,7 @@ var XmaxNonRealtimeManager = class {
10899
11038
  throw error;
10900
11039
  } finally {
10901
11040
  clearTimeout(timer);
10902
- _optionalChain([options, 'access', _731 => _731.signal, 'optionalAccess', _732 => _732.removeEventListener, 'call', _733 => _733("abort", abort)]);
11041
+ _optionalChain([options, 'access', _741 => _741.signal, 'optionalAccess', _742 => _742.removeEventListener, 'call', _743 => _743("abort", abort)]);
10903
11042
  }
10904
11043
  }
10905
11044
  };
@@ -10943,7 +11082,7 @@ function isTransient(error) {
10943
11082
  }
10944
11083
  function isResultAvailable(task) {
10945
11084
  const result = task.result;
10946
- if (!_optionalChain([result, 'optionalAccess', _734 => _734.url, 'optionalAccess', _735 => _735.trim, 'call', _736 => _736()]) || _optionalChain([result, 'access', _737 => _737.uploadError, 'optionalAccess', _738 => _738.trim, 'call', _739 => _739()]) || result.uploadStatus !== void 0 && result.uploadStatus !== "success") {
11085
+ if (!_optionalChain([result, 'optionalAccess', _744 => _744.url, 'optionalAccess', _745 => _745.trim, 'call', _746 => _746()]) || _optionalChain([result, 'access', _747 => _747.uploadError, 'optionalAccess', _748 => _748.trim, 'call', _749 => _749()]) || result.uploadStatus !== void 0 && result.uploadStatus !== "success") {
10947
11086
  return false;
10948
11087
  }
10949
11088
  try {
@@ -11013,9 +11152,9 @@ var XmaxClient = class {
11013
11152
  /**
11014
11153
  * 创建媒体处理与能力查询 Service。
11015
11154
  *
11016
- * @param model 媒体输入规则使用的模型,默认为 `x2.0-trtc`。
11155
+ * @param model 媒体输入规则使用的模型,默认为 `x2.1.1-preview-cn`。
11017
11156
  */
11018
- createMediaService(model = "x2.0-trtc" /* x2_0_trtc */) {
11157
+ createMediaService(model = "x2.1.1-preview-cn" /* x2_1_1_preview_cn */) {
11019
11158
  return new MediaService(model);
11020
11159
  }
11021
11160
  /**
@@ -11033,7 +11172,7 @@ var RealtimeConfiguration = class {
11033
11172
  /**
11034
11173
  * 创建实时业务配置。
11035
11174
  *
11036
- * @param init.model 实时生成业务使用的模型;未知名称原样透传,默认配置同 x2.1-preview。
11175
+ * @param init.model 实时生成业务使用的模型;未知名称原样透传,默认配置同 x2.1.1-preview-cn。
11037
11176
  * @param init.provider RTC 提供方,默认按模型定义选择;必须受模型支持,创建后不可切换。
11038
11177
  * @param init.isFrameInterpolationEnabled 是否默认开启远端生成画面的插帧,默认 false。
11039
11178
  */
@@ -11200,12 +11339,81 @@ var RenderStatisticsController = class {
11200
11339
  */
11201
11340
  notify(statistics) {
11202
11341
  try {
11203
- _optionalChain([this, 'access', _740 => _740.handler, 'optionalCall', _741 => _741(statistics)]);
11342
+ _optionalChain([this, 'access', _750 => _750.handler, 'optionalCall', _751 => _751(statistics)]);
11204
11343
  } catch (e37) {
11205
11344
  }
11206
11345
  }
11207
11346
  };
11208
11347
 
11348
+ // src/Render/Video/RenderWarmupController.ts
11349
+ var RenderWarmupController = class {
11350
+ /**
11351
+ * 创建视图预热控制器。
11352
+ */
11353
+ constructor(element) {
11354
+ _chunk4DE2IREAcjs.__publicField.call(void 0, this, "element", element);
11355
+ /**
11356
+ * 预热配置与生命周期
11357
+ */
11358
+ _chunk4DE2IREAcjs.__publicField.call(void 0, this, "durationMs");
11359
+ _chunk4DE2IREAcjs.__publicField.call(void 0, this, "timer");
11360
+ _chunk4DE2IREAcjs.__publicField.call(void 0, this, "started", false);
11361
+ _chunk4DE2IREAcjs.__publicField.call(void 0, this, "completed", false);
11362
+ }
11363
+ /**
11364
+ * 是否仍在等待首帧或预热完成;期间不允许对不可见画面交互。
11365
+ */
11366
+ get isPending() {
11367
+ return this.durationMs !== void 0 && !this.completed;
11368
+ }
11369
+ /**
11370
+ * 为新媒体隐藏画面;undefined 表示直接显示,不启用预热。
11371
+ */
11372
+ prepare(durationMs) {
11373
+ this.reset();
11374
+ this.durationMs = durationMs;
11375
+ if (durationMs === void 0) {
11376
+ return;
11377
+ }
11378
+ this.element.style.transition = "none";
11379
+ this.element.style.opacity = "0";
11380
+ this.element.style.pointerEvents = "none";
11381
+ }
11382
+ /**
11383
+ * 首帧就绪后开始一次性预热,结束时通知并渐入画面。
11384
+ */
11385
+ start(onCompleted) {
11386
+ if (this.durationMs === void 0 || this.started) {
11387
+ return;
11388
+ }
11389
+ this.started = true;
11390
+ this.timer = setTimeout(() => {
11391
+ this.timer = void 0;
11392
+ this.completed = true;
11393
+ const reduceMotion = _optionalChain([globalThis, 'access', _752 => _752.matchMedia, 'optionalCall', _753 => _753("(prefers-reduced-motion: reduce)"), 'access', _754 => _754.matches]);
11394
+ this.element.style.transition = reduceMotion ? "none" : "opacity 300ms ease-in-out";
11395
+ this.element.style.opacity = "1";
11396
+ this.element.style.pointerEvents = "";
11397
+ onCompleted();
11398
+ }, this.durationMs);
11399
+ }
11400
+ /**
11401
+ * 换流或卸载时取消预热和渐入,防止旧任务显示新画面。
11402
+ */
11403
+ reset() {
11404
+ clearTimeout(this.timer);
11405
+ this.timer = void 0;
11406
+ this.started = false;
11407
+ this.completed = false;
11408
+ if (this.durationMs !== void 0) {
11409
+ this.element.style.transition = "";
11410
+ this.element.style.opacity = "";
11411
+ this.element.style.pointerEvents = "";
11412
+ }
11413
+ this.durationMs = void 0;
11414
+ }
11415
+ };
11416
+
11209
11417
  // ../../node_modules/.pnpm/framegen@1.4.0/node_modules/framegen/rt.js
11210
11418
  var WG = 8;
11211
11419
  var texBgSeq = 0;
@@ -13328,11 +13536,11 @@ var _FrameInterpolationManager = class _FrameInterpolationManager {
13328
13536
  if (this.destroyed) return;
13329
13537
  this.destroyed = true;
13330
13538
  this.device.removeEventListener("uncapturederror", this.onGPUError);
13331
- _optionalChain([this, 'access', _742 => _742.runtime, 'optionalAccess', _743 => _743.destroy, 'call', _744 => _744()]);
13539
+ _optionalChain([this, 'access', _755 => _755.runtime, 'optionalAccess', _756 => _756.destroy, 'call', _757 => _757()]);
13332
13540
  this.runtime = void 0;
13333
13541
  this.textures.forEach((texture) => texture.destroy());
13334
13542
  this.textures = [];
13335
- _optionalChain([this, 'access', _745 => _745.context, 'optionalAccess', _746 => _746.unconfigure, 'call', _747 => _747()]);
13543
+ _optionalChain([this, 'access', _758 => _758.context, 'optionalAccess', _759 => _759.unconfigure, 'call', _760 => _760()]);
13336
13544
  this.device.destroy();
13337
13545
  }
13338
13546
  };
@@ -13509,7 +13717,7 @@ var RemoteVideoFramePipeline = class {
13509
13717
  this.displayedTime = frame.time;
13510
13718
  this.canvas.style.visibility = "visible";
13511
13719
  this.setActive(true);
13512
- _optionalChain([this, 'access', _748 => _748.options, 'access', _749 => _749.onFramePresented, 'optionalCall', _750 => _750(_nullishCoalesce(frame.interpolated, () => ( false)))]);
13720
+ _optionalChain([this, 'access', _761 => _761.options, 'access', _762 => _762.onFramePresented, 'optionalCall', _763 => _763(_nullishCoalesce(frame.interpolated, () => ( false)))]);
13513
13721
  }
13514
13722
  async initialize() {
13515
13723
  this.loading = true;
@@ -13537,7 +13745,7 @@ var RemoteVideoFramePipeline = class {
13537
13745
  setActive(active) {
13538
13746
  if (active === this.active) return;
13539
13747
  this.active = active;
13540
- _optionalChain([this, 'access', _751 => _751.options, 'access', _752 => _752.onActiveChange, 'optionalCall', _753 => _753(active)]);
13748
+ _optionalChain([this, 'access', _764 => _764.options, 'access', _765 => _765.onActiveChange, 'optionalCall', _766 => _766(active)]);
13541
13749
  }
13542
13750
  resetTimeline(preserveGpuTimeout = false) {
13543
13751
  this.generation++;
@@ -13570,7 +13778,7 @@ var RemoteVideoFramePipeline = class {
13570
13778
  this.callback = void 0;
13571
13779
  clearTimeout(this.initializationTimer);
13572
13780
  this.resetTimeline();
13573
- _optionalChain([this, 'access', _754 => _754.processor, 'optionalAccess', _755 => _755.destroy, 'call', _756 => _756()]);
13781
+ _optionalChain([this, 'access', _767 => _767.processor, 'optionalAccess', _768 => _768.destroy, 'call', _769 => _769()]);
13574
13782
  this.processor = void 0;
13575
13783
  }
13576
13784
  };
@@ -13651,9 +13859,9 @@ var TrajectoryOverlayView = class {
13651
13859
  * 卸载时取消采样、动画、捕获和事件监听;Renderer 可以再次挂载。
13652
13860
  */
13653
13861
  dispose() {
13654
- _optionalChain([this, 'access', _757 => _757.binding, 'optionalAccess', _758 => _758.detach, 'call', _759 => _759(this)]);
13862
+ _optionalChain([this, 'access', _770 => _770.binding, 'optionalAccess', _771 => _771.detach, 'call', _772 => _772(this)]);
13655
13863
  this.cancel();
13656
- _optionalChain([this, 'access', _760 => _760.resizeObserver, 'optionalAccess', _761 => _761.disconnect, 'call', _762 => _762()]);
13864
+ _optionalChain([this, 'access', _773 => _773.resizeObserver, 'optionalAccess', _774 => _774.disconnect, 'call', _775 => _775()]);
13657
13865
  document.removeEventListener("visibilitychange", this.visibilityChanged);
13658
13866
  this.element.removeEventListener("pointerdown", this.onDown);
13659
13867
  this.element.removeEventListener("pointermove", this.onMove);
@@ -13805,7 +14013,7 @@ var TrajectoryOverlayView = class {
13805
14013
  return;
13806
14014
  }
13807
14015
  this.lastSample = now;
13808
- _optionalChain([this, 'access', _763 => _763.binding, 'optionalAccess', _764 => _764.submit, 'call', _765 => _765({
14016
+ _optionalChain([this, 'access', _776 => _776.binding, 'optionalAccess', _777 => _777.submit, 'call', _778 => _778({
13809
14017
  points: [...this.pointers.values()].map((point) => point.location),
13810
14018
  viewportSize: this.viewport,
13811
14019
  contentMode: this.contentMode,
@@ -13919,7 +14127,7 @@ var DefaultTrajectoryEffectRenderer = class {
13919
14127
  this.hasTrailPixels = false;
13920
14128
  this.idleFadeFrames = 0;
13921
14129
  for (const canvas of [this.trailCanvas, this.effectCanvas]) {
13922
- _optionalChain([canvas, 'access', _766 => _766.getContext, 'call', _767 => _767("2d"), 'optionalAccess', _768 => _768.clearRect, 'call', _769 => _769(0, 0, canvas.width, canvas.height)]);
14130
+ _optionalChain([canvas, 'access', _779 => _779.getContext, 'call', _780 => _780("2d"), 'optionalAccess', _781 => _781.clearRect, 'call', _782 => _782(0, 0, canvas.width, canvas.height)]);
13923
14131
  }
13924
14132
  }
13925
14133
  /**
@@ -14120,6 +14328,10 @@ var XmaxVideoView = class {
14120
14328
  * 呈现统计组件
14121
14329
  */
14122
14330
  _chunk4DE2IREAcjs.__publicField.call(void 0, this, "statisticsController");
14331
+ /**
14332
+ * 远端画面预热与渐入组件
14333
+ */
14334
+ _chunk4DE2IREAcjs.__publicField.call(void 0, this, "warmupController");
14123
14335
  /**
14124
14336
  * DOM 挂载状态
14125
14337
  */
@@ -14130,12 +14342,13 @@ var XmaxVideoView = class {
14130
14342
  _chunk4DE2IREAcjs.__publicField.call(void 0, this, "interactionEnabled", true);
14131
14343
  _chunk4DE2IREAcjs.__publicField.call(void 0, this, "customTrajectoryRenderer");
14132
14344
  _chunk4DE2IREAcjs.__publicField.call(void 0, this, "trajectoryOverlay");
14133
- this.contentMode = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _770 => _770.videoContentMode]), () => ( "fill")) /* fill */;
14134
- this.interactionEnabled = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _771 => _771.isInteractionEnabled]), () => ( true));
14345
+ this.contentMode = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _783 => _783.videoContentMode]), () => ( "fill")) /* fill */;
14346
+ this.interactionEnabled = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _784 => _784.isInteractionEnabled]), () => ( true));
14135
14347
  this.element = document.createElement("div");
14136
14348
  this.element.style.position = "relative";
14137
14349
  this.element.style.overflow = "hidden";
14138
14350
  this.element.style.backgroundColor = "black";
14351
+ this.warmupController = new RenderWarmupController(this.element);
14139
14352
  this.videoElement = document.createElement("video");
14140
14353
  this.statisticsController = new RenderStatisticsController(this.videoElement);
14141
14354
  this.videoElement.autoplay = true;
@@ -14159,7 +14372,7 @@ var XmaxVideoView = class {
14159
14372
  */
14160
14373
  set isInteractionEnabled(enabled) {
14161
14374
  this.interactionEnabled = enabled;
14162
- _optionalChain([this, 'access', _772 => _772.trajectoryOverlay, 'optionalAccess', _773 => _773.configure, 'call', _774 => _774(this.contentMode, this.mirrored, enabled)]);
14375
+ _optionalChain([this, 'access', _785 => _785.trajectoryOverlay, 'optionalAccess', _786 => _786.configure, 'call', _787 => _787(this.contentMode, this.mirrored, enabled && !this.warmupController.isPending)]);
14163
14376
  }
14164
14377
  /**
14165
14378
  * 自定义轨迹视觉效果;undefined 表示使用 SDK 默认效果。
@@ -14260,6 +14473,7 @@ var XmaxVideoView = class {
14260
14473
  this.startInterpolation();
14261
14474
  this.startRenderStatistics();
14262
14475
  if ((this.videoElement.srcObject || this.videoElement.getAttribute("src")) && !this.hasNotifiedFrameDisplay) {
14476
+ this.prepareRenderWarmup();
14263
14477
  this.armFrameDisplayNotification();
14264
14478
  }
14265
14479
  }
@@ -14272,6 +14486,8 @@ var XmaxVideoView = class {
14272
14486
  this.statisticsController.stop();
14273
14487
  this.stopInterpolation();
14274
14488
  this.cancelFrameDisplayNotification();
14489
+ this.warmupController.reset();
14490
+ this.hasNotifiedFrameDisplay = false;
14275
14491
  if (this.videoElement.getAttribute("src")) {
14276
14492
  this.videoElement.pause();
14277
14493
  }
@@ -14281,6 +14497,7 @@ var XmaxVideoView = class {
14281
14497
  * 设置网络视频预览;静音播放一次,不捕获或发布媒体轨道。 @internal
14282
14498
  */
14283
14499
  setVideoURL(url) {
14500
+ this.warmupController.reset();
14284
14501
  this.statisticsController.stop();
14285
14502
  this.stopInterpolation();
14286
14503
  this.cancelFrameDisplayNotification();
@@ -14309,13 +14526,27 @@ var XmaxVideoView = class {
14309
14526
  this.cancelFrameDisplayNotification();
14310
14527
  this.hasNotifiedFrameDisplay = false;
14311
14528
  this.videoElement.srcObject = stream;
14529
+ this.warmupController.reset();
14312
14530
  if (stream) {
14313
14531
  this.videoElement.autoplay = true;
14532
+ this.prepareRenderWarmup();
14314
14533
  this.armFrameDisplayNotification();
14315
14534
  this.startInterpolation();
14316
14535
  this.startRenderStatistics();
14317
14536
  }
14318
14537
  }
14538
+ /**
14539
+ * 新生成任务重新等待首帧和预热,不替换正在播放的媒体流。 @internal
14540
+ */
14541
+ prepareForGeneration() {
14542
+ this.cancelFrameDisplayNotification();
14543
+ this.warmupController.reset();
14544
+ this.hasNotifiedFrameDisplay = false;
14545
+ this.prepareRenderWarmup();
14546
+ if (!this.detached && this.videoElement.srcObject) {
14547
+ this.armFrameDisplayNotification();
14548
+ }
14549
+ }
14319
14550
  /**
14320
14551
  * 由远端绑定注入配置;本地预览不走此管线。 @internal
14321
14552
  */
@@ -14348,11 +14579,11 @@ var XmaxVideoView = class {
14348
14579
  ...options,
14349
14580
  onActiveChange: (active) => {
14350
14581
  this.statisticsController.setInterpolationActive(active);
14351
- _optionalChain([options, 'access', _775 => _775.onActiveChange, 'optionalCall', _776 => _776(active)]);
14582
+ _optionalChain([options, 'access', _788 => _788.onActiveChange, 'optionalCall', _789 => _789(active)]);
14352
14583
  },
14353
14584
  onFramePresented: (interpolated) => {
14354
14585
  this.statisticsController.recordCanvasFrame(interpolated);
14355
- _optionalChain([options, 'access', _777 => _777.onFramePresented, 'optionalCall', _778 => _778(interpolated)]);
14586
+ _optionalChain([options, 'access', _790 => _790.onFramePresented, 'optionalCall', _791 => _791(interpolated)]);
14356
14587
  }
14357
14588
  });
14358
14589
  this.interpolationPipeline = pipeline;
@@ -14364,8 +14595,8 @@ var XmaxVideoView = class {
14364
14595
  stopInterpolation() {
14365
14596
  const pipeline = this.interpolationPipeline;
14366
14597
  this.interpolationPipeline = void 0;
14367
- _optionalChain([pipeline, 'optionalAccess', _779 => _779.stop, 'call', _780 => _780()]);
14368
- _optionalChain([this, 'access', _781 => _781.interpolationCanvas, 'optionalAccess', _782 => _782.remove, 'call', _783 => _783()]);
14598
+ _optionalChain([pipeline, 'optionalAccess', _792 => _792.stop, 'call', _793 => _793()]);
14599
+ _optionalChain([this, 'access', _794 => _794.interpolationCanvas, 'optionalAccess', _795 => _795.remove, 'call', _796 => _796()]);
14369
14600
  this.interpolationCanvas = void 0;
14370
14601
  }
14371
14602
  /**
@@ -14390,11 +14621,12 @@ var XmaxVideoView = class {
14390
14621
  * 解绑当前轨道并清空渲染内容。
14391
14622
  */
14392
14623
  detachCurrentTrack() {
14624
+ this.warmupController.reset();
14393
14625
  this.disposeTrajectory();
14394
14626
  this.statisticsController.stop();
14395
14627
  const track = this.currentTrack;
14396
14628
  if (track) {
14397
- _optionalChain([VideoRenderRegistry, 'access', _784 => _784.binding, 'call', _785 => _785(track), 'optionalAccess', _786 => _786.detachHandler, 'call', _787 => _787(this)]);
14629
+ _optionalChain([VideoRenderRegistry, 'access', _797 => _797.binding, 'call', _798 => _798(track), 'optionalAccess', _799 => _799.detachHandler, 'call', _800 => _800(this)]);
14398
14630
  }
14399
14631
  this.setFrameInterpolation(void 0);
14400
14632
  this.cancelFrameDisplayNotification();
@@ -14417,8 +14649,15 @@ var XmaxVideoView = class {
14417
14649
  }
14418
14650
  this.hasNotifiedFrameDisplay = true;
14419
14651
  this.cancelFrameDisplayNotification();
14420
- _optionalChain([binding, 'optionalAccess', _788 => _788.frameDisplayHandler, 'optionalCall', _789 => _789()]);
14421
- _optionalChain([this, 'access', _790 => _790.frameDisplayHandler, 'optionalCall', _791 => _791()]);
14652
+ const notifiedSequence = this.frameNotificationSequence;
14653
+ _optionalChain([binding, 'optionalAccess', _801 => _801.frameDisplayHandler, 'optionalCall', _802 => _802()]);
14654
+ _optionalChain([this, 'access', _803 => _803.frameDisplayHandler, 'optionalCall', _804 => _804()]);
14655
+ if (notifiedSequence === this.frameNotificationSequence) {
14656
+ this.warmupController.start(() => {
14657
+ this.applyContentMode();
14658
+ _optionalChain([binding, 'optionalAccess', _805 => _805.renderWarmupCompletedHandler, 'optionalCall', _806 => _806()]);
14659
+ });
14660
+ }
14422
14661
  };
14423
14662
  if (typeof this.videoElement.requestVideoFrameCallback === "function") {
14424
14663
  this.videoFrameCallbackID = this.videoElement.requestVideoFrameCallback(notify);
@@ -14446,11 +14685,19 @@ var XmaxVideoView = class {
14446
14685
  this.onPlaying = void 0;
14447
14686
  }
14448
14687
  }
14688
+ /**
14689
+ * 仅远端渲染绑定启用预热,本地预览和普通媒体直接显示。
14690
+ */
14691
+ prepareRenderWarmup() {
14692
+ const binding = this.currentTrack ? VideoRenderRegistry.binding(this.currentTrack) : void 0;
14693
+ this.warmupController.prepare(_optionalChain([binding, 'optionalAccess', _807 => _807.renderWarmupMs]));
14694
+ this.applyContentMode();
14695
+ }
14449
14696
  /**
14450
14697
  * 应用显示模式与镜像样式。
14451
14698
  */
14452
14699
  applyContentMode() {
14453
- _optionalChain([this, 'access', _792 => _792.trajectoryOverlay, 'optionalAccess', _793 => _793.configure, 'call', _794 => _794(this.contentMode, this.mirrored, this.interactionEnabled)]);
14700
+ _optionalChain([this, 'access', _808 => _808.trajectoryOverlay, 'optionalAccess', _809 => _809.configure, 'call', _810 => _810(this.contentMode, this.mirrored, this.interactionEnabled && !this.warmupController.isPending)]);
14454
14701
  this.videoElement.style.objectFit = OBJECT_FIT[this.contentMode];
14455
14702
  this.videoElement.style.transform = this.mirrored ? "scaleX(-1)" : "";
14456
14703
  if (this.interpolationCanvas) {
@@ -14477,7 +14724,7 @@ var XmaxVideoView = class {
14477
14724
  }
14478
14725
  const overlay = new TrajectoryOverlayView(_nullishCoalesce(this.customTrajectoryRenderer, () => ( new DefaultTrajectoryEffectRenderer())));
14479
14726
  this.trajectoryOverlay = overlay;
14480
- overlay.configure(this.contentMode, this.mirrored, this.interactionEnabled);
14727
+ overlay.configure(this.contentMode, this.mirrored, this.interactionEnabled && !this.warmupController.isPending);
14481
14728
  this.element.appendChild(overlay.element);
14482
14729
  binding.attach(overlay);
14483
14730
  }
@@ -14485,7 +14732,7 @@ var XmaxVideoView = class {
14485
14732
  * 换轨或卸载时移除全部交互资源;再次挂载时重新建立绑定。
14486
14733
  */
14487
14734
  disposeTrajectory() {
14488
- _optionalChain([this, 'access', _795 => _795.trajectoryOverlay, 'optionalAccess', _796 => _796.dispose, 'call', _797 => _797()]);
14735
+ _optionalChain([this, 'access', _811 => _811.trajectoryOverlay, 'optionalAccess', _812 => _812.dispose, 'call', _813 => _813()]);
14489
14736
  this.trajectoryOverlay = void 0;
14490
14737
  }
14491
14738
  };
@@ -14519,15 +14766,15 @@ var XmaxRealtimeVideoView = class {
14519
14766
  _chunk4DE2IREAcjs.__publicField.call(void 0, this, "currentLocalTrack");
14520
14767
  _chunk4DE2IREAcjs.__publicField.call(void 0, this, "currentRemoteTrack");
14521
14768
  _chunk4DE2IREAcjs.__publicField.call(void 0, this, "contentMode");
14522
- this.contentMode = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _798 => _798.videoContentMode]), () => ( "fill")) /* fill */;
14523
- this.currentLocalTrack = _optionalChain([options, 'optionalAccess', _799 => _799.localTrack]);
14524
- this.currentRemoteTrack = _optionalChain([options, 'optionalAccess', _800 => _800.remoteTrack]);
14769
+ this.contentMode = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _814 => _814.videoContentMode]), () => ( "fill")) /* fill */;
14770
+ this.currentLocalTrack = _optionalChain([options, 'optionalAccess', _815 => _815.localTrack]);
14771
+ this.currentRemoteTrack = _optionalChain([options, 'optionalAccess', _816 => _816.remoteTrack]);
14525
14772
  this.element = document.createElement("div");
14526
14773
  this.element.style.position = "relative";
14527
14774
  this.element.style.overflow = "hidden";
14528
14775
  this.element.style.backgroundColor = "black";
14529
14776
  this.localVideoView = new XmaxVideoView({ videoContentMode: this.contentMode, isInteractionEnabled: false });
14530
- this.remoteVideoView = new XmaxVideoView({ videoContentMode: this.contentMode, isInteractionEnabled: _optionalChain([options, 'optionalAccess', _801 => _801.isInteractionEnabled]) });
14777
+ this.remoteVideoView = new XmaxVideoView({ videoContentMode: this.contentMode, isInteractionEnabled: _optionalChain([options, 'optionalAccess', _817 => _817.isInteractionEnabled]) });
14531
14778
  for (const view of [this.localVideoView, this.remoteVideoView]) {
14532
14779
  view.element.style.position = "absolute";
14533
14780
  view.element.style.inset = "0";
@@ -14578,7 +14825,7 @@ var XmaxRealtimeVideoView = class {
14578
14825
  }
14579
14826
  /**
14580
14827
  * 当前显示的远端生成视频轨道。
14581
- * 设置新轨道后等待首帧提交显示再渐入;置空时立即恢复本地预览。
14828
+ * SDK 远端轨道等待首帧及预热后渐入;置空时立即恢复本地预览。
14582
14829
  */
14583
14830
  get remoteTrack() {
14584
14831
  return this.currentRemoteTrack;
@@ -14639,18 +14886,23 @@ var XmaxRealtimeVideoView = class {
14639
14886
  this.element.remove();
14640
14887
  }
14641
14888
  /**
14642
- * 更新远端轨道绑定:重置渐入状态,等待首帧提交后再显示远端画面。
14889
+ * 更新远端轨道绑定;SDK 轨道共用单轨视图的预热,普通轨道等待首帧后渐入。
14643
14890
  */
14644
14891
  updateRemoteTrack() {
14645
14892
  const remoteTrack = this.currentRemoteTrack;
14646
14893
  this.remoteVideoView.frameDisplayHandler = void 0;
14894
+ this.remoteVideoView.track = void 0;
14647
14895
  this.remoteVideoView.element.style.transition = "";
14648
14896
  this.remoteVideoView.element.style.opacity = "0";
14649
14897
  this.remoteVideoView.element.style.visibility = "hidden";
14650
- this.remoteVideoView.track = void 0;
14651
14898
  if (!remoteTrack) {
14652
14899
  return;
14653
14900
  }
14901
+ if (_optionalChain([VideoRenderRegistry, 'access', _818 => _818.binding, 'call', _819 => _819(remoteTrack), 'optionalAccess', _820 => _820.renderWarmupMs]) !== void 0) {
14902
+ this.remoteVideoView.element.style.visibility = "visible";
14903
+ this.remoteVideoView.track = remoteTrack;
14904
+ return;
14905
+ }
14654
14906
  this.remoteVideoView.frameDisplayHandler = () => {
14655
14907
  if (this.currentRemoteTrack !== remoteTrack) {
14656
14908
  return;
@@ -14713,4 +14965,4 @@ var XmaxRealtimeVideoView = class {
14713
14965
 
14714
14966
 
14715
14967
  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.VideoContentMode = VideoContentMode; exports.RealtimeRecordingEventType = RealtimeRecordingEventType; exports.RealtimeRecordingFinishReason = RealtimeRecordingFinishReason; 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.XmaxVideoView = XmaxVideoView; exports.XmaxRealtimeVideoView = XmaxRealtimeVideoView;
14716
- //# sourceMappingURL=chunk-KROY2O4A.cjs.map
14968
+ //# sourceMappingURL=chunk-UF2RD42G.cjs.map