@stream-io/video-client 1.46.1 → 1.48.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.
@@ -13,7 +13,6 @@ export declare class MicrophoneManager extends AudioDeviceManager<MicrophoneMana
13
13
  private soundDetectorCleanup?;
14
14
  private soundDetectorDeviceId?;
15
15
  private noAudioDetectorCleanup?;
16
- private rnSpeechDetector;
17
16
  private noiseCancellation;
18
17
  private noiseCancellationChangeUnsubscribe;
19
18
  private noiseCancellationRegistration?;
@@ -5340,6 +5340,12 @@ export interface JoinCallRequest {
5340
5340
  * @memberof JoinCallRequest
5341
5341
  */
5342
5342
  data?: CallRequest;
5343
+ /**
5344
+ * if true, the participant will be marked as publsihing to large audience
5345
+ * @type {boolean}
5346
+ * @memberof JoinCallRequest
5347
+ */
5348
+ hint_high_scale_livestream_publisher?: boolean;
5343
5349
  /**
5344
5350
  *
5345
5351
  * @type {string}
@@ -371,6 +371,17 @@ export type StreamRNVideoSDKGlobals = {
371
371
  */
372
372
  check(permission: 'microphone' | 'camera'): Promise<boolean>;
373
373
  };
374
+ nativeEvents: {
375
+ speechActivity: {
376
+ /**
377
+ * Subscribes to native speech activity events.
378
+ * Returns an unsubscribe function.
379
+ */
380
+ subscribe(cb: (state: {
381
+ isSoundDetected: boolean;
382
+ }) => void): () => void;
383
+ };
384
+ };
374
385
  };
375
386
  declare global {
376
387
  var streamRNVideoSDK: StreamRNVideoSDKGlobals | undefined;
package/index.ts CHANGED
@@ -23,7 +23,6 @@ export * from './src/helpers/DynascaleManager';
23
23
  export * from './src/helpers/ViewportTracker';
24
24
  export * from './src/helpers/sound-detector';
25
25
  export * from './src/helpers/participantUtils';
26
- export * from './src/helpers/RNSpeechDetector';
27
26
  export * as Browsers from './src/helpers/browsers';
28
27
 
29
28
  export * from './src/logger';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stream-io/video-client",
3
- "version": "1.46.1",
3
+ "version": "1.48.0",
4
4
  "main": "dist/index.cjs.js",
5
5
  "module": "dist/index.es.js",
6
6
  "browser": "dist/index.browser.es.js",
@@ -1,4 +1,4 @@
1
- import { Observable } from 'rxjs';
1
+ import { firstValueFrom, Observable } from 'rxjs';
2
2
  import { Call } from '../Call';
3
3
  import { CameraDirection, CameraManagerState } from './CameraManagerState';
4
4
  import { DeviceManager } from './DeviceManager';
@@ -140,7 +140,14 @@ export class CameraManager extends DeviceManager<CameraManagerState> {
140
140
  this.state.status === undefined &&
141
141
  this.state.optimisticStatus === undefined;
142
142
  let persistedPreferencesApplied = false;
143
- if (shouldApplyDefaults && this.devicePersistence.enabled) {
143
+ const permissionState = await firstValueFrom(
144
+ this.state.browserPermissionState$,
145
+ );
146
+ if (
147
+ shouldApplyDefaults &&
148
+ this.devicePersistence.enabled &&
149
+ permissionState === 'granted'
150
+ ) {
144
151
  persistedPreferencesApplied =
145
152
  await this.applyPersistedPreferences(enabledInCallType);
146
153
  }
@@ -89,9 +89,19 @@ export abstract class DeviceManager<
89
89
  if (this.devicePersistence.enabled) {
90
90
  this.subscriptions.push(
91
91
  createSubscription(
92
- combineLatest([this.state.selectedDevice$, this.state.status$]),
93
- ([selectedDevice, status]) => {
94
- if (!status) return;
92
+ combineLatest([
93
+ this.state.selectedDevice$,
94
+ this.state.status$,
95
+ this.state.browserPermissionState$,
96
+ ]),
97
+ ([selectedDevice, status, browserPermissionState]) => {
98
+ if (
99
+ !status ||
100
+ (this.isTrackStoppedDueToTrackEnd && status === 'disabled') ||
101
+ browserPermissionState !== 'granted'
102
+ )
103
+ return;
104
+
95
105
  this.persistPreference(selectedDevice, status);
96
106
  },
97
107
  ),
@@ -24,7 +24,6 @@ import {
24
24
  createSafeAsyncSubscription,
25
25
  createSubscription,
26
26
  } from '../store/rxUtils';
27
- import { RNSpeechDetector } from '../helpers/RNSpeechDetector';
28
27
  import { withoutConcurrency } from '../helpers/concurrency';
29
28
  import { disposeOfMediaStream } from './utils';
30
29
  import { promiseWithResolvers } from '../helpers/promise';
@@ -36,7 +35,6 @@ export class MicrophoneManager extends AudioDeviceManager<MicrophoneManagerState
36
35
  private soundDetectorCleanup?: () => Promise<void>;
37
36
  private soundDetectorDeviceId?: string;
38
37
  private noAudioDetectorCleanup?: () => Promise<void>;
39
- private rnSpeechDetector: RNSpeechDetector | undefined;
40
38
  private noiseCancellation: INoiseCancellation | undefined;
41
39
  private noiseCancellationChangeUnsubscribe: (() => void) | undefined;
42
40
  private noiseCancellationRegistration?: Promise<void>;
@@ -356,7 +354,14 @@ export class MicrophoneManager extends AudioDeviceManager<MicrophoneManagerState
356
354
  this.state.status === undefined &&
357
355
  this.state.optimisticStatus === undefined;
358
356
  let persistedPreferencesApplied = false;
359
- if (shouldApplyDefaults && this.devicePersistence.enabled) {
357
+ const permissionState = await firstValueFrom(
358
+ this.state.browserPermissionState$,
359
+ );
360
+ if (
361
+ shouldApplyDefaults &&
362
+ this.devicePersistence.enabled &&
363
+ permissionState === 'granted'
364
+ ) {
360
365
  persistedPreferencesApplied = await this.applyPersistedPreferences(true);
361
366
  }
362
367
 
@@ -415,13 +420,19 @@ export class MicrophoneManager extends AudioDeviceManager<MicrophoneManagerState
415
420
  await this.teardownSpeakingWhileMutedDetection();
416
421
 
417
422
  if (isReactNative()) {
418
- this.rnSpeechDetector = new RNSpeechDetector();
419
- const unsubscribe = await this.rnSpeechDetector.start((event) => {
423
+ const speechActivity =
424
+ globalThis.streamRNVideoSDK?.nativeEvents?.speechActivity;
425
+ if (!speechActivity) {
426
+ this.logger.warn(
427
+ 'Native speech activity not available, make sure the "@stream-io/react-native-webrtc" peer dependency version is satisfied',
428
+ );
429
+ return;
430
+ }
431
+ const unsubscribe = speechActivity.subscribe((event) => {
420
432
  this.state.setSpeakingWhileMuted(event.isSoundDetected);
421
433
  });
422
434
  this.soundDetectorCleanup = async () => {
423
435
  unsubscribe();
424
- this.rnSpeechDetector = undefined;
425
436
  };
426
437
  } else {
427
438
  // Need to start a new stream that's not connected to publisher
@@ -2,7 +2,11 @@ import { combineLatest } from 'rxjs';
2
2
  import { Call } from '../Call';
3
3
  import { isReactNative } from '../helpers/platforms';
4
4
  import { SpeakerState } from './SpeakerState';
5
- import { deviceIds$, getAudioOutputDevices } from './devices';
5
+ import {
6
+ deviceIds$,
7
+ getAudioBrowserPermission,
8
+ getAudioOutputDevices,
9
+ } from './devices';
6
10
  import {
7
11
  AudioSettingsRequestDefaultDeviceEnum,
8
12
  CallSettingsResponse,
@@ -111,9 +115,17 @@ export class SpeakerManager {
111
115
 
112
116
  if (!isReactNative() && this.devicePersistence.enabled) {
113
117
  this.subscriptions.push(
114
- createSubscription(this.state.selectedDevice$, (selectedDevice) => {
115
- this.persistSpeakerDevicePreference(selectedDevice);
116
- }),
118
+ createSubscription(
119
+ combineLatest([
120
+ this.state.selectedDevice$,
121
+ getAudioBrowserPermission(this.call.tracer).asStateObservable(),
122
+ ]),
123
+ ([selectedDevice, browserPermissionState]) => {
124
+ if (!selectedDevice || browserPermissionState !== 'granted') return;
125
+
126
+ this.persistSpeakerDevicePreference(selectedDevice);
127
+ },
128
+ ),
117
129
  );
118
130
  }
119
131
  }
@@ -306,6 +306,9 @@ describe('CameraManager', () => {
306
306
  });
307
307
 
308
308
  it('should skip defaults when preferences are applied', async () => {
309
+ vi.spyOn(mockBrowserPermission, 'asStateObservable').mockReturnValue(
310
+ of('granted'),
311
+ );
309
312
  const devicePersistence = { enabled: true, storageKey: '' };
310
313
  const persistedManager = new CameraManager(call, devicePersistence);
311
314
  const applySpy = vi
@@ -329,6 +332,32 @@ describe('CameraManager', () => {
329
332
  expect(enableSpy).not.toHaveBeenCalled();
330
333
  });
331
334
 
335
+ it('should skip persisted preferences when permission is not granted', async () => {
336
+ vi.spyOn(mockBrowserPermission, 'asStateObservable').mockReturnValue(
337
+ of('prompt'),
338
+ );
339
+ const devicePersistence = { enabled: true, storageKey: '' };
340
+ const persistedManager = new CameraManager(call, devicePersistence);
341
+ const applySpy = vi.spyOn(
342
+ persistedManager as never,
343
+ 'applyPersistedPreferences',
344
+ );
345
+ const enableSpy = vi.spyOn(persistedManager, 'enable');
346
+
347
+ await persistedManager.apply(
348
+ fromPartial({
349
+ enabled: true,
350
+ target_resolution: { width: 640, height: 480 },
351
+ camera_facing: 'front',
352
+ camera_default_on: true,
353
+ }),
354
+ true,
355
+ );
356
+
357
+ expect(applySpy).not.toHaveBeenCalled();
358
+ expect(enableSpy).toHaveBeenCalled();
359
+ });
360
+
332
361
  it('should not apply defaults when device is not pristine', async () => {
333
362
  manager.state.setStatus('enabled');
334
363
  const selectDirectionSpy = vi.spyOn(manager, 'selectDirection');
@@ -445,6 +474,9 @@ describe('CameraManager', () => {
445
474
  createVideoStreamForDevice(selectedDevice.deviceId),
446
475
  );
447
476
  });
477
+ vi.spyOn(mockBrowserPermission, 'asStateObservable').mockReturnValue(
478
+ of('granted'),
479
+ );
448
480
 
449
481
  const stressManager = new CameraManager(call, {
450
482
  enabled: true,
@@ -76,6 +76,9 @@ describe('Device Manager', () => {
76
76
  beforeEach(() => {
77
77
  storageKey = '@test/device-preferences';
78
78
  localStorageMock = createLocalStorageMock();
79
+ vi.spyOn(mockBrowserPermission, 'asStateObservable').mockReturnValue(
80
+ of('granted'),
81
+ );
79
82
  Object.defineProperty(window, 'localStorage', {
80
83
  configurable: true,
81
84
  value: localStorageMock,
@@ -455,6 +458,74 @@ describe('Device Manager', () => {
455
458
  },
456
459
  ]);
457
460
  });
461
+
462
+ it('stores preferences when permission is granted', async () => {
463
+ const persistenceEnabledManager = new TestInputMediaDeviceManager(
464
+ manager['call'],
465
+ { enabled: true, storageKey },
466
+ );
467
+ const listDevicesSpy = vi.spyOn(persistenceEnabledManager, 'listDevices');
468
+
469
+ emitDeviceIds(mockVideoDevices);
470
+ persistenceEnabledManager.state.setDevice(mockVideoDevices[0].deviceId);
471
+ persistenceEnabledManager.state.setStatus('enabled');
472
+
473
+ expect(readPreferences(storageKey).camera).toBeDefined();
474
+ expect(listDevicesSpy).toHaveBeenCalled();
475
+ expect(readPreferences(storageKey).camera).toEqual([
476
+ {
477
+ selectedDeviceId: mockVideoDevices[0].deviceId,
478
+ selectedDeviceLabel: mockVideoDevices[0].label,
479
+ muted: false,
480
+ },
481
+ ]);
482
+ });
483
+
484
+ it('does not store preferences when permission is not granted', async () => {
485
+ vi.spyOn(mockBrowserPermission, 'asStateObservable').mockReturnValue(
486
+ of('prompt'),
487
+ );
488
+ const persistenceEnabledManager = new TestInputMediaDeviceManager(
489
+ manager['call'],
490
+ { enabled: true, storageKey },
491
+ );
492
+ const listDevicesSpy = vi.spyOn(persistenceEnabledManager, 'listDevices');
493
+
494
+ emitDeviceIds(mockVideoDevices);
495
+ persistenceEnabledManager.state.setDevice(mockVideoDevices[0].deviceId);
496
+ persistenceEnabledManager.state.setStatus('enabled');
497
+
498
+ expect(readPreferences(storageKey).camera).toBeUndefined();
499
+ expect(listDevicesSpy).not.toHaveBeenCalled();
500
+ });
501
+
502
+ it('does not overwrite preferences when track ends unexpectedly', async () => {
503
+ const persistenceEnabledManager = new TestInputMediaDeviceManager(
504
+ manager['call'],
505
+ { enabled: true, storageKey },
506
+ );
507
+
508
+ await persistenceEnabledManager.enable();
509
+
510
+ expect(readPreferences(storageKey).camera).toEqual([
511
+ {
512
+ selectedDeviceId: mockVideoDevices[0].deviceId,
513
+ selectedDeviceLabel: mockVideoDevices[0].label,
514
+ muted: false,
515
+ },
516
+ ]);
517
+
518
+ const [track] = persistenceEnabledManager.state.mediaStream!.getTracks();
519
+ await ((track as MockTrack).eventHandlers['ended'] as Function)();
520
+
521
+ expect(readPreferences(storageKey).camera).toEqual([
522
+ {
523
+ selectedDeviceId: mockVideoDevices[0].deviceId,
524
+ selectedDeviceLabel: mockVideoDevices[0].label,
525
+ muted: false,
526
+ },
527
+ ]);
528
+ });
458
529
  });
459
530
 
460
531
  describe('applyPersistedPreferences', () => {
@@ -479,6 +479,29 @@ describe('MicrophoneManager', () => {
479
479
  expect(enableSpy).not.toHaveBeenCalled();
480
480
  });
481
481
 
482
+ it('should skip persisted preferences when permission is not granted', async () => {
483
+ vi.spyOn(mockBrowserPermission, 'asStateObservable').mockReturnValue(
484
+ of('prompt'),
485
+ );
486
+ const devicePersistence = { enabled: true, storageKey: '' };
487
+ const persistedManager = new MicrophoneManager(
488
+ call,
489
+ devicePersistence,
490
+ 'disable-tracks',
491
+ );
492
+ const applySpy = vi.spyOn(
493
+ persistedManager as never,
494
+ 'applyPersistedPreferences',
495
+ );
496
+ const enableSpy = vi.spyOn(persistedManager, 'enable');
497
+
498
+ // @ts-expect-error - partial data
499
+ await persistedManager.apply({ mic_default_on: true }, true);
500
+
501
+ expect(applySpy).not.toHaveBeenCalled();
502
+ expect(enableSpy).toHaveBeenCalled();
503
+ });
504
+
482
505
  it('should not apply defaults when mic is not pristine', async () => {
483
506
  manager.state.setStatus('enabled');
484
507
  const applySpy = vi.spyOn(manager as never, 'applyPersistedPreferences');
@@ -12,11 +12,12 @@ import {
12
12
  import { of } from 'rxjs';
13
13
  import '../../rtc/__tests__/mocks/webrtc.mocks';
14
14
  import { OwnCapability } from '../../gen/coordinator';
15
- import { SoundStateChangeHandler } from '../../helpers/sound-detector';
16
15
  import { settled, withoutConcurrency } from '../../helpers/concurrency';
17
16
 
18
- let handler: SoundStateChangeHandler = () => {};
19
- let unsubscribeHandlers: ReturnType<typeof vi.fn>[] = [];
17
+ let speechActivityCallback:
18
+ | ((state: { isSoundDetected: boolean }) => void)
19
+ | null = null;
20
+ let unsubscribeMocks: ReturnType<typeof vi.fn>[] = [];
20
21
 
21
22
  vi.mock('../../helpers/platforms.ts', () => {
22
23
  return {
@@ -46,28 +47,21 @@ vi.mock('../../Call.ts', () => {
46
47
  };
47
48
  });
48
49
 
49
- vi.mock('../../helpers/RNSpeechDetector.ts', () => {
50
- console.log('MOCKING RNSpeechDetector');
51
- return {
52
- RNSpeechDetector: vi.fn().mockImplementation(() => ({
53
- start: vi.fn((callback) => {
54
- handler = callback;
55
- const unsubscribe = vi.fn();
56
- unsubscribeHandlers.push(unsubscribe);
57
- return unsubscribe;
58
- }),
59
- stop: vi.fn(),
60
- onSpeakingDetectedStateChange: vi.fn(),
61
- })),
62
- };
63
- });
64
-
65
50
  describe('MicrophoneManager React Native', () => {
66
51
  let manager: MicrophoneManager;
67
52
  let checkPermissionMock: ReturnType<typeof vi.fn>;
53
+ let subscribeMock: ReturnType<typeof vi.fn>;
54
+
68
55
  beforeEach(() => {
69
- unsubscribeHandlers = [];
56
+ speechActivityCallback = null;
57
+ unsubscribeMocks = [];
70
58
  checkPermissionMock = vi.fn(async () => true);
59
+ subscribeMock = vi.fn((cb) => {
60
+ speechActivityCallback = cb;
61
+ const unsub = vi.fn();
62
+ unsubscribeMocks.push(unsub);
63
+ return unsub;
64
+ });
71
65
 
72
66
  globalThis.streamRNVideoSDK = {
73
67
  callManager: {
@@ -78,6 +72,11 @@ describe('MicrophoneManager React Native', () => {
78
72
  permissions: {
79
73
  check: checkPermissionMock,
80
74
  },
75
+ nativeEvents: {
76
+ speechActivity: {
77
+ subscribe: subscribeMock,
78
+ },
79
+ },
81
80
  };
82
81
 
83
82
  const devicePersistence = { enabled: false, storageKey: '' };
@@ -100,7 +99,7 @@ describe('MicrophoneManager React Native', () => {
100
99
 
101
100
  await vi.waitUntil(() => fn.mock.calls.length > 0, { timeout: 100 });
102
101
  expect(fn).toHaveBeenCalled();
103
- expect(manager['rnSpeechDetector']?.start).toHaveBeenCalled();
102
+ expect(subscribeMock).toHaveBeenCalled();
104
103
  });
105
104
 
106
105
  it('should check native microphone permission before starting detection', async () => {
@@ -146,15 +145,15 @@ describe('MicrophoneManager React Native', () => {
146
145
 
147
146
  it('should update speaking while muted state', async () => {
148
147
  await manager['startSpeakingWhileMutedDetection']();
149
- expect(manager['rnSpeechDetector']?.start).toHaveBeenCalled();
148
+ expect(subscribeMock).toHaveBeenCalled();
150
149
 
151
150
  expect(manager.state.speakingWhileMuted).toBe(false);
152
151
 
153
- handler!({ isSoundDetected: true, audioLevel: 2 });
152
+ speechActivityCallback!({ isSoundDetected: true });
154
153
 
155
154
  expect(manager.state.speakingWhileMuted).toBe(true);
156
155
 
157
- handler!({ isSoundDetected: false, audioLevel: 0 });
156
+ speechActivityCallback!({ isSoundDetected: false });
158
157
 
159
158
  expect(manager.state.speakingWhileMuted).toBe(false);
160
159
  });
@@ -163,21 +162,21 @@ describe('MicrophoneManager React Native', () => {
163
162
  await manager['startSpeakingWhileMutedDetection']('device-1');
164
163
  await manager['startSpeakingWhileMutedDetection']('device-1');
165
164
 
166
- expect(unsubscribeHandlers).toHaveLength(1);
165
+ expect(unsubscribeMocks).toHaveLength(1);
167
166
 
168
167
  await manager['stopSpeakingWhileMutedDetection']();
169
- expect(unsubscribeHandlers[0]).toHaveBeenCalledTimes(1);
168
+ expect(unsubscribeMocks[0]).toHaveBeenCalledTimes(1);
170
169
  });
171
170
 
172
171
  it('should cleanup previous speech detector before starting a new one', async () => {
173
172
  await manager['startSpeakingWhileMutedDetection']('device-1');
174
173
  await manager['startSpeakingWhileMutedDetection']('device-2');
175
174
 
176
- expect(unsubscribeHandlers).toHaveLength(2);
177
- expect(unsubscribeHandlers[0]).toHaveBeenCalledTimes(1);
175
+ expect(unsubscribeMocks).toHaveLength(2);
176
+ expect(unsubscribeMocks[0]).toHaveBeenCalledTimes(1);
178
177
 
179
178
  await manager['stopSpeakingWhileMutedDetection']();
180
- expect(unsubscribeHandlers[1]).toHaveBeenCalledTimes(1);
179
+ expect(unsubscribeMocks[1]).toHaveBeenCalledTimes(1);
181
180
  });
182
181
 
183
182
  it('should stop speaking while muted notifications if user loses permission to send audio', async () => {
@@ -37,6 +37,9 @@ describe('SpeakerManager.test', () => {
37
37
  beforeEach(() => {
38
38
  storageKey = '@test/speaker-preferences';
39
39
  localStorageMock = createLocalStorageMock();
40
+ vi.spyOn(mockBrowserPermission, 'asStateObservable').mockReturnValue(
41
+ of('granted'),
42
+ );
40
43
  Object.defineProperty(window, 'localStorage', {
41
44
  configurable: true,
42
45
  value: localStorageMock,
@@ -125,6 +128,31 @@ describe('SpeakerManager.test', () => {
125
128
  expect(manager.state.selectedDevice).toBe('');
126
129
  });
127
130
 
131
+ it('persists speaker selection when permission is granted', async () => {
132
+ const persistedManager = new SpeakerManager(
133
+ new Call({
134
+ id: '',
135
+ type: '',
136
+ streamClient: new StreamClient('abc123'),
137
+ clientStore: new StreamVideoWriteableStateStore(),
138
+ }),
139
+ { enabled: true, storageKey },
140
+ );
141
+ const listDevicesSpy = vi.spyOn(persistedManager, 'listDevices');
142
+ const audioOutputDevice = {
143
+ deviceId: 'speaker-1',
144
+ kind: 'audiooutput',
145
+ label: 'Speaker 1',
146
+ groupId: 'speaker-group',
147
+ } as MediaDeviceInfo;
148
+
149
+ emitDeviceIds([audioOutputDevice]);
150
+ persistedManager.select(audioOutputDevice.deviceId);
151
+
152
+ expect(listDevicesSpy).toHaveBeenCalled();
153
+ expect(persistedManager.state.selectedDevice).toBe('speaker-1');
154
+ });
155
+
128
156
  describe('apply (web)', () => {
129
157
  it('does nothing when persistence is disabled', () => {
130
158
  const selectSpy = vi.spyOn(manager, 'select');
@@ -5344,6 +5344,12 @@ export interface JoinCallRequest {
5344
5344
  * @memberof JoinCallRequest
5345
5345
  */
5346
5346
  data?: CallRequest;
5347
+ /**
5348
+ * if true, the participant will be marked as publsihing to large audience
5349
+ * @type {boolean}
5350
+ * @memberof JoinCallRequest
5351
+ */
5352
+ hint_high_scale_livestream_publisher?: boolean;
5347
5353
  /**
5348
5354
  *
5349
5355
  * @type {string}
package/src/types.ts CHANGED
@@ -462,6 +462,15 @@ export type StreamRNVideoSDKGlobals = {
462
462
  */
463
463
  check(permission: 'microphone' | 'camera'): Promise<boolean>;
464
464
  };
465
+ nativeEvents: {
466
+ speechActivity: {
467
+ /**
468
+ * Subscribes to native speech activity events.
469
+ * Returns an unsubscribe function.
470
+ */
471
+ subscribe(cb: (state: { isSoundDetected: boolean }) => void): () => void;
472
+ };
473
+ };
465
474
  };
466
475
 
467
476
  declare global {
@@ -1,23 +0,0 @@
1
- import { SoundStateChangeHandler } from './sound-detector';
2
- export declare class RNSpeechDetector {
3
- private readonly pc1;
4
- private readonly pc2;
5
- private audioStream;
6
- private externalAudioStream;
7
- private isStopped;
8
- constructor(externalAudioStream?: MediaStream);
9
- /**
10
- * Starts the speech detection.
11
- */
12
- start(onSoundDetectedStateChanged: SoundStateChangeHandler): Promise<() => void>;
13
- /**
14
- * Stops the speech detection and releases all allocated resources.
15
- */
16
- private stop;
17
- /**
18
- * Public method that detects the audio levels and returns the status.
19
- */
20
- private onSpeakingDetectedStateChange;
21
- private cleanupAudioStream;
22
- private forwardIceCandidate;
23
- }