@stream-io/video-client 1.14.0 → 1.15.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 +7 -0
- package/dist/index.browser.es.js +1532 -1784
- package/dist/index.browser.es.js.map +1 -1
- package/dist/index.cjs.js +1512 -1783
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.es.js +1532 -1784
- package/dist/index.es.js.map +1 -1
- package/dist/src/Call.d.ts +43 -28
- package/dist/src/StreamSfuClient.d.ts +4 -5
- package/dist/src/devices/CameraManager.d.ts +5 -8
- package/dist/src/devices/InputMediaDeviceManager.d.ts +5 -5
- package/dist/src/devices/MicrophoneManager.d.ts +7 -2
- package/dist/src/devices/ScreenShareManager.d.ts +1 -2
- package/dist/src/gen/video/sfu/event/events.d.ts +38 -19
- package/dist/src/gen/video/sfu/models/models.d.ts +76 -9
- package/dist/src/helpers/array.d.ts +7 -0
- package/dist/src/permissions/PermissionsContext.d.ts +6 -0
- package/dist/src/rtc/BasePeerConnection.d.ts +90 -0
- package/dist/src/rtc/Dispatcher.d.ts +0 -1
- package/dist/src/rtc/IceTrickleBuffer.d.ts +3 -2
- package/dist/src/rtc/Publisher.d.ts +32 -86
- package/dist/src/rtc/Subscriber.d.ts +4 -56
- package/dist/src/rtc/TransceiverCache.d.ts +55 -0
- package/dist/src/rtc/codecs.d.ts +1 -15
- package/dist/src/rtc/helpers/sdp.d.ts +8 -0
- package/dist/src/rtc/helpers/tracks.d.ts +1 -0
- package/dist/src/rtc/index.d.ts +3 -0
- package/dist/src/rtc/videoLayers.d.ts +11 -25
- package/dist/src/stats/{stateStoreStatsReporter.d.ts → CallStateStatsReporter.d.ts} +5 -1
- package/dist/src/stats/SfuStatsReporter.d.ts +4 -2
- package/dist/src/stats/index.d.ts +1 -1
- package/dist/src/stats/types.d.ts +8 -0
- package/dist/src/types.d.ts +12 -22
- package/package.json +1 -1
- package/src/Call.ts +254 -268
- package/src/StreamSfuClient.ts +9 -14
- package/src/StreamVideoClient.ts +1 -1
- package/src/__tests__/Call.publishing.test.ts +306 -0
- package/src/devices/CameraManager.ts +33 -16
- package/src/devices/InputMediaDeviceManager.ts +36 -27
- package/src/devices/MicrophoneManager.ts +29 -8
- package/src/devices/ScreenShareManager.ts +6 -8
- package/src/devices/__tests__/CameraManager.test.ts +111 -14
- package/src/devices/__tests__/InputMediaDeviceManager.test.ts +4 -4
- package/src/devices/__tests__/MicrophoneManager.test.ts +59 -21
- package/src/devices/__tests__/ScreenShareManager.test.ts +5 -5
- package/src/devices/__tests__/mocks.ts +1 -0
- package/src/events/__tests__/internal.test.ts +132 -0
- package/src/events/__tests__/mutes.test.ts +0 -3
- package/src/events/__tests__/speaker.test.ts +92 -0
- package/src/events/participant.ts +3 -4
- package/src/gen/video/sfu/event/events.ts +91 -30
- package/src/gen/video/sfu/models/models.ts +105 -13
- package/src/helpers/array.ts +14 -0
- package/src/permissions/PermissionsContext.ts +22 -0
- package/src/permissions/__tests__/PermissionsContext.test.ts +40 -0
- package/src/rpc/__tests__/createClient.test.ts +38 -0
- package/src/rpc/createClient.ts +11 -5
- package/src/rtc/BasePeerConnection.ts +240 -0
- package/src/rtc/Dispatcher.ts +0 -9
- package/src/rtc/IceTrickleBuffer.ts +24 -4
- package/src/rtc/Publisher.ts +210 -528
- package/src/rtc/Subscriber.ts +26 -200
- package/src/rtc/TransceiverCache.ts +120 -0
- package/src/rtc/__tests__/Publisher.test.ts +407 -210
- package/src/rtc/__tests__/Subscriber.test.ts +88 -36
- package/src/rtc/__tests__/mocks/webrtc.mocks.ts +22 -2
- package/src/rtc/__tests__/videoLayers.test.ts +161 -54
- package/src/rtc/codecs.ts +1 -131
- package/src/rtc/helpers/__tests__/rtcConfiguration.test.ts +34 -0
- package/src/rtc/helpers/__tests__/sdp.test.ts +59 -0
- package/src/rtc/helpers/sdp.ts +30 -0
- package/src/rtc/helpers/tracks.ts +3 -0
- package/src/rtc/index.ts +4 -0
- package/src/rtc/videoLayers.ts +68 -76
- package/src/stats/{stateStoreStatsReporter.ts → CallStateStatsReporter.ts} +58 -27
- package/src/stats/SfuStatsReporter.ts +31 -3
- package/src/stats/index.ts +1 -1
- package/src/stats/types.ts +12 -0
- package/src/types.ts +12 -22
- package/dist/src/helpers/sdp-munging.d.ts +0 -24
- package/dist/src/rtc/bitrateLookup.d.ts +0 -2
- package/dist/src/rtc/helpers/iceCandidate.d.ts +0 -2
- package/src/helpers/__tests__/hq-audio-sdp.ts +0 -332
- package/src/helpers/__tests__/sdp-munging.test.ts +0 -283
- package/src/helpers/sdp-munging.ts +0 -265
- package/src/rtc/__tests__/bitrateLookup.test.ts +0 -12
- package/src/rtc/__tests__/codecs.test.ts +0 -145
- package/src/rtc/bitrateLookup.ts +0 -61
- package/src/rtc/helpers/iceCandidate.ts +0 -16
- /package/dist/src/{compatibility.d.ts → helpers/compatibility.d.ts} +0 -0
- /package/src/{compatibility.ts → helpers/compatibility.ts} +0 -0
package/dist/src/Call.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Publisher, Subscriber } from './rtc';
|
|
2
2
|
import { CallState } from './store';
|
|
3
|
-
import type { AcceptCallResponse, BlockUserResponse, CallRingEvent, CollectUserFeedbackResponse, EndCallResponse, GetCallResponse, GetCallStatsResponse, GetOrCreateCallRequest, GetOrCreateCallResponse, GoLiveRequest, GoLiveResponse, JoinCallResponse, ListRecordingsResponse, ListTranscriptionsResponse, MuteUsersResponse, PinRequest, PinResponse, QueryCallMembersRequest, QueryCallMembersResponse, RejectCallResponse, RequestPermissionRequest, RequestPermissionResponse, SendCallEventResponse, SendReactionRequest, SendReactionResponse, StartClosedCaptionsRequest, StartClosedCaptionsResponse, StartHLSBroadcastingResponse, StartRecordingRequest, StartRecordingResponse, StartTranscriptionRequest, StartTranscriptionResponse, StopClosedCaptionsRequest, StopClosedCaptionsResponse, StopHLSBroadcastingResponse, StopLiveResponse, StopRecordingResponse, StopTranscriptionResponse, UnblockUserResponse, UnpinRequest, UnpinResponse, UpdateCallMembersRequest, UpdateCallMembersResponse, UpdateCallRequest, UpdateCallResponse, UpdateUserPermissionsRequest, UpdateUserPermissionsResponse, VideoDimension } from './gen/coordinator';
|
|
4
|
-
import { AudioTrackType, CallConstructor, CallLeaveOptions, ClosedCaptionsSettings, JoinCallData,
|
|
3
|
+
import type { AcceptCallResponse, BlockUserResponse, CallRingEvent, CallSettingsResponse, CollectUserFeedbackResponse, EndCallResponse, GetCallResponse, GetCallStatsResponse, GetOrCreateCallRequest, GetOrCreateCallResponse, GoLiveRequest, GoLiveResponse, JoinCallResponse, ListRecordingsResponse, ListTranscriptionsResponse, MuteUsersResponse, PinRequest, PinResponse, QueryCallMembersRequest, QueryCallMembersResponse, RejectCallResponse, RequestPermissionRequest, RequestPermissionResponse, SendCallEventResponse, SendReactionRequest, SendReactionResponse, StartClosedCaptionsRequest, StartClosedCaptionsResponse, StartHLSBroadcastingResponse, StartRecordingRequest, StartRecordingResponse, StartTranscriptionRequest, StartTranscriptionResponse, StopClosedCaptionsRequest, StopClosedCaptionsResponse, StopHLSBroadcastingResponse, StopLiveResponse, StopRecordingResponse, StopTranscriptionResponse, UnblockUserResponse, UnpinRequest, UnpinResponse, UpdateCallMembersRequest, UpdateCallMembersResponse, UpdateCallRequest, UpdateCallResponse, UpdateUserPermissionsRequest, UpdateUserPermissionsResponse, VideoDimension } from './gen/coordinator';
|
|
4
|
+
import { AudioTrackType, CallConstructor, CallLeaveOptions, ClientPublishOptions, ClosedCaptionsSettings, JoinCallData, TrackMuteType, VideoTrackType } from './types';
|
|
5
5
|
import { TrackType } from './gen/video/sfu/models/models';
|
|
6
6
|
import { DynascaleManager } from './helpers/DynascaleManager';
|
|
7
7
|
import { PermissionsContext } from './permissions';
|
|
@@ -69,7 +69,8 @@ export declare class Call {
|
|
|
69
69
|
* @private
|
|
70
70
|
*/
|
|
71
71
|
private readonly dispatcher;
|
|
72
|
-
private
|
|
72
|
+
private clientPublishOptions?;
|
|
73
|
+
private currentPublishOptions?;
|
|
73
74
|
private statsReporter?;
|
|
74
75
|
private sfuStatsReporter?;
|
|
75
76
|
private dropTimeout;
|
|
@@ -210,6 +211,18 @@ export declare class Call {
|
|
|
210
211
|
* @internal
|
|
211
212
|
*/
|
|
212
213
|
private getReconnectDetails;
|
|
214
|
+
/**
|
|
215
|
+
* Prepares the preferred codec for the call.
|
|
216
|
+
* This is an experimental client feature and subject to change.
|
|
217
|
+
* @internal
|
|
218
|
+
*/
|
|
219
|
+
private getPreferredPublishOptions;
|
|
220
|
+
/**
|
|
221
|
+
* Prepares the preferred options for subscribing to tracks.
|
|
222
|
+
* This is an experimental client feature and subject to change.
|
|
223
|
+
* @internal
|
|
224
|
+
*/
|
|
225
|
+
private getPreferredSubscribeOptions;
|
|
213
226
|
/**
|
|
214
227
|
* Performs an ICE restart on both the Publisher and Subscriber Peer Connections.
|
|
215
228
|
* Uses the provided SFU client to restore the ICE connection.
|
|
@@ -282,48 +295,47 @@ export declare class Call {
|
|
|
282
295
|
private restoreSubscribedTracks;
|
|
283
296
|
/**
|
|
284
297
|
* Starts publishing the given video stream to the call.
|
|
285
|
-
*
|
|
286
|
-
*
|
|
287
|
-
* Consecutive calls to this method will replace the previously published stream.
|
|
288
|
-
* The previous video stream will be stopped.
|
|
289
|
-
*
|
|
290
|
-
* @param videoStream the video stream to publish.
|
|
298
|
+
* @deprecated use `call.publish()`.
|
|
291
299
|
*/
|
|
292
300
|
publishVideoStream: (videoStream: MediaStream) => Promise<void>;
|
|
293
301
|
/**
|
|
294
302
|
* Starts publishing the given audio stream to the call.
|
|
295
|
-
*
|
|
296
|
-
*
|
|
297
|
-
* Consecutive calls to this method will replace the audio stream that is currently being published.
|
|
298
|
-
* The previous audio stream will be stopped.
|
|
299
|
-
*
|
|
300
|
-
* @param audioStream the audio stream to publish.
|
|
303
|
+
* @deprecated use `call.publish()`
|
|
301
304
|
*/
|
|
302
305
|
publishAudioStream: (audioStream: MediaStream) => Promise<void>;
|
|
303
306
|
/**
|
|
304
307
|
* Starts publishing the given screen-share stream to the call.
|
|
305
|
-
*
|
|
306
|
-
* Consecutive calls to this method will replace the previous screen-share stream.
|
|
307
|
-
* The previous screen-share stream will be stopped.
|
|
308
|
-
*
|
|
309
|
-
* @param screenShareStream the screen-share stream to publish.
|
|
308
|
+
* @deprecated use `call.publish()`
|
|
310
309
|
*/
|
|
311
310
|
publishScreenShareStream: (screenShareStream: MediaStream) => Promise<void>;
|
|
311
|
+
/**
|
|
312
|
+
* Publishes the given media stream.
|
|
313
|
+
*
|
|
314
|
+
* @param mediaStream the media stream to publish.
|
|
315
|
+
* @param trackType the type of the track to announce.
|
|
316
|
+
*/
|
|
317
|
+
publish: (mediaStream: MediaStream, trackType: TrackType) => Promise<void>;
|
|
312
318
|
/**
|
|
313
319
|
* Stops publishing the given track type to the call, if it is currently being published.
|
|
314
|
-
* Underlying track will be stopped and removed from the publisher.
|
|
315
320
|
*
|
|
316
|
-
* @param
|
|
317
|
-
|
|
321
|
+
* @param trackTypes the track types to stop publishing.
|
|
322
|
+
*/
|
|
323
|
+
stopPublish: (...trackTypes: TrackType[]) => Promise<void>;
|
|
324
|
+
/**
|
|
325
|
+
* Updates the call state with the new stream.
|
|
326
|
+
*
|
|
327
|
+
* @param mediaStream the new stream to update the call state with.
|
|
328
|
+
* If undefined, the stream will be removed from the call state.
|
|
329
|
+
* @param trackTypes the track types to update the call state with.
|
|
318
330
|
*/
|
|
319
|
-
|
|
331
|
+
private updateLocalStreamState;
|
|
320
332
|
/**
|
|
321
333
|
* Updates the preferred publishing options
|
|
322
334
|
*
|
|
323
335
|
* @internal
|
|
324
336
|
* @param options the options to use.
|
|
325
337
|
*/
|
|
326
|
-
updatePublishOptions(options:
|
|
338
|
+
updatePublishOptions: (options: ClientPublishOptions) => void;
|
|
327
339
|
/**
|
|
328
340
|
* Notifies the SFU that a noise cancellation process has started.
|
|
329
341
|
*
|
|
@@ -336,6 +348,11 @@ export declare class Call {
|
|
|
336
348
|
* @internal
|
|
337
349
|
*/
|
|
338
350
|
notifyNoiseCancellationStopped: () => Promise<void | import("@protobuf-ts/runtime-rpc").FinishedUnaryCall<import("./gen/video/sfu/signal_rpc/signal").StopNoiseCancellationRequest, import("./gen/video/sfu/signal_rpc/signal").StopNoiseCancellationResponse> | undefined>;
|
|
351
|
+
/**
|
|
352
|
+
* Notifies the SFU about the mute state of the given track types.
|
|
353
|
+
* @internal
|
|
354
|
+
*/
|
|
355
|
+
notifyTrackMuteState: (muted: boolean, ...trackTypes: TrackType[]) => Promise<void>;
|
|
339
356
|
/**
|
|
340
357
|
* Will enhance the reported stats with additional participant-specific information (`callStatsReport$` state [store variable](./StreamVideoClient.md/#readonlystatestore)).
|
|
341
358
|
* This is usually helpful when detailed stats for a specific participant are needed.
|
|
@@ -608,9 +625,7 @@ export declare class Call {
|
|
|
608
625
|
*
|
|
609
626
|
* @internal
|
|
610
627
|
*/
|
|
611
|
-
applyDeviceConfig: (
|
|
612
|
-
private initCamera;
|
|
613
|
-
private initMic;
|
|
628
|
+
applyDeviceConfig: (settings: CallSettingsResponse, publish: boolean) => Promise<void>;
|
|
614
629
|
/**
|
|
615
630
|
* Will begin tracking the given element for visibility changes within the
|
|
616
631
|
* configured viewport element (`call.setViewport`).
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Dispatcher, IceTrickleBuffer } from './rtc';
|
|
2
2
|
import { JoinRequest, JoinResponse } from './gen/video/sfu/event/events';
|
|
3
|
-
import { ICERestartRequest, SendAnswerRequest, SendStatsRequest, SetPublisherRequest,
|
|
4
|
-
import { ICETrickle
|
|
3
|
+
import { ICERestartRequest, SendAnswerRequest, SendStatsRequest, SetPublisherRequest, TrackMuteState, TrackSubscriptionDetails } from './gen/video/sfu/signal_rpc/signal';
|
|
4
|
+
import { ICETrickle } from './gen/video/sfu/models/models';
|
|
5
5
|
import { StreamClient } from './coordinator/connection/client';
|
|
6
6
|
import { Credentials } from './gen/coordinator';
|
|
7
7
|
export type StreamSfuClientConstructor = {
|
|
@@ -122,15 +122,14 @@ export declare class StreamSfuClient {
|
|
|
122
122
|
get joinTask(): Promise<JoinResponse>;
|
|
123
123
|
private handleWebSocketClose;
|
|
124
124
|
close: (code?: number, reason?: string) => void;
|
|
125
|
-
dispose
|
|
125
|
+
private dispose;
|
|
126
126
|
leaveAndClose: (reason: string) => Promise<void>;
|
|
127
127
|
updateSubscriptions: (tracks: TrackSubscriptionDetails[]) => Promise<import("@protobuf-ts/runtime-rpc").FinishedUnaryCall<import("./gen/video/sfu/signal_rpc/signal").UpdateSubscriptionsRequest, import("./gen/video/sfu/signal_rpc/signal").UpdateSubscriptionsResponse>>;
|
|
128
128
|
setPublisher: (data: Omit<SetPublisherRequest, "sessionId">) => Promise<import("@protobuf-ts/runtime-rpc").FinishedUnaryCall<SetPublisherRequest, import("./gen/video/sfu/signal_rpc/signal").SetPublisherResponse>>;
|
|
129
129
|
sendAnswer: (data: Omit<SendAnswerRequest, "sessionId">) => Promise<import("@protobuf-ts/runtime-rpc").FinishedUnaryCall<SendAnswerRequest, import("./gen/video/sfu/signal_rpc/signal").SendAnswerResponse>>;
|
|
130
130
|
iceTrickle: (data: Omit<ICETrickle, "sessionId">) => Promise<import("@protobuf-ts/runtime-rpc").FinishedUnaryCall<ICETrickle, import("./gen/video/sfu/signal_rpc/signal").ICETrickleResponse>>;
|
|
131
131
|
iceRestart: (data: Omit<ICERestartRequest, "sessionId">) => Promise<import("@protobuf-ts/runtime-rpc").FinishedUnaryCall<ICERestartRequest, import("./gen/video/sfu/signal_rpc/signal").ICERestartResponse>>;
|
|
132
|
-
|
|
133
|
-
updateMuteStates: (data: Omit<UpdateMuteStatesRequest, "sessionId">) => Promise<import("@protobuf-ts/runtime-rpc").FinishedUnaryCall<UpdateMuteStatesRequest, import("./gen/video/sfu/signal_rpc/signal").UpdateMuteStatesResponse>>;
|
|
132
|
+
updateMuteStates: (muteStates: TrackMuteState[]) => Promise<import("@protobuf-ts/runtime-rpc").FinishedUnaryCall<import("./gen/video/sfu/signal_rpc/signal").UpdateMuteStatesRequest, import("./gen/video/sfu/signal_rpc/signal").UpdateMuteStatesResponse>>;
|
|
134
133
|
sendStats: (stats: Omit<SendStatsRequest, "sessionId">) => Promise<import("@protobuf-ts/runtime-rpc").FinishedUnaryCall<SendStatsRequest, import("./gen/video/sfu/signal_rpc/signal").SendStatsResponse>>;
|
|
135
134
|
startNoiseCancellation: () => Promise<import("@protobuf-ts/runtime-rpc").FinishedUnaryCall<import("./gen/video/sfu/signal_rpc/signal").StartNoiseCancellationRequest, import("./gen/video/sfu/signal_rpc/signal").StartNoiseCancellationResponse>>;
|
|
136
135
|
stopNoiseCancellation: () => Promise<import("@protobuf-ts/runtime-rpc").FinishedUnaryCall<import("./gen/video/sfu/signal_rpc/signal").StopNoiseCancellationRequest, import("./gen/video/sfu/signal_rpc/signal").StopNoiseCancellationResponse>>;
|
|
@@ -2,7 +2,7 @@ import { Observable } from 'rxjs';
|
|
|
2
2
|
import { Call } from '../Call';
|
|
3
3
|
import { CameraDirection, CameraManagerState } from './CameraManagerState';
|
|
4
4
|
import { InputMediaDeviceManager } from './InputMediaDeviceManager';
|
|
5
|
-
import {
|
|
5
|
+
import { VideoSettingsResponse } from '../gen/coordinator';
|
|
6
6
|
export declare class CameraManager extends InputMediaDeviceManager<CameraManagerState> {
|
|
7
7
|
private targetResolution;
|
|
8
8
|
/**
|
|
@@ -33,15 +33,12 @@ export declare class CameraManager extends InputMediaDeviceManager<CameraManager
|
|
|
33
33
|
height: number;
|
|
34
34
|
}): Promise<void>;
|
|
35
35
|
/**
|
|
36
|
-
*
|
|
36
|
+
* Applies the video settings to the camera.
|
|
37
37
|
*
|
|
38
|
-
* @
|
|
39
|
-
* @
|
|
40
|
-
* @param codec the codec to use for encoding the video.
|
|
38
|
+
* @param settings the video settings to apply.
|
|
39
|
+
* @param publish whether to publish the stream after applying the settings.
|
|
41
40
|
*/
|
|
42
|
-
|
|
41
|
+
apply(settings: VideoSettingsResponse, publish: boolean): Promise<void>;
|
|
43
42
|
protected getDevices(): Observable<MediaDeviceInfo[]>;
|
|
44
43
|
protected getStream(constraints: MediaTrackConstraints): Promise<MediaStream>;
|
|
45
|
-
protected publishStream(stream: MediaStream): Promise<void>;
|
|
46
|
-
protected stopPublishStream(stopTracks: boolean): Promise<void>;
|
|
47
44
|
}
|
|
@@ -85,16 +85,16 @@ export declare abstract class InputMediaDeviceManager<T extends InputMediaDevice
|
|
|
85
85
|
protected applySettingsToStream(): Promise<void>;
|
|
86
86
|
protected abstract getDevices(): Observable<MediaDeviceInfo[]>;
|
|
87
87
|
protected abstract getStream(constraints: C): Promise<MediaStream>;
|
|
88
|
-
protected
|
|
89
|
-
protected
|
|
88
|
+
protected publishStream(stream: MediaStream): Promise<void>;
|
|
89
|
+
protected stopPublishStream(): Promise<void>;
|
|
90
90
|
protected getTracks(): MediaStreamTrack[];
|
|
91
91
|
protected muteStream(stopTracks?: boolean): Promise<void>;
|
|
92
|
-
private
|
|
93
|
-
private
|
|
92
|
+
private disableTracks;
|
|
93
|
+
private enableTracks;
|
|
94
94
|
private stopTracks;
|
|
95
95
|
private muteLocalStream;
|
|
96
96
|
protected unmuteStream(): Promise<void>;
|
|
97
97
|
private get mediaDeviceKind();
|
|
98
98
|
private handleDisconnectedOrReplacedDevices;
|
|
99
|
-
private
|
|
99
|
+
private findDevice;
|
|
100
100
|
}
|
|
@@ -4,6 +4,7 @@ import { Call } from '../Call';
|
|
|
4
4
|
import { InputMediaDeviceManager } from './InputMediaDeviceManager';
|
|
5
5
|
import { MicrophoneManagerState } from './MicrophoneManagerState';
|
|
6
6
|
import { TrackDisableMode } from './InputMediaDeviceManagerState';
|
|
7
|
+
import { AudioSettingsResponse } from '../gen/coordinator';
|
|
7
8
|
export declare class MicrophoneManager extends InputMediaDeviceManager<MicrophoneManagerState> {
|
|
8
9
|
private speakingWhileMutedNotificationEnabled;
|
|
9
10
|
private soundDetectorConcurrencyTag;
|
|
@@ -35,10 +36,14 @@ export declare class MicrophoneManager extends InputMediaDeviceManager<Microphon
|
|
|
35
36
|
* Disables speaking while muted notification.
|
|
36
37
|
*/
|
|
37
38
|
disableSpeakingWhileMutedNotification(): Promise<void>;
|
|
39
|
+
/**
|
|
40
|
+
* Applies the audio settings to the microphone.
|
|
41
|
+
* @param settings the audio settings to apply.
|
|
42
|
+
* @param publish whether to publish the stream after applying the settings.
|
|
43
|
+
*/
|
|
44
|
+
apply(settings: AudioSettingsResponse, publish: boolean): Promise<void>;
|
|
38
45
|
protected getDevices(): Observable<MediaDeviceInfo[]>;
|
|
39
46
|
protected getStream(constraints: MediaTrackConstraints): Promise<MediaStream>;
|
|
40
|
-
protected publishStream(stream: MediaStream): Promise<void>;
|
|
41
|
-
protected stopPublishStream(stopTracks: boolean): Promise<void>;
|
|
42
47
|
private startSpeakingWhileMutedDetection;
|
|
43
48
|
private stopSpeakingWhileMutedDetection;
|
|
44
49
|
}
|
|
@@ -28,8 +28,7 @@ export declare class ScreenShareManager extends InputMediaDeviceManager<ScreenSh
|
|
|
28
28
|
setSettings(settings: ScreenShareSettings | undefined): void;
|
|
29
29
|
protected getDevices(): Observable<MediaDeviceInfo[]>;
|
|
30
30
|
protected getStream(constraints: DisplayMediaStreamOptions): Promise<MediaStream>;
|
|
31
|
-
protected
|
|
32
|
-
protected stopPublishStream(stopTracks: boolean): Promise<void>;
|
|
31
|
+
protected stopPublishStream(): Promise<void>;
|
|
33
32
|
/**
|
|
34
33
|
* Overrides the default `select` method to throw an error.
|
|
35
34
|
*
|
|
@@ -4,10 +4,10 @@ import { GoAwayReason } from '../models/models';
|
|
|
4
4
|
import { CallGrants } from '../models/models';
|
|
5
5
|
import { Codec } from '../models/models';
|
|
6
6
|
import { ConnectionQuality } from '../models/models';
|
|
7
|
-
import { PublishOptions } from '../models/models';
|
|
8
7
|
import { CallState } from '../models/models';
|
|
9
8
|
import { TrackSubscriptionDetails } from '../signal_rpc/signal';
|
|
10
9
|
import { TrackInfo } from '../models/models';
|
|
10
|
+
import { SubscribeOption } from '../models/models';
|
|
11
11
|
import { ClientDetails } from '../models/models';
|
|
12
12
|
import { TrackUnpublishReason } from '../models/models';
|
|
13
13
|
import { Participant } from '../models/models';
|
|
@@ -218,15 +218,6 @@ export interface SfuEvent {
|
|
|
218
218
|
* @generated from protobuf field: stream.video.sfu.event.ParticipantMigrationComplete participant_migration_complete = 25;
|
|
219
219
|
*/
|
|
220
220
|
participantMigrationComplete: ParticipantMigrationComplete;
|
|
221
|
-
} | {
|
|
222
|
-
oneofKind: 'codecNegotiationComplete';
|
|
223
|
-
/**
|
|
224
|
-
* CodecNegotiationComplete is sent to signal the completion of a codec negotiation.
|
|
225
|
-
* SDKs can safely stop previous transceivers
|
|
226
|
-
*
|
|
227
|
-
* @generated from protobuf field: stream.video.sfu.event.CodecNegotiationComplete codec_negotiation_complete = 26;
|
|
228
|
-
*/
|
|
229
|
-
codecNegotiationComplete: CodecNegotiationComplete;
|
|
230
221
|
} | {
|
|
231
222
|
oneofKind: 'changePublishOptions';
|
|
232
223
|
/**
|
|
@@ -244,14 +235,18 @@ export interface SfuEvent {
|
|
|
244
235
|
*/
|
|
245
236
|
export interface ChangePublishOptions {
|
|
246
237
|
/**
|
|
247
|
-
* @generated from protobuf field: stream.video.sfu.models.PublishOption
|
|
238
|
+
* @generated from protobuf field: repeated stream.video.sfu.models.PublishOption publish_options = 1;
|
|
239
|
+
*/
|
|
240
|
+
publishOptions: PublishOption[];
|
|
241
|
+
/**
|
|
242
|
+
* @generated from protobuf field: string reason = 2;
|
|
248
243
|
*/
|
|
249
|
-
|
|
244
|
+
reason: string;
|
|
250
245
|
}
|
|
251
246
|
/**
|
|
252
|
-
* @generated from protobuf message stream.video.sfu.event.
|
|
247
|
+
* @generated from protobuf message stream.video.sfu.event.ChangePublishOptionsComplete
|
|
253
248
|
*/
|
|
254
|
-
export interface
|
|
249
|
+
export interface ChangePublishOptionsComplete {
|
|
255
250
|
}
|
|
256
251
|
/**
|
|
257
252
|
* @generated from protobuf message stream.video.sfu.event.ParticipantMigrationComplete
|
|
@@ -475,6 +470,14 @@ export interface JoinRequest {
|
|
|
475
470
|
* @generated from protobuf field: stream.video.sfu.event.ReconnectDetails reconnect_details = 7;
|
|
476
471
|
*/
|
|
477
472
|
reconnectDetails?: ReconnectDetails;
|
|
473
|
+
/**
|
|
474
|
+
* @generated from protobuf field: repeated stream.video.sfu.models.PublishOption preferred_publish_options = 9;
|
|
475
|
+
*/
|
|
476
|
+
preferredPublishOptions: PublishOption[];
|
|
477
|
+
/**
|
|
478
|
+
* @generated from protobuf field: repeated stream.video.sfu.models.SubscribeOption preferred_subscribe_options = 10;
|
|
479
|
+
*/
|
|
480
|
+
preferredSubscribeOptions: SubscribeOption[];
|
|
478
481
|
}
|
|
479
482
|
/**
|
|
480
483
|
* @generated from protobuf message stream.video.sfu.event.ReconnectDetails
|
|
@@ -541,9 +544,9 @@ export interface JoinResponse {
|
|
|
541
544
|
*/
|
|
542
545
|
fastReconnectDeadlineSeconds: number;
|
|
543
546
|
/**
|
|
544
|
-
* @generated from protobuf field: stream.video.sfu.models.
|
|
547
|
+
* @generated from protobuf field: repeated stream.video.sfu.models.PublishOption publish_options = 4;
|
|
545
548
|
*/
|
|
546
|
-
publishOptions
|
|
549
|
+
publishOptions: PublishOption[];
|
|
547
550
|
}
|
|
548
551
|
/**
|
|
549
552
|
* ParticipantJoined is fired when a user joins a call
|
|
@@ -700,6 +703,14 @@ export interface AudioSender {
|
|
|
700
703
|
* @generated from protobuf field: stream.video.sfu.models.Codec codec = 2;
|
|
701
704
|
*/
|
|
702
705
|
codec?: Codec;
|
|
706
|
+
/**
|
|
707
|
+
* @generated from protobuf field: stream.video.sfu.models.TrackType track_type = 3;
|
|
708
|
+
*/
|
|
709
|
+
trackType: TrackType;
|
|
710
|
+
/**
|
|
711
|
+
* @generated from protobuf field: int32 publish_option_id = 4;
|
|
712
|
+
*/
|
|
713
|
+
publishOptionId: number;
|
|
703
714
|
}
|
|
704
715
|
/**
|
|
705
716
|
* VideoLayerSetting is used to specify various parameters of a particular encoding in simulcast.
|
|
@@ -750,6 +761,14 @@ export interface VideoSender {
|
|
|
750
761
|
* @generated from protobuf field: repeated stream.video.sfu.event.VideoLayerSetting layers = 3;
|
|
751
762
|
*/
|
|
752
763
|
layers: VideoLayerSetting[];
|
|
764
|
+
/**
|
|
765
|
+
* @generated from protobuf field: stream.video.sfu.models.TrackType track_type = 4;
|
|
766
|
+
*/
|
|
767
|
+
trackType: TrackType;
|
|
768
|
+
/**
|
|
769
|
+
* @generated from protobuf field: int32 publish_option_id = 5;
|
|
770
|
+
*/
|
|
771
|
+
publishOptionId: number;
|
|
753
772
|
}
|
|
754
773
|
/**
|
|
755
774
|
* sent to users when they need to change the quality of their video
|
|
@@ -834,13 +853,13 @@ declare class ChangePublishOptions$Type extends MessageType<ChangePublishOptions
|
|
|
834
853
|
* @generated MessageType for protobuf message stream.video.sfu.event.ChangePublishOptions
|
|
835
854
|
*/
|
|
836
855
|
export declare const ChangePublishOptions: ChangePublishOptions$Type;
|
|
837
|
-
declare class
|
|
856
|
+
declare class ChangePublishOptionsComplete$Type extends MessageType<ChangePublishOptionsComplete> {
|
|
838
857
|
constructor();
|
|
839
858
|
}
|
|
840
859
|
/**
|
|
841
|
-
* @generated MessageType for protobuf message stream.video.sfu.event.
|
|
860
|
+
* @generated MessageType for protobuf message stream.video.sfu.event.ChangePublishOptionsComplete
|
|
842
861
|
*/
|
|
843
|
-
export declare const
|
|
862
|
+
export declare const ChangePublishOptionsComplete: ChangePublishOptionsComplete$Type;
|
|
844
863
|
declare class ParticipantMigrationComplete$Type extends MessageType<ParticipantMigrationComplete> {
|
|
845
864
|
constructor();
|
|
846
865
|
}
|
|
@@ -190,49 +190,108 @@ export interface VideoLayer {
|
|
|
190
190
|
quality: VideoQuality;
|
|
191
191
|
}
|
|
192
192
|
/**
|
|
193
|
-
*
|
|
193
|
+
* SubscribeOption represents the configuration options for subscribing to a track.
|
|
194
|
+
*
|
|
195
|
+
* @generated from protobuf message stream.video.sfu.models.SubscribeOption
|
|
194
196
|
*/
|
|
195
|
-
export interface
|
|
197
|
+
export interface SubscribeOption {
|
|
196
198
|
/**
|
|
197
|
-
*
|
|
199
|
+
* The type of the track being subscribed (e.g., video, screenshare).
|
|
200
|
+
*
|
|
201
|
+
* @generated from protobuf field: stream.video.sfu.models.TrackType track_type = 1;
|
|
202
|
+
*/
|
|
203
|
+
trackType: TrackType;
|
|
204
|
+
/**
|
|
205
|
+
* The codecs supported by the subscriber for decoding tracks.
|
|
206
|
+
*
|
|
207
|
+
* @generated from protobuf field: repeated stream.video.sfu.models.Codec codecs = 2;
|
|
198
208
|
*/
|
|
199
|
-
codecs:
|
|
209
|
+
codecs: Codec[];
|
|
200
210
|
}
|
|
201
211
|
/**
|
|
212
|
+
* PublishOption represents the configuration options for publishing a track.
|
|
213
|
+
*
|
|
202
214
|
* @generated from protobuf message stream.video.sfu.models.PublishOption
|
|
203
215
|
*/
|
|
204
216
|
export interface PublishOption {
|
|
205
217
|
/**
|
|
218
|
+
* The type of the track being published (e.g., video, screenshare).
|
|
219
|
+
*
|
|
206
220
|
* @generated from protobuf field: stream.video.sfu.models.TrackType track_type = 1;
|
|
207
221
|
*/
|
|
208
222
|
trackType: TrackType;
|
|
209
223
|
/**
|
|
224
|
+
* The codec to be used for encoding the track (e.g., VP8, VP9, H264).
|
|
225
|
+
*
|
|
210
226
|
* @generated from protobuf field: stream.video.sfu.models.Codec codec = 2;
|
|
211
227
|
*/
|
|
212
228
|
codec?: Codec;
|
|
213
229
|
/**
|
|
230
|
+
* The target bitrate for the published track, in bits per second.
|
|
231
|
+
*
|
|
214
232
|
* @generated from protobuf field: int32 bitrate = 3;
|
|
215
233
|
*/
|
|
216
234
|
bitrate: number;
|
|
217
235
|
/**
|
|
236
|
+
* The target frames per second (FPS) for video encoding.
|
|
237
|
+
*
|
|
218
238
|
* @generated from protobuf field: int32 fps = 4;
|
|
219
239
|
*/
|
|
220
240
|
fps: number;
|
|
221
241
|
/**
|
|
242
|
+
* The maximum number of spatial layers to send.
|
|
243
|
+
* - For SVC (e.g., VP9), spatial layers downscale by a factor of 2:
|
|
244
|
+
* - 1 layer: full resolution
|
|
245
|
+
* - 2 layers: full resolution + half resolution
|
|
246
|
+
* - 3 layers: full resolution + half resolution + quarter resolution
|
|
247
|
+
* - For non-SVC codecs (e.g., VP8/H264), this determines the number of
|
|
248
|
+
* encoded resolutions (e.g., quarter, half, full) sent for simulcast.
|
|
249
|
+
*
|
|
222
250
|
* @generated from protobuf field: int32 max_spatial_layers = 5;
|
|
223
251
|
*/
|
|
224
252
|
maxSpatialLayers: number;
|
|
225
253
|
/**
|
|
254
|
+
* The maximum number of temporal layers for scalable video coding (SVC).
|
|
255
|
+
* Temporal layers allow varying frame rates for different bandwidths.
|
|
256
|
+
*
|
|
226
257
|
* @generated from protobuf field: int32 max_temporal_layers = 6;
|
|
227
258
|
*/
|
|
228
259
|
maxTemporalLayers: number;
|
|
260
|
+
/**
|
|
261
|
+
* The dimensions of the video (e.g., width and height in pixels).
|
|
262
|
+
* Spatial layers are based on this base resolution. For example, if the base
|
|
263
|
+
* resolution is 1280x720:
|
|
264
|
+
* - Full resolution (1 layer) = 1280x720
|
|
265
|
+
* - Half resolution (2 layers) = 640x360
|
|
266
|
+
* - Quarter resolution (3 layers) = 320x180
|
|
267
|
+
*
|
|
268
|
+
* @generated from protobuf field: stream.video.sfu.models.VideoDimension video_dimension = 7;
|
|
269
|
+
*/
|
|
270
|
+
videoDimension?: VideoDimension;
|
|
271
|
+
/**
|
|
272
|
+
* The unique identifier for the publish request.
|
|
273
|
+
* - This `id` is assigned exclusively by the SFU. Any `id` set by the client
|
|
274
|
+
* in the `PublishOption` will be ignored and overwritten by the SFU.
|
|
275
|
+
* - The primary purpose of this `id` is to uniquely identify each publish
|
|
276
|
+
* request, even in scenarios where multiple publish requests for the same
|
|
277
|
+
* `track_type` and `codec` are active simultaneously.
|
|
278
|
+
* For example:
|
|
279
|
+
* - A user may publish two tracks of the same type (e.g., video) and codec
|
|
280
|
+
* (e.g., VP9) concurrently.
|
|
281
|
+
* - This uniqueness ensures that individual requests can be managed
|
|
282
|
+
* independently. For instance, an `id` is critical when stopping a specific
|
|
283
|
+
* publish request without affecting others.
|
|
284
|
+
*
|
|
285
|
+
* @generated from protobuf field: int32 id = 8;
|
|
286
|
+
*/
|
|
287
|
+
id: number;
|
|
229
288
|
}
|
|
230
289
|
/**
|
|
231
290
|
* @generated from protobuf message stream.video.sfu.models.Codec
|
|
232
291
|
*/
|
|
233
292
|
export interface Codec {
|
|
234
293
|
/**
|
|
235
|
-
* @generated from protobuf field: uint32 payload_type =
|
|
294
|
+
* @generated from protobuf field: uint32 payload_type = 16;
|
|
236
295
|
*/
|
|
237
296
|
payloadType: number;
|
|
238
297
|
/**
|
|
@@ -244,7 +303,7 @@ export interface Codec {
|
|
|
244
303
|
*/
|
|
245
304
|
clockRate: number;
|
|
246
305
|
/**
|
|
247
|
-
* @generated from protobuf field: string encoding_parameters =
|
|
306
|
+
* @generated from protobuf field: string encoding_parameters = 15;
|
|
248
307
|
*/
|
|
249
308
|
encodingParameters: string;
|
|
250
309
|
/**
|
|
@@ -307,6 +366,14 @@ export interface TrackInfo {
|
|
|
307
366
|
* @generated from protobuf field: bool muted = 10;
|
|
308
367
|
*/
|
|
309
368
|
muted: boolean;
|
|
369
|
+
/**
|
|
370
|
+
* @generated from protobuf field: stream.video.sfu.models.Codec codec = 11;
|
|
371
|
+
*/
|
|
372
|
+
codec?: Codec;
|
|
373
|
+
/**
|
|
374
|
+
* @generated from protobuf field: int32 publish_option_id = 12;
|
|
375
|
+
*/
|
|
376
|
+
publishOptionId: number;
|
|
310
377
|
}
|
|
311
378
|
/**
|
|
312
379
|
* @generated from protobuf message stream.video.sfu.models.Error
|
|
@@ -964,13 +1031,13 @@ declare class VideoLayer$Type extends MessageType<VideoLayer> {
|
|
|
964
1031
|
* @generated MessageType for protobuf message stream.video.sfu.models.VideoLayer
|
|
965
1032
|
*/
|
|
966
1033
|
export declare const VideoLayer: VideoLayer$Type;
|
|
967
|
-
declare class
|
|
1034
|
+
declare class SubscribeOption$Type extends MessageType<SubscribeOption> {
|
|
968
1035
|
constructor();
|
|
969
1036
|
}
|
|
970
1037
|
/**
|
|
971
|
-
* @generated MessageType for protobuf message stream.video.sfu.models.
|
|
1038
|
+
* @generated MessageType for protobuf message stream.video.sfu.models.SubscribeOption
|
|
972
1039
|
*/
|
|
973
|
-
export declare const
|
|
1040
|
+
export declare const SubscribeOption: SubscribeOption$Type;
|
|
974
1041
|
declare class PublishOption$Type extends MessageType<PublishOption> {
|
|
975
1042
|
constructor();
|
|
976
1043
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { CallSettingsResponse, OwnCapability } from '../gen/coordinator';
|
|
2
|
+
import { TrackType } from '../gen/video/sfu/models/models';
|
|
2
3
|
/**
|
|
3
4
|
* Stores the permissions for the current user and exposes
|
|
4
5
|
* a few helper methods which make it easier to work with permissions.
|
|
@@ -28,6 +29,11 @@ export declare class PermissionsContext {
|
|
|
28
29
|
* @param permission the permission to check for.
|
|
29
30
|
*/
|
|
30
31
|
hasPermission: (permission: OwnCapability) => boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Helper method that checks whether the current user has the permission
|
|
34
|
+
* to publish the given track type.
|
|
35
|
+
*/
|
|
36
|
+
canPublish: (trackType: TrackType) => boolean | undefined;
|
|
31
37
|
/**
|
|
32
38
|
* Checks if the current user can request a specific permission
|
|
33
39
|
* within the call.
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import type { CallEventListener, Logger } from '../coordinator/connection/types';
|
|
2
|
+
import { CallState } from '../store';
|
|
3
|
+
import { PeerType } from '../gen/video/sfu/models/models';
|
|
4
|
+
import { StreamSfuClient } from '../StreamSfuClient';
|
|
5
|
+
import { AllSfuEvents, Dispatcher } from './Dispatcher';
|
|
6
|
+
export type BasePeerConnectionOpts = {
|
|
7
|
+
sfuClient: StreamSfuClient;
|
|
8
|
+
state: CallState;
|
|
9
|
+
connectionConfig?: RTCConfiguration;
|
|
10
|
+
dispatcher: Dispatcher;
|
|
11
|
+
onUnrecoverableError?: () => void;
|
|
12
|
+
logTag: string;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* A base class for the `Publisher` and `Subscriber` classes.
|
|
16
|
+
* @internal
|
|
17
|
+
*/
|
|
18
|
+
export declare abstract class BasePeerConnection {
|
|
19
|
+
protected readonly logger: Logger;
|
|
20
|
+
protected readonly peerType: PeerType;
|
|
21
|
+
protected readonly pc: RTCPeerConnection;
|
|
22
|
+
protected readonly state: CallState;
|
|
23
|
+
protected readonly dispatcher: Dispatcher;
|
|
24
|
+
protected sfuClient: StreamSfuClient;
|
|
25
|
+
protected readonly onUnrecoverableError?: () => void;
|
|
26
|
+
protected isIceRestarting: boolean;
|
|
27
|
+
private readonly subscriptions;
|
|
28
|
+
private unsubscribeIceTrickle?;
|
|
29
|
+
/**
|
|
30
|
+
* Constructs a new `BasePeerConnection` instance.
|
|
31
|
+
*/
|
|
32
|
+
protected constructor(peerType: PeerType, { sfuClient, connectionConfig, state, dispatcher, onUnrecoverableError, logTag, }: BasePeerConnectionOpts);
|
|
33
|
+
/**
|
|
34
|
+
* Disposes the `RTCPeerConnection` instance.
|
|
35
|
+
*/
|
|
36
|
+
dispose: () => void;
|
|
37
|
+
/**
|
|
38
|
+
* Detaches the event handlers from the `RTCPeerConnection`.
|
|
39
|
+
*/
|
|
40
|
+
protected detachEventHandlers(): void;
|
|
41
|
+
/**
|
|
42
|
+
* Performs an ICE restart on the `RTCPeerConnection`.
|
|
43
|
+
*/
|
|
44
|
+
protected abstract restartIce(): Promise<void>;
|
|
45
|
+
/**
|
|
46
|
+
* Handles events synchronously.
|
|
47
|
+
* Consecutive events are queued and executed one after the other.
|
|
48
|
+
*/
|
|
49
|
+
protected on: <E extends keyof AllSfuEvents>(event: E, fn: CallEventListener<E>) => void;
|
|
50
|
+
/**
|
|
51
|
+
* Appends the trickled ICE candidates to the `RTCPeerConnection`.
|
|
52
|
+
*/
|
|
53
|
+
protected addTrickledIceCandidates: () => void;
|
|
54
|
+
/**
|
|
55
|
+
* Sets the SFU client to use.
|
|
56
|
+
*
|
|
57
|
+
* @param sfuClient the SFU client to use.
|
|
58
|
+
*/
|
|
59
|
+
setSfuClient: (sfuClient: StreamSfuClient) => void;
|
|
60
|
+
/**
|
|
61
|
+
* Returns the result of the `RTCPeerConnection.getStats()` method
|
|
62
|
+
* @param selector an optional `MediaStreamTrack` to get the stats for.
|
|
63
|
+
*/
|
|
64
|
+
getStats: (selector?: MediaStreamTrack | null) => Promise<RTCStatsReport>;
|
|
65
|
+
/**
|
|
66
|
+
* Handles the ICECandidate event and
|
|
67
|
+
* Initiates an ICE Trickle process with the SFU.
|
|
68
|
+
*/
|
|
69
|
+
private onIceCandidate;
|
|
70
|
+
/**
|
|
71
|
+
* Converts the ICE candidate to a JSON string.
|
|
72
|
+
*/
|
|
73
|
+
private toJSON;
|
|
74
|
+
/**
|
|
75
|
+
* Handles the ICE connection state change event.
|
|
76
|
+
*/
|
|
77
|
+
private onIceConnectionStateChange;
|
|
78
|
+
/**
|
|
79
|
+
* Handles the ICE candidate error event.
|
|
80
|
+
*/
|
|
81
|
+
private onIceCandidateError;
|
|
82
|
+
/**
|
|
83
|
+
* Handles the ICE gathering state change event.
|
|
84
|
+
*/
|
|
85
|
+
private onIceGatherChange;
|
|
86
|
+
/**
|
|
87
|
+
* Handles the signaling state change event.
|
|
88
|
+
*/
|
|
89
|
+
private onSignalingChange;
|
|
90
|
+
}
|
|
@@ -22,5 +22,4 @@ export declare class Dispatcher {
|
|
|
22
22
|
dispatch: <K extends SfuEventKinds>(message: DispatchableMessage<K>, logTag?: string) => void;
|
|
23
23
|
on: <E extends keyof AllSfuEvents>(eventName: E, fn: CallEventListener<E>) => () => void;
|
|
24
24
|
off: <E extends keyof AllSfuEvents>(eventName: E, fn: CallEventListener<E>) => void;
|
|
25
|
-
offAll: (eventName?: SfuEventKinds) => void;
|
|
26
25
|
}
|