livekit-client 2.9.1 → 2.9.3
Sign up to get free protection for your applications and to get access to all the features.
- package/dist/livekit-client.esm.mjs +379 -65
- 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 +3 -3
- 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 +3 -3
- 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 +8 -15
- 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.3";
|
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
|
}
|
@@ -18324,11 +18365,7 @@ class TextStreamReader extends BaseStreamReader {
|
|
18324
18365
|
this.handleChunkReceived(value);
|
18325
18366
|
return {
|
18326
18367
|
done: false,
|
18327
|
-
value:
|
18328
|
-
index: bigIntToNumber(value.chunkIndex),
|
18329
|
-
current: decoder.decode(value.content),
|
18330
|
-
collected: Array.from(this.receivedChunks.values()).sort((a, b) => bigIntToNumber(a.chunkIndex) - bigIntToNumber(b.chunkIndex)).map(chunk => decoder.decode(chunk.content)).join('')
|
18331
|
-
}
|
18368
|
+
value: decoder.decode(value.content)
|
18332
18369
|
};
|
18333
18370
|
}
|
18334
18371
|
} catch (error) {
|
@@ -18353,15 +18390,13 @@ class TextStreamReader extends BaseStreamReader {
|
|
18353
18390
|
readAll() {
|
18354
18391
|
return __awaiter(this, void 0, void 0, function* () {
|
18355
18392
|
var _a, e_2, _b, _c;
|
18356
|
-
let
|
18393
|
+
let finalString = '';
|
18357
18394
|
try {
|
18358
18395
|
for (var _d = true, _e = __asyncValues(this), _f; _f = yield _e.next(), _a = _f.done, !_a; _d = true) {
|
18359
18396
|
_c = _f.value;
|
18360
18397
|
_d = false;
|
18361
|
-
const
|
18362
|
-
|
18363
|
-
} = _c;
|
18364
|
-
latestString = collected;
|
18398
|
+
const chunk = _c;
|
18399
|
+
finalString += chunk;
|
18365
18400
|
}
|
18366
18401
|
} catch (e_2_1) {
|
18367
18402
|
e_2 = {
|
@@ -18374,7 +18409,7 @@ class TextStreamReader extends BaseStreamReader {
|
|
18374
18409
|
if (e_2) throw e_2.error;
|
18375
18410
|
}
|
18376
18411
|
}
|
18377
|
-
return
|
18412
|
+
return finalString;
|
18378
18413
|
});
|
18379
18414
|
}
|
18380
18415
|
}
|
@@ -18387,7 +18422,7 @@ class BaseStreamWriter {
|
|
18387
18422
|
this.info = info;
|
18388
18423
|
}
|
18389
18424
|
write(chunk) {
|
18390
|
-
return this.defaultWriter.write(
|
18425
|
+
return this.defaultWriter.write(chunk);
|
18391
18426
|
}
|
18392
18427
|
close() {
|
18393
18428
|
return __awaiter(this, void 0, void 0, function* () {
|
@@ -20794,14 +20829,9 @@ class LocalParticipant extends Participant {
|
|
20794
20829
|
topic: options === null || options === void 0 ? void 0 : options.topic,
|
20795
20830
|
attachedStreamIds: fileIds
|
20796
20831
|
});
|
20797
|
-
|
20798
|
-
|
20799
|
-
|
20800
|
-
const chunkData = text.slice(i * textChunkSize, Math.min((i + 1) * textChunkSize, totalTextLength));
|
20801
|
-
yield this.engine.waitForBufferStatusLow(DataPacket_Kind.RELIABLE);
|
20802
|
-
yield writer.write(chunkData);
|
20803
|
-
handleProgress(Math.ceil((i + 1) / totalTextChunks), 0);
|
20804
|
-
}
|
20832
|
+
yield writer.write(text);
|
20833
|
+
// set text part of progress to 1
|
20834
|
+
handleProgress(1, 0);
|
20805
20835
|
yield writer.close();
|
20806
20836
|
if ((options === null || options === void 0 ? void 0 : options.attachments) && fileIds) {
|
20807
20837
|
yield Promise.all(options.attachments.map((file, idx) => __awaiter(this, void 0, void 0, function* () {
|
@@ -20861,32 +20891,26 @@ class LocalParticipant extends Participant {
|
|
20861
20891
|
const localP = this;
|
20862
20892
|
const writableStream = new WritableStream({
|
20863
20893
|
// Implement the sink
|
20864
|
-
write(
|
20865
|
-
|
20866
|
-
|
20867
|
-
|
20868
|
-
|
20869
|
-
|
20870
|
-
|
20871
|
-
|
20872
|
-
|
20873
|
-
|
20874
|
-
|
20875
|
-
|
20876
|
-
|
20877
|
-
|
20878
|
-
|
20879
|
-
|
20880
|
-
|
20881
|
-
|
20882
|
-
|
20883
|
-
|
20884
|
-
}
|
20885
|
-
});
|
20886
|
-
yield localP.engine.sendDataPacket(chunkPacket, DataPacket_Kind.RELIABLE);
|
20887
|
-
chunkId += 1;
|
20888
|
-
resolve();
|
20889
|
-
}));
|
20894
|
+
write(text) {
|
20895
|
+
return __awaiter(this, void 0, void 0, function* () {
|
20896
|
+
for (const textChunk of 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
|
+
});
|
20890
20914
|
},
|
20891
20915
|
close() {
|
20892
20916
|
return __awaiter(this, void 0, void 0, function* () {
|
@@ -21000,14 +21024,14 @@ class LocalParticipant extends Participant {
|
|
21000
21024
|
* @throws Error on failure. Details in `message`.
|
21001
21025
|
*/
|
21002
21026
|
performRpc(_a) {
|
21003
|
-
return __awaiter(this, arguments, void 0, function (
|
21027
|
+
return __awaiter(this, arguments, void 0, function (_ref3) {
|
21004
21028
|
var _this5 = this;
|
21005
21029
|
let {
|
21006
21030
|
destinationIdentity,
|
21007
21031
|
method,
|
21008
21032
|
payload,
|
21009
21033
|
responseTimeout = 10000
|
21010
|
-
} =
|
21034
|
+
} = _ref3;
|
21011
21035
|
return function* () {
|
21012
21036
|
const maxRoundTripLatency = 2000;
|
21013
21037
|
return new Promise((resolve, reject) => __awaiter(_this5, void 0, void 0, function* () {
|
@@ -21212,8 +21236,8 @@ class LocalParticipant extends Participant {
|
|
21212
21236
|
const waitForPendingTimeout = 10000;
|
21213
21237
|
const startTime = Date.now();
|
21214
21238
|
while (Date.now() < startTime + waitForPendingTimeout) {
|
21215
|
-
const publishPromiseEntry = Array.from(this.pendingPublishPromises.entries()).find(
|
21216
|
-
let [pendingTrack] =
|
21239
|
+
const publishPromiseEntry = Array.from(this.pendingPublishPromises.entries()).find(_ref4 => {
|
21240
|
+
let [pendingTrack] = _ref4;
|
21217
21241
|
return pendingTrack.source === source;
|
21218
21242
|
});
|
21219
21243
|
if (publishPromiseEntry) {
|
@@ -23670,15 +23694,13 @@ class Checker extends eventsExports.EventEmitter {
|
|
23670
23694
|
super();
|
23671
23695
|
this.status = CheckStatus.IDLE;
|
23672
23696
|
this.logs = [];
|
23673
|
-
this.
|
23697
|
+
this.options = {};
|
23674
23698
|
this.url = url;
|
23675
23699
|
this.token = token;
|
23676
23700
|
this.name = this.constructor.name;
|
23677
23701
|
this.room = new Room(options.roomOptions);
|
23678
23702
|
this.connectOptions = options.connectOptions;
|
23679
|
-
|
23680
|
-
this.errorsAsWarnings = options.errorsAsWarnings;
|
23681
|
-
}
|
23703
|
+
this.options = options;
|
23682
23704
|
}
|
23683
23705
|
run(onComplete) {
|
23684
23706
|
return __awaiter(this, void 0, void 0, function* () {
|
@@ -23690,7 +23712,7 @@ class Checker extends eventsExports.EventEmitter {
|
|
23690
23712
|
yield this.perform();
|
23691
23713
|
} catch (err) {
|
23692
23714
|
if (err instanceof Error) {
|
23693
|
-
if (this.errorsAsWarnings) {
|
23715
|
+
if (this.options.errorsAsWarnings) {
|
23694
23716
|
this.appendWarning(err.message);
|
23695
23717
|
} else {
|
23696
23718
|
this.appendError(err.message);
|
@@ -23713,12 +23735,15 @@ class Checker extends eventsExports.EventEmitter {
|
|
23713
23735
|
isSuccess() {
|
23714
23736
|
return !this.logs.some(l => l.level === 'error');
|
23715
23737
|
}
|
23716
|
-
connect() {
|
23738
|
+
connect(url) {
|
23717
23739
|
return __awaiter(this, void 0, void 0, function* () {
|
23718
23740
|
if (this.room.state === ConnectionState.Connected) {
|
23719
23741
|
return this.room;
|
23720
23742
|
}
|
23721
|
-
|
23743
|
+
if (!url) {
|
23744
|
+
url = this.url;
|
23745
|
+
}
|
23746
|
+
yield this.room.connect(url, this.token, this.connectOptions);
|
23722
23747
|
return this.room;
|
23723
23748
|
});
|
23724
23749
|
}
|
@@ -23734,6 +23759,33 @@ class Checker extends eventsExports.EventEmitter {
|
|
23734
23759
|
skip() {
|
23735
23760
|
this.setStatus(CheckStatus.SKIPPED);
|
23736
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
|
+
}
|
23737
23789
|
appendMessage(message) {
|
23738
23790
|
this.logs.push({
|
23739
23791
|
level: 'info',
|
@@ -23773,6 +23825,201 @@ class Checker extends eventsExports.EventEmitter {
|
|
23773
23825
|
}
|
23774
23826
|
}
|
23775
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
|
+
|
23776
24023
|
/**
|
23777
24024
|
* Creates a local video and audio track at the same time. When acquiring both
|
23778
24025
|
* audio and video tracks together, it'll display a single permission prompt to
|
@@ -23818,11 +24065,12 @@ function createLocalTracks(options) {
|
|
23818
24065
|
}
|
23819
24066
|
// update the constraints with the device id the user gave permissions to in the permission prompt
|
23820
24067
|
// otherwise each track restart (e.g. mute - unmute) will try to initialize the device again -> causing additional permission prompts
|
23821
|
-
|
23822
|
-
|
23823
|
-
|
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) {
|
23824
24072
|
trackConstraints = {
|
23825
|
-
deviceId:
|
24073
|
+
deviceId: newDeviceId
|
23826
24074
|
};
|
23827
24075
|
}
|
23828
24076
|
const track = mediaTrackToLocalTrack(mediaStreamTrack, trackConstraints);
|
@@ -23906,6 +24154,11 @@ class PublishAudioCheck extends Checker {
|
|
23906
24154
|
var _a;
|
23907
24155
|
const room = yield this.connect();
|
23908
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');
|
23909
24162
|
room.localParticipant.publishTrack(track);
|
23910
24163
|
// wait for a few seconds to publish
|
23911
24164
|
yield new Promise(resolve => setTimeout(resolve, 3000));
|
@@ -23937,6 +24190,8 @@ class PublishVideoCheck extends Checker {
|
|
23937
24190
|
var _a;
|
23938
24191
|
const room = yield this.connect();
|
23939
24192
|
const track = yield createLocalVideoTrack();
|
24193
|
+
// check if we have video from camera
|
24194
|
+
yield this.checkForVideo(track.mediaStreamTrack);
|
23940
24195
|
room.localParticipant.publishTrack(track);
|
23941
24196
|
// wait for a few seconds to publish
|
23942
24197
|
yield new Promise(resolve => setTimeout(resolve, 5000));
|
@@ -23957,6 +24212,50 @@ class PublishVideoCheck extends Checker {
|
|
23957
24212
|
this.appendMessage("published ".concat(numPackets, " video packets"));
|
23958
24213
|
});
|
23959
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
|
+
}
|
23960
24259
|
}
|
23961
24260
|
|
23962
24261
|
class ReconnectCheck extends Checker {
|
@@ -24222,6 +24521,21 @@ class ConnectionCheck extends eventsExports.EventEmitter {
|
|
24222
24521
|
return this.createAndRunCheck(PublishVideoCheck);
|
24223
24522
|
});
|
24224
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
|
+
}
|
24225
24539
|
}
|
24226
24540
|
|
24227
24541
|
/**
|
@@ -24305,5 +24619,5 @@ function isFacingModeValue(item) {
|
|
24305
24619
|
return item === undefined || allowedValues.includes(item);
|
24306
24620
|
}
|
24307
24621
|
|
24308
|
-
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 };
|
24309
24623
|
//# sourceMappingURL=livekit-client.esm.mjs.map
|