@stream-io/video-client 0.0.13 → 0.0.15

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.
Files changed (45) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/index.browser.es.js +237 -130
  3. package/dist/index.browser.es.js.map +1 -1
  4. package/dist/index.cjs.js +239 -129
  5. package/dist/index.cjs.js.map +1 -1
  6. package/dist/index.d.ts +1 -0
  7. package/dist/index.es.js +237 -130
  8. package/dist/index.es.js.map +1 -1
  9. package/dist/src/Call.d.ts +2 -1
  10. package/dist/src/StreamSfuClient.d.ts +1 -0
  11. package/dist/src/StreamVideoClient.d.ts +5 -1
  12. package/dist/src/coordinator/connection/types.d.ts +3 -2
  13. package/dist/src/coordinator/connection/utils.d.ts +2 -1
  14. package/dist/src/logger.d.ts +4 -0
  15. package/dist/src/rtc/Dispatcher.d.ts +2 -0
  16. package/dist/src/rtc/IceTrickleBuffer.d.ts +2 -0
  17. package/dist/src/rtc/publisher.d.ts +1 -0
  18. package/dist/src/store/CallState.d.ts +2 -0
  19. package/dist/src/types.d.ts +1 -1
  20. package/index.ts +1 -0
  21. package/package.json +1 -1
  22. package/src/Call.ts +69 -42
  23. package/src/StreamSfuClient.ts +70 -29
  24. package/src/StreamVideoClient.ts +46 -3
  25. package/src/coordinator/connection/client.ts +22 -29
  26. package/src/coordinator/connection/connection.ts +2 -3
  27. package/src/coordinator/connection/connection_fallback.ts +0 -1
  28. package/src/coordinator/connection/types.ts +4 -2
  29. package/src/coordinator/connection/utils.ts +5 -2
  30. package/src/devices/devices.ts +10 -3
  31. package/src/events/__tests__/call-permissions.test.ts +2 -2
  32. package/src/events/call.ts +11 -4
  33. package/src/events/sessions.ts +7 -2
  34. package/src/logger.ts +45 -0
  35. package/src/rtc/Dispatcher.ts +14 -4
  36. package/src/rtc/IceTrickleBuffer.ts +8 -1
  37. package/src/rtc/__tests__/publisher.test.ts +1 -1
  38. package/src/rtc/codecs.ts +7 -5
  39. package/src/rtc/flows/join.ts +4 -1
  40. package/src/rtc/publisher.ts +31 -12
  41. package/src/rtc/signal.ts +8 -7
  42. package/src/rtc/subscriber.ts +16 -10
  43. package/src/stats/state-store-stats-reporter.ts +12 -4
  44. package/src/store/CallState.ts +7 -2
  45. package/src/types.ts +3 -2
@@ -24,6 +24,7 @@ import {
24
24
  setPreferredCodec,
25
25
  toggleDtx,
26
26
  } from '../helpers/sdp-munging';
27
+ import { Logger } from '../coordinator/connection/types';
27
28
 
28
29
  export type PublisherOpts = {
29
30
  sfuClient: StreamSfuClient;
@@ -63,6 +64,7 @@ export class Publisher {
63
64
  private isDtxEnabled: boolean;
64
65
  private isRedEnabled: boolean;
65
66
  private preferredVideoCodec?: string;
67
+ private logger?: Logger;
66
68
 
67
69
  constructor({
68
70
  connectionConfig,
@@ -124,7 +126,10 @@ export class Publisher {
124
126
  * Once the track has ended, it will notify the SFU and update the state.
125
127
  */
126
128
  const handleTrackEnded = async () => {
127
- console.log(`Track ${TrackType[trackType]} has ended, notifying the SFU`);
129
+ this.logger?.(
130
+ 'info',
131
+ `Track ${TrackType[trackType]} has ended, notifying the SFU`,
132
+ );
128
133
  await this.notifyTrackMuteStateChanged(
129
134
  mediaStream,
130
135
  track,
@@ -165,7 +170,8 @@ export class Publisher {
165
170
  this.transceiverRegistry[trackType] = transceiver;
166
171
 
167
172
  if ('setCodecPreferences' in transceiver && codecPreferences) {
168
- console.log(
173
+ this.logger?.(
174
+ 'info',
169
175
  `Setting ${TrackType[trackType]} codec preferences`,
170
176
  codecPreferences,
171
177
  );
@@ -283,7 +289,11 @@ export class Publisher {
283
289
  };
284
290
 
285
291
  updateVideoPublishQuality = async (enabledRids: string[]) => {
286
- console.log('Update publish quality, requested rids by SFU:', enabledRids);
292
+ this.logger?.(
293
+ 'info',
294
+ 'Update publish quality, requested rids by SFU:',
295
+ enabledRids,
296
+ );
287
297
 
288
298
  const videoSender = this.transceiverRegistry[TrackType.VIDEO]?.sender;
289
299
  if (!videoSender) return;
@@ -300,10 +310,11 @@ export class Publisher {
300
310
  });
301
311
  if (changed) {
302
312
  if (params.encodings.length === 0) {
303
- console.warn('No suitable video encoding quality found');
313
+ this.logger?.('warn', 'No suitable video encoding quality found');
304
314
  }
305
315
  await videoSender.setParameters(params);
306
- console.log(
316
+ this.logger?.(
317
+ 'info',
307
318
  `Update publish quality, enabled rids: ${params.encodings
308
319
  .filter((e) => e.active)
309
320
  .map((e) => e.rid)
@@ -342,7 +353,7 @@ export class Publisher {
342
353
  private onIceCandidate = async (e: RTCPeerConnectionIceEvent) => {
343
354
  const { candidate } = e;
344
355
  if (!candidate) {
345
- console.log('null ice candidate');
356
+ this.logger?.('warn', 'null ice candidate');
346
357
  return;
347
358
  }
348
359
  await this.sfuClient.iceTrickle({
@@ -352,7 +363,7 @@ export class Publisher {
352
363
  };
353
364
 
354
365
  private onNegotiationNeeded = async () => {
355
- console.log('AAA onNegotiationNeeded');
366
+ this.logger?.('info', 'AAA onNegotiationNeeded');
356
367
  const offer = await this.publisher.createOffer();
357
368
  let sdp = offer.sdp;
358
369
  if (sdp) {
@@ -430,7 +441,10 @@ export class Publisher {
430
441
  sdp: response.sdp,
431
442
  });
432
443
  } catch (e) {
433
- console.error(`Publisher: setRemoteDescription error`, response.sdp, e);
444
+ this.logger?.('error', `Publisher: setRemoteDescription error`, {
445
+ sdp: response.sdp,
446
+ error: e,
447
+ });
434
448
  }
435
449
 
436
450
  this.sfuClient.iceTrickleBuffer.publisherCandidates.subscribe(
@@ -439,7 +453,10 @@ export class Publisher {
439
453
  const iceCandidate = JSON.parse(candidate.iceCandidate);
440
454
  await this.publisher.addIceCandidate(iceCandidate);
441
455
  } catch (e) {
442
- console.error(`Publisher: ICE candidate error`, e, candidate);
456
+ this.logger?.('error', `Publisher: ICE candidate error`, {
457
+ error: e,
458
+ candidate,
459
+ });
443
460
  }
444
461
  },
445
462
  );
@@ -449,18 +466,20 @@ export class Publisher {
449
466
  const errorMessage =
450
467
  e instanceof RTCPeerConnectionIceErrorEvent &&
451
468
  `${e.errorCode}: ${e.errorText}`;
452
- console.error(`Publisher: ICE Candidate error`, errorMessage);
469
+ this.logger?.('error', `Publisher: ICE Candidate error`, errorMessage);
453
470
  };
454
471
 
455
472
  private onIceConnectionStateChange = () => {
456
- console.log(
473
+ this.logger?.(
474
+ 'error',
457
475
  `Publisher: ICE Connection state changed`,
458
476
  this.publisher.iceConnectionState,
459
477
  );
460
478
  };
461
479
 
462
480
  private onIceGatheringStateChange = () => {
463
- console.log(
481
+ this.logger?.(
482
+ 'error',
464
483
  `Publisher: ICE Gathering State`,
465
484
  this.publisher.iceGatheringState,
466
485
  );
package/src/rtc/signal.ts CHANGED
@@ -1,23 +1,25 @@
1
1
  import { SfuEvent } from '../gen/video/sfu/event/events';
2
+ import { getLogger } from '../logger';
2
3
 
3
4
  export const createWebSocketSignalChannel = (opts: {
4
5
  endpoint: string;
5
6
  onMessage?: (message: SfuEvent) => void;
6
7
  }) => {
8
+ const logger = getLogger(['sfu-client']);
7
9
  const { endpoint, onMessage } = opts;
8
10
  const ws = new WebSocket(endpoint);
9
11
  ws.binaryType = 'arraybuffer'; // do we need this?
10
12
 
11
13
  ws.addEventListener('error', (e) => {
12
- console.log('Signaling WS channel error', e);
14
+ logger?.('error', 'Signaling WS channel error', e);
13
15
  });
14
16
 
15
17
  ws.addEventListener('close', (e) => {
16
- console.log('Signaling WS channel is closed', e);
18
+ logger?.('info', 'Signaling WS channel is closed', e);
17
19
  });
18
20
 
19
21
  ws.addEventListener('open', (e) => {
20
- console.log('Signaling WS channel is open', e);
22
+ logger?.('info', 'Signaling WS channel is open', e);
21
23
  });
22
24
 
23
25
  if (onMessage) {
@@ -30,11 +32,10 @@ export const createWebSocketSignalChannel = (opts: {
30
32
 
31
33
  onMessage(message);
32
34
  } catch (err) {
33
- console.error(
35
+ logger?.(
36
+ 'error',
34
37
  'Failed to decode a message. Check whether the Proto models match.',
35
- e.data,
36
- e,
37
- err,
38
+ { event: e, error: err },
38
39
  );
39
40
  }
40
41
  });
@@ -2,6 +2,7 @@ import { StreamSfuClient } from '../StreamSfuClient';
2
2
  import { getIceCandidate } from './helpers/iceCandidate';
3
3
  import { PeerType } from '../gen/video/sfu/models/models';
4
4
  import { Dispatcher } from './Dispatcher';
5
+ import { getLogger } from '../logger';
5
6
 
6
7
  export type SubscriberOpts = {
7
8
  sfuClient: StreamSfuClient;
@@ -16,13 +17,14 @@ export const createSubscriber = ({
16
17
  connectionConfig,
17
18
  onTrack,
18
19
  }: SubscriberOpts) => {
20
+ const logger = getLogger(['sfu-client']);
19
21
  const subscriber = new RTCPeerConnection(connectionConfig);
20
22
  attachDebugEventListeners(subscriber);
21
23
 
22
24
  subscriber.addEventListener('icecandidate', async (e) => {
23
25
  const { candidate } = e;
24
26
  if (!candidate) {
25
- console.log('null ice candidate');
27
+ logger?.('warn', 'null ice candidate');
26
28
  return;
27
29
  }
28
30
 
@@ -40,7 +42,7 @@ export const createSubscriber = ({
40
42
  const unsubscribe = dispatcher.on('subscriberOffer', async (message) => {
41
43
  if (message.eventPayload.oneofKind !== 'subscriberOffer') return;
42
44
  const { subscriberOffer } = message.eventPayload;
43
- console.log(`Received subscriberOffer`, subscriberOffer);
45
+ logger?.('info', 'Received subscriberOffer', subscriberOffer);
44
46
 
45
47
  await subscriber.setRemoteDescription({
46
48
  type: 'offer',
@@ -52,7 +54,10 @@ export const createSubscriber = ({
52
54
  const iceCandidate = JSON.parse(candidate.iceCandidate);
53
55
  await subscriber.addIceCandidate(iceCandidate);
54
56
  } catch (e) {
55
- console.error(`Subscriber: ICE candidate error`, e, candidate);
57
+ logger?.('error', `Subscriber: ICE candidate error`, {
58
+ error: e,
59
+ candidate,
60
+ });
56
61
  }
57
62
  });
58
63
 
@@ -80,22 +85,23 @@ export const createSubscriber = ({
80
85
  };
81
86
 
82
87
  const attachDebugEventListeners = (subscriber: RTCPeerConnection) => {
88
+ const logger = getLogger(['sfu-client']);
83
89
  subscriber.addEventListener('icecandidateerror', (e) => {
84
90
  const errorMessage =
85
91
  e instanceof RTCPeerConnectionIceErrorEvent &&
86
92
  `${e.errorCode}: ${e.errorText}`;
87
- console.error(`Subscriber: ICE Candidate error`, errorMessage);
93
+ logger?.('error', `Subscriber: ICE Candidate error: ${errorMessage}`);
88
94
  });
89
95
  subscriber.addEventListener('iceconnectionstatechange', () => {
90
- console.log(
91
- `Subscriber: ICE Connection state changed`,
92
- subscriber.iceConnectionState,
96
+ logger?.(
97
+ 'info',
98
+ `Subscriber: ICE Connection state changed: ${subscriber.iceConnectionState}`,
93
99
  );
94
100
  });
95
101
  subscriber.addEventListener('icegatheringstatechange', () => {
96
- console.log(
97
- `Subscriber: ICE Gathering State`,
98
- subscriber.iceGatheringState,
102
+ logger?.(
103
+ 'info',
104
+ `Subscriber: ICE Gathering State: ${subscriber.iceGatheringState}`,
99
105
  );
100
106
  });
101
107
  };
@@ -6,6 +6,7 @@ import type {
6
6
  } from './types';
7
7
  import { CallState } from '../store';
8
8
  import { Publisher } from '../rtc';
9
+ import { getLogger } from '../logger';
9
10
 
10
11
  export type StatsReporterOpts = {
11
12
  subscriber: RTCPeerConnection;
@@ -69,6 +70,7 @@ export const createStatsReporter = ({
69
70
  edgeName,
70
71
  pollingIntervalInMs = 2000,
71
72
  }: StatsReporterOpts): StatsReporter => {
73
+ const logger = getLogger(['stats']);
72
74
  const getRawStatsForTrack = async (
73
75
  kind: 'subscriber' | 'publisher',
74
76
  selector?: MediaStreamTrack,
@@ -78,7 +80,7 @@ export const createStatsReporter = ({
78
80
  } else if (kind === 'publisher' && publisher) {
79
81
  return publisher.getStats(selector);
80
82
  } else {
81
- console.warn(`Can't retrieve RTC stats for`, kind);
83
+ logger('warn', `Can't retrieve RTC stats for ${kind}`);
82
84
  return undefined;
83
85
  }
84
86
  };
@@ -122,7 +124,9 @@ export const createStatsReporter = ({
122
124
  if (sessionIds.size > 0) {
123
125
  for (let participant of state.participants) {
124
126
  if (!sessionIds.has(participant.sessionId)) continue;
125
- const kind = participant.isLoggedInUser ? 'publisher' : 'subscriber';
127
+ const kind = participant.isLocalParticipant
128
+ ? 'publisher'
129
+ : 'subscriber';
126
130
  try {
127
131
  const mergedStream = new MediaStream([
128
132
  ...(participant.videoStream?.getVideoTracks() || []),
@@ -136,7 +140,11 @@ export const createStatsReporter = ({
136
140
  mergedStream.removeTrack(t);
137
141
  });
138
142
  } catch (e) {
139
- console.error(`Failed to collect stats for ${kind}`, participant, e);
143
+ logger(
144
+ 'error',
145
+ `Failed to collect stats for ${kind} if ${participant.userId}`,
146
+ e,
147
+ );
140
148
  }
141
149
  }
142
150
  }
@@ -182,7 +190,7 @@ export const createStatsReporter = ({
182
190
  if (pollingIntervalInMs > 0) {
183
191
  const loop = async () => {
184
192
  await run().catch((e) => {
185
- console.log('Failed to collect stats', e);
193
+ logger('warn', 'Failed to collect stats', e);
186
194
  });
187
195
  timeoutId = setTimeout(loop, pollingIntervalInMs);
188
196
  };
@@ -20,6 +20,8 @@ import {
20
20
  import { TrackType } from '../gen/video/sfu/models/models';
21
21
  import { Comparator } from '../sorting';
22
22
  import * as SortingPreset from '../sorting/presets';
23
+ import { getLogger } from '../logger';
24
+ import { Logger } from '../coordinator/connection/types';
23
25
 
24
26
  /**
25
27
  * Represents the state of the current call.
@@ -267,6 +269,8 @@ export class CallState {
267
269
  */
268
270
  callingState$: Observable<CallingState>;
269
271
 
272
+ readonly logger: Logger;
273
+
270
274
  /**
271
275
  * A list of comparators that are used to sort the participants.
272
276
  *
@@ -280,6 +284,7 @@ export class CallState {
280
284
  *
281
285
  */
282
286
  constructor() {
287
+ this.logger = getLogger(['call-state']);
283
288
  this.participants$ = this.participantsSubject.pipe(
284
289
  map((ps) => ps.sort(this.sortParticipantsBy)),
285
290
  );
@@ -289,7 +294,7 @@ export class CallState {
289
294
  );
290
295
 
291
296
  this.remoteParticipants$ = this.participants$.pipe(
292
- map((participants) => participants.filter((p) => !p.isLoggedInUser)),
297
+ map((participants) => participants.filter((p) => !p.isLocalParticipant)),
293
298
  );
294
299
 
295
300
  this.pinnedParticipants$ = this.participants$.pipe(
@@ -616,7 +621,7 @@ export class CallState {
616
621
  ) => {
617
622
  const participant = this.findParticipantBySessionId(sessionId);
618
623
  if (!participant) {
619
- console.warn(`Participant with sessionId ${sessionId} not found`);
624
+ this.logger('warn', `Participant with sessionId ${sessionId} not found`);
620
625
  return;
621
626
  }
622
627
 
package/src/types.ts CHANGED
@@ -13,6 +13,7 @@ import type { StreamClient } from './coordinator/connection/client';
13
13
  import type { Comparator } from './sorting';
14
14
  import type { StreamVideoWriteableStateStore } from './store';
15
15
  import { AxiosError } from 'axios';
16
+ import { Logger } from './coordinator/connection/types';
16
17
 
17
18
  export type StreamReaction = Pick<
18
19
  ReactionResponse,
@@ -66,7 +67,7 @@ export interface StreamVideoParticipant extends Participant {
66
67
  /**
67
68
  * True if the participant is the local participant.
68
69
  */
69
- isLoggedInUser?: boolean;
70
+ isLocalParticipant?: boolean;
70
71
 
71
72
  /**
72
73
  * Timestamp of when the participant is pinned
@@ -109,7 +110,7 @@ export interface StreamVideoLocalParticipant extends StreamVideoParticipant {
109
110
  export const isStreamVideoLocalParticipant = (
110
111
  p: StreamVideoParticipant | StreamVideoLocalParticipant,
111
112
  ): p is StreamVideoLocalParticipant => {
112
- return !!p.isLoggedInUser;
113
+ return !!p.isLocalParticipant;
113
114
  };
114
115
 
115
116
  /**