livekit-client 0.16.4 → 0.17.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/api/RequestQueue.js +6 -6
- package/dist/api/RequestQueue.js.map +1 -1
- package/dist/api/SignalClient.d.ts +3 -0
- package/dist/api/SignalClient.js +23 -3
- package/dist/api/SignalClient.js.map +1 -1
- package/dist/connect.js +1 -1
- package/dist/connect.js.map +1 -1
- package/dist/options.d.ts +7 -2
- package/dist/proto/livekit_models.d.ts +33 -0
- package/dist/proto/livekit_models.js +213 -3
- package/dist/proto/livekit_models.js.map +1 -1
- package/dist/proto/livekit_rtc.d.ts +15 -1
- package/dist/proto/livekit_rtc.js +128 -2
- package/dist/proto/livekit_rtc.js.map +1 -1
- package/dist/room/RTCEngine.d.ts +3 -2
- package/dist/room/RTCEngine.js +12 -8
- package/dist/room/RTCEngine.js.map +1 -1
- package/dist/room/Room.js +19 -8
- package/dist/room/Room.js.map +1 -1
- package/dist/room/events.d.ts +6 -1
- package/dist/room/events.js +6 -1
- package/dist/room/events.js.map +1 -1
- package/dist/room/participant/LocalParticipant.d.ts +3 -1
- package/dist/room/participant/LocalParticipant.js +17 -1
- package/dist/room/participant/LocalParticipant.js.map +1 -1
- package/dist/room/participant/RemoteParticipant.d.ts +2 -1
- package/dist/room/participant/RemoteParticipant.js +3 -3
- package/dist/room/participant/RemoteParticipant.js.map +1 -1
- package/dist/room/participant/publishUtils.d.ts +6 -0
- package/dist/room/participant/publishUtils.js +65 -24
- package/dist/room/participant/publishUtils.js.map +1 -1
- package/dist/room/participant/publishUtils.test.js +35 -5
- package/dist/room/participant/publishUtils.test.js.map +1 -1
- package/dist/room/track/LocalAudioTrack.d.ts +2 -0
- package/dist/room/track/LocalAudioTrack.js +23 -0
- package/dist/room/track/LocalAudioTrack.js.map +1 -1
- package/dist/room/track/LocalTrack.d.ts +4 -0
- package/dist/room/track/LocalTrack.js +34 -0
- package/dist/room/track/LocalTrack.js.map +1 -1
- package/dist/room/track/LocalVideoTrack.d.ts +1 -0
- package/dist/room/track/LocalVideoTrack.js +13 -0
- package/dist/room/track/LocalVideoTrack.js.map +1 -1
- package/dist/room/track/RemoteTrack.d.ts +1 -0
- package/dist/room/track/RemoteTrack.js +1 -0
- package/dist/room/track/RemoteTrack.js.map +1 -1
- package/dist/room/track/RemoteVideoTrack.d.ts +4 -2
- package/dist/room/track/RemoteVideoTrack.js +25 -11
- package/dist/room/track/RemoteVideoTrack.js.map +1 -1
- package/dist/room/track/Track.d.ts +4 -1
- package/dist/room/track/Track.js +20 -1
- package/dist/room/track/Track.js.map +1 -1
- package/dist/room/track/defaults.js +2 -2
- package/dist/room/track/defaults.js.map +1 -1
- package/dist/room/track/options.d.ts +65 -15
- package/dist/room/track/options.js +38 -0
- package/dist/room/track/options.js.map +1 -1
- package/dist/room/track/types.d.ts +11 -0
- package/dist/room/track/utils.d.ts +10 -0
- package/dist/room/track/utils.js +46 -1
- package/dist/room/track/utils.js.map +1 -1
- package/dist/room/utils.d.ts +1 -0
- package/dist/room/utils.js +5 -1
- package/dist/room/utils.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
- package/src/api/RequestQueue.ts +7 -7
- package/src/api/SignalClient.ts +31 -4
- package/src/connect.ts +1 -1
- package/src/options.ts +12 -3
- package/src/proto/livekit_models.ts +249 -0
- package/src/proto/livekit_rtc.ts +155 -0
- package/src/room/RTCEngine.ts +16 -9
- package/src/room/Room.ts +17 -7
- package/src/room/events.ts +6 -1
- package/src/room/participant/LocalParticipant.ts +23 -4
- package/src/room/participant/RemoteParticipant.ts +4 -4
- package/src/room/participant/publishUtils.test.ts +46 -6
- package/src/room/participant/publishUtils.ts +72 -27
- package/src/room/track/LocalAudioTrack.ts +19 -1
- package/src/room/track/LocalTrack.ts +36 -0
- package/src/room/track/LocalVideoTrack.ts +9 -1
- package/src/room/track/RemoteTrack.ts +2 -0
- package/src/room/track/RemoteVideoTrack.ts +20 -9
- package/src/room/track/Track.ts +14 -2
- package/src/room/track/defaults.ts +2 -2
- package/src/room/track/options.ts +55 -3
- package/src/room/track/types.ts +12 -0
- package/src/room/track/utils.ts +39 -0
- package/src/room/utils.ts +4 -0
- package/src/version.ts +1 -1
@@ -26,32 +26,38 @@ export function mediaTrackToLocalTrack(
|
|
26
26
|
}
|
27
27
|
|
28
28
|
/* @internal */
|
29
|
-
export const presets169 =
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
29
|
+
export const presets169 = Object.values(VideoPresets);
|
30
|
+
|
31
|
+
/* @internal */
|
32
|
+
export const presets43 = Object.values(VideoPresets43);
|
33
|
+
|
34
|
+
/* @internal */
|
35
|
+
export const presetsScreenShare = Object.values(ScreenSharePresets);
|
36
36
|
|
37
37
|
/* @internal */
|
38
|
-
export const
|
39
|
-
|
40
|
-
|
41
|
-
VideoPresets43.qhd,
|
42
|
-
VideoPresets43.hd,
|
43
|
-
VideoPresets43.fhd,
|
38
|
+
export const defaultSimulcastPresets169 = [
|
39
|
+
VideoPresets.h180,
|
40
|
+
VideoPresets.h360,
|
44
41
|
];
|
45
42
|
|
46
43
|
/* @internal */
|
47
|
-
export const
|
48
|
-
|
49
|
-
|
50
|
-
ScreenSharePresets.hd_15,
|
51
|
-
ScreenSharePresets.fhd_15,
|
52
|
-
ScreenSharePresets.fhd_30,
|
44
|
+
export const defaultSimulcastPresets43 = [
|
45
|
+
VideoPresets43.h180,
|
46
|
+
VideoPresets43.h360,
|
53
47
|
];
|
54
48
|
|
49
|
+
/* @internal */
|
50
|
+
export const computeDefaultScreenShareSimulcastPresets = (fromPreset: VideoPreset) => {
|
51
|
+
const layers = [{ scaleResolutionDownBy: 2, fps: 3 }];
|
52
|
+
return layers.map((t) => new VideoPreset(
|
53
|
+
Math.floor(fromPreset.width / t.scaleResolutionDownBy),
|
54
|
+
Math.floor(fromPreset.height / t.scaleResolutionDownBy),
|
55
|
+
Math.max(150_000, Math.floor(fromPreset.encoding.maxBitrate
|
56
|
+
/ (t.scaleResolutionDownBy ** 2 * ((fromPreset.encoding.maxFramerate ?? 30) / t.fps)))),
|
57
|
+
t.fps,
|
58
|
+
));
|
59
|
+
};
|
60
|
+
|
55
61
|
const videoRids = ['q', 'h', 'f'];
|
56
62
|
|
57
63
|
/* @internal */
|
@@ -65,7 +71,7 @@ export function computeVideoEncodings(
|
|
65
71
|
if (isScreenShare) {
|
66
72
|
videoEncoding = options?.screenShareEncoding;
|
67
73
|
}
|
68
|
-
const useSimulcast =
|
74
|
+
const useSimulcast = options?.simulcast;
|
69
75
|
|
70
76
|
if ((!videoEncoding && !useSimulcast) || !width || !height) {
|
71
77
|
// when we aren't simulcasting, will need to return a single encoding without
|
@@ -82,16 +88,22 @@ export function computeVideoEncodings(
|
|
82
88
|
if (!useSimulcast) {
|
83
89
|
return [videoEncoding];
|
84
90
|
}
|
85
|
-
|
86
|
-
|
91
|
+
const original = new VideoPreset(
|
92
|
+
width, height, videoEncoding.maxBitrate, videoEncoding.maxFramerate,
|
93
|
+
);
|
94
|
+
let presets: Array<VideoPreset> = [];
|
95
|
+
if (isScreenShare) {
|
96
|
+
presets = sortPresets(options?.screenShareSimulcastLayers)
|
97
|
+
?? defaultSimulcastLayers(isScreenShare, original);
|
98
|
+
} else {
|
99
|
+
presets = sortPresets(options?.videoSimulcastLayers)
|
100
|
+
?? defaultSimulcastLayers(isScreenShare, original);
|
101
|
+
}
|
87
102
|
let midPreset: VideoPreset | undefined;
|
88
103
|
const lowPreset = presets[0];
|
89
104
|
if (presets.length > 1) {
|
90
|
-
[,midPreset] = presets;
|
105
|
+
[, midPreset] = presets;
|
91
106
|
}
|
92
|
-
const original = new VideoPreset(
|
93
|
-
width, height, videoEncoding.maxBitrate, videoEncoding.maxFramerate,
|
94
|
-
);
|
95
107
|
|
96
108
|
// NOTE:
|
97
109
|
// 1. Ordering of these encodings is important. Chrome seems
|
@@ -108,7 +120,7 @@ export function computeVideoEncodings(
|
|
108
120
|
lowPreset, midPreset, original,
|
109
121
|
]);
|
110
122
|
}
|
111
|
-
if (size >=
|
123
|
+
if (size >= 480) {
|
112
124
|
return encodingsFromPresets(width, height, [
|
113
125
|
lowPreset, original,
|
114
126
|
]);
|
@@ -155,6 +167,21 @@ export function presetsForResolution(
|
|
155
167
|
return presets43;
|
156
168
|
}
|
157
169
|
|
170
|
+
/* @internal */
|
171
|
+
export function defaultSimulcastLayers(
|
172
|
+
isScreenShare: boolean, original: VideoPreset,
|
173
|
+
): VideoPreset[] {
|
174
|
+
if (isScreenShare) {
|
175
|
+
return computeDefaultScreenShareSimulcastPresets(original);
|
176
|
+
}
|
177
|
+
const { width, height } = original;
|
178
|
+
const aspect = width > height ? width / height : height / width;
|
179
|
+
if (Math.abs(aspect - 16.0 / 9) < Math.abs(aspect - 4.0 / 3)) {
|
180
|
+
return defaultSimulcastPresets169;
|
181
|
+
}
|
182
|
+
return defaultSimulcastPresets43;
|
183
|
+
}
|
184
|
+
|
158
185
|
// presets should be ordered by low, medium, high
|
159
186
|
function encodingsFromPresets(
|
160
187
|
width: number,
|
@@ -178,3 +205,21 @@ function encodingsFromPresets(
|
|
178
205
|
});
|
179
206
|
return encodings;
|
180
207
|
}
|
208
|
+
|
209
|
+
/** @internal */
|
210
|
+
export function sortPresets(presets: Array<VideoPreset> | undefined) {
|
211
|
+
if (!presets) return;
|
212
|
+
return presets.sort((a, b) => {
|
213
|
+
const { encoding: aEnc } = a;
|
214
|
+
const { encoding: bEnc } = b;
|
215
|
+
|
216
|
+
if (aEnc.maxBitrate > bEnc.maxBitrate) {
|
217
|
+
return 1;
|
218
|
+
}
|
219
|
+
if (aEnc.maxBitrate < bEnc.maxBitrate) return -1;
|
220
|
+
if (aEnc.maxBitrate === bEnc.maxBitrate && aEnc.maxFramerate && bEnc.maxFramerate) {
|
221
|
+
return aEnc.maxFramerate > bEnc.maxFramerate ? 1 : -1;
|
222
|
+
}
|
223
|
+
return 0;
|
224
|
+
});
|
225
|
+
}
|
@@ -1,9 +1,10 @@
|
|
1
1
|
import log from '../../logger';
|
2
|
+
import { TrackEvent } from '../events';
|
2
3
|
import { AudioSenderStats, computeBitrate, monitorFrequency } from '../stats';
|
3
4
|
import LocalTrack from './LocalTrack';
|
4
5
|
import { AudioCaptureOptions } from './options';
|
5
6
|
import { Track } from './Track';
|
6
|
-
import { constraintsForOptions } from './utils';
|
7
|
+
import { constraintsForOptions, detectSilence } from './utils';
|
7
8
|
|
8
9
|
export default class LocalAudioTrack extends LocalTrack {
|
9
10
|
sender?: RTCRtpSender;
|
@@ -18,6 +19,7 @@ export default class LocalAudioTrack extends LocalTrack {
|
|
18
19
|
constraints?: MediaTrackConstraints,
|
19
20
|
) {
|
20
21
|
super(mediaTrack, Track.Kind.Audio, constraints);
|
22
|
+
this.checkForSilence();
|
21
23
|
}
|
22
24
|
|
23
25
|
async setDeviceId(deviceId: string) {
|
@@ -61,6 +63,12 @@ export default class LocalAudioTrack extends LocalTrack {
|
|
61
63
|
await this.restart(constraints);
|
62
64
|
}
|
63
65
|
|
66
|
+
protected async restart(constraints?: MediaTrackConstraints): Promise<LocalTrack> {
|
67
|
+
const track = await super.restart(constraints);
|
68
|
+
this.checkForSilence();
|
69
|
+
return track;
|
70
|
+
}
|
71
|
+
|
64
72
|
/* @internal */
|
65
73
|
startMonitor() {
|
66
74
|
setTimeout(() => {
|
@@ -116,4 +124,14 @@ export default class LocalAudioTrack extends LocalTrack {
|
|
116
124
|
|
117
125
|
return audioStats;
|
118
126
|
}
|
127
|
+
|
128
|
+
async checkForSilence() {
|
129
|
+
const trackIsSilent = await detectSilence(this);
|
130
|
+
if (trackIsSilent) {
|
131
|
+
if (!this.isMuted) {
|
132
|
+
log.warn('silence detected on local audio track');
|
133
|
+
}
|
134
|
+
this.emit(TrackEvent.AudioSilenceDetected);
|
135
|
+
}
|
136
|
+
}
|
119
137
|
}
|
@@ -2,6 +2,7 @@ import log from '../../logger';
|
|
2
2
|
import DeviceManager from '../DeviceManager';
|
3
3
|
import { TrackInvalidError } from '../errors';
|
4
4
|
import { TrackEvent } from '../events';
|
5
|
+
import { isMobile } from '../utils';
|
5
6
|
import { attachToElement, detachTrack, Track } from './Track';
|
6
7
|
|
7
8
|
export default class LocalTrack extends Track {
|
@@ -10,12 +11,18 @@ export default class LocalTrack extends Track {
|
|
10
11
|
|
11
12
|
protected constraints: MediaTrackConstraints;
|
12
13
|
|
14
|
+
protected wasMuted: boolean;
|
15
|
+
|
16
|
+
protected reacquireTrack: boolean;
|
17
|
+
|
13
18
|
protected constructor(
|
14
19
|
mediaTrack: MediaStreamTrack, kind: Track.Kind, constraints?: MediaTrackConstraints,
|
15
20
|
) {
|
16
21
|
super(mediaTrack, kind);
|
17
22
|
this.mediaStreamTrack.addEventListener('ended', this.handleEnded);
|
18
23
|
this.constraints = constraints ?? mediaTrack.getConstraints();
|
24
|
+
this.reacquireTrack = false;
|
25
|
+
this.wasMuted = false;
|
19
26
|
}
|
20
27
|
|
21
28
|
get id(): string {
|
@@ -118,7 +125,36 @@ export default class LocalTrack extends Track {
|
|
118
125
|
this.emit(muted ? TrackEvent.Muted : TrackEvent.Unmuted, this);
|
119
126
|
}
|
120
127
|
|
128
|
+
protected get needsReAcquisition(): boolean {
|
129
|
+
return this.mediaStreamTrack.readyState !== 'live'
|
130
|
+
|| this.mediaStreamTrack.muted
|
131
|
+
|| !this.mediaStreamTrack.enabled
|
132
|
+
|| this.reacquireTrack;
|
133
|
+
}
|
134
|
+
|
135
|
+
protected async handleAppVisibilityChanged() {
|
136
|
+
await super.handleAppVisibilityChanged();
|
137
|
+
if (!isMobile()) return;
|
138
|
+
log.debug('visibility changed, is in Background: ', this.isInBackground);
|
139
|
+
|
140
|
+
if (!this.isInBackground && this.needsReAcquisition) {
|
141
|
+
log.debug('track needs to be reaquired, restarting', this.source);
|
142
|
+
await this.restart();
|
143
|
+
this.reacquireTrack = false;
|
144
|
+
// Restore muted state if had to be restarted
|
145
|
+
this.setTrackMuted(this.wasMuted);
|
146
|
+
}
|
147
|
+
|
148
|
+
// store muted state each time app goes to background
|
149
|
+
if (this.isInBackground) {
|
150
|
+
this.wasMuted = this.isMuted;
|
151
|
+
}
|
152
|
+
}
|
153
|
+
|
121
154
|
private handleEnded = () => {
|
155
|
+
if (this.isInBackground) {
|
156
|
+
this.reacquireTrack = true;
|
157
|
+
}
|
122
158
|
this.emit(TrackEvent.Ended, this);
|
123
159
|
};
|
124
160
|
}
|
@@ -3,7 +3,7 @@ import log from '../../logger';
|
|
3
3
|
import { VideoLayer, VideoQuality } from '../../proto/livekit_models';
|
4
4
|
import { SubscribedQuality } from '../../proto/livekit_rtc';
|
5
5
|
import { computeBitrate, monitorFrequency, VideoSenderStats } from '../stats';
|
6
|
-
import { isFireFox } from '../utils';
|
6
|
+
import { isFireFox, isMobile } from '../utils';
|
7
7
|
import LocalTrack from './LocalTrack';
|
8
8
|
import { VideoCaptureOptions } from './options';
|
9
9
|
import { Track } from './Track';
|
@@ -238,6 +238,14 @@ export default class LocalVideoTrack extends LocalTrack {
|
|
238
238
|
this.monitorSender();
|
239
239
|
}, monitorFrequency);
|
240
240
|
};
|
241
|
+
|
242
|
+
protected async handleAppVisibilityChanged() {
|
243
|
+
await super.handleAppVisibilityChanged();
|
244
|
+
if (!isMobile()) return;
|
245
|
+
if (this.isInBackground && this.source === Track.Source.Camera) {
|
246
|
+
this.mediaStreamTrack.enabled = false;
|
247
|
+
}
|
248
|
+
}
|
241
249
|
}
|
242
250
|
|
243
251
|
export function videoQualityForRid(rid: string): VideoQuality {
|
@@ -4,6 +4,7 @@ import { computeBitrate, monitorFrequency, VideoReceiverStats } from '../stats';
|
|
4
4
|
import { getIntersectionObserver, getResizeObserver, ObservableMediaElement } from '../utils';
|
5
5
|
import RemoteTrack from './RemoteTrack';
|
6
6
|
import { attachToElement, detachTrack, Track } from './Track';
|
7
|
+
import { AdaptiveStreamSettings } from './types';
|
7
8
|
|
8
9
|
const REACTION_DELAY = 100;
|
9
10
|
|
@@ -15,7 +16,7 @@ export default class RemoteVideoTrack extends RemoteTrack {
|
|
15
16
|
|
16
17
|
private elementInfos: ElementInfo[] = [];
|
17
18
|
|
18
|
-
private
|
19
|
+
private adaptiveStreamSettings?: AdaptiveStreamSettings;
|
19
20
|
|
20
21
|
private lastVisible?: boolean;
|
21
22
|
|
@@ -25,14 +26,14 @@ export default class RemoteVideoTrack extends RemoteTrack {
|
|
25
26
|
mediaTrack: MediaStreamTrack,
|
26
27
|
sid: string,
|
27
28
|
receiver?: RTCRtpReceiver,
|
28
|
-
|
29
|
+
adaptiveStreamSettings?: AdaptiveStreamSettings,
|
29
30
|
) {
|
30
31
|
super(mediaTrack, sid, Track.Kind.Video, receiver);
|
31
|
-
this.
|
32
|
+
this.adaptiveStreamSettings = adaptiveStreamSettings;
|
32
33
|
}
|
33
34
|
|
34
35
|
get isAdaptiveStream(): boolean {
|
35
|
-
return this.
|
36
|
+
return this.adaptiveStreamSettings !== undefined;
|
36
37
|
}
|
37
38
|
|
38
39
|
/** @internal */
|
@@ -60,7 +61,7 @@ export default class RemoteVideoTrack extends RemoteTrack {
|
|
60
61
|
|
61
62
|
// It's possible attach is called multiple times on an element. When that's
|
62
63
|
// the case, we'd want to avoid adding duplicate elementInfos
|
63
|
-
if (this.
|
64
|
+
if (this.adaptiveStreamSettings
|
64
65
|
&& this.elementInfos.find((info) => info.element === element) === undefined
|
65
66
|
) {
|
66
67
|
this.elementInfos.push({
|
@@ -164,6 +165,12 @@ export default class RemoteVideoTrack extends RemoteTrack {
|
|
164
165
|
this.updateVisibility();
|
165
166
|
};
|
166
167
|
|
168
|
+
protected async handleAppVisibilityChanged() {
|
169
|
+
if (!this.isAdaptiveStream) return;
|
170
|
+
await super.handleAppVisibilityChanged();
|
171
|
+
this.updateVisibility();
|
172
|
+
}
|
173
|
+
|
167
174
|
private readonly debouncedHandleResize = debounce(() => {
|
168
175
|
this.updateDimensions();
|
169
176
|
}, REACTION_DELAY);
|
@@ -173,7 +180,7 @@ export default class RemoteVideoTrack extends RemoteTrack {
|
|
173
180
|
(prev, info) => Math.max(prev, info.visibilityChangedAt || 0),
|
174
181
|
0,
|
175
182
|
);
|
176
|
-
const isVisible = this.elementInfos.some((info) => info.visible);
|
183
|
+
const isVisible = this.elementInfos.some((info) => info.visible) && !this.isInBackground;
|
177
184
|
|
178
185
|
if (this.lastVisible === isVisible) {
|
179
186
|
return;
|
@@ -195,9 +202,13 @@ export default class RemoteVideoTrack extends RemoteTrack {
|
|
195
202
|
let maxWidth = 0;
|
196
203
|
let maxHeight = 0;
|
197
204
|
for (const info of this.elementInfos) {
|
198
|
-
|
199
|
-
|
200
|
-
|
205
|
+
const pixelDensity = this.adaptiveStreamSettings?.pixelDensity ?? 1;
|
206
|
+
const pixelDensityValue = pixelDensity === 'screen' ? window.devicePixelRatio : pixelDensity;
|
207
|
+
const currentElementWidth = info.element.clientWidth * pixelDensityValue;
|
208
|
+
const currentElementHeight = info.element.clientHeight * pixelDensityValue;
|
209
|
+
if (currentElementWidth + currentElementHeight > maxWidth + maxHeight) {
|
210
|
+
maxWidth = currentElementWidth;
|
211
|
+
maxHeight = currentElementHeight;
|
201
212
|
}
|
202
213
|
}
|
203
214
|
|
package/src/room/track/Track.ts
CHANGED
@@ -18,10 +18,10 @@ export class Track extends (EventEmitter as new () => TypedEventEmitter<TrackEve
|
|
18
18
|
|
19
19
|
isMuted: boolean = false;
|
20
20
|
|
21
|
-
streamState: Track.StreamState = Track.StreamState.Active;
|
22
|
-
|
23
21
|
source: Track.Source;
|
24
22
|
|
23
|
+
protected isInBackground: boolean;
|
24
|
+
|
25
25
|
/**
|
26
26
|
* sid is set after track is published to server, or if it's a remote track
|
27
27
|
*/
|
@@ -34,6 +34,8 @@ export class Track extends (EventEmitter as new () => TypedEventEmitter<TrackEve
|
|
34
34
|
this.kind = kind;
|
35
35
|
this.mediaStreamTrack = mediaTrack;
|
36
36
|
this.source = Track.Source.Unknown;
|
37
|
+
this.isInBackground = document.visibilityState === 'hidden';
|
38
|
+
document.addEventListener('visibilitychange', this.appVisibilityChangedListener);
|
37
39
|
}
|
38
40
|
|
39
41
|
/** current receive bits per second */
|
@@ -131,6 +133,7 @@ export class Track extends (EventEmitter as new () => TypedEventEmitter<TrackEve
|
|
131
133
|
|
132
134
|
stop() {
|
133
135
|
this.mediaStreamTrack.stop();
|
136
|
+
document.removeEventListener('visibilitychange', this.appVisibilityChangedListener);
|
134
137
|
}
|
135
138
|
|
136
139
|
protected enable() {
|
@@ -156,6 +159,14 @@ export class Track extends (EventEmitter as new () => TypedEventEmitter<TrackEve
|
|
156
159
|
}
|
157
160
|
}
|
158
161
|
}
|
162
|
+
|
163
|
+
appVisibilityChangedListener = () => {
|
164
|
+
this.handleAppVisibilityChanged();
|
165
|
+
};
|
166
|
+
|
167
|
+
protected async handleAppVisibilityChanged() {
|
168
|
+
this.isInBackground = document.visibilityState === 'hidden';
|
169
|
+
}
|
159
170
|
}
|
160
171
|
|
161
172
|
/** @internal */
|
@@ -318,6 +329,7 @@ export type TrackEventCallbacks = {
|
|
318
329
|
updateSubscription: () => void,
|
319
330
|
audioPlaybackStarted: () => void,
|
320
331
|
audioPlaybackFailed: (error: Error) => void,
|
332
|
+
audioSilenceDetected: () => void,
|
321
333
|
visibilityChanged: (visible: boolean, track?: any) => void,
|
322
334
|
videoDimensionsChanged: (dimensions: Track.Dimensions, track?: any) => void,
|
323
335
|
};
|
@@ -7,7 +7,7 @@ export const publishDefaults: TrackPublishDefaults = {
|
|
7
7
|
audioBitrate: AudioPresets.speech.maxBitrate,
|
8
8
|
dtx: true,
|
9
9
|
simulcast: true,
|
10
|
-
screenShareEncoding: ScreenSharePresets.
|
10
|
+
screenShareEncoding: ScreenSharePresets.h720fps15.encoding,
|
11
11
|
stopMicTrackOnMute: false,
|
12
12
|
};
|
13
13
|
|
@@ -19,5 +19,5 @@ export const audioDefaults: AudioCaptureOptions = {
|
|
19
19
|
};
|
20
20
|
|
21
21
|
export const videoDefaults: VideoCaptureOptions = {
|
22
|
-
resolution: VideoPresets.
|
22
|
+
resolution: VideoPresets.h540.resolution,
|
23
23
|
};
|
@@ -33,6 +33,20 @@ export interface TrackPublishDefaults {
|
|
33
33
|
*/
|
34
34
|
simulcast?: boolean;
|
35
35
|
|
36
|
+
/**
|
37
|
+
* custom video simulcast layers for camera tracks, defaults to h180, h360, h540
|
38
|
+
* You can specify up to two custom layers that will be used instead of
|
39
|
+
* the LiveKit default layers.
|
40
|
+
* Note: the layers need to be ordered from lowest to highest quality
|
41
|
+
*/
|
42
|
+
videoSimulcastLayers?: Array<VideoPreset>;
|
43
|
+
|
44
|
+
/**
|
45
|
+
* custom video simulcast layers for screen tracks
|
46
|
+
* Note: the layers need to be ordered from lowest to highest quality
|
47
|
+
*/
|
48
|
+
screenShareSimulcastLayers?: Array<VideoPreset>;
|
49
|
+
|
36
50
|
/**
|
37
51
|
* For local tracks, stop the underlying MediaStreamTrack when the track is muted (or paused)
|
38
52
|
* on some platforms, this option is necessary to disable the microphone recording indicator.
|
@@ -202,28 +216,66 @@ export namespace AudioPresets {
|
|
202
216
|
* Sane presets for video resolution/encoding
|
203
217
|
*/
|
204
218
|
export const VideoPresets = {
|
219
|
+
h90: new VideoPreset(160, 90, 60_000, 15),
|
220
|
+
h180: new VideoPreset(320, 180, 120_000, 15),
|
221
|
+
h216: new VideoPreset(384, 216, 180_000, 15),
|
222
|
+
h360: new VideoPreset(640, 360, 300_000, 20),
|
223
|
+
h540: new VideoPreset(960, 540, 600_000, 25),
|
224
|
+
h720: new VideoPreset(1280, 720, 2_000_000, 30),
|
225
|
+
h1080: new VideoPreset(1920, 1080, 3_000_000, 30),
|
226
|
+
h1440: new VideoPreset(2560, 1440, 5_000_000, 30),
|
227
|
+
h2160: new VideoPreset(3840, 2160, 8_000_000, 30),
|
228
|
+
/** @deprecated */
|
205
229
|
qvga: new VideoPreset(320, 180, 120_000, 10),
|
230
|
+
/** @deprecated */
|
206
231
|
vga: new VideoPreset(640, 360, 300_000, 20),
|
232
|
+
/** @deprecated */
|
207
233
|
qhd: new VideoPreset(960, 540, 600_000, 25),
|
234
|
+
/** @deprecated */
|
208
235
|
hd: new VideoPreset(1280, 720, 2_000_000, 30),
|
236
|
+
/** @deprecated */
|
209
237
|
fhd: new VideoPreset(1920, 1080, 3_000_000, 30),
|
210
|
-
};
|
238
|
+
} as const;
|
211
239
|
|
212
240
|
/**
|
213
241
|
* Four by three presets
|
214
242
|
*/
|
215
243
|
export const VideoPresets43 = {
|
244
|
+
h120: new VideoPreset(160, 120, 80_000, 15),
|
245
|
+
h180: new VideoPreset(240, 180, 100_000, 15),
|
246
|
+
h240: new VideoPreset(320, 240, 150_000, 15),
|
247
|
+
h360: new VideoPreset(480, 360, 225_000, 20),
|
248
|
+
h480: new VideoPreset(640, 480, 300_000, 20),
|
249
|
+
h540: new VideoPreset(720, 540, 450_000, 25),
|
250
|
+
h720: new VideoPreset(960, 720, 1_500_000, 30),
|
251
|
+
h1080: new VideoPreset(1440, 1080, 2_500_000, 30),
|
252
|
+
h1440: new VideoPreset(1920, 1440, 3_500_000, 30),
|
253
|
+
/** @deprecated */
|
216
254
|
qvga: new VideoPreset(240, 180, 90_000, 10),
|
255
|
+
/** @deprecated */
|
217
256
|
vga: new VideoPreset(480, 360, 225_000, 20),
|
257
|
+
/** @deprecated */
|
218
258
|
qhd: new VideoPreset(720, 540, 450_000, 25),
|
259
|
+
/** @deprecated */
|
219
260
|
hd: new VideoPreset(960, 720, 1_500_000, 30),
|
261
|
+
/** @deprecated */
|
220
262
|
fhd: new VideoPreset(1440, 1080, 2_800_000, 30),
|
221
|
-
};
|
263
|
+
} as const;
|
222
264
|
|
223
265
|
export const ScreenSharePresets = {
|
266
|
+
h360fps3: new VideoPreset(640, 360, 200_000, 3),
|
267
|
+
h720fps5: new VideoPreset(1280, 720, 400_000, 5),
|
268
|
+
h720fps15: new VideoPreset(1280, 720, 1_000_000, 15),
|
269
|
+
h1080fps15: new VideoPreset(1920, 1080, 1_500_000, 15),
|
270
|
+
h1080fps30: new VideoPreset(1920, 1080, 3_000_000, 30),
|
271
|
+
/** @deprecated */
|
224
272
|
vga: new VideoPreset(640, 360, 200_000, 3),
|
273
|
+
/** @deprecated */
|
225
274
|
hd_8: new VideoPreset(1280, 720, 400_000, 5),
|
275
|
+
/** @deprecated */
|
226
276
|
hd_15: new VideoPreset(1280, 720, 1_000_000, 15),
|
277
|
+
/** @deprecated */
|
227
278
|
fhd_15: new VideoPreset(1920, 1080, 1_500_000, 15),
|
279
|
+
/** @deprecated */
|
228
280
|
fhd_30: new VideoPreset(1920, 1080, 3_000_000, 30),
|
229
|
-
};
|
281
|
+
} as const;
|
package/src/room/track/types.ts
CHANGED
@@ -6,3 +6,15 @@ import type RemoteVideoTrack from './RemoteVideoTrack';
|
|
6
6
|
export type RemoteTrack = RemoteAudioTrack | RemoteVideoTrack;
|
7
7
|
export type AudioTrack = RemoteAudioTrack | LocalAudioTrack;
|
8
8
|
export type VideoTrack = RemoteVideoTrack | LocalVideoTrack;
|
9
|
+
|
10
|
+
export type AdaptiveStreamSettings = {
|
11
|
+
/**
|
12
|
+
* Set a custom pixel density, defaults to 1
|
13
|
+
* When streaming videos on a ultra high definition screen this setting
|
14
|
+
* let's you account for the devicePixelRatio of those screens.
|
15
|
+
* Set it to `screen` to use the actual pixel density of the screen
|
16
|
+
* Note: this might significantly increase the bandwidth consumed by people
|
17
|
+
* streaming on high definition screens.
|
18
|
+
*/
|
19
|
+
pixelDensity?: number | 'screen'
|
20
|
+
};
|
package/src/room/track/utils.ts
CHANGED
@@ -1,7 +1,9 @@
|
|
1
|
+
import { sleep } from '../utils';
|
1
2
|
import {
|
2
3
|
AudioCaptureOptions, CreateLocalTracksOptions,
|
3
4
|
VideoCaptureOptions,
|
4
5
|
} from './options';
|
6
|
+
import { AudioTrack } from './types';
|
5
7
|
|
6
8
|
export function mergeDefaultOptions(
|
7
9
|
options?: CreateLocalTracksOptions,
|
@@ -74,3 +76,40 @@ export function constraintsForOptions(options: CreateLocalTracksOptions): MediaS
|
|
74
76
|
}
|
75
77
|
return constraints;
|
76
78
|
}
|
79
|
+
/**
|
80
|
+
* This function detects silence on a given [[Track]] instance.
|
81
|
+
* Returns true if the track seems to be entirely silent.
|
82
|
+
*/
|
83
|
+
export async function detectSilence(
|
84
|
+
track: AudioTrack,
|
85
|
+
timeOffset = 200,
|
86
|
+
): Promise<boolean> {
|
87
|
+
const ctx = getNewAudioContext();
|
88
|
+
if (ctx) {
|
89
|
+
const analyser = ctx.createAnalyser();
|
90
|
+
analyser.fftSize = 2048;
|
91
|
+
|
92
|
+
const bufferLength = analyser.frequencyBinCount;
|
93
|
+
const dataArray = new Uint8Array(bufferLength);
|
94
|
+
const source = ctx.createMediaStreamSource(new MediaStream([track.mediaStreamTrack]));
|
95
|
+
|
96
|
+
source.connect(analyser);
|
97
|
+
await sleep(timeOffset);
|
98
|
+
analyser.getByteTimeDomainData(dataArray);
|
99
|
+
const someNoise = dataArray.some((sample) => sample !== 128 && sample !== 0);
|
100
|
+
ctx.close();
|
101
|
+
return !someNoise;
|
102
|
+
}
|
103
|
+
return false;
|
104
|
+
}
|
105
|
+
|
106
|
+
/**
|
107
|
+
* @internal
|
108
|
+
*/
|
109
|
+
export function getNewAudioContext(): AudioContext | void {
|
110
|
+
// @ts-ignore
|
111
|
+
const AudioContext = window.AudioContext || window.webkitAudioContext;
|
112
|
+
if (AudioContext) {
|
113
|
+
return new AudioContext();
|
114
|
+
}
|
115
|
+
}
|
package/src/room/utils.ts
CHANGED
@@ -23,6 +23,10 @@ export function isSafari(): boolean {
|
|
23
23
|
return /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
|
24
24
|
}
|
25
25
|
|
26
|
+
export function isMobile(): boolean {
|
27
|
+
return /Tablet|iPad|Mobile|Android|BlackBerry/.test(navigator.userAgent);
|
28
|
+
}
|
29
|
+
|
26
30
|
function roDispatchCallback(entries: ResizeObserverEntry[]) {
|
27
31
|
for (const entry of entries) {
|
28
32
|
(entry.target as ObservableMediaElement).handleResize(entry);
|
package/src/version.ts
CHANGED
@@ -1,2 +1,2 @@
|
|
1
|
-
export const version = '0.16.
|
1
|
+
export const version = '0.16.6';
|
2
2
|
export const protocolVersion = 6;
|