livekit-client 2.1.0 → 2.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/livekit-client.esm.mjs +2212 -2100
- 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/index.d.ts +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/room/PCTransport.d.ts.map +1 -1
- package/dist/src/room/RTCEngine.d.ts +4 -4
- package/dist/src/room/RTCEngine.d.ts.map +1 -1
- package/dist/src/room/Room.d.ts +5 -2
- package/dist/src/room/Room.d.ts.map +1 -1
- package/dist/src/room/events.d.ts +20 -1
- package/dist/src/room/events.d.ts.map +1 -1
- package/dist/src/room/participant/LocalParticipant.d.ts.map +1 -1
- package/dist/src/room/participant/Participant.d.ts +2 -1
- package/dist/src/room/participant/Participant.d.ts.map +1 -1
- package/dist/src/room/track/RemoteTrack.d.ts +1 -0
- package/dist/src/room/track/RemoteTrack.d.ts.map +1 -1
- package/dist/src/room/track/Track.d.ts +4 -0
- package/dist/src/room/track/Track.d.ts.map +1 -1
- package/dist/src/room/track/TrackPublication.d.ts +3 -1
- package/dist/src/room/track/TrackPublication.d.ts.map +1 -1
- package/dist/src/room/types.d.ts +8 -0
- package/dist/src/room/types.d.ts.map +1 -1
- package/dist/src/room/utils.d.ts +4 -2
- package/dist/src/room/utils.d.ts.map +1 -1
- package/dist/src/utils/browserParser.d.ts +1 -0
- package/dist/src/utils/browserParser.d.ts.map +1 -1
- package/dist/ts4.2/src/index.d.ts +1 -1
- package/dist/ts4.2/src/room/RTCEngine.d.ts +4 -4
- package/dist/ts4.2/src/room/Room.d.ts +5 -2
- package/dist/ts4.2/src/room/events.d.ts +20 -1
- package/dist/ts4.2/src/room/participant/Participant.d.ts +2 -1
- package/dist/ts4.2/src/room/track/RemoteTrack.d.ts +1 -0
- package/dist/ts4.2/src/room/track/Track.d.ts +4 -0
- package/dist/ts4.2/src/room/track/TrackPublication.d.ts +3 -1
- package/dist/ts4.2/src/room/types.d.ts +8 -0
- package/dist/ts4.2/src/room/utils.d.ts +4 -2
- package/dist/ts4.2/src/utils/browserParser.d.ts +1 -0
- package/package.json +8 -8
- package/src/index.ts +1 -1
- package/src/room/PCTransport.ts +0 -2
- package/src/room/RTCEngine.ts +11 -61
- package/src/room/Room.ts +27 -1
- package/src/room/events.ts +23 -0
- package/src/room/participant/LocalParticipant.ts +3 -5
- package/src/room/participant/Participant.ts +5 -1
- package/src/room/track/RemoteTrack.ts +13 -0
- package/src/room/track/Track.ts +9 -0
- package/src/room/track/TrackPublication.ts +3 -1
- package/src/room/types.ts +9 -0
- package/src/room/utils.ts +46 -29
- package/src/utils/browserParser.test.ts +4 -0
- package/src/utils/browserParser.ts +11 -1
@@ -175,6 +175,7 @@ function normalizeEnumValue(value) {
|
|
175
175
|
class Message {
|
176
176
|
/**
|
177
177
|
* Compare with a message of the same type.
|
178
|
+
* Note that this function disregards extensions and unknown fields.
|
178
179
|
*/
|
179
180
|
equals(other) {
|
180
181
|
return this.getType().runtime.util.equals(this.getType(), this, other);
|
@@ -2185,7 +2186,7 @@ function readScalar$1(type, json, longType, nullAsZeroValue) {
|
|
2185
2186
|
if (json.trim().length === json.length) int32 = Number(json);
|
2186
2187
|
}
|
2187
2188
|
if (int32 === undefined) break;
|
2188
|
-
if (type == ScalarType.UINT32) assertUInt32(int32);else assertInt32(int32);
|
2189
|
+
if (type == ScalarType.UINT32 || type == ScalarType.FIXED32) assertUInt32(int32);else assertInt32(int32);
|
2189
2190
|
return int32;
|
2190
2191
|
// int64, fixed64, uint64: JSON value will be a decimal string. Either numbers or strings are accepted.
|
2191
2192
|
case ScalarType.INT64:
|
@@ -2997,6 +2998,9 @@ function makeUtilCommon() {
|
|
2997
2998
|
}
|
2998
2999
|
any[member.localName] = copy;
|
2999
3000
|
}
|
3001
|
+
for (const uf of type.runtime.bin.listUnknownFields(message)) {
|
3002
|
+
type.runtime.bin.onUnknownField(any, uf.no, uf.wireType, uf.data);
|
3003
|
+
}
|
3000
3004
|
return target;
|
3001
3005
|
}
|
3002
3006
|
};
|
@@ -3317,10 +3321,8 @@ function normalizeFieldInfos(fieldInfos, packedByDefault) {
|
|
3317
3321
|
f.req = (_e = field.req) !== null && _e !== void 0 ? _e : false;
|
3318
3322
|
f.opt = (_f = field.opt) !== null && _f !== void 0 ? _f : false;
|
3319
3323
|
if (field.packed === undefined) {
|
3320
|
-
|
3324
|
+
{
|
3321
3325
|
f.packed = field.kind == "enum" || field.kind == "scalar" && field.T != ScalarType.BYTES && field.T != ScalarType.STRING;
|
3322
|
-
} else {
|
3323
|
-
f.packed = false;
|
3324
3326
|
}
|
3325
3327
|
}
|
3326
3328
|
// We do not surface options at this time
|
@@ -3355,7 +3357,7 @@ function normalizeFieldInfos(fieldInfos, packedByDefault) {
|
|
3355
3357
|
* Provides functionality for messages defined with the proto3 syntax.
|
3356
3358
|
*/
|
3357
3359
|
const proto3 = makeProtoRuntime("proto3", fields => {
|
3358
|
-
return new InternalFieldList(fields, source => normalizeFieldInfos(source
|
3360
|
+
return new InternalFieldList(fields, source => normalizeFieldInfos(source));
|
3359
3361
|
},
|
3360
3362
|
// TODO merge with proto2 and initExtensionField, also see initPartial, equals, clone
|
3361
3363
|
target => {
|
@@ -3947,6 +3949,12 @@ const TrackInfo = /*@__PURE__*/proto3.makeMessageType("livekit.TrackInfo", () =>
|
|
3947
3949
|
name: "version",
|
3948
3950
|
kind: "message",
|
3949
3951
|
T: TimedVersion
|
3952
|
+
}, {
|
3953
|
+
no: 19,
|
3954
|
+
name: "audio_features",
|
3955
|
+
kind: "enum",
|
3956
|
+
T: proto3.getEnumType(AudioTrackFeature),
|
3957
|
+
repeated: true
|
3950
3958
|
}]);
|
3951
3959
|
|
3952
3960
|
/**
|
@@ -4020,6 +4028,12 @@ const DataPacket = /*@__PURE__*/proto3.makeMessageType("livekit.DataPacket", ()
|
|
4020
4028
|
kind: "message",
|
4021
4029
|
T: SipDTMF,
|
4022
4030
|
oneof: "value"
|
4031
|
+
}, {
|
4032
|
+
no: 7,
|
4033
|
+
name: "transcription",
|
4034
|
+
kind: "message",
|
4035
|
+
T: Transcription,
|
4036
|
+
oneof: "value"
|
4023
4037
|
}]);
|
4024
4038
|
|
4025
4039
|
/**
|
@@ -4100,6 +4114,24 @@ const UserPacket = /*@__PURE__*/proto3.makeMessageType("livekit.UserPacket", ()
|
|
4100
4114
|
kind: "scalar",
|
4101
4115
|
T: 9 /* ScalarType.STRING */,
|
4102
4116
|
opt: true
|
4117
|
+
}, {
|
4118
|
+
no: 8,
|
4119
|
+
name: "id",
|
4120
|
+
kind: "scalar",
|
4121
|
+
T: 9 /* ScalarType.STRING */,
|
4122
|
+
opt: true
|
4123
|
+
}, {
|
4124
|
+
no: 9,
|
4125
|
+
name: "start_time",
|
4126
|
+
kind: "scalar",
|
4127
|
+
T: 4 /* ScalarType.UINT64 */,
|
4128
|
+
opt: true
|
4129
|
+
}, {
|
4130
|
+
no: 10,
|
4131
|
+
name: "end_time",
|
4132
|
+
kind: "scalar",
|
4133
|
+
T: 4 /* ScalarType.UINT64 */,
|
4134
|
+
opt: true
|
4103
4135
|
}]);
|
4104
4136
|
|
4105
4137
|
/**
|
@@ -4117,6 +4149,62 @@ const SipDTMF = /*@__PURE__*/proto3.makeMessageType("livekit.SipDTMF", () => [{
|
|
4117
4149
|
T: 9 /* ScalarType.STRING */
|
4118
4150
|
}]);
|
4119
4151
|
|
4152
|
+
/**
|
4153
|
+
* @generated from message livekit.Transcription
|
4154
|
+
*/
|
4155
|
+
const Transcription = /*@__PURE__*/proto3.makeMessageType("livekit.Transcription", () => [{
|
4156
|
+
no: 2,
|
4157
|
+
name: "participant_identity",
|
4158
|
+
kind: "scalar",
|
4159
|
+
T: 9 /* ScalarType.STRING */
|
4160
|
+
}, {
|
4161
|
+
no: 3,
|
4162
|
+
name: "track_id",
|
4163
|
+
kind: "scalar",
|
4164
|
+
T: 9 /* ScalarType.STRING */
|
4165
|
+
}, {
|
4166
|
+
no: 4,
|
4167
|
+
name: "segments",
|
4168
|
+
kind: "message",
|
4169
|
+
T: TranscriptionSegment,
|
4170
|
+
repeated: true
|
4171
|
+
}]);
|
4172
|
+
|
4173
|
+
/**
|
4174
|
+
* @generated from message livekit.TranscriptionSegment
|
4175
|
+
*/
|
4176
|
+
const TranscriptionSegment = /*@__PURE__*/proto3.makeMessageType("livekit.TranscriptionSegment", () => [{
|
4177
|
+
no: 1,
|
4178
|
+
name: "id",
|
4179
|
+
kind: "scalar",
|
4180
|
+
T: 9 /* ScalarType.STRING */
|
4181
|
+
}, {
|
4182
|
+
no: 2,
|
4183
|
+
name: "text",
|
4184
|
+
kind: "scalar",
|
4185
|
+
T: 9 /* ScalarType.STRING */
|
4186
|
+
}, {
|
4187
|
+
no: 3,
|
4188
|
+
name: "start_time",
|
4189
|
+
kind: "scalar",
|
4190
|
+
T: 4 /* ScalarType.UINT64 */
|
4191
|
+
}, {
|
4192
|
+
no: 4,
|
4193
|
+
name: "end_time",
|
4194
|
+
kind: "scalar",
|
4195
|
+
T: 4 /* ScalarType.UINT64 */
|
4196
|
+
}, {
|
4197
|
+
no: 5,
|
4198
|
+
name: "final",
|
4199
|
+
kind: "scalar",
|
4200
|
+
T: 8 /* ScalarType.BOOL */
|
4201
|
+
}, {
|
4202
|
+
no: 6,
|
4203
|
+
name: "language",
|
4204
|
+
kind: "scalar",
|
4205
|
+
T: 9 /* ScalarType.STRING */
|
4206
|
+
}]);
|
4207
|
+
|
4120
4208
|
/**
|
4121
4209
|
* @generated from message livekit.ParticipantTracks
|
4122
4210
|
*/
|
@@ -5048,6 +5136,7 @@ const LeaveRequest_Action = /*@__PURE__*/proto3.makeEnum("livekit.LeaveRequest.A
|
|
5048
5136
|
* message to indicate published video track dimensions are changing
|
5049
5137
|
*
|
5050
5138
|
* @generated from message livekit.UpdateVideoLayers
|
5139
|
+
* @deprecated
|
5051
5140
|
*/
|
5052
5141
|
const UpdateVideoLayers = /*@__PURE__*/proto3.makeMessageType("livekit.UpdateVideoLayers", () => [{
|
5053
5142
|
no: 1,
|
@@ -9945,6 +10034,11 @@ var RoomEvent;
|
|
9945
10034
|
* args: (payload: Uint8Array, participant: [[Participant]], kind: [[DataPacket_Kind]], topic?: string)
|
9946
10035
|
*/
|
9947
10036
|
RoomEvent["DataReceived"] = "dataReceived";
|
10037
|
+
/**
|
10038
|
+
* Transcription received from a participant's track.
|
10039
|
+
* @beta
|
10040
|
+
*/
|
10041
|
+
RoomEvent["TranscriptionReceived"] = "transcriptionReceived";
|
9948
10042
|
/**
|
9949
10043
|
* Connection quality was changed for a Participant. It'll receive updates
|
9950
10044
|
* from the local participant, as well as any [[RemoteParticipant]]s that we are
|
@@ -10126,6 +10220,11 @@ var ParticipantEvent;
|
|
10126
10220
|
* args: (payload: Uint8Array, kind: [[DataPacket_Kind]])
|
10127
10221
|
*/
|
10128
10222
|
ParticipantEvent["DataReceived"] = "dataReceived";
|
10223
|
+
/**
|
10224
|
+
* Transcription received from this participant as data source.
|
10225
|
+
* @beta
|
10226
|
+
*/
|
10227
|
+
ParticipantEvent["TranscriptionReceived"] = "transcriptionReceived";
|
10129
10228
|
/**
|
10130
10229
|
* Has speaking status changed for the current participant
|
10131
10230
|
*
|
@@ -10195,6 +10294,7 @@ var EngineEvent;
|
|
10195
10294
|
EngineEvent["MediaTrackAdded"] = "mediaTrackAdded";
|
10196
10295
|
EngineEvent["ActiveSpeakersUpdate"] = "activeSpeakersUpdate";
|
10197
10296
|
EngineEvent["DataPacketReceived"] = "dataPacketReceived";
|
10297
|
+
EngineEvent["TranscriptionReceived"] = "transcriptionReceived";
|
10198
10298
|
EngineEvent["RTPVideoMapUpdate"] = "rtpVideoMapUpdate";
|
10199
10299
|
EngineEvent["DCBufferStatusChanged"] = "dcBufferStatusChanged";
|
10200
10300
|
EngineEvent["ParticipantUpdate"] = "participantUpdate";
|
@@ -10277,6 +10377,14 @@ var TrackEvent;
|
|
10277
10377
|
* @internal
|
10278
10378
|
*/
|
10279
10379
|
TrackEvent["AudioTrackFeatureUpdate"] = "audioTrackFeatureUpdate";
|
10380
|
+
/**
|
10381
|
+
* @beta
|
10382
|
+
*/
|
10383
|
+
TrackEvent["TranscriptionReceived"] = "transcriptionReceived";
|
10384
|
+
/**
|
10385
|
+
* @experimental
|
10386
|
+
*/
|
10387
|
+
TrackEvent["TimeSyncUpdate"] = "timeSyncUpdate";
|
10280
10388
|
})(TrackEvent || (TrackEvent = {}));
|
10281
10389
|
|
10282
10390
|
function r(r, e, n) {
|
@@ -10353,7 +10461,8 @@ const browsersList = [{
|
|
10353
10461
|
const browser = {
|
10354
10462
|
name: 'Firefox',
|
10355
10463
|
version: getMatch(/(?:firefox|iceweasel|fxios)[\s/](\d+(\.?_?\d+)+)/i, ua),
|
10356
|
-
os: ua.toLowerCase().includes('fxios') ? 'iOS' : undefined
|
10464
|
+
os: ua.toLowerCase().includes('fxios') ? 'iOS' : undefined,
|
10465
|
+
osVersion: getOSVersion(ua)
|
10357
10466
|
};
|
10358
10467
|
return browser;
|
10359
10468
|
}
|
@@ -10363,7 +10472,8 @@ const browsersList = [{
|
|
10363
10472
|
const browser = {
|
10364
10473
|
name: 'Chrome',
|
10365
10474
|
version: getMatch(/(?:chrome|chromium|crios|crmo)\/(\d+(\.?_?\d+)+)/i, ua),
|
10366
|
-
os: ua.toLowerCase().includes('crios') ? 'iOS' : undefined
|
10475
|
+
os: ua.toLowerCase().includes('crios') ? 'iOS' : undefined,
|
10476
|
+
osVersion: getOSVersion(ua)
|
10367
10477
|
};
|
10368
10478
|
return browser;
|
10369
10479
|
}
|
@@ -10374,7 +10484,8 @@ const browsersList = [{
|
|
10374
10484
|
const browser = {
|
10375
10485
|
name: 'Safari',
|
10376
10486
|
version: getMatch(commonVersionIdentifier, ua),
|
10377
|
-
os: ua.includes('mobile/') ? 'iOS' : 'macOS'
|
10487
|
+
os: ua.includes('mobile/') ? 'iOS' : 'macOS',
|
10488
|
+
osVersion: getOSVersion(ua)
|
10378
10489
|
};
|
10379
10490
|
return browser;
|
10380
10491
|
}
|
@@ -10384,8 +10495,11 @@ function getMatch(exp, ua) {
|
|
10384
10495
|
const match = ua.match(exp);
|
10385
10496
|
return match && match.length >= id && match[id] || '';
|
10386
10497
|
}
|
10498
|
+
function getOSVersion(ua) {
|
10499
|
+
return ua.includes('mac os') ? getMatch(/\(.+?(\d+_\d+(:?_\d+)?)/, ua, 1).replace(/_/g, '.') : undefined;
|
10500
|
+
}
|
10387
10501
|
|
10388
|
-
var version$1 = "2.1.
|
10502
|
+
var version$1 = "2.1.2";
|
10389
10503
|
|
10390
10504
|
const version = version$1;
|
10391
10505
|
const protocolVersion = 12;
|
@@ -10686,6 +10800,9 @@ class Track extends eventsExports.EventEmitter {
|
|
10686
10800
|
if (this.monitorInterval) {
|
10687
10801
|
clearInterval(this.monitorInterval);
|
10688
10802
|
}
|
10803
|
+
if (this.timeSyncHandle) {
|
10804
|
+
cancelAnimationFrame(this.timeSyncHandle);
|
10805
|
+
}
|
10689
10806
|
}
|
10690
10807
|
/** @internal */
|
10691
10808
|
updateLoggerOptions(loggerOptions) {
|
@@ -11175,29 +11292,6 @@ function supportsSetSinkId(elm) {
|
|
11175
11292
|
}
|
11176
11293
|
return 'setSinkId' in elm;
|
11177
11294
|
}
|
11178
|
-
const setCodecPreferencesVersions = {
|
11179
|
-
Chrome: '100',
|
11180
|
-
Safari: '15',
|
11181
|
-
Firefox: '100'
|
11182
|
-
};
|
11183
|
-
function supportsSetCodecPreferences(transceiver) {
|
11184
|
-
if (!isWeb()) {
|
11185
|
-
return false;
|
11186
|
-
}
|
11187
|
-
if (!('setCodecPreferences' in transceiver)) {
|
11188
|
-
return false;
|
11189
|
-
}
|
11190
|
-
const browser = getBrowser();
|
11191
|
-
if (!(browser === null || browser === void 0 ? void 0 : browser.name) || !browser.version) {
|
11192
|
-
// version is required
|
11193
|
-
return false;
|
11194
|
-
}
|
11195
|
-
const v = setCodecPreferencesVersions[browser.name];
|
11196
|
-
if (v) {
|
11197
|
-
return compareVersions(browser.version, v) >= 0;
|
11198
|
-
}
|
11199
|
-
return false;
|
11200
|
-
}
|
11201
11295
|
function isBrowserSupported() {
|
11202
11296
|
if (typeof RTCPeerConnection === 'undefined') {
|
11203
11297
|
return false;
|
@@ -11217,8 +11311,27 @@ function isSafari17() {
|
|
11217
11311
|
return (b === null || b === void 0 ? void 0 : b.name) === 'Safari' && b.version.startsWith('17.');
|
11218
11312
|
}
|
11219
11313
|
function isMobile() {
|
11314
|
+
var _a, _b;
|
11220
11315
|
if (!isWeb()) return false;
|
11221
|
-
return
|
11316
|
+
return (
|
11317
|
+
// @ts-expect-error `userAgentData` is not yet part of typescript
|
11318
|
+
(_b = (_a = navigator.userAgentData) === null || _a === void 0 ? void 0 : _a.mobile) !== null && _b !== void 0 ? _b : /Tablet|iPad|Mobile|Android|BlackBerry/.test(navigator.userAgent)
|
11319
|
+
);
|
11320
|
+
}
|
11321
|
+
function isE2EESimulcastSupported() {
|
11322
|
+
const browser = getBrowser();
|
11323
|
+
const supportedSafariVersion = '17.2'; // see https://bugs.webkit.org/show_bug.cgi?id=257803
|
11324
|
+
if (browser) {
|
11325
|
+
if (browser.name !== 'Safari' && browser.os !== 'iOS') {
|
11326
|
+
return true;
|
11327
|
+
} else if (browser.os === 'iOS' && browser.osVersion && compareVersions(supportedSafariVersion, browser.osVersion) >= 0) {
|
11328
|
+
return true;
|
11329
|
+
} else if (browser.name === 'Safari' && compareVersions(supportedSafariVersion, browser.version) >= 0) {
|
11330
|
+
return true;
|
11331
|
+
} else {
|
11332
|
+
return false;
|
11333
|
+
}
|
11334
|
+
}
|
11222
11335
|
}
|
11223
11336
|
function isWeb() {
|
11224
11337
|
return typeof document !== 'undefined';
|
@@ -11494,6 +11607,26 @@ function toHttpUrl(url) {
|
|
11494
11607
|
}
|
11495
11608
|
return url;
|
11496
11609
|
}
|
11610
|
+
function extractTranscriptionSegments(transcription) {
|
11611
|
+
return transcription.segments.map(_ref => {
|
11612
|
+
let {
|
11613
|
+
id,
|
11614
|
+
text,
|
11615
|
+
language,
|
11616
|
+
startTime,
|
11617
|
+
endTime,
|
11618
|
+
final
|
11619
|
+
} = _ref;
|
11620
|
+
return {
|
11621
|
+
id,
|
11622
|
+
text,
|
11623
|
+
startTime: Number.parseInt(startTime.toString()),
|
11624
|
+
endTime: Number.parseInt(endTime.toString()),
|
11625
|
+
final,
|
11626
|
+
language
|
11627
|
+
};
|
11628
|
+
});
|
11629
|
+
}
|
11497
11630
|
|
11498
11631
|
const defaultId = 'default';
|
11499
11632
|
class DeviceManager {
|
@@ -14141,8 +14274,6 @@ class PCTransport extends eventsExports.EventEmitter {
|
|
14141
14274
|
yield this.pc.setLocalDescription(sd);
|
14142
14275
|
}
|
14143
14276
|
} catch (e) {
|
14144
|
-
// this error cannot always be caught.
|
14145
|
-
// If the local description has a setCodecPreferences error, this error will be uncaught
|
14146
14277
|
let msg = 'unknown error';
|
14147
14278
|
if (e instanceof Error) {
|
14148
14279
|
msg = e.message;
|
@@ -14568,2330 +14699,2290 @@ class PCTransportManager {
|
|
14568
14699
|
}
|
14569
14700
|
}
|
14570
14701
|
|
14571
|
-
const
|
14572
|
-
|
14573
|
-
|
14574
|
-
|
14575
|
-
var PCState;
|
14576
|
-
(function (PCState) {
|
14577
|
-
PCState[PCState["New"] = 0] = "New";
|
14578
|
-
PCState[PCState["Connected"] = 1] = "Connected";
|
14579
|
-
PCState[PCState["Disconnected"] = 2] = "Disconnected";
|
14580
|
-
PCState[PCState["Reconnecting"] = 3] = "Reconnecting";
|
14581
|
-
PCState[PCState["Closed"] = 4] = "Closed";
|
14582
|
-
})(PCState || (PCState = {}));
|
14583
|
-
/** @internal */
|
14584
|
-
class RTCEngine extends eventsExports.EventEmitter {
|
14585
|
-
get isClosed() {
|
14586
|
-
return this._isClosed;
|
14702
|
+
const monitorFrequency = 2000;
|
14703
|
+
function computeBitrate(currentStats, prevStats) {
|
14704
|
+
if (!prevStats) {
|
14705
|
+
return 0;
|
14587
14706
|
}
|
14588
|
-
|
14589
|
-
|
14707
|
+
let bytesNow;
|
14708
|
+
let bytesPrev;
|
14709
|
+
if ('bytesReceived' in currentStats) {
|
14710
|
+
bytesNow = currentStats.bytesReceived;
|
14711
|
+
bytesPrev = prevStats.bytesReceived;
|
14712
|
+
} else if ('bytesSent' in currentStats) {
|
14713
|
+
bytesNow = currentStats.bytesSent;
|
14714
|
+
bytesPrev = prevStats.bytesSent;
|
14590
14715
|
}
|
14591
|
-
|
14592
|
-
|
14593
|
-
|
14594
|
-
|
14595
|
-
|
14596
|
-
|
14597
|
-
|
14598
|
-
|
14599
|
-
|
14600
|
-
|
14601
|
-
|
14602
|
-
this.
|
14603
|
-
|
14604
|
-
|
14605
|
-
|
14606
|
-
|
14607
|
-
|
14608
|
-
|
14609
|
-
|
14610
|
-
|
14611
|
-
|
14612
|
-
|
14613
|
-
|
14614
|
-
|
14615
|
-
|
14616
|
-
|
14617
|
-
|
14618
|
-
|
14619
|
-
|
14620
|
-
|
14621
|
-
|
14622
|
-
} else if (channel.label === lossyDataChannel) {
|
14623
|
-
_this.lossyDCSub = channel;
|
14624
|
-
} else {
|
14625
|
-
return;
|
14626
|
-
}
|
14627
|
-
_this.log.debug("on data channel ".concat(channel.id, ", ").concat(channel.label), _this.logContext);
|
14628
|
-
channel.onmessage = _this.handleDataMessage;
|
14629
|
-
}();
|
14630
|
-
});
|
14631
|
-
this.handleDataMessage = message => __awaiter(this, void 0, void 0, function* () {
|
14632
|
-
var _c, _d;
|
14633
|
-
// make sure to respect incoming data message order by processing message events one after the other
|
14634
|
-
const unlock = yield this.dataProcessLock.lock();
|
14635
|
-
try {
|
14636
|
-
// decode
|
14637
|
-
let buffer;
|
14638
|
-
if (message.data instanceof ArrayBuffer) {
|
14639
|
-
buffer = message.data;
|
14640
|
-
} else if (message.data instanceof Blob) {
|
14641
|
-
buffer = yield message.data.arrayBuffer();
|
14642
|
-
} else {
|
14643
|
-
this.log.error('unsupported data type', Object.assign(Object.assign({}, this.logContext), {
|
14644
|
-
data: message.data
|
14645
|
-
}));
|
14646
|
-
return;
|
14647
|
-
}
|
14648
|
-
const dp = DataPacket.fromBinary(new Uint8Array(buffer));
|
14649
|
-
if (((_c = dp.value) === null || _c === void 0 ? void 0 : _c.case) === 'speaker') {
|
14650
|
-
// dispatch speaker updates
|
14651
|
-
this.emit(EngineEvent.ActiveSpeakersUpdate, dp.value.value.speakers);
|
14652
|
-
} else if (((_d = dp.value) === null || _d === void 0 ? void 0 : _d.case) === 'user') {
|
14653
|
-
this.emit(EngineEvent.DataPacketReceived, dp.value.value, dp.kind);
|
14654
|
-
}
|
14655
|
-
} finally {
|
14656
|
-
unlock();
|
14716
|
+
if (bytesNow === undefined || bytesPrev === undefined || currentStats.timestamp === undefined || prevStats.timestamp === undefined) {
|
14717
|
+
return 0;
|
14718
|
+
}
|
14719
|
+
return (bytesNow - bytesPrev) * 8 * 1000 / (currentStats.timestamp - prevStats.timestamp);
|
14720
|
+
}
|
14721
|
+
|
14722
|
+
class LocalAudioTrack extends LocalTrack {
|
14723
|
+
/**
|
14724
|
+
* boolean indicating whether enhanced noise cancellation is currently being used on this track
|
14725
|
+
*/
|
14726
|
+
get enhancedNoiseCancellation() {
|
14727
|
+
return this.isKrispNoiseFilterEnabled;
|
14728
|
+
}
|
14729
|
+
/**
|
14730
|
+
*
|
14731
|
+
* @param mediaTrack
|
14732
|
+
* @param constraints MediaTrackConstraints that are being used when restarting or reacquiring tracks
|
14733
|
+
* @param userProvidedTrack Signals to the SDK whether or not the mediaTrack should be managed (i.e. released and reacquired) internally by the SDK
|
14734
|
+
*/
|
14735
|
+
constructor(mediaTrack, constraints) {
|
14736
|
+
let userProvidedTrack = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
|
14737
|
+
let audioContext = arguments.length > 3 ? arguments[3] : undefined;
|
14738
|
+
let loggerOptions = arguments.length > 4 ? arguments[4] : undefined;
|
14739
|
+
super(mediaTrack, Track.Kind.Audio, constraints, userProvidedTrack, loggerOptions);
|
14740
|
+
/** @internal */
|
14741
|
+
this.stopOnMute = false;
|
14742
|
+
this.isKrispNoiseFilterEnabled = false;
|
14743
|
+
this.monitorSender = () => __awaiter(this, void 0, void 0, function* () {
|
14744
|
+
if (!this.sender) {
|
14745
|
+
this._currentBitrate = 0;
|
14746
|
+
return;
|
14657
14747
|
}
|
14658
|
-
|
14659
|
-
|
14660
|
-
|
14661
|
-
|
14662
|
-
|
14663
|
-
|
14664
|
-
error
|
14665
|
-
} = event.error;
|
14666
|
-
this.log.error("DataChannel error on ".concat(channelKind, ": ").concat(event.message), Object.assign(Object.assign({}, this.logContext), {
|
14667
|
-
error
|
14668
|
-
}));
|
14669
|
-
} else {
|
14670
|
-
this.log.error("Unknown DataChannel error on ".concat(channelKind), Object.assign(Object.assign({}, this.logContext), {
|
14671
|
-
event
|
14748
|
+
let stats;
|
14749
|
+
try {
|
14750
|
+
stats = yield this.getSenderStats();
|
14751
|
+
} catch (e) {
|
14752
|
+
this.log.error('could not get audio sender stats', Object.assign(Object.assign({}, this.logContext), {
|
14753
|
+
error: e
|
14672
14754
|
}));
|
14755
|
+
return;
|
14673
14756
|
}
|
14674
|
-
|
14675
|
-
|
14676
|
-
const channel = event.currentTarget;
|
14677
|
-
const channelKind = channel.maxRetransmits === 0 ? DataPacket_Kind.LOSSY : DataPacket_Kind.RELIABLE;
|
14678
|
-
this.updateAndEmitDCBufferStatus(channelKind);
|
14679
|
-
};
|
14680
|
-
// websocket reconnect behavior. if websocket is interrupted, and the PeerConnection
|
14681
|
-
// continues to work, we can reconnect to websocket to continue the session
|
14682
|
-
// after a number of retries, we'll close and give up permanently
|
14683
|
-
this.handleDisconnect = (connection, disconnectReason) => {
|
14684
|
-
if (this._isClosed) {
|
14685
|
-
return;
|
14686
|
-
}
|
14687
|
-
this.log.warn("".concat(connection, " disconnected"), this.logContext);
|
14688
|
-
if (this.reconnectAttempts === 0) {
|
14689
|
-
// only reset start time on the first try
|
14690
|
-
this.reconnectStart = Date.now();
|
14691
|
-
}
|
14692
|
-
const disconnect = duration => {
|
14693
|
-
this.log.warn("could not recover connection after ".concat(this.reconnectAttempts, " attempts, ").concat(duration, "ms. giving up"), this.logContext);
|
14694
|
-
this.emit(EngineEvent.Disconnected);
|
14695
|
-
this.close();
|
14696
|
-
};
|
14697
|
-
const duration = Date.now() - this.reconnectStart;
|
14698
|
-
let delay = this.getNextRetryDelay({
|
14699
|
-
elapsedMs: duration,
|
14700
|
-
retryCount: this.reconnectAttempts
|
14701
|
-
});
|
14702
|
-
if (delay === null) {
|
14703
|
-
disconnect(duration);
|
14704
|
-
return;
|
14705
|
-
}
|
14706
|
-
if (connection === leaveReconnect) {
|
14707
|
-
delay = 0;
|
14708
|
-
}
|
14709
|
-
this.log.debug("reconnecting in ".concat(delay, "ms"), this.logContext);
|
14710
|
-
this.clearReconnectTimeout();
|
14711
|
-
if (this.token && this.regionUrlProvider) {
|
14712
|
-
// token may have been refreshed, we do not want to recreate the regionUrlProvider
|
14713
|
-
// since the current engine may have inherited a regional url
|
14714
|
-
this.regionUrlProvider.updateToken(this.token);
|
14757
|
+
if (stats && this.prevStats) {
|
14758
|
+
this._currentBitrate = computeBitrate(stats, this.prevStats);
|
14715
14759
|
}
|
14716
|
-
this.
|
14717
|
-
};
|
14718
|
-
this.
|
14719
|
-
|
14720
|
-
|
14721
|
-
|
14722
|
-
}
|
14723
|
-
const onRestarted = () => {
|
14724
|
-
this.off(EngineEvent.Disconnected, onDisconnected);
|
14725
|
-
resolve();
|
14726
|
-
};
|
14727
|
-
const onDisconnected = () => {
|
14728
|
-
this.off(EngineEvent.Restarted, onRestarted);
|
14729
|
-
reject();
|
14730
|
-
};
|
14731
|
-
this.once(EngineEvent.Restarted, onRestarted);
|
14732
|
-
this.once(EngineEvent.Disconnected, onDisconnected);
|
14733
|
-
});
|
14760
|
+
this.prevStats = stats;
|
14761
|
+
});
|
14762
|
+
this.handleKrispNoiseFilterEnable = () => {
|
14763
|
+
this.isKrispNoiseFilterEnabled = true;
|
14764
|
+
this.log.debug("Krisp noise filter enabled", this.logContext);
|
14765
|
+
this.emit(TrackEvent.AudioTrackFeatureUpdate, this, AudioTrackFeature.TF_ENHANCED_NOISE_CANCELLATION, true);
|
14734
14766
|
};
|
14735
|
-
this.
|
14736
|
-
|
14737
|
-
|
14738
|
-
|
14739
|
-
this.emit(EngineEvent.DCBufferStatusChanged, status, kind);
|
14740
|
-
}
|
14767
|
+
this.handleKrispNoiseFilterDisable = () => {
|
14768
|
+
this.isKrispNoiseFilterEnabled = false;
|
14769
|
+
this.log.debug("Krisp noise filter disabled", this.logContext);
|
14770
|
+
this.emit(TrackEvent.AudioTrackFeatureUpdate, this, AudioTrackFeature.TF_ENHANCED_NOISE_CANCELLATION, false);
|
14741
14771
|
};
|
14742
|
-
this.
|
14743
|
-
|
14744
|
-
|
14745
|
-
|
14772
|
+
this.audioContext = audioContext;
|
14773
|
+
this.checkForSilence();
|
14774
|
+
}
|
14775
|
+
setDeviceId(deviceId) {
|
14776
|
+
return __awaiter(this, void 0, void 0, function* () {
|
14777
|
+
if (this._constraints.deviceId === deviceId && this._mediaStreamTrack.getSettings().deviceId === unwrapConstraint(deviceId)) {
|
14778
|
+
return true;
|
14746
14779
|
}
|
14747
|
-
|
14748
|
-
|
14749
|
-
|
14750
|
-
if (this.client.currentState === SignalConnectionState.RECONNECTING) {
|
14751
|
-
this.clearReconnectTimeout();
|
14752
|
-
this.attemptReconnect(ReconnectReason.RR_SIGNAL_DISCONNECTED);
|
14780
|
+
this._constraints.deviceId = deviceId;
|
14781
|
+
if (!this.isMuted) {
|
14782
|
+
yield this.restartTrack();
|
14753
14783
|
}
|
14754
|
-
|
14755
|
-
|
14756
|
-
this.loggerOptions = {
|
14757
|
-
loggerName: options.loggerName,
|
14758
|
-
loggerContextCb: () => this.logContext
|
14759
|
-
};
|
14760
|
-
this.client = new SignalClient(undefined, this.loggerOptions);
|
14761
|
-
this.client.signalLatency = this.options.expSignalLatency;
|
14762
|
-
this.reconnectPolicy = this.options.reconnectPolicy;
|
14763
|
-
this.registerOnLineListener();
|
14764
|
-
this.closingLock = new Mutex();
|
14765
|
-
this.dataProcessLock = new Mutex();
|
14766
|
-
this.dcBufferStatus = new Map([[DataPacket_Kind.LOSSY, true], [DataPacket_Kind.RELIABLE, true]]);
|
14767
|
-
this.client.onParticipantUpdate = updates => this.emit(EngineEvent.ParticipantUpdate, updates);
|
14768
|
-
this.client.onConnectionQuality = update => this.emit(EngineEvent.ConnectionQualityUpdate, update);
|
14769
|
-
this.client.onRoomUpdate = update => this.emit(EngineEvent.RoomUpdate, update);
|
14770
|
-
this.client.onSubscriptionError = resp => this.emit(EngineEvent.SubscriptionError, resp);
|
14771
|
-
this.client.onSubscriptionPermissionUpdate = update => this.emit(EngineEvent.SubscriptionPermissionUpdate, update);
|
14772
|
-
this.client.onSpeakersChanged = update => this.emit(EngineEvent.SpeakersChanged, update);
|
14773
|
-
this.client.onStreamStateUpdate = update => this.emit(EngineEvent.StreamStateChanged, update);
|
14774
|
-
}
|
14775
|
-
/** @internal */
|
14776
|
-
get logContext() {
|
14777
|
-
var _a, _b, _c, _d, _e, _f, _g, _h;
|
14778
|
-
return {
|
14779
|
-
room: (_b = (_a = this.latestJoinResponse) === null || _a === void 0 ? void 0 : _a.room) === null || _b === void 0 ? void 0 : _b.name,
|
14780
|
-
roomID: (_d = (_c = this.latestJoinResponse) === null || _c === void 0 ? void 0 : _c.room) === null || _d === void 0 ? void 0 : _d.sid,
|
14781
|
-
participant: (_f = (_e = this.latestJoinResponse) === null || _e === void 0 ? void 0 : _e.participant) === null || _f === void 0 ? void 0 : _f.identity,
|
14782
|
-
pID: (_h = (_g = this.latestJoinResponse) === null || _g === void 0 ? void 0 : _g.participant) === null || _h === void 0 ? void 0 : _h.sid
|
14783
|
-
};
|
14784
|
+
return this.isMuted || unwrapConstraint(deviceId) === this._mediaStreamTrack.getSettings().deviceId;
|
14785
|
+
});
|
14784
14786
|
}
|
14785
|
-
|
14787
|
+
mute() {
|
14788
|
+
const _super = Object.create(null, {
|
14789
|
+
mute: {
|
14790
|
+
get: () => super.mute
|
14791
|
+
}
|
14792
|
+
});
|
14786
14793
|
return __awaiter(this, void 0, void 0, function* () {
|
14787
|
-
|
14788
|
-
this.token = token;
|
14789
|
-
this.signalOpts = opts;
|
14790
|
-
this.maxJoinAttempts = opts.maxRetries;
|
14794
|
+
const unlock = yield this.muteLock.lock();
|
14791
14795
|
try {
|
14792
|
-
this.
|
14793
|
-
|
14794
|
-
|
14795
|
-
this._isClosed = false;
|
14796
|
-
this.latestJoinResponse = joinResponse;
|
14797
|
-
this.subscriberPrimary = joinResponse.subscriberPrimary;
|
14798
|
-
if (!this.pcManager) {
|
14799
|
-
yield this.configure(joinResponse);
|
14800
|
-
}
|
14801
|
-
// create offer
|
14802
|
-
if (!this.subscriberPrimary) {
|
14803
|
-
this.negotiate();
|
14796
|
+
if (this.isMuted) {
|
14797
|
+
this.log.debug('Track already muted', this.logContext);
|
14798
|
+
return this;
|
14804
14799
|
}
|
14805
|
-
|
14806
|
-
|
14807
|
-
|
14808
|
-
|
14809
|
-
|
14810
|
-
this.log.warn("Couldn't connect to server, attempt ".concat(this.joinAttempts, " of ").concat(this.maxJoinAttempts), this.logContext);
|
14811
|
-
if (this.joinAttempts < this.maxJoinAttempts) {
|
14812
|
-
return this.join(url, token, opts, abortSignal);
|
14813
|
-
}
|
14814
|
-
}
|
14800
|
+
// disabled special handling as it will cause BT headsets to switch communication modes
|
14801
|
+
if (this.source === Track.Source.Microphone && this.stopOnMute && !this.isUserProvided) {
|
14802
|
+
this.log.debug('stopping mic track', this.logContext);
|
14803
|
+
// also stop the track, so that microphone indicator is turned off
|
14804
|
+
this._mediaStreamTrack.stop();
|
14815
14805
|
}
|
14816
|
-
|
14806
|
+
yield _super.mute.call(this);
|
14807
|
+
return this;
|
14808
|
+
} finally {
|
14809
|
+
unlock();
|
14817
14810
|
}
|
14818
14811
|
});
|
14819
14812
|
}
|
14820
|
-
|
14821
|
-
|
14822
|
-
|
14823
|
-
|
14824
|
-
unlock();
|
14825
|
-
return;
|
14813
|
+
unmute() {
|
14814
|
+
const _super = Object.create(null, {
|
14815
|
+
unmute: {
|
14816
|
+
get: () => super.unmute
|
14826
14817
|
}
|
14818
|
+
});
|
14819
|
+
return __awaiter(this, void 0, void 0, function* () {
|
14820
|
+
const unlock = yield this.muteLock.lock();
|
14827
14821
|
try {
|
14828
|
-
this.
|
14829
|
-
|
14830
|
-
|
14831
|
-
|
14832
|
-
this.
|
14833
|
-
|
14834
|
-
|
14822
|
+
if (!this.isMuted) {
|
14823
|
+
this.log.debug('Track already unmuted', this.logContext);
|
14824
|
+
return this;
|
14825
|
+
}
|
14826
|
+
const deviceHasChanged = this._constraints.deviceId && this._mediaStreamTrack.getSettings().deviceId !== unwrapConstraint(this._constraints.deviceId);
|
14827
|
+
if (this.source === Track.Source.Microphone && (this.stopOnMute || this._mediaStreamTrack.readyState === 'ended' || deviceHasChanged) && !this.isUserProvided) {
|
14828
|
+
this.log.debug('reacquiring mic track', this.logContext);
|
14829
|
+
yield this.restartTrack();
|
14830
|
+
}
|
14831
|
+
yield _super.unmute.call(this);
|
14832
|
+
return this;
|
14835
14833
|
} finally {
|
14836
14834
|
unlock();
|
14837
14835
|
}
|
14838
14836
|
});
|
14839
14837
|
}
|
14840
|
-
|
14838
|
+
restartTrack(options) {
|
14841
14839
|
return __awaiter(this, void 0, void 0, function* () {
|
14842
|
-
|
14843
|
-
|
14844
|
-
|
14845
|
-
|
14846
|
-
|
14847
|
-
|
14848
|
-
|
14849
|
-
|
14850
|
-
|
14851
|
-
|
14852
|
-
dc.onmessage = null;
|
14853
|
-
dc.onopen = null;
|
14854
|
-
};
|
14855
|
-
dcCleanup(this.lossyDC);
|
14856
|
-
dcCleanup(this.lossyDCSub);
|
14857
|
-
dcCleanup(this.reliableDC);
|
14858
|
-
dcCleanup(this.reliableDCSub);
|
14859
|
-
this.lossyDC = undefined;
|
14860
|
-
this.lossyDCSub = undefined;
|
14861
|
-
this.reliableDC = undefined;
|
14862
|
-
this.reliableDCSub = undefined;
|
14840
|
+
let constraints;
|
14841
|
+
if (options) {
|
14842
|
+
const streamConstraints = constraintsForOptions({
|
14843
|
+
audio: options
|
14844
|
+
});
|
14845
|
+
if (typeof streamConstraints.audio !== 'boolean') {
|
14846
|
+
constraints = streamConstraints.audio;
|
14847
|
+
}
|
14848
|
+
}
|
14849
|
+
yield this.restart(constraints);
|
14863
14850
|
});
|
14864
14851
|
}
|
14865
|
-
|
14852
|
+
restart(constraints) {
|
14853
|
+
const _super = Object.create(null, {
|
14854
|
+
restart: {
|
14855
|
+
get: () => super.restart
|
14856
|
+
}
|
14857
|
+
});
|
14866
14858
|
return __awaiter(this, void 0, void 0, function* () {
|
14867
|
-
yield
|
14868
|
-
this.
|
14859
|
+
const track = yield _super.restart.call(this, constraints);
|
14860
|
+
this.checkForSilence();
|
14861
|
+
return track;
|
14869
14862
|
});
|
14870
14863
|
}
|
14871
|
-
|
14872
|
-
|
14873
|
-
|
14864
|
+
/* @internal */
|
14865
|
+
startMonitor() {
|
14866
|
+
if (!isWeb()) {
|
14867
|
+
return;
|
14874
14868
|
}
|
14875
|
-
|
14876
|
-
|
14877
|
-
delete this.pendingTrackResolvers[req.cid];
|
14878
|
-
reject(new ConnectionError('publication of local track timed out, no response from server'));
|
14879
|
-
}, 10000);
|
14880
|
-
this.pendingTrackResolvers[req.cid] = {
|
14881
|
-
resolve: info => {
|
14882
|
-
clearTimeout(publicationTimeout);
|
14883
|
-
resolve(info);
|
14884
|
-
},
|
14885
|
-
reject: () => {
|
14886
|
-
clearTimeout(publicationTimeout);
|
14887
|
-
reject(new Error('Cancelled publication by calling unpublish'));
|
14888
|
-
}
|
14889
|
-
};
|
14890
|
-
this.client.sendAddTrack(req);
|
14891
|
-
});
|
14892
|
-
}
|
14893
|
-
/**
|
14894
|
-
* Removes sender from PeerConnection, returning true if it was removed successfully
|
14895
|
-
* and a negotiation is necessary
|
14896
|
-
* @param sender
|
14897
|
-
* @returns
|
14898
|
-
*/
|
14899
|
-
removeTrack(sender) {
|
14900
|
-
if (sender.track && this.pendingTrackResolvers[sender.track.id]) {
|
14901
|
-
const {
|
14902
|
-
reject
|
14903
|
-
} = this.pendingTrackResolvers[sender.track.id];
|
14904
|
-
if (reject) {
|
14905
|
-
reject();
|
14906
|
-
}
|
14907
|
-
delete this.pendingTrackResolvers[sender.track.id];
|
14908
|
-
}
|
14909
|
-
try {
|
14910
|
-
this.pcManager.removeTrack(sender);
|
14911
|
-
return true;
|
14912
|
-
} catch (e) {
|
14913
|
-
this.log.warn('failed to remove track', Object.assign(Object.assign({}, this.logContext), {
|
14914
|
-
error: e
|
14915
|
-
}));
|
14869
|
+
if (this.monitorInterval) {
|
14870
|
+
return;
|
14916
14871
|
}
|
14917
|
-
|
14918
|
-
|
14919
|
-
|
14920
|
-
this.client.sendMuteTrack(trackSid, muted);
|
14921
|
-
}
|
14922
|
-
get dataSubscriberReadyState() {
|
14923
|
-
var _a;
|
14924
|
-
return (_a = this.reliableDCSub) === null || _a === void 0 ? void 0 : _a.readyState;
|
14872
|
+
this.monitorInterval = setInterval(() => {
|
14873
|
+
this.monitorSender();
|
14874
|
+
}, monitorFrequency);
|
14925
14875
|
}
|
14926
|
-
|
14876
|
+
setProcessor(processor) {
|
14927
14877
|
return __awaiter(this, void 0, void 0, function* () {
|
14928
14878
|
var _a;
|
14929
|
-
|
14879
|
+
const unlock = yield this.processorLock.lock();
|
14880
|
+
try {
|
14881
|
+
if (!this.audioContext) {
|
14882
|
+
throw Error('Audio context needs to be set on LocalAudioTrack in order to enable processors');
|
14883
|
+
}
|
14884
|
+
if (this.processor) {
|
14885
|
+
yield this.stopProcessor();
|
14886
|
+
}
|
14887
|
+
const processorOptions = {
|
14888
|
+
kind: this.kind,
|
14889
|
+
track: this._mediaStreamTrack,
|
14890
|
+
audioContext: this.audioContext
|
14891
|
+
};
|
14892
|
+
this.log.debug("setting up audio processor ".concat(processor.name), this.logContext);
|
14893
|
+
yield processor.init(processorOptions);
|
14894
|
+
this.processor = processor;
|
14895
|
+
if (this.processor.processedTrack) {
|
14896
|
+
yield (_a = this.sender) === null || _a === void 0 ? void 0 : _a.replaceTrack(this.processor.processedTrack);
|
14897
|
+
this.processor.processedTrack.addEventListener('enable-lk-krisp-noise-filter', this.handleKrispNoiseFilterEnable);
|
14898
|
+
this.processor.processedTrack.addEventListener('disable-lk-krisp-noise-filter', this.handleKrispNoiseFilterDisable);
|
14899
|
+
}
|
14900
|
+
this.emit(TrackEvent.TrackProcessorUpdate, this.processor);
|
14901
|
+
} finally {
|
14902
|
+
unlock();
|
14903
|
+
}
|
14930
14904
|
});
|
14931
14905
|
}
|
14932
|
-
|
14933
|
-
|
14934
|
-
|
14906
|
+
/**
|
14907
|
+
* @internal
|
14908
|
+
* @experimental
|
14909
|
+
*/
|
14910
|
+
setAudioContext(audioContext) {
|
14911
|
+
this.audioContext = audioContext;
|
14935
14912
|
}
|
14936
|
-
|
14913
|
+
getSenderStats() {
|
14937
14914
|
return __awaiter(this, void 0, void 0, function* () {
|
14938
14915
|
var _a;
|
14939
|
-
|
14940
|
-
|
14941
|
-
return;
|
14916
|
+
if (!((_a = this.sender) === null || _a === void 0 ? void 0 : _a.getStats)) {
|
14917
|
+
return undefined;
|
14942
14918
|
}
|
14943
|
-
|
14944
|
-
|
14945
|
-
|
14946
|
-
|
14947
|
-
|
14948
|
-
|
14949
|
-
|
14950
|
-
|
14951
|
-
|
14952
|
-
|
14953
|
-
|
14954
|
-
|
14955
|
-
|
14956
|
-
|
14957
|
-
// reset publisher connection promise
|
14958
|
-
this.publisherConnectionPromise = undefined;
|
14959
|
-
}
|
14960
|
-
if (connectionState === PCTransportState.CONNECTED) {
|
14961
|
-
const shouldEmit = this.pcState === PCState.New;
|
14962
|
-
this.pcState = PCState.Connected;
|
14963
|
-
if (shouldEmit) {
|
14964
|
-
this.emit(EngineEvent.Connected, joinResponse);
|
14965
|
-
}
|
14966
|
-
} else if (connectionState === PCTransportState.FAILED) {
|
14967
|
-
// on Safari, PeerConnection will switch to 'disconnected' during renegotiation
|
14968
|
-
if (this.pcState === PCState.Connected) {
|
14969
|
-
this.pcState = PCState.Disconnected;
|
14970
|
-
this.handleDisconnect('peerconnection failed', subscriberState === 'failed' ? ReconnectReason.RR_SUBSCRIBER_FAILED : ReconnectReason.RR_PUBLISHER_FAILED);
|
14971
|
-
}
|
14972
|
-
}
|
14973
|
-
// detect cases where both signal client and peer connection are severed and assume that user has lost network connection
|
14974
|
-
const isSignalSevered = this.client.isDisconnected || this.client.currentState === SignalConnectionState.RECONNECTING;
|
14975
|
-
const isPCSevered = [PCTransportState.FAILED, PCTransportState.CLOSING, PCTransportState.CLOSED].includes(connectionState);
|
14976
|
-
if (isSignalSevered && isPCSevered && !this._isClosed) {
|
14977
|
-
this.emit(EngineEvent.Offline);
|
14919
|
+
const stats = yield this.sender.getStats();
|
14920
|
+
let audioStats;
|
14921
|
+
stats.forEach(v => {
|
14922
|
+
if (v.type === 'outbound-rtp') {
|
14923
|
+
audioStats = {
|
14924
|
+
type: 'audio',
|
14925
|
+
streamId: v.id,
|
14926
|
+
packetsSent: v.packetsSent,
|
14927
|
+
packetsLost: v.packetsLost,
|
14928
|
+
bytesSent: v.bytesSent,
|
14929
|
+
timestamp: v.timestamp,
|
14930
|
+
roundTripTime: v.roundTripTime,
|
14931
|
+
jitter: v.jitter
|
14932
|
+
};
|
14978
14933
|
}
|
14979
14934
|
});
|
14980
|
-
|
14981
|
-
this.emit(EngineEvent.MediaTrackAdded, ev.track, ev.streams[0], ev.receiver);
|
14982
|
-
};
|
14983
|
-
this.createDataChannels();
|
14935
|
+
return audioStats;
|
14984
14936
|
});
|
14985
14937
|
}
|
14986
|
-
|
14987
|
-
|
14988
|
-
|
14989
|
-
if (
|
14990
|
-
|
14991
|
-
|
14992
|
-
|
14993
|
-
|
14994
|
-
}));
|
14995
|
-
yield this.pcManager.setPublisherAnswer(sd);
|
14996
|
-
});
|
14997
|
-
// add candidate on trickle
|
14998
|
-
this.client.onTrickle = (candidate, target) => {
|
14999
|
-
if (!this.pcManager) {
|
15000
|
-
return;
|
15001
|
-
}
|
15002
|
-
this.log.trace('got ICE candidate from peer', Object.assign(Object.assign({}, this.logContext), {
|
15003
|
-
candidate,
|
15004
|
-
target
|
15005
|
-
}));
|
15006
|
-
this.pcManager.addIceCandidate(candidate, target);
|
15007
|
-
};
|
15008
|
-
// when server creates an offer for the client
|
15009
|
-
this.client.onOffer = sd => __awaiter(this, void 0, void 0, function* () {
|
15010
|
-
if (!this.pcManager) {
|
15011
|
-
return;
|
14938
|
+
checkForSilence() {
|
14939
|
+
return __awaiter(this, void 0, void 0, function* () {
|
14940
|
+
const trackIsSilent = yield detectSilence(this);
|
14941
|
+
if (trackIsSilent) {
|
14942
|
+
if (!this.isMuted) {
|
14943
|
+
this.log.warn('silence detected on local audio track', this.logContext);
|
14944
|
+
}
|
14945
|
+
this.emit(TrackEvent.AudioSilenceDetected);
|
15012
14946
|
}
|
15013
|
-
|
15014
|
-
this.client.sendAnswer(answer);
|
14947
|
+
return trackIsSilent;
|
15015
14948
|
});
|
15016
|
-
this.client.onLocalTrackPublished = res => {
|
15017
|
-
var _a;
|
15018
|
-
this.log.debug('received trackPublishedResponse', Object.assign(Object.assign({}, this.logContext), {
|
15019
|
-
cid: res.cid,
|
15020
|
-
track: (_a = res.track) === null || _a === void 0 ? void 0 : _a.sid
|
15021
|
-
}));
|
15022
|
-
if (!this.pendingTrackResolvers[res.cid]) {
|
15023
|
-
this.log.error("missing track resolver for ".concat(res.cid), Object.assign(Object.assign({}, this.logContext), {
|
15024
|
-
cid: res.cid
|
15025
|
-
}));
|
15026
|
-
return;
|
15027
|
-
}
|
15028
|
-
const {
|
15029
|
-
resolve
|
15030
|
-
} = this.pendingTrackResolvers[res.cid];
|
15031
|
-
delete this.pendingTrackResolvers[res.cid];
|
15032
|
-
resolve(res.track);
|
15033
|
-
};
|
15034
|
-
this.client.onLocalTrackUnpublished = response => {
|
15035
|
-
this.emit(EngineEvent.LocalTrackUnpublished, response);
|
15036
|
-
};
|
15037
|
-
this.client.onTokenRefresh = token => {
|
15038
|
-
this.token = token;
|
15039
|
-
};
|
15040
|
-
this.client.onRemoteMuteChanged = (trackSid, muted) => {
|
15041
|
-
this.emit(EngineEvent.RemoteMute, trackSid, muted);
|
15042
|
-
};
|
15043
|
-
this.client.onSubscribedQualityUpdate = update => {
|
15044
|
-
this.emit(EngineEvent.SubscribedQualityUpdate, update);
|
15045
|
-
};
|
15046
|
-
this.client.onClose = () => {
|
15047
|
-
this.handleDisconnect('signal', ReconnectReason.RR_SIGNAL_DISCONNECTED);
|
15048
|
-
};
|
15049
|
-
this.client.onLeave = leave => {
|
15050
|
-
if (leave === null || leave === void 0 ? void 0 : leave.canReconnect) {
|
15051
|
-
this.fullReconnectOnNext = true;
|
15052
|
-
// reconnect immediately instead of waiting for next attempt
|
15053
|
-
this.handleDisconnect(leaveReconnect);
|
15054
|
-
} else {
|
15055
|
-
this.emit(EngineEvent.Disconnected, leave === null || leave === void 0 ? void 0 : leave.reason);
|
15056
|
-
this.close();
|
15057
|
-
}
|
15058
|
-
this.log.debug('client leave request', Object.assign(Object.assign({}, this.logContext), {
|
15059
|
-
reason: leave === null || leave === void 0 ? void 0 : leave.reason
|
15060
|
-
}));
|
15061
|
-
};
|
15062
14949
|
}
|
15063
|
-
|
15064
|
-
|
15065
|
-
|
15066
|
-
|
15067
|
-
|
15068
|
-
|
15069
|
-
|
15070
|
-
|
15071
|
-
|
15072
|
-
|
15073
|
-
|
15074
|
-
const rtcIceServers = [];
|
15075
|
-
serverResponse.iceServers.forEach(iceServer => {
|
15076
|
-
const rtcIceServer = {
|
15077
|
-
urls: iceServer.urls
|
15078
|
-
};
|
15079
|
-
if (iceServer.username) rtcIceServer.username = iceServer.username;
|
15080
|
-
if (iceServer.credential) {
|
15081
|
-
rtcIceServer.credential = iceServer.credential;
|
15082
|
-
}
|
15083
|
-
rtcIceServers.push(rtcIceServer);
|
15084
|
-
});
|
15085
|
-
rtcConfig.iceServers = rtcIceServers;
|
15086
|
-
}
|
15087
|
-
if (serverResponse.clientConfiguration && serverResponse.clientConfiguration.forceRelay === ClientConfigSetting.ENABLED) {
|
15088
|
-
rtcConfig.iceTransportPolicy = 'relay';
|
15089
|
-
}
|
15090
|
-
// @ts-ignore
|
15091
|
-
rtcConfig.sdpSemantics = 'unified-plan';
|
15092
|
-
// @ts-ignore
|
15093
|
-
rtcConfig.continualGatheringPolicy = 'gather_continually';
|
15094
|
-
return rtcConfig;
|
14950
|
+
}
|
14951
|
+
|
14952
|
+
/** @internal */
|
14953
|
+
function mediaTrackToLocalTrack(mediaStreamTrack, constraints, loggerOptions) {
|
14954
|
+
switch (mediaStreamTrack.kind) {
|
14955
|
+
case 'audio':
|
14956
|
+
return new LocalAudioTrack(mediaStreamTrack, constraints, false, undefined, loggerOptions);
|
14957
|
+
case 'video':
|
14958
|
+
return new LocalVideoTrack(mediaStreamTrack, constraints, false, loggerOptions);
|
14959
|
+
default:
|
14960
|
+
throw new TrackInvalidError("unsupported track type: ".concat(mediaStreamTrack.kind));
|
15095
14961
|
}
|
15096
|
-
|
15097
|
-
|
15098
|
-
|
15099
|
-
|
15100
|
-
|
15101
|
-
|
15102
|
-
|
15103
|
-
|
14962
|
+
}
|
14963
|
+
/* @internal */
|
14964
|
+
const presets169 = Object.values(VideoPresets);
|
14965
|
+
/* @internal */
|
14966
|
+
const presets43 = Object.values(VideoPresets43);
|
14967
|
+
/* @internal */
|
14968
|
+
const presetsScreenShare = Object.values(ScreenSharePresets);
|
14969
|
+
/* @internal */
|
14970
|
+
const defaultSimulcastPresets169 = [VideoPresets.h180, VideoPresets.h360];
|
14971
|
+
/* @internal */
|
14972
|
+
const defaultSimulcastPresets43 = [VideoPresets43.h180, VideoPresets43.h360];
|
14973
|
+
/* @internal */
|
14974
|
+
const computeDefaultScreenShareSimulcastPresets = fromPreset => {
|
14975
|
+
const layers = [{
|
14976
|
+
scaleResolutionDownBy: 2,
|
14977
|
+
fps: fromPreset.encoding.maxFramerate
|
14978
|
+
}];
|
14979
|
+
return layers.map(t => {
|
14980
|
+
var _a, _b;
|
14981
|
+
return new VideoPreset(Math.floor(fromPreset.width / t.scaleResolutionDownBy), Math.floor(fromPreset.height / t.scaleResolutionDownBy), Math.max(150000, Math.floor(fromPreset.encoding.maxBitrate / (Math.pow(t.scaleResolutionDownBy, 2) * (((_a = fromPreset.encoding.maxFramerate) !== null && _a !== void 0 ? _a : 30) / ((_b = t.fps) !== null && _b !== void 0 ? _b : 30))))), t.fps, fromPreset.encoding.priority);
|
14982
|
+
});
|
14983
|
+
};
|
14984
|
+
// /**
|
14985
|
+
// *
|
14986
|
+
// * @internal
|
14987
|
+
// * @experimental
|
14988
|
+
// */
|
14989
|
+
// const computeDefaultMultiCodecSimulcastEncodings = (width: number, height: number) => {
|
14990
|
+
// // use vp8 as a default
|
14991
|
+
// const vp8 = determineAppropriateEncoding(false, width, height);
|
14992
|
+
// const vp9 = { ...vp8, maxBitrate: vp8.maxBitrate * 0.9 };
|
14993
|
+
// const h264 = { ...vp8, maxBitrate: vp8.maxBitrate * 1.1 };
|
14994
|
+
// const av1 = { ...vp8, maxBitrate: vp8.maxBitrate * 0.7 };
|
14995
|
+
// return {
|
14996
|
+
// vp8,
|
14997
|
+
// vp9,
|
14998
|
+
// h264,
|
14999
|
+
// av1,
|
15000
|
+
// };
|
15001
|
+
// };
|
15002
|
+
const videoRids = ['q', 'h', 'f'];
|
15003
|
+
/* @internal */
|
15004
|
+
function computeVideoEncodings(isScreenShare, width, height, options) {
|
15005
|
+
var _a, _b;
|
15006
|
+
let videoEncoding = options === null || options === void 0 ? void 0 : options.videoEncoding;
|
15007
|
+
if (isScreenShare) {
|
15008
|
+
videoEncoding = options === null || options === void 0 ? void 0 : options.screenShareEncoding;
|
15009
|
+
}
|
15010
|
+
const useSimulcast = options === null || options === void 0 ? void 0 : options.simulcast;
|
15011
|
+
const scalabilityMode = options === null || options === void 0 ? void 0 : options.scalabilityMode;
|
15012
|
+
const videoCodec = options === null || options === void 0 ? void 0 : options.videoCodec;
|
15013
|
+
if (!videoEncoding && !useSimulcast && !scalabilityMode || !width || !height) {
|
15014
|
+
// when we aren't simulcasting or svc, will need to return a single encoding without
|
15015
|
+
// capping bandwidth. we always require a encoding for dynacast
|
15016
|
+
return [{}];
|
15017
|
+
}
|
15018
|
+
if (!videoEncoding) {
|
15019
|
+
// find the right encoding based on width/height
|
15020
|
+
videoEncoding = determineAppropriateEncoding(isScreenShare, width, height, videoCodec);
|
15021
|
+
livekitLogger.debug('using video encoding', videoEncoding);
|
15022
|
+
}
|
15023
|
+
const original = new VideoPreset(width, height, videoEncoding.maxBitrate, videoEncoding.maxFramerate, videoEncoding.priority);
|
15024
|
+
if (scalabilityMode && isSVCCodec(videoCodec)) {
|
15025
|
+
const sm = new ScalabilityMode(scalabilityMode);
|
15026
|
+
const encodings = [];
|
15027
|
+
if (sm.spatial > 3) {
|
15028
|
+
throw new Error("unsupported scalabilityMode: ".concat(scalabilityMode));
|
15104
15029
|
}
|
15105
|
-
|
15106
|
-
|
15107
|
-
|
15030
|
+
// Before M113 in Chrome, defining multiple encodings with an SVC codec indicated
|
15031
|
+
// that SVC mode should be used. Safari still works this way.
|
15032
|
+
// This is a bit confusing but is due to how libwebrtc interpreted the encodings field
|
15033
|
+
// before M113.
|
15034
|
+
// Announced here: https://groups.google.com/g/discuss-webrtc/c/-QQ3pxrl-fw?pli=1
|
15035
|
+
const browser = getBrowser();
|
15036
|
+
if (isSafari() || (browser === null || browser === void 0 ? void 0 : browser.name) === 'Chrome' && compareVersions(browser === null || browser === void 0 ? void 0 : browser.version, '113') < 0) {
|
15037
|
+
const bitratesRatio = sm.suffix == 'h' ? 2 : 3;
|
15038
|
+
for (let i = 0; i < sm.spatial; i += 1) {
|
15039
|
+
// in legacy SVC, scaleResolutionDownBy cannot be set
|
15040
|
+
encodings.push({
|
15041
|
+
rid: videoRids[2 - i],
|
15042
|
+
maxBitrate: videoEncoding.maxBitrate / Math.pow(bitratesRatio, i),
|
15043
|
+
maxFramerate: original.encoding.maxFramerate
|
15044
|
+
});
|
15045
|
+
}
|
15046
|
+
// legacy SVC, scalabilityMode is set only on the first encoding
|
15047
|
+
/* @ts-ignore */
|
15048
|
+
encodings[0].scalabilityMode = scalabilityMode;
|
15049
|
+
} else {
|
15050
|
+
encodings.push({
|
15051
|
+
maxBitrate: videoEncoding.maxBitrate,
|
15052
|
+
maxFramerate: original.encoding.maxFramerate,
|
15053
|
+
/* @ts-ignore */
|
15054
|
+
scalabilityMode: scalabilityMode
|
15055
|
+
});
|
15108
15056
|
}
|
15109
|
-
|
15110
|
-
|
15111
|
-
// will drop older packets that arrive
|
15112
|
-
ordered: true,
|
15113
|
-
maxRetransmits: 0
|
15114
|
-
});
|
15115
|
-
this.reliableDC = this.pcManager.createPublisherDataChannel(reliableDataChannel, {
|
15116
|
-
ordered: true
|
15057
|
+
livekitLogger.debug("using svc encoding", {
|
15058
|
+
encodings
|
15117
15059
|
});
|
15118
|
-
|
15119
|
-
this.lossyDC.onmessage = this.handleDataMessage;
|
15120
|
-
this.reliableDC.onmessage = this.handleDataMessage;
|
15121
|
-
// handle datachannel errors
|
15122
|
-
this.lossyDC.onerror = this.handleDataError;
|
15123
|
-
this.reliableDC.onerror = this.handleDataError;
|
15124
|
-
// set up dc buffer threshold, set to 64kB (otherwise 0 by default)
|
15125
|
-
this.lossyDC.bufferedAmountLowThreshold = 65535;
|
15126
|
-
this.reliableDC.bufferedAmountLowThreshold = 65535;
|
15127
|
-
// handle buffer amount low events
|
15128
|
-
this.lossyDC.onbufferedamountlow = this.handleBufferedAmountLow;
|
15129
|
-
this.reliableDC.onbufferedamountlow = this.handleBufferedAmountLow;
|
15060
|
+
return encodings;
|
15130
15061
|
}
|
15131
|
-
|
15132
|
-
|
15133
|
-
|
15062
|
+
if (!useSimulcast) {
|
15063
|
+
return [videoEncoding];
|
15064
|
+
}
|
15065
|
+
let presets = [];
|
15066
|
+
if (isScreenShare) {
|
15067
|
+
presets = (_a = sortPresets(options === null || options === void 0 ? void 0 : options.screenShareSimulcastLayers)) !== null && _a !== void 0 ? _a : defaultSimulcastLayers(isScreenShare, original);
|
15068
|
+
} else {
|
15069
|
+
presets = (_b = sortPresets(options === null || options === void 0 ? void 0 : options.videoSimulcastLayers)) !== null && _b !== void 0 ? _b : defaultSimulcastLayers(isScreenShare, original);
|
15070
|
+
}
|
15071
|
+
let midPreset;
|
15072
|
+
if (presets.length > 0) {
|
15073
|
+
const lowPreset = presets[0];
|
15074
|
+
if (presets.length > 1) {
|
15075
|
+
[, midPreset] = presets;
|
15134
15076
|
}
|
15135
|
-
//
|
15136
|
-
|
15137
|
-
|
15138
|
-
|
15139
|
-
|
15140
|
-
|
15141
|
-
|
15142
|
-
|
15143
|
-
|
15144
|
-
|
15145
|
-
|
15146
|
-
|
15147
|
-
|
15148
|
-
|
15149
|
-
|
15150
|
-
const matchesVideoCodec = codec === "video/".concat(videoCodec);
|
15151
|
-
if (!matchesVideoCodec) {
|
15152
|
-
unmatched.push(c);
|
15153
|
-
return;
|
15154
|
-
}
|
15155
|
-
// for h264 codecs that have sdpFmtpLine available, use only if the
|
15156
|
-
// profile-level-id is 42e01f for cross-browser compatibility
|
15157
|
-
if (videoCodec === 'h264') {
|
15158
|
-
if (c.sdpFmtpLine && c.sdpFmtpLine.includes('profile-level-id=42e01f')) {
|
15159
|
-
matched.push(c);
|
15160
|
-
} else {
|
15161
|
-
partialMatched.push(c);
|
15162
|
-
}
|
15163
|
-
return;
|
15164
|
-
}
|
15165
|
-
matched.push(c);
|
15166
|
-
});
|
15167
|
-
if (supportsSetCodecPreferences(transceiver)) {
|
15168
|
-
transceiver.setCodecPreferences(matched.concat(partialMatched, unmatched));
|
15077
|
+
// NOTE:
|
15078
|
+
// 1. Ordering of these encodings is important. Chrome seems
|
15079
|
+
// to use the index into encodings to decide which layer
|
15080
|
+
// to disable when CPU constrained.
|
15081
|
+
// So encodings should be ordered in increasing spatial
|
15082
|
+
// resolution order.
|
15083
|
+
// 2. livekit-server translates rids into layers. So, all encodings
|
15084
|
+
// should have the base layer `q` and then more added
|
15085
|
+
// based on other conditions.
|
15086
|
+
const size = Math.max(width, height);
|
15087
|
+
if (size >= 960 && midPreset) {
|
15088
|
+
return encodingsFromPresets(width, height, [lowPreset, midPreset, original]);
|
15089
|
+
}
|
15090
|
+
if (size >= 480) {
|
15091
|
+
return encodingsFromPresets(width, height, [lowPreset, original]);
|
15169
15092
|
}
|
15170
15093
|
}
|
15171
|
-
|
15172
|
-
|
15173
|
-
|
15174
|
-
|
15175
|
-
|
15176
|
-
|
15177
|
-
|
15178
|
-
|
15179
|
-
const sender = yield this.createRTCRtpSender(track.mediaStreamTrack);
|
15180
|
-
return sender;
|
15181
|
-
}
|
15182
|
-
throw new UnexpectedConnectionState('Required webRTC APIs not supported on this device');
|
15183
|
-
});
|
15184
|
-
}
|
15185
|
-
createSimulcastSender(track, simulcastTrack, opts, encodings) {
|
15186
|
-
return __awaiter(this, void 0, void 0, function* () {
|
15187
|
-
// store RTCRtpSender
|
15188
|
-
if (supportsTransceiver()) {
|
15189
|
-
return this.createSimulcastTransceiverSender(track, simulcastTrack, opts, encodings);
|
15190
|
-
}
|
15191
|
-
if (supportsAddTrack()) {
|
15192
|
-
this.log.debug('using add-track fallback', this.logContext);
|
15193
|
-
return this.createRTCRtpSender(track.mediaStreamTrack);
|
15194
|
-
}
|
15195
|
-
throw new UnexpectedConnectionState('Cannot stream on this device');
|
15196
|
-
});
|
15094
|
+
return encodingsFromPresets(width, height, [original]);
|
15095
|
+
}
|
15096
|
+
function computeTrackBackupEncodings(track, videoCodec, opts) {
|
15097
|
+
var _a, _b, _c, _d;
|
15098
|
+
// backupCodec should not be true anymore, default codec is set in LocalParticipant.publish
|
15099
|
+
if (!opts.backupCodec || opts.backupCodec === true || opts.backupCodec.codec === opts.videoCodec) {
|
15100
|
+
// backup codec publishing is disabled
|
15101
|
+
return;
|
15197
15102
|
}
|
15198
|
-
|
15199
|
-
|
15200
|
-
|
15201
|
-
|
15202
|
-
}
|
15203
|
-
const streams = [];
|
15204
|
-
if (track.mediaStream) {
|
15205
|
-
streams.push(track.mediaStream);
|
15206
|
-
}
|
15207
|
-
const transceiverInit = {
|
15208
|
-
direction: 'sendonly',
|
15209
|
-
streams
|
15210
|
-
};
|
15211
|
-
if (encodings) {
|
15212
|
-
transceiverInit.sendEncodings = encodings;
|
15213
|
-
}
|
15214
|
-
// addTransceiver for react-native is async. web is synchronous, but await won't effect it.
|
15215
|
-
const transceiver = yield this.pcManager.addPublisherTransceiver(track.mediaStreamTrack, transceiverInit);
|
15216
|
-
if (track.kind === Track.Kind.Video && opts.videoCodec) {
|
15217
|
-
this.setPreferredCodec(transceiver, track.kind, opts.videoCodec);
|
15218
|
-
track.codec = opts.videoCodec;
|
15219
|
-
}
|
15220
|
-
return transceiver.sender;
|
15103
|
+
if (videoCodec !== opts.backupCodec.codec) {
|
15104
|
+
livekitLogger.warn('requested a different codec than specified as backup', {
|
15105
|
+
serverRequested: videoCodec,
|
15106
|
+
backup: opts.backupCodec.codec
|
15221
15107
|
});
|
15222
15108
|
}
|
15223
|
-
|
15224
|
-
|
15225
|
-
|
15226
|
-
|
15227
|
-
|
15228
|
-
|
15229
|
-
|
15230
|
-
|
15231
|
-
|
15232
|
-
|
15233
|
-
|
15234
|
-
|
15235
|
-
|
15236
|
-
|
15237
|
-
|
15109
|
+
opts.videoCodec = videoCodec;
|
15110
|
+
// use backup encoding setting as videoEncoding for backup codec publishing
|
15111
|
+
opts.videoEncoding = opts.backupCodec.encoding;
|
15112
|
+
const settings = track.mediaStreamTrack.getSettings();
|
15113
|
+
const width = (_a = settings.width) !== null && _a !== void 0 ? _a : (_b = track.dimensions) === null || _b === void 0 ? void 0 : _b.width;
|
15114
|
+
const height = (_c = settings.height) !== null && _c !== void 0 ? _c : (_d = track.dimensions) === null || _d === void 0 ? void 0 : _d.height;
|
15115
|
+
const encodings = computeVideoEncodings(track.source === Track.Source.ScreenShare, width, height, opts);
|
15116
|
+
return encodings;
|
15117
|
+
}
|
15118
|
+
/* @internal */
|
15119
|
+
function determineAppropriateEncoding(isScreenShare, width, height, codec) {
|
15120
|
+
const presets = presetsForResolution(isScreenShare, width, height);
|
15121
|
+
let {
|
15122
|
+
encoding
|
15123
|
+
} = presets[0];
|
15124
|
+
// handle portrait by swapping dimensions
|
15125
|
+
const size = Math.max(width, height);
|
15126
|
+
for (let i = 0; i < presets.length; i += 1) {
|
15127
|
+
const preset = presets[i];
|
15128
|
+
encoding = preset.encoding;
|
15129
|
+
if (preset.width >= size) {
|
15130
|
+
break;
|
15131
|
+
}
|
15132
|
+
}
|
15133
|
+
// presets are based on the assumption of vp8 as a codec
|
15134
|
+
// for other codecs we adjust the maxBitrate if no specific videoEncoding has been provided
|
15135
|
+
// users should override these with ones that are optimized for their use case
|
15136
|
+
// NOTE: SVC codec bitrates are inclusive of all scalability layers. while
|
15137
|
+
// bitrate for non-SVC codecs does not include other simulcast layers.
|
15138
|
+
if (codec) {
|
15139
|
+
switch (codec) {
|
15140
|
+
case 'av1':
|
15141
|
+
encoding = Object.assign({}, encoding);
|
15142
|
+
encoding.maxBitrate = encoding.maxBitrate * 0.7;
|
15143
|
+
break;
|
15144
|
+
case 'vp9':
|
15145
|
+
encoding = Object.assign({}, encoding);
|
15146
|
+
encoding.maxBitrate = encoding.maxBitrate * 0.85;
|
15147
|
+
break;
|
15148
|
+
}
|
15149
|
+
}
|
15150
|
+
return encoding;
|
15151
|
+
}
|
15152
|
+
/* @internal */
|
15153
|
+
function presetsForResolution(isScreenShare, width, height) {
|
15154
|
+
if (isScreenShare) {
|
15155
|
+
return presetsScreenShare;
|
15156
|
+
}
|
15157
|
+
const aspect = width > height ? width / height : height / width;
|
15158
|
+
if (Math.abs(aspect - 16.0 / 9) < Math.abs(aspect - 4.0 / 3)) {
|
15159
|
+
return presets169;
|
15160
|
+
}
|
15161
|
+
return presets43;
|
15162
|
+
}
|
15163
|
+
/* @internal */
|
15164
|
+
function defaultSimulcastLayers(isScreenShare, original) {
|
15165
|
+
if (isScreenShare) {
|
15166
|
+
return computeDefaultScreenShareSimulcastPresets(original);
|
15167
|
+
}
|
15168
|
+
const {
|
15169
|
+
width,
|
15170
|
+
height
|
15171
|
+
} = original;
|
15172
|
+
const aspect = width > height ? width / height : height / width;
|
15173
|
+
if (Math.abs(aspect - 16.0 / 9) < Math.abs(aspect - 4.0 / 3)) {
|
15174
|
+
return defaultSimulcastPresets169;
|
15175
|
+
}
|
15176
|
+
return defaultSimulcastPresets43;
|
15177
|
+
}
|
15178
|
+
// presets should be ordered by low, medium, high
|
15179
|
+
function encodingsFromPresets(width, height, presets) {
|
15180
|
+
const encodings = [];
|
15181
|
+
presets.forEach((preset, idx) => {
|
15182
|
+
if (idx >= videoRids.length) {
|
15183
|
+
return;
|
15184
|
+
}
|
15185
|
+
const size = Math.min(width, height);
|
15186
|
+
const rid = videoRids[idx];
|
15187
|
+
const encoding = {
|
15188
|
+
rid,
|
15189
|
+
scaleResolutionDownBy: Math.max(1, size / Math.min(preset.width, preset.height)),
|
15190
|
+
maxBitrate: preset.encoding.maxBitrate
|
15191
|
+
};
|
15192
|
+
if (preset.encoding.maxFramerate) {
|
15193
|
+
encoding.maxFramerate = preset.encoding.maxFramerate;
|
15194
|
+
}
|
15195
|
+
const canSetPriority = isFireFox() || idx === 0;
|
15196
|
+
if (preset.encoding.priority && canSetPriority) {
|
15197
|
+
encoding.priority = preset.encoding.priority;
|
15198
|
+
encoding.networkPriority = preset.encoding.priority;
|
15199
|
+
}
|
15200
|
+
encodings.push(encoding);
|
15201
|
+
});
|
15202
|
+
// RN ios simulcast requires all same framerates.
|
15203
|
+
if (isReactNative() && getReactNativeOs() === 'ios') {
|
15204
|
+
let topFramerate = undefined;
|
15205
|
+
encodings.forEach(encoding => {
|
15206
|
+
if (!topFramerate) {
|
15207
|
+
topFramerate = encoding.maxFramerate;
|
15208
|
+
} else if (encoding.maxFramerate && encoding.maxFramerate > topFramerate) {
|
15209
|
+
topFramerate = encoding.maxFramerate;
|
15238
15210
|
}
|
15239
|
-
this.setPreferredCodec(transceiver, track.kind, opts.videoCodec);
|
15240
|
-
track.setSimulcastTrackSender(opts.videoCodec, transceiver.sender);
|
15241
|
-
return transceiver.sender;
|
15242
15211
|
});
|
15243
|
-
|
15244
|
-
|
15245
|
-
|
15246
|
-
if (
|
15247
|
-
|
15212
|
+
let notifyOnce = true;
|
15213
|
+
encodings.forEach(encoding => {
|
15214
|
+
var _a;
|
15215
|
+
if (encoding.maxFramerate != topFramerate) {
|
15216
|
+
if (notifyOnce) {
|
15217
|
+
notifyOnce = false;
|
15218
|
+
livekitLogger.info("Simulcast on iOS React-Native requires all encodings to share the same framerate.");
|
15219
|
+
}
|
15220
|
+
livekitLogger.info("Setting framerate of encoding \"".concat((_a = encoding.rid) !== null && _a !== void 0 ? _a : '', "\" to ").concat(topFramerate));
|
15221
|
+
encoding.maxFramerate = topFramerate;
|
15248
15222
|
}
|
15249
|
-
return this.pcManager.addPublisherTrack(track);
|
15250
15223
|
});
|
15251
15224
|
}
|
15252
|
-
|
15253
|
-
|
15254
|
-
|
15255
|
-
|
15256
|
-
|
15225
|
+
return encodings;
|
15226
|
+
}
|
15227
|
+
/** @internal */
|
15228
|
+
function sortPresets(presets) {
|
15229
|
+
if (!presets) return;
|
15230
|
+
return presets.sort((a, b) => {
|
15231
|
+
const {
|
15232
|
+
encoding: aEnc
|
15233
|
+
} = a;
|
15234
|
+
const {
|
15235
|
+
encoding: bEnc
|
15236
|
+
} = b;
|
15237
|
+
if (aEnc.maxBitrate > bEnc.maxBitrate) {
|
15238
|
+
return 1;
|
15239
|
+
}
|
15240
|
+
if (aEnc.maxBitrate < bEnc.maxBitrate) return -1;
|
15241
|
+
if (aEnc.maxBitrate === bEnc.maxBitrate && aEnc.maxFramerate && bEnc.maxFramerate) {
|
15242
|
+
return aEnc.maxFramerate > bEnc.maxFramerate ? 1 : -1;
|
15243
|
+
}
|
15244
|
+
return 0;
|
15245
|
+
});
|
15246
|
+
}
|
15247
|
+
/** @internal */
|
15248
|
+
class ScalabilityMode {
|
15249
|
+
constructor(scalabilityMode) {
|
15250
|
+
const results = scalabilityMode.match(/^L(\d)T(\d)(h|_KEY|_KEY_SHIFT){0,1}$/);
|
15251
|
+
if (!results) {
|
15252
|
+
throw new Error('invalid scalability mode');
|
15253
|
+
}
|
15254
|
+
this.spatial = parseInt(results[1]);
|
15255
|
+
this.temporal = parseInt(results[2]);
|
15256
|
+
if (results.length > 3) {
|
15257
|
+
switch (results[3]) {
|
15258
|
+
case 'h':
|
15259
|
+
case '_KEY':
|
15260
|
+
case '_KEY_SHIFT':
|
15261
|
+
this.suffix = results[3];
|
15257
15262
|
}
|
15258
|
-
|
15259
|
-
|
15260
|
-
|
15263
|
+
}
|
15264
|
+
}
|
15265
|
+
toString() {
|
15266
|
+
var _a;
|
15267
|
+
return "L".concat(this.spatial, "T").concat(this.temporal).concat((_a = this.suffix) !== null && _a !== void 0 ? _a : '');
|
15268
|
+
}
|
15269
|
+
}
|
15270
|
+
|
15271
|
+
const refreshSubscribedCodecAfterNewCodec = 5000;
|
15272
|
+
class LocalVideoTrack extends LocalTrack {
|
15273
|
+
/**
|
15274
|
+
*
|
15275
|
+
* @param mediaTrack
|
15276
|
+
* @param constraints MediaTrackConstraints that are being used when restarting or reacquiring tracks
|
15277
|
+
* @param userProvidedTrack Signals to the SDK whether or not the mediaTrack should be managed (i.e. released and reacquired) internally by the SDK
|
15278
|
+
*/
|
15279
|
+
constructor(mediaTrack, constraints) {
|
15280
|
+
let userProvidedTrack = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
|
15281
|
+
let loggerOptions = arguments.length > 3 ? arguments[3] : undefined;
|
15282
|
+
super(mediaTrack, Track.Kind.Video, constraints, userProvidedTrack, loggerOptions);
|
15283
|
+
/* @internal */
|
15284
|
+
this.simulcastCodecs = new Map();
|
15285
|
+
this.monitorSender = () => __awaiter(this, void 0, void 0, function* () {
|
15286
|
+
if (!this.sender) {
|
15287
|
+
this._currentBitrate = 0;
|
15261
15288
|
return;
|
15262
15289
|
}
|
15263
|
-
|
15264
|
-
// signaling state could change to closed due to hardware sleep
|
15265
|
-
// those connections cannot be resumed
|
15266
|
-
((_c = (_b = this.pcManager) === null || _b === void 0 ? void 0 : _b.currentState) !== null && _c !== void 0 ? _c : PCTransportState.NEW) === PCTransportState.NEW) {
|
15267
|
-
this.fullReconnectOnNext = true;
|
15268
|
-
}
|
15290
|
+
let stats;
|
15269
15291
|
try {
|
15270
|
-
|
15271
|
-
if (this.fullReconnectOnNext) {
|
15272
|
-
yield this.restartConnection();
|
15273
|
-
} else {
|
15274
|
-
yield this.resumeConnection(reason);
|
15275
|
-
}
|
15276
|
-
this.clearPendingReconnect();
|
15277
|
-
this.fullReconnectOnNext = false;
|
15292
|
+
stats = yield this.getSenderStats();
|
15278
15293
|
} catch (e) {
|
15279
|
-
this.
|
15280
|
-
|
15281
|
-
|
15282
|
-
|
15283
|
-
|
15284
|
-
|
15285
|
-
|
15286
|
-
|
15287
|
-
|
15288
|
-
|
15289
|
-
this.
|
15290
|
-
|
15291
|
-
|
15292
|
-
|
15293
|
-
} else {
|
15294
|
-
this.log.info("could not recover connection after ".concat(this.reconnectAttempts, " attempts, ").concat(Date.now() - this.reconnectStart, "ms. giving up"), this.logContext);
|
15295
|
-
this.emit(EngineEvent.Disconnected);
|
15296
|
-
yield this.close();
|
15297
|
-
}
|
15298
|
-
} finally {
|
15299
|
-
this.attemptingReconnect = false;
|
15294
|
+
this.log.error('could not get audio sender stats', Object.assign(Object.assign({}, this.logContext), {
|
15295
|
+
error: e
|
15296
|
+
}));
|
15297
|
+
return;
|
15298
|
+
}
|
15299
|
+
const statsMap = new Map(stats.map(s => [s.rid, s]));
|
15300
|
+
if (this.prevStats) {
|
15301
|
+
let totalBitrate = 0;
|
15302
|
+
statsMap.forEach((s, key) => {
|
15303
|
+
var _a;
|
15304
|
+
const prev = (_a = this.prevStats) === null || _a === void 0 ? void 0 : _a.get(key);
|
15305
|
+
totalBitrate += computeBitrate(s, prev);
|
15306
|
+
});
|
15307
|
+
this._currentBitrate = totalBitrate;
|
15300
15308
|
}
|
15309
|
+
this.prevStats = statsMap;
|
15301
15310
|
});
|
15311
|
+
this.senderLock = new Mutex();
|
15302
15312
|
}
|
15303
|
-
|
15304
|
-
|
15305
|
-
return
|
15306
|
-
} catch (e) {
|
15307
|
-
this.log.warn('encountered error in reconnect policy', Object.assign(Object.assign({}, this.logContext), {
|
15308
|
-
error: e
|
15309
|
-
}));
|
15313
|
+
get isSimulcast() {
|
15314
|
+
if (this.sender && this.sender.getParameters().encodings.length > 1) {
|
15315
|
+
return true;
|
15310
15316
|
}
|
15311
|
-
|
15312
|
-
return null;
|
15317
|
+
return false;
|
15313
15318
|
}
|
15314
|
-
|
15319
|
+
/* @internal */
|
15320
|
+
startMonitor(signalClient) {
|
15321
|
+
var _a;
|
15322
|
+
this.signalClient = signalClient;
|
15323
|
+
if (!isWeb()) {
|
15324
|
+
return;
|
15325
|
+
}
|
15326
|
+
// save original encodings
|
15327
|
+
// TODO : merge simulcast tracks stats
|
15328
|
+
const params = (_a = this.sender) === null || _a === void 0 ? void 0 : _a.getParameters();
|
15329
|
+
if (params) {
|
15330
|
+
this.encodings = params.encodings;
|
15331
|
+
}
|
15332
|
+
if (this.monitorInterval) {
|
15333
|
+
return;
|
15334
|
+
}
|
15335
|
+
this.monitorInterval = setInterval(() => {
|
15336
|
+
this.monitorSender();
|
15337
|
+
}, monitorFrequency);
|
15338
|
+
}
|
15339
|
+
stop() {
|
15340
|
+
this._mediaStreamTrack.getConstraints();
|
15341
|
+
this.simulcastCodecs.forEach(trackInfo => {
|
15342
|
+
trackInfo.mediaStreamTrack.stop();
|
15343
|
+
});
|
15344
|
+
super.stop();
|
15345
|
+
}
|
15346
|
+
pauseUpstream() {
|
15347
|
+
const _super = Object.create(null, {
|
15348
|
+
pauseUpstream: {
|
15349
|
+
get: () => super.pauseUpstream
|
15350
|
+
}
|
15351
|
+
});
|
15315
15352
|
return __awaiter(this, void 0, void 0, function* () {
|
15316
|
-
var _a, _b, _c;
|
15353
|
+
var _a, e_1, _b, _c;
|
15354
|
+
var _d;
|
15355
|
+
yield _super.pauseUpstream.call(this);
|
15317
15356
|
try {
|
15318
|
-
|
15319
|
-
|
15320
|
-
|
15321
|
-
|
15322
|
-
|
15323
|
-
this.emit(EngineEvent.Restarting);
|
15324
|
-
if (!this.client.isDisconnected) {
|
15325
|
-
yield this.client.sendLeave();
|
15357
|
+
for (var _e = true, _f = __asyncValues(this.simulcastCodecs.values()), _g; _g = yield _f.next(), _a = _g.done, !_a; _e = true) {
|
15358
|
+
_c = _g.value;
|
15359
|
+
_e = false;
|
15360
|
+
const sc = _c;
|
15361
|
+
yield (_d = sc.sender) === null || _d === void 0 ? void 0 : _d.replaceTrack(null);
|
15326
15362
|
}
|
15327
|
-
|
15328
|
-
|
15329
|
-
|
15363
|
+
} catch (e_1_1) {
|
15364
|
+
e_1 = {
|
15365
|
+
error: e_1_1
|
15366
|
+
};
|
15367
|
+
} finally {
|
15330
15368
|
try {
|
15331
|
-
if (!
|
15332
|
-
|
15333
|
-
|
15334
|
-
}
|
15335
|
-
// in case a regionUrl is passed, the region URL takes precedence
|
15336
|
-
joinResponse = yield this.join(regionUrl !== null && regionUrl !== void 0 ? regionUrl : this.url, this.token, this.signalOpts);
|
15337
|
-
} catch (e) {
|
15338
|
-
if (e instanceof ConnectionError && e.reason === 0 /* ConnectionErrorReason.NotAllowed */) {
|
15339
|
-
throw new UnexpectedConnectionState('could not reconnect, token might be expired');
|
15340
|
-
}
|
15341
|
-
throw new SignalReconnectError();
|
15342
|
-
}
|
15343
|
-
if (this.shouldFailNext) {
|
15344
|
-
this.shouldFailNext = false;
|
15345
|
-
throw new Error('simulated failure');
|
15346
|
-
}
|
15347
|
-
this.client.setReconnected();
|
15348
|
-
this.emit(EngineEvent.SignalRestarted, joinResponse);
|
15349
|
-
yield this.waitForPCReconnected();
|
15350
|
-
// re-check signal connection state before setting engine as resumed
|
15351
|
-
if (this.client.currentState !== SignalConnectionState.CONNECTED) {
|
15352
|
-
throw new SignalReconnectError('Signal connection got severed during reconnect');
|
15353
|
-
}
|
15354
|
-
(_a = this.regionUrlProvider) === null || _a === void 0 ? void 0 : _a.resetAttempts();
|
15355
|
-
// reconnect success
|
15356
|
-
this.emit(EngineEvent.Restarted);
|
15357
|
-
} catch (error) {
|
15358
|
-
const nextRegionUrl = yield (_b = this.regionUrlProvider) === null || _b === void 0 ? void 0 : _b.getNextBestRegionUrl();
|
15359
|
-
if (nextRegionUrl) {
|
15360
|
-
yield this.restartConnection(nextRegionUrl);
|
15361
|
-
return;
|
15362
|
-
} else {
|
15363
|
-
// no more regions to try (or we're not on cloud)
|
15364
|
-
(_c = this.regionUrlProvider) === null || _c === void 0 ? void 0 : _c.resetAttempts();
|
15365
|
-
throw error;
|
15369
|
+
if (!_e && !_a && (_b = _f.return)) yield _b.call(_f);
|
15370
|
+
} finally {
|
15371
|
+
if (e_1) throw e_1.error;
|
15366
15372
|
}
|
15367
15373
|
}
|
15368
15374
|
});
|
15369
15375
|
}
|
15370
|
-
|
15371
|
-
|
15372
|
-
|
15373
|
-
|
15374
|
-
// permanent failure, don't attempt reconnection
|
15375
|
-
throw new UnexpectedConnectionState('could not reconnect, url or token not saved');
|
15376
|
-
}
|
15377
|
-
// trigger publisher reconnect
|
15378
|
-
if (!this.pcManager) {
|
15379
|
-
throw new UnexpectedConnectionState('publisher and subscriber connections unset');
|
15376
|
+
resumeUpstream() {
|
15377
|
+
const _super = Object.create(null, {
|
15378
|
+
resumeUpstream: {
|
15379
|
+
get: () => super.resumeUpstream
|
15380
15380
|
}
|
15381
|
-
|
15382
|
-
|
15383
|
-
|
15381
|
+
});
|
15382
|
+
return __awaiter(this, void 0, void 0, function* () {
|
15383
|
+
var _a, e_2, _b, _c;
|
15384
|
+
var _d;
|
15385
|
+
yield _super.resumeUpstream.call(this);
|
15384
15386
|
try {
|
15385
|
-
this.
|
15386
|
-
|
15387
|
-
|
15388
|
-
|
15389
|
-
|
15390
|
-
message = error.message;
|
15391
|
-
this.log.error(error.message, Object.assign(Object.assign({}, this.logContext), {
|
15392
|
-
error
|
15393
|
-
}));
|
15394
|
-
}
|
15395
|
-
if (error instanceof ConnectionError && error.reason === 0 /* ConnectionErrorReason.NotAllowed */) {
|
15396
|
-
throw new UnexpectedConnectionState('could not reconnect, token might be expired');
|
15387
|
+
for (var _e = true, _f = __asyncValues(this.simulcastCodecs.values()), _g; _g = yield _f.next(), _a = _g.done, !_a; _e = true) {
|
15388
|
+
_c = _g.value;
|
15389
|
+
_e = false;
|
15390
|
+
const sc = _c;
|
15391
|
+
yield (_d = sc.sender) === null || _d === void 0 ? void 0 : _d.replaceTrack(sc.mediaStreamTrack);
|
15397
15392
|
}
|
15398
|
-
|
15399
|
-
|
15393
|
+
} catch (e_2_1) {
|
15394
|
+
e_2 = {
|
15395
|
+
error: e_2_1
|
15396
|
+
};
|
15397
|
+
} finally {
|
15398
|
+
try {
|
15399
|
+
if (!_e && !_a && (_b = _f.return)) yield _b.call(_f);
|
15400
|
+
} finally {
|
15401
|
+
if (e_2) throw e_2.error;
|
15400
15402
|
}
|
15401
|
-
throw new SignalReconnectError(message);
|
15402
|
-
}
|
15403
|
-
this.emit(EngineEvent.SignalResumed);
|
15404
|
-
if (res) {
|
15405
|
-
const rtcConfig = this.makeRTCConfiguration(res);
|
15406
|
-
this.pcManager.updateConfiguration(rtcConfig);
|
15407
|
-
} else {
|
15408
|
-
this.log.warn('Did not receive reconnect response', this.logContext);
|
15409
|
-
}
|
15410
|
-
if (this.shouldFailNext) {
|
15411
|
-
this.shouldFailNext = false;
|
15412
|
-
throw new Error('simulated failure');
|
15413
|
-
}
|
15414
|
-
yield this.pcManager.triggerIceRestart();
|
15415
|
-
yield this.waitForPCReconnected();
|
15416
|
-
// re-check signal connection state before setting engine as resumed
|
15417
|
-
if (this.client.currentState !== SignalConnectionState.CONNECTED) {
|
15418
|
-
throw new SignalReconnectError('Signal connection got severed during reconnect');
|
15419
|
-
}
|
15420
|
-
this.client.setReconnected();
|
15421
|
-
// recreate publish datachannel if it's id is null
|
15422
|
-
// (for safari https://bugs.webkit.org/show_bug.cgi?id=184688)
|
15423
|
-
if (((_a = this.reliableDC) === null || _a === void 0 ? void 0 : _a.readyState) === 'open' && this.reliableDC.id === null) {
|
15424
|
-
this.createDataChannels();
|
15425
15403
|
}
|
15426
|
-
// resume success
|
15427
|
-
this.emit(EngineEvent.Resumed);
|
15428
15404
|
});
|
15429
15405
|
}
|
15430
|
-
|
15431
|
-
|
15432
|
-
|
15433
|
-
|
15406
|
+
mute() {
|
15407
|
+
const _super = Object.create(null, {
|
15408
|
+
mute: {
|
15409
|
+
get: () => super.mute
|
15434
15410
|
}
|
15435
|
-
yield this.pcManager.ensurePCTransportConnection(abortController, timeout);
|
15436
15411
|
});
|
15437
|
-
}
|
15438
|
-
waitForPCReconnected() {
|
15439
15412
|
return __awaiter(this, void 0, void 0, function* () {
|
15440
|
-
|
15441
|
-
this.log.debug('waiting for peer connection to reconnect', this.logContext);
|
15413
|
+
const unlock = yield this.muteLock.lock();
|
15442
15414
|
try {
|
15443
|
-
|
15444
|
-
|
15445
|
-
|
15415
|
+
if (this.isMuted) {
|
15416
|
+
this.log.debug('Track already muted', this.logContext);
|
15417
|
+
return this;
|
15446
15418
|
}
|
15447
|
-
|
15448
|
-
|
15449
|
-
|
15450
|
-
|
15451
|
-
|
15452
|
-
|
15419
|
+
if (this.source === Track.Source.Camera && !this.isUserProvided) {
|
15420
|
+
this.log.debug('stopping camera track', this.logContext);
|
15421
|
+
// also stop the track, so that camera indicator is turned off
|
15422
|
+
this._mediaStreamTrack.stop();
|
15423
|
+
}
|
15424
|
+
yield _super.mute.call(this);
|
15425
|
+
return this;
|
15426
|
+
} finally {
|
15427
|
+
unlock();
|
15453
15428
|
}
|
15454
15429
|
});
|
15455
15430
|
}
|
15456
|
-
|
15457
|
-
|
15458
|
-
|
15459
|
-
|
15460
|
-
// make sure we do have a data connection
|
15461
|
-
yield this.ensurePublisherConnected(kind);
|
15462
|
-
const dc = this.dataChannelForKind(kind);
|
15463
|
-
if (dc) {
|
15464
|
-
dc.send(msg);
|
15431
|
+
unmute() {
|
15432
|
+
const _super = Object.create(null, {
|
15433
|
+
unmute: {
|
15434
|
+
get: () => super.unmute
|
15465
15435
|
}
|
15466
|
-
this.updateAndEmitDCBufferStatus(kind);
|
15467
15436
|
});
|
15468
|
-
|
15469
|
-
|
15470
|
-
|
15471
|
-
|
15472
|
-
|
15473
|
-
|
15474
|
-
var _this2 = this;
|
15475
|
-
let subscriber = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.subscriberPrimary;
|
15476
|
-
return function* () {
|
15477
|
-
var _a;
|
15478
|
-
if (!_this2.pcManager) {
|
15479
|
-
throw new UnexpectedConnectionState('PC manager is closed');
|
15437
|
+
return __awaiter(this, void 0, void 0, function* () {
|
15438
|
+
const unlock = yield this.muteLock.lock();
|
15439
|
+
try {
|
15440
|
+
if (!this.isMuted) {
|
15441
|
+
this.log.debug('Track already unmuted', this.logContext);
|
15442
|
+
return this;
|
15480
15443
|
}
|
15481
|
-
|
15482
|
-
|
15483
|
-
|
15484
|
-
throw new ConnectionError("".concat(transportName, " connection not set"));
|
15444
|
+
if (this.source === Track.Source.Camera && !this.isUserProvided) {
|
15445
|
+
this.log.debug('reacquiring camera track', this.logContext);
|
15446
|
+
yield this.restartTrack();
|
15485
15447
|
}
|
15486
|
-
|
15487
|
-
|
15488
|
-
|
15489
|
-
|
15490
|
-
|
15491
|
-
if ((targetChannel === null || targetChannel === void 0 ? void 0 : targetChannel.readyState) === 'open') {
|
15492
|
-
return;
|
15493
|
-
}
|
15494
|
-
// wait until ICE connected
|
15495
|
-
const endTime = new Date().getTime() + _this2.peerConnectionTimeout;
|
15496
|
-
while (new Date().getTime() < endTime) {
|
15497
|
-
if (transport.isICEConnected && ((_a = _this2.dataChannelForKind(kind, subscriber)) === null || _a === void 0 ? void 0 : _a.readyState) === 'open') {
|
15498
|
-
return;
|
15499
|
-
}
|
15500
|
-
yield sleep(50);
|
15501
|
-
}
|
15502
|
-
throw new ConnectionError("could not establish ".concat(transportName, " connection, state: ").concat(transport.getICEConnectionState()));
|
15503
|
-
}();
|
15448
|
+
yield _super.unmute.call(this);
|
15449
|
+
return this;
|
15450
|
+
} finally {
|
15451
|
+
unlock();
|
15452
|
+
}
|
15504
15453
|
});
|
15505
15454
|
}
|
15506
|
-
|
15455
|
+
setTrackMuted(muted) {
|
15456
|
+
super.setTrackMuted(muted);
|
15457
|
+
for (const sc of this.simulcastCodecs.values()) {
|
15458
|
+
sc.mediaStreamTrack.enabled = !muted;
|
15459
|
+
}
|
15460
|
+
}
|
15461
|
+
getSenderStats() {
|
15507
15462
|
return __awaiter(this, void 0, void 0, function* () {
|
15508
|
-
|
15509
|
-
|
15463
|
+
var _a;
|
15464
|
+
if (!((_a = this.sender) === null || _a === void 0 ? void 0 : _a.getStats)) {
|
15465
|
+
return [];
|
15510
15466
|
}
|
15511
|
-
|
15467
|
+
const items = [];
|
15468
|
+
const stats = yield this.sender.getStats();
|
15469
|
+
stats.forEach(v => {
|
15470
|
+
var _a;
|
15471
|
+
if (v.type === 'outbound-rtp') {
|
15472
|
+
const vs = {
|
15473
|
+
type: 'video',
|
15474
|
+
streamId: v.id,
|
15475
|
+
frameHeight: v.frameHeight,
|
15476
|
+
frameWidth: v.frameWidth,
|
15477
|
+
framesPerSecond: v.framesPerSecond,
|
15478
|
+
framesSent: v.framesSent,
|
15479
|
+
firCount: v.firCount,
|
15480
|
+
pliCount: v.pliCount,
|
15481
|
+
nackCount: v.nackCount,
|
15482
|
+
packetsSent: v.packetsSent,
|
15483
|
+
bytesSent: v.bytesSent,
|
15484
|
+
qualityLimitationReason: v.qualityLimitationReason,
|
15485
|
+
qualityLimitationDurations: v.qualityLimitationDurations,
|
15486
|
+
qualityLimitationResolutionChanges: v.qualityLimitationResolutionChanges,
|
15487
|
+
rid: (_a = v.rid) !== null && _a !== void 0 ? _a : v.id,
|
15488
|
+
retransmittedPacketsSent: v.retransmittedPacketsSent,
|
15489
|
+
targetBitrate: v.targetBitrate,
|
15490
|
+
timestamp: v.timestamp
|
15491
|
+
};
|
15492
|
+
// locate the appropriate remote-inbound-rtp item
|
15493
|
+
const r = stats.get(v.remoteId);
|
15494
|
+
if (r) {
|
15495
|
+
vs.jitter = r.jitter;
|
15496
|
+
vs.packetsLost = r.packetsLost;
|
15497
|
+
vs.roundTripTime = r.roundTripTime;
|
15498
|
+
}
|
15499
|
+
items.push(vs);
|
15500
|
+
}
|
15501
|
+
});
|
15502
|
+
// make sure highest res layer is always first
|
15503
|
+
items.sort((a, b) => {
|
15504
|
+
var _a, _b;
|
15505
|
+
return ((_a = b.frameWidth) !== null && _a !== void 0 ? _a : 0) - ((_b = a.frameWidth) !== null && _b !== void 0 ? _b : 0);
|
15506
|
+
});
|
15507
|
+
return items;
|
15512
15508
|
});
|
15513
15509
|
}
|
15514
|
-
|
15515
|
-
|
15516
|
-
|
15517
|
-
|
15518
|
-
|
15519
|
-
|
15520
|
-
|
15521
|
-
return false;
|
15522
|
-
}
|
15523
|
-
// ensure signal is connected
|
15524
|
-
if (!this.client.ws || this.client.ws.readyState === WebSocket.CLOSED) {
|
15525
|
-
return false;
|
15510
|
+
setPublishingQuality(maxQuality) {
|
15511
|
+
const qualities = [];
|
15512
|
+
for (let q = VideoQuality.LOW; q <= VideoQuality.HIGH; q += 1) {
|
15513
|
+
qualities.push(new SubscribedQuality({
|
15514
|
+
quality: q,
|
15515
|
+
enabled: q <= maxQuality
|
15516
|
+
}));
|
15526
15517
|
}
|
15527
|
-
|
15518
|
+
this.log.debug("setting publishing quality. max quality ".concat(maxQuality), this.logContext);
|
15519
|
+
this.setPublishingLayers(qualities);
|
15528
15520
|
}
|
15529
|
-
|
15530
|
-
negotiate() {
|
15521
|
+
setDeviceId(deviceId) {
|
15531
15522
|
return __awaiter(this, void 0, void 0, function* () {
|
15532
|
-
|
15533
|
-
|
15534
|
-
|
15535
|
-
|
15536
|
-
|
15523
|
+
if (this._constraints.deviceId === deviceId && this._mediaStreamTrack.getSettings().deviceId === unwrapConstraint(deviceId)) {
|
15524
|
+
return true;
|
15525
|
+
}
|
15526
|
+
this._constraints.deviceId = deviceId;
|
15527
|
+
// when video is muted, underlying media stream track is stopped and
|
15528
|
+
// will be restarted later
|
15529
|
+
if (!this.isMuted) {
|
15530
|
+
yield this.restartTrack();
|
15531
|
+
}
|
15532
|
+
return this.isMuted || unwrapConstraint(deviceId) === this._mediaStreamTrack.getSettings().deviceId;
|
15533
|
+
});
|
15534
|
+
}
|
15535
|
+
restartTrack(options) {
|
15536
|
+
return __awaiter(this, void 0, void 0, function* () {
|
15537
|
+
var _a, e_3, _b, _c;
|
15538
|
+
let constraints;
|
15539
|
+
if (options) {
|
15540
|
+
const streamConstraints = constraintsForOptions({
|
15541
|
+
video: options
|
15542
|
+
});
|
15543
|
+
if (typeof streamConstraints.video !== 'boolean') {
|
15544
|
+
constraints = streamConstraints.video;
|
15537
15545
|
}
|
15538
|
-
|
15539
|
-
|
15540
|
-
|
15541
|
-
|
15542
|
-
|
15543
|
-
|
15544
|
-
|
15545
|
-
|
15546
|
-
|
15547
|
-
|
15546
|
+
}
|
15547
|
+
yield this.restart(constraints);
|
15548
|
+
try {
|
15549
|
+
for (var _d = true, _e = __asyncValues(this.simulcastCodecs.values()), _f; _f = yield _e.next(), _a = _f.done, !_a; _d = true) {
|
15550
|
+
_c = _f.value;
|
15551
|
+
_d = false;
|
15552
|
+
const sc = _c;
|
15553
|
+
if (sc.sender) {
|
15554
|
+
sc.mediaStreamTrack = this.mediaStreamTrack.clone();
|
15555
|
+
yield sc.sender.replaceTrack(sc.mediaStreamTrack);
|
15556
|
+
}
|
15548
15557
|
}
|
15549
|
-
|
15550
|
-
|
15551
|
-
|
15552
|
-
|
15553
|
-
|
15554
|
-
if (isVideoCodec(codec)) {
|
15555
|
-
rtpMap.set(rtp.payload, codec);
|
15556
|
-
}
|
15557
|
-
});
|
15558
|
-
this.emit(EngineEvent.RTPVideoMapUpdate, rtpMap);
|
15559
|
-
});
|
15558
|
+
} catch (e_3_1) {
|
15559
|
+
e_3 = {
|
15560
|
+
error: e_3_1
|
15561
|
+
};
|
15562
|
+
} finally {
|
15560
15563
|
try {
|
15561
|
-
yield
|
15562
|
-
resolve();
|
15563
|
-
} catch (e) {
|
15564
|
-
if (e instanceof NegotiationError) {
|
15565
|
-
this.fullReconnectOnNext = true;
|
15566
|
-
}
|
15567
|
-
this.handleDisconnect('negotiation', ReconnectReason.RR_UNKNOWN);
|
15568
|
-
reject(e);
|
15564
|
+
if (!_d && !_a && (_b = _e.return)) yield _b.call(_e);
|
15569
15565
|
} finally {
|
15570
|
-
|
15566
|
+
if (e_3) throw e_3.error;
|
15571
15567
|
}
|
15572
|
-
}
|
15568
|
+
}
|
15573
15569
|
});
|
15574
15570
|
}
|
15575
|
-
|
15576
|
-
|
15577
|
-
|
15578
|
-
|
15579
|
-
}
|
15580
|
-
if (kind === DataPacket_Kind.RELIABLE) {
|
15581
|
-
return this.reliableDC;
|
15582
|
-
}
|
15583
|
-
} else {
|
15584
|
-
if (kind === DataPacket_Kind.LOSSY) {
|
15585
|
-
return this.lossyDCSub;
|
15586
|
-
}
|
15587
|
-
if (kind === DataPacket_Kind.RELIABLE) {
|
15588
|
-
return this.reliableDCSub;
|
15571
|
+
setProcessor(processor_1) {
|
15572
|
+
const _super = Object.create(null, {
|
15573
|
+
setProcessor: {
|
15574
|
+
get: () => super.setProcessor
|
15589
15575
|
}
|
15576
|
+
});
|
15577
|
+
return __awaiter(this, arguments, void 0, function (processor) {
|
15578
|
+
var _this = this;
|
15579
|
+
let showProcessedStreamLocally = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
|
15580
|
+
return function* () {
|
15581
|
+
var _a, e_4, _b, _c;
|
15582
|
+
var _d, _e;
|
15583
|
+
yield _super.setProcessor.call(_this, processor, showProcessedStreamLocally);
|
15584
|
+
if ((_d = _this.processor) === null || _d === void 0 ? void 0 : _d.processedTrack) {
|
15585
|
+
try {
|
15586
|
+
for (var _f = true, _g = __asyncValues(_this.simulcastCodecs.values()), _h; _h = yield _g.next(), _a = _h.done, !_a; _f = true) {
|
15587
|
+
_c = _h.value;
|
15588
|
+
_f = false;
|
15589
|
+
const sc = _c;
|
15590
|
+
yield (_e = sc.sender) === null || _e === void 0 ? void 0 : _e.replaceTrack(_this.processor.processedTrack);
|
15591
|
+
}
|
15592
|
+
} catch (e_4_1) {
|
15593
|
+
e_4 = {
|
15594
|
+
error: e_4_1
|
15595
|
+
};
|
15596
|
+
} finally {
|
15597
|
+
try {
|
15598
|
+
if (!_f && !_a && (_b = _g.return)) yield _b.call(_g);
|
15599
|
+
} finally {
|
15600
|
+
if (e_4) throw e_4.error;
|
15601
|
+
}
|
15602
|
+
}
|
15603
|
+
}
|
15604
|
+
}();
|
15605
|
+
});
|
15606
|
+
}
|
15607
|
+
addSimulcastTrack(codec, encodings) {
|
15608
|
+
if (this.simulcastCodecs.has(codec)) {
|
15609
|
+
this.log.error("".concat(codec, " already added, skipping adding simulcast codec"), this.logContext);
|
15610
|
+
return;
|
15590
15611
|
}
|
15612
|
+
const simulcastCodecInfo = {
|
15613
|
+
codec,
|
15614
|
+
mediaStreamTrack: this.mediaStreamTrack.clone(),
|
15615
|
+
sender: undefined,
|
15616
|
+
encodings
|
15617
|
+
};
|
15618
|
+
this.simulcastCodecs.set(codec, simulcastCodecInfo);
|
15619
|
+
return simulcastCodecInfo;
|
15591
15620
|
}
|
15592
|
-
|
15593
|
-
|
15594
|
-
|
15595
|
-
if (!this.pcManager) {
|
15596
|
-
this.log.warn('sync state cannot be sent without peer connection setup', this.logContext);
|
15621
|
+
setSimulcastTrackSender(codec, sender) {
|
15622
|
+
const simulcastCodecInfo = this.simulcastCodecs.get(codec);
|
15623
|
+
if (!simulcastCodecInfo) {
|
15597
15624
|
return;
|
15598
15625
|
}
|
15599
|
-
|
15600
|
-
|
15601
|
-
|
15602
|
-
|
15603
|
-
|
15604
|
-
|
15605
|
-
*/
|
15606
|
-
const autoSubscribe = (_b = (_a = this.signalOpts) === null || _a === void 0 ? void 0 : _a.autoSubscribe) !== null && _b !== void 0 ? _b : true;
|
15607
|
-
const trackSids = new Array();
|
15608
|
-
const trackSidsDisabled = new Array();
|
15609
|
-
remoteTracks.forEach(track => {
|
15610
|
-
if (track.isDesired !== autoSubscribe) {
|
15611
|
-
trackSids.push(track.trackSid);
|
15612
|
-
}
|
15613
|
-
if (!track.isEnabled) {
|
15614
|
-
trackSidsDisabled.push(track.trackSid);
|
15615
|
-
}
|
15616
|
-
});
|
15617
|
-
this.client.sendSyncState(new SyncState({
|
15618
|
-
answer: previousAnswer ? toProtoSessionDescription({
|
15619
|
-
sdp: previousAnswer.sdp,
|
15620
|
-
type: previousAnswer.type
|
15621
|
-
}) : undefined,
|
15622
|
-
offer: previousOffer ? toProtoSessionDescription({
|
15623
|
-
sdp: previousOffer.sdp,
|
15624
|
-
type: previousOffer.type
|
15625
|
-
}) : undefined,
|
15626
|
-
subscription: new UpdateSubscription({
|
15627
|
-
trackSids,
|
15628
|
-
subscribe: !autoSubscribe,
|
15629
|
-
participantTracks: []
|
15630
|
-
}),
|
15631
|
-
publishTracks: getTrackPublicationInfo(localTracks),
|
15632
|
-
dataChannels: this.dataChannelsInfo(),
|
15633
|
-
trackSidsDisabled
|
15634
|
-
}));
|
15635
|
-
}
|
15636
|
-
/* @internal */
|
15637
|
-
failNext() {
|
15638
|
-
// debugging method to fail the next reconnect/resume attempt
|
15639
|
-
this.shouldFailNext = true;
|
15640
|
-
}
|
15641
|
-
dataChannelsInfo() {
|
15642
|
-
const infos = [];
|
15643
|
-
const getInfo = (dc, target) => {
|
15644
|
-
if ((dc === null || dc === void 0 ? void 0 : dc.id) !== undefined && dc.id !== null) {
|
15645
|
-
infos.push(new DataChannelInfo({
|
15646
|
-
label: dc.label,
|
15647
|
-
id: dc.id,
|
15648
|
-
target
|
15649
|
-
}));
|
15626
|
+
simulcastCodecInfo.sender = sender;
|
15627
|
+
// browser will reenable disabled codec/layers after new codec has been published,
|
15628
|
+
// so refresh subscribedCodecs after publish a new codec
|
15629
|
+
setTimeout(() => {
|
15630
|
+
if (this.subscribedCodecs) {
|
15631
|
+
this.setPublishingCodecs(this.subscribedCodecs);
|
15650
15632
|
}
|
15651
|
-
};
|
15652
|
-
getInfo(this.dataChannelForKind(DataPacket_Kind.LOSSY), SignalTarget.PUBLISHER);
|
15653
|
-
getInfo(this.dataChannelForKind(DataPacket_Kind.RELIABLE), SignalTarget.PUBLISHER);
|
15654
|
-
getInfo(this.dataChannelForKind(DataPacket_Kind.LOSSY, true), SignalTarget.SUBSCRIBER);
|
15655
|
-
getInfo(this.dataChannelForKind(DataPacket_Kind.RELIABLE, true), SignalTarget.SUBSCRIBER);
|
15656
|
-
return infos;
|
15657
|
-
}
|
15658
|
-
clearReconnectTimeout() {
|
15659
|
-
if (this.reconnectTimeout) {
|
15660
|
-
CriticalTimers.clearTimeout(this.reconnectTimeout);
|
15661
|
-
}
|
15662
|
-
}
|
15663
|
-
clearPendingReconnect() {
|
15664
|
-
this.clearReconnectTimeout();
|
15665
|
-
this.reconnectAttempts = 0;
|
15666
|
-
}
|
15667
|
-
registerOnLineListener() {
|
15668
|
-
if (isWeb()) {
|
15669
|
-
window.addEventListener('online', this.handleBrowserOnLine);
|
15670
|
-
}
|
15671
|
-
}
|
15672
|
-
deregisterOnLineListener() {
|
15673
|
-
if (isWeb()) {
|
15674
|
-
window.removeEventListener('online', this.handleBrowserOnLine);
|
15675
|
-
}
|
15676
|
-
}
|
15677
|
-
}
|
15678
|
-
class SignalReconnectError extends Error {}
|
15679
|
-
|
15680
|
-
class RegionUrlProvider {
|
15681
|
-
constructor(url, token) {
|
15682
|
-
this.lastUpdateAt = 0;
|
15683
|
-
this.settingsCacheTime = 3000;
|
15684
|
-
this.attemptedRegions = [];
|
15685
|
-
this.serverUrl = new URL(url);
|
15686
|
-
this.token = token;
|
15687
|
-
}
|
15688
|
-
updateToken(token) {
|
15689
|
-
this.token = token;
|
15690
|
-
}
|
15691
|
-
isCloud() {
|
15692
|
-
return isCloud(this.serverUrl);
|
15693
|
-
}
|
15694
|
-
getServerUrl() {
|
15695
|
-
return this.serverUrl;
|
15633
|
+
}, refreshSubscribedCodecAfterNewCodec);
|
15696
15634
|
}
|
15697
|
-
|
15635
|
+
/**
|
15636
|
+
* @internal
|
15637
|
+
* Sets codecs that should be publishing, returns new codecs that have not yet
|
15638
|
+
* been published
|
15639
|
+
*/
|
15640
|
+
setPublishingCodecs(codecs) {
|
15698
15641
|
return __awaiter(this, void 0, void 0, function* () {
|
15699
|
-
|
15700
|
-
|
15701
|
-
}
|
15702
|
-
|
15703
|
-
|
15704
|
-
}
|
15705
|
-
|
15706
|
-
if (
|
15707
|
-
|
15708
|
-
|
15709
|
-
livekitLogger.debug("next region: ".concat(nextRegion.region));
|
15710
|
-
return nextRegion.url;
|
15711
|
-
} else {
|
15712
|
-
return null;
|
15642
|
+
var _a, codecs_1, codecs_1_1;
|
15643
|
+
var _b, e_5, _c, _d;
|
15644
|
+
this.log.debug('setting publishing codecs', Object.assign(Object.assign({}, this.logContext), {
|
15645
|
+
codecs,
|
15646
|
+
currentCodec: this.codec
|
15647
|
+
}));
|
15648
|
+
// only enable simulcast codec for preference codec setted
|
15649
|
+
if (!this.codec && codecs.length > 0) {
|
15650
|
+
yield this.setPublishingLayers(codecs[0].qualities);
|
15651
|
+
return [];
|
15713
15652
|
}
|
15714
|
-
|
15715
|
-
|
15716
|
-
|
15717
|
-
|
15718
|
-
|
15719
|
-
|
15720
|
-
|
15721
|
-
|
15722
|
-
|
15723
|
-
|
15724
|
-
|
15725
|
-
|
15726
|
-
|
15727
|
-
|
15728
|
-
|
15729
|
-
|
15730
|
-
|
15731
|
-
|
15732
|
-
|
15733
|
-
|
15653
|
+
this.subscribedCodecs = codecs;
|
15654
|
+
const newCodecs = [];
|
15655
|
+
try {
|
15656
|
+
for (_a = true, codecs_1 = __asyncValues(codecs); codecs_1_1 = yield codecs_1.next(), _b = codecs_1_1.done, !_b; _a = true) {
|
15657
|
+
_d = codecs_1_1.value;
|
15658
|
+
_a = false;
|
15659
|
+
const codec = _d;
|
15660
|
+
if (!this.codec || this.codec === codec.codec) {
|
15661
|
+
yield this.setPublishingLayers(codec.qualities);
|
15662
|
+
} else {
|
15663
|
+
const simulcastCodecInfo = this.simulcastCodecs.get(codec.codec);
|
15664
|
+
this.log.debug("try setPublishingCodec for ".concat(codec.codec), Object.assign(Object.assign({}, this.logContext), {
|
15665
|
+
simulcastCodecInfo
|
15666
|
+
}));
|
15667
|
+
if (!simulcastCodecInfo || !simulcastCodecInfo.sender) {
|
15668
|
+
for (const q of codec.qualities) {
|
15669
|
+
if (q.enabled) {
|
15670
|
+
newCodecs.push(codec.codec);
|
15671
|
+
break;
|
15672
|
+
}
|
15673
|
+
}
|
15674
|
+
} else if (simulcastCodecInfo.encodings) {
|
15675
|
+
this.log.debug("try setPublishingLayersForSender ".concat(codec.codec), this.logContext);
|
15676
|
+
yield setPublishingLayersForSender(simulcastCodecInfo.sender, simulcastCodecInfo.encodings, codec.qualities, this.senderLock, this.log, this.logContext);
|
15677
|
+
}
|
15678
|
+
}
|
15679
|
+
}
|
15680
|
+
} catch (e_5_1) {
|
15681
|
+
e_5 = {
|
15682
|
+
error: e_5_1
|
15683
|
+
};
|
15684
|
+
} finally {
|
15685
|
+
try {
|
15686
|
+
if (!_a && !_b && (_c = codecs_1.return)) yield _c.call(codecs_1);
|
15687
|
+
} finally {
|
15688
|
+
if (e_5) throw e_5.error;
|
15689
|
+
}
|
15734
15690
|
}
|
15691
|
+
return newCodecs;
|
15735
15692
|
});
|
15736
15693
|
}
|
15737
|
-
}
|
15738
|
-
function getCloudConfigUrl(serverUrl) {
|
15739
|
-
return "".concat(serverUrl.protocol.replace('ws', 'http'), "//").concat(serverUrl.host, "/settings");
|
15740
|
-
}
|
15741
|
-
|
15742
|
-
const monitorFrequency = 2000;
|
15743
|
-
function computeBitrate(currentStats, prevStats) {
|
15744
|
-
if (!prevStats) {
|
15745
|
-
return 0;
|
15746
|
-
}
|
15747
|
-
let bytesNow;
|
15748
|
-
let bytesPrev;
|
15749
|
-
if ('bytesReceived' in currentStats) {
|
15750
|
-
bytesNow = currentStats.bytesReceived;
|
15751
|
-
bytesPrev = prevStats.bytesReceived;
|
15752
|
-
} else if ('bytesSent' in currentStats) {
|
15753
|
-
bytesNow = currentStats.bytesSent;
|
15754
|
-
bytesPrev = prevStats.bytesSent;
|
15755
|
-
}
|
15756
|
-
if (bytesNow === undefined || bytesPrev === undefined || currentStats.timestamp === undefined || prevStats.timestamp === undefined) {
|
15757
|
-
return 0;
|
15758
|
-
}
|
15759
|
-
return (bytesNow - bytesPrev) * 8 * 1000 / (currentStats.timestamp - prevStats.timestamp);
|
15760
|
-
}
|
15761
|
-
|
15762
|
-
class LocalAudioTrack extends LocalTrack {
|
15763
15694
|
/**
|
15764
|
-
*
|
15765
|
-
|
15766
|
-
get enhancedNoiseCancellation() {
|
15767
|
-
return this.isKrispNoiseFilterEnabled;
|
15768
|
-
}
|
15769
|
-
/**
|
15770
|
-
*
|
15771
|
-
* @param mediaTrack
|
15772
|
-
* @param constraints MediaTrackConstraints that are being used when restarting or reacquiring tracks
|
15773
|
-
* @param userProvidedTrack Signals to the SDK whether or not the mediaTrack should be managed (i.e. released and reacquired) internally by the SDK
|
15695
|
+
* @internal
|
15696
|
+
* Sets layers that should be publishing
|
15774
15697
|
*/
|
15775
|
-
|
15776
|
-
|
15777
|
-
|
15778
|
-
|
15779
|
-
|
15780
|
-
|
15781
|
-
this.stopOnMute = false;
|
15782
|
-
this.isKrispNoiseFilterEnabled = false;
|
15783
|
-
this.monitorSender = () => __awaiter(this, void 0, void 0, function* () {
|
15784
|
-
if (!this.sender) {
|
15785
|
-
this._currentBitrate = 0;
|
15786
|
-
return;
|
15787
|
-
}
|
15788
|
-
let stats;
|
15789
|
-
try {
|
15790
|
-
stats = yield this.getSenderStats();
|
15791
|
-
} catch (e) {
|
15792
|
-
this.log.error('could not get audio sender stats', Object.assign(Object.assign({}, this.logContext), {
|
15793
|
-
error: e
|
15794
|
-
}));
|
15698
|
+
setPublishingLayers(qualities) {
|
15699
|
+
return __awaiter(this, void 0, void 0, function* () {
|
15700
|
+
this.log.debug('setting publishing layers', Object.assign(Object.assign({}, this.logContext), {
|
15701
|
+
qualities
|
15702
|
+
}));
|
15703
|
+
if (!this.sender || !this.encodings) {
|
15795
15704
|
return;
|
15796
15705
|
}
|
15797
|
-
|
15798
|
-
|
15799
|
-
|
15800
|
-
|
15801
|
-
|
15802
|
-
|
15803
|
-
|
15804
|
-
this.log.debug("Krisp noise filter enabled", this.logContext);
|
15805
|
-
this.emit(TrackEvent.AudioTrackFeatureUpdate, this, AudioTrackFeature.TF_ENHANCED_NOISE_CANCELLATION, true);
|
15806
|
-
};
|
15807
|
-
this.handleKrispNoiseFilterDisable = () => {
|
15808
|
-
this.isKrispNoiseFilterEnabled = false;
|
15809
|
-
this.log.debug("Krisp noise filter disabled", this.logContext);
|
15810
|
-
this.emit(TrackEvent.AudioTrackFeatureUpdate, this, AudioTrackFeature.TF_ENHANCED_NOISE_CANCELLATION, false);
|
15811
|
-
};
|
15812
|
-
this.audioContext = audioContext;
|
15813
|
-
this.checkForSilence();
|
15814
|
-
}
|
15815
|
-
setDeviceId(deviceId) {
|
15816
|
-
return __awaiter(this, void 0, void 0, function* () {
|
15817
|
-
if (this._constraints.deviceId === deviceId && this._mediaStreamTrack.getSettings().deviceId === unwrapConstraint(deviceId)) {
|
15818
|
-
return true;
|
15819
|
-
}
|
15820
|
-
this._constraints.deviceId = deviceId;
|
15821
|
-
if (!this.isMuted) {
|
15822
|
-
yield this.restartTrack();
|
15823
|
-
}
|
15824
|
-
return this.isMuted || unwrapConstraint(deviceId) === this._mediaStreamTrack.getSettings().deviceId;
|
15825
|
-
});
|
15826
|
-
}
|
15827
|
-
mute() {
|
15828
|
-
const _super = Object.create(null, {
|
15829
|
-
mute: {
|
15830
|
-
get: () => super.mute
|
15706
|
+
yield setPublishingLayersForSender(this.sender, this.encodings, qualities, this.senderLock, this.log, this.logContext);
|
15707
|
+
});
|
15708
|
+
}
|
15709
|
+
handleAppVisibilityChanged() {
|
15710
|
+
const _super = Object.create(null, {
|
15711
|
+
handleAppVisibilityChanged: {
|
15712
|
+
get: () => super.handleAppVisibilityChanged
|
15831
15713
|
}
|
15832
15714
|
});
|
15833
15715
|
return __awaiter(this, void 0, void 0, function* () {
|
15834
|
-
|
15835
|
-
|
15836
|
-
|
15837
|
-
|
15838
|
-
return this;
|
15839
|
-
}
|
15840
|
-
// disabled special handling as it will cause BT headsets to switch communication modes
|
15841
|
-
if (this.source === Track.Source.Microphone && this.stopOnMute && !this.isUserProvided) {
|
15842
|
-
this.log.debug('stopping mic track', this.logContext);
|
15843
|
-
// also stop the track, so that microphone indicator is turned off
|
15844
|
-
this._mediaStreamTrack.stop();
|
15845
|
-
}
|
15846
|
-
yield _super.mute.call(this);
|
15847
|
-
return this;
|
15848
|
-
} finally {
|
15849
|
-
unlock();
|
15716
|
+
yield _super.handleAppVisibilityChanged.call(this);
|
15717
|
+
if (!isMobile()) return;
|
15718
|
+
if (this.isInBackground && this.source === Track.Source.Camera) {
|
15719
|
+
this._mediaStreamTrack.enabled = false;
|
15850
15720
|
}
|
15851
15721
|
});
|
15852
15722
|
}
|
15853
|
-
|
15854
|
-
|
15855
|
-
|
15856
|
-
|
15723
|
+
}
|
15724
|
+
function setPublishingLayersForSender(sender, senderEncodings, qualities, senderLock, log, logContext) {
|
15725
|
+
return __awaiter(this, void 0, void 0, function* () {
|
15726
|
+
const unlock = yield senderLock.lock();
|
15727
|
+
log.debug('setPublishingLayersForSender', Object.assign(Object.assign({}, logContext), {
|
15728
|
+
sender,
|
15729
|
+
qualities,
|
15730
|
+
senderEncodings
|
15731
|
+
}));
|
15732
|
+
try {
|
15733
|
+
const params = sender.getParameters();
|
15734
|
+
const {
|
15735
|
+
encodings
|
15736
|
+
} = params;
|
15737
|
+
if (!encodings) {
|
15738
|
+
return;
|
15857
15739
|
}
|
15858
|
-
|
15859
|
-
|
15860
|
-
|
15861
|
-
|
15862
|
-
|
15863
|
-
|
15864
|
-
return this;
|
15865
|
-
}
|
15866
|
-
const deviceHasChanged = this._constraints.deviceId && this._mediaStreamTrack.getSettings().deviceId !== unwrapConstraint(this._constraints.deviceId);
|
15867
|
-
if (this.source === Track.Source.Microphone && (this.stopOnMute || this._mediaStreamTrack.readyState === 'ended' || deviceHasChanged) && !this.isUserProvided) {
|
15868
|
-
this.log.debug('reacquiring mic track', this.logContext);
|
15869
|
-
yield this.restartTrack();
|
15870
|
-
}
|
15871
|
-
yield _super.unmute.call(this);
|
15872
|
-
return this;
|
15873
|
-
} finally {
|
15874
|
-
unlock();
|
15740
|
+
if (encodings.length !== senderEncodings.length) {
|
15741
|
+
log.warn('cannot set publishing layers, encodings mismatch', Object.assign(Object.assign({}, logContext), {
|
15742
|
+
encodings,
|
15743
|
+
senderEncodings
|
15744
|
+
}));
|
15745
|
+
return;
|
15875
15746
|
}
|
15876
|
-
|
15877
|
-
|
15878
|
-
|
15879
|
-
|
15880
|
-
|
15881
|
-
|
15882
|
-
|
15883
|
-
|
15747
|
+
let hasChanged = false;
|
15748
|
+
/* disable closable spatial layer as it has video blur / frozen issue with current server / client
|
15749
|
+
1. chrome 113: when switching to up layer with scalability Mode change, it will generate a
|
15750
|
+
low resolution frame and recover very quickly, but noticable
|
15751
|
+
2. livekit sfu: additional pli request cause video frozen for a few frames, also noticable */
|
15752
|
+
const closableSpatial = false;
|
15753
|
+
/* @ts-ignore */
|
15754
|
+
if (closableSpatial && encodings[0].scalabilityMode) ; else {
|
15755
|
+
// simulcast dynacast encodings
|
15756
|
+
encodings.forEach((encoding, idx) => {
|
15757
|
+
var _a;
|
15758
|
+
let rid = (_a = encoding.rid) !== null && _a !== void 0 ? _a : '';
|
15759
|
+
if (rid === '') {
|
15760
|
+
rid = 'q';
|
15761
|
+
}
|
15762
|
+
const quality = videoQualityForRid(rid);
|
15763
|
+
const subscribedQuality = qualities.find(q => q.quality === quality);
|
15764
|
+
if (!subscribedQuality) {
|
15765
|
+
return;
|
15766
|
+
}
|
15767
|
+
if (encoding.active !== subscribedQuality.enabled) {
|
15768
|
+
hasChanged = true;
|
15769
|
+
encoding.active = subscribedQuality.enabled;
|
15770
|
+
log.debug("setting layer ".concat(subscribedQuality.quality, " to ").concat(encoding.active ? 'enabled' : 'disabled'), logContext);
|
15771
|
+
// FireFox does not support setting encoding.active to false, so we
|
15772
|
+
// have a workaround of lowering its bitrate and resolution to the min.
|
15773
|
+
if (isFireFox()) {
|
15774
|
+
if (subscribedQuality.enabled) {
|
15775
|
+
encoding.scaleResolutionDownBy = senderEncodings[idx].scaleResolutionDownBy;
|
15776
|
+
encoding.maxBitrate = senderEncodings[idx].maxBitrate;
|
15777
|
+
/* @ts-ignore */
|
15778
|
+
encoding.maxFrameRate = senderEncodings[idx].maxFrameRate;
|
15779
|
+
} else {
|
15780
|
+
encoding.scaleResolutionDownBy = 4;
|
15781
|
+
encoding.maxBitrate = 10;
|
15782
|
+
/* @ts-ignore */
|
15783
|
+
encoding.maxFrameRate = 2;
|
15784
|
+
}
|
15785
|
+
}
|
15786
|
+
}
|
15884
15787
|
});
|
15885
|
-
if (typeof streamConstraints.audio !== 'boolean') {
|
15886
|
-
constraints = streamConstraints.audio;
|
15887
|
-
}
|
15888
15788
|
}
|
15889
|
-
|
15890
|
-
|
15891
|
-
|
15892
|
-
|
15893
|
-
|
15894
|
-
|
15895
|
-
get: () => super.restart
|
15789
|
+
if (hasChanged) {
|
15790
|
+
params.encodings = encodings;
|
15791
|
+
log.debug("setting encodings", Object.assign(Object.assign({}, logContext), {
|
15792
|
+
encodings: params.encodings
|
15793
|
+
}));
|
15794
|
+
yield sender.setParameters(params);
|
15896
15795
|
}
|
15897
|
-
}
|
15898
|
-
|
15899
|
-
const track = yield _super.restart.call(this, constraints);
|
15900
|
-
this.checkForSilence();
|
15901
|
-
return track;
|
15902
|
-
});
|
15903
|
-
}
|
15904
|
-
/* @internal */
|
15905
|
-
startMonitor() {
|
15906
|
-
if (!isWeb()) {
|
15907
|
-
return;
|
15908
|
-
}
|
15909
|
-
if (this.monitorInterval) {
|
15910
|
-
return;
|
15796
|
+
} finally {
|
15797
|
+
unlock();
|
15911
15798
|
}
|
15912
|
-
|
15913
|
-
|
15914
|
-
|
15799
|
+
});
|
15800
|
+
}
|
15801
|
+
function videoQualityForRid(rid) {
|
15802
|
+
switch (rid) {
|
15803
|
+
case 'f':
|
15804
|
+
return VideoQuality.HIGH;
|
15805
|
+
case 'h':
|
15806
|
+
return VideoQuality.MEDIUM;
|
15807
|
+
case 'q':
|
15808
|
+
return VideoQuality.LOW;
|
15809
|
+
default:
|
15810
|
+
return VideoQuality.HIGH;
|
15915
15811
|
}
|
15916
|
-
|
15917
|
-
|
15918
|
-
|
15919
|
-
|
15920
|
-
|
15921
|
-
|
15922
|
-
|
15923
|
-
|
15924
|
-
|
15925
|
-
|
15926
|
-
|
15927
|
-
const processorOptions = {
|
15928
|
-
kind: this.kind,
|
15929
|
-
track: this._mediaStreamTrack,
|
15930
|
-
audioContext: this.audioContext
|
15931
|
-
};
|
15932
|
-
this.log.debug("setting up audio processor ".concat(processor.name), this.logContext);
|
15933
|
-
yield processor.init(processorOptions);
|
15934
|
-
this.processor = processor;
|
15935
|
-
if (this.processor.processedTrack) {
|
15936
|
-
yield (_a = this.sender) === null || _a === void 0 ? void 0 : _a.replaceTrack(this.processor.processedTrack);
|
15937
|
-
this.processor.processedTrack.addEventListener('enable-lk-krisp-noise-filter', this.handleKrispNoiseFilterEnable);
|
15938
|
-
this.processor.processedTrack.addEventListener('disable-lk-krisp-noise-filter', this.handleKrispNoiseFilterDisable);
|
15939
|
-
}
|
15940
|
-
this.emit(TrackEvent.TrackProcessorUpdate, this.processor);
|
15941
|
-
} finally {
|
15942
|
-
unlock();
|
15943
|
-
}
|
15944
|
-
});
|
15812
|
+
}
|
15813
|
+
function videoLayersFromEncodings(width, height, encodings, svc) {
|
15814
|
+
// default to a single layer, HQ
|
15815
|
+
if (!encodings) {
|
15816
|
+
return [new VideoLayer({
|
15817
|
+
quality: VideoQuality.HIGH,
|
15818
|
+
width,
|
15819
|
+
height,
|
15820
|
+
bitrate: 0,
|
15821
|
+
ssrc: 0
|
15822
|
+
})];
|
15945
15823
|
}
|
15946
|
-
|
15947
|
-
|
15948
|
-
|
15949
|
-
|
15950
|
-
|
15951
|
-
|
15824
|
+
if (svc) {
|
15825
|
+
// svc layers
|
15826
|
+
/* @ts-ignore */
|
15827
|
+
const encodingSM = encodings[0].scalabilityMode;
|
15828
|
+
const sm = new ScalabilityMode(encodingSM);
|
15829
|
+
const layers = [];
|
15830
|
+
const resRatio = sm.suffix == 'h' ? 1.5 : 2;
|
15831
|
+
const bitratesRatio = sm.suffix == 'h' ? 2 : 3;
|
15832
|
+
for (let i = 0; i < sm.spatial; i += 1) {
|
15833
|
+
layers.push(new VideoLayer({
|
15834
|
+
quality: VideoQuality.HIGH - i,
|
15835
|
+
width: Math.ceil(width / Math.pow(resRatio, i)),
|
15836
|
+
height: Math.ceil(height / Math.pow(resRatio, i)),
|
15837
|
+
bitrate: encodings[0].maxBitrate ? Math.ceil(encodings[0].maxBitrate / Math.pow(bitratesRatio, i)) : 0,
|
15838
|
+
ssrc: 0
|
15839
|
+
}));
|
15840
|
+
}
|
15841
|
+
return layers;
|
15952
15842
|
}
|
15953
|
-
|
15954
|
-
|
15955
|
-
|
15956
|
-
|
15957
|
-
|
15958
|
-
|
15959
|
-
|
15960
|
-
|
15961
|
-
|
15962
|
-
|
15963
|
-
audioStats = {
|
15964
|
-
type: 'audio',
|
15965
|
-
streamId: v.id,
|
15966
|
-
packetsSent: v.packetsSent,
|
15967
|
-
packetsLost: v.packetsLost,
|
15968
|
-
bytesSent: v.bytesSent,
|
15969
|
-
timestamp: v.timestamp,
|
15970
|
-
roundTripTime: v.roundTripTime,
|
15971
|
-
jitter: v.jitter
|
15972
|
-
};
|
15973
|
-
}
|
15974
|
-
});
|
15975
|
-
return audioStats;
|
15976
|
-
});
|
15977
|
-
}
|
15978
|
-
checkForSilence() {
|
15979
|
-
return __awaiter(this, void 0, void 0, function* () {
|
15980
|
-
const trackIsSilent = yield detectSilence(this);
|
15981
|
-
if (trackIsSilent) {
|
15982
|
-
if (!this.isMuted) {
|
15983
|
-
this.log.warn('silence detected on local audio track', this.logContext);
|
15984
|
-
}
|
15985
|
-
this.emit(TrackEvent.AudioSilenceDetected);
|
15986
|
-
}
|
15987
|
-
return trackIsSilent;
|
15843
|
+
return encodings.map(encoding => {
|
15844
|
+
var _a, _b, _c;
|
15845
|
+
const scale = (_a = encoding.scaleResolutionDownBy) !== null && _a !== void 0 ? _a : 1;
|
15846
|
+
let quality = videoQualityForRid((_b = encoding.rid) !== null && _b !== void 0 ? _b : '');
|
15847
|
+
return new VideoLayer({
|
15848
|
+
quality,
|
15849
|
+
width: Math.ceil(width / scale),
|
15850
|
+
height: Math.ceil(height / scale),
|
15851
|
+
bitrate: (_c = encoding.maxBitrate) !== null && _c !== void 0 ? _c : 0,
|
15852
|
+
ssrc: 0
|
15988
15853
|
});
|
15989
|
-
}
|
15854
|
+
});
|
15990
15855
|
}
|
15991
15856
|
|
15857
|
+
const lossyDataChannel = '_lossy';
|
15858
|
+
const reliableDataChannel = '_reliable';
|
15859
|
+
const minReconnectWait = 2 * 1000;
|
15860
|
+
const leaveReconnect = 'leave-reconnect';
|
15861
|
+
var PCState;
|
15862
|
+
(function (PCState) {
|
15863
|
+
PCState[PCState["New"] = 0] = "New";
|
15864
|
+
PCState[PCState["Connected"] = 1] = "Connected";
|
15865
|
+
PCState[PCState["Disconnected"] = 2] = "Disconnected";
|
15866
|
+
PCState[PCState["Reconnecting"] = 3] = "Reconnecting";
|
15867
|
+
PCState[PCState["Closed"] = 4] = "Closed";
|
15868
|
+
})(PCState || (PCState = {}));
|
15992
15869
|
/** @internal */
|
15993
|
-
|
15994
|
-
|
15995
|
-
|
15996
|
-
return new LocalAudioTrack(mediaStreamTrack, constraints, false, undefined, loggerOptions);
|
15997
|
-
case 'video':
|
15998
|
-
return new LocalVideoTrack(mediaStreamTrack, constraints, false, loggerOptions);
|
15999
|
-
default:
|
16000
|
-
throw new TrackInvalidError("unsupported track type: ".concat(mediaStreamTrack.kind));
|
16001
|
-
}
|
16002
|
-
}
|
16003
|
-
/* @internal */
|
16004
|
-
const presets169 = Object.values(VideoPresets);
|
16005
|
-
/* @internal */
|
16006
|
-
const presets43 = Object.values(VideoPresets43);
|
16007
|
-
/* @internal */
|
16008
|
-
const presetsScreenShare = Object.values(ScreenSharePresets);
|
16009
|
-
/* @internal */
|
16010
|
-
const defaultSimulcastPresets169 = [VideoPresets.h180, VideoPresets.h360];
|
16011
|
-
/* @internal */
|
16012
|
-
const defaultSimulcastPresets43 = [VideoPresets43.h180, VideoPresets43.h360];
|
16013
|
-
/* @internal */
|
16014
|
-
const computeDefaultScreenShareSimulcastPresets = fromPreset => {
|
16015
|
-
const layers = [{
|
16016
|
-
scaleResolutionDownBy: 2,
|
16017
|
-
fps: fromPreset.encoding.maxFramerate
|
16018
|
-
}];
|
16019
|
-
return layers.map(t => {
|
16020
|
-
var _a, _b;
|
16021
|
-
return new VideoPreset(Math.floor(fromPreset.width / t.scaleResolutionDownBy), Math.floor(fromPreset.height / t.scaleResolutionDownBy), Math.max(150000, Math.floor(fromPreset.encoding.maxBitrate / (Math.pow(t.scaleResolutionDownBy, 2) * (((_a = fromPreset.encoding.maxFramerate) !== null && _a !== void 0 ? _a : 30) / ((_b = t.fps) !== null && _b !== void 0 ? _b : 30))))), t.fps, fromPreset.encoding.priority);
|
16022
|
-
});
|
16023
|
-
};
|
16024
|
-
// /**
|
16025
|
-
// *
|
16026
|
-
// * @internal
|
16027
|
-
// * @experimental
|
16028
|
-
// */
|
16029
|
-
// const computeDefaultMultiCodecSimulcastEncodings = (width: number, height: number) => {
|
16030
|
-
// // use vp8 as a default
|
16031
|
-
// const vp8 = determineAppropriateEncoding(false, width, height);
|
16032
|
-
// const vp9 = { ...vp8, maxBitrate: vp8.maxBitrate * 0.9 };
|
16033
|
-
// const h264 = { ...vp8, maxBitrate: vp8.maxBitrate * 1.1 };
|
16034
|
-
// const av1 = { ...vp8, maxBitrate: vp8.maxBitrate * 0.7 };
|
16035
|
-
// return {
|
16036
|
-
// vp8,
|
16037
|
-
// vp9,
|
16038
|
-
// h264,
|
16039
|
-
// av1,
|
16040
|
-
// };
|
16041
|
-
// };
|
16042
|
-
const videoRids = ['q', 'h', 'f'];
|
16043
|
-
/* @internal */
|
16044
|
-
function computeVideoEncodings(isScreenShare, width, height, options) {
|
16045
|
-
var _a, _b;
|
16046
|
-
let videoEncoding = options === null || options === void 0 ? void 0 : options.videoEncoding;
|
16047
|
-
if (isScreenShare) {
|
16048
|
-
videoEncoding = options === null || options === void 0 ? void 0 : options.screenShareEncoding;
|
16049
|
-
}
|
16050
|
-
const useSimulcast = options === null || options === void 0 ? void 0 : options.simulcast;
|
16051
|
-
const scalabilityMode = options === null || options === void 0 ? void 0 : options.scalabilityMode;
|
16052
|
-
const videoCodec = options === null || options === void 0 ? void 0 : options.videoCodec;
|
16053
|
-
if (!videoEncoding && !useSimulcast && !scalabilityMode || !width || !height) {
|
16054
|
-
// when we aren't simulcasting or svc, will need to return a single encoding without
|
16055
|
-
// capping bandwidth. we always require a encoding for dynacast
|
16056
|
-
return [{}];
|
15870
|
+
class RTCEngine extends eventsExports.EventEmitter {
|
15871
|
+
get isClosed() {
|
15872
|
+
return this._isClosed;
|
16057
15873
|
}
|
16058
|
-
|
16059
|
-
|
16060
|
-
videoEncoding = determineAppropriateEncoding(isScreenShare, width, height, videoCodec);
|
16061
|
-
livekitLogger.debug('using video encoding', videoEncoding);
|
15874
|
+
get pendingReconnect() {
|
15875
|
+
return !!this.reconnectTimeout;
|
16062
15876
|
}
|
16063
|
-
|
16064
|
-
|
16065
|
-
|
16066
|
-
|
16067
|
-
|
16068
|
-
|
16069
|
-
|
16070
|
-
|
16071
|
-
|
16072
|
-
|
16073
|
-
|
16074
|
-
|
16075
|
-
|
16076
|
-
|
16077
|
-
|
16078
|
-
|
16079
|
-
|
16080
|
-
|
16081
|
-
|
16082
|
-
|
16083
|
-
|
16084
|
-
|
15877
|
+
constructor(options) {
|
15878
|
+
var _a;
|
15879
|
+
super();
|
15880
|
+
this.options = options;
|
15881
|
+
this.rtcConfig = {};
|
15882
|
+
this.peerConnectionTimeout = roomConnectOptionDefaults.peerConnectionTimeout;
|
15883
|
+
this.fullReconnectOnNext = false;
|
15884
|
+
this.subscriberPrimary = false;
|
15885
|
+
this.pcState = PCState.New;
|
15886
|
+
this._isClosed = true;
|
15887
|
+
this.pendingTrackResolvers = {};
|
15888
|
+
this.reconnectAttempts = 0;
|
15889
|
+
this.reconnectStart = 0;
|
15890
|
+
this.attemptingReconnect = false;
|
15891
|
+
/** keeps track of how often an initial join connection has been tried */
|
15892
|
+
this.joinAttempts = 0;
|
15893
|
+
/** specifies how often an initial join connection is allowed to retry */
|
15894
|
+
this.maxJoinAttempts = 1;
|
15895
|
+
this.shouldFailNext = false;
|
15896
|
+
this.log = livekitLogger;
|
15897
|
+
this.handleDataChannel = _b => __awaiter(this, [_b], void 0, function (_ref) {
|
15898
|
+
var _this = this;
|
15899
|
+
let {
|
15900
|
+
channel
|
15901
|
+
} = _ref;
|
15902
|
+
return function* () {
|
15903
|
+
if (!channel) {
|
15904
|
+
return;
|
15905
|
+
}
|
15906
|
+
if (channel.label === reliableDataChannel) {
|
15907
|
+
_this.reliableDCSub = channel;
|
15908
|
+
} else if (channel.label === lossyDataChannel) {
|
15909
|
+
_this.lossyDCSub = channel;
|
15910
|
+
} else {
|
15911
|
+
return;
|
15912
|
+
}
|
15913
|
+
_this.log.debug("on data channel ".concat(channel.id, ", ").concat(channel.label), _this.logContext);
|
15914
|
+
channel.onmessage = _this.handleDataMessage;
|
15915
|
+
}();
|
15916
|
+
});
|
15917
|
+
this.handleDataMessage = message => __awaiter(this, void 0, void 0, function* () {
|
15918
|
+
var _c, _d, _e;
|
15919
|
+
// make sure to respect incoming data message order by processing message events one after the other
|
15920
|
+
const unlock = yield this.dataProcessLock.lock();
|
15921
|
+
try {
|
15922
|
+
// decode
|
15923
|
+
let buffer;
|
15924
|
+
if (message.data instanceof ArrayBuffer) {
|
15925
|
+
buffer = message.data;
|
15926
|
+
} else if (message.data instanceof Blob) {
|
15927
|
+
buffer = yield message.data.arrayBuffer();
|
15928
|
+
} else {
|
15929
|
+
this.log.error('unsupported data type', Object.assign(Object.assign({}, this.logContext), {
|
15930
|
+
data: message.data
|
15931
|
+
}));
|
15932
|
+
return;
|
15933
|
+
}
|
15934
|
+
const dp = DataPacket.fromBinary(new Uint8Array(buffer));
|
15935
|
+
if (((_c = dp.value) === null || _c === void 0 ? void 0 : _c.case) === 'speaker') {
|
15936
|
+
// dispatch speaker updates
|
15937
|
+
this.emit(EngineEvent.ActiveSpeakersUpdate, dp.value.value.speakers);
|
15938
|
+
} else if (((_d = dp.value) === null || _d === void 0 ? void 0 : _d.case) === 'user') {
|
15939
|
+
this.emit(EngineEvent.DataPacketReceived, dp.value.value, dp.kind);
|
15940
|
+
} else if (((_e = dp.value) === null || _e === void 0 ? void 0 : _e.case) === 'transcription') {
|
15941
|
+
this.emit(EngineEvent.TranscriptionReceived, dp.value.value);
|
15942
|
+
}
|
15943
|
+
} finally {
|
15944
|
+
unlock();
|
16085
15945
|
}
|
16086
|
-
// legacy SVC, scalabilityMode is set only on the first encoding
|
16087
|
-
/* @ts-ignore */
|
16088
|
-
encodings[0].scalabilityMode = scalabilityMode;
|
16089
|
-
} else {
|
16090
|
-
encodings.push({
|
16091
|
-
maxBitrate: videoEncoding.maxBitrate,
|
16092
|
-
maxFramerate: original.encoding.maxFramerate,
|
16093
|
-
/* @ts-ignore */
|
16094
|
-
scalabilityMode: scalabilityMode
|
16095
|
-
});
|
16096
|
-
}
|
16097
|
-
livekitLogger.debug("using svc encoding", {
|
16098
|
-
encodings
|
16099
15946
|
});
|
16100
|
-
|
16101
|
-
|
16102
|
-
|
16103
|
-
|
16104
|
-
|
16105
|
-
|
16106
|
-
|
16107
|
-
|
16108
|
-
|
16109
|
-
|
16110
|
-
|
16111
|
-
|
16112
|
-
|
16113
|
-
|
16114
|
-
|
16115
|
-
|
16116
|
-
|
16117
|
-
|
16118
|
-
|
16119
|
-
|
16120
|
-
|
16121
|
-
//
|
16122
|
-
//
|
16123
|
-
//
|
16124
|
-
|
16125
|
-
|
16126
|
-
|
16127
|
-
|
16128
|
-
|
16129
|
-
|
16130
|
-
|
16131
|
-
|
16132
|
-
|
15947
|
+
this.handleDataError = event => {
|
15948
|
+
const channel = event.currentTarget;
|
15949
|
+
const channelKind = channel.maxRetransmits === 0 ? 'lossy' : 'reliable';
|
15950
|
+
if (event instanceof ErrorEvent && event.error) {
|
15951
|
+
const {
|
15952
|
+
error
|
15953
|
+
} = event.error;
|
15954
|
+
this.log.error("DataChannel error on ".concat(channelKind, ": ").concat(event.message), Object.assign(Object.assign({}, this.logContext), {
|
15955
|
+
error
|
15956
|
+
}));
|
15957
|
+
} else {
|
15958
|
+
this.log.error("Unknown DataChannel error on ".concat(channelKind), Object.assign(Object.assign({}, this.logContext), {
|
15959
|
+
event
|
15960
|
+
}));
|
15961
|
+
}
|
15962
|
+
};
|
15963
|
+
this.handleBufferedAmountLow = event => {
|
15964
|
+
const channel = event.currentTarget;
|
15965
|
+
const channelKind = channel.maxRetransmits === 0 ? DataPacket_Kind.LOSSY : DataPacket_Kind.RELIABLE;
|
15966
|
+
this.updateAndEmitDCBufferStatus(channelKind);
|
15967
|
+
};
|
15968
|
+
// websocket reconnect behavior. if websocket is interrupted, and the PeerConnection
|
15969
|
+
// continues to work, we can reconnect to websocket to continue the session
|
15970
|
+
// after a number of retries, we'll close and give up permanently
|
15971
|
+
this.handleDisconnect = (connection, disconnectReason) => {
|
15972
|
+
if (this._isClosed) {
|
15973
|
+
return;
|
15974
|
+
}
|
15975
|
+
this.log.warn("".concat(connection, " disconnected"), this.logContext);
|
15976
|
+
if (this.reconnectAttempts === 0) {
|
15977
|
+
// only reset start time on the first try
|
15978
|
+
this.reconnectStart = Date.now();
|
15979
|
+
}
|
15980
|
+
const disconnect = duration => {
|
15981
|
+
this.log.warn("could not recover connection after ".concat(this.reconnectAttempts, " attempts, ").concat(duration, "ms. giving up"), this.logContext);
|
15982
|
+
this.emit(EngineEvent.Disconnected);
|
15983
|
+
this.close();
|
15984
|
+
};
|
15985
|
+
const duration = Date.now() - this.reconnectStart;
|
15986
|
+
let delay = this.getNextRetryDelay({
|
15987
|
+
elapsedMs: duration,
|
15988
|
+
retryCount: this.reconnectAttempts
|
15989
|
+
});
|
15990
|
+
if (delay === null) {
|
15991
|
+
disconnect(duration);
|
15992
|
+
return;
|
15993
|
+
}
|
15994
|
+
if (connection === leaveReconnect) {
|
15995
|
+
delay = 0;
|
15996
|
+
}
|
15997
|
+
this.log.debug("reconnecting in ".concat(delay, "ms"), this.logContext);
|
15998
|
+
this.clearReconnectTimeout();
|
15999
|
+
if (this.token && this.regionUrlProvider) {
|
16000
|
+
// token may have been refreshed, we do not want to recreate the regionUrlProvider
|
16001
|
+
// since the current engine may have inherited a regional url
|
16002
|
+
this.regionUrlProvider.updateToken(this.token);
|
16003
|
+
}
|
16004
|
+
this.reconnectTimeout = CriticalTimers.setTimeout(() => this.attemptReconnect(disconnectReason).finally(() => this.reconnectTimeout = undefined), delay);
|
16005
|
+
};
|
16006
|
+
this.waitForRestarted = () => {
|
16007
|
+
return new Promise((resolve, reject) => {
|
16008
|
+
if (this.pcState === PCState.Connected) {
|
16009
|
+
resolve();
|
16010
|
+
}
|
16011
|
+
const onRestarted = () => {
|
16012
|
+
this.off(EngineEvent.Disconnected, onDisconnected);
|
16013
|
+
resolve();
|
16014
|
+
};
|
16015
|
+
const onDisconnected = () => {
|
16016
|
+
this.off(EngineEvent.Restarted, onRestarted);
|
16017
|
+
reject();
|
16018
|
+
};
|
16019
|
+
this.once(EngineEvent.Restarted, onRestarted);
|
16020
|
+
this.once(EngineEvent.Disconnected, onDisconnected);
|
16021
|
+
});
|
16022
|
+
};
|
16023
|
+
this.updateAndEmitDCBufferStatus = kind => {
|
16024
|
+
const status = this.isBufferStatusLow(kind);
|
16025
|
+
if (typeof status !== 'undefined' && status !== this.dcBufferStatus.get(kind)) {
|
16026
|
+
this.dcBufferStatus.set(kind, status);
|
16027
|
+
this.emit(EngineEvent.DCBufferStatusChanged, status, kind);
|
16028
|
+
}
|
16029
|
+
};
|
16030
|
+
this.isBufferStatusLow = kind => {
|
16031
|
+
const dc = this.dataChannelForKind(kind);
|
16032
|
+
if (dc) {
|
16033
|
+
return dc.bufferedAmount <= dc.bufferedAmountLowThreshold;
|
16034
|
+
}
|
16035
|
+
};
|
16036
|
+
this.handleBrowserOnLine = () => {
|
16037
|
+
// in case the engine is currently reconnecting, attempt a reconnect immediately after the browser state has changed to 'onLine'
|
16038
|
+
if (this.client.currentState === SignalConnectionState.RECONNECTING) {
|
16039
|
+
this.clearReconnectTimeout();
|
16040
|
+
this.attemptReconnect(ReconnectReason.RR_SIGNAL_DISCONNECTED);
|
16041
|
+
}
|
16042
|
+
};
|
16043
|
+
this.log = getLogger((_a = options.loggerName) !== null && _a !== void 0 ? _a : LoggerNames.Engine);
|
16044
|
+
this.loggerOptions = {
|
16045
|
+
loggerName: options.loggerName,
|
16046
|
+
loggerContextCb: () => this.logContext
|
16047
|
+
};
|
16048
|
+
this.client = new SignalClient(undefined, this.loggerOptions);
|
16049
|
+
this.client.signalLatency = this.options.expSignalLatency;
|
16050
|
+
this.reconnectPolicy = this.options.reconnectPolicy;
|
16051
|
+
this.registerOnLineListener();
|
16052
|
+
this.closingLock = new Mutex();
|
16053
|
+
this.dataProcessLock = new Mutex();
|
16054
|
+
this.dcBufferStatus = new Map([[DataPacket_Kind.LOSSY, true], [DataPacket_Kind.RELIABLE, true]]);
|
16055
|
+
this.client.onParticipantUpdate = updates => this.emit(EngineEvent.ParticipantUpdate, updates);
|
16056
|
+
this.client.onConnectionQuality = update => this.emit(EngineEvent.ConnectionQualityUpdate, update);
|
16057
|
+
this.client.onRoomUpdate = update => this.emit(EngineEvent.RoomUpdate, update);
|
16058
|
+
this.client.onSubscriptionError = resp => this.emit(EngineEvent.SubscriptionError, resp);
|
16059
|
+
this.client.onSubscriptionPermissionUpdate = update => this.emit(EngineEvent.SubscriptionPermissionUpdate, update);
|
16060
|
+
this.client.onSpeakersChanged = update => this.emit(EngineEvent.SpeakersChanged, update);
|
16061
|
+
this.client.onStreamStateUpdate = update => this.emit(EngineEvent.StreamStateChanged, update);
|
16133
16062
|
}
|
16134
|
-
|
16135
|
-
|
16136
|
-
|
16137
|
-
|
16138
|
-
|
16139
|
-
|
16140
|
-
|
16141
|
-
|
16063
|
+
/** @internal */
|
16064
|
+
get logContext() {
|
16065
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
16066
|
+
return {
|
16067
|
+
room: (_b = (_a = this.latestJoinResponse) === null || _a === void 0 ? void 0 : _a.room) === null || _b === void 0 ? void 0 : _b.name,
|
16068
|
+
roomID: (_d = (_c = this.latestJoinResponse) === null || _c === void 0 ? void 0 : _c.room) === null || _d === void 0 ? void 0 : _d.sid,
|
16069
|
+
participant: (_f = (_e = this.latestJoinResponse) === null || _e === void 0 ? void 0 : _e.participant) === null || _f === void 0 ? void 0 : _f.identity,
|
16070
|
+
pID: (_h = (_g = this.latestJoinResponse) === null || _g === void 0 ? void 0 : _g.participant) === null || _h === void 0 ? void 0 : _h.sid
|
16071
|
+
};
|
16142
16072
|
}
|
16143
|
-
|
16144
|
-
|
16145
|
-
|
16146
|
-
|
16073
|
+
join(url, token, opts, abortSignal) {
|
16074
|
+
return __awaiter(this, void 0, void 0, function* () {
|
16075
|
+
this.url = url;
|
16076
|
+
this.token = token;
|
16077
|
+
this.signalOpts = opts;
|
16078
|
+
this.maxJoinAttempts = opts.maxRetries;
|
16079
|
+
try {
|
16080
|
+
this.joinAttempts += 1;
|
16081
|
+
this.setupSignalClientCallbacks();
|
16082
|
+
const joinResponse = yield this.client.join(url, token, opts, abortSignal);
|
16083
|
+
this._isClosed = false;
|
16084
|
+
this.latestJoinResponse = joinResponse;
|
16085
|
+
this.subscriberPrimary = joinResponse.subscriberPrimary;
|
16086
|
+
if (!this.pcManager) {
|
16087
|
+
yield this.configure(joinResponse);
|
16088
|
+
}
|
16089
|
+
// create offer
|
16090
|
+
if (!this.subscriberPrimary) {
|
16091
|
+
this.negotiate();
|
16092
|
+
}
|
16093
|
+
this.clientConfiguration = joinResponse.clientConfiguration;
|
16094
|
+
return joinResponse;
|
16095
|
+
} catch (e) {
|
16096
|
+
if (e instanceof ConnectionError) {
|
16097
|
+
if (e.reason === 1 /* ConnectionErrorReason.ServerUnreachable */) {
|
16098
|
+
this.log.warn("Couldn't connect to server, attempt ".concat(this.joinAttempts, " of ").concat(this.maxJoinAttempts), this.logContext);
|
16099
|
+
if (this.joinAttempts < this.maxJoinAttempts) {
|
16100
|
+
return this.join(url, token, opts, abortSignal);
|
16101
|
+
}
|
16102
|
+
}
|
16103
|
+
}
|
16104
|
+
throw e;
|
16105
|
+
}
|
16147
16106
|
});
|
16148
16107
|
}
|
16149
|
-
|
16150
|
-
|
16151
|
-
|
16152
|
-
|
16153
|
-
|
16154
|
-
|
16155
|
-
|
16156
|
-
|
16157
|
-
|
16158
|
-
|
16159
|
-
|
16160
|
-
|
16161
|
-
|
16162
|
-
|
16163
|
-
|
16164
|
-
|
16165
|
-
|
16166
|
-
for (let i = 0; i < presets.length; i += 1) {
|
16167
|
-
const preset = presets[i];
|
16168
|
-
encoding = preset.encoding;
|
16169
|
-
if (preset.width >= size) {
|
16170
|
-
break;
|
16171
|
-
}
|
16172
|
-
}
|
16173
|
-
// presets are based on the assumption of vp8 as a codec
|
16174
|
-
// for other codecs we adjust the maxBitrate if no specific videoEncoding has been provided
|
16175
|
-
// users should override these with ones that are optimized for their use case
|
16176
|
-
// NOTE: SVC codec bitrates are inclusive of all scalability layers. while
|
16177
|
-
// bitrate for non-SVC codecs does not include other simulcast layers.
|
16178
|
-
if (codec) {
|
16179
|
-
switch (codec) {
|
16180
|
-
case 'av1':
|
16181
|
-
encoding = Object.assign({}, encoding);
|
16182
|
-
encoding.maxBitrate = encoding.maxBitrate * 0.7;
|
16183
|
-
break;
|
16184
|
-
case 'vp9':
|
16185
|
-
encoding = Object.assign({}, encoding);
|
16186
|
-
encoding.maxBitrate = encoding.maxBitrate * 0.85;
|
16187
|
-
break;
|
16188
|
-
}
|
16189
|
-
}
|
16190
|
-
return encoding;
|
16191
|
-
}
|
16192
|
-
/* @internal */
|
16193
|
-
function presetsForResolution(isScreenShare, width, height) {
|
16194
|
-
if (isScreenShare) {
|
16195
|
-
return presetsScreenShare;
|
16196
|
-
}
|
16197
|
-
const aspect = width > height ? width / height : height / width;
|
16198
|
-
if (Math.abs(aspect - 16.0 / 9) < Math.abs(aspect - 4.0 / 3)) {
|
16199
|
-
return presets169;
|
16200
|
-
}
|
16201
|
-
return presets43;
|
16202
|
-
}
|
16203
|
-
/* @internal */
|
16204
|
-
function defaultSimulcastLayers(isScreenShare, original) {
|
16205
|
-
if (isScreenShare) {
|
16206
|
-
return computeDefaultScreenShareSimulcastPresets(original);
|
16207
|
-
}
|
16208
|
-
const {
|
16209
|
-
width,
|
16210
|
-
height
|
16211
|
-
} = original;
|
16212
|
-
const aspect = width > height ? width / height : height / width;
|
16213
|
-
if (Math.abs(aspect - 16.0 / 9) < Math.abs(aspect - 4.0 / 3)) {
|
16214
|
-
return defaultSimulcastPresets169;
|
16215
|
-
}
|
16216
|
-
return defaultSimulcastPresets43;
|
16217
|
-
}
|
16218
|
-
// presets should be ordered by low, medium, high
|
16219
|
-
function encodingsFromPresets(width, height, presets) {
|
16220
|
-
const encodings = [];
|
16221
|
-
presets.forEach((preset, idx) => {
|
16222
|
-
if (idx >= videoRids.length) {
|
16223
|
-
return;
|
16224
|
-
}
|
16225
|
-
const size = Math.min(width, height);
|
16226
|
-
const rid = videoRids[idx];
|
16227
|
-
const encoding = {
|
16228
|
-
rid,
|
16229
|
-
scaleResolutionDownBy: Math.max(1, size / Math.min(preset.width, preset.height)),
|
16230
|
-
maxBitrate: preset.encoding.maxBitrate
|
16231
|
-
};
|
16232
|
-
if (preset.encoding.maxFramerate) {
|
16233
|
-
encoding.maxFramerate = preset.encoding.maxFramerate;
|
16234
|
-
}
|
16235
|
-
const canSetPriority = isFireFox() || idx === 0;
|
16236
|
-
if (preset.encoding.priority && canSetPriority) {
|
16237
|
-
encoding.priority = preset.encoding.priority;
|
16238
|
-
encoding.networkPriority = preset.encoding.priority;
|
16239
|
-
}
|
16240
|
-
encodings.push(encoding);
|
16241
|
-
});
|
16242
|
-
// RN ios simulcast requires all same framerates.
|
16243
|
-
if (isReactNative() && getReactNativeOs() === 'ios') {
|
16244
|
-
let topFramerate = undefined;
|
16245
|
-
encodings.forEach(encoding => {
|
16246
|
-
if (!topFramerate) {
|
16247
|
-
topFramerate = encoding.maxFramerate;
|
16248
|
-
} else if (encoding.maxFramerate && encoding.maxFramerate > topFramerate) {
|
16249
|
-
topFramerate = encoding.maxFramerate;
|
16108
|
+
close() {
|
16109
|
+
return __awaiter(this, void 0, void 0, function* () {
|
16110
|
+
const unlock = yield this.closingLock.lock();
|
16111
|
+
if (this.isClosed) {
|
16112
|
+
unlock();
|
16113
|
+
return;
|
16114
|
+
}
|
16115
|
+
try {
|
16116
|
+
this._isClosed = true;
|
16117
|
+
this.emit(EngineEvent.Closing);
|
16118
|
+
this.removeAllListeners();
|
16119
|
+
this.deregisterOnLineListener();
|
16120
|
+
this.clearPendingReconnect();
|
16121
|
+
yield this.cleanupPeerConnections();
|
16122
|
+
yield this.cleanupClient();
|
16123
|
+
} finally {
|
16124
|
+
unlock();
|
16250
16125
|
}
|
16251
16126
|
});
|
16252
|
-
|
16253
|
-
|
16254
|
-
|
16255
|
-
|
16256
|
-
|
16257
|
-
|
16258
|
-
|
16127
|
+
}
|
16128
|
+
cleanupPeerConnections() {
|
16129
|
+
return __awaiter(this, void 0, void 0, function* () {
|
16130
|
+
var _a;
|
16131
|
+
yield (_a = this.pcManager) === null || _a === void 0 ? void 0 : _a.close();
|
16132
|
+
this.pcManager = undefined;
|
16133
|
+
const dcCleanup = dc => {
|
16134
|
+
if (!dc) return;
|
16135
|
+
dc.close();
|
16136
|
+
dc.onbufferedamountlow = null;
|
16137
|
+
dc.onclose = null;
|
16138
|
+
dc.onclosing = null;
|
16139
|
+
dc.onerror = null;
|
16140
|
+
dc.onmessage = null;
|
16141
|
+
dc.onopen = null;
|
16142
|
+
};
|
16143
|
+
dcCleanup(this.lossyDC);
|
16144
|
+
dcCleanup(this.lossyDCSub);
|
16145
|
+
dcCleanup(this.reliableDC);
|
16146
|
+
dcCleanup(this.reliableDCSub);
|
16147
|
+
this.lossyDC = undefined;
|
16148
|
+
this.lossyDCSub = undefined;
|
16149
|
+
this.reliableDC = undefined;
|
16150
|
+
this.reliableDCSub = undefined;
|
16151
|
+
});
|
16152
|
+
}
|
16153
|
+
cleanupClient() {
|
16154
|
+
return __awaiter(this, void 0, void 0, function* () {
|
16155
|
+
yield this.client.close();
|
16156
|
+
this.client.resetCallbacks();
|
16157
|
+
});
|
16158
|
+
}
|
16159
|
+
addTrack(req) {
|
16160
|
+
if (this.pendingTrackResolvers[req.cid]) {
|
16161
|
+
throw new TrackInvalidError('a track with the same ID has already been published');
|
16162
|
+
}
|
16163
|
+
return new Promise((resolve, reject) => {
|
16164
|
+
const publicationTimeout = setTimeout(() => {
|
16165
|
+
delete this.pendingTrackResolvers[req.cid];
|
16166
|
+
reject(new ConnectionError('publication of local track timed out, no response from server'));
|
16167
|
+
}, 10000);
|
16168
|
+
this.pendingTrackResolvers[req.cid] = {
|
16169
|
+
resolve: info => {
|
16170
|
+
clearTimeout(publicationTimeout);
|
16171
|
+
resolve(info);
|
16172
|
+
},
|
16173
|
+
reject: () => {
|
16174
|
+
clearTimeout(publicationTimeout);
|
16175
|
+
reject(new Error('Cancelled publication by calling unpublish'));
|
16259
16176
|
}
|
16260
|
-
|
16261
|
-
|
16262
|
-
}
|
16177
|
+
};
|
16178
|
+
this.client.sendAddTrack(req);
|
16263
16179
|
});
|
16264
16180
|
}
|
16265
|
-
|
16266
|
-
|
16267
|
-
|
16268
|
-
|
16269
|
-
|
16270
|
-
|
16271
|
-
|
16272
|
-
|
16273
|
-
|
16274
|
-
|
16275
|
-
|
16276
|
-
|
16277
|
-
|
16278
|
-
return 1;
|
16279
|
-
}
|
16280
|
-
if (aEnc.maxBitrate < bEnc.maxBitrate) return -1;
|
16281
|
-
if (aEnc.maxBitrate === bEnc.maxBitrate && aEnc.maxFramerate && bEnc.maxFramerate) {
|
16282
|
-
return aEnc.maxFramerate > bEnc.maxFramerate ? 1 : -1;
|
16283
|
-
}
|
16284
|
-
return 0;
|
16285
|
-
});
|
16286
|
-
}
|
16287
|
-
/** @internal */
|
16288
|
-
class ScalabilityMode {
|
16289
|
-
constructor(scalabilityMode) {
|
16290
|
-
const results = scalabilityMode.match(/^L(\d)T(\d)(h|_KEY|_KEY_SHIFT){0,1}$/);
|
16291
|
-
if (!results) {
|
16292
|
-
throw new Error('invalid scalability mode');
|
16293
|
-
}
|
16294
|
-
this.spatial = parseInt(results[1]);
|
16295
|
-
this.temporal = parseInt(results[2]);
|
16296
|
-
if (results.length > 3) {
|
16297
|
-
switch (results[3]) {
|
16298
|
-
case 'h':
|
16299
|
-
case '_KEY':
|
16300
|
-
case '_KEY_SHIFT':
|
16301
|
-
this.suffix = results[3];
|
16181
|
+
/**
|
16182
|
+
* Removes sender from PeerConnection, returning true if it was removed successfully
|
16183
|
+
* and a negotiation is necessary
|
16184
|
+
* @param sender
|
16185
|
+
* @returns
|
16186
|
+
*/
|
16187
|
+
removeTrack(sender) {
|
16188
|
+
if (sender.track && this.pendingTrackResolvers[sender.track.id]) {
|
16189
|
+
const {
|
16190
|
+
reject
|
16191
|
+
} = this.pendingTrackResolvers[sender.track.id];
|
16192
|
+
if (reject) {
|
16193
|
+
reject();
|
16302
16194
|
}
|
16195
|
+
delete this.pendingTrackResolvers[sender.track.id];
|
16196
|
+
}
|
16197
|
+
try {
|
16198
|
+
this.pcManager.removeTrack(sender);
|
16199
|
+
return true;
|
16200
|
+
} catch (e) {
|
16201
|
+
this.log.warn('failed to remove track', Object.assign(Object.assign({}, this.logContext), {
|
16202
|
+
error: e
|
16203
|
+
}));
|
16303
16204
|
}
|
16205
|
+
return false;
|
16304
16206
|
}
|
16305
|
-
|
16207
|
+
updateMuteStatus(trackSid, muted) {
|
16208
|
+
this.client.sendMuteTrack(trackSid, muted);
|
16209
|
+
}
|
16210
|
+
get dataSubscriberReadyState() {
|
16306
16211
|
var _a;
|
16307
|
-
return
|
16212
|
+
return (_a = this.reliableDCSub) === null || _a === void 0 ? void 0 : _a.readyState;
|
16308
16213
|
}
|
16309
|
-
|
16310
|
-
|
16311
|
-
|
16312
|
-
|
16313
|
-
|
16314
|
-
|
16315
|
-
|
16316
|
-
|
16317
|
-
|
16318
|
-
|
16319
|
-
|
16320
|
-
|
16321
|
-
|
16322
|
-
|
16323
|
-
|
16324
|
-
this.simulcastCodecs = new Map();
|
16325
|
-
this.monitorSender = () => __awaiter(this, void 0, void 0, function* () {
|
16326
|
-
if (!this.sender) {
|
16327
|
-
this._currentBitrate = 0;
|
16214
|
+
getConnectedServerAddress() {
|
16215
|
+
return __awaiter(this, void 0, void 0, function* () {
|
16216
|
+
var _a;
|
16217
|
+
return (_a = this.pcManager) === null || _a === void 0 ? void 0 : _a.getConnectedAddress();
|
16218
|
+
});
|
16219
|
+
}
|
16220
|
+
/* @internal */
|
16221
|
+
setRegionUrlProvider(provider) {
|
16222
|
+
this.regionUrlProvider = provider;
|
16223
|
+
}
|
16224
|
+
configure(joinResponse) {
|
16225
|
+
return __awaiter(this, void 0, void 0, function* () {
|
16226
|
+
var _a;
|
16227
|
+
// already configured
|
16228
|
+
if (this.pcManager && this.pcManager.currentState !== PCTransportState.NEW) {
|
16328
16229
|
return;
|
16329
16230
|
}
|
16330
|
-
|
16331
|
-
|
16332
|
-
|
16333
|
-
|
16334
|
-
|
16335
|
-
|
16231
|
+
this.participantSid = (_a = joinResponse.participant) === null || _a === void 0 ? void 0 : _a.sid;
|
16232
|
+
const rtcConfig = this.makeRTCConfiguration(joinResponse);
|
16233
|
+
this.pcManager = new PCTransportManager(rtcConfig, joinResponse.subscriberPrimary, this.loggerOptions);
|
16234
|
+
this.emit(EngineEvent.TransportsCreated, this.pcManager.publisher, this.pcManager.subscriber);
|
16235
|
+
this.pcManager.onIceCandidate = (candidate, target) => {
|
16236
|
+
this.client.sendIceCandidate(candidate, target);
|
16237
|
+
};
|
16238
|
+
this.pcManager.onPublisherOffer = offer => {
|
16239
|
+
this.client.sendOffer(offer);
|
16240
|
+
};
|
16241
|
+
this.pcManager.onDataChannel = this.handleDataChannel;
|
16242
|
+
this.pcManager.onStateChange = (connectionState, publisherState, subscriberState) => __awaiter(this, void 0, void 0, function* () {
|
16243
|
+
this.log.debug("primary PC state changed ".concat(connectionState), this.logContext);
|
16244
|
+
if (['closed', 'disconnected', 'failed'].includes(publisherState)) {
|
16245
|
+
// reset publisher connection promise
|
16246
|
+
this.publisherConnectionPromise = undefined;
|
16247
|
+
}
|
16248
|
+
if (connectionState === PCTransportState.CONNECTED) {
|
16249
|
+
const shouldEmit = this.pcState === PCState.New;
|
16250
|
+
this.pcState = PCState.Connected;
|
16251
|
+
if (shouldEmit) {
|
16252
|
+
this.emit(EngineEvent.Connected, joinResponse);
|
16253
|
+
}
|
16254
|
+
} else if (connectionState === PCTransportState.FAILED) {
|
16255
|
+
// on Safari, PeerConnection will switch to 'disconnected' during renegotiation
|
16256
|
+
if (this.pcState === PCState.Connected) {
|
16257
|
+
this.pcState = PCState.Disconnected;
|
16258
|
+
this.handleDisconnect('peerconnection failed', subscriberState === 'failed' ? ReconnectReason.RR_SUBSCRIBER_FAILED : ReconnectReason.RR_PUBLISHER_FAILED);
|
16259
|
+
}
|
16260
|
+
}
|
16261
|
+
// detect cases where both signal client and peer connection are severed and assume that user has lost network connection
|
16262
|
+
const isSignalSevered = this.client.isDisconnected || this.client.currentState === SignalConnectionState.RECONNECTING;
|
16263
|
+
const isPCSevered = [PCTransportState.FAILED, PCTransportState.CLOSING, PCTransportState.CLOSED].includes(connectionState);
|
16264
|
+
if (isSignalSevered && isPCSevered && !this._isClosed) {
|
16265
|
+
this.emit(EngineEvent.Offline);
|
16266
|
+
}
|
16267
|
+
});
|
16268
|
+
this.pcManager.onTrack = ev => {
|
16269
|
+
this.emit(EngineEvent.MediaTrackAdded, ev.track, ev.streams[0], ev.receiver);
|
16270
|
+
};
|
16271
|
+
this.createDataChannels();
|
16272
|
+
});
|
16273
|
+
}
|
16274
|
+
setupSignalClientCallbacks() {
|
16275
|
+
// configure signaling client
|
16276
|
+
this.client.onAnswer = sd => __awaiter(this, void 0, void 0, function* () {
|
16277
|
+
if (!this.pcManager) {
|
16278
|
+
return;
|
16279
|
+
}
|
16280
|
+
this.log.debug('received server answer', Object.assign(Object.assign({}, this.logContext), {
|
16281
|
+
RTCSdpType: sd.type
|
16282
|
+
}));
|
16283
|
+
yield this.pcManager.setPublisherAnswer(sd);
|
16284
|
+
});
|
16285
|
+
// add candidate on trickle
|
16286
|
+
this.client.onTrickle = (candidate, target) => {
|
16287
|
+
if (!this.pcManager) {
|
16288
|
+
return;
|
16289
|
+
}
|
16290
|
+
this.log.trace('got ICE candidate from peer', Object.assign(Object.assign({}, this.logContext), {
|
16291
|
+
candidate,
|
16292
|
+
target
|
16293
|
+
}));
|
16294
|
+
this.pcManager.addIceCandidate(candidate, target);
|
16295
|
+
};
|
16296
|
+
// when server creates an offer for the client
|
16297
|
+
this.client.onOffer = sd => __awaiter(this, void 0, void 0, function* () {
|
16298
|
+
if (!this.pcManager) {
|
16299
|
+
return;
|
16300
|
+
}
|
16301
|
+
const answer = yield this.pcManager.createSubscriberAnswerFromOffer(sd);
|
16302
|
+
this.client.sendAnswer(answer);
|
16303
|
+
});
|
16304
|
+
this.client.onLocalTrackPublished = res => {
|
16305
|
+
var _a;
|
16306
|
+
this.log.debug('received trackPublishedResponse', Object.assign(Object.assign({}, this.logContext), {
|
16307
|
+
cid: res.cid,
|
16308
|
+
track: (_a = res.track) === null || _a === void 0 ? void 0 : _a.sid
|
16309
|
+
}));
|
16310
|
+
if (!this.pendingTrackResolvers[res.cid]) {
|
16311
|
+
this.log.error("missing track resolver for ".concat(res.cid), Object.assign(Object.assign({}, this.logContext), {
|
16312
|
+
cid: res.cid
|
16336
16313
|
}));
|
16337
16314
|
return;
|
16338
16315
|
}
|
16339
|
-
const
|
16340
|
-
|
16341
|
-
|
16342
|
-
|
16343
|
-
|
16344
|
-
|
16345
|
-
|
16346
|
-
|
16347
|
-
|
16316
|
+
const {
|
16317
|
+
resolve
|
16318
|
+
} = this.pendingTrackResolvers[res.cid];
|
16319
|
+
delete this.pendingTrackResolvers[res.cid];
|
16320
|
+
resolve(res.track);
|
16321
|
+
};
|
16322
|
+
this.client.onLocalTrackUnpublished = response => {
|
16323
|
+
this.emit(EngineEvent.LocalTrackUnpublished, response);
|
16324
|
+
};
|
16325
|
+
this.client.onTokenRefresh = token => {
|
16326
|
+
this.token = token;
|
16327
|
+
};
|
16328
|
+
this.client.onRemoteMuteChanged = (trackSid, muted) => {
|
16329
|
+
this.emit(EngineEvent.RemoteMute, trackSid, muted);
|
16330
|
+
};
|
16331
|
+
this.client.onSubscribedQualityUpdate = update => {
|
16332
|
+
this.emit(EngineEvent.SubscribedQualityUpdate, update);
|
16333
|
+
};
|
16334
|
+
this.client.onClose = () => {
|
16335
|
+
this.handleDisconnect('signal', ReconnectReason.RR_SIGNAL_DISCONNECTED);
|
16336
|
+
};
|
16337
|
+
this.client.onLeave = leave => {
|
16338
|
+
if (leave === null || leave === void 0 ? void 0 : leave.canReconnect) {
|
16339
|
+
this.fullReconnectOnNext = true;
|
16340
|
+
// reconnect immediately instead of waiting for next attempt
|
16341
|
+
this.handleDisconnect(leaveReconnect);
|
16342
|
+
} else {
|
16343
|
+
this.emit(EngineEvent.Disconnected, leave === null || leave === void 0 ? void 0 : leave.reason);
|
16344
|
+
this.close();
|
16348
16345
|
}
|
16349
|
-
this.
|
16350
|
-
|
16351
|
-
|
16346
|
+
this.log.debug('client leave request', Object.assign(Object.assign({}, this.logContext), {
|
16347
|
+
reason: leave === null || leave === void 0 ? void 0 : leave.reason
|
16348
|
+
}));
|
16349
|
+
};
|
16352
16350
|
}
|
16353
|
-
|
16354
|
-
|
16355
|
-
|
16351
|
+
makeRTCConfiguration(serverResponse) {
|
16352
|
+
var _a;
|
16353
|
+
const rtcConfig = Object.assign({}, this.rtcConfig);
|
16354
|
+
if ((_a = this.signalOpts) === null || _a === void 0 ? void 0 : _a.e2eeEnabled) {
|
16355
|
+
this.log.debug('E2EE - setting up transports with insertable streams', this.logContext);
|
16356
|
+
// this makes sure that no data is sent before the transforms are ready
|
16357
|
+
// @ts-ignore
|
16358
|
+
rtcConfig.encodedInsertableStreams = true;
|
16356
16359
|
}
|
16357
|
-
|
16360
|
+
// update ICE servers before creating PeerConnection
|
16361
|
+
if (serverResponse.iceServers && !rtcConfig.iceServers) {
|
16362
|
+
const rtcIceServers = [];
|
16363
|
+
serverResponse.iceServers.forEach(iceServer => {
|
16364
|
+
const rtcIceServer = {
|
16365
|
+
urls: iceServer.urls
|
16366
|
+
};
|
16367
|
+
if (iceServer.username) rtcIceServer.username = iceServer.username;
|
16368
|
+
if (iceServer.credential) {
|
16369
|
+
rtcIceServer.credential = iceServer.credential;
|
16370
|
+
}
|
16371
|
+
rtcIceServers.push(rtcIceServer);
|
16372
|
+
});
|
16373
|
+
rtcConfig.iceServers = rtcIceServers;
|
16374
|
+
}
|
16375
|
+
if (serverResponse.clientConfiguration && serverResponse.clientConfiguration.forceRelay === ClientConfigSetting.ENABLED) {
|
16376
|
+
rtcConfig.iceTransportPolicy = 'relay';
|
16377
|
+
}
|
16378
|
+
// @ts-ignore
|
16379
|
+
rtcConfig.sdpSemantics = 'unified-plan';
|
16380
|
+
// @ts-ignore
|
16381
|
+
rtcConfig.continualGatheringPolicy = 'gather_continually';
|
16382
|
+
return rtcConfig;
|
16358
16383
|
}
|
16359
|
-
|
16360
|
-
|
16361
|
-
var _a;
|
16362
|
-
this.signalClient = signalClient;
|
16363
|
-
if (!isWeb()) {
|
16384
|
+
createDataChannels() {
|
16385
|
+
if (!this.pcManager) {
|
16364
16386
|
return;
|
16365
16387
|
}
|
16366
|
-
//
|
16367
|
-
|
16368
|
-
|
16369
|
-
|
16370
|
-
this.encodings = params.encodings;
|
16388
|
+
// clear old data channel callbacks if recreate
|
16389
|
+
if (this.lossyDC) {
|
16390
|
+
this.lossyDC.onmessage = null;
|
16391
|
+
this.lossyDC.onerror = null;
|
16371
16392
|
}
|
16372
|
-
if (this.
|
16373
|
-
|
16393
|
+
if (this.reliableDC) {
|
16394
|
+
this.reliableDC.onmessage = null;
|
16395
|
+
this.reliableDC.onerror = null;
|
16374
16396
|
}
|
16375
|
-
|
16376
|
-
|
16377
|
-
|
16378
|
-
|
16379
|
-
|
16380
|
-
this._mediaStreamTrack.getConstraints();
|
16381
|
-
this.simulcastCodecs.forEach(trackInfo => {
|
16382
|
-
trackInfo.mediaStreamTrack.stop();
|
16397
|
+
// create data channels
|
16398
|
+
this.lossyDC = this.pcManager.createPublisherDataChannel(lossyDataChannel, {
|
16399
|
+
// will drop older packets that arrive
|
16400
|
+
ordered: true,
|
16401
|
+
maxRetransmits: 0
|
16383
16402
|
});
|
16384
|
-
|
16403
|
+
this.reliableDC = this.pcManager.createPublisherDataChannel(reliableDataChannel, {
|
16404
|
+
ordered: true
|
16405
|
+
});
|
16406
|
+
// also handle messages over the pub channel, for backwards compatibility
|
16407
|
+
this.lossyDC.onmessage = this.handleDataMessage;
|
16408
|
+
this.reliableDC.onmessage = this.handleDataMessage;
|
16409
|
+
// handle datachannel errors
|
16410
|
+
this.lossyDC.onerror = this.handleDataError;
|
16411
|
+
this.reliableDC.onerror = this.handleDataError;
|
16412
|
+
// set up dc buffer threshold, set to 64kB (otherwise 0 by default)
|
16413
|
+
this.lossyDC.bufferedAmountLowThreshold = 65535;
|
16414
|
+
this.reliableDC.bufferedAmountLowThreshold = 65535;
|
16415
|
+
// handle buffer amount low events
|
16416
|
+
this.lossyDC.onbufferedamountlow = this.handleBufferedAmountLow;
|
16417
|
+
this.reliableDC.onbufferedamountlow = this.handleBufferedAmountLow;
|
16385
16418
|
}
|
16386
|
-
|
16387
|
-
|
16388
|
-
|
16389
|
-
|
16419
|
+
createSender(track, opts, encodings) {
|
16420
|
+
return __awaiter(this, void 0, void 0, function* () {
|
16421
|
+
if (supportsTransceiver()) {
|
16422
|
+
const sender = yield this.createTransceiverRTCRtpSender(track, opts, encodings);
|
16423
|
+
return sender;
|
16424
|
+
}
|
16425
|
+
if (supportsAddTrack()) {
|
16426
|
+
this.log.warn('using add-track fallback', this.logContext);
|
16427
|
+
const sender = yield this.createRTCRtpSender(track.mediaStreamTrack);
|
16428
|
+
return sender;
|
16390
16429
|
}
|
16430
|
+
throw new UnexpectedConnectionState('Required webRTC APIs not supported on this device');
|
16391
16431
|
});
|
16432
|
+
}
|
16433
|
+
createSimulcastSender(track, simulcastTrack, opts, encodings) {
|
16392
16434
|
return __awaiter(this, void 0, void 0, function* () {
|
16393
|
-
|
16394
|
-
|
16395
|
-
|
16396
|
-
|
16397
|
-
|
16398
|
-
|
16399
|
-
|
16400
|
-
const sc = _c;
|
16401
|
-
yield (_d = sc.sender) === null || _d === void 0 ? void 0 : _d.replaceTrack(null);
|
16402
|
-
}
|
16403
|
-
} catch (e_1_1) {
|
16404
|
-
e_1 = {
|
16405
|
-
error: e_1_1
|
16406
|
-
};
|
16407
|
-
} finally {
|
16408
|
-
try {
|
16409
|
-
if (!_e && !_a && (_b = _f.return)) yield _b.call(_f);
|
16410
|
-
} finally {
|
16411
|
-
if (e_1) throw e_1.error;
|
16412
|
-
}
|
16435
|
+
// store RTCRtpSender
|
16436
|
+
if (supportsTransceiver()) {
|
16437
|
+
return this.createSimulcastTransceiverSender(track, simulcastTrack, opts, encodings);
|
16438
|
+
}
|
16439
|
+
if (supportsAddTrack()) {
|
16440
|
+
this.log.debug('using add-track fallback', this.logContext);
|
16441
|
+
return this.createRTCRtpSender(track.mediaStreamTrack);
|
16413
16442
|
}
|
16443
|
+
throw new UnexpectedConnectionState('Cannot stream on this device');
|
16414
16444
|
});
|
16415
16445
|
}
|
16416
|
-
|
16417
|
-
|
16418
|
-
|
16419
|
-
|
16446
|
+
createTransceiverRTCRtpSender(track, opts, encodings) {
|
16447
|
+
return __awaiter(this, void 0, void 0, function* () {
|
16448
|
+
if (!this.pcManager) {
|
16449
|
+
throw new UnexpectedConnectionState('publisher is closed');
|
16450
|
+
}
|
16451
|
+
const streams = [];
|
16452
|
+
if (track.mediaStream) {
|
16453
|
+
streams.push(track.mediaStream);
|
16454
|
+
}
|
16455
|
+
if (track instanceof LocalVideoTrack) {
|
16456
|
+
track.codec = opts.videoCodec;
|
16457
|
+
}
|
16458
|
+
const transceiverInit = {
|
16459
|
+
direction: 'sendonly',
|
16460
|
+
streams
|
16461
|
+
};
|
16462
|
+
if (encodings) {
|
16463
|
+
transceiverInit.sendEncodings = encodings;
|
16420
16464
|
}
|
16465
|
+
// addTransceiver for react-native is async. web is synchronous, but await won't effect it.
|
16466
|
+
const transceiver = yield this.pcManager.addPublisherTransceiver(track.mediaStreamTrack, transceiverInit);
|
16467
|
+
return transceiver.sender;
|
16421
16468
|
});
|
16469
|
+
}
|
16470
|
+
createSimulcastTransceiverSender(track, simulcastTrack, opts, encodings) {
|
16422
16471
|
return __awaiter(this, void 0, void 0, function* () {
|
16423
|
-
|
16424
|
-
|
16425
|
-
yield _super.resumeUpstream.call(this);
|
16426
|
-
try {
|
16427
|
-
for (var _e = true, _f = __asyncValues(this.simulcastCodecs.values()), _g; _g = yield _f.next(), _a = _g.done, !_a; _e = true) {
|
16428
|
-
_c = _g.value;
|
16429
|
-
_e = false;
|
16430
|
-
const sc = _c;
|
16431
|
-
yield (_d = sc.sender) === null || _d === void 0 ? void 0 : _d.replaceTrack(sc.mediaStreamTrack);
|
16432
|
-
}
|
16433
|
-
} catch (e_2_1) {
|
16434
|
-
e_2 = {
|
16435
|
-
error: e_2_1
|
16436
|
-
};
|
16437
|
-
} finally {
|
16438
|
-
try {
|
16439
|
-
if (!_e && !_a && (_b = _f.return)) yield _b.call(_f);
|
16440
|
-
} finally {
|
16441
|
-
if (e_2) throw e_2.error;
|
16442
|
-
}
|
16472
|
+
if (!this.pcManager) {
|
16473
|
+
throw new UnexpectedConnectionState('publisher is closed');
|
16443
16474
|
}
|
16475
|
+
const transceiverInit = {
|
16476
|
+
direction: 'sendonly'
|
16477
|
+
};
|
16478
|
+
if (encodings) {
|
16479
|
+
transceiverInit.sendEncodings = encodings;
|
16480
|
+
}
|
16481
|
+
// addTransceiver for react-native is async. web is synchronous, but await won't effect it.
|
16482
|
+
const transceiver = yield this.pcManager.addPublisherTransceiver(simulcastTrack.mediaStreamTrack, transceiverInit);
|
16483
|
+
if (!opts.videoCodec) {
|
16484
|
+
return;
|
16485
|
+
}
|
16486
|
+
track.setSimulcastTrackSender(opts.videoCodec, transceiver.sender);
|
16487
|
+
return transceiver.sender;
|
16444
16488
|
});
|
16445
16489
|
}
|
16446
|
-
|
16447
|
-
|
16448
|
-
|
16449
|
-
|
16490
|
+
createRTCRtpSender(track) {
|
16491
|
+
return __awaiter(this, void 0, void 0, function* () {
|
16492
|
+
if (!this.pcManager) {
|
16493
|
+
throw new UnexpectedConnectionState('publisher is closed');
|
16450
16494
|
}
|
16495
|
+
return this.pcManager.addPublisherTrack(track);
|
16451
16496
|
});
|
16497
|
+
}
|
16498
|
+
attemptReconnect(reason) {
|
16452
16499
|
return __awaiter(this, void 0, void 0, function* () {
|
16453
|
-
|
16500
|
+
var _a, _b, _c;
|
16501
|
+
if (this._isClosed) {
|
16502
|
+
return;
|
16503
|
+
}
|
16504
|
+
// guard for attempting reconnection multiple times while one attempt is still not finished
|
16505
|
+
if (this.attemptingReconnect) {
|
16506
|
+
livekitLogger.warn('already attempting reconnect, returning early', this.logContext);
|
16507
|
+
return;
|
16508
|
+
}
|
16509
|
+
if (((_a = this.clientConfiguration) === null || _a === void 0 ? void 0 : _a.resumeConnection) === ClientConfigSetting.DISABLED ||
|
16510
|
+
// signaling state could change to closed due to hardware sleep
|
16511
|
+
// those connections cannot be resumed
|
16512
|
+
((_c = (_b = this.pcManager) === null || _b === void 0 ? void 0 : _b.currentState) !== null && _c !== void 0 ? _c : PCTransportState.NEW) === PCTransportState.NEW) {
|
16513
|
+
this.fullReconnectOnNext = true;
|
16514
|
+
}
|
16454
16515
|
try {
|
16455
|
-
|
16456
|
-
|
16457
|
-
|
16516
|
+
this.attemptingReconnect = true;
|
16517
|
+
if (this.fullReconnectOnNext) {
|
16518
|
+
yield this.restartConnection();
|
16519
|
+
} else {
|
16520
|
+
yield this.resumeConnection(reason);
|
16458
16521
|
}
|
16459
|
-
|
16460
|
-
|
16461
|
-
|
16462
|
-
|
16522
|
+
this.clearPendingReconnect();
|
16523
|
+
this.fullReconnectOnNext = false;
|
16524
|
+
} catch (e) {
|
16525
|
+
this.reconnectAttempts += 1;
|
16526
|
+
let recoverable = true;
|
16527
|
+
if (e instanceof UnexpectedConnectionState) {
|
16528
|
+
this.log.debug('received unrecoverable error', Object.assign(Object.assign({}, this.logContext), {
|
16529
|
+
error: e
|
16530
|
+
}));
|
16531
|
+
// unrecoverable
|
16532
|
+
recoverable = false;
|
16533
|
+
} else if (!(e instanceof SignalReconnectError)) {
|
16534
|
+
// cannot resume
|
16535
|
+
this.fullReconnectOnNext = true;
|
16536
|
+
}
|
16537
|
+
if (recoverable) {
|
16538
|
+
this.handleDisconnect('reconnect', ReconnectReason.RR_UNKNOWN);
|
16539
|
+
} else {
|
16540
|
+
this.log.info("could not recover connection after ".concat(this.reconnectAttempts, " attempts, ").concat(Date.now() - this.reconnectStart, "ms. giving up"), this.logContext);
|
16541
|
+
this.emit(EngineEvent.Disconnected);
|
16542
|
+
yield this.close();
|
16463
16543
|
}
|
16464
|
-
yield _super.mute.call(this);
|
16465
|
-
return this;
|
16466
16544
|
} finally {
|
16467
|
-
|
16545
|
+
this.attemptingReconnect = false;
|
16468
16546
|
}
|
16469
16547
|
});
|
16470
16548
|
}
|
16471
|
-
|
16472
|
-
|
16473
|
-
|
16474
|
-
|
16475
|
-
}
|
16476
|
-
|
16549
|
+
getNextRetryDelay(context) {
|
16550
|
+
try {
|
16551
|
+
return this.reconnectPolicy.nextRetryDelayInMs(context);
|
16552
|
+
} catch (e) {
|
16553
|
+
this.log.warn('encountered error in reconnect policy', Object.assign(Object.assign({}, this.logContext), {
|
16554
|
+
error: e
|
16555
|
+
}));
|
16556
|
+
}
|
16557
|
+
// error in user code with provided reconnect policy, stop reconnecting
|
16558
|
+
return null;
|
16559
|
+
}
|
16560
|
+
restartConnection(regionUrl) {
|
16477
16561
|
return __awaiter(this, void 0, void 0, function* () {
|
16478
|
-
|
16562
|
+
var _a, _b, _c;
|
16479
16563
|
try {
|
16480
|
-
if (!this.
|
16481
|
-
|
16482
|
-
|
16564
|
+
if (!this.url || !this.token) {
|
16565
|
+
// permanent failure, don't attempt reconnection
|
16566
|
+
throw new UnexpectedConnectionState('could not reconnect, url or token not saved');
|
16483
16567
|
}
|
16484
|
-
|
16485
|
-
|
16486
|
-
|
16568
|
+
this.log.info("reconnecting, attempt: ".concat(this.reconnectAttempts), this.logContext);
|
16569
|
+
this.emit(EngineEvent.Restarting);
|
16570
|
+
if (!this.client.isDisconnected) {
|
16571
|
+
yield this.client.sendLeave();
|
16572
|
+
}
|
16573
|
+
yield this.cleanupPeerConnections();
|
16574
|
+
yield this.cleanupClient();
|
16575
|
+
let joinResponse;
|
16576
|
+
try {
|
16577
|
+
if (!this.signalOpts) {
|
16578
|
+
this.log.warn('attempted connection restart, without signal options present', this.logContext);
|
16579
|
+
throw new SignalReconnectError();
|
16580
|
+
}
|
16581
|
+
// in case a regionUrl is passed, the region URL takes precedence
|
16582
|
+
joinResponse = yield this.join(regionUrl !== null && regionUrl !== void 0 ? regionUrl : this.url, this.token, this.signalOpts);
|
16583
|
+
} catch (e) {
|
16584
|
+
if (e instanceof ConnectionError && e.reason === 0 /* ConnectionErrorReason.NotAllowed */) {
|
16585
|
+
throw new UnexpectedConnectionState('could not reconnect, token might be expired');
|
16586
|
+
}
|
16587
|
+
throw new SignalReconnectError();
|
16588
|
+
}
|
16589
|
+
if (this.shouldFailNext) {
|
16590
|
+
this.shouldFailNext = false;
|
16591
|
+
throw new Error('simulated failure');
|
16592
|
+
}
|
16593
|
+
this.client.setReconnected();
|
16594
|
+
this.emit(EngineEvent.SignalRestarted, joinResponse);
|
16595
|
+
yield this.waitForPCReconnected();
|
16596
|
+
// re-check signal connection state before setting engine as resumed
|
16597
|
+
if (this.client.currentState !== SignalConnectionState.CONNECTED) {
|
16598
|
+
throw new SignalReconnectError('Signal connection got severed during reconnect');
|
16599
|
+
}
|
16600
|
+
(_a = this.regionUrlProvider) === null || _a === void 0 ? void 0 : _a.resetAttempts();
|
16601
|
+
// reconnect success
|
16602
|
+
this.emit(EngineEvent.Restarted);
|
16603
|
+
} catch (error) {
|
16604
|
+
const nextRegionUrl = yield (_b = this.regionUrlProvider) === null || _b === void 0 ? void 0 : _b.getNextBestRegionUrl();
|
16605
|
+
if (nextRegionUrl) {
|
16606
|
+
yield this.restartConnection(nextRegionUrl);
|
16607
|
+
return;
|
16608
|
+
} else {
|
16609
|
+
// no more regions to try (or we're not on cloud)
|
16610
|
+
(_c = this.regionUrlProvider) === null || _c === void 0 ? void 0 : _c.resetAttempts();
|
16611
|
+
throw error;
|
16487
16612
|
}
|
16488
|
-
yield _super.unmute.call(this);
|
16489
|
-
return this;
|
16490
|
-
} finally {
|
16491
|
-
unlock();
|
16492
16613
|
}
|
16493
16614
|
});
|
16494
16615
|
}
|
16495
|
-
|
16496
|
-
super.setTrackMuted(muted);
|
16497
|
-
for (const sc of this.simulcastCodecs.values()) {
|
16498
|
-
sc.mediaStreamTrack.enabled = !muted;
|
16499
|
-
}
|
16500
|
-
}
|
16501
|
-
getSenderStats() {
|
16616
|
+
resumeConnection(reason) {
|
16502
16617
|
return __awaiter(this, void 0, void 0, function* () {
|
16503
16618
|
var _a;
|
16504
|
-
if (!
|
16505
|
-
|
16619
|
+
if (!this.url || !this.token) {
|
16620
|
+
// permanent failure, don't attempt reconnection
|
16621
|
+
throw new UnexpectedConnectionState('could not reconnect, url or token not saved');
|
16506
16622
|
}
|
16507
|
-
|
16508
|
-
|
16509
|
-
|
16510
|
-
|
16511
|
-
|
16512
|
-
|
16513
|
-
|
16514
|
-
|
16515
|
-
|
16516
|
-
|
16517
|
-
|
16518
|
-
|
16519
|
-
|
16520
|
-
|
16521
|
-
|
16522
|
-
|
16523
|
-
|
16524
|
-
qualityLimitationReason: v.qualityLimitationReason,
|
16525
|
-
qualityLimitationDurations: v.qualityLimitationDurations,
|
16526
|
-
qualityLimitationResolutionChanges: v.qualityLimitationResolutionChanges,
|
16527
|
-
rid: (_a = v.rid) !== null && _a !== void 0 ? _a : v.id,
|
16528
|
-
retransmittedPacketsSent: v.retransmittedPacketsSent,
|
16529
|
-
targetBitrate: v.targetBitrate,
|
16530
|
-
timestamp: v.timestamp
|
16531
|
-
};
|
16532
|
-
// locate the appropriate remote-inbound-rtp item
|
16533
|
-
const r = stats.get(v.remoteId);
|
16534
|
-
if (r) {
|
16535
|
-
vs.jitter = r.jitter;
|
16536
|
-
vs.packetsLost = r.packetsLost;
|
16537
|
-
vs.roundTripTime = r.roundTripTime;
|
16538
|
-
}
|
16539
|
-
items.push(vs);
|
16623
|
+
// trigger publisher reconnect
|
16624
|
+
if (!this.pcManager) {
|
16625
|
+
throw new UnexpectedConnectionState('publisher and subscriber connections unset');
|
16626
|
+
}
|
16627
|
+
this.log.info("resuming signal connection, attempt ".concat(this.reconnectAttempts), this.logContext);
|
16628
|
+
this.emit(EngineEvent.Resuming);
|
16629
|
+
let res;
|
16630
|
+
try {
|
16631
|
+
this.setupSignalClientCallbacks();
|
16632
|
+
res = yield this.client.reconnect(this.url, this.token, this.participantSid, reason);
|
16633
|
+
} catch (error) {
|
16634
|
+
let message = '';
|
16635
|
+
if (error instanceof Error) {
|
16636
|
+
message = error.message;
|
16637
|
+
this.log.error(error.message, Object.assign(Object.assign({}, this.logContext), {
|
16638
|
+
error
|
16639
|
+
}));
|
16540
16640
|
}
|
16541
|
-
|
16542
|
-
|
16543
|
-
|
16544
|
-
|
16545
|
-
|
16546
|
-
|
16547
|
-
|
16641
|
+
if (error instanceof ConnectionError && error.reason === 0 /* ConnectionErrorReason.NotAllowed */) {
|
16642
|
+
throw new UnexpectedConnectionState('could not reconnect, token might be expired');
|
16643
|
+
}
|
16644
|
+
if (error instanceof ConnectionError && error.reason === 4 /* ConnectionErrorReason.LeaveRequest */) {
|
16645
|
+
throw error;
|
16646
|
+
}
|
16647
|
+
throw new SignalReconnectError(message);
|
16648
|
+
}
|
16649
|
+
this.emit(EngineEvent.SignalResumed);
|
16650
|
+
if (res) {
|
16651
|
+
const rtcConfig = this.makeRTCConfiguration(res);
|
16652
|
+
this.pcManager.updateConfiguration(rtcConfig);
|
16653
|
+
} else {
|
16654
|
+
this.log.warn('Did not receive reconnect response', this.logContext);
|
16655
|
+
}
|
16656
|
+
if (this.shouldFailNext) {
|
16657
|
+
this.shouldFailNext = false;
|
16658
|
+
throw new Error('simulated failure');
|
16659
|
+
}
|
16660
|
+
yield this.pcManager.triggerIceRestart();
|
16661
|
+
yield this.waitForPCReconnected();
|
16662
|
+
// re-check signal connection state before setting engine as resumed
|
16663
|
+
if (this.client.currentState !== SignalConnectionState.CONNECTED) {
|
16664
|
+
throw new SignalReconnectError('Signal connection got severed during reconnect');
|
16665
|
+
}
|
16666
|
+
this.client.setReconnected();
|
16667
|
+
// recreate publish datachannel if it's id is null
|
16668
|
+
// (for safari https://bugs.webkit.org/show_bug.cgi?id=184688)
|
16669
|
+
if (((_a = this.reliableDC) === null || _a === void 0 ? void 0 : _a.readyState) === 'open' && this.reliableDC.id === null) {
|
16670
|
+
this.createDataChannels();
|
16671
|
+
}
|
16672
|
+
// resume success
|
16673
|
+
this.emit(EngineEvent.Resumed);
|
16548
16674
|
});
|
16549
16675
|
}
|
16550
|
-
|
16551
|
-
const qualities = [];
|
16552
|
-
for (let q = VideoQuality.LOW; q <= VideoQuality.HIGH; q += 1) {
|
16553
|
-
qualities.push(new SubscribedQuality({
|
16554
|
-
quality: q,
|
16555
|
-
enabled: q <= maxQuality
|
16556
|
-
}));
|
16557
|
-
}
|
16558
|
-
this.log.debug("setting publishing quality. max quality ".concat(maxQuality), this.logContext);
|
16559
|
-
this.setPublishingLayers(qualities);
|
16560
|
-
}
|
16561
|
-
setDeviceId(deviceId) {
|
16676
|
+
waitForPCInitialConnection(timeout, abortController) {
|
16562
16677
|
return __awaiter(this, void 0, void 0, function* () {
|
16563
|
-
if (this.
|
16564
|
-
|
16565
|
-
}
|
16566
|
-
this._constraints.deviceId = deviceId;
|
16567
|
-
// when video is muted, underlying media stream track is stopped and
|
16568
|
-
// will be restarted later
|
16569
|
-
if (!this.isMuted) {
|
16570
|
-
yield this.restartTrack();
|
16678
|
+
if (!this.pcManager) {
|
16679
|
+
throw new UnexpectedConnectionState('PC manager is closed');
|
16571
16680
|
}
|
16572
|
-
|
16681
|
+
yield this.pcManager.ensurePCTransportConnection(abortController, timeout);
|
16573
16682
|
});
|
16574
16683
|
}
|
16575
|
-
|
16684
|
+
waitForPCReconnected() {
|
16576
16685
|
return __awaiter(this, void 0, void 0, function* () {
|
16577
|
-
|
16578
|
-
|
16579
|
-
if (options) {
|
16580
|
-
const streamConstraints = constraintsForOptions({
|
16581
|
-
video: options
|
16582
|
-
});
|
16583
|
-
if (typeof streamConstraints.video !== 'boolean') {
|
16584
|
-
constraints = streamConstraints.video;
|
16585
|
-
}
|
16586
|
-
}
|
16587
|
-
yield this.restart(constraints);
|
16686
|
+
this.pcState = PCState.Reconnecting;
|
16687
|
+
this.log.debug('waiting for peer connection to reconnect', this.logContext);
|
16588
16688
|
try {
|
16589
|
-
|
16590
|
-
|
16591
|
-
|
16592
|
-
const sc = _c;
|
16593
|
-
if (sc.sender) {
|
16594
|
-
sc.mediaStreamTrack = this.mediaStreamTrack.clone();
|
16595
|
-
yield sc.sender.replaceTrack(sc.mediaStreamTrack);
|
16596
|
-
}
|
16597
|
-
}
|
16598
|
-
} catch (e_3_1) {
|
16599
|
-
e_3 = {
|
16600
|
-
error: e_3_1
|
16601
|
-
};
|
16602
|
-
} finally {
|
16603
|
-
try {
|
16604
|
-
if (!_d && !_a && (_b = _e.return)) yield _b.call(_e);
|
16605
|
-
} finally {
|
16606
|
-
if (e_3) throw e_3.error;
|
16689
|
+
yield sleep(minReconnectWait); // FIXME setTimeout again not ideal for a connection critical path
|
16690
|
+
if (!this.pcManager) {
|
16691
|
+
throw new UnexpectedConnectionState('PC manager is closed');
|
16607
16692
|
}
|
16693
|
+
yield this.pcManager.ensurePCTransportConnection(undefined, this.peerConnectionTimeout);
|
16694
|
+
this.pcState = PCState.Connected;
|
16695
|
+
} catch (e) {
|
16696
|
+
// TODO do we need a `failed` state here for the PC?
|
16697
|
+
this.pcState = PCState.Disconnected;
|
16698
|
+
throw new ConnectionError("could not establish PC connection, ".concat(e.message));
|
16608
16699
|
}
|
16609
16700
|
});
|
16610
16701
|
}
|
16611
|
-
|
16612
|
-
|
16613
|
-
|
16614
|
-
|
16702
|
+
/* @internal */
|
16703
|
+
sendDataPacket(packet, kind) {
|
16704
|
+
return __awaiter(this, void 0, void 0, function* () {
|
16705
|
+
const msg = packet.toBinary();
|
16706
|
+
// make sure we do have a data connection
|
16707
|
+
yield this.ensurePublisherConnected(kind);
|
16708
|
+
const dc = this.dataChannelForKind(kind);
|
16709
|
+
if (dc) {
|
16710
|
+
dc.send(msg);
|
16615
16711
|
}
|
16712
|
+
this.updateAndEmitDCBufferStatus(kind);
|
16616
16713
|
});
|
16617
|
-
|
16618
|
-
|
16619
|
-
|
16714
|
+
}
|
16715
|
+
/**
|
16716
|
+
* @internal
|
16717
|
+
*/
|
16718
|
+
ensureDataTransportConnected(kind_1) {
|
16719
|
+
return __awaiter(this, arguments, void 0, function (kind) {
|
16720
|
+
var _this2 = this;
|
16721
|
+
let subscriber = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.subscriberPrimary;
|
16620
16722
|
return function* () {
|
16621
|
-
var _a
|
16622
|
-
|
16623
|
-
|
16624
|
-
|
16625
|
-
|
16626
|
-
|
16627
|
-
|
16628
|
-
|
16629
|
-
|
16630
|
-
|
16631
|
-
|
16632
|
-
|
16633
|
-
|
16634
|
-
|
16635
|
-
|
16636
|
-
|
16637
|
-
|
16638
|
-
|
16639
|
-
|
16640
|
-
|
16641
|
-
|
16723
|
+
var _a;
|
16724
|
+
if (!_this2.pcManager) {
|
16725
|
+
throw new UnexpectedConnectionState('PC manager is closed');
|
16726
|
+
}
|
16727
|
+
const transport = subscriber ? _this2.pcManager.subscriber : _this2.pcManager.publisher;
|
16728
|
+
const transportName = subscriber ? 'Subscriber' : 'Publisher';
|
16729
|
+
if (!transport) {
|
16730
|
+
throw new ConnectionError("".concat(transportName, " connection not set"));
|
16731
|
+
}
|
16732
|
+
if (!subscriber && !_this2.pcManager.publisher.isICEConnected && _this2.pcManager.publisher.getICEConnectionState() !== 'checking') {
|
16733
|
+
// start negotiation
|
16734
|
+
_this2.negotiate();
|
16735
|
+
}
|
16736
|
+
const targetChannel = _this2.dataChannelForKind(kind, subscriber);
|
16737
|
+
if ((targetChannel === null || targetChannel === void 0 ? void 0 : targetChannel.readyState) === 'open') {
|
16738
|
+
return;
|
16739
|
+
}
|
16740
|
+
// wait until ICE connected
|
16741
|
+
const endTime = new Date().getTime() + _this2.peerConnectionTimeout;
|
16742
|
+
while (new Date().getTime() < endTime) {
|
16743
|
+
if (transport.isICEConnected && ((_a = _this2.dataChannelForKind(kind, subscriber)) === null || _a === void 0 ? void 0 : _a.readyState) === 'open') {
|
16744
|
+
return;
|
16642
16745
|
}
|
16746
|
+
yield sleep(50);
|
16643
16747
|
}
|
16748
|
+
throw new ConnectionError("could not establish ".concat(transportName, " connection, state: ").concat(transport.getICEConnectionState()));
|
16644
16749
|
}();
|
16645
16750
|
});
|
16646
16751
|
}
|
16647
|
-
|
16648
|
-
|
16649
|
-
this.
|
16650
|
-
|
16651
|
-
|
16652
|
-
|
16653
|
-
|
16654
|
-
mediaStreamTrack: this.mediaStreamTrack.clone(),
|
16655
|
-
sender: undefined,
|
16656
|
-
encodings
|
16657
|
-
};
|
16658
|
-
this.simulcastCodecs.set(codec, simulcastCodecInfo);
|
16659
|
-
return simulcastCodecInfo;
|
16752
|
+
ensurePublisherConnected(kind) {
|
16753
|
+
return __awaiter(this, void 0, void 0, function* () {
|
16754
|
+
if (!this.publisherConnectionPromise) {
|
16755
|
+
this.publisherConnectionPromise = this.ensureDataTransportConnected(kind, false);
|
16756
|
+
}
|
16757
|
+
yield this.publisherConnectionPromise;
|
16758
|
+
});
|
16660
16759
|
}
|
16661
|
-
|
16662
|
-
|
16663
|
-
if (!
|
16664
|
-
return;
|
16760
|
+
/* @internal */
|
16761
|
+
verifyTransport() {
|
16762
|
+
if (!this.pcManager) {
|
16763
|
+
return false;
|
16665
16764
|
}
|
16666
|
-
|
16667
|
-
|
16668
|
-
|
16669
|
-
|
16670
|
-
|
16671
|
-
|
16672
|
-
|
16673
|
-
}
|
16765
|
+
// primary connection
|
16766
|
+
if (this.pcManager.currentState !== PCTransportState.CONNECTED) {
|
16767
|
+
return false;
|
16768
|
+
}
|
16769
|
+
// ensure signal is connected
|
16770
|
+
if (!this.client.ws || this.client.ws.readyState === WebSocket.CLOSED) {
|
16771
|
+
return false;
|
16772
|
+
}
|
16773
|
+
return true;
|
16674
16774
|
}
|
16675
|
-
/**
|
16676
|
-
|
16677
|
-
* Sets codecs that should be publishing, returns new codecs that have not yet
|
16678
|
-
* been published
|
16679
|
-
*/
|
16680
|
-
setPublishingCodecs(codecs) {
|
16775
|
+
/** @internal */
|
16776
|
+
negotiate() {
|
16681
16777
|
return __awaiter(this, void 0, void 0, function* () {
|
16682
|
-
|
16683
|
-
|
16684
|
-
|
16685
|
-
|
16686
|
-
|
16687
|
-
}));
|
16688
|
-
// only enable simulcast codec for preference codec setted
|
16689
|
-
if (!this.codec && codecs.length > 0) {
|
16690
|
-
yield this.setPublishingLayers(codecs[0].qualities);
|
16691
|
-
return [];
|
16692
|
-
}
|
16693
|
-
this.subscribedCodecs = codecs;
|
16694
|
-
const newCodecs = [];
|
16695
|
-
try {
|
16696
|
-
for (_a = true, codecs_1 = __asyncValues(codecs); codecs_1_1 = yield codecs_1.next(), _b = codecs_1_1.done, !_b; _a = true) {
|
16697
|
-
_d = codecs_1_1.value;
|
16698
|
-
_a = false;
|
16699
|
-
const codec = _d;
|
16700
|
-
if (!this.codec || this.codec === codec.codec) {
|
16701
|
-
yield this.setPublishingLayers(codec.qualities);
|
16702
|
-
} else {
|
16703
|
-
const simulcastCodecInfo = this.simulcastCodecs.get(codec.codec);
|
16704
|
-
this.log.debug("try setPublishingCodec for ".concat(codec.codec), Object.assign(Object.assign({}, this.logContext), {
|
16705
|
-
simulcastCodecInfo
|
16706
|
-
}));
|
16707
|
-
if (!simulcastCodecInfo || !simulcastCodecInfo.sender) {
|
16708
|
-
for (const q of codec.qualities) {
|
16709
|
-
if (q.enabled) {
|
16710
|
-
newCodecs.push(codec.codec);
|
16711
|
-
break;
|
16712
|
-
}
|
16713
|
-
}
|
16714
|
-
} else if (simulcastCodecInfo.encodings) {
|
16715
|
-
this.log.debug("try setPublishingLayersForSender ".concat(codec.codec), this.logContext);
|
16716
|
-
yield setPublishingLayersForSender(simulcastCodecInfo.sender, simulcastCodecInfo.encodings, codec.qualities, this.senderLock, this.log, this.logContext);
|
16717
|
-
}
|
16718
|
-
}
|
16778
|
+
// observe signal state
|
16779
|
+
return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
|
16780
|
+
if (!this.pcManager) {
|
16781
|
+
reject(new NegotiationError('PC manager is closed'));
|
16782
|
+
return;
|
16719
16783
|
}
|
16720
|
-
|
16721
|
-
|
16722
|
-
|
16784
|
+
this.pcManager.requirePublisher();
|
16785
|
+
const abortController = new AbortController();
|
16786
|
+
const handleClosed = () => {
|
16787
|
+
abortController.abort();
|
16788
|
+
this.log.debug('engine disconnected while negotiation was ongoing', this.logContext);
|
16789
|
+
resolve();
|
16790
|
+
return;
|
16723
16791
|
};
|
16724
|
-
|
16792
|
+
if (this.isClosed) {
|
16793
|
+
reject('cannot negotiate on closed engine');
|
16794
|
+
}
|
16795
|
+
this.on(EngineEvent.Closing, handleClosed);
|
16796
|
+
this.pcManager.publisher.once(PCEvents.RTPVideoPayloadTypes, rtpTypes => {
|
16797
|
+
const rtpMap = new Map();
|
16798
|
+
rtpTypes.forEach(rtp => {
|
16799
|
+
const codec = rtp.codec.toLowerCase();
|
16800
|
+
if (isVideoCodec(codec)) {
|
16801
|
+
rtpMap.set(rtp.payload, codec);
|
16802
|
+
}
|
16803
|
+
});
|
16804
|
+
this.emit(EngineEvent.RTPVideoMapUpdate, rtpMap);
|
16805
|
+
});
|
16725
16806
|
try {
|
16726
|
-
|
16807
|
+
yield this.pcManager.negotiate(abortController);
|
16808
|
+
resolve();
|
16809
|
+
} catch (e) {
|
16810
|
+
if (e instanceof NegotiationError) {
|
16811
|
+
this.fullReconnectOnNext = true;
|
16812
|
+
}
|
16813
|
+
this.handleDisconnect('negotiation', ReconnectReason.RR_UNKNOWN);
|
16814
|
+
reject(e);
|
16727
16815
|
} finally {
|
16728
|
-
|
16816
|
+
this.off(EngineEvent.Closing, handleClosed);
|
16729
16817
|
}
|
16730
|
-
}
|
16731
|
-
return newCodecs;
|
16818
|
+
}));
|
16732
16819
|
});
|
16733
16820
|
}
|
16734
|
-
|
16735
|
-
|
16736
|
-
|
16737
|
-
|
16738
|
-
setPublishingLayers(qualities) {
|
16739
|
-
return __awaiter(this, void 0, void 0, function* () {
|
16740
|
-
this.log.debug('setting publishing layers', Object.assign(Object.assign({}, this.logContext), {
|
16741
|
-
qualities
|
16742
|
-
}));
|
16743
|
-
if (!this.sender || !this.encodings) {
|
16744
|
-
return;
|
16821
|
+
dataChannelForKind(kind, sub) {
|
16822
|
+
if (!sub) {
|
16823
|
+
if (kind === DataPacket_Kind.LOSSY) {
|
16824
|
+
return this.lossyDC;
|
16745
16825
|
}
|
16746
|
-
|
16747
|
-
|
16826
|
+
if (kind === DataPacket_Kind.RELIABLE) {
|
16827
|
+
return this.reliableDC;
|
16828
|
+
}
|
16829
|
+
} else {
|
16830
|
+
if (kind === DataPacket_Kind.LOSSY) {
|
16831
|
+
return this.lossyDCSub;
|
16832
|
+
}
|
16833
|
+
if (kind === DataPacket_Kind.RELIABLE) {
|
16834
|
+
return this.reliableDCSub;
|
16835
|
+
}
|
16836
|
+
}
|
16748
16837
|
}
|
16749
|
-
|
16750
|
-
|
16751
|
-
|
16752
|
-
|
16838
|
+
/** @internal */
|
16839
|
+
sendSyncState(remoteTracks, localTracks) {
|
16840
|
+
var _a, _b;
|
16841
|
+
if (!this.pcManager) {
|
16842
|
+
this.log.warn('sync state cannot be sent without peer connection setup', this.logContext);
|
16843
|
+
return;
|
16844
|
+
}
|
16845
|
+
const previousAnswer = this.pcManager.subscriber.getLocalDescription();
|
16846
|
+
const previousOffer = this.pcManager.subscriber.getRemoteDescription();
|
16847
|
+
/* 1. autosubscribe on, so subscribed tracks = all tracks - unsub tracks,
|
16848
|
+
in this case, we send unsub tracks, so server add all tracks to this
|
16849
|
+
subscribe pc and unsub special tracks from it.
|
16850
|
+
2. autosubscribe off, we send subscribed tracks.
|
16851
|
+
*/
|
16852
|
+
const autoSubscribe = (_b = (_a = this.signalOpts) === null || _a === void 0 ? void 0 : _a.autoSubscribe) !== null && _b !== void 0 ? _b : true;
|
16853
|
+
const trackSids = new Array();
|
16854
|
+
const trackSidsDisabled = new Array();
|
16855
|
+
remoteTracks.forEach(track => {
|
16856
|
+
if (track.isDesired !== autoSubscribe) {
|
16857
|
+
trackSids.push(track.trackSid);
|
16753
16858
|
}
|
16754
|
-
|
16755
|
-
|
16756
|
-
yield _super.handleAppVisibilityChanged.call(this);
|
16757
|
-
if (!isMobile()) return;
|
16758
|
-
if (this.isInBackground && this.source === Track.Source.Camera) {
|
16759
|
-
this._mediaStreamTrack.enabled = false;
|
16859
|
+
if (!track.isEnabled) {
|
16860
|
+
trackSidsDisabled.push(track.trackSid);
|
16760
16861
|
}
|
16761
16862
|
});
|
16762
|
-
|
16763
|
-
|
16764
|
-
|
16765
|
-
|
16766
|
-
|
16767
|
-
|
16768
|
-
|
16769
|
-
|
16770
|
-
|
16863
|
+
this.client.sendSyncState(new SyncState({
|
16864
|
+
answer: previousAnswer ? toProtoSessionDescription({
|
16865
|
+
sdp: previousAnswer.sdp,
|
16866
|
+
type: previousAnswer.type
|
16867
|
+
}) : undefined,
|
16868
|
+
offer: previousOffer ? toProtoSessionDescription({
|
16869
|
+
sdp: previousOffer.sdp,
|
16870
|
+
type: previousOffer.type
|
16871
|
+
}) : undefined,
|
16872
|
+
subscription: new UpdateSubscription({
|
16873
|
+
trackSids,
|
16874
|
+
subscribe: !autoSubscribe,
|
16875
|
+
participantTracks: []
|
16876
|
+
}),
|
16877
|
+
publishTracks: getTrackPublicationInfo(localTracks),
|
16878
|
+
dataChannels: this.dataChannelsInfo(),
|
16879
|
+
trackSidsDisabled
|
16771
16880
|
}));
|
16772
|
-
|
16773
|
-
|
16774
|
-
|
16775
|
-
|
16776
|
-
|
16777
|
-
|
16778
|
-
|
16779
|
-
|
16780
|
-
|
16781
|
-
|
16782
|
-
|
16783
|
-
|
16784
|
-
|
16785
|
-
|
16786
|
-
}
|
16787
|
-
let hasChanged = false;
|
16788
|
-
/* disable closable spatial layer as it has video blur / frozen issue with current server / client
|
16789
|
-
1. chrome 113: when switching to up layer with scalability Mode change, it will generate a
|
16790
|
-
low resolution frame and recover very quickly, but noticable
|
16791
|
-
2. livekit sfu: additional pli request cause video frozen for a few frames, also noticable */
|
16792
|
-
const closableSpatial = false;
|
16793
|
-
/* @ts-ignore */
|
16794
|
-
if (closableSpatial && encodings[0].scalabilityMode) ; else {
|
16795
|
-
// simulcast dynacast encodings
|
16796
|
-
encodings.forEach((encoding, idx) => {
|
16797
|
-
var _a;
|
16798
|
-
let rid = (_a = encoding.rid) !== null && _a !== void 0 ? _a : '';
|
16799
|
-
if (rid === '') {
|
16800
|
-
rid = 'q';
|
16801
|
-
}
|
16802
|
-
const quality = videoQualityForRid(rid);
|
16803
|
-
const subscribedQuality = qualities.find(q => q.quality === quality);
|
16804
|
-
if (!subscribedQuality) {
|
16805
|
-
return;
|
16806
|
-
}
|
16807
|
-
if (encoding.active !== subscribedQuality.enabled) {
|
16808
|
-
hasChanged = true;
|
16809
|
-
encoding.active = subscribedQuality.enabled;
|
16810
|
-
log.debug("setting layer ".concat(subscribedQuality.quality, " to ").concat(encoding.active ? 'enabled' : 'disabled'), logContext);
|
16811
|
-
// FireFox does not support setting encoding.active to false, so we
|
16812
|
-
// have a workaround of lowering its bitrate and resolution to the min.
|
16813
|
-
if (isFireFox()) {
|
16814
|
-
if (subscribedQuality.enabled) {
|
16815
|
-
encoding.scaleResolutionDownBy = senderEncodings[idx].scaleResolutionDownBy;
|
16816
|
-
encoding.maxBitrate = senderEncodings[idx].maxBitrate;
|
16817
|
-
/* @ts-ignore */
|
16818
|
-
encoding.maxFrameRate = senderEncodings[idx].maxFrameRate;
|
16819
|
-
} else {
|
16820
|
-
encoding.scaleResolutionDownBy = 4;
|
16821
|
-
encoding.maxBitrate = 10;
|
16822
|
-
/* @ts-ignore */
|
16823
|
-
encoding.maxFrameRate = 2;
|
16824
|
-
}
|
16825
|
-
}
|
16826
|
-
}
|
16827
|
-
});
|
16828
|
-
}
|
16829
|
-
if (hasChanged) {
|
16830
|
-
params.encodings = encodings;
|
16831
|
-
log.debug("setting encodings", Object.assign(Object.assign({}, logContext), {
|
16832
|
-
encodings: params.encodings
|
16881
|
+
}
|
16882
|
+
/* @internal */
|
16883
|
+
failNext() {
|
16884
|
+
// debugging method to fail the next reconnect/resume attempt
|
16885
|
+
this.shouldFailNext = true;
|
16886
|
+
}
|
16887
|
+
dataChannelsInfo() {
|
16888
|
+
const infos = [];
|
16889
|
+
const getInfo = (dc, target) => {
|
16890
|
+
if ((dc === null || dc === void 0 ? void 0 : dc.id) !== undefined && dc.id !== null) {
|
16891
|
+
infos.push(new DataChannelInfo({
|
16892
|
+
label: dc.label,
|
16893
|
+
id: dc.id,
|
16894
|
+
target
|
16833
16895
|
}));
|
16834
|
-
yield sender.setParameters(params);
|
16835
16896
|
}
|
16836
|
-
}
|
16837
|
-
|
16897
|
+
};
|
16898
|
+
getInfo(this.dataChannelForKind(DataPacket_Kind.LOSSY), SignalTarget.PUBLISHER);
|
16899
|
+
getInfo(this.dataChannelForKind(DataPacket_Kind.RELIABLE), SignalTarget.PUBLISHER);
|
16900
|
+
getInfo(this.dataChannelForKind(DataPacket_Kind.LOSSY, true), SignalTarget.SUBSCRIBER);
|
16901
|
+
getInfo(this.dataChannelForKind(DataPacket_Kind.RELIABLE, true), SignalTarget.SUBSCRIBER);
|
16902
|
+
return infos;
|
16903
|
+
}
|
16904
|
+
clearReconnectTimeout() {
|
16905
|
+
if (this.reconnectTimeout) {
|
16906
|
+
CriticalTimers.clearTimeout(this.reconnectTimeout);
|
16838
16907
|
}
|
16839
|
-
});
|
16840
|
-
}
|
16841
|
-
function videoQualityForRid(rid) {
|
16842
|
-
switch (rid) {
|
16843
|
-
case 'f':
|
16844
|
-
return VideoQuality.HIGH;
|
16845
|
-
case 'h':
|
16846
|
-
return VideoQuality.MEDIUM;
|
16847
|
-
case 'q':
|
16848
|
-
return VideoQuality.LOW;
|
16849
|
-
default:
|
16850
|
-
return VideoQuality.HIGH;
|
16851
16908
|
}
|
16852
|
-
|
16853
|
-
|
16854
|
-
|
16855
|
-
if (!encodings) {
|
16856
|
-
return [new VideoLayer({
|
16857
|
-
quality: VideoQuality.HIGH,
|
16858
|
-
width,
|
16859
|
-
height,
|
16860
|
-
bitrate: 0,
|
16861
|
-
ssrc: 0
|
16862
|
-
})];
|
16909
|
+
clearPendingReconnect() {
|
16910
|
+
this.clearReconnectTimeout();
|
16911
|
+
this.reconnectAttempts = 0;
|
16863
16912
|
}
|
16864
|
-
|
16865
|
-
|
16866
|
-
|
16867
|
-
const encodingSM = encodings[0].scalabilityMode;
|
16868
|
-
const sm = new ScalabilityMode(encodingSM);
|
16869
|
-
const layers = [];
|
16870
|
-
const resRatio = sm.suffix == 'h' ? 1.5 : 2;
|
16871
|
-
const bitratesRatio = sm.suffix == 'h' ? 2 : 3;
|
16872
|
-
for (let i = 0; i < sm.spatial; i += 1) {
|
16873
|
-
layers.push(new VideoLayer({
|
16874
|
-
quality: VideoQuality.HIGH - i,
|
16875
|
-
width: Math.ceil(width / Math.pow(resRatio, i)),
|
16876
|
-
height: Math.ceil(height / Math.pow(resRatio, i)),
|
16877
|
-
bitrate: encodings[0].maxBitrate ? Math.ceil(encodings[0].maxBitrate / Math.pow(bitratesRatio, i)) : 0,
|
16878
|
-
ssrc: 0
|
16879
|
-
}));
|
16913
|
+
registerOnLineListener() {
|
16914
|
+
if (isWeb()) {
|
16915
|
+
window.addEventListener('online', this.handleBrowserOnLine);
|
16880
16916
|
}
|
16881
|
-
return layers;
|
16882
16917
|
}
|
16883
|
-
|
16884
|
-
|
16885
|
-
|
16886
|
-
|
16887
|
-
|
16888
|
-
|
16889
|
-
|
16890
|
-
|
16891
|
-
|
16892
|
-
|
16918
|
+
deregisterOnLineListener() {
|
16919
|
+
if (isWeb()) {
|
16920
|
+
window.removeEventListener('online', this.handleBrowserOnLine);
|
16921
|
+
}
|
16922
|
+
}
|
16923
|
+
}
|
16924
|
+
class SignalReconnectError extends Error {}
|
16925
|
+
|
16926
|
+
class RegionUrlProvider {
|
16927
|
+
constructor(url, token) {
|
16928
|
+
this.lastUpdateAt = 0;
|
16929
|
+
this.settingsCacheTime = 3000;
|
16930
|
+
this.attemptedRegions = [];
|
16931
|
+
this.serverUrl = new URL(url);
|
16932
|
+
this.token = token;
|
16933
|
+
}
|
16934
|
+
updateToken(token) {
|
16935
|
+
this.token = token;
|
16936
|
+
}
|
16937
|
+
isCloud() {
|
16938
|
+
return isCloud(this.serverUrl);
|
16939
|
+
}
|
16940
|
+
getServerUrl() {
|
16941
|
+
return this.serverUrl;
|
16942
|
+
}
|
16943
|
+
getNextBestRegionUrl(abortSignal) {
|
16944
|
+
return __awaiter(this, void 0, void 0, function* () {
|
16945
|
+
if (!this.isCloud()) {
|
16946
|
+
throw Error('region availability is only supported for LiveKit Cloud domains');
|
16947
|
+
}
|
16948
|
+
if (!this.regionSettings || Date.now() - this.lastUpdateAt > this.settingsCacheTime) {
|
16949
|
+
this.regionSettings = yield this.fetchRegionSettings(abortSignal);
|
16950
|
+
}
|
16951
|
+
const regionsLeft = this.regionSettings.regions.filter(region => !this.attemptedRegions.find(attempted => attempted.url === region.url));
|
16952
|
+
if (regionsLeft.length > 0) {
|
16953
|
+
const nextRegion = regionsLeft[0];
|
16954
|
+
this.attemptedRegions.push(nextRegion);
|
16955
|
+
livekitLogger.debug("next region: ".concat(nextRegion.region));
|
16956
|
+
return nextRegion.url;
|
16957
|
+
} else {
|
16958
|
+
return null;
|
16959
|
+
}
|
16893
16960
|
});
|
16894
|
-
}
|
16961
|
+
}
|
16962
|
+
resetAttempts() {
|
16963
|
+
this.attemptedRegions = [];
|
16964
|
+
}
|
16965
|
+
/* @internal */
|
16966
|
+
fetchRegionSettings(signal) {
|
16967
|
+
return __awaiter(this, void 0, void 0, function* () {
|
16968
|
+
const regionSettingsResponse = yield fetch("".concat(getCloudConfigUrl(this.serverUrl), "/regions"), {
|
16969
|
+
headers: {
|
16970
|
+
authorization: "Bearer ".concat(this.token)
|
16971
|
+
},
|
16972
|
+
signal
|
16973
|
+
});
|
16974
|
+
if (regionSettingsResponse.ok) {
|
16975
|
+
const regionSettings = yield regionSettingsResponse.json();
|
16976
|
+
this.lastUpdateAt = Date.now();
|
16977
|
+
return regionSettings;
|
16978
|
+
} else {
|
16979
|
+
throw new ConnectionError("Could not fetch region settings: ".concat(regionSettingsResponse.statusText), regionSettingsResponse.status === 401 ? 0 /* ConnectionErrorReason.NotAllowed */ : undefined, regionSettingsResponse.status);
|
16980
|
+
}
|
16981
|
+
});
|
16982
|
+
}
|
16983
|
+
}
|
16984
|
+
function getCloudConfigUrl(serverUrl) {
|
16985
|
+
return "".concat(serverUrl.protocol.replace('ws', 'http'), "//").concat(serverUrl.host, "/settings");
|
16895
16986
|
}
|
16896
16987
|
|
16897
16988
|
class RemoteTrack extends Track {
|
@@ -16953,6 +17044,19 @@ class RemoteTrack extends Track {
|
|
16953
17044
|
if (!this.monitorInterval) {
|
16954
17045
|
this.monitorInterval = setInterval(() => this.monitorReceiver(), monitorFrequency);
|
16955
17046
|
}
|
17047
|
+
this.registerTimeSyncUpdate();
|
17048
|
+
}
|
17049
|
+
registerTimeSyncUpdate() {
|
17050
|
+
const loop = () => {
|
17051
|
+
var _a, _b;
|
17052
|
+
this.timeSyncHandle = requestAnimationFrame(() => loop());
|
17053
|
+
const newTime = (_b = (_a = this.receiver) === null || _a === void 0 ? void 0 : _a.getSynchronizationSources()[0]) === null || _b === void 0 ? void 0 : _b.rtpTimestamp;
|
17054
|
+
if (newTime && this.rtpTimestamp !== newTime) {
|
17055
|
+
this.emit(TrackEvent.TimeSyncUpdate, newTime);
|
17056
|
+
this.rtpTimestamp = newTime;
|
17057
|
+
}
|
17058
|
+
};
|
17059
|
+
loop();
|
16956
17060
|
}
|
16957
17061
|
}
|
16958
17062
|
|
@@ -18468,9 +18572,8 @@ class LocalParticipant extends Participant {
|
|
18468
18572
|
(_d = options.red) !== null && _d !== void 0 ? _d : options.red = false;
|
18469
18573
|
}
|
18470
18574
|
const opts = Object.assign(Object.assign({}, this.roomOptions.publishDefaults), options);
|
18471
|
-
|
18472
|
-
|
18473
|
-
this.log.info("End-to-end encryption is set up, simulcast publishing will be disabled on Safari", Object.assign({}, this.logContext));
|
18575
|
+
if (!isE2EESimulcastSupported() && this.roomOptions.e2ee) {
|
18576
|
+
this.log.info("End-to-end encryption is set up, simulcast publishing will be disabled on Safari versions and iOS browsers running iOS < v17.2", Object.assign({}, this.logContext));
|
18474
18577
|
opts.simulcast = false;
|
18475
18578
|
}
|
18476
18579
|
if (opts.source) {
|
@@ -18630,7 +18733,6 @@ class LocalParticipant extends Participant {
|
|
18630
18733
|
this.log.debug('falling back to server selected codec', Object.assign(Object.assign(Object.assign({}, this.logContext), getLogContextFromTrack(track)), {
|
18631
18734
|
codec: updatedCodec
|
18632
18735
|
}));
|
18633
|
-
/* @ts-ignore */
|
18634
18736
|
opts.videoCodec = updatedCodec;
|
18635
18737
|
// recompute encodings since bitrates/etc could have changed
|
18636
18738
|
encodings = computeVideoEncodings(track.source === Track.Source.ScreenShare, req.width, req.height, opts);
|
@@ -20046,6 +20148,16 @@ class Room extends eventsExports.EventEmitter {
|
|
20046
20148
|
// also emit on the participant
|
20047
20149
|
participant === null || participant === void 0 ? void 0 : participant.emit(ParticipantEvent.DataReceived, userPacket.payload, kind);
|
20048
20150
|
};
|
20151
|
+
this.bufferedSegments = new Map();
|
20152
|
+
this.handleTranscription = transcription => {
|
20153
|
+
// find the participant
|
20154
|
+
const participant = transcription.participantIdentity === this.localParticipant.identity ? this.localParticipant : this.remoteParticipants.get(transcription.participantIdentity);
|
20155
|
+
const publication = participant === null || participant === void 0 ? void 0 : participant.trackPublications.get(transcription.trackId);
|
20156
|
+
const segments = extractTranscriptionSegments(transcription);
|
20157
|
+
publication === null || publication === void 0 ? void 0 : publication.emit(TrackEvent.TranscriptionReceived, segments);
|
20158
|
+
participant === null || participant === void 0 ? void 0 : participant.emit(ParticipantEvent.TranscriptionReceived, segments, publication);
|
20159
|
+
this.emit(RoomEvent.TranscriptionReceived, segments, participant, publication);
|
20160
|
+
};
|
20049
20161
|
this.handleAudioPlaybackStarted = () => {
|
20050
20162
|
if (this.canPlaybackAudio) {
|
20051
20163
|
return;
|
@@ -20271,7 +20383,7 @@ class Room extends eventsExports.EventEmitter {
|
|
20271
20383
|
this.onTrackAdded(mediaTrack, stream, receiver);
|
20272
20384
|
}).on(EngineEvent.Disconnected, reason => {
|
20273
20385
|
this.handleDisconnect(this.options.stopLocalTrackOnUnpublish, reason);
|
20274
|
-
}).on(EngineEvent.ActiveSpeakersUpdate, this.handleActiveSpeakersUpdate).on(EngineEvent.DataPacketReceived, this.handleDataPacket).on(EngineEvent.Resuming, () => {
|
20386
|
+
}).on(EngineEvent.ActiveSpeakersUpdate, this.handleActiveSpeakersUpdate).on(EngineEvent.DataPacketReceived, this.handleDataPacket).on(EngineEvent.TranscriptionReceived, this.handleTranscription).on(EngineEvent.Resuming, () => {
|
20275
20387
|
this.clearConnectionReconcile();
|
20276
20388
|
this.isResuming = true;
|
20277
20389
|
this.log.info('Resuming signal connection', this.logContext);
|