@stream-io/video-client 1.52.0 → 1.53.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.
- package/CHANGELOG.md +10 -0
- package/dist/index.browser.es.js +796 -51
- package/dist/index.browser.es.js.map +1 -1
- package/dist/index.cjs.js +796 -50
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.es.js +796 -51
- package/dist/index.es.js.map +1 -1
- package/dist/src/Call.d.ts +5 -1
- package/dist/src/StreamVideoClient.d.ts +2 -0
- package/dist/src/coordinator/connection/client.d.ts +1 -0
- package/dist/src/errors/SfuTimeoutError.d.ts +8 -0
- package/dist/src/errors/index.d.ts +1 -0
- 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/types.d.ts +24 -1
- package/dist/src/types.d.ts +5 -0
- package/package.json +1 -1
- package/src/Call.ts +184 -60
- 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/__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 +4 -1
- 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/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/firstVideoFrame.ts +38 -0
- package/src/reporting/ClientEventReporter.ts +859 -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/Subscriber.ts +1 -0
- package/src/rtc/__tests__/Call.reconnect.test.ts +3 -0
- package/src/rtc/types.ts +34 -0
- package/src/types.ts +6 -0
package/dist/src/Call.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ import type { AcceptCallResponse, BlockUserResponse, CallRingEvent, CallSettings
|
|
|
5
5
|
import { AudioTrackType, CallConstructor, CallLeaveOptions, CallRecordingType, ClientPublishOptions, ClosedCaptionsSettings, JoinCallData, StartCallRecordingFnType, TrackMuteType, VideoTrackType } from './types';
|
|
6
6
|
import { ClientCapability, TrackType, VideoDimension } from './gen/video/sfu/models/models';
|
|
7
7
|
import { Tracer } from './stats';
|
|
8
|
+
import type { ClientEventReporter } from './reporting';
|
|
8
9
|
import { AudioBindingsWatchdog } from './helpers/AudioBindingsWatchdog';
|
|
9
10
|
import { BlockedAudioTracker } from './helpers/BlockedAudioTracker';
|
|
10
11
|
import { TrackSubscriptionManager } from './helpers/TrackSubscriptionManager';
|
|
@@ -107,6 +108,7 @@ export declare class Call {
|
|
|
107
108
|
private dropTimeout;
|
|
108
109
|
private readonly clientStore;
|
|
109
110
|
readonly streamClient: StreamClient;
|
|
111
|
+
readonly clientEventReporter: ClientEventReporter;
|
|
110
112
|
private sfuClient?;
|
|
111
113
|
private sfuClientTag;
|
|
112
114
|
private unifiedSessionId?;
|
|
@@ -152,7 +154,7 @@ export declare class Call {
|
|
|
152
154
|
* Use the [`StreamVideoClient.call`](./StreamVideoClient.md/#call)
|
|
153
155
|
* method to construct a `Call` instance.
|
|
154
156
|
*/
|
|
155
|
-
constructor({ type, id, streamClient, members, ownCapabilities, sortParticipantsBy, clientStore, ringing, watching, }: CallConstructor);
|
|
157
|
+
constructor({ type, id, streamClient, clientEventReporter, members, ownCapabilities, sortParticipantsBy, clientStore, ringing, watching, }: CallConstructor);
|
|
156
158
|
/**
|
|
157
159
|
* Sets up the call instance.
|
|
158
160
|
*
|
|
@@ -836,6 +838,8 @@ export declare class Call {
|
|
|
836
838
|
* @param trackType the kind of video.
|
|
837
839
|
*/
|
|
838
840
|
bindVideoElement: (videoElement: HTMLVideoElement, sessionId: string, trackType: VideoTrackType) => (() => void) | undefined;
|
|
841
|
+
private bindFirstVideoFrameDetector;
|
|
842
|
+
private reportFirstRenderedVideoFrame;
|
|
839
843
|
/**
|
|
840
844
|
* Binds a DOM <audio> element to the given session id.
|
|
841
845
|
*
|
|
@@ -5,6 +5,7 @@ import type { ConnectedEvent, CreateDeviceRequest, CreateGuestRequest, CreateGue
|
|
|
5
5
|
import { AllClientEvents, ClientEventListener, StreamClientOptions, TokenOrProvider, User } from './coordinator/connection/types';
|
|
6
6
|
import type { StreamVideoClientOptions } from './types';
|
|
7
7
|
import { ScopedLogger } from './logger';
|
|
8
|
+
import { ClientEventReporter } from './reporting';
|
|
8
9
|
/**
|
|
9
10
|
* A `StreamVideoClient` instance lets you communicate with our API, and authenticate users.
|
|
10
11
|
*/
|
|
@@ -21,6 +22,7 @@ export declare class StreamVideoClient {
|
|
|
21
22
|
readonly logger: ScopedLogger;
|
|
22
23
|
protected readonly writeableStateStore: StreamVideoWriteableStateStore;
|
|
23
24
|
streamClient: StreamClient;
|
|
25
|
+
readonly clientEventReporter: ClientEventReporter;
|
|
24
26
|
private effectsRegistered;
|
|
25
27
|
private eventHandlersToUnregister;
|
|
26
28
|
private readonly connectionConcurrencyTag;
|
|
@@ -137,6 +137,7 @@ export declare class StreamClient {
|
|
|
137
137
|
* @private
|
|
138
138
|
*/
|
|
139
139
|
connect: () => Promise<ConnectedEvent | undefined>;
|
|
140
|
+
getSdkVersion: () => string;
|
|
140
141
|
getUserAgent: () => string;
|
|
141
142
|
_enrichAxiosOptions: (options?: AxiosRequestConfig & {
|
|
142
143
|
config?: AxiosRequestConfig;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* An error thrown when a client-side SFU deadline (e.g., waiting for the
|
|
3
|
+
* signaling WS to open or for the `joinResponse` to arrive) fires before
|
|
4
|
+
* the awaited operation resolves. Allows consumers (e.g., the client event
|
|
5
|
+
* reporter) to classify timeouts without relying on message wording.
|
|
6
|
+
*/
|
|
7
|
+
export declare class SfuTimeoutError extends Error {
|
|
8
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
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 declare const createFirstVideoFrameDetector: (videoElement: HTMLVideoElement, onFirstFrame: () => void) => () => void;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { TrackType } from '../gen/video/sfu/models/models';
|
|
2
|
+
import type { StreamClient } from '../coordinator/connection/client';
|
|
3
|
+
import type { PeerConnectionStateChangeEvent } from '../rtc';
|
|
4
|
+
export type ClientEventPeerConnection = 'publish' | 'subscribe';
|
|
5
|
+
export type ClientEventStage = 'JoinInitiated' | 'CoordinatorWS' | 'MediaDevicePermission' | 'CoordinatorJoin' | 'WSJoin' | 'PeerConnectionConnect' | 'FirstVideoFrame' | 'FirstAudioFrame';
|
|
6
|
+
export type MediaPermissionState = 'INITIATED' | 'FAILED' | 'GRANTED' | 'NOT_INITIATED';
|
|
7
|
+
export type JoinReason = 'first-attempt' | 'network-available' | 'migration' | 'full-rejoin';
|
|
8
|
+
export type ClientEventStandardCode = 'CLIENT_ABORTED' | 'BACKEND_LEAVE' | 'REQUEST_TIMEOUT' | 'NETWORK_ERROR' | 'SFU_ERROR' | 'SFU_GO_AWAY' | 'ICE_CONNECTIVITY_FAILED' | 'DTLS_CONNECTIVITY_FAILED';
|
|
9
|
+
export type CallReportContext = {
|
|
10
|
+
callType: string;
|
|
11
|
+
callId: string;
|
|
12
|
+
getSfuId: () => string;
|
|
13
|
+
getCallSessionId: () => string;
|
|
14
|
+
getUserSessionId: () => string;
|
|
15
|
+
};
|
|
16
|
+
export type ClientEventReporterOptions = {
|
|
17
|
+
streamClient: StreamClient;
|
|
18
|
+
};
|
|
19
|
+
export declare class ClientEventReporter {
|
|
20
|
+
private readonly logger;
|
|
21
|
+
private streamClient;
|
|
22
|
+
private coordinatorConnectId?;
|
|
23
|
+
private coordinatorConnectUserId?;
|
|
24
|
+
private coordinatorWsPair?;
|
|
25
|
+
private callContexts;
|
|
26
|
+
private joinAttemptIds;
|
|
27
|
+
private joinReasons;
|
|
28
|
+
private coordinatorPairs;
|
|
29
|
+
private wsPairs;
|
|
30
|
+
private peerConnectionPairs;
|
|
31
|
+
private pcEverConnected;
|
|
32
|
+
private firstFrameReported;
|
|
33
|
+
constructor(options: ClientEventReporterOptions);
|
|
34
|
+
/**
|
|
35
|
+
* Starts a new coordinator connection correlation scope.
|
|
36
|
+
*
|
|
37
|
+
* @param userId the id of the user being connected. Captured here because
|
|
38
|
+
* the `CoordinatorWS` stage emits before the connection flow assigns
|
|
39
|
+
* the user to the client, so it can't be read from the client yet.
|
|
40
|
+
*/
|
|
41
|
+
startCoordinatorConnection: (userId?: string) => string;
|
|
42
|
+
trackCoordinatorWs: <T>(op: () => Promise<T>) => Promise<T>;
|
|
43
|
+
private beginCoordinatorWs;
|
|
44
|
+
private succeedCoordinatorWs;
|
|
45
|
+
closeCoordinatorWs: () => void;
|
|
46
|
+
private buildCoordinatorWsCommon;
|
|
47
|
+
private emitMediaPermission;
|
|
48
|
+
registerCall: (cid: string, ctx: CallReportContext) => void;
|
|
49
|
+
unregisterCall: (cid: string) => void;
|
|
50
|
+
startCorrelation: (cid: string, joinReason: JoinReason) => void;
|
|
51
|
+
withJoinLifecycle: <T>(cid: string, joinReason: JoinReason, op: () => Promise<T>) => Promise<T>;
|
|
52
|
+
track: <T>(cid: string, stage: "CoordinatorJoin" | "WSJoin", op: () => Promise<T>) => Promise<T>;
|
|
53
|
+
reportFirstFrame: (cid: string, trackType: TrackType, trackId: string) => void;
|
|
54
|
+
captureWsError: (cid: string, opts: {
|
|
55
|
+
code: string;
|
|
56
|
+
reason: string;
|
|
57
|
+
}) => void;
|
|
58
|
+
close: (cid: string) => void;
|
|
59
|
+
abort: (cid: string, opts: {
|
|
60
|
+
code: "CLIENT_ABORTED" | "BACKEND_LEAVE";
|
|
61
|
+
reason: string;
|
|
62
|
+
}) => void;
|
|
63
|
+
private closeCallPairs;
|
|
64
|
+
private emitJoinInitiated;
|
|
65
|
+
private beginAttempt;
|
|
66
|
+
private succeedAttempt;
|
|
67
|
+
private applyStageError;
|
|
68
|
+
private beginCoordinatorAttempt;
|
|
69
|
+
private succeedCoordinator;
|
|
70
|
+
private failCoordinator;
|
|
71
|
+
private beginWsAttempt;
|
|
72
|
+
private succeedWs;
|
|
73
|
+
private failWs;
|
|
74
|
+
onPeerConnectionStateChange: (cid: string, event: PeerConnectionStateChangeEvent) => void;
|
|
75
|
+
private openPeerConnectionPair;
|
|
76
|
+
private emitPeerConnectionSuccess;
|
|
77
|
+
private emitPeerConnectionFailure;
|
|
78
|
+
private getSfuId;
|
|
79
|
+
private getUserSessionId;
|
|
80
|
+
private sessionIdField;
|
|
81
|
+
private buildCommon;
|
|
82
|
+
private send;
|
|
83
|
+
private sendWithRetry;
|
|
84
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './ClientEventReporter';
|
|
@@ -5,7 +5,7 @@ import { PeerType, TrackType } from '../gen/video/sfu/models/models';
|
|
|
5
5
|
import { StreamSfuClient } from '../StreamSfuClient';
|
|
6
6
|
import { AllSfuEvents, Dispatcher } from './Dispatcher';
|
|
7
7
|
import { StatsTracer, Tracer } from '../stats';
|
|
8
|
-
import { BasePeerConnectionOpts } from './types';
|
|
8
|
+
import { BasePeerConnectionOpts, OnRemoteTrackUnmute } from './types';
|
|
9
9
|
import type { ClientPublishOptions } from '../types';
|
|
10
10
|
/**
|
|
11
11
|
* A base class for the `Publisher` and `Subscriber` classes.
|
|
@@ -22,6 +22,8 @@ export declare abstract class BasePeerConnection {
|
|
|
22
22
|
protected sfuClient: StreamSfuClient;
|
|
23
23
|
private onReconnectionNeeded?;
|
|
24
24
|
private onIceConnected?;
|
|
25
|
+
private onPeerConnectionStateChange?;
|
|
26
|
+
protected onRemoteTrackUnmute?: OnRemoteTrackUnmute;
|
|
25
27
|
private readonly iceRestartDelay;
|
|
26
28
|
private iceHasEverConnected;
|
|
27
29
|
private iceRestartTimeout?;
|
|
@@ -37,7 +39,7 @@ export declare abstract class BasePeerConnection {
|
|
|
37
39
|
/**
|
|
38
40
|
* Constructs a new `BasePeerConnection` instance.
|
|
39
41
|
*/
|
|
40
|
-
protected constructor(peerType: PeerType, { sfuClient, connectionConfig, state, dispatcher, onReconnectionNeeded, onIceConnected, tag, enableTracing, clientPublishOptions, iceRestartDelay, statsTimestampDriftThresholdMs, }: BasePeerConnectionOpts);
|
|
42
|
+
protected constructor(peerType: PeerType, { sfuClient, connectionConfig, state, dispatcher, onReconnectionNeeded, onIceConnected, onPeerConnectionStateChange, onRemoteTrackUnmute, tag, enableTracing, clientPublishOptions, iceRestartDelay, statsTimestampDriftThresholdMs, }: BasePeerConnectionOpts);
|
|
41
43
|
private createPeerConnection;
|
|
42
44
|
/**
|
|
43
45
|
* Disposes the `RTCPeerConnection` instance.
|
|
@@ -116,6 +118,7 @@ export declare abstract class BasePeerConnection {
|
|
|
116
118
|
* Handles the ICE connection state change event.
|
|
117
119
|
*/
|
|
118
120
|
private onIceConnectionStateChange;
|
|
121
|
+
private fireOnPeerConnectionStateChange;
|
|
119
122
|
private handleConnectionStateUpdate;
|
|
120
123
|
/**
|
|
121
124
|
* Handles the ICE candidate error event.
|
package/dist/src/rtc/types.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AudioBitrateProfile, PeerType, PublishOption, WebsocketReconnectStrategy } from '../gen/video/sfu/models/models';
|
|
1
|
+
import { AudioBitrateProfile, PeerType, PublishOption, TrackType, WebsocketReconnectStrategy } from '../gen/video/sfu/models/models';
|
|
2
2
|
import { StreamSfuClient } from '../StreamSfuClient';
|
|
3
3
|
import { CallState } from '../store';
|
|
4
4
|
import { Dispatcher } from './Dispatcher';
|
|
@@ -37,6 +37,27 @@ export type OnReconnectionNeeded = (kind: WebsocketReconnectStrategy, reason: Re
|
|
|
37
37
|
* recovered, not merely when the SFU join handshake succeeded.
|
|
38
38
|
*/
|
|
39
39
|
export type OnIceConnected = (peerType: PeerType) => void;
|
|
40
|
+
/**
|
|
41
|
+
* Snapshot of the peer connection's ICE and DTLS state surfaced to telemetry
|
|
42
|
+
* consumers (e.g. `ClientEventReporter`). Fired on every transition of
|
|
43
|
+
* either `iceConnectionState` or `peerConnectionState`.
|
|
44
|
+
*/
|
|
45
|
+
export type PeerConnectionStateChangeEvent = {
|
|
46
|
+
peerType: PeerType;
|
|
47
|
+
stateType: 'ice';
|
|
48
|
+
state: RTCIceConnectionState;
|
|
49
|
+
} | {
|
|
50
|
+
peerType: PeerType;
|
|
51
|
+
stateType: 'peerConnection';
|
|
52
|
+
state: RTCPeerConnectionState;
|
|
53
|
+
};
|
|
54
|
+
export type OnPeerConnectionStateChange = (event: PeerConnectionStateChangeEvent) => void;
|
|
55
|
+
/**
|
|
56
|
+
* Fired when a remote track starts receiving media (`unmute`). Used by
|
|
57
|
+
* telemetry to report the `FirstVideoFrame` / `FirstAudioFrame` stage; the
|
|
58
|
+
* consumer decides which track types are relevant.
|
|
59
|
+
*/
|
|
60
|
+
export type OnRemoteTrackUnmute = (trackType: TrackType, trackId: string) => void;
|
|
40
61
|
export type BasePeerConnectionOpts = {
|
|
41
62
|
sfuClient: StreamSfuClient;
|
|
42
63
|
state: CallState;
|
|
@@ -44,6 +65,8 @@ export type BasePeerConnectionOpts = {
|
|
|
44
65
|
dispatcher: Dispatcher;
|
|
45
66
|
onReconnectionNeeded?: OnReconnectionNeeded;
|
|
46
67
|
onIceConnected?: OnIceConnected;
|
|
68
|
+
onPeerConnectionStateChange?: OnPeerConnectionStateChange;
|
|
69
|
+
onRemoteTrackUnmute?: OnRemoteTrackUnmute;
|
|
47
70
|
tag: string;
|
|
48
71
|
enableTracing: boolean;
|
|
49
72
|
iceRestartDelay?: number;
|
package/dist/src/types.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Participant, TrackType, VideoDimension } from './gen/video/sfu/models/models';
|
|
2
2
|
import type { AudioSettingsRequestDefaultDeviceEnum, CallRecordingStartedEventRecordingTypeEnum, JoinCallRequest, MemberResponse, OwnCapability, ReactionResponse, StartRecordingRequest, StartRecordingResponse } from './gen/coordinator';
|
|
3
3
|
import type { StreamClient } from './coordinator/connection/client';
|
|
4
|
+
import type { ClientEventReporter } from './reporting';
|
|
4
5
|
import type { RejectReason, StreamClientOptions, TokenProvider, User } from './coordinator/connection/types';
|
|
5
6
|
import type { Comparator } from './sorting';
|
|
6
7
|
import type { StreamVideoWriteableStateStore } from './store';
|
|
@@ -252,6 +253,10 @@ export type CallConstructor = {
|
|
|
252
253
|
* The streamClient instance to use.
|
|
253
254
|
*/
|
|
254
255
|
streamClient: StreamClient;
|
|
256
|
+
/**
|
|
257
|
+
* The shared client event reporter, owned by `StreamVideoClient`.
|
|
258
|
+
*/
|
|
259
|
+
clientEventReporter: ClientEventReporter;
|
|
255
260
|
/**
|
|
256
261
|
* The Call type.
|
|
257
262
|
*/
|