livekit-client 0.15.0 → 0.15.4
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/.gitmodules +3 -0
- package/README.md +21 -4
- package/dist/api/SignalClient.d.ts +11 -2
- package/dist/api/SignalClient.js +92 -25
- package/dist/api/SignalClient.js.map +1 -1
- package/dist/connect.js +3 -0
- package/dist/connect.js.map +1 -1
- package/dist/index.d.ts +5 -2
- package/dist/index.js +6 -3
- package/dist/index.js.map +1 -1
- package/dist/logger.js +1 -0
- package/dist/logger.js.map +1 -1
- package/dist/options.d.ts +28 -14
- package/dist/proto/livekit_models.d.ts +48 -0
- package/dist/proto/livekit_models.js +367 -5
- package/dist/proto/livekit_models.js.map +1 -1
- package/dist/proto/livekit_rtc.d.ts +100 -1
- package/dist/proto/livekit_rtc.js +745 -3
- package/dist/proto/livekit_rtc.js.map +1 -1
- package/dist/room/PCTransport.js +4 -0
- package/dist/room/PCTransport.js.map +1 -1
- package/dist/room/RTCEngine.d.ts +4 -0
- package/dist/room/RTCEngine.js +73 -34
- package/dist/room/RTCEngine.js.map +1 -1
- package/dist/room/Room.d.ts +15 -0
- package/dist/room/Room.js +172 -59
- package/dist/room/Room.js.map +1 -1
- package/dist/room/events.d.ts +60 -24
- package/dist/room/events.js +58 -22
- package/dist/room/events.js.map +1 -1
- package/dist/room/participant/LocalParticipant.d.ts +26 -2
- package/dist/room/participant/LocalParticipant.js +69 -21
- package/dist/room/participant/LocalParticipant.js.map +1 -1
- package/dist/room/participant/Participant.d.ts +3 -1
- package/dist/room/participant/Participant.js +1 -0
- package/dist/room/participant/Participant.js.map +1 -1
- package/dist/room/participant/ParticipantTrackPermission.d.ts +19 -0
- package/dist/room/participant/ParticipantTrackPermission.js +16 -0
- package/dist/room/participant/ParticipantTrackPermission.js.map +1 -0
- package/dist/room/participant/RemoteParticipant.d.ts +2 -2
- package/dist/room/participant/RemoteParticipant.js +9 -15
- package/dist/room/participant/RemoteParticipant.js.map +1 -1
- package/dist/room/participant/publishUtils.d.ts +1 -1
- package/dist/room/participant/publishUtils.js +4 -4
- package/dist/room/participant/publishUtils.js.map +1 -1
- package/dist/room/participant/publishUtils.test.js +10 -1
- package/dist/room/participant/publishUtils.test.js.map +1 -1
- package/dist/room/stats.d.ts +21 -6
- package/dist/room/stats.js +22 -1
- package/dist/room/stats.js.map +1 -1
- package/dist/room/track/LocalAudioTrack.d.ts +5 -1
- package/dist/room/track/LocalAudioTrack.js +45 -1
- package/dist/room/track/LocalAudioTrack.js.map +1 -1
- package/dist/room/track/LocalTrack.js +1 -1
- package/dist/room/track/LocalTrack.js.map +1 -1
- package/dist/room/track/LocalTrackPublication.d.ts +3 -1
- package/dist/room/track/LocalTrackPublication.js +15 -5
- package/dist/room/track/LocalTrackPublication.js.map +1 -1
- package/dist/room/track/LocalVideoTrack.d.ts +8 -1
- package/dist/room/track/LocalVideoTrack.js +117 -52
- package/dist/room/track/LocalVideoTrack.js.map +1 -1
- package/dist/room/track/RemoteAudioTrack.d.ts +6 -8
- package/dist/room/track/RemoteAudioTrack.js +55 -19
- package/dist/room/track/RemoteAudioTrack.js.map +1 -1
- package/dist/room/track/RemoteTrack.d.ts +14 -0
- package/dist/room/track/RemoteTrack.js +47 -0
- package/dist/room/track/RemoteTrack.js.map +1 -0
- package/dist/room/track/RemoteTrackPublication.d.ts +10 -2
- package/dist/room/track/RemoteTrackPublication.js +49 -16
- package/dist/room/track/RemoteTrackPublication.js.map +1 -1
- package/dist/room/track/RemoteVideoTrack.d.ts +7 -7
- package/dist/room/track/RemoteVideoTrack.js +66 -22
- package/dist/room/track/RemoteVideoTrack.js.map +1 -1
- package/dist/room/track/Track.d.ts +12 -0
- package/dist/room/track/Track.js +33 -0
- package/dist/room/track/Track.js.map +1 -1
- package/dist/room/track/TrackPublication.d.ts +14 -1
- package/dist/room/track/TrackPublication.js +24 -7
- package/dist/room/track/TrackPublication.js.map +1 -1
- package/dist/room/track/create.d.ts +23 -0
- package/dist/room/track/create.js +130 -0
- package/dist/room/track/create.js.map +1 -0
- package/dist/room/track/defaults.d.ts +4 -0
- package/dist/room/track/defaults.js +21 -0
- package/dist/room/track/defaults.js.map +1 -0
- package/dist/room/utils.d.ts +3 -1
- package/dist/room/utils.js +36 -6
- package/dist/room/utils.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +5 -3
- package/src/api/SignalClient.ts +434 -0
- package/src/connect.ts +100 -0
- package/src/index.ts +47 -0
- package/src/logger.ts +22 -0
- package/src/options.ts +152 -0
- package/src/proto/livekit_models.ts +1863 -0
- package/src/proto/livekit_rtc.ts +3401 -0
- package/src/room/DeviceManager.ts +57 -0
- package/src/room/PCTransport.ts +86 -0
- package/src/room/RTCEngine.ts +484 -0
- package/src/room/Room.ts +785 -0
- package/src/room/errors.ts +65 -0
- package/src/room/events.ts +396 -0
- package/src/room/participant/LocalParticipant.ts +685 -0
- package/src/room/participant/Participant.ts +214 -0
- package/src/room/participant/ParticipantTrackPermission.ts +32 -0
- package/src/room/participant/RemoteParticipant.ts +238 -0
- package/src/room/participant/publishUtils.test.ts +105 -0
- package/src/room/participant/publishUtils.ts +180 -0
- package/src/room/stats.ts +130 -0
- package/src/room/track/LocalAudioTrack.ts +112 -0
- package/src/room/track/LocalTrack.ts +124 -0
- package/src/room/track/LocalTrackPublication.ts +63 -0
- package/src/room/track/LocalVideoTrack.test.ts +70 -0
- package/src/room/track/LocalVideoTrack.ts +416 -0
- package/src/room/track/RemoteAudioTrack.ts +58 -0
- package/src/room/track/RemoteTrack.ts +59 -0
- package/src/room/track/RemoteTrackPublication.ts +192 -0
- package/src/room/track/RemoteVideoTrack.ts +213 -0
- package/src/room/track/Track.ts +301 -0
- package/src/room/track/TrackPublication.ts +120 -0
- package/src/room/track/create.ts +120 -0
- package/src/room/track/defaults.ts +23 -0
- package/src/room/track/options.ts +229 -0
- package/src/room/track/types.ts +8 -0
- package/src/room/track/utils.test.ts +93 -0
- package/src/room/track/utils.ts +76 -0
- package/src/room/utils.ts +74 -0
- package/src/version.ts +2 -0
- package/.github/workflows/publish.yaml +0 -55
- package/.github/workflows/test.yaml +0 -36
- package/example/index.html +0 -237
- package/example/sample.ts +0 -575
- package/example/styles.css +0 -144
- package/example/webpack.config.js +0 -33
@@ -0,0 +1,434 @@
|
|
1
|
+
import 'webrtc-adapter';
|
2
|
+
import log from '../logger';
|
3
|
+
import {
|
4
|
+
ClientInfo,
|
5
|
+
ParticipantInfo, Room, SpeakerInfo, VideoLayer,
|
6
|
+
} from '../proto/livekit_models';
|
7
|
+
import {
|
8
|
+
AddTrackRequest,
|
9
|
+
ConnectionQualityUpdate,
|
10
|
+
JoinResponse,
|
11
|
+
SessionDescription,
|
12
|
+
SignalRequest,
|
13
|
+
SignalResponse,
|
14
|
+
SignalTarget, SimulateScenario,
|
15
|
+
StreamStateUpdate,
|
16
|
+
SubscribedQualityUpdate,
|
17
|
+
SubscriptionPermissionUpdate, SyncState, TrackPermission,
|
18
|
+
TrackPublishedResponse,
|
19
|
+
UpdateSubscription, UpdateTrackSettings,
|
20
|
+
} from '../proto/livekit_rtc';
|
21
|
+
import { ConnectionError } from '../room/errors';
|
22
|
+
import { getClientInfo, sleep } from '../room/utils';
|
23
|
+
|
24
|
+
// internal options
|
25
|
+
interface ConnectOpts {
|
26
|
+
autoSubscribe?: boolean;
|
27
|
+
/** internal */
|
28
|
+
reconnect?: boolean;
|
29
|
+
}
|
30
|
+
|
31
|
+
// public options
|
32
|
+
export interface SignalOptions {
|
33
|
+
autoSubscribe?: boolean;
|
34
|
+
}
|
35
|
+
|
36
|
+
/** @internal */
|
37
|
+
export class SignalClient {
|
38
|
+
isConnected: boolean;
|
39
|
+
|
40
|
+
useJSON: boolean;
|
41
|
+
|
42
|
+
/** simulate signaling latency by delaying messages */
|
43
|
+
signalLatency?: number;
|
44
|
+
|
45
|
+
onClose?: (reason: string) => void;
|
46
|
+
|
47
|
+
onAnswer?: (sd: RTCSessionDescriptionInit) => void;
|
48
|
+
|
49
|
+
onOffer?: (sd: RTCSessionDescriptionInit) => void;
|
50
|
+
|
51
|
+
// when a new ICE candidate is made available
|
52
|
+
onTrickle?: (sd: RTCIceCandidateInit, target: SignalTarget) => void;
|
53
|
+
|
54
|
+
onParticipantUpdate?: (updates: ParticipantInfo[]) => void;
|
55
|
+
|
56
|
+
onLocalTrackPublished?: (res: TrackPublishedResponse) => void;
|
57
|
+
|
58
|
+
onNegotiateRequested?: () => void;
|
59
|
+
|
60
|
+
onSpeakersChanged?: (res: SpeakerInfo[]) => void;
|
61
|
+
|
62
|
+
onRemoteMuteChanged?: (trackSid: string, muted: boolean) => void;
|
63
|
+
|
64
|
+
onRoomUpdate?: (room: Room) => void;
|
65
|
+
|
66
|
+
onConnectionQuality?: (update: ConnectionQualityUpdate) => void;
|
67
|
+
|
68
|
+
onStreamStateUpdate?: (update: StreamStateUpdate) => void;
|
69
|
+
|
70
|
+
onSubscribedQualityUpdate?: (update: SubscribedQualityUpdate) => void;
|
71
|
+
|
72
|
+
onSubscriptionPermissionUpdate?: (update: SubscriptionPermissionUpdate) => void;
|
73
|
+
|
74
|
+
onLeave?: () => void;
|
75
|
+
|
76
|
+
ws?: WebSocket;
|
77
|
+
|
78
|
+
constructor(useJSON: boolean = false) {
|
79
|
+
this.isConnected = false;
|
80
|
+
this.useJSON = useJSON;
|
81
|
+
}
|
82
|
+
|
83
|
+
async join(
|
84
|
+
url: string,
|
85
|
+
token: string,
|
86
|
+
opts?: SignalOptions,
|
87
|
+
): Promise<JoinResponse> {
|
88
|
+
const res = await this.connect(url, token, {
|
89
|
+
autoSubscribe: opts?.autoSubscribe,
|
90
|
+
});
|
91
|
+
return res as JoinResponse;
|
92
|
+
}
|
93
|
+
|
94
|
+
async reconnect(url: string, token: string): Promise<void> {
|
95
|
+
await this.connect(url, token, {
|
96
|
+
reconnect: true,
|
97
|
+
});
|
98
|
+
}
|
99
|
+
|
100
|
+
connect(
|
101
|
+
url: string,
|
102
|
+
token: string,
|
103
|
+
opts: ConnectOpts,
|
104
|
+
): Promise<JoinResponse | void> {
|
105
|
+
if (url.startsWith('http')) {
|
106
|
+
url = url.replace('http', 'ws');
|
107
|
+
}
|
108
|
+
// strip trailing slash
|
109
|
+
url = url.replace(/\/$/, '');
|
110
|
+
url += '/rtc';
|
111
|
+
|
112
|
+
const clientInfo = getClientInfo();
|
113
|
+
const params = createConnectionParams(token, clientInfo, opts);
|
114
|
+
|
115
|
+
return new Promise<JoinResponse | void>((resolve, reject) => {
|
116
|
+
log.debug('connecting to', url + params);
|
117
|
+
this.ws = undefined;
|
118
|
+
const ws = new WebSocket(url + params);
|
119
|
+
ws.binaryType = 'arraybuffer';
|
120
|
+
|
121
|
+
ws.onerror = async (ev: Event) => {
|
122
|
+
if (!this.ws) {
|
123
|
+
try {
|
124
|
+
const resp = await fetch(`http${url.substring(2)}/validate${params}`);
|
125
|
+
if (!resp.ok) {
|
126
|
+
const msg = await resp.text();
|
127
|
+
reject(new ConnectionError(msg));
|
128
|
+
} else {
|
129
|
+
reject(new ConnectionError('Internal error'));
|
130
|
+
}
|
131
|
+
} catch (e) {
|
132
|
+
reject(new ConnectionError('server was not reachable'));
|
133
|
+
}
|
134
|
+
return;
|
135
|
+
}
|
136
|
+
// other errors, handle
|
137
|
+
this.handleWSError(ev);
|
138
|
+
};
|
139
|
+
|
140
|
+
ws.onopen = () => {
|
141
|
+
this.ws = ws;
|
142
|
+
if (opts.reconnect) {
|
143
|
+
// upon reconnection, there will not be additional handshake
|
144
|
+
this.isConnected = true;
|
145
|
+
resolve();
|
146
|
+
}
|
147
|
+
};
|
148
|
+
|
149
|
+
ws.onmessage = async (ev: MessageEvent) => {
|
150
|
+
// not considered connected until JoinResponse is received
|
151
|
+
let msg: SignalResponse;
|
152
|
+
if (typeof ev.data === 'string') {
|
153
|
+
const json = JSON.parse(ev.data);
|
154
|
+
msg = SignalResponse.fromJSON(json);
|
155
|
+
} else if (ev.data instanceof ArrayBuffer) {
|
156
|
+
msg = SignalResponse.decode(new Uint8Array(ev.data));
|
157
|
+
} else {
|
158
|
+
log.error('could not decode websocket message', typeof ev.data);
|
159
|
+
return;
|
160
|
+
}
|
161
|
+
|
162
|
+
if (!this.isConnected) {
|
163
|
+
// handle join message only
|
164
|
+
if (msg.join) {
|
165
|
+
this.isConnected = true;
|
166
|
+
resolve(msg.join);
|
167
|
+
} else {
|
168
|
+
reject(new ConnectionError('did not receive join response'));
|
169
|
+
}
|
170
|
+
return;
|
171
|
+
}
|
172
|
+
|
173
|
+
if (this.signalLatency) {
|
174
|
+
await sleep(this.signalLatency);
|
175
|
+
}
|
176
|
+
this.handleSignalResponse(msg);
|
177
|
+
};
|
178
|
+
|
179
|
+
ws.onclose = (ev: CloseEvent) => {
|
180
|
+
if (!this.isConnected) return;
|
181
|
+
|
182
|
+
log.debug('websocket connection closed', ev.reason);
|
183
|
+
this.isConnected = false;
|
184
|
+
if (this.onClose) this.onClose(ev.reason);
|
185
|
+
if (this.ws === ws) {
|
186
|
+
this.ws = undefined;
|
187
|
+
}
|
188
|
+
};
|
189
|
+
});
|
190
|
+
}
|
191
|
+
|
192
|
+
close() {
|
193
|
+
this.isConnected = false;
|
194
|
+
if (this.ws) this.ws.onclose = null;
|
195
|
+
this.ws?.close();
|
196
|
+
this.ws = undefined;
|
197
|
+
}
|
198
|
+
|
199
|
+
// initial offer after joining
|
200
|
+
sendOffer(offer: RTCSessionDescriptionInit) {
|
201
|
+
log.debug('sending offer', offer);
|
202
|
+
this.sendRequest({
|
203
|
+
offer: toProtoSessionDescription(offer),
|
204
|
+
});
|
205
|
+
}
|
206
|
+
|
207
|
+
// answer a server-initiated offer
|
208
|
+
sendAnswer(answer: RTCSessionDescriptionInit) {
|
209
|
+
log.debug('sending answer');
|
210
|
+
this.sendRequest({
|
211
|
+
answer: toProtoSessionDescription(answer),
|
212
|
+
});
|
213
|
+
}
|
214
|
+
|
215
|
+
sendIceCandidate(candidate: RTCIceCandidateInit, target: SignalTarget) {
|
216
|
+
log.trace('sending ice candidate', candidate);
|
217
|
+
this.sendRequest({
|
218
|
+
trickle: {
|
219
|
+
candidateInit: JSON.stringify(candidate),
|
220
|
+
target,
|
221
|
+
},
|
222
|
+
});
|
223
|
+
}
|
224
|
+
|
225
|
+
sendMuteTrack(trackSid: string, muted: boolean) {
|
226
|
+
this.sendRequest({
|
227
|
+
mute: {
|
228
|
+
sid: trackSid,
|
229
|
+
muted,
|
230
|
+
},
|
231
|
+
});
|
232
|
+
}
|
233
|
+
|
234
|
+
sendAddTrack(req: AddTrackRequest): void {
|
235
|
+
this.sendRequest({
|
236
|
+
addTrack: AddTrackRequest.fromPartial(req),
|
237
|
+
});
|
238
|
+
}
|
239
|
+
|
240
|
+
sendUpdateTrackSettings(settings: UpdateTrackSettings) {
|
241
|
+
this.sendRequest({ trackSetting: settings });
|
242
|
+
}
|
243
|
+
|
244
|
+
sendUpdateSubscription(sub: UpdateSubscription) {
|
245
|
+
this.sendRequest({ subscription: sub });
|
246
|
+
}
|
247
|
+
|
248
|
+
sendSyncState(sync: SyncState) {
|
249
|
+
this.sendRequest({ syncState: sync });
|
250
|
+
}
|
251
|
+
|
252
|
+
sendUpdateVideoLayers(trackSid: string, layers: VideoLayer[]) {
|
253
|
+
this.sendRequest({
|
254
|
+
updateLayers: {
|
255
|
+
trackSid,
|
256
|
+
layers,
|
257
|
+
},
|
258
|
+
});
|
259
|
+
}
|
260
|
+
|
261
|
+
sendUpdateSubscriptionPermissions(
|
262
|
+
allParticipants: boolean,
|
263
|
+
trackPermissions: TrackPermission[],
|
264
|
+
) {
|
265
|
+
this.sendRequest({
|
266
|
+
subscriptionPermissions: {
|
267
|
+
allParticipants,
|
268
|
+
trackPermissions,
|
269
|
+
},
|
270
|
+
});
|
271
|
+
}
|
272
|
+
|
273
|
+
sendSimulateScenario(scenario: SimulateScenario) {
|
274
|
+
this.sendRequest({
|
275
|
+
simulate: scenario,
|
276
|
+
});
|
277
|
+
}
|
278
|
+
|
279
|
+
sendLeave() {
|
280
|
+
this.sendRequest(SignalRequest.fromPartial({ leave: {} }));
|
281
|
+
}
|
282
|
+
|
283
|
+
async sendRequest(req: SignalRequest) {
|
284
|
+
if (this.signalLatency) {
|
285
|
+
await sleep(this.signalLatency);
|
286
|
+
}
|
287
|
+
if (!this.ws) {
|
288
|
+
log.error('cannot send signal request before connected');
|
289
|
+
return;
|
290
|
+
}
|
291
|
+
|
292
|
+
try {
|
293
|
+
if (this.useJSON) {
|
294
|
+
this.ws.send(JSON.stringify(SignalRequest.toJSON(req)));
|
295
|
+
} else {
|
296
|
+
this.ws.send(SignalRequest.encode(req).finish());
|
297
|
+
}
|
298
|
+
} catch (e) {
|
299
|
+
log.error('error sending signal message', e);
|
300
|
+
}
|
301
|
+
}
|
302
|
+
|
303
|
+
private handleSignalResponse(msg: SignalResponse) {
|
304
|
+
if (msg.answer) {
|
305
|
+
const sd = fromProtoSessionDescription(msg.answer);
|
306
|
+
if (this.onAnswer) {
|
307
|
+
this.onAnswer(sd);
|
308
|
+
}
|
309
|
+
} else if (msg.offer) {
|
310
|
+
const sd = fromProtoSessionDescription(msg.offer);
|
311
|
+
if (this.onOffer) {
|
312
|
+
this.onOffer(sd);
|
313
|
+
}
|
314
|
+
} else if (msg.trickle) {
|
315
|
+
const candidate: RTCIceCandidateInit = JSON.parse(
|
316
|
+
msg.trickle.candidateInit,
|
317
|
+
);
|
318
|
+
if (this.onTrickle) {
|
319
|
+
this.onTrickle(candidate, msg.trickle.target);
|
320
|
+
}
|
321
|
+
} else if (msg.update) {
|
322
|
+
if (this.onParticipantUpdate) {
|
323
|
+
this.onParticipantUpdate(msg.update.participants);
|
324
|
+
}
|
325
|
+
} else if (msg.trackPublished) {
|
326
|
+
if (this.onLocalTrackPublished) {
|
327
|
+
this.onLocalTrackPublished(msg.trackPublished);
|
328
|
+
}
|
329
|
+
} else if (msg.speakersChanged) {
|
330
|
+
if (this.onSpeakersChanged) {
|
331
|
+
this.onSpeakersChanged(msg.speakersChanged.speakers);
|
332
|
+
}
|
333
|
+
} else if (msg.leave) {
|
334
|
+
if (this.onLeave) {
|
335
|
+
this.onLeave();
|
336
|
+
}
|
337
|
+
} else if (msg.mute) {
|
338
|
+
if (this.onRemoteMuteChanged) {
|
339
|
+
this.onRemoteMuteChanged(msg.mute.sid, msg.mute.muted);
|
340
|
+
}
|
341
|
+
} else if (msg.roomUpdate) {
|
342
|
+
if (this.onRoomUpdate) {
|
343
|
+
this.onRoomUpdate(msg.roomUpdate.room!);
|
344
|
+
}
|
345
|
+
} else if (msg.connectionQuality) {
|
346
|
+
if (this.onConnectionQuality) {
|
347
|
+
this.onConnectionQuality(msg.connectionQuality);
|
348
|
+
}
|
349
|
+
} else if (msg.streamStateUpdate) {
|
350
|
+
if (this.onStreamStateUpdate) {
|
351
|
+
this.onStreamStateUpdate(msg.streamStateUpdate);
|
352
|
+
}
|
353
|
+
} else if (msg.subscribedQualityUpdate) {
|
354
|
+
if (this.onSubscribedQualityUpdate) {
|
355
|
+
this.onSubscribedQualityUpdate(msg.subscribedQualityUpdate);
|
356
|
+
}
|
357
|
+
} else if (msg.subscriptionPermissionUpdate) {
|
358
|
+
if (this.onSubscriptionPermissionUpdate) {
|
359
|
+
this.onSubscriptionPermissionUpdate(msg.subscriptionPermissionUpdate);
|
360
|
+
}
|
361
|
+
} else {
|
362
|
+
log.debug('unsupported message', msg);
|
363
|
+
}
|
364
|
+
}
|
365
|
+
|
366
|
+
private handleWSError(ev: Event) {
|
367
|
+
log.error('websocket error', ev);
|
368
|
+
}
|
369
|
+
}
|
370
|
+
|
371
|
+
function fromProtoSessionDescription(
|
372
|
+
sd: SessionDescription,
|
373
|
+
): RTCSessionDescriptionInit {
|
374
|
+
const rsd: RTCSessionDescriptionInit = {
|
375
|
+
type: 'offer',
|
376
|
+
sdp: sd.sdp,
|
377
|
+
};
|
378
|
+
switch (sd.type) {
|
379
|
+
case 'answer':
|
380
|
+
case 'offer':
|
381
|
+
case 'pranswer':
|
382
|
+
case 'rollback':
|
383
|
+
rsd.type = sd.type;
|
384
|
+
break;
|
385
|
+
default:
|
386
|
+
break;
|
387
|
+
}
|
388
|
+
return rsd;
|
389
|
+
}
|
390
|
+
|
391
|
+
export function toProtoSessionDescription(
|
392
|
+
rsd: RTCSessionDescription | RTCSessionDescriptionInit,
|
393
|
+
): SessionDescription {
|
394
|
+
const sd: SessionDescription = {
|
395
|
+
sdp: rsd.sdp!,
|
396
|
+
type: rsd.type!,
|
397
|
+
};
|
398
|
+
return sd;
|
399
|
+
}
|
400
|
+
|
401
|
+
function createConnectionParams(token: string, info: ClientInfo, opts?: ConnectOpts): string {
|
402
|
+
const params = new URLSearchParams();
|
403
|
+
params.set('access_token', token);
|
404
|
+
|
405
|
+
// opts
|
406
|
+
if (opts?.reconnect) {
|
407
|
+
params.set('reconnect', '1');
|
408
|
+
}
|
409
|
+
if (opts?.autoSubscribe !== undefined) {
|
410
|
+
params.set('auto_subscribe', opts.autoSubscribe ? '1' : '0');
|
411
|
+
}
|
412
|
+
|
413
|
+
// ClientInfo
|
414
|
+
params.set('sdk', 'js');
|
415
|
+
params.set('version', info.version);
|
416
|
+
params.set('protocol', info.protocol.toString());
|
417
|
+
if (info.deviceModel) {
|
418
|
+
params.set('device_model', info.deviceModel);
|
419
|
+
}
|
420
|
+
if (info.os) {
|
421
|
+
params.set('os', info.os);
|
422
|
+
}
|
423
|
+
if (info.osVersion) {
|
424
|
+
params.set('os_version', info.osVersion);
|
425
|
+
}
|
426
|
+
if (info.browser) {
|
427
|
+
params.set('browser', info.browser);
|
428
|
+
}
|
429
|
+
if (info.browserVersion) {
|
430
|
+
params.set('browser_version', info.browserVersion);
|
431
|
+
}
|
432
|
+
|
433
|
+
return `?${params.toString()}`;
|
434
|
+
}
|
package/src/connect.ts
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
import log, { LogLevel, setLogLevel } from './logger';
|
2
|
+
import {
|
3
|
+
ConnectOptions,
|
4
|
+
} from './options';
|
5
|
+
import { MediaDeviceFailure } from './room/errors';
|
6
|
+
import { RoomEvent } from './room/events';
|
7
|
+
import Room from './room/Room';
|
8
|
+
|
9
|
+
export { version } from './version';
|
10
|
+
|
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(
|
29
|
+
url: string,
|
30
|
+
token: string,
|
31
|
+
options?: ConnectOptions,
|
32
|
+
): Promise<Room> {
|
33
|
+
options ??= {};
|
34
|
+
if (options.adaptiveStream === undefined) {
|
35
|
+
options.adaptiveStream = options.autoManageVideo;
|
36
|
+
}
|
37
|
+
setLogLevel(options.logLevel ?? LogLevel.warn);
|
38
|
+
|
39
|
+
const config: RTCConfiguration = options.rtcConfig ?? {};
|
40
|
+
if (options.iceServers) {
|
41
|
+
config.iceServers = options.iceServers;
|
42
|
+
}
|
43
|
+
|
44
|
+
const room = new Room(options);
|
45
|
+
|
46
|
+
// connect to room
|
47
|
+
await room.connect(url, token, options);
|
48
|
+
|
49
|
+
const publishAudio: boolean = options.audio ?? false;
|
50
|
+
const publishVideo: boolean = options.video ?? false;
|
51
|
+
|
52
|
+
if (publishAudio || publishVideo) {
|
53
|
+
setTimeout(async () => {
|
54
|
+
// if publishing both
|
55
|
+
let err: any;
|
56
|
+
if (publishAudio && publishVideo) {
|
57
|
+
try {
|
58
|
+
await room.localParticipant.enableCameraAndMicrophone();
|
59
|
+
} catch (e) {
|
60
|
+
const errKind = MediaDeviceFailure.getFailure(e);
|
61
|
+
log.warn('received error while creating media', errKind);
|
62
|
+
if (e instanceof Error) {
|
63
|
+
log.warn(e.message);
|
64
|
+
}
|
65
|
+
|
66
|
+
// when it's a device issue, try to publish the other kind
|
67
|
+
if (errKind === MediaDeviceFailure.NotFound
|
68
|
+
|| errKind === MediaDeviceFailure.DeviceInUse) {
|
69
|
+
try {
|
70
|
+
await room.localParticipant.setMicrophoneEnabled(true);
|
71
|
+
} catch (audioErr) {
|
72
|
+
err = audioErr;
|
73
|
+
}
|
74
|
+
} else {
|
75
|
+
err = e;
|
76
|
+
}
|
77
|
+
}
|
78
|
+
} else if (publishAudio) {
|
79
|
+
try {
|
80
|
+
await room.localParticipant.setMicrophoneEnabled(true);
|
81
|
+
} catch (e) {
|
82
|
+
err = e;
|
83
|
+
}
|
84
|
+
} else if (publishVideo) {
|
85
|
+
try {
|
86
|
+
await room.localParticipant.setCameraEnabled(true);
|
87
|
+
} catch (e) {
|
88
|
+
err = e;
|
89
|
+
}
|
90
|
+
}
|
91
|
+
|
92
|
+
if (err) {
|
93
|
+
room.emit(RoomEvent.MediaDevicesError, err);
|
94
|
+
log.error('could not create media', err);
|
95
|
+
}
|
96
|
+
});
|
97
|
+
}
|
98
|
+
|
99
|
+
return room;
|
100
|
+
}
|
package/src/index.ts
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
import { setLogLevel } from './logger';
|
2
|
+
import { DataPacket_Kind, VideoQuality } from './proto/livekit_models';
|
3
|
+
import LocalParticipant from './room/participant/LocalParticipant';
|
4
|
+
import Participant, { ConnectionQuality } from './room/participant/Participant';
|
5
|
+
import { ParticipantTrackPermission } from './room/participant/ParticipantTrackPermission';
|
6
|
+
import RemoteParticipant from './room/participant/RemoteParticipant';
|
7
|
+
import Room, { RoomState } from './room/Room';
|
8
|
+
import LocalAudioTrack from './room/track/LocalAudioTrack';
|
9
|
+
import LocalTrack from './room/track/LocalTrack';
|
10
|
+
import LocalTrackPublication from './room/track/LocalTrackPublication';
|
11
|
+
import LocalVideoTrack from './room/track/LocalVideoTrack';
|
12
|
+
import RemoteAudioTrack from './room/track/RemoteAudioTrack';
|
13
|
+
import RemoteTrack from './room/track/RemoteTrack';
|
14
|
+
import RemoteTrackPublication from './room/track/RemoteTrackPublication';
|
15
|
+
import RemoteVideoTrack from './room/track/RemoteVideoTrack';
|
16
|
+
import { TrackPublication } from './room/track/TrackPublication';
|
17
|
+
|
18
|
+
export * from './connect';
|
19
|
+
export * from './options';
|
20
|
+
export * from './room/errors';
|
21
|
+
export * from './room/events';
|
22
|
+
export * from './room/track/create';
|
23
|
+
export * from './room/track/options';
|
24
|
+
export * from './room/track/Track';
|
25
|
+
export * from './room/track/types';
|
26
|
+
export * from './version';
|
27
|
+
export {
|
28
|
+
setLogLevel,
|
29
|
+
Room,
|
30
|
+
RoomState,
|
31
|
+
DataPacket_Kind,
|
32
|
+
ConnectionQuality,
|
33
|
+
Participant,
|
34
|
+
RemoteParticipant,
|
35
|
+
LocalParticipant,
|
36
|
+
LocalAudioTrack,
|
37
|
+
LocalVideoTrack,
|
38
|
+
LocalTrack,
|
39
|
+
LocalTrackPublication,
|
40
|
+
RemoteTrack,
|
41
|
+
RemoteAudioTrack,
|
42
|
+
RemoteVideoTrack,
|
43
|
+
RemoteTrackPublication,
|
44
|
+
ParticipantTrackPermission,
|
45
|
+
TrackPublication,
|
46
|
+
VideoQuality,
|
47
|
+
};
|
package/src/logger.ts
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
import log from 'loglevel';
|
2
|
+
|
3
|
+
export type LogLevelDesc = 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'silent';
|
4
|
+
|
5
|
+
export enum LogLevel {
|
6
|
+
trace = 'trace',
|
7
|
+
debug = 'debug',
|
8
|
+
info = 'info',
|
9
|
+
warn = 'warn',
|
10
|
+
error = 'error',
|
11
|
+
silent = 'silent',
|
12
|
+
}
|
13
|
+
|
14
|
+
const livekitLogger = log.getLogger('livekit');
|
15
|
+
|
16
|
+
livekitLogger.setLevel(LogLevel.info);
|
17
|
+
|
18
|
+
export default livekitLogger;
|
19
|
+
|
20
|
+
export function setLogLevel(level: LogLevel | LogLevelDesc) {
|
21
|
+
livekitLogger.setLevel(level);
|
22
|
+
}
|