@stream-io/video-client 1.58.0 → 1.59.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.
Files changed (47) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/index.browser.es.js +319 -71
  3. package/dist/index.browser.es.js.map +1 -1
  4. package/dist/index.cjs.js +329 -69
  5. package/dist/index.cjs.js.map +1 -1
  6. package/dist/index.d.ts +2 -0
  7. package/dist/index.es.js +319 -71
  8. package/dist/index.es.js.map +1 -1
  9. package/dist/src/Call.d.ts +15 -4
  10. package/dist/src/StreamVideoClient.d.ts +9 -1
  11. package/dist/src/devices/CameraManager.d.ts +2 -1
  12. package/dist/src/devices/DeviceManager.d.ts +1 -1
  13. package/dist/src/devices/MicrophoneManager.d.ts +2 -1
  14. package/dist/src/gen/coordinator/index.d.ts +26 -0
  15. package/dist/src/helpers/MediaStreamRecorder.d.ts +26 -0
  16. package/dist/src/helpers/concurrency.d.ts +8 -0
  17. package/dist/src/helpers/loopback.d.ts +55 -0
  18. package/dist/src/reporting/ClientEventReporter.d.ts +0 -2
  19. package/dist/src/rtc/Subscriber.d.ts +5 -0
  20. package/index.ts +2 -0
  21. package/package.json +1 -1
  22. package/src/Call.ts +156 -134
  23. package/src/StreamVideoClient.ts +35 -8
  24. package/src/__tests__/Call.lifecycle.test.ts +33 -2
  25. package/src/__tests__/Call.publishing.test.ts +42 -1
  26. package/src/__tests__/StreamVideoClient.api.test.ts +33 -0
  27. package/src/__tests__/StreamVideoClient.rewatch.test.ts +213 -0
  28. package/src/__tests__/StreamVideoClient.ringing.test.ts +11 -4
  29. package/src/__tests__/StreamVideoClient.test.ts +14 -12
  30. package/src/devices/CameraManager.ts +16 -4
  31. package/src/devices/DeviceManager.ts +7 -4
  32. package/src/devices/MicrophoneManager.ts +11 -3
  33. package/src/devices/__tests__/CameraManager.test.ts +27 -1
  34. package/src/devices/__tests__/DeviceManager.test.ts +49 -1
  35. package/src/devices/__tests__/MicrophoneManager.test.ts +23 -1
  36. package/src/events/call.ts +6 -3
  37. package/src/gen/coordinator/index.ts +26 -0
  38. package/src/helpers/MediaStreamRecorder.ts +104 -0
  39. package/src/helpers/__tests__/MediaStreamRecorder.test.ts +324 -0
  40. package/src/helpers/__tests__/concurrency.test.ts +74 -0
  41. package/src/helpers/__tests__/loopback.test.ts +367 -0
  42. package/src/helpers/concurrency.ts +24 -0
  43. package/src/helpers/loopback.ts +200 -0
  44. package/src/reporting/ClientEventReporter.ts +15 -28
  45. package/src/reporting/__tests__/ClientEventReporter.test.ts +3 -7
  46. package/src/rtc/Subscriber.ts +8 -0
  47. package/src/rtc/__tests__/Subscriber.test.ts +101 -0
@@ -2,7 +2,7 @@ import { type CallMediaEngine, Publisher, Subscriber, TrackPublishOptions } from
2
2
  import type { E2EEManager } from './rtc/e2ee/E2EEManager';
3
3
  import { CallState } from './store';
4
4
  import { ScopedLogger } from './logger';
5
- import { AcceptCallResponse, BlockUserResponse, CallRingEvent, CallSettingsResponse, CollectUserFeedbackRequest, CollectUserFeedbackResponse, DeleteCallRequest, DeleteCallResponse, DeleteRecordingResponse, DeleteTranscriptionResponse, EndCallResponse, GetCallReportResponse, GetCallResponse, GetCallSessionParticipantStatsDetailsResponse, GetOrCreateCallRequest, GetOrCreateCallResponse, GoLiveRequest, GoLiveResponse, JoinCallResponse, KickUserRequest, KickUserResponse, ListRecordingsResponse, ListTranscriptionsResponse, MuteUsersResponse, PinRequest, PinResponse, QueryCallMembersRequest, QueryCallMembersResponse, QueryCallParticipantsRequest, QueryCallParticipantsResponse, QueryCallSessionParticipantStatsResponse, QueryCallSessionParticipantStatsTimelineResponse, QueryCallStatsMapResponse, RejectCallResponse, RequestPermissionRequest, RequestPermissionResponse, RingCallRequest, RingCallResponse, SendCallEventResponse, SendVideoReactionRequest, SendVideoReactionResponse, StartClosedCaptionsRequest, StartClosedCaptionsResponse, StartFrameRecordingRequest, StartFrameRecordingResponse, StartHLSBroadcastingResponse, StartRTMPBroadcastsRequest, StartRTMPBroadcastsResponse, StartTranscriptionRequest, StartTranscriptionResponse, StopAllRTMPBroadcastsResponse, StopClosedCaptionsRequest, StopClosedCaptionsResponse, StopFrameRecordingResponse, StopHLSBroadcastingResponse, StopLiveRequest, StopLiveResponse, StopRecordingResponse, StopRTMPBroadcastsResponse, StopTranscriptionResponse, UnblockUserResponse, UnpinRequest, UnpinResponse, UpdateCallMembersRequest, UpdateCallMembersResponse, UpdateCallRequest, UpdateCallResponse, UpdateUserPermissionsRequest, UpdateUserPermissionsRequestGrantPermissionsEnum, UpdateUserPermissionsRequestRevokePermissionsEnum, UpdateUserPermissionsResponse } from './gen/coordinator';
5
+ import { AcceptCallResponse, BlockUserResponse, CallRingEvent, CallSettingsResponse, CallStateResponseFields, CollectUserFeedbackRequest, CollectUserFeedbackResponse, DeleteCallRequest, DeleteCallResponse, DeleteRecordingResponse, DeleteTranscriptionResponse, EndCallResponse, GetCallReportResponse, GetCallResponse, GetCallSessionParticipantStatsDetailsResponse, GetOrCreateCallRequest, GetOrCreateCallResponse, GoLiveRequest, GoLiveResponse, JoinCallResponse, KickUserRequest, KickUserResponse, ListRecordingsResponse, ListTranscriptionsResponse, MuteUsersResponse, PinRequest, PinResponse, QueryCallMembersRequest, QueryCallMembersResponse, QueryCallParticipantsRequest, QueryCallParticipantsResponse, QueryCallSessionParticipantStatsResponse, QueryCallSessionParticipantStatsTimelineResponse, QueryCallStatsMapResponse, RejectCallResponse, RequestPermissionRequest, RequestPermissionResponse, RingCallRequest, RingCallResponse, SendCallEventResponse, SendVideoReactionRequest, SendVideoReactionResponse, StartClosedCaptionsRequest, StartClosedCaptionsResponse, StartFrameRecordingRequest, StartFrameRecordingResponse, StartHLSBroadcastingResponse, StartRTMPBroadcastsRequest, StartRTMPBroadcastsResponse, StartTranscriptionRequest, StartTranscriptionResponse, StopAllRTMPBroadcastsResponse, StopClosedCaptionsRequest, StopClosedCaptionsResponse, StopFrameRecordingResponse, StopHLSBroadcastingResponse, StopLiveRequest, StopLiveResponse, StopRecordingResponse, StopRTMPBroadcastsResponse, StopTranscriptionResponse, UnblockUserResponse, UnpinRequest, UnpinResponse, UpdateCallMembersRequest, UpdateCallMembersResponse, UpdateCallRequest, UpdateCallResponse, UpdateUserPermissionsRequest, UpdateUserPermissionsRequestGrantPermissionsEnum, UpdateUserPermissionsRequestRevokePermissionsEnum, UpdateUserPermissionsResponse } from './gen/coordinator';
6
6
  import { AudioTrackType, CallConstructor, CallLeaveOptions, CallRecordingType, ClientPublishOptions, ClosedCaptionsSettings, JoinCallData, StartCallRecordingFnType, TrackMuteType, VideoTrackType } from './types';
7
7
  import { ClientCapability, TrackType, VideoDimension } from './gen/video/sfu/models/models';
8
8
  import { Tracer } from './stats';
@@ -223,6 +223,13 @@ export declare class Call {
223
223
  * @internal
224
224
  */
225
225
  updateFromRingingEvent: (event: CallRingEvent) => Promise<void>;
226
+ /**
227
+ * Applies a backend-provided call state snapshot
228
+ * (call, members, own capabilities) to this instance.
229
+ *
230
+ * @internal an internal method and should not be used outside the SDK.
231
+ */
232
+ updateFromCallStateResponse: (response: CallStateResponseFields) => void;
226
233
  /**
227
234
  * Loads the information about the call.
228
235
  *
@@ -286,12 +293,12 @@ export declare class Call {
286
293
  *
287
294
  * @returns a promise which resolves once the call join-flow has finished.
288
295
  */
289
- join: ({ maxJoinRetries, joinResponseTimeout, rpcRequestTimeout, allowOwnTracksLoopback, ...data }?: JoinCallData & {
296
+ join: (args_0?: (JoinCallData & {
290
297
  maxJoinRetries?: number;
291
298
  joinResponseTimeout?: number;
292
299
  rpcRequestTimeout?: number;
293
300
  allowOwnTracksLoopback?: boolean;
294
- }) => Promise<void>;
301
+ }) | undefined) => Promise<void>;
295
302
  /**
296
303
  * Will make a single attempt to watch for call related WebSocket events
297
304
  * and initiate a call session with the server.
@@ -868,7 +875,11 @@ export declare class Call {
868
875
  *
869
876
  * @internal
870
877
  */
871
- applyDeviceConfig: (settings: CallSettingsResponse, publish: boolean, skipSpeakerApply: boolean) => Promise<void>;
878
+ applyDeviceConfig: (settings: CallSettingsResponse, opts: {
879
+ publish: boolean;
880
+ skipSpeakerApply?: boolean;
881
+ withDisabledDevices?: boolean;
882
+ }) => Promise<void>;
872
883
  /**
873
884
  * Will begin tracking the given element for visibility changes within the
874
885
  * configured viewport element (`call.setViewport`).
@@ -50,6 +50,11 @@ export declare class StreamVideoClient {
50
50
  * @param e the event.
51
51
  */
52
52
  private initCallFromEvent;
53
+ /**
54
+ * Queries the API for calls matching the given filters.
55
+ * @param data the query data.
56
+ */
57
+ private doQueryCalls;
53
58
  /**
54
59
  * Rewatches the given calls with retry logic.
55
60
  * @param callsToReWatch array of call IDs to rewatch
@@ -110,8 +115,11 @@ export declare class StreamVideoClient {
110
115
  * Will query the API for calls matching the given filters.
111
116
  *
112
117
  * @param data the query data.
118
+ * @param opts additional options, for tweaking the API behavior.
113
119
  */
114
- queryCalls: (data?: QueryCallsRequest) => Promise<{
120
+ queryCalls: (data?: QueryCallsRequest, opts?: {
121
+ withDisabledDevices?: boolean;
122
+ }) => Promise<{
115
123
  calls: Call[];
116
124
  duration: string;
117
125
  next?: string;
@@ -48,8 +48,9 @@ export declare class CameraManager extends DeviceManager<CameraManagerState> {
48
48
  *
49
49
  * @param settings the video settings to apply.
50
50
  * @param publish whether to publish the stream after applying the settings.
51
+ * @param forceDisabled whether to keep the camera disabled, regardless of server-side or local preferences.
51
52
  */
52
- apply(settings: VideoSettingsResponse, publish: boolean): Promise<void>;
53
+ apply(settings: VideoSettingsResponse, publish: boolean, forceDisabled?: boolean): Promise<void>;
53
54
  protected getDevices(): Observable<MediaDeviceInfo[]>;
54
55
  protected getResolvedConstraints(constraints: MediaTrackConstraints): MediaTrackConstraints;
55
56
  protected getStream(constraints: MediaTrackConstraints): Promise<MediaStream>;
@@ -130,6 +130,6 @@ export declare abstract class DeviceManager<S extends DeviceManagerState<C>, C =
130
130
  protected findDevice(devices: MediaDeviceInfo[], deviceId: string): MediaDeviceInfo | undefined;
131
131
  private dispatchDeviceDisconnectedEvent;
132
132
  private persistPreference;
133
- protected applyPersistedPreferences(enabledInCallType: boolean): Promise<boolean>;
133
+ protected applyPersistedPreferences(enabledInCallType: boolean, forceDisabled?: boolean): Promise<boolean>;
134
134
  private applyMutedState;
135
135
  }
@@ -69,8 +69,9 @@ export declare class MicrophoneManager extends AudioDeviceManager<MicrophoneMana
69
69
  * Applies the audio settings to the microphone.
70
70
  * @param settings the audio settings to apply.
71
71
  * @param publish whether to publish the stream after applying the settings.
72
+ * @param forceDisabled whether to keep the mic disabled, regardless of server-side or local preferences.
72
73
  */
73
- apply(settings: AudioSettingsResponse, publish: boolean): Promise<void>;
74
+ apply(settings: AudioSettingsResponse, publish: boolean, forceDisabled?: boolean): Promise<void>;
74
75
  protected getDevices(): Observable<MediaDeviceInfo[]>;
75
76
  protected getStream(constraints: MediaTrackConstraints): Promise<MediaStream>;
76
77
  protected doSetAudioBitrateProfile(profile: AudioBitrateProfile): void;
@@ -8481,6 +8481,32 @@ export interface ReportByHistogramBucket {
8481
8481
  */
8482
8482
  upper_bound?: Bound;
8483
8483
  }
8484
+ /**
8485
+ * Reports a batch of client-side telemetry events. Each event is validated and processed independently; one invalid event does not block the rest of the batch.
8486
+ * @export
8487
+ * @interface ReportClientEventRequest
8488
+ */
8489
+ export interface ReportClientEventRequest {
8490
+ /**
8491
+ * Client-side events to report (1-100 per request)
8492
+ * @type {Array<ClientEvent>}
8493
+ * @memberof ReportClientEventRequest
8494
+ */
8495
+ events: Array<ClientEvent>;
8496
+ }
8497
+ /**
8498
+ * Response for reporting client-side telemetry events
8499
+ * @export
8500
+ * @interface ReportClientEventResponse
8501
+ */
8502
+ export interface ReportClientEventResponse {
8503
+ /**
8504
+ * Duration of the request in milliseconds
8505
+ * @type {string}
8506
+ * @memberof ReportClientEventResponse
8507
+ */
8508
+ duration: string;
8509
+ }
8484
8510
  /**
8485
8511
  *
8486
8512
  * @export
@@ -0,0 +1,26 @@
1
+ export type MediaStreamRecorderOptions = {
2
+ /** Auto-stop after this many milliseconds. */
3
+ maxDurationMs?: number;
4
+ };
5
+ export type MediaRecordingResult = {
6
+ blob: Blob;
7
+ };
8
+ /** Single-use `MediaRecorder` wrapper that returns an in-memory blob. */
9
+ export declare class MediaStreamRecorder {
10
+ private recording;
11
+ private logger;
12
+ private chunks;
13
+ private timeoutId;
14
+ private recorder;
15
+ private readonly tracks;
16
+ private readonly maxDurationMs;
17
+ constructor(stream: MediaStream, options?: MediaStreamRecorderOptions);
18
+ /** Starts recording and resolves when recording stops. */
19
+ start: () => Promise<MediaRecordingResult | null>;
20
+ /** Stops recording early. */
21
+ stop: () => void;
22
+ private isSettled;
23
+ private detach;
24
+ private handleStop;
25
+ private handleError;
26
+ }
@@ -27,6 +27,14 @@ export declare const withoutConcurrency: <T>(tag: TagKey, cb: () => Promise<T>)
27
27
  * it's up to the function to decide how to react to cancelation.
28
28
  */
29
29
  export declare const withCancellation: <T>(tag: TagKey, cb: (signal: AbortSignal) => Promise<T | "canceled">) => Promise<T | "canceled">;
30
+ /**
31
+ * Wraps an async function so concurrent calls share the in-flight promise
32
+ * instead of queueing another execution.
33
+ *
34
+ * @param cb Async function to wrap.
35
+ * @returns Wrapped async function.
36
+ */
37
+ export declare function singleFlight<P extends unknown[], T>(cb: (...args: P) => Promise<T>): (...args: P) => Promise<T>;
30
38
  export declare function hasPending(tag: TagKey): boolean;
31
39
  export declare function settled(tag: TagKey): Promise<void>;
32
40
  export {};
@@ -0,0 +1,55 @@
1
+ import type { Call } from '../Call';
2
+ import type { StreamVideoParticipant } from '../types';
3
+ /** Default loopback stream wait timeout. */
4
+ export declare const LOOPBACK_STREAMS_WAIT_TIMEOUT_MS: number;
5
+ /** Default loopback recording duration. */
6
+ export declare const DEFAULT_LOOPBACK_RECORDING_DURATION_MS: number;
7
+ /** Shortest permitted loopback recording duration. */
8
+ export declare const MIN_LOOPBACK_RECORDING_DURATION_MS: number;
9
+ /** Longest permitted loopback recording duration. */
10
+ export declare const MAX_LOOPBACK_RECORDING_DURATION_MS: number;
11
+ /** SFU-echoed streams on the local participant. */
12
+ export type LoopbackStreams = {
13
+ loopbackVideoStream?: MediaStream;
14
+ loopbackAudioStream?: MediaStream;
15
+ };
16
+ /** SFU-echoed tracks on the local participant. */
17
+ export type LoopbackTracks = {
18
+ audioTrack?: MediaStreamTrack;
19
+ videoTrack?: MediaStreamTrack;
20
+ };
21
+ /** Thrown when loopback tracks do not arrive in time. */
22
+ export declare class LoopbackStreamsTimeoutError extends Error {
23
+ constructor();
24
+ }
25
+ /**
26
+ * Returns the streams on the given participant that the SFU echoed back to us,
27
+ * ignoring the local capture streams the publishing path writes to the same
28
+ * fields.
29
+ */
30
+ export declare const getLoopbackStreams: (call: Call | undefined, participant: StreamVideoParticipant | undefined) => LoopbackStreams;
31
+ /**
32
+ * Returns loopback tracks when all required tracks are present.
33
+ *
34
+ * Prefer {@link waitForLoopbackStreams}, which observes call state until the
35
+ * tracks arrive. This is the synchronous snapshot it is built from.
36
+ *
37
+ * @internal
38
+ */
39
+ export declare const getLoopbackTracks: (call: Call | undefined, participant: StreamVideoParticipant | undefined, includeVideo: boolean) => LoopbackTracks | undefined;
40
+ export type WaitForLoopbackStreamsOptions = {
41
+ /** Whether video is required. Audio is always required. */
42
+ includeVideo: boolean;
43
+ /** Aborts the wait. */
44
+ signal: AbortSignal;
45
+ /** Defaults to {@link LOOPBACK_STREAMS_WAIT_TIMEOUT_MS}. */
46
+ timeoutMs?: number;
47
+ };
48
+ /** Waits for the requested loopback tracks. */
49
+ export declare const waitForLoopbackStreams: (call: Call, opts: WaitForLoopbackStreamsOptions) => Promise<LoopbackTracks | null>;
50
+ /** Enables loopback audio while `fn` runs, then restores it. */
51
+ export declare const withLoopbackAudioEnabled: <T>(tracks: LoopbackTracks, fn: () => Promise<T>) => Promise<T>;
52
+ /** Enables loopback audio and returns a restore function. */
53
+ export declare const enableLoopbackAudio: (tracks: LoopbackTracks) => (() => void);
54
+ /** Clamps and rounds a loopback recording duration. */
55
+ export declare const clampLoopbackRecordingDuration: (durationMs: number) => number;
@@ -12,7 +12,6 @@ export type CallReportContext = {
12
12
  callId: string;
13
13
  getSfuId: () => string;
14
14
  getCallSessionId: () => string;
15
- getUserSessionId: () => string;
16
15
  };
17
16
  export type ClientEventReporterOptions = {
18
17
  streamClient: StreamClient;
@@ -79,7 +78,6 @@ export declare class ClientEventReporter {
79
78
  private emitPeerConnectionSuccess;
80
79
  private emitPeerConnectionFailure;
81
80
  private getSfuId;
82
- private getUserSessionId;
83
81
  private sessionIdField;
84
82
  private buildCommon;
85
83
  private send;
@@ -29,6 +29,11 @@ export declare class Subscriber extends BasePeerConnection {
29
29
  * Restarts the ICE connection and renegotiates with the SFU.
30
30
  */
31
31
  restartIce: () => Promise<void>;
32
+ /**
33
+ * Whether the given stream is one the SFU echoed back to us
34
+ * (`allowOwnTracksLoopback`) rather than a local capture stream.
35
+ */
36
+ isSelfSubscribedStream: (stream: MediaStream | undefined) => boolean;
32
37
  private handleOnTrack;
33
38
  private setRemoteTrackInterrupted;
34
39
  private negotiate;
package/index.ts CHANGED
@@ -23,6 +23,8 @@ export * from './src/helpers/humanize';
23
23
  export * from './src/helpers/DynascaleManager';
24
24
  export * from './src/helpers/ViewportTracker';
25
25
  export * from './src/helpers/sound-detector';
26
+ export * from './src/helpers/loopback';
27
+ export * from './src/helpers/MediaStreamRecorder';
26
28
  export * from './src/helpers/participantUtils';
27
29
  export * from './src/rtc/e2ee/E2EEManager';
28
30
  export * from './src/rtc/e2ee/EncryptionManager';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stream-io/video-client",
3
- "version": "1.58.0",
3
+ "version": "1.59.0",
4
4
  "main": "dist/index.cjs.js",
5
5
  "module": "dist/index.es.js",
6
6
  "browser": "dist/index.browser.es.js",