react-jssip-kit 0.7.5 → 0.7.7

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 CHANGED
@@ -481,7 +481,6 @@ function createSessionHandlers(deps) {
481
481
  rtc,
482
482
  detachSessionHandlers,
483
483
  onSessionFailed,
484
- onSessionConfirmed,
485
484
  sessionId
486
485
  } = deps;
487
486
  return {
@@ -490,7 +489,6 @@ function createSessionHandlers(deps) {
490
489
  },
491
490
  accepted: (e) => {
492
491
  emitter.emit("accepted", e);
493
- onSessionAccepted?.(sessionId);
494
492
  state.batchSet({
495
493
  sessions: state.getState().sessions.map(
496
494
  (s) => s.id === sessionId ? {
@@ -503,7 +501,7 @@ function createSessionHandlers(deps) {
503
501
  },
504
502
  confirmed: (e) => {
505
503
  emitter.emit("confirmed", e);
506
- onSessionConfirmed?.(sessionId);
504
+ deps.enableMicrophoneRecovery?.(sessionId);
507
505
  },
508
506
  ended: (e) => {
509
507
  emitter.emit("ended", e);
@@ -615,8 +613,8 @@ var WebRTCSessionController = class {
615
613
  }
616
614
  cleanup(stopTracks = true) {
617
615
  const pc = this.getPC();
616
+ const isClosed = pc?.connectionState === "closed" || pc?.signalingState === "closed";
618
617
  if (pc && typeof pc.getSenders === "function") {
619
- const isClosed = pc.connectionState === "closed" || pc.signalingState === "closed";
620
618
  if (!isClosed) {
621
619
  for (const s of pc.getSenders()) {
622
620
  try {
@@ -627,8 +625,14 @@ var WebRTCSessionController = class {
627
625
  }
628
626
  }
629
627
  if (stopTracks && this.mediaStream) {
630
- for (const t of this.mediaStream.getTracks())
628
+ const senderTracks = pc && !isClosed ? new Set(
629
+ pc.getSenders().map((s) => s.track).filter((t) => Boolean(t))
630
+ ) : null;
631
+ for (const t of this.mediaStream.getTracks()) {
632
+ if (senderTracks?.has(t))
633
+ continue;
631
634
  t.stop();
635
+ }
632
636
  }
633
637
  this.mediaStream = null;
634
638
  this.currentSession = null;
@@ -707,29 +711,14 @@ var WebRTCSessionController = class {
707
711
  var SessionManager = class {
708
712
  constructor() {
709
713
  this.entries = /* @__PURE__ */ new Map();
710
- this.pendingMediaQueue = [];
711
- this.pendingMediaTtlMs = 3e4;
712
- }
713
- setPendingMediaTtl(ms) {
714
- if (typeof ms === "number" && ms > 0)
715
- this.pendingMediaTtlMs = ms;
716
- }
717
- enqueueOutgoingMedia(stream) {
718
- this.pendingMediaQueue.push({ stream, addedAt: Date.now() });
719
- }
720
- dequeueOutgoingMedia() {
721
- const now = Date.now();
722
- while (this.pendingMediaQueue.length) {
723
- const next = this.pendingMediaQueue.shift();
724
- if (!next)
725
- break;
726
- if (now - next.addedAt <= this.pendingMediaTtlMs) {
727
- return next.stream;
728
- } else {
729
- next.stream.getTracks().forEach((t) => t.stop());
730
- }
714
+ }
715
+ stopMediaStream(stream) {
716
+ if (!stream)
717
+ return;
718
+ for (const t of stream.getTracks()) {
719
+ if (t.readyState !== "ended")
720
+ t.stop();
731
721
  }
732
- return null;
733
722
  }
734
723
  getOrCreateRtc(sessionId, session) {
735
724
  let entry = this.entries.get(sessionId);
@@ -771,6 +760,9 @@ var SessionManager = class {
771
760
  session: null,
772
761
  media: null
773
762
  };
763
+ if (entry.media && entry.media !== stream) {
764
+ this.stopMediaStream(entry.media);
765
+ }
774
766
  entry.media = stream;
775
767
  entry.rtc.setMediaStream(stream);
776
768
  this.entries.set(sessionId, entry);
@@ -800,15 +792,16 @@ var SessionManager = class {
800
792
  const entry = this.entries.get(sessionId);
801
793
  if (entry) {
802
794
  entry.rtc.cleanup();
795
+ this.stopMediaStream(entry.media);
803
796
  this.entries.delete(sessionId);
804
797
  }
805
798
  }
806
799
  cleanupAllSessions() {
807
800
  for (const [, entry] of this.entries.entries()) {
808
801
  entry.rtc.cleanup();
802
+ this.stopMediaStream(entry.media);
809
803
  }
810
804
  this.entries.clear();
811
- this.pendingMediaQueue = [];
812
805
  }
813
806
  answer(sessionId, options) {
814
807
  const rtc = this.getRtc(sessionId);
@@ -856,35 +849,102 @@ var SessionLifecycle = class {
856
849
  }
857
850
  handleNewRTCSession(e) {
858
851
  const session = e.session;
859
- const sessionId = String(session?.id ?? crypto.randomUUID?.() ?? Date.now());
852
+ const sessionId = String(
853
+ session?.id ?? crypto.randomUUID?.() ?? Date.now()
854
+ );
860
855
  const currentSessions = this.state.getState().sessions;
861
856
  if (currentSessions.length >= this.getMaxSessionCount()) {
862
857
  try {
863
- session.terminate?.({ status_code: 486, reason_phrase: "Busy Here" });
858
+ session.terminate?.({
859
+ status_code: 486,
860
+ reason_phrase: "Busy Here"
861
+ });
864
862
  } catch {
865
863
  }
866
864
  if (e.originator === "remote") {
867
865
  this.emit("missed", e);
866
+ } else {
867
+ this.emitError(
868
+ "max session count reached",
869
+ "MAX_SESSIONS_REACHED",
870
+ "max session count reached"
871
+ );
868
872
  }
869
- this.emitError("max session count reached", "MAX_SESSIONS_REACHED", "max session count reached");
870
873
  return;
871
874
  }
872
- const outgoingMedia = e.originator === "local" ? this.sessionManager.dequeueOutgoingMedia() : null;
873
- if (outgoingMedia)
874
- this.sessionManager.setSessionMedia(sessionId, outgoingMedia);
875
875
  const rtc = this.sessionManager.getOrCreateRtc(sessionId, session);
876
- if (outgoingMedia)
877
- rtc.setMediaStream(outgoingMedia);
878
876
  this.sessionManager.setSession(sessionId, session);
879
877
  this.attachSessionHandlers(sessionId, session);
880
- holdOtherSessions(
881
- this.state,
882
- sessionId,
883
- (id) => {
884
- const otherRtc = this.sessionManager.getRtc(id);
885
- otherRtc?.hold();
878
+ if (e.originator === "local" && !rtc.mediaStream) {
879
+ const maxAttempts = 5;
880
+ const retryDelayMs = 500;
881
+ let attempts = 0;
882
+ let retryScheduled = false;
883
+ let retryTimer = null;
884
+ let stopped = false;
885
+ const tryBindFromPc = (pc) => {
886
+ if (stopped || !pc || this.sessionManager.getRtc(sessionId)?.mediaStream) {
887
+ return false;
888
+ }
889
+ const audioSender = pc?.getSenders?.()?.find((s) => s.track?.kind === "audio");
890
+ const audioTrack = audioSender?.track;
891
+ if (!audioTrack)
892
+ return false;
893
+ const outgoingStream = new MediaStream([audioTrack]);
894
+ this.sessionManager.setSessionMedia(sessionId, outgoingStream);
895
+ return true;
896
+ };
897
+ const scheduleRetry = (pc) => {
898
+ if (stopped || retryScheduled || attempts >= maxAttempts) {
899
+ session.off?.("peerconnection", onPeer);
900
+ return;
901
+ }
902
+ retryScheduled = true;
903
+ attempts += 1;
904
+ retryTimer = setTimeout(() => {
905
+ retryScheduled = false;
906
+ retryTimer = null;
907
+ if (tryBindFromPc(pc)) {
908
+ session.off?.("peerconnection", onPeer);
909
+ return;
910
+ }
911
+ scheduleRetry(pc);
912
+ }, retryDelayMs);
913
+ };
914
+ const onPeer = (data) => {
915
+ if (stopped)
916
+ return;
917
+ if (tryBindFromPc(data.peerconnection)) {
918
+ session.off?.("peerconnection", onPeer);
919
+ return;
920
+ }
921
+ scheduleRetry(data.peerconnection);
922
+ };
923
+ const stopRetry = () => {
924
+ if (stopped)
925
+ return;
926
+ stopped = true;
927
+ if (retryTimer) {
928
+ clearTimeout(retryTimer);
929
+ retryTimer = null;
930
+ }
931
+ session.off?.("peerconnection", onPeer);
932
+ session.off?.("ended", stopRetry);
933
+ session.off?.("failed", stopRetry);
934
+ };
935
+ const existingPc = session?.connection;
936
+ if (!tryBindFromPc(existingPc)) {
937
+ if (existingPc)
938
+ scheduleRetry(existingPc);
939
+ session.on?.("peerconnection", onPeer);
886
940
  }
887
- );
941
+ session.on?.("ended", stopRetry);
942
+ session.on?.("failed", stopRetry);
943
+ }
944
+ holdOtherSessions(this.state, sessionId, (id) => {
945
+ const otherRtc = this.sessionManager.getRtc(id);
946
+ otherRtc?.hold();
947
+ });
888
948
  const sdpHasVideo = e.request?.body && e.request.body.toString().includes("m=video") || session?.connection?.getReceivers?.()?.some((r) => r.track?.kind === "video");
889
949
  upsertSessionState(this.state, sessionId, {
890
950
  direction: e.originator,
@@ -898,6 +958,126 @@ var SessionLifecycle = class {
898
958
  }
899
959
  };
900
960
 
961
+ // src/jssip-lib/sip/micRecovery.ts
962
+ var MicRecoveryManager = class {
963
+ constructor(deps) {
964
+ this.enabled = false;
965
+ this.defaults = {
966
+ intervalMs: 2e3,
967
+ maxRetries: Infinity
968
+ };
969
+ this.active = /* @__PURE__ */ new Map();
970
+ this.deps = deps;
971
+ }
972
+ configure(config) {
973
+ if (typeof config.enabled === "boolean") {
974
+ this.enabled = config.enabled;
975
+ }
976
+ if (typeof config.intervalMs === "number") {
977
+ this.defaults.intervalMs = config.intervalMs;
978
+ }
979
+ if (typeof config.maxRetries === "number") {
980
+ this.defaults.maxRetries = config.maxRetries;
981
+ }
982
+ }
983
+ enable(sessionId, options = {}) {
984
+ if (!this.enabled)
985
+ return () => {
986
+ };
987
+ this.disable(sessionId);
988
+ const intervalMs = options.intervalMs ?? this.defaults.intervalMs;
989
+ const maxRetries = options.maxRetries ?? this.defaults.maxRetries;
990
+ let retries = 0;
991
+ let stopped = false;
992
+ const startedAt = Date.now();
993
+ const warmupMs = Math.max(intervalMs * 2, 2e3);
994
+ const tick = async () => {
995
+ if (stopped || retries >= maxRetries)
996
+ return;
997
+ const rtc = this.deps.getRtc(sessionId);
998
+ const session2 = this.deps.getSession(sessionId);
999
+ if (!rtc || !session2)
1000
+ return;
1001
+ const sessionState = this.deps.getSessionState(sessionId);
1002
+ if (sessionState?.muted)
1003
+ return;
1004
+ const stream = rtc.mediaStream;
1005
+ const track = stream?.getAudioTracks?.()[0];
1006
+ const pc2 = session2?.connection;
1007
+ const sender = pc2?.getSenders?.()?.find((s) => s.track?.kind === "audio");
1008
+ if (!track && !sender)
1009
+ return;
1010
+ if (Date.now() - startedAt < warmupMs)
1011
+ return;
1012
+ if (pc2?.connectionState === "new" || pc2?.connectionState === "connecting" || pc2?.iceConnectionState === "new" || pc2?.iceConnectionState === "checking") {
1013
+ return;
1014
+ }
1015
+ const trackLive = track?.readyState === "live";
1016
+ const senderLive = sender?.track?.readyState === "live";
1017
+ if (trackLive && senderLive)
1018
+ return;
1019
+ this.deps.emitError(
1020
+ {
1021
+ cause: "microphone dropped",
1022
+ trackLive,
1023
+ senderLive
1024
+ },
1025
+ "MICROPHONE_DROPPED",
1026
+ "microphone dropped"
1027
+ );
1028
+ retries += 1;
1029
+ if (trackLive && !senderLive && track) {
1030
+ await rtc.replaceAudioTrack(track);
1031
+ return;
1032
+ }
1033
+ let nextStream;
1034
+ try {
1035
+ const deviceId = track?.getSettings?.().deviceId ?? sender?.track?.getSettings?.().deviceId;
1036
+ nextStream = await this.deps.requestMicrophoneStream(deviceId);
1037
+ } catch (err) {
1038
+ console.warn("[sip] mic recovery failed to get stream", err);
1039
+ return;
1040
+ }
1041
+ const nextTrack = nextStream.getAudioTracks()[0];
1042
+ if (!nextTrack)
1043
+ return;
1044
+ await rtc.replaceAudioTrack(nextTrack);
1045
+ this.deps.setSessionMedia(sessionId, nextStream);
1046
+ };
1047
+ const timer = setInterval(() => {
1048
+ void tick();
1049
+ }, intervalMs);
1050
+ void tick();
1051
+ const session = this.deps.getSession(sessionId);
1052
+ const pc = session?.connection;
1053
+ const onIceChange = () => {
1054
+ const state = pc?.iceConnectionState;
1055
+ if (state === "failed" || state === "disconnected")
1056
+ void tick();
1057
+ };
1058
+ pc?.addEventListener?.("iceconnectionstatechange", onIceChange);
1059
+ const stop = () => {
1060
+ stopped = true;
1061
+ clearInterval(timer);
1062
+ pc?.removeEventListener?.("iceconnectionstatechange", onIceChange);
1063
+ };
1064
+ this.active.set(sessionId, { stop });
1065
+ return stop;
1066
+ }
1067
+ disable(sessionId) {
1068
+ const entry = this.active.get(sessionId);
1069
+ if (!entry)
1070
+ return false;
1071
+ entry.stop();
1072
+ this.active.delete(sessionId);
1073
+ return true;
1074
+ }
1075
+ cleanupAll() {
1076
+ this.active.forEach((entry) => entry.stop());
1077
+ this.active.clear();
1078
+ }
1079
+ };
1080
+
901
1081
  // src/jssip-lib/sip/client.ts
902
1082
  var SESSION_DEBUG_KEY = "sip-debug-enabled";
903
1083
  var SipClient = class extends EventTargetEmitter {
@@ -908,12 +1088,6 @@ var SipClient = class extends EventTargetEmitter {
908
1088
  this.sessionHandlers = /* @__PURE__ */ new Map();
909
1089
  this.maxSessionCount = Infinity;
910
1090
  this.sessionManager = new SessionManager();
911
- this.micRecovery = /* @__PURE__ */ new Map();
912
- this.micRecoveryEnabled = false;
913
- this.micRecoveryDefaults = {
914
- intervalMs: 2e3,
915
- maxRetries: Infinity
916
- };
917
1091
  this.errorHandler = options.errorHandler ?? new SipErrorHandler({
918
1092
  formatter: options.formatError,
919
1093
  messages: options.errorMessages
@@ -935,6 +1109,14 @@ var SipClient = class extends EventTargetEmitter {
935
1109
  attachSessionHandlers: (sessionId, session) => this.attachSessionHandlers(sessionId, session),
936
1110
  getMaxSessionCount: () => this.maxSessionCount
937
1111
  });
1112
+ this.micRecovery = new MicRecoveryManager({
1113
+ getRtc: (sessionId) => this.sessionManager.getRtc(sessionId),
1114
+ getSession: (sessionId) => this.sessionManager.getSession(sessionId),
1115
+ getSessionState: (sessionId) => this.stateStore.getState().sessions.find((s) => s.id === sessionId),
1116
+ setSessionMedia: (sessionId, stream) => this.sessionManager.setSessionMedia(sessionId, stream),
1117
+ emitError: (raw, code, fallback) => this.emitError(raw, code, fallback),
1118
+ requestMicrophoneStream: (deviceId) => this.requestMicrophoneStreamInternal(deviceId)
1119
+ });
938
1120
  if (typeof window !== "undefined") {
939
1121
  window.sipDebugBridge = (debug) => this.setDebug(debug ?? true);
940
1122
  }
@@ -951,18 +1133,14 @@ var SipClient = class extends EventTargetEmitter {
951
1133
  micRecoveryIntervalMs,
952
1134
  micRecoveryMaxRetries,
953
1135
  maxSessionCount,
954
- pendingMediaTtlMs,
955
1136
  ...uaCfg
956
1137
  } = config;
957
1138
  this.maxSessionCount = typeof maxSessionCount === "number" ? maxSessionCount : Infinity;
958
- this.micRecoveryEnabled = Boolean(enableMicRecovery);
959
- if (typeof micRecoveryIntervalMs === "number") {
960
- this.micRecoveryDefaults.intervalMs = micRecoveryIntervalMs;
961
- }
962
- if (typeof micRecoveryMaxRetries === "number") {
963
- this.micRecoveryDefaults.maxRetries = micRecoveryMaxRetries;
964
- }
965
- this.sessionManager.setPendingMediaTtl(pendingMediaTtlMs);
1139
+ this.micRecovery.configure({
1140
+ enabled: Boolean(enableMicRecovery),
1141
+ intervalMs: micRecoveryIntervalMs,
1142
+ maxRetries: micRecoveryMaxRetries
1143
+ });
966
1144
  const debug = cfgDebug ?? this.getPersistedDebug() ?? this.debugPattern;
967
1145
  this.userAgent.start(uri, password, uaCfg, { debug });
968
1146
  this.attachUAHandlers();
@@ -982,10 +1160,15 @@ var SipClient = class extends EventTargetEmitter {
982
1160
  call(target, callOptions = {}) {
983
1161
  try {
984
1162
  const opts = this.ensureMediaConstraints(callOptions);
985
- if (opts.mediaStream)
986
- this.sessionManager.enqueueOutgoingMedia(opts.mediaStream);
987
1163
  const ua = this.userAgent.getUA();
988
- ua?.call(target, opts);
1164
+ const session = ua?.call(target, opts);
1165
+ if (session && opts.mediaStream) {
1166
+ const sessionId = String(session?.id ?? "");
1167
+ if (sessionId) {
1168
+ this.sessionManager.setSessionMedia(sessionId, opts.mediaStream);
1169
+ this.sessionManager.setSession(sessionId, session);
1170
+ }
1171
+ }
989
1172
  } catch (e) {
990
1173
  const err = this.emitError(e, "CALL_FAILED", "call failed");
991
1174
  this.cleanupAllSessions();
@@ -1075,14 +1258,13 @@ var SipClient = class extends EventTargetEmitter {
1075
1258
  cleanupSession(sessionId, session) {
1076
1259
  const targetSession = session ?? this.sessionManager.getSession(sessionId) ?? this.sessionManager.getRtc(sessionId)?.currentSession;
1077
1260
  this.detachSessionHandlers(sessionId, targetSession);
1078
- this.disableMicrophoneRecovery(sessionId);
1261
+ this.micRecovery.disable(sessionId);
1079
1262
  this.sessionManager.cleanupSession(sessionId);
1080
1263
  removeSessionState(this.stateStore, sessionId);
1081
1264
  }
1082
1265
  cleanupAllSessions() {
1083
1266
  this.sessionManager.cleanupAllSessions();
1084
- this.micRecovery.forEach((entry) => entry.stop());
1085
- this.micRecovery.clear();
1267
+ this.micRecovery.cleanupAll();
1086
1268
  this.sessionHandlers.clear();
1087
1269
  this.stateStore.setState({
1088
1270
  sessions: [],
@@ -1096,7 +1278,9 @@ var SipClient = class extends EventTargetEmitter {
1096
1278
  state: this.stateStore,
1097
1279
  rtc,
1098
1280
  detachSessionHandlers: () => this.cleanupSession(sessionId, session),
1281
+ emitError: (raw, code, fallback) => this.emitError(raw, code, fallback),
1099
1282
  onSessionFailed: (err, event) => this.onSessionFailed(err, event),
1283
+ enableMicrophoneRecovery: (confirmedSessionId) => this.micRecovery.enable(confirmedSessionId),
1100
1284
  sessionId
1101
1285
  });
1102
1286
  }
@@ -1204,102 +1388,6 @@ var SipClient = class extends EventTargetEmitter {
1204
1388
  setSessionMedia(sessionId, stream) {
1205
1389
  this.sessionManager.setSessionMedia(sessionId, stream);
1206
1390
  }
1207
- enableMicrophoneRecovery(sessionId, options = {}) {
1208
- const resolved = this.resolveExistingSessionId(sessionId);
1209
- if (!resolved)
1210
- return () => {
1211
- };
1212
- this.disableMicrophoneRecovery(resolved);
1213
- const intervalMs = options.intervalMs ?? this.micRecoveryDefaults.intervalMs;
1214
- const maxRetries = options.maxRetries ?? this.micRecoveryDefaults.maxRetries;
1215
- let retries = 0;
1216
- let stopped = false;
1217
- const startedAt = Date.now();
1218
- const warmupMs = Math.max(intervalMs * 2, 2e3);
1219
- const tick = async () => {
1220
- if (stopped || retries >= maxRetries)
1221
- return;
1222
- const rtc = this.sessionManager.getRtc(resolved);
1223
- const session2 = this.sessionManager.getSession(resolved);
1224
- if (!rtc || !session2)
1225
- return;
1226
- const sessionState = this.stateStore.getState().sessions.find((s) => s.id === resolved);
1227
- if (sessionState?.muted)
1228
- return;
1229
- const stream = rtc.mediaStream;
1230
- const track = stream?.getAudioTracks?.()[0];
1231
- const pc2 = session2?.connection;
1232
- const sender = pc2?.getSenders?.().find((s) => s.track?.kind === "audio");
1233
- if (!track && !sender)
1234
- return;
1235
- if (Date.now() - startedAt < warmupMs)
1236
- return;
1237
- if (pc2?.connectionState === "new" || pc2?.connectionState === "connecting" || pc2?.iceConnectionState === "new" || pc2?.iceConnectionState === "checking") {
1238
- return;
1239
- }
1240
- const trackLive = track?.readyState === "live";
1241
- const senderLive = sender?.track?.readyState === "live";
1242
- if (trackLive && senderLive)
1243
- return;
1244
- this.emitError(
1245
- {
1246
- cause: "microphone dropped",
1247
- trackLive,
1248
- senderLive
1249
- },
1250
- "MICROPHONE_DROPPED",
1251
- "microphone dropped"
1252
- );
1253
- retries += 1;
1254
- if (trackLive && !senderLive && track) {
1255
- await rtc.replaceAudioTrack(track);
1256
- return;
1257
- }
1258
- let nextStream;
1259
- try {
1260
- const deviceId = track?.getSettings?.().deviceId ?? sender?.track?.getSettings?.().deviceId;
1261
- nextStream = await this.requestMicrophoneStreamInternal(deviceId);
1262
- } catch (err) {
1263
- console.warn("[sip] mic recovery failed to get stream", err);
1264
- return;
1265
- }
1266
- const nextTrack = nextStream.getAudioTracks()[0];
1267
- if (!nextTrack)
1268
- return;
1269
- await rtc.replaceAudioTrack(nextTrack);
1270
- this.sessionManager.setSessionMedia(resolved, nextStream);
1271
- };
1272
- const timer = setInterval(() => {
1273
- void tick();
1274
- }, intervalMs);
1275
- void tick();
1276
- const session = this.sessionManager.getSession(resolved);
1277
- const pc = session?.connection;
1278
- const onIceChange = () => {
1279
- const state = pc?.iceConnectionState;
1280
- if (state === "failed" || state === "disconnected")
1281
- void tick();
1282
- };
1283
- pc?.addEventListener?.("iceconnectionstatechange", onIceChange);
1284
- const stop = () => {
1285
- stopped = true;
1286
- clearInterval(timer);
1287
- pc?.removeEventListener?.("iceconnectionstatechange", onIceChange);
1288
- };
1289
- this.micRecovery.set(resolved, { stop });
1290
- return stop;
1291
- }
1292
- disableMicrophoneRecovery(sessionId) {
1293
- const resolved = this.resolveExistingSessionId(sessionId);
1294
- if (!resolved)
1295
- return false;
1296
- const entry = this.micRecovery.get(resolved);
1297
- if (!entry)
1298
- return false;
1299
- entry.stop();
1300
- this.micRecovery.delete(resolved);
1301
- return true;
1302
- }
1303
1391
  switchCameraSession(sessionId, track) {
1304
1392
  if (!this.sessionExists(sessionId))
1305
1393
  return false;
@@ -1465,8 +1553,6 @@ function useSipActions() {
1465
1553
  getSessionIds: () => client.getSessionIds(),
1466
1554
  getSessions: () => client.getSessions(),
1467
1555
  setSessionMedia: (...args) => client.setSessionMedia(...args),
1468
- enableMicrophoneRecovery: (...args) => client.enableMicrophoneRecovery(...args),
1469
- disableMicrophoneRecovery: (...args) => client.disableMicrophoneRecovery(...args),
1470
1556
  switchCamera: (...args) => client.switchCameraSession(...args),
1471
1557
  enableVideo: (...args) => client.enableVideoSession(...args),
1472
1558
  disableVideo: (...args) => client.disableVideoSession(...args)
@@ -1540,6 +1626,7 @@ function createCallPlayer(audioEl) {
1540
1626
  return () => session.off("peerconnection", onPeer);
1541
1627
  };
1542
1628
  function bindToSession(session) {
1629
+ clearAudioStream(audioEl.srcObject);
1543
1630
  if (session?.direction === "outgoing" && session.connection instanceof RTCPeerConnection) {
1544
1631
  cleanupTrackListener = dispose(cleanupTrackListener);
1545
1632
  cleanupTrackListener = attachTracks(session.connection);
@@ -1556,6 +1643,7 @@ function createCallPlayer(audioEl) {
1556
1643
  const e = payload?.data;
1557
1644
  cleanupSessionPeerListener = dispose(cleanupSessionPeerListener);
1558
1645
  cleanupTrackListener = dispose(cleanupTrackListener);
1646
+ clearAudioStream(audioEl.srcObject);
1559
1647
  if (!e?.session)
1560
1648
  return;
1561
1649
  cleanupSessionPeerListener = listenSessionPeerconnection(e.session);