react-jssip-kit 0.6.9 → 0.7.1
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 +147 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +24 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.js +147 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -679,6 +679,23 @@ var WebRTCSessionController = class {
|
|
|
679
679
|
old.stop();
|
|
680
680
|
return true;
|
|
681
681
|
}
|
|
682
|
+
async replaceAudioTrack(nextAudioTrack) {
|
|
683
|
+
const pc = this.getPC();
|
|
684
|
+
if (!pc)
|
|
685
|
+
return false;
|
|
686
|
+
if (!this.mediaStream)
|
|
687
|
+
this.mediaStream = new MediaStream();
|
|
688
|
+
const old = this.mediaStream.getAudioTracks()[0];
|
|
689
|
+
this.mediaStream.addTrack(nextAudioTrack);
|
|
690
|
+
if (old)
|
|
691
|
+
this.mediaStream.removeTrack(old);
|
|
692
|
+
const sender = pc.getSenders?.().find((s) => s.track?.kind === "audio");
|
|
693
|
+
if (sender)
|
|
694
|
+
await sender.replaceTrack(nextAudioTrack);
|
|
695
|
+
if (old && old !== nextAudioTrack)
|
|
696
|
+
old.stop();
|
|
697
|
+
return true;
|
|
698
|
+
}
|
|
682
699
|
};
|
|
683
700
|
|
|
684
701
|
// src/jssip-lib/sip/sessionManager.ts
|
|
@@ -886,6 +903,12 @@ var SipClient = class extends EventTargetEmitter {
|
|
|
886
903
|
this.sessionHandlers = /* @__PURE__ */ new Map();
|
|
887
904
|
this.maxSessionCount = Infinity;
|
|
888
905
|
this.sessionManager = new SessionManager();
|
|
906
|
+
this.micRecovery = /* @__PURE__ */ new Map();
|
|
907
|
+
this.micRecoveryEnabled = false;
|
|
908
|
+
this.micRecoveryDefaults = {
|
|
909
|
+
intervalMs: 2e3,
|
|
910
|
+
maxRetries: Infinity
|
|
911
|
+
};
|
|
889
912
|
this.errorHandler = options.errorHandler ?? new SipErrorHandler({
|
|
890
913
|
formatter: options.formatError,
|
|
891
914
|
messages: options.errorMessages
|
|
@@ -919,11 +942,21 @@ var SipClient = class extends EventTargetEmitter {
|
|
|
919
942
|
this.stateStore.setState({ sipStatus: SipStatus.Connecting });
|
|
920
943
|
const {
|
|
921
944
|
debug: cfgDebug,
|
|
945
|
+
enableMicRecovery,
|
|
946
|
+
micRecoveryIntervalMs,
|
|
947
|
+
micRecoveryMaxRetries,
|
|
922
948
|
maxSessionCount,
|
|
923
949
|
pendingMediaTtlMs,
|
|
924
950
|
...uaCfg
|
|
925
951
|
} = config;
|
|
926
952
|
this.maxSessionCount = typeof maxSessionCount === "number" ? maxSessionCount : Infinity;
|
|
953
|
+
this.micRecoveryEnabled = Boolean(enableMicRecovery);
|
|
954
|
+
if (typeof micRecoveryIntervalMs === "number") {
|
|
955
|
+
this.micRecoveryDefaults.intervalMs = micRecoveryIntervalMs;
|
|
956
|
+
}
|
|
957
|
+
if (typeof micRecoveryMaxRetries === "number") {
|
|
958
|
+
this.micRecoveryDefaults.maxRetries = micRecoveryMaxRetries;
|
|
959
|
+
}
|
|
927
960
|
this.sessionManager.setPendingMediaTtl(pendingMediaTtlMs);
|
|
928
961
|
const debug = cfgDebug ?? this.getPersistedDebug() ?? this.debugPattern;
|
|
929
962
|
this.userAgent.start(uri, password, uaCfg, { debug });
|
|
@@ -1012,6 +1045,9 @@ var SipClient = class extends EventTargetEmitter {
|
|
|
1012
1045
|
if (h)
|
|
1013
1046
|
session.on(ev, h);
|
|
1014
1047
|
});
|
|
1048
|
+
if (this.micRecoveryEnabled) {
|
|
1049
|
+
this.enableMicrophoneRecovery(sessionId);
|
|
1050
|
+
}
|
|
1015
1051
|
}
|
|
1016
1052
|
detachSessionHandlers(sessionId, session) {
|
|
1017
1053
|
const handlers = this.sessionHandlers.get(sessionId);
|
|
@@ -1037,11 +1073,14 @@ var SipClient = class extends EventTargetEmitter {
|
|
|
1037
1073
|
cleanupSession(sessionId, session) {
|
|
1038
1074
|
const targetSession = session ?? this.sessionManager.getSession(sessionId) ?? this.sessionManager.getRtc(sessionId)?.currentSession;
|
|
1039
1075
|
this.detachSessionHandlers(sessionId, targetSession);
|
|
1076
|
+
this.disableMicrophoneRecovery(sessionId);
|
|
1040
1077
|
this.sessionManager.cleanupSession(sessionId);
|
|
1041
1078
|
removeSessionState(this.stateStore, sessionId);
|
|
1042
1079
|
}
|
|
1043
1080
|
cleanupAllSessions() {
|
|
1044
1081
|
this.sessionManager.cleanupAllSessions();
|
|
1082
|
+
this.micRecovery.forEach((entry) => entry.stop());
|
|
1083
|
+
this.micRecovery.clear();
|
|
1045
1084
|
this.sessionHandlers.clear();
|
|
1046
1085
|
this.stateStore.setState({
|
|
1047
1086
|
sessions: [],
|
|
@@ -1103,6 +1142,9 @@ var SipClient = class extends EventTargetEmitter {
|
|
|
1103
1142
|
if (!sessionId || !this.sessionExists(sessionId))
|
|
1104
1143
|
return false;
|
|
1105
1144
|
const opts = this.ensureMediaConstraints(options);
|
|
1145
|
+
if (opts.mediaStream) {
|
|
1146
|
+
this.sessionManager.setSessionMedia(sessionId, opts.mediaStream);
|
|
1147
|
+
}
|
|
1106
1148
|
return this.sessionManager.answer(sessionId, opts);
|
|
1107
1149
|
}
|
|
1108
1150
|
hangupSession(sessionId, options) {
|
|
@@ -1160,6 +1202,92 @@ var SipClient = class extends EventTargetEmitter {
|
|
|
1160
1202
|
setSessionMedia(sessionId, stream) {
|
|
1161
1203
|
this.sessionManager.setSessionMedia(sessionId, stream);
|
|
1162
1204
|
}
|
|
1205
|
+
enableMicrophoneRecovery(sessionId, options = {}) {
|
|
1206
|
+
const resolved = this.resolveExistingSessionId(sessionId);
|
|
1207
|
+
if (!resolved)
|
|
1208
|
+
return () => {
|
|
1209
|
+
};
|
|
1210
|
+
this.disableMicrophoneRecovery(resolved);
|
|
1211
|
+
const intervalMs = options.intervalMs ?? this.micRecoveryDefaults.intervalMs;
|
|
1212
|
+
const maxRetries = options.maxRetries ?? this.micRecoveryDefaults.maxRetries;
|
|
1213
|
+
let retries = 0;
|
|
1214
|
+
let stopped = false;
|
|
1215
|
+
const tick = async () => {
|
|
1216
|
+
if (stopped || retries >= maxRetries)
|
|
1217
|
+
return;
|
|
1218
|
+
const rtc = this.sessionManager.getRtc(resolved);
|
|
1219
|
+
const session2 = this.sessionManager.getSession(resolved);
|
|
1220
|
+
if (!rtc || !session2)
|
|
1221
|
+
return;
|
|
1222
|
+
const sessionState = this.stateStore.getState().sessions.find((s) => s.id === resolved);
|
|
1223
|
+
if (sessionState?.muted)
|
|
1224
|
+
return;
|
|
1225
|
+
const stream = rtc.mediaStream;
|
|
1226
|
+
const track = stream?.getAudioTracks?.()[0];
|
|
1227
|
+
const sender = session2?.connection?.getSenders?.().find((s) => s.track?.kind === "audio");
|
|
1228
|
+
const trackLive = track?.readyState === "live";
|
|
1229
|
+
const senderLive = sender?.track?.readyState === "live";
|
|
1230
|
+
if (trackLive && senderLive)
|
|
1231
|
+
return;
|
|
1232
|
+
this.emitError(
|
|
1233
|
+
{
|
|
1234
|
+
cause: "microphone dropped",
|
|
1235
|
+
trackLive,
|
|
1236
|
+
senderLive
|
|
1237
|
+
},
|
|
1238
|
+
"MICROPHONE_DROPPED",
|
|
1239
|
+
"microphone dropped"
|
|
1240
|
+
);
|
|
1241
|
+
retries += 1;
|
|
1242
|
+
if (trackLive && !senderLive && track) {
|
|
1243
|
+
await rtc.replaceAudioTrack(track);
|
|
1244
|
+
return;
|
|
1245
|
+
}
|
|
1246
|
+
let nextStream;
|
|
1247
|
+
try {
|
|
1248
|
+
const deviceId = track?.getSettings?.().deviceId ?? sender?.track?.getSettings?.().deviceId;
|
|
1249
|
+
nextStream = await this.requestMicrophoneStreamInternal(deviceId);
|
|
1250
|
+
} catch (err) {
|
|
1251
|
+
console.warn("[sip] mic recovery failed to get stream", err);
|
|
1252
|
+
return;
|
|
1253
|
+
}
|
|
1254
|
+
const nextTrack = nextStream.getAudioTracks()[0];
|
|
1255
|
+
if (!nextTrack)
|
|
1256
|
+
return;
|
|
1257
|
+
await rtc.replaceAudioTrack(nextTrack);
|
|
1258
|
+
this.sessionManager.setSessionMedia(resolved, nextStream);
|
|
1259
|
+
};
|
|
1260
|
+
const timer = setInterval(() => {
|
|
1261
|
+
void tick();
|
|
1262
|
+
}, intervalMs);
|
|
1263
|
+
void tick();
|
|
1264
|
+
const session = this.sessionManager.getSession(resolved);
|
|
1265
|
+
const pc = session?.connection;
|
|
1266
|
+
const onIceChange = () => {
|
|
1267
|
+
const state = pc?.iceConnectionState;
|
|
1268
|
+
if (state === "failed" || state === "disconnected")
|
|
1269
|
+
void tick();
|
|
1270
|
+
};
|
|
1271
|
+
pc?.addEventListener?.("iceconnectionstatechange", onIceChange);
|
|
1272
|
+
const stop = () => {
|
|
1273
|
+
stopped = true;
|
|
1274
|
+
clearInterval(timer);
|
|
1275
|
+
pc?.removeEventListener?.("iceconnectionstatechange", onIceChange);
|
|
1276
|
+
};
|
|
1277
|
+
this.micRecovery.set(resolved, { stop });
|
|
1278
|
+
return stop;
|
|
1279
|
+
}
|
|
1280
|
+
disableMicrophoneRecovery(sessionId) {
|
|
1281
|
+
const resolved = this.resolveExistingSessionId(sessionId);
|
|
1282
|
+
if (!resolved)
|
|
1283
|
+
return false;
|
|
1284
|
+
const entry = this.micRecovery.get(resolved);
|
|
1285
|
+
if (!entry)
|
|
1286
|
+
return false;
|
|
1287
|
+
entry.stop();
|
|
1288
|
+
this.micRecovery.delete(resolved);
|
|
1289
|
+
return true;
|
|
1290
|
+
}
|
|
1163
1291
|
switchCameraSession(sessionId, track) {
|
|
1164
1292
|
if (!this.sessionExists(sessionId))
|
|
1165
1293
|
return false;
|
|
@@ -1251,13 +1379,28 @@ var SipClient = class extends EventTargetEmitter {
|
|
|
1251
1379
|
const persisted = window.sessionStorage.getItem(SESSION_DEBUG_KEY);
|
|
1252
1380
|
if (!persisted)
|
|
1253
1381
|
return void 0;
|
|
1254
|
-
if (persisted === "true")
|
|
1255
|
-
return true;
|
|
1256
1382
|
return persisted;
|
|
1257
1383
|
} catch {
|
|
1258
1384
|
return void 0;
|
|
1259
1385
|
}
|
|
1260
1386
|
}
|
|
1387
|
+
async requestMicrophoneStreamInternal(deviceId) {
|
|
1388
|
+
if (typeof navigator === "undefined" || !navigator.mediaDevices?.getUserMedia) {
|
|
1389
|
+
throw new Error("getUserMedia not available");
|
|
1390
|
+
}
|
|
1391
|
+
const audio = deviceId && deviceId !== "default" ? { deviceId: { exact: deviceId } } : true;
|
|
1392
|
+
try {
|
|
1393
|
+
return await navigator.mediaDevices.getUserMedia({ audio });
|
|
1394
|
+
} catch (err) {
|
|
1395
|
+
const cause = err?.name || "getUserMedia failed";
|
|
1396
|
+
this.emitError(
|
|
1397
|
+
{ raw: err, cause },
|
|
1398
|
+
"MICROPHONE_UNAVAILABLE",
|
|
1399
|
+
"microphone unavailable"
|
|
1400
|
+
);
|
|
1401
|
+
throw err;
|
|
1402
|
+
}
|
|
1403
|
+
}
|
|
1261
1404
|
};
|
|
1262
1405
|
function createSipClientInstance(options) {
|
|
1263
1406
|
return new SipClient(options);
|
|
@@ -1310,6 +1453,8 @@ function useSipActions() {
|
|
|
1310
1453
|
getSessionIds: () => client.getSessionIds(),
|
|
1311
1454
|
getSessions: () => client.getSessions(),
|
|
1312
1455
|
setSessionMedia: (...args) => client.setSessionMedia(...args),
|
|
1456
|
+
enableMicrophoneRecovery: (...args) => client.enableMicrophoneRecovery(...args),
|
|
1457
|
+
disableMicrophoneRecovery: (...args) => client.disableMicrophoneRecovery(...args),
|
|
1313
1458
|
switchCamera: (...args) => client.switchCameraSession(...args),
|
|
1314
1459
|
enableVideo: (...args) => client.enableVideoSession(...args),
|
|
1315
1460
|
disableVideo: (...args) => client.disableVideoSession(...args)
|