react-jssip-kit 0.6.8 → 0.7.0
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 +192 -29
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +27 -1
- package/dist/index.d.ts +27 -1
- package/dist/index.js +179 -17
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var JsSIP = require('jssip');
|
|
4
|
-
var
|
|
4
|
+
var React = require('react');
|
|
5
5
|
var jsxRuntime = require('react/jsx-runtime');
|
|
6
6
|
|
|
7
7
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
8
8
|
|
|
9
9
|
var JsSIP__default = /*#__PURE__*/_interopDefault(JsSIP);
|
|
10
|
+
var React__default = /*#__PURE__*/_interopDefault(React);
|
|
10
11
|
|
|
11
12
|
// src/jssip-lib/sip/debugger.ts
|
|
12
13
|
var SipDebugger = class {
|
|
@@ -18,11 +19,8 @@ var SipDebugger = class {
|
|
|
18
19
|
initFromSession(storage = safeSessionStorage()) {
|
|
19
20
|
try {
|
|
20
21
|
const saved = storage?.getItem(this.storageKey);
|
|
21
|
-
if (saved
|
|
22
|
-
this.enable(
|
|
23
|
-
} else if (saved) {
|
|
24
|
-
storage?.removeItem?.(this.storageKey);
|
|
25
|
-
}
|
|
22
|
+
if (saved)
|
|
23
|
+
this.enable(saved, storage);
|
|
26
24
|
} catch {
|
|
27
25
|
}
|
|
28
26
|
}
|
|
@@ -32,9 +30,9 @@ var SipDebugger = class {
|
|
|
32
30
|
JsSIP__default.default.debug.enable(pattern);
|
|
33
31
|
this.logger = console;
|
|
34
32
|
}
|
|
35
|
-
storage?.setItem?.(this.storageKey,
|
|
33
|
+
storage?.setItem?.(this.storageKey, pattern || this.defaultPattern);
|
|
36
34
|
try {
|
|
37
|
-
window.sipDebugBridge?.(
|
|
35
|
+
window.sipDebugBridge?.(pattern);
|
|
38
36
|
} catch {
|
|
39
37
|
}
|
|
40
38
|
this.enabled = true;
|
|
@@ -180,8 +178,9 @@ var SipUserAgent = class {
|
|
|
180
178
|
}
|
|
181
179
|
}
|
|
182
180
|
applyDebug(debug) {
|
|
183
|
-
const
|
|
184
|
-
const
|
|
181
|
+
const stored = debug === void 0 ? this.readSessionFlag() : debug;
|
|
182
|
+
const enabled = !!stored;
|
|
183
|
+
const pattern = typeof stored === "string" ? stored : "JsSIP:*";
|
|
185
184
|
if (enabled) {
|
|
186
185
|
JsSIP__default.default.debug.enable(pattern);
|
|
187
186
|
const dbg = JsSIP__default.default.debug;
|
|
@@ -189,7 +188,7 @@ var SipUserAgent = class {
|
|
|
189
188
|
dbg.setLogger(console);
|
|
190
189
|
else if (dbg)
|
|
191
190
|
dbg.logger = console;
|
|
192
|
-
this.persistSessionFlag();
|
|
191
|
+
this.persistSessionFlag(typeof stored === "string" ? stored : void 0);
|
|
193
192
|
} else {
|
|
194
193
|
JsSIP__default.default.debug?.disable?.();
|
|
195
194
|
this.clearSessionFlag();
|
|
@@ -202,15 +201,21 @@ var SipUserAgent = class {
|
|
|
202
201
|
try {
|
|
203
202
|
if (typeof window === "undefined")
|
|
204
203
|
return false;
|
|
205
|
-
|
|
204
|
+
const value = window.sessionStorage.getItem("sip-debug-enabled");
|
|
205
|
+
if (!value)
|
|
206
|
+
return false;
|
|
207
|
+
return value;
|
|
206
208
|
} catch {
|
|
207
209
|
return false;
|
|
208
210
|
}
|
|
209
211
|
}
|
|
210
|
-
persistSessionFlag() {
|
|
212
|
+
persistSessionFlag(pattern) {
|
|
211
213
|
try {
|
|
212
214
|
if (typeof window !== "undefined") {
|
|
213
|
-
window.sessionStorage.setItem(
|
|
215
|
+
window.sessionStorage.setItem(
|
|
216
|
+
"sip-debug-enabled",
|
|
217
|
+
pattern || "JsSIP:*"
|
|
218
|
+
);
|
|
214
219
|
}
|
|
215
220
|
} catch {
|
|
216
221
|
}
|
|
@@ -675,6 +680,23 @@ var WebRTCSessionController = class {
|
|
|
675
680
|
old.stop();
|
|
676
681
|
return true;
|
|
677
682
|
}
|
|
683
|
+
async replaceAudioTrack(nextAudioTrack) {
|
|
684
|
+
const pc = this.getPC();
|
|
685
|
+
if (!pc)
|
|
686
|
+
return false;
|
|
687
|
+
if (!this.mediaStream)
|
|
688
|
+
this.mediaStream = new MediaStream();
|
|
689
|
+
const old = this.mediaStream.getAudioTracks()[0];
|
|
690
|
+
this.mediaStream.addTrack(nextAudioTrack);
|
|
691
|
+
if (old)
|
|
692
|
+
this.mediaStream.removeTrack(old);
|
|
693
|
+
const sender = pc.getSenders?.().find((s) => s.track?.kind === "audio");
|
|
694
|
+
if (sender)
|
|
695
|
+
await sender.replaceTrack(nextAudioTrack);
|
|
696
|
+
if (old && old !== nextAudioTrack)
|
|
697
|
+
old.stop();
|
|
698
|
+
return true;
|
|
699
|
+
}
|
|
678
700
|
};
|
|
679
701
|
|
|
680
702
|
// src/jssip-lib/sip/sessionManager.ts
|
|
@@ -882,6 +904,12 @@ var SipClient = class extends EventTargetEmitter {
|
|
|
882
904
|
this.sessionHandlers = /* @__PURE__ */ new Map();
|
|
883
905
|
this.maxSessionCount = Infinity;
|
|
884
906
|
this.sessionManager = new SessionManager();
|
|
907
|
+
this.micRecovery = /* @__PURE__ */ new Map();
|
|
908
|
+
this.micRecoveryEnabled = false;
|
|
909
|
+
this.micRecoveryDefaults = {
|
|
910
|
+
intervalMs: 2e3,
|
|
911
|
+
maxRetries: Infinity
|
|
912
|
+
};
|
|
885
913
|
this.errorHandler = options.errorHandler ?? new SipErrorHandler({
|
|
886
914
|
formatter: options.formatError,
|
|
887
915
|
messages: options.errorMessages
|
|
@@ -915,11 +943,21 @@ var SipClient = class extends EventTargetEmitter {
|
|
|
915
943
|
this.stateStore.setState({ sipStatus: SipStatus.Connecting });
|
|
916
944
|
const {
|
|
917
945
|
debug: cfgDebug,
|
|
946
|
+
enableMicRecovery,
|
|
947
|
+
micRecoveryIntervalMs,
|
|
948
|
+
micRecoveryMaxRetries,
|
|
918
949
|
maxSessionCount,
|
|
919
950
|
pendingMediaTtlMs,
|
|
920
951
|
...uaCfg
|
|
921
952
|
} = config;
|
|
922
953
|
this.maxSessionCount = typeof maxSessionCount === "number" ? maxSessionCount : Infinity;
|
|
954
|
+
this.micRecoveryEnabled = Boolean(enableMicRecovery);
|
|
955
|
+
if (typeof micRecoveryIntervalMs === "number") {
|
|
956
|
+
this.micRecoveryDefaults.intervalMs = micRecoveryIntervalMs;
|
|
957
|
+
}
|
|
958
|
+
if (typeof micRecoveryMaxRetries === "number") {
|
|
959
|
+
this.micRecoveryDefaults.maxRetries = micRecoveryMaxRetries;
|
|
960
|
+
}
|
|
923
961
|
this.sessionManager.setPendingMediaTtl(pendingMediaTtlMs);
|
|
924
962
|
const debug = cfgDebug ?? this.getPersistedDebug() ?? this.debugPattern;
|
|
925
963
|
this.userAgent.start(uri, password, uaCfg, { debug });
|
|
@@ -927,6 +965,14 @@ var SipClient = class extends EventTargetEmitter {
|
|
|
927
965
|
this.attachBeforeUnload();
|
|
928
966
|
this.syncDebugInspector(debug);
|
|
929
967
|
}
|
|
968
|
+
setMicrophoneProvider(fn) {
|
|
969
|
+
this.requestMicrophoneStream = fn;
|
|
970
|
+
if (fn && this.micRecoveryEnabled) {
|
|
971
|
+
this.sessionManager.getSessions().forEach(({ id }) => {
|
|
972
|
+
this.enableMicrophoneRecovery(id);
|
|
973
|
+
});
|
|
974
|
+
}
|
|
975
|
+
}
|
|
930
976
|
registerUA() {
|
|
931
977
|
this.userAgent.register();
|
|
932
978
|
}
|
|
@@ -938,6 +984,21 @@ var SipClient = class extends EventTargetEmitter {
|
|
|
938
984
|
this.stateStore.reset();
|
|
939
985
|
}
|
|
940
986
|
call(target, callOptions = {}) {
|
|
987
|
+
if (!callOptions.mediaStream && this.requestMicrophoneStream) {
|
|
988
|
+
void this.requestMicrophoneStream().then((stream) => {
|
|
989
|
+
if (!stream)
|
|
990
|
+
throw new Error("microphone stream unavailable");
|
|
991
|
+
this.call(target, { ...callOptions, mediaStream: stream });
|
|
992
|
+
}).catch((e) => {
|
|
993
|
+
const err = this.emitError(
|
|
994
|
+
e,
|
|
995
|
+
"MICROPHONE_FAILED",
|
|
996
|
+
"microphone failed"
|
|
997
|
+
);
|
|
998
|
+
this.stateStore.batchSet({ error: err.cause });
|
|
999
|
+
});
|
|
1000
|
+
return;
|
|
1001
|
+
}
|
|
941
1002
|
try {
|
|
942
1003
|
const opts = this.ensureMediaConstraints(callOptions);
|
|
943
1004
|
if (opts.mediaStream)
|
|
@@ -1008,6 +1069,9 @@ var SipClient = class extends EventTargetEmitter {
|
|
|
1008
1069
|
if (h)
|
|
1009
1070
|
session.on(ev, h);
|
|
1010
1071
|
});
|
|
1072
|
+
if (this.requestMicrophoneStream && this.micRecoveryEnabled) {
|
|
1073
|
+
this.enableMicrophoneRecovery(sessionId);
|
|
1074
|
+
}
|
|
1011
1075
|
}
|
|
1012
1076
|
detachSessionHandlers(sessionId, session) {
|
|
1013
1077
|
const handlers = this.sessionHandlers.get(sessionId);
|
|
@@ -1033,11 +1097,14 @@ var SipClient = class extends EventTargetEmitter {
|
|
|
1033
1097
|
cleanupSession(sessionId, session) {
|
|
1034
1098
|
const targetSession = session ?? this.sessionManager.getSession(sessionId) ?? this.sessionManager.getRtc(sessionId)?.currentSession;
|
|
1035
1099
|
this.detachSessionHandlers(sessionId, targetSession);
|
|
1100
|
+
this.disableMicrophoneRecovery(sessionId);
|
|
1036
1101
|
this.sessionManager.cleanupSession(sessionId);
|
|
1037
1102
|
removeSessionState(this.stateStore, sessionId);
|
|
1038
1103
|
}
|
|
1039
1104
|
cleanupAllSessions() {
|
|
1040
1105
|
this.sessionManager.cleanupAllSessions();
|
|
1106
|
+
this.micRecovery.forEach((entry) => entry.stop());
|
|
1107
|
+
this.micRecovery.clear();
|
|
1041
1108
|
this.sessionHandlers.clear();
|
|
1042
1109
|
this.stateStore.setState({
|
|
1043
1110
|
sessions: [],
|
|
@@ -1098,6 +1165,21 @@ var SipClient = class extends EventTargetEmitter {
|
|
|
1098
1165
|
answerSession(sessionId, options = {}) {
|
|
1099
1166
|
if (!sessionId || !this.sessionExists(sessionId))
|
|
1100
1167
|
return false;
|
|
1168
|
+
if (!options.mediaStream && this.requestMicrophoneStream) {
|
|
1169
|
+
void this.requestMicrophoneStream().then((stream) => {
|
|
1170
|
+
if (!stream)
|
|
1171
|
+
throw new Error("microphone stream unavailable");
|
|
1172
|
+
this.answerSession(sessionId, { ...options, mediaStream: stream });
|
|
1173
|
+
}).catch((e) => {
|
|
1174
|
+
const err = this.emitError(
|
|
1175
|
+
e,
|
|
1176
|
+
"MICROPHONE_FAILED",
|
|
1177
|
+
"microphone failed"
|
|
1178
|
+
);
|
|
1179
|
+
this.stateStore.batchSet({ error: err.cause });
|
|
1180
|
+
});
|
|
1181
|
+
return true;
|
|
1182
|
+
}
|
|
1101
1183
|
const opts = this.ensureMediaConstraints(options);
|
|
1102
1184
|
return this.sessionManager.answer(sessionId, opts);
|
|
1103
1185
|
}
|
|
@@ -1156,6 +1238,83 @@ var SipClient = class extends EventTargetEmitter {
|
|
|
1156
1238
|
setSessionMedia(sessionId, stream) {
|
|
1157
1239
|
this.sessionManager.setSessionMedia(sessionId, stream);
|
|
1158
1240
|
}
|
|
1241
|
+
enableMicrophoneRecovery(sessionId, options = {}) {
|
|
1242
|
+
const resolved = this.resolveExistingSessionId(sessionId);
|
|
1243
|
+
if (!resolved)
|
|
1244
|
+
return () => {
|
|
1245
|
+
};
|
|
1246
|
+
if (!this.requestMicrophoneStream)
|
|
1247
|
+
return () => {
|
|
1248
|
+
};
|
|
1249
|
+
this.disableMicrophoneRecovery(resolved);
|
|
1250
|
+
const intervalMs = options.intervalMs ?? this.micRecoveryDefaults.intervalMs;
|
|
1251
|
+
const maxRetries = options.maxRetries ?? this.micRecoveryDefaults.maxRetries;
|
|
1252
|
+
let retries = 0;
|
|
1253
|
+
let stopped = false;
|
|
1254
|
+
const tick = async () => {
|
|
1255
|
+
if (stopped || retries >= maxRetries)
|
|
1256
|
+
return;
|
|
1257
|
+
const rtc = this.sessionManager.getRtc(resolved);
|
|
1258
|
+
const session2 = this.sessionManager.getSession(resolved);
|
|
1259
|
+
if (!rtc || !session2)
|
|
1260
|
+
return;
|
|
1261
|
+
const sessionState = this.stateStore.getState().sessions.find((s) => s.id === resolved);
|
|
1262
|
+
if (sessionState?.muted)
|
|
1263
|
+
return;
|
|
1264
|
+
const stream = rtc.mediaStream;
|
|
1265
|
+
const track = stream?.getAudioTracks?.()[0];
|
|
1266
|
+
const sender = session2?.connection?.getSenders?.().find((s) => s.track?.kind === "audio");
|
|
1267
|
+
const trackLive = track?.readyState === "live";
|
|
1268
|
+
const senderLive = sender?.track?.readyState === "live";
|
|
1269
|
+
if (trackLive && senderLive)
|
|
1270
|
+
return;
|
|
1271
|
+
retries += 1;
|
|
1272
|
+
let nextStream;
|
|
1273
|
+
try {
|
|
1274
|
+
if (!this.requestMicrophoneStream)
|
|
1275
|
+
return;
|
|
1276
|
+
nextStream = await this.requestMicrophoneStream();
|
|
1277
|
+
} catch (err) {
|
|
1278
|
+
console.warn("[sip] mic recovery failed to get stream", err);
|
|
1279
|
+
return;
|
|
1280
|
+
}
|
|
1281
|
+
const nextTrack = nextStream.getAudioTracks()[0];
|
|
1282
|
+
if (!nextTrack)
|
|
1283
|
+
return;
|
|
1284
|
+
await rtc.replaceAudioTrack(nextTrack);
|
|
1285
|
+
this.sessionManager.setSessionMedia(resolved, nextStream);
|
|
1286
|
+
};
|
|
1287
|
+
const timer = setInterval(() => {
|
|
1288
|
+
void tick();
|
|
1289
|
+
}, intervalMs);
|
|
1290
|
+
void tick();
|
|
1291
|
+
const session = this.sessionManager.getSession(resolved);
|
|
1292
|
+
const pc = session?.connection;
|
|
1293
|
+
const onIceChange = () => {
|
|
1294
|
+
const state = pc?.iceConnectionState;
|
|
1295
|
+
if (state === "failed" || state === "disconnected")
|
|
1296
|
+
void tick();
|
|
1297
|
+
};
|
|
1298
|
+
pc?.addEventListener?.("iceconnectionstatechange", onIceChange);
|
|
1299
|
+
const stop = () => {
|
|
1300
|
+
stopped = true;
|
|
1301
|
+
clearInterval(timer);
|
|
1302
|
+
pc?.removeEventListener?.("iceconnectionstatechange", onIceChange);
|
|
1303
|
+
};
|
|
1304
|
+
this.micRecovery.set(resolved, { stop });
|
|
1305
|
+
return stop;
|
|
1306
|
+
}
|
|
1307
|
+
disableMicrophoneRecovery(sessionId) {
|
|
1308
|
+
const resolved = this.resolveExistingSessionId(sessionId);
|
|
1309
|
+
if (!resolved)
|
|
1310
|
+
return false;
|
|
1311
|
+
const entry = this.micRecovery.get(resolved);
|
|
1312
|
+
if (!entry)
|
|
1313
|
+
return false;
|
|
1314
|
+
entry.stop();
|
|
1315
|
+
this.micRecovery.delete(resolved);
|
|
1316
|
+
return true;
|
|
1317
|
+
}
|
|
1159
1318
|
switchCameraSession(sessionId, track) {
|
|
1160
1319
|
if (!this.sessionExists(sessionId))
|
|
1161
1320
|
return false;
|
|
@@ -1247,8 +1406,6 @@ var SipClient = class extends EventTargetEmitter {
|
|
|
1247
1406
|
const persisted = window.sessionStorage.getItem(SESSION_DEBUG_KEY);
|
|
1248
1407
|
if (!persisted)
|
|
1249
1408
|
return void 0;
|
|
1250
|
-
if (persisted === "true")
|
|
1251
|
-
return true;
|
|
1252
1409
|
return persisted;
|
|
1253
1410
|
} catch {
|
|
1254
1411
|
return void 0;
|
|
@@ -1273,9 +1430,9 @@ function createSipEventManager(client) {
|
|
|
1273
1430
|
}
|
|
1274
1431
|
};
|
|
1275
1432
|
}
|
|
1276
|
-
var SipContext =
|
|
1433
|
+
var SipContext = React.createContext(null);
|
|
1277
1434
|
function useSip() {
|
|
1278
|
-
const ctx =
|
|
1435
|
+
const ctx = React.useContext(SipContext);
|
|
1279
1436
|
if (!ctx)
|
|
1280
1437
|
throw new Error("Must be used within SipProvider");
|
|
1281
1438
|
return ctx;
|
|
@@ -1284,16 +1441,16 @@ function useSip() {
|
|
|
1284
1441
|
// src/hooks/useSipState.ts
|
|
1285
1442
|
function useSipState() {
|
|
1286
1443
|
const { client } = useSip();
|
|
1287
|
-
const subscribe =
|
|
1444
|
+
const subscribe = React.useCallback(
|
|
1288
1445
|
(onStoreChange) => client.onChange(onStoreChange),
|
|
1289
1446
|
[client]
|
|
1290
1447
|
);
|
|
1291
|
-
const getSnapshot =
|
|
1292
|
-
return
|
|
1448
|
+
const getSnapshot = React.useCallback(() => client.state, [client]);
|
|
1449
|
+
return React.useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
1293
1450
|
}
|
|
1294
1451
|
function useSipActions() {
|
|
1295
1452
|
const { client } = useSip();
|
|
1296
|
-
return
|
|
1453
|
+
return React.useMemo(
|
|
1297
1454
|
() => ({
|
|
1298
1455
|
call: (...args) => client.call(...args),
|
|
1299
1456
|
answer: (...args) => client.answerSession(...args),
|
|
@@ -1306,6 +1463,8 @@ function useSipActions() {
|
|
|
1306
1463
|
getSessionIds: () => client.getSessionIds(),
|
|
1307
1464
|
getSessions: () => client.getSessions(),
|
|
1308
1465
|
setSessionMedia: (...args) => client.setSessionMedia(...args),
|
|
1466
|
+
enableMicrophoneRecovery: (...args) => client.enableMicrophoneRecovery(...args),
|
|
1467
|
+
disableMicrophoneRecovery: (...args) => client.disableMicrophoneRecovery(...args),
|
|
1309
1468
|
switchCamera: (...args) => client.switchCameraSession(...args),
|
|
1310
1469
|
enableVideo: (...args) => client.enableVideoSession(...args),
|
|
1311
1470
|
disableVideo: (...args) => client.disableVideoSession(...args)
|
|
@@ -1321,7 +1480,7 @@ function useSipSessions() {
|
|
|
1321
1480
|
}
|
|
1322
1481
|
function useSipEvent(event, handler) {
|
|
1323
1482
|
const { sipEventManager } = useSip();
|
|
1324
|
-
|
|
1483
|
+
React.useEffect(() => {
|
|
1325
1484
|
if (!handler)
|
|
1326
1485
|
return;
|
|
1327
1486
|
return sipEventManager.onUA(event, handler);
|
|
@@ -1329,7 +1488,7 @@ function useSipEvent(event, handler) {
|
|
|
1329
1488
|
}
|
|
1330
1489
|
function useSipSessionEvent(sessionId, event, handler) {
|
|
1331
1490
|
const { sipEventManager } = useSip();
|
|
1332
|
-
|
|
1491
|
+
React.useEffect(() => {
|
|
1333
1492
|
if (!handler)
|
|
1334
1493
|
return;
|
|
1335
1494
|
return sipEventManager.onSession(sessionId, event, handler);
|
|
@@ -1427,8 +1586,8 @@ function createCallPlayer(audioEl) {
|
|
|
1427
1586
|
}
|
|
1428
1587
|
function CallPlayer({ sessionId }) {
|
|
1429
1588
|
const { client } = useSip();
|
|
1430
|
-
const audioRef =
|
|
1431
|
-
|
|
1589
|
+
const audioRef = React.useRef(null);
|
|
1590
|
+
React.useEffect(() => {
|
|
1432
1591
|
if (!audioRef.current)
|
|
1433
1592
|
return;
|
|
1434
1593
|
const player = createCallPlayer(audioRef.current);
|
|
@@ -1444,13 +1603,17 @@ function CallPlayer({ sessionId }) {
|
|
|
1444
1603
|
function SipProvider({
|
|
1445
1604
|
client,
|
|
1446
1605
|
children,
|
|
1447
|
-
sipEventManager
|
|
1606
|
+
sipEventManager,
|
|
1607
|
+
requestMicrophoneStream
|
|
1448
1608
|
}) {
|
|
1449
|
-
const manager =
|
|
1609
|
+
const manager = React.useMemo(
|
|
1450
1610
|
() => sipEventManager ?? createSipEventManager(client),
|
|
1451
1611
|
[client, sipEventManager]
|
|
1452
1612
|
);
|
|
1453
|
-
|
|
1613
|
+
React__default.default.useEffect(() => {
|
|
1614
|
+
client.setMicrophoneProvider(requestMicrophoneStream);
|
|
1615
|
+
}, [client, requestMicrophoneStream]);
|
|
1616
|
+
const contextValue = React.useMemo(() => ({ client, sipEventManager: manager }), [client, manager]);
|
|
1454
1617
|
return /* @__PURE__ */ jsxRuntime.jsx(SipContext.Provider, { value: contextValue, children });
|
|
1455
1618
|
}
|
|
1456
1619
|
|