livekit-client 2.0.9 → 2.1.0
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.
- 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 = {
|