livekit-client 0.15.3 → 0.16.2

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 (90) hide show
  1. package/dist/api/SignalClient.d.ts +6 -3
  2. package/dist/api/SignalClient.js +70 -28
  3. package/dist/api/SignalClient.js.map +1 -1
  4. package/dist/options.d.ts +5 -0
  5. package/dist/proto/livekit_models.d.ts +30 -0
  6. package/dist/proto/livekit_models.js +219 -1
  7. package/dist/proto/livekit_models.js.map +1 -1
  8. package/dist/proto/livekit_rtc.d.ts +15 -10
  9. package/dist/proto/livekit_rtc.js +36 -22
  10. package/dist/proto/livekit_rtc.js.map +1 -1
  11. package/dist/room/RTCEngine.d.ts +11 -2
  12. package/dist/room/RTCEngine.js +196 -44
  13. package/dist/room/RTCEngine.js.map +1 -1
  14. package/dist/room/Room.d.ts +7 -0
  15. package/dist/room/Room.js +70 -16
  16. package/dist/room/Room.js.map +1 -1
  17. package/dist/room/events.d.ts +5 -3
  18. package/dist/room/events.js +5 -3
  19. package/dist/room/events.js.map +1 -1
  20. package/dist/room/participant/LocalParticipant.d.ts +1 -2
  21. package/dist/room/participant/LocalParticipant.js +7 -6
  22. package/dist/room/participant/LocalParticipant.js.map +1 -1
  23. package/dist/room/participant/RemoteParticipant.js +3 -0
  24. package/dist/room/participant/RemoteParticipant.js.map +1 -1
  25. package/dist/room/participant/publishUtils.js +1 -1
  26. package/dist/room/participant/publishUtils.js.map +1 -1
  27. package/dist/room/participant/publishUtils.test.js +9 -0
  28. package/dist/room/participant/publishUtils.test.js.map +1 -1
  29. package/dist/room/track/LocalTrackPublication.d.ts +2 -0
  30. package/dist/room/track/LocalTrackPublication.js.map +1 -1
  31. package/dist/room/track/RemoteTrackPublication.d.ts +2 -1
  32. package/dist/room/track/RemoteTrackPublication.js +22 -8
  33. package/dist/room/track/RemoteTrackPublication.js.map +1 -1
  34. package/dist/room/track/RemoteVideoTrack.js +12 -7
  35. package/dist/room/track/RemoteVideoTrack.js.map +1 -1
  36. package/dist/room/track/Track.js +28 -20
  37. package/dist/room/track/Track.js.map +1 -1
  38. package/dist/room/track/create.js +5 -0
  39. package/dist/room/track/create.js.map +1 -1
  40. package/dist/room/utils.d.ts +3 -0
  41. package/dist/room/utils.js +16 -1
  42. package/dist/room/utils.js.map +1 -1
  43. package/dist/version.d.ts +2 -2
  44. package/dist/version.js +2 -2
  45. package/package.json +3 -3
  46. package/src/api/SignalClient.ts +444 -0
  47. package/src/connect.ts +100 -0
  48. package/src/index.ts +47 -0
  49. package/src/logger.ts +22 -0
  50. package/src/options.ts +152 -0
  51. package/src/proto/livekit_models.ts +1863 -0
  52. package/src/proto/livekit_rtc.ts +3415 -0
  53. package/src/room/DeviceManager.ts +57 -0
  54. package/src/room/PCTransport.ts +86 -0
  55. package/src/room/RTCEngine.ts +598 -0
  56. package/src/room/Room.ts +840 -0
  57. package/src/room/errors.ts +65 -0
  58. package/src/room/events.ts +398 -0
  59. package/src/room/participant/LocalParticipant.ts +685 -0
  60. package/src/room/participant/Participant.ts +214 -0
  61. package/src/room/participant/ParticipantTrackPermission.ts +32 -0
  62. package/src/room/participant/RemoteParticipant.ts +241 -0
  63. package/src/room/participant/publishUtils.test.ts +105 -0
  64. package/src/room/participant/publishUtils.ts +180 -0
  65. package/src/room/stats.ts +130 -0
  66. package/src/room/track/LocalAudioTrack.ts +112 -0
  67. package/src/room/track/LocalTrack.ts +124 -0
  68. package/src/room/track/LocalTrackPublication.ts +66 -0
  69. package/src/room/track/LocalVideoTrack.test.ts +70 -0
  70. package/src/room/track/LocalVideoTrack.ts +416 -0
  71. package/src/room/track/RemoteAudioTrack.ts +58 -0
  72. package/src/room/track/RemoteTrack.ts +59 -0
  73. package/src/room/track/RemoteTrackPublication.ts +198 -0
  74. package/src/room/track/RemoteVideoTrack.ts +220 -0
  75. package/src/room/track/Track.ts +307 -0
  76. package/src/room/track/TrackPublication.ts +120 -0
  77. package/src/room/track/create.ts +120 -0
  78. package/src/room/track/defaults.ts +23 -0
  79. package/src/room/track/options.ts +229 -0
  80. package/src/room/track/types.ts +8 -0
  81. package/src/room/track/utils.test.ts +93 -0
  82. package/src/room/track/utils.ts +76 -0
  83. package/src/room/utils.ts +62 -0
  84. package/src/version.ts +2 -0
  85. package/.github/workflows/publish.yaml +0 -55
  86. package/.github/workflows/test.yaml +0 -36
  87. package/example/index.html +0 -247
  88. package/example/sample.ts +0 -632
  89. package/example/styles.css +0 -144
  90. package/example/webpack.config.js +0 -33
package/example/sample.ts DELETED
@@ -1,632 +0,0 @@
1
- import {
2
- DataPacket_Kind, LocalParticipant, MediaDeviceFailure,
3
- Participant, ParticipantEvent, RemoteParticipant, Room,
4
- RoomConnectOptions, RoomEvent,
5
- RoomOptions, RoomState, setLogLevel, Track, TrackPublication,
6
- VideoCaptureOptions, VideoPresets,
7
- } from '../src/index';
8
- import { ConnectionQuality } from '../src/room/participant/Participant';
9
-
10
- const $ = (id: string) => document.getElementById(id);
11
-
12
- const state = {
13
- isFrontFacing: false,
14
- encoder: new TextEncoder(),
15
- decoder: new TextDecoder(),
16
- defaultDevices: new Map<MediaDeviceKind, string>(),
17
- bitrateInterval: undefined as any,
18
- };
19
- let currentRoom: Room | undefined;
20
-
21
- // handles actions from the HTML
22
- const appActions = {
23
- connectWithFormInput: async () => {
24
- const url = (<HTMLInputElement>$('url')).value;
25
- const token = (<HTMLInputElement>$('token')).value;
26
- const simulcast = (<HTMLInputElement>$('simulcast')).checked;
27
- const dynacast = (<HTMLInputElement>$('dynacast')).checked;
28
- const forceTURN = (<HTMLInputElement>$('force-turn')).checked;
29
- const adaptiveStream = (<HTMLInputElement>$('adaptive-stream')).checked;
30
- const shouldPublish = (<HTMLInputElement>$('publish-option')).checked;
31
-
32
- setLogLevel('debug');
33
-
34
- const roomOpts: RoomOptions = {
35
- adaptiveStream,
36
- dynacast,
37
- publishDefaults: {
38
- simulcast,
39
- },
40
- videoCaptureDefaults: {
41
- resolution: VideoPresets.hd.resolution,
42
- },
43
- };
44
-
45
- const connectOpts: RoomConnectOptions = {};
46
- if (forceTURN) {
47
- connectOpts.rtcConfig = {
48
- iceTransportPolicy: 'relay',
49
- };
50
- }
51
- const room = await appActions.connectToRoom(url, token, roomOpts, connectOpts);
52
-
53
- if (room && shouldPublish) {
54
- await room.localParticipant.enableCameraAndMicrophone();
55
- updateButtonsForPublishState();
56
- }
57
-
58
- state.bitrateInterval = setInterval(renderBitrate, 1000);
59
- },
60
-
61
- connectToRoom: async (
62
- url: string,
63
- token: string,
64
- roomOptions?: RoomOptions,
65
- connectOptions?: RoomConnectOptions,
66
- ): Promise<Room | undefined> => {
67
- const room = new Room(roomOptions);
68
- room
69
- .on(RoomEvent.ParticipantConnected, participantConnected)
70
- .on(RoomEvent.ParticipantDisconnected, participantDisconnected)
71
- .on(RoomEvent.DataReceived, handleData)
72
- .on(RoomEvent.Disconnected, handleRoomDisconnect)
73
- .on(RoomEvent.Reconnecting, () => appendLog('Reconnecting to room'))
74
- .on(RoomEvent.Reconnected, () => appendLog('Successfully reconnected!'))
75
- .on(RoomEvent.LocalTrackPublished, () => {
76
- renderParticipant(room.localParticipant);
77
- updateButtonsForPublishState();
78
- renderScreenShare();
79
- })
80
- .on(RoomEvent.LocalTrackUnpublished, () => {
81
- renderParticipant(room.localParticipant);
82
- updateButtonsForPublishState();
83
- renderScreenShare();
84
- })
85
- .on(RoomEvent.RoomMetadataChanged, (metadata) => {
86
- appendLog('new metadata for room', metadata);
87
- })
88
- .on(RoomEvent.MediaDevicesChanged, handleDevicesChanged)
89
- .on(RoomEvent.AudioPlaybackStatusChanged, () => {
90
- if (room.canPlaybackAudio) {
91
- $('start-audio-button')?.setAttribute('disabled', 'true');
92
- } else {
93
- $('start-audio-button')?.removeAttribute('disabled');
94
- }
95
- })
96
- .on(RoomEvent.MediaDevicesError, (e: Error) => {
97
- const failure = MediaDeviceFailure.getFailure(e);
98
- appendLog('media device failure', failure);
99
- })
100
- .on(RoomEvent.ConnectionQualityChanged,
101
- (quality: ConnectionQuality, participant: Participant) => {
102
- appendLog('connection quality changed', participant.identity, quality);
103
- });
104
-
105
- try {
106
- const start = Date.now();
107
- await room.connect(url, token, connectOptions);
108
- const elapsed = Date.now() - start;
109
- appendLog(`successfully connected to ${room.name} in ${Math.round(elapsed)}ms`);
110
- } catch (error) {
111
- let message: any = error;
112
- if (error.message) {
113
- message = error.message;
114
- }
115
- appendLog('could not connect:', message);
116
- return;
117
- }
118
- currentRoom = room;
119
- window.currentRoom = room;
120
- setButtonsForState(true);
121
-
122
- room.participants.forEach((participant) => {
123
- participantConnected(participant);
124
- });
125
- participantConnected(room.localParticipant);
126
-
127
- return room;
128
- },
129
-
130
- toggleAudio: async () => {
131
- if (!currentRoom) return;
132
- const enabled = currentRoom.localParticipant.isMicrophoneEnabled;
133
- if (enabled) {
134
- appendLog('disabling audio');
135
- } else {
136
- appendLog('enabling audio');
137
- }
138
- await currentRoom.localParticipant.setMicrophoneEnabled(!enabled);
139
- updateButtonsForPublishState();
140
- },
141
-
142
- toggleVideo: async () => {
143
- if (!currentRoom) return;
144
- const enabled = currentRoom.localParticipant.isCameraEnabled;
145
- if (enabled) {
146
- appendLog('disabling video');
147
- } else {
148
- appendLog('enabling video');
149
- }
150
- await currentRoom.localParticipant.setCameraEnabled(!enabled);
151
- renderParticipant(currentRoom.localParticipant);
152
-
153
- // update display
154
- updateButtonsForPublishState();
155
- },
156
-
157
- flipVideo: () => {
158
- const videoPub = currentRoom?.localParticipant.getTrack(Track.Source.Camera);
159
- if (!videoPub) {
160
- return;
161
- }
162
- if (state.isFrontFacing) {
163
- setButtonState('flip-video-button', 'Front Camera', false);
164
- } else {
165
- setButtonState('flip-video-button', 'Back Camera', false);
166
- }
167
- state.isFrontFacing = !state.isFrontFacing;
168
- const options: VideoCaptureOptions = {
169
- resolution: VideoPresets.qhd.resolution,
170
- facingMode: state.isFrontFacing ? 'user' : 'environment',
171
- };
172
- videoPub.videoTrack?.restartTrack(options);
173
- },
174
-
175
- shareScreen: async () => {
176
- if (!currentRoom) return;
177
-
178
- const enabled = currentRoom.localParticipant.isScreenShareEnabled;
179
- appendLog(`${enabled ? 'stopping' : 'starting'} screen share`);
180
- await currentRoom.localParticipant.setScreenShareEnabled(!enabled);
181
- updateButtonsForPublishState();
182
- },
183
-
184
- startAudio: () => {
185
- currentRoom?.startAudio();
186
- },
187
-
188
- enterText: () => {
189
- if (!currentRoom) return;
190
- const textField = <HTMLInputElement>$('entry');
191
- if (textField.value) {
192
- const msg = state.encoder.encode(textField.value);
193
- currentRoom.localParticipant.publishData(msg, DataPacket_Kind.RELIABLE);
194
- (<HTMLTextAreaElement>(
195
- $('chat')
196
- )).value += `${currentRoom.localParticipant.identity} (me): ${textField.value}\n`;
197
- textField.value = '';
198
- }
199
- },
200
-
201
- disconnectRoom: () => {
202
- if (currentRoom) {
203
- currentRoom.disconnect();
204
- }
205
- if (state.bitrateInterval) {
206
- clearInterval(state.bitrateInterval);
207
- }
208
- },
209
-
210
- handleScenario: (e: Event) => {
211
- const scenario = (<HTMLSelectElement>e.target).value;
212
- if (scenario !== '') {
213
- if (scenario === 'signal-reconnect') {
214
- appActions.disconnectSignal();
215
- } else {
216
- currentRoom?.simulateScenario(scenario);
217
- }
218
- (<HTMLSelectElement>e.target).value = '';
219
- }
220
- },
221
-
222
- disconnectSignal: () => {
223
- if (!currentRoom) return;
224
- currentRoom.engine.client.close();
225
- if (currentRoom.engine.client.onClose) {
226
- currentRoom.engine.client.onClose('manual disconnect');
227
- }
228
- },
229
-
230
- handleDeviceSelected: async (e: Event) => {
231
- const deviceId = (<HTMLSelectElement>e.target).value;
232
- const elementId = (<HTMLSelectElement>e.target).id;
233
- const kind = elementMapping[elementId];
234
- if (!kind) {
235
- return;
236
- }
237
-
238
- state.defaultDevices.set(kind, deviceId);
239
-
240
- if (currentRoom) {
241
- await currentRoom.switchActiveDevice(kind, deviceId);
242
- }
243
- },
244
- };
245
-
246
- declare global {
247
- interface Window {
248
- currentRoom: any;
249
- appActions: typeof appActions;
250
- }
251
- }
252
-
253
- window.appActions = appActions;
254
-
255
- // --------------------------- event handlers ------------------------------- //
256
-
257
- function handleData(msg: Uint8Array, participant?: RemoteParticipant) {
258
- const str = state.decoder.decode(msg);
259
- const chat = <HTMLTextAreaElement>$('chat');
260
- let from = 'server';
261
- if (participant) {
262
- from = participant.identity;
263
- }
264
- chat.value += `${from}: ${str}\n`;
265
- }
266
-
267
- function participantConnected(participant: Participant) {
268
- appendLog('participant', participant.identity, 'connected', participant.metadata);
269
- participant
270
- .on(ParticipantEvent.TrackSubscribed, (_, pub: TrackPublication) => {
271
- appendLog('subscribed to track', pub.trackSid, participant.identity);
272
- renderParticipant(participant);
273
- renderScreenShare();
274
- })
275
- .on(ParticipantEvent.TrackUnsubscribed, (_, pub: TrackPublication) => {
276
- appendLog('unsubscribed from track', pub.trackSid);
277
- renderParticipant(participant);
278
- renderScreenShare();
279
- })
280
- .on(ParticipantEvent.TrackMuted, (pub: TrackPublication) => {
281
- appendLog('track was muted', pub.trackSid, participant.identity);
282
- renderParticipant(participant);
283
- })
284
- .on(ParticipantEvent.TrackUnmuted, (pub: TrackPublication) => {
285
- appendLog('track was unmuted', pub.trackSid, participant.identity);
286
- renderParticipant(participant);
287
- })
288
- .on(ParticipantEvent.IsSpeakingChanged, () => {
289
- renderParticipant(participant);
290
- })
291
- .on(ParticipantEvent.ConnectionQualityChanged, () => {
292
- renderParticipant(participant);
293
- });
294
- }
295
-
296
- function participantDisconnected(participant: RemoteParticipant) {
297
- appendLog('participant', participant.sid, 'disconnected');
298
-
299
- renderParticipant(participant, true);
300
- }
301
-
302
- function handleRoomDisconnect() {
303
- if (!currentRoom) return;
304
- appendLog('disconnected from room');
305
- setButtonsForState(false);
306
- renderParticipant(currentRoom.localParticipant, true);
307
- currentRoom.participants.forEach((p) => {
308
- renderParticipant(p, true);
309
- });
310
- renderScreenShare();
311
-
312
- const container = $('participants-area');
313
- if (container) {
314
- container.innerHTML = '';
315
- }
316
-
317
- // clear the chat area on disconnect
318
- const chat = <HTMLTextAreaElement>$('chat');
319
- chat.value = '';
320
-
321
- currentRoom = undefined;
322
- window.currentRoom = undefined;
323
- }
324
-
325
- // -------------------------- rendering helpers ----------------------------- //
326
-
327
- function appendLog(...args: any[]) {
328
- const logger = $('log')!;
329
- for (let i = 0; i < arguments.length; i += 1) {
330
- if (typeof args[i] === 'object') {
331
- logger.innerHTML
332
- += `${JSON && JSON.stringify
333
- ? JSON.stringify(args[i], undefined, 2)
334
- : args[i]} `;
335
- } else {
336
- logger.innerHTML += `${args[i]} `;
337
- }
338
- }
339
- logger.innerHTML += '\n';
340
- (() => {
341
- logger.scrollTop = logger.scrollHeight;
342
- })();
343
- }
344
-
345
- // updates participant UI
346
- function renderParticipant(participant: Participant, remove: boolean = false) {
347
- const container = $('participants-area');
348
- if (!container) return;
349
- let div = $(`participant-${participant.sid}`);
350
- if (!div && !remove) {
351
- div = document.createElement('div');
352
- div.id = `participant-${participant.sid}`;
353
- div.className = 'participant';
354
- div.innerHTML = `
355
- <video id="video-${participant.sid}"></video>
356
- <audio id="audio-${participant.sid}"></audio>
357
- <div class="info-bar">
358
- <div id="name-${participant.sid}" class="name">
359
- </div>
360
- <div style="text-align: center;">
361
- <span id="size-${participant.sid}" class="size">
362
- </span>
363
- <span id="bitrate-${participant.sid}" class="bitrate">
364
- </span>
365
- </div>
366
- <div class="right">
367
- <span id="signal-${participant.sid}"></span>
368
- <span id="mic-${participant.sid}" class="mic-on"></span>
369
- </div>
370
- </div>
371
- `;
372
- container.appendChild(div);
373
-
374
- const sizeElm = $(`size-${participant.sid}`);
375
- const videoElm = <HTMLVideoElement>$(`video-${participant.sid}`);
376
- videoElm.onresize = () => {
377
- updateVideoSize(videoElm!, sizeElm!);
378
- };
379
- }
380
- const videoElm = <HTMLVideoElement>$(`video-${participant.sid}`);
381
- const audioELm = <HTMLAudioElement>$(`audio-${participant.sid}`);
382
- if (remove) {
383
- div?.remove();
384
- if (videoElm) {
385
- videoElm.srcObject = null;
386
- videoElm.src = '';
387
- }
388
- if (audioELm) {
389
- audioELm.srcObject = null;
390
- audioELm.src = '';
391
- }
392
- return;
393
- }
394
-
395
- // update properties
396
- $(`name-${participant.sid}`)!.innerHTML = participant.identity;
397
- const micElm = $(`mic-${participant.sid}`)!;
398
- const signalElm = $(`signal-${participant.sid}`)!;
399
- const cameraPub = participant.getTrack(Track.Source.Camera);
400
- const micPub = participant.getTrack(Track.Source.Microphone);
401
- if (participant.isSpeaking) {
402
- div!.classList.add('speaking');
403
- } else {
404
- div!.classList.remove('speaking');
405
- }
406
-
407
- const cameraEnabled = cameraPub && cameraPub.isSubscribed && !cameraPub.isMuted;
408
- if (cameraEnabled) {
409
- if (participant instanceof LocalParticipant) {
410
- // flip
411
- videoElm.style.transform = 'scale(-1, 1)';
412
- } else if (!cameraPub?.videoTrack?.attachedElements.includes(videoElm)) {
413
- const startTime = Date.now();
414
- // measure time to render
415
- videoElm.onloadeddata = () => {
416
- const elapsed = Date.now() - startTime;
417
- appendLog(`RemoteVideoTrack ${cameraPub?.trackSid} rendered in ${elapsed}ms`);
418
- };
419
- }
420
- cameraPub?.videoTrack?.attach(videoElm);
421
- } else {
422
- // clear information display
423
- $(`size-${participant.sid}`)!.innerHTML = '';
424
- if (cameraPub?.videoTrack) {
425
- // detach manually whenever possible
426
- cameraPub.videoTrack?.detach(videoElm);
427
- } else {
428
- videoElm.src = '';
429
- videoElm.srcObject = null;
430
- }
431
- }
432
-
433
- const micEnabled = micPub && micPub.isSubscribed && !micPub.isMuted;
434
- if (micEnabled) {
435
- if (!(participant instanceof LocalParticipant)) {
436
- // don't attach local audio
437
- micPub?.audioTrack?.attach(audioELm);
438
- }
439
- micElm.className = 'mic-on';
440
- micElm.innerHTML = '<i class="fas fa-microphone"></i>';
441
- } else {
442
- micElm.className = 'mic-off';
443
- micElm.innerHTML = '<i class="fas fa-microphone-slash"></i>';
444
- }
445
-
446
- switch (participant.connectionQuality) {
447
- case ConnectionQuality.Excellent:
448
- case ConnectionQuality.Good:
449
- case ConnectionQuality.Poor:
450
- signalElm.className = `connection-${participant.connectionQuality}`;
451
- signalElm.innerHTML = '<i class="fas fa-circle"></i>';
452
- break;
453
- default:
454
- signalElm.innerHTML = '';
455
- // do nothing
456
- }
457
- }
458
-
459
- function renderScreenShare() {
460
- const div = $('screenshare-area')!;
461
- if (!currentRoom || currentRoom.state !== RoomState.Connected) {
462
- div.style.display = 'none';
463
- return;
464
- }
465
- let participant: Participant | undefined;
466
- let screenSharePub: TrackPublication | undefined = currentRoom.localParticipant.getTrack(
467
- Track.Source.ScreenShare,
468
- );
469
- if (!screenSharePub) {
470
- currentRoom.participants.forEach((p) => {
471
- if (screenSharePub) {
472
- return;
473
- }
474
- participant = p;
475
- const pub = p.getTrack(Track.Source.ScreenShare);
476
- if (pub?.isSubscribed) {
477
- screenSharePub = pub;
478
- }
479
- });
480
- } else {
481
- participant = currentRoom.localParticipant;
482
- }
483
-
484
- if (screenSharePub && participant) {
485
- div.style.display = 'block';
486
- const videoElm = <HTMLVideoElement>$('screenshare-video');
487
- screenSharePub.videoTrack?.attach(videoElm);
488
- videoElm.onresize = () => {
489
- updateVideoSize(videoElm, <HTMLSpanElement>$('screenshare-resolution'));
490
- };
491
- const infoElm = $('screenshare-info')!;
492
- infoElm.innerHTML = `Screenshare from ${participant.identity}`;
493
- } else {
494
- div.style.display = 'none';
495
- }
496
- }
497
-
498
- function renderBitrate() {
499
- if (!currentRoom || currentRoom.state !== RoomState.Connected) {
500
- return;
501
- }
502
- const participants: Participant[] = [...currentRoom.participants.values()];
503
- participants.push(currentRoom.localParticipant);
504
-
505
- for (const p of participants) {
506
- const elm = $(`bitrate-${p.sid}`);
507
- let totalBitrate = 0;
508
- for (const t of p.tracks.values()) {
509
- if (t.track) {
510
- totalBitrate += t.track.currentBitrate;
511
- }
512
- }
513
- let displayText = '';
514
- if (totalBitrate > 0) {
515
- displayText = `${Math.round(totalBitrate / 1024).toLocaleString()} kbps`;
516
- }
517
- if (elm) {
518
- elm.innerHTML = displayText;
519
- }
520
- }
521
- }
522
-
523
- function updateVideoSize(element: HTMLVideoElement, target: HTMLElement) {
524
- target.innerHTML = `(${element.videoWidth}x${element.videoHeight})`;
525
- }
526
-
527
- function setButtonState(buttonId: string, buttonText: string, isActive: boolean) {
528
- const el = $(buttonId);
529
- if (!el) return;
530
-
531
- el.innerHTML = buttonText;
532
- if (isActive) {
533
- el.classList.add('active');
534
- } else {
535
- el.classList.remove('active');
536
- }
537
- }
538
-
539
- setTimeout(handleDevicesChanged, 100);
540
-
541
- function setButtonsForState(connected: boolean) {
542
- const connectedSet = [
543
- 'toggle-video-button',
544
- 'toggle-audio-button',
545
- 'share-screen-button',
546
- 'disconnect-ws-button',
547
- 'disconnect-room-button',
548
- 'flip-video-button',
549
- 'send-button',
550
- ];
551
- const disconnectedSet = ['connect-button'];
552
-
553
- const toRemove = connected ? connectedSet : disconnectedSet;
554
- const toAdd = connected ? disconnectedSet : connectedSet;
555
-
556
- toRemove.forEach((id) => $(id)?.removeAttribute('disabled'));
557
- toAdd.forEach((id) => $(id)?.setAttribute('disabled', 'true'));
558
- }
559
-
560
- const elementMapping: { [k: string]: MediaDeviceKind } = {
561
- 'video-input': 'videoinput',
562
- 'audio-input': 'audioinput',
563
- 'audio-output': 'audiooutput',
564
- };
565
- async function handleDevicesChanged() {
566
- Promise.all(Object.keys(elementMapping).map(async (id) => {
567
- const kind = elementMapping[id];
568
- if (!kind) {
569
- return;
570
- }
571
- const devices = await Room.getLocalDevices(kind);
572
- const element = <HTMLSelectElement>$(id);
573
- populateSelect(kind, element, devices, state.defaultDevices.get(kind));
574
- }));
575
- }
576
-
577
- function populateSelect(
578
- kind: MediaDeviceKind,
579
- element: HTMLSelectElement,
580
- devices: MediaDeviceInfo[],
581
- selectedDeviceId?: string,
582
- ) {
583
- // clear all elements
584
- element.innerHTML = '';
585
- const initialOption = document.createElement('option');
586
- if (kind === 'audioinput') {
587
- initialOption.text = 'Audio Input (default)';
588
- } else if (kind === 'videoinput') {
589
- initialOption.text = 'Video Input (default)';
590
- } else if (kind === 'audiooutput') {
591
- initialOption.text = 'Audio Output (default)';
592
- }
593
- element.appendChild(initialOption);
594
-
595
- for (const device of devices) {
596
- const option = document.createElement('option');
597
- option.text = device.label;
598
- option.value = device.deviceId;
599
- if (device.deviceId === selectedDeviceId) {
600
- option.selected = true;
601
- }
602
- element.appendChild(option);
603
- }
604
- }
605
-
606
- function updateButtonsForPublishState() {
607
- if (!currentRoom) {
608
- return;
609
- }
610
- const lp = currentRoom.localParticipant;
611
-
612
- // video
613
- setButtonState(
614
- 'toggle-video-button',
615
- `${lp.isCameraEnabled ? 'Disable' : 'Enable'} Video`,
616
- lp.isCameraEnabled,
617
- );
618
-
619
- // audio
620
- setButtonState(
621
- 'toggle-audio-button',
622
- `${lp.isMicrophoneEnabled ? 'Disable' : 'Enable'} Audio`,
623
- lp.isMicrophoneEnabled,
624
- );
625
-
626
- // screen share
627
- setButtonState(
628
- 'share-screen-button',
629
- lp.isScreenShareEnabled ? 'Stop Screen Share' : 'Share Screen',
630
- lp.isScreenShareEnabled,
631
- );
632
- }