@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/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 = async (e) => {
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
- await this.sfuClient.iceTrickle({
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 = async () => {
8473
- await this.negotiate();
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', async (iceRestart) => {
8667
+ this.unsubscribeOnIceRestart = dispatcher.on('iceRestart', (iceRestart) => {
8664
8668
  if (iceRestart.peerType !== PeerType.PUBLISHER_UNSPECIFIED)
8665
8669
  return;
8666
- await this.restartIce();
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 = async (e) => {
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
- await this.sfuClient.iceTrickle({
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', async (subscriberOffer) => {
8941
- await this.negotiate(subscriberOffer);
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', async (iceRestart) => {
8955
+ this.unregisterOnIceRestart = dispatcher.on('iceRestart', (iceRestart) => {
8944
8956
  if (iceRestart.peerType !== PeerType.SUBSCRIBER)
8945
8957
  return;
8946
- await this.restartIce();
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.status === 'enabled')
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.status === 'disabled')
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.status === 'enabled') {
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.enablePromise) {
11128
- await this.enablePromise;
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.enablePromise) {
11162
- await this.enablePromise;
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.enablePromise) {
11492
+ if (this.statusChangePromise && this.state.optimisticStatus === 'enabled') {
11437
11493
  try {
11438
- await this.enablePromise;
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
- if (this.camera.enablePromise) {
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
- if (this.microphone.enablePromise) {
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.1" ;
15215
+ const version = "1.0.3" ;
15168
15216
  return (this.userAgent ||
15169
15217
  `stream-video-javascript-client-${this.node ? 'node' : 'browser'}-${version}`);
15170
15218
  };