@stream-io/video-client 0.1.3 → 0.1.5
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 +19 -0
- package/dist/index.browser.es.js +401 -106
- package/dist/index.browser.es.js.map +1 -1
- package/dist/index.cjs.js +400 -105
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.es.js +401 -106
- package/dist/index.es.js.map +1 -1
- package/dist/src/Call.d.ts +1 -1
- package/dist/src/StreamSfuClient.d.ts +2 -1
- package/dist/src/StreamVideoClient.d.ts +3 -4
- package/dist/src/coordinator/connection/client.d.ts +0 -6
- package/dist/src/gen/video/sfu/event/events.d.ts +45 -2
- package/dist/src/gen/video/sfu/models/models.d.ts +12 -0
- package/dist/src/gen/video/sfu/signal_rpc/signal.client.d.ts +9 -1
- package/dist/src/gen/video/sfu/signal_rpc/signal.d.ts +42 -0
- package/dist/src/rtc/Publisher.d.ts +10 -2
- package/dist/src/rtc/Subscriber.d.ts +8 -3
- package/dist/version.d.ts +1 -1
- package/package.json +1 -1
- package/src/Call.ts +18 -9
- package/src/StreamSfuClient.ts +16 -5
- package/src/StreamVideoClient.ts +32 -30
- package/src/coordinator/connection/client.ts +0 -25
- package/src/gen/google/protobuf/struct.ts +1 -2
- package/src/gen/google/protobuf/timestamp.ts +1 -1
- package/src/gen/video/sfu/event/events.ts +165 -5
- package/src/gen/video/sfu/models/models.ts +13 -1
- package/src/gen/video/sfu/signal_rpc/signal.client.ts +27 -1
- package/src/gen/video/sfu/signal_rpc/signal.ts +194 -1
- package/src/rtc/Dispatcher.ts +1 -0
- package/src/rtc/Publisher.ts +69 -34
- package/src/rtc/Subscriber.ts +74 -7
- package/src/rtc/__tests__/Publisher.test.ts +82 -2
- package/src/rtc/__tests__/Subscriber.test.ts +84 -1
package/dist/src/Call.d.ts
CHANGED
|
@@ -164,7 +164,6 @@ export declare class Call {
|
|
|
164
164
|
*/
|
|
165
165
|
join: (data?: JoinCallData) => Promise<void>;
|
|
166
166
|
private waitForJoinResponse;
|
|
167
|
-
private assertCallJoined;
|
|
168
167
|
/**
|
|
169
168
|
* Starts publishing the given video stream to the call.
|
|
170
169
|
* The stream will be stopped if the user changes an input device, or if the user leaves the call.
|
|
@@ -276,6 +275,7 @@ export declare class Call {
|
|
|
276
275
|
* @returns
|
|
277
276
|
*/
|
|
278
277
|
updatePublishQuality: (enabledRids: string[]) => Promise<void | undefined>;
|
|
278
|
+
private assertCallJoined;
|
|
279
279
|
/**
|
|
280
280
|
* Sends a reaction to the other call participants.
|
|
281
281
|
*
|
|
@@ -2,7 +2,7 @@ import type { WebSocket } from 'ws';
|
|
|
2
2
|
import type { FinishedUnaryCall } from '@protobuf-ts/runtime-rpc';
|
|
3
3
|
import { Dispatcher, IceTrickleBuffer } from './rtc';
|
|
4
4
|
import { JoinRequest, SfuRequest } from './gen/video/sfu/event/events';
|
|
5
|
-
import { SendAnswerRequest, SetPublisherRequest, TrackSubscriptionDetails, UpdateMuteStatesRequest } from './gen/video/sfu/signal_rpc/signal';
|
|
5
|
+
import { ICERestartRequest, SendAnswerRequest, SetPublisherRequest, TrackSubscriptionDetails, UpdateMuteStatesRequest } from './gen/video/sfu/signal_rpc/signal';
|
|
6
6
|
import { ICETrickle, TrackType } from './gen/video/sfu/models/models';
|
|
7
7
|
import { SFUResponse } from './gen/coordinator';
|
|
8
8
|
export type StreamSfuClientConstructor = {
|
|
@@ -84,6 +84,7 @@ export declare class StreamSfuClient {
|
|
|
84
84
|
setPublisher: (data: Omit<SetPublisherRequest, 'sessionId'>) => Promise<FinishedUnaryCall<SetPublisherRequest, import("./gen/video/sfu/signal_rpc/signal").SetPublisherResponse>>;
|
|
85
85
|
sendAnswer: (data: Omit<SendAnswerRequest, 'sessionId'>) => Promise<FinishedUnaryCall<SendAnswerRequest, import("./gen/video/sfu/signal_rpc/signal").SendAnswerResponse>>;
|
|
86
86
|
iceTrickle: (data: Omit<ICETrickle, 'sessionId'>) => Promise<FinishedUnaryCall<ICETrickle, import("./gen/video/sfu/signal_rpc/signal").ICETrickleResponse>>;
|
|
87
|
+
iceRestart: (data: Omit<ICERestartRequest, 'sessionId'>) => Promise<FinishedUnaryCall<ICERestartRequest, import("./gen/video/sfu/signal_rpc/signal").ICERestartResponse>>;
|
|
87
88
|
updateMuteState: (trackType: TrackType, muted: boolean) => Promise<FinishedUnaryCall<UpdateMuteStatesRequest, import("./gen/video/sfu/signal_rpc/signal").UpdateMuteStatesResponse>>;
|
|
88
89
|
updateMuteStates: (data: Omit<UpdateMuteStatesRequest, 'sessionId'>) => Promise<FinishedUnaryCall<UpdateMuteStatesRequest, import("./gen/video/sfu/signal_rpc/signal").UpdateMuteStatesResponse>>;
|
|
89
90
|
join: (data: Omit<JoinRequest, 'sessionId' | 'token'>) => Promise<void>;
|
|
@@ -2,7 +2,7 @@ import { Call } from './Call';
|
|
|
2
2
|
import { StreamClient } from './coordinator/connection/client';
|
|
3
3
|
import { StreamVideoReadOnlyStateStore, StreamVideoWriteableStateStore } from './store';
|
|
4
4
|
import type { ConnectedEvent, CreateDeviceRequest, CreateGuestRequest, CreateGuestResponse, GetEdgesResponse, ListDevicesResponse, QueryCallsRequest } from './gen/coordinator';
|
|
5
|
-
import type { EventHandler, EventTypes, Logger, StreamClientOptions, TokenOrProvider, TokenProvider, User, UserWithId } from './coordinator/connection/types';
|
|
5
|
+
import type { EventHandler, EventTypes, Logger, LogLevel, StreamClientOptions, TokenOrProvider, TokenProvider, User, UserWithId } from './coordinator/connection/types';
|
|
6
6
|
/**
|
|
7
7
|
* A `StreamVideoClient` instance lets you communicate with our API, and authenticate users.
|
|
8
8
|
*/
|
|
@@ -11,8 +11,7 @@ export declare class StreamVideoClient {
|
|
|
11
11
|
* A reactive store that exposes all the state variables in a reactive manner - you can subscribe to changes of the different state variables. Our library is built in a way that all state changes are exposed in this store, so all UI changes in your application should be handled by subscribing to these variables.
|
|
12
12
|
*/
|
|
13
13
|
readonly readOnlyStateStore: StreamVideoReadOnlyStateStore;
|
|
14
|
-
readonly
|
|
15
|
-
readonly token?: TokenOrProvider;
|
|
14
|
+
readonly logLevel: LogLevel;
|
|
16
15
|
readonly logger: Logger;
|
|
17
16
|
protected readonly writeableStateStore: StreamVideoWriteableStateStore;
|
|
18
17
|
streamClient: StreamClient;
|
|
@@ -38,7 +37,7 @@ export declare class StreamVideoClient {
|
|
|
38
37
|
* @param user the user to connect.
|
|
39
38
|
* @param token a token or a function that returns a token.
|
|
40
39
|
*/
|
|
41
|
-
connectUser(user
|
|
40
|
+
connectUser(user: User, token?: TokenOrProvider): Promise<void | ConnectedEvent>;
|
|
42
41
|
/**
|
|
43
42
|
* Disconnects the currently connected user from the client.
|
|
44
43
|
*
|
|
@@ -39,8 +39,6 @@ export declare class StreamClient {
|
|
|
39
39
|
rejectConnectionId?: Function;
|
|
40
40
|
connectionIdPromise?: Promise<string | undefined>;
|
|
41
41
|
private nextRequestAbortController;
|
|
42
|
-
private waitForConnectPromise?;
|
|
43
|
-
private resolveConnectPromise?;
|
|
44
42
|
/**
|
|
45
43
|
* Initialize a client.
|
|
46
44
|
*
|
|
@@ -60,10 +58,6 @@ export declare class StreamClient {
|
|
|
60
58
|
getLocationHint: (hintUrl?: string, timeout?: number) => Promise<string>;
|
|
61
59
|
_getConnectionID: () => string | undefined;
|
|
62
60
|
_hasConnectionID: () => boolean;
|
|
63
|
-
/**
|
|
64
|
-
* This will start a promise to hold API calls until `connectUser` is called, useful when user is set in `StreamVideoClient constructor`
|
|
65
|
-
*/
|
|
66
|
-
startWaitingForConnection: () => void;
|
|
67
61
|
/**
|
|
68
62
|
* connectUser - Set the current user and open a WebSocket connection
|
|
69
63
|
*
|
|
@@ -160,6 +160,14 @@ export interface SfuEvent {
|
|
|
160
160
|
* @generated from protobuf field: stream.video.sfu.event.GoAway go_away = 20;
|
|
161
161
|
*/
|
|
162
162
|
goAway: GoAway;
|
|
163
|
+
} | {
|
|
164
|
+
oneofKind: 'iceRestart';
|
|
165
|
+
/**
|
|
166
|
+
* ICERestart tells the client to perform ICE restart.
|
|
167
|
+
*
|
|
168
|
+
* @generated from protobuf field: stream.video.sfu.event.ICERestart ice_restart = 21;
|
|
169
|
+
*/
|
|
170
|
+
iceRestart: ICERestart;
|
|
163
171
|
} | {
|
|
164
172
|
oneofKind: undefined;
|
|
165
173
|
};
|
|
@@ -186,6 +194,15 @@ export interface ICETrickle {
|
|
|
186
194
|
*/
|
|
187
195
|
iceCandidate: string;
|
|
188
196
|
}
|
|
197
|
+
/**
|
|
198
|
+
* @generated from protobuf message stream.video.sfu.event.ICERestart
|
|
199
|
+
*/
|
|
200
|
+
export interface ICERestart {
|
|
201
|
+
/**
|
|
202
|
+
* @generated from protobuf field: stream.video.sfu.models.PeerType peer_type = 1;
|
|
203
|
+
*/
|
|
204
|
+
peerType: PeerType;
|
|
205
|
+
}
|
|
189
206
|
/**
|
|
190
207
|
* SfuRequest is a message that is sent from the client to the SFU.
|
|
191
208
|
*
|
|
@@ -312,6 +329,20 @@ export interface JoinRequest {
|
|
|
312
329
|
* @generated from protobuf field: stream.video.sfu.event.Migration migration = 5;
|
|
313
330
|
*/
|
|
314
331
|
migration?: Migration;
|
|
332
|
+
/**
|
|
333
|
+
* Fast reconnect flag explicitly indicates that if the participant session
|
|
334
|
+
* and the associated state is still present in the SFU, the client is ready
|
|
335
|
+
* to restore the PeerConnection with an ICE restart. If the SFU replies with
|
|
336
|
+
* "reconnected: true" in its JoinResponse, then it is safe to perform an ICE
|
|
337
|
+
* restart or else the existing PeerConnections must be cleaned up.
|
|
338
|
+
*
|
|
339
|
+
* For the SFU, fast_reconnect:false indicates that even if it has the state
|
|
340
|
+
* cached, the client state is not in sync and hence it must be cleaned up before
|
|
341
|
+
* proceeding further.
|
|
342
|
+
*
|
|
343
|
+
* @generated from protobuf field: bool fast_reconnect = 6;
|
|
344
|
+
*/
|
|
345
|
+
fastReconnect: boolean;
|
|
315
346
|
}
|
|
316
347
|
/**
|
|
317
348
|
* @generated from protobuf message stream.video.sfu.event.Migration
|
|
@@ -335,11 +366,13 @@ export interface Migration {
|
|
|
335
366
|
*/
|
|
336
367
|
export interface JoinResponse {
|
|
337
368
|
/**
|
|
338
|
-
* TODO: include full list of participants with track and audio info
|
|
339
|
-
*
|
|
340
369
|
* @generated from protobuf field: stream.video.sfu.models.CallState call_state = 1;
|
|
341
370
|
*/
|
|
342
371
|
callState?: CallState;
|
|
372
|
+
/**
|
|
373
|
+
* @generated from protobuf field: bool reconnected = 2;
|
|
374
|
+
*/
|
|
375
|
+
reconnected: boolean;
|
|
343
376
|
}
|
|
344
377
|
/**
|
|
345
378
|
* ParticipantJoined is fired when a user joins a call
|
|
@@ -666,6 +699,16 @@ declare class ICETrickle$Type extends MessageType<ICETrickle> {
|
|
|
666
699
|
* @generated MessageType for protobuf message stream.video.sfu.event.ICETrickle
|
|
667
700
|
*/
|
|
668
701
|
export declare const ICETrickle: ICETrickle$Type;
|
|
702
|
+
declare class ICERestart$Type extends MessageType<ICERestart> {
|
|
703
|
+
constructor();
|
|
704
|
+
create(value?: PartialMessage<ICERestart>): ICERestart;
|
|
705
|
+
internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: ICERestart): ICERestart;
|
|
706
|
+
internalBinaryWrite(message: ICERestart, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter;
|
|
707
|
+
}
|
|
708
|
+
/**
|
|
709
|
+
* @generated MessageType for protobuf message stream.video.sfu.event.ICERestart
|
|
710
|
+
*/
|
|
711
|
+
export declare const ICERestart: ICERestart$Type;
|
|
669
712
|
declare class SfuRequest$Type extends MessageType<SfuRequest> {
|
|
670
713
|
constructor();
|
|
671
714
|
create(value?: PartialMessage<SfuRequest>): SfuRequest;
|
|
@@ -534,6 +534,18 @@ export declare enum ErrorCode {
|
|
|
534
534
|
* @generated from protobuf enum value: ERROR_CODE_PARTICIPANT_MIGRATION_FAILED = 202;
|
|
535
535
|
*/
|
|
536
536
|
PARTICIPANT_MIGRATION_FAILED = 202,
|
|
537
|
+
/**
|
|
538
|
+
* @generated from protobuf enum value: ERROR_CODE_PARTICIPANT_MIGRATING = 203;
|
|
539
|
+
*/
|
|
540
|
+
PARTICIPANT_MIGRATING = 203,
|
|
541
|
+
/**
|
|
542
|
+
* @generated from protobuf enum value: ERROR_CODE_PARTICIPANT_RECONNECT_FAILED = 204;
|
|
543
|
+
*/
|
|
544
|
+
PARTICIPANT_RECONNECT_FAILED = 204,
|
|
545
|
+
/**
|
|
546
|
+
* @generated from protobuf enum value: ERROR_CODE_PARTICIPANT_MEDIA_TRANSPORT_FAILURE = 205;
|
|
547
|
+
*/
|
|
548
|
+
PARTICIPANT_MEDIA_TRANSPORT_FAILURE = 205,
|
|
537
549
|
/**
|
|
538
550
|
* @generated from protobuf enum value: ERROR_CODE_CALL_NOT_FOUND = 300;
|
|
539
551
|
*/
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { RpcOptions, RpcTransport, ServiceInfo, UnaryCall } from '@protobuf-ts/runtime-rpc';
|
|
2
|
-
import type { ICETrickleResponse, SendAnswerRequest, SendAnswerResponse, SetPublisherRequest, SetPublisherResponse, UpdateMuteStatesRequest, UpdateMuteStatesResponse, UpdateSubscriptionsRequest, UpdateSubscriptionsResponse } from './signal';
|
|
2
|
+
import type { ICERestartRequest, ICERestartResponse, ICETrickleResponse, SendAnswerRequest, SendAnswerResponse, SetPublisherRequest, SetPublisherResponse, UpdateMuteStatesRequest, UpdateMuteStatesResponse, UpdateSubscriptionsRequest, UpdateSubscriptionsResponse } from './signal';
|
|
3
3
|
import type { ICETrickle } from '../models/models';
|
|
4
4
|
/**
|
|
5
5
|
* @generated from protobuf service stream.video.sfu.signal.SignalServer
|
|
@@ -34,6 +34,10 @@ export interface ISignalServerClient {
|
|
|
34
34
|
* @generated from protobuf rpc: UpdateMuteStates(stream.video.sfu.signal.UpdateMuteStatesRequest) returns (stream.video.sfu.signal.UpdateMuteStatesResponse);
|
|
35
35
|
*/
|
|
36
36
|
updateMuteStates(input: UpdateMuteStatesRequest, options?: RpcOptions): UnaryCall<UpdateMuteStatesRequest, UpdateMuteStatesResponse>;
|
|
37
|
+
/**
|
|
38
|
+
* @generated from protobuf rpc: IceRestart(stream.video.sfu.signal.ICERestartRequest) returns (stream.video.sfu.signal.ICERestartResponse);
|
|
39
|
+
*/
|
|
40
|
+
iceRestart(input: ICERestartRequest, options?: RpcOptions): UnaryCall<ICERestartRequest, ICERestartResponse>;
|
|
37
41
|
}
|
|
38
42
|
/**
|
|
39
43
|
* @generated from protobuf service stream.video.sfu.signal.SignalServer
|
|
@@ -75,4 +79,8 @@ export declare class SignalServerClient implements ISignalServerClient, ServiceI
|
|
|
75
79
|
* @generated from protobuf rpc: UpdateMuteStates(stream.video.sfu.signal.UpdateMuteStatesRequest) returns (stream.video.sfu.signal.UpdateMuteStatesResponse);
|
|
76
80
|
*/
|
|
77
81
|
updateMuteStates(input: UpdateMuteStatesRequest, options?: RpcOptions): UnaryCall<UpdateMuteStatesRequest, UpdateMuteStatesResponse>;
|
|
82
|
+
/**
|
|
83
|
+
* @generated from protobuf rpc: IceRestart(stream.video.sfu.signal.ICERestartRequest) returns (stream.video.sfu.signal.ICERestartResponse);
|
|
84
|
+
*/
|
|
85
|
+
iceRestart(input: ICERestartRequest, options?: RpcOptions): UnaryCall<ICERestartRequest, ICERestartResponse>;
|
|
78
86
|
}
|
|
@@ -2,6 +2,28 @@ import { Error, PeerType, TrackInfo, TrackType, VideoDimension } from '../models
|
|
|
2
2
|
import { ServiceType } from '@protobuf-ts/runtime-rpc';
|
|
3
3
|
import type { BinaryReadOptions, BinaryWriteOptions, IBinaryReader, IBinaryWriter, PartialMessage } from '@protobuf-ts/runtime';
|
|
4
4
|
import { MessageType } from '@protobuf-ts/runtime';
|
|
5
|
+
/**
|
|
6
|
+
* @generated from protobuf message stream.video.sfu.signal.ICERestartRequest
|
|
7
|
+
*/
|
|
8
|
+
export interface ICERestartRequest {
|
|
9
|
+
/**
|
|
10
|
+
* @generated from protobuf field: string session_id = 1;
|
|
11
|
+
*/
|
|
12
|
+
sessionId: string;
|
|
13
|
+
/**
|
|
14
|
+
* @generated from protobuf field: stream.video.sfu.models.PeerType peer_type = 2;
|
|
15
|
+
*/
|
|
16
|
+
peerType: PeerType;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* @generated from protobuf message stream.video.sfu.signal.ICERestartResponse
|
|
20
|
+
*/
|
|
21
|
+
export interface ICERestartResponse {
|
|
22
|
+
/**
|
|
23
|
+
* @generated from protobuf field: stream.video.sfu.models.Error error = 1;
|
|
24
|
+
*/
|
|
25
|
+
error?: Error;
|
|
26
|
+
}
|
|
5
27
|
/**
|
|
6
28
|
* @generated from protobuf message stream.video.sfu.signal.UpdateMuteStatesRequest
|
|
7
29
|
*/
|
|
@@ -175,6 +197,26 @@ export interface SetPublisherResponse {
|
|
|
175
197
|
*/
|
|
176
198
|
error?: Error;
|
|
177
199
|
}
|
|
200
|
+
declare class ICERestartRequest$Type extends MessageType<ICERestartRequest> {
|
|
201
|
+
constructor();
|
|
202
|
+
create(value?: PartialMessage<ICERestartRequest>): ICERestartRequest;
|
|
203
|
+
internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: ICERestartRequest): ICERestartRequest;
|
|
204
|
+
internalBinaryWrite(message: ICERestartRequest, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter;
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* @generated MessageType for protobuf message stream.video.sfu.signal.ICERestartRequest
|
|
208
|
+
*/
|
|
209
|
+
export declare const ICERestartRequest: ICERestartRequest$Type;
|
|
210
|
+
declare class ICERestartResponse$Type extends MessageType<ICERestartResponse> {
|
|
211
|
+
constructor();
|
|
212
|
+
create(value?: PartialMessage<ICERestartResponse>): ICERestartResponse;
|
|
213
|
+
internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: ICERestartResponse): ICERestartResponse;
|
|
214
|
+
internalBinaryWrite(message: ICERestartResponse, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter;
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* @generated MessageType for protobuf message stream.video.sfu.signal.ICERestartResponse
|
|
218
|
+
*/
|
|
219
|
+
export declare const ICERestartResponse: ICERestartResponse$Type;
|
|
178
220
|
declare class UpdateMuteStatesRequest$Type extends MessageType<UpdateMuteStatesRequest> {
|
|
179
221
|
constructor();
|
|
180
222
|
create(value?: PartialMessage<UpdateMuteStatesRequest>): UpdateMuteStatesRequest;
|
|
@@ -2,13 +2,16 @@ import { StreamSfuClient } from '../StreamSfuClient';
|
|
|
2
2
|
import { TrackInfo, TrackType } from '../gen/video/sfu/models/models';
|
|
3
3
|
import { CallState } from '../store';
|
|
4
4
|
import { PublishOptions } from '../types';
|
|
5
|
+
import { Dispatcher } from './Dispatcher';
|
|
5
6
|
export type PublisherOpts = {
|
|
6
7
|
sfuClient: StreamSfuClient;
|
|
7
8
|
state: CallState;
|
|
9
|
+
dispatcher: Dispatcher;
|
|
8
10
|
connectionConfig?: RTCConfiguration;
|
|
9
11
|
isDtxEnabled: boolean;
|
|
10
12
|
isRedEnabled: boolean;
|
|
11
13
|
preferredVideoCodec?: string;
|
|
14
|
+
iceRestartDelay?: number;
|
|
12
15
|
};
|
|
13
16
|
/**
|
|
14
17
|
* The `Publisher` is responsible for publishing/unpublishing media streams to/from the SFU
|
|
@@ -17,6 +20,7 @@ export type PublisherOpts = {
|
|
|
17
20
|
export declare class Publisher {
|
|
18
21
|
private pc;
|
|
19
22
|
private readonly state;
|
|
23
|
+
private readonly dispatcher;
|
|
20
24
|
private readonly transceiverRegistry;
|
|
21
25
|
/**
|
|
22
26
|
* An array maintaining the order how transceivers were added to the peer connection.
|
|
@@ -31,7 +35,9 @@ export declare class Publisher {
|
|
|
31
35
|
private readonly isDtxEnabled;
|
|
32
36
|
private readonly isRedEnabled;
|
|
33
37
|
private readonly preferredVideoCodec?;
|
|
34
|
-
private
|
|
38
|
+
private readonly unsubscribeOnIceRestart;
|
|
39
|
+
private readonly iceRestartDelay;
|
|
40
|
+
private isIceRestarting;
|
|
35
41
|
/**
|
|
36
42
|
* The SFU client instance to use for publishing and signaling.
|
|
37
43
|
*/
|
|
@@ -42,11 +48,13 @@ export declare class Publisher {
|
|
|
42
48
|
* @param connectionConfig the connection configuration to use.
|
|
43
49
|
* @param sfuClient the SFU client to use.
|
|
44
50
|
* @param state the call state to use.
|
|
51
|
+
* @param dispatcher the dispatcher to use.
|
|
45
52
|
* @param isDtxEnabled whether DTX is enabled.
|
|
46
53
|
* @param isRedEnabled whether RED is enabled.
|
|
47
54
|
* @param preferredVideoCodec the preferred video codec.
|
|
55
|
+
* @param iceRestartDelay the delay in milliseconds to wait before restarting ICE once connection goes to `disconnected` state.
|
|
48
56
|
*/
|
|
49
|
-
constructor({ connectionConfig, sfuClient, state, isDtxEnabled, isRedEnabled, preferredVideoCodec, }: PublisherOpts);
|
|
57
|
+
constructor({ connectionConfig, sfuClient, dispatcher, state, isDtxEnabled, isRedEnabled, preferredVideoCodec, iceRestartDelay, }: PublisherOpts);
|
|
50
58
|
private createPeerConnection;
|
|
51
59
|
/**
|
|
52
60
|
* Closes the publisher PeerConnection and cleans up the resources.
|
|
@@ -6,6 +6,7 @@ export type SubscriberOpts = {
|
|
|
6
6
|
dispatcher: Dispatcher;
|
|
7
7
|
state: CallState;
|
|
8
8
|
connectionConfig?: RTCConfiguration;
|
|
9
|
+
iceRestartDelay?: number;
|
|
9
10
|
};
|
|
10
11
|
/**
|
|
11
12
|
* A wrapper around the `RTCPeerConnection` that handles the incoming
|
|
@@ -13,10 +14,13 @@ export type SubscriberOpts = {
|
|
|
13
14
|
*/
|
|
14
15
|
export declare class Subscriber {
|
|
15
16
|
private pc;
|
|
16
|
-
private readonly unregisterOnSubscriberOffer;
|
|
17
17
|
private sfuClient;
|
|
18
18
|
private dispatcher;
|
|
19
19
|
private state;
|
|
20
|
+
private readonly unregisterOnSubscriberOffer;
|
|
21
|
+
private readonly unregisterOnIceRestart;
|
|
22
|
+
private readonly iceRestartDelay;
|
|
23
|
+
private isIceRestarting;
|
|
20
24
|
/**
|
|
21
25
|
* Constructs a new `Subscriber` instance.
|
|
22
26
|
*
|
|
@@ -24,8 +28,9 @@ export declare class Subscriber {
|
|
|
24
28
|
* @param dispatcher the dispatcher to use.
|
|
25
29
|
* @param state the state of the call.
|
|
26
30
|
* @param connectionConfig the connection configuration to use.
|
|
31
|
+
* @param iceRestartDelay the delay in milliseconds to wait before restarting ICE when connection goes to `disconnected` state.
|
|
27
32
|
*/
|
|
28
|
-
constructor({ sfuClient, dispatcher, state, connectionConfig, }: SubscriberOpts);
|
|
33
|
+
constructor({ sfuClient, dispatcher, state, connectionConfig, iceRestartDelay, }: SubscriberOpts);
|
|
29
34
|
/**
|
|
30
35
|
* Creates a new `RTCPeerConnection` instance with the given configuration.
|
|
31
36
|
*
|
|
@@ -52,7 +57,7 @@ export declare class Subscriber {
|
|
|
52
57
|
/**
|
|
53
58
|
* Restarts the ICE connection and renegotiates with the SFU.
|
|
54
59
|
*/
|
|
55
|
-
restartIce: () => void
|
|
60
|
+
restartIce: () => Promise<void>;
|
|
56
61
|
private handleOnTrack;
|
|
57
62
|
private onIceCandidate;
|
|
58
63
|
private negotiate;
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const version = "0.1.
|
|
1
|
+
export declare const version = "0.1.5";
|
package/package.json
CHANGED
package/src/Call.ts
CHANGED
|
@@ -72,6 +72,7 @@ import {
|
|
|
72
72
|
import {
|
|
73
73
|
BehaviorSubject,
|
|
74
74
|
debounce,
|
|
75
|
+
filter,
|
|
75
76
|
map,
|
|
76
77
|
pairwise,
|
|
77
78
|
Subject,
|
|
@@ -371,12 +372,15 @@ export class Call {
|
|
|
371
372
|
* Leave the call and stop the media streams that were published by the call.
|
|
372
373
|
*/
|
|
373
374
|
leave = async ({ reject = false }: CallLeaveOptions = {}) => {
|
|
374
|
-
// TODO: handle case when leave is called during JOINING
|
|
375
375
|
const callingState = this.state.callingState;
|
|
376
376
|
if (callingState === CallingState.LEFT) {
|
|
377
377
|
throw new Error('Cannot leave call that has already been left.');
|
|
378
378
|
}
|
|
379
379
|
|
|
380
|
+
if (callingState === CallingState.JOINING) {
|
|
381
|
+
await this.assertCallJoined();
|
|
382
|
+
}
|
|
383
|
+
|
|
380
384
|
if (this.ringing) {
|
|
381
385
|
// I'm the one who started the call, so I should cancel it.
|
|
382
386
|
const hasOtherParticipants = this.state.remoteParticipants.length > 0;
|
|
@@ -804,6 +808,7 @@ export class Call {
|
|
|
804
808
|
if (!this.publisher) {
|
|
805
809
|
this.publisher = new Publisher({
|
|
806
810
|
sfuClient,
|
|
811
|
+
dispatcher: this.dispatcher,
|
|
807
812
|
state: this.state,
|
|
808
813
|
connectionConfig,
|
|
809
814
|
isDtxEnabled,
|
|
@@ -848,6 +853,7 @@ export class Call {
|
|
|
848
853
|
subscriberSdp: sdp || '',
|
|
849
854
|
clientDetails: getClientDetails(),
|
|
850
855
|
migration,
|
|
856
|
+
fastReconnect: false,
|
|
851
857
|
});
|
|
852
858
|
});
|
|
853
859
|
|
|
@@ -928,14 +934,6 @@ export class Call {
|
|
|
928
934
|
});
|
|
929
935
|
};
|
|
930
936
|
|
|
931
|
-
private assertCallJoined = () => {
|
|
932
|
-
return new Promise<void>((resolve) => {
|
|
933
|
-
this.state.callingState$
|
|
934
|
-
.pipe(takeWhile((state) => state !== CallingState.JOINED, true))
|
|
935
|
-
.subscribe(() => resolve());
|
|
936
|
-
});
|
|
937
|
-
};
|
|
938
|
-
|
|
939
937
|
/**
|
|
940
938
|
* Starts publishing the given video stream to the call.
|
|
941
939
|
* The stream will be stopped if the user changes an input device, or if the user leaves the call.
|
|
@@ -1221,6 +1219,17 @@ export class Call {
|
|
|
1221
1219
|
return this.publisher?.updateVideoPublishQuality(enabledRids);
|
|
1222
1220
|
};
|
|
1223
1221
|
|
|
1222
|
+
private assertCallJoined = () => {
|
|
1223
|
+
return new Promise<void>((resolve) => {
|
|
1224
|
+
this.state.callingState$
|
|
1225
|
+
.pipe(
|
|
1226
|
+
takeWhile((state) => state !== CallingState.JOINED, true),
|
|
1227
|
+
filter((s) => s === CallingState.JOINED),
|
|
1228
|
+
)
|
|
1229
|
+
.subscribe(() => resolve());
|
|
1230
|
+
});
|
|
1231
|
+
};
|
|
1232
|
+
|
|
1224
1233
|
/**
|
|
1225
1234
|
* Sends a reaction to the other call participants.
|
|
1226
1235
|
*
|
package/src/StreamSfuClient.ts
CHANGED
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
} from './rtc';
|
|
17
17
|
import { JoinRequest, SfuRequest } from './gen/video/sfu/event/events';
|
|
18
18
|
import {
|
|
19
|
+
ICERestartRequest,
|
|
19
20
|
SendAnswerRequest,
|
|
20
21
|
SetPublisherRequest,
|
|
21
22
|
TrackSubscriptionDetails,
|
|
@@ -131,15 +132,14 @@ export class StreamSfuClient {
|
|
|
131
132
|
this.edgeName = sfuServer.edge_name;
|
|
132
133
|
this.token = token;
|
|
133
134
|
this.logger = getLogger(['sfu-client']);
|
|
134
|
-
const logger = this.logger;
|
|
135
135
|
const logInterceptor: RpcInterceptor = {
|
|
136
|
-
interceptUnary(
|
|
136
|
+
interceptUnary: (
|
|
137
137
|
next: NextUnaryFn,
|
|
138
138
|
method: MethodInfo,
|
|
139
139
|
input: object,
|
|
140
140
|
options: RpcOptions,
|
|
141
|
-
): UnaryCall {
|
|
142
|
-
logger('trace', `Calling SFU RPC method ${method.name}`, {
|
|
141
|
+
): UnaryCall => {
|
|
142
|
+
this.logger('trace', `Calling SFU RPC method ${method.name}`, {
|
|
143
143
|
input,
|
|
144
144
|
options,
|
|
145
145
|
});
|
|
@@ -190,6 +190,7 @@ export class StreamSfuClient {
|
|
|
190
190
|
code: number = 1000,
|
|
191
191
|
reason: string = 'Requested signal connection close',
|
|
192
192
|
) => {
|
|
193
|
+
this.logger('debug', 'Closing SFU WS connection', code, reason);
|
|
193
194
|
this.signalWs.close(code, reason);
|
|
194
195
|
|
|
195
196
|
this.unsubscribeIceTrickle();
|
|
@@ -241,6 +242,17 @@ export class StreamSfuClient {
|
|
|
241
242
|
);
|
|
242
243
|
};
|
|
243
244
|
|
|
245
|
+
iceRestart = async (data: Omit<ICERestartRequest, 'sessionId'>) => {
|
|
246
|
+
return retryable(
|
|
247
|
+
() =>
|
|
248
|
+
this.rpc.iceRestart({
|
|
249
|
+
...data,
|
|
250
|
+
sessionId: this.sessionId,
|
|
251
|
+
}),
|
|
252
|
+
this.logger,
|
|
253
|
+
);
|
|
254
|
+
};
|
|
255
|
+
|
|
244
256
|
updateMuteState = async (trackType: TrackType, muted: boolean) => {
|
|
245
257
|
return this.updateMuteStates({
|
|
246
258
|
muteStates: [
|
|
@@ -319,7 +331,6 @@ export class StreamSfuClient {
|
|
|
319
331
|
new Date().getTime() - this.lastMessageTimestamp.getTime();
|
|
320
332
|
|
|
321
333
|
if (timeSinceLastMessage > this.unhealthyTimeoutInMs) {
|
|
322
|
-
this.logger('debug', 'SFU connection unhealthy, closing');
|
|
323
334
|
this.close(
|
|
324
335
|
4001,
|
|
325
336
|
`SFU connection unhealthy. Didn't receive any healthcheck messages for ${this.unhealthyTimeoutInMs}ms`,
|
package/src/StreamVideoClient.ts
CHANGED
|
@@ -38,8 +38,7 @@ export class StreamVideoClient {
|
|
|
38
38
|
* A reactive store that exposes all the state variables in a reactive manner - you can subscribe to changes of the different state variables. Our library is built in a way that all state changes are exposed in this store, so all UI changes in your application should be handled by subscribing to these variables.
|
|
39
39
|
*/
|
|
40
40
|
readonly readOnlyStateStore: StreamVideoReadOnlyStateStore;
|
|
41
|
-
readonly
|
|
42
|
-
readonly token?: TokenOrProvider;
|
|
41
|
+
readonly logLevel: LogLevel = 'warn';
|
|
43
42
|
readonly logger: Logger;
|
|
44
43
|
|
|
45
44
|
protected readonly writeableStateStore: StreamVideoWriteableStateStore;
|
|
@@ -109,17 +108,20 @@ export class StreamVideoClient {
|
|
|
109
108
|
}.${sdkInfo.minor}.${sdkInfo.patch}`,
|
|
110
109
|
);
|
|
111
110
|
}
|
|
112
|
-
this.user = apiKeyOrArgs.user;
|
|
113
|
-
this.token = apiKeyOrArgs.token || apiKeyOrArgs.tokenProvider;
|
|
114
|
-
if (this.user) {
|
|
115
|
-
this.streamClient.startWaitingForConnection();
|
|
116
|
-
}
|
|
117
111
|
}
|
|
118
112
|
|
|
119
113
|
this.writeableStateStore = new StreamVideoWriteableStateStore();
|
|
120
114
|
this.readOnlyStateStore = new StreamVideoReadOnlyStateStore(
|
|
121
115
|
this.writeableStateStore,
|
|
122
116
|
);
|
|
117
|
+
|
|
118
|
+
if (typeof apiKeyOrArgs !== 'string') {
|
|
119
|
+
const user = apiKeyOrArgs.user;
|
|
120
|
+
const token = apiKeyOrArgs.token || apiKeyOrArgs.tokenProvider;
|
|
121
|
+
if (user) {
|
|
122
|
+
this.connectUser(user, token);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
123
125
|
}
|
|
124
126
|
|
|
125
127
|
/**
|
|
@@ -131,30 +133,30 @@ export class StreamVideoClient {
|
|
|
131
133
|
* @param token a token or a function that returns a token.
|
|
132
134
|
*/
|
|
133
135
|
async connectUser(
|
|
134
|
-
user
|
|
136
|
+
user: User,
|
|
135
137
|
token?: TokenOrProvider,
|
|
136
138
|
): Promise<void | ConnectedEvent> {
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
throw new Error('Connect user is called without user');
|
|
141
|
-
}
|
|
142
|
-
if (userToConnect.type === 'anonymous') {
|
|
143
|
-
userToConnect.id = '!anon';
|
|
144
|
-
return this.connectAnonymousUser(userToConnect as UserWithId, tokenToUse);
|
|
139
|
+
if (user.type === 'anonymous') {
|
|
140
|
+
user.id = '!anon';
|
|
141
|
+
return this.connectAnonymousUser(user as UserWithId, token);
|
|
145
142
|
}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
user: {
|
|
149
|
-
...userToConnect,
|
|
150
|
-
role: 'guest',
|
|
151
|
-
},
|
|
152
|
-
});
|
|
153
|
-
return this.connectUser(response.user, response.access_token);
|
|
154
|
-
}
|
|
155
|
-
const connectUser = () => {
|
|
156
|
-
return this.streamClient.connectUser(userToConnect, tokenToUse);
|
|
143
|
+
let connectUser = () => {
|
|
144
|
+
return this.streamClient.connectUser(user, token);
|
|
157
145
|
};
|
|
146
|
+
if (user.type === 'guest') {
|
|
147
|
+
connectUser = async () => {
|
|
148
|
+
const response = await this.createGuestUser({
|
|
149
|
+
user: {
|
|
150
|
+
...user,
|
|
151
|
+
role: 'guest',
|
|
152
|
+
},
|
|
153
|
+
});
|
|
154
|
+
return this.streamClient.connectUser(
|
|
155
|
+
response.user,
|
|
156
|
+
response.access_token,
|
|
157
|
+
);
|
|
158
|
+
};
|
|
159
|
+
}
|
|
158
160
|
this.connectionPromise = this.disconnectionPromise
|
|
159
161
|
? this.disconnectionPromise.then(() => connectUser())
|
|
160
162
|
: connectUser();
|
|
@@ -199,7 +201,7 @@ export class StreamVideoClient {
|
|
|
199
201
|
this.on('call.created', (event) => {
|
|
200
202
|
if (event.type !== 'call.created') return;
|
|
201
203
|
const { call, members } = event;
|
|
202
|
-
if (
|
|
204
|
+
if (user.id === call.created_by.id) {
|
|
203
205
|
this.logger(
|
|
204
206
|
'warn',
|
|
205
207
|
'Received `call.created` sent by the current user',
|
|
@@ -225,7 +227,7 @@ export class StreamVideoClient {
|
|
|
225
227
|
this.on('call.ring', async (event) => {
|
|
226
228
|
if (event.type !== 'call.ring') return;
|
|
227
229
|
const { call, members } = event;
|
|
228
|
-
if (
|
|
230
|
+
if (user.id === call.created_by.id) {
|
|
229
231
|
this.logger(
|
|
230
232
|
'debug',
|
|
231
233
|
'Received `call.ring` sent by the current user so ignoring the event',
|
|
@@ -267,7 +269,7 @@ export class StreamVideoClient {
|
|
|
267
269
|
* https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent
|
|
268
270
|
*/
|
|
269
271
|
disconnectUser = async (timeout?: number) => {
|
|
270
|
-
if (!this.streamClient.user) {
|
|
272
|
+
if (!this.streamClient.user && !this.connectionPromise) {
|
|
271
273
|
return;
|
|
272
274
|
}
|
|
273
275
|
const disconnectUser = () => this.streamClient.disconnectUser(timeout);
|