@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.
@@ -3095,13 +3095,13 @@ var require_of = /* @__PURE__ */ __commonJSMin(((exports) => {
3095
3095
  exports.of = void 0;
3096
3096
  var args_1$12 = require_args();
3097
3097
  var from_1$7 = require_from();
3098
- function of$1() {
3098
+ function of$2() {
3099
3099
  var args = [];
3100
3100
  for (var _i = 0; _i < arguments.length; _i++) args[_i] = arguments[_i];
3101
3101
  var scheduler = args_1$12.popScheduler(args);
3102
3102
  return from_1$7.from(args, scheduler);
3103
3103
  }
3104
- exports.of = of$1;
3104
+ exports.of = of$2;
3105
3105
  }));
3106
3106
 
3107
3107
  //#endregion
@@ -3394,7 +3394,7 @@ var require_map = /* @__PURE__ */ __commonJSMin(((exports) => {
3394
3394
  exports.map = void 0;
3395
3395
  var lift_1$64 = require_lift();
3396
3396
  var OperatorSubscriber_1$54 = require_OperatorSubscriber();
3397
- function map$18(project, thisArg) {
3397
+ function map$20(project, thisArg) {
3398
3398
  return lift_1$64.operate(function(source, subscriber) {
3399
3399
  var index = 0;
3400
3400
  source.subscribe(OperatorSubscriber_1$54.createOperatorSubscriber(subscriber, function(value) {
@@ -3402,7 +3402,7 @@ var require_map = /* @__PURE__ */ __commonJSMin(((exports) => {
3402
3402
  }));
3403
3403
  });
3404
3404
  }
3405
- exports.map = map$18;
3405
+ exports.map = map$20;
3406
3406
  }));
3407
3407
 
3408
3408
  //#endregion
@@ -4173,13 +4173,13 @@ var require_interval = /* @__PURE__ */ __commonJSMin(((exports) => {
4173
4173
  exports.interval = void 0;
4174
4174
  var async_1$10 = require_async();
4175
4175
  var timer_1$6 = require_timer();
4176
- function interval$2(period, scheduler) {
4176
+ function interval$4(period, scheduler) {
4177
4177
  if (period === void 0) period = 0;
4178
4178
  if (scheduler === void 0) scheduler = async_1$10.asyncScheduler;
4179
4179
  if (period < 0) period = 0;
4180
4180
  return timer_1$6.timer(period, period, scheduler);
4181
4181
  }
4182
- exports.interval = interval$2;
4182
+ exports.interval = interval$4;
4183
4183
  }));
4184
4184
 
4185
4185
  //#endregion
@@ -5474,7 +5474,7 @@ var require_distinctUntilChanged = /* @__PURE__ */ __commonJSMin(((exports) => {
5474
5474
  var identity_1$10 = require_identity();
5475
5475
  var lift_1$42 = require_lift();
5476
5476
  var OperatorSubscriber_1$31 = require_OperatorSubscriber();
5477
- function distinctUntilChanged$8(comparator, keySelector) {
5477
+ function distinctUntilChanged$9(comparator, keySelector) {
5478
5478
  if (keySelector === void 0) keySelector = identity_1$10.identity;
5479
5479
  comparator = comparator !== null && comparator !== void 0 ? comparator : defaultCompare;
5480
5480
  return lift_1$42.operate(function(source, subscriber) {
@@ -5490,7 +5490,7 @@ var require_distinctUntilChanged = /* @__PURE__ */ __commonJSMin(((exports) => {
5490
5490
  }));
5491
5491
  });
5492
5492
  }
5493
- exports.distinctUntilChanged = distinctUntilChanged$8;
5493
+ exports.distinctUntilChanged = distinctUntilChanged$9;
5494
5494
  function defaultCompare(a, b) {
5495
5495
  return a === b;
5496
5496
  }
@@ -7186,17 +7186,17 @@ var require_timeInterval = /* @__PURE__ */ __commonJSMin(((exports) => {
7186
7186
  var last$2 = scheduler.now();
7187
7187
  source.subscribe(OperatorSubscriber_1$6.createOperatorSubscriber(subscriber, function(value) {
7188
7188
  var now = scheduler.now();
7189
- var interval$3 = now - last$2;
7189
+ var interval$5 = now - last$2;
7190
7190
  last$2 = now;
7191
- subscriber.next(new TimeInterval(value, interval$3));
7191
+ subscriber.next(new TimeInterval(value, interval$5));
7192
7192
  }));
7193
7193
  });
7194
7194
  }
7195
7195
  exports.timeInterval = timeInterval;
7196
7196
  var TimeInterval = function() {
7197
- function TimeInterval$1(value, interval$3) {
7197
+ function TimeInterval$1(value, interval$5) {
7198
7198
  this.value = value;
7199
- this.interval = interval$3;
7199
+ this.interval = interval$5;
7200
7200
  }
7201
7201
  return TimeInterval$1;
7202
7202
  }();
@@ -8937,12 +8937,12 @@ var require_cjs = /* @__PURE__ */ __commonJSMin(((exports) => {
8937
8937
 
8938
8938
  //#endregion
8939
8939
  //#region src/behaviors/Destroyable.ts
8940
- var import_cjs$28 = require_cjs();
8940
+ var import_cjs$30 = require_cjs();
8941
8941
  var Destroyable = class {
8942
8942
  constructor() {
8943
8943
  this.subscriptions = [];
8944
8944
  this.subjects = [];
8945
- this._destroyed$ = new import_cjs$28.Subject();
8945
+ this._destroyed$ = new import_cjs$30.Subject();
8946
8946
  }
8947
8947
  destroy() {
8948
8948
  this._observableCache?.clear();
@@ -8978,7 +8978,7 @@ var Destroyable = class {
8978
8978
  this._observableCache ??= /* @__PURE__ */ new Map();
8979
8979
  let cached = this._observableCache.get(publicKey);
8980
8980
  if (!cached) {
8981
- cached = factory().pipe((0, import_cjs$28.observeOn)(import_cjs$28.asapScheduler));
8981
+ cached = factory().pipe((0, import_cjs$30.observeOn)(import_cjs$30.asapScheduler));
8982
8982
  this._observableCache.set(publicKey, cached);
8983
8983
  }
8984
8984
  return cached;
@@ -8992,24 +8992,24 @@ var Destroyable = class {
8992
8992
  * Do NOT use for observables consumed internally by the SDK.
8993
8993
  */
8994
8994
  deferEmission(observable) {
8995
- return observable.pipe((0, import_cjs$28.observeOn)(import_cjs$28.asapScheduler));
8995
+ return observable.pipe((0, import_cjs$30.observeOn)(import_cjs$30.asapScheduler));
8996
8996
  }
8997
8997
  subscribeTo(observable, observerOrNext) {
8998
8998
  const subscription = observable.subscribe(observerOrNext);
8999
8999
  this.subscriptions.push(subscription);
9000
9000
  }
9001
9001
  createSubject() {
9002
- const subject = new import_cjs$28.Subject();
9002
+ const subject = new import_cjs$30.Subject();
9003
9003
  this.subjects.push(subject);
9004
9004
  return subject;
9005
9005
  }
9006
9006
  createReplaySubject(bufferSize, windowTime$1) {
9007
- const subject = new import_cjs$28.ReplaySubject(bufferSize, windowTime$1);
9007
+ const subject = new import_cjs$30.ReplaySubject(bufferSize, windowTime$1);
9008
9008
  this.subjects.push(subject);
9009
9009
  return subject;
9010
9010
  }
9011
9011
  createBehaviorSubject(initialValue) {
9012
- const subject = new import_cjs$28.BehaviorSubject(initialValue);
9012
+ const subject = new import_cjs$30.BehaviorSubject(initialValue);
9013
9013
  this.subjects.push(subject);
9014
9014
  return subject;
9015
9015
  }
@@ -9470,9 +9470,9 @@ var require_loglevel = /* @__PURE__ */ __commonJSMin(((exports, module) => {
9470
9470
  defaultLogger$1 = new Logger();
9471
9471
  defaultLogger$1.getLogger = function getLogger$1(name) {
9472
9472
  if (typeof name !== "symbol" && typeof name !== "string" || name === "") throw new TypeError("You must supply a name when creating a logger.");
9473
- var logger$30 = _loggersByName[name];
9474
- if (!logger$30) logger$30 = _loggersByName[name] = new Logger(name, defaultLogger$1.methodFactory);
9475
- return logger$30;
9473
+ var logger$32 = _loggersByName[name];
9474
+ if (!logger$32) logger$32 = _loggersByName[name] = new Logger(name, defaultLogger$1.methodFactory);
9475
+ return logger$32;
9476
9476
  };
9477
9477
  var _log = typeof window !== undefinedType ? window.log : void 0;
9478
9478
  defaultLogger$1.noConflict = function() {
@@ -9508,8 +9508,8 @@ const defaultLoggerLevel = defaultLogger.levels.WARN;
9508
9508
  defaultLogger.setLevel(defaultLoggerLevel);
9509
9509
  let userLogger = null;
9510
9510
  /** Replace the built-in logger with a custom implementation. Pass `null` to restore defaults. */
9511
- const setLogger = (logger$30) => {
9512
- userLogger = logger$30;
9511
+ const setLogger = (logger$32) => {
9512
+ userLogger = logger$32;
9513
9513
  };
9514
9514
  let debugOptions = {};
9515
9515
  /** Configure debug options (e.g., `{ logWsTraffic: true }`). */
@@ -9553,8 +9553,8 @@ const wsTraffic = (options) => {
9553
9553
  loggerInstance.debug(`${options.type.toUpperCase()}: \n`, msg, "\n");
9554
9554
  };
9555
9555
  const getLogger = () => {
9556
- const logger$30 = getLoggerInstance();
9557
- return new Proxy(logger$30, { get(_target, prop, _receiver) {
9556
+ const logger$32 = getLoggerInstance();
9557
+ return new Proxy(logger$32, { get(_target, prop, _receiver) {
9558
9558
  if (prop === "wsTraffic") return wsTraffic;
9559
9559
  const instance = getLoggerInstance();
9560
9560
  const value = Reflect.get(instance, prop);
@@ -9606,7 +9606,7 @@ const asyncRetry = async ({ asyncCallable, maxRetries: retries = DEFAULT_MAX_RET
9606
9606
 
9607
9607
  //#endregion
9608
9608
  //#region src/controllers/HTTPRequestController.ts
9609
- const logger$29 = getLogger();
9609
+ const logger$31 = getLogger();
9610
9610
  const GET_PARAMS = {
9611
9611
  method: "GET",
9612
9612
  headers: { Accept: "application/json" }
@@ -9670,7 +9670,7 @@ var HTTPRequestController = class HTTPRequestController extends Destroyable {
9670
9670
  this._responses$.next(response);
9671
9671
  return response;
9672
9672
  } catch (error) {
9673
- logger$29.error("[HTTPRequestController] Request error:", error);
9673
+ logger$31.error("[HTTPRequestController] Request error:", error);
9674
9674
  this._status$.next("error");
9675
9675
  const err = error instanceof Error ? error : new Error("HTTP request failed", { cause: error });
9676
9676
  this._errors$.next(err);
@@ -9697,7 +9697,7 @@ var HTTPRequestController = class HTTPRequestController extends Destroyable {
9697
9697
  const url = this.buildURL(request.url);
9698
9698
  const headers = this.buildHeaders(request.headers);
9699
9699
  const timeout$5 = request.timeout ?? this.requestTimeout;
9700
- logger$29.debug("[HTTPRequestController] Executing request:", {
9700
+ logger$31.debug("[HTTPRequestController] Executing request:", {
9701
9701
  method: request.method,
9702
9702
  url,
9703
9703
  headers: Object.keys(headers).reduce((acc, key) => {
@@ -9717,7 +9717,7 @@ var HTTPRequestController = class HTTPRequestController extends Destroyable {
9717
9717
  });
9718
9718
  clearTimeout(timeoutId);
9719
9719
  const httpResponse = await this.convertResponse(response);
9720
- logger$29.debug("[HTTPRequestController] Response received:", {
9720
+ logger$31.debug("[HTTPRequestController] Response received:", {
9721
9721
  status: response.status,
9722
9722
  statusText: response.statusText,
9723
9723
  headers: [...response.headers.entries()],
@@ -9727,7 +9727,7 @@ var HTTPRequestController = class HTTPRequestController extends Destroyable {
9727
9727
  } catch (error) {
9728
9728
  clearTimeout(timeoutId);
9729
9729
  if (error instanceof Error && error.name === "AbortError") throw new RequestTimeoutError(`Request timeout after ${timeout$5}ms`, { cause: error });
9730
- logger$29.error("[HTTPRequestController] Request failed:", error);
9730
+ logger$31.error("[HTTPRequestController] Request failed:", error);
9731
9731
  throw error;
9732
9732
  }
9733
9733
  }
@@ -9741,8 +9741,8 @@ var HTTPRequestController = class HTTPRequestController extends Destroyable {
9741
9741
  const credential = this.getCredential();
9742
9742
  if (credential.token) {
9743
9743
  headers.Authorization = `Bearer ${credential.token}`;
9744
- logger$29.debug("[HTTPRequestController] Using Bearer token auth, token length:", credential.token.length);
9745
- } else logger$29.warn("[HTTPRequestController] No credentials available for authentication");
9744
+ logger$31.debug("[HTTPRequestController] Using Bearer token auth, token length:", credential.token.length);
9745
+ } else logger$31.warn("[HTTPRequestController] No credentials available for authentication");
9746
9746
  return headers;
9747
9747
  }
9748
9748
  /**
@@ -9960,6 +9960,12 @@ const ICE_GATHERING_COMPLETE_TIMEOUT_MS = 1e4;
9960
9960
  const PEER_CONNECTION_RECOVERY_WAIT_MS = 5e3;
9961
9961
  /** Polling interval in ms while waiting for RTCPeerConnection.connectionState to transition. */
9962
9962
  const PEER_CONNECTION_RECOVERY_POLL_MS = 100;
9963
+ /** Polling interval for LocalAudioPipeline.level$ (ms). ~30fps is smooth for meters. */
9964
+ const AUDIO_LEVEL_POLL_INTERVAL_MS = 33;
9965
+ /** RMS level threshold (0..1) above which the local participant is considered speaking. */
9966
+ const VAD_THRESHOLD = .03;
9967
+ /** Hold window in ms below the threshold before speaking$ flips back to false. */
9968
+ const VAD_HOLD_MS = 250;
9963
9969
  /** Whether to persist device selections to storage by default. */
9964
9970
  const DEFAULT_PERSIST_DEVICE_SELECTION = true;
9965
9971
  /** Whether to auto-apply device changes to active calls by default. */
@@ -10008,7 +10014,7 @@ function fromMsToSec(milliseconds) {
10008
10014
 
10009
10015
  //#endregion
10010
10016
  //#region src/containers/PreferencesContainer.ts
10011
- const logger$28 = getLogger();
10017
+ const logger$30 = getLogger();
10012
10018
  var PreferencesContainer = class PreferencesContainer {
10013
10019
  static get instance() {
10014
10020
  this._instance ??= new PreferencesContainer();
@@ -10670,7 +10676,7 @@ var ClientPreferences = class {
10670
10676
  if (!this._storage) return;
10671
10677
  const data = collectStoredPreferences();
10672
10678
  this._storage.setItem(PREFERENCES_STORAGE_KEY, data, "local").catch((error) => {
10673
- logger$28.error(`[ClientPreferences] Failed to save preferences: ${String(error)}`);
10679
+ logger$30.error(`[ClientPreferences] Failed to save preferences: ${String(error)}`);
10674
10680
  });
10675
10681
  }
10676
10682
  /** Loads preferences from storage and applies them to the container. */
@@ -10679,7 +10685,7 @@ var ClientPreferences = class {
10679
10685
  this._storage.getItem(PREFERENCES_STORAGE_KEY, "local").then((stored) => {
10680
10686
  if (stored) applyStoredPreferences(stored);
10681
10687
  }).catch((error) => {
10682
- logger$28.error(`[ClientPreferences] Failed to load preferences: ${String(error)}`);
10688
+ logger$30.error(`[ClientPreferences] Failed to load preferences: ${String(error)}`);
10683
10689
  });
10684
10690
  }
10685
10691
  };
@@ -10700,8 +10706,8 @@ function toError(value) {
10700
10706
 
10701
10707
  //#endregion
10702
10708
  //#region src/controllers/NavigatorDeviceController.ts
10703
- var import_cjs$27 = require_cjs();
10704
- const logger$27 = getLogger();
10709
+ var import_cjs$29 = require_cjs();
10710
+ const logger$29 = getLogger();
10705
10711
  /** Maps a device kind to its storage key. */
10706
10712
  const DEVICE_STORAGE_KEYS = {
10707
10713
  audioinput: DEVICE_STORAGE_KEY_AUDIO_INPUT,
@@ -10723,7 +10729,7 @@ var NavigatorDeviceController = class extends Destroyable {
10723
10729
  super();
10724
10730
  this.webRTCApiProvider = webRTCApiProvider;
10725
10731
  this.deviceChangeHandler = () => {
10726
- logger$27.debug("[DeviceController] Device change detected");
10732
+ logger$29.debug("[DeviceController] Device change detected");
10727
10733
  this.enumerateDevices();
10728
10734
  };
10729
10735
  this._devicesState$ = this.createBehaviorSubject(initialDevicesState);
@@ -10760,17 +10766,17 @@ var NavigatorDeviceController = class extends Destroyable {
10760
10766
  return {};
10761
10767
  }
10762
10768
  get errors$() {
10763
- return this.cachedObservable("errors$", () => this._errors$.asObservable().pipe((0, import_cjs$27.takeUntil)(this.destroyed$)));
10769
+ return this.cachedObservable("errors$", () => this._errors$.asObservable().pipe((0, import_cjs$29.takeUntil)(this.destroyed$)));
10764
10770
  }
10765
10771
  /** Observable that emits when the SDK auto-switches a device. */
10766
10772
  get deviceRecovered$() {
10767
- return this._deviceRecovered$.asObservable().pipe((0, import_cjs$27.takeUntil)(this.destroyed$));
10773
+ return this._deviceRecovered$.asObservable().pipe((0, import_cjs$29.takeUntil)(this.destroyed$));
10768
10774
  }
10769
10775
  get videoInputDisabled$() {
10770
- return this.cachedObservable("videoInputDisabled$", () => this._videoInputDisabled$.asObservable().pipe((0, import_cjs$27.distinctUntilChanged)(), (0, import_cjs$27.takeUntil)(this.destroyed$)));
10776
+ return this.cachedObservable("videoInputDisabled$", () => this._videoInputDisabled$.asObservable().pipe((0, import_cjs$29.distinctUntilChanged)(), (0, import_cjs$29.takeUntil)(this.destroyed$)));
10771
10777
  }
10772
10778
  get audioInputDisabled$() {
10773
- return this.cachedObservable("audioInputDisabled$", () => this._audioInputDisabled$.asObservable().pipe((0, import_cjs$27.distinctUntilChanged)(), (0, import_cjs$27.takeUntil)(this.destroyed$)));
10779
+ return this.cachedObservable("audioInputDisabled$", () => this._audioInputDisabled$.asObservable().pipe((0, import_cjs$29.distinctUntilChanged)(), (0, import_cjs$29.takeUntil)(this.destroyed$)));
10774
10780
  }
10775
10781
  get videoInputDisabled() {
10776
10782
  return this._videoInputDisabled$.value;
@@ -10779,22 +10785,22 @@ var NavigatorDeviceController = class extends Destroyable {
10779
10785
  return this._audioInputDisabled$.value;
10780
10786
  }
10781
10787
  get audioInputDevices$() {
10782
- 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$)));
10788
+ 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$)));
10783
10789
  }
10784
10790
  get audioOutputDevices$() {
10785
- 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$)));
10791
+ 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$)));
10786
10792
  }
10787
10793
  get videoInputDevices$() {
10788
- 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$)));
10794
+ 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$)));
10789
10795
  }
10790
10796
  get selectedAudioInputDevice$() {
10791
- 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))));
10797
+ 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))));
10792
10798
  }
10793
10799
  get selectedAudioOutputDevice$() {
10794
- 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))));
10800
+ 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))));
10795
10801
  }
10796
10802
  get selectedVideoInputDevice$() {
10797
- 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))));
10803
+ 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))));
10798
10804
  }
10799
10805
  get selectedAudioInputDevice() {
10800
10806
  if (this._audioInputDisabled$.value) return null;
@@ -10869,7 +10875,7 @@ var NavigatorDeviceController = class extends Destroyable {
10869
10875
  if (device) this.persistDeviceSelection("audioinput", device);
10870
10876
  }
10871
10877
  selectVideoInputDevice(device) {
10872
- logger$27.debug("[DeviceController] Setting selected video input device:", device);
10878
+ logger$29.debug("[DeviceController] Setting selected video input device:", device);
10873
10879
  if (this._videoInputDisabled$.value && device) this._videoInputDisabled$.next(false);
10874
10880
  const previous = this._selectedDevicesState$.value.videoinput;
10875
10881
  if (previous && previous.deviceId !== device?.deviceId) this._deviceHistory.push("videoinput", previous);
@@ -10890,7 +10896,7 @@ var NavigatorDeviceController = class extends Destroyable {
10890
10896
  }
10891
10897
  init() {
10892
10898
  this.loadPersistedDevices();
10893
- this.subscribeTo(this._devicesState$.pipe((0, import_cjs$27.debounceTime)(PreferencesContainer.instance.deviceDebounceTime)), (devicesState) => {
10899
+ this.subscribeTo(this._devicesState$.pipe((0, import_cjs$29.debounceTime)(PreferencesContainer.instance.deviceDebounceTime)), (devicesState) => {
10894
10900
  const currentSelected = this._selectedDevicesState$.value;
10895
10901
  const newAudioInput = this._audioInputDisabled$.value ? null : this.resolveDevice("audioinput", devicesState.audioinput, currentSelected.audioinput, PreferencesContainer.instance.preferredAudioInput);
10896
10902
  const newAudioOutput = this.resolveDevice("audiooutput", devicesState.audiooutput, currentSelected.audiooutput, PreferencesContainer.instance.preferredAudioOutput);
@@ -10926,7 +10932,7 @@ var NavigatorDeviceController = class extends Destroyable {
10926
10932
  }
10927
10933
  const fromHistory = this._deviceHistory.findInHistory(kind, devices);
10928
10934
  if (fromHistory) {
10929
- logger$27.debug(`[DeviceController] Device disappeared, falling back to history: ${fromHistory.label}`);
10935
+ logger$29.debug(`[DeviceController] Device disappeared, falling back to history: ${fromHistory.label}`);
10930
10936
  this.emitDeviceRecovered(kind, selected, fromHistory, "device_disconnected");
10931
10937
  return fromHistory;
10932
10938
  }
@@ -10979,7 +10985,7 @@ var NavigatorDeviceController = class extends Destroyable {
10979
10985
  try {
10980
10986
  await this._storageManager.setItem(DEVICE_STORAGE_KEYS[kind], stored, "local");
10981
10987
  } catch (error) {
10982
- logger$27.error(`[DeviceController] Failed to persist device selection for ${kind}:`, error);
10988
+ logger$29.error(`[DeviceController] Failed to persist device selection for ${kind}:`, error);
10983
10989
  }
10984
10990
  }
10985
10991
  async loadPersistedDevices() {
@@ -10995,7 +11001,7 @@ var NavigatorDeviceController = class extends Destroyable {
10995
11001
  [kind]: stored
10996
11002
  };
10997
11003
  } catch (error) {
10998
- logger$27.error(`[DeviceController] Failed to load persisted device for ${kind}:`, error);
11004
+ logger$29.error(`[DeviceController] Failed to load persisted device for ${kind}:`, error);
10999
11005
  }
11000
11006
  }
11001
11007
  /** Clears device history, persisted selections, and re-enumerates devices. */
@@ -11012,8 +11018,8 @@ var NavigatorDeviceController = class extends Destroyable {
11012
11018
  enableDeviceMonitoring() {
11013
11019
  this.disableDeviceMonitoring();
11014
11020
  this.webRTCApiProvider.mediaDevices.addEventListener("devicechange", this.deviceChangeHandler);
11015
- if (PreferencesContainer.instance.devicePollingInterval > 0) this._devicesPoolingSubscription = (0, import_cjs$27.interval)(PreferencesContainer.instance.devicePollingInterval).subscribe(() => {
11016
- logger$27.debug("[DeviceController] Polling devices due to interval");
11021
+ if (PreferencesContainer.instance.devicePollingInterval > 0) this._devicesPoolingSubscription = (0, import_cjs$29.interval)(PreferencesContainer.instance.devicePollingInterval).subscribe(() => {
11022
+ logger$29.debug("[DeviceController] Polling devices due to interval");
11017
11023
  this.enumerateDevices();
11018
11024
  });
11019
11025
  this.enumerateDevices();
@@ -11039,13 +11045,13 @@ var NavigatorDeviceController = class extends Destroyable {
11039
11045
  videoinput: []
11040
11046
  });
11041
11047
  this._devicesState$.next(devicesByKind);
11042
- logger$27.debug("[DeviceController] Devices enumerated:", {
11048
+ logger$29.debug("[DeviceController] Devices enumerated:", {
11043
11049
  audioInputs: devicesByKind.audioinput.length,
11044
11050
  audioOutputs: devicesByKind.audiooutput.length,
11045
11051
  videoInputs: devicesByKind.videoinput.length
11046
11052
  });
11047
11053
  } catch (error) {
11048
- logger$27.error("[DeviceController] Failed to enumerate devices:", error);
11054
+ logger$29.error("[DeviceController] Failed to enumerate devices:", error);
11049
11055
  this._errors$.next(toError(error));
11050
11056
  }
11051
11057
  }
@@ -11061,7 +11067,7 @@ var NavigatorDeviceController = class extends Destroyable {
11061
11067
  stream.getTracks().forEach((t) => t.stop());
11062
11068
  return capabilities;
11063
11069
  } catch (error) {
11064
- logger$27.error("[DeviceController] Failed to get device capabilities:", error);
11070
+ logger$29.error("[DeviceController] Failed to get device capabilities:", error);
11065
11071
  this._errors$.next(toError(error));
11066
11072
  throw error;
11067
11073
  }
@@ -11312,7 +11318,7 @@ var DependencyContainer = class {
11312
11318
 
11313
11319
  //#endregion
11314
11320
  //#region src/controllers/CryptoController.ts
11315
- const logger$26 = getLogger();
11321
+ const logger$28 = getLogger();
11316
11322
  const DPOP_DB_NAME = "sw-dpop";
11317
11323
  const DPOP_DB_VERSION = 1;
11318
11324
  const DPOP_STORE_NAME = "keys";
@@ -11371,7 +11377,7 @@ async function loadKeyPairFromDB() {
11371
11377
  tx.oncomplete = () => db.close();
11372
11378
  });
11373
11379
  } catch (error) {
11374
- logger$26.warn("[DPoP] Failed to load key pair from IndexedDB:", error);
11380
+ logger$28.warn("[DPoP] Failed to load key pair from IndexedDB:", error);
11375
11381
  return null;
11376
11382
  }
11377
11383
  }
@@ -11391,7 +11397,7 @@ async function saveKeyPairToDB(keyPair) {
11391
11397
  };
11392
11398
  });
11393
11399
  } catch (error) {
11394
- logger$26.warn("[DPoP] Failed to save key pair to IndexedDB:", error);
11400
+ logger$28.warn("[DPoP] Failed to save key pair to IndexedDB:", error);
11395
11401
  }
11396
11402
  }
11397
11403
  async function deleteKeyPairFromDB() {
@@ -11410,7 +11416,7 @@ async function deleteKeyPairFromDB() {
11410
11416
  };
11411
11417
  });
11412
11418
  } catch (error) {
11413
- logger$26.warn("[DPoP] Failed to delete key pair from IndexedDB:", error);
11419
+ logger$28.warn("[DPoP] Failed to delete key pair from IndexedDB:", error);
11414
11420
  }
11415
11421
  }
11416
11422
  /**
@@ -11470,13 +11476,13 @@ var CryptoController = class {
11470
11476
  this._publicJwk = await crypto.subtle.exportKey("jwk", stored.publicKey);
11471
11477
  this._fingerprint = await computeJwkThumbprint(this._publicJwk);
11472
11478
  this._initialized = true;
11473
- logger$26.debug("[DPoP] Key pair restored from IndexedDB, fingerprint:", this._fingerprint);
11479
+ logger$28.debug("[DPoP] Key pair restored from IndexedDB, fingerprint:", this._fingerprint);
11474
11480
  return this._fingerprint;
11475
11481
  } catch (error) {
11476
- logger$26.warn("[DPoP] Stored key pair unusable, generating new one:", error);
11482
+ logger$28.warn("[DPoP] Stored key pair unusable, generating new one:", error);
11477
11483
  await deleteKeyPairFromDB();
11478
11484
  }
11479
- logger$26.debug("[DPoP] Generating RSA key pair");
11485
+ logger$28.debug("[DPoP] Generating RSA key pair");
11480
11486
  this._keyPair = await crypto.subtle.generateKey({
11481
11487
  name: "RSASSA-PKCS1-v1_5",
11482
11488
  modulusLength: 2048,
@@ -11491,7 +11497,7 @@ var CryptoController = class {
11491
11497
  this._fingerprint = await computeJwkThumbprint(this._publicJwk);
11492
11498
  this._initialized = true;
11493
11499
  await saveKeyPairToDB(this._keyPair);
11494
- logger$26.debug("[DPoP] Key pair generated and persisted, fingerprint:", this._fingerprint);
11500
+ logger$28.debug("[DPoP] Key pair generated and persisted, fingerprint:", this._fingerprint);
11495
11501
  return this._fingerprint;
11496
11502
  }
11497
11503
  /**
@@ -11557,7 +11563,7 @@ var CryptoController = class {
11557
11563
  this._fingerprint = null;
11558
11564
  this._initialized = false;
11559
11565
  deleteKeyPairFromDB();
11560
- logger$26.debug("[DPoP] Controller destroyed");
11566
+ logger$28.debug("[DPoP] Controller destroyed");
11561
11567
  }
11562
11568
  get publicJwk() {
11563
11569
  if (!this._publicJwk) throw new DPoPInitError("CryptoController not initialized. Call init() first.");
@@ -11580,8 +11586,8 @@ var CryptoController = class {
11580
11586
 
11581
11587
  //#endregion
11582
11588
  //#region src/controllers/NetworkMonitor.ts
11583
- var import_cjs$26 = require_cjs();
11584
- const logger$25 = getLogger();
11589
+ var import_cjs$28 = require_cjs();
11590
+ const logger$27 = getLogger();
11585
11591
  /**
11586
11592
  * Safely check whether we are running in a browser environment
11587
11593
  * with `window` and the relevant event targets.
@@ -11624,13 +11630,13 @@ var NetworkMonitor = class extends Destroyable {
11624
11630
  this.attachListeners();
11625
11631
  }
11626
11632
  get isOnline$() {
11627
- return this._isOnline$.asObservable().pipe((0, import_cjs$26.takeUntil)(this._destroyed$));
11633
+ return this._isOnline$.asObservable().pipe((0, import_cjs$28.takeUntil)(this._destroyed$));
11628
11634
  }
11629
11635
  get isOnline() {
11630
11636
  return this._isOnline$.value;
11631
11637
  }
11632
11638
  get networkChange$() {
11633
- return this._networkChange$.asObservable().pipe((0, import_cjs$26.takeUntil)(this._destroyed$));
11639
+ return this._networkChange$.asObservable().pipe((0, import_cjs$28.takeUntil)(this._destroyed$));
11634
11640
  }
11635
11641
  destroy() {
11636
11642
  this.removeListeners();
@@ -11638,7 +11644,7 @@ var NetworkMonitor = class extends Destroyable {
11638
11644
  }
11639
11645
  attachListeners() {
11640
11646
  if (!hasBrowserNetworkEvents()) {
11641
- logger$25.debug("NetworkMonitor: no browser environment detected, skipping event listeners");
11647
+ logger$27.debug("NetworkMonitor: no browser environment detected, skipping event listeners");
11642
11648
  return;
11643
11649
  }
11644
11650
  window.addEventListener("online", this._onOnline);
@@ -11646,7 +11652,7 @@ var NetworkMonitor = class extends Destroyable {
11646
11652
  const connection = getNetworkConnection();
11647
11653
  if (connection) connection.addEventListener("change", this._onConnectionChange);
11648
11654
  this._listenersAttached = true;
11649
- logger$25.debug("NetworkMonitor: event listeners attached");
11655
+ logger$27.debug("NetworkMonitor: event listeners attached");
11650
11656
  }
11651
11657
  removeListeners() {
11652
11658
  if (!this._listenersAttached) return;
@@ -11657,10 +11663,10 @@ var NetworkMonitor = class extends Destroyable {
11657
11663
  if (connection) connection.removeEventListener("change", this._onConnectionChange);
11658
11664
  }
11659
11665
  this._listenersAttached = false;
11660
- logger$25.debug("NetworkMonitor: event listeners removed");
11666
+ logger$27.debug("NetworkMonitor: event listeners removed");
11661
11667
  }
11662
11668
  handleOnline() {
11663
- logger$25.info("NetworkMonitor: browser went online");
11669
+ logger$27.info("NetworkMonitor: browser went online");
11664
11670
  this._isOnline$.next(true);
11665
11671
  this._networkChange$.next({
11666
11672
  type: "online",
@@ -11669,7 +11675,7 @@ var NetworkMonitor = class extends Destroyable {
11669
11675
  });
11670
11676
  }
11671
11677
  handleOffline() {
11672
- logger$25.info("NetworkMonitor: browser went offline");
11678
+ logger$27.info("NetworkMonitor: browser went offline");
11673
11679
  this._isOnline$.next(false);
11674
11680
  this._networkChange$.next({
11675
11681
  type: "offline",
@@ -11678,7 +11684,7 @@ var NetworkMonitor = class extends Destroyable {
11678
11684
  }
11679
11685
  handleConnectionChange() {
11680
11686
  const networkType = getNetworkType();
11681
- logger$25.info(`NetworkMonitor: connection changed — effectiveType=${networkType ?? "unknown"}`);
11687
+ logger$27.info(`NetworkMonitor: connection changed — effectiveType=${networkType ?? "unknown"}`);
11682
11688
  this._networkChange$.next({
11683
11689
  type: "connection_change",
11684
11690
  timestamp: Date.now(),
@@ -11793,8 +11799,8 @@ function getNavigatorMediaDevices() {
11793
11799
 
11794
11800
  //#endregion
11795
11801
  //#region src/controllers/PreflightRunner.ts
11796
- var import_cjs$25 = require_cjs();
11797
- const logger$24 = getLogger();
11802
+ var import_cjs$27 = require_cjs();
11803
+ const logger$26 = getLogger();
11798
11804
  const DEFAULT_MEDIA_TEST_DURATION_S = 10;
11799
11805
  const ICE_GATHERING_TIMEOUT_MS = 1e4;
11800
11806
  const SIGNALING_RTT_TIMEOUT_MS = 5e3;
@@ -11843,7 +11849,7 @@ var PreflightRunner = class extends Destroyable {
11843
11849
  if (!this._options.skipMediaTest) try {
11844
11850
  bandwidth = await this.testMediaBandwidth(destination);
11845
11851
  } catch (error) {
11846
- logger$24.warn("[PreflightRunner] Media bandwidth test failed:", error);
11852
+ logger$26.warn("[PreflightRunner] Media bandwidth test failed:", error);
11847
11853
  warnings.push("Media bandwidth test failed");
11848
11854
  }
11849
11855
  return {
@@ -11855,7 +11861,7 @@ var PreflightRunner = class extends Destroyable {
11855
11861
  warnings
11856
11862
  };
11857
11863
  } catch (error) {
11858
- logger$24.error("[PreflightRunner] Preflight test failed:", error);
11864
+ logger$26.error("[PreflightRunner] Preflight test failed:", error);
11859
11865
  throw new PreflightError("preflight", error instanceof Error ? error : new Error(String(error)));
11860
11866
  } finally {
11861
11867
  this.destroy();
@@ -11886,7 +11892,7 @@ var PreflightRunner = class extends Destroyable {
11886
11892
  if (track.kind === "video" && track.readyState === "live") videoWorking = true;
11887
11893
  }
11888
11894
  } catch (error) {
11889
- logger$24.warn("[PreflightRunner] Device test failed:", error);
11895
+ logger$26.warn("[PreflightRunner] Device test failed:", error);
11890
11896
  } finally {
11891
11897
  if (audioStream) audioStream.getTracks().forEach((t) => t.stop());
11892
11898
  }
@@ -11944,7 +11950,7 @@ var PreflightRunner = class extends Destroyable {
11944
11950
  rttMs
11945
11951
  };
11946
11952
  } catch (error) {
11947
- logger$24.warn("[PreflightRunner] ICE connectivity test failed:", error);
11953
+ logger$26.warn("[PreflightRunner] ICE connectivity test failed:", error);
11948
11954
  return {
11949
11955
  type: "failed",
11950
11956
  turnReachable: false,
@@ -11962,7 +11968,7 @@ var PreflightRunner = class extends Destroyable {
11962
11968
  audio: true,
11963
11969
  video: false
11964
11970
  });
11965
- 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)));
11971
+ 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)));
11966
11972
  const durationMs = this._options.duration * 1e3;
11967
11973
  await new Promise((resolve) => setTimeout(resolve, durationMs));
11968
11974
  const metrics = call.networkMetrics;
@@ -11991,8 +11997,8 @@ var PreflightRunner = class extends Destroyable {
11991
11997
 
11992
11998
  //#endregion
11993
11999
  //#region src/controllers/VisibilityController.ts
11994
- var import_cjs$24 = require_cjs();
11995
- const logger$23 = getLogger();
12000
+ var import_cjs$26 = require_cjs();
12001
+ const logger$25 = getLogger();
11996
12002
  /**
11997
12003
  * Checks whether the document visibility API is available.
11998
12004
  */
@@ -12029,15 +12035,15 @@ var VisibilityController = class extends Destroyable {
12029
12035
  this._boundHandler = this._handleVisibilityChange.bind(this);
12030
12036
  if (this._hasVisibilityApi) {
12031
12037
  document.addEventListener("visibilitychange", this._boundHandler);
12032
- logger$23.debug("VisibilityController: listening for visibilitychange events");
12033
- } else logger$23.debug("VisibilityController: document visibility API not available, defaulting to visible");
12038
+ logger$25.debug("VisibilityController: listening for visibilitychange events");
12039
+ } else logger$25.debug("VisibilityController: document visibility API not available, defaulting to visible");
12034
12040
  }
12035
12041
  /**
12036
12042
  * Observable of the current visibility state.
12037
12043
  * Emits 'visible' or 'hidden'. Always starts with the current state.
12038
12044
  */
12039
12045
  get visibility$() {
12040
- return this._visibility$.pipe((0, import_cjs$24.takeUntil)(this._destroyed$));
12046
+ return this._visibility$.pipe((0, import_cjs$26.takeUntil)(this._destroyed$));
12041
12047
  }
12042
12048
  /**
12043
12049
  * The current visibility state value.
@@ -12050,12 +12056,12 @@ var VisibilityController = class extends Destroyable {
12050
12056
  * Each event includes the previous state, new state, and timestamp.
12051
12057
  */
12052
12058
  get visibilityChange$() {
12053
- return this._visibilityChange$.pipe((0, import_cjs$24.takeUntil)(this._destroyed$));
12059
+ return this._visibilityChange$.pipe((0, import_cjs$26.takeUntil)(this._destroyed$));
12054
12060
  }
12055
12061
  destroy() {
12056
12062
  if (this._hasVisibilityApi) {
12057
12063
  document.removeEventListener("visibilitychange", this._boundHandler);
12058
- logger$23.debug("VisibilityController: removed visibilitychange listener");
12064
+ logger$25.debug("VisibilityController: removed visibilitychange listener");
12059
12065
  }
12060
12066
  super.destroy();
12061
12067
  }
@@ -12073,7 +12079,7 @@ var VisibilityController = class extends Destroyable {
12073
12079
  timestamp: Date.now()
12074
12080
  };
12075
12081
  this._visibilityChange$.next(changeEvent);
12076
- logger$23.debug("VisibilityController: visibility changed", {
12082
+ logger$25.debug("VisibilityController: visibility changed", {
12077
12083
  from: previousState,
12078
12084
  to: newState
12079
12085
  });
@@ -12082,13 +12088,13 @@ var VisibilityController = class extends Destroyable {
12082
12088
 
12083
12089
  //#endregion
12084
12090
  //#region src/behaviors/Fetchable.ts
12085
- var import_cjs$23 = require_cjs();
12091
+ var import_cjs$25 = require_cjs();
12086
12092
  var Fetchable = class extends Destroyable {
12087
12093
  constructor(fromPath, http) {
12088
12094
  super();
12089
12095
  this.fromPath = fromPath;
12090
12096
  this.http = http;
12091
- 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$));
12097
+ 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$));
12092
12098
  }
12093
12099
  async fetch() {
12094
12100
  const response = await this.http.request({
@@ -12323,7 +12329,7 @@ const RPCEventAckResponse = (id) => makeRPCResponse({
12323
12329
 
12324
12330
  //#endregion
12325
12331
  //#region src/managers/AttachManager.ts
12326
- const logger$22 = getLogger();
12332
+ const logger$24 = getLogger();
12327
12333
  var AttachManager = class {
12328
12334
  constructor(storage, deviceController, reconnectCallsTimeout, attachKey) {
12329
12335
  this.storage = storage;
@@ -12344,7 +12350,7 @@ var AttachManager = class {
12344
12350
  try {
12345
12351
  return await this.storage.getItem(this.attachKey) ?? {};
12346
12352
  } catch (error) {
12347
- logger$22.warn("[AttachManager] Failed to retrieve attached calls from storage", error);
12353
+ logger$24.warn("[AttachManager] Failed to retrieve attached calls from storage", error);
12348
12354
  return {};
12349
12355
  }
12350
12356
  }
@@ -12352,7 +12358,7 @@ var AttachManager = class {
12352
12358
  try {
12353
12359
  await this.storage.setItem(this.attachKey, attached);
12354
12360
  } catch (error) {
12355
- logger$22.warn("[AttachManager] Failed to write attached calls to storage", error);
12361
+ logger$24.warn("[AttachManager] Failed to write attached calls to storage", error);
12356
12362
  }
12357
12363
  }
12358
12364
  /**
@@ -12371,7 +12377,7 @@ var AttachManager = class {
12371
12377
  }
12372
12378
  async attach(call) {
12373
12379
  if (!call.to) {
12374
- logger$22.warn("[AttachManager] Skip attach for calls with no destination");
12380
+ logger$24.warn("[AttachManager] Skip attach for calls with no destination");
12375
12381
  return;
12376
12382
  }
12377
12383
  const destination = call.to;
@@ -12424,15 +12430,15 @@ var AttachManager = class {
12424
12430
  callId,
12425
12431
  ...options
12426
12432
  });
12427
- logger$22.info(`[AttachManager] Reattached call ${callId} (attempt ${attempt})`);
12433
+ logger$24.info(`[AttachManager] Reattached call ${callId} (attempt ${attempt})`);
12428
12434
  succeeded = true;
12429
12435
  break;
12430
12436
  } catch (error) {
12431
- logger$22.warn(`[AttachManager] Reattach attempt ${attempt}/3 failed for call ${callId}:`, error);
12437
+ logger$24.warn(`[AttachManager] Reattach attempt ${attempt}/3 failed for call ${callId}:`, error);
12432
12438
  if (attempt < 3) await new Promise((r) => setTimeout(r, (attempt + 1) * 1e3));
12433
12439
  }
12434
12440
  if (!succeeded) {
12435
- logger$22.warn(`[AttachManager] Reattach failed after 3 attempts for call ${callId}, removing reference`);
12441
+ logger$24.warn(`[AttachManager] Reattach failed after 3 attempts for call ${callId}, removing reference`);
12436
12442
  await this.detach({
12437
12443
  id: callId,
12438
12444
  mediaDirections: attachment.mediaDirections
@@ -13471,7 +13477,7 @@ function computeCapabilities(capabilities) {
13471
13477
 
13472
13478
  //#endregion
13473
13479
  //#region src/core/capabilities/SelfCapabilities.ts
13474
- var import_cjs$22 = require_cjs();
13480
+ var import_cjs$24 = require_cjs();
13475
13481
  /**
13476
13482
  * SelfCapabilities manages the capability state for the self participant.
13477
13483
  *
@@ -13507,7 +13513,7 @@ var SelfCapabilities = class extends Destroyable {
13507
13513
  }
13508
13514
  /** Observable for self member capabilities */
13509
13515
  get self$() {
13510
- return this.cachedObservable("self$", () => this._state$.pipe((0, import_cjs$22.map)((state) => state.self), (0, import_cjs$22.distinctUntilChanged)()));
13516
+ return this.cachedObservable("self$", () => this._state$.pipe((0, import_cjs$24.map)((state) => state.self), (0, import_cjs$24.distinctUntilChanged)()));
13511
13517
  }
13512
13518
  /** Current self member capabilities */
13513
13519
  get self() {
@@ -13515,7 +13521,7 @@ var SelfCapabilities = class extends Destroyable {
13515
13521
  }
13516
13522
  /** Observable for other member capabilities */
13517
13523
  get member$() {
13518
- return this.cachedObservable("member$", () => this._state$.pipe((0, import_cjs$22.map)((state) => state.member), (0, import_cjs$22.distinctUntilChanged)()));
13524
+ return this.cachedObservable("member$", () => this._state$.pipe((0, import_cjs$24.map)((state) => state.member), (0, import_cjs$24.distinctUntilChanged)()));
13519
13525
  }
13520
13526
  /** Current other member capabilities */
13521
13527
  get member() {
@@ -13523,7 +13529,7 @@ var SelfCapabilities = class extends Destroyable {
13523
13529
  }
13524
13530
  /** Observable for end call capability */
13525
13531
  get end$() {
13526
- return this.cachedObservable("end$", () => this._state$.pipe((0, import_cjs$22.map)((state) => state.end), (0, import_cjs$22.distinctUntilChanged)()));
13532
+ return this.cachedObservable("end$", () => this._state$.pipe((0, import_cjs$24.map)((state) => state.end), (0, import_cjs$24.distinctUntilChanged)()));
13527
13533
  }
13528
13534
  /** Current end call capability */
13529
13535
  get end() {
@@ -13531,7 +13537,7 @@ var SelfCapabilities = class extends Destroyable {
13531
13537
  }
13532
13538
  /** Observable for set layout capability */
13533
13539
  get setLayout$() {
13534
- return this.cachedObservable("setLayout$", () => this._state$.pipe((0, import_cjs$22.map)((state) => state.setLayout), (0, import_cjs$22.distinctUntilChanged)()));
13540
+ return this.cachedObservable("setLayout$", () => this._state$.pipe((0, import_cjs$24.map)((state) => state.setLayout), (0, import_cjs$24.distinctUntilChanged)()));
13535
13541
  }
13536
13542
  /** Current set layout capability */
13537
13543
  get setLayout() {
@@ -13539,7 +13545,7 @@ var SelfCapabilities = class extends Destroyable {
13539
13545
  }
13540
13546
  /** Observable for send digit capability */
13541
13547
  get sendDigit$() {
13542
- return this.cachedObservable("sendDigit$", () => this._state$.pipe((0, import_cjs$22.map)((state) => state.sendDigit), (0, import_cjs$22.distinctUntilChanged)()));
13548
+ return this.cachedObservable("sendDigit$", () => this._state$.pipe((0, import_cjs$24.map)((state) => state.sendDigit), (0, import_cjs$24.distinctUntilChanged)()));
13543
13549
  }
13544
13550
  /** Current send digit capability */
13545
13551
  get sendDigit() {
@@ -13547,7 +13553,7 @@ var SelfCapabilities = class extends Destroyable {
13547
13553
  }
13548
13554
  /** Observable for vmuted hide capability */
13549
13555
  get vmutedHide$() {
13550
- return this.cachedObservable("vmutedHide$", () => this._state$.pipe((0, import_cjs$22.map)((state) => state.vmutedHide), (0, import_cjs$22.distinctUntilChanged)()));
13556
+ return this.cachedObservable("vmutedHide$", () => this._state$.pipe((0, import_cjs$24.map)((state) => state.vmutedHide), (0, import_cjs$24.distinctUntilChanged)()));
13551
13557
  }
13552
13558
  /** Current vmuted hide capability */
13553
13559
  get vmutedHide() {
@@ -13555,7 +13561,7 @@ var SelfCapabilities = class extends Destroyable {
13555
13561
  }
13556
13562
  /** Observable for lock capability */
13557
13563
  get lock$() {
13558
- return this.cachedObservable("lock$", () => this._state$.pipe((0, import_cjs$22.map)((state) => state.lock), (0, import_cjs$22.distinctUntilChanged)()));
13564
+ return this.cachedObservable("lock$", () => this._state$.pipe((0, import_cjs$24.map)((state) => state.lock), (0, import_cjs$24.distinctUntilChanged)()));
13559
13565
  }
13560
13566
  /** Current lock capability */
13561
13567
  get lock() {
@@ -13563,7 +13569,7 @@ var SelfCapabilities = class extends Destroyable {
13563
13569
  }
13564
13570
  /** Observable for device capability */
13565
13571
  get device$() {
13566
- return this.cachedObservable("device$", () => this._state$.pipe((0, import_cjs$22.map)((state) => state.device), (0, import_cjs$22.distinctUntilChanged)()));
13572
+ return this.cachedObservable("device$", () => this._state$.pipe((0, import_cjs$24.map)((state) => state.device), (0, import_cjs$24.distinctUntilChanged)()));
13567
13573
  }
13568
13574
  /** Current device capability */
13569
13575
  get device() {
@@ -13571,7 +13577,7 @@ var SelfCapabilities = class extends Destroyable {
13571
13577
  }
13572
13578
  /** Observable for screenshare capability */
13573
13579
  get screenshare$() {
13574
- return this.cachedObservable("screenshare$", () => this._state$.pipe((0, import_cjs$22.map)((state) => state.screenshare), (0, import_cjs$22.distinctUntilChanged)()));
13580
+ return this.cachedObservable("screenshare$", () => this._state$.pipe((0, import_cjs$24.map)((state) => state.screenshare), (0, import_cjs$24.distinctUntilChanged)()));
13575
13581
  }
13576
13582
  /** Current screenshare capability */
13577
13583
  get screenshare() {
@@ -13599,7 +13605,7 @@ function toggleHandraiseMethod(is) {
13599
13605
 
13600
13606
  //#endregion
13601
13607
  //#region src/core/entities/Participant.ts
13602
- const logger$21 = getLogger();
13608
+ const logger$23 = getLogger();
13603
13609
  const initialState = {};
13604
13610
  /**
13605
13611
  * Represents a participant in a call.
@@ -13651,15 +13657,35 @@ var Participant = class extends Destroyable {
13651
13657
  get deaf$() {
13652
13658
  return this.cachedObservable("deaf$", () => this._state$.pipe((0, import_operators$1.map)((state) => state.deaf), (0, import_operators$1.distinctUntilChanged)()));
13653
13659
  }
13654
- /** Observable of the participant's microphone input volume. */
13660
+ /**
13661
+ * Observable of the participant's **server-side** microphone input volume
13662
+ * as reported by the mix engine. This is gain applied on the bridged audio
13663
+ * leg (FreeSWITCH channel read volume), NOT the local browser mic. For a
13664
+ * local PC mic control, see {@link Call.setLocalMicrophoneGain}.
13665
+ *
13666
+ * @see {@link setAudioInputVolume}
13667
+ */
13655
13668
  get inputVolume$() {
13656
13669
  return this.cachedObservable("inputVolume$", () => this._state$.pipe((0, import_operators$1.map)((state) => state.input_volume), (0, import_operators$1.distinctUntilChanged)()));
13657
13670
  }
13658
- /** Observable of the participant's speaker output volume. */
13671
+ /**
13672
+ * Observable of the participant's **server-side** speaker output volume as
13673
+ * reported by the mix engine (FreeSWITCH channel write volume). NOT the
13674
+ * local HTML `<audio>` element volume — set that on your own element.
13675
+ *
13676
+ * @see {@link setAudioOutputVolume}
13677
+ */
13659
13678
  get outputVolume$() {
13660
13679
  return this.cachedObservable("outputVolume$", () => this._state$.pipe((0, import_operators$1.map)((state) => state.output_volume), (0, import_operators$1.distinctUntilChanged)()));
13661
13680
  }
13662
- /** Observable of the microphone input sensitivity level. */
13681
+ /**
13682
+ * Observable of the **conference-only** microphone energy/gate sensitivity
13683
+ * level for this member. Routes through the conferencing mix engine and has
13684
+ * no effect on 1:1 WebRTC calls. Populated from `member.updated` events for
13685
+ * conference members.
13686
+ *
13687
+ * @see {@link setAudioInputSensitivity}
13688
+ */
13663
13689
  get inputSensitivity$() {
13664
13690
  return this.cachedObservable("inputSensitivity$", () => this._state$.pipe((0, import_operators$1.map)((state) => state.input_sensitivity), (0, import_operators$1.distinctUntilChanged)()));
13665
13691
  }
@@ -13747,15 +13773,25 @@ var Participant = class extends Destroyable {
13747
13773
  get deaf() {
13748
13774
  return this._state$.value.deaf ?? false;
13749
13775
  }
13750
- /** Current microphone input volume level, or `undefined` if not set. */
13776
+ /**
13777
+ * Current **server-side** microphone input volume as reported by the mix
13778
+ * engine, or `undefined` if not set. Not the local PC mic — see
13779
+ * {@link Call.setLocalMicrophoneGain} for browser-side control.
13780
+ */
13751
13781
  get inputVolume() {
13752
13782
  return this._state$.value.input_volume;
13753
13783
  }
13754
- /** Current speaker output volume level, or `undefined` if not set. */
13784
+ /**
13785
+ * Current **server-side** speaker output volume from the mix engine, or
13786
+ * `undefined` if not set. Not the local `<audio>` element volume.
13787
+ */
13755
13788
  get outputVolume() {
13756
13789
  return this._state$.value.output_volume;
13757
13790
  }
13758
- /** Current microphone input sensitivity level, or `undefined` if not set. */
13791
+ /**
13792
+ * Current **conference-only** microphone sensitivity/gate level, or
13793
+ * `undefined` if not set. Applies only to conference members.
13794
+ */
13759
13795
  get inputSensitivity() {
13760
13796
  return this._state$.value.input_sensitivity;
13761
13797
  }
@@ -13859,19 +13895,44 @@ var Participant = class extends Destroyable {
13859
13895
  async toggleLowbitrate() {
13860
13896
  throw new UnimplementedError();
13861
13897
  }
13862
- /** Sets the microphone input sensitivity level. */
13898
+ /**
13899
+ * Adjusts the **conference-only** microphone energy gate / sensitivity level
13900
+ * for this member. Routes through the conferencing mix engine
13901
+ * (`signalwire.conferencing member.set_input_sensitivity`) and has no effect
13902
+ * on 1:1 WebRTC calls — for those, use browser audio constraints via
13903
+ * {@link Call.setNoiseSuppression} / {@link Call.setAutoGainControl}.
13904
+ *
13905
+ * This is **not** a local PC mic gain control; it only changes how the
13906
+ * server-side mixer decides to open the mic gate on this participant.
13907
+ *
13908
+ * @param value - Sensitivity level as understood by the conference engine
13909
+ * (integer, larger values are more sensitive).
13910
+ */
13863
13911
  async setAudioInputSensitivity(value) {
13864
13912
  await this.executeMethod(this.id, "call.microphone.sensitivity.set", { sensitivity: value });
13865
13913
  }
13866
13914
  /**
13867
- * Sets the microphone input volume level.
13915
+ * Sets the **server-side** microphone volume on this participant's bridged
13916
+ * call leg. Applies a multiplier to the audio flowing through the mix
13917
+ * engine (FreeSWITCH channel read volume) — changes what other participants
13918
+ * hear, not what the local browser captures.
13919
+ *
13920
+ * For local PC mic gain, use {@link Call.setLocalMicrophoneGain} instead.
13921
+ *
13868
13922
  * @param value - Volume level (0-100).
13869
13923
  */
13870
13924
  async setAudioInputVolume(value) {
13871
13925
  await this.executeMethod(this.id, "call.microphone.volume.set", { volume: value });
13872
13926
  }
13873
13927
  /**
13874
- * Sets the speaker output volume level.
13928
+ * Sets the **server-side** speaker volume on this participant's bridged call
13929
+ * leg (FreeSWITCH channel write volume) — what this participant hears from
13930
+ * the mix before it reaches their client.
13931
+ *
13932
+ * For local playback volume (the `<audio>` element the consumer attaches
13933
+ * `remoteStream` to), set `audioElement.volume` directly in the consumer's
13934
+ * code.
13935
+ *
13875
13936
  * @param value - Volume level (0-100).
13876
13937
  */
13877
13938
  async setAudioOutputVolume(value) {
@@ -13977,7 +14038,7 @@ var SelfParticipant = class extends Participant {
13977
14038
  try {
13978
14039
  await this.vertoManager.addScreenMedia();
13979
14040
  } catch (error) {
13980
- logger$21.error("[Participant.startScreenShare] Screen share error:", error);
14041
+ logger$23.error("[Participant.startScreenShare] Screen share error:", error);
13981
14042
  }
13982
14043
  }
13983
14044
  /** Observable of the current screen share status. */
@@ -13997,7 +14058,7 @@ var SelfParticipant = class extends Participant {
13997
14058
  try {
13998
14059
  await this.vertoManager.addInputDevice(options);
13999
14060
  } catch (error) {
14000
- logger$21.error("[Participant.startScreenShare] Screen share error:", error);
14061
+ logger$23.error("[Participant.startScreenShare] Screen share error:", error);
14001
14062
  }
14002
14063
  }
14003
14064
  /** Removes an additional media input device by ID. */
@@ -14059,7 +14120,7 @@ var SelfParticipant = class extends Participant {
14059
14120
  */
14060
14121
  exitStudioModeIfActive() {
14061
14122
  if (this._studioAudio$.value) {
14062
- logger$21.debug("[SelfParticipant] Exiting studio audio mode due to individual flag toggle");
14123
+ logger$23.debug("[SelfParticipant] Exiting studio audio mode due to individual flag toggle");
14063
14124
  this._studioAudio$.next(false);
14064
14125
  }
14065
14126
  }
@@ -14083,7 +14144,7 @@ var SelfParticipant = class extends Participant {
14083
14144
  try {
14084
14145
  await super.mute();
14085
14146
  } catch (error) {
14086
- logger$21.warn("[Participant.toggleAudioInput] Server Error while muting audio input, proceeding with local toggle anyway", error);
14147
+ logger$23.warn("[Participant.toggleAudioInput] Server Error while muting audio input, proceeding with local toggle anyway", error);
14087
14148
  } finally {
14088
14149
  this.vertoManager.muteMainAudioInputDevice();
14089
14150
  }
@@ -14093,7 +14154,7 @@ var SelfParticipant = class extends Participant {
14093
14154
  try {
14094
14155
  await super.unmute();
14095
14156
  } catch (error) {
14096
- logger$21.warn("[Participant.toggleAudioInput] Server Error while unmuting audio input, proceeding with local toggle anyway", error);
14157
+ logger$23.warn("[Participant.toggleAudioInput] Server Error while unmuting audio input, proceeding with local toggle anyway", error);
14097
14158
  } finally {
14098
14159
  await this.vertoManager.unmuteMainAudioInputDevice();
14099
14160
  }
@@ -14103,7 +14164,7 @@ var SelfParticipant = class extends Participant {
14103
14164
  try {
14104
14165
  await super.muteVideo();
14105
14166
  } catch (error) {
14106
- logger$21.warn("[Participant.toggleVideoInput] Server Error while muting video input, proceeding with local toggle anyway", error);
14167
+ logger$23.warn("[Participant.toggleVideoInput] Server Error while muting video input, proceeding with local toggle anyway", error);
14107
14168
  } finally {
14108
14169
  this.vertoManager.muteMainVideoInputDevice();
14109
14170
  }
@@ -14113,7 +14174,7 @@ var SelfParticipant = class extends Participant {
14113
14174
  try {
14114
14175
  await super.unmuteVideo();
14115
14176
  } catch (error) {
14116
- logger$21.warn("[Participant.toggleVideoInput] Server Error while unmuting video input, proceeding with local toggle anyway", error);
14177
+ logger$23.warn("[Participant.toggleVideoInput] Server Error while unmuting video input, proceeding with local toggle anyway", error);
14117
14178
  } finally {
14118
14179
  await this.vertoManager.unmuteMainVideoInputDevice();
14119
14180
  }
@@ -14207,7 +14268,7 @@ function isLayoutChangedPayload(value) {
14207
14268
 
14208
14269
  //#endregion
14209
14270
  //#region src/operators/filterNull.ts
14210
- var import_cjs$21 = require_cjs();
14271
+ var import_cjs$23 = require_cjs();
14211
14272
  /**
14212
14273
  * RxJS operator that filters out `null` and `undefined` values with type narrowing.
14213
14274
  *
@@ -14219,7 +14280,7 @@ var import_cjs$21 = require_cjs();
14219
14280
  * ```
14220
14281
  */
14221
14282
  function filterNull() {
14222
- return (0, import_cjs$21.filter)((value) => value != null);
14283
+ return (0, import_cjs$23.filter)((value) => value != null);
14223
14284
  }
14224
14285
 
14225
14286
  //#endregion
@@ -14234,7 +14295,7 @@ const getValueFrom = (obj, path, defaultValue) => {
14234
14295
 
14235
14296
  //#endregion
14236
14297
  //#region src/operators/filterEventAs.ts
14237
- var import_cjs$20 = require_cjs();
14298
+ var import_cjs$22 = require_cjs();
14238
14299
  var import_operators = require_operators();
14239
14300
  /**
14240
14301
  * RxJS operator that filters events based on a predicate and maps matching events.
@@ -14268,7 +14329,7 @@ var import_operators = require_operators();
14268
14329
  * ```
14269
14330
  */
14270
14331
  function ifIsMap(predicate, mapFn) {
14271
- return (0, import_cjs$20.pipe)((0, import_operators.filter)(predicate), (0, import_operators.map)(mapFn));
14332
+ return (0, import_cjs$22.pipe)((0, import_operators.filter)(predicate), (0, import_operators.map)(mapFn));
14272
14333
  }
14273
14334
  /**
14274
14335
  * Generic RxJS operator that filters events using a type guard and extracts a property.
@@ -14310,38 +14371,38 @@ function ifIsMap(predicate, mapFn) {
14310
14371
  * ```
14311
14372
  */
14312
14373
  function filterAs(predicate, resultPath) {
14313
- return (0, import_cjs$20.pipe)(ifIsMap(predicate, (event) => {
14374
+ return (0, import_cjs$22.pipe)(ifIsMap(predicate, (event) => {
14314
14375
  return getValueFrom(event, resultPath);
14315
14376
  }), (0, import_operators.filter)((value) => value !== void 0));
14316
14377
  }
14317
14378
 
14318
14379
  //#endregion
14319
14380
  //#region src/operators/throwOnRPCError.ts
14320
- var import_cjs$19 = require_cjs();
14321
- const logger$20 = getLogger();
14381
+ var import_cjs$21 = require_cjs();
14382
+ const logger$22 = getLogger();
14322
14383
  /**
14323
14384
  * RxJS operator that throws a {@link JSONRPCError} when the RPC response contains an error.
14324
14385
  * Passes successful responses through unchanged.
14325
14386
  */
14326
14387
  function throwOnRPCError() {
14327
- return (0, import_cjs$19.map)((response) => {
14388
+ return (0, import_cjs$21.map)((response) => {
14328
14389
  if (response.error) {
14329
- logger$20.error("[throwOnRPCError] RPC error response:", {
14390
+ logger$22.error("[throwOnRPCError] RPC error response:", {
14330
14391
  code: response.error.code,
14331
14392
  message: response.error.message,
14332
14393
  data: response.error.data
14333
14394
  });
14334
14395
  throw new JSONRPCError(response.error.code, response.error.message, response.error.data);
14335
14396
  }
14336
- logger$20.debug("[throwOnRPCError] RPC successful response:", response);
14397
+ logger$22.debug("[throwOnRPCError] RPC successful response:", response);
14337
14398
  return response;
14338
14399
  });
14339
14400
  }
14340
14401
 
14341
14402
  //#endregion
14342
14403
  //#region src/managers/CallEventsManager.ts
14343
- var import_cjs$18 = require_cjs();
14344
- const logger$19 = getLogger();
14404
+ var import_cjs$20 = require_cjs();
14405
+ const logger$21 = getLogger();
14345
14406
  const initialSessionState = {};
14346
14407
  /** @internal */
14347
14408
  var CallEventsManager = class extends Destroyable {
@@ -14357,7 +14418,7 @@ var CallEventsManager = class extends Destroyable {
14357
14418
  this.initSubscriptions();
14358
14419
  }
14359
14420
  get participants$() {
14360
- return this.cachedObservable("participants$", () => this._participants$.asObservable().pipe((0, import_cjs$18.map)((participantsRecord) => Object.values(participantsRecord))));
14421
+ return this.cachedObservable("participants$", () => this._participants$.asObservable().pipe((0, import_cjs$20.map)((participantsRecord) => Object.values(participantsRecord))));
14361
14422
  }
14362
14423
  get participants() {
14363
14424
  return Object.values(this._participants$.value);
@@ -14375,40 +14436,40 @@ var CallEventsManager = class extends Destroyable {
14375
14436
  return this.callIds.has(callId);
14376
14437
  }
14377
14438
  get recording$() {
14378
- return this.cachedObservable("recording$", () => this._sessionState$.pipe((0, import_cjs$18.map)((state) => state.recording), (0, import_cjs$18.distinctUntilChanged)(), filterNull()));
14439
+ return this.cachedObservable("recording$", () => this._sessionState$.pipe((0, import_cjs$20.map)((state) => state.recording), (0, import_cjs$20.distinctUntilChanged)(), filterNull()));
14379
14440
  }
14380
14441
  get recordings$() {
14381
- return this.cachedObservable("recordings$", () => this._sessionState$.pipe((0, import_cjs$18.map)((state) => state.recordings), (0, import_cjs$18.distinctUntilChanged)(), filterNull()));
14442
+ return this.cachedObservable("recordings$", () => this._sessionState$.pipe((0, import_cjs$20.map)((state) => state.recordings), (0, import_cjs$20.distinctUntilChanged)(), filterNull()));
14382
14443
  }
14383
14444
  get streaming$() {
14384
- return this.cachedObservable("streaming$", () => this._sessionState$.pipe((0, import_cjs$18.map)((state) => state.streaming), (0, import_cjs$18.distinctUntilChanged)(), filterNull()));
14445
+ return this.cachedObservable("streaming$", () => this._sessionState$.pipe((0, import_cjs$20.map)((state) => state.streaming), (0, import_cjs$20.distinctUntilChanged)(), filterNull()));
14385
14446
  }
14386
14447
  get streams$() {
14387
- return this.cachedObservable("streams$", () => this._sessionState$.pipe((0, import_cjs$18.map)((state) => state.streams), (0, import_cjs$18.distinctUntilChanged)(), filterNull()));
14448
+ return this.cachedObservable("streams$", () => this._sessionState$.pipe((0, import_cjs$20.map)((state) => state.streams), (0, import_cjs$20.distinctUntilChanged)(), filterNull()));
14388
14449
  }
14389
14450
  get playbacks$() {
14390
- return this.cachedObservable("playbacks$", () => this._sessionState$.pipe((0, import_cjs$18.map)((state) => state.playbacks), (0, import_cjs$18.distinctUntilChanged)(), filterNull()));
14451
+ return this.cachedObservable("playbacks$", () => this._sessionState$.pipe((0, import_cjs$20.map)((state) => state.playbacks), (0, import_cjs$20.distinctUntilChanged)(), filterNull()));
14391
14452
  }
14392
14453
  get raiseHandPriority$() {
14393
- return this.cachedObservable("raiseHandPriority$", () => this._sessionState$.pipe((0, import_cjs$18.map)((state) => state.prioritize_handraise), (0, import_cjs$18.distinctUntilChanged)(), filterNull()));
14454
+ return this.cachedObservable("raiseHandPriority$", () => this._sessionState$.pipe((0, import_cjs$20.map)((state) => state.prioritize_handraise), (0, import_cjs$20.distinctUntilChanged)(), filterNull()));
14394
14455
  }
14395
14456
  get locked$() {
14396
- return this.cachedObservable("locked$", () => this._sessionState$.pipe((0, import_cjs$18.map)((state) => state.locked), (0, import_cjs$18.distinctUntilChanged)(), filterNull()));
14457
+ return this.cachedObservable("locked$", () => this._sessionState$.pipe((0, import_cjs$20.map)((state) => state.locked), (0, import_cjs$20.distinctUntilChanged)(), filterNull()));
14397
14458
  }
14398
14459
  get meta$() {
14399
- return this.cachedObservable("meta$", () => this._sessionState$.pipe((0, import_cjs$18.map)((state) => state.meta), (0, import_cjs$18.distinctUntilChanged)(), filterNull()));
14460
+ return this.cachedObservable("meta$", () => this._sessionState$.pipe((0, import_cjs$20.map)((state) => state.meta), (0, import_cjs$20.distinctUntilChanged)(), filterNull()));
14400
14461
  }
14401
14462
  get capabilities$() {
14402
- return this.cachedObservable("capabilities$", () => this._sessionState$.pipe((0, import_cjs$18.map)((state) => state.capabilities), (0, import_cjs$18.distinctUntilChanged)(), filterNull()));
14463
+ return this.cachedObservable("capabilities$", () => this._sessionState$.pipe((0, import_cjs$20.map)((state) => state.capabilities), (0, import_cjs$20.distinctUntilChanged)(), filterNull()));
14403
14464
  }
14404
14465
  get layout$() {
14405
- return this.cachedObservable("layout$", () => this._sessionState$.pipe((0, import_cjs$18.map)((state) => state.layout_name), (0, import_cjs$18.distinctUntilChanged)(), filterNull()));
14466
+ return this.cachedObservable("layout$", () => this._sessionState$.pipe((0, import_cjs$20.map)((state) => state.layout_name), (0, import_cjs$20.distinctUntilChanged)(), filterNull()));
14406
14467
  }
14407
14468
  get layouts$() {
14408
- return this.cachedObservable("layouts$", () => this._sessionState$.pipe((0, import_cjs$18.map)((state) => state.layouts), (0, import_cjs$18.distinctUntilChanged)(), filterNull()));
14469
+ return this.cachedObservable("layouts$", () => this._sessionState$.pipe((0, import_cjs$20.map)((state) => state.layouts), (0, import_cjs$20.distinctUntilChanged)(), filterNull()));
14409
14470
  }
14410
14471
  get layoutLayers$() {
14411
- return this.cachedObservable("layoutLayers$", () => this._sessionState$.pipe((0, import_cjs$18.map)((state) => state.layout_layers), (0, import_cjs$18.distinctUntilChanged)(), filterNull()));
14472
+ return this.cachedObservable("layoutLayers$", () => this._sessionState$.pipe((0, import_cjs$20.map)((state) => state.layout_layers), (0, import_cjs$20.distinctUntilChanged)(), filterNull()));
14412
14473
  }
14413
14474
  get self() {
14414
14475
  return this._self$.value;
@@ -14445,7 +14506,7 @@ var CallEventsManager = class extends Destroyable {
14445
14506
  }
14446
14507
  initSubscriptions() {
14447
14508
  this.subscribeTo(this.callJoinedEvent$, (callJoinedEvent) => {
14448
- logger$19.debug("[CallEventsManager] Handling call.joined event for call/session IDs:", {
14509
+ logger$21.debug("[CallEventsManager] Handling call.joined event for call/session IDs:", {
14449
14510
  callId: callJoinedEvent.call_id,
14450
14511
  roomSessionId: callJoinedEvent.room_session_id
14451
14512
  });
@@ -14472,19 +14533,19 @@ var CallEventsManager = class extends Destroyable {
14472
14533
  if (this._self$.value?.capabilities.setLayout) this.updateLayouts();
14473
14534
  });
14474
14535
  this.subscribeTo(this.memberUpdates$, (member) => {
14475
- logger$19.debug("[CallEventsManager] Handling member update event for member ID:", member);
14536
+ logger$21.debug("[CallEventsManager] Handling member update event for member ID:", member);
14476
14537
  this.upsertParticipant(member);
14477
14538
  });
14478
14539
  this.subscribeTo(this.webRtcCallSession.memberLeft$, (memberLeftEvent) => {
14479
- logger$19.debug("[CallEventsManager] Handling member.left event for member ID:", memberLeftEvent.member.member_id);
14540
+ logger$21.debug("[CallEventsManager] Handling member.left event for member ID:", memberLeftEvent.member.member_id);
14480
14541
  const participants = { ...this._participants$.value };
14481
14542
  if (memberLeftEvent.member.member_id in participants) {
14482
14543
  delete participants[memberLeftEvent.member.member_id];
14483
14544
  this._participants$.next(participants);
14484
- } else logger$19.warn(`[CallEventsManager] Received member.left event for unknown member ID: ${memberLeftEvent.member.member_id}`);
14545
+ } else logger$21.warn(`[CallEventsManager] Received member.left event for unknown member ID: ${memberLeftEvent.member.member_id}`);
14485
14546
  });
14486
14547
  this.subscribeTo(this.webRtcCallSession.callUpdated$, (callUpdatedEvent) => {
14487
- logger$19.debug("[CallEventsManager] Handling call.updated event:", callUpdatedEvent);
14548
+ logger$21.debug("[CallEventsManager] Handling call.updated event:", callUpdatedEvent);
14488
14549
  const roomSession = callUpdatedEvent.room_session;
14489
14550
  this._sessionState$.next({
14490
14551
  ...this._sessionState$.value,
@@ -14499,7 +14560,7 @@ var CallEventsManager = class extends Destroyable {
14499
14560
  });
14500
14561
  });
14501
14562
  this.subscribeTo(this.layoutChangedEvent$, (layoutChangedEvent) => {
14502
- logger$19.debug("[CallEventsManager] Handling layout.changed event:", layoutChangedEvent);
14563
+ logger$21.debug("[CallEventsManager] Handling layout.changed event:", layoutChangedEvent);
14503
14564
  this._sessionState$.next({
14504
14565
  ...this._sessionState$.value,
14505
14566
  layout_name: layoutChangedEvent.id,
@@ -14509,10 +14570,10 @@ var CallEventsManager = class extends Destroyable {
14509
14570
  });
14510
14571
  }
14511
14572
  updateParticipantPositions(layoutChangedEvent) {
14512
- 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.");
14573
+ 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.");
14513
14574
  layoutChangedEvent.layers.filter((layer) => !!layer.member_id).filter((layer) => {
14514
14575
  if (!(layer.member_id in this._participants$.value)) {
14515
- logger$19.warn(`[CallEventsManager] Skipping layout layer for unknown member_id: ${layer.member_id}`);
14576
+ logger$21.warn(`[CallEventsManager] Skipping layout layer for unknown member_id: ${layer.member_id}`);
14516
14577
  return false;
14517
14578
  }
14518
14579
  return true;
@@ -14535,7 +14596,7 @@ var CallEventsManager = class extends Destroyable {
14535
14596
  layouts: response.result.layouts
14536
14597
  });
14537
14598
  }).catch((error) => {
14538
- logger$19.error("[CallEventsManager] Error fetching layouts:", error);
14599
+ logger$21.error("[CallEventsManager] Error fetching layouts:", error);
14539
14600
  });
14540
14601
  }
14541
14602
  updateParticipants(members) {
@@ -14551,7 +14612,7 @@ var CallEventsManager = class extends Destroyable {
14551
14612
  }
14552
14613
  const participant = this._participants$.value[member.member_id];
14553
14614
  const oldValue = participant.value;
14554
- logger$19.debug("[CallEventsManager] Updating participant:", member.member_id, {
14615
+ logger$21.debug("[CallEventsManager] Updating participant:", member.member_id, {
14555
14616
  oldValue,
14556
14617
  newValue: member
14557
14618
  });
@@ -14563,18 +14624,18 @@ var CallEventsManager = class extends Destroyable {
14563
14624
  this._participants$.next(this._participants$.value);
14564
14625
  }
14565
14626
  get callJoinedEvent$() {
14566
- return this.cachedObservable("callJoinedEvent$", () => this.webRtcCallSession.callEvent$.pipe((0, import_cjs$18.filter)(isCallJoinedPayload), (0, import_cjs$18.tap)((event) => {
14567
- logger$19.debug("[CallEventsManager] Call joined event:", event);
14627
+ return this.cachedObservable("callJoinedEvent$", () => this.webRtcCallSession.callEvent$.pipe((0, import_cjs$20.filter)(isCallJoinedPayload), (0, import_cjs$20.tap)((event) => {
14628
+ logger$21.debug("[CallEventsManager] Call joined event:", event);
14568
14629
  })));
14569
14630
  }
14570
14631
  get layoutChangedEvent$() {
14571
- return this.cachedObservable("layoutChangedEvent$", () => this.webRtcCallSession.callEvent$.pipe(filterAs(isLayoutChangedPayload, "layout"), (0, import_cjs$18.tap)((event) => {
14572
- logger$19.debug("[CallEventsManager] Layout changed event:", event);
14632
+ return this.cachedObservable("layoutChangedEvent$", () => this.webRtcCallSession.callEvent$.pipe(filterAs(isLayoutChangedPayload, "layout"), (0, import_cjs$20.tap)((event) => {
14633
+ logger$21.debug("[CallEventsManager] Layout changed event:", event);
14573
14634
  })));
14574
14635
  }
14575
14636
  get memberUpdates$() {
14576
- 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) => {
14577
- logger$19.debug("[CallEventsManager] Member update event:", event);
14637
+ 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) => {
14638
+ logger$21.debug("[CallEventsManager] Member update event:", event);
14578
14639
  })));
14579
14640
  }
14580
14641
  destroy() {
@@ -14830,8 +14891,8 @@ function appendStereoParams(fmtpLine, maxBitrate) {
14830
14891
 
14831
14892
  //#endregion
14832
14893
  //#region src/controllers/ICEGatheringController.ts
14833
- var import_cjs$17 = require_cjs();
14834
- const logger$18 = getLogger();
14894
+ var import_cjs$19 = require_cjs();
14895
+ const logger$20 = getLogger();
14835
14896
  var ICEGatheringController = class extends Destroyable {
14836
14897
  constructor(peerConnection, peerConnectionControllerNegotiating$, options = {}) {
14837
14898
  super();
@@ -14839,23 +14900,23 @@ var ICEGatheringController = class extends Destroyable {
14839
14900
  this.peerConnectionControllerNegotiating$ = peerConnectionControllerNegotiating$;
14840
14901
  this.onicegatheringstatechangeHandler = () => {
14841
14902
  const { iceGatheringState } = this.peerConnection;
14842
- logger$18.debug(`[ICEGatheringController] ICE gathering state changed to: ${iceGatheringState}`);
14903
+ logger$20.debug(`[ICEGatheringController] ICE gathering state changed to: ${iceGatheringState}`);
14843
14904
  if (iceGatheringState === "gathering") this._iceCandidatesState.next({
14844
14905
  state: "gathering",
14845
14906
  validSDP: false
14846
14907
  });
14847
14908
  };
14848
14909
  this.onicecandidateHandler = (event) => {
14849
- logger$18.debug("[ICEGatheringController] ICE candidate event received:", event.candidate);
14910
+ logger$20.debug("[ICEGatheringController] ICE candidate event received:", event.candidate);
14850
14911
  this.removeTimer("iceCandidateTimer");
14851
14912
  if (event.candidate) this.iceCandidateTimer = setTimeout(() => {
14852
14913
  if (this.peerConnection.iceGatheringState !== "complete") {
14853
- logger$18.warn("[ICEGatheringController] ICE candidate timeout, using current SDP");
14914
+ logger$20.warn("[ICEGatheringController] ICE candidate timeout, using current SDP");
14854
14915
  this.handleICECandidateTimeout();
14855
14916
  }
14856
14917
  }, this.iceCandidateTimeout);
14857
14918
  else {
14858
- logger$18.debug("[ICEGatheringController] ICE gathering completed: null candidate received");
14919
+ logger$20.debug("[ICEGatheringController] ICE gathering completed: null candidate received");
14859
14920
  this.removeTimer("iceGatheringTimer");
14860
14921
  this.handleICEGatheringComplete();
14861
14922
  }
@@ -14868,12 +14929,12 @@ var ICEGatheringController = class extends Destroyable {
14868
14929
  this.iceGatheringTimeout = options.iceGatheringTimeout ?? DEFAULT_ICE_GATHERING_TIMEOUT_MS;
14869
14930
  this.relayOnly = options.relayOnly ?? false;
14870
14931
  this.setupEventListeners();
14871
- this.subscribeTo(this.peerConnectionControllerNegotiating$.pipe((0, import_cjs$17.filter)((isNegotiating) => isNegotiating)), (isNegotiating) => {
14932
+ this.subscribeTo(this.peerConnectionControllerNegotiating$.pipe((0, import_cjs$19.filter)((isNegotiating) => isNegotiating)), (isNegotiating) => {
14872
14933
  if (isNegotiating) {
14873
14934
  this.setupEventListeners();
14874
14935
  this.iceGatheringTimer = setTimeout(() => {
14875
14936
  if (this.peerConnection.iceGatheringState !== "complete") {
14876
- logger$18.warn("[ICEGatheringController] ICE gathering timeout, using current SDP");
14937
+ logger$20.warn("[ICEGatheringController] ICE gathering timeout, using current SDP");
14877
14938
  this.handleICEGatheringTimeout();
14878
14939
  }
14879
14940
  }, this.iceGatheringTimeout);
@@ -14887,7 +14948,7 @@ var ICEGatheringController = class extends Destroyable {
14887
14948
  this.peerConnection.addEventListener("icegatheringstatechange", this.onicegatheringstatechangeHandler);
14888
14949
  }
14889
14950
  get iceCandidatesState$() {
14890
- 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));
14951
+ 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));
14891
14952
  }
14892
14953
  get hasValidLocalDescriptionSDP() {
14893
14954
  const sdp = this.peerConnection.localDescription?.sdp;
@@ -14900,9 +14961,9 @@ var ICEGatheringController = class extends Destroyable {
14900
14961
  this.relayOnly = value;
14901
14962
  }
14902
14963
  handleICEGatheringComplete() {
14903
- logger$18.debug("[ICEGatheringController] Handling ICE gathering complete");
14904
- logger$18.debug(`[ICEGatheringController] Checking ICE gathering state: ${this.peerConnection.iceGatheringState}`);
14905
- logger$18.debug("[ICEGatheringController] ICE gathering complete");
14964
+ logger$20.debug("[ICEGatheringController] Handling ICE gathering complete");
14965
+ logger$20.debug(`[ICEGatheringController] Checking ICE gathering state: ${this.peerConnection.iceGatheringState}`);
14966
+ logger$20.debug("[ICEGatheringController] ICE gathering complete");
14906
14967
  this._iceCandidatesState.next({
14907
14968
  state: "complete",
14908
14969
  validSDP: this.hasValidLocalDescriptionSDP
@@ -14918,21 +14979,21 @@ var ICEGatheringController = class extends Destroyable {
14918
14979
  this.removeTimer("iceGatheringTimer");
14919
14980
  const validSDP = this.hasValidLocalDescriptionSDP;
14920
14981
  if (validSDP) {
14921
- logger$18.debug("[ICEGatheringController] Local SDP is valid");
14982
+ logger$20.debug("[ICEGatheringController] Local SDP is valid");
14922
14983
  this._iceCandidatesState.next({
14923
14984
  state: "timeout",
14924
14985
  validSDP
14925
14986
  });
14926
14987
  this.stopGathering();
14927
- } else logger$18.debug("### ICE gathering timeout\n", this.peerConnection.localDescription?.sdp);
14988
+ } else logger$20.debug("### ICE gathering timeout\n", this.peerConnection.localDescription?.sdp);
14928
14989
  }
14929
14990
  handleICECandidateTimeout() {
14930
14991
  if (this.iceCandidateTimer) this.removeTimer("iceCandidateTimer");
14931
- logger$18.warn("[ICEGatheringController] ICE candidate timeout");
14992
+ logger$20.warn("[ICEGatheringController] ICE candidate timeout");
14932
14993
  const validSDP = this.hasValidLocalDescriptionSDP;
14933
14994
  if (!validSDP && !this.relayOnly) this.restartICEGatheringWithRelayOnly();
14934
14995
  else {
14935
- logger$18.debug("[ICEGatheringController] Using current SDP due to ICE candidate timeout");
14996
+ logger$20.debug("[ICEGatheringController] Using current SDP due to ICE candidate timeout");
14936
14997
  this._iceCandidatesState.next({
14937
14998
  state: "timeout",
14938
14999
  validSDP
@@ -14941,7 +15002,7 @@ var ICEGatheringController = class extends Destroyable {
14941
15002
  }
14942
15003
  }
14943
15004
  restartICEGatheringWithRelayOnly() {
14944
- logger$18.debug("[ICEGatheringController] Restarting ICE gathering with relay-only candidates");
15005
+ logger$20.debug("[ICEGatheringController] Restarting ICE gathering with relay-only candidates");
14945
15006
  this.relayOnly = true;
14946
15007
  this.peerConnection.setConfiguration({
14947
15008
  ...this.peerConnection.getConfiguration(),
@@ -14956,7 +15017,7 @@ var ICEGatheringController = class extends Destroyable {
14956
15017
  }
14957
15018
  }
14958
15019
  clearAllTimers() {
14959
- logger$18.debug("[ICEGatheringController] Clearing all timers");
15020
+ logger$20.debug("[ICEGatheringController] Clearing all timers");
14960
15021
  this.removeTimer("iceGatheringTimer");
14961
15022
  this.removeTimer("iceCandidateTimer");
14962
15023
  }
@@ -14965,17 +15026,181 @@ var ICEGatheringController = class extends Destroyable {
14965
15026
  this.peerConnection.removeEventListener("icecandidate", this.onicecandidateHandler);
14966
15027
  }
14967
15028
  destroy() {
14968
- logger$18.debug("[ICEGatheringController] Destroying ICEGatheringController");
15029
+ logger$20.debug("[ICEGatheringController] Destroying ICEGatheringController");
14969
15030
  this.clearAllTimers();
14970
15031
  this.removeEventListeners();
14971
15032
  super.destroy();
14972
15033
  }
14973
15034
  };
14974
15035
 
15036
+ //#endregion
15037
+ //#region src/controllers/LocalAudioPipeline.ts
15038
+ var import_cjs$18 = require_cjs();
15039
+ const logger$19 = getLogger();
15040
+ /**
15041
+ * Web Audio pipeline for the local microphone stream.
15042
+ *
15043
+ * Wraps the raw mic `MediaStreamTrack` in a graph of:
15044
+ *
15045
+ * ```
15046
+ * MediaStreamAudioSourceNode → GainNode → AnalyserNode → MediaStreamAudioDestinationNode
15047
+ * ```
15048
+ *
15049
+ * The {@link outputTrack} from the destination node is what callers should
15050
+ * attach to the `RTCRtpSender` in place of the raw mic track. The same
15051
+ * destination track is reused across input changes (device switch, mute /
15052
+ * unmute track replacement) so the sender reference stays stable — only the
15053
+ * source end of the graph is rebuilt.
15054
+ *
15055
+ * The pipeline owns a single {@link AudioContext}. Callers must invoke
15056
+ * {@link destroy} to release it when the call ends.
15057
+ */
15058
+ var LocalAudioPipeline = class extends Destroyable {
15059
+ constructor(options = {}) {
15060
+ super();
15061
+ this._inputSource = null;
15062
+ this._inputStream = null;
15063
+ this._lastSpokeAt = 0;
15064
+ this._gain$ = this.createBehaviorSubject(1);
15065
+ this._pttMultiplier = 1;
15066
+ this._audioContext = (options.audioContextFactory ?? (() => new AudioContext()))();
15067
+ this._gainNode = this._audioContext.createGain();
15068
+ this._analyser = this._audioContext.createAnalyser();
15069
+ this._analyser.fftSize = 2048;
15070
+ this._analyser.smoothingTimeConstant = .3;
15071
+ this._analyserBuffer = new Uint8Array(new ArrayBuffer(this._analyser.fftSize));
15072
+ this._destination = this._audioContext.createMediaStreamDestination();
15073
+ this._gainNode.connect(this._analyser);
15074
+ this._analyser.connect(this._destination);
15075
+ this._speakingThreshold = options.speakingThreshold ?? VAD_THRESHOLD;
15076
+ this._speakingHoldMs = options.speakingHoldMs ?? VAD_HOLD_MS;
15077
+ this._pollIntervalMs = options.pollIntervalMs ?? AUDIO_LEVEL_POLL_INTERVAL_MS;
15078
+ const initial = options.initialGain ?? 1;
15079
+ this._gain$.next(initial);
15080
+ this.applyEffectiveGain();
15081
+ }
15082
+ /** Observable of the current gain value (0..2). */
15083
+ get gain$() {
15084
+ return this._gain$.asObservable();
15085
+ }
15086
+ /** Current gain value (0..2). */
15087
+ get gain() {
15088
+ return this._gain$.value;
15089
+ }
15090
+ /**
15091
+ * Processed output track to attach to the RTCRtpSender. Stable reference
15092
+ * across input changes, so `sender.replaceTrack(pipeline.outputTrack)` only
15093
+ * needs to be called once.
15094
+ */
15095
+ get outputTrack() {
15096
+ const [track] = this._destination.stream.getAudioTracks();
15097
+ return track;
15098
+ }
15099
+ /**
15100
+ * Root-mean-square audio level of the input signal, 0..1. Emits on a fixed
15101
+ * interval (~30fps by default).
15102
+ */
15103
+ get level$() {
15104
+ return this.deferEmission((0, import_cjs$18.interval)(this._pollIntervalMs, import_cjs$18.animationFrameScheduler).pipe((0, import_cjs$18.map)(() => this.computeLevel())));
15105
+ }
15106
+ /**
15107
+ * Boolean VAD derived from {@link level$}. True while level ≥ threshold or
15108
+ * during the hold window after the last frame that crossed the threshold.
15109
+ */
15110
+ get speaking$() {
15111
+ return this.deferEmission(this.level$.pipe((0, import_cjs$18.map)((level) => this.evaluateSpeaking(level)), (0, import_cjs$18.distinctUntilChanged)()));
15112
+ }
15113
+ /**
15114
+ * Set gain multiplier applied to the input signal. 0 = silence,
15115
+ * 1 = unity, 2 = 2x. Values are clamped to [0, 2]. The effective gain on
15116
+ * the graph also respects the current PTT state.
15117
+ */
15118
+ setGain(value) {
15119
+ const clamped = Math.max(0, Math.min(2, value));
15120
+ this._gain$.next(clamped);
15121
+ this.applyEffectiveGain();
15122
+ }
15123
+ /**
15124
+ * Silence the graph when `active = false`, otherwise restore the configured
15125
+ * gain. Use this from a PTT handler: released → `false`, held → `true`.
15126
+ * Orthogonal to {@link setGain} — once PTT returns to active, the last
15127
+ * configured gain reappears.
15128
+ */
15129
+ setPTTActive(active) {
15130
+ this._pttMultiplier = active ? 1 : 0;
15131
+ this.applyEffectiveGain();
15132
+ }
15133
+ applyEffectiveGain() {
15134
+ this._gainNode.gain.value = this._gain$.value * this._pttMultiplier;
15135
+ }
15136
+ /**
15137
+ * Wire a new raw mic track as the pipeline's input. Replaces any previous
15138
+ * input source and reconnects the graph so {@link outputTrack} continues
15139
+ * to emit the processed audio. Pass `null` to disconnect the input (the
15140
+ * output track stays alive but emits silence).
15141
+ *
15142
+ * Also resumes the underlying AudioContext on attach — Chrome creates it
15143
+ * in a suspended state and the graph won't process (the destination
15144
+ * track emits silence) until resume() succeeds.
15145
+ */
15146
+ setInputTrack(track) {
15147
+ if (this._inputSource) {
15148
+ try {
15149
+ this._inputSource.disconnect();
15150
+ } catch (error) {
15151
+ logger$19.debug("[LocalAudioPipeline] input disconnect warning:", error);
15152
+ }
15153
+ this._inputSource = null;
15154
+ }
15155
+ if (this._inputStream) this._inputStream = null;
15156
+ if (!track) return;
15157
+ this._inputStream = new MediaStream([track]);
15158
+ this._inputSource = this._audioContext.createMediaStreamSource(this._inputStream);
15159
+ this._inputSource.connect(this._gainNode);
15160
+ if (this._audioContext.state === "suspended") this._audioContext.resume().catch((error) => {
15161
+ logger$19.warn("[LocalAudioPipeline] AudioContext resume failed:", error);
15162
+ });
15163
+ }
15164
+ destroy() {
15165
+ if (this._inputSource) {
15166
+ try {
15167
+ this._inputSource.disconnect();
15168
+ } catch {}
15169
+ this._inputSource = null;
15170
+ }
15171
+ try {
15172
+ this._gainNode.disconnect();
15173
+ this._analyser.disconnect();
15174
+ } catch {}
15175
+ this._audioContext.close().catch((error) => {
15176
+ logger$19.debug("[LocalAudioPipeline] audio context close warning:", error);
15177
+ });
15178
+ super.destroy();
15179
+ }
15180
+ computeLevel() {
15181
+ if (!this._inputSource) return 0;
15182
+ this._analyser.getByteTimeDomainData(this._analyserBuffer);
15183
+ let sum = 0;
15184
+ for (const sample$1 of this._analyserBuffer) {
15185
+ const normalized = (sample$1 - 128) / 128;
15186
+ sum += normalized * normalized;
15187
+ }
15188
+ return Math.sqrt(sum / this._analyserBuffer.length);
15189
+ }
15190
+ evaluateSpeaking(level) {
15191
+ const now = Date.now();
15192
+ if (level >= this._speakingThreshold) {
15193
+ this._lastSpokeAt = now;
15194
+ return true;
15195
+ }
15196
+ return now - this._lastSpokeAt < this._speakingHoldMs;
15197
+ }
15198
+ };
15199
+
14975
15200
  //#endregion
14976
15201
  //#region src/controllers/LocalStreamController.ts
14977
- var import_cjs$16 = require_cjs();
14978
- const logger$17 = getLogger();
15202
+ var import_cjs$17 = require_cjs();
15203
+ const logger$18 = getLogger();
14979
15204
  var LocalStreamController = class extends Destroyable {
14980
15205
  constructor(options) {
14981
15206
  super();
@@ -14989,16 +15214,16 @@ var LocalStreamController = class extends Destroyable {
14989
15214
  this._mediaTrackEnded$ = this.createSubject();
14990
15215
  }
14991
15216
  get localStream$() {
14992
- return this._localStream$.asObservable().pipe((0, import_cjs$16.takeUntil)(this.destroyed$));
15217
+ return this._localStream$.asObservable().pipe((0, import_cjs$17.takeUntil)(this.destroyed$));
14993
15218
  }
14994
15219
  get localAudioTracks$() {
14995
- return this._localAudioTracks$.asObservable().pipe((0, import_cjs$16.takeUntil)(this.destroyed$));
15220
+ return this._localAudioTracks$.asObservable().pipe((0, import_cjs$17.takeUntil)(this.destroyed$));
14996
15221
  }
14997
15222
  get localVideoTracks$() {
14998
- return this._localVideoTracks$.asObservable().pipe((0, import_cjs$16.takeUntil)(this.destroyed$));
15223
+ return this._localVideoTracks$.asObservable().pipe((0, import_cjs$17.takeUntil)(this.destroyed$));
14999
15224
  }
15000
15225
  get mediaTrackEnded$() {
15001
- return this._mediaTrackEnded$.asObservable().pipe((0, import_cjs$16.takeUntil)(this.destroyed$));
15226
+ return this._mediaTrackEnded$.asObservable().pipe((0, import_cjs$17.takeUntil)(this.destroyed$));
15002
15227
  }
15003
15228
  get localStream() {
15004
15229
  return this._localStream$.value;
@@ -15013,26 +15238,26 @@ var LocalStreamController = class extends Destroyable {
15013
15238
  * Build the local media stream based on the provided options.
15014
15239
  */
15015
15240
  async buildLocalStream() {
15016
- logger$17.debug("[LocalStreamController] Building local media stream.");
15241
+ logger$18.debug("[LocalStreamController] Building local media stream.");
15017
15242
  let stream;
15018
15243
  if (this.options.inputAudioStream ?? this.options.inputVideoStream) {
15019
15244
  const tracks = [...this.options.inputAudioStream?.getTracks() ?? [], ...this.options.inputVideoStream?.getTracks() ?? []];
15020
15245
  stream = new MediaStream(tracks);
15021
15246
  } else if (this.options.propose === "screenshare") {
15022
- logger$17.debug("[LocalStreamController] Requesting display media for screen sharing with audio:", Boolean(this.options.inputAudioDeviceConstraints));
15247
+ logger$18.debug("[LocalStreamController] Requesting display media for screen sharing with audio:", Boolean(this.options.inputAudioDeviceConstraints));
15023
15248
  stream = await this.options.getDisplayMedia({
15024
15249
  video: true,
15025
15250
  audio: Boolean(this.options.inputAudioDeviceConstraints)
15026
15251
  });
15027
- logger$17.debug("[LocalStreamController] Screen share media obtained:", stream);
15252
+ logger$18.debug("[LocalStreamController] Screen share media obtained:", stream);
15028
15253
  } else {
15029
15254
  const constraints = {
15030
15255
  audio: this.options.inputAudioDeviceConstraints,
15031
15256
  video: this.options.inputVideoDeviceConstraints
15032
15257
  };
15033
- logger$17.debug("[LocalStreamController] Requesting user media with constraints:", constraints);
15258
+ logger$18.debug("[LocalStreamController] Requesting user media with constraints:", constraints);
15034
15259
  stream = await this.options.getUserMedia(constraints);
15035
- logger$17.debug("[LocalStreamController] User media obtained:", stream);
15260
+ logger$18.debug("[LocalStreamController] User media obtained:", stream);
15036
15261
  }
15037
15262
  this._localStream$.next(stream);
15038
15263
  return stream;
@@ -15049,7 +15274,7 @@ var LocalStreamController = class extends Destroyable {
15049
15274
  this._localStream$.next(localStream);
15050
15275
  if (track.kind === "video") this._localVideoTracks$.next(localStream.getVideoTracks());
15051
15276
  else this._localAudioTracks$.next(localStream.getAudioTracks());
15052
- logger$17.debug(`[LocalStreamController] ${track.kind} track added:`, track.id);
15277
+ logger$18.debug(`[LocalStreamController] ${track.kind} track added:`, track.id);
15053
15278
  return localStream;
15054
15279
  }
15055
15280
  /**
@@ -15061,7 +15286,7 @@ var LocalStreamController = class extends Destroyable {
15061
15286
  const stream = this._localStream$.value;
15062
15287
  const track = stream?.getTracks().find((t) => t.id === trackId);
15063
15288
  if (!track) {
15064
- logger$17.debug(`[LocalStreamController] track not found: ${trackId}`);
15289
+ logger$18.debug(`[LocalStreamController] track not found: ${trackId}`);
15065
15290
  return;
15066
15291
  }
15067
15292
  track.removeEventListener("ended", this.mediaTrackEndedHandler);
@@ -15070,7 +15295,7 @@ var LocalStreamController = class extends Destroyable {
15070
15295
  this._localStream$.next(stream);
15071
15296
  if (track.kind === "video") this._localVideoTracks$.next(stream?.getVideoTracks() ?? []);
15072
15297
  else this._localAudioTracks$.next(stream?.getAudioTracks() ?? []);
15073
- logger$17.debug(`[LocalStreamController] ${track.kind} track removed:`, trackId);
15298
+ logger$18.debug(`[LocalStreamController] ${track.kind} track removed:`, trackId);
15074
15299
  return track;
15075
15300
  }
15076
15301
  /**
@@ -15105,7 +15330,7 @@ var LocalStreamController = class extends Destroyable {
15105
15330
  */
15106
15331
  stopAllTracks() {
15107
15332
  this._localStream$.value?.getTracks().forEach((track) => {
15108
- logger$17.debug(`[LocalStreamController] Stopping local track: ${track.kind}`);
15333
+ logger$18.debug(`[LocalStreamController] Stopping local track: ${track.kind}`);
15109
15334
  track.removeEventListener("ended", this.mediaTrackEndedHandler);
15110
15335
  track.stop();
15111
15336
  });
@@ -15121,7 +15346,7 @@ var LocalStreamController = class extends Destroyable {
15121
15346
 
15122
15347
  //#endregion
15123
15348
  //#region src/controllers/TransceiverController.ts
15124
- const logger$16 = getLogger();
15349
+ const logger$17 = getLogger();
15125
15350
  const getDirection = (send, recv) => {
15126
15351
  if (send && recv) return "sendrecv";
15127
15352
  else if (send && !recv) return "sendonly";
@@ -15223,7 +15448,7 @@ var TransceiverController = class extends Destroyable {
15223
15448
  sendEncodings: isAudio ? void 0 : this.sendEncodings,
15224
15449
  streams: direction === "recvonly" ? void 0 : [localStream]
15225
15450
  };
15226
- logger$16.debug(`[TransceiverController] Setting up transceiver sender for local ${track.kind} track:`, {
15451
+ logger$17.debug(`[TransceiverController] Setting up transceiver sender for local ${track.kind} track:`, {
15227
15452
  transceiver,
15228
15453
  transceiverParams
15229
15454
  });
@@ -15231,11 +15456,11 @@ var TransceiverController = class extends Destroyable {
15231
15456
  await transceiver.sender.replaceTrack(track);
15232
15457
  transceiver.direction = transceiverParams.direction;
15233
15458
  if (transceiverParams.streams?.some((stream) => Boolean(stream))) {
15234
- logger$16.debug(`[TransceiverController] Setting streams for transceiver sender for local ${track.kind} track:`, transceiverParams.streams);
15459
+ logger$17.debug(`[TransceiverController] Setting streams for transceiver sender for local ${track.kind} track:`, transceiverParams.streams);
15235
15460
  transceiver.sender.setStreams(...transceiverParams.streams);
15236
15461
  }
15237
15462
  } else {
15238
- logger$16.debug(`[TransceiverController] Adding new transceiver for local ${track.kind} track:`, track.id);
15463
+ logger$17.debug(`[TransceiverController] Adding new transceiver for local ${track.kind} track:`, track.id);
15239
15464
  this.peerConnection.addTransceiver(track, transceiverParams);
15240
15465
  }
15241
15466
  }
@@ -15249,13 +15474,13 @@ var TransceiverController = class extends Destroyable {
15249
15474
  if (options.updateTransceiverDirection) transceiver.direction = "inactive";
15250
15475
  }
15251
15476
  } catch (error) {
15252
- logger$16.error("[TransceiverController] stopTrackSender error", kind, error);
15477
+ logger$17.error("[TransceiverController] stopTrackSender error", kind, error);
15253
15478
  this.options.onError?.(new MediaTrackError("stopTrackSender", kind, error));
15254
15479
  }
15255
15480
  }
15256
15481
  async restoreTrackSender(kind) {
15257
15482
  try {
15258
- logger$16.debug("[TransceiverController] restoreTrackSender called", kind);
15483
+ logger$17.debug("[TransceiverController] restoreTrackSender called", kind);
15259
15484
  const constraints = {};
15260
15485
  const transceivers = this.transceiverByKind(kind);
15261
15486
  for (const transceiver of transceivers) {
@@ -15265,23 +15490,23 @@ var TransceiverController = class extends Destroyable {
15265
15490
  if (trackKind === "audio" || trackKind === "video") constraints[trackKind] = this.getConstraintsFor(trackKind);
15266
15491
  }
15267
15492
  }
15268
- logger$16.debug("[TransceiverController] restoreTrackSender constraints:", constraints);
15493
+ logger$17.debug("[TransceiverController] restoreTrackSender constraints:", constraints);
15269
15494
  if (Object.keys(constraints).length === 0) {
15270
- logger$16.warn("[TransceiverController] restoreTrackSender: no tracks need restoration", kind);
15495
+ logger$17.warn("[TransceiverController] restoreTrackSender: no tracks need restoration", kind);
15271
15496
  return;
15272
15497
  }
15273
15498
  const newTracks = (await this.options.getUserMedia(constraints)).getTracks();
15274
- logger$16.debug("[TransceiverController] restoreTrackSender new tracks:", newTracks);
15499
+ logger$17.debug("[TransceiverController] restoreTrackSender new tracks:", newTracks);
15275
15500
  for (const newTrack of newTracks) {
15276
15501
  this.options.localStreamController.addTrack(newTrack);
15277
15502
  const trackKind = newTrack.kind;
15278
15503
  const transceiverOfKind = this.transceiverByKind(trackKind)[0];
15279
15504
  transceiverOfKind.direction = trackKind === "audio" ? this.audioDirection : this.videoDirection;
15280
- logger$16.debug("[TransceiverController] restoreTrackSender setting direction for", trackKind, transceiverOfKind.direction);
15505
+ logger$17.debug("[TransceiverController] restoreTrackSender setting direction for", trackKind, transceiverOfKind.direction);
15281
15506
  await transceiverOfKind.sender.replaceTrack(newTrack);
15282
15507
  }
15283
15508
  } catch (error) {
15284
- logger$16.error("[TransceiverController] restoreTrackSender error", kind, error);
15509
+ logger$17.error("[TransceiverController] restoreTrackSender error", kind, error);
15285
15510
  this.options.onError?.(new MediaTrackError("restoreTrackSender", kind, error));
15286
15511
  }
15287
15512
  }
@@ -15322,14 +15547,14 @@ var TransceiverController = class extends Destroyable {
15322
15547
  };
15323
15548
  try {
15324
15549
  await track.applyConstraints(constraintsToApply);
15325
- logger$16.debug(`[TransceiverController] Updated ${kind} sender constraints:`, constraintsToApply);
15326
- logger$16.debug(`[TransceiverController] Updated ${kind} sender constraints:`, track.getConstraints());
15550
+ logger$17.debug(`[TransceiverController] Updated ${kind} sender constraints:`, constraintsToApply);
15551
+ logger$17.debug(`[TransceiverController] Updated ${kind} sender constraints:`, track.getConstraints());
15327
15552
  } catch (error) {
15328
- logger$16.warn(`[TransceiverController] applyConstraints failed for ${kind} track ${track.id}, attempting track replacement fallback:`, error);
15553
+ logger$17.warn(`[TransceiverController] applyConstraints failed for ${kind} track ${track.id}, attempting track replacement fallback:`, error);
15329
15554
  try {
15330
15555
  await this.replaceTrackFallback(sender, track, kind, constraintsToApply);
15331
15556
  } catch (fallbackError) {
15332
- logger$16.warn(`[TransceiverController] Track replacement fallback also failed for ${kind} track:`, fallbackError);
15557
+ logger$17.warn(`[TransceiverController] Track replacement fallback also failed for ${kind} track:`, fallbackError);
15333
15558
  this.options.onError?.(new MediaTrackError("updateSendersConstraints", kind, fallbackError));
15334
15559
  }
15335
15560
  }
@@ -15357,7 +15582,7 @@ var TransceiverController = class extends Destroyable {
15357
15582
  if (!newTrack) throw new MediaTrackError("replaceTrackFallback", kind, /* @__PURE__ */ new Error("getUserMedia returned no track of the requested kind"));
15358
15583
  await sender.replaceTrack(newTrack);
15359
15584
  this.options.localStreamController.addTrack(newTrack);
15360
- logger$16.debug(`[TransceiverController] Track replacement fallback succeeded for ${kind}. New track: ${newTrack.id}`);
15585
+ logger$17.debug(`[TransceiverController] Track replacement fallback succeeded for ${kind}. New track: ${newTrack.id}`);
15361
15586
  }
15362
15587
  getMediaDirections() {
15363
15588
  if (this.peerConnection.connectionState === "connected") return this.peerConnection.getTransceivers().reduce((acc, transceiver) => {
@@ -15387,60 +15612,60 @@ var TransceiverController = class extends Destroyable {
15387
15612
 
15388
15613
  //#endregion
15389
15614
  //#region src/controllers/RTCPeerConnectionController.ts
15390
- var import_cjs$15 = require_cjs();
15391
- const logger$15 = getLogger();
15615
+ var import_cjs$16 = require_cjs();
15616
+ const logger$16 = getLogger();
15392
15617
  var RTCPeerConnectionController = class extends Destroyable {
15393
15618
  constructor(options = {}, remoteSessionDescription, deviceController) {
15394
15619
  super();
15395
15620
  this.options = options;
15396
15621
  this.firstSDPExchangeCompleted = false;
15397
15622
  this.negotiationNeeded$ = this.createSubject();
15398
- 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)(() => {
15623
+ 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)(() => {
15399
15624
  this.negotiationEnded();
15400
- }), (0, import_cjs$15.filter)(() => this.shouldEmitLocalDescription), (0, import_cjs$15.map)(() => this.peerConnection?.localDescription), filterNull(), (0, import_cjs$15.tap)((desc) => {
15625
+ }), (0, import_cjs$16.filter)(() => this.shouldEmitLocalDescription), (0, import_cjs$16.map)(() => this.peerConnection?.localDescription), filterNull(), (0, import_cjs$16.tap)((desc) => {
15401
15626
  if (desc.type === "answer") this._type = "offer";
15402
- }))), (0, import_cjs$15.shareReplay)(1), (0, import_cjs$15.takeUntil)(this.destroyed$));
15627
+ }))), (0, import_cjs$16.shareReplay)(1), (0, import_cjs$16.takeUntil)(this.destroyed$));
15403
15628
  this.connectionTimeout = 3e3;
15404
15629
  this.oniceconnectionstatechangeHandler = () => {
15405
15630
  if (this.peerConnection) {
15406
15631
  const { iceConnectionState } = this.peerConnection;
15407
- logger$15.debug(`[RTCPeerConnectionController] ICE connection state changed to: ${iceConnectionState}`);
15632
+ logger$16.debug(`[RTCPeerConnectionController] ICE connection state changed to: ${iceConnectionState}`);
15408
15633
  this._iceConnectionState$.next(this.peerConnection.iceConnectionState);
15409
15634
  }
15410
15635
  };
15411
15636
  this.onconnectionstatechangeHandler = () => {
15412
15637
  if (this.peerConnection) {
15413
15638
  const { connectionState } = this.peerConnection;
15414
- logger$15.debug(`[RTCPeerConnectionController] Connection state changed to: ${connectionState}`);
15639
+ logger$16.debug(`[RTCPeerConnectionController] Connection state changed to: ${connectionState}`);
15415
15640
  if (connectionState === "connected") this.removeConnectionTimer();
15416
15641
  this._connectionState$.next(this.peerConnection.connectionState);
15417
15642
  }
15418
15643
  };
15419
15644
  this.onsignalingstatechangeHandler = () => {
15420
- logger$15.debug(`[RTCPeerConnectionController] Signaling state changed to: ${this.peerConnection?.signalingState}`);
15645
+ logger$16.debug(`[RTCPeerConnectionController] Signaling state changed to: ${this.peerConnection?.signalingState}`);
15421
15646
  };
15422
15647
  this.onicegatheringstatechangeHandler = () => {
15423
15648
  if (this.peerConnection) this._iceGatheringState$.next(this.peerConnection.iceGatheringState);
15424
15649
  };
15425
15650
  this.onnegotiationneededHandler = (event) => {
15426
- logger$15.debug("[RTCPeerConnectionController] Negotiation needed event received.", event);
15651
+ logger$16.debug("[RTCPeerConnectionController] Negotiation needed event received.", event);
15427
15652
  this.negotiationNeeded$.next();
15428
15653
  };
15429
15654
  this.updateSelectedInputDevice = async (kind, deviceInfo) => {
15430
15655
  try {
15431
15656
  const { localStream } = this;
15432
15657
  if (!localStream) {
15433
- logger$15.warn("[RTCPeerConnectionController] No local stream available to update input device.");
15658
+ logger$16.warn("[RTCPeerConnectionController] No local stream available to update input device.");
15434
15659
  return;
15435
15660
  }
15436
- logger$15.debug(`[RTCPeerConnectionController] Updating selected ${kind} input device:`, localStream.getTracks());
15661
+ logger$16.debug(`[RTCPeerConnectionController] Updating selected ${kind} input device:`, localStream.getTracks());
15437
15662
  const track = localStream.getTracks().find((track$1) => track$1.kind === kind);
15438
15663
  if (track) {
15439
15664
  this.transceiverController?.stopTrackSender(kind);
15440
15665
  this.localStream?.removeTrack(track);
15441
- logger$15.debug(`[RTCPeerConnectionController] Stopped existing ${kind} track: ${track.id}`, localStream.getTracks());
15666
+ logger$16.debug(`[RTCPeerConnectionController] Stopped existing ${kind} track: ${track.id}`, localStream.getTracks());
15442
15667
  if (!deviceInfo) {
15443
- logger$15.debug(`[RTCPeerConnectionController] ${kind} input device selected: none`);
15668
+ logger$16.debug(`[RTCPeerConnectionController] ${kind} input device selected: none`);
15444
15669
  return;
15445
15670
  }
15446
15671
  const streamTrack = (await this.getUserMedia({ [kind]: {
@@ -15448,15 +15673,15 @@ var RTCPeerConnectionController = class extends Destroyable {
15448
15673
  ...this.deviceController.deviceInfoToConstraints(deviceInfo)
15449
15674
  } })).getTracks().find((t) => t.kind === kind);
15450
15675
  if (streamTrack) {
15451
- logger$15.debug(`[RTCPeerConnectionController] Adding new ${kind} track: ${streamTrack.id}`);
15676
+ logger$16.debug(`[RTCPeerConnectionController] Adding new ${kind} track: ${streamTrack.id}`);
15452
15677
  this.localStream?.addTrack(streamTrack);
15453
15678
  await this.transceiverController?.replaceSenderTrack(kind, streamTrack);
15454
- logger$15.debug(`[RTCPeerConnectionController] Added new ${kind} track: ${streamTrack.id}`, this.localStream?.getTracks());
15679
+ logger$16.debug(`[RTCPeerConnectionController] Added new ${kind} track: ${streamTrack.id}`, this.localStream?.getTracks());
15455
15680
  }
15456
15681
  }
15457
- logger$15.debug(`[RTCPeerConnectionController] ${kind} input device selected:`, deviceInfo?.label);
15682
+ logger$16.debug(`[RTCPeerConnectionController] ${kind} input device selected:`, deviceInfo?.label);
15458
15683
  } catch (error) {
15459
- logger$15.error(`[RTCPeerConnectionController] Failed to select ${kind} input device:`, error);
15684
+ logger$16.error(`[RTCPeerConnectionController] Failed to select ${kind} input device:`, error);
15460
15685
  this._errors$.next(toError(error));
15461
15686
  throw error;
15462
15687
  }
@@ -15473,6 +15698,7 @@ var RTCPeerConnectionController = class extends Destroyable {
15473
15698
  this._remoteDescription$ = this.createReplaySubject(1);
15474
15699
  this._remoteStream$ = this.createBehaviorSubject(null);
15475
15700
  this._remoteOfferMediaDirections = null;
15701
+ this._localAudioPipeline = null;
15476
15702
  this.deviceController = deviceController ?? {};
15477
15703
  this.id = options.callId ?? v4_default();
15478
15704
  this._type = remoteSessionDescription ? "answer" : "offer";
@@ -15542,43 +15768,43 @@ var RTCPeerConnectionController = class extends Destroyable {
15542
15768
  };
15543
15769
  }
15544
15770
  get iceGatheringState$() {
15545
- return this.cachedObservable("iceGatheringState$", () => this._iceGatheringState$.asObservable().pipe((0, import_cjs$15.takeUntil)(this.destroyed$)));
15771
+ return this.cachedObservable("iceGatheringState$", () => this._iceGatheringState$.asObservable().pipe((0, import_cjs$16.takeUntil)(this.destroyed$)));
15546
15772
  }
15547
15773
  get mediaTrackEnded$() {
15548
- return this.cachedObservable("mediaTrackEnded$", () => this.localStreamController.mediaTrackEnded$.pipe((0, import_cjs$15.takeUntil)(this.destroyed$)));
15774
+ return this.cachedObservable("mediaTrackEnded$", () => this.localStreamController.mediaTrackEnded$.pipe((0, import_cjs$16.takeUntil)(this.destroyed$)));
15549
15775
  }
15550
15776
  get errors$() {
15551
- return this.cachedObservable("errors$", () => this._errors$.asObservable().pipe((0, import_cjs$15.takeUntil)(this.destroyed$)));
15777
+ return this.cachedObservable("errors$", () => this._errors$.asObservable().pipe((0, import_cjs$16.takeUntil)(this.destroyed$)));
15552
15778
  }
15553
15779
  get iceCandidates$() {
15554
- return this.cachedObservable("iceCandidates$", () => this._iceCandidates$.asObservable().pipe((0, import_cjs$15.takeUntil)(this.destroyed$)));
15780
+ return this.cachedObservable("iceCandidates$", () => this._iceCandidates$.asObservable().pipe((0, import_cjs$16.takeUntil)(this.destroyed$)));
15555
15781
  }
15556
15782
  get initialized$() {
15557
- return this.cachedObservable("initialized$", () => this._initialized$.asObservable().pipe((0, import_cjs$15.filter)((initialized) => initialized), (0, import_cjs$15.takeUntil)(this.destroyed$)));
15783
+ return this.cachedObservable("initialized$", () => this._initialized$.asObservable().pipe((0, import_cjs$16.filter)((initialized) => initialized), (0, import_cjs$16.takeUntil)(this.destroyed$)));
15558
15784
  }
15559
15785
  get remoteDescription$() {
15560
- return this.cachedObservable("remoteDescription$", () => this._remoteDescription$.asObservable().pipe((0, import_cjs$15.takeUntil)(this.destroyed$)));
15786
+ return this.cachedObservable("remoteDescription$", () => this._remoteDescription$.asObservable().pipe((0, import_cjs$16.takeUntil)(this.destroyed$)));
15561
15787
  }
15562
15788
  get localStream$() {
15563
- return this.cachedObservable("localStream$", () => this.localStreamController.localStream$.pipe((0, import_cjs$15.takeUntil)(this.destroyed$)));
15789
+ return this.cachedObservable("localStream$", () => this.localStreamController.localStream$.pipe((0, import_cjs$16.takeUntil)(this.destroyed$)));
15564
15790
  }
15565
15791
  get remoteStream$() {
15566
- return this.cachedObservable("remoteStream$", () => this._remoteStream$.asObservable().pipe((0, import_cjs$15.takeUntil)(this.destroyed$)));
15792
+ return this.cachedObservable("remoteStream$", () => this._remoteStream$.asObservable().pipe((0, import_cjs$16.takeUntil)(this.destroyed$)));
15567
15793
  }
15568
15794
  get localAudioTracks$() {
15569
- return this.cachedObservable("localAudioTracks$", () => this.localStreamController.localAudioTracks$.pipe((0, import_cjs$15.takeUntil)(this.destroyed$)));
15795
+ return this.cachedObservable("localAudioTracks$", () => this.localStreamController.localAudioTracks$.pipe((0, import_cjs$16.takeUntil)(this.destroyed$)));
15570
15796
  }
15571
15797
  get localVideoTracks$() {
15572
- return this.cachedObservable("localVideoTracks$", () => this.localStreamController.localVideoTracks$.pipe((0, import_cjs$15.takeUntil)(this.destroyed$)));
15798
+ return this.cachedObservable("localVideoTracks$", () => this.localStreamController.localVideoTracks$.pipe((0, import_cjs$16.takeUntil)(this.destroyed$)));
15573
15799
  }
15574
15800
  get iceConnectionState$() {
15575
- return this.cachedObservable("iceConnectionState$", () => this._iceConnectionState$.asObservable().pipe((0, import_cjs$15.takeUntil)(this.destroyed$)));
15801
+ return this.cachedObservable("iceConnectionState$", () => this._iceConnectionState$.asObservable().pipe((0, import_cjs$16.takeUntil)(this.destroyed$)));
15576
15802
  }
15577
15803
  get connectionState$() {
15578
- return this.cachedObservable("connectionState$", () => this._connectionState$.asObservable().pipe((0, import_cjs$15.takeUntil)(this.destroyed$)));
15804
+ return this.cachedObservable("connectionState$", () => this._connectionState$.asObservable().pipe((0, import_cjs$16.takeUntil)(this.destroyed$)));
15579
15805
  }
15580
15806
  get signalingState$() {
15581
- return this.cachedObservable("signalingState$", () => this._signalingState$.asObservable().pipe((0, import_cjs$15.takeUntil)(this.destroyed$)));
15807
+ return this.cachedObservable("signalingState$", () => this._signalingState$.asObservable().pipe((0, import_cjs$16.takeUntil)(this.destroyed$)));
15582
15808
  }
15583
15809
  get type() {
15584
15810
  return this._type;
@@ -15691,17 +15917,17 @@ var RTCPeerConnectionController = class extends Destroyable {
15691
15917
  async doInit() {
15692
15918
  try {
15693
15919
  this.setupPeerConnection();
15694
- this.subscribeTo(this.negotiationNeeded$.pipe((0, import_cjs$15.auditTime)(0), (0, import_cjs$15.exhaustMap)(async () => this.startNegotiation())), {
15920
+ this.subscribeTo(this.negotiationNeeded$.pipe((0, import_cjs$16.auditTime)(0), (0, import_cjs$16.exhaustMap)(async () => this.startNegotiation())), {
15695
15921
  next: () => {
15696
- logger$15.debug("[RTCPeerConnectionController] Start Negotiation completed successfully");
15922
+ logger$16.debug("[RTCPeerConnectionController] Start Negotiation completed successfully");
15697
15923
  },
15698
15924
  error: (error) => {
15699
- logger$15.error("[RTCPeerConnectionController] Start Negotiation error:", error);
15925
+ logger$16.error("[RTCPeerConnectionController] Start Negotiation error:", error);
15700
15926
  this._errors$.next(toError(error));
15701
15927
  }
15702
15928
  });
15703
- 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]) => {
15704
- logger$15.debug(`[RTCPeerConnectionController] Selected input device changed for:`, {
15929
+ 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]) => {
15930
+ logger$16.debug(`[RTCPeerConnectionController] Selected input device changed for:`, {
15705
15931
  kind,
15706
15932
  deviceInfo
15707
15933
  });
@@ -15718,7 +15944,7 @@ var RTCPeerConnectionController = class extends Destroyable {
15718
15944
  this._initialized$.next(true);
15719
15945
  }
15720
15946
  } catch (error) {
15721
- logger$15.error("[RTCPeerConnectionController] Initialization error:", error);
15947
+ logger$16.error("[RTCPeerConnectionController] Initialization error:", error);
15722
15948
  this._errors$.next(toError(error));
15723
15949
  this.destroy();
15724
15950
  }
@@ -15750,22 +15976,22 @@ var RTCPeerConnectionController = class extends Destroyable {
15750
15976
  }
15751
15977
  async startNegotiation() {
15752
15978
  if (this.isNegotiating) {
15753
- logger$15.debug("[RTCPeerConnectionController] Negotiation already in progress, skipping.");
15979
+ logger$16.debug("[RTCPeerConnectionController] Negotiation already in progress, skipping.");
15754
15980
  return;
15755
15981
  }
15756
15982
  this.setupEventListeners();
15757
15983
  if (this.type === "answer") {
15758
- logger$15.debug("[RTCPeerConnectionController] This is an answer type still, skipping offer creation.");
15984
+ logger$16.debug("[RTCPeerConnectionController] This is an answer type still, skipping offer creation.");
15759
15985
  return;
15760
15986
  }
15761
15987
  this._isNegotiating$.next(true);
15762
- logger$15.debug("[RTCPeerConnectionController] Starting negotiation.");
15988
+ logger$16.debug("[RTCPeerConnectionController] Starting negotiation.");
15763
15989
  try {
15764
15990
  const { offerOptions } = this;
15765
- logger$15.debug("[RTCPeerConnectionController] Creating offer with options:", offerOptions);
15991
+ logger$16.debug("[RTCPeerConnectionController] Creating offer with options:", offerOptions);
15766
15992
  await this.createOffer(offerOptions);
15767
15993
  } catch (error) {
15768
- logger$15.error("[RTCPeerConnectionController] Error during negotiation:", error);
15994
+ logger$16.error("[RTCPeerConnectionController] Error during negotiation:", error);
15769
15995
  this._errors$.next(toError(error));
15770
15996
  }
15771
15997
  }
@@ -15781,14 +16007,14 @@ var RTCPeerConnectionController = class extends Destroyable {
15781
16007
  let readyToConnect = status !== "failed";
15782
16008
  try {
15783
16009
  if (status === "received" && sdp) {
15784
- logger$15.debug("[RTCPeerConnectionController] Received answer SDP:", sdp);
16010
+ logger$16.debug("[RTCPeerConnectionController] Received answer SDP:", sdp);
15785
16011
  await this._setRemoteDescription({
15786
16012
  type: "answer",
15787
16013
  sdp
15788
16014
  });
15789
16015
  }
15790
16016
  } catch (error) {
15791
- logger$15.error("[RTCPeerConnectionController] Error updating answer status:", error);
16017
+ logger$16.error("[RTCPeerConnectionController] Error updating answer status:", error);
15792
16018
  this._errors$.next(toError(error));
15793
16019
  readyToConnect = false;
15794
16020
  } finally {
@@ -15807,7 +16033,7 @@ var RTCPeerConnectionController = class extends Destroyable {
15807
16033
  await this.handleOfferReceived();
15808
16034
  break;
15809
16035
  case "failed":
15810
- logger$15.error("[RTCPeerConnectionController] Offer failed to be processed by remote.");
16036
+ logger$16.error("[RTCPeerConnectionController] Offer failed to be processed by remote.");
15811
16037
  break;
15812
16038
  case "sent":
15813
16039
  default:
@@ -15839,7 +16065,7 @@ var RTCPeerConnectionController = class extends Destroyable {
15839
16065
  }
15840
16066
  await this.setupLocalTracks();
15841
16067
  const { answerOptions } = this;
15842
- logger$15.debug("[RTCPeerConnectionController] Creating inbound answer with options:", answerOptions);
16068
+ logger$16.debug("[RTCPeerConnectionController] Creating inbound answer with options:", answerOptions);
15843
16069
  await this.createAnswer(answerOptions);
15844
16070
  }
15845
16071
  async handleOfferReceived() {
@@ -15847,7 +16073,7 @@ var RTCPeerConnectionController = class extends Destroyable {
15847
16073
  this._isNegotiating$.next(true);
15848
16074
  await this._setRemoteDescription(this.sdpInit);
15849
16075
  const { answerOptions } = this;
15850
- logger$15.debug("[RTCPeerConnectionController] Creating answer with options:", answerOptions);
16076
+ logger$16.debug("[RTCPeerConnectionController] Creating answer with options:", answerOptions);
15851
16077
  await this.createAnswer(answerOptions);
15852
16078
  }
15853
16079
  readyToConnect() {
@@ -15855,7 +16081,7 @@ var RTCPeerConnectionController = class extends Destroyable {
15855
16081
  this.connectionTimer = setTimeout(() => {
15856
16082
  this.removeConnectionTimer();
15857
16083
  if (this.peerConnection?.connectionState !== "connected") {
15858
- logger$15.debug("[RTCPeerConnectionController] Connection timeout, restarting ICE gathering with relay only.");
16084
+ logger$16.debug("[RTCPeerConnectionController] Connection timeout, restarting ICE gathering with relay only.");
15859
16085
  this.iceGatheringController.restartICEGatheringWithRelayOnly();
15860
16086
  }
15861
16087
  }, this.connectionTimeout);
@@ -15877,14 +16103,14 @@ var RTCPeerConnectionController = class extends Destroyable {
15877
16103
  const stereo = this.options.stereo ?? PreferencesContainer.instance.stereoAudio;
15878
16104
  if (preferredAudioCodecs.length > 0 || preferredVideoCodecs.length > 0) {
15879
16105
  result = setCodecPreferences(result, preferredAudioCodecs, preferredVideoCodecs);
15880
- logger$15.debug("[RTCPeerConnectionController] Applied codec preferences to SDP", {
16106
+ logger$16.debug("[RTCPeerConnectionController] Applied codec preferences to SDP", {
15881
16107
  preferredAudioCodecs,
15882
16108
  preferredVideoCodecs
15883
16109
  });
15884
16110
  }
15885
16111
  if (stereo) {
15886
16112
  result = enableStereoOpus(result);
15887
- logger$15.debug("[RTCPeerConnectionController] Applied stereo Opus to SDP");
16113
+ logger$16.debug("[RTCPeerConnectionController] Applied stereo Opus to SDP");
15888
16114
  }
15889
16115
  return Promise.resolve(result);
15890
16116
  }
@@ -15940,25 +16166,25 @@ var RTCPeerConnectionController = class extends Destroyable {
15940
16166
  ...this.peerConnection.getConfiguration(),
15941
16167
  iceTransportPolicy: "relay"
15942
16168
  });
15943
- logger$15.debug("[RTCPeerConnectionController] ICE transport policy set to relay-only");
16169
+ logger$16.debug("[RTCPeerConnectionController] ICE transport policy set to relay-only");
15944
16170
  } catch (error) {
15945
- logger$15.warn("[RTCPeerConnectionController] Failed to set relay-only policy:", error);
16171
+ logger$16.warn("[RTCPeerConnectionController] Failed to set relay-only policy:", error);
15946
16172
  }
15947
16173
  this.setupEventListeners();
15948
16174
  this._isNegotiating$.next(true);
15949
- logger$15.debug(`[RTCPeerConnectionController] Triggering ICE restart${relayOnly ? " (relay-only)" : ""}.`);
16175
+ logger$16.debug(`[RTCPeerConnectionController] Triggering ICE restart${relayOnly ? " (relay-only)" : ""}.`);
15950
16176
  try {
15951
16177
  const offer = await this.peerConnection.createOffer({ iceRestart: true });
15952
16178
  await this.setLocalDescription(offer);
15953
16179
  } catch (error) {
15954
- logger$15.error("[RTCPeerConnectionController] ICE restart offer failed:", error);
16180
+ logger$16.error("[RTCPeerConnectionController] ICE restart offer failed:", error);
15955
16181
  this._errors$.next(toError(error));
15956
16182
  this.negotiationEnded();
15957
16183
  if (policyChanged) this.restoreIceTransportPolicy();
15958
16184
  throw error;
15959
16185
  }
15960
- 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) => {
15961
- logger$15.warn("[RTCPeerConnectionController] Error waiting for ICE gathering to complete:", error);
16186
+ 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) => {
16187
+ logger$16.warn("[RTCPeerConnectionController] Error waiting for ICE gathering to complete:", error);
15962
16188
  this.restoreIceTransportPolicy();
15963
16189
  });
15964
16190
  }
@@ -15968,9 +16194,9 @@ var RTCPeerConnectionController = class extends Destroyable {
15968
16194
  ...this.peerConnection.getConfiguration(),
15969
16195
  iceTransportPolicy: this.options.relayOnly ? "relay" : "all"
15970
16196
  });
15971
- logger$15.debug("[RTCPeerConnectionController] ICE transport policy restored");
16197
+ logger$16.debug("[RTCPeerConnectionController] ICE transport policy restored");
15972
16198
  } catch (error) {
15973
- logger$15.warn("[RTCPeerConnectionController] Failed to restore ICE transport policy:", error);
16199
+ logger$16.warn("[RTCPeerConnectionController] Failed to restore ICE transport policy:", error);
15974
16200
  }
15975
16201
  }
15976
16202
  /**
@@ -15982,13 +16208,13 @@ var RTCPeerConnectionController = class extends Destroyable {
15982
16208
  await this.setupRemoteTracks();
15983
16209
  }
15984
16210
  async setupLocalTracks() {
15985
- logger$15.debug("[RTCPeerConnectionController] Setting up local tracks/transceivers.");
16211
+ logger$16.debug("[RTCPeerConnectionController] Setting up local tracks/transceivers.");
15986
16212
  const localStream = this.localStream ?? await this.localStreamController.buildLocalStream();
15987
16213
  if (this.transceiverController?.useAddStream ?? false) {
15988
- logger$15.warn("[RTCPeerConnectionController] Using deprecated addStream API to add local stream.");
16214
+ logger$16.warn("[RTCPeerConnectionController] Using deprecated addStream API to add local stream.");
15989
16215
  this.peerConnection?.addStream(localStream);
15990
16216
  if (!this.isNegotiating) {
15991
- logger$15.debug("[RTCPeerConnectionController] Forcing negotiationneeded after local tracks setup.");
16217
+ logger$16.debug("[RTCPeerConnectionController] Forcing negotiationneeded after local tracks setup.");
15992
16218
  this.negotiationNeeded$.next();
15993
16219
  }
15994
16220
  return;
@@ -16004,7 +16230,7 @@ var RTCPeerConnectionController = class extends Destroyable {
16004
16230
  const transceivers = (kind === "audio" ? this.transceiverController?.audioTransceivers : this.transceiverController?.videoTransceivers) ?? [];
16005
16231
  await this.transceiverController?.setupTransceiverSender(track, localStream, transceivers[index]);
16006
16232
  } else {
16007
- logger$15.debug(`[RTCPeerConnectionController] Using addTrack for local ${kind} track:`, track.id);
16233
+ logger$16.debug(`[RTCPeerConnectionController] Using addTrack for local ${kind} track:`, track.id);
16008
16234
  this.peerConnection?.addTrack(track, localStream);
16009
16235
  }
16010
16236
  }
@@ -16021,7 +16247,7 @@ var RTCPeerConnectionController = class extends Destroyable {
16021
16247
  async setupRemoteTracks() {
16022
16248
  if (!this.peerConnection) throw new DependencyError("RTCPeerConnection is not initialized");
16023
16249
  this.peerConnection.ontrack = (event) => {
16024
- logger$15.debug("[RTCPeerConnectionController] Remote track received:", event.track.kind);
16250
+ logger$16.debug("[RTCPeerConnectionController] Remote track received:", event.track.kind);
16025
16251
  if (event.streams[0]) this._remoteStream$.next(event.streams[0]);
16026
16252
  else {
16027
16253
  const existingTracks = this._remoteStream$.value?.getTracks() ?? [];
@@ -16033,6 +16259,46 @@ var RTCPeerConnectionController = class extends Destroyable {
16033
16259
  }
16034
16260
  async restoreTrackSender(kind) {
16035
16261
  await this.transceiverController?.restoreTrackSender(kind);
16262
+ if (kind !== "video" && this._localAudioPipeline) await this.applyLocalAudioPipelineToSender();
16263
+ }
16264
+ /**
16265
+ * Return the lazily-created {@link LocalAudioPipeline}, constructing it on
16266
+ * first access. On creation the current audio sender's track is routed
16267
+ * through the pipeline (input → gain → analyser → destination) and the
16268
+ * sender is switched to emit the processed track. Returns `null` when no
16269
+ * audio sender exists yet (pre-negotiation).
16270
+ */
16271
+ ensureLocalAudioPipeline() {
16272
+ if (this._localAudioPipeline) return this._localAudioPipeline;
16273
+ if (!this.peerConnection) return null;
16274
+ try {
16275
+ this._localAudioPipeline = new LocalAudioPipeline();
16276
+ } catch (error) {
16277
+ logger$16.warn("[RTCPeerConnectionController] Failed to create LocalAudioPipeline:", error);
16278
+ return null;
16279
+ }
16280
+ this.subscribeTo(this.localStreamController.localAudioTracks$, () => {
16281
+ this.applyLocalAudioPipelineToSender();
16282
+ });
16283
+ this.applyLocalAudioPipelineToSender();
16284
+ return this._localAudioPipeline;
16285
+ }
16286
+ /** The active LocalAudioPipeline, or null if it hasn't been created yet. */
16287
+ get localAudioPipeline() {
16288
+ return this._localAudioPipeline;
16289
+ }
16290
+ async applyLocalAudioPipelineToSender() {
16291
+ if (!this._localAudioPipeline || !this.peerConnection) return;
16292
+ const [raw] = this.localStreamController.localAudioTracks;
16293
+ this._localAudioPipeline.setInputTrack(raw ?? null);
16294
+ const [audioTransceiver] = this.transceiverController?.audioTransceivers ?? [];
16295
+ const sender = audioTransceiver?.sender ?? this.peerConnection.getSenders().find((s) => s.track?.kind === "audio");
16296
+ if (!sender || !raw) return;
16297
+ try {
16298
+ await sender.replaceTrack(this._localAudioPipeline.outputTrack);
16299
+ } catch (error) {
16300
+ logger$16.warn("[RTCPeerConnectionController] Failed to route audio sender through pipeline:", error);
16301
+ }
16036
16302
  }
16037
16303
  /**
16038
16304
  * Add a local media track to the peer connection.
@@ -16047,9 +16313,9 @@ var RTCPeerConnectionController = class extends Destroyable {
16047
16313
  try {
16048
16314
  const localStream = this.localStreamController.addTrack(track);
16049
16315
  this.peerConnection.addTrack(track, localStream);
16050
- logger$15.debug(`[RTCPeerConnectionController] ${track.kind} track added:`, track.id);
16316
+ logger$16.debug(`[RTCPeerConnectionController] ${track.kind} track added:`, track.id);
16051
16317
  } catch (error) {
16052
- logger$15.error(`[RTCPeerConnectionController] Failed to add ${track.kind} track:`, error);
16318
+ logger$16.error(`[RTCPeerConnectionController] Failed to add ${track.kind} track:`, error);
16053
16319
  this._errors$.next(toError(error));
16054
16320
  throw error;
16055
16321
  }
@@ -16066,15 +16332,15 @@ var RTCPeerConnectionController = class extends Destroyable {
16066
16332
  }
16067
16333
  const sender = this.peerConnection.getSenders().find((sender$1) => sender$1.track?.id === trackId);
16068
16334
  if (!sender) {
16069
- logger$15.debug(`[RTCPeerConnectionController] track not found: ${trackId}`);
16335
+ logger$16.debug(`[RTCPeerConnectionController] track not found: ${trackId}`);
16070
16336
  return;
16071
16337
  }
16072
16338
  try {
16073
16339
  this.peerConnection.removeTrack(sender);
16074
16340
  this.localStreamController.removeTrack(trackId);
16075
- logger$15.debug(`[RTCPeerConnectionController] ${sender.track?.kind} track removed:`, trackId);
16341
+ logger$16.debug(`[RTCPeerConnectionController] ${sender.track?.kind} track removed:`, trackId);
16076
16342
  } catch (error) {
16077
- logger$15.error(`[RTCPeerConnectionController] Failed to remove ${sender.track?.kind} track:`, error);
16343
+ logger$16.error(`[RTCPeerConnectionController] Failed to remove ${sender.track?.kind} track:`, error);
16078
16344
  this._errors$.next(toError(error));
16079
16345
  throw error;
16080
16346
  }
@@ -16101,7 +16367,7 @@ var RTCPeerConnectionController = class extends Destroyable {
16101
16367
  async replaceAudioTrackWithConstraints(constraints) {
16102
16368
  const senders = this.peerConnection?.getSenders().filter((s) => s.track?.kind === "audio" && s.track.readyState === "live");
16103
16369
  if (!senders || senders.length === 0) {
16104
- logger$15.warn("[RTCPeerConnectionController] No live audio sender to replace");
16370
+ logger$16.warn("[RTCPeerConnectionController] No live audio sender to replace");
16105
16371
  return;
16106
16372
  }
16107
16373
  for (const sender of senders) {
@@ -16119,7 +16385,7 @@ var RTCPeerConnectionController = class extends Destroyable {
16119
16385
  const newTrack = (await this.getUserMedia({ audio: mergedConstraints })).getAudioTracks()[0];
16120
16386
  await sender.replaceTrack(newTrack);
16121
16387
  this.localStreamController.addTrack(newTrack);
16122
- logger$15.debug(`[RTCPeerConnectionController] Audio track replaced for server-pushed params. New track: ${newTrack.id}`);
16388
+ logger$16.debug(`[RTCPeerConnectionController] Audio track replaced for server-pushed params. New track: ${newTrack.id}`);
16123
16389
  }
16124
16390
  }
16125
16391
  /**
@@ -16127,9 +16393,11 @@ var RTCPeerConnectionController = class extends Destroyable {
16127
16393
  * Completes all observables to prevent memory leaks.
16128
16394
  */
16129
16395
  destroy() {
16130
- logger$15.debug(`[RTCPeerConnectionController] Destroying RTCPeerConnectionController. ${this.propose}`);
16396
+ logger$16.debug(`[RTCPeerConnectionController] Destroying RTCPeerConnectionController. ${this.propose}`);
16131
16397
  this.removeConnectionTimer();
16132
16398
  this._iceGatheringController?.destroy();
16399
+ this._localAudioPipeline?.destroy();
16400
+ this._localAudioPipeline = null;
16133
16401
  this.localStreamController.destroy();
16134
16402
  this.transceiverController?.destroy();
16135
16403
  if (this.peerConnection) {
@@ -16151,7 +16419,7 @@ var RTCPeerConnectionController = class extends Destroyable {
16151
16419
  }
16152
16420
  stopRemoteTracks() {
16153
16421
  this._remoteStream$.value?.getTracks().forEach((track) => {
16154
- logger$15.debug(`[RTCPeerConnectionController] Stopping remote track: ${track.kind}`);
16422
+ logger$16.debug(`[RTCPeerConnectionController] Stopping remote track: ${track.kind}`);
16155
16423
  track.stop();
16156
16424
  });
16157
16425
  }
@@ -16168,7 +16436,7 @@ var RTCPeerConnectionController = class extends Destroyable {
16168
16436
  ...params,
16169
16437
  sdp: finalRemote
16170
16438
  };
16171
- logger$15.debug("[RTCPeerConnectionController] Setting remote description:", answer);
16439
+ logger$16.debug("[RTCPeerConnectionController] Setting remote description:", answer);
16172
16440
  return this.peerConnection.setRemoteDescription(answer);
16173
16441
  }
16174
16442
  };
@@ -16206,8 +16474,8 @@ function isVertoPingInnerParams(value) {
16206
16474
 
16207
16475
  //#endregion
16208
16476
  //#region src/managers/VertoManager.ts
16209
- var import_cjs$14 = require_cjs();
16210
- const logger$14 = getLogger();
16477
+ var import_cjs$15 = require_cjs();
16478
+ const logger$15 = getLogger();
16211
16479
  var VertoManager = class extends Destroyable {
16212
16480
  constructor(callSession) {
16213
16481
  super();
@@ -16246,7 +16514,7 @@ var WebRTCVertoManager = class extends VertoManager {
16246
16514
  try {
16247
16515
  await this.executeVerto(vertoModifyMessage);
16248
16516
  } catch (error) {
16249
- logger$14.warn("[WebRTCManager] Call might already be disconnected, error sending Verto hold:", error);
16517
+ logger$15.warn("[WebRTCManager] Call might already be disconnected, error sending Verto hold:", error);
16250
16518
  throw error;
16251
16519
  }
16252
16520
  }
@@ -16259,7 +16527,7 @@ var WebRTCVertoManager = class extends VertoManager {
16259
16527
  try {
16260
16528
  await this.executeVerto(vertoModifyMessage);
16261
16529
  } catch (error) {
16262
- logger$14.warn("[WebRTCManager] Call might already be disconnected, error sending Verto unhold:", error);
16530
+ logger$15.warn("[WebRTCManager] Call might already be disconnected, error sending Verto unhold:", error);
16263
16531
  throw error;
16264
16532
  }
16265
16533
  }
@@ -16299,7 +16567,7 @@ var WebRTCVertoManager = class extends VertoManager {
16299
16567
  return rtcPeerConnection;
16300
16568
  }
16301
16569
  get signalingStatus$() {
16302
- return this.cachedObservable("signalingStatus$", () => (0, import_cjs$14.merge)(this._signalingStatus$.asObservable(), this.mainPeerConnection.connectionState$.pipe((0, import_cjs$14.filter)((connectionState) => [
16570
+ return this.cachedObservable("signalingStatus$", () => (0, import_cjs$15.merge)(this._signalingStatus$.asObservable(), this.mainPeerConnection.connectionState$.pipe((0, import_cjs$15.filter)((connectionState) => [
16303
16571
  "connected",
16304
16572
  "disconnected",
16305
16573
  "failed"
@@ -16312,7 +16580,7 @@ var WebRTCVertoManager = class extends VertoManager {
16312
16580
  if (event.member_id) this.setSelfIdIfNull(event.member_id);
16313
16581
  });
16314
16582
  this.subscribeTo(this.vertoMedia$, (event) => {
16315
- logger$14.debug("[WebRTCManager] Received Verto media event (early media SDP):", event);
16583
+ logger$15.debug("[WebRTCManager] Received Verto media event (early media SDP):", event);
16316
16584
  this._signalingStatus$.next("ringing");
16317
16585
  const { sdp, callID } = event;
16318
16586
  this._rtcPeerConnectionsMap.get(callID)?.updateAnswerStatus({
@@ -16321,7 +16589,7 @@ var WebRTCVertoManager = class extends VertoManager {
16321
16589
  });
16322
16590
  });
16323
16591
  this.subscribeTo(this.vertoAnswer$, (event) => {
16324
- logger$14.debug("[WebRTCManager] Received Verto answer event:", event);
16592
+ logger$15.debug("[WebRTCManager] Received Verto answer event:", event);
16325
16593
  this._signalingStatus$.next("connecting");
16326
16594
  const { sdp, callID } = event;
16327
16595
  this._rtcPeerConnectionsMap.get(callID)?.updateAnswerStatus({
@@ -16330,7 +16598,7 @@ var WebRTCVertoManager = class extends VertoManager {
16330
16598
  });
16331
16599
  });
16332
16600
  this.subscribeTo(this.vertoMediaParams$, (event) => {
16333
- logger$14.debug("[WebRTCManager] Received Verto mediaParams event:", event);
16601
+ logger$15.debug("[WebRTCManager] Received Verto mediaParams event:", event);
16334
16602
  const { mediaParams, callID } = event;
16335
16603
  const rtcPeerConnController = this._rtcPeerConnectionsMap.get(callID);
16336
16604
  const { audio, video } = mediaParams;
@@ -16344,7 +16612,7 @@ var WebRTCVertoManager = class extends VertoManager {
16344
16612
  timestamp: Date.now()
16345
16613
  });
16346
16614
  } catch (error) {
16347
- logger$14.warn("[WebRTCManager] Error applying server-pushed media params:", error);
16615
+ logger$15.warn("[WebRTCManager] Error applying server-pushed media params:", error);
16348
16616
  this.onError?.(error instanceof Error ? error : new Error(String(error), { cause: error }));
16349
16617
  }
16350
16618
  })();
@@ -16366,13 +16634,13 @@ var WebRTCVertoManager = class extends VertoManager {
16366
16634
  */
16367
16635
  setNodeIdIfNull(nodeId) {
16368
16636
  if (!this._nodeId$.value && nodeId) {
16369
- logger$14.debug(`[WebRTCManager] Early node_id set: ${nodeId}`);
16637
+ logger$15.debug(`[WebRTCManager] Early node_id set: ${nodeId}`);
16370
16638
  this._nodeId$.next(nodeId);
16371
16639
  }
16372
16640
  }
16373
16641
  setSelfIdIfNull(selfId) {
16374
16642
  if (!this._selfId$.value && selfId) {
16375
- logger$14.debug(`[WebRTCManager] Early selfId set: ${selfId}`);
16643
+ logger$15.debug(`[WebRTCManager] Early selfId set: ${selfId}`);
16376
16644
  this._selfId$.next(selfId);
16377
16645
  }
16378
16646
  }
@@ -16381,7 +16649,7 @@ var WebRTCVertoManager = class extends VertoManager {
16381
16649
  const vertoPongMessage = VertoPong({ ...vertoPing });
16382
16650
  await this.executeVerto(vertoPongMessage);
16383
16651
  } catch (error) {
16384
- logger$14.warn("[WebRTCManager] Call might disconnect, error sending Verto pong:", error);
16652
+ logger$15.warn("[WebRTCManager] Call might disconnect, error sending Verto pong:", error);
16385
16653
  this.onError?.(new VertoPongError(error));
16386
16654
  }
16387
16655
  }
@@ -16391,7 +16659,7 @@ var WebRTCVertoManager = class extends VertoManager {
16391
16659
  if (audio) await this.mainPeerConnection.updateSendersConstraints("audio", audio);
16392
16660
  if (video) await this.mainPeerConnection.updateSendersConstraints("video", video);
16393
16661
  } catch (error) {
16394
- logger$14.warn("[WebRTCManager] Error updating media constraints:", error);
16662
+ logger$15.warn("[WebRTCManager] Error updating media constraints:", error);
16395
16663
  this.onError?.(error instanceof Error ? error : new Error(String(error), { cause: error }));
16396
16664
  throw error;
16397
16665
  }
@@ -16421,20 +16689,20 @@ var WebRTCVertoManager = class extends VertoManager {
16421
16689
  try {
16422
16690
  const pc = this.mainPeerConnection.peerConnection;
16423
16691
  if (!pc) {
16424
- logger$14.warn("[WebRTCManager] No peer connection for keyframe request");
16692
+ logger$15.warn("[WebRTCManager] No peer connection for keyframe request");
16425
16693
  return;
16426
16694
  }
16427
16695
  const videoReceiver = pc.getReceivers().find((r) => r.track.kind === "video");
16428
16696
  if (!videoReceiver) {
16429
- logger$14.warn("[WebRTCManager] No video receiver for keyframe request");
16697
+ logger$15.warn("[WebRTCManager] No video receiver for keyframe request");
16430
16698
  return;
16431
16699
  }
16432
16700
  if (typeof videoReceiver.requestKeyFrame === "function") {
16433
16701
  videoReceiver.requestKeyFrame();
16434
- logger$14.debug("[WebRTCManager] Keyframe requested via RTCRtpReceiver.requestKeyFrame()");
16435
- } else logger$14.debug("[WebRTCManager] requestKeyFrame() not supported, skipping");
16702
+ logger$15.debug("[WebRTCManager] Keyframe requested via RTCRtpReceiver.requestKeyFrame()");
16703
+ } else logger$15.debug("[WebRTCManager] requestKeyFrame() not supported, skipping");
16436
16704
  } catch (error) {
16437
- logger$14.warn("[WebRTCManager] Keyframe request failed (non-fatal):", error);
16705
+ logger$15.warn("[WebRTCManager] Keyframe request failed (non-fatal):", error);
16438
16706
  }
16439
16707
  }
16440
16708
  /**
@@ -16452,13 +16720,13 @@ var WebRTCVertoManager = class extends VertoManager {
16452
16720
  try {
16453
16721
  const controller = this.mainPeerConnection;
16454
16722
  if (!controller.peerConnection) {
16455
- logger$14.warn("[WebRTCManager] No peer connection for ICE restart");
16723
+ logger$15.warn("[WebRTCManager] No peer connection for ICE restart");
16456
16724
  return;
16457
16725
  }
16458
16726
  await controller.triggerIceRestart(relayOnly);
16459
- logger$14.info(`[WebRTCManager] ICE restart initiated${relayOnly ? " (relay-only)" : ""}`);
16727
+ logger$15.info(`[WebRTCManager] ICE restart initiated${relayOnly ? " (relay-only)" : ""}`);
16460
16728
  } catch (error) {
16461
- logger$14.error("[WebRTCManager] ICE restart failed:", error);
16729
+ logger$15.error("[WebRTCManager] ICE restart failed:", error);
16462
16730
  throw error;
16463
16731
  }
16464
16732
  }
@@ -16476,13 +16744,13 @@ var WebRTCVertoManager = class extends VertoManager {
16476
16744
  const entries = Array.from(this._rtcPeerConnectionsMap.entries());
16477
16745
  for (const [id, controller] of entries) try {
16478
16746
  if (!controller.peerConnection) {
16479
- logger$14.debug(`[WebRTCManager] No peer connection for leg ${id}, skipping ICE restart`);
16747
+ logger$15.debug(`[WebRTCManager] No peer connection for leg ${id}, skipping ICE restart`);
16480
16748
  continue;
16481
16749
  }
16482
16750
  await controller.triggerIceRestart(relayOnly);
16483
- logger$14.info(`[WebRTCManager] ICE restart initiated for leg ${id}${relayOnly ? " (relay-only)" : ""}`);
16751
+ logger$15.info(`[WebRTCManager] ICE restart initiated for leg ${id}${relayOnly ? " (relay-only)" : ""}`);
16484
16752
  } catch (error) {
16485
- logger$14.warn(`[WebRTCManager] ICE restart failed for leg ${id}:`, error);
16753
+ logger$15.warn(`[WebRTCManager] ICE restart failed for leg ${id}:`, error);
16486
16754
  }
16487
16755
  }
16488
16756
  /**
@@ -16494,7 +16762,7 @@ var WebRTCVertoManager = class extends VertoManager {
16494
16762
  requestKeyframeAll() {
16495
16763
  for (const [id, controller] of this._rtcPeerConnectionsMap) {
16496
16764
  if (controller.isScreenShare) {
16497
- logger$14.debug(`[WebRTCManager] Skipping keyframe for send-only screen share leg ${id}`);
16765
+ logger$15.debug(`[WebRTCManager] Skipping keyframe for send-only screen share leg ${id}`);
16498
16766
  continue;
16499
16767
  }
16500
16768
  try {
@@ -16504,33 +16772,33 @@ var WebRTCVertoManager = class extends VertoManager {
16504
16772
  if (!videoReceiver) continue;
16505
16773
  if (typeof videoReceiver.requestKeyFrame === "function") {
16506
16774
  videoReceiver.requestKeyFrame();
16507
- logger$14.debug(`[WebRTCManager] Keyframe requested for leg ${id}`);
16775
+ logger$15.debug(`[WebRTCManager] Keyframe requested for leg ${id}`);
16508
16776
  }
16509
16777
  } catch (error) {
16510
- logger$14.warn(`[WebRTCManager] Keyframe request failed for leg ${id} (non-fatal):`, error);
16778
+ logger$15.warn(`[WebRTCManager] Keyframe request failed for leg ${id} (non-fatal):`, error);
16511
16779
  }
16512
16780
  }
16513
16781
  }
16514
16782
  get callJoinedEvent$() {
16515
- return this.webRtcCallSession.callEvent$.pipe((0, import_cjs$14.filter)(isCallJoinedPayload), (0, import_cjs$14.takeUntil)(this.destroyed$));
16783
+ return this.webRtcCallSession.callEvent$.pipe((0, import_cjs$15.filter)(isCallJoinedPayload), (0, import_cjs$15.takeUntil)(this.destroyed$));
16516
16784
  }
16517
16785
  get vertoMedia$() {
16518
- return this.webRtcCallSession.webrtcMessages$.pipe(filterAs(isVertoMediaInnerParams, "params"), (0, import_cjs$14.takeUntil)(this.destroyed$));
16786
+ return this.webRtcCallSession.webrtcMessages$.pipe(filterAs(isVertoMediaInnerParams, "params"), (0, import_cjs$15.takeUntil)(this.destroyed$));
16519
16787
  }
16520
16788
  get vertoAnswer$() {
16521
- return this.cachedObservable("vertoAnswer$", () => this.webRtcCallSession.webrtcMessages$.pipe(filterAs(isVertoAnswerInnerParams, "params"), (0, import_cjs$14.takeUntil)(this.destroyed$)));
16789
+ return this.cachedObservable("vertoAnswer$", () => this.webRtcCallSession.webrtcMessages$.pipe(filterAs(isVertoAnswerInnerParams, "params"), (0, import_cjs$15.takeUntil)(this.destroyed$)));
16522
16790
  }
16523
16791
  get vertoMediaParams$() {
16524
- return this.cachedObservable("vertoMediaParams$", () => this.webRtcCallSession.webrtcMessages$.pipe(filterAs(isVertoMediaParamsInnerParams, "params"), (0, import_cjs$14.takeUntil)(this.destroyed$)));
16792
+ return this.cachedObservable("vertoMediaParams$", () => this.webRtcCallSession.webrtcMessages$.pipe(filterAs(isVertoMediaParamsInnerParams, "params"), (0, import_cjs$15.takeUntil)(this.destroyed$)));
16525
16793
  }
16526
16794
  get vertoBye$() {
16527
- return this.cachedObservable("vertoBye$", () => this.webRtcCallSession.webrtcMessages$.pipe(filterAs(isVertoByeMessage, "params"), (0, import_cjs$14.takeUntil)(this.destroyed$)));
16795
+ return this.cachedObservable("vertoBye$", () => this.webRtcCallSession.webrtcMessages$.pipe(filterAs(isVertoByeMessage, "params"), (0, import_cjs$15.takeUntil)(this.destroyed$)));
16528
16796
  }
16529
16797
  get vertoAttach$() {
16530
- return this.cachedObservable("vertoAttach$", () => this.webRtcCallSession.webrtcMessages$.pipe(filterAs(isVertoAttachMessage, "params"), (0, import_cjs$14.takeUntil)(this.destroyed$)));
16798
+ return this.cachedObservable("vertoAttach$", () => this.webRtcCallSession.webrtcMessages$.pipe(filterAs(isVertoAttachMessage, "params"), (0, import_cjs$15.takeUntil)(this.destroyed$)));
16531
16799
  }
16532
16800
  get vertoPing$() {
16533
- return this.cachedObservable("vertoPing$", () => this.webRtcCallSession.webrtcMessages$.pipe(filterAs(isVertoPingInnerParams, "params"), (0, import_cjs$14.takeUntil)(this.destroyed$)));
16801
+ return this.cachedObservable("vertoPing$", () => this.webRtcCallSession.webrtcMessages$.pipe(filterAs(isVertoPingInnerParams, "params"), (0, import_cjs$15.takeUntil)(this.destroyed$)));
16534
16802
  }
16535
16803
  async executeVerto(message, optionals = {}) {
16536
16804
  const webrtcVertoMessage = WebrtcVerto({
@@ -16568,7 +16836,7 @@ var WebRTCVertoManager = class extends VertoManager {
16568
16836
  default:
16569
16837
  }
16570
16838
  } catch (error) {
16571
- logger$14.error(`[WebRTCManager] Error sending Verto ${vertoMethod}:`, error);
16839
+ logger$15.error(`[WebRTCManager] Error sending Verto ${vertoMethod}:`, error);
16572
16840
  this.onError?.(error instanceof Error ? error : new Error(String(error), { cause: error }));
16573
16841
  if (vertoMethod === "verto.modify") this.onModifyFailed?.();
16574
16842
  }
@@ -16583,7 +16851,7 @@ var WebRTCVertoManager = class extends VertoManager {
16583
16851
  sdp
16584
16852
  });
16585
16853
  } catch (error) {
16586
- logger$14.warn("[WebRTCManager] Error processing modify response:", error);
16854
+ logger$15.warn("[WebRTCManager] Error processing modify response:", error);
16587
16855
  const modifyError = error instanceof Error ? error : new Error(String(error), { cause: error });
16588
16856
  this.onError?.(modifyError);
16589
16857
  }
@@ -16595,7 +16863,7 @@ var WebRTCVertoManager = class extends VertoManager {
16595
16863
  this._nodeId$.next(getValueFrom(response, "result.node_id") ?? null);
16596
16864
  const memberId = getValueFrom(response, "result.result.result.memberID") ?? null;
16597
16865
  const callId = getValueFrom(response, "result.result.result.callID") ?? null;
16598
- logger$14.debug("[WebRTCManager] Verto invite response:", {
16866
+ logger$15.debug("[WebRTCManager] Verto invite response:", {
16599
16867
  callId,
16600
16868
  memberId,
16601
16869
  response
@@ -16605,14 +16873,14 @@ var WebRTCVertoManager = class extends VertoManager {
16605
16873
  if (callId) {
16606
16874
  this.webRtcCallSession.addCallId(callId);
16607
16875
  this.attachManager.attach(this.buildAttachableCall(callId));
16608
- } else logger$14.warn("[WebRTCManager] Cannot attach call, missing callId:", {
16876
+ } else logger$15.warn("[WebRTCManager] Cannot attach call, missing callId:", {
16609
16877
  nodeId: this.nodeId,
16610
16878
  callId
16611
16879
  });
16612
- logger$14.info("[WebRTCManager] Verto invite successful");
16613
- logger$14.debug(`[WebRTCManager] nodeid: ${this._nodeId$.value}, selfId: ${this._selfId$.value}`);
16880
+ logger$15.info("[WebRTCManager] Verto invite successful");
16881
+ logger$15.debug(`[WebRTCManager] nodeid: ${this._nodeId$.value}, selfId: ${this._selfId$.value}`);
16614
16882
  } else {
16615
- logger$14.error("[WebRTCManager] Verto invite failed:", response);
16883
+ logger$15.error("[WebRTCManager] Verto invite failed:", response);
16616
16884
  const inviteError = response.error ? new JSONRPCError(response.error.code, response.error.message, response.error.data) : /* @__PURE__ */ new Error("Verto invite failed: unexpected response");
16617
16885
  this.onError?.(inviteError);
16618
16886
  }
@@ -16657,17 +16925,17 @@ var WebRTCVertoManager = class extends VertoManager {
16657
16925
  if (options.initOffer) this.handleInboundAnswer(rtcPeerConnController);
16658
16926
  }
16659
16927
  async handleInboundAnswer(rtcPeerConnController) {
16660
- logger$14.debug("[WebRTCManager] Waiting for inbound call to be accepted or rejected");
16661
- 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);
16928
+ logger$15.debug("[WebRTCManager] Waiting for inbound call to be accepted or rejected");
16929
+ 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);
16662
16930
  if (vertoByeOrAccepted === null) {
16663
- logger$14.debug("[WebRTCManager] Inbound answer handler aborted (destroyed).");
16931
+ logger$15.debug("[WebRTCManager] Inbound answer handler aborted (destroyed).");
16664
16932
  return;
16665
16933
  }
16666
16934
  if (isVertoByeMessage(vertoByeOrAccepted)) {
16667
- logger$14.info("[WebRTCManager] Inbound call ended by remote before answer.");
16935
+ logger$15.info("[WebRTCManager] Inbound call ended by remote before answer.");
16668
16936
  this.callSession?.destroy();
16669
16937
  } else if (!vertoByeOrAccepted) {
16670
- logger$14.info("[WebRTCManager] Inbound call rejected by user.");
16938
+ logger$15.info("[WebRTCManager] Inbound call rejected by user.");
16671
16939
  try {
16672
16940
  await this.bye("USER_BUSY");
16673
16941
  } finally {
@@ -16675,19 +16943,19 @@ var WebRTCVertoManager = class extends VertoManager {
16675
16943
  this.callSession?.destroy();
16676
16944
  }
16677
16945
  } else {
16678
- logger$14.debug("[WebRTCManager] Inbound call accepted, creating SDP answer");
16946
+ logger$15.debug("[WebRTCManager] Inbound call accepted, creating SDP answer");
16679
16947
  const answerOptions = this.webRtcCallSession.answerMediaOptions;
16680
16948
  try {
16681
16949
  await rtcPeerConnController.acceptInbound(answerOptions);
16682
16950
  } catch (error) {
16683
- logger$14.error("[WebRTCManager] Error creating inbound answer:", error);
16951
+ logger$15.error("[WebRTCManager] Error creating inbound answer:", error);
16684
16952
  this.onError?.(error instanceof Error ? error : new Error(String(error), { cause: error }));
16685
16953
  }
16686
16954
  }
16687
16955
  }
16688
16956
  setupVertoAttachHandler() {
16689
16957
  this.subscribeTo(this.vertoAttach$, async (vertoAttach) => {
16690
- logger$14.debug("[WebRTCManager] Received Verto attach event for existing call:", vertoAttach);
16958
+ logger$15.debug("[WebRTCManager] Received Verto attach event for existing call:", vertoAttach);
16691
16959
  const { callID } = vertoAttach;
16692
16960
  await this.attachManager.attach({
16693
16961
  nodeId: this.nodeId ?? void 0,
@@ -16701,12 +16969,12 @@ var WebRTCVertoManager = class extends VertoManager {
16701
16969
  });
16702
16970
  }
16703
16971
  initObservables(rtcPeerConnController) {
16704
- 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$));
16705
- this.localStream$ = rtcPeerConnController.localStream$.pipe(filterNull(), (0, import_cjs$14.takeUntil)(this.destroyed$));
16706
- this.remoteStream$ = rtcPeerConnController.remoteStream$.pipe(filterNull(), (0, import_cjs$14.takeUntil)(this.destroyed$));
16972
+ 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$));
16973
+ this.localStream$ = rtcPeerConnController.localStream$.pipe(filterNull(), (0, import_cjs$15.takeUntil)(this.destroyed$));
16974
+ this.remoteStream$ = rtcPeerConnController.remoteStream$.pipe(filterNull(), (0, import_cjs$15.takeUntil)(this.destroyed$));
16707
16975
  }
16708
16976
  setupLocalDescriptionHandler(rtcPeerConnController) {
16709
- this.subscribeTo(rtcPeerConnController.localDescription$.pipe((0, import_cjs$14.filter)((description) => description !== null), (0, import_cjs$14.takeUntil)(this.destroyed$)), (description) => {
16977
+ this.subscribeTo(rtcPeerConnController.localDescription$.pipe((0, import_cjs$15.filter)((description) => description !== null), (0, import_cjs$15.takeUntil)(this.destroyed$)), (description) => {
16710
16978
  const { type, sdp } = description;
16711
16979
  const dialogParams = this.dialogParams(rtcPeerConnController);
16712
16980
  const initial = !rtcPeerConnController.firstSDPExchangeCompleted;
@@ -16756,17 +17024,17 @@ var WebRTCVertoManager = class extends VertoManager {
16756
17024
  };
16757
17025
  }
16758
17026
  async sendLocalDescriptionOnceAccepted(vertoMessageRequest, rtcPeerConnectionController) {
16759
- logger$14.debug("[WebRTCManager] Waiting for call to be accepted or ended before sending answer");
16760
- 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);
17027
+ logger$15.debug("[WebRTCManager] Waiting for call to be accepted or ended before sending answer");
17028
+ 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);
16761
17029
  if (vertoByeOrAccepted === null) {
16762
- logger$14.debug("[WebRTCManager] Destroyed while waiting for call acceptance");
17030
+ logger$15.debug("[WebRTCManager] Destroyed while waiting for call acceptance");
16763
17031
  return;
16764
17032
  }
16765
17033
  if (isVertoByeMessage(vertoByeOrAccepted)) {
16766
- logger$14.info("[WebRTCManager] Call ended before answer was sent.");
17034
+ logger$15.info("[WebRTCManager] Call ended before answer was sent.");
16767
17035
  this.callSession?.destroy();
16768
17036
  } else if (!vertoByeOrAccepted) {
16769
- logger$14.info("[WebRTCManager] Call was not accepted, sending verto.bye.");
17037
+ logger$15.info("[WebRTCManager] Call was not accepted, sending verto.bye.");
16770
17038
  try {
16771
17039
  await this.bye("USER_BUSY");
16772
17040
  } finally {
@@ -16774,14 +17042,14 @@ var WebRTCVertoManager = class extends VertoManager {
16774
17042
  this.callSession?.destroy();
16775
17043
  }
16776
17044
  } else {
16777
- logger$14.debug("[WebRTCManager] Call accepted, sending answer");
17045
+ logger$15.debug("[WebRTCManager] Call accepted, sending answer");
16778
17046
  try {
16779
17047
  this._signalingStatus$.next("connecting");
16780
17048
  await this.sendLocalDescription(vertoMessageRequest, rtcPeerConnectionController);
16781
17049
  await rtcPeerConnectionController.updateAnswerStatus({ status: "sent" });
16782
17050
  await this.attachManager.attach(this.buildAttachableCall());
16783
17051
  } catch (error) {
16784
- logger$14.error("[WebRTCManager] Error sending Verto answer:", error);
17052
+ logger$15.error("[WebRTCManager] Error sending Verto answer:", error);
16785
17053
  this.onError?.(error instanceof Error ? error : new Error(String(error), { cause: error }));
16786
17054
  await rtcPeerConnectionController.updateAnswerStatus({ status: "failed" });
16787
17055
  }
@@ -16822,6 +17090,14 @@ var WebRTCVertoManager = class extends VertoManager {
16822
17090
  async unmuteMainVideoInputDevice() {
16823
17091
  return this.mainPeerConnection.restoreTrackSender("video");
16824
17092
  }
17093
+ /** Get or lazily create the local audio pipeline for the main peer connection. */
17094
+ ensureLocalAudioPipeline() {
17095
+ return this.mainPeerConnection.ensureLocalAudioPipeline();
17096
+ }
17097
+ /** The currently-active local audio pipeline, or null if it hasn't been created. */
17098
+ get localAudioPipeline() {
17099
+ return this.mainPeerConnection.localAudioPipeline;
17100
+ }
16825
17101
  async addInputDevice(options = {
16826
17102
  audio: false,
16827
17103
  video: true
@@ -16870,12 +17146,12 @@ var WebRTCVertoManager = class extends VertoManager {
16870
17146
  this.subscribeTo(rtcPeerConnController.errors$, (error) => {
16871
17147
  this.onError?.(error);
16872
17148
  });
16873
- 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$)));
17149
+ 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$)));
16874
17150
  this._screenShareStatus$.next("started");
16875
- logger$14.info("[WebRTCManager] Screen share started successfully.");
17151
+ logger$15.info("[WebRTCManager] Screen share started successfully.");
16876
17152
  return rtcPeerConnController.id;
16877
17153
  } catch (error) {
16878
- logger$14.warn("[WebRTCManager] Error initializing additional peer connection:", error);
17154
+ logger$15.warn("[WebRTCManager] Error initializing additional peer connection:", error);
16879
17155
  this.onError?.(error instanceof Error ? error : new Error(String(error), { cause: error }));
16880
17156
  if (rtcPeerConnController) rtcPeerConnController.destroy();
16881
17157
  this._screenShareStatus$.next("none");
@@ -16894,9 +17170,9 @@ var WebRTCVertoManager = class extends VertoManager {
16894
17170
  if (removeTrack) return this.mainPeerConnection.stopTrackSender(removeTrack, { updateTransceiverDirection: true });
16895
17171
  }
16896
17172
  async removeScreenMedia() {
16897
- if (!["starting", "started"].includes(this._screenShareStatus$.value)) logger$14.warn("[WebRTCManager] No active screen share to stop.");
17173
+ if (!["starting", "started"].includes(this._screenShareStatus$.value)) logger$15.warn("[WebRTCManager] No active screen share to stop.");
16898
17174
  if (!this._screenShareId) {
16899
- logger$14.debug("[WebRTCManager] No screen share peer connection found.");
17175
+ logger$15.debug("[WebRTCManager] No screen share peer connection found.");
16900
17176
  return;
16901
17177
  }
16902
17178
  this._screenShareStatus$.next("stopping");
@@ -16925,7 +17201,7 @@ var WebRTCVertoManager = class extends VertoManager {
16925
17201
  dialogParams: this.dialogParams(rtcPeerConnController)
16926
17202
  }));
16927
17203
  } catch (error) {
16928
- logger$14.warn("[WebRTCManager] Call might already be disconnected, error sending Verto bye:", error);
17204
+ logger$15.warn("[WebRTCManager] Call might already be disconnected, error sending Verto bye:", error);
16929
17205
  throw error;
16930
17206
  }
16931
17207
  }
@@ -16943,7 +17219,7 @@ var WebRTCVertoManager = class extends VertoManager {
16943
17219
  try {
16944
17220
  await this.executeVerto(vertoInfoMessage);
16945
17221
  } catch (error) {
16946
- logger$14.warn("[WebRTCManager] Error sending DTMF digits:", error);
17222
+ logger$15.warn("[WebRTCManager] Error sending DTMF digits:", error);
16947
17223
  throw error;
16948
17224
  }
16949
17225
  }
@@ -16954,10 +17230,10 @@ var WebRTCVertoManager = class extends VertoManager {
16954
17230
  action: "transfer"
16955
17231
  });
16956
17232
  try {
16957
- logger$14.debug("[WebRTCManager] Transferring call with options:", options);
17233
+ logger$15.debug("[WebRTCManager] Transferring call with options:", options);
16958
17234
  await this.executeVerto(message);
16959
17235
  } catch (error) {
16960
- logger$14.error("[WebRTCManager] Error transferring call:", error);
17236
+ logger$15.error("[WebRTCManager] Error transferring call:", error);
16961
17237
  throw error;
16962
17238
  }
16963
17239
  }
@@ -16971,6 +17247,77 @@ var WebRTCVertoManager = class extends VertoManager {
16971
17247
  }
16972
17248
  };
16973
17249
 
17250
+ //#endregion
17251
+ //#region src/controllers/RemoteAudioMeter.ts
17252
+ var import_cjs$14 = require_cjs();
17253
+ const logger$14 = getLogger();
17254
+ /**
17255
+ * Read-only audio level meter for a remote MediaStream. Attaches an
17256
+ * AnalyserNode to a MediaStreamAudioSourceNode so it observes the stream
17257
+ * without affecting the caller's playback path (no GainNode, no destination).
17258
+ *
17259
+ * The server delivers all remote audio as a single mixed stream — there is
17260
+ * no per-participant demux — so this meter reports the aggregate remote
17261
+ * level, not per-member.
17262
+ */
17263
+ var RemoteAudioMeter = class extends Destroyable {
17264
+ constructor(options = {}) {
17265
+ super();
17266
+ this._source = null;
17267
+ this._stream = null;
17268
+ this._audioContext = (options.audioContextFactory ?? (() => new AudioContext()))();
17269
+ this._analyser = this._audioContext.createAnalyser();
17270
+ this._analyser.fftSize = 2048;
17271
+ this._analyser.smoothingTimeConstant = .3;
17272
+ this._analyserBuffer = new Uint8Array(new ArrayBuffer(this._analyser.fftSize));
17273
+ this._pollIntervalMs = options.pollIntervalMs ?? AUDIO_LEVEL_POLL_INTERVAL_MS;
17274
+ }
17275
+ /** RMS level of the remote audio, 0..1. 0 when no stream is attached. */
17276
+ get level$() {
17277
+ return this.deferEmission((0, import_cjs$14.interval)(this._pollIntervalMs, import_cjs$14.animationFrameScheduler).pipe((0, import_cjs$14.map)(() => this.computeLevel())));
17278
+ }
17279
+ /**
17280
+ * Attach (or replace) the MediaStream whose audio track is being metered.
17281
+ * Pass null to detach without destroying the meter.
17282
+ */
17283
+ setStream(stream) {
17284
+ if (this._source) {
17285
+ try {
17286
+ this._source.disconnect();
17287
+ } catch (error) {
17288
+ logger$14.debug("[RemoteAudioMeter] source disconnect warning:", error);
17289
+ }
17290
+ this._source = null;
17291
+ this._stream = null;
17292
+ }
17293
+ if (!stream || stream.getAudioTracks().length === 0) return;
17294
+ this._stream = new MediaStream(stream.getAudioTracks());
17295
+ this._source = this._audioContext.createMediaStreamSource(this._stream);
17296
+ }
17297
+ destroy() {
17298
+ if (this._source) {
17299
+ try {
17300
+ this._source.disconnect();
17301
+ } catch {}
17302
+ this._source = null;
17303
+ }
17304
+ this._audioContext.close().catch((error) => {
17305
+ logger$14.debug("[RemoteAudioMeter] audio context close warning:", error);
17306
+ });
17307
+ super.destroy();
17308
+ }
17309
+ computeLevel() {
17310
+ if (!this._source) return 0;
17311
+ this._analyser.getByteTimeDomainData(this._analyserBuffer);
17312
+ let sum = 0;
17313
+ for (const sample$1 of this._analyserBuffer) {
17314
+ const normalized = (sample$1 - 128) / 128;
17315
+ sum += normalized * normalized;
17316
+ }
17317
+ return Math.sqrt(sum / this._analyserBuffer.length);
17318
+ }
17319
+ };
17320
+
16974
17321
  //#endregion
16975
17322
  //#region src/controllers/RTCStatsMonitor.ts
16976
17323
  var import_cjs$13 = require_cjs();
@@ -17784,6 +18131,8 @@ var WebRTCCall = class extends Destroyable {
17784
18131
  this._bandwidthConstrained$ = this.createBehaviorSubject(false);
17785
18132
  this._mediaParamsUpdated$ = this.createSubject();
17786
18133
  this._customSubscriptions = /* @__PURE__ */ new Map();
18134
+ this._pushToTalkEnabled = false;
18135
+ this._remoteAudioMeter = null;
17787
18136
  this.id = options.callId ?? v4_default();
17788
18137
  this.to = options.to;
17789
18138
  this._userVariables$.next({
@@ -18555,11 +18904,129 @@ var WebRTCCall = class extends Destroyable {
18555
18904
  async transfer(options) {
18556
18905
  return this.vertoManager.transfer(options);
18557
18906
  }
18907
+ /**
18908
+ * Set the local microphone gain as a percentage applied before transmission.
18909
+ *
18910
+ * - `0` = silent
18911
+ * - `100` = unity (no change, default)
18912
+ * - `200` = 2× digital boost (max; expect clipping / noise amplification)
18913
+ *
18914
+ * Values are clamped to [0, 200]. Engages the local audio pipeline on
18915
+ * first use (one-time cost).
18916
+ *
18917
+ * Note: this is a **digital** multiplier applied in a Web Audio GainNode
18918
+ * between your mic track and the RTCRtpSender — it does not change the
18919
+ * physical mic's hardware sensitivity. Browsers' autoGainControl can
18920
+ * fight the setting; call {@link setAutoGainControl}(false) for
18921
+ * predictable behaviour.
18922
+ *
18923
+ * @param value - Gain percentage (0..200; 100 = unity).
18924
+ */
18925
+ setLocalMicrophoneGain(value) {
18926
+ const pipeline = this.vertoManager.ensureLocalAudioPipeline();
18927
+ if (!pipeline) {
18928
+ logger$11.warn("[Call] setLocalMicrophoneGain: audio pipeline unavailable");
18929
+ return;
18930
+ }
18931
+ const percent = Math.max(0, Math.min(200, value));
18932
+ pipeline.setGain(percent / 100);
18933
+ }
18934
+ /** Observable of the current local microphone gain (0..200, where 100 = unity). */
18935
+ get localMicrophoneGain$() {
18936
+ const pipeline = this.vertoManager.ensureLocalAudioPipeline();
18937
+ if (!pipeline) return (0, import_cjs$11.of)(100).pipe((0, import_cjs$11.takeUntil)(this._destroyed$));
18938
+ return pipeline.gain$.pipe((0, import_cjs$11.map)((multiplier) => multiplier * 100), (0, import_cjs$11.takeUntil)(this._destroyed$));
18939
+ }
18940
+ /**
18941
+ * Observable of the RMS audio level of the local microphone, 0..1.
18942
+ * Emits at ~30fps while a mic track is active. Engages the local audio
18943
+ * pipeline on first subscription.
18944
+ */
18945
+ get localAudioLevel$() {
18946
+ return this.vertoManager.ensureLocalAudioPipeline()?.level$ ?? (0, import_cjs$11.of)(0).pipe((0, import_cjs$11.takeUntil)(this._destroyed$));
18947
+ }
18948
+ /**
18949
+ * Observable that is `true` while the local participant is speaking
18950
+ * (RMS level above the VAD threshold, with hold time to avoid flicker).
18951
+ */
18952
+ get localSpeaking$() {
18953
+ return this.vertoManager.ensureLocalAudioPipeline()?.speaking$ ?? (0, import_cjs$11.of)(false).pipe((0, import_cjs$11.takeUntil)(this._destroyed$));
18954
+ }
18955
+ /**
18956
+ * Enable push-to-talk: while {@link setPushToTalkActive} has been called
18957
+ * with `false`, the microphone gain is forced to 0; calling
18958
+ * {@link setPushToTalkActive} with `true` restores the configured gain.
18959
+ * Use this instead of mute/unmute for instant talk/silence transitions
18960
+ * because it doesn't rebuild the track.
18961
+ *
18962
+ * This method installs the pipeline but does not attach any keyboard
18963
+ * listener — consumers bind the key themselves and call
18964
+ * {@link setPushToTalkActive} on keydown/keyup.
18965
+ */
18966
+ enablePushToTalk() {
18967
+ const pipeline = this.vertoManager.ensureLocalAudioPipeline();
18968
+ if (!pipeline) {
18969
+ logger$11.warn("[Call] enablePushToTalk: audio pipeline unavailable");
18970
+ return;
18971
+ }
18972
+ pipeline.setPTTActive(false);
18973
+ this._pushToTalkEnabled = true;
18974
+ }
18975
+ /** Disable push-to-talk; mic gain returns to the configured value. */
18976
+ disablePushToTalk() {
18977
+ this.vertoManager.localAudioPipeline?.setPTTActive(true);
18978
+ this._pushToTalkEnabled = false;
18979
+ }
18980
+ /**
18981
+ * While push-to-talk is enabled, sets the talk state. `true` = transmitting,
18982
+ * `false` = silent. No-op if push-to-talk has not been enabled.
18983
+ */
18984
+ setPushToTalkActive(active) {
18985
+ if (!this._pushToTalkEnabled) return;
18986
+ this.vertoManager.localAudioPipeline?.setPTTActive(active);
18987
+ }
18988
+ /**
18989
+ * Toggle echo cancellation on the local mic at runtime. Applied via
18990
+ * `track.applyConstraints`; browsers that don't honour runtime constraints
18991
+ * (notably iOS Safari) fall back to re-acquiring the track with the new
18992
+ * constraint set and plumbing the replacement through the local audio
18993
+ * pipeline if one is active.
18994
+ */
18995
+ async setEchoCancellation(enabled) {
18996
+ await this.vertoManager.updateMediaConstraints({ audio: { echoCancellation: enabled } });
18997
+ }
18998
+ /** Toggle browser noise suppression on the local mic at runtime. */
18999
+ async setNoiseSuppression(enabled) {
19000
+ await this.vertoManager.updateMediaConstraints({ audio: { noiseSuppression: enabled } });
19001
+ }
19002
+ /** Toggle browser automatic gain control on the local mic at runtime. */
19003
+ async setAutoGainControl(enabled) {
19004
+ await this.vertoManager.updateMediaConstraints({ audio: { autoGainControl: enabled } });
19005
+ }
19006
+ /**
19007
+ * Observable of the aggregate remote audio level, 0..1 RMS. The server
19008
+ * delivers a single mixed audio stream for all remote participants — this
19009
+ * meter reports that mix. Per-participant audio is not available client-side.
19010
+ *
19011
+ * Engages a shared AudioContext on first subscription (cheap — one
19012
+ * AnalyserNode, no GainNode, no destination) so it does not affect the
19013
+ * caller's audio element playback.
19014
+ */
19015
+ get remoteAudioLevel$() {
19016
+ this._remoteAudioMeter ??= new RemoteAudioMeter();
19017
+ const meter = this._remoteAudioMeter;
19018
+ this.subscribeTo(this.vertoManager.remoteStream$, (stream) => {
19019
+ meter.setStream(stream);
19020
+ });
19021
+ return meter.level$.pipe((0, import_cjs$11.takeUntil)(this._destroyed$));
19022
+ }
18558
19023
  /** Destroys the call, releasing all resources and subscriptions. */
18559
19024
  destroy() {
18560
19025
  if (this._status$.value === "destroyed") return;
18561
19026
  this._status$.next("destroyed");
18562
19027
  this.stopResilienceSubsystems();
19028
+ this._remoteAudioMeter?.destroy();
19029
+ this._remoteAudioMeter = null;
18563
19030
  this.vertoManager.destroy();
18564
19031
  this.callEventsManager.destroy();
18565
19032
  super.destroy();
@@ -21220,6 +21687,36 @@ var SignalWire = class extends Destroyable {
21220
21687
  selectAudioOutputDevice(device) {
21221
21688
  this._deviceController.selectAudioOutputDevice(device);
21222
21689
  }
21690
+ /**
21691
+ * Apply the currently selected audio output device to an HTMLMediaElement
21692
+ * (e.g. the `<audio>` or `<video>` element the consumer attached the
21693
+ * remote stream to). Uses `HTMLMediaElement.setSinkId` under the hood.
21694
+ * Returns a `Promise<boolean>`: `true` if the sink was applied,
21695
+ * `false` if the browser doesn't support `setSinkId` or no device is
21696
+ * selected.
21697
+ *
21698
+ * @example
21699
+ * ```ts
21700
+ * audioEl.srcObject = call.remoteStream;
21701
+ * await client.applySelectedAudioOutputDevice(audioEl);
21702
+ * ```
21703
+ */
21704
+ async applySelectedAudioOutputDevice(element) {
21705
+ const device = this._deviceController.selectedAudioOutputDevice;
21706
+ if (!device?.deviceId) return false;
21707
+ const withSink = element;
21708
+ if (typeof withSink.setSinkId !== "function") {
21709
+ logger$1.warn("[SignalWire] setSinkId not supported on this element / browser");
21710
+ return false;
21711
+ }
21712
+ try {
21713
+ await withSink.setSinkId(device.deviceId);
21714
+ return true;
21715
+ } catch (error) {
21716
+ logger$1.warn("[SignalWire] Failed to apply audio output device:", error);
21717
+ return false;
21718
+ }
21719
+ }
21223
21720
  /** Starts monitoring for media device changes (connect/disconnect). */
21224
21721
  enableDeviceMonitoring() {
21225
21722
  this._deviceController.enableDeviceMonitoring();