@100mslive/hms-video-store 0.8.5-alpha.0 → 0.8.5-alpha.2
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/controller/beam/BeamSpeakerLabelsLogger.d.ts +28 -0
- package/dist/core/IHMSActions.d.ts +7 -1
- package/dist/core/hmsSDKStore/HMSReactiveStore.d.ts +1 -0
- package/dist/core/hmsSDKStore/HMSSDKActions.d.ts +3 -0
- package/dist/core/hmsSDKStore/sdkTypes.d.ts +2 -2
- package/dist/core/index.d.ts +1 -1
- package/dist/index.cjs.js +1 -1
- package/dist/index.cjs.js.map +3 -3
- package/dist/index.js +1 -1
- package/dist/index.js.map +3 -3
- package/package.json +3 -3
- package/src/controller/beam/BeamSpeakerLabelsLogger.ts +146 -0
- package/src/core/IHMSActions.ts +11 -0
- package/src/core/hmsSDKStore/HMSReactiveStore.ts +1 -0
- package/src/core/hmsSDKStore/HMSSDKActions.ts +26 -6
- package/src/core/hmsSDKStore/sdkTypes.ts +6 -0
- package/src/core/index.ts +3 -0
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "0.8.5-alpha.
|
|
2
|
+
"version": "0.8.5-alpha.2",
|
|
3
3
|
"license": "MIT",
|
|
4
4
|
"main": "dist/index.cjs.js",
|
|
5
5
|
"module": "dist/index.js",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"author": "100ms",
|
|
42
42
|
"sideEffects": false,
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"@100mslive/hms-video": "0.7.5-alpha.
|
|
44
|
+
"@100mslive/hms-video": "0.7.5-alpha.2",
|
|
45
45
|
"eventemitter2": "^6.4.7",
|
|
46
46
|
"immer": "^9.0.6",
|
|
47
47
|
"reselect": "4.0.0",
|
|
@@ -66,5 +66,5 @@
|
|
|
66
66
|
"url": "https://github.com/100mslive/hms-video-store/issues"
|
|
67
67
|
},
|
|
68
68
|
"homepage": "https://github.com/100mslive/hms-video-store#readme",
|
|
69
|
-
"gitHead": "
|
|
69
|
+
"gitHead": "2c9b127a10783f0a5d2f73f4fdd8bed866800c7e"
|
|
70
70
|
}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { HMSLogger } from '../../common/ui-logger';
|
|
2
|
+
import { HMSPeer, IHMSStore, selectIsConnectedToRoom, selectPeers } from '../../core';
|
|
3
|
+
import { IHMSActions } from '../../core/IHMSActions';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Log data of audio level and speaker speaking periodically to beam for transcript
|
|
7
|
+
* diarization.
|
|
8
|
+
*/
|
|
9
|
+
export class BeamSpeakerLabelsLogger {
|
|
10
|
+
private audioContext?: AudioContext;
|
|
11
|
+
private readonly intervalMs: number;
|
|
12
|
+
private shouldMonitor: boolean;
|
|
13
|
+
private hasStarted: boolean;
|
|
14
|
+
private unsubs: any[];
|
|
15
|
+
private readonly analysers: Record<string, AnalyserNode>;
|
|
16
|
+
private readonly store: IHMSStore;
|
|
17
|
+
private actions: IHMSActions;
|
|
18
|
+
constructor(store: IHMSStore, actions: IHMSActions) {
|
|
19
|
+
this.intervalMs = 100;
|
|
20
|
+
this.shouldMonitor = false;
|
|
21
|
+
this.hasStarted = false;
|
|
22
|
+
this.unsubs = [];
|
|
23
|
+
this.analysers = {};
|
|
24
|
+
this.store = store;
|
|
25
|
+
this.actions = actions;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async start() {
|
|
29
|
+
if (this.hasStarted) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
this.hasStarted = true;
|
|
33
|
+
HMSLogger.d('starting audio level monitor for remote peers', this.store);
|
|
34
|
+
const isConnected = this.store.getState(selectIsConnectedToRoom);
|
|
35
|
+
HMSLogger.d('starting audio levels is connected to room', isConnected);
|
|
36
|
+
if (isConnected) {
|
|
37
|
+
await this.monitorAudioLevels();
|
|
38
|
+
}
|
|
39
|
+
const unsub = this.store.subscribe(this.monitorAudioLevels.bind(this), selectIsConnectedToRoom);
|
|
40
|
+
this.unsubs.push(unsub);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async stop() {
|
|
44
|
+
if (!this.hasStarted) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
this.hasStarted = false;
|
|
48
|
+
this.shouldMonitor = false;
|
|
49
|
+
this.unsubs.forEach(unsub => unsub());
|
|
50
|
+
HMSLogger.d('stopped audio level monitor for remote peers');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async monitorAudioLevels() {
|
|
54
|
+
const isConnected = this.store.getState(selectIsConnectedToRoom);
|
|
55
|
+
if (!isConnected) {
|
|
56
|
+
if (this.shouldMonitor) {
|
|
57
|
+
HMSLogger.i('room no longer connected, stopping audio level monitoring for remote');
|
|
58
|
+
this.shouldMonitor = false;
|
|
59
|
+
}
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
if (this.shouldMonitor) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
HMSLogger.i('monitoring audio levels');
|
|
66
|
+
this.shouldMonitor = true;
|
|
67
|
+
const loop = () => {
|
|
68
|
+
if (this.shouldMonitor) {
|
|
69
|
+
this.logAllPeersAudioLevels();
|
|
70
|
+
setTimeout(loop, this.intervalMs);
|
|
71
|
+
} else {
|
|
72
|
+
HMSLogger.i('stopped monitoring audio levels');
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
setTimeout(loop, 1000);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// eslint-disable-next-line complexity
|
|
79
|
+
async logAllPeersAudioLevels() {
|
|
80
|
+
if (!window.__triggerBeamEvent__) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
// optimise this to selectTracks instead of selecting peers
|
|
84
|
+
const allPeers = this.store.getState(selectPeers);
|
|
85
|
+
const peers = allPeers.filter(peer => !!peer.audioTrack);
|
|
86
|
+
const peerAudioLevels = [];
|
|
87
|
+
for (const peer of peers) {
|
|
88
|
+
// @ts-ignore
|
|
89
|
+
const sdkTrack = this.actions.hmsSDKTracks[peer.audioTrack];
|
|
90
|
+
const nativeStream: MediaStream = sdkTrack?.stream?.nativeStream;
|
|
91
|
+
if (!peer.joinedAt) {
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
if (nativeStream) {
|
|
95
|
+
const peerLevel = await this.getAudioLevel(peer, nativeStream);
|
|
96
|
+
if (peerLevel.level > 0) {
|
|
97
|
+
peerAudioLevels.push(peerLevel);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
if (peerAudioLevels.length > 0) {
|
|
102
|
+
const payload = {
|
|
103
|
+
event: 'app-audio-level',
|
|
104
|
+
data: peerAudioLevels,
|
|
105
|
+
};
|
|
106
|
+
// HMSLogger.d('logging audio levels', peerAudioLevels);
|
|
107
|
+
window.__triggerBeamEvent__(JSON.stringify(payload));
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
async getAudioLevel(peer: HMSPeer, stream: MediaStream) {
|
|
112
|
+
if (!this.analysers[stream.id]) {
|
|
113
|
+
this.analysers[stream.id] = this.createAnalyserNode(stream);
|
|
114
|
+
}
|
|
115
|
+
const analyserNode = this.analysers[stream.id];
|
|
116
|
+
const level = this.calculateAudioLevel(analyserNode);
|
|
117
|
+
return {
|
|
118
|
+
peerId: peer.id,
|
|
119
|
+
peerName: peer.name,
|
|
120
|
+
level,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
createAnalyserNode(stream: MediaStream) {
|
|
125
|
+
if (!this.audioContext) {
|
|
126
|
+
this.audioContext = new AudioContext();
|
|
127
|
+
}
|
|
128
|
+
const analyser = this.audioContext.createAnalyser();
|
|
129
|
+
const source = this.audioContext.createMediaStreamSource(stream);
|
|
130
|
+
source.connect(analyser);
|
|
131
|
+
return analyser;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
calculateAudioLevel(analyserNode: AnalyserNode) {
|
|
135
|
+
const data = new Uint8Array(analyserNode.fftSize);
|
|
136
|
+
analyserNode.getByteTimeDomainData(data);
|
|
137
|
+
const lowest = 0.009;
|
|
138
|
+
let max = lowest;
|
|
139
|
+
for (const frequency of data) {
|
|
140
|
+
max = Math.max(max, (frequency - 128) / 128);
|
|
141
|
+
}
|
|
142
|
+
const normalized = (Math.log(lowest) - Math.log(max)) / Math.log(lowest);
|
|
143
|
+
const percent = Math.ceil(Math.min(Math.max(normalized * 100, 0), 100));
|
|
144
|
+
return percent;
|
|
145
|
+
}
|
|
146
|
+
}
|
package/src/core/IHMSActions.ts
CHANGED
|
@@ -10,6 +10,9 @@ import {
|
|
|
10
10
|
HMSScreenShareConfig,
|
|
11
11
|
HMSVideoPlugin,
|
|
12
12
|
HMSVideoTrackSettings,
|
|
13
|
+
TokenRequest,
|
|
14
|
+
TokenRequestOptions,
|
|
15
|
+
TokenResult,
|
|
13
16
|
} from '@100mslive/hms-video';
|
|
14
17
|
import { HLSConfig, RTMPRecordingConfig } from './hmsSDKStore/sdkTypes';
|
|
15
18
|
import {
|
|
@@ -472,4 +475,12 @@ export interface IHMSActions {
|
|
|
472
475
|
**/
|
|
473
476
|
setAppData(key: string, value: Record<string | number, any>, merge?: boolean): void;
|
|
474
477
|
setAppData(key: string, value: any): void;
|
|
478
|
+
|
|
479
|
+
getAuthTokenByRoomCode(tokenRequest: TokenRequest, tokenRequestOptions?: TokenRequestOptions): Promise<TokenResult>;
|
|
480
|
+
|
|
481
|
+
/**
|
|
482
|
+
* enable sending audio speaker data to beam
|
|
483
|
+
* @alpha
|
|
484
|
+
*/
|
|
485
|
+
enableBeamSpeakerLabelsLogging(): Promise<void>;
|
|
475
486
|
}
|
|
@@ -30,6 +30,7 @@ import { HMSPlaylist } from './HMSPlaylist';
|
|
|
30
30
|
import { NamedSetState } from './internalTypes';
|
|
31
31
|
import * as sdkTypes from './sdkTypes';
|
|
32
32
|
import { HMSLogger } from '../../common/ui-logger';
|
|
33
|
+
import { BeamSpeakerLabelsLogger } from '../../controller/beam/BeamSpeakerLabelsLogger';
|
|
33
34
|
import { IHMSActions } from '../IHMSActions';
|
|
34
35
|
import { IHMSStore } from '../IHMSStore';
|
|
35
36
|
import {
|
|
@@ -104,6 +105,7 @@ export class HMSSDKActions implements IHMSActions {
|
|
|
104
105
|
// private actionBatcher: ActionBatcher;
|
|
105
106
|
audioPlaylist!: IHMSPlaylistActions;
|
|
106
107
|
videoPlaylist!: IHMSPlaylistActions;
|
|
108
|
+
private beamSpeakerLabelsLogger?: BeamSpeakerLabelsLogger;
|
|
107
109
|
|
|
108
110
|
constructor(store: IHMSStore, sdk: HMSSdk, notificationManager: HMSNotifications) {
|
|
109
111
|
this.store = store;
|
|
@@ -144,12 +146,12 @@ export class HMSSDKActions implements IHMSActions {
|
|
|
144
146
|
if (track instanceof SDKHMSRemoteVideoTrack) {
|
|
145
147
|
//@ts-ignore
|
|
146
148
|
if (layer === HMSSimulcastLayer.NONE) {
|
|
147
|
-
HMSLogger.
|
|
149
|
+
HMSLogger.d(`layer ${HMSSimulcastLayer.NONE} will be ignored`);
|
|
148
150
|
return;
|
|
149
151
|
}
|
|
150
152
|
const alreadyInSameState = this.store.getState(selectVideoTrackByID(trackId))?.preferredLayer === layer;
|
|
151
153
|
if (alreadyInSameState) {
|
|
152
|
-
HMSLogger.
|
|
154
|
+
HMSLogger.d(`preferred layer is already ${layer}`);
|
|
153
155
|
return;
|
|
154
156
|
}
|
|
155
157
|
this.setState(draftStore => {
|
|
@@ -160,13 +162,20 @@ export class HMSSDKActions implements IHMSActions {
|
|
|
160
162
|
}, 'setPreferredLayer');
|
|
161
163
|
await track.setPreferredLayer(layer);
|
|
162
164
|
} else {
|
|
163
|
-
HMSLogger.
|
|
165
|
+
HMSLogger.d(`track ${trackId} is not a remote video track`);
|
|
164
166
|
}
|
|
165
167
|
} else {
|
|
166
168
|
this.logPossibleInconsistency(`track ${trackId} not present, unable to set preffer layer`);
|
|
167
169
|
}
|
|
168
170
|
}
|
|
169
171
|
|
|
172
|
+
getAuthTokenByRoomCode(
|
|
173
|
+
tokenRequest: sdkTypes.TokenRequest,
|
|
174
|
+
tokenRequestOptions?: sdkTypes.TokenRequestOptions,
|
|
175
|
+
): Promise<sdkTypes.TokenResult> {
|
|
176
|
+
return this.sdk.getAuthTokenByRoomCode(tokenRequest, tokenRequestOptions);
|
|
177
|
+
}
|
|
178
|
+
|
|
170
179
|
async preview(config: sdkTypes.HMSPreviewConfig) {
|
|
171
180
|
if (this.isRoomJoinCalled) {
|
|
172
181
|
this.logPossibleInconsistency('attempting to call preview after join was called');
|
|
@@ -222,6 +231,9 @@ export class HMSSDKActions implements IHMSActions {
|
|
|
222
231
|
.leave(notifyServer)
|
|
223
232
|
.then(() => {
|
|
224
233
|
this.resetState('leave');
|
|
234
|
+
if (this.beamSpeakerLabelsLogger) {
|
|
235
|
+
this.beamSpeakerLabelsLogger.stop().catch(HMSLogger.e);
|
|
236
|
+
}
|
|
225
237
|
HMSLogger.i('left room');
|
|
226
238
|
})
|
|
227
239
|
.catch(err => {
|
|
@@ -393,12 +405,12 @@ export class HMSSDKActions implements IHMSActions {
|
|
|
393
405
|
async detachVideo(trackID: string, videoElement: HTMLVideoElement) {
|
|
394
406
|
const sdkTrack = this.hmsSDKTracks[trackID];
|
|
395
407
|
if (sdkTrack?.type === 'video') {
|
|
396
|
-
(sdkTrack as SDKHMSVideoTrack
|
|
408
|
+
await this.sdk.detachVideo(sdkTrack as SDKHMSVideoTrack, videoElement);
|
|
397
409
|
} else {
|
|
398
410
|
if (videoElement) {
|
|
399
411
|
videoElement.srcObject = null; // so chrome can clean up
|
|
400
412
|
}
|
|
401
|
-
|
|
413
|
+
HMSLogger.d('possible inconsistency detected - no video track found to remove sink');
|
|
402
414
|
}
|
|
403
415
|
}
|
|
404
416
|
|
|
@@ -664,6 +676,14 @@ export class HMSSDKActions implements IHMSActions {
|
|
|
664
676
|
}
|
|
665
677
|
}
|
|
666
678
|
|
|
679
|
+
async enableBeamSpeakerLabelsLogging() {
|
|
680
|
+
if (!this.beamSpeakerLabelsLogger) {
|
|
681
|
+
HMSLogger.i('enabling beam speaker labels logging');
|
|
682
|
+
this.beamSpeakerLabelsLogger = new BeamSpeakerLabelsLogger(this.store, this);
|
|
683
|
+
await this.beamSpeakerLabelsLogger.start();
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
|
|
667
687
|
private resetState(reason = 'resetState') {
|
|
668
688
|
this.isRoomJoinCalled = false;
|
|
669
689
|
this.hmsSDKTracks = {};
|
|
@@ -789,7 +809,7 @@ export class HMSSDKActions implements IHMSActions {
|
|
|
789
809
|
private async attachVideoInternal(trackID: string, videoElement: HTMLVideoElement) {
|
|
790
810
|
const sdkTrack = this.hmsSDKTracks[trackID];
|
|
791
811
|
if (sdkTrack && sdkTrack.type === 'video') {
|
|
792
|
-
await (sdkTrack as SDKHMSVideoTrack
|
|
812
|
+
await this.sdk.attachVideo(sdkTrack as SDKHMSVideoTrack, videoElement);
|
|
793
813
|
} else {
|
|
794
814
|
this.logPossibleInconsistency('no video track found to add sink');
|
|
795
815
|
}
|
|
@@ -50,6 +50,9 @@ import {
|
|
|
50
50
|
RTMPRecordingConfig,
|
|
51
51
|
ScreenCaptureHandle,
|
|
52
52
|
simulcastMapping,
|
|
53
|
+
TokenRequest,
|
|
54
|
+
TokenRequestOptions,
|
|
55
|
+
TokenResult,
|
|
53
56
|
} from '@100mslive/hms-video';
|
|
54
57
|
|
|
55
58
|
export {
|
|
@@ -107,4 +110,7 @@ export type {
|
|
|
107
110
|
RID,
|
|
108
111
|
ScreenCaptureHandle,
|
|
109
112
|
HMSPreferredSimulcastLayer,
|
|
113
|
+
TokenRequest,
|
|
114
|
+
TokenRequestOptions,
|
|
115
|
+
TokenResult,
|
|
110
116
|
};
|
package/src/core/index.ts
CHANGED