@stream-io/video-client 1.52.1-beta.0 → 1.53.1
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/CHANGELOG.md +17 -0
- package/dist/index.browser.es.js +819 -123
- package/dist/index.browser.es.js.map +1 -1
- package/dist/index.cjs.js +819 -122
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.es.js +819 -123
- package/dist/index.es.js.map +1 -1
- package/dist/src/Call.d.ts +6 -14
- package/dist/src/StreamVideoClient.d.ts +2 -0
- package/dist/src/coordinator/connection/client.d.ts +1 -0
- package/dist/src/devices/MicrophoneManager.d.ts +6 -0
- package/dist/src/errors/SfuTimeoutError.d.ts +8 -0
- package/dist/src/errors/index.d.ts +1 -0
- package/dist/src/gen/google/protobuf/struct.d.ts +1 -3
- package/dist/src/gen/google/protobuf/timestamp.d.ts +1 -3
- package/dist/src/gen/video/sfu/event/events.d.ts +1 -22
- package/dist/src/gen/video/sfu/models/models.d.ts +0 -4
- package/dist/src/gen/video/sfu/signal_rpc/signal.client.d.ts +2 -23
- package/dist/src/helpers/firstVideoFrame.d.ts +7 -0
- package/dist/src/reporting/ClientEventReporter.d.ts +84 -0
- package/dist/src/reporting/index.d.ts +1 -0
- package/dist/src/rtc/BasePeerConnection.d.ts +5 -2
- package/dist/src/rtc/Publisher.d.ts +1 -4
- package/dist/src/rtc/Subscriber.d.ts +0 -7
- package/dist/src/rtc/types.d.ts +24 -1
- package/dist/src/types.d.ts +16 -0
- package/package.json +1 -1
- package/src/Call.ts +185 -106
- package/src/StreamSfuClient.ts +3 -3
- package/src/StreamVideoClient.ts +18 -3
- package/src/__tests__/Call.autodrop.test.ts +4 -1
- package/src/__tests__/Call.lifecycle.test.ts +4 -1
- package/src/__tests__/Call.publishing.test.ts +4 -1
- package/src/__tests__/Call.test.ts +23 -0
- package/src/coordinator/connection/client.ts +5 -0
- package/src/devices/MicrophoneManager.ts +16 -0
- package/src/devices/__tests__/CameraManager.test.ts +10 -1
- package/src/devices/__tests__/DeviceManager.test.ts +10 -1
- package/src/devices/__tests__/DeviceManagerFilters.test.ts +4 -1
- package/src/devices/__tests__/MicrophoneManager.test.ts +10 -1
- package/src/devices/__tests__/MicrophoneManagerRN.test.ts +78 -2
- package/src/devices/__tests__/ScreenShareManager.test.ts +4 -1
- package/src/devices/__tests__/SpeakerManager.test.ts +13 -4
- package/src/errors/SfuTimeoutError.ts +7 -0
- package/src/errors/index.ts +1 -0
- package/src/events/__tests__/call.test.ts +2 -0
- package/src/events/__tests__/mutes.test.ts +4 -1
- package/src/events/call.ts +8 -0
- package/src/gen/google/protobuf/struct.ts +12 -7
- package/src/gen/google/protobuf/timestamp.ts +7 -6
- package/src/gen/video/sfu/event/events.ts +25 -23
- package/src/gen/video/sfu/models/models.ts +1 -11
- package/src/gen/video/sfu/signal_rpc/signal.client.ts +29 -25
- package/src/gen/video/sfu/signal_rpc/signal.ts +0 -1
- package/src/helpers/__tests__/AudioBindingsWatchdog.test.ts +6 -3
- package/src/helpers/__tests__/DynascaleManager.test.ts +6 -3
- package/src/helpers/__tests__/ViewportTracker.test.ts +6 -3
- package/src/helpers/__tests__/firstVideoFrame.test.ts +91 -0
- package/src/helpers/client-details.ts +1 -1
- package/src/helpers/firstVideoFrame.ts +38 -0
- package/src/reporting/ClientEventReporter.ts +864 -0
- package/src/reporting/__tests__/ClientEventReporter.test.ts +620 -0
- package/src/reporting/index.ts +1 -0
- package/src/rtc/BasePeerConnection.ts +30 -0
- package/src/rtc/Publisher.ts +0 -4
- package/src/rtc/Subscriber.ts +2 -28
- package/src/rtc/__tests__/Call.reconnect.test.ts +3 -0
- package/src/rtc/types.ts +34 -0
- package/src/types.ts +18 -0
|
@@ -1,33 +1,37 @@
|
|
|
1
|
-
|
|
2
1
|
// @generated by protobuf-ts 2.10.0 with parameter long_type_string,client_generic,server_none,eslint_disable,optimize_code_size
|
|
3
2
|
// @generated from protobuf file "video/sfu/signal_rpc/signal.proto" (package "stream.video.sfu.signal", syntax proto3)
|
|
4
3
|
// tslint:disable
|
|
5
|
-
import type {
|
|
6
|
-
|
|
4
|
+
import type {
|
|
5
|
+
RpcOptions,
|
|
6
|
+
RpcTransport,
|
|
7
|
+
ServiceInfo,
|
|
8
|
+
UnaryCall,
|
|
9
|
+
} from '@protobuf-ts/runtime-rpc';
|
|
10
|
+
import { stackIntercept } from '@protobuf-ts/runtime-rpc';
|
|
11
|
+
import type {
|
|
12
|
+
ICERestartRequest,
|
|
13
|
+
ICERestartResponse,
|
|
14
|
+
ICETrickleResponse,
|
|
15
|
+
SendAnswerRequest,
|
|
16
|
+
SendAnswerResponse,
|
|
17
|
+
SendMetricsRequest,
|
|
18
|
+
SendMetricsResponse,
|
|
19
|
+
SendStatsRequest,
|
|
20
|
+
SendStatsResponse,
|
|
21
|
+
SetPublisherRequest,
|
|
22
|
+
SetPublisherResponse,
|
|
23
|
+
StartNoiseCancellationRequest,
|
|
24
|
+
StartNoiseCancellationResponse,
|
|
25
|
+
StopNoiseCancellationRequest,
|
|
26
|
+
StopNoiseCancellationResponse,
|
|
27
|
+
UpdateMuteStatesRequest,
|
|
28
|
+
UpdateMuteStatesResponse,
|
|
29
|
+
UpdateSubscriptionsRequest,
|
|
30
|
+
UpdateSubscriptionsResponse,
|
|
31
|
+
} from './signal';
|
|
7
32
|
import { SignalServer } from './signal';
|
|
8
|
-
import type { StopNoiseCancellationResponse } from './signal';
|
|
9
|
-
import type { StopNoiseCancellationRequest } from './signal';
|
|
10
|
-
import type { StartNoiseCancellationResponse } from './signal';
|
|
11
|
-
import type { StartNoiseCancellationRequest } from './signal';
|
|
12
|
-
import type { SendMetricsResponse } from './signal';
|
|
13
|
-
import type { SendMetricsRequest } from './signal';
|
|
14
|
-
import type { SendStatsResponse } from './signal';
|
|
15
|
-
import type { SendStatsRequest } from './signal';
|
|
16
|
-
import type { ICERestartResponse } from './signal';
|
|
17
|
-
import type { ICERestartRequest } from './signal';
|
|
18
|
-
import type { UpdateMuteStatesResponse } from './signal';
|
|
19
|
-
import type { UpdateMuteStatesRequest } from './signal';
|
|
20
|
-
import type { UpdateSubscriptionsResponse } from './signal';
|
|
21
|
-
import type { UpdateSubscriptionsRequest } from './signal';
|
|
22
|
-
import type { ICETrickleResponse } from './signal';
|
|
23
33
|
import type { ICETrickle } from '../models/models';
|
|
24
|
-
|
|
25
|
-
import type { SendAnswerRequest } from './signal';
|
|
26
|
-
import { stackIntercept } from '@protobuf-ts/runtime-rpc';
|
|
27
|
-
import type { SetPublisherResponse } from './signal';
|
|
28
|
-
import type { SetPublisherRequest } from './signal';
|
|
29
|
-
import type { UnaryCall } from '@protobuf-ts/runtime-rpc';
|
|
30
|
-
import type { RpcOptions } from '@protobuf-ts/runtime-rpc';
|
|
34
|
+
|
|
31
35
|
/**
|
|
32
36
|
* @generated from protobuf service stream.video.sfu.signal.SignalServer
|
|
33
37
|
*/
|
|
@@ -8,6 +8,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
|
8
8
|
import { AudioBindingsWatchdog } from '../AudioBindingsWatchdog';
|
|
9
9
|
import { Call } from '../../Call';
|
|
10
10
|
import { StreamClient } from '../../coordinator/connection/client';
|
|
11
|
+
import { ClientEventReporter } from '../../reporting';
|
|
11
12
|
import { CallingState, StreamVideoWriteableStateStore } from '../../store';
|
|
12
13
|
import { noopComparator } from '../../sorting';
|
|
13
14
|
import { fromPartial } from '@total-typescript/shoehorn';
|
|
@@ -19,12 +20,14 @@ describe('AudioBindingsWatchdog', () => {
|
|
|
19
20
|
|
|
20
21
|
beforeEach(() => {
|
|
21
22
|
vi.useFakeTimers();
|
|
23
|
+
const streamClient = new StreamClient('api-key', {
|
|
24
|
+
devicePersistence: { enabled: false },
|
|
25
|
+
});
|
|
22
26
|
call = new Call({
|
|
23
27
|
id: 'id',
|
|
24
28
|
type: 'default',
|
|
25
|
-
streamClient
|
|
26
|
-
|
|
27
|
-
}),
|
|
29
|
+
streamClient,
|
|
30
|
+
clientEventReporter: new ClientEventReporter({ streamClient }),
|
|
28
31
|
clientStore: new StreamVideoWriteableStateStore(),
|
|
29
32
|
});
|
|
30
33
|
call.setSortParticipantsBy(noopComparator());
|
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
import { DynascaleManager } from '../DynascaleManager';
|
|
18
18
|
import { Call } from '../../Call';
|
|
19
19
|
import { StreamClient } from '../../coordinator/connection/client';
|
|
20
|
+
import { ClientEventReporter } from '../../reporting';
|
|
20
21
|
import { StreamVideoWriteableStateStore } from '../../store';
|
|
21
22
|
import { getCurrentValue } from '../../store/rxUtils';
|
|
22
23
|
import { VisibilityState } from '../../types';
|
|
@@ -36,12 +37,14 @@ describe('DynascaleManager', () => {
|
|
|
36
37
|
let call: Call;
|
|
37
38
|
|
|
38
39
|
beforeEach(() => {
|
|
40
|
+
const streamClient = new StreamClient('api-key', {
|
|
41
|
+
devicePersistence: { enabled: false },
|
|
42
|
+
});
|
|
39
43
|
call = new Call({
|
|
40
44
|
id: 'id',
|
|
41
45
|
type: 'default',
|
|
42
|
-
streamClient
|
|
43
|
-
|
|
44
|
-
}),
|
|
46
|
+
streamClient,
|
|
47
|
+
clientEventReporter: new ClientEventReporter({ streamClient }),
|
|
45
48
|
clientStore: new StreamVideoWriteableStateStore(),
|
|
46
49
|
});
|
|
47
50
|
call.setSortParticipantsBy(noopComparator());
|
|
@@ -7,6 +7,7 @@ import '../../rtc/__tests__/mocks/webrtc.mocks';
|
|
|
7
7
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
8
8
|
import { Call } from '../../Call';
|
|
9
9
|
import { StreamClient } from '../../coordinator/connection/client';
|
|
10
|
+
import { ClientEventReporter } from '../../reporting';
|
|
10
11
|
import { StreamVideoWriteableStateStore } from '../../store';
|
|
11
12
|
import { noopComparator } from '../../sorting';
|
|
12
13
|
import { VisibilityState } from '../../types';
|
|
@@ -17,12 +18,14 @@ describe('ViewportTracker', () => {
|
|
|
17
18
|
let viewportTracker: ViewportTracker;
|
|
18
19
|
|
|
19
20
|
beforeEach(() => {
|
|
21
|
+
const streamClient = new StreamClient('api-key', {
|
|
22
|
+
devicePersistence: { enabled: false },
|
|
23
|
+
});
|
|
20
24
|
call = new Call({
|
|
21
25
|
id: 'id',
|
|
22
26
|
type: 'default',
|
|
23
|
-
streamClient
|
|
24
|
-
|
|
25
|
-
}),
|
|
27
|
+
streamClient,
|
|
28
|
+
clientEventReporter: new ClientEventReporter({ streamClient }),
|
|
26
29
|
clientStore: new StreamVideoWriteableStateStore(),
|
|
27
30
|
});
|
|
28
31
|
call.setSortParticipantsBy(noopComparator());
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import { createFirstVideoFrameDetector } from '../firstVideoFrame';
|
|
3
|
+
|
|
4
|
+
const createVideoElement = () => {
|
|
5
|
+
const listeners = new Map<string, EventListener>();
|
|
6
|
+
|
|
7
|
+
return {
|
|
8
|
+
readyState: 0,
|
|
9
|
+
HAVE_CURRENT_DATA: 2,
|
|
10
|
+
addEventListener: vi.fn((type: string, listener: EventListener) => {
|
|
11
|
+
listeners.set(type, listener);
|
|
12
|
+
}),
|
|
13
|
+
removeEventListener: vi.fn((type: string, listener: EventListener) => {
|
|
14
|
+
if (listeners.get(type) === listener) {
|
|
15
|
+
listeners.delete(type);
|
|
16
|
+
}
|
|
17
|
+
}),
|
|
18
|
+
dispatchEvent: vi.fn((event: Event) => {
|
|
19
|
+
listeners.get(event.type)?.(event);
|
|
20
|
+
return true;
|
|
21
|
+
}),
|
|
22
|
+
} as unknown as HTMLVideoElement;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
describe('createFirstVideoFrameDetector', () => {
|
|
26
|
+
let videoElement: HTMLVideoElement;
|
|
27
|
+
|
|
28
|
+
beforeEach(() => {
|
|
29
|
+
videoElement = createVideoElement();
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('invokes the callback once on loadeddata when video frame callbacks are unavailable', () => {
|
|
33
|
+
const onFirstFrame = vi.fn();
|
|
34
|
+
const stop = createFirstVideoFrameDetector(videoElement, onFirstFrame);
|
|
35
|
+
|
|
36
|
+
videoElement.dispatchEvent(new Event('loadeddata'));
|
|
37
|
+
videoElement.dispatchEvent(new Event('loadeddata'));
|
|
38
|
+
|
|
39
|
+
expect(onFirstFrame).toHaveBeenCalledOnce();
|
|
40
|
+
stop();
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('cancels the loadeddata fallback', () => {
|
|
44
|
+
const onFirstFrame = vi.fn();
|
|
45
|
+
const stop = createFirstVideoFrameDetector(videoElement, onFirstFrame);
|
|
46
|
+
|
|
47
|
+
stop();
|
|
48
|
+
videoElement.dispatchEvent(new Event('loadeddata'));
|
|
49
|
+
|
|
50
|
+
expect(onFirstFrame).not.toHaveBeenCalled();
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('invokes the callback when frame data is already available', async () => {
|
|
54
|
+
const onFirstFrame = vi.fn();
|
|
55
|
+
Object.defineProperty(videoElement, 'requestVideoFrameCallback', {
|
|
56
|
+
configurable: true,
|
|
57
|
+
value: undefined,
|
|
58
|
+
});
|
|
59
|
+
Object.defineProperty(videoElement, 'HAVE_CURRENT_DATA', {
|
|
60
|
+
configurable: true,
|
|
61
|
+
value: 2,
|
|
62
|
+
});
|
|
63
|
+
Object.defineProperty(videoElement, 'readyState', {
|
|
64
|
+
configurable: true,
|
|
65
|
+
value: 2,
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
createFirstVideoFrameDetector(videoElement, onFirstFrame);
|
|
69
|
+
await Promise.resolve();
|
|
70
|
+
|
|
71
|
+
expect(onFirstFrame).toHaveBeenCalledOnce();
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('uses requestVideoFrameCallback when available', () => {
|
|
75
|
+
const onFirstFrame = vi.fn();
|
|
76
|
+
let frameCallback: VideoFrameRequestCallback | undefined;
|
|
77
|
+
videoElement.requestVideoFrameCallback = vi.fn((callback) => {
|
|
78
|
+
frameCallback = callback;
|
|
79
|
+
return 1;
|
|
80
|
+
});
|
|
81
|
+
videoElement.cancelVideoFrameCallback = vi.fn();
|
|
82
|
+
|
|
83
|
+
const stop = createFirstVideoFrameDetector(videoElement, onFirstFrame);
|
|
84
|
+
frameCallback?.(0, {} as VideoFrameCallbackMetadata);
|
|
85
|
+
|
|
86
|
+
expect(onFirstFrame).toHaveBeenCalledOnce();
|
|
87
|
+
|
|
88
|
+
stop();
|
|
89
|
+
expect(videoElement.cancelVideoFrameCallback).toHaveBeenCalledWith(1);
|
|
90
|
+
});
|
|
91
|
+
});
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Invokes `onFirstFrame` once when the video element renders a frame.
|
|
3
|
+
*
|
|
4
|
+
* Uses `requestVideoFrameCallback` when available, falling back to `loadeddata`
|
|
5
|
+
* for browsers that don't support it.
|
|
6
|
+
*/
|
|
7
|
+
export const createFirstVideoFrameDetector = (
|
|
8
|
+
videoElement: HTMLVideoElement,
|
|
9
|
+
onFirstFrame: () => void,
|
|
10
|
+
) => {
|
|
11
|
+
let done = false;
|
|
12
|
+
const notify = () => {
|
|
13
|
+
if (done) return;
|
|
14
|
+
done = true;
|
|
15
|
+
onFirstFrame();
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
if (typeof videoElement.requestVideoFrameCallback === 'function') {
|
|
19
|
+
const handle = videoElement.requestVideoFrameCallback(notify);
|
|
20
|
+
return () => {
|
|
21
|
+
done = true;
|
|
22
|
+
videoElement.cancelVideoFrameCallback(handle);
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (videoElement.readyState >= videoElement.HAVE_CURRENT_DATA) {
|
|
27
|
+
queueMicrotask(notify);
|
|
28
|
+
return () => {
|
|
29
|
+
done = true;
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
videoElement.addEventListener('loadeddata', notify, { once: true });
|
|
34
|
+
return () => {
|
|
35
|
+
done = true;
|
|
36
|
+
videoElement.removeEventListener('loadeddata', notify);
|
|
37
|
+
};
|
|
38
|
+
};
|