react-jssip-kit 0.7.6 → 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 {
@@ -502,7 +501,7 @@ function createSessionHandlers(deps) {
502
501
  },
503
502
  confirmed: (e) => {
504
503
  emitter.emit("confirmed", e);
505
- onSessionConfirmed?.(sessionId);
504
+ deps.enableMicrophoneRecovery?.(sessionId);
506
505
  },
507
506
  ended: (e) => {
508
507
  emitter.emit("ended", e);
@@ -614,8 +613,8 @@ var WebRTCSessionController = class {
614
613
  }
615
614
  cleanup(stopTracks = true) {
616
615
  const pc = this.getPC();
616
+ const isClosed = pc?.connectionState === "closed" || pc?.signalingState === "closed";
617
617
  if (pc && typeof pc.getSenders === "function") {
618
- const isClosed = pc.connectionState === "closed" || pc.signalingState === "closed";
619
618
  if (!isClosed) {
620
619
  for (const s of pc.getSenders()) {
621
620
  try {
@@ -626,8 +625,14 @@ var WebRTCSessionController = class {
626
625
  }
627
626
  }
628
627
  if (stopTracks && this.mediaStream) {
629
- 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;
630
634
  t.stop();
635
+ }
631
636
  }
632
637
  this.mediaStream = null;
633
638
  this.currentSession = null;
@@ -706,29 +711,14 @@ var WebRTCSessionController = class {
706
711
  var SessionManager = class {
707
712
  constructor() {
708
713
  this.entries = /* @__PURE__ */ new Map();
709
- this.pendingMediaQueue = [];
710
- this.pendingMediaTtlMs = 3e4;
711
- }
712
- setPendingMediaTtl(ms) {
713
- if (typeof ms === "number" && ms > 0)
714
- this.pendingMediaTtlMs = ms;
715
- }
716
- enqueueOutgoingMedia(stream) {
717
- this.pendingMediaQueue.push({ stream, addedAt: Date.now() });
718
- }
719
- dequeueOutgoingMedia() {
720
- const now = Date.now();
721
- while (this.pendingMediaQueue.length) {
722
- const next = this.pendingMediaQueue.shift();
723
- if (!next)
724
- break;
725
- if (now - next.addedAt <= this.pendingMediaTtlMs) {
726
- return next.stream;
727
- } else {
728
- next.stream.getTracks().forEach((t) => t.stop());
729
- }
714
+ }
715
+ stopMediaStream(stream) {
716
+ if (!stream)
717
+ return;
718
+ for (const t of stream.getTracks()) {
719
+ if (t.readyState !== "ended")
720
+ t.stop();
730
721
  }
731
- return null;
732
722
  }
733
723
  getOrCreateRtc(sessionId, session) {
734
724
  let entry = this.entries.get(sessionId);
@@ -770,6 +760,9 @@ var SessionManager = class {
770
760
  session: null,
771
761
  media: null
772
762
  };
763
+ if (entry.media && entry.media !== stream) {
764
+ this.stopMediaStream(entry.media);
765
+ }
773
766
  entry.media = stream;
774
767
  entry.rtc.setMediaStream(stream);
775
768
  this.entries.set(sessionId, entry);
@@ -799,15 +792,16 @@ var SessionManager = class {
799
792
  const entry = this.entries.get(sessionId);
800
793
  if (entry) {
801
794
  entry.rtc.cleanup();
795
+ this.stopMediaStream(entry.media);
802
796
  this.entries.delete(sessionId);
803
797
  }
804
798
  }
805
799
  cleanupAllSessions() {
806
800
  for (const [, entry] of this.entries.entries()) {
807
801
  entry.rtc.cleanup();
802
+ this.stopMediaStream(entry.media);
808
803
  }
809
804
  this.entries.clear();
810
- this.pendingMediaQueue = [];
811
805
  }
812
806
  answer(sessionId, options) {
813
807
  const rtc = this.getRtc(sessionId);
@@ -855,35 +849,102 @@ var SessionLifecycle = class {
855
849
  }
856
850
  handleNewRTCSession(e) {
857
851
  const session = e.session;
858
- const sessionId = String(session?.id ?? crypto.randomUUID?.() ?? Date.now());
852
+ const sessionId = String(
853
+ session?.id ?? crypto.randomUUID?.() ?? Date.now()
854
+ );
859
855
  const currentSessions = this.state.getState().sessions;
860
856
  if (currentSessions.length >= this.getMaxSessionCount()) {
861
857
  try {
862
- session.terminate?.({ status_code: 486, reason_phrase: "Busy Here" });
858
+ session.terminate?.({
859
+ status_code: 486,
860
+ reason_phrase: "Busy Here"
861
+ });
863
862
  } catch {
864
863
  }
865
864
  if (e.originator === "remote") {
866
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
+ );
867
872
  }
868
- this.emitError("max session count reached", "MAX_SESSIONS_REACHED", "max session count reached");
869
873
  return;
870
874
  }
871
- const outgoingMedia = e.originator === "local" ? this.sessionManager.dequeueOutgoingMedia() : null;
872
- if (outgoingMedia)
873
- this.sessionManager.setSessionMedia(sessionId, outgoingMedia);
874
875
  const rtc = this.sessionManager.getOrCreateRtc(sessionId, session);
875
- if (outgoingMedia)
876
- rtc.setMediaStream(outgoingMedia);
877
876
  this.sessionManager.setSession(sessionId, session);
878
877
  this.attachSessionHandlers(sessionId, session);
879
- holdOtherSessions(
880
- this.state,
881
- sessionId,
882
- (id) => {
883
- const otherRtc = this.sessionManager.getRtc(id);
884
- 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);
885
940
  }
886
- );
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
+ });
887
948
  const sdpHasVideo = e.request?.body && e.request.body.toString().includes("m=video") || session?.connection?.getReceivers?.()?.some((r) => r.track?.kind === "video");
888
949
  upsertSessionState(this.state, sessionId, {
889
950
  direction: e.originator,
@@ -897,6 +958,126 @@ var SessionLifecycle = class {
897
958
  }
898
959
  };
899
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
+
900
1081
  // src/jssip-lib/sip/client.ts
901
1082
  var SESSION_DEBUG_KEY = "sip-debug-enabled";
902
1083
  var SipClient = class extends EventTargetEmitter {
@@ -907,12 +1088,6 @@ var SipClient = class extends EventTargetEmitter {
907
1088
  this.sessionHandlers = /* @__PURE__ */ new Map();
908
1089
  this.maxSessionCount = Infinity;
909
1090
  this.sessionManager = new SessionManager();
910
- this.micRecovery = /* @__PURE__ */ new Map();
911
- this.micRecoveryEnabled = false;
912
- this.micRecoveryDefaults = {
913
- intervalMs: 2e3,
914
- maxRetries: Infinity
915
- };
916
1091
  this.errorHandler = options.errorHandler ?? new SipErrorHandler({
917
1092
  formatter: options.formatError,
918
1093
  messages: options.errorMessages
@@ -934,6 +1109,14 @@ var SipClient = class extends EventTargetEmitter {
934
1109
  attachSessionHandlers: (sessionId, session) => this.attachSessionHandlers(sessionId, session),
935
1110
  getMaxSessionCount: () => this.maxSessionCount
936
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
+ });
937
1120
  if (typeof window !== "undefined") {
938
1121
  window.sipDebugBridge = (debug) => this.setDebug(debug ?? true);
939
1122
  }
@@ -950,18 +1133,14 @@ var SipClient = class extends EventTargetEmitter {
950
1133
  micRecoveryIntervalMs,
951
1134
  micRecoveryMaxRetries,
952
1135
  maxSessionCount,
953
- pendingMediaTtlMs,
954
1136
  ...uaCfg
955
1137
  } = config;
956
1138
  this.maxSessionCount = typeof maxSessionCount === "number" ? maxSessionCount : Infinity;
957
- this.micRecoveryEnabled = Boolean(enableMicRecovery);
958
- if (typeof micRecoveryIntervalMs === "number") {
959
- this.micRecoveryDefaults.intervalMs = micRecoveryIntervalMs;
960
- }
961
- if (typeof micRecoveryMaxRetries === "number") {
962
- this.micRecoveryDefaults.maxRetries = micRecoveryMaxRetries;
963
- }
964
- this.sessionManager.setPendingMediaTtl(pendingMediaTtlMs);
1139
+ this.micRecovery.configure({
1140
+ enabled: Boolean(enableMicRecovery),
1141
+ intervalMs: micRecoveryIntervalMs,
1142
+ maxRetries: micRecoveryMaxRetries
1143
+ });
965
1144
  const debug = cfgDebug ?? this.getPersistedDebug() ?? this.debugPattern;
966
1145
  this.userAgent.start(uri, password, uaCfg, { debug });
967
1146
  this.attachUAHandlers();
@@ -981,10 +1160,15 @@ var SipClient = class extends EventTargetEmitter {
981
1160
  call(target, callOptions = {}) {
982
1161
  try {
983
1162
  const opts = this.ensureMediaConstraints(callOptions);
984
- if (opts.mediaStream)
985
- this.sessionManager.enqueueOutgoingMedia(opts.mediaStream);
986
1163
  const ua = this.userAgent.getUA();
987
- 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
+ }
988
1172
  } catch (e) {
989
1173
  const err = this.emitError(e, "CALL_FAILED", "call failed");
990
1174
  this.cleanupAllSessions();
@@ -1074,14 +1258,13 @@ var SipClient = class extends EventTargetEmitter {
1074
1258
  cleanupSession(sessionId, session) {
1075
1259
  const targetSession = session ?? this.sessionManager.getSession(sessionId) ?? this.sessionManager.getRtc(sessionId)?.currentSession;
1076
1260
  this.detachSessionHandlers(sessionId, targetSession);
1077
- this.disableMicrophoneRecovery(sessionId);
1261
+ this.micRecovery.disable(sessionId);
1078
1262
  this.sessionManager.cleanupSession(sessionId);
1079
1263
  removeSessionState(this.stateStore, sessionId);
1080
1264
  }
1081
1265
  cleanupAllSessions() {
1082
1266
  this.sessionManager.cleanupAllSessions();
1083
- this.micRecovery.forEach((entry) => entry.stop());
1084
- this.micRecovery.clear();
1267
+ this.micRecovery.cleanupAll();
1085
1268
  this.sessionHandlers.clear();
1086
1269
  this.stateStore.setState({
1087
1270
  sessions: [],
@@ -1095,12 +1278,9 @@ var SipClient = class extends EventTargetEmitter {
1095
1278
  state: this.stateStore,
1096
1279
  rtc,
1097
1280
  detachSessionHandlers: () => this.cleanupSession(sessionId, session),
1281
+ emitError: (raw, code, fallback) => this.emitError(raw, code, fallback),
1098
1282
  onSessionFailed: (err, event) => this.onSessionFailed(err, event),
1099
- onSessionConfirmed: (confirmedSessionId) => {
1100
- if (this.micRecoveryEnabled) {
1101
- this.enableMicrophoneRecovery(confirmedSessionId);
1102
- }
1103
- },
1283
+ enableMicrophoneRecovery: (confirmedSessionId) => this.micRecovery.enable(confirmedSessionId),
1104
1284
  sessionId
1105
1285
  });
1106
1286
  }
@@ -1208,102 +1388,6 @@ var SipClient = class extends EventTargetEmitter {
1208
1388
  setSessionMedia(sessionId, stream) {
1209
1389
  this.sessionManager.setSessionMedia(sessionId, stream);
1210
1390
  }
1211
- enableMicrophoneRecovery(sessionId, options = {}) {
1212
- const resolved = this.resolveExistingSessionId(sessionId);
1213
- if (!resolved)
1214
- return () => {
1215
- };
1216
- this.disableMicrophoneRecovery(resolved);
1217
- const intervalMs = options.intervalMs ?? this.micRecoveryDefaults.intervalMs;
1218
- const maxRetries = options.maxRetries ?? this.micRecoveryDefaults.maxRetries;
1219
- let retries = 0;
1220
- let stopped = false;
1221
- const startedAt = Date.now();
1222
- const warmupMs = Math.max(intervalMs * 2, 2e3);
1223
- const tick = async () => {
1224
- if (stopped || retries >= maxRetries)
1225
- return;
1226
- const rtc = this.sessionManager.getRtc(resolved);
1227
- const session2 = this.sessionManager.getSession(resolved);
1228
- if (!rtc || !session2)
1229
- return;
1230
- const sessionState = this.stateStore.getState().sessions.find((s) => s.id === resolved);
1231
- if (sessionState?.muted)
1232
- return;
1233
- const stream = rtc.mediaStream;
1234
- const track = stream?.getAudioTracks?.()[0];
1235
- const pc2 = session2?.connection;
1236
- const sender = pc2?.getSenders?.().find((s) => s.track?.kind === "audio");
1237
- if (!track && !sender)
1238
- return;
1239
- if (Date.now() - startedAt < warmupMs)
1240
- return;
1241
- if (pc2?.connectionState === "new" || pc2?.connectionState === "connecting" || pc2?.iceConnectionState === "new" || pc2?.iceConnectionState === "checking") {
1242
- return;
1243
- }
1244
- const trackLive = track?.readyState === "live";
1245
- const senderLive = sender?.track?.readyState === "live";
1246
- if (trackLive && senderLive)
1247
- return;
1248
- this.emitError(
1249
- {
1250
- cause: "microphone dropped",
1251
- trackLive,
1252
- senderLive
1253
- },
1254
- "MICROPHONE_DROPPED",
1255
- "microphone dropped"
1256
- );
1257
- retries += 1;
1258
- if (trackLive && !senderLive && track) {
1259
- await rtc.replaceAudioTrack(track);
1260
- return;
1261
- }
1262
- let nextStream;
1263
- try {
1264
- const deviceId = track?.getSettings?.().deviceId ?? sender?.track?.getSettings?.().deviceId;
1265
- nextStream = await this.requestMicrophoneStreamInternal(deviceId);
1266
- } catch (err) {
1267
- console.warn("[sip] mic recovery failed to get stream", err);
1268
- return;
1269
- }
1270
- const nextTrack = nextStream.getAudioTracks()[0];
1271
- if (!nextTrack)
1272
- return;
1273
- await rtc.replaceAudioTrack(nextTrack);
1274
- this.sessionManager.setSessionMedia(resolved, nextStream);
1275
- };
1276
- const timer = setInterval(() => {
1277
- void tick();
1278
- }, intervalMs);
1279
- void tick();
1280
- const session = this.sessionManager.getSession(resolved);
1281
- const pc = session?.connection;
1282
- const onIceChange = () => {
1283
- const state = pc?.iceConnectionState;
1284
- if (state === "failed" || state === "disconnected")
1285
- void tick();
1286
- };
1287
- pc?.addEventListener?.("iceconnectionstatechange", onIceChange);
1288
- const stop = () => {
1289
- stopped = true;
1290
- clearInterval(timer);
1291
- pc?.removeEventListener?.("iceconnectionstatechange", onIceChange);
1292
- };
1293
- this.micRecovery.set(resolved, { stop });
1294
- return stop;
1295
- }
1296
- disableMicrophoneRecovery(sessionId) {
1297
- const resolved = this.resolveExistingSessionId(sessionId);
1298
- if (!resolved)
1299
- return false;
1300
- const entry = this.micRecovery.get(resolved);
1301
- if (!entry)
1302
- return false;
1303
- entry.stop();
1304
- this.micRecovery.delete(resolved);
1305
- return true;
1306
- }
1307
1391
  switchCameraSession(sessionId, track) {
1308
1392
  if (!this.sessionExists(sessionId))
1309
1393
  return false;
@@ -1469,8 +1553,6 @@ function useSipActions() {
1469
1553
  getSessionIds: () => client.getSessionIds(),
1470
1554
  getSessions: () => client.getSessions(),
1471
1555
  setSessionMedia: (...args) => client.setSessionMedia(...args),
1472
- enableMicrophoneRecovery: (...args) => client.enableMicrophoneRecovery(...args),
1473
- disableMicrophoneRecovery: (...args) => client.disableMicrophoneRecovery(...args),
1474
1556
  switchCamera: (...args) => client.switchCameraSession(...args),
1475
1557
  enableVideo: (...args) => client.enableVideoSession(...args),
1476
1558
  disableVideo: (...args) => client.disableVideoSession(...args)
@@ -1544,6 +1626,7 @@ function createCallPlayer(audioEl) {
1544
1626
  return () => session.off("peerconnection", onPeer);
1545
1627
  };
1546
1628
  function bindToSession(session) {
1629
+ clearAudioStream(audioEl.srcObject);
1547
1630
  if (session?.direction === "outgoing" && session.connection instanceof RTCPeerConnection) {
1548
1631
  cleanupTrackListener = dispose(cleanupTrackListener);
1549
1632
  cleanupTrackListener = attachTracks(session.connection);
@@ -1560,6 +1643,7 @@ function createCallPlayer(audioEl) {
1560
1643
  const e = payload?.data;
1561
1644
  cleanupSessionPeerListener = dispose(cleanupSessionPeerListener);
1562
1645
  cleanupTrackListener = dispose(cleanupTrackListener);
1646
+ clearAudioStream(audioEl.srcObject);
1563
1647
  if (!e?.session)
1564
1648
  return;
1565
1649
  cleanupSessionPeerListener = listenSessionPeerconnection(e.session);