@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/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
This file was generated using [@jscutlery/semver](https://github.com/jscutlery/semver).
|
|
4
4
|
|
|
5
|
+
### [1.0.3](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-client-1.0.2...@stream-io/video-client-1.0.3) (2024-05-13)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Bug Fixes
|
|
9
|
+
|
|
10
|
+
* improve error handling across the SDK ([#1350](https://github.com/GetStream/stream-video-js/issues/1350)) ([ac0ae3b](https://github.com/GetStream/stream-video-js/commit/ac0ae3b7d5da91152d0f41a203b73e6c99c42ff9))
|
|
11
|
+
|
|
12
|
+
### [1.0.2](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-client-1.0.1...@stream-io/video-client-1.0.2) (2024-05-13)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
### Bug Fixes
|
|
16
|
+
|
|
17
|
+
* optimistically toggle device status ([#1342](https://github.com/GetStream/stream-video-js/issues/1342)) ([2e4e470](https://github.com/GetStream/stream-video-js/commit/2e4e470347fce7c7499dd21a931e5dec74bf9618))
|
|
18
|
+
|
|
5
19
|
### [1.0.1](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-client-1.0.0...@stream-io/video-client-1.0.1) (2024-05-07)
|
|
6
20
|
|
|
7
21
|
|
package/dist/index.browser.es.js
CHANGED
|
@@ -8399,15 +8399,19 @@ class Publisher {
|
|
|
8399
8399
|
return getPreferredCodecs('audio', preferredCodec ?? defaultAudioCodec, codecToRemove);
|
|
8400
8400
|
}
|
|
8401
8401
|
};
|
|
8402
|
-
this.onIceCandidate =
|
|
8402
|
+
this.onIceCandidate = (e) => {
|
|
8403
8403
|
const { candidate } = e;
|
|
8404
8404
|
if (!candidate) {
|
|
8405
8405
|
logger$3('debug', 'null ice candidate');
|
|
8406
8406
|
return;
|
|
8407
8407
|
}
|
|
8408
|
-
|
|
8408
|
+
this.sfuClient
|
|
8409
|
+
.iceTrickle({
|
|
8409
8410
|
iceCandidate: getIceCandidate(candidate),
|
|
8410
8411
|
peerType: PeerType.PUBLISHER_UNSPECIFIED,
|
|
8412
|
+
})
|
|
8413
|
+
.catch((err) => {
|
|
8414
|
+
logger$3('warn', `ICETrickle failed`, err);
|
|
8411
8415
|
});
|
|
8412
8416
|
};
|
|
8413
8417
|
/**
|
|
@@ -8448,8 +8452,8 @@ class Publisher {
|
|
|
8448
8452
|
}
|
|
8449
8453
|
await this.negotiate({ iceRestart: true });
|
|
8450
8454
|
};
|
|
8451
|
-
this.onNegotiationNeeded =
|
|
8452
|
-
|
|
8455
|
+
this.onNegotiationNeeded = () => {
|
|
8456
|
+
this.negotiate().catch((err) => logger$3('warn', `Negotiation failed.`, err));
|
|
8453
8457
|
};
|
|
8454
8458
|
/**
|
|
8455
8459
|
* Initiates a new offer/answer exchange with the currently connected SFU.
|
|
@@ -8639,10 +8643,12 @@ class Publisher {
|
|
|
8639
8643
|
this.isDtxEnabled = isDtxEnabled;
|
|
8640
8644
|
this.isRedEnabled = isRedEnabled;
|
|
8641
8645
|
this.iceRestartDelay = iceRestartDelay;
|
|
8642
|
-
this.unsubscribeOnIceRestart = dispatcher.on('iceRestart',
|
|
8646
|
+
this.unsubscribeOnIceRestart = dispatcher.on('iceRestart', (iceRestart) => {
|
|
8643
8647
|
if (iceRestart.peerType !== PeerType.PUBLISHER_UNSPECIFIED)
|
|
8644
8648
|
return;
|
|
8645
|
-
|
|
8649
|
+
this.restartIce().catch((err) => {
|
|
8650
|
+
logger$3('warn', `ICERestart failed`, err);
|
|
8651
|
+
});
|
|
8646
8652
|
});
|
|
8647
8653
|
}
|
|
8648
8654
|
}
|
|
@@ -8836,15 +8842,19 @@ class Subscriber {
|
|
|
8836
8842
|
[streamKindProp]: primaryStream,
|
|
8837
8843
|
});
|
|
8838
8844
|
};
|
|
8839
|
-
this.onIceCandidate =
|
|
8845
|
+
this.onIceCandidate = (e) => {
|
|
8840
8846
|
const { candidate } = e;
|
|
8841
8847
|
if (!candidate) {
|
|
8842
8848
|
logger$2('debug', 'null ice candidate');
|
|
8843
8849
|
return;
|
|
8844
8850
|
}
|
|
8845
|
-
|
|
8851
|
+
this.sfuClient
|
|
8852
|
+
.iceTrickle({
|
|
8846
8853
|
iceCandidate: getIceCandidate(candidate),
|
|
8847
8854
|
peerType: PeerType.SUBSCRIBER,
|
|
8855
|
+
})
|
|
8856
|
+
.catch((err) => {
|
|
8857
|
+
logger$2('warn', `ICETrickle failed`, err);
|
|
8848
8858
|
});
|
|
8849
8859
|
};
|
|
8850
8860
|
this.negotiate = async (subscriberOffer) => {
|
|
@@ -8916,13 +8926,17 @@ class Subscriber {
|
|
|
8916
8926
|
this.state = state;
|
|
8917
8927
|
this.iceRestartDelay = iceRestartDelay;
|
|
8918
8928
|
this.pc = this.createPeerConnection(connectionConfig);
|
|
8919
|
-
this.unregisterOnSubscriberOffer = dispatcher.on('subscriberOffer',
|
|
8920
|
-
|
|
8929
|
+
this.unregisterOnSubscriberOffer = dispatcher.on('subscriberOffer', (subscriberOffer) => {
|
|
8930
|
+
this.negotiate(subscriberOffer).catch((err) => {
|
|
8931
|
+
logger$2('warn', `Negotiation failed.`, err);
|
|
8932
|
+
});
|
|
8921
8933
|
});
|
|
8922
|
-
this.unregisterOnIceRestart = dispatcher.on('iceRestart',
|
|
8934
|
+
this.unregisterOnIceRestart = dispatcher.on('iceRestart', (iceRestart) => {
|
|
8923
8935
|
if (iceRestart.peerType !== PeerType.SUBSCRIBER)
|
|
8924
8936
|
return;
|
|
8925
|
-
|
|
8937
|
+
this.restartIce().catch((err) => {
|
|
8938
|
+
logger$2('warn', `ICERestart failed`, err);
|
|
8939
|
+
});
|
|
8926
8940
|
});
|
|
8927
8941
|
}
|
|
8928
8942
|
}
|
|
@@ -10869,18 +10883,27 @@ class InputMediaDeviceManager {
|
|
|
10869
10883
|
* Starts stream.
|
|
10870
10884
|
*/
|
|
10871
10885
|
async enable() {
|
|
10872
|
-
if (this.state.
|
|
10886
|
+
if (this.state.optimisticStatus === 'enabled') {
|
|
10887
|
+
await this.statusChangePromise;
|
|
10873
10888
|
return;
|
|
10874
|
-
this.enablePromise = this.unmuteStream();
|
|
10875
|
-
try {
|
|
10876
|
-
await this.enablePromise;
|
|
10877
|
-
this.state.setStatus('enabled');
|
|
10878
|
-
this.enablePromise = undefined;
|
|
10879
|
-
}
|
|
10880
|
-
catch (error) {
|
|
10881
|
-
this.enablePromise = undefined;
|
|
10882
|
-
throw error;
|
|
10883
10889
|
}
|
|
10890
|
+
const signal = this.nextAbortableStatusChangeRequest('enabled');
|
|
10891
|
+
const doEnable = async () => {
|
|
10892
|
+
if (signal.aborted)
|
|
10893
|
+
return;
|
|
10894
|
+
try {
|
|
10895
|
+
await this.unmuteStream();
|
|
10896
|
+
this.state.setStatus('enabled');
|
|
10897
|
+
}
|
|
10898
|
+
finally {
|
|
10899
|
+
if (!signal.aborted)
|
|
10900
|
+
this.resetStatusChangeRequest();
|
|
10901
|
+
}
|
|
10902
|
+
};
|
|
10903
|
+
this.statusChangePromise = this.statusChangePromise
|
|
10904
|
+
? this.statusChangePromise.then(doEnable)
|
|
10905
|
+
: doEnable();
|
|
10906
|
+
await this.statusChangePromise;
|
|
10884
10907
|
}
|
|
10885
10908
|
/**
|
|
10886
10909
|
* Stops or pauses the stream based on state.disableMode
|
|
@@ -10888,19 +10911,28 @@ class InputMediaDeviceManager {
|
|
|
10888
10911
|
*/
|
|
10889
10912
|
async disable(forceStop = false) {
|
|
10890
10913
|
this.state.prevStatus = this.state.status;
|
|
10891
|
-
if (!forceStop && this.state.
|
|
10914
|
+
if (!forceStop && this.state.optimisticStatus === 'disabled') {
|
|
10915
|
+
await this.statusChangePromise;
|
|
10892
10916
|
return;
|
|
10893
|
-
const stopTracks = forceStop || this.state.disableMode === 'stop-tracks';
|
|
10894
|
-
this.disablePromise = this.muteStream(stopTracks);
|
|
10895
|
-
try {
|
|
10896
|
-
await this.disablePromise;
|
|
10897
|
-
this.state.setStatus('disabled');
|
|
10898
|
-
this.disablePromise = undefined;
|
|
10899
|
-
}
|
|
10900
|
-
catch (error) {
|
|
10901
|
-
this.disablePromise = undefined;
|
|
10902
|
-
throw error;
|
|
10903
10917
|
}
|
|
10918
|
+
const stopTracks = forceStop || this.state.disableMode === 'stop-tracks';
|
|
10919
|
+
const signal = this.nextAbortableStatusChangeRequest('disabled');
|
|
10920
|
+
const doDisable = async () => {
|
|
10921
|
+
if (signal.aborted)
|
|
10922
|
+
return;
|
|
10923
|
+
try {
|
|
10924
|
+
await this.muteStream(stopTracks);
|
|
10925
|
+
this.state.setStatus('disabled');
|
|
10926
|
+
}
|
|
10927
|
+
finally {
|
|
10928
|
+
if (!signal.aborted)
|
|
10929
|
+
this.resetStatusChangeRequest();
|
|
10930
|
+
}
|
|
10931
|
+
};
|
|
10932
|
+
this.statusChangePromise = this.statusChangePromise
|
|
10933
|
+
? this.statusChangePromise.then(doDisable)
|
|
10934
|
+
: doDisable();
|
|
10935
|
+
await this.statusChangePromise;
|
|
10904
10936
|
}
|
|
10905
10937
|
/**
|
|
10906
10938
|
* If status was previously enabled, it will re-enable the device.
|
|
@@ -10916,7 +10948,7 @@ class InputMediaDeviceManager {
|
|
|
10916
10948
|
* else it will disable it.
|
|
10917
10949
|
*/
|
|
10918
10950
|
async toggle() {
|
|
10919
|
-
if (this.state.
|
|
10951
|
+
if (this.state.optimisticStatus === 'enabled') {
|
|
10920
10952
|
return this.disable();
|
|
10921
10953
|
}
|
|
10922
10954
|
else {
|
|
@@ -11103,11 +11135,8 @@ class InputMediaDeviceManager {
|
|
|
11103
11135
|
this.state.setMediaStream(stream, await rootStream);
|
|
11104
11136
|
this.getTracks().forEach((track) => {
|
|
11105
11137
|
track.addEventListener('ended', async () => {
|
|
11106
|
-
if (this.
|
|
11107
|
-
await this.
|
|
11108
|
-
}
|
|
11109
|
-
if (this.disablePromise) {
|
|
11110
|
-
await this.disablePromise;
|
|
11138
|
+
if (this.statusChangePromise) {
|
|
11139
|
+
await this.statusChangePromise;
|
|
11111
11140
|
}
|
|
11112
11141
|
if (this.state.status === 'enabled') {
|
|
11113
11142
|
this.isTrackStoppedDueToTrackEnd = true;
|
|
@@ -11137,11 +11166,8 @@ class InputMediaDeviceManager {
|
|
|
11137
11166
|
if (!deviceId) {
|
|
11138
11167
|
return;
|
|
11139
11168
|
}
|
|
11140
|
-
if (this.
|
|
11141
|
-
await this.
|
|
11142
|
-
}
|
|
11143
|
-
if (this.disablePromise) {
|
|
11144
|
-
await this.disablePromise;
|
|
11169
|
+
if (this.statusChangePromise) {
|
|
11170
|
+
await this.statusChangePromise;
|
|
11145
11171
|
}
|
|
11146
11172
|
let isDeviceDisconnected = false;
|
|
11147
11173
|
let isDeviceReplaced = false;
|
|
@@ -11175,6 +11201,16 @@ class InputMediaDeviceManager {
|
|
|
11175
11201
|
findDeviceInList(devices, deviceId) {
|
|
11176
11202
|
return devices.find((d) => d.deviceId === deviceId && d.kind === this.mediaDeviceKind);
|
|
11177
11203
|
}
|
|
11204
|
+
nextAbortableStatusChangeRequest(status) {
|
|
11205
|
+
this.statusChangeAbortController?.abort();
|
|
11206
|
+
this.statusChangeAbortController = new AbortController();
|
|
11207
|
+
this.state.setPendingStatus(status);
|
|
11208
|
+
return this.statusChangeAbortController.signal;
|
|
11209
|
+
}
|
|
11210
|
+
resetStatusChangeRequest() {
|
|
11211
|
+
this.statusChangePromise = undefined;
|
|
11212
|
+
this.statusChangeAbortController = undefined;
|
|
11213
|
+
}
|
|
11178
11214
|
}
|
|
11179
11215
|
|
|
11180
11216
|
class InputMediaDeviceManagerState {
|
|
@@ -11189,6 +11225,7 @@ class InputMediaDeviceManagerState {
|
|
|
11189
11225
|
this.disableMode = disableMode;
|
|
11190
11226
|
this.permissionName = permissionName;
|
|
11191
11227
|
this.statusSubject = new BehaviorSubject(undefined);
|
|
11228
|
+
this.optimisticStatusSubject = new BehaviorSubject(undefined);
|
|
11192
11229
|
this.mediaStreamSubject = new BehaviorSubject(undefined);
|
|
11193
11230
|
this.selectedDeviceSubject = new BehaviorSubject(undefined);
|
|
11194
11231
|
this.defaultConstraintsSubject = new BehaviorSubject(undefined);
|
|
@@ -11207,6 +11244,12 @@ class InputMediaDeviceManagerState {
|
|
|
11207
11244
|
* An Observable that emits the device status
|
|
11208
11245
|
*/
|
|
11209
11246
|
this.status$ = this.statusSubject.asObservable().pipe(distinctUntilChanged());
|
|
11247
|
+
/**
|
|
11248
|
+
* An Observable the reflects the requested device status. Useful for optimistic UIs
|
|
11249
|
+
*/
|
|
11250
|
+
this.optimisticStatus$ = this.optimisticStatusSubject
|
|
11251
|
+
.asObservable()
|
|
11252
|
+
.pipe(distinctUntilChanged());
|
|
11210
11253
|
/**
|
|
11211
11254
|
* The default constraints for the device.
|
|
11212
11255
|
*/
|
|
@@ -11274,6 +11317,12 @@ class InputMediaDeviceManagerState {
|
|
|
11274
11317
|
get status() {
|
|
11275
11318
|
return this.getCurrentValue(this.status$);
|
|
11276
11319
|
}
|
|
11320
|
+
/**
|
|
11321
|
+
* The requested device status. Useful for optimistic UIs
|
|
11322
|
+
*/
|
|
11323
|
+
get optimisticStatus() {
|
|
11324
|
+
return this.getCurrentValue(this.optimisticStatus$);
|
|
11325
|
+
}
|
|
11277
11326
|
/**
|
|
11278
11327
|
* The currently selected device
|
|
11279
11328
|
*/
|
|
@@ -11293,6 +11342,13 @@ class InputMediaDeviceManagerState {
|
|
|
11293
11342
|
setStatus(status) {
|
|
11294
11343
|
this.setCurrentValue(this.statusSubject, status);
|
|
11295
11344
|
}
|
|
11345
|
+
/**
|
|
11346
|
+
* @internal
|
|
11347
|
+
* @param pendingStatus
|
|
11348
|
+
*/
|
|
11349
|
+
setPendingStatus(pendingStatus) {
|
|
11350
|
+
this.setCurrentValue(this.optimisticStatusSubject, pendingStatus);
|
|
11351
|
+
}
|
|
11296
11352
|
/**
|
|
11297
11353
|
* Updates the `mediaStream` state variable.
|
|
11298
11354
|
*
|
|
@@ -11412,9 +11468,9 @@ class CameraManager extends InputMediaDeviceManager {
|
|
|
11412
11468
|
async selectTargetResolution(resolution) {
|
|
11413
11469
|
this.targetResolution.height = resolution.height;
|
|
11414
11470
|
this.targetResolution.width = resolution.width;
|
|
11415
|
-
if (this.
|
|
11471
|
+
if (this.statusChangePromise && this.state.optimisticStatus === 'enabled') {
|
|
11416
11472
|
try {
|
|
11417
|
-
await this.
|
|
11473
|
+
await this.statusChangePromise;
|
|
11418
11474
|
}
|
|
11419
11475
|
catch (error) {
|
|
11420
11476
|
// couldn't enable device, target resolution will be applied the next time user attempts to start the device
|
|
@@ -13446,7 +13502,9 @@ class Call {
|
|
|
13446
13502
|
});
|
|
13447
13503
|
this.leaveCallHooks.add(registerEventHandlers(this, this.state, this.dispatcher));
|
|
13448
13504
|
this.registerEffects();
|
|
13449
|
-
this.leaveCallHooks.add(createSubscription(this.trackSubscriptionsSubject.pipe(debounce((v) => timer(v.type)), map$1((v) => v.data)), (subscriptions) => this.sfuClient?.updateSubscriptions(subscriptions))
|
|
13505
|
+
this.leaveCallHooks.add(createSubscription(this.trackSubscriptionsSubject.pipe(debounce((v) => timer(v.type)), map$1((v) => v.data)), (subscriptions) => this.sfuClient?.updateSubscriptions(subscriptions).catch((err) => {
|
|
13506
|
+
this.logger('debug', `Failed to update track subscriptions`, err);
|
|
13507
|
+
})));
|
|
13450
13508
|
this.camera = new CameraManager(this);
|
|
13451
13509
|
this.microphone = new MicrophoneManager(this);
|
|
13452
13510
|
this.speaker = new SpeakerManager(this);
|
|
@@ -13555,12 +13613,7 @@ class Call {
|
|
|
13555
13613
|
}
|
|
13556
13614
|
async initCamera(options) {
|
|
13557
13615
|
// Wait for any in progress camera operation
|
|
13558
|
-
|
|
13559
|
-
await this.camera.enablePromise;
|
|
13560
|
-
}
|
|
13561
|
-
if (this.camera.disablePromise) {
|
|
13562
|
-
await this.camera.disablePromise;
|
|
13563
|
-
}
|
|
13616
|
+
await this.camera.statusChangePromise;
|
|
13564
13617
|
if (this.state.localParticipant?.videoStream ||
|
|
13565
13618
|
!this.permissionsContext.hasPermission('send-video')) {
|
|
13566
13619
|
return;
|
|
@@ -13597,12 +13650,7 @@ class Call {
|
|
|
13597
13650
|
}
|
|
13598
13651
|
async initMic(options) {
|
|
13599
13652
|
// Wait for any in progress mic operation
|
|
13600
|
-
|
|
13601
|
-
await this.microphone.enablePromise;
|
|
13602
|
-
}
|
|
13603
|
-
if (this.microphone.disablePromise) {
|
|
13604
|
-
await this.microphone.disablePromise;
|
|
13605
|
-
}
|
|
13653
|
+
await this.microphone.statusChangePromise;
|
|
13606
13654
|
if (this.state.localParticipant?.audioStream ||
|
|
13607
13655
|
!this.permissionsContext.hasPermission('send-audio')) {
|
|
13608
13656
|
return;
|
|
@@ -15145,7 +15193,7 @@ class StreamClient {
|
|
|
15145
15193
|
});
|
|
15146
15194
|
};
|
|
15147
15195
|
this.getUserAgent = () => {
|
|
15148
|
-
const version = "1.0.
|
|
15196
|
+
const version = "1.0.3" ;
|
|
15149
15197
|
return (this.userAgent ||
|
|
15150
15198
|
`stream-video-javascript-client-${this.node ? 'node' : 'browser'}-${version}`);
|
|
15151
15199
|
};
|