livekit-client 2.8.1 → 2.9.0
Sign up to get free protection for your applications and to get access to all the features.
- package/README.md +18 -7
- package/dist/livekit-client.e2ee.worker.js +1 -1
- package/dist/livekit-client.e2ee.worker.js.map +1 -1
- package/dist/livekit-client.e2ee.worker.mjs +1 -0
- package/dist/livekit-client.e2ee.worker.mjs.map +1 -1
- package/dist/livekit-client.esm.mjs +2565 -1849
- 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.map +1 -1
- package/dist/src/index.d.ts +7 -5
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/room/RTCEngine.d.ts +6 -0
- package/dist/src/room/RTCEngine.d.ts.map +1 -1
- package/dist/src/room/Room.d.ts +50 -1
- package/dist/src/room/Room.d.ts.map +1 -1
- package/dist/src/room/StreamReader.d.ts +56 -0
- package/dist/src/room/StreamReader.d.ts.map +1 -0
- package/dist/src/room/StreamWriter.d.ts +16 -0
- package/dist/src/room/StreamWriter.d.ts.map +1 -0
- package/dist/src/room/errors.d.ts +3 -1
- package/dist/src/room/errors.d.ts.map +1 -1
- package/dist/src/room/participant/LocalParticipant.d.ts +23 -36
- package/dist/src/room/participant/LocalParticipant.d.ts.map +1 -1
- package/dist/src/room/participant/Participant.d.ts.map +1 -1
- package/dist/src/room/participant/RemoteParticipant.d.ts.map +1 -1
- package/dist/src/room/track/LocalAudioTrack.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/LocalTrackPublication.d.ts +1 -0
- package/dist/src/room/track/LocalTrackPublication.d.ts.map +1 -1
- package/dist/src/room/track/RemoteTrack.d.ts +1 -0
- package/dist/src/room/track/RemoteTrack.d.ts.map +1 -1
- package/dist/src/room/track/RemoteTrackPublication.d.ts +1 -0
- package/dist/src/room/track/RemoteTrackPublication.d.ts.map +1 -1
- package/dist/src/room/track/Track.d.ts +1 -0
- package/dist/src/room/track/Track.d.ts.map +1 -1
- package/dist/src/room/track/TrackPublication.d.ts +2 -1
- package/dist/src/room/track/TrackPublication.d.ts.map +1 -1
- package/dist/src/room/track/create.d.ts.map +1 -1
- package/dist/src/room/track/facingMode.d.ts.map +1 -1
- package/dist/src/room/track/options.d.ts +18 -2
- package/dist/src/room/track/options.d.ts.map +1 -1
- package/dist/src/room/types.d.ts +43 -0
- package/dist/src/room/types.d.ts.map +1 -1
- package/dist/src/room/utils.d.ts +26 -0
- package/dist/src/room/utils.d.ts.map +1 -1
- package/dist/ts4.2/src/index.d.ts +7 -5
- package/dist/ts4.2/src/room/RTCEngine.d.ts +6 -0
- package/dist/ts4.2/src/room/Room.d.ts +49 -0
- package/dist/ts4.2/src/room/StreamReader.d.ts +56 -0
- package/dist/ts4.2/src/room/StreamWriter.d.ts +25 -0
- package/dist/ts4.2/src/room/errors.d.ts +3 -1
- package/dist/ts4.2/src/room/participant/LocalParticipant.d.ts +23 -36
- package/dist/ts4.2/src/room/track/LocalTrack.d.ts +1 -0
- package/dist/ts4.2/src/room/track/LocalTrackPublication.d.ts +1 -0
- package/dist/ts4.2/src/room/track/RemoteTrack.d.ts +1 -0
- package/dist/ts4.2/src/room/track/RemoteTrackPublication.d.ts +1 -0
- package/dist/ts4.2/src/room/track/Track.d.ts +1 -0
- package/dist/ts4.2/src/room/track/TrackPublication.d.ts +2 -1
- package/dist/ts4.2/src/room/track/options.d.ts +18 -2
- package/dist/ts4.2/src/room/types.d.ts +43 -0
- package/dist/ts4.2/src/room/utils.d.ts +26 -0
- package/package.json +3 -3
- package/src/api/SignalClient.ts +5 -2
- package/src/e2ee/E2eeManager.ts +2 -2
- package/src/index.ts +17 -1
- package/src/room/RTCEngine.ts +69 -2
- package/src/room/Room.ts +311 -23
- package/src/room/StreamReader.ts +177 -0
- package/src/room/StreamWriter.ts +32 -0
- package/src/room/errors.ts +16 -2
- package/src/room/participant/LocalParticipant.ts +320 -165
- package/src/room/participant/Participant.ts +2 -5
- package/src/room/participant/RemoteParticipant.ts +3 -2
- package/src/room/{participant/LocalParticipant.test.ts → rpc.test.ts} +22 -29
- package/src/room/track/LocalAudioTrack.ts +4 -3
- package/src/room/track/LocalTrack.ts +12 -4
- package/src/room/track/LocalTrackPublication.ts +6 -1
- package/src/room/track/LocalVideoTrack.ts +1 -1
- package/src/room/track/RemoteTrack.ts +4 -0
- package/src/room/track/RemoteTrackPublication.ts +8 -4
- package/src/room/track/Track.ts +2 -0
- package/src/room/track/TrackPublication.ts +6 -3
- package/src/room/track/create.ts +4 -3
- package/src/room/track/facingMode.ts +2 -1
- package/src/room/track/options.ts +20 -2
- package/src/room/track/utils.ts +1 -1
- package/src/room/types.ts +50 -0
- package/src/room/utils.ts +77 -0
@@ -11,15 +11,14 @@ import { EventEmitter } from 'events';
|
|
11
11
|
import type TypedEmitter from 'typed-emitter';
|
12
12
|
import log, { LoggerNames, type StructuredLogger, getLogger } from '../../logger';
|
13
13
|
import { ParticipantEvent, TrackEvent } from '../events';
|
14
|
-
import LocalAudioTrack from '../track/LocalAudioTrack';
|
15
14
|
import type LocalTrackPublication from '../track/LocalTrackPublication';
|
16
|
-
import RemoteAudioTrack from '../track/RemoteAudioTrack';
|
17
15
|
import type RemoteTrack from '../track/RemoteTrack';
|
18
16
|
import type RemoteTrackPublication from '../track/RemoteTrackPublication';
|
19
17
|
import { Track } from '../track/Track';
|
20
18
|
import type { TrackPublication } from '../track/TrackPublication';
|
21
19
|
import { diffAttributes } from '../track/utils';
|
22
20
|
import type { ChatMessage, LoggerOptions, TranscriptionSegment } from '../types';
|
21
|
+
import { isAudioTrack } from '../utils';
|
23
22
|
|
24
23
|
export enum ConnectionQuality {
|
25
24
|
Excellent = 'excellent',
|
@@ -317,9 +316,7 @@ export default class Participant extends (EventEmitter as new () => TypedEmitter
|
|
317
316
|
setAudioContext(ctx: AudioContext | undefined) {
|
318
317
|
this.audioContext = ctx;
|
319
318
|
this.audioTrackPublications.forEach(
|
320
|
-
(track) =>
|
321
|
-
(track.track instanceof RemoteAudioTrack || track.track instanceof LocalAudioTrack) &&
|
322
|
-
track.track.setAudioContext(ctx),
|
319
|
+
(track) => isAudioTrack(track.track) && track.track.setAudioContext(ctx),
|
323
320
|
);
|
324
321
|
}
|
325
322
|
|
@@ -16,6 +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 { isAudioTrack, isRemoteTrack } from '../utils';
|
19
20
|
import Participant, { ParticipantKind } from './Participant';
|
20
21
|
import type { ParticipantEventCallbacks } from './Participant';
|
21
22
|
|
@@ -239,7 +240,7 @@ export default class RemoteParticipant extends Participant {
|
|
239
240
|
|
240
241
|
publication.setTrack(track);
|
241
242
|
// set participant volumes on new audio tracks
|
242
|
-
if (this.volumeMap.has(publication.source) && track
|
243
|
+
if (this.volumeMap.has(publication.source) && isRemoteTrack(track) && isAudioTrack(track)) {
|
243
244
|
track.setVolume(this.volumeMap.get(publication.source)!);
|
244
245
|
}
|
245
246
|
|
@@ -367,7 +368,7 @@ export default class RemoteParticipant extends Participant {
|
|
367
368
|
this.audioOutput = output;
|
368
369
|
const promises: Promise<void>[] = [];
|
369
370
|
this.audioTrackPublications.forEach((pub) => {
|
370
|
-
if (pub.track
|
371
|
+
if (isAudioTrack(pub.track) && isRemoteTrack(pub.track)) {
|
371
372
|
promises.push(pub.track.setSinkId(output.deviceId ?? 'default'));
|
372
373
|
}
|
373
374
|
});
|
@@ -1,44 +1,37 @@
|
|
1
1
|
import { DataPacket, DataPacket_Kind } from '@livekit/protocol';
|
2
2
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
3
|
-
import type { InternalRoomOptions } from '
|
4
|
-
import type RTCEngine from '
|
5
|
-
import
|
6
|
-
import LocalParticipant from './LocalParticipant';
|
7
|
-
import { ParticipantKind } from './Participant';
|
8
|
-
import RemoteParticipant from './RemoteParticipant';
|
3
|
+
import type { InternalRoomOptions } from '../options';
|
4
|
+
import type RTCEngine from './RTCEngine';
|
5
|
+
import Room from './Room';
|
6
|
+
import LocalParticipant from './participant/LocalParticipant';
|
7
|
+
import { ParticipantKind } from './participant/Participant';
|
8
|
+
import RemoteParticipant from './participant/RemoteParticipant';
|
9
|
+
import { RpcError } from './rpc';
|
9
10
|
|
10
11
|
describe('LocalParticipant', () => {
|
11
12
|
describe('registerRpcMethod', () => {
|
12
|
-
let
|
13
|
-
let mockEngine: RTCEngine;
|
14
|
-
let mockRoomOptions: InternalRoomOptions;
|
13
|
+
let room: Room;
|
15
14
|
let mockSendDataPacket: ReturnType<typeof vi.fn>;
|
16
15
|
|
17
16
|
beforeEach(() => {
|
18
17
|
mockSendDataPacket = vi.fn();
|
19
|
-
mockEngine = {
|
20
|
-
client: {
|
21
|
-
sendUpdateLocalMetadata: vi.fn(),
|
22
|
-
},
|
23
|
-
on: vi.fn().mockReturnThis(),
|
24
|
-
sendDataPacket: mockSendDataPacket,
|
25
|
-
} as unknown as RTCEngine;
|
26
18
|
|
27
|
-
|
19
|
+
room = new Room();
|
20
|
+
room.engine.client = {
|
21
|
+
sendUpdateLocalMetadata: vi.fn(),
|
22
|
+
};
|
23
|
+
room.engine.on = vi.fn().mockReturnThis();
|
24
|
+
room.engine.sendDataPacket = mockSendDataPacket;
|
28
25
|
|
29
|
-
localParticipant =
|
30
|
-
|
31
|
-
'test-identity',
|
32
|
-
mockEngine,
|
33
|
-
mockRoomOptions,
|
34
|
-
);
|
26
|
+
room.localParticipant.sid = 'test-sid';
|
27
|
+
room.localParticipant.identity = 'test-identity';
|
35
28
|
});
|
36
29
|
|
37
30
|
it('should register an RPC method handler', async () => {
|
38
31
|
const methodName = 'testMethod';
|
39
32
|
const handler = vi.fn().mockResolvedValue('test response');
|
40
33
|
|
41
|
-
|
34
|
+
room.registerRpcMethod(methodName, handler);
|
42
35
|
|
43
36
|
const mockCaller = new RemoteParticipant(
|
44
37
|
{} as any,
|
@@ -51,7 +44,7 @@ describe('LocalParticipant', () => {
|
|
51
44
|
ParticipantKind.STANDARD,
|
52
45
|
);
|
53
46
|
|
54
|
-
await
|
47
|
+
await room.handleIncomingRpcRequest(
|
55
48
|
mockCaller.identity,
|
56
49
|
'test-request-id',
|
57
50
|
methodName,
|
@@ -84,7 +77,7 @@ describe('LocalParticipant', () => {
|
|
84
77
|
const errorMessage = 'Test error';
|
85
78
|
const handler = vi.fn().mockRejectedValue(new Error(errorMessage));
|
86
79
|
|
87
|
-
|
80
|
+
room.registerRpcMethod(methodName, handler);
|
88
81
|
|
89
82
|
const mockCaller = new RemoteParticipant(
|
90
83
|
{} as any,
|
@@ -97,7 +90,7 @@ describe('LocalParticipant', () => {
|
|
97
90
|
ParticipantKind.STANDARD,
|
98
91
|
);
|
99
92
|
|
100
|
-
await
|
93
|
+
await room.handleIncomingRpcRequest(
|
101
94
|
mockCaller.identity,
|
102
95
|
'test-error-request-id',
|
103
96
|
methodName,
|
@@ -127,7 +120,7 @@ describe('LocalParticipant', () => {
|
|
127
120
|
const errorMessage = 'some-error-message';
|
128
121
|
const handler = vi.fn().mockRejectedValue(new RpcError(errorCode, errorMessage));
|
129
122
|
|
130
|
-
localParticipant.registerRpcMethod(methodName, handler);
|
123
|
+
room.localParticipant.registerRpcMethod(methodName, handler);
|
131
124
|
|
132
125
|
const mockCaller = new RemoteParticipant(
|
133
126
|
{} as any,
|
@@ -140,7 +133,7 @@ describe('LocalParticipant', () => {
|
|
140
133
|
ParticipantKind.STANDARD,
|
141
134
|
);
|
142
135
|
|
143
|
-
await
|
136
|
+
await room.handleIncomingRpcRequest(
|
144
137
|
mockCaller.identity,
|
145
138
|
'test-rpc-error-request-id',
|
146
139
|
methodName,
|
@@ -3,7 +3,7 @@ import { TrackEvent } from '../events';
|
|
3
3
|
import { computeBitrate, monitorFrequency } from '../stats';
|
4
4
|
import type { AudioSenderStats } from '../stats';
|
5
5
|
import type { LoggerOptions } from '../types';
|
6
|
-
import { isWeb, unwrapConstraint } from '../utils';
|
6
|
+
import { isReactNative, isWeb, unwrapConstraint } from '../utils';
|
7
7
|
import LocalTrack from './LocalTrack';
|
8
8
|
import { Track } from './Track';
|
9
9
|
import type { AudioCaptureOptions } from './options';
|
@@ -171,7 +171,7 @@ export default class LocalAudioTrack extends LocalTrack<Track.Kind.Audio> {
|
|
171
171
|
async setProcessor(processor: TrackProcessor<Track.Kind.Audio, AudioProcessorOptions>) {
|
172
172
|
const unlock = await this.processorLock.lock();
|
173
173
|
try {
|
174
|
-
if (!this.audioContext) {
|
174
|
+
if (!isReactNative() && !this.audioContext) {
|
175
175
|
throw Error(
|
176
176
|
'Audio context needs to be set on LocalAudioTrack in order to enable processors',
|
177
177
|
);
|
@@ -183,7 +183,8 @@ export default class LocalAudioTrack extends LocalTrack<Track.Kind.Audio> {
|
|
183
183
|
const processorOptions = {
|
184
184
|
kind: this.kind,
|
185
185
|
track: this._mediaStreamTrack,
|
186
|
-
|
186
|
+
// RN won't have or use AudioContext
|
187
|
+
audioContext: this.audioContext as AudioContext,
|
187
188
|
};
|
188
189
|
this.log.debug(`setting up audio processor ${processor.name}`, this.logContext);
|
189
190
|
|
@@ -120,6 +120,10 @@ export default abstract class LocalTrack<
|
|
120
120
|
return this.processor?.processedTrack ?? this._mediaStreamTrack;
|
121
121
|
}
|
122
122
|
|
123
|
+
get isLocal() {
|
124
|
+
return true;
|
125
|
+
}
|
126
|
+
|
123
127
|
/**
|
124
128
|
* @internal
|
125
129
|
* returns mediaStreamTrack settings of the capturing mediastreamtrack source - ignoring processors
|
@@ -179,7 +183,7 @@ export default abstract class LocalTrack<
|
|
179
183
|
unlock();
|
180
184
|
}
|
181
185
|
}
|
182
|
-
if (this.sender) {
|
186
|
+
if (this.sender && this.sender.transport?.state !== 'closed') {
|
183
187
|
await this.sender.replaceTrack(processedTrack ?? newTrack);
|
184
188
|
}
|
185
189
|
// if `newTrack` is different from the existing track, stop the
|
@@ -446,7 +450,9 @@ export default abstract class LocalTrack<
|
|
446
450
|
// https://bugs.webkit.org/show_bug.cgi?id=184911
|
447
451
|
throw new DeviceUnsupportedError('pauseUpstream is not supported on Safari < 12.');
|
448
452
|
}
|
449
|
-
|
453
|
+
if (this.sender.transport?.state !== 'closed') {
|
454
|
+
await this.sender.replaceTrack(null);
|
455
|
+
}
|
450
456
|
} finally {
|
451
457
|
unlock();
|
452
458
|
}
|
@@ -465,8 +471,10 @@ export default abstract class LocalTrack<
|
|
465
471
|
this._isUpstreamPaused = false;
|
466
472
|
this.emit(TrackEvent.UpstreamResumed, this);
|
467
473
|
|
468
|
-
|
469
|
-
|
474
|
+
if (this.sender.transport?.state !== 'closed') {
|
475
|
+
// this operation is noop if mediastreamtrack is already being sent
|
476
|
+
await this.sender.replaceTrack(this.mediaStreamTrack);
|
477
|
+
}
|
470
478
|
} finally {
|
471
479
|
unlock();
|
472
480
|
}
|
@@ -1,6 +1,7 @@
|
|
1
1
|
import { AudioTrackFeature, TrackInfo } from '@livekit/protocol';
|
2
2
|
import { TrackEvent } from '../events';
|
3
3
|
import type { LoggerOptions } from '../types';
|
4
|
+
import { isAudioTrack } from '../utils';
|
4
5
|
import LocalAudioTrack from './LocalAudioTrack';
|
5
6
|
import type LocalTrack from './LocalTrack';
|
6
7
|
import type LocalVideoTrack from './LocalVideoTrack';
|
@@ -51,6 +52,10 @@ export default class LocalTrackPublication extends TrackPublication {
|
|
51
52
|
return super.videoTrack as LocalVideoTrack | undefined;
|
52
53
|
}
|
53
54
|
|
55
|
+
get isLocal() {
|
56
|
+
return true;
|
57
|
+
}
|
58
|
+
|
54
59
|
/**
|
55
60
|
* Mute the track associated with this publication
|
56
61
|
*/
|
@@ -83,7 +88,7 @@ export default class LocalTrackPublication extends TrackPublication {
|
|
83
88
|
}
|
84
89
|
|
85
90
|
getTrackFeatures() {
|
86
|
-
if (this.track
|
91
|
+
if (isAudioTrack(this.track)) {
|
87
92
|
const settings = this.track!.getSourceTrackSettings();
|
88
93
|
const features: Set<AudioTrackFeature> = new Set();
|
89
94
|
if (settings.autoGainControl) {
|
@@ -252,7 +252,7 @@ export default class LocalVideoTrack extends LocalTrack<Track.Kind.Video> {
|
|
252
252
|
await this.restart(constraints);
|
253
253
|
|
254
254
|
for await (const sc of this.simulcastCodecs.values()) {
|
255
|
-
if (sc.sender) {
|
255
|
+
if (sc.sender && sc.sender.transport?.state !== 'closed') {
|
256
256
|
sc.mediaStreamTrack = this.mediaStreamTrack.clone();
|
257
257
|
await sc.sender.replaceTrack(sc.mediaStreamTrack);
|
258
258
|
}
|
@@ -7,8 +7,8 @@ import {
|
|
7
7
|
} from '@livekit/protocol';
|
8
8
|
import { TrackEvent } from '../events';
|
9
9
|
import type { LoggerOptions } from '../types';
|
10
|
+
import { isRemoteVideoTrack } from '../utils';
|
10
11
|
import type RemoteTrack from './RemoteTrack';
|
11
|
-
import RemoteVideoTrack from './RemoteVideoTrack';
|
12
12
|
import { Track, VideoQuality } from './Track';
|
13
13
|
import { TrackPublication } from './TrackPublication';
|
14
14
|
|
@@ -108,6 +108,10 @@ export default class RemoteTrackPublication extends TrackPublication {
|
|
108
108
|
return !this.disabled;
|
109
109
|
}
|
110
110
|
|
111
|
+
get isLocal() {
|
112
|
+
return false;
|
113
|
+
}
|
114
|
+
|
111
115
|
/**
|
112
116
|
* disable server from sending down data for this track. this is useful when
|
113
117
|
* the participant is off screen, you may disable streaming down their video
|
@@ -150,7 +154,7 @@ export default class RemoteTrackPublication extends TrackPublication {
|
|
150
154
|
) {
|
151
155
|
return;
|
152
156
|
}
|
153
|
-
if (this.track
|
157
|
+
if (isRemoteVideoTrack(this.track)) {
|
154
158
|
this.videoDimensions = dimensions;
|
155
159
|
}
|
156
160
|
this.currentVideoQuality = undefined;
|
@@ -163,7 +167,7 @@ export default class RemoteTrackPublication extends TrackPublication {
|
|
163
167
|
return;
|
164
168
|
}
|
165
169
|
|
166
|
-
if (!(this.track
|
170
|
+
if (!isRemoteVideoTrack(this.track)) {
|
167
171
|
return;
|
168
172
|
}
|
169
173
|
|
@@ -276,7 +280,7 @@ export default class RemoteTrackPublication extends TrackPublication {
|
|
276
280
|
};
|
277
281
|
|
278
282
|
protected get isAdaptiveStream(): boolean {
|
279
|
-
return this.track
|
283
|
+
return isRemoteVideoTrack(this.track) && this.track.isAdaptiveStream;
|
280
284
|
}
|
281
285
|
|
282
286
|
protected handleVisibilityChange = (visible: boolean) => {
|
package/src/room/track/Track.ts
CHANGED
@@ -10,6 +10,7 @@ import type TypedEventEmitter from 'typed-emitter';
|
|
10
10
|
import log, { LoggerNames, getLogger } from '../../logger';
|
11
11
|
import { TrackEvent } from '../events';
|
12
12
|
import type { LoggerOptions, TranscriptionSegment } from '../types';
|
13
|
+
import { isAudioTrack, isVideoTrack } from '../utils';
|
13
14
|
import LocalAudioTrack from './LocalAudioTrack';
|
14
15
|
import LocalVideoTrack from './LocalVideoTrack';
|
15
16
|
import RemoteAudioTrack from './RemoteAudioTrack';
|
@@ -18,7 +19,7 @@ import RemoteVideoTrack from './RemoteVideoTrack';
|
|
18
19
|
import { Track } from './Track';
|
19
20
|
import { getLogContextFromTrack } from './utils';
|
20
21
|
|
21
|
-
export class TrackPublication extends (EventEmitter as new () => TypedEventEmitter<PublicationEventCallbacks>) {
|
22
|
+
export abstract class TrackPublication extends (EventEmitter as new () => TypedEventEmitter<PublicationEventCallbacks>) {
|
22
23
|
kind: Track.Kind;
|
23
24
|
|
24
25
|
trackName: string;
|
@@ -99,11 +100,13 @@ export class TrackPublication extends (EventEmitter as new () => TypedEventEmitt
|
|
99
100
|
return this.encryption !== Encryption_Type.NONE;
|
100
101
|
}
|
101
102
|
|
103
|
+
abstract get isLocal(): boolean;
|
104
|
+
|
102
105
|
/**
|
103
106
|
* an [AudioTrack] if this publication holds an audio track
|
104
107
|
*/
|
105
108
|
get audioTrack(): LocalAudioTrack | RemoteAudioTrack | undefined {
|
106
|
-
if (this.track
|
109
|
+
if (isAudioTrack(this.track)) {
|
107
110
|
return this.track;
|
108
111
|
}
|
109
112
|
}
|
@@ -112,7 +115,7 @@ export class TrackPublication extends (EventEmitter as new () => TypedEventEmitt
|
|
112
115
|
* an [VideoTrack] if this publication holds a video track
|
113
116
|
*/
|
114
117
|
get videoTrack(): LocalVideoTrack | RemoteVideoTrack | undefined {
|
115
|
-
if (this.track
|
118
|
+
if (isVideoTrack(this.track)) {
|
116
119
|
return this.track;
|
117
120
|
}
|
118
121
|
}
|
package/src/room/track/create.ts
CHANGED
@@ -2,7 +2,7 @@ import DeviceManager from '../DeviceManager';
|
|
2
2
|
import { audioDefaults, videoDefaults } from '../defaults';
|
3
3
|
import { DeviceUnsupportedError, TrackInvalidError } from '../errors';
|
4
4
|
import { mediaTrackToLocalTrack } from '../participant/publishUtils';
|
5
|
-
import { isSafari17 } from '../utils';
|
5
|
+
import { isAudioTrack, isSafari17, isVideoTrack } from '../utils';
|
6
6
|
import LocalAudioTrack from './LocalAudioTrack';
|
7
7
|
import type LocalTrack from './LocalTrack';
|
8
8
|
import LocalVideoTrack from './LocalVideoTrack';
|
@@ -81,9 +81,10 @@ export async function createLocalTracks(
|
|
81
81
|
track.source = Track.Source.Microphone;
|
82
82
|
}
|
83
83
|
track.mediaStream = stream;
|
84
|
-
|
84
|
+
|
85
|
+
if (isAudioTrack(track) && audioProcessor) {
|
85
86
|
await track.setProcessor(audioProcessor);
|
86
|
-
} else if (track
|
87
|
+
} else if (isVideoTrack(track) && videoProcessor) {
|
87
88
|
await track.setProcessor(videoProcessor);
|
88
89
|
}
|
89
90
|
|
@@ -1,4 +1,5 @@
|
|
1
1
|
import log from '../../logger';
|
2
|
+
import { isLocalTrack } from '../utils';
|
2
3
|
import LocalTrack from './LocalTrack';
|
3
4
|
import type { VideoCaptureOptions } from './options';
|
4
5
|
|
@@ -37,7 +38,7 @@ export function facingModeFromLocalTrack(
|
|
37
38
|
localTrack: LocalTrack | MediaStreamTrack,
|
38
39
|
options: FacingModeFromLocalTrackOptions = {},
|
39
40
|
): FacingModeFromLocalTrackReturnValue {
|
40
|
-
const track = localTrack
|
41
|
+
const track = isLocalTrack(localTrack) ? localTrack.mediaStreamTrack : localTrack;
|
41
42
|
const trackSettings = track.getSettings();
|
42
43
|
let result: FacingModeFromLocalTrackReturnValue = {
|
43
44
|
facingMode: options.defaultFacingMode ?? 'user',
|
@@ -12,8 +12,7 @@ export interface TrackPublishDefaults {
|
|
12
12
|
videoEncoding?: VideoEncoding;
|
13
13
|
|
14
14
|
/**
|
15
|
-
*
|
16
|
-
* VP9 and AV1 are not supported by all browser clients. When backupCodec is
|
15
|
+
* Advanced codecs (VP9/AV1/H265) are not supported by all browser clients. When backupCodec is
|
17
16
|
* set, when an incompatible client attempts to subscribe to the track, LiveKit
|
18
17
|
* will automatically publish a secondary track encoded with the backup codec.
|
19
18
|
*
|
@@ -24,6 +23,20 @@ export interface TrackPublishDefaults {
|
|
24
23
|
*/
|
25
24
|
backupCodec?: true | false | { codec: BackupVideoCodec; encoding?: VideoEncoding };
|
26
25
|
|
26
|
+
/**
|
27
|
+
* When backup codec is enabled, there are two options to decide whether to
|
28
|
+
* send the primary codec at the same time:
|
29
|
+
* * codec regression: publisher stops sending primary codec and all subscribers
|
30
|
+
* will receive backup codec even if the primary codec is supported on their browser. It is the default
|
31
|
+
* behavior and provides maximum compatibility. It also reduces CPU
|
32
|
+
* and bandwidth consumption for publisher.
|
33
|
+
* * multi-codec simulcast: publisher encodes and sends both codecs at same time,
|
34
|
+
* subscribers will get most efficient codec. It will provide most bandwidth
|
35
|
+
* efficiency, especially in the large 1:N room but requires more device performance
|
36
|
+
* and bandwidth consumption for publisher.
|
37
|
+
*/
|
38
|
+
backupCodecPolicy?: BackupCodecPolicy;
|
39
|
+
|
27
40
|
/**
|
28
41
|
* encoding parameters for screen share track
|
29
42
|
*/
|
@@ -375,6 +388,11 @@ export function isBackupCodec(codec: string): codec is BackupVideoCodec {
|
|
375
388
|
return !!backupCodecs.find((backup) => backup === codec);
|
376
389
|
}
|
377
390
|
|
391
|
+
export enum BackupCodecPolicy {
|
392
|
+
REGRESSION = 0,
|
393
|
+
SIMULCAST = 1,
|
394
|
+
}
|
395
|
+
|
378
396
|
/**
|
379
397
|
* scalability modes for svc.
|
380
398
|
*/
|
package/src/room/track/utils.ts
CHANGED
@@ -222,7 +222,7 @@ export function getTrackPublicationInfo<T extends TrackPublication>(
|
|
222
222
|
}
|
223
223
|
|
224
224
|
export function getLogContextFromTrack(track: Track | TrackPublication): Record<string, unknown> {
|
225
|
-
if (
|
225
|
+
if ('mediaStreamTrack' in track) {
|
226
226
|
return {
|
227
227
|
trackID: track.sid,
|
228
228
|
source: track.source,
|
package/src/room/types.ts
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
import type { DataStream_Chunk } from '@livekit/protocol';
|
2
|
+
|
1
3
|
export type SimulationOptions = {
|
2
4
|
publish?: {
|
3
5
|
audio?: boolean;
|
@@ -12,6 +14,25 @@ export type SimulationOptions = {
|
|
12
14
|
};
|
13
15
|
};
|
14
16
|
|
17
|
+
export interface SendTextOptions {
|
18
|
+
topic?: string;
|
19
|
+
// replyToMessageId?: string;
|
20
|
+
destinationIdentities?: Array<string>;
|
21
|
+
attachments?: Array<File>;
|
22
|
+
onProgress?: (progress: number) => void;
|
23
|
+
}
|
24
|
+
|
25
|
+
export interface StreamTextOptions {
|
26
|
+
topic?: string;
|
27
|
+
destinationIdentities?: Array<string>;
|
28
|
+
type?: 'create' | 'update';
|
29
|
+
streamId?: string;
|
30
|
+
version?: number;
|
31
|
+
attachedStreamIds?: Array<string>;
|
32
|
+
replyToStreamId?: string;
|
33
|
+
totalSize?: number;
|
34
|
+
}
|
35
|
+
|
15
36
|
export type DataPublishOptions = {
|
16
37
|
/**
|
17
38
|
* whether to send this as reliable or lossy.
|
@@ -74,4 +95,33 @@ export interface ChatMessage {
|
|
74
95
|
timestamp: number;
|
75
96
|
message: string;
|
76
97
|
editTimestamp?: number;
|
98
|
+
attachedFiles?: Array<File>;
|
99
|
+
}
|
100
|
+
|
101
|
+
export interface StreamController<T extends DataStream_Chunk> {
|
102
|
+
info: BaseStreamInfo;
|
103
|
+
controller: ReadableStreamDefaultController<T>;
|
104
|
+
startTime: number;
|
105
|
+
endTime?: number;
|
106
|
+
}
|
107
|
+
|
108
|
+
export interface BaseStreamInfo {
|
109
|
+
id: string;
|
110
|
+
mimeType: string;
|
111
|
+
topic: string;
|
112
|
+
timestamp: number;
|
113
|
+
/** total size in bytes for finite streams and undefined for streams of unknown size */
|
114
|
+
size?: number;
|
115
|
+
attributes?: Record<string, string>;
|
77
116
|
}
|
117
|
+
export interface ByteStreamInfo extends BaseStreamInfo {
|
118
|
+
name: string;
|
119
|
+
}
|
120
|
+
|
121
|
+
export interface TextStreamInfo extends BaseStreamInfo {}
|
122
|
+
|
123
|
+
export type TextStreamChunk = {
|
124
|
+
index: number;
|
125
|
+
current: string;
|
126
|
+
collected: string;
|
127
|
+
};
|
package/src/room/utils.ts
CHANGED
@@ -8,9 +8,20 @@ import {
|
|
8
8
|
import { getBrowser } from '../utils/browserParser';
|
9
9
|
import { protocolVersion, version } from '../version';
|
10
10
|
import { type ConnectionError, ConnectionErrorReason } from './errors';
|
11
|
+
import type LocalParticipant from './participant/LocalParticipant';
|
12
|
+
import type Participant from './participant/Participant';
|
13
|
+
import type RemoteParticipant from './participant/RemoteParticipant';
|
11
14
|
import CriticalTimers from './timers';
|
12
15
|
import type LocalAudioTrack from './track/LocalAudioTrack';
|
16
|
+
import type LocalTrack from './track/LocalTrack';
|
17
|
+
import type LocalTrackPublication from './track/LocalTrackPublication';
|
18
|
+
import type LocalVideoTrack from './track/LocalVideoTrack';
|
13
19
|
import type RemoteAudioTrack from './track/RemoteAudioTrack';
|
20
|
+
import type RemoteTrack from './track/RemoteTrack';
|
21
|
+
import type RemoteTrackPublication from './track/RemoteTrackPublication';
|
22
|
+
import type RemoteVideoTrack from './track/RemoteVideoTrack';
|
23
|
+
import { Track } from './track/Track';
|
24
|
+
import type { TrackPublication } from './track/TrackPublication';
|
14
25
|
import { type VideoCodec, videoCodecs } from './track/options';
|
15
26
|
import { getNewAudioContext } from './track/utils';
|
16
27
|
import type { ChatMessage, LiveKitReactNativeInfo, TranscriptionSegment } from './types';
|
@@ -548,3 +559,69 @@ export function getDisconnectReasonFromConnectionError(e: ConnectionError) {
|
|
548
559
|
return DisconnectReason.UNKNOWN_REASON;
|
549
560
|
}
|
550
561
|
}
|
562
|
+
|
563
|
+
/** convert bigints to numbers preserving undefined values */
|
564
|
+
export function bigIntToNumber<T extends BigInt | undefined>(
|
565
|
+
value: T,
|
566
|
+
): T extends BigInt ? number : undefined {
|
567
|
+
return (value !== undefined ? Number(value) : undefined) as T extends BigInt ? number : undefined;
|
568
|
+
}
|
569
|
+
|
570
|
+
/** convert numbers to bigints preserving undefined values */
|
571
|
+
export function numberToBigInt<T extends number | undefined>(
|
572
|
+
value: T,
|
573
|
+
): T extends number ? bigint : undefined {
|
574
|
+
return (value !== undefined ? BigInt(value) : undefined) as T extends number ? bigint : undefined;
|
575
|
+
}
|
576
|
+
|
577
|
+
export function isLocalTrack(track: Track | MediaStreamTrack | undefined): track is LocalTrack {
|
578
|
+
return !!track && !(track instanceof MediaStreamTrack) && track.isLocal;
|
579
|
+
}
|
580
|
+
|
581
|
+
export function isAudioTrack(
|
582
|
+
track: Track | undefined,
|
583
|
+
): track is LocalAudioTrack | RemoteAudioTrack {
|
584
|
+
return !!track && track.kind == Track.Kind.Audio;
|
585
|
+
}
|
586
|
+
|
587
|
+
export function isVideoTrack(
|
588
|
+
track: Track | undefined,
|
589
|
+
): track is LocalVideoTrack | RemoteVideoTrack {
|
590
|
+
return !!track && track.kind == Track.Kind.Video;
|
591
|
+
}
|
592
|
+
|
593
|
+
export function isLocalVideoTrack(
|
594
|
+
track: Track | MediaStreamTrack | undefined,
|
595
|
+
): track is LocalVideoTrack {
|
596
|
+
return isLocalTrack(track) && isVideoTrack(track);
|
597
|
+
}
|
598
|
+
|
599
|
+
export function isLocalAudioTrack(
|
600
|
+
track: Track | MediaStreamTrack | undefined,
|
601
|
+
): track is LocalAudioTrack {
|
602
|
+
return isLocalTrack(track) && isAudioTrack(track);
|
603
|
+
}
|
604
|
+
|
605
|
+
export function isRemoteTrack(track: Track | undefined): track is RemoteTrack {
|
606
|
+
return !!track && !track.isLocal;
|
607
|
+
}
|
608
|
+
|
609
|
+
export function isRemotePub(pub: TrackPublication | undefined): pub is RemoteTrackPublication {
|
610
|
+
return !!pub && !pub.isLocal;
|
611
|
+
}
|
612
|
+
|
613
|
+
export function isLocalPub(pub: TrackPublication | undefined): pub is LocalTrackPublication {
|
614
|
+
return !!pub && !pub.isLocal;
|
615
|
+
}
|
616
|
+
|
617
|
+
export function isRemoteVideoTrack(track: Track | undefined): track is RemoteVideoTrack {
|
618
|
+
return isRemoteTrack(track) && isVideoTrack(track);
|
619
|
+
}
|
620
|
+
|
621
|
+
export function isLocalParticipant(p: Participant): p is LocalParticipant {
|
622
|
+
return p.isLocal;
|
623
|
+
}
|
624
|
+
|
625
|
+
export function isRemoteParticipant(p: Participant): p is RemoteParticipant {
|
626
|
+
return !p.isLocal;
|
627
|
+
}
|