@vivix-ai/ivi-frontend-sdk 0.3.3 → 0.3.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +133 -50
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +133 -50
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1117,6 +1117,7 @@ var TrtcSourceManager = class {
|
|
|
1117
1117
|
this.onTrtcEvent = onTrtcEvent;
|
|
1118
1118
|
this.aiDenoiser = aiDenoiser;
|
|
1119
1119
|
this.sessions = /* @__PURE__ */ new Map();
|
|
1120
|
+
this.sourceSessionKeys = /* @__PURE__ */ new Map();
|
|
1120
1121
|
this.listeners = /* @__PURE__ */ new Map();
|
|
1121
1122
|
}
|
|
1122
1123
|
/**
|
|
@@ -1125,15 +1126,16 @@ var TrtcSourceManager = class {
|
|
|
1125
1126
|
* - 清理已不在 runtime 中的会话(释放资源)
|
|
1126
1127
|
*/
|
|
1127
1128
|
syncRuntimeSources(sources) {
|
|
1128
|
-
const
|
|
1129
|
+
const trtcSourceIds = /* @__PURE__ */ new Set();
|
|
1129
1130
|
sources.forEach((source, sourceId) => {
|
|
1130
1131
|
if (!isRuntimeTrtcSource(source)) {
|
|
1131
1132
|
return;
|
|
1132
1133
|
}
|
|
1134
|
+
trtcSourceIds.add(sourceId);
|
|
1133
1135
|
this.upsertSource(sourceId, source.playback.trtc);
|
|
1134
1136
|
});
|
|
1135
|
-
this.
|
|
1136
|
-
if (!
|
|
1137
|
+
Array.from(this.sourceSessionKeys.keys()).forEach((sourceId) => {
|
|
1138
|
+
if (!trtcSourceIds.has(sourceId)) {
|
|
1137
1139
|
this.removeSource(sourceId);
|
|
1138
1140
|
}
|
|
1139
1141
|
});
|
|
@@ -1143,21 +1145,28 @@ var TrtcSourceManager = class {
|
|
|
1143
1145
|
* 若配置发生变化,会先销毁旧会话再按新参数重建连接。
|
|
1144
1146
|
*/
|
|
1145
1147
|
upsertSource(sourceId, trtc, aiDenoiser) {
|
|
1146
|
-
const existing = this.
|
|
1147
|
-
if (
|
|
1148
|
-
this.log("info", `\u65B0\u5EFA\u4F1A\u8BDD source=${sourceId} room=${getTrtcString(trtc, "room_id")} user=${getTrtcString(trtc, "user_id")}`);
|
|
1149
|
-
const session2 = this.createSession(sourceId, trtc, aiDenoiser);
|
|
1150
|
-
this.sessions.set(sourceId, session2);
|
|
1151
|
-
void this.ensureConnected(session2);
|
|
1148
|
+
const existing = this.getSession(sourceId);
|
|
1149
|
+
if (existing && isSameTrtcConfig(existing.trtc, trtc) && isSameAIDenoiserOptions(existing.aiDenoiser, aiDenoiser)) {
|
|
1152
1150
|
return;
|
|
1153
1151
|
}
|
|
1154
|
-
if (
|
|
1152
|
+
if (existing) {
|
|
1153
|
+
this.log("info", `\u914D\u7F6E\u53D8\u66F4\uFF0C\u91CD\u5EFA\u4F1A\u8BDD source=${sourceId} room=${getTrtcString(trtc, "room_id")}`);
|
|
1154
|
+
this.unlinkSourceFromSession(sourceId, existing);
|
|
1155
|
+
}
|
|
1156
|
+
const sessionKey = buildTrtcSessionKey(trtc, aiDenoiser);
|
|
1157
|
+
const reusable = this.sessions.get(sessionKey);
|
|
1158
|
+
if (reusable) {
|
|
1159
|
+
this.log("info", `\u590D\u7528\u4F1A\u8BDD source=${sourceId} room=${getTrtcString(trtc, "room_id")} user=${getTrtcString(trtc, "user_id")}`);
|
|
1160
|
+
reusable.sourceIds.add(sourceId);
|
|
1161
|
+
this.sourceSessionKeys.set(sourceId, sessionKey);
|
|
1162
|
+
this.emitSnapshot(sourceId, this.snapshotFromSession(reusable));
|
|
1163
|
+
void this.ensureConnected(reusable);
|
|
1155
1164
|
return;
|
|
1156
1165
|
}
|
|
1157
|
-
this.log("info", `\
|
|
1158
|
-
void this.disposeSession(existing);
|
|
1166
|
+
this.log("info", `\u65B0\u5EFA\u4F1A\u8BDD source=${sourceId} room=${getTrtcString(trtc, "room_id")} user=${getTrtcString(trtc, "user_id")}`);
|
|
1159
1167
|
const session = this.createSession(sourceId, trtc, aiDenoiser);
|
|
1160
|
-
this.sessions.set(
|
|
1168
|
+
this.sessions.set(session.sessionKey, session);
|
|
1169
|
+
this.sourceSessionKeys.set(sourceId, session.sessionKey);
|
|
1161
1170
|
void this.ensureConnected(session);
|
|
1162
1171
|
}
|
|
1163
1172
|
/**
|
|
@@ -1165,13 +1174,12 @@ var TrtcSourceManager = class {
|
|
|
1165
1174
|
* 该操作通常由 source.deleted 或会话重置触发。
|
|
1166
1175
|
*/
|
|
1167
1176
|
removeSource(sourceId) {
|
|
1168
|
-
const session = this.
|
|
1177
|
+
const session = this.getSession(sourceId);
|
|
1169
1178
|
if (!session) {
|
|
1170
1179
|
return;
|
|
1171
1180
|
}
|
|
1172
1181
|
this.log("info", `\u79FB\u9664\u4F1A\u8BDD source=${sourceId}`);
|
|
1173
|
-
this.
|
|
1174
|
-
void this.disposeSession(session);
|
|
1182
|
+
this.unlinkSourceFromSession(sourceId, session);
|
|
1175
1183
|
this.emitSnapshot(sourceId, {
|
|
1176
1184
|
status: "idle"
|
|
1177
1185
|
});
|
|
@@ -1181,6 +1189,7 @@ var TrtcSourceManager = class {
|
|
|
1181
1189
|
this.log("info", `\u91CD\u7F6E\u5168\u90E8\u4F1A\u8BDD count=${this.sessions.size}`);
|
|
1182
1190
|
const sessions = Array.from(this.sessions.values());
|
|
1183
1191
|
this.sessions.clear();
|
|
1192
|
+
this.sourceSessionKeys.clear();
|
|
1184
1193
|
sessions.forEach((session) => {
|
|
1185
1194
|
void this.disposeSession(session);
|
|
1186
1195
|
});
|
|
@@ -1203,7 +1212,7 @@ var TrtcSourceManager = class {
|
|
|
1203
1212
|
};
|
|
1204
1213
|
}
|
|
1205
1214
|
getSnapshot(sourceId) {
|
|
1206
|
-
const session = this.
|
|
1215
|
+
const session = this.getSession(sourceId);
|
|
1207
1216
|
if (!session) {
|
|
1208
1217
|
return {
|
|
1209
1218
|
status: "idle"
|
|
@@ -1218,7 +1227,7 @@ var TrtcSourceManager = class {
|
|
|
1218
1227
|
* 检查指定 source 的 TRTC 会话是否曾收到过 REMOTE_VIDEO_AVAILABLE 事件。
|
|
1219
1228
|
*/
|
|
1220
1229
|
hasRemoteVideoAvailable(sourceId) {
|
|
1221
|
-
const session = this.
|
|
1230
|
+
const session = this.getSession(sourceId);
|
|
1222
1231
|
return session?.hasEverReceivedRemoteVideo ?? false;
|
|
1223
1232
|
}
|
|
1224
1233
|
/**
|
|
@@ -1227,7 +1236,7 @@ var TrtcSourceManager = class {
|
|
|
1227
1236
|
* - 若会话被销毁或超时,返回 false。
|
|
1228
1237
|
*/
|
|
1229
1238
|
waitForRemoteVideoAvailable(sourceId, timeoutMs = 3e4) {
|
|
1230
|
-
const session = this.
|
|
1239
|
+
const session = this.getSession(sourceId);
|
|
1231
1240
|
if (!session) return Promise.resolve(false);
|
|
1232
1241
|
if (session.hasEverReceivedRemoteVideo) return Promise.resolve(true);
|
|
1233
1242
|
return new Promise((resolve) => {
|
|
@@ -1255,11 +1264,12 @@ var TrtcSourceManager = class {
|
|
|
1255
1264
|
* - 应用全局音频策略(根据所有 view 的 muted 汇总)
|
|
1256
1265
|
*/
|
|
1257
1266
|
async attachView(sourceId, viewId, container, muted) {
|
|
1258
|
-
const session = this.
|
|
1267
|
+
const session = this.getSession(sourceId);
|
|
1259
1268
|
if (!session) {
|
|
1260
1269
|
throw new Error(`TRTC source session not found: ${sourceId}`);
|
|
1261
1270
|
}
|
|
1262
1271
|
session.views.set(viewId, {
|
|
1272
|
+
sourceId,
|
|
1263
1273
|
container,
|
|
1264
1274
|
muted,
|
|
1265
1275
|
startedVideoKeys: /* @__PURE__ */ new Set(),
|
|
@@ -1274,15 +1284,18 @@ var TrtcSourceManager = class {
|
|
|
1274
1284
|
await this.applyAudioPolicy(session);
|
|
1275
1285
|
}
|
|
1276
1286
|
detachView(sourceId, viewId) {
|
|
1277
|
-
const session = this.
|
|
1287
|
+
const session = this.getSession(sourceId);
|
|
1278
1288
|
if (!session) {
|
|
1279
1289
|
return;
|
|
1280
1290
|
}
|
|
1281
|
-
session.views.
|
|
1291
|
+
const binding = session.views.get(viewId);
|
|
1292
|
+
if (binding?.sourceId === sourceId) {
|
|
1293
|
+
session.views.delete(viewId);
|
|
1294
|
+
}
|
|
1282
1295
|
void this.applyAudioPolicy(session);
|
|
1283
1296
|
}
|
|
1284
1297
|
updateViewMuted(sourceId, viewId, muted) {
|
|
1285
|
-
const session = this.
|
|
1298
|
+
const session = this.getSession(sourceId);
|
|
1286
1299
|
if (!session) {
|
|
1287
1300
|
return;
|
|
1288
1301
|
}
|
|
@@ -1293,9 +1306,43 @@ var TrtcSourceManager = class {
|
|
|
1293
1306
|
binding.muted = muted;
|
|
1294
1307
|
void this.applyAudioPolicy(session);
|
|
1295
1308
|
}
|
|
1309
|
+
unlinkSourceFromSession(sourceId, session) {
|
|
1310
|
+
session.sourceIds.delete(sourceId);
|
|
1311
|
+
this.sourceSessionKeys.delete(sourceId);
|
|
1312
|
+
session.views.forEach((binding, viewId) => {
|
|
1313
|
+
if (binding.sourceId === sourceId) {
|
|
1314
|
+
session.views.delete(viewId);
|
|
1315
|
+
}
|
|
1316
|
+
});
|
|
1317
|
+
if (session.sourceIds.size > 0) {
|
|
1318
|
+
session.sourceId = session.sourceIds.values().next().value ?? session.sourceId;
|
|
1319
|
+
void this.applyAudioPolicy(session);
|
|
1320
|
+
return;
|
|
1321
|
+
}
|
|
1322
|
+
this.sessions.delete(session.sessionKey);
|
|
1323
|
+
void this.disposeSession(session);
|
|
1324
|
+
}
|
|
1325
|
+
getSession(sourceId) {
|
|
1326
|
+
const sessionKey = this.sourceSessionKeys.get(sourceId);
|
|
1327
|
+
return sessionKey ? this.sessions.get(sessionKey) : void 0;
|
|
1328
|
+
}
|
|
1329
|
+
snapshotFromSession(session) {
|
|
1330
|
+
return {
|
|
1331
|
+
status: session.status,
|
|
1332
|
+
error: session.error
|
|
1333
|
+
};
|
|
1334
|
+
}
|
|
1335
|
+
emitSessionSnapshot(session, snapshot) {
|
|
1336
|
+
session.sourceIds.forEach((sourceId) => this.emitSnapshot(sourceId, snapshot));
|
|
1337
|
+
}
|
|
1338
|
+
emitSessionTrtcEvent(session, type, rawType, payload) {
|
|
1339
|
+
session.sourceIds.forEach((sourceId) => this.emitTrtcEvent(sourceId, type, rawType, payload));
|
|
1340
|
+
}
|
|
1296
1341
|
createSession(sourceId, trtc, aiDenoiser) {
|
|
1297
1342
|
return {
|
|
1343
|
+
sessionKey: buildTrtcSessionKey(trtc, aiDenoiser),
|
|
1298
1344
|
sourceId,
|
|
1345
|
+
sourceIds: /* @__PURE__ */ new Set([sourceId]),
|
|
1299
1346
|
trtc,
|
|
1300
1347
|
aiDenoiser,
|
|
1301
1348
|
TRTC: null,
|
|
@@ -1323,7 +1370,7 @@ var TrtcSourceManager = class {
|
|
|
1323
1370
|
}
|
|
1324
1371
|
session.status = "connecting";
|
|
1325
1372
|
session.error = void 0;
|
|
1326
|
-
this.
|
|
1373
|
+
this.emitSessionSnapshot(session, {
|
|
1327
1374
|
status: session.status
|
|
1328
1375
|
});
|
|
1329
1376
|
session.connectPromise = (async () => {
|
|
@@ -1352,7 +1399,7 @@ var TrtcSourceManager = class {
|
|
|
1352
1399
|
session.views.forEach((binding) => {
|
|
1353
1400
|
void this.startRemoteVideoForBinding(client, event.userId, event.streamType, binding, remoteVideoKey);
|
|
1354
1401
|
});
|
|
1355
|
-
this.
|
|
1402
|
+
this.emitSessionTrtcEvent(session, "remote_video_available", TRTC.EVENT.REMOTE_VIDEO_AVAILABLE, event);
|
|
1356
1403
|
};
|
|
1357
1404
|
const onRemoteVideoUnavailable = (event) => {
|
|
1358
1405
|
this.log("info", `\u8FDC\u7AEF\u89C6\u9891\u4E0D\u53EF\u7528 source=${session.sourceId} userId=${event.userId} streamType=${event.streamType}`);
|
|
@@ -1366,19 +1413,19 @@ var TrtcSourceManager = class {
|
|
|
1366
1413
|
userId: event.userId,
|
|
1367
1414
|
streamType: event.streamType
|
|
1368
1415
|
}).catch(() => void 0);
|
|
1369
|
-
this.
|
|
1416
|
+
this.emitSessionTrtcEvent(session, "remote_video_unavailable", TRTC.EVENT.REMOTE_VIDEO_UNAVAILABLE, event);
|
|
1370
1417
|
};
|
|
1371
1418
|
const onRemoteAudioAvailable = (event) => {
|
|
1372
1419
|
this.log("info", `\u8FDC\u7AEF\u97F3\u9891\u53EF\u7528 source=${session.sourceId} userId=${event.userId}`);
|
|
1373
1420
|
session.remoteAudioUsers.add(event.userId);
|
|
1374
1421
|
void this.applyAudioPolicy(session);
|
|
1375
|
-
this.
|
|
1422
|
+
this.emitSessionTrtcEvent(session, "remote_audio_available", TRTC.EVENT.REMOTE_AUDIO_AVAILABLE, event);
|
|
1376
1423
|
};
|
|
1377
1424
|
const onRemoteAudioUnavailable = (event) => {
|
|
1378
1425
|
this.log("info", `\u8FDC\u7AEF\u97F3\u9891\u4E0D\u53EF\u7528 source=${session.sourceId} userId=${event.userId}`);
|
|
1379
1426
|
session.remoteAudioUsers.delete(event.userId);
|
|
1380
1427
|
void this.applyAudioPolicy(session);
|
|
1381
|
-
this.
|
|
1428
|
+
this.emitSessionTrtcEvent(session, "remote_audio_unavailable", TRTC.EVENT.REMOTE_AUDIO_UNAVAILABLE, event);
|
|
1382
1429
|
};
|
|
1383
1430
|
session.onRemoteVideoAvailable = onRemoteVideoAvailable;
|
|
1384
1431
|
session.onRemoteVideoUnavailable = onRemoteVideoUnavailable;
|
|
@@ -1404,7 +1451,7 @@ var TrtcSourceManager = class {
|
|
|
1404
1451
|
session.error = void 0;
|
|
1405
1452
|
this.log("info", `\u8FDB\u623F\u6210\u529F source=${session.sourceId} room=${roomId}`);
|
|
1406
1453
|
await this.applyAIDenoiserPolicy(session, client, sdkAppId, userId, userSig);
|
|
1407
|
-
this.
|
|
1454
|
+
this.emitSessionSnapshot(session, {
|
|
1408
1455
|
status: session.status
|
|
1409
1456
|
});
|
|
1410
1457
|
await this.applyAudioPolicy(session);
|
|
@@ -1412,7 +1459,7 @@ var TrtcSourceManager = class {
|
|
|
1412
1459
|
session.status = "error";
|
|
1413
1460
|
session.error = error instanceof Error ? error.message : String(error);
|
|
1414
1461
|
this.log("error", `\u8FDE\u63A5\u5931\u8D25 source=${session.sourceId} error=${session.error}`);
|
|
1415
|
-
this.
|
|
1462
|
+
this.emitSessionSnapshot(session, {
|
|
1416
1463
|
status: session.status,
|
|
1417
1464
|
error: session.error
|
|
1418
1465
|
});
|
|
@@ -1589,6 +1636,20 @@ function isRuntimeTrtcSource(source) {
|
|
|
1589
1636
|
function isSameTrtcConfig(a, b) {
|
|
1590
1637
|
return a.app_id === b.app_id && a.user_id === b.user_id && a.user_sig === b.user_sig && a.room_id === b.room_id;
|
|
1591
1638
|
}
|
|
1639
|
+
function buildTrtcSessionKey(trtc, aiDenoiser) {
|
|
1640
|
+
const denoiser = resolveAIDenoiserOptions(aiDenoiser);
|
|
1641
|
+
return JSON.stringify({
|
|
1642
|
+
appId: trtc.app_id,
|
|
1643
|
+
roomId: trtc.room_id,
|
|
1644
|
+
userId: trtc.user_id,
|
|
1645
|
+
userSig: trtc.user_sig,
|
|
1646
|
+
aiDenoiser: {
|
|
1647
|
+
enabled: denoiser.enabled,
|
|
1648
|
+
mode: denoiser.mode,
|
|
1649
|
+
assetsPath: denoiser.assetsPath
|
|
1650
|
+
}
|
|
1651
|
+
});
|
|
1652
|
+
}
|
|
1592
1653
|
function isSameAIDenoiserOptions(a, b) {
|
|
1593
1654
|
const left = resolveAIDenoiserOptions(a);
|
|
1594
1655
|
const right = resolveAIDenoiserOptions(b);
|
|
@@ -3767,6 +3828,19 @@ function detectMediaVolumeType(source) {
|
|
|
3767
3828
|
if (!url) return null;
|
|
3768
3829
|
return isM3u8Url(url) ? "hls" : "video";
|
|
3769
3830
|
}
|
|
3831
|
+
function getSourceRenderKey(sourceId, source) {
|
|
3832
|
+
const trtc = getTrtcPlayback(source.playback);
|
|
3833
|
+
if (!trtc) {
|
|
3834
|
+
return sourceId;
|
|
3835
|
+
}
|
|
3836
|
+
return [
|
|
3837
|
+
"trtc",
|
|
3838
|
+
trtc.app_id ?? "",
|
|
3839
|
+
trtc.room_id ?? "",
|
|
3840
|
+
trtc.user_id ?? "",
|
|
3841
|
+
trtc.user_sig ?? ""
|
|
3842
|
+
].join(":");
|
|
3843
|
+
}
|
|
3770
3844
|
function TrackSlotMediaContent(props) {
|
|
3771
3845
|
const {
|
|
3772
3846
|
slot,
|
|
@@ -3804,7 +3878,7 @@ function TrackSlotMediaContent(props) {
|
|
|
3804
3878
|
IVITrtcPlayer,
|
|
3805
3879
|
{
|
|
3806
3880
|
trtc,
|
|
3807
|
-
sourceId:
|
|
3881
|
+
sourceId: slotSourceId,
|
|
3808
3882
|
runtime,
|
|
3809
3883
|
...trtcPlayerProps,
|
|
3810
3884
|
muted: trtcMuted,
|
|
@@ -3959,26 +4033,35 @@ function SlotVideo(props) {
|
|
|
3959
4033
|
// src/react/internal/use-multi-preload-sources.ts
|
|
3960
4034
|
function useMultiPreloadSources(sources, activeSourceId) {
|
|
3961
4035
|
return react.useMemo(() => {
|
|
3962
|
-
|
|
3963
|
-
for (const [id, runtimeSource] of sources) {
|
|
3964
|
-
const isActive = id === activeSourceId;
|
|
3965
|
-
const shouldMount = Boolean(runtimeSource.preload) || isActive;
|
|
3966
|
-
if (!shouldMount) {
|
|
3967
|
-
continue;
|
|
3968
|
-
}
|
|
3969
|
-
const ready = toReadyRuntimeSource(runtimeSource);
|
|
3970
|
-
if (!ready) {
|
|
3971
|
-
continue;
|
|
3972
|
-
}
|
|
3973
|
-
entries.push({
|
|
3974
|
-
sourceId: id,
|
|
3975
|
-
source: ready,
|
|
3976
|
-
isActive
|
|
3977
|
-
});
|
|
3978
|
-
}
|
|
3979
|
-
return entries;
|
|
4036
|
+
return buildPreloadSourceEntries(sources, activeSourceId);
|
|
3980
4037
|
}, [sources, activeSourceId]);
|
|
3981
4038
|
}
|
|
4039
|
+
function buildPreloadSourceEntries(sources, activeSourceId) {
|
|
4040
|
+
const entriesByRenderKey = /* @__PURE__ */ new Map();
|
|
4041
|
+
for (const [id, runtimeSource] of sources) {
|
|
4042
|
+
const isActive = id === activeSourceId;
|
|
4043
|
+
const shouldMount = Boolean(runtimeSource.preload) || isActive;
|
|
4044
|
+
if (!shouldMount) {
|
|
4045
|
+
continue;
|
|
4046
|
+
}
|
|
4047
|
+
const ready = toReadyRuntimeSource(runtimeSource);
|
|
4048
|
+
if (!ready) {
|
|
4049
|
+
continue;
|
|
4050
|
+
}
|
|
4051
|
+
const renderKey = getSourceRenderKey(id, ready);
|
|
4052
|
+
const entry = {
|
|
4053
|
+
sourceId: id,
|
|
4054
|
+
renderKey,
|
|
4055
|
+
source: ready,
|
|
4056
|
+
isActive
|
|
4057
|
+
};
|
|
4058
|
+
const previous = entriesByRenderKey.get(renderKey);
|
|
4059
|
+
if (!previous || !previous.isActive && isActive) {
|
|
4060
|
+
entriesByRenderKey.set(renderKey, entry);
|
|
4061
|
+
}
|
|
4062
|
+
}
|
|
4063
|
+
return Array.from(entriesByRenderKey.values());
|
|
4064
|
+
}
|
|
3982
4065
|
function IVITrackSlot(props) {
|
|
3983
4066
|
const {
|
|
3984
4067
|
slot,
|
|
@@ -4068,7 +4151,7 @@ function IVITrackSlot(props) {
|
|
|
4068
4151
|
}
|
|
4069
4152
|
) })
|
|
4070
4153
|
},
|
|
4071
|
-
entry.
|
|
4154
|
+
entry.renderKey
|
|
4072
4155
|
);
|
|
4073
4156
|
}),
|
|
4074
4157
|
showSubtitle && activeSource && supportsSubtitleOverlay(activeSource) && /* @__PURE__ */ jsxRuntime.jsx("div", { style: SUBTITLE_OVERLAY_STYLE, children: /* @__PURE__ */ jsxRuntime.jsx(
|