@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/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.8.5-alpha.0",
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.0",
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": "8ea7a607a55e81770857c4ab8928e54a5c26a928"
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
+ }
@@ -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
  }
@@ -25,6 +25,7 @@ declare global {
25
25
  interface Window {
26
26
  __hms: HMSReactiveStore;
27
27
  __beam: BeamControllerStore;
28
+ __triggerBeamEvent__: (args: any) => void;
28
29
  }
29
30
  }
30
31
 
@@ -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.w(`layer ${HMSSimulcastLayer.NONE} will be ignored`);
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.w(`preferred layer is already ${layer}`);
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.w(`track ${trackId} is not a remote video track`);
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).removeSink(videoElement);
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
- this.logPossibleInconsistency('no video track found to remove sink');
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).addSink(videoElement);
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
@@ -39,6 +39,9 @@ export type {
39
39
  HMSScreenShareConfig,
40
40
  ScreenCaptureHandle,
41
41
  HMSPreferredSimulcastLayer,
42
+ TokenRequest,
43
+ TokenRequestOptions,
44
+ TokenResult,
42
45
  } from './hmsSDKStore/sdkTypes';
43
46
 
44
47
  export * from '../controller/beam/BeamController';