@stream-io/video-client 0.0.2-alpha.2 → 0.0.2-alpha.20

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.
@@ -13,6 +13,7 @@ import {
13
13
  import {
14
14
  CallAcceptedEvent,
15
15
  CallEndedEvent,
16
+ CallResponse,
16
17
  CallUpdatedEvent,
17
18
  } from '../../gen/coordinator';
18
19
  import { Call } from '../../Call';
@@ -93,28 +94,58 @@ describe('Call ringing events', () => {
93
94
  });
94
95
 
95
96
  describe(`call.rejected`, () => {
96
- it(`will leave the call if all callees have rejected`, async () => {
97
- const call = fakeCall();
98
- // @ts-expect-error
99
- call.state.setMembers([{ user_id: 'm1' }, { user_id: 'm2' }]);
97
+ it(`caller will leave the call if all callees have rejected`, async () => {
98
+ const call = fakeCall({ currentUserId: 'm1' });
99
+ call.state.setMetadata({
100
+ ...fakeMetadata(),
101
+ // @ts-ignore
102
+ created_by: { id: 'm1' },
103
+ });
104
+ call.state.setMembers([
105
+ // @ts-expect-error
106
+ { user_id: 'm1' },
107
+ // @ts-expect-error
108
+ { user_id: 'm2' },
109
+ // @ts-expect-error
110
+ { user_id: 'm3' },
111
+ ]);
112
+ call.state.setCallingState(CallingState.RINGING);
100
113
  vi.spyOn(call, 'leave').mockImplementation(async () => {
101
114
  console.log(`TEST: leave() called`);
102
115
  });
103
116
 
104
117
  const handler = watchCallRejected(call);
105
118
  // all members reject the call
106
- call.state.members.forEach(() => {
107
- // @ts-ignore
108
- const event: CallAcceptedEvent = { type: 'call.rejected' };
119
+ await handler({
120
+ type: 'call.rejected',
109
121
  // @ts-ignore
110
- handler(event);
122
+ user: {
123
+ id: 'm2',
124
+ },
125
+ call: {
126
+ // @ts-ignore
127
+ created_by: {
128
+ id: 'm1',
129
+ },
130
+ // @ts-ignore
131
+ session: {
132
+ rejected_by: {
133
+ m2: new Date().toISOString(),
134
+ m3: new Date().toISOString(),
135
+ },
136
+ },
137
+ },
111
138
  });
112
-
113
139
  expect(call.leave).toHaveBeenCalled();
114
140
  });
115
141
 
116
- it(`will not leave the call if only one callee rejects`, async () => {
142
+ it(`caller will not leave the call if only one callee rejects`, async () => {
117
143
  const call = fakeCall();
144
+ call.state.setMetadata({
145
+ ...fakeMetadata(),
146
+ // @ts-ignore
147
+ created_by: { id: 'm0' },
148
+ });
118
149
  // @ts-expect-error
119
150
  call.state.setMembers([{ user_id: 'm1' }, { user_id: 'm2' }]);
120
151
  vi.spyOn(call, 'leave').mockImplementation(async () => {
@@ -124,12 +155,71 @@ describe('Call ringing events', () => {
124
155
 
125
156
  // only one member rejects the call
126
157
  // @ts-ignore
127
- const event: CallAcceptedEvent = { type: 'call.rejected' };
158
+ const event: CallAcceptedEvent = {
159
+ type: 'call.rejected',
160
+ // @ts-ignore
161
+ user: {
162
+ id: 'm2',
163
+ },
164
+ call: {
165
+ // @ts-ignore
166
+ created_by: {
167
+ id: 'm0',
168
+ },
169
+ // @ts-ignore
170
+ session: {
171
+ rejected_by: {
172
+ m2: new Date().toISOString(),
173
+ },
174
+ },
175
+ },
176
+ };
128
177
  // @ts-ignore
129
178
  await handler(event);
130
179
 
131
180
  expect(call.leave).not.toHaveBeenCalled();
132
181
  });
182
+
183
+ it('callee will leave the call if caller rejects', async () => {
184
+ const call = fakeCall({ currentUserId: 'm1' });
185
+ call.state.setMetadata({
186
+ ...fakeMetadata(),
187
+ // @ts-ignore
188
+ created_by: { id: 'm0' },
189
+ });
190
+ // @ts-expect-error
191
+ call.state.setMembers([{ user_id: 'm1' }, { user_id: 'm2' }]);
192
+ vi.spyOn(call, 'leave').mockImplementation(async () => {
193
+ console.log(`TEST: leave() called`);
194
+ });
195
+ const handler = watchCallRejected(call);
196
+
197
+ // only one member rejects the call
198
+ // @ts-ignore
199
+ const event: CallAcceptedEvent = {
200
+ type: 'call.rejected',
201
+ // @ts-ignore
202
+ user: {
203
+ id: 'm0',
204
+ },
205
+ call: {
206
+ // @ts-ignore
207
+ created_by: {
208
+ id: 'm0',
209
+ },
210
+ // @ts-ignore
211
+ session: {
212
+ rejected_by: {
213
+ m0: new Date().toISOString(),
214
+ },
215
+ },
216
+ },
217
+ };
218
+ // @ts-ignore
219
+ await handler(event);
220
+
221
+ expect(call.leave).toHaveBeenCalled();
222
+ });
133
223
  });
134
224
 
135
225
  describe(`call.ended`, () => {
@@ -171,8 +261,7 @@ describe('Call ringing events', () => {
171
261
  });
172
262
 
173
263
  it(`will not leave the call if idle`, async () => {
174
- const ringing = false;
175
- const call = fakeCall(ringing);
264
+ const call = fakeCall({ ring: false });
176
265
  vi.spyOn(call, 'leave').mockImplementation(async () => {
177
266
  console.log(`TEST: leave() called`);
178
267
  });
@@ -189,10 +278,10 @@ describe('Call ringing events', () => {
189
278
  });
190
279
  });
191
280
 
192
- const fakeCall = (ring = true) => {
281
+ const fakeCall = ({ ring = true, currentUserId = 'test-user-id' } = {}) => {
193
282
  const store = new StreamVideoWriteableStateStore();
194
283
  store.setConnectedUser({
195
- id: 'test-user-id',
284
+ id: currentUserId,
196
285
  });
197
286
  const client = new StreamClient('api-key');
198
287
  return new Call({
@@ -203,3 +292,26 @@ const fakeCall = (ring = true) => {
203
292
  ringing: ring,
204
293
  });
205
294
  };
295
+
296
+ const fakeMetadata = (): CallResponse => {
297
+ return {
298
+ id: '12345',
299
+ type: 'development',
300
+ cid: 'development:12345',
301
+
302
+ // @ts-ignore
303
+ created_by: {
304
+ id: 'test-user-id',
305
+ },
306
+ own_capabilities: [],
307
+ blocked_user_ids: [],
308
+
309
+ // @ts-ignore
310
+ settings: {
311
+ ring: {
312
+ auto_cancel_timeout_ms: 30000,
313
+ incoming_call_timeout_ms: 30000,
314
+ },
315
+ },
316
+ };
317
+ };
@@ -24,7 +24,6 @@ describe('call.session events', () => {
24
24
 
25
25
  expect(state.metadata).toEqual({
26
26
  cid: 'cid',
27
- own_capabilities: [],
28
27
  session: {
29
28
  id: 'session-id',
30
29
  },
@@ -46,7 +45,6 @@ describe('call.session events', () => {
46
45
  });
47
46
  expect(state.metadata).toEqual({
48
47
  cid: 'cid',
49
- own_capabilities: [],
50
48
  session: {
51
49
  id: 'session-id',
52
50
  },
@@ -22,10 +22,7 @@ export const watchCallPermissionsUpdated = (state: CallState) => {
22
22
  if (event.type !== 'call.permissions_updated') return;
23
23
  const { localParticipant } = state;
24
24
  if (event.user.id === localParticipant?.userId) {
25
- state.setMetadata((metadata) => ({
26
- ...metadata!,
27
- own_capabilities: event.own_capabilities,
28
- }));
25
+ state.setOwnCapabilities(event.own_capabilities);
29
26
  }
30
27
  };
31
28
  };
@@ -49,7 +46,7 @@ export const watchCallGrantsUpdated = (state: CallState) => {
49
46
  [OwnCapability.SCREENSHARE]: canScreenshare,
50
47
  };
51
48
 
52
- const nextCapabilities = (state.metadata?.own_capabilities || []).filter(
49
+ const nextCapabilities = state.ownCapabilities.filter(
53
50
  (capability) => update[capability] !== false,
54
51
  );
55
52
  Object.entries(update).forEach(([capability, value]) => {
@@ -58,12 +55,7 @@ export const watchCallGrantsUpdated = (state: CallState) => {
58
55
  }
59
56
  });
60
57
 
61
- state.setMetadata((metadata) => {
62
- return {
63
- ...metadata!,
64
- own_capabilities: nextCapabilities,
65
- };
66
- });
58
+ state.setOwnCapabilities(nextCapabilities);
67
59
  }
68
60
  };
69
61
  };
@@ -27,16 +27,39 @@ export const watchCallAccepted = (call: Call) => {
27
27
  * Once the event is received, the call is left.
28
28
  */
29
29
  export const watchCallRejected = (call: Call) => {
30
- let totalRejections = 0;
31
30
  return async function onCallRejected(event: StreamVideoEvent) {
32
31
  if (event.type !== 'call.rejected') return;
33
- totalRejections++;
34
- const { state } = call;
35
- if (
36
- totalRejections >= state.members.length &&
37
- state.callingState === CallingState.RINGING
38
- ) {
39
- await call.leave();
32
+ // We want to discard the event if it's from the current user
33
+ if (event.user.id === call.currentUserId) return;
34
+ const { call: eventCall } = event;
35
+ const { session: callSession } = eventCall;
36
+
37
+ if (!callSession) {
38
+ console.log('No call session provided. Ignoring call.rejected event.');
39
+ return;
40
+ }
41
+
42
+ const rejectedBy = callSession.rejected_by;
43
+ const { members, callingState } = call.state;
44
+ if (callingState !== CallingState.RINGING) {
45
+ console.log(
46
+ 'Call is not in ringing mode (it is either accepted or rejected already). Ignoring call.rejected event.',
47
+ );
48
+ return;
49
+ }
50
+ if (call.isCreatedByMe) {
51
+ const everyoneElseRejected = members
52
+ .filter((m) => m.user_id !== call.currentUserId)
53
+ .every((m) => rejectedBy[m.user_id]);
54
+ if (everyoneElseRejected) {
55
+ console.log('everyone rejected, leaving the call');
56
+ await call.leave();
57
+ }
58
+ } else {
59
+ if (rejectedBy[eventCall.created_by.id]) {
60
+ console.log('call creator rejected, leaving call');
61
+ await call.leave();
62
+ }
40
63
  }
41
64
  };
42
65
  };
@@ -45,13 +45,22 @@ type RingCallEvents = Extract<
45
45
  'call.accepted' | 'call.rejected'
46
46
  >;
47
47
 
48
- // call.created is handled by the StreamVideoClient
49
- // custom events should be handled by integrators
50
48
  type AllCallEvents = Exclude<
51
49
  CallEventTypes,
52
- 'call.created' | 'custom' | RingCallEvents
50
+ | 'call.created' // handled by StreamVideoClient
51
+ | 'call.ring' // handled by StreamVideoClient
52
+ | 'call.notification' // not used currently
53
+ | 'custom' // integrators should handle custom events
54
+ | RingCallEvents // handled by registerRingingCallEventHandlers
53
55
  >;
54
56
 
57
+ /**
58
+ * Registers the default event handlers for a call during its lifecycle.
59
+ *
60
+ * @param call the call to register event handlers for.
61
+ * @param state the call state.
62
+ * @param dispatcher the dispatcher.
63
+ */
55
64
  export const registerEventHandlers = (
56
65
  call: Call,
57
66
  state: CallState,
@@ -80,10 +89,6 @@ export const registerEventHandlers = (
80
89
  'call.session_participant_left': watchCallSessionParticipantLeft(state),
81
90
  'call.unblocked_user': watchUnblockedUser(state),
82
91
  'call.updated': watchCallUpdated(state),
83
- 'call.notification': (event: StreamCallEvent) =>
84
- console.log(`Received ${event.type} event`, event),
85
- 'call.ring': (event: StreamCallEvent) =>
86
- console.log(`Received ${event.type} event`, event),
87
92
  };
88
93
  const eventHandlers = [
89
94
  watchChangePublishQuality(dispatcher, call),
@@ -117,6 +122,11 @@ export const registerEventHandlers = (
117
122
  };
118
123
  };
119
124
 
125
+ /**
126
+ * Registers event handlers for a call that is of ringing type.
127
+ *
128
+ * @param call the call to register event handlers for.
129
+ */
120
130
  export const registerRingingCallEventHandlers = (call: Call) => {
121
131
  const coordinatorRingEvents: {
122
132
  [key in RingCallEvents]: (e: StreamCallEvent) => any;
@@ -9,12 +9,7 @@ import { StreamVideoEvent } from '../coordinator/connection/types';
9
9
  export const watchCallSessionStarted = (state: CallState) => {
10
10
  return function onCallSessionStarted(event: StreamVideoEvent) {
11
11
  if (event.type !== 'call.session_started') return;
12
- const { call } = event;
13
- state.setMetadata((metadata) => ({
14
- ...call,
15
- // FIXME OL: temporary, until the backend sends the own_capabilities
16
- own_capabilities: metadata?.own_capabilities || [],
17
- }));
12
+ state.setMetadata(event.call);
18
13
  };
19
14
  };
20
15
 
@@ -26,12 +21,7 @@ export const watchCallSessionStarted = (state: CallState) => {
26
21
  export const watchCallSessionEnded = (state: CallState) => {
27
22
  return function onCallSessionEnded(event: StreamVideoEvent) {
28
23
  if (event.type !== 'call.session_ended') return;
29
- const { call } = event;
30
- state.setMetadata((metadata) => ({
31
- ...call,
32
- // FIXME OL: temporary, until the backend sends the own_capabilities
33
- own_capabilities: metadata?.own_capabilities || [],
34
- }));
24
+ state.setMetadata(event.call);
35
25
  };
36
26
  };
37
27
 
@@ -708,6 +708,12 @@ export interface CallNotificationEvent {
708
708
  * @memberof CallNotificationEvent
709
709
  */
710
710
  created_at: string;
711
+ /**
712
+ * Call members
713
+ * @type {Array<MemberResponse>}
714
+ * @memberof CallNotificationEvent
715
+ */
716
+ members: Array<MemberResponse>;
711
717
  /**
712
718
  * Call session ID
713
719
  * @type {string}
@@ -1022,12 +1028,6 @@ export interface CallResponse {
1022
1028
  * @memberof CallResponse
1023
1029
  */
1024
1030
  ingress: CallIngressResponse;
1025
- /**
1026
- * The capabilities of the current user
1027
- * @type {Array<OwnCapability>}
1028
- * @memberof CallResponse
1029
- */
1030
- own_capabilities: Array<OwnCapability>;
1031
1031
  /**
1032
1032
  *
1033
1033
  * @type {boolean}
@@ -1101,6 +1101,12 @@ export interface CallRingEvent {
1101
1101
  * @memberof CallRingEvent
1102
1102
  */
1103
1103
  created_at: string;
1104
+ /**
1105
+ * Call members
1106
+ * @type {Array<MemberResponse>}
1107
+ * @memberof CallRingEvent
1108
+ */
1109
+ members: Array<MemberResponse>;
1104
1110
  /**
1105
1111
  * Call session ID
1106
1112
  * @type {string}
@@ -1463,6 +1469,12 @@ export interface CallStateResponseFields {
1463
1469
  * @memberof CallStateResponseFields
1464
1470
  */
1465
1471
  membership?: MemberResponse;
1472
+ /**
1473
+ *
1474
+ * @type {Array<OwnCapability>}
1475
+ * @memberof CallStateResponseFields
1476
+ */
1477
+ own_capabilities: Array<OwnCapability>;
1466
1478
  }
1467
1479
  /**
1468
1480
  *
@@ -2036,62 +2048,6 @@ export interface GeofenceSettingsRequest {
2036
2048
  */
2037
2049
  names?: Array<string>;
2038
2050
  }
2039
- /**
2040
- *
2041
- * @export
2042
- * @interface GetCallEdgeServerRequest
2043
- */
2044
- export interface GetCallEdgeServerRequest {
2045
- /**
2046
- *
2047
- * @type {{ [key: string]: Array<number>; }}
2048
- * @memberof GetCallEdgeServerRequest
2049
- */
2050
- latency_measurements: { [key: string]: Array<number> };
2051
- }
2052
- /**
2053
- *
2054
- * @export
2055
- * @interface GetCallEdgeServerResponse
2056
- */
2057
- export interface GetCallEdgeServerResponse {
2058
- /**
2059
- *
2060
- * @type {Array<UserResponse>}
2061
- * @memberof GetCallEdgeServerResponse
2062
- */
2063
- blocked_users: Array<UserResponse>;
2064
- /**
2065
- *
2066
- * @type {CallResponse}
2067
- * @memberof GetCallEdgeServerResponse
2068
- */
2069
- call: CallResponse;
2070
- /**
2071
- *
2072
- * @type {Credentials}
2073
- * @memberof GetCallEdgeServerResponse
2074
- */
2075
- credentials: Credentials;
2076
- /**
2077
- * Duration of the request in human-readable format
2078
- * @type {string}
2079
- * @memberof GetCallEdgeServerResponse
2080
- */
2081
- duration: string;
2082
- /**
2083
- *
2084
- * @type {Array<MemberResponse>}
2085
- * @memberof GetCallEdgeServerResponse
2086
- */
2087
- members: Array<MemberResponse>;
2088
- /**
2089
- *
2090
- * @type {MemberResponse}
2091
- * @memberof GetCallEdgeServerResponse
2092
- */
2093
- membership?: MemberResponse;
2094
- }
2095
2051
  /**
2096
2052
  *
2097
2053
  * @export
@@ -2128,6 +2084,12 @@ export interface GetCallResponse {
2128
2084
  * @memberof GetCallResponse
2129
2085
  */
2130
2086
  membership?: MemberResponse;
2087
+ /**
2088
+ *
2089
+ * @type {Array<OwnCapability>}
2090
+ * @memberof GetCallResponse
2091
+ */
2092
+ own_capabilities: Array<OwnCapability>;
2131
2093
  }
2132
2094
  /**
2133
2095
  *
@@ -2270,6 +2232,12 @@ export interface GetOrCreateCallResponse {
2270
2232
  * @memberof GetOrCreateCallResponse
2271
2233
  */
2272
2234
  membership?: MemberResponse;
2235
+ /**
2236
+ *
2237
+ * @type {Array<OwnCapability>}
2238
+ * @memberof GetOrCreateCallResponse
2239
+ */
2240
+ own_capabilities: Array<OwnCapability>;
2273
2241
  }
2274
2242
  /**
2275
2243
  *
@@ -2456,6 +2424,12 @@ export interface JoinCallResponse {
2456
2424
  * @memberof JoinCallResponse
2457
2425
  */
2458
2426
  membership?: MemberResponse;
2427
+ /**
2428
+ *
2429
+ * @type {Array<OwnCapability>}
2430
+ * @memberof JoinCallResponse
2431
+ */
2432
+ own_capabilities: Array<OwnCapability>;
2459
2433
  }
2460
2434
  /**
2461
2435
  *
@@ -3213,12 +3187,6 @@ export interface RingSettings {
3213
3187
  * @memberof RingSettings
3214
3188
  */
3215
3189
  auto_cancel_timeout_ms: number;
3216
- /**
3217
- *
3218
- * @type {number}
3219
- * @memberof RingSettings
3220
- */
3221
- auto_reject_timeout_ms: number;
3222
3190
  /**
3223
3191
  *
3224
3192
  * @type {number}
@@ -3238,12 +3206,6 @@ export interface RingSettingsRequest {
3238
3206
  * @memberof RingSettingsRequest
3239
3207
  */
3240
3208
  auto_cancel_timeout_ms?: number;
3241
- /**
3242
- *
3243
- * @type {number}
3244
- * @memberof RingSettingsRequest
3245
- */
3246
- auto_reject_timeout_ms?: number;
3247
3209
  /**
3248
3210
  *
3249
3211
  * @type {number}
@@ -3745,6 +3707,12 @@ export interface UpdateCallRequest {
3745
3707
  * @interface UpdateCallResponse
3746
3708
  */
3747
3709
  export interface UpdateCallResponse {
3710
+ /**
3711
+ *
3712
+ * @type {Array<UserResponse>}
3713
+ * @memberof UpdateCallResponse
3714
+ */
3715
+ blocked_users: Array<UserResponse>;
3748
3716
  /**
3749
3717
  *
3750
3718
  * @type {CallResponse}
@@ -3757,6 +3725,24 @@ export interface UpdateCallResponse {
3757
3725
  * @memberof UpdateCallResponse
3758
3726
  */
3759
3727
  duration: string;
3728
+ /**
3729
+ *
3730
+ * @type {Array<MemberResponse>}
3731
+ * @memberof UpdateCallResponse
3732
+ */
3733
+ members: Array<MemberResponse>;
3734
+ /**
3735
+ *
3736
+ * @type {MemberResponse}
3737
+ * @memberof UpdateCallResponse
3738
+ */
3739
+ membership?: MemberResponse;
3740
+ /**
3741
+ *
3742
+ * @type {Array<OwnCapability>}
3743
+ * @memberof UpdateCallResponse
3744
+ */
3745
+ own_capabilities: Array<OwnCapability>;
3760
3746
  }
3761
3747
  /**
3762
3748
  *
@@ -27,6 +27,17 @@ export type SoundDetectorOptions = {
27
27
  destroyStreamOnStop?: boolean;
28
28
  };
29
29
 
30
+ export type SoundDetectorState = {
31
+ isSoundDetected: boolean;
32
+ /**
33
+ * Represented as percentage (0-100) where 100% is defined by `audioLevelThreshold` property.
34
+ * Decrease time between samples (to 50-100ms) with `detectionFrequencyInMs` property.
35
+ */
36
+ audioLevel: number;
37
+ };
38
+
39
+ export type SoundStateChangeHandler = (state: SoundDetectorState) => void;
40
+
30
41
  const DETECTION_FREQUENCY_IN_MS = 500;
31
42
  const AUDIO_LEVEL_THRESHOLD = 150;
32
43
  const FFT_SIZE = 128;
@@ -41,14 +52,7 @@ const FFT_SIZE = 128;
41
52
  */
42
53
  export const createSoundDetector = (
43
54
  audioStream: MediaStream,
44
- onSoundDetectedStateChanged: (
45
- isSoundDetected: boolean,
46
- /**
47
- * Represented as percentage (0-100) where 100% is defined by `audioLevelThreshold` property.
48
- * Decrease time between samples (to 50-100ms) with `detectionFrequencyInMs` property.
49
- */
50
- audioLevel: number,
51
- ) => void,
55
+ onSoundDetectedStateChanged: SoundStateChangeHandler,
52
56
  options: SoundDetectorOptions = {},
53
57
  ) => {
54
58
  const {
@@ -78,7 +82,7 @@ export const createSoundDetector = (
78
82
  ? 100
79
83
  : Math.round((averagedDataValue / audioLevelThreshold) * 100);
80
84
 
81
- onSoundDetectedStateChanged(isSoundDetected, percentage);
85
+ onSoundDetectedStateChanged({ isSoundDetected, audioLevel: percentage });
82
86
  }, detectionFrequencyInMs);
83
87
 
84
88
  return async function stop() {
@@ -24,13 +24,14 @@ export const join = async (
24
24
  await httpClient.connectionIdPromise;
25
25
 
26
26
  const joinCallResponse = await doJoin(httpClient, type, id, data);
27
- const { call, credentials, members } = joinCallResponse;
27
+ const { call, credentials, members, own_capabilities } = joinCallResponse;
28
28
  return {
29
29
  connectionConfig: toRtcConfiguration(credentials.ice_servers),
30
30
  sfuServer: credentials.server,
31
31
  token: credentials.token,
32
32
  metadata: call,
33
33
  members,
34
+ ownCapabilities: own_capabilities,
34
35
  };
35
36
  };
36
37
 
@@ -68,15 +69,20 @@ const doJoin = async (
68
69
 
69
70
  const getLocationHint = async () => {
70
71
  const hintURL = `https://hint.stream-io-video.com/`;
72
+ const abortController = new AbortController();
73
+ const timeoutId = setTimeout(() => abortController.abort(), 1000);
71
74
  try {
72
75
  const response = await fetch(hintURL, {
73
76
  method: 'HEAD',
77
+ signal: abortController.signal,
74
78
  });
75
79
  const awsPop = response.headers.get('x-amz-cf-pop') || 'ERR';
76
80
  return awsPop.substring(0, 3); // AMS1-P2 -> AMS
77
81
  } catch (e) {
78
82
  console.error(`Failed to get location hint from ${hintURL}`, e);
79
83
  return 'ERR';
84
+ } finally {
85
+ clearTimeout(timeoutId);
80
86
  }
81
87
  };
82
88
 
@@ -269,19 +269,14 @@ export class Publisher {
269
269
  };
270
270
 
271
271
  updateVideoPublishQuality = async (enabledRids: string[]) => {
272
- console.log(
273
- 'Updating publish quality, qualities requested by SFU:',
274
- enabledRids,
275
- );
272
+ console.log('Update publish quality, requested rids by SFU:', enabledRids);
276
273
 
277
274
  const videoSender = this.transceiverRegistry[TrackType.VIDEO]?.sender;
278
-
279
275
  if (!videoSender) return;
280
276
 
281
277
  const params = videoSender.getParameters();
282
278
  let changed = false;
283
279
  params.encodings.forEach((enc) => {
284
- console.log(enc.rid, enc.active);
285
280
  // flip 'active' flag only when necessary
286
281
  const shouldEnable = enabledRids.includes(enc.rid!);
287
282
  if (shouldEnable !== enc.active) {
@@ -294,6 +289,12 @@ export class Publisher {
294
289
  console.warn('No suitable video encoding quality found');
295
290
  }
296
291
  await videoSender.setParameters(params);
292
+ console.log(
293
+ `Update publish quality, enabled rids: ${params.encodings
294
+ .filter((e) => e.active)
295
+ .map((e) => e.rid)
296
+ .join(', ')}`,
297
+ );
297
298
  }
298
299
  };
299
300