livekit-client 1.5.0 → 1.6.1
Sign up to get free protection for your applications and to get access to all the features.
- package/dist/livekit-client.esm.mjs +2257 -5488
- package/dist/livekit-client.esm.mjs.map +1 -1
- package/dist/livekit-client.umd.js +1 -1
- package/dist/livekit-client.umd.js.map +1 -1
- package/dist/src/api/SignalClient.d.ts +3 -2
- package/dist/src/api/SignalClient.d.ts.map +1 -1
- package/dist/src/connectionHelper/ConnectionCheck.d.ts +1 -1
- package/dist/src/connectionHelper/ConnectionCheck.d.ts.map +1 -1
- package/dist/src/connectionHelper/checks/Checker.d.ts +4 -4
- package/dist/src/connectionHelper/checks/Checker.d.ts.map +1 -1
- package/dist/src/index.d.ts +3 -2
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/logger.d.ts +3 -3
- package/dist/src/logger.d.ts.map +1 -1
- package/dist/src/options.d.ts +4 -1
- package/dist/src/options.d.ts.map +1 -1
- package/dist/src/proto/google/protobuf/timestamp.d.ts +4 -4
- package/dist/src/proto/google/protobuf/timestamp.d.ts.map +1 -1
- package/dist/src/proto/livekit_models.d.ts +4 -4
- package/dist/src/proto/livekit_models.d.ts.map +1 -1
- package/dist/src/proto/livekit_rtc.d.ts +4 -4
- package/dist/src/proto/livekit_rtc.d.ts.map +1 -1
- package/dist/src/room/PCTransport.d.ts +7 -1
- package/dist/src/room/PCTransport.d.ts.map +1 -1
- package/dist/src/room/RTCEngine.d.ts +10 -4
- package/dist/src/room/RTCEngine.d.ts.map +1 -1
- package/dist/src/room/Room.d.ts +21 -4
- package/dist/src/room/Room.d.ts.map +1 -1
- package/dist/src/room/events.d.ts +5 -0
- package/dist/src/room/events.d.ts.map +1 -1
- package/dist/src/room/participant/LocalParticipant.d.ts +3 -2
- package/dist/src/room/participant/LocalParticipant.d.ts.map +1 -1
- package/dist/src/room/participant/Participant.d.ts +1 -1
- package/dist/src/room/participant/Participant.d.ts.map +1 -1
- package/dist/src/room/track/LocalTrack.d.ts +1 -0
- package/dist/src/room/track/LocalTrack.d.ts.map +1 -1
- package/dist/src/room/track/RemoteVideoTrack.d.ts.map +1 -1
- package/dist/src/room/track/Track.d.ts +2 -1
- package/dist/src/room/track/Track.d.ts.map +1 -1
- package/dist/src/room/track/TrackPublication.d.ts +1 -1
- package/dist/src/room/track/TrackPublication.d.ts.map +1 -1
- package/dist/src/room/track/options.d.ts +3 -3
- package/dist/src/room/track/options.d.ts.map +1 -1
- package/dist/src/room/track/types.d.ts +3 -3
- package/dist/src/room/track/types.d.ts.map +1 -1
- package/dist/src/room/types.d.ts +13 -0
- package/dist/src/room/types.d.ts.map +1 -0
- package/dist/src/room/utils.d.ts +44 -0
- package/dist/src/room/utils.d.ts.map +1 -1
- package/dist/ts4.2/src/api/SignalClient.d.ts +3 -2
- package/dist/ts4.2/src/connectionHelper/ConnectionCheck.d.ts +1 -1
- package/dist/ts4.2/src/connectionHelper/checks/Checker.d.ts +4 -4
- package/dist/ts4.2/src/index.d.ts +3 -2
- package/dist/ts4.2/src/logger.d.ts +3 -3
- package/dist/ts4.2/src/options.d.ts +4 -1
- package/dist/ts4.2/src/proto/google/protobuf/timestamp.d.ts +4 -4
- package/dist/ts4.2/src/proto/livekit_models.d.ts +4 -4
- package/dist/ts4.2/src/proto/livekit_rtc.d.ts +4 -4
- package/dist/ts4.2/src/room/PCTransport.d.ts +7 -1
- package/dist/ts4.2/src/room/RTCEngine.d.ts +10 -4
- package/dist/ts4.2/src/room/Room.d.ts +21 -4
- package/dist/ts4.2/src/room/events.d.ts +5 -0
- package/dist/ts4.2/src/room/participant/LocalParticipant.d.ts +3 -2
- package/dist/ts4.2/src/room/participant/Participant.d.ts +1 -1
- package/dist/ts4.2/src/room/track/LocalTrack.d.ts +1 -0
- package/dist/ts4.2/src/room/track/Track.d.ts +2 -1
- package/dist/ts4.2/src/room/track/TrackPublication.d.ts +1 -1
- package/dist/ts4.2/src/room/track/options.d.ts +3 -3
- package/dist/ts4.2/src/room/track/types.d.ts +3 -3
- package/dist/ts4.2/src/room/types.d.ts +13 -0
- package/dist/ts4.2/src/room/utils.d.ts +44 -0
- package/package.json +23 -23
- package/src/api/SignalClient.ts +40 -16
- package/src/connectionHelper/checks/turn.ts +1 -1
- package/src/connectionHelper/checks/websocket.ts +1 -1
- package/src/index.ts +5 -0
- package/src/options.ts +5 -1
- package/src/room/PCTransport.ts +11 -1
- package/src/room/RTCEngine.ts +111 -49
- package/src/room/Room.ts +234 -63
- package/src/room/events.ts +5 -0
- package/src/room/participant/LocalParticipant.ts +46 -22
- package/src/room/participant/RemoteParticipant.ts +5 -5
- package/src/room/participant/publishUtils.ts +1 -1
- package/src/room/track/LocalAudioTrack.ts +1 -1
- package/src/room/track/LocalTrack.ts +20 -1
- package/src/room/track/LocalVideoTrack.ts +1 -1
- package/src/room/track/RemoteVideoTrack.ts +4 -0
- package/src/room/track/Track.ts +22 -5
- package/src/room/types.ts +12 -0
- package/src/room/utils.ts +150 -12
@@ -118,6 +118,10 @@ export default class RemoteVideoTrack extends RemoteTrack {
|
|
118
118
|
* @internal
|
119
119
|
*/
|
120
120
|
stopObservingElementInfo(elementInfo: ElementInfo) {
|
121
|
+
if (!this.isAdaptiveStream) {
|
122
|
+
log.warn('stopObservingElementInfo ignored');
|
123
|
+
return;
|
124
|
+
}
|
121
125
|
const stopElementInfos = this.elementInfos.filter((info) => info === elementInfo);
|
122
126
|
for (const info of stopElementInfos) {
|
123
127
|
info.stopObserving();
|
package/src/room/track/Track.ts
CHANGED
@@ -121,7 +121,9 @@ export abstract class Track extends (EventEmitter as new () => TypedEventEmitter
|
|
121
121
|
// we'll want to re-attach it in that case
|
122
122
|
attachToElement(this._mediaStreamTrack, element);
|
123
123
|
|
124
|
-
|
124
|
+
// handle auto playback failures
|
125
|
+
const allMediaStreamTracks = (element.srcObject as MediaStream).getTracks();
|
126
|
+
if (allMediaStreamTracks.some((tr) => tr.kind === 'audio')) {
|
125
127
|
// manually play audio to detect audio playback status
|
126
128
|
element
|
127
129
|
.play()
|
@@ -130,6 +132,17 @@ export abstract class Track extends (EventEmitter as new () => TypedEventEmitter
|
|
130
132
|
})
|
131
133
|
.catch((e) => {
|
132
134
|
this.emit(TrackEvent.AudioPlaybackFailed, e);
|
135
|
+
// If audio playback isn't allowed make sure we still play back the video
|
136
|
+
if (
|
137
|
+
element &&
|
138
|
+
allMediaStreamTracks.some((tr) => tr.kind === 'video') &&
|
139
|
+
e.name === 'NotAllowedError'
|
140
|
+
) {
|
141
|
+
element.muted = true;
|
142
|
+
element.play().catch(() => {
|
143
|
+
// catch for Safari, exceeded options at this point to automatically play the media element
|
144
|
+
});
|
145
|
+
}
|
133
146
|
});
|
134
147
|
}
|
135
148
|
|
@@ -259,6 +272,13 @@ export function attachToElement(track: MediaStreamTrack, element: HTMLMediaEleme
|
|
259
272
|
mediaStream.addTrack(track);
|
260
273
|
}
|
261
274
|
|
275
|
+
element.autoplay = true;
|
276
|
+
// In case there are no audio tracks present on the mediastream, we set the element as muted to ensure autoplay works
|
277
|
+
element.muted = mediaStream.getAudioTracks().length === 0;
|
278
|
+
if (element instanceof HTMLVideoElement) {
|
279
|
+
element.playsInline = true;
|
280
|
+
}
|
281
|
+
|
262
282
|
// avoid flicker
|
263
283
|
if (element.srcObject !== mediaStream) {
|
264
284
|
element.srcObject = mediaStream;
|
@@ -280,10 +300,6 @@ export function attachToElement(track: MediaStreamTrack, element: HTMLMediaEleme
|
|
280
300
|
}, 0);
|
281
301
|
}
|
282
302
|
}
|
283
|
-
element.autoplay = true;
|
284
|
-
if (element instanceof HTMLVideoElement) {
|
285
|
-
element.playsInline = true;
|
286
|
-
}
|
287
303
|
}
|
288
304
|
|
289
305
|
/** @internal */
|
@@ -398,6 +414,7 @@ export type TrackEventCallbacks = {
|
|
398
414
|
message: () => void;
|
399
415
|
muted: (track?: any) => void;
|
400
416
|
unmuted: (track?: any) => void;
|
417
|
+
restarted: (track?: any) => void;
|
401
418
|
ended: (track?: any) => void;
|
402
419
|
updateSettings: () => void;
|
403
420
|
updateSubscription: () => void;
|
package/src/room/utils.ts
CHANGED
@@ -1,6 +1,9 @@
|
|
1
1
|
import UAParser from 'ua-parser-js';
|
2
2
|
import { ClientInfo, ClientInfo_SDK } from '../proto/livekit_models';
|
3
3
|
import { protocolVersion, version } from '../version';
|
4
|
+
import type LocalAudioTrack from './track/LocalAudioTrack';
|
5
|
+
import type RemoteAudioTrack from './track/RemoteAudioTrack';
|
6
|
+
import { getNewAudioContext } from './track/utils';
|
4
7
|
|
5
8
|
const separator = '|';
|
6
9
|
|
@@ -178,22 +181,41 @@ let emptyVideoStreamTrack: MediaStreamTrack | undefined;
|
|
178
181
|
|
179
182
|
export function getEmptyVideoStreamTrack() {
|
180
183
|
if (!emptyVideoStreamTrack) {
|
181
|
-
|
182
|
-
// the canvas size is set to 16, because electron apps seem to fail with smaller values
|
183
|
-
canvas.width = 16;
|
184
|
-
canvas.height = 16;
|
185
|
-
canvas.getContext('2d')?.fillRect(0, 0, canvas.width, canvas.height);
|
186
|
-
// @ts-ignore
|
187
|
-
const emptyStream = canvas.captureStream();
|
188
|
-
[emptyVideoStreamTrack] = emptyStream.getTracks();
|
189
|
-
if (!emptyVideoStreamTrack) {
|
190
|
-
throw Error('Could not get empty media stream video track');
|
191
|
-
}
|
192
|
-
emptyVideoStreamTrack.enabled = false;
|
184
|
+
emptyVideoStreamTrack = createDummyVideoStreamTrack();
|
193
185
|
}
|
194
186
|
return emptyVideoStreamTrack;
|
195
187
|
}
|
196
188
|
|
189
|
+
export function createDummyVideoStreamTrack(
|
190
|
+
width: number = 16,
|
191
|
+
height: number = 16,
|
192
|
+
enabled: boolean = false,
|
193
|
+
paintContent: boolean = false,
|
194
|
+
) {
|
195
|
+
const canvas = document.createElement('canvas');
|
196
|
+
// the canvas size is set to 16 by default, because electron apps seem to fail with smaller values
|
197
|
+
canvas.width = width;
|
198
|
+
canvas.height = height;
|
199
|
+
const ctx = canvas.getContext('2d');
|
200
|
+
ctx?.fillRect(0, 0, canvas.width, canvas.height);
|
201
|
+
if (paintContent && ctx) {
|
202
|
+
ctx.beginPath();
|
203
|
+
ctx.arc(width / 2, height / 2, 50, 0, Math.PI * 2, true);
|
204
|
+
ctx.closePath();
|
205
|
+
ctx.fillStyle = 'grey';
|
206
|
+
ctx.fill();
|
207
|
+
}
|
208
|
+
// @ts-ignore
|
209
|
+
const dummyStream = canvas.captureStream();
|
210
|
+
const [dummyTrack] = dummyStream.getTracks();
|
211
|
+
if (!dummyTrack) {
|
212
|
+
throw Error('Could not get empty media stream video track');
|
213
|
+
}
|
214
|
+
dummyTrack.enabled = enabled;
|
215
|
+
|
216
|
+
return dummyTrack;
|
217
|
+
}
|
218
|
+
|
197
219
|
let emptyAudioStreamTrack: MediaStreamTrack | undefined;
|
198
220
|
|
199
221
|
export function getEmptyAudioStreamTrack() {
|
@@ -236,3 +258,119 @@ export class Future<T> {
|
|
236
258
|
}).finally(() => this.onFinally?.());
|
237
259
|
}
|
238
260
|
}
|
261
|
+
|
262
|
+
export type AudioAnalyserOptions = {
|
263
|
+
/**
|
264
|
+
* If set to true, the analyser will use a cloned version of the underlying mediastreamtrack, which won't be impacted by muting the track.
|
265
|
+
* Useful for local tracks when implementing things like "seems like you're muted, but trying to speak".
|
266
|
+
* Defaults to false
|
267
|
+
*/
|
268
|
+
cloneTrack?: boolean;
|
269
|
+
/**
|
270
|
+
* see https://developer.mozilla.org/en-US/docs/Web/API/AnalyserNode/fftSize
|
271
|
+
*/
|
272
|
+
fftSize?: number;
|
273
|
+
/**
|
274
|
+
* see https://developer.mozilla.org/en-US/docs/Web/API/AnalyserNode/smoothingTimeConstant
|
275
|
+
*/
|
276
|
+
smoothingTimeConstant?: number;
|
277
|
+
/**
|
278
|
+
* see https://developer.mozilla.org/en-US/docs/Web/API/AnalyserNode/minDecibels
|
279
|
+
*/
|
280
|
+
minDecibels?: number;
|
281
|
+
/**
|
282
|
+
* see https://developer.mozilla.org/en-US/docs/Web/API/AnalyserNode/maxDecibels
|
283
|
+
*/
|
284
|
+
maxDecibels?: number;
|
285
|
+
};
|
286
|
+
|
287
|
+
/**
|
288
|
+
* Creates and returns an analyser web audio node that is attached to the provided track.
|
289
|
+
* Additionally returns a convenience method `calculateVolume` to perform instant volume readings on that track.
|
290
|
+
* Call the returned `cleanup` function to close the audioContext that has been created for the instance of this helper
|
291
|
+
*/
|
292
|
+
export function createAudioAnalyser(
|
293
|
+
track: LocalAudioTrack | RemoteAudioTrack,
|
294
|
+
options?: AudioAnalyserOptions,
|
295
|
+
) {
|
296
|
+
const opts = {
|
297
|
+
cloneTrack: false,
|
298
|
+
fftSize: 2048,
|
299
|
+
smoothingTimeConstant: 0.8,
|
300
|
+
minDecibels: -100,
|
301
|
+
maxDecibels: -80,
|
302
|
+
...options,
|
303
|
+
};
|
304
|
+
const audioContext = getNewAudioContext();
|
305
|
+
|
306
|
+
if (!audioContext) {
|
307
|
+
throw new Error('Audio Context not supported on this browser');
|
308
|
+
}
|
309
|
+
const streamTrack = opts.cloneTrack ? track.mediaStreamTrack.clone() : track.mediaStreamTrack;
|
310
|
+
const mediaStreamSource = audioContext.createMediaStreamSource(new MediaStream([streamTrack]));
|
311
|
+
const analyser = audioContext.createAnalyser();
|
312
|
+
analyser.minDecibels = opts.minDecibels;
|
313
|
+
analyser.maxDecibels = opts.maxDecibels;
|
314
|
+
analyser.fftSize = opts.fftSize;
|
315
|
+
analyser.smoothingTimeConstant = opts.smoothingTimeConstant;
|
316
|
+
|
317
|
+
mediaStreamSource.connect(analyser);
|
318
|
+
const dataArray = new Uint8Array(analyser.frequencyBinCount);
|
319
|
+
|
320
|
+
/**
|
321
|
+
* Calculates the current volume of the track in the range from 0 to 1
|
322
|
+
*/
|
323
|
+
const calculateVolume = () => {
|
324
|
+
analyser.getByteFrequencyData(dataArray);
|
325
|
+
let sum = 0;
|
326
|
+
for (const amplitude of dataArray) {
|
327
|
+
sum += Math.pow(amplitude / 255, 2);
|
328
|
+
}
|
329
|
+
const volume = Math.sqrt(sum / dataArray.length);
|
330
|
+
return volume;
|
331
|
+
};
|
332
|
+
|
333
|
+
const cleanup = () => {
|
334
|
+
audioContext.close();
|
335
|
+
if (opts.cloneTrack) {
|
336
|
+
streamTrack.stop();
|
337
|
+
}
|
338
|
+
};
|
339
|
+
|
340
|
+
return { calculateVolume, analyser, cleanup };
|
341
|
+
}
|
342
|
+
|
343
|
+
export class Mutex {
|
344
|
+
private _locking: Promise<void>;
|
345
|
+
|
346
|
+
private _locks: number;
|
347
|
+
|
348
|
+
constructor() {
|
349
|
+
this._locking = Promise.resolve();
|
350
|
+
this._locks = 0;
|
351
|
+
}
|
352
|
+
|
353
|
+
isLocked() {
|
354
|
+
return this._locks > 0;
|
355
|
+
}
|
356
|
+
|
357
|
+
lock() {
|
358
|
+
this._locks += 1;
|
359
|
+
|
360
|
+
let unlockNext: () => void;
|
361
|
+
|
362
|
+
const willLock = new Promise<void>(
|
363
|
+
(resolve) =>
|
364
|
+
(unlockNext = () => {
|
365
|
+
this._locks -= 1;
|
366
|
+
resolve();
|
367
|
+
}),
|
368
|
+
);
|
369
|
+
|
370
|
+
const willUnlock = this._locking.then(() => unlockNext);
|
371
|
+
|
372
|
+
this._locking = this._locking.then(() => willLock);
|
373
|
+
|
374
|
+
return willUnlock;
|
375
|
+
}
|
376
|
+
}
|