@stream-io/video-client 1.0.1 → 1.0.3
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/CHANGELOG.md +14 -0
- package/dist/index.browser.es.js +108 -60
- package/dist/index.browser.es.js.map +1 -1
- package/dist/index.cjs.js +108 -60
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.es.js +108 -60
- package/dist/index.es.js.map +1 -1
- package/dist/src/devices/InputMediaDeviceManager.d.ts +6 -7
- package/dist/src/devices/InputMediaDeviceManagerState.d.ts +14 -0
- package/package.json +1 -1
- package/src/Call.ts +6 -13
- package/src/devices/CameraManager.ts +2 -2
- package/src/devices/InputMediaDeviceManager.ts +59 -37
- package/src/devices/InputMediaDeviceManagerState.ts +25 -0
- package/src/rtc/Publisher.ts +17 -14
- package/src/rtc/Subscriber.ts +19 -14
package/dist/index.cjs.js
CHANGED
|
@@ -8420,15 +8420,19 @@ class Publisher {
|
|
|
8420
8420
|
return getPreferredCodecs('audio', preferredCodec ?? defaultAudioCodec, codecToRemove);
|
|
8421
8421
|
}
|
|
8422
8422
|
};
|
|
8423
|
-
this.onIceCandidate =
|
|
8423
|
+
this.onIceCandidate = (e) => {
|
|
8424
8424
|
const { candidate } = e;
|
|
8425
8425
|
if (!candidate) {
|
|
8426
8426
|
logger$3('debug', 'null ice candidate');
|
|
8427
8427
|
return;
|
|
8428
8428
|
}
|
|
8429
|
-
|
|
8429
|
+
this.sfuClient
|
|
8430
|
+
.iceTrickle({
|
|
8430
8431
|
iceCandidate: getIceCandidate(candidate),
|
|
8431
8432
|
peerType: PeerType.PUBLISHER_UNSPECIFIED,
|
|
8433
|
+
})
|
|
8434
|
+
.catch((err) => {
|
|
8435
|
+
logger$3('warn', `ICETrickle failed`, err);
|
|
8432
8436
|
});
|
|
8433
8437
|
};
|
|
8434
8438
|
/**
|
|
@@ -8469,8 +8473,8 @@ class Publisher {
|
|
|
8469
8473
|
}
|
|
8470
8474
|
await this.negotiate({ iceRestart: true });
|
|
8471
8475
|
};
|
|
8472
|
-
this.onNegotiationNeeded =
|
|
8473
|
-
|
|
8476
|
+
this.onNegotiationNeeded = () => {
|
|
8477
|
+
this.negotiate().catch((err) => logger$3('warn', `Negotiation failed.`, err));
|
|
8474
8478
|
};
|
|
8475
8479
|
/**
|
|
8476
8480
|
* Initiates a new offer/answer exchange with the currently connected SFU.
|
|
@@ -8660,10 +8664,12 @@ class Publisher {
|
|
|
8660
8664
|
this.isDtxEnabled = isDtxEnabled;
|
|
8661
8665
|
this.isRedEnabled = isRedEnabled;
|
|
8662
8666
|
this.iceRestartDelay = iceRestartDelay;
|
|
8663
|
-
this.unsubscribeOnIceRestart = dispatcher.on('iceRestart',
|
|
8667
|
+
this.unsubscribeOnIceRestart = dispatcher.on('iceRestart', (iceRestart) => {
|
|
8664
8668
|
if (iceRestart.peerType !== PeerType.PUBLISHER_UNSPECIFIED)
|
|
8665
8669
|
return;
|
|
8666
|
-
|
|
8670
|
+
this.restartIce().catch((err) => {
|
|
8671
|
+
logger$3('warn', `ICERestart failed`, err);
|
|
8672
|
+
});
|
|
8667
8673
|
});
|
|
8668
8674
|
}
|
|
8669
8675
|
}
|
|
@@ -8857,15 +8863,19 @@ class Subscriber {
|
|
|
8857
8863
|
[streamKindProp]: primaryStream,
|
|
8858
8864
|
});
|
|
8859
8865
|
};
|
|
8860
|
-
this.onIceCandidate =
|
|
8866
|
+
this.onIceCandidate = (e) => {
|
|
8861
8867
|
const { candidate } = e;
|
|
8862
8868
|
if (!candidate) {
|
|
8863
8869
|
logger$2('debug', 'null ice candidate');
|
|
8864
8870
|
return;
|
|
8865
8871
|
}
|
|
8866
|
-
|
|
8872
|
+
this.sfuClient
|
|
8873
|
+
.iceTrickle({
|
|
8867
8874
|
iceCandidate: getIceCandidate(candidate),
|
|
8868
8875
|
peerType: PeerType.SUBSCRIBER,
|
|
8876
|
+
})
|
|
8877
|
+
.catch((err) => {
|
|
8878
|
+
logger$2('warn', `ICETrickle failed`, err);
|
|
8869
8879
|
});
|
|
8870
8880
|
};
|
|
8871
8881
|
this.negotiate = async (subscriberOffer) => {
|
|
@@ -8937,13 +8947,17 @@ class Subscriber {
|
|
|
8937
8947
|
this.state = state;
|
|
8938
8948
|
this.iceRestartDelay = iceRestartDelay;
|
|
8939
8949
|
this.pc = this.createPeerConnection(connectionConfig);
|
|
8940
|
-
this.unregisterOnSubscriberOffer = dispatcher.on('subscriberOffer',
|
|
8941
|
-
|
|
8950
|
+
this.unregisterOnSubscriberOffer = dispatcher.on('subscriberOffer', (subscriberOffer) => {
|
|
8951
|
+
this.negotiate(subscriberOffer).catch((err) => {
|
|
8952
|
+
logger$2('warn', `Negotiation failed.`, err);
|
|
8953
|
+
});
|
|
8942
8954
|
});
|
|
8943
|
-
this.unregisterOnIceRestart = dispatcher.on('iceRestart',
|
|
8955
|
+
this.unregisterOnIceRestart = dispatcher.on('iceRestart', (iceRestart) => {
|
|
8944
8956
|
if (iceRestart.peerType !== PeerType.SUBSCRIBER)
|
|
8945
8957
|
return;
|
|
8946
|
-
|
|
8958
|
+
this.restartIce().catch((err) => {
|
|
8959
|
+
logger$2('warn', `ICERestart failed`, err);
|
|
8960
|
+
});
|
|
8947
8961
|
});
|
|
8948
8962
|
}
|
|
8949
8963
|
}
|
|
@@ -10890,18 +10904,27 @@ class InputMediaDeviceManager {
|
|
|
10890
10904
|
* Starts stream.
|
|
10891
10905
|
*/
|
|
10892
10906
|
async enable() {
|
|
10893
|
-
if (this.state.
|
|
10907
|
+
if (this.state.optimisticStatus === 'enabled') {
|
|
10908
|
+
await this.statusChangePromise;
|
|
10894
10909
|
return;
|
|
10895
|
-
this.enablePromise = this.unmuteStream();
|
|
10896
|
-
try {
|
|
10897
|
-
await this.enablePromise;
|
|
10898
|
-
this.state.setStatus('enabled');
|
|
10899
|
-
this.enablePromise = undefined;
|
|
10900
|
-
}
|
|
10901
|
-
catch (error) {
|
|
10902
|
-
this.enablePromise = undefined;
|
|
10903
|
-
throw error;
|
|
10904
10910
|
}
|
|
10911
|
+
const signal = this.nextAbortableStatusChangeRequest('enabled');
|
|
10912
|
+
const doEnable = async () => {
|
|
10913
|
+
if (signal.aborted)
|
|
10914
|
+
return;
|
|
10915
|
+
try {
|
|
10916
|
+
await this.unmuteStream();
|
|
10917
|
+
this.state.setStatus('enabled');
|
|
10918
|
+
}
|
|
10919
|
+
finally {
|
|
10920
|
+
if (!signal.aborted)
|
|
10921
|
+
this.resetStatusChangeRequest();
|
|
10922
|
+
}
|
|
10923
|
+
};
|
|
10924
|
+
this.statusChangePromise = this.statusChangePromise
|
|
10925
|
+
? this.statusChangePromise.then(doEnable)
|
|
10926
|
+
: doEnable();
|
|
10927
|
+
await this.statusChangePromise;
|
|
10905
10928
|
}
|
|
10906
10929
|
/**
|
|
10907
10930
|
* Stops or pauses the stream based on state.disableMode
|
|
@@ -10909,19 +10932,28 @@ class InputMediaDeviceManager {
|
|
|
10909
10932
|
*/
|
|
10910
10933
|
async disable(forceStop = false) {
|
|
10911
10934
|
this.state.prevStatus = this.state.status;
|
|
10912
|
-
if (!forceStop && this.state.
|
|
10935
|
+
if (!forceStop && this.state.optimisticStatus === 'disabled') {
|
|
10936
|
+
await this.statusChangePromise;
|
|
10913
10937
|
return;
|
|
10914
|
-
const stopTracks = forceStop || this.state.disableMode === 'stop-tracks';
|
|
10915
|
-
this.disablePromise = this.muteStream(stopTracks);
|
|
10916
|
-
try {
|
|
10917
|
-
await this.disablePromise;
|
|
10918
|
-
this.state.setStatus('disabled');
|
|
10919
|
-
this.disablePromise = undefined;
|
|
10920
|
-
}
|
|
10921
|
-
catch (error) {
|
|
10922
|
-
this.disablePromise = undefined;
|
|
10923
|
-
throw error;
|
|
10924
10938
|
}
|
|
10939
|
+
const stopTracks = forceStop || this.state.disableMode === 'stop-tracks';
|
|
10940
|
+
const signal = this.nextAbortableStatusChangeRequest('disabled');
|
|
10941
|
+
const doDisable = async () => {
|
|
10942
|
+
if (signal.aborted)
|
|
10943
|
+
return;
|
|
10944
|
+
try {
|
|
10945
|
+
await this.muteStream(stopTracks);
|
|
10946
|
+
this.state.setStatus('disabled');
|
|
10947
|
+
}
|
|
10948
|
+
finally {
|
|
10949
|
+
if (!signal.aborted)
|
|
10950
|
+
this.resetStatusChangeRequest();
|
|
10951
|
+
}
|
|
10952
|
+
};
|
|
10953
|
+
this.statusChangePromise = this.statusChangePromise
|
|
10954
|
+
? this.statusChangePromise.then(doDisable)
|
|
10955
|
+
: doDisable();
|
|
10956
|
+
await this.statusChangePromise;
|
|
10925
10957
|
}
|
|
10926
10958
|
/**
|
|
10927
10959
|
* If status was previously enabled, it will re-enable the device.
|
|
@@ -10937,7 +10969,7 @@ class InputMediaDeviceManager {
|
|
|
10937
10969
|
* else it will disable it.
|
|
10938
10970
|
*/
|
|
10939
10971
|
async toggle() {
|
|
10940
|
-
if (this.state.
|
|
10972
|
+
if (this.state.optimisticStatus === 'enabled') {
|
|
10941
10973
|
return this.disable();
|
|
10942
10974
|
}
|
|
10943
10975
|
else {
|
|
@@ -11124,11 +11156,8 @@ class InputMediaDeviceManager {
|
|
|
11124
11156
|
this.state.setMediaStream(stream, await rootStream);
|
|
11125
11157
|
this.getTracks().forEach((track) => {
|
|
11126
11158
|
track.addEventListener('ended', async () => {
|
|
11127
|
-
if (this.
|
|
11128
|
-
await this.
|
|
11129
|
-
}
|
|
11130
|
-
if (this.disablePromise) {
|
|
11131
|
-
await this.disablePromise;
|
|
11159
|
+
if (this.statusChangePromise) {
|
|
11160
|
+
await this.statusChangePromise;
|
|
11132
11161
|
}
|
|
11133
11162
|
if (this.state.status === 'enabled') {
|
|
11134
11163
|
this.isTrackStoppedDueToTrackEnd = true;
|
|
@@ -11158,11 +11187,8 @@ class InputMediaDeviceManager {
|
|
|
11158
11187
|
if (!deviceId) {
|
|
11159
11188
|
return;
|
|
11160
11189
|
}
|
|
11161
|
-
if (this.
|
|
11162
|
-
await this.
|
|
11163
|
-
}
|
|
11164
|
-
if (this.disablePromise) {
|
|
11165
|
-
await this.disablePromise;
|
|
11190
|
+
if (this.statusChangePromise) {
|
|
11191
|
+
await this.statusChangePromise;
|
|
11166
11192
|
}
|
|
11167
11193
|
let isDeviceDisconnected = false;
|
|
11168
11194
|
let isDeviceReplaced = false;
|
|
@@ -11196,6 +11222,16 @@ class InputMediaDeviceManager {
|
|
|
11196
11222
|
findDeviceInList(devices, deviceId) {
|
|
11197
11223
|
return devices.find((d) => d.deviceId === deviceId && d.kind === this.mediaDeviceKind);
|
|
11198
11224
|
}
|
|
11225
|
+
nextAbortableStatusChangeRequest(status) {
|
|
11226
|
+
this.statusChangeAbortController?.abort();
|
|
11227
|
+
this.statusChangeAbortController = new AbortController();
|
|
11228
|
+
this.state.setPendingStatus(status);
|
|
11229
|
+
return this.statusChangeAbortController.signal;
|
|
11230
|
+
}
|
|
11231
|
+
resetStatusChangeRequest() {
|
|
11232
|
+
this.statusChangePromise = undefined;
|
|
11233
|
+
this.statusChangeAbortController = undefined;
|
|
11234
|
+
}
|
|
11199
11235
|
}
|
|
11200
11236
|
|
|
11201
11237
|
class InputMediaDeviceManagerState {
|
|
@@ -11210,6 +11246,7 @@ class InputMediaDeviceManagerState {
|
|
|
11210
11246
|
this.disableMode = disableMode;
|
|
11211
11247
|
this.permissionName = permissionName;
|
|
11212
11248
|
this.statusSubject = new rxjs.BehaviorSubject(undefined);
|
|
11249
|
+
this.optimisticStatusSubject = new rxjs.BehaviorSubject(undefined);
|
|
11213
11250
|
this.mediaStreamSubject = new rxjs.BehaviorSubject(undefined);
|
|
11214
11251
|
this.selectedDeviceSubject = new rxjs.BehaviorSubject(undefined);
|
|
11215
11252
|
this.defaultConstraintsSubject = new rxjs.BehaviorSubject(undefined);
|
|
@@ -11228,6 +11265,12 @@ class InputMediaDeviceManagerState {
|
|
|
11228
11265
|
* An Observable that emits the device status
|
|
11229
11266
|
*/
|
|
11230
11267
|
this.status$ = this.statusSubject.asObservable().pipe(rxjs.distinctUntilChanged());
|
|
11268
|
+
/**
|
|
11269
|
+
* An Observable the reflects the requested device status. Useful for optimistic UIs
|
|
11270
|
+
*/
|
|
11271
|
+
this.optimisticStatus$ = this.optimisticStatusSubject
|
|
11272
|
+
.asObservable()
|
|
11273
|
+
.pipe(rxjs.distinctUntilChanged());
|
|
11231
11274
|
/**
|
|
11232
11275
|
* The default constraints for the device.
|
|
11233
11276
|
*/
|
|
@@ -11295,6 +11338,12 @@ class InputMediaDeviceManagerState {
|
|
|
11295
11338
|
get status() {
|
|
11296
11339
|
return this.getCurrentValue(this.status$);
|
|
11297
11340
|
}
|
|
11341
|
+
/**
|
|
11342
|
+
* The requested device status. Useful for optimistic UIs
|
|
11343
|
+
*/
|
|
11344
|
+
get optimisticStatus() {
|
|
11345
|
+
return this.getCurrentValue(this.optimisticStatus$);
|
|
11346
|
+
}
|
|
11298
11347
|
/**
|
|
11299
11348
|
* The currently selected device
|
|
11300
11349
|
*/
|
|
@@ -11314,6 +11363,13 @@ class InputMediaDeviceManagerState {
|
|
|
11314
11363
|
setStatus(status) {
|
|
11315
11364
|
this.setCurrentValue(this.statusSubject, status);
|
|
11316
11365
|
}
|
|
11366
|
+
/**
|
|
11367
|
+
* @internal
|
|
11368
|
+
* @param pendingStatus
|
|
11369
|
+
*/
|
|
11370
|
+
setPendingStatus(pendingStatus) {
|
|
11371
|
+
this.setCurrentValue(this.optimisticStatusSubject, pendingStatus);
|
|
11372
|
+
}
|
|
11317
11373
|
/**
|
|
11318
11374
|
* Updates the `mediaStream` state variable.
|
|
11319
11375
|
*
|
|
@@ -11433,9 +11489,9 @@ class CameraManager extends InputMediaDeviceManager {
|
|
|
11433
11489
|
async selectTargetResolution(resolution) {
|
|
11434
11490
|
this.targetResolution.height = resolution.height;
|
|
11435
11491
|
this.targetResolution.width = resolution.width;
|
|
11436
|
-
if (this.
|
|
11492
|
+
if (this.statusChangePromise && this.state.optimisticStatus === 'enabled') {
|
|
11437
11493
|
try {
|
|
11438
|
-
await this.
|
|
11494
|
+
await this.statusChangePromise;
|
|
11439
11495
|
}
|
|
11440
11496
|
catch (error) {
|
|
11441
11497
|
// couldn't enable device, target resolution will be applied the next time user attempts to start the device
|
|
@@ -13467,7 +13523,9 @@ class Call {
|
|
|
13467
13523
|
});
|
|
13468
13524
|
this.leaveCallHooks.add(registerEventHandlers(this, this.state, this.dispatcher));
|
|
13469
13525
|
this.registerEffects();
|
|
13470
|
-
this.leaveCallHooks.add(createSubscription(this.trackSubscriptionsSubject.pipe(rxjs.debounce((v) => rxjs.timer(v.type)), rxjs.map((v) => v.data)), (subscriptions) => this.sfuClient?.updateSubscriptions(subscriptions))
|
|
13526
|
+
this.leaveCallHooks.add(createSubscription(this.trackSubscriptionsSubject.pipe(rxjs.debounce((v) => rxjs.timer(v.type)), rxjs.map((v) => v.data)), (subscriptions) => this.sfuClient?.updateSubscriptions(subscriptions).catch((err) => {
|
|
13527
|
+
this.logger('debug', `Failed to update track subscriptions`, err);
|
|
13528
|
+
})));
|
|
13471
13529
|
this.camera = new CameraManager(this);
|
|
13472
13530
|
this.microphone = new MicrophoneManager(this);
|
|
13473
13531
|
this.speaker = new SpeakerManager(this);
|
|
@@ -13576,12 +13634,7 @@ class Call {
|
|
|
13576
13634
|
}
|
|
13577
13635
|
async initCamera(options) {
|
|
13578
13636
|
// Wait for any in progress camera operation
|
|
13579
|
-
|
|
13580
|
-
await this.camera.enablePromise;
|
|
13581
|
-
}
|
|
13582
|
-
if (this.camera.disablePromise) {
|
|
13583
|
-
await this.camera.disablePromise;
|
|
13584
|
-
}
|
|
13637
|
+
await this.camera.statusChangePromise;
|
|
13585
13638
|
if (this.state.localParticipant?.videoStream ||
|
|
13586
13639
|
!this.permissionsContext.hasPermission('send-video')) {
|
|
13587
13640
|
return;
|
|
@@ -13618,12 +13671,7 @@ class Call {
|
|
|
13618
13671
|
}
|
|
13619
13672
|
async initMic(options) {
|
|
13620
13673
|
// Wait for any in progress mic operation
|
|
13621
|
-
|
|
13622
|
-
await this.microphone.enablePromise;
|
|
13623
|
-
}
|
|
13624
|
-
if (this.microphone.disablePromise) {
|
|
13625
|
-
await this.microphone.disablePromise;
|
|
13626
|
-
}
|
|
13674
|
+
await this.microphone.statusChangePromise;
|
|
13627
13675
|
if (this.state.localParticipant?.audioStream ||
|
|
13628
13676
|
!this.permissionsContext.hasPermission('send-audio')) {
|
|
13629
13677
|
return;
|
|
@@ -15164,7 +15212,7 @@ class StreamClient {
|
|
|
15164
15212
|
});
|
|
15165
15213
|
};
|
|
15166
15214
|
this.getUserAgent = () => {
|
|
15167
|
-
const version = "1.0.
|
|
15215
|
+
const version = "1.0.3" ;
|
|
15168
15216
|
return (this.userAgent ||
|
|
15169
15217
|
`stream-video-javascript-client-${this.node ? 'node' : 'browser'}-${version}`);
|
|
15170
15218
|
};
|