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