@signalwire/js 4.0.0-dev-20260421201955 → 4.0.0-dev-20260422003445

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/browser.mjs CHANGED
@@ -3091,13 +3091,13 @@ var require_of = /* @__PURE__ */ __commonJSMin(((exports) => {
3091
3091
  exports.of = void 0;
3092
3092
  var args_1$12 = require_args();
3093
3093
  var from_1$7 = require_from();
3094
- function of$1() {
3094
+ function of$2() {
3095
3095
  var args = [];
3096
3096
  for (var _i = 0; _i < arguments.length; _i++) args[_i] = arguments[_i];
3097
3097
  var scheduler = args_1$12.popScheduler(args);
3098
3098
  return from_1$7.from(args, scheduler);
3099
3099
  }
3100
- exports.of = of$1;
3100
+ exports.of = of$2;
3101
3101
  }));
3102
3102
 
3103
3103
  //#endregion
@@ -3390,7 +3390,7 @@ var require_map = /* @__PURE__ */ __commonJSMin(((exports) => {
3390
3390
  exports.map = void 0;
3391
3391
  var lift_1$64 = require_lift();
3392
3392
  var OperatorSubscriber_1$54 = require_OperatorSubscriber();
3393
- function map$18(project, thisArg) {
3393
+ function map$20(project, thisArg) {
3394
3394
  return lift_1$64.operate(function(source, subscriber) {
3395
3395
  var index = 0;
3396
3396
  source.subscribe(OperatorSubscriber_1$54.createOperatorSubscriber(subscriber, function(value) {
@@ -3398,7 +3398,7 @@ var require_map = /* @__PURE__ */ __commonJSMin(((exports) => {
3398
3398
  }));
3399
3399
  });
3400
3400
  }
3401
- exports.map = map$18;
3401
+ exports.map = map$20;
3402
3402
  }));
3403
3403
 
3404
3404
  //#endregion
@@ -4169,13 +4169,13 @@ var require_interval = /* @__PURE__ */ __commonJSMin(((exports) => {
4169
4169
  exports.interval = void 0;
4170
4170
  var async_1$10 = require_async();
4171
4171
  var timer_1$6 = require_timer();
4172
- function interval$2(period, scheduler) {
4172
+ function interval$4(period, scheduler) {
4173
4173
  if (period === void 0) period = 0;
4174
4174
  if (scheduler === void 0) scheduler = async_1$10.asyncScheduler;
4175
4175
  if (period < 0) period = 0;
4176
4176
  return timer_1$6.timer(period, period, scheduler);
4177
4177
  }
4178
- exports.interval = interval$2;
4178
+ exports.interval = interval$4;
4179
4179
  }));
4180
4180
 
4181
4181
  //#endregion
@@ -5470,7 +5470,7 @@ var require_distinctUntilChanged = /* @__PURE__ */ __commonJSMin(((exports) => {
5470
5470
  var identity_1$10 = require_identity();
5471
5471
  var lift_1$42 = require_lift();
5472
5472
  var OperatorSubscriber_1$31 = require_OperatorSubscriber();
5473
- function distinctUntilChanged$8(comparator, keySelector) {
5473
+ function distinctUntilChanged$9(comparator, keySelector) {
5474
5474
  if (keySelector === void 0) keySelector = identity_1$10.identity;
5475
5475
  comparator = comparator !== null && comparator !== void 0 ? comparator : defaultCompare;
5476
5476
  return lift_1$42.operate(function(source, subscriber) {
@@ -5486,7 +5486,7 @@ var require_distinctUntilChanged = /* @__PURE__ */ __commonJSMin(((exports) => {
5486
5486
  }));
5487
5487
  });
5488
5488
  }
5489
- exports.distinctUntilChanged = distinctUntilChanged$8;
5489
+ exports.distinctUntilChanged = distinctUntilChanged$9;
5490
5490
  function defaultCompare(a, b) {
5491
5491
  return a === b;
5492
5492
  }
@@ -7182,17 +7182,17 @@ var require_timeInterval = /* @__PURE__ */ __commonJSMin(((exports) => {
7182
7182
  var last$2 = scheduler.now();
7183
7183
  source.subscribe(OperatorSubscriber_1$6.createOperatorSubscriber(subscriber, function(value) {
7184
7184
  var now = scheduler.now();
7185
- var interval$3 = now - last$2;
7185
+ var interval$5 = now - last$2;
7186
7186
  last$2 = now;
7187
- subscriber.next(new TimeInterval(value, interval$3));
7187
+ subscriber.next(new TimeInterval(value, interval$5));
7188
7188
  }));
7189
7189
  });
7190
7190
  }
7191
7191
  exports.timeInterval = timeInterval;
7192
7192
  var TimeInterval = function() {
7193
- function TimeInterval$1(value, interval$3) {
7193
+ function TimeInterval$1(value, interval$5) {
7194
7194
  this.value = value;
7195
- this.interval = interval$3;
7195
+ this.interval = interval$5;
7196
7196
  }
7197
7197
  return TimeInterval$1;
7198
7198
  }();
@@ -8933,12 +8933,12 @@ var require_cjs = /* @__PURE__ */ __commonJSMin(((exports) => {
8933
8933
 
8934
8934
  //#endregion
8935
8935
  //#region src/behaviors/Destroyable.ts
8936
- var import_cjs$28 = require_cjs();
8936
+ var import_cjs$30 = require_cjs();
8937
8937
  var Destroyable = class {
8938
8938
  constructor() {
8939
8939
  this.subscriptions = [];
8940
8940
  this.subjects = [];
8941
- this._destroyed$ = new import_cjs$28.Subject();
8941
+ this._destroyed$ = new import_cjs$30.Subject();
8942
8942
  }
8943
8943
  destroy() {
8944
8944
  this._observableCache?.clear();
@@ -8974,7 +8974,7 @@ var Destroyable = class {
8974
8974
  this._observableCache ??= /* @__PURE__ */ new Map();
8975
8975
  let cached = this._observableCache.get(publicKey);
8976
8976
  if (!cached) {
8977
- cached = factory().pipe((0, import_cjs$28.observeOn)(import_cjs$28.asapScheduler));
8977
+ cached = factory().pipe((0, import_cjs$30.observeOn)(import_cjs$30.asapScheduler));
8978
8978
  this._observableCache.set(publicKey, cached);
8979
8979
  }
8980
8980
  return cached;
@@ -8988,24 +8988,24 @@ var Destroyable = class {
8988
8988
  * Do NOT use for observables consumed internally by the SDK.
8989
8989
  */
8990
8990
  deferEmission(observable) {
8991
- return observable.pipe((0, import_cjs$28.observeOn)(import_cjs$28.asapScheduler));
8991
+ return observable.pipe((0, import_cjs$30.observeOn)(import_cjs$30.asapScheduler));
8992
8992
  }
8993
8993
  subscribeTo(observable, observerOrNext) {
8994
8994
  const subscription = observable.subscribe(observerOrNext);
8995
8995
  this.subscriptions.push(subscription);
8996
8996
  }
8997
8997
  createSubject() {
8998
- const subject = new import_cjs$28.Subject();
8998
+ const subject = new import_cjs$30.Subject();
8999
8999
  this.subjects.push(subject);
9000
9000
  return subject;
9001
9001
  }
9002
9002
  createReplaySubject(bufferSize, windowTime$1) {
9003
- const subject = new import_cjs$28.ReplaySubject(bufferSize, windowTime$1);
9003
+ const subject = new import_cjs$30.ReplaySubject(bufferSize, windowTime$1);
9004
9004
  this.subjects.push(subject);
9005
9005
  return subject;
9006
9006
  }
9007
9007
  createBehaviorSubject(initialValue) {
9008
- const subject = new import_cjs$28.BehaviorSubject(initialValue);
9008
+ const subject = new import_cjs$30.BehaviorSubject(initialValue);
9009
9009
  this.subjects.push(subject);
9010
9010
  return subject;
9011
9011
  }
@@ -9466,9 +9466,9 @@ var require_loglevel = /* @__PURE__ */ __commonJSMin(((exports, module) => {
9466
9466
  defaultLogger$1 = new Logger();
9467
9467
  defaultLogger$1.getLogger = function getLogger$1(name) {
9468
9468
  if (typeof name !== "symbol" && typeof name !== "string" || name === "") throw new TypeError("You must supply a name when creating a logger.");
9469
- var logger$30 = _loggersByName[name];
9470
- if (!logger$30) logger$30 = _loggersByName[name] = new Logger(name, defaultLogger$1.methodFactory);
9471
- return logger$30;
9469
+ var logger$32 = _loggersByName[name];
9470
+ if (!logger$32) logger$32 = _loggersByName[name] = new Logger(name, defaultLogger$1.methodFactory);
9471
+ return logger$32;
9472
9472
  };
9473
9473
  var _log = typeof window !== undefinedType ? window.log : void 0;
9474
9474
  defaultLogger$1.noConflict = function() {
@@ -9504,8 +9504,8 @@ const defaultLoggerLevel = defaultLogger.levels.WARN;
9504
9504
  defaultLogger.setLevel(defaultLoggerLevel);
9505
9505
  let userLogger = null;
9506
9506
  /** Replace the built-in logger with a custom implementation. Pass `null` to restore defaults. */
9507
- const setLogger = (logger$30) => {
9508
- userLogger = logger$30;
9507
+ const setLogger = (logger$32) => {
9508
+ userLogger = logger$32;
9509
9509
  };
9510
9510
  let debugOptions = {};
9511
9511
  /** Configure debug options (e.g., `{ logWsTraffic: true }`). */
@@ -9549,8 +9549,8 @@ const wsTraffic = (options) => {
9549
9549
  loggerInstance.debug(`${options.type.toUpperCase()}: \n`, msg, "\n");
9550
9550
  };
9551
9551
  const getLogger = () => {
9552
- const logger$30 = getLoggerInstance();
9553
- return new Proxy(logger$30, { get(_target, prop, _receiver) {
9552
+ const logger$32 = getLoggerInstance();
9553
+ return new Proxy(logger$32, { get(_target, prop, _receiver) {
9554
9554
  if (prop === "wsTraffic") return wsTraffic;
9555
9555
  const instance = getLoggerInstance();
9556
9556
  const value = Reflect.get(instance, prop);
@@ -9602,7 +9602,7 @@ const asyncRetry = async ({ asyncCallable, maxRetries: retries = DEFAULT_MAX_RET
9602
9602
 
9603
9603
  //#endregion
9604
9604
  //#region src/controllers/HTTPRequestController.ts
9605
- const logger$29 = getLogger();
9605
+ const logger$31 = getLogger();
9606
9606
  const GET_PARAMS = {
9607
9607
  method: "GET",
9608
9608
  headers: { Accept: "application/json" }
@@ -9666,7 +9666,7 @@ var HTTPRequestController = class HTTPRequestController extends Destroyable {
9666
9666
  this._responses$.next(response);
9667
9667
  return response;
9668
9668
  } catch (error) {
9669
- logger$29.error("[HTTPRequestController] Request error:", error);
9669
+ logger$31.error("[HTTPRequestController] Request error:", error);
9670
9670
  this._status$.next("error");
9671
9671
  const err = error instanceof Error ? error : new Error("HTTP request failed", { cause: error });
9672
9672
  this._errors$.next(err);
@@ -9693,7 +9693,7 @@ var HTTPRequestController = class HTTPRequestController extends Destroyable {
9693
9693
  const url = this.buildURL(request.url);
9694
9694
  const headers = this.buildHeaders(request.headers);
9695
9695
  const timeout$5 = request.timeout ?? this.requestTimeout;
9696
- logger$29.debug("[HTTPRequestController] Executing request:", {
9696
+ logger$31.debug("[HTTPRequestController] Executing request:", {
9697
9697
  method: request.method,
9698
9698
  url,
9699
9699
  headers: Object.keys(headers).reduce((acc, key) => {
@@ -9713,7 +9713,7 @@ var HTTPRequestController = class HTTPRequestController extends Destroyable {
9713
9713
  });
9714
9714
  clearTimeout(timeoutId);
9715
9715
  const httpResponse = await this.convertResponse(response);
9716
- logger$29.debug("[HTTPRequestController] Response received:", {
9716
+ logger$31.debug("[HTTPRequestController] Response received:", {
9717
9717
  status: response.status,
9718
9718
  statusText: response.statusText,
9719
9719
  headers: [...response.headers.entries()],
@@ -9723,7 +9723,7 @@ var HTTPRequestController = class HTTPRequestController extends Destroyable {
9723
9723
  } catch (error) {
9724
9724
  clearTimeout(timeoutId);
9725
9725
  if (error instanceof Error && error.name === "AbortError") throw new RequestTimeoutError(`Request timeout after ${timeout$5}ms`, { cause: error });
9726
- logger$29.error("[HTTPRequestController] Request failed:", error);
9726
+ logger$31.error("[HTTPRequestController] Request failed:", error);
9727
9727
  throw error;
9728
9728
  }
9729
9729
  }
@@ -9737,8 +9737,8 @@ var HTTPRequestController = class HTTPRequestController extends Destroyable {
9737
9737
  const credential = this.getCredential();
9738
9738
  if (credential.token) {
9739
9739
  headers.Authorization = `Bearer ${credential.token}`;
9740
- logger$29.debug("[HTTPRequestController] Using Bearer token auth, token length:", credential.token.length);
9741
- } else logger$29.warn("[HTTPRequestController] No credentials available for authentication");
9740
+ logger$31.debug("[HTTPRequestController] Using Bearer token auth, token length:", credential.token.length);
9741
+ } else logger$31.warn("[HTTPRequestController] No credentials available for authentication");
9742
9742
  return headers;
9743
9743
  }
9744
9744
  /**
@@ -9956,6 +9956,12 @@ const ICE_GATHERING_COMPLETE_TIMEOUT_MS = 1e4;
9956
9956
  const PEER_CONNECTION_RECOVERY_WAIT_MS = 5e3;
9957
9957
  /** Polling interval in ms while waiting for RTCPeerConnection.connectionState to transition. */
9958
9958
  const PEER_CONNECTION_RECOVERY_POLL_MS = 100;
9959
+ /** Polling interval for LocalAudioPipeline.level$ (ms). ~30fps is smooth for meters. */
9960
+ const AUDIO_LEVEL_POLL_INTERVAL_MS = 33;
9961
+ /** RMS level threshold (0..1) above which the local participant is considered speaking. */
9962
+ const VAD_THRESHOLD = .03;
9963
+ /** Hold window in ms below the threshold before speaking$ flips back to false. */
9964
+ const VAD_HOLD_MS = 250;
9959
9965
  /** Whether to persist device selections to storage by default. */
9960
9966
  const DEFAULT_PERSIST_DEVICE_SELECTION = true;
9961
9967
  /** Whether to auto-apply device changes to active calls by default. */
@@ -10004,7 +10010,7 @@ function fromMsToSec(milliseconds) {
10004
10010
 
10005
10011
  //#endregion
10006
10012
  //#region src/containers/PreferencesContainer.ts
10007
- const logger$28 = getLogger();
10013
+ const logger$30 = getLogger();
10008
10014
  var PreferencesContainer = class PreferencesContainer {
10009
10015
  static get instance() {
10010
10016
  this._instance ??= new PreferencesContainer();
@@ -10666,7 +10672,7 @@ var ClientPreferences = class {
10666
10672
  if (!this._storage) return;
10667
10673
  const data = collectStoredPreferences();
10668
10674
  this._storage.setItem(PREFERENCES_STORAGE_KEY, data, "local").catch((error) => {
10669
- logger$28.error(`[ClientPreferences] Failed to save preferences: ${String(error)}`);
10675
+ logger$30.error(`[ClientPreferences] Failed to save preferences: ${String(error)}`);
10670
10676
  });
10671
10677
  }
10672
10678
  /** Loads preferences from storage and applies them to the container. */
@@ -10675,7 +10681,7 @@ var ClientPreferences = class {
10675
10681
  this._storage.getItem(PREFERENCES_STORAGE_KEY, "local").then((stored) => {
10676
10682
  if (stored) applyStoredPreferences(stored);
10677
10683
  }).catch((error) => {
10678
- logger$28.error(`[ClientPreferences] Failed to load preferences: ${String(error)}`);
10684
+ logger$30.error(`[ClientPreferences] Failed to load preferences: ${String(error)}`);
10679
10685
  });
10680
10686
  }
10681
10687
  };
@@ -10696,8 +10702,8 @@ function toError(value) {
10696
10702
 
10697
10703
  //#endregion
10698
10704
  //#region src/controllers/NavigatorDeviceController.ts
10699
- var import_cjs$27 = require_cjs();
10700
- const logger$27 = getLogger();
10705
+ var import_cjs$29 = require_cjs();
10706
+ const logger$29 = getLogger();
10701
10707
  /** Maps a device kind to its storage key. */
10702
10708
  const DEVICE_STORAGE_KEYS = {
10703
10709
  audioinput: DEVICE_STORAGE_KEY_AUDIO_INPUT,
@@ -10719,7 +10725,7 @@ var NavigatorDeviceController = class extends Destroyable {
10719
10725
  super();
10720
10726
  this.webRTCApiProvider = webRTCApiProvider;
10721
10727
  this.deviceChangeHandler = () => {
10722
- logger$27.debug("[DeviceController] Device change detected");
10728
+ logger$29.debug("[DeviceController] Device change detected");
10723
10729
  this.enumerateDevices();
10724
10730
  };
10725
10731
  this._devicesState$ = this.createBehaviorSubject(initialDevicesState);
@@ -10756,17 +10762,17 @@ var NavigatorDeviceController = class extends Destroyable {
10756
10762
  return {};
10757
10763
  }
10758
10764
  get errors$() {
10759
- return this.cachedObservable("errors$", () => this._errors$.asObservable().pipe((0, import_cjs$27.takeUntil)(this.destroyed$)));
10765
+ return this.cachedObservable("errors$", () => this._errors$.asObservable().pipe((0, import_cjs$29.takeUntil)(this.destroyed$)));
10760
10766
  }
10761
10767
  /** Observable that emits when the SDK auto-switches a device. */
10762
10768
  get deviceRecovered$() {
10763
- return this._deviceRecovered$.asObservable().pipe((0, import_cjs$27.takeUntil)(this.destroyed$));
10769
+ return this._deviceRecovered$.asObservable().pipe((0, import_cjs$29.takeUntil)(this.destroyed$));
10764
10770
  }
10765
10771
  get videoInputDisabled$() {
10766
- return this.cachedObservable("videoInputDisabled$", () => this._videoInputDisabled$.asObservable().pipe((0, import_cjs$27.distinctUntilChanged)(), (0, import_cjs$27.takeUntil)(this.destroyed$)));
10772
+ return this.cachedObservable("videoInputDisabled$", () => this._videoInputDisabled$.asObservable().pipe((0, import_cjs$29.distinctUntilChanged)(), (0, import_cjs$29.takeUntil)(this.destroyed$)));
10767
10773
  }
10768
10774
  get audioInputDisabled$() {
10769
- return this.cachedObservable("audioInputDisabled$", () => this._audioInputDisabled$.asObservable().pipe((0, import_cjs$27.distinctUntilChanged)(), (0, import_cjs$27.takeUntil)(this.destroyed$)));
10775
+ return this.cachedObservable("audioInputDisabled$", () => this._audioInputDisabled$.asObservable().pipe((0, import_cjs$29.distinctUntilChanged)(), (0, import_cjs$29.takeUntil)(this.destroyed$)));
10770
10776
  }
10771
10777
  get videoInputDisabled() {
10772
10778
  return this._videoInputDisabled$.value;
@@ -10775,22 +10781,22 @@ var NavigatorDeviceController = class extends Destroyable {
10775
10781
  return this._audioInputDisabled$.value;
10776
10782
  }
10777
10783
  get audioInputDevices$() {
10778
- return this.cachedObservable("audioInputDevices$", () => this._devicesState$.pipe((0, import_cjs$27.map)((state) => state.audioinput), (0, import_cjs$27.distinctUntilChanged)(), (0, import_cjs$27.takeUntil)(this.destroyed$)));
10784
+ return this.cachedObservable("audioInputDevices$", () => this._devicesState$.pipe((0, import_cjs$29.map)((state) => state.audioinput), (0, import_cjs$29.distinctUntilChanged)(), (0, import_cjs$29.takeUntil)(this.destroyed$)));
10779
10785
  }
10780
10786
  get audioOutputDevices$() {
10781
- return this.cachedObservable("audioOutputDevices$", () => this._devicesState$.pipe((0, import_cjs$27.map)((state) => state.audiooutput), (0, import_cjs$27.distinctUntilChanged)(), (0, import_cjs$27.takeUntil)(this.destroyed$)));
10787
+ return this.cachedObservable("audioOutputDevices$", () => this._devicesState$.pipe((0, import_cjs$29.map)((state) => state.audiooutput), (0, import_cjs$29.distinctUntilChanged)(), (0, import_cjs$29.takeUntil)(this.destroyed$)));
10782
10788
  }
10783
10789
  get videoInputDevices$() {
10784
- return this.cachedObservable("videoInputDevices$", () => this._devicesState$.pipe((0, import_cjs$27.map)((state) => state.videoinput), (0, import_cjs$27.distinctUntilChanged)(), (0, import_cjs$27.takeUntil)(this.destroyed$)));
10790
+ return this.cachedObservable("videoInputDevices$", () => this._devicesState$.pipe((0, import_cjs$29.map)((state) => state.videoinput), (0, import_cjs$29.distinctUntilChanged)(), (0, import_cjs$29.takeUntil)(this.destroyed$)));
10785
10791
  }
10786
10792
  get selectedAudioInputDevice$() {
10787
- return this.cachedObservable("selectedAudioInputDevice$", () => this._selectedDevicesState$.asObservable().pipe((0, import_cjs$27.map)((state) => state.audioinput), (0, import_cjs$27.distinctUntilChanged)(), (0, import_cjs$27.takeUntil)(this.destroyed$), (0, import_cjs$27.tap)((info) => logger$27.debug("[DeviceController] Selected audio input device changed:", info))));
10793
+ return this.cachedObservable("selectedAudioInputDevice$", () => this._selectedDevicesState$.asObservable().pipe((0, import_cjs$29.map)((state) => state.audioinput), (0, import_cjs$29.distinctUntilChanged)(), (0, import_cjs$29.takeUntil)(this.destroyed$), (0, import_cjs$29.tap)((info) => logger$29.debug("[DeviceController] Selected audio input device changed:", info))));
10788
10794
  }
10789
10795
  get selectedAudioOutputDevice$() {
10790
- return this.cachedObservable("selectedAudioOutputDevice$", () => this._selectedDevicesState$.asObservable().pipe((0, import_cjs$27.map)((state) => state.audiooutput), (0, import_cjs$27.distinctUntilChanged)(), (0, import_cjs$27.takeUntil)(this.destroyed$), (0, import_cjs$27.tap)((info) => logger$27.debug("[DeviceController] Selected audio output device changed:", info))));
10796
+ return this.cachedObservable("selectedAudioOutputDevice$", () => this._selectedDevicesState$.asObservable().pipe((0, import_cjs$29.map)((state) => state.audiooutput), (0, import_cjs$29.distinctUntilChanged)(), (0, import_cjs$29.takeUntil)(this.destroyed$), (0, import_cjs$29.tap)((info) => logger$29.debug("[DeviceController] Selected audio output device changed:", info))));
10791
10797
  }
10792
10798
  get selectedVideoInputDevice$() {
10793
- return this.cachedObservable("selectedVideoInputDevice$", () => this._selectedDevicesState$.asObservable().pipe((0, import_cjs$27.map)((state) => state.videoinput), (0, import_cjs$27.distinctUntilChanged)(), (0, import_cjs$27.takeUntil)(this.destroyed$), (0, import_cjs$27.tap)((info) => logger$27.debug("[DeviceController] Selected video input device changed:", info))));
10799
+ return this.cachedObservable("selectedVideoInputDevice$", () => this._selectedDevicesState$.asObservable().pipe((0, import_cjs$29.map)((state) => state.videoinput), (0, import_cjs$29.distinctUntilChanged)(), (0, import_cjs$29.takeUntil)(this.destroyed$), (0, import_cjs$29.tap)((info) => logger$29.debug("[DeviceController] Selected video input device changed:", info))));
10794
10800
  }
10795
10801
  get selectedAudioInputDevice() {
10796
10802
  if (this._audioInputDisabled$.value) return null;
@@ -10865,7 +10871,7 @@ var NavigatorDeviceController = class extends Destroyable {
10865
10871
  if (device) this.persistDeviceSelection("audioinput", device);
10866
10872
  }
10867
10873
  selectVideoInputDevice(device) {
10868
- logger$27.debug("[DeviceController] Setting selected video input device:", device);
10874
+ logger$29.debug("[DeviceController] Setting selected video input device:", device);
10869
10875
  if (this._videoInputDisabled$.value && device) this._videoInputDisabled$.next(false);
10870
10876
  const previous = this._selectedDevicesState$.value.videoinput;
10871
10877
  if (previous && previous.deviceId !== device?.deviceId) this._deviceHistory.push("videoinput", previous);
@@ -10886,7 +10892,7 @@ var NavigatorDeviceController = class extends Destroyable {
10886
10892
  }
10887
10893
  init() {
10888
10894
  this.loadPersistedDevices();
10889
- this.subscribeTo(this._devicesState$.pipe((0, import_cjs$27.debounceTime)(PreferencesContainer.instance.deviceDebounceTime)), (devicesState) => {
10895
+ this.subscribeTo(this._devicesState$.pipe((0, import_cjs$29.debounceTime)(PreferencesContainer.instance.deviceDebounceTime)), (devicesState) => {
10890
10896
  const currentSelected = this._selectedDevicesState$.value;
10891
10897
  const newAudioInput = this._audioInputDisabled$.value ? null : this.resolveDevice("audioinput", devicesState.audioinput, currentSelected.audioinput, PreferencesContainer.instance.preferredAudioInput);
10892
10898
  const newAudioOutput = this.resolveDevice("audiooutput", devicesState.audiooutput, currentSelected.audiooutput, PreferencesContainer.instance.preferredAudioOutput);
@@ -10922,7 +10928,7 @@ var NavigatorDeviceController = class extends Destroyable {
10922
10928
  }
10923
10929
  const fromHistory = this._deviceHistory.findInHistory(kind, devices);
10924
10930
  if (fromHistory) {
10925
- logger$27.debug(`[DeviceController] Device disappeared, falling back to history: ${fromHistory.label}`);
10931
+ logger$29.debug(`[DeviceController] Device disappeared, falling back to history: ${fromHistory.label}`);
10926
10932
  this.emitDeviceRecovered(kind, selected, fromHistory, "device_disconnected");
10927
10933
  return fromHistory;
10928
10934
  }
@@ -10975,7 +10981,7 @@ var NavigatorDeviceController = class extends Destroyable {
10975
10981
  try {
10976
10982
  await this._storageManager.setItem(DEVICE_STORAGE_KEYS[kind], stored, "local");
10977
10983
  } catch (error) {
10978
- logger$27.error(`[DeviceController] Failed to persist device selection for ${kind}:`, error);
10984
+ logger$29.error(`[DeviceController] Failed to persist device selection for ${kind}:`, error);
10979
10985
  }
10980
10986
  }
10981
10987
  async loadPersistedDevices() {
@@ -10991,7 +10997,7 @@ var NavigatorDeviceController = class extends Destroyable {
10991
10997
  [kind]: stored
10992
10998
  };
10993
10999
  } catch (error) {
10994
- logger$27.error(`[DeviceController] Failed to load persisted device for ${kind}:`, error);
11000
+ logger$29.error(`[DeviceController] Failed to load persisted device for ${kind}:`, error);
10995
11001
  }
10996
11002
  }
10997
11003
  /** Clears device history, persisted selections, and re-enumerates devices. */
@@ -11008,8 +11014,8 @@ var NavigatorDeviceController = class extends Destroyable {
11008
11014
  enableDeviceMonitoring() {
11009
11015
  this.disableDeviceMonitoring();
11010
11016
  this.webRTCApiProvider.mediaDevices.addEventListener("devicechange", this.deviceChangeHandler);
11011
- if (PreferencesContainer.instance.devicePollingInterval > 0) this._devicesPoolingSubscription = (0, import_cjs$27.interval)(PreferencesContainer.instance.devicePollingInterval).subscribe(() => {
11012
- logger$27.debug("[DeviceController] Polling devices due to interval");
11017
+ if (PreferencesContainer.instance.devicePollingInterval > 0) this._devicesPoolingSubscription = (0, import_cjs$29.interval)(PreferencesContainer.instance.devicePollingInterval).subscribe(() => {
11018
+ logger$29.debug("[DeviceController] Polling devices due to interval");
11013
11019
  this.enumerateDevices();
11014
11020
  });
11015
11021
  this.enumerateDevices();
@@ -11035,13 +11041,13 @@ var NavigatorDeviceController = class extends Destroyable {
11035
11041
  videoinput: []
11036
11042
  });
11037
11043
  this._devicesState$.next(devicesByKind);
11038
- logger$27.debug("[DeviceController] Devices enumerated:", {
11044
+ logger$29.debug("[DeviceController] Devices enumerated:", {
11039
11045
  audioInputs: devicesByKind.audioinput.length,
11040
11046
  audioOutputs: devicesByKind.audiooutput.length,
11041
11047
  videoInputs: devicesByKind.videoinput.length
11042
11048
  });
11043
11049
  } catch (error) {
11044
- logger$27.error("[DeviceController] Failed to enumerate devices:", error);
11050
+ logger$29.error("[DeviceController] Failed to enumerate devices:", error);
11045
11051
  this._errors$.next(toError(error));
11046
11052
  }
11047
11053
  }
@@ -11057,7 +11063,7 @@ var NavigatorDeviceController = class extends Destroyable {
11057
11063
  stream.getTracks().forEach((t) => t.stop());
11058
11064
  return capabilities;
11059
11065
  } catch (error) {
11060
- logger$27.error("[DeviceController] Failed to get device capabilities:", error);
11066
+ logger$29.error("[DeviceController] Failed to get device capabilities:", error);
11061
11067
  this._errors$.next(toError(error));
11062
11068
  throw error;
11063
11069
  }
@@ -11308,7 +11314,7 @@ var DependencyContainer = class {
11308
11314
 
11309
11315
  //#endregion
11310
11316
  //#region src/controllers/CryptoController.ts
11311
- const logger$26 = getLogger();
11317
+ const logger$28 = getLogger();
11312
11318
  const DPOP_DB_NAME = "sw-dpop";
11313
11319
  const DPOP_DB_VERSION = 1;
11314
11320
  const DPOP_STORE_NAME = "keys";
@@ -11367,7 +11373,7 @@ async function loadKeyPairFromDB() {
11367
11373
  tx.oncomplete = () => db.close();
11368
11374
  });
11369
11375
  } catch (error) {
11370
- logger$26.warn("[DPoP] Failed to load key pair from IndexedDB:", error);
11376
+ logger$28.warn("[DPoP] Failed to load key pair from IndexedDB:", error);
11371
11377
  return null;
11372
11378
  }
11373
11379
  }
@@ -11387,7 +11393,7 @@ async function saveKeyPairToDB(keyPair) {
11387
11393
  };
11388
11394
  });
11389
11395
  } catch (error) {
11390
- logger$26.warn("[DPoP] Failed to save key pair to IndexedDB:", error);
11396
+ logger$28.warn("[DPoP] Failed to save key pair to IndexedDB:", error);
11391
11397
  }
11392
11398
  }
11393
11399
  async function deleteKeyPairFromDB() {
@@ -11406,7 +11412,7 @@ async function deleteKeyPairFromDB() {
11406
11412
  };
11407
11413
  });
11408
11414
  } catch (error) {
11409
- logger$26.warn("[DPoP] Failed to delete key pair from IndexedDB:", error);
11415
+ logger$28.warn("[DPoP] Failed to delete key pair from IndexedDB:", error);
11410
11416
  }
11411
11417
  }
11412
11418
  /**
@@ -11466,13 +11472,13 @@ var CryptoController = class {
11466
11472
  this._publicJwk = await crypto.subtle.exportKey("jwk", stored.publicKey);
11467
11473
  this._fingerprint = await computeJwkThumbprint(this._publicJwk);
11468
11474
  this._initialized = true;
11469
- logger$26.debug("[DPoP] Key pair restored from IndexedDB, fingerprint:", this._fingerprint);
11475
+ logger$28.debug("[DPoP] Key pair restored from IndexedDB, fingerprint:", this._fingerprint);
11470
11476
  return this._fingerprint;
11471
11477
  } catch (error) {
11472
- logger$26.warn("[DPoP] Stored key pair unusable, generating new one:", error);
11478
+ logger$28.warn("[DPoP] Stored key pair unusable, generating new one:", error);
11473
11479
  await deleteKeyPairFromDB();
11474
11480
  }
11475
- logger$26.debug("[DPoP] Generating RSA key pair");
11481
+ logger$28.debug("[DPoP] Generating RSA key pair");
11476
11482
  this._keyPair = await crypto.subtle.generateKey({
11477
11483
  name: "RSASSA-PKCS1-v1_5",
11478
11484
  modulusLength: 2048,
@@ -11487,7 +11493,7 @@ var CryptoController = class {
11487
11493
  this._fingerprint = await computeJwkThumbprint(this._publicJwk);
11488
11494
  this._initialized = true;
11489
11495
  await saveKeyPairToDB(this._keyPair);
11490
- logger$26.debug("[DPoP] Key pair generated and persisted, fingerprint:", this._fingerprint);
11496
+ logger$28.debug("[DPoP] Key pair generated and persisted, fingerprint:", this._fingerprint);
11491
11497
  return this._fingerprint;
11492
11498
  }
11493
11499
  /**
@@ -11553,7 +11559,7 @@ var CryptoController = class {
11553
11559
  this._fingerprint = null;
11554
11560
  this._initialized = false;
11555
11561
  deleteKeyPairFromDB();
11556
- logger$26.debug("[DPoP] Controller destroyed");
11562
+ logger$28.debug("[DPoP] Controller destroyed");
11557
11563
  }
11558
11564
  get publicJwk() {
11559
11565
  if (!this._publicJwk) throw new DPoPInitError("CryptoController not initialized. Call init() first.");
@@ -11576,8 +11582,8 @@ var CryptoController = class {
11576
11582
 
11577
11583
  //#endregion
11578
11584
  //#region src/controllers/NetworkMonitor.ts
11579
- var import_cjs$26 = require_cjs();
11580
- const logger$25 = getLogger();
11585
+ var import_cjs$28 = require_cjs();
11586
+ const logger$27 = getLogger();
11581
11587
  /**
11582
11588
  * Safely check whether we are running in a browser environment
11583
11589
  * with `window` and the relevant event targets.
@@ -11620,13 +11626,13 @@ var NetworkMonitor = class extends Destroyable {
11620
11626
  this.attachListeners();
11621
11627
  }
11622
11628
  get isOnline$() {
11623
- return this._isOnline$.asObservable().pipe((0, import_cjs$26.takeUntil)(this._destroyed$));
11629
+ return this._isOnline$.asObservable().pipe((0, import_cjs$28.takeUntil)(this._destroyed$));
11624
11630
  }
11625
11631
  get isOnline() {
11626
11632
  return this._isOnline$.value;
11627
11633
  }
11628
11634
  get networkChange$() {
11629
- return this._networkChange$.asObservable().pipe((0, import_cjs$26.takeUntil)(this._destroyed$));
11635
+ return this._networkChange$.asObservable().pipe((0, import_cjs$28.takeUntil)(this._destroyed$));
11630
11636
  }
11631
11637
  destroy() {
11632
11638
  this.removeListeners();
@@ -11634,7 +11640,7 @@ var NetworkMonitor = class extends Destroyable {
11634
11640
  }
11635
11641
  attachListeners() {
11636
11642
  if (!hasBrowserNetworkEvents()) {
11637
- logger$25.debug("NetworkMonitor: no browser environment detected, skipping event listeners");
11643
+ logger$27.debug("NetworkMonitor: no browser environment detected, skipping event listeners");
11638
11644
  return;
11639
11645
  }
11640
11646
  window.addEventListener("online", this._onOnline);
@@ -11642,7 +11648,7 @@ var NetworkMonitor = class extends Destroyable {
11642
11648
  const connection = getNetworkConnection();
11643
11649
  if (connection) connection.addEventListener("change", this._onConnectionChange);
11644
11650
  this._listenersAttached = true;
11645
- logger$25.debug("NetworkMonitor: event listeners attached");
11651
+ logger$27.debug("NetworkMonitor: event listeners attached");
11646
11652
  }
11647
11653
  removeListeners() {
11648
11654
  if (!this._listenersAttached) return;
@@ -11653,10 +11659,10 @@ var NetworkMonitor = class extends Destroyable {
11653
11659
  if (connection) connection.removeEventListener("change", this._onConnectionChange);
11654
11660
  }
11655
11661
  this._listenersAttached = false;
11656
- logger$25.debug("NetworkMonitor: event listeners removed");
11662
+ logger$27.debug("NetworkMonitor: event listeners removed");
11657
11663
  }
11658
11664
  handleOnline() {
11659
- logger$25.info("NetworkMonitor: browser went online");
11665
+ logger$27.info("NetworkMonitor: browser went online");
11660
11666
  this._isOnline$.next(true);
11661
11667
  this._networkChange$.next({
11662
11668
  type: "online",
@@ -11665,7 +11671,7 @@ var NetworkMonitor = class extends Destroyable {
11665
11671
  });
11666
11672
  }
11667
11673
  handleOffline() {
11668
- logger$25.info("NetworkMonitor: browser went offline");
11674
+ logger$27.info("NetworkMonitor: browser went offline");
11669
11675
  this._isOnline$.next(false);
11670
11676
  this._networkChange$.next({
11671
11677
  type: "offline",
@@ -11674,7 +11680,7 @@ var NetworkMonitor = class extends Destroyable {
11674
11680
  }
11675
11681
  handleConnectionChange() {
11676
11682
  const networkType = getNetworkType();
11677
- logger$25.info(`NetworkMonitor: connection changed — effectiveType=${networkType ?? "unknown"}`);
11683
+ logger$27.info(`NetworkMonitor: connection changed — effectiveType=${networkType ?? "unknown"}`);
11678
11684
  this._networkChange$.next({
11679
11685
  type: "connection_change",
11680
11686
  timestamp: Date.now(),
@@ -11789,8 +11795,8 @@ function getNavigatorMediaDevices() {
11789
11795
 
11790
11796
  //#endregion
11791
11797
  //#region src/controllers/PreflightRunner.ts
11792
- var import_cjs$25 = require_cjs();
11793
- const logger$24 = getLogger();
11798
+ var import_cjs$27 = require_cjs();
11799
+ const logger$26 = getLogger();
11794
11800
  const DEFAULT_MEDIA_TEST_DURATION_S = 10;
11795
11801
  const ICE_GATHERING_TIMEOUT_MS = 1e4;
11796
11802
  const SIGNALING_RTT_TIMEOUT_MS = 5e3;
@@ -11839,7 +11845,7 @@ var PreflightRunner = class extends Destroyable {
11839
11845
  if (!this._options.skipMediaTest) try {
11840
11846
  bandwidth = await this.testMediaBandwidth(destination);
11841
11847
  } catch (error) {
11842
- logger$24.warn("[PreflightRunner] Media bandwidth test failed:", error);
11848
+ logger$26.warn("[PreflightRunner] Media bandwidth test failed:", error);
11843
11849
  warnings.push("Media bandwidth test failed");
11844
11850
  }
11845
11851
  return {
@@ -11851,7 +11857,7 @@ var PreflightRunner = class extends Destroyable {
11851
11857
  warnings
11852
11858
  };
11853
11859
  } catch (error) {
11854
- logger$24.error("[PreflightRunner] Preflight test failed:", error);
11860
+ logger$26.error("[PreflightRunner] Preflight test failed:", error);
11855
11861
  throw new PreflightError("preflight", error instanceof Error ? error : new Error(String(error)));
11856
11862
  } finally {
11857
11863
  this.destroy();
@@ -11882,7 +11888,7 @@ var PreflightRunner = class extends Destroyable {
11882
11888
  if (track.kind === "video" && track.readyState === "live") videoWorking = true;
11883
11889
  }
11884
11890
  } catch (error) {
11885
- logger$24.warn("[PreflightRunner] Device test failed:", error);
11891
+ logger$26.warn("[PreflightRunner] Device test failed:", error);
11886
11892
  } finally {
11887
11893
  if (audioStream) audioStream.getTracks().forEach((t) => t.stop());
11888
11894
  }
@@ -11940,7 +11946,7 @@ var PreflightRunner = class extends Destroyable {
11940
11946
  rttMs
11941
11947
  };
11942
11948
  } catch (error) {
11943
- logger$24.warn("[PreflightRunner] ICE connectivity test failed:", error);
11949
+ logger$26.warn("[PreflightRunner] ICE connectivity test failed:", error);
11944
11950
  return {
11945
11951
  type: "failed",
11946
11952
  turnReachable: false,
@@ -11958,7 +11964,7 @@ var PreflightRunner = class extends Destroyable {
11958
11964
  audio: true,
11959
11965
  video: false
11960
11966
  });
11961
- await (0, import_cjs$25.firstValueFrom)(call.status$.pipe((0, import_cjs$25.filter)((s) => s === "connected"), (0, import_cjs$25.take)(1), (0, import_cjs$25.timeout)(SIGNALING_RTT_TIMEOUT_MS)));
11967
+ await (0, import_cjs$27.firstValueFrom)(call.status$.pipe((0, import_cjs$27.filter)((s) => s === "connected"), (0, import_cjs$27.take)(1), (0, import_cjs$27.timeout)(SIGNALING_RTT_TIMEOUT_MS)));
11962
11968
  const durationMs = this._options.duration * 1e3;
11963
11969
  await new Promise((resolve) => setTimeout(resolve, durationMs));
11964
11970
  const metrics = call.networkMetrics;
@@ -11987,8 +11993,8 @@ var PreflightRunner = class extends Destroyable {
11987
11993
 
11988
11994
  //#endregion
11989
11995
  //#region src/controllers/VisibilityController.ts
11990
- var import_cjs$24 = require_cjs();
11991
- const logger$23 = getLogger();
11996
+ var import_cjs$26 = require_cjs();
11997
+ const logger$25 = getLogger();
11992
11998
  /**
11993
11999
  * Checks whether the document visibility API is available.
11994
12000
  */
@@ -12025,15 +12031,15 @@ var VisibilityController = class extends Destroyable {
12025
12031
  this._boundHandler = this._handleVisibilityChange.bind(this);
12026
12032
  if (this._hasVisibilityApi) {
12027
12033
  document.addEventListener("visibilitychange", this._boundHandler);
12028
- logger$23.debug("VisibilityController: listening for visibilitychange events");
12029
- } else logger$23.debug("VisibilityController: document visibility API not available, defaulting to visible");
12034
+ logger$25.debug("VisibilityController: listening for visibilitychange events");
12035
+ } else logger$25.debug("VisibilityController: document visibility API not available, defaulting to visible");
12030
12036
  }
12031
12037
  /**
12032
12038
  * Observable of the current visibility state.
12033
12039
  * Emits 'visible' or 'hidden'. Always starts with the current state.
12034
12040
  */
12035
12041
  get visibility$() {
12036
- return this._visibility$.pipe((0, import_cjs$24.takeUntil)(this._destroyed$));
12042
+ return this._visibility$.pipe((0, import_cjs$26.takeUntil)(this._destroyed$));
12037
12043
  }
12038
12044
  /**
12039
12045
  * The current visibility state value.
@@ -12046,12 +12052,12 @@ var VisibilityController = class extends Destroyable {
12046
12052
  * Each event includes the previous state, new state, and timestamp.
12047
12053
  */
12048
12054
  get visibilityChange$() {
12049
- return this._visibilityChange$.pipe((0, import_cjs$24.takeUntil)(this._destroyed$));
12055
+ return this._visibilityChange$.pipe((0, import_cjs$26.takeUntil)(this._destroyed$));
12050
12056
  }
12051
12057
  destroy() {
12052
12058
  if (this._hasVisibilityApi) {
12053
12059
  document.removeEventListener("visibilitychange", this._boundHandler);
12054
- logger$23.debug("VisibilityController: removed visibilitychange listener");
12060
+ logger$25.debug("VisibilityController: removed visibilitychange listener");
12055
12061
  }
12056
12062
  super.destroy();
12057
12063
  }
@@ -12069,7 +12075,7 @@ var VisibilityController = class extends Destroyable {
12069
12075
  timestamp: Date.now()
12070
12076
  };
12071
12077
  this._visibilityChange$.next(changeEvent);
12072
- logger$23.debug("VisibilityController: visibility changed", {
12078
+ logger$25.debug("VisibilityController: visibility changed", {
12073
12079
  from: previousState,
12074
12080
  to: newState
12075
12081
  });
@@ -12078,13 +12084,13 @@ var VisibilityController = class extends Destroyable {
12078
12084
 
12079
12085
  //#endregion
12080
12086
  //#region src/behaviors/Fetchable.ts
12081
- var import_cjs$23 = require_cjs();
12087
+ var import_cjs$25 = require_cjs();
12082
12088
  var Fetchable = class extends Destroyable {
12083
12089
  constructor(fromPath, http) {
12084
12090
  super();
12085
12091
  this.fromPath = fromPath;
12086
12092
  this.http = http;
12087
- this.fetched$ = (0, import_cjs$23.defer)(() => (0, import_cjs$23.from)(this.fetch())).pipe((0, import_cjs$23.shareReplay)(1), (0, import_cjs$23.takeUntil)(this.destroyed$));
12093
+ this.fetched$ = (0, import_cjs$25.defer)(() => (0, import_cjs$25.from)(this.fetch())).pipe((0, import_cjs$25.shareReplay)(1), (0, import_cjs$25.takeUntil)(this.destroyed$));
12088
12094
  }
12089
12095
  async fetch() {
12090
12096
  const response = await this.http.request({
@@ -12319,7 +12325,7 @@ const RPCEventAckResponse = (id) => makeRPCResponse({
12319
12325
 
12320
12326
  //#endregion
12321
12327
  //#region src/managers/AttachManager.ts
12322
- const logger$22 = getLogger();
12328
+ const logger$24 = getLogger();
12323
12329
  var AttachManager = class {
12324
12330
  constructor(storage, deviceController, reconnectCallsTimeout, attachKey) {
12325
12331
  this.storage = storage;
@@ -12340,7 +12346,7 @@ var AttachManager = class {
12340
12346
  try {
12341
12347
  return await this.storage.getItem(this.attachKey) ?? {};
12342
12348
  } catch (error) {
12343
- logger$22.warn("[AttachManager] Failed to retrieve attached calls from storage", error);
12349
+ logger$24.warn("[AttachManager] Failed to retrieve attached calls from storage", error);
12344
12350
  return {};
12345
12351
  }
12346
12352
  }
@@ -12348,7 +12354,7 @@ var AttachManager = class {
12348
12354
  try {
12349
12355
  await this.storage.setItem(this.attachKey, attached);
12350
12356
  } catch (error) {
12351
- logger$22.warn("[AttachManager] Failed to write attached calls to storage", error);
12357
+ logger$24.warn("[AttachManager] Failed to write attached calls to storage", error);
12352
12358
  }
12353
12359
  }
12354
12360
  /**
@@ -12367,7 +12373,7 @@ var AttachManager = class {
12367
12373
  }
12368
12374
  async attach(call) {
12369
12375
  if (!call.to) {
12370
- logger$22.warn("[AttachManager] Skip attach for calls with no destination");
12376
+ logger$24.warn("[AttachManager] Skip attach for calls with no destination");
12371
12377
  return;
12372
12378
  }
12373
12379
  const destination = call.to;
@@ -12420,15 +12426,15 @@ var AttachManager = class {
12420
12426
  callId,
12421
12427
  ...options
12422
12428
  });
12423
- logger$22.info(`[AttachManager] Reattached call ${callId} (attempt ${attempt})`);
12429
+ logger$24.info(`[AttachManager] Reattached call ${callId} (attempt ${attempt})`);
12424
12430
  succeeded = true;
12425
12431
  break;
12426
12432
  } catch (error) {
12427
- logger$22.warn(`[AttachManager] Reattach attempt ${attempt}/3 failed for call ${callId}:`, error);
12433
+ logger$24.warn(`[AttachManager] Reattach attempt ${attempt}/3 failed for call ${callId}:`, error);
12428
12434
  if (attempt < 3) await new Promise((r) => setTimeout(r, (attempt + 1) * 1e3));
12429
12435
  }
12430
12436
  if (!succeeded) {
12431
- logger$22.warn(`[AttachManager] Reattach failed after 3 attempts for call ${callId}, removing reference`);
12437
+ logger$24.warn(`[AttachManager] Reattach failed after 3 attempts for call ${callId}, removing reference`);
12432
12438
  await this.detach({
12433
12439
  id: callId,
12434
12440
  mediaDirections: attachment.mediaDirections
@@ -13467,7 +13473,7 @@ function computeCapabilities(capabilities) {
13467
13473
 
13468
13474
  //#endregion
13469
13475
  //#region src/core/capabilities/SelfCapabilities.ts
13470
- var import_cjs$22 = require_cjs();
13476
+ var import_cjs$24 = require_cjs();
13471
13477
  /**
13472
13478
  * SelfCapabilities manages the capability state for the self participant.
13473
13479
  *
@@ -13503,7 +13509,7 @@ var SelfCapabilities = class extends Destroyable {
13503
13509
  }
13504
13510
  /** Observable for self member capabilities */
13505
13511
  get self$() {
13506
- return this.cachedObservable("self$", () => this._state$.pipe((0, import_cjs$22.map)((state) => state.self), (0, import_cjs$22.distinctUntilChanged)()));
13512
+ return this.cachedObservable("self$", () => this._state$.pipe((0, import_cjs$24.map)((state) => state.self), (0, import_cjs$24.distinctUntilChanged)()));
13507
13513
  }
13508
13514
  /** Current self member capabilities */
13509
13515
  get self() {
@@ -13511,7 +13517,7 @@ var SelfCapabilities = class extends Destroyable {
13511
13517
  }
13512
13518
  /** Observable for other member capabilities */
13513
13519
  get member$() {
13514
- return this.cachedObservable("member$", () => this._state$.pipe((0, import_cjs$22.map)((state) => state.member), (0, import_cjs$22.distinctUntilChanged)()));
13520
+ return this.cachedObservable("member$", () => this._state$.pipe((0, import_cjs$24.map)((state) => state.member), (0, import_cjs$24.distinctUntilChanged)()));
13515
13521
  }
13516
13522
  /** Current other member capabilities */
13517
13523
  get member() {
@@ -13519,7 +13525,7 @@ var SelfCapabilities = class extends Destroyable {
13519
13525
  }
13520
13526
  /** Observable for end call capability */
13521
13527
  get end$() {
13522
- return this.cachedObservable("end$", () => this._state$.pipe((0, import_cjs$22.map)((state) => state.end), (0, import_cjs$22.distinctUntilChanged)()));
13528
+ return this.cachedObservable("end$", () => this._state$.pipe((0, import_cjs$24.map)((state) => state.end), (0, import_cjs$24.distinctUntilChanged)()));
13523
13529
  }
13524
13530
  /** Current end call capability */
13525
13531
  get end() {
@@ -13527,7 +13533,7 @@ var SelfCapabilities = class extends Destroyable {
13527
13533
  }
13528
13534
  /** Observable for set layout capability */
13529
13535
  get setLayout$() {
13530
- return this.cachedObservable("setLayout$", () => this._state$.pipe((0, import_cjs$22.map)((state) => state.setLayout), (0, import_cjs$22.distinctUntilChanged)()));
13536
+ return this.cachedObservable("setLayout$", () => this._state$.pipe((0, import_cjs$24.map)((state) => state.setLayout), (0, import_cjs$24.distinctUntilChanged)()));
13531
13537
  }
13532
13538
  /** Current set layout capability */
13533
13539
  get setLayout() {
@@ -13535,7 +13541,7 @@ var SelfCapabilities = class extends Destroyable {
13535
13541
  }
13536
13542
  /** Observable for send digit capability */
13537
13543
  get sendDigit$() {
13538
- return this.cachedObservable("sendDigit$", () => this._state$.pipe((0, import_cjs$22.map)((state) => state.sendDigit), (0, import_cjs$22.distinctUntilChanged)()));
13544
+ return this.cachedObservable("sendDigit$", () => this._state$.pipe((0, import_cjs$24.map)((state) => state.sendDigit), (0, import_cjs$24.distinctUntilChanged)()));
13539
13545
  }
13540
13546
  /** Current send digit capability */
13541
13547
  get sendDigit() {
@@ -13543,7 +13549,7 @@ var SelfCapabilities = class extends Destroyable {
13543
13549
  }
13544
13550
  /** Observable for vmuted hide capability */
13545
13551
  get vmutedHide$() {
13546
- return this.cachedObservable("vmutedHide$", () => this._state$.pipe((0, import_cjs$22.map)((state) => state.vmutedHide), (0, import_cjs$22.distinctUntilChanged)()));
13552
+ return this.cachedObservable("vmutedHide$", () => this._state$.pipe((0, import_cjs$24.map)((state) => state.vmutedHide), (0, import_cjs$24.distinctUntilChanged)()));
13547
13553
  }
13548
13554
  /** Current vmuted hide capability */
13549
13555
  get vmutedHide() {
@@ -13551,7 +13557,7 @@ var SelfCapabilities = class extends Destroyable {
13551
13557
  }
13552
13558
  /** Observable for lock capability */
13553
13559
  get lock$() {
13554
- return this.cachedObservable("lock$", () => this._state$.pipe((0, import_cjs$22.map)((state) => state.lock), (0, import_cjs$22.distinctUntilChanged)()));
13560
+ return this.cachedObservable("lock$", () => this._state$.pipe((0, import_cjs$24.map)((state) => state.lock), (0, import_cjs$24.distinctUntilChanged)()));
13555
13561
  }
13556
13562
  /** Current lock capability */
13557
13563
  get lock() {
@@ -13559,7 +13565,7 @@ var SelfCapabilities = class extends Destroyable {
13559
13565
  }
13560
13566
  /** Observable for device capability */
13561
13567
  get device$() {
13562
- return this.cachedObservable("device$", () => this._state$.pipe((0, import_cjs$22.map)((state) => state.device), (0, import_cjs$22.distinctUntilChanged)()));
13568
+ return this.cachedObservable("device$", () => this._state$.pipe((0, import_cjs$24.map)((state) => state.device), (0, import_cjs$24.distinctUntilChanged)()));
13563
13569
  }
13564
13570
  /** Current device capability */
13565
13571
  get device() {
@@ -13567,7 +13573,7 @@ var SelfCapabilities = class extends Destroyable {
13567
13573
  }
13568
13574
  /** Observable for screenshare capability */
13569
13575
  get screenshare$() {
13570
- return this.cachedObservable("screenshare$", () => this._state$.pipe((0, import_cjs$22.map)((state) => state.screenshare), (0, import_cjs$22.distinctUntilChanged)()));
13576
+ return this.cachedObservable("screenshare$", () => this._state$.pipe((0, import_cjs$24.map)((state) => state.screenshare), (0, import_cjs$24.distinctUntilChanged)()));
13571
13577
  }
13572
13578
  /** Current screenshare capability */
13573
13579
  get screenshare() {
@@ -13595,7 +13601,7 @@ function toggleHandraiseMethod(is) {
13595
13601
 
13596
13602
  //#endregion
13597
13603
  //#region src/core/entities/Participant.ts
13598
- const logger$21 = getLogger();
13604
+ const logger$23 = getLogger();
13599
13605
  const initialState = {};
13600
13606
  /**
13601
13607
  * Represents a participant in a call.
@@ -13647,15 +13653,35 @@ var Participant = class extends Destroyable {
13647
13653
  get deaf$() {
13648
13654
  return this.cachedObservable("deaf$", () => this._state$.pipe((0, import_operators$1.map)((state) => state.deaf), (0, import_operators$1.distinctUntilChanged)()));
13649
13655
  }
13650
- /** Observable of the participant's microphone input volume. */
13656
+ /**
13657
+ * Observable of the participant's **server-side** microphone input volume
13658
+ * as reported by the mix engine. This is gain applied on the bridged audio
13659
+ * leg (FreeSWITCH channel read volume), NOT the local browser mic. For a
13660
+ * local PC mic control, see {@link Call.setLocalMicrophoneGain}.
13661
+ *
13662
+ * @see {@link setAudioInputVolume}
13663
+ */
13651
13664
  get inputVolume$() {
13652
13665
  return this.cachedObservable("inputVolume$", () => this._state$.pipe((0, import_operators$1.map)((state) => state.input_volume), (0, import_operators$1.distinctUntilChanged)()));
13653
13666
  }
13654
- /** Observable of the participant's speaker output volume. */
13667
+ /**
13668
+ * Observable of the participant's **server-side** speaker output volume as
13669
+ * reported by the mix engine (FreeSWITCH channel write volume). NOT the
13670
+ * local HTML `<audio>` element volume — set that on your own element.
13671
+ *
13672
+ * @see {@link setAudioOutputVolume}
13673
+ */
13655
13674
  get outputVolume$() {
13656
13675
  return this.cachedObservable("outputVolume$", () => this._state$.pipe((0, import_operators$1.map)((state) => state.output_volume), (0, import_operators$1.distinctUntilChanged)()));
13657
13676
  }
13658
- /** Observable of the microphone input sensitivity level. */
13677
+ /**
13678
+ * Observable of the **conference-only** microphone energy/gate sensitivity
13679
+ * level for this member. Routes through the conferencing mix engine and has
13680
+ * no effect on 1:1 WebRTC calls. Populated from `member.updated` events for
13681
+ * conference members.
13682
+ *
13683
+ * @see {@link setAudioInputSensitivity}
13684
+ */
13659
13685
  get inputSensitivity$() {
13660
13686
  return this.cachedObservable("inputSensitivity$", () => this._state$.pipe((0, import_operators$1.map)((state) => state.input_sensitivity), (0, import_operators$1.distinctUntilChanged)()));
13661
13687
  }
@@ -13743,15 +13769,25 @@ var Participant = class extends Destroyable {
13743
13769
  get deaf() {
13744
13770
  return this._state$.value.deaf ?? false;
13745
13771
  }
13746
- /** Current microphone input volume level, or `undefined` if not set. */
13772
+ /**
13773
+ * Current **server-side** microphone input volume as reported by the mix
13774
+ * engine, or `undefined` if not set. Not the local PC mic — see
13775
+ * {@link Call.setLocalMicrophoneGain} for browser-side control.
13776
+ */
13747
13777
  get inputVolume() {
13748
13778
  return this._state$.value.input_volume;
13749
13779
  }
13750
- /** Current speaker output volume level, or `undefined` if not set. */
13780
+ /**
13781
+ * Current **server-side** speaker output volume from the mix engine, or
13782
+ * `undefined` if not set. Not the local `<audio>` element volume.
13783
+ */
13751
13784
  get outputVolume() {
13752
13785
  return this._state$.value.output_volume;
13753
13786
  }
13754
- /** Current microphone input sensitivity level, or `undefined` if not set. */
13787
+ /**
13788
+ * Current **conference-only** microphone sensitivity/gate level, or
13789
+ * `undefined` if not set. Applies only to conference members.
13790
+ */
13755
13791
  get inputSensitivity() {
13756
13792
  return this._state$.value.input_sensitivity;
13757
13793
  }
@@ -13855,19 +13891,44 @@ var Participant = class extends Destroyable {
13855
13891
  async toggleLowbitrate() {
13856
13892
  throw new UnimplementedError();
13857
13893
  }
13858
- /** Sets the microphone input sensitivity level. */
13894
+ /**
13895
+ * Adjusts the **conference-only** microphone energy gate / sensitivity level
13896
+ * for this member. Routes through the conferencing mix engine
13897
+ * (`signalwire.conferencing member.set_input_sensitivity`) and has no effect
13898
+ * on 1:1 WebRTC calls — for those, use browser audio constraints via
13899
+ * {@link Call.setNoiseSuppression} / {@link Call.setAutoGainControl}.
13900
+ *
13901
+ * This is **not** a local PC mic gain control; it only changes how the
13902
+ * server-side mixer decides to open the mic gate on this participant.
13903
+ *
13904
+ * @param value - Sensitivity level as understood by the conference engine
13905
+ * (integer, larger values are more sensitive).
13906
+ */
13859
13907
  async setAudioInputSensitivity(value) {
13860
13908
  await this.executeMethod(this.id, "call.microphone.sensitivity.set", { sensitivity: value });
13861
13909
  }
13862
13910
  /**
13863
- * Sets the microphone input volume level.
13911
+ * Sets the **server-side** microphone volume on this participant's bridged
13912
+ * call leg. Applies a multiplier to the audio flowing through the mix
13913
+ * engine (FreeSWITCH channel read volume) — changes what other participants
13914
+ * hear, not what the local browser captures.
13915
+ *
13916
+ * For local PC mic gain, use {@link Call.setLocalMicrophoneGain} instead.
13917
+ *
13864
13918
  * @param value - Volume level (0-100).
13865
13919
  */
13866
13920
  async setAudioInputVolume(value) {
13867
13921
  await this.executeMethod(this.id, "call.microphone.volume.set", { volume: value });
13868
13922
  }
13869
13923
  /**
13870
- * Sets the speaker output volume level.
13924
+ * Sets the **server-side** speaker volume on this participant's bridged call
13925
+ * leg (FreeSWITCH channel write volume) — what this participant hears from
13926
+ * the mix before it reaches their client.
13927
+ *
13928
+ * For local playback volume (the `<audio>` element the consumer attaches
13929
+ * `remoteStream` to), set `audioElement.volume` directly in the consumer's
13930
+ * code.
13931
+ *
13871
13932
  * @param value - Volume level (0-100).
13872
13933
  */
13873
13934
  async setAudioOutputVolume(value) {
@@ -13973,7 +14034,7 @@ var SelfParticipant = class extends Participant {
13973
14034
  try {
13974
14035
  await this.vertoManager.addScreenMedia();
13975
14036
  } catch (error) {
13976
- logger$21.error("[Participant.startScreenShare] Screen share error:", error);
14037
+ logger$23.error("[Participant.startScreenShare] Screen share error:", error);
13977
14038
  }
13978
14039
  }
13979
14040
  /** Observable of the current screen share status. */
@@ -13993,7 +14054,7 @@ var SelfParticipant = class extends Participant {
13993
14054
  try {
13994
14055
  await this.vertoManager.addInputDevice(options);
13995
14056
  } catch (error) {
13996
- logger$21.error("[Participant.startScreenShare] Screen share error:", error);
14057
+ logger$23.error("[Participant.startScreenShare] Screen share error:", error);
13997
14058
  }
13998
14059
  }
13999
14060
  /** Removes an additional media input device by ID. */
@@ -14055,7 +14116,7 @@ var SelfParticipant = class extends Participant {
14055
14116
  */
14056
14117
  exitStudioModeIfActive() {
14057
14118
  if (this._studioAudio$.value) {
14058
- logger$21.debug("[SelfParticipant] Exiting studio audio mode due to individual flag toggle");
14119
+ logger$23.debug("[SelfParticipant] Exiting studio audio mode due to individual flag toggle");
14059
14120
  this._studioAudio$.next(false);
14060
14121
  }
14061
14122
  }
@@ -14079,7 +14140,7 @@ var SelfParticipant = class extends Participant {
14079
14140
  try {
14080
14141
  await super.mute();
14081
14142
  } catch (error) {
14082
- logger$21.warn("[Participant.toggleAudioInput] Server Error while muting audio input, proceeding with local toggle anyway", error);
14143
+ logger$23.warn("[Participant.toggleAudioInput] Server Error while muting audio input, proceeding with local toggle anyway", error);
14083
14144
  } finally {
14084
14145
  this.vertoManager.muteMainAudioInputDevice();
14085
14146
  }
@@ -14089,7 +14150,7 @@ var SelfParticipant = class extends Participant {
14089
14150
  try {
14090
14151
  await super.unmute();
14091
14152
  } catch (error) {
14092
- logger$21.warn("[Participant.toggleAudioInput] Server Error while unmuting audio input, proceeding with local toggle anyway", error);
14153
+ logger$23.warn("[Participant.toggleAudioInput] Server Error while unmuting audio input, proceeding with local toggle anyway", error);
14093
14154
  } finally {
14094
14155
  await this.vertoManager.unmuteMainAudioInputDevice();
14095
14156
  }
@@ -14099,7 +14160,7 @@ var SelfParticipant = class extends Participant {
14099
14160
  try {
14100
14161
  await super.muteVideo();
14101
14162
  } catch (error) {
14102
- logger$21.warn("[Participant.toggleVideoInput] Server Error while muting video input, proceeding with local toggle anyway", error);
14163
+ logger$23.warn("[Participant.toggleVideoInput] Server Error while muting video input, proceeding with local toggle anyway", error);
14103
14164
  } finally {
14104
14165
  this.vertoManager.muteMainVideoInputDevice();
14105
14166
  }
@@ -14109,7 +14170,7 @@ var SelfParticipant = class extends Participant {
14109
14170
  try {
14110
14171
  await super.unmuteVideo();
14111
14172
  } catch (error) {
14112
- logger$21.warn("[Participant.toggleVideoInput] Server Error while unmuting video input, proceeding with local toggle anyway", error);
14173
+ logger$23.warn("[Participant.toggleVideoInput] Server Error while unmuting video input, proceeding with local toggle anyway", error);
14113
14174
  } finally {
14114
14175
  await this.vertoManager.unmuteMainVideoInputDevice();
14115
14176
  }
@@ -14203,7 +14264,7 @@ function isLayoutChangedPayload(value) {
14203
14264
 
14204
14265
  //#endregion
14205
14266
  //#region src/operators/filterNull.ts
14206
- var import_cjs$21 = require_cjs();
14267
+ var import_cjs$23 = require_cjs();
14207
14268
  /**
14208
14269
  * RxJS operator that filters out `null` and `undefined` values with type narrowing.
14209
14270
  *
@@ -14215,7 +14276,7 @@ var import_cjs$21 = require_cjs();
14215
14276
  * ```
14216
14277
  */
14217
14278
  function filterNull() {
14218
- return (0, import_cjs$21.filter)((value) => value != null);
14279
+ return (0, import_cjs$23.filter)((value) => value != null);
14219
14280
  }
14220
14281
 
14221
14282
  //#endregion
@@ -14230,7 +14291,7 @@ const getValueFrom = (obj, path, defaultValue) => {
14230
14291
 
14231
14292
  //#endregion
14232
14293
  //#region src/operators/filterEventAs.ts
14233
- var import_cjs$20 = require_cjs();
14294
+ var import_cjs$22 = require_cjs();
14234
14295
  var import_operators = require_operators();
14235
14296
  /**
14236
14297
  * RxJS operator that filters events based on a predicate and maps matching events.
@@ -14264,7 +14325,7 @@ var import_operators = require_operators();
14264
14325
  * ```
14265
14326
  */
14266
14327
  function ifIsMap(predicate, mapFn) {
14267
- return (0, import_cjs$20.pipe)((0, import_operators.filter)(predicate), (0, import_operators.map)(mapFn));
14328
+ return (0, import_cjs$22.pipe)((0, import_operators.filter)(predicate), (0, import_operators.map)(mapFn));
14268
14329
  }
14269
14330
  /**
14270
14331
  * Generic RxJS operator that filters events using a type guard and extracts a property.
@@ -14306,38 +14367,38 @@ function ifIsMap(predicate, mapFn) {
14306
14367
  * ```
14307
14368
  */
14308
14369
  function filterAs(predicate, resultPath) {
14309
- return (0, import_cjs$20.pipe)(ifIsMap(predicate, (event) => {
14370
+ return (0, import_cjs$22.pipe)(ifIsMap(predicate, (event) => {
14310
14371
  return getValueFrom(event, resultPath);
14311
14372
  }), (0, import_operators.filter)((value) => value !== void 0));
14312
14373
  }
14313
14374
 
14314
14375
  //#endregion
14315
14376
  //#region src/operators/throwOnRPCError.ts
14316
- var import_cjs$19 = require_cjs();
14317
- const logger$20 = getLogger();
14377
+ var import_cjs$21 = require_cjs();
14378
+ const logger$22 = getLogger();
14318
14379
  /**
14319
14380
  * RxJS operator that throws a {@link JSONRPCError} when the RPC response contains an error.
14320
14381
  * Passes successful responses through unchanged.
14321
14382
  */
14322
14383
  function throwOnRPCError() {
14323
- return (0, import_cjs$19.map)((response) => {
14384
+ return (0, import_cjs$21.map)((response) => {
14324
14385
  if (response.error) {
14325
- logger$20.error("[throwOnRPCError] RPC error response:", {
14386
+ logger$22.error("[throwOnRPCError] RPC error response:", {
14326
14387
  code: response.error.code,
14327
14388
  message: response.error.message,
14328
14389
  data: response.error.data
14329
14390
  });
14330
14391
  throw new JSONRPCError(response.error.code, response.error.message, response.error.data);
14331
14392
  }
14332
- logger$20.debug("[throwOnRPCError] RPC successful response:", response);
14393
+ logger$22.debug("[throwOnRPCError] RPC successful response:", response);
14333
14394
  return response;
14334
14395
  });
14335
14396
  }
14336
14397
 
14337
14398
  //#endregion
14338
14399
  //#region src/managers/CallEventsManager.ts
14339
- var import_cjs$18 = require_cjs();
14340
- const logger$19 = getLogger();
14400
+ var import_cjs$20 = require_cjs();
14401
+ const logger$21 = getLogger();
14341
14402
  const initialSessionState = {};
14342
14403
  /** @internal */
14343
14404
  var CallEventsManager = class extends Destroyable {
@@ -14353,7 +14414,7 @@ var CallEventsManager = class extends Destroyable {
14353
14414
  this.initSubscriptions();
14354
14415
  }
14355
14416
  get participants$() {
14356
- return this.cachedObservable("participants$", () => this._participants$.asObservable().pipe((0, import_cjs$18.map)((participantsRecord) => Object.values(participantsRecord))));
14417
+ return this.cachedObservable("participants$", () => this._participants$.asObservable().pipe((0, import_cjs$20.map)((participantsRecord) => Object.values(participantsRecord))));
14357
14418
  }
14358
14419
  get participants() {
14359
14420
  return Object.values(this._participants$.value);
@@ -14371,40 +14432,40 @@ var CallEventsManager = class extends Destroyable {
14371
14432
  return this.callIds.has(callId);
14372
14433
  }
14373
14434
  get recording$() {
14374
- return this.cachedObservable("recording$", () => this._sessionState$.pipe((0, import_cjs$18.map)((state) => state.recording), (0, import_cjs$18.distinctUntilChanged)(), filterNull()));
14435
+ return this.cachedObservable("recording$", () => this._sessionState$.pipe((0, import_cjs$20.map)((state) => state.recording), (0, import_cjs$20.distinctUntilChanged)(), filterNull()));
14375
14436
  }
14376
14437
  get recordings$() {
14377
- return this.cachedObservable("recordings$", () => this._sessionState$.pipe((0, import_cjs$18.map)((state) => state.recordings), (0, import_cjs$18.distinctUntilChanged)(), filterNull()));
14438
+ return this.cachedObservable("recordings$", () => this._sessionState$.pipe((0, import_cjs$20.map)((state) => state.recordings), (0, import_cjs$20.distinctUntilChanged)(), filterNull()));
14378
14439
  }
14379
14440
  get streaming$() {
14380
- return this.cachedObservable("streaming$", () => this._sessionState$.pipe((0, import_cjs$18.map)((state) => state.streaming), (0, import_cjs$18.distinctUntilChanged)(), filterNull()));
14441
+ return this.cachedObservable("streaming$", () => this._sessionState$.pipe((0, import_cjs$20.map)((state) => state.streaming), (0, import_cjs$20.distinctUntilChanged)(), filterNull()));
14381
14442
  }
14382
14443
  get streams$() {
14383
- return this.cachedObservable("streams$", () => this._sessionState$.pipe((0, import_cjs$18.map)((state) => state.streams), (0, import_cjs$18.distinctUntilChanged)(), filterNull()));
14444
+ return this.cachedObservable("streams$", () => this._sessionState$.pipe((0, import_cjs$20.map)((state) => state.streams), (0, import_cjs$20.distinctUntilChanged)(), filterNull()));
14384
14445
  }
14385
14446
  get playbacks$() {
14386
- return this.cachedObservable("playbacks$", () => this._sessionState$.pipe((0, import_cjs$18.map)((state) => state.playbacks), (0, import_cjs$18.distinctUntilChanged)(), filterNull()));
14447
+ return this.cachedObservable("playbacks$", () => this._sessionState$.pipe((0, import_cjs$20.map)((state) => state.playbacks), (0, import_cjs$20.distinctUntilChanged)(), filterNull()));
14387
14448
  }
14388
14449
  get raiseHandPriority$() {
14389
- return this.cachedObservable("raiseHandPriority$", () => this._sessionState$.pipe((0, import_cjs$18.map)((state) => state.prioritize_handraise), (0, import_cjs$18.distinctUntilChanged)(), filterNull()));
14450
+ return this.cachedObservable("raiseHandPriority$", () => this._sessionState$.pipe((0, import_cjs$20.map)((state) => state.prioritize_handraise), (0, import_cjs$20.distinctUntilChanged)(), filterNull()));
14390
14451
  }
14391
14452
  get locked$() {
14392
- return this.cachedObservable("locked$", () => this._sessionState$.pipe((0, import_cjs$18.map)((state) => state.locked), (0, import_cjs$18.distinctUntilChanged)(), filterNull()));
14453
+ return this.cachedObservable("locked$", () => this._sessionState$.pipe((0, import_cjs$20.map)((state) => state.locked), (0, import_cjs$20.distinctUntilChanged)(), filterNull()));
14393
14454
  }
14394
14455
  get meta$() {
14395
- return this.cachedObservable("meta$", () => this._sessionState$.pipe((0, import_cjs$18.map)((state) => state.meta), (0, import_cjs$18.distinctUntilChanged)(), filterNull()));
14456
+ return this.cachedObservable("meta$", () => this._sessionState$.pipe((0, import_cjs$20.map)((state) => state.meta), (0, import_cjs$20.distinctUntilChanged)(), filterNull()));
14396
14457
  }
14397
14458
  get capabilities$() {
14398
- return this.cachedObservable("capabilities$", () => this._sessionState$.pipe((0, import_cjs$18.map)((state) => state.capabilities), (0, import_cjs$18.distinctUntilChanged)(), filterNull()));
14459
+ return this.cachedObservable("capabilities$", () => this._sessionState$.pipe((0, import_cjs$20.map)((state) => state.capabilities), (0, import_cjs$20.distinctUntilChanged)(), filterNull()));
14399
14460
  }
14400
14461
  get layout$() {
14401
- return this.cachedObservable("layout$", () => this._sessionState$.pipe((0, import_cjs$18.map)((state) => state.layout_name), (0, import_cjs$18.distinctUntilChanged)(), filterNull()));
14462
+ return this.cachedObservable("layout$", () => this._sessionState$.pipe((0, import_cjs$20.map)((state) => state.layout_name), (0, import_cjs$20.distinctUntilChanged)(), filterNull()));
14402
14463
  }
14403
14464
  get layouts$() {
14404
- return this.cachedObservable("layouts$", () => this._sessionState$.pipe((0, import_cjs$18.map)((state) => state.layouts), (0, import_cjs$18.distinctUntilChanged)(), filterNull()));
14465
+ return this.cachedObservable("layouts$", () => this._sessionState$.pipe((0, import_cjs$20.map)((state) => state.layouts), (0, import_cjs$20.distinctUntilChanged)(), filterNull()));
14405
14466
  }
14406
14467
  get layoutLayers$() {
14407
- return this.cachedObservable("layoutLayers$", () => this._sessionState$.pipe((0, import_cjs$18.map)((state) => state.layout_layers), (0, import_cjs$18.distinctUntilChanged)(), filterNull()));
14468
+ return this.cachedObservable("layoutLayers$", () => this._sessionState$.pipe((0, import_cjs$20.map)((state) => state.layout_layers), (0, import_cjs$20.distinctUntilChanged)(), filterNull()));
14408
14469
  }
14409
14470
  get self() {
14410
14471
  return this._self$.value;
@@ -14441,7 +14502,7 @@ var CallEventsManager = class extends Destroyable {
14441
14502
  }
14442
14503
  initSubscriptions() {
14443
14504
  this.subscribeTo(this.callJoinedEvent$, (callJoinedEvent) => {
14444
- logger$19.debug("[CallEventsManager] Handling call.joined event for call/session IDs:", {
14505
+ logger$21.debug("[CallEventsManager] Handling call.joined event for call/session IDs:", {
14445
14506
  callId: callJoinedEvent.call_id,
14446
14507
  roomSessionId: callJoinedEvent.room_session_id
14447
14508
  });
@@ -14468,19 +14529,19 @@ var CallEventsManager = class extends Destroyable {
14468
14529
  if (this._self$.value?.capabilities.setLayout) this.updateLayouts();
14469
14530
  });
14470
14531
  this.subscribeTo(this.memberUpdates$, (member) => {
14471
- logger$19.debug("[CallEventsManager] Handling member update event for member ID:", member);
14532
+ logger$21.debug("[CallEventsManager] Handling member update event for member ID:", member);
14472
14533
  this.upsertParticipant(member);
14473
14534
  });
14474
14535
  this.subscribeTo(this.webRtcCallSession.memberLeft$, (memberLeftEvent) => {
14475
- logger$19.debug("[CallEventsManager] Handling member.left event for member ID:", memberLeftEvent.member.member_id);
14536
+ logger$21.debug("[CallEventsManager] Handling member.left event for member ID:", memberLeftEvent.member.member_id);
14476
14537
  const participants = { ...this._participants$.value };
14477
14538
  if (memberLeftEvent.member.member_id in participants) {
14478
14539
  delete participants[memberLeftEvent.member.member_id];
14479
14540
  this._participants$.next(participants);
14480
- } else logger$19.warn(`[CallEventsManager] Received member.left event for unknown member ID: ${memberLeftEvent.member.member_id}`);
14541
+ } else logger$21.warn(`[CallEventsManager] Received member.left event for unknown member ID: ${memberLeftEvent.member.member_id}`);
14481
14542
  });
14482
14543
  this.subscribeTo(this.webRtcCallSession.callUpdated$, (callUpdatedEvent) => {
14483
- logger$19.debug("[CallEventsManager] Handling call.updated event:", callUpdatedEvent);
14544
+ logger$21.debug("[CallEventsManager] Handling call.updated event:", callUpdatedEvent);
14484
14545
  const roomSession = callUpdatedEvent.room_session;
14485
14546
  this._sessionState$.next({
14486
14547
  ...this._sessionState$.value,
@@ -14495,7 +14556,7 @@ var CallEventsManager = class extends Destroyable {
14495
14556
  });
14496
14557
  });
14497
14558
  this.subscribeTo(this.layoutChangedEvent$, (layoutChangedEvent) => {
14498
- logger$19.debug("[CallEventsManager] Handling layout.changed event:", layoutChangedEvent);
14559
+ logger$21.debug("[CallEventsManager] Handling layout.changed event:", layoutChangedEvent);
14499
14560
  this._sessionState$.next({
14500
14561
  ...this._sessionState$.value,
14501
14562
  layout_name: layoutChangedEvent.id,
@@ -14505,10 +14566,10 @@ var CallEventsManager = class extends Destroyable {
14505
14566
  });
14506
14567
  }
14507
14568
  updateParticipantPositions(layoutChangedEvent) {
14508
- if (Object.keys(this._participants$.value).length > 0 && !layoutChangedEvent.layers.some((layer) => !!layer.member_id)) logger$19.warn("[CallEventsManager] No layers with member_id found in layout.changed event. Nothing to update.");
14569
+ if (Object.keys(this._participants$.value).length > 0 && !layoutChangedEvent.layers.some((layer) => !!layer.member_id)) logger$21.warn("[CallEventsManager] No layers with member_id found in layout.changed event. Nothing to update.");
14509
14570
  layoutChangedEvent.layers.filter((layer) => !!layer.member_id).filter((layer) => {
14510
14571
  if (!(layer.member_id in this._participants$.value)) {
14511
- logger$19.warn(`[CallEventsManager] Skipping layout layer for unknown member_id: ${layer.member_id}`);
14572
+ logger$21.warn(`[CallEventsManager] Skipping layout layer for unknown member_id: ${layer.member_id}`);
14512
14573
  return false;
14513
14574
  }
14514
14575
  return true;
@@ -14531,7 +14592,7 @@ var CallEventsManager = class extends Destroyable {
14531
14592
  layouts: response.result.layouts
14532
14593
  });
14533
14594
  }).catch((error) => {
14534
- logger$19.error("[CallEventsManager] Error fetching layouts:", error);
14595
+ logger$21.error("[CallEventsManager] Error fetching layouts:", error);
14535
14596
  });
14536
14597
  }
14537
14598
  updateParticipants(members) {
@@ -14547,7 +14608,7 @@ var CallEventsManager = class extends Destroyable {
14547
14608
  }
14548
14609
  const participant = this._participants$.value[member.member_id];
14549
14610
  const oldValue = participant.value;
14550
- logger$19.debug("[CallEventsManager] Updating participant:", member.member_id, {
14611
+ logger$21.debug("[CallEventsManager] Updating participant:", member.member_id, {
14551
14612
  oldValue,
14552
14613
  newValue: member
14553
14614
  });
@@ -14559,18 +14620,18 @@ var CallEventsManager = class extends Destroyable {
14559
14620
  this._participants$.next(this._participants$.value);
14560
14621
  }
14561
14622
  get callJoinedEvent$() {
14562
- return this.cachedObservable("callJoinedEvent$", () => this.webRtcCallSession.callEvent$.pipe((0, import_cjs$18.filter)(isCallJoinedPayload), (0, import_cjs$18.tap)((event) => {
14563
- logger$19.debug("[CallEventsManager] Call joined event:", event);
14623
+ return this.cachedObservable("callJoinedEvent$", () => this.webRtcCallSession.callEvent$.pipe((0, import_cjs$20.filter)(isCallJoinedPayload), (0, import_cjs$20.tap)((event) => {
14624
+ logger$21.debug("[CallEventsManager] Call joined event:", event);
14564
14625
  })));
14565
14626
  }
14566
14627
  get layoutChangedEvent$() {
14567
- return this.cachedObservable("layoutChangedEvent$", () => this.webRtcCallSession.callEvent$.pipe(filterAs(isLayoutChangedPayload, "layout"), (0, import_cjs$18.tap)((event) => {
14568
- logger$19.debug("[CallEventsManager] Layout changed event:", event);
14628
+ return this.cachedObservable("layoutChangedEvent$", () => this.webRtcCallSession.callEvent$.pipe(filterAs(isLayoutChangedPayload, "layout"), (0, import_cjs$20.tap)((event) => {
14629
+ logger$21.debug("[CallEventsManager] Layout changed event:", event);
14569
14630
  })));
14570
14631
  }
14571
14632
  get memberUpdates$() {
14572
- return this.cachedObservable("memberUpdates$", () => (0, import_cjs$18.merge)(this.webRtcCallSession.memberJoined$, this.webRtcCallSession.memberUpdated$, this.webRtcCallSession.memberTalking$).pipe((0, import_cjs$18.map)((event) => event.member), (0, import_cjs$18.tap)((event) => {
14573
- logger$19.debug("[CallEventsManager] Member update event:", event);
14633
+ return this.cachedObservable("memberUpdates$", () => (0, import_cjs$20.merge)(this.webRtcCallSession.memberJoined$, this.webRtcCallSession.memberUpdated$, this.webRtcCallSession.memberTalking$).pipe((0, import_cjs$20.map)((event) => event.member), (0, import_cjs$20.tap)((event) => {
14634
+ logger$21.debug("[CallEventsManager] Member update event:", event);
14574
14635
  })));
14575
14636
  }
14576
14637
  destroy() {
@@ -14826,8 +14887,8 @@ function appendStereoParams(fmtpLine, maxBitrate) {
14826
14887
 
14827
14888
  //#endregion
14828
14889
  //#region src/controllers/ICEGatheringController.ts
14829
- var import_cjs$17 = require_cjs();
14830
- const logger$18 = getLogger();
14890
+ var import_cjs$19 = require_cjs();
14891
+ const logger$20 = getLogger();
14831
14892
  var ICEGatheringController = class extends Destroyable {
14832
14893
  constructor(peerConnection, peerConnectionControllerNegotiating$, options = {}) {
14833
14894
  super();
@@ -14835,23 +14896,23 @@ var ICEGatheringController = class extends Destroyable {
14835
14896
  this.peerConnectionControllerNegotiating$ = peerConnectionControllerNegotiating$;
14836
14897
  this.onicegatheringstatechangeHandler = () => {
14837
14898
  const { iceGatheringState } = this.peerConnection;
14838
- logger$18.debug(`[ICEGatheringController] ICE gathering state changed to: ${iceGatheringState}`);
14899
+ logger$20.debug(`[ICEGatheringController] ICE gathering state changed to: ${iceGatheringState}`);
14839
14900
  if (iceGatheringState === "gathering") this._iceCandidatesState.next({
14840
14901
  state: "gathering",
14841
14902
  validSDP: false
14842
14903
  });
14843
14904
  };
14844
14905
  this.onicecandidateHandler = (event) => {
14845
- logger$18.debug("[ICEGatheringController] ICE candidate event received:", event.candidate);
14906
+ logger$20.debug("[ICEGatheringController] ICE candidate event received:", event.candidate);
14846
14907
  this.removeTimer("iceCandidateTimer");
14847
14908
  if (event.candidate) this.iceCandidateTimer = setTimeout(() => {
14848
14909
  if (this.peerConnection.iceGatheringState !== "complete") {
14849
- logger$18.warn("[ICEGatheringController] ICE candidate timeout, using current SDP");
14910
+ logger$20.warn("[ICEGatheringController] ICE candidate timeout, using current SDP");
14850
14911
  this.handleICECandidateTimeout();
14851
14912
  }
14852
14913
  }, this.iceCandidateTimeout);
14853
14914
  else {
14854
- logger$18.debug("[ICEGatheringController] ICE gathering completed: null candidate received");
14915
+ logger$20.debug("[ICEGatheringController] ICE gathering completed: null candidate received");
14855
14916
  this.removeTimer("iceGatheringTimer");
14856
14917
  this.handleICEGatheringComplete();
14857
14918
  }
@@ -14864,12 +14925,12 @@ var ICEGatheringController = class extends Destroyable {
14864
14925
  this.iceGatheringTimeout = options.iceGatheringTimeout ?? DEFAULT_ICE_GATHERING_TIMEOUT_MS;
14865
14926
  this.relayOnly = options.relayOnly ?? false;
14866
14927
  this.setupEventListeners();
14867
- this.subscribeTo(this.peerConnectionControllerNegotiating$.pipe((0, import_cjs$17.filter)((isNegotiating) => isNegotiating)), (isNegotiating) => {
14928
+ this.subscribeTo(this.peerConnectionControllerNegotiating$.pipe((0, import_cjs$19.filter)((isNegotiating) => isNegotiating)), (isNegotiating) => {
14868
14929
  if (isNegotiating) {
14869
14930
  this.setupEventListeners();
14870
14931
  this.iceGatheringTimer = setTimeout(() => {
14871
14932
  if (this.peerConnection.iceGatheringState !== "complete") {
14872
- logger$18.warn("[ICEGatheringController] ICE gathering timeout, using current SDP");
14933
+ logger$20.warn("[ICEGatheringController] ICE gathering timeout, using current SDP");
14873
14934
  this.handleICEGatheringTimeout();
14874
14935
  }
14875
14936
  }, this.iceGatheringTimeout);
@@ -14883,7 +14944,7 @@ var ICEGatheringController = class extends Destroyable {
14883
14944
  this.peerConnection.addEventListener("icegatheringstatechange", this.onicegatheringstatechangeHandler);
14884
14945
  }
14885
14946
  get iceCandidatesState$() {
14886
- return this._iceCandidatesState.pipe((0, import_cjs$17.withLatestFrom)(this.peerConnectionControllerNegotiating$), (0, import_cjs$17.filter)(([_, isNegotiating]) => isNegotiating), (0, import_cjs$17.map)(([state, _]) => state.state));
14947
+ return this._iceCandidatesState.pipe((0, import_cjs$19.withLatestFrom)(this.peerConnectionControllerNegotiating$), (0, import_cjs$19.filter)(([_, isNegotiating]) => isNegotiating), (0, import_cjs$19.map)(([state, _]) => state.state));
14887
14948
  }
14888
14949
  get hasValidLocalDescriptionSDP() {
14889
14950
  const sdp = this.peerConnection.localDescription?.sdp;
@@ -14896,9 +14957,9 @@ var ICEGatheringController = class extends Destroyable {
14896
14957
  this.relayOnly = value;
14897
14958
  }
14898
14959
  handleICEGatheringComplete() {
14899
- logger$18.debug("[ICEGatheringController] Handling ICE gathering complete");
14900
- logger$18.debug(`[ICEGatheringController] Checking ICE gathering state: ${this.peerConnection.iceGatheringState}`);
14901
- logger$18.debug("[ICEGatheringController] ICE gathering complete");
14960
+ logger$20.debug("[ICEGatheringController] Handling ICE gathering complete");
14961
+ logger$20.debug(`[ICEGatheringController] Checking ICE gathering state: ${this.peerConnection.iceGatheringState}`);
14962
+ logger$20.debug("[ICEGatheringController] ICE gathering complete");
14902
14963
  this._iceCandidatesState.next({
14903
14964
  state: "complete",
14904
14965
  validSDP: this.hasValidLocalDescriptionSDP
@@ -14914,21 +14975,21 @@ var ICEGatheringController = class extends Destroyable {
14914
14975
  this.removeTimer("iceGatheringTimer");
14915
14976
  const validSDP = this.hasValidLocalDescriptionSDP;
14916
14977
  if (validSDP) {
14917
- logger$18.debug("[ICEGatheringController] Local SDP is valid");
14978
+ logger$20.debug("[ICEGatheringController] Local SDP is valid");
14918
14979
  this._iceCandidatesState.next({
14919
14980
  state: "timeout",
14920
14981
  validSDP
14921
14982
  });
14922
14983
  this.stopGathering();
14923
- } else logger$18.debug("### ICE gathering timeout\n", this.peerConnection.localDescription?.sdp);
14984
+ } else logger$20.debug("### ICE gathering timeout\n", this.peerConnection.localDescription?.sdp);
14924
14985
  }
14925
14986
  handleICECandidateTimeout() {
14926
14987
  if (this.iceCandidateTimer) this.removeTimer("iceCandidateTimer");
14927
- logger$18.warn("[ICEGatheringController] ICE candidate timeout");
14988
+ logger$20.warn("[ICEGatheringController] ICE candidate timeout");
14928
14989
  const validSDP = this.hasValidLocalDescriptionSDP;
14929
14990
  if (!validSDP && !this.relayOnly) this.restartICEGatheringWithRelayOnly();
14930
14991
  else {
14931
- logger$18.debug("[ICEGatheringController] Using current SDP due to ICE candidate timeout");
14992
+ logger$20.debug("[ICEGatheringController] Using current SDP due to ICE candidate timeout");
14932
14993
  this._iceCandidatesState.next({
14933
14994
  state: "timeout",
14934
14995
  validSDP
@@ -14937,7 +14998,7 @@ var ICEGatheringController = class extends Destroyable {
14937
14998
  }
14938
14999
  }
14939
15000
  restartICEGatheringWithRelayOnly() {
14940
- logger$18.debug("[ICEGatheringController] Restarting ICE gathering with relay-only candidates");
15001
+ logger$20.debug("[ICEGatheringController] Restarting ICE gathering with relay-only candidates");
14941
15002
  this.relayOnly = true;
14942
15003
  this.peerConnection.setConfiguration({
14943
15004
  ...this.peerConnection.getConfiguration(),
@@ -14952,7 +15013,7 @@ var ICEGatheringController = class extends Destroyable {
14952
15013
  }
14953
15014
  }
14954
15015
  clearAllTimers() {
14955
- logger$18.debug("[ICEGatheringController] Clearing all timers");
15016
+ logger$20.debug("[ICEGatheringController] Clearing all timers");
14956
15017
  this.removeTimer("iceGatheringTimer");
14957
15018
  this.removeTimer("iceCandidateTimer");
14958
15019
  }
@@ -14961,17 +15022,181 @@ var ICEGatheringController = class extends Destroyable {
14961
15022
  this.peerConnection.removeEventListener("icecandidate", this.onicecandidateHandler);
14962
15023
  }
14963
15024
  destroy() {
14964
- logger$18.debug("[ICEGatheringController] Destroying ICEGatheringController");
15025
+ logger$20.debug("[ICEGatheringController] Destroying ICEGatheringController");
14965
15026
  this.clearAllTimers();
14966
15027
  this.removeEventListeners();
14967
15028
  super.destroy();
14968
15029
  }
14969
15030
  };
14970
15031
 
15032
+ //#endregion
15033
+ //#region src/controllers/LocalAudioPipeline.ts
15034
+ var import_cjs$18 = require_cjs();
15035
+ const logger$19 = getLogger();
15036
+ /**
15037
+ * Web Audio pipeline for the local microphone stream.
15038
+ *
15039
+ * Wraps the raw mic `MediaStreamTrack` in a graph of:
15040
+ *
15041
+ * ```
15042
+ * MediaStreamAudioSourceNode → GainNode → AnalyserNode → MediaStreamAudioDestinationNode
15043
+ * ```
15044
+ *
15045
+ * The {@link outputTrack} from the destination node is what callers should
15046
+ * attach to the `RTCRtpSender` in place of the raw mic track. The same
15047
+ * destination track is reused across input changes (device switch, mute /
15048
+ * unmute track replacement) so the sender reference stays stable — only the
15049
+ * source end of the graph is rebuilt.
15050
+ *
15051
+ * The pipeline owns a single {@link AudioContext}. Callers must invoke
15052
+ * {@link destroy} to release it when the call ends.
15053
+ */
15054
+ var LocalAudioPipeline = class extends Destroyable {
15055
+ constructor(options = {}) {
15056
+ super();
15057
+ this._inputSource = null;
15058
+ this._inputStream = null;
15059
+ this._lastSpokeAt = 0;
15060
+ this._gain$ = this.createBehaviorSubject(1);
15061
+ this._pttMultiplier = 1;
15062
+ this._audioContext = (options.audioContextFactory ?? (() => new AudioContext()))();
15063
+ this._gainNode = this._audioContext.createGain();
15064
+ this._analyser = this._audioContext.createAnalyser();
15065
+ this._analyser.fftSize = 2048;
15066
+ this._analyser.smoothingTimeConstant = .3;
15067
+ this._analyserBuffer = new Uint8Array(new ArrayBuffer(this._analyser.fftSize));
15068
+ this._destination = this._audioContext.createMediaStreamDestination();
15069
+ this._gainNode.connect(this._analyser);
15070
+ this._analyser.connect(this._destination);
15071
+ this._speakingThreshold = options.speakingThreshold ?? VAD_THRESHOLD;
15072
+ this._speakingHoldMs = options.speakingHoldMs ?? VAD_HOLD_MS;
15073
+ this._pollIntervalMs = options.pollIntervalMs ?? AUDIO_LEVEL_POLL_INTERVAL_MS;
15074
+ const initial = options.initialGain ?? 1;
15075
+ this._gain$.next(initial);
15076
+ this.applyEffectiveGain();
15077
+ }
15078
+ /** Observable of the current gain value (0..2). */
15079
+ get gain$() {
15080
+ return this._gain$.asObservable();
15081
+ }
15082
+ /** Current gain value (0..2). */
15083
+ get gain() {
15084
+ return this._gain$.value;
15085
+ }
15086
+ /**
15087
+ * Processed output track to attach to the RTCRtpSender. Stable reference
15088
+ * across input changes, so `sender.replaceTrack(pipeline.outputTrack)` only
15089
+ * needs to be called once.
15090
+ */
15091
+ get outputTrack() {
15092
+ const [track] = this._destination.stream.getAudioTracks();
15093
+ return track;
15094
+ }
15095
+ /**
15096
+ * Root-mean-square audio level of the input signal, 0..1. Emits on a fixed
15097
+ * interval (~30fps by default).
15098
+ */
15099
+ get level$() {
15100
+ return this.deferEmission((0, import_cjs$18.interval)(this._pollIntervalMs, import_cjs$18.animationFrameScheduler).pipe((0, import_cjs$18.map)(() => this.computeLevel())));
15101
+ }
15102
+ /**
15103
+ * Boolean VAD derived from {@link level$}. True while level ≥ threshold or
15104
+ * during the hold window after the last frame that crossed the threshold.
15105
+ */
15106
+ get speaking$() {
15107
+ return this.deferEmission(this.level$.pipe((0, import_cjs$18.map)((level) => this.evaluateSpeaking(level)), (0, import_cjs$18.distinctUntilChanged)()));
15108
+ }
15109
+ /**
15110
+ * Set gain multiplier applied to the input signal. 0 = silence,
15111
+ * 1 = unity, 2 = 2x. Values are clamped to [0, 2]. The effective gain on
15112
+ * the graph also respects the current PTT state.
15113
+ */
15114
+ setGain(value) {
15115
+ const clamped = Math.max(0, Math.min(2, value));
15116
+ this._gain$.next(clamped);
15117
+ this.applyEffectiveGain();
15118
+ }
15119
+ /**
15120
+ * Silence the graph when `active = false`, otherwise restore the configured
15121
+ * gain. Use this from a PTT handler: released → `false`, held → `true`.
15122
+ * Orthogonal to {@link setGain} — once PTT returns to active, the last
15123
+ * configured gain reappears.
15124
+ */
15125
+ setPTTActive(active) {
15126
+ this._pttMultiplier = active ? 1 : 0;
15127
+ this.applyEffectiveGain();
15128
+ }
15129
+ applyEffectiveGain() {
15130
+ this._gainNode.gain.value = this._gain$.value * this._pttMultiplier;
15131
+ }
15132
+ /**
15133
+ * Wire a new raw mic track as the pipeline's input. Replaces any previous
15134
+ * input source and reconnects the graph so {@link outputTrack} continues
15135
+ * to emit the processed audio. Pass `null` to disconnect the input (the
15136
+ * output track stays alive but emits silence).
15137
+ *
15138
+ * Also resumes the underlying AudioContext on attach — Chrome creates it
15139
+ * in a suspended state and the graph won't process (the destination
15140
+ * track emits silence) until resume() succeeds.
15141
+ */
15142
+ setInputTrack(track) {
15143
+ if (this._inputSource) {
15144
+ try {
15145
+ this._inputSource.disconnect();
15146
+ } catch (error) {
15147
+ logger$19.debug("[LocalAudioPipeline] input disconnect warning:", error);
15148
+ }
15149
+ this._inputSource = null;
15150
+ }
15151
+ if (this._inputStream) this._inputStream = null;
15152
+ if (!track) return;
15153
+ this._inputStream = new MediaStream([track]);
15154
+ this._inputSource = this._audioContext.createMediaStreamSource(this._inputStream);
15155
+ this._inputSource.connect(this._gainNode);
15156
+ if (this._audioContext.state === "suspended") this._audioContext.resume().catch((error) => {
15157
+ logger$19.warn("[LocalAudioPipeline] AudioContext resume failed:", error);
15158
+ });
15159
+ }
15160
+ destroy() {
15161
+ if (this._inputSource) {
15162
+ try {
15163
+ this._inputSource.disconnect();
15164
+ } catch {}
15165
+ this._inputSource = null;
15166
+ }
15167
+ try {
15168
+ this._gainNode.disconnect();
15169
+ this._analyser.disconnect();
15170
+ } catch {}
15171
+ this._audioContext.close().catch((error) => {
15172
+ logger$19.debug("[LocalAudioPipeline] audio context close warning:", error);
15173
+ });
15174
+ super.destroy();
15175
+ }
15176
+ computeLevel() {
15177
+ if (!this._inputSource) return 0;
15178
+ this._analyser.getByteTimeDomainData(this._analyserBuffer);
15179
+ let sum = 0;
15180
+ for (const sample$1 of this._analyserBuffer) {
15181
+ const normalized = (sample$1 - 128) / 128;
15182
+ sum += normalized * normalized;
15183
+ }
15184
+ return Math.sqrt(sum / this._analyserBuffer.length);
15185
+ }
15186
+ evaluateSpeaking(level) {
15187
+ const now = Date.now();
15188
+ if (level >= this._speakingThreshold) {
15189
+ this._lastSpokeAt = now;
15190
+ return true;
15191
+ }
15192
+ return now - this._lastSpokeAt < this._speakingHoldMs;
15193
+ }
15194
+ };
15195
+
14971
15196
  //#endregion
14972
15197
  //#region src/controllers/LocalStreamController.ts
14973
- var import_cjs$16 = require_cjs();
14974
- const logger$17 = getLogger();
15198
+ var import_cjs$17 = require_cjs();
15199
+ const logger$18 = getLogger();
14975
15200
  var LocalStreamController = class extends Destroyable {
14976
15201
  constructor(options) {
14977
15202
  super();
@@ -14985,16 +15210,16 @@ var LocalStreamController = class extends Destroyable {
14985
15210
  this._mediaTrackEnded$ = this.createSubject();
14986
15211
  }
14987
15212
  get localStream$() {
14988
- return this._localStream$.asObservable().pipe((0, import_cjs$16.takeUntil)(this.destroyed$));
15213
+ return this._localStream$.asObservable().pipe((0, import_cjs$17.takeUntil)(this.destroyed$));
14989
15214
  }
14990
15215
  get localAudioTracks$() {
14991
- return this._localAudioTracks$.asObservable().pipe((0, import_cjs$16.takeUntil)(this.destroyed$));
15216
+ return this._localAudioTracks$.asObservable().pipe((0, import_cjs$17.takeUntil)(this.destroyed$));
14992
15217
  }
14993
15218
  get localVideoTracks$() {
14994
- return this._localVideoTracks$.asObservable().pipe((0, import_cjs$16.takeUntil)(this.destroyed$));
15219
+ return this._localVideoTracks$.asObservable().pipe((0, import_cjs$17.takeUntil)(this.destroyed$));
14995
15220
  }
14996
15221
  get mediaTrackEnded$() {
14997
- return this._mediaTrackEnded$.asObservable().pipe((0, import_cjs$16.takeUntil)(this.destroyed$));
15222
+ return this._mediaTrackEnded$.asObservable().pipe((0, import_cjs$17.takeUntil)(this.destroyed$));
14998
15223
  }
14999
15224
  get localStream() {
15000
15225
  return this._localStream$.value;
@@ -15009,26 +15234,26 @@ var LocalStreamController = class extends Destroyable {
15009
15234
  * Build the local media stream based on the provided options.
15010
15235
  */
15011
15236
  async buildLocalStream() {
15012
- logger$17.debug("[LocalStreamController] Building local media stream.");
15237
+ logger$18.debug("[LocalStreamController] Building local media stream.");
15013
15238
  let stream;
15014
15239
  if (this.options.inputAudioStream ?? this.options.inputVideoStream) {
15015
15240
  const tracks = [...this.options.inputAudioStream?.getTracks() ?? [], ...this.options.inputVideoStream?.getTracks() ?? []];
15016
15241
  stream = new MediaStream(tracks);
15017
15242
  } else if (this.options.propose === "screenshare") {
15018
- logger$17.debug("[LocalStreamController] Requesting display media for screen sharing with audio:", Boolean(this.options.inputAudioDeviceConstraints));
15243
+ logger$18.debug("[LocalStreamController] Requesting display media for screen sharing with audio:", Boolean(this.options.inputAudioDeviceConstraints));
15019
15244
  stream = await this.options.getDisplayMedia({
15020
15245
  video: true,
15021
15246
  audio: Boolean(this.options.inputAudioDeviceConstraints)
15022
15247
  });
15023
- logger$17.debug("[LocalStreamController] Screen share media obtained:", stream);
15248
+ logger$18.debug("[LocalStreamController] Screen share media obtained:", stream);
15024
15249
  } else {
15025
15250
  const constraints = {
15026
15251
  audio: this.options.inputAudioDeviceConstraints,
15027
15252
  video: this.options.inputVideoDeviceConstraints
15028
15253
  };
15029
- logger$17.debug("[LocalStreamController] Requesting user media with constraints:", constraints);
15254
+ logger$18.debug("[LocalStreamController] Requesting user media with constraints:", constraints);
15030
15255
  stream = await this.options.getUserMedia(constraints);
15031
- logger$17.debug("[LocalStreamController] User media obtained:", stream);
15256
+ logger$18.debug("[LocalStreamController] User media obtained:", stream);
15032
15257
  }
15033
15258
  this._localStream$.next(stream);
15034
15259
  return stream;
@@ -15045,7 +15270,7 @@ var LocalStreamController = class extends Destroyable {
15045
15270
  this._localStream$.next(localStream);
15046
15271
  if (track.kind === "video") this._localVideoTracks$.next(localStream.getVideoTracks());
15047
15272
  else this._localAudioTracks$.next(localStream.getAudioTracks());
15048
- logger$17.debug(`[LocalStreamController] ${track.kind} track added:`, track.id);
15273
+ logger$18.debug(`[LocalStreamController] ${track.kind} track added:`, track.id);
15049
15274
  return localStream;
15050
15275
  }
15051
15276
  /**
@@ -15057,7 +15282,7 @@ var LocalStreamController = class extends Destroyable {
15057
15282
  const stream = this._localStream$.value;
15058
15283
  const track = stream?.getTracks().find((t) => t.id === trackId);
15059
15284
  if (!track) {
15060
- logger$17.debug(`[LocalStreamController] track not found: ${trackId}`);
15285
+ logger$18.debug(`[LocalStreamController] track not found: ${trackId}`);
15061
15286
  return;
15062
15287
  }
15063
15288
  track.removeEventListener("ended", this.mediaTrackEndedHandler);
@@ -15066,7 +15291,7 @@ var LocalStreamController = class extends Destroyable {
15066
15291
  this._localStream$.next(stream);
15067
15292
  if (track.kind === "video") this._localVideoTracks$.next(stream?.getVideoTracks() ?? []);
15068
15293
  else this._localAudioTracks$.next(stream?.getAudioTracks() ?? []);
15069
- logger$17.debug(`[LocalStreamController] ${track.kind} track removed:`, trackId);
15294
+ logger$18.debug(`[LocalStreamController] ${track.kind} track removed:`, trackId);
15070
15295
  return track;
15071
15296
  }
15072
15297
  /**
@@ -15101,7 +15326,7 @@ var LocalStreamController = class extends Destroyable {
15101
15326
  */
15102
15327
  stopAllTracks() {
15103
15328
  this._localStream$.value?.getTracks().forEach((track) => {
15104
- logger$17.debug(`[LocalStreamController] Stopping local track: ${track.kind}`);
15329
+ logger$18.debug(`[LocalStreamController] Stopping local track: ${track.kind}`);
15105
15330
  track.removeEventListener("ended", this.mediaTrackEndedHandler);
15106
15331
  track.stop();
15107
15332
  });
@@ -15117,7 +15342,7 @@ var LocalStreamController = class extends Destroyable {
15117
15342
 
15118
15343
  //#endregion
15119
15344
  //#region src/controllers/TransceiverController.ts
15120
- const logger$16 = getLogger();
15345
+ const logger$17 = getLogger();
15121
15346
  const getDirection = (send, recv) => {
15122
15347
  if (send && recv) return "sendrecv";
15123
15348
  else if (send && !recv) return "sendonly";
@@ -15219,7 +15444,7 @@ var TransceiverController = class extends Destroyable {
15219
15444
  sendEncodings: isAudio ? void 0 : this.sendEncodings,
15220
15445
  streams: direction === "recvonly" ? void 0 : [localStream]
15221
15446
  };
15222
- logger$16.debug(`[TransceiverController] Setting up transceiver sender for local ${track.kind} track:`, {
15447
+ logger$17.debug(`[TransceiverController] Setting up transceiver sender for local ${track.kind} track:`, {
15223
15448
  transceiver,
15224
15449
  transceiverParams
15225
15450
  });
@@ -15227,11 +15452,11 @@ var TransceiverController = class extends Destroyable {
15227
15452
  await transceiver.sender.replaceTrack(track);
15228
15453
  transceiver.direction = transceiverParams.direction;
15229
15454
  if (transceiverParams.streams?.some((stream) => Boolean(stream))) {
15230
- logger$16.debug(`[TransceiverController] Setting streams for transceiver sender for local ${track.kind} track:`, transceiverParams.streams);
15455
+ logger$17.debug(`[TransceiverController] Setting streams for transceiver sender for local ${track.kind} track:`, transceiverParams.streams);
15231
15456
  transceiver.sender.setStreams(...transceiverParams.streams);
15232
15457
  }
15233
15458
  } else {
15234
- logger$16.debug(`[TransceiverController] Adding new transceiver for local ${track.kind} track:`, track.id);
15459
+ logger$17.debug(`[TransceiverController] Adding new transceiver for local ${track.kind} track:`, track.id);
15235
15460
  this.peerConnection.addTransceiver(track, transceiverParams);
15236
15461
  }
15237
15462
  }
@@ -15245,13 +15470,13 @@ var TransceiverController = class extends Destroyable {
15245
15470
  if (options.updateTransceiverDirection) transceiver.direction = "inactive";
15246
15471
  }
15247
15472
  } catch (error) {
15248
- logger$16.error("[TransceiverController] stopTrackSender error", kind, error);
15473
+ logger$17.error("[TransceiverController] stopTrackSender error", kind, error);
15249
15474
  this.options.onError?.(new MediaTrackError("stopTrackSender", kind, error));
15250
15475
  }
15251
15476
  }
15252
15477
  async restoreTrackSender(kind) {
15253
15478
  try {
15254
- logger$16.debug("[TransceiverController] restoreTrackSender called", kind);
15479
+ logger$17.debug("[TransceiverController] restoreTrackSender called", kind);
15255
15480
  const constraints = {};
15256
15481
  const transceivers = this.transceiverByKind(kind);
15257
15482
  for (const transceiver of transceivers) {
@@ -15261,23 +15486,23 @@ var TransceiverController = class extends Destroyable {
15261
15486
  if (trackKind === "audio" || trackKind === "video") constraints[trackKind] = this.getConstraintsFor(trackKind);
15262
15487
  }
15263
15488
  }
15264
- logger$16.debug("[TransceiverController] restoreTrackSender constraints:", constraints);
15489
+ logger$17.debug("[TransceiverController] restoreTrackSender constraints:", constraints);
15265
15490
  if (Object.keys(constraints).length === 0) {
15266
- logger$16.warn("[TransceiverController] restoreTrackSender: no tracks need restoration", kind);
15491
+ logger$17.warn("[TransceiverController] restoreTrackSender: no tracks need restoration", kind);
15267
15492
  return;
15268
15493
  }
15269
15494
  const newTracks = (await this.options.getUserMedia(constraints)).getTracks();
15270
- logger$16.debug("[TransceiverController] restoreTrackSender new tracks:", newTracks);
15495
+ logger$17.debug("[TransceiverController] restoreTrackSender new tracks:", newTracks);
15271
15496
  for (const newTrack of newTracks) {
15272
15497
  this.options.localStreamController.addTrack(newTrack);
15273
15498
  const trackKind = newTrack.kind;
15274
15499
  const transceiverOfKind = this.transceiverByKind(trackKind)[0];
15275
15500
  transceiverOfKind.direction = trackKind === "audio" ? this.audioDirection : this.videoDirection;
15276
- logger$16.debug("[TransceiverController] restoreTrackSender setting direction for", trackKind, transceiverOfKind.direction);
15501
+ logger$17.debug("[TransceiverController] restoreTrackSender setting direction for", trackKind, transceiverOfKind.direction);
15277
15502
  await transceiverOfKind.sender.replaceTrack(newTrack);
15278
15503
  }
15279
15504
  } catch (error) {
15280
- logger$16.error("[TransceiverController] restoreTrackSender error", kind, error);
15505
+ logger$17.error("[TransceiverController] restoreTrackSender error", kind, error);
15281
15506
  this.options.onError?.(new MediaTrackError("restoreTrackSender", kind, error));
15282
15507
  }
15283
15508
  }
@@ -15318,14 +15543,14 @@ var TransceiverController = class extends Destroyable {
15318
15543
  };
15319
15544
  try {
15320
15545
  await track.applyConstraints(constraintsToApply);
15321
- logger$16.debug(`[TransceiverController] Updated ${kind} sender constraints:`, constraintsToApply);
15322
- logger$16.debug(`[TransceiverController] Updated ${kind} sender constraints:`, track.getConstraints());
15546
+ logger$17.debug(`[TransceiverController] Updated ${kind} sender constraints:`, constraintsToApply);
15547
+ logger$17.debug(`[TransceiverController] Updated ${kind} sender constraints:`, track.getConstraints());
15323
15548
  } catch (error) {
15324
- logger$16.warn(`[TransceiverController] applyConstraints failed for ${kind} track ${track.id}, attempting track replacement fallback:`, error);
15549
+ logger$17.warn(`[TransceiverController] applyConstraints failed for ${kind} track ${track.id}, attempting track replacement fallback:`, error);
15325
15550
  try {
15326
15551
  await this.replaceTrackFallback(sender, track, kind, constraintsToApply);
15327
15552
  } catch (fallbackError) {
15328
- logger$16.warn(`[TransceiverController] Track replacement fallback also failed for ${kind} track:`, fallbackError);
15553
+ logger$17.warn(`[TransceiverController] Track replacement fallback also failed for ${kind} track:`, fallbackError);
15329
15554
  this.options.onError?.(new MediaTrackError("updateSendersConstraints", kind, fallbackError));
15330
15555
  }
15331
15556
  }
@@ -15353,7 +15578,7 @@ var TransceiverController = class extends Destroyable {
15353
15578
  if (!newTrack) throw new MediaTrackError("replaceTrackFallback", kind, /* @__PURE__ */ new Error("getUserMedia returned no track of the requested kind"));
15354
15579
  await sender.replaceTrack(newTrack);
15355
15580
  this.options.localStreamController.addTrack(newTrack);
15356
- logger$16.debug(`[TransceiverController] Track replacement fallback succeeded for ${kind}. New track: ${newTrack.id}`);
15581
+ logger$17.debug(`[TransceiverController] Track replacement fallback succeeded for ${kind}. New track: ${newTrack.id}`);
15357
15582
  }
15358
15583
  getMediaDirections() {
15359
15584
  if (this.peerConnection.connectionState === "connected") return this.peerConnection.getTransceivers().reduce((acc, transceiver) => {
@@ -15383,60 +15608,60 @@ var TransceiverController = class extends Destroyable {
15383
15608
 
15384
15609
  //#endregion
15385
15610
  //#region src/controllers/RTCPeerConnectionController.ts
15386
- var import_cjs$15 = require_cjs();
15387
- const logger$15 = getLogger();
15611
+ var import_cjs$16 = require_cjs();
15612
+ const logger$16 = getLogger();
15388
15613
  var RTCPeerConnectionController = class extends Destroyable {
15389
15614
  constructor(options = {}, remoteSessionDescription, deviceController) {
15390
15615
  super();
15391
15616
  this.options = options;
15392
15617
  this.firstSDPExchangeCompleted = false;
15393
15618
  this.negotiationNeeded$ = this.createSubject();
15394
- this.localDescription$ = (0, import_cjs$15.defer)(() => (0, import_cjs$15.from)(this.init())).pipe((0, import_cjs$15.switchMap)(() => this.iceGatheringController.iceCandidatesState$.pipe((0, import_cjs$15.filter)((iceCandidateState) => !["new", "gathering"].includes(iceCandidateState)), (0, import_cjs$15.tap)(() => {
15619
+ this.localDescription$ = (0, import_cjs$16.defer)(() => (0, import_cjs$16.from)(this.init())).pipe((0, import_cjs$16.switchMap)(() => this.iceGatheringController.iceCandidatesState$.pipe((0, import_cjs$16.filter)((iceCandidateState) => !["new", "gathering"].includes(iceCandidateState)), (0, import_cjs$16.tap)(() => {
15395
15620
  this.negotiationEnded();
15396
- }), (0, import_cjs$15.filter)(() => this.shouldEmitLocalDescription), (0, import_cjs$15.map)(() => this.peerConnection?.localDescription), filterNull(), (0, import_cjs$15.tap)((desc) => {
15621
+ }), (0, import_cjs$16.filter)(() => this.shouldEmitLocalDescription), (0, import_cjs$16.map)(() => this.peerConnection?.localDescription), filterNull(), (0, import_cjs$16.tap)((desc) => {
15397
15622
  if (desc.type === "answer") this._type = "offer";
15398
- }))), (0, import_cjs$15.shareReplay)(1), (0, import_cjs$15.takeUntil)(this.destroyed$));
15623
+ }))), (0, import_cjs$16.shareReplay)(1), (0, import_cjs$16.takeUntil)(this.destroyed$));
15399
15624
  this.connectionTimeout = 3e3;
15400
15625
  this.oniceconnectionstatechangeHandler = () => {
15401
15626
  if (this.peerConnection) {
15402
15627
  const { iceConnectionState } = this.peerConnection;
15403
- logger$15.debug(`[RTCPeerConnectionController] ICE connection state changed to: ${iceConnectionState}`);
15628
+ logger$16.debug(`[RTCPeerConnectionController] ICE connection state changed to: ${iceConnectionState}`);
15404
15629
  this._iceConnectionState$.next(this.peerConnection.iceConnectionState);
15405
15630
  }
15406
15631
  };
15407
15632
  this.onconnectionstatechangeHandler = () => {
15408
15633
  if (this.peerConnection) {
15409
15634
  const { connectionState } = this.peerConnection;
15410
- logger$15.debug(`[RTCPeerConnectionController] Connection state changed to: ${connectionState}`);
15635
+ logger$16.debug(`[RTCPeerConnectionController] Connection state changed to: ${connectionState}`);
15411
15636
  if (connectionState === "connected") this.removeConnectionTimer();
15412
15637
  this._connectionState$.next(this.peerConnection.connectionState);
15413
15638
  }
15414
15639
  };
15415
15640
  this.onsignalingstatechangeHandler = () => {
15416
- logger$15.debug(`[RTCPeerConnectionController] Signaling state changed to: ${this.peerConnection?.signalingState}`);
15641
+ logger$16.debug(`[RTCPeerConnectionController] Signaling state changed to: ${this.peerConnection?.signalingState}`);
15417
15642
  };
15418
15643
  this.onicegatheringstatechangeHandler = () => {
15419
15644
  if (this.peerConnection) this._iceGatheringState$.next(this.peerConnection.iceGatheringState);
15420
15645
  };
15421
15646
  this.onnegotiationneededHandler = (event) => {
15422
- logger$15.debug("[RTCPeerConnectionController] Negotiation needed event received.", event);
15647
+ logger$16.debug("[RTCPeerConnectionController] Negotiation needed event received.", event);
15423
15648
  this.negotiationNeeded$.next();
15424
15649
  };
15425
15650
  this.updateSelectedInputDevice = async (kind, deviceInfo) => {
15426
15651
  try {
15427
15652
  const { localStream } = this;
15428
15653
  if (!localStream) {
15429
- logger$15.warn("[RTCPeerConnectionController] No local stream available to update input device.");
15654
+ logger$16.warn("[RTCPeerConnectionController] No local stream available to update input device.");
15430
15655
  return;
15431
15656
  }
15432
- logger$15.debug(`[RTCPeerConnectionController] Updating selected ${kind} input device:`, localStream.getTracks());
15657
+ logger$16.debug(`[RTCPeerConnectionController] Updating selected ${kind} input device:`, localStream.getTracks());
15433
15658
  const track = localStream.getTracks().find((track$1) => track$1.kind === kind);
15434
15659
  if (track) {
15435
15660
  this.transceiverController?.stopTrackSender(kind);
15436
15661
  this.localStream?.removeTrack(track);
15437
- logger$15.debug(`[RTCPeerConnectionController] Stopped existing ${kind} track: ${track.id}`, localStream.getTracks());
15662
+ logger$16.debug(`[RTCPeerConnectionController] Stopped existing ${kind} track: ${track.id}`, localStream.getTracks());
15438
15663
  if (!deviceInfo) {
15439
- logger$15.debug(`[RTCPeerConnectionController] ${kind} input device selected: none`);
15664
+ logger$16.debug(`[RTCPeerConnectionController] ${kind} input device selected: none`);
15440
15665
  return;
15441
15666
  }
15442
15667
  const streamTrack = (await this.getUserMedia({ [kind]: {
@@ -15444,15 +15669,15 @@ var RTCPeerConnectionController = class extends Destroyable {
15444
15669
  ...this.deviceController.deviceInfoToConstraints(deviceInfo)
15445
15670
  } })).getTracks().find((t) => t.kind === kind);
15446
15671
  if (streamTrack) {
15447
- logger$15.debug(`[RTCPeerConnectionController] Adding new ${kind} track: ${streamTrack.id}`);
15672
+ logger$16.debug(`[RTCPeerConnectionController] Adding new ${kind} track: ${streamTrack.id}`);
15448
15673
  this.localStream?.addTrack(streamTrack);
15449
15674
  await this.transceiverController?.replaceSenderTrack(kind, streamTrack);
15450
- logger$15.debug(`[RTCPeerConnectionController] Added new ${kind} track: ${streamTrack.id}`, this.localStream?.getTracks());
15675
+ logger$16.debug(`[RTCPeerConnectionController] Added new ${kind} track: ${streamTrack.id}`, this.localStream?.getTracks());
15451
15676
  }
15452
15677
  }
15453
- logger$15.debug(`[RTCPeerConnectionController] ${kind} input device selected:`, deviceInfo?.label);
15678
+ logger$16.debug(`[RTCPeerConnectionController] ${kind} input device selected:`, deviceInfo?.label);
15454
15679
  } catch (error) {
15455
- logger$15.error(`[RTCPeerConnectionController] Failed to select ${kind} input device:`, error);
15680
+ logger$16.error(`[RTCPeerConnectionController] Failed to select ${kind} input device:`, error);
15456
15681
  this._errors$.next(toError(error));
15457
15682
  throw error;
15458
15683
  }
@@ -15469,6 +15694,7 @@ var RTCPeerConnectionController = class extends Destroyable {
15469
15694
  this._remoteDescription$ = this.createReplaySubject(1);
15470
15695
  this._remoteStream$ = this.createBehaviorSubject(null);
15471
15696
  this._remoteOfferMediaDirections = null;
15697
+ this._localAudioPipeline = null;
15472
15698
  this.deviceController = deviceController ?? {};
15473
15699
  this.id = options.callId ?? v4_default();
15474
15700
  this._type = remoteSessionDescription ? "answer" : "offer";
@@ -15538,43 +15764,43 @@ var RTCPeerConnectionController = class extends Destroyable {
15538
15764
  };
15539
15765
  }
15540
15766
  get iceGatheringState$() {
15541
- return this.cachedObservable("iceGatheringState$", () => this._iceGatheringState$.asObservable().pipe((0, import_cjs$15.takeUntil)(this.destroyed$)));
15767
+ return this.cachedObservable("iceGatheringState$", () => this._iceGatheringState$.asObservable().pipe((0, import_cjs$16.takeUntil)(this.destroyed$)));
15542
15768
  }
15543
15769
  get mediaTrackEnded$() {
15544
- return this.cachedObservable("mediaTrackEnded$", () => this.localStreamController.mediaTrackEnded$.pipe((0, import_cjs$15.takeUntil)(this.destroyed$)));
15770
+ return this.cachedObservable("mediaTrackEnded$", () => this.localStreamController.mediaTrackEnded$.pipe((0, import_cjs$16.takeUntil)(this.destroyed$)));
15545
15771
  }
15546
15772
  get errors$() {
15547
- return this.cachedObservable("errors$", () => this._errors$.asObservable().pipe((0, import_cjs$15.takeUntil)(this.destroyed$)));
15773
+ return this.cachedObservable("errors$", () => this._errors$.asObservable().pipe((0, import_cjs$16.takeUntil)(this.destroyed$)));
15548
15774
  }
15549
15775
  get iceCandidates$() {
15550
- return this.cachedObservable("iceCandidates$", () => this._iceCandidates$.asObservable().pipe((0, import_cjs$15.takeUntil)(this.destroyed$)));
15776
+ return this.cachedObservable("iceCandidates$", () => this._iceCandidates$.asObservable().pipe((0, import_cjs$16.takeUntil)(this.destroyed$)));
15551
15777
  }
15552
15778
  get initialized$() {
15553
- return this.cachedObservable("initialized$", () => this._initialized$.asObservable().pipe((0, import_cjs$15.filter)((initialized) => initialized), (0, import_cjs$15.takeUntil)(this.destroyed$)));
15779
+ return this.cachedObservable("initialized$", () => this._initialized$.asObservable().pipe((0, import_cjs$16.filter)((initialized) => initialized), (0, import_cjs$16.takeUntil)(this.destroyed$)));
15554
15780
  }
15555
15781
  get remoteDescription$() {
15556
- return this.cachedObservable("remoteDescription$", () => this._remoteDescription$.asObservable().pipe((0, import_cjs$15.takeUntil)(this.destroyed$)));
15782
+ return this.cachedObservable("remoteDescription$", () => this._remoteDescription$.asObservable().pipe((0, import_cjs$16.takeUntil)(this.destroyed$)));
15557
15783
  }
15558
15784
  get localStream$() {
15559
- return this.cachedObservable("localStream$", () => this.localStreamController.localStream$.pipe((0, import_cjs$15.takeUntil)(this.destroyed$)));
15785
+ return this.cachedObservable("localStream$", () => this.localStreamController.localStream$.pipe((0, import_cjs$16.takeUntil)(this.destroyed$)));
15560
15786
  }
15561
15787
  get remoteStream$() {
15562
- return this.cachedObservable("remoteStream$", () => this._remoteStream$.asObservable().pipe((0, import_cjs$15.takeUntil)(this.destroyed$)));
15788
+ return this.cachedObservable("remoteStream$", () => this._remoteStream$.asObservable().pipe((0, import_cjs$16.takeUntil)(this.destroyed$)));
15563
15789
  }
15564
15790
  get localAudioTracks$() {
15565
- return this.cachedObservable("localAudioTracks$", () => this.localStreamController.localAudioTracks$.pipe((0, import_cjs$15.takeUntil)(this.destroyed$)));
15791
+ return this.cachedObservable("localAudioTracks$", () => this.localStreamController.localAudioTracks$.pipe((0, import_cjs$16.takeUntil)(this.destroyed$)));
15566
15792
  }
15567
15793
  get localVideoTracks$() {
15568
- return this.cachedObservable("localVideoTracks$", () => this.localStreamController.localVideoTracks$.pipe((0, import_cjs$15.takeUntil)(this.destroyed$)));
15794
+ return this.cachedObservable("localVideoTracks$", () => this.localStreamController.localVideoTracks$.pipe((0, import_cjs$16.takeUntil)(this.destroyed$)));
15569
15795
  }
15570
15796
  get iceConnectionState$() {
15571
- return this.cachedObservable("iceConnectionState$", () => this._iceConnectionState$.asObservable().pipe((0, import_cjs$15.takeUntil)(this.destroyed$)));
15797
+ return this.cachedObservable("iceConnectionState$", () => this._iceConnectionState$.asObservable().pipe((0, import_cjs$16.takeUntil)(this.destroyed$)));
15572
15798
  }
15573
15799
  get connectionState$() {
15574
- return this.cachedObservable("connectionState$", () => this._connectionState$.asObservable().pipe((0, import_cjs$15.takeUntil)(this.destroyed$)));
15800
+ return this.cachedObservable("connectionState$", () => this._connectionState$.asObservable().pipe((0, import_cjs$16.takeUntil)(this.destroyed$)));
15575
15801
  }
15576
15802
  get signalingState$() {
15577
- return this.cachedObservable("signalingState$", () => this._signalingState$.asObservable().pipe((0, import_cjs$15.takeUntil)(this.destroyed$)));
15803
+ return this.cachedObservable("signalingState$", () => this._signalingState$.asObservable().pipe((0, import_cjs$16.takeUntil)(this.destroyed$)));
15578
15804
  }
15579
15805
  get type() {
15580
15806
  return this._type;
@@ -15687,17 +15913,17 @@ var RTCPeerConnectionController = class extends Destroyable {
15687
15913
  async doInit() {
15688
15914
  try {
15689
15915
  this.setupPeerConnection();
15690
- this.subscribeTo(this.negotiationNeeded$.pipe((0, import_cjs$15.auditTime)(0), (0, import_cjs$15.exhaustMap)(async () => this.startNegotiation())), {
15916
+ this.subscribeTo(this.negotiationNeeded$.pipe((0, import_cjs$16.auditTime)(0), (0, import_cjs$16.exhaustMap)(async () => this.startNegotiation())), {
15691
15917
  next: () => {
15692
- logger$15.debug("[RTCPeerConnectionController] Start Negotiation completed successfully");
15918
+ logger$16.debug("[RTCPeerConnectionController] Start Negotiation completed successfully");
15693
15919
  },
15694
15920
  error: (error) => {
15695
- logger$15.error("[RTCPeerConnectionController] Start Negotiation error:", error);
15921
+ logger$16.error("[RTCPeerConnectionController] Start Negotiation error:", error);
15696
15922
  this._errors$.next(toError(error));
15697
15923
  }
15698
15924
  });
15699
- this.subscribeTo((0, import_cjs$15.merge)(this.deviceController.selectedAudioInputDevice$.pipe((0, import_cjs$15.map)((deviceInfo) => ["audio", deviceInfo])), this.deviceController.selectedVideoInputDevice$.pipe((0, import_cjs$15.map)((deviceInfo) => ["video", deviceInfo]))).pipe((0, import_cjs$15.skipWhile)(() => !this.localStreamController.localStream)), async ([kind, deviceInfo]) => {
15700
- logger$15.debug(`[RTCPeerConnectionController] Selected input device changed for:`, {
15925
+ this.subscribeTo((0, import_cjs$16.merge)(this.deviceController.selectedAudioInputDevice$.pipe((0, import_cjs$16.map)((deviceInfo) => ["audio", deviceInfo])), this.deviceController.selectedVideoInputDevice$.pipe((0, import_cjs$16.map)((deviceInfo) => ["video", deviceInfo]))).pipe((0, import_cjs$16.skipWhile)(() => !this.localStreamController.localStream)), async ([kind, deviceInfo]) => {
15926
+ logger$16.debug(`[RTCPeerConnectionController] Selected input device changed for:`, {
15701
15927
  kind,
15702
15928
  deviceInfo
15703
15929
  });
@@ -15714,7 +15940,7 @@ var RTCPeerConnectionController = class extends Destroyable {
15714
15940
  this._initialized$.next(true);
15715
15941
  }
15716
15942
  } catch (error) {
15717
- logger$15.error("[RTCPeerConnectionController] Initialization error:", error);
15943
+ logger$16.error("[RTCPeerConnectionController] Initialization error:", error);
15718
15944
  this._errors$.next(toError(error));
15719
15945
  this.destroy();
15720
15946
  }
@@ -15746,22 +15972,22 @@ var RTCPeerConnectionController = class extends Destroyable {
15746
15972
  }
15747
15973
  async startNegotiation() {
15748
15974
  if (this.isNegotiating) {
15749
- logger$15.debug("[RTCPeerConnectionController] Negotiation already in progress, skipping.");
15975
+ logger$16.debug("[RTCPeerConnectionController] Negotiation already in progress, skipping.");
15750
15976
  return;
15751
15977
  }
15752
15978
  this.setupEventListeners();
15753
15979
  if (this.type === "answer") {
15754
- logger$15.debug("[RTCPeerConnectionController] This is an answer type still, skipping offer creation.");
15980
+ logger$16.debug("[RTCPeerConnectionController] This is an answer type still, skipping offer creation.");
15755
15981
  return;
15756
15982
  }
15757
15983
  this._isNegotiating$.next(true);
15758
- logger$15.debug("[RTCPeerConnectionController] Starting negotiation.");
15984
+ logger$16.debug("[RTCPeerConnectionController] Starting negotiation.");
15759
15985
  try {
15760
15986
  const { offerOptions } = this;
15761
- logger$15.debug("[RTCPeerConnectionController] Creating offer with options:", offerOptions);
15987
+ logger$16.debug("[RTCPeerConnectionController] Creating offer with options:", offerOptions);
15762
15988
  await this.createOffer(offerOptions);
15763
15989
  } catch (error) {
15764
- logger$15.error("[RTCPeerConnectionController] Error during negotiation:", error);
15990
+ logger$16.error("[RTCPeerConnectionController] Error during negotiation:", error);
15765
15991
  this._errors$.next(toError(error));
15766
15992
  }
15767
15993
  }
@@ -15777,14 +16003,14 @@ var RTCPeerConnectionController = class extends Destroyable {
15777
16003
  let readyToConnect = status !== "failed";
15778
16004
  try {
15779
16005
  if (status === "received" && sdp) {
15780
- logger$15.debug("[RTCPeerConnectionController] Received answer SDP:", sdp);
16006
+ logger$16.debug("[RTCPeerConnectionController] Received answer SDP:", sdp);
15781
16007
  await this._setRemoteDescription({
15782
16008
  type: "answer",
15783
16009
  sdp
15784
16010
  });
15785
16011
  }
15786
16012
  } catch (error) {
15787
- logger$15.error("[RTCPeerConnectionController] Error updating answer status:", error);
16013
+ logger$16.error("[RTCPeerConnectionController] Error updating answer status:", error);
15788
16014
  this._errors$.next(toError(error));
15789
16015
  readyToConnect = false;
15790
16016
  } finally {
@@ -15803,7 +16029,7 @@ var RTCPeerConnectionController = class extends Destroyable {
15803
16029
  await this.handleOfferReceived();
15804
16030
  break;
15805
16031
  case "failed":
15806
- logger$15.error("[RTCPeerConnectionController] Offer failed to be processed by remote.");
16032
+ logger$16.error("[RTCPeerConnectionController] Offer failed to be processed by remote.");
15807
16033
  break;
15808
16034
  case "sent":
15809
16035
  default:
@@ -15835,7 +16061,7 @@ var RTCPeerConnectionController = class extends Destroyable {
15835
16061
  }
15836
16062
  await this.setupLocalTracks();
15837
16063
  const { answerOptions } = this;
15838
- logger$15.debug("[RTCPeerConnectionController] Creating inbound answer with options:", answerOptions);
16064
+ logger$16.debug("[RTCPeerConnectionController] Creating inbound answer with options:", answerOptions);
15839
16065
  await this.createAnswer(answerOptions);
15840
16066
  }
15841
16067
  async handleOfferReceived() {
@@ -15843,7 +16069,7 @@ var RTCPeerConnectionController = class extends Destroyable {
15843
16069
  this._isNegotiating$.next(true);
15844
16070
  await this._setRemoteDescription(this.sdpInit);
15845
16071
  const { answerOptions } = this;
15846
- logger$15.debug("[RTCPeerConnectionController] Creating answer with options:", answerOptions);
16072
+ logger$16.debug("[RTCPeerConnectionController] Creating answer with options:", answerOptions);
15847
16073
  await this.createAnswer(answerOptions);
15848
16074
  }
15849
16075
  readyToConnect() {
@@ -15851,7 +16077,7 @@ var RTCPeerConnectionController = class extends Destroyable {
15851
16077
  this.connectionTimer = setTimeout(() => {
15852
16078
  this.removeConnectionTimer();
15853
16079
  if (this.peerConnection?.connectionState !== "connected") {
15854
- logger$15.debug("[RTCPeerConnectionController] Connection timeout, restarting ICE gathering with relay only.");
16080
+ logger$16.debug("[RTCPeerConnectionController] Connection timeout, restarting ICE gathering with relay only.");
15855
16081
  this.iceGatheringController.restartICEGatheringWithRelayOnly();
15856
16082
  }
15857
16083
  }, this.connectionTimeout);
@@ -15873,14 +16099,14 @@ var RTCPeerConnectionController = class extends Destroyable {
15873
16099
  const stereo = this.options.stereo ?? PreferencesContainer.instance.stereoAudio;
15874
16100
  if (preferredAudioCodecs.length > 0 || preferredVideoCodecs.length > 0) {
15875
16101
  result = setCodecPreferences(result, preferredAudioCodecs, preferredVideoCodecs);
15876
- logger$15.debug("[RTCPeerConnectionController] Applied codec preferences to SDP", {
16102
+ logger$16.debug("[RTCPeerConnectionController] Applied codec preferences to SDP", {
15877
16103
  preferredAudioCodecs,
15878
16104
  preferredVideoCodecs
15879
16105
  });
15880
16106
  }
15881
16107
  if (stereo) {
15882
16108
  result = enableStereoOpus(result);
15883
- logger$15.debug("[RTCPeerConnectionController] Applied stereo Opus to SDP");
16109
+ logger$16.debug("[RTCPeerConnectionController] Applied stereo Opus to SDP");
15884
16110
  }
15885
16111
  return Promise.resolve(result);
15886
16112
  }
@@ -15936,25 +16162,25 @@ var RTCPeerConnectionController = class extends Destroyable {
15936
16162
  ...this.peerConnection.getConfiguration(),
15937
16163
  iceTransportPolicy: "relay"
15938
16164
  });
15939
- logger$15.debug("[RTCPeerConnectionController] ICE transport policy set to relay-only");
16165
+ logger$16.debug("[RTCPeerConnectionController] ICE transport policy set to relay-only");
15940
16166
  } catch (error) {
15941
- logger$15.warn("[RTCPeerConnectionController] Failed to set relay-only policy:", error);
16167
+ logger$16.warn("[RTCPeerConnectionController] Failed to set relay-only policy:", error);
15942
16168
  }
15943
16169
  this.setupEventListeners();
15944
16170
  this._isNegotiating$.next(true);
15945
- logger$15.debug(`[RTCPeerConnectionController] Triggering ICE restart${relayOnly ? " (relay-only)" : ""}.`);
16171
+ logger$16.debug(`[RTCPeerConnectionController] Triggering ICE restart${relayOnly ? " (relay-only)" : ""}.`);
15946
16172
  try {
15947
16173
  const offer = await this.peerConnection.createOffer({ iceRestart: true });
15948
16174
  await this.setLocalDescription(offer);
15949
16175
  } catch (error) {
15950
- logger$15.error("[RTCPeerConnectionController] ICE restart offer failed:", error);
16176
+ logger$16.error("[RTCPeerConnectionController] ICE restart offer failed:", error);
15951
16177
  this._errors$.next(toError(error));
15952
16178
  this.negotiationEnded();
15953
16179
  if (policyChanged) this.restoreIceTransportPolicy();
15954
16180
  throw error;
15955
16181
  }
15956
- if (policyChanged) (0, import_cjs$15.firstValueFrom)((0, import_cjs$15.race)(this._iceGatheringState$.pipe((0, import_cjs$15.filter)((state) => state === "complete"), (0, import_cjs$15.take)(1)), (0, import_cjs$15.timer)(ICE_GATHERING_COMPLETE_TIMEOUT_MS).pipe((0, import_cjs$15.map)(() => "timeout")))).then(() => this.restoreIceTransportPolicy()).catch((error) => {
15957
- logger$15.warn("[RTCPeerConnectionController] Error waiting for ICE gathering to complete:", error);
16182
+ if (policyChanged) (0, import_cjs$16.firstValueFrom)((0, import_cjs$16.race)(this._iceGatheringState$.pipe((0, import_cjs$16.filter)((state) => state === "complete"), (0, import_cjs$16.take)(1)), (0, import_cjs$16.timer)(ICE_GATHERING_COMPLETE_TIMEOUT_MS).pipe((0, import_cjs$16.map)(() => "timeout")))).then(() => this.restoreIceTransportPolicy()).catch((error) => {
16183
+ logger$16.warn("[RTCPeerConnectionController] Error waiting for ICE gathering to complete:", error);
15958
16184
  this.restoreIceTransportPolicy();
15959
16185
  });
15960
16186
  }
@@ -15964,9 +16190,9 @@ var RTCPeerConnectionController = class extends Destroyable {
15964
16190
  ...this.peerConnection.getConfiguration(),
15965
16191
  iceTransportPolicy: this.options.relayOnly ? "relay" : "all"
15966
16192
  });
15967
- logger$15.debug("[RTCPeerConnectionController] ICE transport policy restored");
16193
+ logger$16.debug("[RTCPeerConnectionController] ICE transport policy restored");
15968
16194
  } catch (error) {
15969
- logger$15.warn("[RTCPeerConnectionController] Failed to restore ICE transport policy:", error);
16195
+ logger$16.warn("[RTCPeerConnectionController] Failed to restore ICE transport policy:", error);
15970
16196
  }
15971
16197
  }
15972
16198
  /**
@@ -15978,13 +16204,13 @@ var RTCPeerConnectionController = class extends Destroyable {
15978
16204
  await this.setupRemoteTracks();
15979
16205
  }
15980
16206
  async setupLocalTracks() {
15981
- logger$15.debug("[RTCPeerConnectionController] Setting up local tracks/transceivers.");
16207
+ logger$16.debug("[RTCPeerConnectionController] Setting up local tracks/transceivers.");
15982
16208
  const localStream = this.localStream ?? await this.localStreamController.buildLocalStream();
15983
16209
  if (this.transceiverController?.useAddStream ?? false) {
15984
- logger$15.warn("[RTCPeerConnectionController] Using deprecated addStream API to add local stream.");
16210
+ logger$16.warn("[RTCPeerConnectionController] Using deprecated addStream API to add local stream.");
15985
16211
  this.peerConnection?.addStream(localStream);
15986
16212
  if (!this.isNegotiating) {
15987
- logger$15.debug("[RTCPeerConnectionController] Forcing negotiationneeded after local tracks setup.");
16213
+ logger$16.debug("[RTCPeerConnectionController] Forcing negotiationneeded after local tracks setup.");
15988
16214
  this.negotiationNeeded$.next();
15989
16215
  }
15990
16216
  return;
@@ -16000,7 +16226,7 @@ var RTCPeerConnectionController = class extends Destroyable {
16000
16226
  const transceivers = (kind === "audio" ? this.transceiverController?.audioTransceivers : this.transceiverController?.videoTransceivers) ?? [];
16001
16227
  await this.transceiverController?.setupTransceiverSender(track, localStream, transceivers[index]);
16002
16228
  } else {
16003
- logger$15.debug(`[RTCPeerConnectionController] Using addTrack for local ${kind} track:`, track.id);
16229
+ logger$16.debug(`[RTCPeerConnectionController] Using addTrack for local ${kind} track:`, track.id);
16004
16230
  this.peerConnection?.addTrack(track, localStream);
16005
16231
  }
16006
16232
  }
@@ -16017,7 +16243,7 @@ var RTCPeerConnectionController = class extends Destroyable {
16017
16243
  async setupRemoteTracks() {
16018
16244
  if (!this.peerConnection) throw new DependencyError("RTCPeerConnection is not initialized");
16019
16245
  this.peerConnection.ontrack = (event) => {
16020
- logger$15.debug("[RTCPeerConnectionController] Remote track received:", event.track.kind);
16246
+ logger$16.debug("[RTCPeerConnectionController] Remote track received:", event.track.kind);
16021
16247
  if (event.streams[0]) this._remoteStream$.next(event.streams[0]);
16022
16248
  else {
16023
16249
  const existingTracks = this._remoteStream$.value?.getTracks() ?? [];
@@ -16029,6 +16255,46 @@ var RTCPeerConnectionController = class extends Destroyable {
16029
16255
  }
16030
16256
  async restoreTrackSender(kind) {
16031
16257
  await this.transceiverController?.restoreTrackSender(kind);
16258
+ if (kind !== "video" && this._localAudioPipeline) await this.applyLocalAudioPipelineToSender();
16259
+ }
16260
+ /**
16261
+ * Return the lazily-created {@link LocalAudioPipeline}, constructing it on
16262
+ * first access. On creation the current audio sender's track is routed
16263
+ * through the pipeline (input → gain → analyser → destination) and the
16264
+ * sender is switched to emit the processed track. Returns `null` when no
16265
+ * audio sender exists yet (pre-negotiation).
16266
+ */
16267
+ ensureLocalAudioPipeline() {
16268
+ if (this._localAudioPipeline) return this._localAudioPipeline;
16269
+ if (!this.peerConnection) return null;
16270
+ try {
16271
+ this._localAudioPipeline = new LocalAudioPipeline();
16272
+ } catch (error) {
16273
+ logger$16.warn("[RTCPeerConnectionController] Failed to create LocalAudioPipeline:", error);
16274
+ return null;
16275
+ }
16276
+ this.subscribeTo(this.localStreamController.localAudioTracks$, () => {
16277
+ this.applyLocalAudioPipelineToSender();
16278
+ });
16279
+ this.applyLocalAudioPipelineToSender();
16280
+ return this._localAudioPipeline;
16281
+ }
16282
+ /** The active LocalAudioPipeline, or null if it hasn't been created yet. */
16283
+ get localAudioPipeline() {
16284
+ return this._localAudioPipeline;
16285
+ }
16286
+ async applyLocalAudioPipelineToSender() {
16287
+ if (!this._localAudioPipeline || !this.peerConnection) return;
16288
+ const [raw] = this.localStreamController.localAudioTracks;
16289
+ this._localAudioPipeline.setInputTrack(raw ?? null);
16290
+ const [audioTransceiver] = this.transceiverController?.audioTransceivers ?? [];
16291
+ const sender = audioTransceiver?.sender ?? this.peerConnection.getSenders().find((s) => s.track?.kind === "audio");
16292
+ if (!sender || !raw) return;
16293
+ try {
16294
+ await sender.replaceTrack(this._localAudioPipeline.outputTrack);
16295
+ } catch (error) {
16296
+ logger$16.warn("[RTCPeerConnectionController] Failed to route audio sender through pipeline:", error);
16297
+ }
16032
16298
  }
16033
16299
  /**
16034
16300
  * Add a local media track to the peer connection.
@@ -16043,9 +16309,9 @@ var RTCPeerConnectionController = class extends Destroyable {
16043
16309
  try {
16044
16310
  const localStream = this.localStreamController.addTrack(track);
16045
16311
  this.peerConnection.addTrack(track, localStream);
16046
- logger$15.debug(`[RTCPeerConnectionController] ${track.kind} track added:`, track.id);
16312
+ logger$16.debug(`[RTCPeerConnectionController] ${track.kind} track added:`, track.id);
16047
16313
  } catch (error) {
16048
- logger$15.error(`[RTCPeerConnectionController] Failed to add ${track.kind} track:`, error);
16314
+ logger$16.error(`[RTCPeerConnectionController] Failed to add ${track.kind} track:`, error);
16049
16315
  this._errors$.next(toError(error));
16050
16316
  throw error;
16051
16317
  }
@@ -16062,15 +16328,15 @@ var RTCPeerConnectionController = class extends Destroyable {
16062
16328
  }
16063
16329
  const sender = this.peerConnection.getSenders().find((sender$1) => sender$1.track?.id === trackId);
16064
16330
  if (!sender) {
16065
- logger$15.debug(`[RTCPeerConnectionController] track not found: ${trackId}`);
16331
+ logger$16.debug(`[RTCPeerConnectionController] track not found: ${trackId}`);
16066
16332
  return;
16067
16333
  }
16068
16334
  try {
16069
16335
  this.peerConnection.removeTrack(sender);
16070
16336
  this.localStreamController.removeTrack(trackId);
16071
- logger$15.debug(`[RTCPeerConnectionController] ${sender.track?.kind} track removed:`, trackId);
16337
+ logger$16.debug(`[RTCPeerConnectionController] ${sender.track?.kind} track removed:`, trackId);
16072
16338
  } catch (error) {
16073
- logger$15.error(`[RTCPeerConnectionController] Failed to remove ${sender.track?.kind} track:`, error);
16339
+ logger$16.error(`[RTCPeerConnectionController] Failed to remove ${sender.track?.kind} track:`, error);
16074
16340
  this._errors$.next(toError(error));
16075
16341
  throw error;
16076
16342
  }
@@ -16097,7 +16363,7 @@ var RTCPeerConnectionController = class extends Destroyable {
16097
16363
  async replaceAudioTrackWithConstraints(constraints) {
16098
16364
  const senders = this.peerConnection?.getSenders().filter((s) => s.track?.kind === "audio" && s.track.readyState === "live");
16099
16365
  if (!senders || senders.length === 0) {
16100
- logger$15.warn("[RTCPeerConnectionController] No live audio sender to replace");
16366
+ logger$16.warn("[RTCPeerConnectionController] No live audio sender to replace");
16101
16367
  return;
16102
16368
  }
16103
16369
  for (const sender of senders) {
@@ -16115,7 +16381,7 @@ var RTCPeerConnectionController = class extends Destroyable {
16115
16381
  const newTrack = (await this.getUserMedia({ audio: mergedConstraints })).getAudioTracks()[0];
16116
16382
  await sender.replaceTrack(newTrack);
16117
16383
  this.localStreamController.addTrack(newTrack);
16118
- logger$15.debug(`[RTCPeerConnectionController] Audio track replaced for server-pushed params. New track: ${newTrack.id}`);
16384
+ logger$16.debug(`[RTCPeerConnectionController] Audio track replaced for server-pushed params. New track: ${newTrack.id}`);
16119
16385
  }
16120
16386
  }
16121
16387
  /**
@@ -16123,9 +16389,11 @@ var RTCPeerConnectionController = class extends Destroyable {
16123
16389
  * Completes all observables to prevent memory leaks.
16124
16390
  */
16125
16391
  destroy() {
16126
- logger$15.debug(`[RTCPeerConnectionController] Destroying RTCPeerConnectionController. ${this.propose}`);
16392
+ logger$16.debug(`[RTCPeerConnectionController] Destroying RTCPeerConnectionController. ${this.propose}`);
16127
16393
  this.removeConnectionTimer();
16128
16394
  this._iceGatheringController?.destroy();
16395
+ this._localAudioPipeline?.destroy();
16396
+ this._localAudioPipeline = null;
16129
16397
  this.localStreamController.destroy();
16130
16398
  this.transceiverController?.destroy();
16131
16399
  if (this.peerConnection) {
@@ -16147,7 +16415,7 @@ var RTCPeerConnectionController = class extends Destroyable {
16147
16415
  }
16148
16416
  stopRemoteTracks() {
16149
16417
  this._remoteStream$.value?.getTracks().forEach((track) => {
16150
- logger$15.debug(`[RTCPeerConnectionController] Stopping remote track: ${track.kind}`);
16418
+ logger$16.debug(`[RTCPeerConnectionController] Stopping remote track: ${track.kind}`);
16151
16419
  track.stop();
16152
16420
  });
16153
16421
  }
@@ -16164,7 +16432,7 @@ var RTCPeerConnectionController = class extends Destroyable {
16164
16432
  ...params,
16165
16433
  sdp: finalRemote
16166
16434
  };
16167
- logger$15.debug("[RTCPeerConnectionController] Setting remote description:", answer);
16435
+ logger$16.debug("[RTCPeerConnectionController] Setting remote description:", answer);
16168
16436
  return this.peerConnection.setRemoteDescription(answer);
16169
16437
  }
16170
16438
  };
@@ -16202,8 +16470,8 @@ function isVertoPingInnerParams(value) {
16202
16470
 
16203
16471
  //#endregion
16204
16472
  //#region src/managers/VertoManager.ts
16205
- var import_cjs$14 = require_cjs();
16206
- const logger$14 = getLogger();
16473
+ var import_cjs$15 = require_cjs();
16474
+ const logger$15 = getLogger();
16207
16475
  var VertoManager = class extends Destroyable {
16208
16476
  constructor(callSession) {
16209
16477
  super();
@@ -16242,7 +16510,7 @@ var WebRTCVertoManager = class extends VertoManager {
16242
16510
  try {
16243
16511
  await this.executeVerto(vertoModifyMessage);
16244
16512
  } catch (error) {
16245
- logger$14.warn("[WebRTCManager] Call might already be disconnected, error sending Verto hold:", error);
16513
+ logger$15.warn("[WebRTCManager] Call might already be disconnected, error sending Verto hold:", error);
16246
16514
  throw error;
16247
16515
  }
16248
16516
  }
@@ -16255,7 +16523,7 @@ var WebRTCVertoManager = class extends VertoManager {
16255
16523
  try {
16256
16524
  await this.executeVerto(vertoModifyMessage);
16257
16525
  } catch (error) {
16258
- logger$14.warn("[WebRTCManager] Call might already be disconnected, error sending Verto unhold:", error);
16526
+ logger$15.warn("[WebRTCManager] Call might already be disconnected, error sending Verto unhold:", error);
16259
16527
  throw error;
16260
16528
  }
16261
16529
  }
@@ -16295,7 +16563,7 @@ var WebRTCVertoManager = class extends VertoManager {
16295
16563
  return rtcPeerConnection;
16296
16564
  }
16297
16565
  get signalingStatus$() {
16298
- return this.cachedObservable("signalingStatus$", () => (0, import_cjs$14.merge)(this._signalingStatus$.asObservable(), this.mainPeerConnection.connectionState$.pipe((0, import_cjs$14.filter)((connectionState) => [
16566
+ return this.cachedObservable("signalingStatus$", () => (0, import_cjs$15.merge)(this._signalingStatus$.asObservable(), this.mainPeerConnection.connectionState$.pipe((0, import_cjs$15.filter)((connectionState) => [
16299
16567
  "connected",
16300
16568
  "disconnected",
16301
16569
  "failed"
@@ -16308,7 +16576,7 @@ var WebRTCVertoManager = class extends VertoManager {
16308
16576
  if (event.member_id) this.setSelfIdIfNull(event.member_id);
16309
16577
  });
16310
16578
  this.subscribeTo(this.vertoMedia$, (event) => {
16311
- logger$14.debug("[WebRTCManager] Received Verto media event (early media SDP):", event);
16579
+ logger$15.debug("[WebRTCManager] Received Verto media event (early media SDP):", event);
16312
16580
  this._signalingStatus$.next("ringing");
16313
16581
  const { sdp, callID } = event;
16314
16582
  this._rtcPeerConnectionsMap.get(callID)?.updateAnswerStatus({
@@ -16317,7 +16585,7 @@ var WebRTCVertoManager = class extends VertoManager {
16317
16585
  });
16318
16586
  });
16319
16587
  this.subscribeTo(this.vertoAnswer$, (event) => {
16320
- logger$14.debug("[WebRTCManager] Received Verto answer event:", event);
16588
+ logger$15.debug("[WebRTCManager] Received Verto answer event:", event);
16321
16589
  this._signalingStatus$.next("connecting");
16322
16590
  const { sdp, callID } = event;
16323
16591
  this._rtcPeerConnectionsMap.get(callID)?.updateAnswerStatus({
@@ -16326,7 +16594,7 @@ var WebRTCVertoManager = class extends VertoManager {
16326
16594
  });
16327
16595
  });
16328
16596
  this.subscribeTo(this.vertoMediaParams$, (event) => {
16329
- logger$14.debug("[WebRTCManager] Received Verto mediaParams event:", event);
16597
+ logger$15.debug("[WebRTCManager] Received Verto mediaParams event:", event);
16330
16598
  const { mediaParams, callID } = event;
16331
16599
  const rtcPeerConnController = this._rtcPeerConnectionsMap.get(callID);
16332
16600
  const { audio, video } = mediaParams;
@@ -16340,7 +16608,7 @@ var WebRTCVertoManager = class extends VertoManager {
16340
16608
  timestamp: Date.now()
16341
16609
  });
16342
16610
  } catch (error) {
16343
- logger$14.warn("[WebRTCManager] Error applying server-pushed media params:", error);
16611
+ logger$15.warn("[WebRTCManager] Error applying server-pushed media params:", error);
16344
16612
  this.onError?.(error instanceof Error ? error : new Error(String(error), { cause: error }));
16345
16613
  }
16346
16614
  })();
@@ -16362,13 +16630,13 @@ var WebRTCVertoManager = class extends VertoManager {
16362
16630
  */
16363
16631
  setNodeIdIfNull(nodeId) {
16364
16632
  if (!this._nodeId$.value && nodeId) {
16365
- logger$14.debug(`[WebRTCManager] Early node_id set: ${nodeId}`);
16633
+ logger$15.debug(`[WebRTCManager] Early node_id set: ${nodeId}`);
16366
16634
  this._nodeId$.next(nodeId);
16367
16635
  }
16368
16636
  }
16369
16637
  setSelfIdIfNull(selfId) {
16370
16638
  if (!this._selfId$.value && selfId) {
16371
- logger$14.debug(`[WebRTCManager] Early selfId set: ${selfId}`);
16639
+ logger$15.debug(`[WebRTCManager] Early selfId set: ${selfId}`);
16372
16640
  this._selfId$.next(selfId);
16373
16641
  }
16374
16642
  }
@@ -16377,7 +16645,7 @@ var WebRTCVertoManager = class extends VertoManager {
16377
16645
  const vertoPongMessage = VertoPong({ ...vertoPing });
16378
16646
  await this.executeVerto(vertoPongMessage);
16379
16647
  } catch (error) {
16380
- logger$14.warn("[WebRTCManager] Call might disconnect, error sending Verto pong:", error);
16648
+ logger$15.warn("[WebRTCManager] Call might disconnect, error sending Verto pong:", error);
16381
16649
  this.onError?.(new VertoPongError(error));
16382
16650
  }
16383
16651
  }
@@ -16387,7 +16655,7 @@ var WebRTCVertoManager = class extends VertoManager {
16387
16655
  if (audio) await this.mainPeerConnection.updateSendersConstraints("audio", audio);
16388
16656
  if (video) await this.mainPeerConnection.updateSendersConstraints("video", video);
16389
16657
  } catch (error) {
16390
- logger$14.warn("[WebRTCManager] Error updating media constraints:", error);
16658
+ logger$15.warn("[WebRTCManager] Error updating media constraints:", error);
16391
16659
  this.onError?.(error instanceof Error ? error : new Error(String(error), { cause: error }));
16392
16660
  throw error;
16393
16661
  }
@@ -16417,20 +16685,20 @@ var WebRTCVertoManager = class extends VertoManager {
16417
16685
  try {
16418
16686
  const pc = this.mainPeerConnection.peerConnection;
16419
16687
  if (!pc) {
16420
- logger$14.warn("[WebRTCManager] No peer connection for keyframe request");
16688
+ logger$15.warn("[WebRTCManager] No peer connection for keyframe request");
16421
16689
  return;
16422
16690
  }
16423
16691
  const videoReceiver = pc.getReceivers().find((r) => r.track.kind === "video");
16424
16692
  if (!videoReceiver) {
16425
- logger$14.warn("[WebRTCManager] No video receiver for keyframe request");
16693
+ logger$15.warn("[WebRTCManager] No video receiver for keyframe request");
16426
16694
  return;
16427
16695
  }
16428
16696
  if (typeof videoReceiver.requestKeyFrame === "function") {
16429
16697
  videoReceiver.requestKeyFrame();
16430
- logger$14.debug("[WebRTCManager] Keyframe requested via RTCRtpReceiver.requestKeyFrame()");
16431
- } else logger$14.debug("[WebRTCManager] requestKeyFrame() not supported, skipping");
16698
+ logger$15.debug("[WebRTCManager] Keyframe requested via RTCRtpReceiver.requestKeyFrame()");
16699
+ } else logger$15.debug("[WebRTCManager] requestKeyFrame() not supported, skipping");
16432
16700
  } catch (error) {
16433
- logger$14.warn("[WebRTCManager] Keyframe request failed (non-fatal):", error);
16701
+ logger$15.warn("[WebRTCManager] Keyframe request failed (non-fatal):", error);
16434
16702
  }
16435
16703
  }
16436
16704
  /**
@@ -16448,13 +16716,13 @@ var WebRTCVertoManager = class extends VertoManager {
16448
16716
  try {
16449
16717
  const controller = this.mainPeerConnection;
16450
16718
  if (!controller.peerConnection) {
16451
- logger$14.warn("[WebRTCManager] No peer connection for ICE restart");
16719
+ logger$15.warn("[WebRTCManager] No peer connection for ICE restart");
16452
16720
  return;
16453
16721
  }
16454
16722
  await controller.triggerIceRestart(relayOnly);
16455
- logger$14.info(`[WebRTCManager] ICE restart initiated${relayOnly ? " (relay-only)" : ""}`);
16723
+ logger$15.info(`[WebRTCManager] ICE restart initiated${relayOnly ? " (relay-only)" : ""}`);
16456
16724
  } catch (error) {
16457
- logger$14.error("[WebRTCManager] ICE restart failed:", error);
16725
+ logger$15.error("[WebRTCManager] ICE restart failed:", error);
16458
16726
  throw error;
16459
16727
  }
16460
16728
  }
@@ -16472,13 +16740,13 @@ var WebRTCVertoManager = class extends VertoManager {
16472
16740
  const entries = Array.from(this._rtcPeerConnectionsMap.entries());
16473
16741
  for (const [id, controller] of entries) try {
16474
16742
  if (!controller.peerConnection) {
16475
- logger$14.debug(`[WebRTCManager] No peer connection for leg ${id}, skipping ICE restart`);
16743
+ logger$15.debug(`[WebRTCManager] No peer connection for leg ${id}, skipping ICE restart`);
16476
16744
  continue;
16477
16745
  }
16478
16746
  await controller.triggerIceRestart(relayOnly);
16479
- logger$14.info(`[WebRTCManager] ICE restart initiated for leg ${id}${relayOnly ? " (relay-only)" : ""}`);
16747
+ logger$15.info(`[WebRTCManager] ICE restart initiated for leg ${id}${relayOnly ? " (relay-only)" : ""}`);
16480
16748
  } catch (error) {
16481
- logger$14.warn(`[WebRTCManager] ICE restart failed for leg ${id}:`, error);
16749
+ logger$15.warn(`[WebRTCManager] ICE restart failed for leg ${id}:`, error);
16482
16750
  }
16483
16751
  }
16484
16752
  /**
@@ -16490,7 +16758,7 @@ var WebRTCVertoManager = class extends VertoManager {
16490
16758
  requestKeyframeAll() {
16491
16759
  for (const [id, controller] of this._rtcPeerConnectionsMap) {
16492
16760
  if (controller.isScreenShare) {
16493
- logger$14.debug(`[WebRTCManager] Skipping keyframe for send-only screen share leg ${id}`);
16761
+ logger$15.debug(`[WebRTCManager] Skipping keyframe for send-only screen share leg ${id}`);
16494
16762
  continue;
16495
16763
  }
16496
16764
  try {
@@ -16500,33 +16768,33 @@ var WebRTCVertoManager = class extends VertoManager {
16500
16768
  if (!videoReceiver) continue;
16501
16769
  if (typeof videoReceiver.requestKeyFrame === "function") {
16502
16770
  videoReceiver.requestKeyFrame();
16503
- logger$14.debug(`[WebRTCManager] Keyframe requested for leg ${id}`);
16771
+ logger$15.debug(`[WebRTCManager] Keyframe requested for leg ${id}`);
16504
16772
  }
16505
16773
  } catch (error) {
16506
- logger$14.warn(`[WebRTCManager] Keyframe request failed for leg ${id} (non-fatal):`, error);
16774
+ logger$15.warn(`[WebRTCManager] Keyframe request failed for leg ${id} (non-fatal):`, error);
16507
16775
  }
16508
16776
  }
16509
16777
  }
16510
16778
  get callJoinedEvent$() {
16511
- return this.webRtcCallSession.callEvent$.pipe((0, import_cjs$14.filter)(isCallJoinedPayload), (0, import_cjs$14.takeUntil)(this.destroyed$));
16779
+ return this.webRtcCallSession.callEvent$.pipe((0, import_cjs$15.filter)(isCallJoinedPayload), (0, import_cjs$15.takeUntil)(this.destroyed$));
16512
16780
  }
16513
16781
  get vertoMedia$() {
16514
- return this.webRtcCallSession.webrtcMessages$.pipe(filterAs(isVertoMediaInnerParams, "params"), (0, import_cjs$14.takeUntil)(this.destroyed$));
16782
+ return this.webRtcCallSession.webrtcMessages$.pipe(filterAs(isVertoMediaInnerParams, "params"), (0, import_cjs$15.takeUntil)(this.destroyed$));
16515
16783
  }
16516
16784
  get vertoAnswer$() {
16517
- return this.cachedObservable("vertoAnswer$", () => this.webRtcCallSession.webrtcMessages$.pipe(filterAs(isVertoAnswerInnerParams, "params"), (0, import_cjs$14.takeUntil)(this.destroyed$)));
16785
+ return this.cachedObservable("vertoAnswer$", () => this.webRtcCallSession.webrtcMessages$.pipe(filterAs(isVertoAnswerInnerParams, "params"), (0, import_cjs$15.takeUntil)(this.destroyed$)));
16518
16786
  }
16519
16787
  get vertoMediaParams$() {
16520
- return this.cachedObservable("vertoMediaParams$", () => this.webRtcCallSession.webrtcMessages$.pipe(filterAs(isVertoMediaParamsInnerParams, "params"), (0, import_cjs$14.takeUntil)(this.destroyed$)));
16788
+ return this.cachedObservable("vertoMediaParams$", () => this.webRtcCallSession.webrtcMessages$.pipe(filterAs(isVertoMediaParamsInnerParams, "params"), (0, import_cjs$15.takeUntil)(this.destroyed$)));
16521
16789
  }
16522
16790
  get vertoBye$() {
16523
- return this.cachedObservable("vertoBye$", () => this.webRtcCallSession.webrtcMessages$.pipe(filterAs(isVertoByeMessage, "params"), (0, import_cjs$14.takeUntil)(this.destroyed$)));
16791
+ return this.cachedObservable("vertoBye$", () => this.webRtcCallSession.webrtcMessages$.pipe(filterAs(isVertoByeMessage, "params"), (0, import_cjs$15.takeUntil)(this.destroyed$)));
16524
16792
  }
16525
16793
  get vertoAttach$() {
16526
- return this.cachedObservable("vertoAttach$", () => this.webRtcCallSession.webrtcMessages$.pipe(filterAs(isVertoAttachMessage, "params"), (0, import_cjs$14.takeUntil)(this.destroyed$)));
16794
+ return this.cachedObservable("vertoAttach$", () => this.webRtcCallSession.webrtcMessages$.pipe(filterAs(isVertoAttachMessage, "params"), (0, import_cjs$15.takeUntil)(this.destroyed$)));
16527
16795
  }
16528
16796
  get vertoPing$() {
16529
- return this.cachedObservable("vertoPing$", () => this.webRtcCallSession.webrtcMessages$.pipe(filterAs(isVertoPingInnerParams, "params"), (0, import_cjs$14.takeUntil)(this.destroyed$)));
16797
+ return this.cachedObservable("vertoPing$", () => this.webRtcCallSession.webrtcMessages$.pipe(filterAs(isVertoPingInnerParams, "params"), (0, import_cjs$15.takeUntil)(this.destroyed$)));
16530
16798
  }
16531
16799
  async executeVerto(message, optionals = {}) {
16532
16800
  const webrtcVertoMessage = WebrtcVerto({
@@ -16564,7 +16832,7 @@ var WebRTCVertoManager = class extends VertoManager {
16564
16832
  default:
16565
16833
  }
16566
16834
  } catch (error) {
16567
- logger$14.error(`[WebRTCManager] Error sending Verto ${vertoMethod}:`, error);
16835
+ logger$15.error(`[WebRTCManager] Error sending Verto ${vertoMethod}:`, error);
16568
16836
  this.onError?.(error instanceof Error ? error : new Error(String(error), { cause: error }));
16569
16837
  if (vertoMethod === "verto.modify") this.onModifyFailed?.();
16570
16838
  }
@@ -16579,7 +16847,7 @@ var WebRTCVertoManager = class extends VertoManager {
16579
16847
  sdp
16580
16848
  });
16581
16849
  } catch (error) {
16582
- logger$14.warn("[WebRTCManager] Error processing modify response:", error);
16850
+ logger$15.warn("[WebRTCManager] Error processing modify response:", error);
16583
16851
  const modifyError = error instanceof Error ? error : new Error(String(error), { cause: error });
16584
16852
  this.onError?.(modifyError);
16585
16853
  }
@@ -16591,7 +16859,7 @@ var WebRTCVertoManager = class extends VertoManager {
16591
16859
  this._nodeId$.next(getValueFrom(response, "result.node_id") ?? null);
16592
16860
  const memberId = getValueFrom(response, "result.result.result.memberID") ?? null;
16593
16861
  const callId = getValueFrom(response, "result.result.result.callID") ?? null;
16594
- logger$14.debug("[WebRTCManager] Verto invite response:", {
16862
+ logger$15.debug("[WebRTCManager] Verto invite response:", {
16595
16863
  callId,
16596
16864
  memberId,
16597
16865
  response
@@ -16601,14 +16869,14 @@ var WebRTCVertoManager = class extends VertoManager {
16601
16869
  if (callId) {
16602
16870
  this.webRtcCallSession.addCallId(callId);
16603
16871
  this.attachManager.attach(this.buildAttachableCall(callId));
16604
- } else logger$14.warn("[WebRTCManager] Cannot attach call, missing callId:", {
16872
+ } else logger$15.warn("[WebRTCManager] Cannot attach call, missing callId:", {
16605
16873
  nodeId: this.nodeId,
16606
16874
  callId
16607
16875
  });
16608
- logger$14.info("[WebRTCManager] Verto invite successful");
16609
- logger$14.debug(`[WebRTCManager] nodeid: ${this._nodeId$.value}, selfId: ${this._selfId$.value}`);
16876
+ logger$15.info("[WebRTCManager] Verto invite successful");
16877
+ logger$15.debug(`[WebRTCManager] nodeid: ${this._nodeId$.value}, selfId: ${this._selfId$.value}`);
16610
16878
  } else {
16611
- logger$14.error("[WebRTCManager] Verto invite failed:", response);
16879
+ logger$15.error("[WebRTCManager] Verto invite failed:", response);
16612
16880
  const inviteError = response.error ? new JSONRPCError(response.error.code, response.error.message, response.error.data) : /* @__PURE__ */ new Error("Verto invite failed: unexpected response");
16613
16881
  this.onError?.(inviteError);
16614
16882
  }
@@ -16653,17 +16921,17 @@ var WebRTCVertoManager = class extends VertoManager {
16653
16921
  if (options.initOffer) this.handleInboundAnswer(rtcPeerConnController);
16654
16922
  }
16655
16923
  async handleInboundAnswer(rtcPeerConnController) {
16656
- logger$14.debug("[WebRTCManager] Waiting for inbound call to be accepted or rejected");
16657
- const vertoByeOrAccepted = await (0, import_cjs$14.firstValueFrom)((0, import_cjs$14.race)(this.vertoBye$, this.webRtcCallSession.answered$).pipe((0, import_cjs$14.takeUntil)(this.destroyed$))).catch(() => null);
16924
+ logger$15.debug("[WebRTCManager] Waiting for inbound call to be accepted or rejected");
16925
+ const vertoByeOrAccepted = await (0, import_cjs$15.firstValueFrom)((0, import_cjs$15.race)(this.vertoBye$, this.webRtcCallSession.answered$).pipe((0, import_cjs$15.takeUntil)(this.destroyed$))).catch(() => null);
16658
16926
  if (vertoByeOrAccepted === null) {
16659
- logger$14.debug("[WebRTCManager] Inbound answer handler aborted (destroyed).");
16927
+ logger$15.debug("[WebRTCManager] Inbound answer handler aborted (destroyed).");
16660
16928
  return;
16661
16929
  }
16662
16930
  if (isVertoByeMessage(vertoByeOrAccepted)) {
16663
- logger$14.info("[WebRTCManager] Inbound call ended by remote before answer.");
16931
+ logger$15.info("[WebRTCManager] Inbound call ended by remote before answer.");
16664
16932
  this.callSession?.destroy();
16665
16933
  } else if (!vertoByeOrAccepted) {
16666
- logger$14.info("[WebRTCManager] Inbound call rejected by user.");
16934
+ logger$15.info("[WebRTCManager] Inbound call rejected by user.");
16667
16935
  try {
16668
16936
  await this.bye("USER_BUSY");
16669
16937
  } finally {
@@ -16671,19 +16939,19 @@ var WebRTCVertoManager = class extends VertoManager {
16671
16939
  this.callSession?.destroy();
16672
16940
  }
16673
16941
  } else {
16674
- logger$14.debug("[WebRTCManager] Inbound call accepted, creating SDP answer");
16942
+ logger$15.debug("[WebRTCManager] Inbound call accepted, creating SDP answer");
16675
16943
  const answerOptions = this.webRtcCallSession.answerMediaOptions;
16676
16944
  try {
16677
16945
  await rtcPeerConnController.acceptInbound(answerOptions);
16678
16946
  } catch (error) {
16679
- logger$14.error("[WebRTCManager] Error creating inbound answer:", error);
16947
+ logger$15.error("[WebRTCManager] Error creating inbound answer:", error);
16680
16948
  this.onError?.(error instanceof Error ? error : new Error(String(error), { cause: error }));
16681
16949
  }
16682
16950
  }
16683
16951
  }
16684
16952
  setupVertoAttachHandler() {
16685
16953
  this.subscribeTo(this.vertoAttach$, async (vertoAttach) => {
16686
- logger$14.debug("[WebRTCManager] Received Verto attach event for existing call:", vertoAttach);
16954
+ logger$15.debug("[WebRTCManager] Received Verto attach event for existing call:", vertoAttach);
16687
16955
  const { callID } = vertoAttach;
16688
16956
  await this.attachManager.attach({
16689
16957
  nodeId: this.nodeId ?? void 0,
@@ -16697,12 +16965,12 @@ var WebRTCVertoManager = class extends VertoManager {
16697
16965
  });
16698
16966
  }
16699
16967
  initObservables(rtcPeerConnController) {
16700
- this.mediaDirections$ = rtcPeerConnController.connectionState$.pipe((0, import_cjs$14.filter)((state) => state === "connected"), (0, import_cjs$14.map)(() => rtcPeerConnController.mediaDirections), (0, import_cjs$14.startWith)(rtcPeerConnController.mediaDirections), (0, import_cjs$14.takeUntil)(this.destroyed$));
16701
- this.localStream$ = rtcPeerConnController.localStream$.pipe(filterNull(), (0, import_cjs$14.takeUntil)(this.destroyed$));
16702
- this.remoteStream$ = rtcPeerConnController.remoteStream$.pipe(filterNull(), (0, import_cjs$14.takeUntil)(this.destroyed$));
16968
+ this.mediaDirections$ = rtcPeerConnController.connectionState$.pipe((0, import_cjs$15.filter)((state) => state === "connected"), (0, import_cjs$15.map)(() => rtcPeerConnController.mediaDirections), (0, import_cjs$15.startWith)(rtcPeerConnController.mediaDirections), (0, import_cjs$15.takeUntil)(this.destroyed$));
16969
+ this.localStream$ = rtcPeerConnController.localStream$.pipe(filterNull(), (0, import_cjs$15.takeUntil)(this.destroyed$));
16970
+ this.remoteStream$ = rtcPeerConnController.remoteStream$.pipe(filterNull(), (0, import_cjs$15.takeUntil)(this.destroyed$));
16703
16971
  }
16704
16972
  setupLocalDescriptionHandler(rtcPeerConnController) {
16705
- this.subscribeTo(rtcPeerConnController.localDescription$.pipe((0, import_cjs$14.filter)((description) => description !== null), (0, import_cjs$14.takeUntil)(this.destroyed$)), (description) => {
16973
+ this.subscribeTo(rtcPeerConnController.localDescription$.pipe((0, import_cjs$15.filter)((description) => description !== null), (0, import_cjs$15.takeUntil)(this.destroyed$)), (description) => {
16706
16974
  const { type, sdp } = description;
16707
16975
  const dialogParams = this.dialogParams(rtcPeerConnController);
16708
16976
  const initial = !rtcPeerConnController.firstSDPExchangeCompleted;
@@ -16752,17 +17020,17 @@ var WebRTCVertoManager = class extends VertoManager {
16752
17020
  };
16753
17021
  }
16754
17022
  async sendLocalDescriptionOnceAccepted(vertoMessageRequest, rtcPeerConnectionController) {
16755
- logger$14.debug("[WebRTCManager] Waiting for call to be accepted or ended before sending answer");
16756
- const vertoByeOrAccepted = await (0, import_cjs$14.firstValueFrom)((0, import_cjs$14.race)(this.vertoBye$, this.webRtcCallSession.answered$).pipe((0, import_cjs$14.takeUntil)(this.destroyed$))).catch(() => null);
17023
+ logger$15.debug("[WebRTCManager] Waiting for call to be accepted or ended before sending answer");
17024
+ const vertoByeOrAccepted = await (0, import_cjs$15.firstValueFrom)((0, import_cjs$15.race)(this.vertoBye$, this.webRtcCallSession.answered$).pipe((0, import_cjs$15.takeUntil)(this.destroyed$))).catch(() => null);
16757
17025
  if (vertoByeOrAccepted === null) {
16758
- logger$14.debug("[WebRTCManager] Destroyed while waiting for call acceptance");
17026
+ logger$15.debug("[WebRTCManager] Destroyed while waiting for call acceptance");
16759
17027
  return;
16760
17028
  }
16761
17029
  if (isVertoByeMessage(vertoByeOrAccepted)) {
16762
- logger$14.info("[WebRTCManager] Call ended before answer was sent.");
17030
+ logger$15.info("[WebRTCManager] Call ended before answer was sent.");
16763
17031
  this.callSession?.destroy();
16764
17032
  } else if (!vertoByeOrAccepted) {
16765
- logger$14.info("[WebRTCManager] Call was not accepted, sending verto.bye.");
17033
+ logger$15.info("[WebRTCManager] Call was not accepted, sending verto.bye.");
16766
17034
  try {
16767
17035
  await this.bye("USER_BUSY");
16768
17036
  } finally {
@@ -16770,14 +17038,14 @@ var WebRTCVertoManager = class extends VertoManager {
16770
17038
  this.callSession?.destroy();
16771
17039
  }
16772
17040
  } else {
16773
- logger$14.debug("[WebRTCManager] Call accepted, sending answer");
17041
+ logger$15.debug("[WebRTCManager] Call accepted, sending answer");
16774
17042
  try {
16775
17043
  this._signalingStatus$.next("connecting");
16776
17044
  await this.sendLocalDescription(vertoMessageRequest, rtcPeerConnectionController);
16777
17045
  await rtcPeerConnectionController.updateAnswerStatus({ status: "sent" });
16778
17046
  await this.attachManager.attach(this.buildAttachableCall());
16779
17047
  } catch (error) {
16780
- logger$14.error("[WebRTCManager] Error sending Verto answer:", error);
17048
+ logger$15.error("[WebRTCManager] Error sending Verto answer:", error);
16781
17049
  this.onError?.(error instanceof Error ? error : new Error(String(error), { cause: error }));
16782
17050
  await rtcPeerConnectionController.updateAnswerStatus({ status: "failed" });
16783
17051
  }
@@ -16818,6 +17086,14 @@ var WebRTCVertoManager = class extends VertoManager {
16818
17086
  async unmuteMainVideoInputDevice() {
16819
17087
  return this.mainPeerConnection.restoreTrackSender("video");
16820
17088
  }
17089
+ /** Get or lazily create the local audio pipeline for the main peer connection. */
17090
+ ensureLocalAudioPipeline() {
17091
+ return this.mainPeerConnection.ensureLocalAudioPipeline();
17092
+ }
17093
+ /** The currently-active local audio pipeline, or null if it hasn't been created. */
17094
+ get localAudioPipeline() {
17095
+ return this.mainPeerConnection.localAudioPipeline;
17096
+ }
16821
17097
  async addInputDevice(options = {
16822
17098
  audio: false,
16823
17099
  video: true
@@ -16866,12 +17142,12 @@ var WebRTCVertoManager = class extends VertoManager {
16866
17142
  this.subscribeTo(rtcPeerConnController.errors$, (error) => {
16867
17143
  this.onError?.(error);
16868
17144
  });
16869
- await (0, import_cjs$14.firstValueFrom)(rtcPeerConnController.connectionState$.pipe((0, import_cjs$14.filter)((state) => state === "connected"), (0, import_cjs$14.take)(1), (0, import_cjs$14.timeout)(this._screenShareTimeoutMs), (0, import_cjs$14.takeUntil)(this.destroyed$)));
17145
+ await (0, import_cjs$15.firstValueFrom)(rtcPeerConnController.connectionState$.pipe((0, import_cjs$15.filter)((state) => state === "connected"), (0, import_cjs$15.take)(1), (0, import_cjs$15.timeout)(this._screenShareTimeoutMs), (0, import_cjs$15.takeUntil)(this.destroyed$)));
16870
17146
  this._screenShareStatus$.next("started");
16871
- logger$14.info("[WebRTCManager] Screen share started successfully.");
17147
+ logger$15.info("[WebRTCManager] Screen share started successfully.");
16872
17148
  return rtcPeerConnController.id;
16873
17149
  } catch (error) {
16874
- logger$14.warn("[WebRTCManager] Error initializing additional peer connection:", error);
17150
+ logger$15.warn("[WebRTCManager] Error initializing additional peer connection:", error);
16875
17151
  this.onError?.(error instanceof Error ? error : new Error(String(error), { cause: error }));
16876
17152
  if (rtcPeerConnController) rtcPeerConnController.destroy();
16877
17153
  this._screenShareStatus$.next("none");
@@ -16890,9 +17166,9 @@ var WebRTCVertoManager = class extends VertoManager {
16890
17166
  if (removeTrack) return this.mainPeerConnection.stopTrackSender(removeTrack, { updateTransceiverDirection: true });
16891
17167
  }
16892
17168
  async removeScreenMedia() {
16893
- if (!["starting", "started"].includes(this._screenShareStatus$.value)) logger$14.warn("[WebRTCManager] No active screen share to stop.");
17169
+ if (!["starting", "started"].includes(this._screenShareStatus$.value)) logger$15.warn("[WebRTCManager] No active screen share to stop.");
16894
17170
  if (!this._screenShareId) {
16895
- logger$14.debug("[WebRTCManager] No screen share peer connection found.");
17171
+ logger$15.debug("[WebRTCManager] No screen share peer connection found.");
16896
17172
  return;
16897
17173
  }
16898
17174
  this._screenShareStatus$.next("stopping");
@@ -16921,7 +17197,7 @@ var WebRTCVertoManager = class extends VertoManager {
16921
17197
  dialogParams: this.dialogParams(rtcPeerConnController)
16922
17198
  }));
16923
17199
  } catch (error) {
16924
- logger$14.warn("[WebRTCManager] Call might already be disconnected, error sending Verto bye:", error);
17200
+ logger$15.warn("[WebRTCManager] Call might already be disconnected, error sending Verto bye:", error);
16925
17201
  throw error;
16926
17202
  }
16927
17203
  }
@@ -16939,7 +17215,7 @@ var WebRTCVertoManager = class extends VertoManager {
16939
17215
  try {
16940
17216
  await this.executeVerto(vertoInfoMessage);
16941
17217
  } catch (error) {
16942
- logger$14.warn("[WebRTCManager] Error sending DTMF digits:", error);
17218
+ logger$15.warn("[WebRTCManager] Error sending DTMF digits:", error);
16943
17219
  throw error;
16944
17220
  }
16945
17221
  }
@@ -16950,10 +17226,10 @@ var WebRTCVertoManager = class extends VertoManager {
16950
17226
  action: "transfer"
16951
17227
  });
16952
17228
  try {
16953
- logger$14.debug("[WebRTCManager] Transferring call with options:", options);
17229
+ logger$15.debug("[WebRTCManager] Transferring call with options:", options);
16954
17230
  await this.executeVerto(message);
16955
17231
  } catch (error) {
16956
- logger$14.error("[WebRTCManager] Error transferring call:", error);
17232
+ logger$15.error("[WebRTCManager] Error transferring call:", error);
16957
17233
  throw error;
16958
17234
  }
16959
17235
  }
@@ -16967,6 +17243,77 @@ var WebRTCVertoManager = class extends VertoManager {
16967
17243
  }
16968
17244
  };
16969
17245
 
17246
+ //#endregion
17247
+ //#region src/controllers/RemoteAudioMeter.ts
17248
+ var import_cjs$14 = require_cjs();
17249
+ const logger$14 = getLogger();
17250
+ /**
17251
+ * Read-only audio level meter for a remote MediaStream. Attaches an
17252
+ * AnalyserNode to a MediaStreamAudioSourceNode so it observes the stream
17253
+ * without affecting the caller's playback path (no GainNode, no destination).
17254
+ *
17255
+ * The server delivers all remote audio as a single mixed stream — there is
17256
+ * no per-participant demux — so this meter reports the aggregate remote
17257
+ * level, not per-member.
17258
+ */
17259
+ var RemoteAudioMeter = class extends Destroyable {
17260
+ constructor(options = {}) {
17261
+ super();
17262
+ this._source = null;
17263
+ this._stream = null;
17264
+ this._audioContext = (options.audioContextFactory ?? (() => new AudioContext()))();
17265
+ this._analyser = this._audioContext.createAnalyser();
17266
+ this._analyser.fftSize = 2048;
17267
+ this._analyser.smoothingTimeConstant = .3;
17268
+ this._analyserBuffer = new Uint8Array(new ArrayBuffer(this._analyser.fftSize));
17269
+ this._pollIntervalMs = options.pollIntervalMs ?? AUDIO_LEVEL_POLL_INTERVAL_MS;
17270
+ }
17271
+ /** RMS level of the remote audio, 0..1. 0 when no stream is attached. */
17272
+ get level$() {
17273
+ return this.deferEmission((0, import_cjs$14.interval)(this._pollIntervalMs, import_cjs$14.animationFrameScheduler).pipe((0, import_cjs$14.map)(() => this.computeLevel())));
17274
+ }
17275
+ /**
17276
+ * Attach (or replace) the MediaStream whose audio track is being metered.
17277
+ * Pass null to detach without destroying the meter.
17278
+ */
17279
+ setStream(stream) {
17280
+ if (this._source) {
17281
+ try {
17282
+ this._source.disconnect();
17283
+ } catch (error) {
17284
+ logger$14.debug("[RemoteAudioMeter] source disconnect warning:", error);
17285
+ }
17286
+ this._source = null;
17287
+ this._stream = null;
17288
+ }
17289
+ if (!stream || stream.getAudioTracks().length === 0) return;
17290
+ this._stream = new MediaStream(stream.getAudioTracks());
17291
+ this._source = this._audioContext.createMediaStreamSource(this._stream);
17292
+ }
17293
+ destroy() {
17294
+ if (this._source) {
17295
+ try {
17296
+ this._source.disconnect();
17297
+ } catch {}
17298
+ this._source = null;
17299
+ }
17300
+ this._audioContext.close().catch((error) => {
17301
+ logger$14.debug("[RemoteAudioMeter] audio context close warning:", error);
17302
+ });
17303
+ super.destroy();
17304
+ }
17305
+ computeLevel() {
17306
+ if (!this._source) return 0;
17307
+ this._analyser.getByteTimeDomainData(this._analyserBuffer);
17308
+ let sum = 0;
17309
+ for (const sample$1 of this._analyserBuffer) {
17310
+ const normalized = (sample$1 - 128) / 128;
17311
+ sum += normalized * normalized;
17312
+ }
17313
+ return Math.sqrt(sum / this._analyserBuffer.length);
17314
+ }
17315
+ };
17316
+
16970
17317
  //#endregion
16971
17318
  //#region src/controllers/RTCStatsMonitor.ts
16972
17319
  var import_cjs$13 = require_cjs();
@@ -17780,6 +18127,8 @@ var WebRTCCall = class extends Destroyable {
17780
18127
  this._bandwidthConstrained$ = this.createBehaviorSubject(false);
17781
18128
  this._mediaParamsUpdated$ = this.createSubject();
17782
18129
  this._customSubscriptions = /* @__PURE__ */ new Map();
18130
+ this._pushToTalkEnabled = false;
18131
+ this._remoteAudioMeter = null;
17783
18132
  this.id = options.callId ?? v4_default();
17784
18133
  this.to = options.to;
17785
18134
  this._userVariables$.next({
@@ -18551,11 +18900,129 @@ var WebRTCCall = class extends Destroyable {
18551
18900
  async transfer(options) {
18552
18901
  return this.vertoManager.transfer(options);
18553
18902
  }
18903
+ /**
18904
+ * Set the local microphone gain as a percentage applied before transmission.
18905
+ *
18906
+ * - `0` = silent
18907
+ * - `100` = unity (no change, default)
18908
+ * - `200` = 2× digital boost (max; expect clipping / noise amplification)
18909
+ *
18910
+ * Values are clamped to [0, 200]. Engages the local audio pipeline on
18911
+ * first use (one-time cost).
18912
+ *
18913
+ * Note: this is a **digital** multiplier applied in a Web Audio GainNode
18914
+ * between your mic track and the RTCRtpSender — it does not change the
18915
+ * physical mic's hardware sensitivity. Browsers' autoGainControl can
18916
+ * fight the setting; call {@link setAutoGainControl}(false) for
18917
+ * predictable behaviour.
18918
+ *
18919
+ * @param value - Gain percentage (0..200; 100 = unity).
18920
+ */
18921
+ setLocalMicrophoneGain(value) {
18922
+ const pipeline = this.vertoManager.ensureLocalAudioPipeline();
18923
+ if (!pipeline) {
18924
+ logger$11.warn("[Call] setLocalMicrophoneGain: audio pipeline unavailable");
18925
+ return;
18926
+ }
18927
+ const percent = Math.max(0, Math.min(200, value));
18928
+ pipeline.setGain(percent / 100);
18929
+ }
18930
+ /** Observable of the current local microphone gain (0..200, where 100 = unity). */
18931
+ get localMicrophoneGain$() {
18932
+ const pipeline = this.vertoManager.ensureLocalAudioPipeline();
18933
+ if (!pipeline) return (0, import_cjs$11.of)(100).pipe((0, import_cjs$11.takeUntil)(this._destroyed$));
18934
+ return pipeline.gain$.pipe((0, import_cjs$11.map)((multiplier) => multiplier * 100), (0, import_cjs$11.takeUntil)(this._destroyed$));
18935
+ }
18936
+ /**
18937
+ * Observable of the RMS audio level of the local microphone, 0..1.
18938
+ * Emits at ~30fps while a mic track is active. Engages the local audio
18939
+ * pipeline on first subscription.
18940
+ */
18941
+ get localAudioLevel$() {
18942
+ return this.vertoManager.ensureLocalAudioPipeline()?.level$ ?? (0, import_cjs$11.of)(0).pipe((0, import_cjs$11.takeUntil)(this._destroyed$));
18943
+ }
18944
+ /**
18945
+ * Observable that is `true` while the local participant is speaking
18946
+ * (RMS level above the VAD threshold, with hold time to avoid flicker).
18947
+ */
18948
+ get localSpeaking$() {
18949
+ return this.vertoManager.ensureLocalAudioPipeline()?.speaking$ ?? (0, import_cjs$11.of)(false).pipe((0, import_cjs$11.takeUntil)(this._destroyed$));
18950
+ }
18951
+ /**
18952
+ * Enable push-to-talk: while {@link setPushToTalkActive} has been called
18953
+ * with `false`, the microphone gain is forced to 0; calling
18954
+ * {@link setPushToTalkActive} with `true` restores the configured gain.
18955
+ * Use this instead of mute/unmute for instant talk/silence transitions
18956
+ * because it doesn't rebuild the track.
18957
+ *
18958
+ * This method installs the pipeline but does not attach any keyboard
18959
+ * listener — consumers bind the key themselves and call
18960
+ * {@link setPushToTalkActive} on keydown/keyup.
18961
+ */
18962
+ enablePushToTalk() {
18963
+ const pipeline = this.vertoManager.ensureLocalAudioPipeline();
18964
+ if (!pipeline) {
18965
+ logger$11.warn("[Call] enablePushToTalk: audio pipeline unavailable");
18966
+ return;
18967
+ }
18968
+ pipeline.setPTTActive(false);
18969
+ this._pushToTalkEnabled = true;
18970
+ }
18971
+ /** Disable push-to-talk; mic gain returns to the configured value. */
18972
+ disablePushToTalk() {
18973
+ this.vertoManager.localAudioPipeline?.setPTTActive(true);
18974
+ this._pushToTalkEnabled = false;
18975
+ }
18976
+ /**
18977
+ * While push-to-talk is enabled, sets the talk state. `true` = transmitting,
18978
+ * `false` = silent. No-op if push-to-talk has not been enabled.
18979
+ */
18980
+ setPushToTalkActive(active) {
18981
+ if (!this._pushToTalkEnabled) return;
18982
+ this.vertoManager.localAudioPipeline?.setPTTActive(active);
18983
+ }
18984
+ /**
18985
+ * Toggle echo cancellation on the local mic at runtime. Applied via
18986
+ * `track.applyConstraints`; browsers that don't honour runtime constraints
18987
+ * (notably iOS Safari) fall back to re-acquiring the track with the new
18988
+ * constraint set and plumbing the replacement through the local audio
18989
+ * pipeline if one is active.
18990
+ */
18991
+ async setEchoCancellation(enabled) {
18992
+ await this.vertoManager.updateMediaConstraints({ audio: { echoCancellation: enabled } });
18993
+ }
18994
+ /** Toggle browser noise suppression on the local mic at runtime. */
18995
+ async setNoiseSuppression(enabled) {
18996
+ await this.vertoManager.updateMediaConstraints({ audio: { noiseSuppression: enabled } });
18997
+ }
18998
+ /** Toggle browser automatic gain control on the local mic at runtime. */
18999
+ async setAutoGainControl(enabled) {
19000
+ await this.vertoManager.updateMediaConstraints({ audio: { autoGainControl: enabled } });
19001
+ }
19002
+ /**
19003
+ * Observable of the aggregate remote audio level, 0..1 RMS. The server
19004
+ * delivers a single mixed audio stream for all remote participants — this
19005
+ * meter reports that mix. Per-participant audio is not available client-side.
19006
+ *
19007
+ * Engages a shared AudioContext on first subscription (cheap — one
19008
+ * AnalyserNode, no GainNode, no destination) so it does not affect the
19009
+ * caller's audio element playback.
19010
+ */
19011
+ get remoteAudioLevel$() {
19012
+ this._remoteAudioMeter ??= new RemoteAudioMeter();
19013
+ const meter = this._remoteAudioMeter;
19014
+ this.subscribeTo(this.vertoManager.remoteStream$, (stream) => {
19015
+ meter.setStream(stream);
19016
+ });
19017
+ return meter.level$.pipe((0, import_cjs$11.takeUntil)(this._destroyed$));
19018
+ }
18554
19019
  /** Destroys the call, releasing all resources and subscriptions. */
18555
19020
  destroy() {
18556
19021
  if (this._status$.value === "destroyed") return;
18557
19022
  this._status$.next("destroyed");
18558
19023
  this.stopResilienceSubsystems();
19024
+ this._remoteAudioMeter?.destroy();
19025
+ this._remoteAudioMeter = null;
18559
19026
  this.vertoManager.destroy();
18560
19027
  this.callEventsManager.destroy();
18561
19028
  super.destroy();
@@ -21216,6 +21683,36 @@ var SignalWire = class extends Destroyable {
21216
21683
  selectAudioOutputDevice(device) {
21217
21684
  this._deviceController.selectAudioOutputDevice(device);
21218
21685
  }
21686
+ /**
21687
+ * Apply the currently selected audio output device to an HTMLMediaElement
21688
+ * (e.g. the `<audio>` or `<video>` element the consumer attached the
21689
+ * remote stream to). Uses `HTMLMediaElement.setSinkId` under the hood.
21690
+ * Returns a `Promise<boolean>`: `true` if the sink was applied,
21691
+ * `false` if the browser doesn't support `setSinkId` or no device is
21692
+ * selected.
21693
+ *
21694
+ * @example
21695
+ * ```ts
21696
+ * audioEl.srcObject = call.remoteStream;
21697
+ * await client.applySelectedAudioOutputDevice(audioEl);
21698
+ * ```
21699
+ */
21700
+ async applySelectedAudioOutputDevice(element) {
21701
+ const device = this._deviceController.selectedAudioOutputDevice;
21702
+ if (!device?.deviceId) return false;
21703
+ const withSink = element;
21704
+ if (typeof withSink.setSinkId !== "function") {
21705
+ logger$1.warn("[SignalWire] setSinkId not supported on this element / browser");
21706
+ return false;
21707
+ }
21708
+ try {
21709
+ await withSink.setSinkId(device.deviceId);
21710
+ return true;
21711
+ } catch (error) {
21712
+ logger$1.warn("[SignalWire] Failed to apply audio output device:", error);
21713
+ return false;
21714
+ }
21715
+ }
21219
21716
  /** Starts monitoring for media device changes (connect/disconnect). */
21220
21717
  enableDeviceMonitoring() {
21221
21718
  this._deviceController.enableDeviceMonitoring();