@stream-io/video-client 1.18.4 → 1.18.6
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 +17 -0
- package/dist/index.browser.es.js +47 -58
- package/dist/index.browser.es.js.map +1 -1
- package/dist/index.cjs.js +47 -58
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.es.js +47 -58
- package/dist/index.es.js.map +1 -1
- package/dist/src/devices/BrowserPermission.d.ts +5 -2
- package/dist/src/devices/InputMediaDeviceManagerState.d.ts +5 -0
- package/dist/src/rtc/BasePeerConnection.d.ts +1 -1
- package/dist/src/rtc/Publisher.d.ts +0 -7
- package/dist/src/rtc/TransceiverCache.d.ts +1 -5
- package/package.json +2 -2
- package/src/devices/BrowserPermission.ts +22 -8
- package/src/devices/InputMediaDeviceManagerState.ts +10 -0
- package/src/devices/__tests__/mocks.ts +1 -0
- package/src/helpers/concurrency.ts +4 -1
- package/src/rtc/BasePeerConnection.ts +2 -4
- package/src/rtc/Publisher.ts +36 -56
- package/src/rtc/TransceiverCache.ts +1 -8
- package/src/rtc/__tests__/Publisher.test.ts +8 -8
package/dist/index.cjs.js
CHANGED
|
@@ -3647,7 +3647,10 @@ function hasPending(tag) {
|
|
|
3647
3647
|
return pendingPromises.has(tag);
|
|
3648
3648
|
}
|
|
3649
3649
|
async function settled(tag) {
|
|
3650
|
-
|
|
3650
|
+
let pending;
|
|
3651
|
+
while ((pending = pendingPromises.get(tag))) {
|
|
3652
|
+
await pending.promise;
|
|
3653
|
+
}
|
|
3651
3654
|
}
|
|
3652
3655
|
/**
|
|
3653
3656
|
* Implements common functionality of running async functions serially, by chaining
|
|
@@ -5346,8 +5349,6 @@ class BasePeerConnection {
|
|
|
5346
5349
|
this.pc.removeEventListener('icecandidateerror', this.onIceCandidateError);
|
|
5347
5350
|
this.pc.removeEventListener('signalingstatechange', this.onSignalingChange);
|
|
5348
5351
|
this.pc.removeEventListener('iceconnectionstatechange', this.onIceConnectionStateChange);
|
|
5349
|
-
// cancel any ongoing ICE restart process
|
|
5350
|
-
withCancellation('onIceConnectionStateChange', () => Promise.resolve());
|
|
5351
5352
|
this.pc.removeEventListener('icegatheringstatechange', this.onIceGatherChange);
|
|
5352
5353
|
this.unsubscribeIceTrickle?.();
|
|
5353
5354
|
this.subscriptions.forEach((unsubscribe) => unsubscribe());
|
|
@@ -5377,12 +5378,6 @@ class TransceiverCache {
|
|
|
5377
5378
|
this.get = (publishOption) => {
|
|
5378
5379
|
return this.findTransceiver(publishOption)?.transceiver;
|
|
5379
5380
|
};
|
|
5380
|
-
/**
|
|
5381
|
-
* Gets the last transceiver for the given track type and publish option id.
|
|
5382
|
-
*/
|
|
5383
|
-
this.getWith = (trackType, id) => {
|
|
5384
|
-
return this.findTransceiver({ trackType, id })?.transceiver;
|
|
5385
|
-
};
|
|
5386
5381
|
/**
|
|
5387
5382
|
* Checks if the cache has the given publish option.
|
|
5388
5383
|
*/
|
|
@@ -5696,7 +5691,7 @@ class Publisher extends BasePeerConnection {
|
|
|
5696
5691
|
const trackToPublish = this.cloneTrack(track);
|
|
5697
5692
|
const transceiver = this.transceiverCache.get(publishOption);
|
|
5698
5693
|
if (!transceiver) {
|
|
5699
|
-
this.addTransceiver(trackToPublish, publishOption);
|
|
5694
|
+
await this.addTransceiver(trackToPublish, publishOption);
|
|
5700
5695
|
}
|
|
5701
5696
|
else {
|
|
5702
5697
|
const previousTrack = transceiver.sender.track;
|
|
@@ -5710,7 +5705,7 @@ class Publisher extends BasePeerConnection {
|
|
|
5710
5705
|
/**
|
|
5711
5706
|
* Adds a new transceiver carrying the given track to the peer connection.
|
|
5712
5707
|
*/
|
|
5713
|
-
this.addTransceiver = (track, publishOption) => {
|
|
5708
|
+
this.addTransceiver = async (track, publishOption) => {
|
|
5714
5709
|
const videoEncodings = computeVideoLayers(track, publishOption);
|
|
5715
5710
|
const sendEncodings = isSvcCodec(publishOption.codec?.name)
|
|
5716
5711
|
? toSvcEncodings(videoEncodings)
|
|
@@ -5722,6 +5717,7 @@ class Publisher extends BasePeerConnection {
|
|
|
5722
5717
|
const trackType = publishOption.trackType;
|
|
5723
5718
|
this.logger('debug', `Added ${TrackType[trackType]} transceiver`);
|
|
5724
5719
|
this.transceiverCache.add(publishOption, transceiver);
|
|
5720
|
+
await this.negotiate();
|
|
5725
5721
|
};
|
|
5726
5722
|
/**
|
|
5727
5723
|
* Synchronizes the current Publisher state with the provided publish options.
|
|
@@ -5741,7 +5737,7 @@ class Publisher extends BasePeerConnection {
|
|
|
5741
5737
|
// take the track from the existing transceiver for the same track type,
|
|
5742
5738
|
// clone it and publish it with the new publish options
|
|
5743
5739
|
const track = this.cloneTrack(item.transceiver.sender.track);
|
|
5744
|
-
this.addTransceiver(track, publishOption);
|
|
5740
|
+
await this.addTransceiver(track, publishOption);
|
|
5745
5741
|
}
|
|
5746
5742
|
// stop publishing with options not required anymore -> [vp9]
|
|
5747
5743
|
for (const item of this.transceiverCache.items()) {
|
|
@@ -5811,7 +5807,9 @@ class Publisher extends BasePeerConnection {
|
|
|
5811
5807
|
const enabledLayers = layers.filter((l) => l.active);
|
|
5812
5808
|
const tag = 'Update publish quality:';
|
|
5813
5809
|
this.logger('info', `${tag} requested layers by SFU:`, enabledLayers);
|
|
5814
|
-
const
|
|
5810
|
+
const transceiverId = this.transceiverCache.find((t) => t.publishOption.id === publishOptionId &&
|
|
5811
|
+
t.publishOption.trackType === trackType);
|
|
5812
|
+
const sender = transceiverId?.transceiver.sender;
|
|
5815
5813
|
if (!sender) {
|
|
5816
5814
|
return this.logger('warn', `${tag} no video sender found.`);
|
|
5817
5815
|
}
|
|
@@ -5819,8 +5817,8 @@ class Publisher extends BasePeerConnection {
|
|
|
5819
5817
|
if (params.encodings.length === 0) {
|
|
5820
5818
|
return this.logger('warn', `${tag} there are no encodings set.`);
|
|
5821
5819
|
}
|
|
5822
|
-
const
|
|
5823
|
-
const usesSvcCodec = codecInUse && isSvcCodec(codecInUse
|
|
5820
|
+
const codecInUse = transceiverId?.publishOption.codec?.name;
|
|
5821
|
+
const usesSvcCodec = codecInUse && isSvcCodec(codecInUse);
|
|
5824
5822
|
let changed = false;
|
|
5825
5823
|
for (const encoder of params.encodings) {
|
|
5826
5824
|
const layer = usesSvcCodec
|
|
@@ -5878,40 +5876,32 @@ class Publisher extends BasePeerConnection {
|
|
|
5878
5876
|
}
|
|
5879
5877
|
await this.negotiate({ iceRestart: true });
|
|
5880
5878
|
};
|
|
5881
|
-
this.onNegotiationNeeded = () => {
|
|
5882
|
-
withCancellation('publisher.negotiate', (signal) => this.negotiate().catch((err) => {
|
|
5883
|
-
if (signal.aborted)
|
|
5884
|
-
return;
|
|
5885
|
-
this.logger('error', `Negotiation failed.`, err);
|
|
5886
|
-
this.onUnrecoverableError?.();
|
|
5887
|
-
}));
|
|
5888
|
-
};
|
|
5889
5879
|
/**
|
|
5890
5880
|
* Initiates a new offer/answer exchange with the currently connected SFU.
|
|
5891
5881
|
*
|
|
5892
5882
|
* @param options the optional offer options to use.
|
|
5893
5883
|
*/
|
|
5894
5884
|
this.negotiate = async (options) => {
|
|
5895
|
-
|
|
5896
|
-
|
|
5897
|
-
|
|
5898
|
-
|
|
5899
|
-
|
|
5900
|
-
|
|
5901
|
-
|
|
5902
|
-
|
|
5903
|
-
|
|
5904
|
-
|
|
5905
|
-
|
|
5906
|
-
|
|
5907
|
-
|
|
5908
|
-
|
|
5909
|
-
|
|
5910
|
-
|
|
5911
|
-
|
|
5912
|
-
|
|
5913
|
-
|
|
5914
|
-
|
|
5885
|
+
return withoutConcurrency('publisher.negotiate', async () => {
|
|
5886
|
+
const offer = await this.pc.createOffer(options);
|
|
5887
|
+
const tracks = this.getAnnouncedTracks(offer.sdp);
|
|
5888
|
+
if (!tracks.length)
|
|
5889
|
+
throw new Error(`Can't negotiate without any tracks`);
|
|
5890
|
+
try {
|
|
5891
|
+
this.isIceRestarting = options?.iceRestart ?? false;
|
|
5892
|
+
await this.pc.setLocalDescription(offer);
|
|
5893
|
+
const { sdp = '' } = offer;
|
|
5894
|
+
const { response } = await this.sfuClient.setPublisher({ sdp, tracks });
|
|
5895
|
+
if (response.error)
|
|
5896
|
+
throw new Error(response.error.message);
|
|
5897
|
+
const { sdp: answerSdp } = response;
|
|
5898
|
+
await this.pc.setRemoteDescription({ type: 'answer', sdp: answerSdp });
|
|
5899
|
+
}
|
|
5900
|
+
finally {
|
|
5901
|
+
this.isIceRestarting = false;
|
|
5902
|
+
}
|
|
5903
|
+
this.addTrickledIceCandidates();
|
|
5904
|
+
});
|
|
5915
5905
|
};
|
|
5916
5906
|
/**
|
|
5917
5907
|
* Returns a list of tracks that are currently being published.
|
|
@@ -5995,7 +5985,6 @@ class Publisher extends BasePeerConnection {
|
|
|
5995
5985
|
this.clonedTracks.delete(track);
|
|
5996
5986
|
};
|
|
5997
5987
|
this.publishOptions = publishOptions;
|
|
5998
|
-
this.pc.addEventListener('negotiationneeded', this.onNegotiationNeeded);
|
|
5999
5988
|
this.on('iceRestart', (iceRestart) => {
|
|
6000
5989
|
if (iceRestart.peerType !== PeerType.PUBLISHER_UNSPECIFIED)
|
|
6001
5990
|
return;
|
|
@@ -6014,17 +6003,6 @@ class Publisher extends BasePeerConnection {
|
|
|
6014
6003
|
return this.syncPublishOptions();
|
|
6015
6004
|
});
|
|
6016
6005
|
}
|
|
6017
|
-
/**
|
|
6018
|
-
* Detaches the event handlers from the `RTCPeerConnection`.
|
|
6019
|
-
* This is useful when we want to replace the `RTCPeerConnection`
|
|
6020
|
-
* instance with a new one (in case of migration).
|
|
6021
|
-
*/
|
|
6022
|
-
detachEventHandlers() {
|
|
6023
|
-
super.detachEventHandlers();
|
|
6024
|
-
this.pc.removeEventListener('negotiationneeded', this.onNegotiationNeeded);
|
|
6025
|
-
// abort any ongoing negotiation
|
|
6026
|
-
withCancellation('publisher.negotiate', () => Promise.resolve());
|
|
6027
|
-
}
|
|
6028
6006
|
/**
|
|
6029
6007
|
* Disposes this Publisher instance.
|
|
6030
6008
|
*/
|
|
@@ -7456,7 +7434,7 @@ const aggregate = (stats) => {
|
|
|
7456
7434
|
return report;
|
|
7457
7435
|
};
|
|
7458
7436
|
|
|
7459
|
-
const version = "1.18.
|
|
7437
|
+
const version = "1.18.6";
|
|
7460
7438
|
const [major, minor, patch] = version.split('.');
|
|
7461
7439
|
let sdkInfo = {
|
|
7462
7440
|
type: SdkType.PLAIN_JAVASCRIPT,
|
|
@@ -8390,6 +8368,7 @@ class BrowserPermission {
|
|
|
8390
8368
|
}
|
|
8391
8369
|
try {
|
|
8392
8370
|
this.wasPrompted = true;
|
|
8371
|
+
this.setState('prompting');
|
|
8393
8372
|
const stream = await navigator.mediaDevices.getUserMedia(this.permission.constraints);
|
|
8394
8373
|
disposeOfMediaStream(stream);
|
|
8395
8374
|
this.setState('granted');
|
|
@@ -8413,6 +8392,7 @@ class BrowserPermission {
|
|
|
8413
8392
|
error: e,
|
|
8414
8393
|
permission: this.permission,
|
|
8415
8394
|
});
|
|
8395
|
+
this.setState('prompt');
|
|
8416
8396
|
throw e;
|
|
8417
8397
|
}
|
|
8418
8398
|
});
|
|
@@ -8424,13 +8404,19 @@ class BrowserPermission {
|
|
|
8424
8404
|
return () => this.listeners.delete(cb);
|
|
8425
8405
|
}
|
|
8426
8406
|
asObservable() {
|
|
8427
|
-
return
|
|
8407
|
+
return this.getStateObservable().pipe(
|
|
8428
8408
|
// In some browsers, the 'change' event doesn't reliably emit and hence,
|
|
8429
8409
|
// permissionState stays in 'prompt' state forever.
|
|
8430
8410
|
// Typically, this happens when a user grants one-time permission.
|
|
8431
8411
|
// Instead of checking if a permission is granted, we check if it isn't denied
|
|
8432
8412
|
rxjs.map((state) => state !== 'denied'));
|
|
8433
8413
|
}
|
|
8414
|
+
getIsPromptingObservable() {
|
|
8415
|
+
return this.getStateObservable().pipe(rxjs.map((state) => state === 'prompting'));
|
|
8416
|
+
}
|
|
8417
|
+
getStateObservable() {
|
|
8418
|
+
return rxjs.fromEventPattern((handler) => this.listen(handler), (handler, unlisten) => unlisten());
|
|
8419
|
+
}
|
|
8434
8420
|
setState(state) {
|
|
8435
8421
|
if (this.state !== state) {
|
|
8436
8422
|
this.state = state;
|
|
@@ -9186,6 +9172,9 @@ class InputMediaDeviceManagerState {
|
|
|
9186
9172
|
this.hasBrowserPermission$ = permission
|
|
9187
9173
|
? permission.asObservable().pipe(rxjs.shareReplay(1))
|
|
9188
9174
|
: rxjs.of(true);
|
|
9175
|
+
this.isPromptingPermission$ = permission
|
|
9176
|
+
? permission.getIsPromptingObservable().pipe(rxjs.shareReplay(1))
|
|
9177
|
+
: rxjs.of(false);
|
|
9189
9178
|
}
|
|
9190
9179
|
/**
|
|
9191
9180
|
* The device status
|
|
@@ -13082,7 +13071,7 @@ class StreamClient {
|
|
|
13082
13071
|
this.getUserAgent = () => {
|
|
13083
13072
|
if (!this.cachedUserAgent) {
|
|
13084
13073
|
const { clientAppIdentifier = {} } = this.options;
|
|
13085
|
-
const { sdkName = 'js', sdkVersion = "1.18.
|
|
13074
|
+
const { sdkName = 'js', sdkVersion = "1.18.6", ...extras } = clientAppIdentifier;
|
|
13086
13075
|
this.cachedUserAgent = [
|
|
13087
13076
|
`stream-video-${sdkName}-v${sdkVersion}`,
|
|
13088
13077
|
...Object.entries(extras).map(([key, value]) => `${key}=${value}`),
|