@100mslive/hms-video-store 0.12.19-alpha.21 → 0.12.19-alpha.23
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/dist/audio-sink-manager/AudioSinkManager.d.ts +0 -7
- package/dist/device-manager/DeviceManager.d.ts +7 -0
- package/dist/index.cjs.js +6 -6
- package/dist/index.cjs.js.map +3 -3
- package/dist/index.js +6 -6
- package/dist/index.js.map +3 -3
- package/dist/media/tracks/HMSLocalAudioTrack.d.ts +6 -0
- package/package.json +2 -2
- package/src/audio-sink-manager/AudioSinkManager.ts +0 -59
- package/src/device-manager/DeviceManager.ts +64 -1
- package/src/media/tracks/HMSLocalAudioTrack.ts +13 -1
- package/src/sdk/WakeLockManager.ts +1 -0
|
@@ -14,6 +14,12 @@ export declare class HMSLocalAudioTrack extends HMSAudioTrack {
|
|
|
14
14
|
private pluginsManager;
|
|
15
15
|
private processedTrack?;
|
|
16
16
|
private manuallySelectedDeviceId?;
|
|
17
|
+
/**
|
|
18
|
+
* This is to keep track of all the tracks created so far and stop and clear them when creating new tracks to release microphone
|
|
19
|
+
* This is needed because when replaceTrackWith is called before updating native track, there is no way that track is available
|
|
20
|
+
* for you to stop, which leads to the microphone not released even after leave is called.
|
|
21
|
+
*/
|
|
22
|
+
private tracksCreated;
|
|
17
23
|
audioLevelMonitor?: TrackAudioLevelMonitor;
|
|
18
24
|
/**
|
|
19
25
|
* see the doc in HMSLocalVideoTrack
|
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "0.12.19-alpha.
|
|
2
|
+
"version": "0.12.19-alpha.23",
|
|
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": "
|
|
76
|
+
"gitHead": "6d6c172062d69b0bd667798f32ce3a726dd796c5"
|
|
77
77
|
}
|
|
@@ -39,8 +39,6 @@ export class AudioSinkManager {
|
|
|
39
39
|
private volume = 100;
|
|
40
40
|
private state = { ...INITIAL_STATE };
|
|
41
41
|
private listener?: HMSUpdateListener;
|
|
42
|
-
private timer: ReturnType<typeof setInterval> | null = null;
|
|
43
|
-
private earpieceSelected = false;
|
|
44
42
|
|
|
45
43
|
constructor(private store: Store, private deviceManager: DeviceManager, private eventBus: EventBus) {
|
|
46
44
|
this.eventBus.audioTrackAdded.subscribe(this.handleTrackAdd);
|
|
@@ -48,7 +46,6 @@ export class AudioSinkManager {
|
|
|
48
46
|
this.eventBus.audioTrackUpdate.subscribe(this.handleTrackUpdate);
|
|
49
47
|
this.eventBus.deviceChange.subscribe(this.handleAudioDeviceChange);
|
|
50
48
|
this.eventBus.localVideoUnmutedNatively.subscribe(this.unpauseAudioTracks);
|
|
51
|
-
this.startPollingForDevices();
|
|
52
49
|
}
|
|
53
50
|
|
|
54
51
|
setListener(listener?: HMSUpdateListener) {
|
|
@@ -95,12 +92,7 @@ export class AudioSinkManager {
|
|
|
95
92
|
|
|
96
93
|
cleanup() {
|
|
97
94
|
this.audioSink?.remove();
|
|
98
|
-
this.earpieceSelected = false;
|
|
99
95
|
this.audioSink = undefined;
|
|
100
|
-
if (this.timer) {
|
|
101
|
-
clearInterval(this.timer);
|
|
102
|
-
this.timer = null;
|
|
103
|
-
}
|
|
104
96
|
this.eventBus.audioTrackAdded.unsubscribe(this.handleTrackAdd);
|
|
105
97
|
this.eventBus.audioTrackRemoved.unsubscribe(this.handleTrackRemove);
|
|
106
98
|
this.eventBus.audioTrackUpdate.unsubscribe(this.handleTrackUpdate);
|
|
@@ -185,11 +177,6 @@ export class AudioSinkManager {
|
|
|
185
177
|
};
|
|
186
178
|
|
|
187
179
|
private handleAudioDeviceChange = (event: HMSDeviceChangeEvent) => {
|
|
188
|
-
// this means the initial load
|
|
189
|
-
if (!event.selection) {
|
|
190
|
-
HMSLogger.d(this.TAG, 'device change called');
|
|
191
|
-
this.autoSelectAudioOutput();
|
|
192
|
-
}
|
|
193
180
|
// if there is no selection that means this is an init request. No need to do anything
|
|
194
181
|
if (event.isUserSelection || event.error || !event.selection || event.type === 'video') {
|
|
195
182
|
return;
|
|
@@ -260,50 +247,4 @@ export class AudioSinkManager {
|
|
|
260
247
|
track.setAudioElement(null);
|
|
261
248
|
}
|
|
262
249
|
};
|
|
263
|
-
|
|
264
|
-
private startPollingForDevices = () => {
|
|
265
|
-
// device change supported, no polling needed
|
|
266
|
-
if ('ondevicechange' in navigator.mediaDevices) {
|
|
267
|
-
return;
|
|
268
|
-
}
|
|
269
|
-
this.timer = setInterval(() => {
|
|
270
|
-
(async () => {
|
|
271
|
-
await this.deviceManager.init(true, false);
|
|
272
|
-
await this.autoSelectAudioOutput();
|
|
273
|
-
this.unpauseAudioTracks();
|
|
274
|
-
})();
|
|
275
|
-
}, 5000);
|
|
276
|
-
};
|
|
277
|
-
|
|
278
|
-
/**
|
|
279
|
-
* Mweb is not able to play via call channel by default, this is to switch from media channel to call channel
|
|
280
|
-
*/
|
|
281
|
-
// eslint-disable-next-line complexity
|
|
282
|
-
private autoSelectAudioOutput = async () => {
|
|
283
|
-
if ('ondevicechange' in navigator.mediaDevices) {
|
|
284
|
-
return;
|
|
285
|
-
}
|
|
286
|
-
const { bluetoothDevice, earpiece, speakerPhone, wired } = this.deviceManager.categorizeAudioInputDevices();
|
|
287
|
-
const localAudioTrack = this.store.getLocalPeer()?.audioTrack;
|
|
288
|
-
if (localAudioTrack && earpiece) {
|
|
289
|
-
const manualSelection = this.deviceManager.getManuallySelectedAudioDevice();
|
|
290
|
-
const externalDeviceID =
|
|
291
|
-
manualSelection?.deviceId || bluetoothDevice?.deviceId || wired?.deviceId || speakerPhone?.deviceId;
|
|
292
|
-
HMSLogger.d(this.TAG, 'externalDeviceID', externalDeviceID);
|
|
293
|
-
// already selected appropriate device
|
|
294
|
-
if (localAudioTrack.settings.deviceId === externalDeviceID && this.earpieceSelected) {
|
|
295
|
-
return;
|
|
296
|
-
}
|
|
297
|
-
if (!this.earpieceSelected && bluetoothDevice?.deviceId !== externalDeviceID) {
|
|
298
|
-
await localAudioTrack.setSettings({ deviceId: earpiece?.deviceId }, true);
|
|
299
|
-
this.earpieceSelected = true;
|
|
300
|
-
}
|
|
301
|
-
await localAudioTrack.setSettings(
|
|
302
|
-
{
|
|
303
|
-
deviceId: externalDeviceID,
|
|
304
|
-
},
|
|
305
|
-
true,
|
|
306
|
-
);
|
|
307
|
-
}
|
|
308
|
-
};
|
|
309
250
|
}
|
|
@@ -34,6 +34,8 @@ export class DeviceManager implements HMSDeviceManager {
|
|
|
34
34
|
private initialized = false;
|
|
35
35
|
private videoInputChanged = false;
|
|
36
36
|
private audioInputChanged = false;
|
|
37
|
+
private earpieceSelected = false;
|
|
38
|
+
private timer: ReturnType<typeof setTimeout> | null = null;
|
|
37
39
|
|
|
38
40
|
constructor(private store: Store, private eventBus: EventBus) {
|
|
39
41
|
const isLocalTrackEnabled = ({ enabled, track }: { enabled: boolean; track: HMSLocalTrack }) =>
|
|
@@ -97,6 +99,8 @@ export class DeviceManager implements HMSDeviceManager {
|
|
|
97
99
|
// do it only on initial load.
|
|
98
100
|
if (!force) {
|
|
99
101
|
await this.updateToActualDefaultDevice();
|
|
102
|
+
await this.autoSelectAudioOutput();
|
|
103
|
+
this.startPollingForDevices();
|
|
100
104
|
}
|
|
101
105
|
this.logDevices('Init');
|
|
102
106
|
await this.setOutputDevice();
|
|
@@ -124,6 +128,11 @@ export class DeviceManager implements HMSDeviceManager {
|
|
|
124
128
|
|
|
125
129
|
cleanup() {
|
|
126
130
|
this.initialized = false;
|
|
131
|
+
this.earpieceSelected = false;
|
|
132
|
+
if (this.timer) {
|
|
133
|
+
clearTimeout(this.timer);
|
|
134
|
+
this.timer = null;
|
|
135
|
+
}
|
|
127
136
|
this.audioInput = [];
|
|
128
137
|
this.audioOutput = [];
|
|
129
138
|
this.videoInput = [];
|
|
@@ -210,7 +219,10 @@ export class DeviceManager implements HMSDeviceManager {
|
|
|
210
219
|
const nonIPhoneDevice = this.audioInput.find(device => !device.label.toLowerCase().includes('iphone'));
|
|
211
220
|
return isIOS() && nonIPhoneDevice ? nonIPhoneDevice?.deviceId : this.getNewAudioInputDevice()?.deviceId;
|
|
212
221
|
};
|
|
213
|
-
|
|
222
|
+
const deviceIdToUpdate = getInitialDeviceId();
|
|
223
|
+
if (deviceIdToUpdate) {
|
|
224
|
+
await localPeer.audioTrack.setSettings({ deviceId: getInitialDeviceId() }, true);
|
|
225
|
+
}
|
|
214
226
|
}
|
|
215
227
|
};
|
|
216
228
|
|
|
@@ -446,6 +458,57 @@ export class DeviceManager implements HMSDeviceManager {
|
|
|
446
458
|
return { bluetoothDevice, speakerPhone, wired, earpiece };
|
|
447
459
|
}
|
|
448
460
|
|
|
461
|
+
private startPollingForDevices = () => {
|
|
462
|
+
// device change supported, no polling needed
|
|
463
|
+
if ('ondevicechange' in navigator.mediaDevices) {
|
|
464
|
+
return;
|
|
465
|
+
}
|
|
466
|
+
this.timer = setTimeout(() => {
|
|
467
|
+
(async () => {
|
|
468
|
+
await this.enumerateDevices();
|
|
469
|
+
await this.autoSelectAudioOutput();
|
|
470
|
+
this.startPollingForDevices();
|
|
471
|
+
})();
|
|
472
|
+
}, 5000);
|
|
473
|
+
};
|
|
474
|
+
|
|
475
|
+
/**
|
|
476
|
+
* Mweb is not able to play via call channel by default, this is to switch from media channel to call channel
|
|
477
|
+
*/
|
|
478
|
+
// eslint-disable-next-line complexity
|
|
479
|
+
private autoSelectAudioOutput = async () => {
|
|
480
|
+
if ('ondevicechange' in navigator.mediaDevices) {
|
|
481
|
+
return;
|
|
482
|
+
}
|
|
483
|
+
const { bluetoothDevice, earpiece, speakerPhone, wired } = this.categorizeAudioInputDevices();
|
|
484
|
+
const localAudioTrack = this.store.getLocalPeer()?.audioTrack;
|
|
485
|
+
if (!localAudioTrack || !earpiece) {
|
|
486
|
+
return;
|
|
487
|
+
}
|
|
488
|
+
const manualSelection = this.getManuallySelectedAudioDevice();
|
|
489
|
+
const externalDeviceID =
|
|
490
|
+
manualSelection?.deviceId || bluetoothDevice?.deviceId || wired?.deviceId || speakerPhone?.deviceId;
|
|
491
|
+
HMSLogger.d(this.TAG, 'externalDeviceID', externalDeviceID);
|
|
492
|
+
// already selected appropriate device
|
|
493
|
+
if (localAudioTrack.settings.deviceId === externalDeviceID && this.earpieceSelected) {
|
|
494
|
+
return;
|
|
495
|
+
}
|
|
496
|
+
if (!this.earpieceSelected) {
|
|
497
|
+
if (bluetoothDevice?.deviceId === externalDeviceID) {
|
|
498
|
+
this.earpieceSelected = true;
|
|
499
|
+
return;
|
|
500
|
+
}
|
|
501
|
+
await localAudioTrack.setSettings({ deviceId: earpiece?.deviceId }, true);
|
|
502
|
+
this.earpieceSelected = true;
|
|
503
|
+
}
|
|
504
|
+
await localAudioTrack.setSettings(
|
|
505
|
+
{
|
|
506
|
+
deviceId: externalDeviceID,
|
|
507
|
+
},
|
|
508
|
+
true,
|
|
509
|
+
);
|
|
510
|
+
};
|
|
511
|
+
|
|
449
512
|
// eslint-disable-next-line complexity
|
|
450
513
|
private getAudioOutputDeviceMatchingInput(inputDevice?: MediaDeviceInfo) {
|
|
451
514
|
const blacklist = this.store.getConfig()?.settings?.speakerAutoSelectionBlacklist || [];
|
|
@@ -26,6 +26,12 @@ export class HMSLocalAudioTrack extends HMSAudioTrack {
|
|
|
26
26
|
private pluginsManager: HMSAudioPluginsManager;
|
|
27
27
|
private processedTrack?: MediaStreamTrack;
|
|
28
28
|
private manuallySelectedDeviceId?: string;
|
|
29
|
+
/**
|
|
30
|
+
* This is to keep track of all the tracks created so far and stop and clear them when creating new tracks to release microphone
|
|
31
|
+
* This is needed because when replaceTrackWith is called before updating native track, there is no way that track is available
|
|
32
|
+
* for you to stop, which leads to the microphone not released even after leave is called.
|
|
33
|
+
*/
|
|
34
|
+
private tracksCreated = new Set<MediaStreamTrack>();
|
|
29
35
|
|
|
30
36
|
audioLevelMonitor?: TrackAudioLevelMonitor;
|
|
31
37
|
|
|
@@ -127,6 +133,7 @@ export class HMSLocalAudioTrack extends HMSAudioTrack {
|
|
|
127
133
|
* @param track
|
|
128
134
|
*/
|
|
129
135
|
private async updateTrack(track: MediaStreamTrack) {
|
|
136
|
+
track.enabled = this.enabled;
|
|
130
137
|
const localStream = this.stream as HMSLocalStream;
|
|
131
138
|
await localStream.replaceStreamTrack(this.nativeTrack, track);
|
|
132
139
|
// change nativeTrack so plugin can start its work
|
|
@@ -144,14 +151,17 @@ export class HMSLocalAudioTrack extends HMSAudioTrack {
|
|
|
144
151
|
* no audio when the above getAudioTrack throws an error. ex: DeviceInUse error
|
|
145
152
|
*/
|
|
146
153
|
prevTrack?.stop();
|
|
154
|
+
this.tracksCreated.forEach(track => track.stop());
|
|
155
|
+
this.tracksCreated.clear();
|
|
147
156
|
try {
|
|
148
157
|
const newTrack = await getAudioTrack(settings);
|
|
149
|
-
|
|
158
|
+
this.tracksCreated.add(newTrack);
|
|
150
159
|
HMSLogger.d(this.TAG, 'replaceTrack, Previous track stopped', prevTrack, 'newTrack', newTrack);
|
|
151
160
|
await this.updateTrack(newTrack);
|
|
152
161
|
} catch (e) {
|
|
153
162
|
// Generate a new track from previous settings so there will be audio because previous track is stopped
|
|
154
163
|
const newTrack = await getAudioTrack(this.settings);
|
|
164
|
+
this.tracksCreated.add(newTrack);
|
|
155
165
|
await this.updateTrack(newTrack);
|
|
156
166
|
if (this.isPublished) {
|
|
157
167
|
this.eventBus.analytics.publish(
|
|
@@ -271,6 +281,8 @@ export class HMSLocalAudioTrack extends HMSAudioTrack {
|
|
|
271
281
|
await this.pluginsManager.closeContext();
|
|
272
282
|
this.transceiver = undefined;
|
|
273
283
|
this.processedTrack?.stop();
|
|
284
|
+
this.tracksCreated.forEach(track => track.stop());
|
|
285
|
+
this.tracksCreated.clear();
|
|
274
286
|
this.isPublished = false;
|
|
275
287
|
this.destroyAudioLevelMonitor();
|
|
276
288
|
document.removeEventListener('visibilitychange', this.handleVisibilityChange);
|