livekit-client 2.1.5 → 2.3.0
Sign up to get free protection for your applications and to get access to all the features.
- package/README.md +2 -6
- package/dist/livekit-client.esm.mjs +175 -77
- 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/index.d.ts +2 -2
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/room/DeviceManager.d.ts.map +1 -1
- package/dist/src/room/RTCEngine.d.ts +2 -2
- package/dist/src/room/RTCEngine.d.ts.map +1 -1
- package/dist/src/room/Room.d.ts +7 -2
- package/dist/src/room/Room.d.ts.map +1 -1
- package/dist/src/room/events.d.ts +18 -1
- package/dist/src/room/events.d.ts.map +1 -1
- package/dist/src/room/participant/LocalParticipant.d.ts.map +1 -1
- package/dist/src/room/participant/Participant.d.ts +6 -3
- package/dist/src/room/participant/Participant.d.ts.map +1 -1
- package/dist/src/room/participant/RemoteParticipant.d.ts +3 -3
- package/dist/src/room/participant/RemoteParticipant.d.ts.map +1 -1
- package/dist/src/room/participant/publishUtils.d.ts.map +1 -1
- package/dist/src/room/track/LocalTrack.d.ts +1 -1
- package/dist/src/room/track/LocalTrack.d.ts.map +1 -1
- package/dist/src/room/track/LocalVideoTrack.d.ts +1 -1
- package/dist/src/room/track/LocalVideoTrack.d.ts.map +1 -1
- package/dist/src/room/track/create.d.ts.map +1 -1
- package/dist/src/room/track/options.d.ts +9 -0
- package/dist/src/room/track/options.d.ts.map +1 -1
- package/dist/ts4.2/src/index.d.ts +2 -2
- package/dist/ts4.2/src/room/RTCEngine.d.ts +2 -2
- package/dist/ts4.2/src/room/Room.d.ts +7 -2
- package/dist/ts4.2/src/room/events.d.ts +18 -1
- package/dist/ts4.2/src/room/participant/Participant.d.ts +7 -3
- package/dist/ts4.2/src/room/participant/RemoteParticipant.d.ts +3 -3
- package/dist/ts4.2/src/room/track/LocalTrack.d.ts +1 -1
- package/dist/ts4.2/src/room/track/LocalVideoTrack.d.ts +1 -1
- package/dist/ts4.2/src/room/track/options.d.ts +9 -0
- package/package.json +9 -9
- package/src/index.ts +2 -1
- package/src/room/DeviceManager.test.ts +105 -0
- package/src/room/DeviceManager.ts +11 -6
- package/src/room/RTCEngine.ts +23 -6
- package/src/room/Room.ts +48 -11
- package/src/room/defaults.ts +1 -1
- package/src/room/events.ts +21 -1
- package/src/room/participant/LocalParticipant.ts +36 -25
- package/src/room/participant/Participant.ts +14 -1
- package/src/room/participant/RemoteParticipant.ts +17 -4
- package/src/room/participant/publishUtils.ts +4 -0
- package/src/room/track/LocalTrack.ts +14 -10
- package/src/room/track/LocalVideoTrack.ts +4 -1
- package/src/room/track/create.ts +37 -27
- package/src/room/track/options.ts +15 -0
@@ -30,6 +30,7 @@ import type {
|
|
30
30
|
VideoCaptureOptions,
|
31
31
|
} from '../track/options';
|
32
32
|
import { ScreenSharePresets, VideoPresets, isBackupCodec } from '../track/options';
|
33
|
+
import type { TrackProcessor } from '../track/processor/types';
|
33
34
|
import {
|
34
35
|
constraintsForOptions,
|
35
36
|
getLogContextFromTrack,
|
@@ -394,13 +395,13 @@ export default class LocalParticipant extends Participant {
|
|
394
395
|
* @returns
|
395
396
|
*/
|
396
397
|
async createTracks(options?: CreateLocalTracksOptions): Promise<LocalTrack[]> {
|
397
|
-
const
|
398
|
+
const mergedOptions = mergeDefaultOptions(
|
398
399
|
options,
|
399
400
|
this.roomOptions?.audioCaptureDefaults,
|
400
401
|
this.roomOptions?.videoCaptureDefaults,
|
401
402
|
);
|
402
403
|
|
403
|
-
const constraints = constraintsForOptions(
|
404
|
+
const constraints = constraintsForOptions(mergedOptions);
|
404
405
|
let stream: MediaStream | undefined;
|
405
406
|
try {
|
406
407
|
stream = await navigator.mediaDevices.getUserMedia(constraints);
|
@@ -425,29 +426,39 @@ export default class LocalParticipant extends Participant {
|
|
425
426
|
this.cameraError = undefined;
|
426
427
|
}
|
427
428
|
|
428
|
-
return
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
trackOptions
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
track.
|
445
|
-
|
446
|
-
track.
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
429
|
+
return Promise.all(
|
430
|
+
stream.getTracks().map(async (mediaStreamTrack) => {
|
431
|
+
const isAudio = mediaStreamTrack.kind === 'audio';
|
432
|
+
let trackOptions = isAudio ? mergedOptions!.audio : mergedOptions!.video;
|
433
|
+
if (typeof trackOptions === 'boolean' || !trackOptions) {
|
434
|
+
trackOptions = {};
|
435
|
+
}
|
436
|
+
let trackConstraints: MediaTrackConstraints | undefined;
|
437
|
+
const conOrBool = isAudio ? constraints.audio : constraints.video;
|
438
|
+
if (typeof conOrBool !== 'boolean') {
|
439
|
+
trackConstraints = conOrBool;
|
440
|
+
}
|
441
|
+
const track = mediaTrackToLocalTrack(mediaStreamTrack, trackConstraints, {
|
442
|
+
loggerName: this.roomOptions.loggerName,
|
443
|
+
loggerContextCb: () => this.logContext,
|
444
|
+
});
|
445
|
+
if (track.kind === Track.Kind.Video) {
|
446
|
+
track.source = Track.Source.Camera;
|
447
|
+
} else if (track.kind === Track.Kind.Audio) {
|
448
|
+
track.source = Track.Source.Microphone;
|
449
|
+
track.setAudioContext(this.audioContext);
|
450
|
+
}
|
451
|
+
track.mediaStream = stream;
|
452
|
+
if (trackOptions.processor) {
|
453
|
+
if (track instanceof LocalAudioTrack) {
|
454
|
+
await track.setProcessor(trackOptions.processor as TrackProcessor<Track.Kind.Audio>);
|
455
|
+
} else {
|
456
|
+
await track.setProcessor(trackOptions.processor as TrackProcessor<Track.Kind.Video>);
|
457
|
+
}
|
458
|
+
}
|
459
|
+
return track;
|
460
|
+
}),
|
461
|
+
);
|
451
462
|
}
|
452
463
|
|
453
464
|
/**
|
@@ -1,8 +1,10 @@
|
|
1
1
|
import {
|
2
2
|
DataPacket_Kind,
|
3
3
|
ParticipantInfo,
|
4
|
+
ParticipantInfo_Kind as ParticipantKind,
|
4
5
|
ParticipantPermission,
|
5
6
|
ConnectionQuality as ProtoQuality,
|
7
|
+
type SipDTMF,
|
6
8
|
SubscriptionError,
|
7
9
|
} from '@livekit/protocol';
|
8
10
|
import { EventEmitter } from 'events';
|
@@ -45,6 +47,8 @@ function qualityFromProto(q: ProtoQuality): ConnectionQuality {
|
|
45
47
|
}
|
46
48
|
}
|
47
49
|
|
50
|
+
export { ParticipantKind };
|
51
|
+
|
48
52
|
export default class Participant extends (EventEmitter as new () => TypedEmitter<ParticipantEventCallbacks>) {
|
49
53
|
protected participantInfo?: ParticipantInfo;
|
50
54
|
|
@@ -77,6 +81,8 @@ export default class Participant extends (EventEmitter as new () => TypedEmitter
|
|
77
81
|
|
78
82
|
permissions?: ParticipantPermission;
|
79
83
|
|
84
|
+
protected _kind: ParticipantKind;
|
85
|
+
|
80
86
|
private _connectionQuality: ConnectionQuality = ConnectionQuality.Unknown;
|
81
87
|
|
82
88
|
protected audioContext?: AudioContext;
|
@@ -99,7 +105,11 @@ export default class Participant extends (EventEmitter as new () => TypedEmitter
|
|
99
105
|
}
|
100
106
|
|
101
107
|
get isAgent() {
|
102
|
-
return this.permissions?.agent
|
108
|
+
return this.permissions?.agent || this.kind === ParticipantKind.AGENT;
|
109
|
+
}
|
110
|
+
|
111
|
+
get kind() {
|
112
|
+
return this._kind;
|
103
113
|
}
|
104
114
|
|
105
115
|
/** @internal */
|
@@ -109,6 +119,7 @@ export default class Participant extends (EventEmitter as new () => TypedEmitter
|
|
109
119
|
name?: string,
|
110
120
|
metadata?: string,
|
111
121
|
loggerOptions?: LoggerOptions,
|
122
|
+
kind: ParticipantKind = ParticipantKind.STANDARD,
|
112
123
|
) {
|
113
124
|
super();
|
114
125
|
|
@@ -123,6 +134,7 @@ export default class Participant extends (EventEmitter as new () => TypedEmitter
|
|
123
134
|
this.audioTrackPublications = new Map();
|
124
135
|
this.videoTrackPublications = new Map();
|
125
136
|
this.trackPublications = new Map();
|
137
|
+
this._kind = kind;
|
126
138
|
}
|
127
139
|
|
128
140
|
getTrackPublications(): TrackPublication[] {
|
@@ -329,6 +341,7 @@ export type ParticipantEventCallbacks = {
|
|
329
341
|
participantMetadataChanged: (prevMetadata: string | undefined, participant?: any) => void;
|
330
342
|
participantNameChanged: (name: string) => void;
|
331
343
|
dataReceived: (payload: Uint8Array, kind: DataPacket_Kind) => void;
|
344
|
+
sipDTMFReceived: (dtmf: SipDTMF) => void;
|
332
345
|
transcriptionReceived: (
|
333
346
|
transcription: TranscriptionSegment[],
|
334
347
|
publication?: TrackPublication,
|
@@ -16,7 +16,7 @@ import type { AudioOutputOptions } from '../track/options';
|
|
16
16
|
import type { AdaptiveStreamSettings } from '../track/types';
|
17
17
|
import { getLogContextFromTrack } from '../track/utils';
|
18
18
|
import type { LoggerOptions } from '../types';
|
19
|
-
import Participant from './Participant';
|
19
|
+
import Participant, { ParticipantKind } from './Participant';
|
20
20
|
import type { ParticipantEventCallbacks } from './Participant';
|
21
21
|
|
22
22
|
export default class RemoteParticipant extends Participant {
|
@@ -33,8 +33,20 @@ export default class RemoteParticipant extends Participant {
|
|
33
33
|
private audioOutput?: AudioOutputOptions;
|
34
34
|
|
35
35
|
/** @internal */
|
36
|
-
static fromParticipantInfo(
|
37
|
-
|
36
|
+
static fromParticipantInfo(
|
37
|
+
signalClient: SignalClient,
|
38
|
+
pi: ParticipantInfo,
|
39
|
+
loggerOptions: LoggerOptions,
|
40
|
+
): RemoteParticipant {
|
41
|
+
return new RemoteParticipant(
|
42
|
+
signalClient,
|
43
|
+
pi.sid,
|
44
|
+
pi.identity,
|
45
|
+
pi.name,
|
46
|
+
pi.metadata,
|
47
|
+
loggerOptions,
|
48
|
+
pi.kind,
|
49
|
+
);
|
38
50
|
}
|
39
51
|
|
40
52
|
protected get logContext() {
|
@@ -53,8 +65,9 @@ export default class RemoteParticipant extends Participant {
|
|
53
65
|
name?: string,
|
54
66
|
metadata?: string,
|
55
67
|
loggerOptions?: LoggerOptions,
|
68
|
+
kind: ParticipantKind = ParticipantKind.STANDARD,
|
56
69
|
) {
|
57
|
-
super(sid, identity || '', name, metadata, loggerOptions);
|
70
|
+
super(sid, identity || '', name, metadata, loggerOptions, kind);
|
58
71
|
this.signalClient = signalClient;
|
59
72
|
this.trackPublications = new Map();
|
60
73
|
this.audioTrackPublications = new Map();
|
@@ -149,6 +149,10 @@ export function computeVideoEncodings(
|
|
149
149
|
const browser = getBrowser();
|
150
150
|
if (
|
151
151
|
isSafari() ||
|
152
|
+
// Even tho RN runs M114, it does not produce SVC layers when a single encoding
|
153
|
+
// is provided. So we'll use the legacy SVC specification for now.
|
154
|
+
// TODO: when we upstream libwebrtc, this will need additional verification
|
155
|
+
isReactNative() ||
|
152
156
|
(browser?.name === 'Chrome' && compareVersions(browser?.version, '113') < 0)
|
153
157
|
) {
|
154
158
|
const bitratesRatio = sm.suffix == 'h' ? 2 : 3;
|
@@ -433,7 +433,7 @@ export default abstract class LocalTrack<
|
|
433
433
|
this.emit(TrackEvent.UpstreamResumed, this);
|
434
434
|
|
435
435
|
// this operation is noop if mediastreamtrack is already being sent
|
436
|
-
await this.sender.replaceTrack(this.
|
436
|
+
await this.sender.replaceTrack(this.mediaStreamTrack);
|
437
437
|
} finally {
|
438
438
|
unlock();
|
439
439
|
}
|
@@ -468,16 +468,17 @@ export default abstract class LocalTrack<
|
|
468
468
|
try {
|
469
469
|
this.log.debug('setting up processor', this.logContext);
|
470
470
|
|
471
|
-
this.
|
472
|
-
this.processorElement ?? (document.createElement(this.kind) as HTMLMediaElement);
|
471
|
+
const processorElement = document.createElement(this.kind) as HTMLMediaElement;
|
473
472
|
|
474
473
|
const processorOptions = {
|
475
474
|
kind: this.kind,
|
476
475
|
track: this._mediaStreamTrack,
|
477
|
-
element:
|
476
|
+
element: processorElement,
|
478
477
|
audioContext: this.audioContext,
|
479
478
|
};
|
480
479
|
await processor.init(processorOptions);
|
480
|
+
this.log.debug('processor initialized', this.logContext);
|
481
|
+
|
481
482
|
if (this.processor) {
|
482
483
|
await this.stopProcessor();
|
483
484
|
}
|
@@ -485,16 +486,17 @@ export default abstract class LocalTrack<
|
|
485
486
|
throw TypeError('cannot set processor on track of unknown kind');
|
486
487
|
}
|
487
488
|
|
488
|
-
attachToElement(this._mediaStreamTrack,
|
489
|
-
|
489
|
+
attachToElement(this._mediaStreamTrack, processorElement);
|
490
|
+
processorElement.muted = true;
|
490
491
|
|
491
|
-
|
492
|
+
processorElement
|
492
493
|
.play()
|
493
494
|
.catch((error) =>
|
494
495
|
this.log.error('failed to play processor element', { ...this.logContext, error }),
|
495
496
|
);
|
496
497
|
|
497
498
|
this.processor = processor;
|
499
|
+
this.processorElement = processorElement;
|
498
500
|
if (this.processor.processedTrack) {
|
499
501
|
for (const el of this.attachedElements) {
|
500
502
|
if (el !== this.processorElement && showProcessedStreamLocally) {
|
@@ -521,15 +523,17 @@ export default abstract class LocalTrack<
|
|
521
523
|
* @experimental
|
522
524
|
* @returns
|
523
525
|
*/
|
524
|
-
async stopProcessor() {
|
526
|
+
async stopProcessor(keepElement = true) {
|
525
527
|
if (!this.processor) return;
|
526
528
|
|
527
529
|
this.log.debug('stopping processor', this.logContext);
|
528
530
|
this.processor.processedTrack?.stop();
|
529
531
|
await this.processor.destroy();
|
530
532
|
this.processor = undefined;
|
531
|
-
|
532
|
-
|
533
|
+
if (!keepElement) {
|
534
|
+
this.processorElement?.remove();
|
535
|
+
this.processorElement = undefined;
|
536
|
+
}
|
533
537
|
// apply original track constraints in case the processor changed them
|
534
538
|
await this._mediaStreamTrack.applyConstraints(this._constraints);
|
535
539
|
// force re-setting of the mediaStreamTrack on the sender
|
@@ -276,7 +276,10 @@ export default class LocalVideoTrack extends LocalTrack<Track.Kind.Video> {
|
|
276
276
|
}
|
277
277
|
}
|
278
278
|
|
279
|
-
async setProcessor(
|
279
|
+
async setProcessor(
|
280
|
+
processor: TrackProcessor<Track.Kind.Video>,
|
281
|
+
showProcessedStreamLocally = true,
|
282
|
+
) {
|
280
283
|
await super.setProcessor(processor, showProcessedStreamLocally);
|
281
284
|
|
282
285
|
if (this.processor?.processedTrack) {
|
package/src/room/track/create.ts
CHANGED
@@ -14,6 +14,7 @@ import type {
|
|
14
14
|
VideoCaptureOptions,
|
15
15
|
} from './options';
|
16
16
|
import { ScreenSharePresets } from './options';
|
17
|
+
import type { TrackProcessor } from './processor/types';
|
17
18
|
import {
|
18
19
|
constraintsForOptions,
|
19
20
|
mergeDefaultOptions,
|
@@ -51,35 +52,44 @@ export async function createLocalTracks(
|
|
51
52
|
}
|
52
53
|
|
53
54
|
const stream = await mediaPromise;
|
54
|
-
return
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
trackOptions
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
55
|
+
return Promise.all(
|
56
|
+
stream.getTracks().map(async (mediaStreamTrack) => {
|
57
|
+
const isAudio = mediaStreamTrack.kind === 'audio';
|
58
|
+
let trackOptions = isAudio ? options!.audio : options!.video;
|
59
|
+
if (typeof trackOptions === 'boolean' || !trackOptions) {
|
60
|
+
trackOptions = {};
|
61
|
+
}
|
62
|
+
let trackConstraints: MediaTrackConstraints | undefined;
|
63
|
+
const conOrBool = isAudio ? constraints.audio : constraints.video;
|
64
|
+
if (typeof conOrBool !== 'boolean') {
|
65
|
+
trackConstraints = conOrBool;
|
66
|
+
}
|
65
67
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
68
|
+
// update the constraints with the device id the user gave permissions to in the permission prompt
|
69
|
+
// otherwise each track restart (e.g. mute - unmute) will try to initialize the device again -> causing additional permission prompts
|
70
|
+
if (trackConstraints) {
|
71
|
+
trackConstraints.deviceId = mediaStreamTrack.getSettings().deviceId;
|
72
|
+
} else {
|
73
|
+
trackConstraints = { deviceId: mediaStreamTrack.getSettings().deviceId };
|
74
|
+
}
|
73
75
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
76
|
+
const track = mediaTrackToLocalTrack(mediaStreamTrack, trackConstraints);
|
77
|
+
if (track.kind === Track.Kind.Video) {
|
78
|
+
track.source = Track.Source.Camera;
|
79
|
+
} else if (track.kind === Track.Kind.Audio) {
|
80
|
+
track.source = Track.Source.Microphone;
|
81
|
+
}
|
82
|
+
track.mediaStream = stream;
|
83
|
+
if (trackOptions.processor) {
|
84
|
+
if (track instanceof LocalAudioTrack) {
|
85
|
+
await track.setProcessor(trackOptions.processor as TrackProcessor<Track.Kind.Audio>);
|
86
|
+
} else if (track instanceof LocalVideoTrack) {
|
87
|
+
await track.setProcessor(trackOptions.processor as TrackProcessor<Track.Kind.Video>);
|
88
|
+
}
|
89
|
+
}
|
90
|
+
return track;
|
91
|
+
}),
|
92
|
+
);
|
83
93
|
}
|
84
94
|
|
85
95
|
/**
|
@@ -1,4 +1,9 @@
|
|
1
1
|
import type { Track } from './Track';
|
2
|
+
import type {
|
3
|
+
AudioProcessorOptions,
|
4
|
+
TrackProcessor,
|
5
|
+
VideoProcessorOptions,
|
6
|
+
} from './processor/types';
|
2
7
|
|
3
8
|
export interface TrackPublishDefaults {
|
4
9
|
/**
|
@@ -152,6 +157,11 @@ export interface VideoCaptureOptions {
|
|
152
157
|
facingMode?: 'user' | 'environment' | 'left' | 'right';
|
153
158
|
|
154
159
|
resolution?: VideoResolution;
|
160
|
+
|
161
|
+
/**
|
162
|
+
* initialize the track with a given processor
|
163
|
+
*/
|
164
|
+
processor?: TrackProcessor<Track.Kind.Video, VideoProcessorOptions>;
|
155
165
|
}
|
156
166
|
|
157
167
|
export interface ScreenShareCaptureOptions {
|
@@ -245,6 +255,11 @@ export interface AudioCaptureOptions {
|
|
245
255
|
* sample size or range of sample sizes which are acceptable and/or required.
|
246
256
|
*/
|
247
257
|
sampleSize?: ConstrainULong;
|
258
|
+
|
259
|
+
/**
|
260
|
+
* initialize the track with a given processor
|
261
|
+
*/
|
262
|
+
processor?: TrackProcessor<Track.Kind.Audio, AudioProcessorOptions>;
|
248
263
|
}
|
249
264
|
|
250
265
|
export interface AudioOutputOptions {
|