livekit-client 2.0.9 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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 +203 -140
- package/dist/livekit-client.e2ee.worker.mjs.map +1 -1
- package/dist/livekit-client.esm.mjs +3792 -6746
- 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 +2 -1
- package/dist/src/api/SignalClient.d.ts.map +1 -1
- package/dist/src/connectionHelper/ConnectionCheck.d.ts +3 -2
- package/dist/src/connectionHelper/ConnectionCheck.d.ts.map +1 -1
- package/dist/src/e2ee/worker/FrameCryptor.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/room/RTCEngine.d.ts +1 -0
- package/dist/src/room/RTCEngine.d.ts.map +1 -1
- package/dist/src/room/events.d.ts +5 -1
- package/dist/src/room/events.d.ts.map +1 -1
- package/dist/src/room/participant/LocalParticipant.d.ts +1 -0
- package/dist/src/room/participant/LocalParticipant.d.ts.map +1 -1
- package/dist/src/room/participant/publishUtils.d.ts.map +1 -1
- package/dist/src/room/stats.d.ts +6 -3
- package/dist/src/room/stats.d.ts.map +1 -1
- package/dist/src/room/track/LocalAudioTrack.d.ts +7 -0
- package/dist/src/room/track/LocalAudioTrack.d.ts.map +1 -1
- package/dist/src/room/track/LocalTrack.d.ts.map +1 -1
- package/dist/src/room/track/LocalTrackPublication.d.ts +3 -2
- package/dist/src/room/track/LocalTrackPublication.d.ts.map +1 -1
- package/dist/src/room/track/LocalVideoTrack.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/options.d.ts +1 -1
- package/dist/src/room/track/options.d.ts.map +1 -1
- package/dist/src/room/utils.d.ts +3 -0
- package/dist/src/room/utils.d.ts.map +1 -1
- package/dist/ts4.2/src/api/SignalClient.d.ts +2 -1
- package/dist/ts4.2/src/connectionHelper/ConnectionCheck.d.ts +3 -2
- package/dist/ts4.2/src/index.d.ts +3 -2
- package/dist/ts4.2/src/room/RTCEngine.d.ts +1 -0
- package/dist/ts4.2/src/room/events.d.ts +5 -1
- package/dist/ts4.2/src/room/participant/LocalParticipant.d.ts +1 -0
- package/dist/ts4.2/src/room/stats.d.ts +6 -3
- package/dist/ts4.2/src/room/track/LocalAudioTrack.d.ts +7 -0
- package/dist/ts4.2/src/room/track/LocalTrackPublication.d.ts +3 -2
- package/dist/ts4.2/src/room/track/Track.d.ts +2 -1
- package/dist/ts4.2/src/room/track/options.d.ts +1 -1
- package/dist/ts4.2/src/room/utils.d.ts +3 -0
- package/package.json +10 -10
- package/src/api/SignalClient.ts +9 -0
- package/src/connectionHelper/ConnectionCheck.ts +6 -3
- package/src/e2ee/worker/FrameCryptor.ts +0 -1
- package/src/e2ee/worker/e2ee.worker.ts +3 -1
- package/src/index.ts +3 -0
- package/src/room/RTCEngine.ts +11 -1
- package/src/room/events.ts +5 -0
- package/src/room/participant/LocalParticipant.ts +14 -0
- package/src/room/participant/publishUtils.ts +2 -1
- package/src/room/stats.ts +9 -3
- package/src/room/track/LocalAudioTrack.ts +40 -0
- package/src/room/track/LocalTrack.ts +4 -2
- package/src/room/track/LocalTrackPublication.ts +28 -2
- package/src/room/track/LocalVideoTrack.ts +16 -7
- package/src/room/track/Track.ts +13 -0
- package/src/room/track/options.ts +22 -1
- package/src/room/utils.ts +3 -0
@@ -1,4 +1,4 @@
|
|
1
|
-
import { StreamState as ProtoStreamState, TrackSource, TrackType } from '@livekit/protocol';
|
1
|
+
import { AudioTrackFeature, StreamState as ProtoStreamState, TrackSource, TrackType } from '@livekit/protocol';
|
2
2
|
import type TypedEventEmitter from 'typed-emitter';
|
3
3
|
import type { SignalClient } from '../../api/SignalClient';
|
4
4
|
import { StructuredLogger } from '../../logger';
|
@@ -136,6 +136,7 @@ export type TrackEventCallbacks = {
|
|
136
136
|
upstreamPaused: (track: any) => void;
|
137
137
|
upstreamResumed: (track: any) => void;
|
138
138
|
trackProcessorUpdate: (processor?: TrackProcessor<Track.Kind, any>) => void;
|
139
|
+
audioTrackFeatureUpdate: (track: any, feature: AudioTrackFeature, enabled: boolean) => void;
|
139
140
|
};
|
140
141
|
export {};
|
141
142
|
//# sourceMappingURL=Track.d.ts.map
|
@@ -263,7 +263,7 @@ export declare function isBackupCodec(codec: string): codec is BackupVideoCodec;
|
|
263
263
|
/**
|
264
264
|
* scalability modes for svc.
|
265
265
|
*/
|
266
|
-
export type ScalabilityMode = 'L1T3' | 'L2T3' | 'L2T3_KEY' | 'L3T3' | 'L3T3_KEY';
|
266
|
+
export type ScalabilityMode = 'L1T1' | 'L1T2' | 'L1T3' | 'L2T1' | 'L2T1h' | 'L2T1_KEY' | 'L2T2' | 'L2T2h' | 'L2T2_KEY' | 'L2T3' | 'L2T3h' | 'L2T3_KEY' | 'L3T1' | 'L3T1h' | 'L3T1_KEY' | 'L3T2' | 'L3T2h' | 'L3T2_KEY' | 'L3T3' | 'L3T3h' | 'L3T3_KEY';
|
267
267
|
export declare namespace AudioPresets {
|
268
268
|
const telephone: AudioPreset;
|
269
269
|
const speech: AudioPreset;
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "livekit-client",
|
3
|
-
"version": "2.0
|
3
|
+
"version": "2.1.0",
|
4
4
|
"description": "JavaScript/TypeScript client SDK for LiveKit",
|
5
5
|
"main": "./dist/livekit-client.umd.js",
|
6
6
|
"unpkg": "./dist/livekit-client.umd.js",
|
@@ -36,7 +36,7 @@
|
|
36
36
|
"author": "David Zhao <david@davidzhao.com>",
|
37
37
|
"license": "Apache-2.0",
|
38
38
|
"dependencies": {
|
39
|
-
"@livekit/protocol": "1.
|
39
|
+
"@livekit/protocol": "1.13.0",
|
40
40
|
"events": "^3.3.0",
|
41
41
|
"loglevel": "^1.8.0",
|
42
42
|
"sdp-transform": "^2.14.1",
|
@@ -46,8 +46,8 @@
|
|
46
46
|
"webrtc-adapter": "^8.1.1"
|
47
47
|
},
|
48
48
|
"devDependencies": {
|
49
|
-
"@babel/core": "7.
|
50
|
-
"@babel/preset-env": "7.
|
49
|
+
"@babel/core": "7.24.4",
|
50
|
+
"@babel/preset-env": "7.24.4",
|
51
51
|
"@bufbuild/protoc-gen-es": "^1.3.0",
|
52
52
|
"@changesets/cli": "2.27.1",
|
53
53
|
"@livekit/changesets-changelog-github": "^0.0.4",
|
@@ -65,23 +65,23 @@
|
|
65
65
|
"@typescript-eslint/eslint-plugin": "5.62.0",
|
66
66
|
"@typescript-eslint/parser": "5.62.0",
|
67
67
|
"downlevel-dts": "^0.11.0",
|
68
|
-
"eslint": "8.
|
69
|
-
"eslint-config-airbnb-typescript": "
|
68
|
+
"eslint": "8.57.0",
|
69
|
+
"eslint-config-airbnb-typescript": "18.0.0",
|
70
70
|
"eslint-config-prettier": "9.1.0",
|
71
71
|
"eslint-plugin-ecmascript-compat": "^3.0.0",
|
72
72
|
"eslint-plugin-import": "2.29.1",
|
73
73
|
"gh-pages": "6.1.1",
|
74
74
|
"jsdom": "^24.0.0",
|
75
75
|
"prettier": "^3.0.0",
|
76
|
-
"rollup": "4.
|
76
|
+
"rollup": "4.14.0",
|
77
77
|
"rollup-plugin-delete": "^2.0.0",
|
78
78
|
"rollup-plugin-re": "1.0.7",
|
79
79
|
"rollup-plugin-typescript2": "0.36.0",
|
80
80
|
"size-limit": "^8.2.4",
|
81
|
-
"typedoc": "0.25.
|
81
|
+
"typedoc": "0.25.12",
|
82
82
|
"typedoc-plugin-no-inherit": "1.4.0",
|
83
|
-
"typescript": "5.
|
84
|
-
"vite": "5.0.
|
83
|
+
"typescript": "5.4.3",
|
84
|
+
"vite": "5.0.13",
|
85
85
|
"vitest": "^1.0.0"
|
86
86
|
},
|
87
87
|
"scripts": {
|
package/src/api/SignalClient.ts
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
import {
|
2
2
|
AddTrackRequest,
|
3
|
+
AudioTrackFeature,
|
3
4
|
ClientInfo,
|
4
5
|
ConnectionQualityUpdate,
|
5
6
|
DisconnectReason,
|
@@ -27,6 +28,7 @@ import {
|
|
27
28
|
TrackPublishedResponse,
|
28
29
|
TrackUnpublishedResponse,
|
29
30
|
TrickleRequest,
|
31
|
+
UpdateLocalAudioTrack,
|
30
32
|
UpdateParticipantMetadata,
|
31
33
|
UpdateSubscription,
|
32
34
|
UpdateTrackSettings,
|
@@ -583,6 +585,13 @@ export class SignalClient {
|
|
583
585
|
]);
|
584
586
|
}
|
585
587
|
|
588
|
+
sendUpdateLocalAudioTrack(trackSid: string, features: AudioTrackFeature[]) {
|
589
|
+
return this.sendRequest({
|
590
|
+
case: 'updateAudioTrack',
|
591
|
+
value: new UpdateLocalAudioTrack({ trackSid, features }),
|
592
|
+
});
|
593
|
+
}
|
594
|
+
|
586
595
|
sendLeave() {
|
587
596
|
return this.sendRequest({
|
588
597
|
case: 'leave',
|
@@ -1,7 +1,7 @@
|
|
1
1
|
import { EventEmitter } from 'events';
|
2
2
|
import type TypedEmitter from 'typed-emitter';
|
3
|
+
import type { CheckInfo, CheckerOptions, InstantiableCheck } from './checks/Checker';
|
3
4
|
import { CheckStatus, Checker } from './checks/Checker';
|
4
|
-
import type { CheckInfo, InstantiableCheck } from './checks/Checker';
|
5
5
|
import { PublishAudioCheck } from './checks/publishAudio';
|
6
6
|
import { PublishVideoCheck } from './checks/publishVideo';
|
7
7
|
import { ReconnectCheck } from './checks/reconnect';
|
@@ -16,12 +16,15 @@ export class ConnectionCheck extends (EventEmitter as new () => TypedEmitter<Con
|
|
16
16
|
|
17
17
|
url: string;
|
18
18
|
|
19
|
+
options: CheckerOptions = {};
|
20
|
+
|
19
21
|
private checkResults: Map<number, CheckInfo> = new Map();
|
20
22
|
|
21
|
-
constructor(url: string, token: string) {
|
23
|
+
constructor(url: string, token: string, options: CheckerOptions = {}) {
|
22
24
|
super();
|
23
25
|
this.url = url;
|
24
26
|
this.token = token;
|
27
|
+
this.options = options;
|
25
28
|
}
|
26
29
|
|
27
30
|
private getNextCheckId() {
|
@@ -50,7 +53,7 @@ export class ConnectionCheck extends (EventEmitter as new () => TypedEmitter<Con
|
|
50
53
|
|
51
54
|
async createAndRunCheck<T extends Checker>(check: InstantiableCheck<T>) {
|
52
55
|
const checkId = this.getNextCheckId();
|
53
|
-
const test = new check(this.url, this.token);
|
56
|
+
const test = new check(this.url, this.token, this.options);
|
54
57
|
const handleUpdate = (info: CheckInfo) => {
|
55
58
|
this.updateCheck(checkId, info);
|
56
59
|
};
|
@@ -607,7 +607,6 @@ export class FrameCryptor extends BaseFrameCryptor {
|
|
607
607
|
if (this.rtpMap.size === 0) {
|
608
608
|
return undefined;
|
609
609
|
}
|
610
|
-
// @ts-expect-error payloadType is not yet part of the typescript definition and currently not supported in Safari
|
611
610
|
const payloadType = frame.getMetadata().payloadType;
|
612
611
|
const codec = payloadType ? this.rtpMap.get(payloadType) : undefined;
|
613
612
|
return codec;
|
@@ -245,9 +245,11 @@ function handleSifTrailer(trailer: Uint8Array) {
|
|
245
245
|
if (self.RTCTransformEvent) {
|
246
246
|
workerLogger.debug('setup transform event');
|
247
247
|
// @ts-ignore
|
248
|
-
self.onrtctransform = (event) => {
|
248
|
+
self.onrtctransform = (event: RTCTransformEvent) => {
|
249
|
+
// @ts-ignore .transformer property is part of RTCTransformEvent
|
249
250
|
const transformer = event.transformer;
|
250
251
|
workerLogger.debug('transformer', transformer);
|
252
|
+
// @ts-ignore monkey patching non standard flag
|
251
253
|
transformer.handled = true;
|
252
254
|
const { kind, participantIdentity, trackId, codec } = transformer.options;
|
253
255
|
const cryptor = getTrackCryptor(participantIdentity, trackId);
|
package/src/index.ts
CHANGED
@@ -20,6 +20,7 @@ import { TrackPublication } from './room/track/TrackPublication';
|
|
20
20
|
import type { LiveKitReactNativeInfo } from './room/types';
|
21
21
|
import type { AudioAnalyserOptions } from './room/utils';
|
22
22
|
import {
|
23
|
+
Mutex,
|
23
24
|
createAudioAnalyser,
|
24
25
|
getEmptyAudioStreamTrack,
|
25
26
|
getEmptyVideoStreamTrack,
|
@@ -32,6 +33,7 @@ import {
|
|
32
33
|
import { getBrowser } from './utils/browserParser';
|
33
34
|
|
34
35
|
export * from './connectionHelper/ConnectionCheck';
|
36
|
+
export * from './connectionHelper/checks/Checker';
|
35
37
|
export * from './e2ee';
|
36
38
|
export * from './options';
|
37
39
|
export * from './room/errors';
|
@@ -79,6 +81,7 @@ export {
|
|
79
81
|
supportsAdaptiveStream,
|
80
82
|
supportsDynacast,
|
81
83
|
supportsVP9,
|
84
|
+
Mutex,
|
82
85
|
};
|
83
86
|
export type {
|
84
87
|
AudioAnalyserOptions,
|
package/src/room/RTCEngine.ts
CHANGED
@@ -169,6 +169,8 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
|
|
169
169
|
|
170
170
|
private loggerOptions: LoggerOptions;
|
171
171
|
|
172
|
+
private publisherConnectionPromise: Promise<void> | undefined;
|
173
|
+
|
172
174
|
constructor(private options: InternalRoomOptions) {
|
173
175
|
super();
|
174
176
|
this.log = getLogger(options.loggerName ?? LoggerNames.Engine);
|
@@ -398,6 +400,11 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
|
|
398
400
|
this.pcManager.onDataChannel = this.handleDataChannel;
|
399
401
|
this.pcManager.onStateChange = async (connectionState, publisherState, subscriberState) => {
|
400
402
|
this.log.debug(`primary PC state changed ${connectionState}`, this.logContext);
|
403
|
+
|
404
|
+
if (['closed', 'disconnected', 'failed'].includes(publisherState)) {
|
405
|
+
// reset publisher connection promise
|
406
|
+
this.publisherConnectionPromise = undefined;
|
407
|
+
}
|
401
408
|
if (connectionState === PCTransportState.CONNECTED) {
|
402
409
|
const shouldEmit = this.pcState === PCState.New;
|
403
410
|
this.pcState = PCState.Connected;
|
@@ -1179,7 +1186,10 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
|
|
1179
1186
|
}
|
1180
1187
|
|
1181
1188
|
private async ensurePublisherConnected(kind: DataPacket_Kind) {
|
1182
|
-
|
1189
|
+
if (!this.publisherConnectionPromise) {
|
1190
|
+
this.publisherConnectionPromise = this.ensureDataTransportConnected(kind, false);
|
1191
|
+
}
|
1192
|
+
await this.publisherConnectionPromise;
|
1183
1193
|
}
|
1184
1194
|
|
1185
1195
|
/* @internal */
|
package/src/room/events.ts
CHANGED
@@ -685,6 +685,7 @@ export default class LocalParticipant extends Participant {
|
|
685
685
|
track.on(TrackEvent.Ended, this.handleTrackEnded);
|
686
686
|
track.on(TrackEvent.UpstreamPaused, this.onTrackUpstreamPaused);
|
687
687
|
track.on(TrackEvent.UpstreamResumed, this.onTrackUpstreamResumed);
|
688
|
+
track.on(TrackEvent.AudioTrackFeatureUpdate, this.onTrackFeatureUpdate);
|
688
689
|
|
689
690
|
// create track publication from track
|
690
691
|
const req = new AddTrackRequest({
|
@@ -1037,6 +1038,7 @@ export default class LocalParticipant extends Participant {
|
|
1037
1038
|
track.off(TrackEvent.Ended, this.handleTrackEnded);
|
1038
1039
|
track.off(TrackEvent.UpstreamPaused, this.onTrackUpstreamPaused);
|
1039
1040
|
track.off(TrackEvent.UpstreamResumed, this.onTrackUpstreamResumed);
|
1041
|
+
track.off(TrackEvent.AudioTrackFeatureUpdate, this.onTrackFeatureUpdate);
|
1040
1042
|
|
1041
1043
|
if (stopOnUnpublish === undefined) {
|
1042
1044
|
stopOnUnpublish = this.roomOptions?.stopLocalTrackOnUnpublish ?? true;
|
@@ -1293,6 +1295,18 @@ export default class LocalParticipant extends Participant {
|
|
1293
1295
|
this.onTrackMuted(track, track.isMuted);
|
1294
1296
|
};
|
1295
1297
|
|
1298
|
+
private onTrackFeatureUpdate = (track: LocalAudioTrack) => {
|
1299
|
+
const pub = this.audioTrackPublications.get(track.sid!);
|
1300
|
+
if (!pub) {
|
1301
|
+
this.log.warn(
|
1302
|
+
`Could not update local audio track settings, missing publication for track ${track.sid}`,
|
1303
|
+
this.logContext,
|
1304
|
+
);
|
1305
|
+
return;
|
1306
|
+
}
|
1307
|
+
this.engine.client.sendUpdateLocalAudioTrack(pub.trackSid, pub.getTrackFeatures());
|
1308
|
+
};
|
1309
|
+
|
1296
1310
|
private handleSubscribedQualityUpdate = async (update: SubscribedQualityUpdate) => {
|
1297
1311
|
if (!this.roomOptions?.dynacast) {
|
1298
1312
|
return;
|
@@ -150,11 +150,12 @@ export function computeVideoEncodings(
|
|
150
150
|
isSafari() ||
|
151
151
|
(browser?.name === 'Chrome' && compareVersions(browser?.version, '113') < 0)
|
152
152
|
) {
|
153
|
+
const bitratesRatio = sm.suffix == 'h' ? 2 : 3;
|
153
154
|
for (let i = 0; i < sm.spatial; i += 1) {
|
154
155
|
// in legacy SVC, scaleResolutionDownBy cannot be set
|
155
156
|
encodings.push({
|
156
157
|
rid: videoRids[2 - i],
|
157
|
-
maxBitrate: videoEncoding.maxBitrate /
|
158
|
+
maxBitrate: videoEncoding.maxBitrate / bitratesRatio ** i,
|
158
159
|
maxFramerate: original.encoding.maxFramerate,
|
159
160
|
});
|
160
161
|
}
|
package/src/room/stats.ts
CHANGED
@@ -42,14 +42,20 @@ export interface VideoSenderStats extends SenderStats {
|
|
42
42
|
|
43
43
|
frameHeight: number;
|
44
44
|
|
45
|
+
framesPerSecond: number;
|
46
|
+
|
45
47
|
framesSent: number;
|
46
48
|
|
47
49
|
// bandwidth, cpu, other, none
|
48
|
-
qualityLimitationReason
|
50
|
+
qualityLimitationReason?: string;
|
51
|
+
|
52
|
+
qualityLimitationDurations?: Record<string, number>;
|
53
|
+
|
54
|
+
qualityLimitationResolutionChanges?: number;
|
49
55
|
|
50
|
-
|
56
|
+
retransmittedPacketsSent?: number;
|
51
57
|
|
52
|
-
|
58
|
+
targetBitrate: number;
|
53
59
|
}
|
54
60
|
|
55
61
|
interface ReceiverStats {
|
@@ -1,3 +1,4 @@
|
|
1
|
+
import { AudioTrackFeature } from '@livekit/protocol';
|
1
2
|
import { TrackEvent } from '../events';
|
2
3
|
import { computeBitrate, monitorFrequency } from '../stats';
|
3
4
|
import type { AudioSenderStats } from '../stats';
|
@@ -15,8 +16,17 @@ export default class LocalAudioTrack extends LocalTrack<Track.Kind.Audio> {
|
|
15
16
|
|
16
17
|
private prevStats?: AudioSenderStats;
|
17
18
|
|
19
|
+
private isKrispNoiseFilterEnabled = false;
|
20
|
+
|
18
21
|
protected processor?: TrackProcessor<Track.Kind.Audio, AudioProcessorOptions> | undefined;
|
19
22
|
|
23
|
+
/**
|
24
|
+
* boolean indicating whether enhanced noise cancellation is currently being used on this track
|
25
|
+
*/
|
26
|
+
get enhancedNoiseCancellation() {
|
27
|
+
return this.isKrispNoiseFilterEnabled;
|
28
|
+
}
|
29
|
+
|
20
30
|
/**
|
21
31
|
*
|
22
32
|
* @param mediaTrack
|
@@ -152,6 +162,28 @@ export default class LocalAudioTrack extends LocalTrack<Track.Kind.Audio> {
|
|
152
162
|
this.prevStats = stats;
|
153
163
|
};
|
154
164
|
|
165
|
+
private handleKrispNoiseFilterEnable = () => {
|
166
|
+
this.isKrispNoiseFilterEnabled = true;
|
167
|
+
this.log.debug(`Krisp noise filter enabled`, this.logContext);
|
168
|
+
this.emit(
|
169
|
+
TrackEvent.AudioTrackFeatureUpdate,
|
170
|
+
this,
|
171
|
+
AudioTrackFeature.TF_ENHANCED_NOISE_CANCELLATION,
|
172
|
+
true,
|
173
|
+
);
|
174
|
+
};
|
175
|
+
|
176
|
+
private handleKrispNoiseFilterDisable = () => {
|
177
|
+
this.isKrispNoiseFilterEnabled = false;
|
178
|
+
this.log.debug(`Krisp noise filter disabled`, this.logContext);
|
179
|
+
this.emit(
|
180
|
+
TrackEvent.AudioTrackFeatureUpdate,
|
181
|
+
this,
|
182
|
+
AudioTrackFeature.TF_ENHANCED_NOISE_CANCELLATION,
|
183
|
+
false,
|
184
|
+
);
|
185
|
+
};
|
186
|
+
|
155
187
|
async setProcessor(processor: TrackProcessor<Track.Kind.Audio, AudioProcessorOptions>) {
|
156
188
|
const unlock = await this.processorLock.lock();
|
157
189
|
try {
|
@@ -175,6 +207,14 @@ export default class LocalAudioTrack extends LocalTrack<Track.Kind.Audio> {
|
|
175
207
|
this.processor = processor;
|
176
208
|
if (this.processor.processedTrack) {
|
177
209
|
await this.sender?.replaceTrack(this.processor.processedTrack);
|
210
|
+
this.processor.processedTrack.addEventListener(
|
211
|
+
'enable-lk-krisp-noise-filter',
|
212
|
+
this.handleKrispNoiseFilterEnable,
|
213
|
+
);
|
214
|
+
this.processor.processedTrack.addEventListener(
|
215
|
+
'disable-lk-krisp-noise-filter',
|
216
|
+
this.handleKrispNoiseFilterDisable,
|
217
|
+
);
|
178
218
|
}
|
179
219
|
this.emit(TrackEvent.TrackProcessorUpdate, this.processor);
|
180
220
|
} finally {
|
@@ -448,6 +448,10 @@ export default abstract class LocalTrack<
|
|
448
448
|
const unlock = await this.processorLock.lock();
|
449
449
|
try {
|
450
450
|
this.log.debug('setting up processor', this.logContext);
|
451
|
+
|
452
|
+
this.processorElement =
|
453
|
+
this.processorElement ?? (document.createElement(this.kind) as HTMLMediaElement);
|
454
|
+
|
451
455
|
const processorOptions = {
|
452
456
|
kind: this.kind,
|
453
457
|
track: this._mediaStreamTrack,
|
@@ -461,8 +465,6 @@ export default abstract class LocalTrack<
|
|
461
465
|
if (this.kind === 'unknown') {
|
462
466
|
throw TypeError('cannot set processor on track of unknown kind');
|
463
467
|
}
|
464
|
-
this.processorElement =
|
465
|
-
this.processorElement ?? (document.createElement(this.kind) as HTMLMediaElement);
|
466
468
|
|
467
469
|
attachToElement(this._mediaStreamTrack, this.processorElement);
|
468
470
|
this.processorElement.muted = true;
|
@@ -1,7 +1,7 @@
|
|
1
|
-
import
|
1
|
+
import { AudioTrackFeature, TrackInfo } from '@livekit/protocol';
|
2
2
|
import { TrackEvent } from '../events';
|
3
3
|
import type { LoggerOptions } from '../types';
|
4
|
-
import
|
4
|
+
import LocalAudioTrack from './LocalAudioTrack';
|
5
5
|
import type LocalTrack from './LocalTrack';
|
6
6
|
import type LocalVideoTrack from './LocalVideoTrack';
|
7
7
|
import type { Track } from './Track';
|
@@ -82,6 +82,32 @@ export default class LocalTrackPublication extends TrackPublication {
|
|
82
82
|
await this.track?.resumeUpstream();
|
83
83
|
}
|
84
84
|
|
85
|
+
getTrackFeatures() {
|
86
|
+
if (this.track instanceof LocalAudioTrack) {
|
87
|
+
const settings = this.track!.mediaStreamTrack.getSettings();
|
88
|
+
const features: Set<AudioTrackFeature> = new Set();
|
89
|
+
if (settings.autoGainControl) {
|
90
|
+
features.add(AudioTrackFeature.TF_AUTO_GAIN_CONTROL);
|
91
|
+
}
|
92
|
+
if (settings.echoCancellation) {
|
93
|
+
features.add(AudioTrackFeature.TF_ECHO_CANCELLATION);
|
94
|
+
}
|
95
|
+
if (settings.noiseSuppression) {
|
96
|
+
features.add(AudioTrackFeature.TF_NOISE_SUPPRESSION);
|
97
|
+
}
|
98
|
+
if (settings.channelCount && settings.channelCount > 1) {
|
99
|
+
features.add(AudioTrackFeature.TF_STEREO);
|
100
|
+
}
|
101
|
+
if (!this.options?.dtx) {
|
102
|
+
features.add(AudioTrackFeature.TF_STEREO);
|
103
|
+
}
|
104
|
+
if (this.track.enhancedNoiseCancellation) {
|
105
|
+
features.add(AudioTrackFeature.TF_ENHANCED_NOISE_CANCELLATION);
|
106
|
+
}
|
107
|
+
return Array.from(features.values());
|
108
|
+
} else return [];
|
109
|
+
}
|
110
|
+
|
85
111
|
handleTrackEnded = () => {
|
86
112
|
this.emit(TrackEvent.Ended);
|
87
113
|
};
|
@@ -180,17 +180,20 @@ export default class LocalVideoTrack extends LocalTrack<Track.Kind.Video> {
|
|
180
180
|
streamId: v.id,
|
181
181
|
frameHeight: v.frameHeight,
|
182
182
|
frameWidth: v.frameWidth,
|
183
|
+
framesPerSecond: v.framesPerSecond,
|
184
|
+
framesSent: v.framesSent,
|
183
185
|
firCount: v.firCount,
|
184
186
|
pliCount: v.pliCount,
|
185
187
|
nackCount: v.nackCount,
|
186
188
|
packetsSent: v.packetsSent,
|
187
189
|
bytesSent: v.bytesSent,
|
188
|
-
framesSent: v.framesSent,
|
189
|
-
timestamp: v.timestamp,
|
190
|
-
rid: v.rid ?? v.id,
|
191
|
-
retransmittedPacketsSent: v.retransmittedPacketsSent,
|
192
190
|
qualityLimitationReason: v.qualityLimitationReason,
|
191
|
+
qualityLimitationDurations: v.qualityLimitationDurations,
|
193
192
|
qualityLimitationResolutionChanges: v.qualityLimitationResolutionChanges,
|
193
|
+
rid: v.rid ?? v.id,
|
194
|
+
retransmittedPacketsSent: v.retransmittedPacketsSent,
|
195
|
+
targetBitrate: v.targetBitrate,
|
196
|
+
timestamp: v.timestamp,
|
194
197
|
};
|
195
198
|
|
196
199
|
// locate the appropriate remote-inbound-rtp item
|
@@ -205,6 +208,8 @@ export default class LocalVideoTrack extends LocalTrack<Track.Kind.Video> {
|
|
205
208
|
}
|
206
209
|
});
|
207
210
|
|
211
|
+
// make sure highest res layer is always first
|
212
|
+
items.sort((a, b) => (b.frameWidth ?? 0) - (a.frameWidth ?? 0));
|
208
213
|
return items;
|
209
214
|
}
|
210
215
|
|
@@ -567,13 +572,17 @@ export function videoLayersFromEncodings(
|
|
567
572
|
const encodingSM = encodings[0].scalabilityMode as string;
|
568
573
|
const sm = new ScalabilityMode(encodingSM);
|
569
574
|
const layers = [];
|
575
|
+
const resRatio = sm.suffix == 'h' ? 1.5 : 2;
|
576
|
+
const bitratesRatio = sm.suffix == 'h' ? 2 : 3;
|
570
577
|
for (let i = 0; i < sm.spatial; i += 1) {
|
571
578
|
layers.push(
|
572
579
|
new VideoLayer({
|
573
580
|
quality: VideoQuality.HIGH - i,
|
574
|
-
width: Math.ceil(width /
|
575
|
-
height: Math.ceil(height /
|
576
|
-
bitrate: encodings[0].maxBitrate
|
581
|
+
width: Math.ceil(width / resRatio ** i),
|
582
|
+
height: Math.ceil(height / resRatio ** i),
|
583
|
+
bitrate: encodings[0].maxBitrate
|
584
|
+
? Math.ceil(encodings[0].maxBitrate / bitratesRatio ** i)
|
585
|
+
: 0,
|
577
586
|
ssrc: 0,
|
578
587
|
}),
|
579
588
|
);
|
package/src/room/track/Track.ts
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
import {
|
2
|
+
AudioTrackFeature,
|
2
3
|
VideoQuality as ProtoQuality,
|
3
4
|
StreamState as ProtoStreamState,
|
4
5
|
TrackSource,
|
@@ -300,6 +301,17 @@ export abstract class Track<
|
|
300
301
|
|
301
302
|
protected async handleAppVisibilityChanged() {
|
302
303
|
this.isInBackground = document.visibilityState === 'hidden';
|
304
|
+
if (!this.isInBackground && this.kind === Track.Kind.Video) {
|
305
|
+
setTimeout(
|
306
|
+
() =>
|
307
|
+
this.attachedElements.forEach((el) =>
|
308
|
+
el.play().catch(() => {
|
309
|
+
/** catch clause necessary for Safari */
|
310
|
+
}),
|
311
|
+
),
|
312
|
+
0,
|
313
|
+
);
|
314
|
+
}
|
303
315
|
}
|
304
316
|
|
305
317
|
protected addAppVisibilityListener() {
|
@@ -504,4 +516,5 @@ export type TrackEventCallbacks = {
|
|
504
516
|
upstreamPaused: (track: any) => void;
|
505
517
|
upstreamResumed: (track: any) => void;
|
506
518
|
trackProcessorUpdate: (processor?: TrackProcessor<Track.Kind, any>) => void;
|
519
|
+
audioTrackFeatureUpdate: (track: any, feature: AudioTrackFeature, enabled: boolean) => void;
|
507
520
|
};
|
@@ -350,7 +350,28 @@ export function isBackupCodec(codec: string): codec is BackupVideoCodec {
|
|
350
350
|
/**
|
351
351
|
* scalability modes for svc.
|
352
352
|
*/
|
353
|
-
export type ScalabilityMode =
|
353
|
+
export type ScalabilityMode =
|
354
|
+
| 'L1T1'
|
355
|
+
| 'L1T2'
|
356
|
+
| 'L1T3'
|
357
|
+
| 'L2T1'
|
358
|
+
| 'L2T1h'
|
359
|
+
| 'L2T1_KEY'
|
360
|
+
| 'L2T2'
|
361
|
+
| 'L2T2h'
|
362
|
+
| 'L2T2_KEY'
|
363
|
+
| 'L2T3'
|
364
|
+
| 'L2T3h'
|
365
|
+
| 'L2T3_KEY'
|
366
|
+
| 'L3T1'
|
367
|
+
| 'L3T1h'
|
368
|
+
| 'L3T1_KEY'
|
369
|
+
| 'L3T2'
|
370
|
+
| 'L3T2h'
|
371
|
+
| 'L3T2_KEY'
|
372
|
+
| 'L3T3'
|
373
|
+
| 'L3T3h'
|
374
|
+
| 'L3T3_KEY';
|
354
375
|
|
355
376
|
export namespace AudioPresets {
|
356
377
|
export const telephone: AudioPreset = {
|