livekit-client 2.9.0 → 2.9.2
Sign up to get free protection for your applications and to get access to all the features.
- package/dist/livekit-client.esm.mjs +393 -75
- 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/connectionHelper/ConnectionCheck.d.ts +2 -0
- package/dist/src/connectionHelper/ConnectionCheck.d.ts.map +1 -1
- package/dist/src/connectionHelper/checks/Checker.d.ts +5 -2
- package/dist/src/connectionHelper/checks/Checker.d.ts.map +1 -1
- package/dist/src/connectionHelper/checks/cloudRegion.d.ts +17 -0
- package/dist/src/connectionHelper/checks/cloudRegion.d.ts.map +1 -0
- package/dist/src/connectionHelper/checks/connectionProtocol.d.ts +19 -0
- package/dist/src/connectionHelper/checks/connectionProtocol.d.ts.map +1 -0
- package/dist/src/connectionHelper/checks/publishAudio.d.ts.map +1 -1
- package/dist/src/connectionHelper/checks/publishVideo.d.ts +1 -0
- package/dist/src/connectionHelper/checks/publishVideo.d.ts.map +1 -1
- package/dist/src/index.d.ts +2 -2
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/room/StreamReader.d.ts +4 -4
- package/dist/src/room/StreamReader.d.ts.map +1 -1
- package/dist/src/room/StreamWriter.d.ts +3 -3
- package/dist/src/room/StreamWriter.d.ts.map +1 -1
- 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/track/LocalTrack.d.ts.map +1 -1
- package/dist/src/room/track/create.d.ts.map +1 -1
- package/dist/src/room/types.d.ts +0 -5
- package/dist/src/room/types.d.ts.map +1 -1
- package/dist/src/room/utils.d.ts +1 -0
- package/dist/src/room/utils.d.ts.map +1 -1
- package/dist/ts4.2/src/connectionHelper/ConnectionCheck.d.ts +2 -0
- package/dist/ts4.2/src/connectionHelper/checks/Checker.d.ts +5 -2
- package/dist/ts4.2/src/connectionHelper/checks/cloudRegion.d.ts +18 -0
- package/dist/ts4.2/src/connectionHelper/checks/connectionProtocol.d.ts +20 -0
- package/dist/ts4.2/src/connectionHelper/checks/publishVideo.d.ts +1 -0
- package/dist/ts4.2/src/index.d.ts +2 -2
- package/dist/ts4.2/src/room/StreamReader.d.ts +4 -4
- package/dist/ts4.2/src/room/StreamWriter.d.ts +3 -12
- package/dist/ts4.2/src/room/types.d.ts +0 -5
- package/dist/ts4.2/src/room/utils.d.ts +1 -0
- package/package.json +17 -17
- package/src/connectionHelper/ConnectionCheck.ts +15 -0
- package/src/connectionHelper/checks/Checker.ts +41 -8
- package/src/connectionHelper/checks/cloudRegion.ts +94 -0
- package/src/connectionHelper/checks/connectionProtocol.ts +149 -0
- package/src/connectionHelper/checks/publishAudio.ts +8 -0
- package/src/connectionHelper/checks/publishVideo.ts +52 -0
- package/src/index.ts +1 -1
- package/src/room/StreamReader.ts +9 -16
- package/src/room/StreamWriter.ts +4 -4
- package/src/room/participant/LocalParticipant.ts +9 -26
- package/src/room/participant/publishUtils.ts +4 -0
- package/src/room/track/LocalTrack.ts +5 -2
- package/src/room/track/create.ts +9 -5
- package/src/room/types.ts +0 -6
- package/src/room/utils.ts +16 -0
@@ -6666,6 +6666,18 @@ PERFORMANCE OF THIS SOFTWARE.
|
|
6666
6666
|
/* global Reflect, Promise, SuppressedError, Symbol, Iterator */
|
6667
6667
|
|
6668
6668
|
|
6669
|
+
function __rest(s, e) {
|
6670
|
+
var t = {};
|
6671
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
6672
|
+
t[p] = s[p];
|
6673
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
6674
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
6675
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
6676
|
+
t[p[i]] = s[p[i]];
|
6677
|
+
}
|
6678
|
+
return t;
|
6679
|
+
}
|
6680
|
+
|
6669
6681
|
function __awaiter(thisArg, _arguments, P, generator) {
|
6670
6682
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
6671
6683
|
return new (P || (P = Promise))(function (resolve, reject) {
|
@@ -11150,7 +11162,7 @@ function getOSVersion(ua) {
|
|
11150
11162
|
return ua.includes('mac os') ? getMatch(/\(.+?(\d+_\d+(:?_\d+)?)/, ua, 1).replace(/_/g, '.') : undefined;
|
11151
11163
|
}
|
11152
11164
|
|
11153
|
-
var version$1 = "2.9.
|
11165
|
+
var version$1 = "2.9.2";
|
11154
11166
|
|
11155
11167
|
const version = version$1;
|
11156
11168
|
const protocolVersion = 15;
|
@@ -12130,6 +12142,21 @@ function isLocalParticipant(p) {
|
|
12130
12142
|
function isRemoteParticipant(p) {
|
12131
12143
|
return !p.isLocal;
|
12132
12144
|
}
|
12145
|
+
function splitUtf8(s, n) {
|
12146
|
+
// adapted from https://stackoverflow.com/a/6043797
|
12147
|
+
const result = [];
|
12148
|
+
while (s.length > n) {
|
12149
|
+
let k = n;
|
12150
|
+
// Move back to find the start of a UTF-8 character
|
12151
|
+
while ((s.charCodeAt(k) & 0xc0) === 0x80) {
|
12152
|
+
k--;
|
12153
|
+
}
|
12154
|
+
result.push(s.slice(0, k));
|
12155
|
+
s = s.slice(k);
|
12156
|
+
}
|
12157
|
+
result.push(s);
|
12158
|
+
return result;
|
12159
|
+
}
|
12133
12160
|
|
12134
12161
|
function mergeDefaultOptions(options, audioDefaults, videoDefaults) {
|
12135
12162
|
var _a, _b, _c;
|
@@ -15557,6 +15584,11 @@ class LocalTrack extends Track {
|
|
15557
15584
|
if (!constraints) {
|
15558
15585
|
constraints = this._constraints;
|
15559
15586
|
}
|
15587
|
+
const _a = this._constraints,
|
15588
|
+
{
|
15589
|
+
deviceId
|
15590
|
+
} = _a,
|
15591
|
+
otherConstraints = __rest(_a, ["deviceId"]);
|
15560
15592
|
this.log.debug('restarting track with constraints', Object.assign(Object.assign({}, this.logContext), {
|
15561
15593
|
constraints
|
15562
15594
|
}));
|
@@ -15565,9 +15597,13 @@ class LocalTrack extends Track {
|
|
15565
15597
|
video: false
|
15566
15598
|
};
|
15567
15599
|
if (this.kind === Track.Kind.Video) {
|
15568
|
-
streamConstraints.video =
|
15600
|
+
streamConstraints.video = deviceId ? {
|
15601
|
+
deviceId
|
15602
|
+
} : true;
|
15569
15603
|
} else {
|
15570
|
-
streamConstraints.audio =
|
15604
|
+
streamConstraints.audio = deviceId ? {
|
15605
|
+
deviceId
|
15606
|
+
} : true;
|
15571
15607
|
}
|
15572
15608
|
// these steps are duplicated from setMediaStreamTrack because we must stop
|
15573
15609
|
// the previous tracks before new tracks can be acquired
|
@@ -15582,6 +15618,7 @@ class LocalTrack extends Track {
|
|
15582
15618
|
// create new track and attach
|
15583
15619
|
const mediaStream = yield navigator.mediaDevices.getUserMedia(streamConstraints);
|
15584
15620
|
const newTrack = mediaStream.getTracks()[0];
|
15621
|
+
yield newTrack.applyConstraints(otherConstraints);
|
15585
15622
|
newTrack.addEventListener('ended', this.handleEnded);
|
15586
15623
|
this.log.debug('re-acquired MediaStreamTrack', this.logContext);
|
15587
15624
|
yield this.setMediaStreamTrack(newTrack);
|
@@ -16191,6 +16228,10 @@ function computeTrackBackupEncodings(track, videoCodec, opts) {
|
|
16191
16228
|
const settings = track.mediaStreamTrack.getSettings();
|
16192
16229
|
const width = (_a = settings.width) !== null && _a !== void 0 ? _a : (_b = track.dimensions) === null || _b === void 0 ? void 0 : _b.width;
|
16193
16230
|
const height = (_c = settings.height) !== null && _c !== void 0 ? _c : (_d = track.dimensions) === null || _d === void 0 ? void 0 : _d.height;
|
16231
|
+
// disable simulcast for screenshare backup codec since L1Tx is used by primary codec
|
16232
|
+
if (track.source === Track.Source.ScreenShare && opts.simulcast) {
|
16233
|
+
opts.simulcast = false;
|
16234
|
+
}
|
16194
16235
|
const encodings = computeVideoEncodings(track.source === Track.Source.ScreenShare, width, height, opts);
|
16195
16236
|
return encodings;
|
16196
16237
|
}
|
@@ -18239,11 +18280,13 @@ class ByteStreamReader extends BaseStreamReader {
|
|
18239
18280
|
}
|
18240
18281
|
}),
|
18241
18282
|
return() {
|
18242
|
-
|
18243
|
-
|
18244
|
-
|
18245
|
-
|
18246
|
-
|
18283
|
+
return __awaiter(this, void 0, void 0, function* () {
|
18284
|
+
reader.releaseLock();
|
18285
|
+
return {
|
18286
|
+
done: true,
|
18287
|
+
value: undefined
|
18288
|
+
};
|
18289
|
+
});
|
18247
18290
|
}
|
18248
18291
|
};
|
18249
18292
|
}
|
@@ -18322,11 +18365,7 @@ class TextStreamReader extends BaseStreamReader {
|
|
18322
18365
|
this.handleChunkReceived(value);
|
18323
18366
|
return {
|
18324
18367
|
done: false,
|
18325
|
-
value:
|
18326
|
-
index: bigIntToNumber(value.chunkIndex),
|
18327
|
-
current: decoder.decode(value.content),
|
18328
|
-
collected: Array.from(this.receivedChunks.values()).sort((a, b) => bigIntToNumber(a.chunkIndex) - bigIntToNumber(b.chunkIndex)).map(chunk => decoder.decode(chunk.content)).join('')
|
18329
|
-
}
|
18368
|
+
value: decoder.decode(value.content)
|
18330
18369
|
};
|
18331
18370
|
}
|
18332
18371
|
} catch (error) {
|
@@ -18338,26 +18377,26 @@ class TextStreamReader extends BaseStreamReader {
|
|
18338
18377
|
}
|
18339
18378
|
}),
|
18340
18379
|
return() {
|
18341
|
-
|
18342
|
-
|
18343
|
-
|
18344
|
-
|
18345
|
-
|
18380
|
+
return __awaiter(this, void 0, void 0, function* () {
|
18381
|
+
reader.releaseLock();
|
18382
|
+
return {
|
18383
|
+
done: true,
|
18384
|
+
value: undefined
|
18385
|
+
};
|
18386
|
+
});
|
18346
18387
|
}
|
18347
18388
|
};
|
18348
18389
|
}
|
18349
18390
|
readAll() {
|
18350
18391
|
return __awaiter(this, void 0, void 0, function* () {
|
18351
18392
|
var _a, e_2, _b, _c;
|
18352
|
-
let
|
18393
|
+
let finalString = '';
|
18353
18394
|
try {
|
18354
18395
|
for (var _d = true, _e = __asyncValues(this), _f; _f = yield _e.next(), _a = _f.done, !_a; _d = true) {
|
18355
18396
|
_c = _f.value;
|
18356
18397
|
_d = false;
|
18357
|
-
const
|
18358
|
-
|
18359
|
-
} = _c;
|
18360
|
-
latestString = collected;
|
18398
|
+
const chunk = _c;
|
18399
|
+
finalString += chunk;
|
18361
18400
|
}
|
18362
18401
|
} catch (e_2_1) {
|
18363
18402
|
e_2 = {
|
@@ -18370,7 +18409,7 @@ class TextStreamReader extends BaseStreamReader {
|
|
18370
18409
|
if (e_2) throw e_2.error;
|
18371
18410
|
}
|
18372
18411
|
}
|
18373
|
-
return
|
18412
|
+
return finalString;
|
18374
18413
|
});
|
18375
18414
|
}
|
18376
18415
|
}
|
@@ -18383,7 +18422,7 @@ class BaseStreamWriter {
|
|
18383
18422
|
this.info = info;
|
18384
18423
|
}
|
18385
18424
|
write(chunk) {
|
18386
|
-
return this.defaultWriter.write(
|
18425
|
+
return this.defaultWriter.write(chunk);
|
18387
18426
|
}
|
18388
18427
|
close() {
|
18389
18428
|
return __awaiter(this, void 0, void 0, function* () {
|
@@ -20790,14 +20829,9 @@ class LocalParticipant extends Participant {
|
|
20790
20829
|
topic: options === null || options === void 0 ? void 0 : options.topic,
|
20791
20830
|
attachedStreamIds: fileIds
|
20792
20831
|
});
|
20793
|
-
|
20794
|
-
|
20795
|
-
|
20796
|
-
const chunkData = text.slice(i * textChunkSize, Math.min((i + 1) * textChunkSize, totalTextLength));
|
20797
|
-
yield this.engine.waitForBufferStatusLow(DataPacket_Kind.RELIABLE);
|
20798
|
-
yield writer.write(chunkData);
|
20799
|
-
handleProgress(Math.ceil((i + 1) / totalTextChunks), 0);
|
20800
|
-
}
|
20832
|
+
yield writer.write(text);
|
20833
|
+
// set text part of progress to 1
|
20834
|
+
handleProgress(1, 0);
|
20801
20835
|
yield writer.close();
|
20802
20836
|
if ((options === null || options === void 0 ? void 0 : options.attachments) && fileIds) {
|
20803
20837
|
yield Promise.all(options.attachments.map((file, idx) => __awaiter(this, void 0, void 0, function* () {
|
@@ -20857,32 +20891,26 @@ class LocalParticipant extends Participant {
|
|
20857
20891
|
const localP = this;
|
20858
20892
|
const writableStream = new WritableStream({
|
20859
20893
|
// Implement the sink
|
20860
|
-
write(
|
20861
|
-
|
20862
|
-
|
20863
|
-
|
20864
|
-
|
20865
|
-
|
20866
|
-
|
20867
|
-
|
20868
|
-
|
20869
|
-
|
20870
|
-
|
20871
|
-
|
20872
|
-
|
20873
|
-
|
20874
|
-
|
20875
|
-
|
20876
|
-
|
20877
|
-
|
20878
|
-
|
20879
|
-
|
20880
|
-
}
|
20881
|
-
});
|
20882
|
-
yield localP.engine.sendDataPacket(chunkPacket, DataPacket_Kind.RELIABLE);
|
20883
|
-
chunkId += 1;
|
20884
|
-
resolve();
|
20885
|
-
}));
|
20894
|
+
write(text) {
|
20895
|
+
return __awaiter(this, void 0, void 0, function* () {
|
20896
|
+
for (const textChunk in splitUtf8(text, STREAM_CHUNK_SIZE)) {
|
20897
|
+
yield localP.engine.waitForBufferStatusLow(DataPacket_Kind.RELIABLE);
|
20898
|
+
const chunk = new DataStream_Chunk({
|
20899
|
+
content: new TextEncoder().encode(textChunk),
|
20900
|
+
streamId,
|
20901
|
+
chunkIndex: numberToBigInt(chunkId)
|
20902
|
+
});
|
20903
|
+
const chunkPacket = new DataPacket({
|
20904
|
+
destinationIdentities,
|
20905
|
+
value: {
|
20906
|
+
case: 'streamChunk',
|
20907
|
+
value: chunk
|
20908
|
+
}
|
20909
|
+
});
|
20910
|
+
yield localP.engine.sendDataPacket(chunkPacket, DataPacket_Kind.RELIABLE);
|
20911
|
+
chunkId += 1;
|
20912
|
+
}
|
20913
|
+
});
|
20886
20914
|
},
|
20887
20915
|
close() {
|
20888
20916
|
return __awaiter(this, void 0, void 0, function* () {
|
@@ -20996,14 +21024,14 @@ class LocalParticipant extends Participant {
|
|
20996
21024
|
* @throws Error on failure. Details in `message`.
|
20997
21025
|
*/
|
20998
21026
|
performRpc(_a) {
|
20999
|
-
return __awaiter(this, arguments, void 0, function (
|
21027
|
+
return __awaiter(this, arguments, void 0, function (_ref3) {
|
21000
21028
|
var _this5 = this;
|
21001
21029
|
let {
|
21002
21030
|
destinationIdentity,
|
21003
21031
|
method,
|
21004
21032
|
payload,
|
21005
21033
|
responseTimeout = 10000
|
21006
|
-
} =
|
21034
|
+
} = _ref3;
|
21007
21035
|
return function* () {
|
21008
21036
|
const maxRoundTripLatency = 2000;
|
21009
21037
|
return new Promise((resolve, reject) => __awaiter(_this5, void 0, void 0, function* () {
|
@@ -21208,8 +21236,8 @@ class LocalParticipant extends Participant {
|
|
21208
21236
|
const waitForPendingTimeout = 10000;
|
21209
21237
|
const startTime = Date.now();
|
21210
21238
|
while (Date.now() < startTime + waitForPendingTimeout) {
|
21211
|
-
const publishPromiseEntry = Array.from(this.pendingPublishPromises.entries()).find(
|
21212
|
-
let [pendingTrack] =
|
21239
|
+
const publishPromiseEntry = Array.from(this.pendingPublishPromises.entries()).find(_ref4 => {
|
21240
|
+
let [pendingTrack] = _ref4;
|
21213
21241
|
return pendingTrack.source === source;
|
21214
21242
|
});
|
21215
21243
|
if (publishPromiseEntry) {
|
@@ -23666,15 +23694,13 @@ class Checker extends eventsExports.EventEmitter {
|
|
23666
23694
|
super();
|
23667
23695
|
this.status = CheckStatus.IDLE;
|
23668
23696
|
this.logs = [];
|
23669
|
-
this.
|
23697
|
+
this.options = {};
|
23670
23698
|
this.url = url;
|
23671
23699
|
this.token = token;
|
23672
23700
|
this.name = this.constructor.name;
|
23673
23701
|
this.room = new Room(options.roomOptions);
|
23674
23702
|
this.connectOptions = options.connectOptions;
|
23675
|
-
|
23676
|
-
this.errorsAsWarnings = options.errorsAsWarnings;
|
23677
|
-
}
|
23703
|
+
this.options = options;
|
23678
23704
|
}
|
23679
23705
|
run(onComplete) {
|
23680
23706
|
return __awaiter(this, void 0, void 0, function* () {
|
@@ -23686,7 +23712,7 @@ class Checker extends eventsExports.EventEmitter {
|
|
23686
23712
|
yield this.perform();
|
23687
23713
|
} catch (err) {
|
23688
23714
|
if (err instanceof Error) {
|
23689
|
-
if (this.errorsAsWarnings) {
|
23715
|
+
if (this.options.errorsAsWarnings) {
|
23690
23716
|
this.appendWarning(err.message);
|
23691
23717
|
} else {
|
23692
23718
|
this.appendError(err.message);
|
@@ -23709,12 +23735,15 @@ class Checker extends eventsExports.EventEmitter {
|
|
23709
23735
|
isSuccess() {
|
23710
23736
|
return !this.logs.some(l => l.level === 'error');
|
23711
23737
|
}
|
23712
|
-
connect() {
|
23738
|
+
connect(url) {
|
23713
23739
|
return __awaiter(this, void 0, void 0, function* () {
|
23714
23740
|
if (this.room.state === ConnectionState.Connected) {
|
23715
23741
|
return this.room;
|
23716
23742
|
}
|
23717
|
-
|
23743
|
+
if (!url) {
|
23744
|
+
url = this.url;
|
23745
|
+
}
|
23746
|
+
yield this.room.connect(url, this.token, this.connectOptions);
|
23718
23747
|
return this.room;
|
23719
23748
|
});
|
23720
23749
|
}
|
@@ -23730,6 +23759,33 @@ class Checker extends eventsExports.EventEmitter {
|
|
23730
23759
|
skip() {
|
23731
23760
|
this.setStatus(CheckStatus.SKIPPED);
|
23732
23761
|
}
|
23762
|
+
switchProtocol(protocol) {
|
23763
|
+
return __awaiter(this, void 0, void 0, function* () {
|
23764
|
+
let hasReconnecting = false;
|
23765
|
+
let hasReconnected = false;
|
23766
|
+
this.room.on(RoomEvent.Reconnecting, () => {
|
23767
|
+
hasReconnecting = true;
|
23768
|
+
});
|
23769
|
+
this.room.once(RoomEvent.Reconnected, () => {
|
23770
|
+
hasReconnected = true;
|
23771
|
+
});
|
23772
|
+
this.room.simulateScenario("force-".concat(protocol));
|
23773
|
+
yield new Promise(resolve => setTimeout(resolve, 1000));
|
23774
|
+
if (!hasReconnecting) {
|
23775
|
+
// no need to wait for reconnection
|
23776
|
+
return;
|
23777
|
+
}
|
23778
|
+
// wait for 10 seconds for reconnection
|
23779
|
+
const timeout = Date.now() + 10000;
|
23780
|
+
while (Date.now() < timeout) {
|
23781
|
+
if (hasReconnected) {
|
23782
|
+
return;
|
23783
|
+
}
|
23784
|
+
yield sleep(100);
|
23785
|
+
}
|
23786
|
+
throw new Error("Could not reconnect using ".concat(protocol, " protocol after 10 seconds"));
|
23787
|
+
});
|
23788
|
+
}
|
23733
23789
|
appendMessage(message) {
|
23734
23790
|
this.logs.push({
|
23735
23791
|
level: 'info',
|
@@ -23769,6 +23825,201 @@ class Checker extends eventsExports.EventEmitter {
|
|
23769
23825
|
}
|
23770
23826
|
}
|
23771
23827
|
|
23828
|
+
/**
|
23829
|
+
* Checks for connections quality to closests Cloud regions and determining the best quality
|
23830
|
+
*/
|
23831
|
+
class CloudRegionCheck extends Checker {
|
23832
|
+
get description() {
|
23833
|
+
return 'Cloud regions';
|
23834
|
+
}
|
23835
|
+
perform() {
|
23836
|
+
return __awaiter(this, void 0, void 0, function* () {
|
23837
|
+
const regionProvider = new RegionUrlProvider(this.url, this.token);
|
23838
|
+
if (!regionProvider.isCloud()) {
|
23839
|
+
this.skip();
|
23840
|
+
return;
|
23841
|
+
}
|
23842
|
+
const regionStats = [];
|
23843
|
+
const seenUrls = new Set();
|
23844
|
+
for (let i = 0; i < 3; i++) {
|
23845
|
+
const regionUrl = yield regionProvider.getNextBestRegionUrl();
|
23846
|
+
if (!regionUrl) {
|
23847
|
+
break;
|
23848
|
+
}
|
23849
|
+
if (seenUrls.has(regionUrl)) {
|
23850
|
+
continue;
|
23851
|
+
}
|
23852
|
+
seenUrls.add(regionUrl);
|
23853
|
+
const stats = yield this.checkCloudRegion(regionUrl);
|
23854
|
+
this.appendMessage("".concat(stats.region, " RTT: ").concat(stats.rtt, "ms, duration: ").concat(stats.duration, "ms"));
|
23855
|
+
regionStats.push(stats);
|
23856
|
+
}
|
23857
|
+
regionStats.sort((a, b) => {
|
23858
|
+
return (a.duration - b.duration) * 0.5 + (a.rtt - b.rtt) * 0.5;
|
23859
|
+
});
|
23860
|
+
const bestRegion = regionStats[0];
|
23861
|
+
this.bestStats = bestRegion;
|
23862
|
+
this.appendMessage("best Cloud region: ".concat(bestRegion.region));
|
23863
|
+
});
|
23864
|
+
}
|
23865
|
+
getInfo() {
|
23866
|
+
const info = super.getInfo();
|
23867
|
+
info.data = this.bestStats;
|
23868
|
+
return info;
|
23869
|
+
}
|
23870
|
+
checkCloudRegion(url) {
|
23871
|
+
return __awaiter(this, void 0, void 0, function* () {
|
23872
|
+
var _a, _b;
|
23873
|
+
yield this.connect(url);
|
23874
|
+
if (this.options.protocol === 'tcp') {
|
23875
|
+
yield this.switchProtocol('tcp');
|
23876
|
+
}
|
23877
|
+
const region = (_a = this.room.serverInfo) === null || _a === void 0 ? void 0 : _a.region;
|
23878
|
+
if (!region) {
|
23879
|
+
throw new Error('Region not found');
|
23880
|
+
}
|
23881
|
+
const writer = yield this.room.localParticipant.streamText({
|
23882
|
+
topic: 'test'
|
23883
|
+
});
|
23884
|
+
const chunkSize = 1000; // each chunk is about 1000 bytes
|
23885
|
+
const totalSize = 1000000; // approximately 1MB of data
|
23886
|
+
const numChunks = totalSize / chunkSize; // will yield 1000 chunks
|
23887
|
+
const chunkData = 'A'.repeat(chunkSize); // create a string of 1000 'A' characters
|
23888
|
+
const startTime = Date.now();
|
23889
|
+
for (let i = 0; i < numChunks; i++) {
|
23890
|
+
yield writer.write(chunkData);
|
23891
|
+
}
|
23892
|
+
yield writer.close();
|
23893
|
+
const endTime = Date.now();
|
23894
|
+
const stats = yield (_b = this.room.engine.pcManager) === null || _b === void 0 ? void 0 : _b.publisher.getStats();
|
23895
|
+
const regionStats = {
|
23896
|
+
region: region,
|
23897
|
+
rtt: 10000,
|
23898
|
+
duration: endTime - startTime
|
23899
|
+
};
|
23900
|
+
stats === null || stats === void 0 ? void 0 : stats.forEach(stat => {
|
23901
|
+
if (stat.type === 'candidate-pair' && stat.nominated) {
|
23902
|
+
regionStats.rtt = stat.currentRoundTripTime * 1000;
|
23903
|
+
}
|
23904
|
+
});
|
23905
|
+
yield this.disconnect();
|
23906
|
+
return regionStats;
|
23907
|
+
});
|
23908
|
+
}
|
23909
|
+
}
|
23910
|
+
|
23911
|
+
const TEST_DURATION = 10000;
|
23912
|
+
class ConnectionProtocolCheck extends Checker {
|
23913
|
+
get description() {
|
23914
|
+
return 'Connection via UDP vs TCP';
|
23915
|
+
}
|
23916
|
+
perform() {
|
23917
|
+
return __awaiter(this, void 0, void 0, function* () {
|
23918
|
+
const udpStats = yield this.checkConnectionProtocol('udp');
|
23919
|
+
const tcpStats = yield this.checkConnectionProtocol('tcp');
|
23920
|
+
this.bestStats = udpStats;
|
23921
|
+
// udp should is the better protocol typically. however, we'd prefer TCP when either of these conditions are true:
|
23922
|
+
// 1. the bandwidth limitation is worse on UDP by 500ms
|
23923
|
+
// 2. the packet loss is higher on UDP by 1%
|
23924
|
+
if (udpStats.qualityLimitationDurations.bandwidth - tcpStats.qualityLimitationDurations.bandwidth > 0.5 || (udpStats.packetsLost - tcpStats.packetsLost) / udpStats.packetsSent > 0.01) {
|
23925
|
+
this.appendMessage('best connection quality via tcp');
|
23926
|
+
this.bestStats = tcpStats;
|
23927
|
+
} else {
|
23928
|
+
this.appendMessage('best connection quality via udp');
|
23929
|
+
}
|
23930
|
+
const stats = this.bestStats;
|
23931
|
+
this.appendMessage("upstream bitrate: ".concat((stats.bitrateTotal / stats.count / 1000 / 1000).toFixed(2), " mbps"));
|
23932
|
+
this.appendMessage("RTT: ".concat((stats.rttTotal / stats.count * 1000).toFixed(2), " ms"));
|
23933
|
+
this.appendMessage("jitter: ".concat((stats.jitterTotal / stats.count * 1000).toFixed(2), " ms"));
|
23934
|
+
if (stats.packetsLost > 0) {
|
23935
|
+
this.appendWarning("packets lost: ".concat((stats.packetsLost / stats.packetsSent * 100).toFixed(2), "%"));
|
23936
|
+
}
|
23937
|
+
if (stats.qualityLimitationDurations.bandwidth > 1) {
|
23938
|
+
this.appendWarning("bandwidth limited ".concat((stats.qualityLimitationDurations.bandwidth / (TEST_DURATION / 1000) * 100).toFixed(2), "%"));
|
23939
|
+
}
|
23940
|
+
if (stats.qualityLimitationDurations.cpu > 0) {
|
23941
|
+
this.appendWarning("cpu limited ".concat((stats.qualityLimitationDurations.cpu / (TEST_DURATION / 1000) * 100).toFixed(2), "%"));
|
23942
|
+
}
|
23943
|
+
});
|
23944
|
+
}
|
23945
|
+
getInfo() {
|
23946
|
+
const info = super.getInfo();
|
23947
|
+
info.data = this.bestStats;
|
23948
|
+
return info;
|
23949
|
+
}
|
23950
|
+
checkConnectionProtocol(protocol) {
|
23951
|
+
return __awaiter(this, void 0, void 0, function* () {
|
23952
|
+
yield this.connect();
|
23953
|
+
if (protocol === 'tcp') {
|
23954
|
+
yield this.switchProtocol('tcp');
|
23955
|
+
} else {
|
23956
|
+
yield this.switchProtocol('udp');
|
23957
|
+
}
|
23958
|
+
// create a canvas with animated content
|
23959
|
+
const canvas = document.createElement('canvas');
|
23960
|
+
canvas.width = 1280;
|
23961
|
+
canvas.height = 720;
|
23962
|
+
const ctx = canvas.getContext('2d');
|
23963
|
+
if (!ctx) {
|
23964
|
+
throw new Error('Could not get canvas context');
|
23965
|
+
}
|
23966
|
+
let hue = 0;
|
23967
|
+
const animate = () => {
|
23968
|
+
hue = (hue + 1) % 360;
|
23969
|
+
ctx.fillStyle = "hsl(".concat(hue, ", 100%, 50%)");
|
23970
|
+
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
23971
|
+
requestAnimationFrame(animate);
|
23972
|
+
};
|
23973
|
+
animate();
|
23974
|
+
// create video track from canvas
|
23975
|
+
const stream = canvas.captureStream(30); // 30fps
|
23976
|
+
const videoTrack = stream.getVideoTracks()[0];
|
23977
|
+
// publish to room
|
23978
|
+
const pub = yield this.room.localParticipant.publishTrack(videoTrack, {
|
23979
|
+
simulcast: false,
|
23980
|
+
degradationPreference: 'maintain-resolution',
|
23981
|
+
videoEncoding: {
|
23982
|
+
maxBitrate: 2000000
|
23983
|
+
}
|
23984
|
+
});
|
23985
|
+
const track = pub.track;
|
23986
|
+
const protocolStats = {
|
23987
|
+
protocol,
|
23988
|
+
packetsLost: 0,
|
23989
|
+
packetsSent: 0,
|
23990
|
+
qualityLimitationDurations: {},
|
23991
|
+
rttTotal: 0,
|
23992
|
+
jitterTotal: 0,
|
23993
|
+
bitrateTotal: 0,
|
23994
|
+
count: 0
|
23995
|
+
};
|
23996
|
+
// gather stats once a second
|
23997
|
+
const interval = setInterval(() => __awaiter(this, void 0, void 0, function* () {
|
23998
|
+
const stats = yield track.getRTCStatsReport();
|
23999
|
+
stats === null || stats === void 0 ? void 0 : stats.forEach(stat => {
|
24000
|
+
if (stat.type === 'outbound-rtp') {
|
24001
|
+
protocolStats.packetsSent = stat.packetsSent;
|
24002
|
+
protocolStats.qualityLimitationDurations = stat.qualityLimitationDurations;
|
24003
|
+
protocolStats.bitrateTotal += stat.targetBitrate;
|
24004
|
+
protocolStats.count++;
|
24005
|
+
} else if (stat.type === 'remote-inbound-rtp') {
|
24006
|
+
protocolStats.packetsLost = stat.packetsLost;
|
24007
|
+
protocolStats.rttTotal += stat.roundTripTime;
|
24008
|
+
protocolStats.jitterTotal += stat.jitter;
|
24009
|
+
}
|
24010
|
+
});
|
24011
|
+
}), 1000);
|
24012
|
+
// wait a bit to gather stats
|
24013
|
+
yield new Promise(resolve => setTimeout(resolve, TEST_DURATION));
|
24014
|
+
clearInterval(interval);
|
24015
|
+
videoTrack.stop();
|
24016
|
+
canvas.remove();
|
24017
|
+
yield this.disconnect();
|
24018
|
+
return protocolStats;
|
24019
|
+
});
|
24020
|
+
}
|
24021
|
+
}
|
24022
|
+
|
23772
24023
|
/**
|
23773
24024
|
* Creates a local video and audio track at the same time. When acquiring both
|
23774
24025
|
* audio and video tracks together, it'll display a single permission prompt to
|
@@ -23814,11 +24065,12 @@ function createLocalTracks(options) {
|
|
23814
24065
|
}
|
23815
24066
|
// update the constraints with the device id the user gave permissions to in the permission prompt
|
23816
24067
|
// otherwise each track restart (e.g. mute - unmute) will try to initialize the device again -> causing additional permission prompts
|
23817
|
-
|
23818
|
-
|
23819
|
-
|
24068
|
+
const newDeviceId = mediaStreamTrack.getSettings().deviceId;
|
24069
|
+
if ((trackConstraints === null || trackConstraints === void 0 ? void 0 : trackConstraints.deviceId) && unwrapConstraint(trackConstraints.deviceId) !== newDeviceId) {
|
24070
|
+
trackConstraints.deviceId = newDeviceId;
|
24071
|
+
} else if (!trackConstraints) {
|
23820
24072
|
trackConstraints = {
|
23821
|
-
deviceId:
|
24073
|
+
deviceId: newDeviceId
|
23822
24074
|
};
|
23823
24075
|
}
|
23824
24076
|
const track = mediaTrackToLocalTrack(mediaStreamTrack, trackConstraints);
|
@@ -23902,6 +24154,11 @@ class PublishAudioCheck extends Checker {
|
|
23902
24154
|
var _a;
|
23903
24155
|
const room = yield this.connect();
|
23904
24156
|
const track = yield createLocalAudioTrack();
|
24157
|
+
const trackIsSilent = yield detectSilence(track, 1000);
|
24158
|
+
if (trackIsSilent) {
|
24159
|
+
throw new Error('unable to detect audio from microphone');
|
24160
|
+
}
|
24161
|
+
this.appendMessage('detected audio from microphone');
|
23905
24162
|
room.localParticipant.publishTrack(track);
|
23906
24163
|
// wait for a few seconds to publish
|
23907
24164
|
yield new Promise(resolve => setTimeout(resolve, 3000));
|
@@ -23933,6 +24190,8 @@ class PublishVideoCheck extends Checker {
|
|
23933
24190
|
var _a;
|
23934
24191
|
const room = yield this.connect();
|
23935
24192
|
const track = yield createLocalVideoTrack();
|
24193
|
+
// check if we have video from camera
|
24194
|
+
yield this.checkForVideo(track.mediaStreamTrack);
|
23936
24195
|
room.localParticipant.publishTrack(track);
|
23937
24196
|
// wait for a few seconds to publish
|
23938
24197
|
yield new Promise(resolve => setTimeout(resolve, 5000));
|
@@ -23953,6 +24212,50 @@ class PublishVideoCheck extends Checker {
|
|
23953
24212
|
this.appendMessage("published ".concat(numPackets, " video packets"));
|
23954
24213
|
});
|
23955
24214
|
}
|
24215
|
+
checkForVideo(track) {
|
24216
|
+
return __awaiter(this, void 0, void 0, function* () {
|
24217
|
+
const stream = new MediaStream();
|
24218
|
+
stream.addTrack(track.clone());
|
24219
|
+
// Create video element to check frames
|
24220
|
+
const video = document.createElement('video');
|
24221
|
+
video.srcObject = stream;
|
24222
|
+
video.muted = true;
|
24223
|
+
yield new Promise(resolve => {
|
24224
|
+
video.onplay = () => {
|
24225
|
+
setTimeout(() => {
|
24226
|
+
var _a, _b, _c, _d;
|
24227
|
+
const canvas = document.createElement('canvas');
|
24228
|
+
const settings = track.getSettings();
|
24229
|
+
const width = (_b = (_a = settings.width) !== null && _a !== void 0 ? _a : video.videoWidth) !== null && _b !== void 0 ? _b : 1280;
|
24230
|
+
const height = (_d = (_c = settings.height) !== null && _c !== void 0 ? _c : video.videoHeight) !== null && _d !== void 0 ? _d : 720;
|
24231
|
+
canvas.width = width;
|
24232
|
+
canvas.height = height;
|
24233
|
+
const ctx = canvas.getContext('2d');
|
24234
|
+
// Draw video frame to canvas
|
24235
|
+
ctx.drawImage(video, 0, 0);
|
24236
|
+
// Get image data and check if all pixels are black
|
24237
|
+
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
24238
|
+
const data = imageData.data;
|
24239
|
+
let isAllBlack = true;
|
24240
|
+
for (let i = 0; i < data.length; i += 4) {
|
24241
|
+
if (data[i] !== 0 || data[i + 1] !== 0 || data[i + 2] !== 0) {
|
24242
|
+
isAllBlack = false;
|
24243
|
+
break;
|
24244
|
+
}
|
24245
|
+
}
|
24246
|
+
if (isAllBlack) {
|
24247
|
+
this.appendError('camera appears to be producing only black frames');
|
24248
|
+
} else {
|
24249
|
+
this.appendMessage('received video frames');
|
24250
|
+
}
|
24251
|
+
resolve();
|
24252
|
+
}, 1000);
|
24253
|
+
};
|
24254
|
+
video.play();
|
24255
|
+
});
|
24256
|
+
video.remove();
|
24257
|
+
});
|
24258
|
+
}
|
23956
24259
|
}
|
23957
24260
|
|
23958
24261
|
class ReconnectCheck extends Checker {
|
@@ -24218,6 +24521,21 @@ class ConnectionCheck extends eventsExports.EventEmitter {
|
|
24218
24521
|
return this.createAndRunCheck(PublishVideoCheck);
|
24219
24522
|
});
|
24220
24523
|
}
|
24524
|
+
checkConnectionProtocol() {
|
24525
|
+
return __awaiter(this, void 0, void 0, function* () {
|
24526
|
+
const info = yield this.createAndRunCheck(ConnectionProtocolCheck);
|
24527
|
+
if (info.data && 'protocol' in info.data) {
|
24528
|
+
const stats = info.data;
|
24529
|
+
this.options.protocol = stats.protocol;
|
24530
|
+
}
|
24531
|
+
return info;
|
24532
|
+
});
|
24533
|
+
}
|
24534
|
+
checkCloudRegion() {
|
24535
|
+
return __awaiter(this, void 0, void 0, function* () {
|
24536
|
+
return this.createAndRunCheck(CloudRegionCheck);
|
24537
|
+
});
|
24538
|
+
}
|
24221
24539
|
}
|
24222
24540
|
|
24223
24541
|
/**
|
@@ -24301,5 +24619,5 @@ function isFacingModeValue(item) {
|
|
24301
24619
|
return item === undefined || allowedValues.includes(item);
|
24302
24620
|
}
|
24303
24621
|
|
24304
|
-
export { AudioPresets, BackupCodecPolicy, BaseKeyProvider, CheckStatus, Checker, ConnectionCheck, ConnectionError, ConnectionErrorReason, ConnectionQuality, ConnectionState, CriticalTimers, CryptorError, CryptorErrorReason, CryptorEvent, DataPacket_Kind, DefaultReconnectPolicy, DeviceUnsupportedError, DisconnectReason, EncryptionEvent, EngineEvent, ExternalE2EEKeyProvider, KeyHandlerEvent, KeyProviderEvent, LivekitError, LocalAudioTrack, LocalParticipant, LocalTrack, LocalTrackPublication, LocalVideoTrack, LogLevel, LoggerNames, MediaDeviceFailure, _ as Mutex, NegotiationError, Participant, ParticipantEvent, ParticipantInfo_Kind as ParticipantKind, PublishDataError, RemoteAudioTrack, RemoteParticipant, RemoteTrack, RemoteTrackPublication, RemoteVideoTrack, Room, RoomEvent, RpcError, ScreenSharePresets, SignalRequestError, SubscriptionError, Track, TrackEvent, TrackInvalidError, TrackPublication, UnexpectedConnectionState, UnsupportedServer, VideoPreset, VideoPresets, VideoPresets43, VideoQuality, attachToElement, compareVersions, createAudioAnalyser, createE2EEKey, createKeyMaterialFromBuffer, createKeyMaterialFromString, createLocalAudioTrack, createLocalScreenTracks, createLocalTracks, createLocalVideoTrack, deriveKeys, detachTrack, facingModeFromDeviceLabel, facingModeFromLocalTrack, getBrowser, getEmptyAudioStreamTrack, getEmptyVideoStreamTrack, getLogger, importKey, isAudioTrack, isBackupCodec, isBrowserSupported, isE2EESupported, isInsertableStreamSupported, isLocalParticipant, isLocalTrack, isRemoteParticipant, isRemoteTrack, isScriptTransformSupported, isVideoFrame, isVideoTrack, needsRbspUnescaping, parseRbsp, protocolVersion, ratchet, setLogExtension, setLogLevel, supportsAV1, supportsAdaptiveStream, supportsDynacast, supportsVP9, version, videoCodecs, writeRbsp };
|
24622
|
+
export { AudioPresets, BackupCodecPolicy, BaseKeyProvider, CheckStatus, Checker, ConnectionCheck, ConnectionError, ConnectionErrorReason, ConnectionQuality, ConnectionState, CriticalTimers, CryptorError, CryptorErrorReason, CryptorEvent, DataPacket_Kind, DefaultReconnectPolicy, DeviceUnsupportedError, DisconnectReason, EncryptionEvent, EngineEvent, ExternalE2EEKeyProvider, KeyHandlerEvent, KeyProviderEvent, LivekitError, LocalAudioTrack, LocalParticipant, LocalTrack, LocalTrackPublication, LocalVideoTrack, LogLevel, LoggerNames, MediaDeviceFailure, _ as Mutex, NegotiationError, Participant, ParticipantEvent, ParticipantInfo_Kind as ParticipantKind, PublishDataError, RemoteAudioTrack, RemoteParticipant, RemoteTrack, RemoteTrackPublication, RemoteVideoTrack, Room, RoomEvent, RpcError, ScreenSharePresets, SignalRequestError, SubscriptionError, Track, TrackEvent, TrackInvalidError, TrackPublication, TrackType, UnexpectedConnectionState, UnsupportedServer, VideoPreset, VideoPresets, VideoPresets43, VideoQuality, attachToElement, compareVersions, createAudioAnalyser, createE2EEKey, createKeyMaterialFromBuffer, createKeyMaterialFromString, createLocalAudioTrack, createLocalScreenTracks, createLocalTracks, createLocalVideoTrack, deriveKeys, detachTrack, facingModeFromDeviceLabel, facingModeFromLocalTrack, getBrowser, getEmptyAudioStreamTrack, getEmptyVideoStreamTrack, getLogger, importKey, isAudioTrack, isBackupCodec, isBrowserSupported, isE2EESupported, isInsertableStreamSupported, isLocalParticipant, isLocalTrack, isRemoteParticipant, isRemoteTrack, isScriptTransformSupported, isVideoFrame, isVideoTrack, needsRbspUnescaping, parseRbsp, protocolVersion, ratchet, setLogExtension, setLogLevel, supportsAV1, supportsAdaptiveStream, supportsDynacast, supportsVP9, version, videoCodecs, writeRbsp };
|
24305
24623
|
//# sourceMappingURL=livekit-client.esm.mjs.map
|