livekit-client 0.18.4-RC7 → 0.18.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.
Files changed (127) hide show
  1. package/README.md +2 -5
  2. package/dist/api/RequestQueue.d.ts +13 -12
  3. package/dist/api/RequestQueue.d.ts.map +1 -0
  4. package/dist/api/SignalClient.d.ts +67 -66
  5. package/dist/api/SignalClient.d.ts.map +1 -0
  6. package/dist/connect.d.ts +24 -23
  7. package/dist/connect.d.ts.map +1 -0
  8. package/dist/index.d.ts +27 -26
  9. package/dist/index.d.ts.map +1 -0
  10. package/dist/livekit-client.esm.mjs +638 -517
  11. package/dist/livekit-client.esm.mjs.map +1 -1
  12. package/dist/livekit-client.umd.js +1 -1
  13. package/dist/livekit-client.umd.js.map +1 -1
  14. package/dist/logger.d.ts +26 -25
  15. package/dist/logger.d.ts.map +1 -0
  16. package/dist/options.d.ts +128 -127
  17. package/dist/options.d.ts.map +1 -0
  18. package/dist/proto/google/protobuf/timestamp.d.ts +133 -132
  19. package/dist/proto/google/protobuf/timestamp.d.ts.map +1 -0
  20. package/dist/proto/livekit_models.d.ts +876 -875
  21. package/dist/proto/livekit_models.d.ts.map +1 -0
  22. package/dist/proto/livekit_rtc.d.ts +3904 -3903
  23. package/dist/proto/livekit_rtc.d.ts.map +1 -0
  24. package/dist/room/DeviceManager.d.ts +8 -7
  25. package/dist/room/DeviceManager.d.ts.map +1 -0
  26. package/dist/room/PCTransport.d.ts +16 -15
  27. package/dist/room/PCTransport.d.ts.map +1 -0
  28. package/dist/room/RTCEngine.d.ts +67 -66
  29. package/dist/room/RTCEngine.d.ts.map +1 -0
  30. package/dist/room/Room.d.ts +166 -165
  31. package/dist/room/Room.d.ts.map +1 -0
  32. package/dist/room/errors.d.ts +29 -28
  33. package/dist/room/errors.d.ts.map +1 -0
  34. package/dist/room/events.d.ts +391 -390
  35. package/dist/room/events.d.ts.map +1 -0
  36. package/dist/room/participant/LocalParticipant.d.ts +126 -125
  37. package/dist/room/participant/LocalParticipant.d.ts.map +1 -0
  38. package/dist/room/participant/Participant.d.ts +94 -93
  39. package/dist/room/participant/Participant.d.ts.map +1 -0
  40. package/dist/room/participant/ParticipantTrackPermission.d.ts +26 -25
  41. package/dist/room/participant/ParticipantTrackPermission.d.ts.map +1 -0
  42. package/dist/room/participant/RemoteParticipant.d.ts +40 -39
  43. package/dist/room/participant/RemoteParticipant.d.ts.map +1 -0
  44. package/dist/room/participant/publishUtils.d.ts +18 -17
  45. package/dist/room/participant/publishUtils.d.ts.map +1 -0
  46. package/dist/room/stats.d.ts +66 -65
  47. package/dist/room/stats.d.ts.map +1 -0
  48. package/dist/room/track/LocalAudioTrack.d.ts +20 -19
  49. package/dist/room/track/LocalAudioTrack.d.ts.map +1 -0
  50. package/dist/room/track/LocalTrack.d.ts +28 -27
  51. package/dist/room/track/LocalTrack.d.ts.map +1 -0
  52. package/dist/room/track/LocalTrackPublication.d.ts +38 -37
  53. package/dist/room/track/LocalTrackPublication.d.ts.map +1 -0
  54. package/dist/room/track/LocalVideoTrack.d.ts +31 -30
  55. package/dist/room/track/LocalVideoTrack.d.ts.map +1 -0
  56. package/dist/room/track/RemoteAudioTrack.d.ts +20 -19
  57. package/dist/room/track/RemoteAudioTrack.d.ts.map +1 -0
  58. package/dist/room/track/RemoteTrack.d.ts +16 -15
  59. package/dist/room/track/RemoteTrack.d.ts.map +1 -0
  60. package/dist/room/track/RemoteTrackPublication.d.ts +51 -50
  61. package/dist/room/track/RemoteTrackPublication.d.ts.map +1 -0
  62. package/dist/room/track/RemoteVideoTrack.d.ts +29 -27
  63. package/dist/room/track/RemoteVideoTrack.d.ts.map +1 -0
  64. package/dist/room/track/Track.d.ts +105 -100
  65. package/dist/room/track/Track.d.ts.map +1 -0
  66. package/dist/room/track/TrackPublication.d.ts +50 -49
  67. package/dist/room/track/TrackPublication.d.ts.map +1 -0
  68. package/dist/room/track/create.d.ts +24 -23
  69. package/dist/room/track/create.d.ts.map +1 -0
  70. package/dist/room/track/defaults.d.ts +5 -4
  71. package/dist/room/track/defaults.d.ts.map +1 -0
  72. package/dist/room/track/options.d.ts +232 -222
  73. package/dist/room/track/options.d.ts.map +1 -0
  74. package/dist/room/track/types.d.ts +19 -18
  75. package/dist/room/track/types.d.ts.map +1 -0
  76. package/dist/room/track/utils.d.ts +14 -13
  77. package/dist/room/track/utils.d.ts.map +1 -0
  78. package/dist/room/utils.d.ts +17 -15
  79. package/dist/room/utils.d.ts.map +1 -0
  80. package/dist/test/mocks.d.ts +12 -11
  81. package/dist/test/mocks.d.ts.map +1 -0
  82. package/dist/version.d.ts +3 -2
  83. package/dist/version.d.ts.map +1 -0
  84. package/package.json +4 -5
  85. package/src/api/RequestQueue.ts +53 -0
  86. package/src/api/SignalClient.ts +497 -0
  87. package/src/connect.ts +98 -0
  88. package/src/index.ts +49 -0
  89. package/src/logger.ts +56 -0
  90. package/src/options.ts +156 -0
  91. package/src/proto/google/protobuf/timestamp.ts +216 -0
  92. package/src/proto/livekit_models.ts +2456 -0
  93. package/src/proto/livekit_rtc.ts +2859 -0
  94. package/src/room/DeviceManager.ts +80 -0
  95. package/src/room/PCTransport.ts +88 -0
  96. package/src/room/RTCEngine.ts +695 -0
  97. package/src/room/Room.ts +970 -0
  98. package/src/room/errors.ts +65 -0
  99. package/src/room/events.ts +438 -0
  100. package/src/room/participant/LocalParticipant.ts +779 -0
  101. package/src/room/participant/Participant.ts +287 -0
  102. package/src/room/participant/ParticipantTrackPermission.ts +42 -0
  103. package/src/room/participant/RemoteParticipant.ts +263 -0
  104. package/src/room/participant/publishUtils.test.ts +144 -0
  105. package/src/room/participant/publishUtils.ts +258 -0
  106. package/src/room/stats.ts +134 -0
  107. package/src/room/track/LocalAudioTrack.ts +134 -0
  108. package/src/room/track/LocalTrack.ts +229 -0
  109. package/src/room/track/LocalTrackPublication.ts +87 -0
  110. package/src/room/track/LocalVideoTrack.test.ts +72 -0
  111. package/src/room/track/LocalVideoTrack.ts +295 -0
  112. package/src/room/track/RemoteAudioTrack.ts +86 -0
  113. package/src/room/track/RemoteTrack.ts +62 -0
  114. package/src/room/track/RemoteTrackPublication.ts +207 -0
  115. package/src/room/track/RemoteVideoTrack.ts +249 -0
  116. package/src/room/track/Track.ts +365 -0
  117. package/src/room/track/TrackPublication.ts +120 -0
  118. package/src/room/track/create.ts +122 -0
  119. package/src/room/track/defaults.ts +26 -0
  120. package/src/room/track/options.ts +292 -0
  121. package/src/room/track/types.ts +20 -0
  122. package/src/room/track/utils.test.ts +110 -0
  123. package/src/room/track/utils.ts +113 -0
  124. package/src/room/utils.ts +115 -0
  125. package/src/test/mocks.ts +17 -0
  126. package/src/version.ts +2 -0
  127. package/CHANGELOG.md +0 -5
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "livekit-client",
3
- "version": "0.18.4-RC7",
3
+ "version": "0.18.5",
4
4
  "description": "JavaScript/TypeScript client SDK for LiveKit",
5
5
  "main": "./dist/livekit-client.umd.js",
6
6
  "unpkg": "./dist/livekit-client.umd.js",
@@ -12,9 +12,9 @@
12
12
  }
13
13
  },
14
14
  "files": [
15
- "dist"
15
+ "dist",
16
+ "src"
16
17
  ],
17
- "source": "src/index.ts",
18
18
  "types": "dist/index.d.ts",
19
19
  "repository": "git@github.com:livekit/client-sdk-js.git",
20
20
  "author": "David Zhao <david@davidzhao.com>",
@@ -45,7 +45,6 @@
45
45
  "@rollup/plugin-babel": "^5.3.1",
46
46
  "@rollup/plugin-commonjs": "^21.0.3",
47
47
  "@rollup/plugin-node-resolve": "^13.1.3",
48
- "@rollup/plugin-typescript": "^8.3.1",
49
48
  "@types/jest": "^27.0.3",
50
49
  "@types/ws": "^7.4.0",
51
50
  "@typescript-eslint/eslint-plugin": "^4.31.2",
@@ -61,8 +60,8 @@
61
60
  "rollup-plugin-re": "^1.0.7",
62
61
  "rollup-plugin-serve": "^1.1.0",
63
62
  "rollup-plugin-terser": "^7.0.2",
63
+ "rollup-plugin-typescript2": "^0.31.2",
64
64
  "ts-jest": "^27.0.7",
65
- "ts-loader": "^8.1.0",
66
65
  "ts-proto": "^1.110.1",
67
66
  "typedoc": "^0.22.13",
68
67
  "typedoc-plugin-no-inherit": "1.3.1",
@@ -0,0 +1,53 @@
1
+ import log from '../logger';
2
+
3
+ export default class Queue {
4
+ private queue: Array<() => void>;
5
+
6
+ private running: boolean;
7
+
8
+ constructor() {
9
+ this.queue = [];
10
+ this.running = false;
11
+ }
12
+
13
+ enqueue(cb: () => void) {
14
+ log.trace('enqueuing request to fire later');
15
+ this.queue.push(cb);
16
+ }
17
+
18
+ dequeue() {
19
+ const evt = this.queue.shift();
20
+ if (evt) evt();
21
+ log.trace('firing request from queue');
22
+ }
23
+
24
+ async run() {
25
+ if (this.running) return;
26
+ log.trace('start queue');
27
+ this.running = true;
28
+ while (this.running && this.queue.length > 0) {
29
+ this.dequeue();
30
+ }
31
+ this.running = false;
32
+ log.trace('queue finished');
33
+ }
34
+
35
+ pause() {
36
+ log.trace('pausing queue');
37
+ this.running = false;
38
+ }
39
+
40
+ reset() {
41
+ log.trace('resetting queue');
42
+ this.running = false;
43
+ this.queue = [];
44
+ }
45
+
46
+ isRunning() {
47
+ return this.running;
48
+ }
49
+
50
+ isEmpty() {
51
+ return this.queue.length === 0;
52
+ }
53
+ }
@@ -0,0 +1,497 @@
1
+ import log from '../logger';
2
+ import {
3
+ ClientInfo,
4
+ ParticipantInfo,
5
+ Room,
6
+ SpeakerInfo,
7
+ VideoLayer,
8
+ } from '../proto/livekit_models';
9
+ import {
10
+ AddTrackRequest,
11
+ ConnectionQualityUpdate,
12
+ JoinResponse,
13
+ LeaveRequest,
14
+ SessionDescription,
15
+ SignalRequest,
16
+ SignalResponse,
17
+ SignalTarget,
18
+ SimulateScenario,
19
+ StreamStateUpdate,
20
+ SubscribedQualityUpdate,
21
+ SubscriptionPermissionUpdate,
22
+ SyncState,
23
+ TrackPermission,
24
+ TrackPublishedResponse,
25
+ TrackUnpublishedResponse,
26
+ UpdateSubscription,
27
+ UpdateTrackSettings,
28
+ } from '../proto/livekit_rtc';
29
+ import { ConnectionError } from '../room/errors';
30
+ import { getClientInfo, sleep } from '../room/utils';
31
+ import Queue from './RequestQueue';
32
+ import 'webrtc-adapter';
33
+
34
+ // internal options
35
+ interface ConnectOpts {
36
+ autoSubscribe?: boolean;
37
+ /** internal */
38
+ reconnect?: boolean;
39
+
40
+ publishOnly?: string;
41
+
42
+ adaptiveStream?: boolean;
43
+ }
44
+
45
+ // public options
46
+ export interface SignalOptions {
47
+ autoSubscribe?: boolean;
48
+ publishOnly?: string;
49
+ adaptiveStream?: boolean;
50
+ }
51
+
52
+ const passThroughQueueSignals: Array<keyof SignalRequest> = [
53
+ 'syncState',
54
+ 'trickle',
55
+ 'offer',
56
+ 'answer',
57
+ 'simulate',
58
+ 'leave',
59
+ ];
60
+
61
+ function canPassThroughQueue(req: SignalRequest): boolean {
62
+ const canPass =
63
+ Object.keys(req).find((key) => passThroughQueueSignals.includes(key as keyof SignalRequest)) !==
64
+ undefined;
65
+ log.trace('request allowed to bypass queue:', { canPass, req });
66
+ return canPass;
67
+ }
68
+
69
+ /** @internal */
70
+ export class SignalClient {
71
+ isConnected: boolean;
72
+
73
+ isReconnecting: boolean;
74
+
75
+ requestQueue: Queue;
76
+
77
+ useJSON: boolean;
78
+
79
+ /** simulate signaling latency by delaying messages */
80
+ signalLatency?: number;
81
+
82
+ onClose?: (reason: string) => void;
83
+
84
+ onAnswer?: (sd: RTCSessionDescriptionInit) => void;
85
+
86
+ onOffer?: (sd: RTCSessionDescriptionInit) => void;
87
+
88
+ // when a new ICE candidate is made available
89
+ onTrickle?: (sd: RTCIceCandidateInit, target: SignalTarget) => void;
90
+
91
+ onParticipantUpdate?: (updates: ParticipantInfo[]) => void;
92
+
93
+ onLocalTrackPublished?: (res: TrackPublishedResponse) => void;
94
+
95
+ onNegotiateRequested?: () => void;
96
+
97
+ onSpeakersChanged?: (res: SpeakerInfo[]) => void;
98
+
99
+ onRemoteMuteChanged?: (trackSid: string, muted: boolean) => void;
100
+
101
+ onRoomUpdate?: (room: Room) => void;
102
+
103
+ onConnectionQuality?: (update: ConnectionQualityUpdate) => void;
104
+
105
+ onStreamStateUpdate?: (update: StreamStateUpdate) => void;
106
+
107
+ onSubscribedQualityUpdate?: (update: SubscribedQualityUpdate) => void;
108
+
109
+ onSubscriptionPermissionUpdate?: (update: SubscriptionPermissionUpdate) => void;
110
+
111
+ onLocalTrackUnpublished?: (res: TrackUnpublishedResponse) => void;
112
+
113
+ onTokenRefresh?: (token: string) => void;
114
+
115
+ onLeave?: (leave: LeaveRequest) => void;
116
+
117
+ ws?: WebSocket;
118
+
119
+ constructor(useJSON: boolean = false) {
120
+ this.isConnected = false;
121
+ this.isReconnecting = false;
122
+ this.useJSON = useJSON;
123
+ this.requestQueue = new Queue();
124
+ }
125
+
126
+ async join(url: string, token: string, opts?: SignalOptions): Promise<JoinResponse> {
127
+ // during a full reconnect, we'd want to start the sequence even if currently
128
+ // connected
129
+ this.isConnected = false;
130
+ const res = await this.connect(url, token, {
131
+ autoSubscribe: opts?.autoSubscribe,
132
+ publishOnly: opts?.publishOnly,
133
+ adaptiveStream: opts?.adaptiveStream,
134
+ });
135
+ return res as JoinResponse;
136
+ }
137
+
138
+ async reconnect(url: string, token: string): Promise<void> {
139
+ this.isReconnecting = true;
140
+ await this.connect(url, token, {
141
+ reconnect: true,
142
+ });
143
+ }
144
+
145
+ connect(url: string, token: string, opts: ConnectOpts): Promise<JoinResponse | void> {
146
+ if (url.startsWith('http')) {
147
+ url = url.replace('http', 'ws');
148
+ }
149
+ // strip trailing slash
150
+ url = url.replace(/\/$/, '');
151
+ url += '/rtc';
152
+
153
+ const clientInfo = getClientInfo();
154
+ const params = createConnectionParams(token, clientInfo, opts);
155
+
156
+ return new Promise<JoinResponse | void>((resolve, reject) => {
157
+ log.debug(`connecting to ${url + params}`);
158
+ this.ws = undefined;
159
+ const ws = new WebSocket(url + params);
160
+ ws.binaryType = 'arraybuffer';
161
+
162
+ ws.onerror = async (ev: Event) => {
163
+ if (!this.ws) {
164
+ try {
165
+ const resp = await fetch(`http${url.substring(2)}/validate${params}`);
166
+ if (!resp.ok) {
167
+ const msg = await resp.text();
168
+ reject(new ConnectionError(msg));
169
+ } else {
170
+ reject(new ConnectionError('Internal error'));
171
+ }
172
+ } catch (e) {
173
+ reject(new ConnectionError('server was not reachable'));
174
+ }
175
+ return;
176
+ }
177
+ // other errors, handle
178
+ this.handleWSError(ev);
179
+ };
180
+
181
+ ws.onopen = () => {
182
+ this.ws = ws;
183
+ if (opts.reconnect) {
184
+ // upon reconnection, there will not be additional handshake
185
+ this.isConnected = true;
186
+ resolve();
187
+ }
188
+ };
189
+
190
+ ws.onmessage = async (ev: MessageEvent) => {
191
+ // not considered connected until JoinResponse is received
192
+ let msg: SignalResponse;
193
+ if (typeof ev.data === 'string') {
194
+ const json = JSON.parse(ev.data);
195
+ msg = SignalResponse.fromJSON(json);
196
+ } else if (ev.data instanceof ArrayBuffer) {
197
+ msg = SignalResponse.decode(new Uint8Array(ev.data));
198
+ } else {
199
+ log.error(`could not decode websocket message: ${typeof ev.data}`);
200
+ return;
201
+ }
202
+
203
+ if (!this.isConnected) {
204
+ // handle join message only
205
+ if (msg.join) {
206
+ this.isConnected = true;
207
+ resolve(msg.join);
208
+ } else {
209
+ reject(new ConnectionError('did not receive join response'));
210
+ }
211
+ return;
212
+ }
213
+
214
+ if (this.signalLatency) {
215
+ await sleep(this.signalLatency);
216
+ }
217
+ this.handleSignalResponse(msg);
218
+ };
219
+
220
+ ws.onclose = (ev: CloseEvent) => {
221
+ if (!this.isConnected || this.ws !== ws) return;
222
+
223
+ log.debug(`websocket connection closed: ${ev.reason}`);
224
+ this.isConnected = false;
225
+ if (this.onClose) this.onClose(ev.reason);
226
+ if (this.ws === ws) {
227
+ this.ws = undefined;
228
+ }
229
+ };
230
+ });
231
+ }
232
+
233
+ close() {
234
+ this.isConnected = false;
235
+ if (this.ws) this.ws.onclose = null;
236
+ this.ws?.close();
237
+ this.ws = undefined;
238
+ }
239
+
240
+ // initial offer after joining
241
+ sendOffer(offer: RTCSessionDescriptionInit) {
242
+ log.debug('sending offer', offer);
243
+ this.sendRequest({
244
+ offer: toProtoSessionDescription(offer),
245
+ });
246
+ }
247
+
248
+ // answer a server-initiated offer
249
+ sendAnswer(answer: RTCSessionDescriptionInit) {
250
+ log.debug('sending answer');
251
+ this.sendRequest({
252
+ answer: toProtoSessionDescription(answer),
253
+ });
254
+ }
255
+
256
+ sendIceCandidate(candidate: RTCIceCandidateInit, target: SignalTarget) {
257
+ log.trace('sending ice candidate', candidate);
258
+ this.sendRequest({
259
+ trickle: {
260
+ candidateInit: JSON.stringify(candidate),
261
+ target,
262
+ },
263
+ });
264
+ }
265
+
266
+ sendMuteTrack(trackSid: string, muted: boolean) {
267
+ this.sendRequest({
268
+ mute: {
269
+ sid: trackSid,
270
+ muted,
271
+ },
272
+ });
273
+ }
274
+
275
+ sendAddTrack(req: AddTrackRequest): void {
276
+ this.sendRequest({
277
+ addTrack: AddTrackRequest.fromPartial(req),
278
+ });
279
+ }
280
+
281
+ sendUpdateTrackSettings(settings: UpdateTrackSettings) {
282
+ this.sendRequest({ trackSetting: settings });
283
+ }
284
+
285
+ sendUpdateSubscription(sub: UpdateSubscription) {
286
+ this.sendRequest({ subscription: sub });
287
+ }
288
+
289
+ sendSyncState(sync: SyncState) {
290
+ this.sendRequest({ syncState: sync });
291
+ }
292
+
293
+ sendUpdateVideoLayers(trackSid: string, layers: VideoLayer[]) {
294
+ this.sendRequest({
295
+ updateLayers: {
296
+ trackSid,
297
+ layers,
298
+ },
299
+ });
300
+ }
301
+
302
+ sendUpdateSubscriptionPermissions(allParticipants: boolean, trackPermissions: TrackPermission[]) {
303
+ this.sendRequest({
304
+ subscriptionPermission: {
305
+ allParticipants,
306
+ trackPermissions,
307
+ },
308
+ });
309
+ }
310
+
311
+ sendSimulateScenario(scenario: SimulateScenario) {
312
+ this.sendRequest({
313
+ simulate: scenario,
314
+ });
315
+ }
316
+
317
+ sendLeave() {
318
+ this.sendRequest(SignalRequest.fromPartial({ leave: {} }));
319
+ }
320
+
321
+ async sendRequest(req: SignalRequest, fromQueue: boolean = false) {
322
+ // capture all requests while reconnecting and put them in a queue.
323
+ // keep order by queueing up new events as long as the queue is not empty
324
+ // unless the request originates from the queue, then don't enqueue again
325
+ const canQueue = !fromQueue && !canPassThroughQueue(req);
326
+ if (canQueue && (this.isReconnecting || !this.requestQueue.isEmpty())) {
327
+ this.requestQueue.enqueue(() => this.sendRequest(req, true));
328
+ return;
329
+ }
330
+ if (this.signalLatency) {
331
+ await sleep(this.signalLatency);
332
+ }
333
+ if (!this.ws) {
334
+ log.error('cannot send signal request before connected');
335
+ return;
336
+ }
337
+
338
+ try {
339
+ if (this.useJSON) {
340
+ this.ws.send(JSON.stringify(SignalRequest.toJSON(req)));
341
+ } else {
342
+ this.ws.send(SignalRequest.encode(req).finish());
343
+ }
344
+ } catch (e) {
345
+ log.error('error sending signal message', { error: e });
346
+ }
347
+ }
348
+
349
+ private handleSignalResponse(msg: SignalResponse) {
350
+ if (msg.answer) {
351
+ const sd = fromProtoSessionDescription(msg.answer);
352
+ if (this.onAnswer) {
353
+ this.onAnswer(sd);
354
+ }
355
+ } else if (msg.offer) {
356
+ const sd = fromProtoSessionDescription(msg.offer);
357
+ if (this.onOffer) {
358
+ this.onOffer(sd);
359
+ }
360
+ } else if (msg.trickle) {
361
+ const candidate: RTCIceCandidateInit = JSON.parse(msg.trickle.candidateInit);
362
+ if (this.onTrickle) {
363
+ this.onTrickle(candidate, msg.trickle.target);
364
+ }
365
+ } else if (msg.update) {
366
+ if (this.onParticipantUpdate) {
367
+ this.onParticipantUpdate(msg.update.participants);
368
+ }
369
+ } else if (msg.trackPublished) {
370
+ if (this.onLocalTrackPublished) {
371
+ this.onLocalTrackPublished(msg.trackPublished);
372
+ }
373
+ } else if (msg.speakersChanged) {
374
+ if (this.onSpeakersChanged) {
375
+ this.onSpeakersChanged(msg.speakersChanged.speakers);
376
+ }
377
+ } else if (msg.leave) {
378
+ if (this.onLeave) {
379
+ this.onLeave(msg.leave);
380
+ }
381
+ } else if (msg.mute) {
382
+ if (this.onRemoteMuteChanged) {
383
+ this.onRemoteMuteChanged(msg.mute.sid, msg.mute.muted);
384
+ }
385
+ } else if (msg.roomUpdate) {
386
+ if (this.onRoomUpdate) {
387
+ this.onRoomUpdate(msg.roomUpdate.room!);
388
+ }
389
+ } else if (msg.connectionQuality) {
390
+ if (this.onConnectionQuality) {
391
+ this.onConnectionQuality(msg.connectionQuality);
392
+ }
393
+ } else if (msg.streamStateUpdate) {
394
+ if (this.onStreamStateUpdate) {
395
+ this.onStreamStateUpdate(msg.streamStateUpdate);
396
+ }
397
+ } else if (msg.subscribedQualityUpdate) {
398
+ if (this.onSubscribedQualityUpdate) {
399
+ this.onSubscribedQualityUpdate(msg.subscribedQualityUpdate);
400
+ }
401
+ } else if (msg.subscriptionPermissionUpdate) {
402
+ if (this.onSubscriptionPermissionUpdate) {
403
+ this.onSubscriptionPermissionUpdate(msg.subscriptionPermissionUpdate);
404
+ }
405
+ } else if (msg.refreshToken) {
406
+ if (this.onTokenRefresh) {
407
+ this.onTokenRefresh(msg.refreshToken);
408
+ }
409
+ } else if (msg.trackUnpublished) {
410
+ if (this.onLocalTrackUnpublished) {
411
+ this.onLocalTrackUnpublished(msg.trackUnpublished);
412
+ }
413
+ } else {
414
+ log.debug('unsupported message', msg);
415
+ }
416
+ }
417
+
418
+ setReconnected() {
419
+ this.isReconnecting = false;
420
+ this.requestQueue.run();
421
+ }
422
+
423
+ private handleWSError(ev: Event) {
424
+ log.error('websocket error', ev);
425
+ }
426
+ }
427
+
428
+ function fromProtoSessionDescription(sd: SessionDescription): RTCSessionDescriptionInit {
429
+ const rsd: RTCSessionDescriptionInit = {
430
+ type: 'offer',
431
+ sdp: sd.sdp,
432
+ };
433
+ switch (sd.type) {
434
+ case 'answer':
435
+ case 'offer':
436
+ case 'pranswer':
437
+ case 'rollback':
438
+ rsd.type = sd.type;
439
+ break;
440
+ default:
441
+ break;
442
+ }
443
+ return rsd;
444
+ }
445
+
446
+ export function toProtoSessionDescription(
447
+ rsd: RTCSessionDescription | RTCSessionDescriptionInit,
448
+ ): SessionDescription {
449
+ const sd: SessionDescription = {
450
+ sdp: rsd.sdp!,
451
+ type: rsd.type!,
452
+ };
453
+ return sd;
454
+ }
455
+
456
+ function createConnectionParams(token: string, info: ClientInfo, opts?: ConnectOpts): string {
457
+ const params = new URLSearchParams();
458
+ params.set('access_token', token);
459
+
460
+ // opts
461
+ if (opts?.reconnect) {
462
+ params.set('reconnect', '1');
463
+ }
464
+ if (opts?.autoSubscribe !== undefined) {
465
+ params.set('auto_subscribe', opts.autoSubscribe ? '1' : '0');
466
+ }
467
+
468
+ // ClientInfo
469
+ params.set('sdk', 'js');
470
+ params.set('version', info.version);
471
+ params.set('protocol', info.protocol.toString());
472
+ if (info.deviceModel) {
473
+ params.set('device_model', info.deviceModel);
474
+ }
475
+ if (info.os) {
476
+ params.set('os', info.os);
477
+ }
478
+ if (info.osVersion) {
479
+ params.set('os_version', info.osVersion);
480
+ }
481
+ if (info.browser) {
482
+ params.set('browser', info.browser);
483
+ }
484
+ if (info.browserVersion) {
485
+ params.set('browser_version', info.browserVersion);
486
+ }
487
+
488
+ if (opts?.publishOnly !== undefined) {
489
+ params.set('publish', opts.publishOnly);
490
+ }
491
+
492
+ if (opts?.adaptiveStream) {
493
+ params.set('adaptive_stream', '1');
494
+ }
495
+
496
+ return `?${params.toString()}`;
497
+ }
package/src/connect.ts ADDED
@@ -0,0 +1,98 @@
1
+ import log, { LogLevel, setLogLevel } from './logger';
2
+ import { ConnectOptions } from './options';
3
+ import { MediaDeviceFailure } from './room/errors';
4
+ import { RoomEvent } from './room/events';
5
+ import Room from './room/Room';
6
+
7
+ export { version } from './version';
8
+
9
+ /**
10
+ * @deprecated Use room.connect() instead
11
+ *
12
+ * Connects to a LiveKit room, shorthand for `new Room()` and [[Room.connect]]
13
+ *
14
+ * ```typescript
15
+ * connect('wss://myhost.livekit.io', token, {
16
+ * // publish audio and video tracks on joining
17
+ * audio: true,
18
+ * video: true,
19
+ * captureDefaults: {
20
+ * facingMode: 'user',
21
+ * },
22
+ * })
23
+ * ```
24
+ * @param url URL to LiveKit server
25
+ * @param token AccessToken, a JWT token that includes authentication and room details
26
+ * @param options
27
+ */
28
+ export async function connect(url: string, token: string, options?: ConnectOptions): Promise<Room> {
29
+ options ??= {};
30
+ if (options.adaptiveStream === undefined) {
31
+ options.adaptiveStream = options.autoManageVideo === true ? {} : undefined;
32
+ }
33
+ setLogLevel(options.logLevel ?? LogLevel.warn);
34
+
35
+ const config: RTCConfiguration = options.rtcConfig ?? {};
36
+ if (options.iceServers) {
37
+ config.iceServers = options.iceServers;
38
+ }
39
+
40
+ const room = new Room(options);
41
+
42
+ // connect to room
43
+ await room.connect(url, token, options);
44
+
45
+ const publishAudio: boolean = options.audio ?? false;
46
+ const publishVideo: boolean = options.video ?? false;
47
+
48
+ if (publishAudio || publishVideo) {
49
+ setTimeout(async () => {
50
+ // if publishing both
51
+ let err: any;
52
+ if (publishAudio && publishVideo) {
53
+ try {
54
+ await room.localParticipant.enableCameraAndMicrophone();
55
+ } catch (e) {
56
+ const errKind = MediaDeviceFailure.getFailure(e);
57
+ log.warn('received error while creating media', { error: errKind });
58
+ if (e instanceof Error) {
59
+ log.warn(e.message);
60
+ }
61
+
62
+ // when it's a device issue, try to publish the other kind
63
+ if (
64
+ errKind === MediaDeviceFailure.NotFound ||
65
+ errKind === MediaDeviceFailure.DeviceInUse
66
+ ) {
67
+ try {
68
+ await room.localParticipant.setMicrophoneEnabled(true);
69
+ } catch (audioErr) {
70
+ err = audioErr;
71
+ }
72
+ } else {
73
+ err = e;
74
+ }
75
+ }
76
+ } else if (publishAudio) {
77
+ try {
78
+ await room.localParticipant.setMicrophoneEnabled(true);
79
+ } catch (e) {
80
+ err = e;
81
+ }
82
+ } else if (publishVideo) {
83
+ try {
84
+ await room.localParticipant.setCameraEnabled(true);
85
+ } catch (e) {
86
+ err = e;
87
+ }
88
+ }
89
+
90
+ if (err) {
91
+ room.emit(RoomEvent.MediaDevicesError, err);
92
+ log.error('could not create media', err);
93
+ }
94
+ });
95
+ }
96
+
97
+ return room;
98
+ }