livekit-client 1.1.1 → 1.1.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.
Files changed (31) hide show
  1. package/dist/livekit-client.esm.mjs +299 -122
  2. package/dist/livekit-client.esm.mjs.map +1 -1
  3. package/dist/livekit-client.umd.js +1 -1
  4. package/dist/livekit-client.umd.js.map +1 -1
  5. package/dist/src/proto/livekit_rtc.d.ts +7 -0
  6. package/dist/src/proto/livekit_rtc.d.ts.map +1 -1
  7. package/dist/src/room/Room.d.ts +2 -0
  8. package/dist/src/room/Room.d.ts.map +1 -1
  9. package/dist/src/room/participant/LocalParticipant.d.ts +7 -2
  10. package/dist/src/room/participant/LocalParticipant.d.ts.map +1 -1
  11. package/dist/src/room/participant/RemoteParticipant.d.ts.map +1 -1
  12. package/dist/src/room/track/LocalAudioTrack.d.ts +1 -1
  13. package/dist/src/room/track/LocalAudioTrack.d.ts.map +1 -1
  14. package/dist/src/room/track/LocalTrack.d.ts +4 -2
  15. package/dist/src/room/track/LocalTrack.d.ts.map +1 -1
  16. package/dist/src/room/track/LocalVideoTrack.d.ts +3 -2
  17. package/dist/src/room/track/LocalVideoTrack.d.ts.map +1 -1
  18. package/dist/src/room/track/RemoteVideoTrack.d.ts.map +1 -1
  19. package/dist/src/room/utils.d.ts.map +1 -1
  20. package/package.json +1 -1
  21. package/src/proto/livekit_rtc.ts +12 -0
  22. package/src/room/Room.ts +79 -30
  23. package/src/room/participant/LocalParticipant.ts +135 -61
  24. package/src/room/participant/RemoteParticipant.ts +5 -11
  25. package/src/room/participant/publishUtils.ts +2 -2
  26. package/src/room/track/LocalAudioTrack.ts +7 -3
  27. package/src/room/track/LocalTrack.ts +11 -2
  28. package/src/room/track/LocalVideoTrack.ts +41 -10
  29. package/src/room/track/RemoteVideoTrack.ts +30 -2
  30. package/src/room/track/create.ts +2 -2
  31. package/src/room/utils.ts +3 -2
@@ -24,6 +24,8 @@ export class SimulcastTrackInfo {
24
24
  }
25
25
  }
26
26
 
27
+ const refreshSubscribedCodecAfterNewCodec = 5000;
28
+
27
29
  export default class LocalVideoTrack extends LocalTrack {
28
30
  /* internal */
29
31
  signalClient?: SignalClient;
@@ -37,8 +39,14 @@ export default class LocalVideoTrack extends LocalTrack {
37
39
  SimulcastTrackInfo
38
40
  >();
39
41
 
40
- constructor(mediaTrack: MediaStreamTrack, constraints?: MediaTrackConstraints) {
41
- super(mediaTrack, Track.Kind.Video, constraints);
42
+ private subscribedCodecs?: SubscribedCodec[];
43
+
44
+ constructor(
45
+ mediaTrack: MediaStreamTrack,
46
+ constraints?: MediaTrackConstraints,
47
+ userProvidedTrack = true,
48
+ ) {
49
+ super(mediaTrack, Track.Kind.Video, constraints, userProvidedTrack);
42
50
  }
43
51
 
44
52
  get isSimulcast(): boolean {
@@ -88,7 +96,7 @@ export default class LocalVideoTrack extends LocalTrack {
88
96
  }
89
97
 
90
98
  async unmute(): Promise<LocalVideoTrack> {
91
- if (this.source === Track.Source.Camera) {
99
+ if (this.source === Track.Source.Camera && !this.isUserProvided) {
92
100
  log.debug('reacquiring camera track');
93
101
  await this.restartTrack();
94
102
  }
@@ -194,26 +202,48 @@ export default class LocalVideoTrack extends LocalTrack {
194
202
  return;
195
203
  }
196
204
  simulcastCodecInfo.sender = sender;
205
+
206
+ // browser will reenable disabled codec/layers after new codec has been published,
207
+ // so refresh subscribedCodecs after publish a new codec
208
+ setTimeout(() => {
209
+ if (this.subscribedCodecs) {
210
+ this.setPublishingCodecs(this.subscribedCodecs);
211
+ }
212
+ }, refreshSubscribedCodecAfterNewCodec);
197
213
  }
198
214
 
199
215
  /**
200
216
  * @internal
201
217
  * Sets codecs that should be publishing
202
218
  */
203
- async setPublishingCodecs(codecs: SubscribedCodec[]) {
204
- log.debug('setting publishing codecs', codecs);
219
+ async setPublishingCodecs(codecs: SubscribedCodec[]): Promise<VideoCodec[]> {
220
+ log.debug('setting publishing codecs', {
221
+ codecs,
222
+ currentCodec: this.codec,
223
+ });
224
+ // only enable simulcast codec for preference codec setted
225
+ if (!this.codec && codecs.length > 0) {
226
+ await this.setPublishingLayers(codecs[0].qualities);
227
+ return [];
228
+ }
205
229
 
230
+ this.subscribedCodecs = codecs;
231
+
232
+ const newCodecs: VideoCodec[] = [];
206
233
  for await (const codec of codecs) {
207
- if (this.codec === codec.codec) {
234
+ if (!this.codec || this.codec === codec.codec) {
208
235
  await this.setPublishingLayers(codec.qualities);
209
236
  } else {
210
237
  const simulcastCodecInfo = this.simulcastCodecs.get(codec.codec as VideoCodec);
211
238
  log.debug(`try setPublishingCodec for ${codec.codec}`, simulcastCodecInfo);
212
239
  if (!simulcastCodecInfo || !simulcastCodecInfo.sender) {
213
- return;
214
- }
215
-
216
- if (simulcastCodecInfo.encodings) {
240
+ for (const q of codec.qualities) {
241
+ if (q.enabled) {
242
+ newCodecs.push(codec.codec as VideoCodec);
243
+ break;
244
+ }
245
+ }
246
+ } else if (simulcastCodecInfo.encodings) {
217
247
  log.debug(`try setPublishingLayersForSender ${codec.codec}`);
218
248
  await setPublishingLayersForSender(
219
249
  simulcastCodecInfo.sender,
@@ -223,6 +253,7 @@ export default class LocalVideoTrack extends LocalTrack {
223
253
  }
224
254
  }
225
255
  }
256
+ return newCodecs;
226
257
  }
227
258
 
228
259
  /**
@@ -110,6 +110,8 @@ export default class RemoteVideoTrack extends RemoteTrack {
110
110
  // the tab comes into focus for the first time.
111
111
  this.debouncedHandleResize();
112
112
  this.updateVisibility();
113
+ } else {
114
+ log.warn('visibility resize observer not triggered');
113
115
  }
114
116
  }
115
117
 
@@ -294,9 +296,9 @@ class HTMLElementInfo implements ElementInfo {
294
296
 
295
297
  handleVisibilityChanged?: () => void;
296
298
 
297
- constructor(element: HTMLMediaElement, visible: boolean = false) {
299
+ constructor(element: HTMLMediaElement, visible?: boolean) {
298
300
  this.element = element;
299
- this.visible = visible;
301
+ this.visible = visible ?? isElementInViewport(element);
300
302
  this.visibilityChangedAt = 0;
301
303
  }
302
304
 
@@ -332,3 +334,29 @@ class HTMLElementInfo implements ElementInfo {
332
334
  getResizeObserver()?.unobserve(this.element);
333
335
  }
334
336
  }
337
+
338
+ // does not account for occlusion by other elements
339
+ function isElementInViewport(el: HTMLElement) {
340
+ let top = el.offsetTop;
341
+ let left = el.offsetLeft;
342
+ const width = el.offsetWidth;
343
+ const height = el.offsetHeight;
344
+ const { hidden } = el;
345
+ const { opacity, display } = getComputedStyle(el);
346
+
347
+ while (el.offsetParent) {
348
+ el = el.offsetParent as HTMLElement;
349
+ top += el.offsetTop;
350
+ left += el.offsetLeft;
351
+ }
352
+
353
+ return (
354
+ top < window.pageYOffset + window.innerHeight &&
355
+ left < window.pageXOffset + window.innerWidth &&
356
+ top + height > window.pageYOffset &&
357
+ left + width > window.pageXOffset &&
358
+ !hidden &&
359
+ (opacity !== '' ? parseFloat(opacity) > 0 : true) &&
360
+ display !== 'none'
361
+ );
362
+ }
@@ -110,11 +110,11 @@ export async function createLocalScreenTracks(
110
110
  if (tracks.length === 0) {
111
111
  throw new TrackInvalidError('no video track found');
112
112
  }
113
- const screenVideo = new LocalVideoTrack(tracks[0]);
113
+ const screenVideo = new LocalVideoTrack(tracks[0], undefined, false);
114
114
  screenVideo.source = Track.Source.ScreenShare;
115
115
  const localTracks: Array<LocalTrack> = [screenVideo];
116
116
  if (stream.getAudioTracks().length > 0) {
117
- const screenAudio = new LocalAudioTrack(stream.getAudioTracks()[0]);
117
+ const screenAudio = new LocalAudioTrack(stream.getAudioTracks()[0], undefined, false);
118
118
  screenAudio.source = Track.Source.ScreenShareAudio;
119
119
  localTracks.push(screenAudio);
120
120
  }
package/src/room/utils.ts CHANGED
@@ -81,8 +81,9 @@ let emptyVideoStreamTrack: MediaStreamTrack | undefined;
81
81
  export function getEmptyVideoStreamTrack() {
82
82
  if (!emptyVideoStreamTrack) {
83
83
  const canvas = document.createElement('canvas');
84
- canvas.width = 2;
85
- canvas.height = 2;
84
+ // the canvas size is set to 16, because electron apps seem to fail with smaller values
85
+ canvas.width = 16;
86
+ canvas.height = 16;
86
87
  canvas.getContext('2d')?.fillRect(0, 0, canvas.width, canvas.height);
87
88
  // @ts-ignore
88
89
  const emptyStream = canvas.captureStream();