@stream-io/video-client 1.20.0 → 1.20.2
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 +157 -128
- package/dist/index.browser.es.js.map +1 -1
- package/dist/index.cjs.js +157 -128
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.es.js +157 -128
- package/dist/index.es.js.map +1 -1
- package/dist/src/Call.d.ts +1 -1
- package/dist/src/devices/BrowserPermission.d.ts +1 -0
- package/dist/src/devices/InputMediaDeviceManagerState.d.ts +7 -2
- package/dist/src/gen/coordinator/index.d.ts +7 -0
- package/dist/src/types.d.ts +7 -2
- package/package.json +1 -1
- package/src/Call.ts +12 -10
- package/src/__tests__/Call.autodrop.test.ts +101 -0
- package/src/devices/BrowserPermission.ts +4 -0
- package/src/devices/InputMediaDeviceManager.ts +135 -123
- package/src/devices/InputMediaDeviceManagerState.ts +12 -2
- package/src/devices/__tests__/mocks.ts +1 -0
- package/src/events/__tests__/call.test.ts +5 -1
- package/src/events/call.ts +8 -4
- package/src/events/internal.ts +1 -1
- package/src/gen/coordinator/index.ts +7 -0
- package/src/stats/SfuStatsReporter.ts +4 -4
- package/src/store/stateStore.ts +1 -1
- package/src/types.ts +8 -2
package/dist/index.cjs.js
CHANGED
|
@@ -119,6 +119,7 @@ const FrameRecordingSettingsRequestQualityEnum = {
|
|
|
119
119
|
_720P: '720p',
|
|
120
120
|
_1080P: '1080p',
|
|
121
121
|
_1440P: '1440p',
|
|
122
|
+
_2160P: '2160p',
|
|
122
123
|
};
|
|
123
124
|
/**
|
|
124
125
|
* @export
|
|
@@ -191,11 +192,13 @@ const RTMPBroadcastRequestQualityEnum = {
|
|
|
191
192
|
_720P: '720p',
|
|
192
193
|
_1080P: '1080p',
|
|
193
194
|
_1440P: '1440p',
|
|
195
|
+
_2160P: '2160p',
|
|
194
196
|
PORTRAIT_360X640: 'portrait-360x640',
|
|
195
197
|
PORTRAIT_480X854: 'portrait-480x854',
|
|
196
198
|
PORTRAIT_720X1280: 'portrait-720x1280',
|
|
197
199
|
PORTRAIT_1080X1920: 'portrait-1080x1920',
|
|
198
200
|
PORTRAIT_1440X2560: 'portrait-1440x2560',
|
|
201
|
+
PORTRAIT_2160X3840: 'portrait-2160x3840',
|
|
199
202
|
};
|
|
200
203
|
/**
|
|
201
204
|
* @export
|
|
@@ -206,11 +209,13 @@ const RTMPSettingsRequestQualityEnum = {
|
|
|
206
209
|
_720P: '720p',
|
|
207
210
|
_1080P: '1080p',
|
|
208
211
|
_1440P: '1440p',
|
|
212
|
+
_2160P: '2160p',
|
|
209
213
|
PORTRAIT_360X640: 'portrait-360x640',
|
|
210
214
|
PORTRAIT_480X854: 'portrait-480x854',
|
|
211
215
|
PORTRAIT_720X1280: 'portrait-720x1280',
|
|
212
216
|
PORTRAIT_1080X1920: 'portrait-1080x1920',
|
|
213
217
|
PORTRAIT_1440X2560: 'portrait-1440x2560',
|
|
218
|
+
PORTRAIT_2160X3840: 'portrait-2160x3840',
|
|
214
219
|
};
|
|
215
220
|
/**
|
|
216
221
|
* @export
|
|
@@ -229,11 +234,13 @@ const RecordSettingsRequestQualityEnum = {
|
|
|
229
234
|
_720P: '720p',
|
|
230
235
|
_1080P: '1080p',
|
|
231
236
|
_1440P: '1440p',
|
|
237
|
+
_2160P: '2160p',
|
|
232
238
|
PORTRAIT_360X640: 'portrait-360x640',
|
|
233
239
|
PORTRAIT_480X854: 'portrait-480x854',
|
|
234
240
|
PORTRAIT_720X1280: 'portrait-720x1280',
|
|
235
241
|
PORTRAIT_1080X1920: 'portrait-1080x1920',
|
|
236
242
|
PORTRAIT_1440X2560: 'portrait-1440x2560',
|
|
243
|
+
PORTRAIT_2160X3840: 'portrait-2160x3840',
|
|
237
244
|
};
|
|
238
245
|
/**
|
|
239
246
|
* @export
|
|
@@ -4060,7 +4067,7 @@ class StreamVideoWriteableStateStore {
|
|
|
4060
4067
|
continue;
|
|
4061
4068
|
logger('info', `User disconnected, leaving call: ${call.cid}`);
|
|
4062
4069
|
await call
|
|
4063
|
-
.leave({
|
|
4070
|
+
.leave({ message: 'client.disconnectUser() called' })
|
|
4064
4071
|
.catch((err) => {
|
|
4065
4072
|
logger('error', `Error leaving call: ${call.cid}`, err);
|
|
4066
4073
|
});
|
|
@@ -5645,7 +5652,7 @@ const aggregate = (stats) => {
|
|
|
5645
5652
|
return report;
|
|
5646
5653
|
};
|
|
5647
5654
|
|
|
5648
|
-
const version = "1.20.
|
|
5655
|
+
const version = "1.20.2";
|
|
5649
5656
|
const [major, minor, patch] = version.split('.');
|
|
5650
5657
|
let sdkInfo = {
|
|
5651
5658
|
type: SdkType.PLAIN_JAVASCRIPT,
|
|
@@ -5786,9 +5793,9 @@ class SfuStatsReporter {
|
|
|
5786
5793
|
this.logger = getLogger(['SfuStatsReporter']);
|
|
5787
5794
|
this.inputDevices = new Map();
|
|
5788
5795
|
this.observeDevice = (device, kind) => {
|
|
5789
|
-
const {
|
|
5796
|
+
const { browserPermissionState$ } = device.state;
|
|
5790
5797
|
this.unsubscribeDevicePermissionsSubscription?.();
|
|
5791
|
-
this.unsubscribeDevicePermissionsSubscription = createSubscription(rxjs.combineLatest([
|
|
5798
|
+
this.unsubscribeDevicePermissionsSubscription = createSubscription(rxjs.combineLatest([browserPermissionState$, this.state.ownCapabilities$]), ([browserPermissionState, ownCapabilities]) => {
|
|
5792
5799
|
// cleanup the previous listDevices() subscription in case
|
|
5793
5800
|
// permissions or capabilities have changed.
|
|
5794
5801
|
// we will subscribe again if everything is in order.
|
|
@@ -5796,7 +5803,7 @@ class SfuStatsReporter {
|
|
|
5796
5803
|
const hasCapability = kind === 'mic'
|
|
5797
5804
|
? ownCapabilities.includes(OwnCapability.SEND_AUDIO)
|
|
5798
5805
|
: ownCapabilities.includes(OwnCapability.SEND_VIDEO);
|
|
5799
|
-
if (
|
|
5806
|
+
if (browserPermissionState !== 'granted' || !hasCapability) {
|
|
5800
5807
|
this.inputDevices.set(kind, {
|
|
5801
5808
|
currentDevice: '',
|
|
5802
5809
|
availableDevices: [],
|
|
@@ -7603,13 +7610,17 @@ const watchCallRejected = (call) => {
|
|
|
7603
7610
|
.every((m) => rejectedBy[m.user_id]);
|
|
7604
7611
|
if (everyoneElseRejected) {
|
|
7605
7612
|
call.logger('info', 'everyone rejected, leaving the call');
|
|
7606
|
-
await call.leave({
|
|
7613
|
+
await call.leave({
|
|
7614
|
+
reject: true,
|
|
7615
|
+
reason: 'cancel',
|
|
7616
|
+
message: 'ring: everyone rejected',
|
|
7617
|
+
});
|
|
7607
7618
|
}
|
|
7608
7619
|
}
|
|
7609
7620
|
else {
|
|
7610
7621
|
if (rejectedBy[eventCall.created_by.id]) {
|
|
7611
7622
|
call.logger('info', 'call creator rejected, leaving call');
|
|
7612
|
-
await call.leave({
|
|
7623
|
+
await call.leave({ message: 'ring: creator rejected' });
|
|
7613
7624
|
}
|
|
7614
7625
|
}
|
|
7615
7626
|
};
|
|
@@ -7623,7 +7634,7 @@ const watchCallEnded = (call) => {
|
|
|
7623
7634
|
if (callingState !== exports.CallingState.IDLE &&
|
|
7624
7635
|
callingState !== exports.CallingState.LEFT) {
|
|
7625
7636
|
call
|
|
7626
|
-
.leave({
|
|
7637
|
+
.leave({ message: 'call.ended event received', reject: false })
|
|
7627
7638
|
.catch((err) => {
|
|
7628
7639
|
call.logger('error', 'Failed to leave call after call.ended ', err);
|
|
7629
7640
|
});
|
|
@@ -7643,7 +7654,7 @@ const watchSfuCallEnded = (call) => {
|
|
|
7643
7654
|
// update the call state to reflect the call has ended.
|
|
7644
7655
|
call.state.setEndedAt(new Date());
|
|
7645
7656
|
const reason = CallEndedReason[e.reason];
|
|
7646
|
-
await call.leave({
|
|
7657
|
+
await call.leave({ message: `callEnded received: ${reason}` });
|
|
7647
7658
|
}
|
|
7648
7659
|
catch (err) {
|
|
7649
7660
|
call.logger('error', 'Failed to leave call after being ended by the SFU', err);
|
|
@@ -7710,7 +7721,7 @@ const watchLiveEnded = (dispatcher, call) => {
|
|
|
7710
7721
|
return;
|
|
7711
7722
|
call.state.setBackstage(true);
|
|
7712
7723
|
if (!call.permissionsContext.hasPermission(OwnCapability.JOIN_BACKSTAGE)) {
|
|
7713
|
-
call.leave({
|
|
7724
|
+
call.leave({ message: 'live ended' }).catch((err) => {
|
|
7714
7725
|
call.logger('error', 'Failed to leave call after live ended', err);
|
|
7715
7726
|
});
|
|
7716
7727
|
}
|
|
@@ -8730,6 +8741,9 @@ class BrowserPermission {
|
|
|
8730
8741
|
// Instead of checking if a permission is granted, we check if it isn't denied
|
|
8731
8742
|
rxjs.map((state) => state !== 'denied'));
|
|
8732
8743
|
}
|
|
8744
|
+
asStateObservable() {
|
|
8745
|
+
return this.getStateObservable();
|
|
8746
|
+
}
|
|
8733
8747
|
getIsPromptingObservable() {
|
|
8734
8748
|
return this.getStateObservable().pipe(rxjs.map((state) => state === 'prompting'));
|
|
8735
8749
|
}
|
|
@@ -9256,122 +9270,130 @@ class InputMediaDeviceManager {
|
|
|
9256
9270
|
this.logger('debug', 'Starting stream');
|
|
9257
9271
|
let stream;
|
|
9258
9272
|
let rootStream;
|
|
9259
|
-
|
|
9260
|
-
this.
|
|
9261
|
-
|
|
9262
|
-
|
|
9263
|
-
|
|
9264
|
-
|
|
9265
|
-
|
|
9266
|
-
|
|
9267
|
-
|
|
9268
|
-
|
|
9269
|
-
|
|
9270
|
-
|
|
9271
|
-
|
|
9272
|
-
|
|
9273
|
-
|
|
9274
|
-
|
|
9275
|
-
|
|
9276
|
-
|
|
9277
|
-
|
|
9278
|
-
|
|
9279
|
-
|
|
9280
|
-
|
|
9281
|
-
|
|
9282
|
-
|
|
9283
|
-
|
|
9284
|
-
|
|
9285
|
-
|
|
9286
|
-
|
|
9287
|
-
|
|
9288
|
-
|
|
9289
|
-
|
|
9290
|
-
|
|
9291
|
-
|
|
9292
|
-
|
|
9293
|
-
|
|
9294
|
-
|
|
9295
|
-
|
|
9296
|
-
|
|
9297
|
-
|
|
9298
|
-
|
|
9299
|
-
|
|
9300
|
-
|
|
9301
|
-
parentTrack.
|
|
9302
|
-
|
|
9303
|
-
|
|
9304
|
-
|
|
9305
|
-
|
|
9306
|
-
|
|
9307
|
-
|
|
9308
|
-
|
|
9309
|
-
|
|
9310
|
-
|
|
9311
|
-
|
|
9312
|
-
|
|
9313
|
-
|
|
9314
|
-
|
|
9315
|
-
|
|
9316
|
-
|
|
9273
|
+
try {
|
|
9274
|
+
if (this.state.mediaStream &&
|
|
9275
|
+
this.getTracks().every((t) => t.readyState === 'live')) {
|
|
9276
|
+
stream = this.state.mediaStream;
|
|
9277
|
+
this.enableTracks();
|
|
9278
|
+
}
|
|
9279
|
+
else {
|
|
9280
|
+
const defaultConstraints = this.state.defaultConstraints;
|
|
9281
|
+
const constraints = {
|
|
9282
|
+
...defaultConstraints,
|
|
9283
|
+
deviceId: this.state.selectedDevice
|
|
9284
|
+
? { exact: this.state.selectedDevice }
|
|
9285
|
+
: undefined,
|
|
9286
|
+
};
|
|
9287
|
+
/**
|
|
9288
|
+
* Chains two media streams together.
|
|
9289
|
+
*
|
|
9290
|
+
* In our case, filters MediaStreams are derived from their parent MediaStream.
|
|
9291
|
+
* However, once a child filter's track is stopped,
|
|
9292
|
+
* the tracks of the parent MediaStream aren't automatically stopped.
|
|
9293
|
+
* This leads to a situation where the camera indicator light is still on
|
|
9294
|
+
* even though the user stopped publishing video.
|
|
9295
|
+
*
|
|
9296
|
+
* This function works around this issue by stopping the parent MediaStream's tracks
|
|
9297
|
+
* as well once the child filter's tracks are stopped.
|
|
9298
|
+
*
|
|
9299
|
+
* It works by patching the stop() method of the child filter's tracks to also stop
|
|
9300
|
+
* the parent MediaStream's tracks of the same type. Here we assume that
|
|
9301
|
+
* the parent MediaStream has only one track of each type.
|
|
9302
|
+
*
|
|
9303
|
+
* @param parentStream the parent MediaStream. Omit for the root stream.
|
|
9304
|
+
*/
|
|
9305
|
+
const chainWith = (parentStream) => async (filterStream) => {
|
|
9306
|
+
if (!parentStream)
|
|
9307
|
+
return filterStream;
|
|
9308
|
+
// TODO OL: take care of track.enabled property as well
|
|
9309
|
+
const parent = await parentStream;
|
|
9310
|
+
filterStream.getTracks().forEach((track) => {
|
|
9311
|
+
const originalStop = track.stop;
|
|
9312
|
+
track.stop = function stop() {
|
|
9313
|
+
originalStop.call(track);
|
|
9314
|
+
parent.getTracks().forEach((parentTrack) => {
|
|
9315
|
+
if (parentTrack.kind === track.kind) {
|
|
9316
|
+
parentTrack.stop();
|
|
9317
|
+
}
|
|
9318
|
+
});
|
|
9319
|
+
};
|
|
9320
|
+
});
|
|
9321
|
+
parent.getTracks().forEach((parentTrack) => {
|
|
9322
|
+
// When the parent stream abruptly ends, we propagate the event
|
|
9323
|
+
// to the filter stream.
|
|
9324
|
+
// This usually happens when the camera/microphone permissions
|
|
9325
|
+
// are revoked or when the device is disconnected.
|
|
9326
|
+
const handleParentTrackEnded = () => {
|
|
9327
|
+
filterStream.getTracks().forEach((track) => {
|
|
9328
|
+
if (parentTrack.kind !== track.kind)
|
|
9329
|
+
return;
|
|
9330
|
+
track.stop();
|
|
9331
|
+
track.dispatchEvent(new Event('ended')); // propagate the event
|
|
9332
|
+
});
|
|
9333
|
+
};
|
|
9334
|
+
parentTrack.addEventListener('ended', handleParentTrackEnded);
|
|
9335
|
+
this.subscriptions.push(() => {
|
|
9336
|
+
parentTrack.removeEventListener('ended', handleParentTrackEnded);
|
|
9317
9337
|
});
|
|
9318
|
-
};
|
|
9319
|
-
|
|
9338
|
+
});
|
|
9339
|
+
return filterStream;
|
|
9340
|
+
};
|
|
9341
|
+
// the rootStream represents the stream coming from the actual device
|
|
9342
|
+
// e.g. camera or microphone stream
|
|
9343
|
+
rootStream = this.getStream(constraints);
|
|
9344
|
+
// we publish the last MediaStream of the chain
|
|
9345
|
+
stream = await this.filters.reduce((parent, entry) => parent
|
|
9346
|
+
.then((inputStream) => {
|
|
9347
|
+
const { stop, output } = entry.start(inputStream);
|
|
9348
|
+
entry.stop = stop;
|
|
9349
|
+
return output;
|
|
9350
|
+
})
|
|
9351
|
+
.then(chainWith(parent), (error) => {
|
|
9352
|
+
this.logger('warn', 'Filter failed to start and will be ignored', error);
|
|
9353
|
+
return parent;
|
|
9354
|
+
}), rootStream);
|
|
9355
|
+
}
|
|
9356
|
+
if (this.call.state.callingState === exports.CallingState.JOINED) {
|
|
9357
|
+
await this.publishStream(stream);
|
|
9358
|
+
}
|
|
9359
|
+
if (this.state.mediaStream !== stream) {
|
|
9360
|
+
this.state.setMediaStream(stream, await rootStream);
|
|
9361
|
+
const handleTrackEnded = async () => {
|
|
9362
|
+
await this.statusChangeSettled();
|
|
9363
|
+
if (this.enabled) {
|
|
9364
|
+
this.isTrackStoppedDueToTrackEnd = true;
|
|
9365
|
+
setTimeout(() => {
|
|
9366
|
+
this.isTrackStoppedDueToTrackEnd = false;
|
|
9367
|
+
}, 2000);
|
|
9368
|
+
await this.disable();
|
|
9369
|
+
}
|
|
9370
|
+
};
|
|
9371
|
+
const createTrackMuteHandler = (muted) => () => {
|
|
9372
|
+
if (!isMobile() || this.trackType !== TrackType.VIDEO)
|
|
9373
|
+
return;
|
|
9374
|
+
this.call.notifyTrackMuteState(muted, this.trackType).catch((err) => {
|
|
9375
|
+
this.logger('warn', 'Error while notifying track mute state', err);
|
|
9376
|
+
});
|
|
9377
|
+
};
|
|
9378
|
+
stream.getTracks().forEach((track) => {
|
|
9379
|
+
const muteHandler = createTrackMuteHandler(true);
|
|
9380
|
+
const unmuteHandler = createTrackMuteHandler(false);
|
|
9381
|
+
track.addEventListener('mute', muteHandler);
|
|
9382
|
+
track.addEventListener('unmute', unmuteHandler);
|
|
9383
|
+
track.addEventListener('ended', handleTrackEnded);
|
|
9320
9384
|
this.subscriptions.push(() => {
|
|
9321
|
-
|
|
9385
|
+
track.removeEventListener('mute', muteHandler);
|
|
9386
|
+
track.removeEventListener('unmute', unmuteHandler);
|
|
9387
|
+
track.removeEventListener('ended', handleTrackEnded);
|
|
9322
9388
|
});
|
|
9323
9389
|
});
|
|
9324
|
-
|
|
9325
|
-
};
|
|
9326
|
-
// the rootStream represents the stream coming from the actual device
|
|
9327
|
-
// e.g. camera or microphone stream
|
|
9328
|
-
rootStream = this.getStream(constraints);
|
|
9329
|
-
// we publish the last MediaStream of the chain
|
|
9330
|
-
stream = await this.filters.reduce((parent, entry) => parent
|
|
9331
|
-
.then((inputStream) => {
|
|
9332
|
-
const { stop, output } = entry.start(inputStream);
|
|
9333
|
-
entry.stop = stop;
|
|
9334
|
-
return output;
|
|
9335
|
-
})
|
|
9336
|
-
.then(chainWith(parent), (error) => {
|
|
9337
|
-
this.logger('warn', 'Filter failed to start and will be ignored', error);
|
|
9338
|
-
return parent;
|
|
9339
|
-
}), rootStream);
|
|
9340
|
-
}
|
|
9341
|
-
if (this.call.state.callingState === exports.CallingState.JOINED) {
|
|
9342
|
-
await this.publishStream(stream);
|
|
9390
|
+
}
|
|
9343
9391
|
}
|
|
9344
|
-
|
|
9345
|
-
|
|
9346
|
-
|
|
9347
|
-
|
|
9348
|
-
|
|
9349
|
-
this.isTrackStoppedDueToTrackEnd = true;
|
|
9350
|
-
setTimeout(() => {
|
|
9351
|
-
this.isTrackStoppedDueToTrackEnd = false;
|
|
9352
|
-
}, 2000);
|
|
9353
|
-
await this.disable();
|
|
9354
|
-
}
|
|
9355
|
-
};
|
|
9356
|
-
const createTrackMuteHandler = (muted) => () => {
|
|
9357
|
-
if (!isMobile() || this.trackType !== TrackType.VIDEO)
|
|
9358
|
-
return;
|
|
9359
|
-
this.call.notifyTrackMuteState(muted, this.trackType).catch((err) => {
|
|
9360
|
-
this.logger('warn', 'Error while notifying track mute state', err);
|
|
9361
|
-
});
|
|
9362
|
-
};
|
|
9363
|
-
stream.getTracks().forEach((track) => {
|
|
9364
|
-
const muteHandler = createTrackMuteHandler(true);
|
|
9365
|
-
const unmuteHandler = createTrackMuteHandler(false);
|
|
9366
|
-
track.addEventListener('mute', muteHandler);
|
|
9367
|
-
track.addEventListener('unmute', unmuteHandler);
|
|
9368
|
-
track.addEventListener('ended', handleTrackEnded);
|
|
9369
|
-
this.subscriptions.push(() => {
|
|
9370
|
-
track.removeEventListener('mute', muteHandler);
|
|
9371
|
-
track.removeEventListener('unmute', unmuteHandler);
|
|
9372
|
-
track.removeEventListener('ended', handleTrackEnded);
|
|
9373
|
-
});
|
|
9374
|
-
});
|
|
9392
|
+
catch (err) {
|
|
9393
|
+
if (rootStream) {
|
|
9394
|
+
disposeOfMediaStream(await rootStream);
|
|
9395
|
+
}
|
|
9396
|
+
throw err;
|
|
9375
9397
|
}
|
|
9376
9398
|
}
|
|
9377
9399
|
get mediaDeviceKind() {
|
|
@@ -9493,6 +9515,9 @@ class InputMediaDeviceManagerState {
|
|
|
9493
9515
|
this.hasBrowserPermission$ = permission
|
|
9494
9516
|
? permission.asObservable().pipe(rxjs.shareReplay(1))
|
|
9495
9517
|
: rxjs.of(true);
|
|
9518
|
+
this.browserPermissionState$ = permission
|
|
9519
|
+
? permission.asStateObservable().pipe(rxjs.shareReplay(1))
|
|
9520
|
+
: rxjs.of('prompt');
|
|
9496
9521
|
this.isPromptingPermission$ = permission
|
|
9497
9522
|
? permission.getIsPromptingObservable().pipe(rxjs.shareReplay(1))
|
|
9498
9523
|
: rxjs.of(false);
|
|
@@ -10558,7 +10583,7 @@ class Call {
|
|
|
10558
10583
|
const currentUserId = this.currentUserId;
|
|
10559
10584
|
if (currentUserId && blockedUserIds.includes(currentUserId)) {
|
|
10560
10585
|
this.logger('info', 'Leaving call because of being blocked');
|
|
10561
|
-
await this.leave({
|
|
10586
|
+
await this.leave({ message: 'user blocked' }).catch((err) => {
|
|
10562
10587
|
this.logger('error', 'Error leaving call after being blocked', err);
|
|
10563
10588
|
});
|
|
10564
10589
|
}
|
|
@@ -10711,7 +10736,7 @@ class Call {
|
|
|
10711
10736
|
/**
|
|
10712
10737
|
* Leave the call and stop the media streams that were published by the call.
|
|
10713
10738
|
*/
|
|
10714
|
-
this.leave = async ({ reject, reason
|
|
10739
|
+
this.leave = async ({ reject, reason, message } = {}) => {
|
|
10715
10740
|
await withoutConcurrency(this.joinLeaveConcurrencyTag, async () => {
|
|
10716
10741
|
const callingState = this.state.callingState;
|
|
10717
10742
|
if (callingState === exports.CallingState.LEFT) {
|
|
@@ -10729,7 +10754,7 @@ class Call {
|
|
|
10729
10754
|
}
|
|
10730
10755
|
if (callingState === exports.CallingState.RINGING && reject !== false) {
|
|
10731
10756
|
if (reject) {
|
|
10732
|
-
await this.reject('decline');
|
|
10757
|
+
await this.reject(reason ?? 'decline');
|
|
10733
10758
|
}
|
|
10734
10759
|
else {
|
|
10735
10760
|
// if reject was undefined, we still have to cancel the call automatically
|
|
@@ -10748,7 +10773,7 @@ class Call {
|
|
|
10748
10773
|
this.subscriber = undefined;
|
|
10749
10774
|
this.publisher?.dispose();
|
|
10750
10775
|
this.publisher = undefined;
|
|
10751
|
-
await this.sfuClient?.leaveAndClose(reason);
|
|
10776
|
+
await this.sfuClient?.leaveAndClose(message ?? reason ?? 'user is leaving the call');
|
|
10752
10777
|
this.sfuClient = undefined;
|
|
10753
10778
|
this.dynascaleManager.setSfuClient(undefined);
|
|
10754
10779
|
this.state.setCallingState(exports.CallingState.LEFT);
|
|
@@ -11455,7 +11480,7 @@ class Call {
|
|
|
11455
11480
|
if (strategy === WebsocketReconnectStrategy.UNSPECIFIED)
|
|
11456
11481
|
return;
|
|
11457
11482
|
if (strategy === WebsocketReconnectStrategy.DISCONNECT) {
|
|
11458
|
-
this.leave({
|
|
11483
|
+
this.leave({ message: 'SFU instructed to disconnect' }).catch((err) => {
|
|
11459
11484
|
this.logger('warn', `Can't leave call after disconnect request`, err);
|
|
11460
11485
|
});
|
|
11461
11486
|
}
|
|
@@ -12085,7 +12110,11 @@ class Call {
|
|
|
12085
12110
|
// e.g. it was already accepted and joined
|
|
12086
12111
|
if (this.state.callingState !== exports.CallingState.RINGING)
|
|
12087
12112
|
return;
|
|
12088
|
-
this.leave({
|
|
12113
|
+
this.leave({
|
|
12114
|
+
reject: true,
|
|
12115
|
+
reason: 'timeout',
|
|
12116
|
+
message: `ringing timeout - ${this.isCreatedByMe ? 'no one accepted' : `user didn't interact with incoming call screen`}`,
|
|
12117
|
+
}).catch((err) => {
|
|
12089
12118
|
this.logger('error', 'Failed to drop call', err);
|
|
12090
12119
|
});
|
|
12091
12120
|
}, timeoutInMs);
|
|
@@ -13458,7 +13487,7 @@ class StreamClient {
|
|
|
13458
13487
|
this.getUserAgent = () => {
|
|
13459
13488
|
if (!this.cachedUserAgent) {
|
|
13460
13489
|
const { clientAppIdentifier = {} } = this.options;
|
|
13461
|
-
const { sdkName = 'js', sdkVersion = "1.20.
|
|
13490
|
+
const { sdkName = 'js', sdkVersion = "1.20.2", ...extras } = clientAppIdentifier;
|
|
13462
13491
|
this.cachedUserAgent = [
|
|
13463
13492
|
`stream-video-${sdkName}-v${sdkVersion}`,
|
|
13464
13493
|
...Object.entries(extras).map(([key, value]) => `${key}=${value}`),
|