@signalwire/js 4.0.0-dev-20260911182554 → 4.0.0-dev-20260911205444
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 +96 -34
- package/dist/browser.mjs.map +1 -1
- package/dist/browser.umd.js +96 -34
- package/dist/browser.umd.js.map +1 -1
- package/dist/index.cjs +91 -35
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +35 -4
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +35 -4
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +91 -35
- package/dist/index.mjs.map +1 -1
- package/dist/operators/index.cjs +1 -1
- package/dist/operators/index.mjs +1 -1
- package/dist/{operators-Cgz7QUVJ.cjs → operators-BJ6QW1VP.cjs} +13 -1
- package/dist/operators-BJ6QW1VP.cjs.map +1 -0
- package/dist/{operators-B0xqG7Eu.mjs → operators-BVxi3KpN.mjs} +8 -2
- package/dist/operators-BVxi3KpN.mjs.map +1 -0
- package/package.json +1 -1
- package/dist/operators-B0xqG7Eu.mjs.map +0 -1
- package/dist/operators-Cgz7QUVJ.cjs.map +0 -1
package/dist/browser.mjs
CHANGED
|
@@ -9094,6 +9094,8 @@ const MEDIA_ACCESS_DENIAL_NAMES = [
|
|
|
9094
9094
|
"SecurityError",
|
|
9095
9095
|
"PermissionDeniedError"
|
|
9096
9096
|
];
|
|
9097
|
+
/** Error names browsers use when the capture hardware is already held exclusively. */
|
|
9098
|
+
const MEDIA_DEVICE_IN_USE_NAMES = ["NotReadableError", "TrackStartError"];
|
|
9097
9099
|
/** Default polling interval for RTCPeerConnection.getStats() in milliseconds. */
|
|
9098
9100
|
const DEFAULT_STATS_POLLING_INTERVAL_MS = 1e3;
|
|
9099
9101
|
/** Number of initial samples used to build a baseline for spike detection. */
|
|
@@ -9432,6 +9434,10 @@ var MediaTrackError = class extends Error {
|
|
|
9432
9434
|
function isMediaAccessDenial(originalError) {
|
|
9433
9435
|
return originalError instanceof Error && MEDIA_ACCESS_DENIAL_NAMES.includes(originalError.name);
|
|
9434
9436
|
}
|
|
9437
|
+
/** True when a `getUserMedia` rejection means the hardware is already held exclusively. */
|
|
9438
|
+
function isMediaDeviceInUse(originalError) {
|
|
9439
|
+
return originalError instanceof Error && MEDIA_DEVICE_IN_USE_NAMES.includes(originalError.name);
|
|
9440
|
+
}
|
|
9435
9441
|
/**
|
|
9436
9442
|
* Failure to acquire local media (camera, microphone, or screen capture)
|
|
9437
9443
|
* via `getUserMedia`/`getDisplayMedia`.
|
|
@@ -14248,6 +14254,8 @@ var SelfParticipant = class extends Participant {
|
|
|
14248
14254
|
*
|
|
14249
14255
|
* The call is unaffected when acquisition fails.
|
|
14250
14256
|
*
|
|
14257
|
+
* @param options - Pass `{ audio: true }` to also request the shared
|
|
14258
|
+
* surface's audio. Defaults to video only.
|
|
14251
14259
|
* @throws {ScreenShareAlreadyActiveError} When this call is already
|
|
14252
14260
|
* sharing a screen. Call {@link stopScreenShare} before starting another.
|
|
14253
14261
|
* @throws {AuxiliaryLegCancelledError} When {@link stopScreenShare} removes
|
|
@@ -14256,9 +14264,9 @@ var SelfParticipant = class extends Participant {
|
|
|
14256
14264
|
* permission denial rejects with a `NotAllowedError` `DOMException` —
|
|
14257
14265
|
* inspect `error.name` to tell benign cancels apart from real failures.
|
|
14258
14266
|
*/
|
|
14259
|
-
async startScreenShare() {
|
|
14267
|
+
async startScreenShare(options) {
|
|
14260
14268
|
try {
|
|
14261
|
-
await this.vertoManager.addScreenMedia();
|
|
14269
|
+
await this.vertoManager.addScreenMedia(options);
|
|
14262
14270
|
} catch (error) {
|
|
14263
14271
|
if (error instanceof AuxiliaryLegCancelledError) logger$25.debug("[Participant.startScreenShare] Screen share cancelled before connecting.");
|
|
14264
14272
|
else logger$25.error("[Participant.startScreenShare] Screen share error:", error);
|
|
@@ -15608,10 +15616,11 @@ var LocalStreamController = class extends Destroyable {
|
|
|
15608
15616
|
stream = new MediaStream(tracks);
|
|
15609
15617
|
this.tagTracks(tracks, "application");
|
|
15610
15618
|
} else if (this.options.propose === "screenshare") {
|
|
15611
|
-
|
|
15619
|
+
const audio = this.options.screenShareAudio ?? false;
|
|
15620
|
+
logger$19.debug("[LocalStreamController] Requesting display media for screen sharing with audio:", audio);
|
|
15612
15621
|
stream = await this.options.getDisplayMedia({
|
|
15613
15622
|
video: true,
|
|
15614
|
-
audio
|
|
15623
|
+
audio
|
|
15615
15624
|
});
|
|
15616
15625
|
logger$19.debug("[LocalStreamController] Screen share media obtained:", stream);
|
|
15617
15626
|
this.tagTracks(stream.getTracks(), "display");
|
|
@@ -16047,38 +16056,32 @@ var RTCPeerConnectionController = class extends Destroyable {
|
|
|
16047
16056
|
this.negotiationNeeded$.next();
|
|
16048
16057
|
};
|
|
16049
16058
|
this.updateSelectedInputDevice = async (kind, deviceInfo) => {
|
|
16059
|
+
const { localStream } = this;
|
|
16060
|
+
if (!localStream) {
|
|
16061
|
+
logger$17.warn("[RTCPeerConnectionController] No local stream available to update input device.");
|
|
16062
|
+
return;
|
|
16063
|
+
}
|
|
16064
|
+
const currentTrack = localStream.getTracks().find((track) => track.kind === kind);
|
|
16065
|
+
if (!currentTrack) {
|
|
16066
|
+
logger$17.debug(`[RTCPeerConnectionController] No ${kind} track to switch.`);
|
|
16067
|
+
return;
|
|
16068
|
+
}
|
|
16069
|
+
if (!deviceInfo) {
|
|
16070
|
+
logger$17.debug(`[RTCPeerConnectionController] ${kind} input device selected: none`);
|
|
16071
|
+
this.stopTrackSender(kind);
|
|
16072
|
+
return;
|
|
16073
|
+
}
|
|
16074
|
+
const constraints = {
|
|
16075
|
+
...currentTrack.getConstraints(),
|
|
16076
|
+
...this.deviceController.deviceInfoToConstraints(deviceInfo)
|
|
16077
|
+
};
|
|
16050
16078
|
try {
|
|
16051
|
-
const
|
|
16052
|
-
|
|
16053
|
-
|
|
16054
|
-
return;
|
|
16055
|
-
}
|
|
16056
|
-
logger$17.debug(`[RTCPeerConnectionController] Updating selected ${kind} input device:`, localStream.getTracks());
|
|
16057
|
-
const track = localStream.getTracks().find((track$1) => track$1.kind === kind);
|
|
16058
|
-
if (track) {
|
|
16059
|
-
this.transceiverController?.stopTrackSender(kind);
|
|
16060
|
-
this.localStreamController.removeTrack(track.id);
|
|
16061
|
-
logger$17.debug(`[RTCPeerConnectionController] Stopped existing ${kind} track: ${track.id}`, localStream.getTracks());
|
|
16062
|
-
if (!deviceInfo) {
|
|
16063
|
-
logger$17.debug(`[RTCPeerConnectionController] ${kind} input device selected: none`);
|
|
16064
|
-
return;
|
|
16065
|
-
}
|
|
16066
|
-
const streamTrack = (await this.getUserMedia({ [kind]: {
|
|
16067
|
-
...track.getConstraints(),
|
|
16068
|
-
...this.deviceController.deviceInfoToConstraints(deviceInfo)
|
|
16069
|
-
} })).getTracks().find((t) => t.kind === kind);
|
|
16070
|
-
if (streamTrack) {
|
|
16071
|
-
logger$17.debug(`[RTCPeerConnectionController] Adding new ${kind} track: ${streamTrack.id}`);
|
|
16072
|
-
this.localStreamController.addTrack(streamTrack);
|
|
16073
|
-
await this.transceiverController?.replaceSenderTrack(kind, streamTrack);
|
|
16074
|
-
logger$17.debug(`[RTCPeerConnectionController] Added new ${kind} track: ${streamTrack.id}`, this.localStream?.getTracks());
|
|
16075
|
-
}
|
|
16076
|
-
}
|
|
16077
|
-
logger$17.debug(`[RTCPeerConnectionController] ${kind} input device selected:`, deviceInfo?.label);
|
|
16079
|
+
const newTrack = await this.acquireInputTrack(kind, constraints, deviceInfo, currentTrack);
|
|
16080
|
+
await this.attachInputTrack(kind, newTrack, currentTrack);
|
|
16081
|
+
logger$17.debug(`[RTCPeerConnectionController] ${kind} input device selected:`, deviceInfo.label, newTrack.id);
|
|
16078
16082
|
} catch (error) {
|
|
16079
16083
|
logger$17.error(`[RTCPeerConnectionController] Failed to select ${kind} input device:`, error);
|
|
16080
16084
|
this._errors$.next(new MediaTrackError("updateSelectedInputDevice", kind, error));
|
|
16081
|
-
throw error;
|
|
16082
16085
|
}
|
|
16083
16086
|
};
|
|
16084
16087
|
this._isNegotiating$ = this.createBehaviorSubject(false);
|
|
@@ -16123,6 +16126,7 @@ var RTCPeerConnectionController = class extends Destroyable {
|
|
|
16123
16126
|
inputVideoStream: this.options.inputVideoStream,
|
|
16124
16127
|
inputAudioDeviceConstraints: this.inputAudioDeviceConstraints,
|
|
16125
16128
|
inputVideoDeviceConstraints: this.inputVideoDeviceConstraints,
|
|
16129
|
+
screenShareAudio: this.options.screenShareAudio,
|
|
16126
16130
|
getUserMedia: async (constraints) => this.getUserMedia(constraints),
|
|
16127
16131
|
getDisplayMedia: async (options$1) => this.getDisplayMedia(options$1)
|
|
16128
16132
|
});
|
|
@@ -16780,6 +16784,61 @@ var RTCPeerConnectionController = class extends Destroyable {
|
|
|
16780
16784
|
this._localAudioPipeline.setInputTrack(newTrack);
|
|
16781
16785
|
}
|
|
16782
16786
|
/**
|
|
16787
|
+
* Capture the newly selected device, leaving the current capture running.
|
|
16788
|
+
*
|
|
16789
|
+
* A rejection must leave the current track sending, so nothing is released
|
|
16790
|
+
* until the replacement is in hand. The one exception is hardware that admits
|
|
16791
|
+
* a single opener — a phone's front and back cameras, typically — which
|
|
16792
|
+
* rejects the second capture until the first is closed.
|
|
16793
|
+
*/
|
|
16794
|
+
async acquireInputTrack(kind, constraints, deviceInfo, currentTrack) {
|
|
16795
|
+
try {
|
|
16796
|
+
return await this.captureTrack(kind, constraints, deviceInfo.deviceId);
|
|
16797
|
+
} catch (error) {
|
|
16798
|
+
if (!isMediaDeviceInUse(error)) throw error;
|
|
16799
|
+
logger$17.warn(`[RTCPeerConnectionController] ${kind} device is held exclusively; releasing the current capture to retry:`, error);
|
|
16800
|
+
const previousDeviceId = currentTrack.getSettings().deviceId;
|
|
16801
|
+
this.stopTrackSender(kind);
|
|
16802
|
+
try {
|
|
16803
|
+
return await this.captureTrack(kind, constraints, deviceInfo.deviceId);
|
|
16804
|
+
} catch (retryError) {
|
|
16805
|
+
await this.restorePreviousInputTrack(kind, constraints, previousDeviceId, currentTrack);
|
|
16806
|
+
throw retryError;
|
|
16807
|
+
}
|
|
16808
|
+
}
|
|
16809
|
+
}
|
|
16810
|
+
async captureTrack(kind, constraints, deviceId) {
|
|
16811
|
+
const { stream, fallbackLevel } = await getUserMediaWithFallback({ getUserMedia: async (c) => this.getUserMedia(c) }, { [kind]: constraints }, kind, deviceId);
|
|
16812
|
+
const track = stream.getTracks().find((t) => t.kind === kind);
|
|
16813
|
+
if (!track) {
|
|
16814
|
+
stream.getTracks().forEach((t) => t.stop());
|
|
16815
|
+
throw new MediaTrackError("updateSelectedInputDevice", kind, /* @__PURE__ */ new Error("getUserMedia returned no track of the requested kind"));
|
|
16816
|
+
}
|
|
16817
|
+
if (fallbackLevel !== "exact") logger$17.warn(`[RTCPeerConnectionController] ${kind} device acquired at fallback level '${fallbackLevel}'; the capture may not be the requested device.`);
|
|
16818
|
+
return track;
|
|
16819
|
+
}
|
|
16820
|
+
/** Best-effort return to the device that was released for an exclusive retry. */
|
|
16821
|
+
async restorePreviousInputTrack(kind, constraints, previousDeviceId, releasedTrack) {
|
|
16822
|
+
try {
|
|
16823
|
+
const restored = await this.captureTrack(kind, constraints, previousDeviceId);
|
|
16824
|
+
await this.attachInputTrack(kind, restored, releasedTrack);
|
|
16825
|
+
} catch (error) {
|
|
16826
|
+
logger$17.error(`[RTCPeerConnectionController] Failed to restore the previous ${kind} device:`, error);
|
|
16827
|
+
}
|
|
16828
|
+
}
|
|
16829
|
+
async attachInputTrack(kind, newTrack, oldTrack) {
|
|
16830
|
+
const pipelineOwnsAudio = kind === "audio" && this._localAudioPipeline;
|
|
16831
|
+
if (!pipelineOwnsAudio) try {
|
|
16832
|
+
await this.transceiverController?.replaceSenderTrack(kind, newTrack);
|
|
16833
|
+
} catch (error) {
|
|
16834
|
+
newTrack.stop();
|
|
16835
|
+
throw error;
|
|
16836
|
+
}
|
|
16837
|
+
this.localStreamController.removeTrack(oldTrack.id);
|
|
16838
|
+
this.localStreamController.addTrack(newTrack);
|
|
16839
|
+
if (pipelineOwnsAudio) this._localAudioPipeline?.setInputTrack(newTrack);
|
|
16840
|
+
}
|
|
16841
|
+
/**
|
|
16783
16842
|
* Return the lazily-created {@link LocalAudioPipeline}, constructing it on
|
|
16784
16843
|
* first access. On creation the current audio sender's track is routed
|
|
16785
16844
|
* through the pipeline (input → gain → analyser → destination) and the
|
|
@@ -17855,8 +17914,11 @@ var WebRTCVertoManager = class extends VertoManager {
|
|
|
17855
17914
|
throw error;
|
|
17856
17915
|
}
|
|
17857
17916
|
}
|
|
17858
|
-
async addScreenMedia(options = {
|
|
17859
|
-
await this.initAdditionalPeerConnection("screenshare",
|
|
17917
|
+
async addScreenMedia(options = {}) {
|
|
17918
|
+
await this.initAdditionalPeerConnection("screenshare", {
|
|
17919
|
+
audio: false,
|
|
17920
|
+
screenShareAudio: options.audio ?? false
|
|
17921
|
+
});
|
|
17860
17922
|
}
|
|
17861
17923
|
async initAdditionalPeerConnection(propose, options) {
|
|
17862
17924
|
const isScreenShare = propose === "screenshare";
|