@100mslive/hms-video-store 0.12.37 → 0.12.38-alpha.0

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.
@@ -20,6 +20,7 @@ export declare class HMSLocalAudioTrack extends HMSAudioTrack {
20
20
  * for you to stop, which leads to the microphone not released even after leave is called.
21
21
  */
22
22
  private tracksCreated;
23
+ private permissionState?;
23
24
  audioLevelMonitor?: TrackAudioLevelMonitor;
24
25
  /**
25
26
  * see the doc in HMSLocalVideoTrack
@@ -16,6 +16,7 @@ export declare class HMSLocalVideoTrack extends HMSVideoTrack {
16
16
  private _layerDefinitions;
17
17
  private TAG;
18
18
  private enabledStateBeforeBackground;
19
+ private permissionState?;
19
20
  /**
20
21
  * true if it's screenshare and current tab is what is being shared. Browser dependent, Chromium only
21
22
  * at the point of writing this comment.
@@ -0,0 +1 @@
1
+ export {};
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.12.37",
2
+ "version": "0.12.38-alpha.0",
3
3
  "license": "MIT",
4
4
  "repository": {
5
5
  "type": "git",
@@ -73,5 +73,5 @@
73
73
  "conferencing",
74
74
  "100ms"
75
75
  ],
76
- "gitHead": "fb5f4afe6a342bcb25bed49bdb294b797e68b005"
76
+ "gitHead": "37d70e0a4986c804cb8dcc51fecb4ea1c75761dc"
77
77
  }
@@ -31,13 +31,21 @@ export class AnalyticsTimer {
31
31
  private eventPerformanceMeasures: Partial<Record<TimedEvent, PerformanceMeasure>> = {};
32
32
 
33
33
  start(eventName: TimedEvent) {
34
- performance.mark(eventName);
34
+ try {
35
+ if (typeof performance !== 'undefined' && performance.mark) {
36
+ performance.mark(eventName);
37
+ }
38
+ } catch (error) {
39
+ HMSLogger.w('[AnalyticsTimer]', `Error marking performance for event ${eventName}`, { error });
40
+ }
35
41
  }
36
42
 
37
43
  end(eventName: TimedEvent) {
38
44
  try {
39
- this.eventPerformanceMeasures[eventName] = performance.measure(eventName, eventName);
40
- HMSLogger.d('[HMSPerformanceTiming]', eventName, this.eventPerformanceMeasures[eventName]?.duration);
45
+ if (typeof performance !== 'undefined' && performance.measure) {
46
+ this.eventPerformanceMeasures[eventName] = performance.measure(eventName, eventName);
47
+ HMSLogger.d('[HMSPerformanceTiming]', eventName, this.eventPerformanceMeasures[eventName]?.duration);
48
+ }
41
49
  } catch (error) {
42
50
  HMSLogger.w('[AnalyticsTimer]', `Error in measuring performance for event ${eventName}`, { error });
43
51
  }
@@ -2,11 +2,13 @@ import isEqual from 'lodash.isequal';
2
2
  import { HMSAudioTrack } from './HMSAudioTrack';
3
3
  import AnalyticsEventFactory from '../../analytics/AnalyticsEventFactory';
4
4
  import { DeviceStorageManager } from '../../device-manager/DeviceStorage';
5
+ import { ErrorCodes } from '../../error/ErrorCodes';
5
6
  import { HMSException } from '../../error/HMSException';
6
7
  import { EventBus } from '../../events/EventBus';
7
8
  import { HMSAudioTrackSettings as IHMSAudioTrackSettings } from '../../interfaces';
8
9
  import { HMSAudioPlugin, HMSPluginSupportResult } from '../../plugins';
9
10
  import { HMSAudioPluginsManager } from '../../plugins/audio';
11
+ import { LocalTrackManager } from '../../sdk/LocalTrackManager';
10
12
  import Room from '../../sdk/models/HMSRoom';
11
13
  import HMSLogger from '../../utils/logger';
12
14
  import { getAudioTrack, isEmptyTrack, listenToPermissionChange } from '../../utils/track';
@@ -33,6 +35,7 @@ export class HMSLocalAudioTrack extends HMSAudioTrack {
33
35
  */
34
36
  private tracksCreated = new Set<MediaStreamTrack>();
35
37
 
38
+ private permissionState?: PermissionState;
36
39
  audioLevelMonitor?: TrackAudioLevelMonitor;
37
40
 
38
41
  /**
@@ -113,18 +116,22 @@ export class HMSLocalAudioTrack extends HMSAudioTrack {
113
116
  }),
114
117
  );
115
118
  } else {
116
- HMSLogger.d(this.TAG, 'On visibile replacing track as it is not publishing');
117
- try {
118
- await this.replaceTrackWith(this.settings);
119
- } catch (error) {
120
- this.eventBus.error.publish(error as HMSException);
121
- }
122
119
  this.eventBus.analytics.publish(
123
120
  this.sendInterruptionEvent({
124
121
  started: false,
125
122
  reason: 'visibility-change',
126
123
  }),
127
124
  );
125
+ if (this.permissionState && this.permissionState !== 'granted') {
126
+ HMSLogger.d(this.TAG, 'On visibile not replacing track as permission is not granted');
127
+ return;
128
+ }
129
+ HMSLogger.d(this.TAG, 'On visibile replacing track as it is not publishing');
130
+ try {
131
+ await this.replaceTrackWith(this.settings);
132
+ } catch (error) {
133
+ this.eventBus.error.publish(error as HMSException);
134
+ }
128
135
  }
129
136
  };
130
137
 
@@ -161,6 +168,18 @@ export class HMSLocalAudioTrack extends HMSAudioTrack {
161
168
  HMSLogger.d(this.TAG, 'replaceTrack, Previous track stopped', prevTrack, 'newTrack', newTrack);
162
169
  await this.updateTrack(newTrack);
163
170
  } catch (e) {
171
+ const error = e as HMSException;
172
+
173
+ if (
174
+ error.code === ErrorCodes.TracksErrors.CANT_ACCESS_CAPTURE_DEVICE ||
175
+ error.code === ErrorCodes.TracksErrors.SYSTEM_DENIED_PERMISSION
176
+ ) {
177
+ const newTrack = await LocalTrackManager.getEmptyAudioTrack();
178
+ this.addTrackEventListeners(newTrack);
179
+ this.tracksCreated.add(newTrack);
180
+ await this.updateTrack(newTrack);
181
+ throw error;
182
+ }
164
183
  // Generate a new track from previous settings so there will be audio because previous track is stopped
165
184
  const newTrack = await getAudioTrack(this.settings);
166
185
  this.addTrackEventListeners(newTrack);
@@ -317,6 +336,7 @@ export class HMSLocalAudioTrack extends HMSAudioTrack {
317
336
 
318
337
  private trackPermissions = () => {
319
338
  listenToPermissionChange('microphone', (state: PermissionState) => {
339
+ this.permissionState = state;
320
340
  this.eventBus.analytics.publish(AnalyticsEventFactory.permissionChange(this.type, state));
321
341
  if (state === 'denied') {
322
342
  this.eventBus.localAudioEnabled.publish({ enabled: false, track: this });
@@ -3,6 +3,7 @@ import { HMSVideoTrack } from './HMSVideoTrack';
3
3
  import { VideoElementManager } from './VideoElementManager';
4
4
  import AnalyticsEventFactory from '../../analytics/AnalyticsEventFactory';
5
5
  import { DeviceStorageManager } from '../../device-manager/DeviceStorage';
6
+ import { ErrorCodes } from '../../error/ErrorCodes';
6
7
  import { ErrorFactory } from '../../error/ErrorFactory';
7
8
  import { HMSAction } from '../../error/HMSAction';
8
9
  import { HMSException } from '../../error/HMSException';
@@ -40,6 +41,7 @@ export class HMSLocalVideoTrack extends HMSVideoTrack {
40
41
  private _layerDefinitions: HMSSimulcastLayerDefinition[] = [];
41
42
  private TAG = '[HMSLocalVideoTrack]';
42
43
  private enabledStateBeforeBackground = false;
44
+ private permissionState?: PermissionState;
43
45
 
44
46
  /**
45
47
  * true if it's screenshare and current tab is what is being shared. Browser dependent, Chromium only
@@ -392,7 +394,20 @@ export class HMSLocalVideoTrack extends HMSVideoTrack {
392
394
  this.settings = this.buildNewSettings({ deviceId: this.nativeTrack.getSettings().deviceId });
393
395
  }
394
396
  return newTrack;
395
- } catch (error) {
397
+ } catch (e) {
398
+ const error = e as HMSException;
399
+
400
+ if (
401
+ error.code === ErrorCodes.TracksErrors.CANT_ACCESS_CAPTURE_DEVICE ||
402
+ error.code === ErrorCodes.TracksErrors.SYSTEM_DENIED_PERMISSION
403
+ ) {
404
+ const track = await this.replaceTrackWithBlank();
405
+ this.addTrackEventListeners(track);
406
+ await this.replaceSender(track, this.enabled);
407
+ this.nativeTrack = track;
408
+ this.videoHandler.updateSinks();
409
+ throw error;
410
+ }
396
411
  // Generate a new track from previous settings so there won't be blank tile because previous track is stopped
397
412
  const track = await getVideoTrack(this.settings);
398
413
  this.addTrackEventListeners(track);
@@ -595,6 +610,17 @@ export class HMSLocalVideoTrack extends HMSVideoTrack {
595
610
  }),
596
611
  );
597
612
  } else {
613
+ // ended interruption event
614
+ this.eventBus.analytics.publish(
615
+ this.sendInterruptionEvent({
616
+ started: false,
617
+ reason: 'visibility-change',
618
+ }),
619
+ );
620
+ if (this.permissionState && this.permissionState !== 'granted') {
621
+ HMSLogger.d(this.TAG, 'On visibile not replacing track as permission is not granted');
622
+ return;
623
+ }
598
624
  HMSLogger.d(this.TAG, 'visibility visible, restoring track state', this.enabledStateBeforeBackground);
599
625
  if (this.enabledStateBeforeBackground) {
600
626
  try {
@@ -603,13 +629,6 @@ export class HMSLocalVideoTrack extends HMSVideoTrack {
603
629
  this.eventBus.error.publish(error as HMSException);
604
630
  }
605
631
  }
606
- // ended interruption event
607
- this.eventBus.analytics.publish(
608
- this.sendInterruptionEvent({
609
- started: false,
610
- reason: 'visibility-change',
611
- }),
612
- );
613
632
  }
614
633
  };
615
634
  }
@@ -189,7 +189,6 @@ export const getActiveCandidatePairFromReport = (report?: RTCStatsReport): RTCIc
189
189
  report?.forEach(stat => {
190
190
  if (stat.type === 'transport') {
191
191
  // TS doesn't have correct types for RTCStatsReports
192
- // @ts-expect-error
193
192
  activeCandidatePair = report?.get(stat.selectedCandidatePairId);
194
193
  }
195
194
  });
@@ -0,0 +1,14 @@
1
+ // Setup for Jest tests
2
+
3
+ // Mock Performance API properly for Jest 29
4
+ global.performance = {
5
+ mark: jest.fn(),
6
+ measure: jest.fn(() => ({ duration: 0 } as PerformanceMeasure)),
7
+ clearMarks: jest.fn(),
8
+ clearMeasures: jest.fn(),
9
+ getEntriesByName: jest.fn(() => []),
10
+ getEntriesByType: jest.fn(() => []),
11
+ now: jest.fn(() => Date.now()),
12
+ } as any;
13
+
14
+ export {};
@@ -45,7 +45,9 @@ export const listenToPermissionChange = (
45
45
  // @ts-ignore
46
46
  .query({ name: permissionName })
47
47
  .then(permission => {
48
+ onChange(permission.state);
48
49
  permission.onchange = () => {
50
+ HMSLogger.d(`${permissionName} permission changed`, permission.state);
49
51
  onChange(permission.state);
50
52
  };
51
53
  })