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