@stream-io/video-client 1.35.1 → 1.36.1
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 +10 -0
- package/dist/index.browser.es.js +382 -329
- package/dist/index.browser.es.js.map +1 -1
- package/dist/index.cjs.js +404 -333
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.es.js +382 -329
- package/dist/index.es.js.map +1 -1
- package/dist/src/Call.d.ts +3 -2
- package/dist/src/StreamVideoClient.d.ts +3 -2
- package/dist/src/coordinator/connection/client.d.ts +3 -2
- package/dist/src/coordinator/connection/connection.d.ts +2 -1
- package/dist/src/coordinator/connection/types.d.ts +17 -1
- package/dist/src/devices/DeviceManager.d.ts +2 -2
- package/dist/src/logger.d.ts +9 -6
- package/dist/src/rpc/createClient.d.ts +3 -2
- package/dist/src/rtc/BasePeerConnection.d.ts +7 -4
- package/dist/src/rtc/Publisher.d.ts +3 -3
- package/dist/src/rtc/codecs.d.ts +3 -1
- package/dist/src/rtc/helpers/sdp.d.ts +8 -0
- package/dist/src/rtc/types.d.ts +4 -5
- package/dist/src/store/CallState.d.ts +1 -1
- package/dist/src/types.d.ts +6 -0
- package/package.json +3 -2
- package/src/Call.ts +49 -68
- package/src/StreamSfuClient.ts +11 -11
- package/src/StreamVideoClient.ts +19 -21
- package/src/coordinator/connection/client.ts +21 -30
- package/src/coordinator/connection/connection.ts +5 -4
- package/src/coordinator/connection/location.ts +4 -4
- package/src/coordinator/connection/types.ts +21 -2
- package/src/devices/BrowserPermission.ts +5 -5
- package/src/devices/CameraManager.ts +3 -4
- package/src/devices/DeviceManager.ts +11 -11
- package/src/devices/MicrophoneManager.ts +8 -8
- package/src/devices/devices.ts +18 -14
- package/src/events/call.ts +6 -9
- package/src/events/internal.ts +4 -4
- package/src/events/mutes.ts +3 -8
- package/src/helpers/DynascaleManager.ts +9 -9
- package/src/helpers/RNSpeechDetector.ts +5 -5
- package/src/helpers/clientUtils.ts +1 -3
- package/src/helpers/ensureExhausted.ts +2 -2
- package/src/logger.ts +9 -34
- package/src/rpc/__tests__/createClient.test.ts +5 -1
- package/src/rpc/createClient.ts +4 -3
- package/src/rpc/retryable.ts +4 -2
- package/src/rtc/BasePeerConnection.ts +26 -24
- package/src/rtc/Dispatcher.ts +4 -4
- package/src/rtc/IceTrickleBuffer.ts +5 -5
- package/src/rtc/Publisher.ts +21 -13
- package/src/rtc/Subscriber.ts +22 -17
- package/src/rtc/__tests__/Publisher.test.ts +12 -8
- package/src/rtc/codecs.ts +13 -2
- package/src/rtc/helpers/__tests__/sdp.codecs.test.ts +628 -0
- package/src/rtc/helpers/sdp.ts +82 -0
- package/src/rtc/signal.ts +7 -7
- package/src/rtc/types.ts +4 -4
- package/src/stats/CallStateStatsReporter.ts +4 -4
- package/src/stats/SfuStatsReporter.ts +6 -6
- package/src/store/CallState.ts +3 -3
- package/src/store/rxUtils.ts +4 -2
- package/src/store/stateStore.ts +6 -6
- package/src/types.ts +6 -0
package/src/Call.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { StreamSfuClient } from './StreamSfuClient';
|
|
2
2
|
import {
|
|
3
|
+
BasePeerConnectionOpts,
|
|
3
4
|
Dispatcher,
|
|
4
5
|
getGenericSdp,
|
|
5
6
|
isAudioTrackType,
|
|
@@ -25,6 +26,7 @@ import {
|
|
|
25
26
|
createSubscription,
|
|
26
27
|
getCurrentValue,
|
|
27
28
|
} from './store/rxUtils';
|
|
29
|
+
import { ScopedLogger, videoLoggerSystem } from './logger';
|
|
28
30
|
import type {
|
|
29
31
|
AcceptCallResponse,
|
|
30
32
|
BlockUserRequest,
|
|
@@ -117,6 +119,7 @@ import {
|
|
|
117
119
|
ClientDetails,
|
|
118
120
|
Codec,
|
|
119
121
|
ParticipantSource,
|
|
122
|
+
PeerType,
|
|
120
123
|
PublishOption,
|
|
121
124
|
SubscribeOption,
|
|
122
125
|
TrackType,
|
|
@@ -139,12 +142,10 @@ import {
|
|
|
139
142
|
AllCallEvents,
|
|
140
143
|
CallEventListener,
|
|
141
144
|
ErrorFromResponse,
|
|
142
|
-
Logger,
|
|
143
145
|
RejectReason,
|
|
144
146
|
StreamCallEvent,
|
|
145
147
|
} from './coordinator/connection/types';
|
|
146
148
|
import { getClientDetails } from './helpers/client-details';
|
|
147
|
-
import { getLogger } from './logger';
|
|
148
149
|
import {
|
|
149
150
|
CameraManager,
|
|
150
151
|
MicrophoneManager,
|
|
@@ -229,7 +230,7 @@ export class Call {
|
|
|
229
230
|
*/
|
|
230
231
|
readonly permissionsContext = new PermissionsContext();
|
|
231
232
|
readonly tracer = new Tracer(null);
|
|
232
|
-
readonly logger:
|
|
233
|
+
readonly logger: ScopedLogger;
|
|
233
234
|
|
|
234
235
|
/**
|
|
235
236
|
* The event dispatcher instance dedicated to this Call instance.
|
|
@@ -312,7 +313,7 @@ export class Call {
|
|
|
312
313
|
this.streamClient = streamClient;
|
|
313
314
|
this.clientStore = clientStore;
|
|
314
315
|
this.streamClientBasePath = `/call/${this.type}/${this.id}`;
|
|
315
|
-
this.logger = getLogger(
|
|
316
|
+
this.logger = videoLoggerSystem.getLogger('Call');
|
|
316
317
|
|
|
317
318
|
const callTypeConfig = CallTypes.get(type);
|
|
318
319
|
const participantSorter =
|
|
@@ -396,9 +397,9 @@ export class Call {
|
|
|
396
397
|
if (!blockedUserIds || blockedUserIds.length === 0) return;
|
|
397
398
|
const currentUserId = this.currentUserId;
|
|
398
399
|
if (currentUserId && blockedUserIds.includes(currentUserId)) {
|
|
399
|
-
this.logger('
|
|
400
|
+
this.logger.info('Leaving call because of being blocked');
|
|
400
401
|
await this.leave({ message: 'user blocked' }).catch((err) => {
|
|
401
|
-
this.logger('
|
|
402
|
+
this.logger.error('Error leaving call after being blocked', err);
|
|
402
403
|
});
|
|
403
404
|
}
|
|
404
405
|
}),
|
|
@@ -442,8 +443,7 @@ export class Call {
|
|
|
442
443
|
!hasPending(this.joinLeaveConcurrencyTag)
|
|
443
444
|
) {
|
|
444
445
|
this.leave().catch(() => {
|
|
445
|
-
this.logger(
|
|
446
|
-
'error',
|
|
446
|
+
this.logger.error(
|
|
447
447
|
'Could not leave a call that was accepted or rejected elsewhere',
|
|
448
448
|
);
|
|
449
449
|
});
|
|
@@ -526,8 +526,7 @@ export class Call {
|
|
|
526
526
|
break;
|
|
527
527
|
}
|
|
528
528
|
} catch (err) {
|
|
529
|
-
this.logger(
|
|
530
|
-
'error',
|
|
529
|
+
this.logger.error(
|
|
531
530
|
`Can't disable mic/camera/screenshare after revoked permissions`,
|
|
532
531
|
err,
|
|
533
532
|
);
|
|
@@ -886,12 +885,12 @@ export class Call {
|
|
|
886
885
|
maxJoinRetries = Math.max(maxJoinRetries, 1);
|
|
887
886
|
for (let attempt = 0; attempt < maxJoinRetries; attempt++) {
|
|
888
887
|
try {
|
|
889
|
-
this.logger(
|
|
888
|
+
this.logger.trace(`Joining call (${attempt})`, this.cid);
|
|
890
889
|
await this.doJoin(data);
|
|
891
890
|
delete joinData.migrating_from;
|
|
892
891
|
break;
|
|
893
892
|
} catch (err) {
|
|
894
|
-
this.logger(
|
|
893
|
+
this.logger.warn(`Failed to join call (${attempt})`, this.cid);
|
|
895
894
|
if (err instanceof ErrorFromResponse && err.unrecoverable) {
|
|
896
895
|
// if the error is unrecoverable, we should not retry as that signals
|
|
897
896
|
// that connectivity is good, but the coordinator doesn't allow the user
|
|
@@ -927,7 +926,7 @@ export class Call {
|
|
|
927
926
|
|
|
928
927
|
this.joinCallData = data;
|
|
929
928
|
|
|
930
|
-
this.logger('
|
|
929
|
+
this.logger.debug('Starting join flow');
|
|
931
930
|
this.state.setCallingState(CallingState.JOINING);
|
|
932
931
|
|
|
933
932
|
const performingMigration =
|
|
@@ -990,9 +989,11 @@ export class Call {
|
|
|
990
989
|
// prepare a generic SDP and send it to the SFU.
|
|
991
990
|
// these are throw-away SDPs that the SFU will use to determine
|
|
992
991
|
// the capabilities of the client (codec support, etc.)
|
|
992
|
+
const { dangerouslyForceCodec, fmtpLine, subscriberFmtpLine } =
|
|
993
|
+
this.clientPublishOptions || {};
|
|
993
994
|
const [subscriberSdp, publisherSdp] = await Promise.all([
|
|
994
|
-
getGenericSdp('recvonly'),
|
|
995
|
-
getGenericSdp('sendonly'),
|
|
995
|
+
getGenericSdp('recvonly', dangerouslyForceCodec, subscriberFmtpLine),
|
|
996
|
+
getGenericSdp('sendonly', dangerouslyForceCodec, fmtpLine),
|
|
996
997
|
]);
|
|
997
998
|
const isReconnecting =
|
|
998
999
|
this.reconnectStrategy !== WebsocketReconnectStrategy.UNSPECIFIED;
|
|
@@ -1031,7 +1032,7 @@ export class Call {
|
|
|
1031
1032
|
);
|
|
1032
1033
|
}
|
|
1033
1034
|
} catch (error) {
|
|
1034
|
-
this.logger('
|
|
1035
|
+
this.logger.warn('Join SFU request failed', error);
|
|
1035
1036
|
sfuClient.close(
|
|
1036
1037
|
StreamSfuClient.JOIN_FAILED,
|
|
1037
1038
|
'Join request failed, connection considered unhealthy',
|
|
@@ -1103,7 +1104,7 @@ export class Call {
|
|
|
1103
1104
|
this.reconnectStrategy = WebsocketReconnectStrategy.UNSPECIFIED;
|
|
1104
1105
|
this.reconnectReason = '';
|
|
1105
1106
|
|
|
1106
|
-
this.logger(
|
|
1107
|
+
this.logger.info(`Joined call ${this.cid}`);
|
|
1107
1108
|
};
|
|
1108
1109
|
|
|
1109
1110
|
/**
|
|
@@ -1240,20 +1241,23 @@ export class Call {
|
|
|
1240
1241
|
if (closePreviousInstances && this.subscriber) {
|
|
1241
1242
|
this.subscriber.dispose();
|
|
1242
1243
|
}
|
|
1243
|
-
|
|
1244
|
+
const basePeerConnectionOptions: BasePeerConnectionOpts = {
|
|
1244
1245
|
sfuClient,
|
|
1245
1246
|
dispatcher: this.dispatcher,
|
|
1246
1247
|
state: this.state,
|
|
1247
1248
|
connectionConfig,
|
|
1248
1249
|
tag: sfuClient.tag,
|
|
1249
1250
|
enableTracing,
|
|
1250
|
-
|
|
1251
|
+
clientPublishOptions: this.clientPublishOptions,
|
|
1252
|
+
onReconnectionNeeded: (kind, reason, peerType) => {
|
|
1251
1253
|
this.reconnect(kind, reason).catch((err) => {
|
|
1252
|
-
const message = `[Reconnect] Error reconnecting after a
|
|
1253
|
-
this.logger(
|
|
1254
|
+
const message = `[Reconnect] Error reconnecting, after a ${PeerType[peerType]} error: ${reason}`;
|
|
1255
|
+
this.logger.warn(message, err);
|
|
1254
1256
|
});
|
|
1255
1257
|
},
|
|
1256
|
-
}
|
|
1258
|
+
};
|
|
1259
|
+
|
|
1260
|
+
this.subscriber = new Subscriber(basePeerConnectionOptions);
|
|
1257
1261
|
|
|
1258
1262
|
// anonymous users can't publish anything hence, there is no need
|
|
1259
1263
|
// to create Publisher Peer Connection for them
|
|
@@ -1262,21 +1266,7 @@ export class Call {
|
|
|
1262
1266
|
if (closePreviousInstances && this.publisher) {
|
|
1263
1267
|
this.publisher.dispose();
|
|
1264
1268
|
}
|
|
1265
|
-
this.publisher = new Publisher(
|
|
1266
|
-
sfuClient,
|
|
1267
|
-
dispatcher: this.dispatcher,
|
|
1268
|
-
state: this.state,
|
|
1269
|
-
connectionConfig,
|
|
1270
|
-
publishOptions,
|
|
1271
|
-
tag: sfuClient.tag,
|
|
1272
|
-
enableTracing,
|
|
1273
|
-
onReconnectionNeeded: (kind, reason) => {
|
|
1274
|
-
this.reconnect(kind, reason).catch((err) => {
|
|
1275
|
-
const message = `[Reconnect] Error reconnecting after a publisher error: ${reason}`;
|
|
1276
|
-
this.logger('warn', message, err);
|
|
1277
|
-
});
|
|
1278
|
-
},
|
|
1279
|
-
});
|
|
1269
|
+
this.publisher = new Publisher(basePeerConnectionOptions, publishOptions);
|
|
1280
1270
|
}
|
|
1281
1271
|
|
|
1282
1272
|
this.statsReporter?.stop();
|
|
@@ -1358,7 +1348,7 @@ export class Call {
|
|
|
1358
1348
|
sfuClient: StreamSfuClient,
|
|
1359
1349
|
reason: string,
|
|
1360
1350
|
) => {
|
|
1361
|
-
this.logger('
|
|
1351
|
+
this.logger.debug('[Reconnect] SFU signal connection closed');
|
|
1362
1352
|
const { callingState } = this.state;
|
|
1363
1353
|
if (
|
|
1364
1354
|
// SFU WS closed before we finished current join,
|
|
@@ -1381,7 +1371,7 @@ export class Call {
|
|
|
1381
1371
|
? WebsocketReconnectStrategy.FAST
|
|
1382
1372
|
: WebsocketReconnectStrategy.REJOIN;
|
|
1383
1373
|
this.reconnect(strategy, reason).catch((err) => {
|
|
1384
|
-
this.logger('
|
|
1374
|
+
this.logger.warn('[Reconnect] Error reconnecting', err);
|
|
1385
1375
|
});
|
|
1386
1376
|
};
|
|
1387
1377
|
|
|
@@ -1427,8 +1417,7 @@ export class Call {
|
|
|
1427
1417
|
reconnectingTime / 1000 > this.disconnectionTimeoutSeconds;
|
|
1428
1418
|
|
|
1429
1419
|
if (shouldGiveUpReconnecting) {
|
|
1430
|
-
this.logger(
|
|
1431
|
-
'warn',
|
|
1420
|
+
this.logger.warn(
|
|
1432
1421
|
'[Reconnect] Stopping reconnection attempts after reaching disconnection timeout',
|
|
1433
1422
|
);
|
|
1434
1423
|
await markAsReconnectingFailed();
|
|
@@ -1445,16 +1434,14 @@ export class Call {
|
|
|
1445
1434
|
// wait until the network is available
|
|
1446
1435
|
await this.networkAvailableTask?.promise;
|
|
1447
1436
|
|
|
1448
|
-
this.logger(
|
|
1449
|
-
'info',
|
|
1437
|
+
this.logger.info(
|
|
1450
1438
|
`[Reconnect] Reconnecting with strategy ${WebsocketReconnectStrategy[this.reconnectStrategy]}`,
|
|
1451
1439
|
);
|
|
1452
1440
|
|
|
1453
1441
|
switch (this.reconnectStrategy) {
|
|
1454
1442
|
case WebsocketReconnectStrategy.UNSPECIFIED:
|
|
1455
1443
|
case WebsocketReconnectStrategy.DISCONNECT:
|
|
1456
|
-
this.logger(
|
|
1457
|
-
'debug',
|
|
1444
|
+
this.logger.debug(
|
|
1458
1445
|
`[Reconnect] No-op strategy ${currentStrategy}`,
|
|
1459
1446
|
);
|
|
1460
1447
|
break;
|
|
@@ -1477,8 +1464,7 @@ export class Call {
|
|
|
1477
1464
|
break; // do-while loop, reconnection worked, exit the loop
|
|
1478
1465
|
} catch (error) {
|
|
1479
1466
|
if (this.state.callingState === CallingState.OFFLINE) {
|
|
1480
|
-
this.logger(
|
|
1481
|
-
'debug',
|
|
1467
|
+
this.logger.debug(
|
|
1482
1468
|
`[Reconnect] Can't reconnect while offline, stopping reconnection attempts`,
|
|
1483
1469
|
);
|
|
1484
1470
|
break;
|
|
@@ -1486,8 +1472,7 @@ export class Call {
|
|
|
1486
1472
|
// network change event will trigger the reconnection
|
|
1487
1473
|
}
|
|
1488
1474
|
if (error instanceof ErrorFromResponse && error.unrecoverable) {
|
|
1489
|
-
this.logger(
|
|
1490
|
-
'warn',
|
|
1475
|
+
this.logger.warn(
|
|
1491
1476
|
`[Reconnect] Can't reconnect due to coordinator unrecoverable error`,
|
|
1492
1477
|
error,
|
|
1493
1478
|
);
|
|
@@ -1520,8 +1505,7 @@ export class Call {
|
|
|
1520
1505
|
: WebsocketReconnectStrategy.FAST;
|
|
1521
1506
|
this.reconnectStrategy = nextStrategy;
|
|
1522
1507
|
|
|
1523
|
-
this.logger(
|
|
1524
|
-
'info',
|
|
1508
|
+
this.logger.info(
|
|
1525
1509
|
`[Reconnect] ${currentStrategy} (${this.reconnectAttempts}) failed. Attempting with ${WebsocketReconnectStrategy[nextStrategy]}`,
|
|
1526
1510
|
error,
|
|
1527
1511
|
);
|
|
@@ -1531,7 +1515,7 @@ export class Call {
|
|
|
1531
1515
|
this.state.callingState !== CallingState.RECONNECTING_FAILED &&
|
|
1532
1516
|
this.state.callingState !== CallingState.LEFT
|
|
1533
1517
|
);
|
|
1534
|
-
this.logger('
|
|
1518
|
+
this.logger.info('[Reconnect] Reconnection flow finished');
|
|
1535
1519
|
});
|
|
1536
1520
|
};
|
|
1537
1521
|
|
|
@@ -1633,7 +1617,7 @@ export class Call {
|
|
|
1633
1617
|
// handles the legacy "goAway" event
|
|
1634
1618
|
const unregisterGoAway = this.on('goAway', () => {
|
|
1635
1619
|
this.reconnect(WebsocketReconnectStrategy.MIGRATE, 'goAway').catch(
|
|
1636
|
-
(err) => this.logger('
|
|
1620
|
+
(err) => this.logger.warn('[Reconnect] Error reconnecting', err),
|
|
1637
1621
|
);
|
|
1638
1622
|
});
|
|
1639
1623
|
|
|
@@ -1643,11 +1627,11 @@ export class Call {
|
|
|
1643
1627
|
if (strategy === WebsocketReconnectStrategy.UNSPECIFIED) return;
|
|
1644
1628
|
if (strategy === WebsocketReconnectStrategy.DISCONNECT) {
|
|
1645
1629
|
this.leave({ message: 'SFU instructed to disconnect' }).catch((err) => {
|
|
1646
|
-
this.logger(
|
|
1630
|
+
this.logger.warn(`Can't leave call after disconnect request`, err);
|
|
1647
1631
|
});
|
|
1648
1632
|
} else {
|
|
1649
1633
|
this.reconnect(strategy, error?.message || 'SFU Error').catch((err) => {
|
|
1650
|
-
this.logger('
|
|
1634
|
+
this.logger.warn('[Reconnect] Error reconnecting', err);
|
|
1651
1635
|
});
|
|
1652
1636
|
}
|
|
1653
1637
|
});
|
|
@@ -1657,7 +1641,7 @@ export class Call {
|
|
|
1657
1641
|
(e) => {
|
|
1658
1642
|
this.tracer.trace('network.changed', e);
|
|
1659
1643
|
if (!e.online) {
|
|
1660
|
-
this.logger('
|
|
1644
|
+
this.logger.debug('[Reconnect] Going offline');
|
|
1661
1645
|
if (!this.hasJoinedOnce) return;
|
|
1662
1646
|
this.lastOfflineTimestamp = Date.now();
|
|
1663
1647
|
// create a new task that would resolve when the network is available
|
|
@@ -1674,8 +1658,7 @@ export class Call {
|
|
|
1674
1658
|
}
|
|
1675
1659
|
|
|
1676
1660
|
this.reconnect(strategy, 'Going online').catch((err) => {
|
|
1677
|
-
this.logger(
|
|
1678
|
-
'warn',
|
|
1661
|
+
this.logger.warn(
|
|
1679
1662
|
'[Reconnect] Error reconnecting after going online',
|
|
1680
1663
|
err,
|
|
1681
1664
|
);
|
|
@@ -1685,7 +1668,7 @@ export class Call {
|
|
|
1685
1668
|
this.sfuStatsReporter?.stop();
|
|
1686
1669
|
this.state.setCallingState(CallingState.OFFLINE);
|
|
1687
1670
|
} else {
|
|
1688
|
-
this.logger('
|
|
1671
|
+
this.logger.debug('[Reconnect] Going online');
|
|
1689
1672
|
this.sfuClient?.close(
|
|
1690
1673
|
StreamSfuClient.DISPOSE_OLD_SOCKET,
|
|
1691
1674
|
'Closing WS to reconnect after going online',
|
|
@@ -1875,14 +1858,12 @@ export class Call {
|
|
|
1875
1858
|
* @param options the options to use.
|
|
1876
1859
|
*/
|
|
1877
1860
|
updatePublishOptions = (options: ClientPublishOptions) => {
|
|
1878
|
-
this.logger(
|
|
1879
|
-
'warn',
|
|
1861
|
+
this.logger.warn(
|
|
1880
1862
|
'[call.updatePublishOptions]: You are manually overriding the publish options for this call. ' +
|
|
1881
1863
|
'This is not recommended, and it can cause call stability/compatibility issues. Use with caution.',
|
|
1882
1864
|
);
|
|
1883
1865
|
if (this.state.callingState === CallingState.JOINED) {
|
|
1884
|
-
this.logger(
|
|
1885
|
-
'warn',
|
|
1866
|
+
this.logger.warn(
|
|
1886
1867
|
'Updating publish options after joining the call does not have an effect',
|
|
1887
1868
|
);
|
|
1888
1869
|
}
|
|
@@ -1896,7 +1877,7 @@ export class Call {
|
|
|
1896
1877
|
*/
|
|
1897
1878
|
notifyNoiseCancellationStarting = async () => {
|
|
1898
1879
|
return this.sfuClient?.startNoiseCancellation().catch((err) => {
|
|
1899
|
-
this.logger('
|
|
1880
|
+
this.logger.warn('Failed to notify start of noise cancellation', err);
|
|
1900
1881
|
});
|
|
1901
1882
|
};
|
|
1902
1883
|
|
|
@@ -1907,7 +1888,7 @@ export class Call {
|
|
|
1907
1888
|
*/
|
|
1908
1889
|
notifyNoiseCancellationStopped = async () => {
|
|
1909
1890
|
return this.sfuClient?.stopNoiseCancellation().catch((err) => {
|
|
1910
|
-
this.logger('
|
|
1891
|
+
this.logger.warn('Failed to notify stop of noise cancellation', err);
|
|
1911
1892
|
});
|
|
1912
1893
|
};
|
|
1913
1894
|
|
|
@@ -2489,7 +2470,7 @@ export class Call {
|
|
|
2489
2470
|
reason: 'timeout',
|
|
2490
2471
|
message: `ringing timeout - ${this.isCreatedByMe ? 'no one accepted' : `user didn't interact with incoming call screen`}`,
|
|
2491
2472
|
}).catch((err) => {
|
|
2492
|
-
this.logger('
|
|
2473
|
+
this.logger.error('Failed to drop call', err);
|
|
2493
2474
|
});
|
|
2494
2475
|
}, timeoutInMs);
|
|
2495
2476
|
};
|
|
@@ -2653,10 +2634,10 @@ export class Call {
|
|
|
2653
2634
|
publish: boolean,
|
|
2654
2635
|
) => {
|
|
2655
2636
|
await this.camera.apply(settings.video, publish).catch((err) => {
|
|
2656
|
-
this.logger('
|
|
2637
|
+
this.logger.warn('Camera init failed', err);
|
|
2657
2638
|
});
|
|
2658
2639
|
await this.microphone.apply(settings.audio, publish).catch((err) => {
|
|
2659
|
-
this.logger('
|
|
2640
|
+
this.logger.warn('Mic init failed', err);
|
|
2660
2641
|
});
|
|
2661
2642
|
};
|
|
2662
2643
|
|
package/src/StreamSfuClient.ts
CHANGED
|
@@ -29,8 +29,7 @@ import { ICETrickle } from './gen/video/sfu/models/models';
|
|
|
29
29
|
import { StreamClient } from './coordinator/connection/client';
|
|
30
30
|
import { generateUUIDv4 } from './coordinator/connection/utils';
|
|
31
31
|
import { Credentials } from './gen/coordinator';
|
|
32
|
-
import {
|
|
33
|
-
import { getLogger, getLogLevel } from './logger';
|
|
32
|
+
import { ScopedLogger, videoLoggerSystem } from './logger';
|
|
34
33
|
import {
|
|
35
34
|
makeSafePromise,
|
|
36
35
|
PromiseWithResolvers,
|
|
@@ -152,7 +151,7 @@ export class StreamSfuClient {
|
|
|
152
151
|
private readonly unsubscribeIceTrickle: () => void;
|
|
153
152
|
private readonly unsubscribeNetworkChanged: () => void;
|
|
154
153
|
private readonly onSignalClose: ((reason: string) => void) | undefined;
|
|
155
|
-
private readonly logger:
|
|
154
|
+
private readonly logger: ScopedLogger;
|
|
156
155
|
readonly tag: string;
|
|
157
156
|
private readonly credentials: Credentials;
|
|
158
157
|
private readonly dispatcher: Dispatcher;
|
|
@@ -219,7 +218,7 @@ export class StreamSfuClient {
|
|
|
219
218
|
this.edgeName = server.edge_name;
|
|
220
219
|
this.joinResponseTimeout = joinResponseTimeout;
|
|
221
220
|
this.tag = tag;
|
|
222
|
-
this.logger = getLogger(
|
|
221
|
+
this.logger = videoLoggerSystem.getLogger('SfuClient', { tags: [tag] });
|
|
223
222
|
this.tracer = enableTracing
|
|
224
223
|
? new Tracer(`${tag}-${this.edgeName}`)
|
|
225
224
|
: undefined;
|
|
@@ -228,7 +227,8 @@ export class StreamSfuClient {
|
|
|
228
227
|
interceptors: [
|
|
229
228
|
withHeaders({ Authorization: `Bearer ${token}` }),
|
|
230
229
|
this.tracer && withRequestTracer(this.tracer.trace),
|
|
231
|
-
getLogLevel() === 'trace' &&
|
|
230
|
+
this.logger.getLogLevel() === 'trace' &&
|
|
231
|
+
withRequestLogger(this.logger, 'trace'),
|
|
232
232
|
].filter((v) => !!v),
|
|
233
233
|
});
|
|
234
234
|
|
|
@@ -346,7 +346,7 @@ export class StreamSfuClient {
|
|
|
346
346
|
close = (code: number = StreamSfuClient.NORMAL_CLOSURE, reason?: string) => {
|
|
347
347
|
this.isClosingClean = code !== StreamSfuClient.ERROR_CONNECTION_UNHEALTHY;
|
|
348
348
|
if (this.signalWs.readyState === WebSocket.OPEN) {
|
|
349
|
-
this.logger(
|
|
349
|
+
this.logger.debug(`Closing SFU WS connection: ${code} - ${reason}`);
|
|
350
350
|
this.signalWs.close(code, `js-client: ${reason}`);
|
|
351
351
|
this.signalWs.removeEventListener('close', this.handleWebSocketClose);
|
|
352
352
|
}
|
|
@@ -354,7 +354,7 @@ export class StreamSfuClient {
|
|
|
354
354
|
};
|
|
355
355
|
|
|
356
356
|
private dispose = () => {
|
|
357
|
-
this.logger('
|
|
357
|
+
this.logger.debug('Disposing SFU client');
|
|
358
358
|
this.unsubscribeIceTrickle();
|
|
359
359
|
this.unsubscribeNetworkChanged();
|
|
360
360
|
clearInterval(this.keepAliveInterval);
|
|
@@ -375,7 +375,7 @@ export class StreamSfuClient {
|
|
|
375
375
|
await this.joinTask;
|
|
376
376
|
await this.notifyLeave(reason);
|
|
377
377
|
} catch (err) {
|
|
378
|
-
this.logger('
|
|
378
|
+
this.logger.debug('Error notifying SFU about leaving call', err);
|
|
379
379
|
}
|
|
380
380
|
|
|
381
381
|
this.close(StreamSfuClient.NORMAL_CLOSURE, reason.substring(0, 115));
|
|
@@ -557,10 +557,10 @@ export class StreamSfuClient {
|
|
|
557
557
|
await this.signalReady(); // wait for the signal ws to be open
|
|
558
558
|
const msgJson = SfuRequest.toJson(message);
|
|
559
559
|
if (this.signalWs.readyState !== WebSocket.OPEN) {
|
|
560
|
-
this.logger('
|
|
560
|
+
this.logger.debug('Signal WS is not open. Skipping message', msgJson);
|
|
561
561
|
return;
|
|
562
562
|
}
|
|
563
|
-
this.logger(
|
|
563
|
+
this.logger.debug(`Sending message to: ${this.edgeName}`, msgJson);
|
|
564
564
|
this.signalWs.send(SfuRequest.toBinary(message));
|
|
565
565
|
};
|
|
566
566
|
|
|
@@ -569,7 +569,7 @@ export class StreamSfuClient {
|
|
|
569
569
|
timers.clearInterval(this.keepAliveInterval);
|
|
570
570
|
this.keepAliveInterval = timers.setInterval(() => {
|
|
571
571
|
this.ping().catch((e) => {
|
|
572
|
-
this.logger('
|
|
572
|
+
this.logger.error('Error sending healthCheckRequest to SFU', e);
|
|
573
573
|
});
|
|
574
574
|
}, this.pingIntervalInMs);
|
|
575
575
|
};
|
package/src/StreamVideoClient.ts
CHANGED
|
@@ -24,7 +24,6 @@ import type {
|
|
|
24
24
|
import {
|
|
25
25
|
AllClientEvents,
|
|
26
26
|
ClientEventListener,
|
|
27
|
-
Logger,
|
|
28
27
|
StreamClientOptions,
|
|
29
28
|
TokenOrProvider,
|
|
30
29
|
TokenProvider,
|
|
@@ -38,7 +37,7 @@ import {
|
|
|
38
37
|
getCallInitConcurrencyTag,
|
|
39
38
|
getInstanceKey,
|
|
40
39
|
} from './helpers/clientUtils';
|
|
41
|
-
import {
|
|
40
|
+
import { logToConsole, ScopedLogger, videoLoggerSystem } from './logger';
|
|
42
41
|
import { withoutConcurrency } from './helpers/concurrency';
|
|
43
42
|
import { enableTimerWorker } from './timers';
|
|
44
43
|
|
|
@@ -63,7 +62,7 @@ export class StreamVideoClient {
|
|
|
63
62
|
* @deprecated use the `client.state` getter.
|
|
64
63
|
*/
|
|
65
64
|
readonly readOnlyStateStore: StreamVideoReadOnlyStateStore;
|
|
66
|
-
readonly logger:
|
|
65
|
+
readonly logger: ScopedLogger;
|
|
67
66
|
|
|
68
67
|
protected readonly writeableStateStore: StreamVideoWriteableStateStore;
|
|
69
68
|
streamClient: StreamClient;
|
|
@@ -94,9 +93,13 @@ export class StreamVideoClient {
|
|
|
94
93
|
if (clientOptions?.enableTimerWorker) enableTimerWorker();
|
|
95
94
|
|
|
96
95
|
const rootLogger = clientOptions?.logger || logToConsole;
|
|
97
|
-
setLogger(rootLogger, clientOptions?.logLevel || 'warn');
|
|
98
96
|
|
|
99
|
-
|
|
97
|
+
videoLoggerSystem.configureLoggers({
|
|
98
|
+
default: { sink: rootLogger, level: clientOptions?.logLevel || 'warn' },
|
|
99
|
+
...clientOptions?.logOptions,
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
this.logger = videoLoggerSystem.getLogger('client');
|
|
100
103
|
this.rejectCallWhenBusy = clientOptions?.rejectCallWhenBusy ?? false;
|
|
101
104
|
|
|
102
105
|
this.streamClient = createCoordinatorClient(apiKey, clientOptions);
|
|
@@ -113,7 +116,7 @@ export class StreamVideoClient {
|
|
|
113
116
|
|
|
114
117
|
const tokenOrProvider = createTokenOrProvider(apiKeyOrArgs);
|
|
115
118
|
this.connectUser(user, tokenOrProvider).catch((err) => {
|
|
116
|
-
this.logger('
|
|
119
|
+
this.logger.error('Failed to connect', err);
|
|
117
120
|
});
|
|
118
121
|
}
|
|
119
122
|
}
|
|
@@ -149,8 +152,7 @@ export class StreamVideoClient {
|
|
|
149
152
|
private registerClientInstance = (apiKey: string, user: User) => {
|
|
150
153
|
const instanceKey = getInstanceKey(apiKey, user);
|
|
151
154
|
if (StreamVideoClient._instances.has(instanceKey)) {
|
|
152
|
-
this.logger(
|
|
153
|
-
'warn',
|
|
155
|
+
this.logger.warn(
|
|
154
156
|
`A StreamVideoClient already exists for ${user.id}; Prefer using getOrCreateInstance method`,
|
|
155
157
|
);
|
|
156
158
|
}
|
|
@@ -178,13 +180,13 @@ export class StreamVideoClient {
|
|
|
178
180
|
.map((call) => call.cid);
|
|
179
181
|
if (callsToReWatch.length <= 0) return;
|
|
180
182
|
|
|
181
|
-
this.logger(
|
|
183
|
+
this.logger.info(`Rewatching calls ${callsToReWatch.join(', ')}`);
|
|
182
184
|
this.queryCalls({
|
|
183
185
|
watch: true,
|
|
184
186
|
filter_conditions: { cid: { $in: callsToReWatch } },
|
|
185
187
|
sort: [{ field: 'cid', direction: 1 }],
|
|
186
188
|
}).catch((err) => {
|
|
187
|
-
this.logger('
|
|
189
|
+
this.logger.error('Failed to re-watch calls', err);
|
|
188
190
|
});
|
|
189
191
|
}),
|
|
190
192
|
);
|
|
@@ -198,7 +200,7 @@ export class StreamVideoClient {
|
|
|
198
200
|
*/
|
|
199
201
|
private initCallFromEvent = async (e: CallCreatedEvent | CallRingEvent) => {
|
|
200
202
|
if (this.state.connectedUser?.id === e.call.created_by.id) {
|
|
201
|
-
this.logger(
|
|
203
|
+
this.logger.debug(`Ignoring ${e.type} event sent by the current user`);
|
|
202
204
|
return;
|
|
203
205
|
}
|
|
204
206
|
|
|
@@ -210,8 +212,7 @@ export class StreamVideoClient {
|
|
|
210
212
|
if (call) {
|
|
211
213
|
if (ringing) {
|
|
212
214
|
if (this.shouldRejectCall(call.cid)) {
|
|
213
|
-
this.logger(
|
|
214
|
-
'info',
|
|
215
|
+
this.logger.info(
|
|
215
216
|
`Leaving call with busy reject reason ${call.cid} because user is busy`,
|
|
216
217
|
);
|
|
217
218
|
// remove the instance from the state store
|
|
@@ -238,10 +239,7 @@ export class StreamVideoClient {
|
|
|
238
239
|
|
|
239
240
|
if (ringing) {
|
|
240
241
|
if (this.shouldRejectCall(call.cid)) {
|
|
241
|
-
this.logger(
|
|
242
|
-
'info',
|
|
243
|
-
`Rejecting call ${call.cid} because user is busy`,
|
|
244
|
-
);
|
|
242
|
+
this.logger.info(`Rejecting call ${call.cid} because user is busy`);
|
|
245
243
|
// call is not in the state store yet, so just reject api is enough
|
|
246
244
|
await call.reject('busy');
|
|
247
245
|
} else {
|
|
@@ -251,11 +249,11 @@ export class StreamVideoClient {
|
|
|
251
249
|
} else {
|
|
252
250
|
call.state.updateFromCallResponse(e.call);
|
|
253
251
|
this.writeableStateStore.registerCall(call);
|
|
254
|
-
this.logger(
|
|
252
|
+
this.logger.info(`New call created and registered: ${call.cid}`);
|
|
255
253
|
}
|
|
256
254
|
});
|
|
257
255
|
} catch (err) {
|
|
258
|
-
this.logger(
|
|
256
|
+
this.logger.error(`Failed to init call from event ${e.type}`, err);
|
|
259
257
|
}
|
|
260
258
|
};
|
|
261
259
|
|
|
@@ -288,12 +286,12 @@ export class StreamVideoClient {
|
|
|
288
286
|
const errorQueue: Error[] = [];
|
|
289
287
|
for (let attempt = 0; attempt < maxConnectUserRetries; attempt++) {
|
|
290
288
|
try {
|
|
291
|
-
this.logger(
|
|
289
|
+
this.logger.trace(`Connecting user (${attempt})`, user);
|
|
292
290
|
return user.type === 'guest'
|
|
293
291
|
? await client.connectGuestUser(user)
|
|
294
292
|
: await client.connectUser(user, tokenOrProvider);
|
|
295
293
|
} catch (err) {
|
|
296
|
-
this.logger(
|
|
294
|
+
this.logger.warn(`Failed to connect a user (${attempt})`, err);
|
|
297
295
|
errorQueue.push(err as Error);
|
|
298
296
|
if (attempt === maxConnectUserRetries - 1) {
|
|
299
297
|
onConnectUserError?.(err as Error, errorQueue);
|