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