livekit-client 1.15.10 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -17
- package/dist/livekit-client.esm.mjs +1603 -1493
- package/dist/livekit-client.esm.mjs.map +1 -1
- package/dist/livekit-client.umd.js +1 -1
- package/dist/livekit-client.umd.js.map +1 -1
- package/dist/src/api/SignalClient.d.ts +1 -3
- package/dist/src/api/SignalClient.d.ts.map +1 -1
- package/dist/src/index.d.ts +3 -3
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/options.d.ts +3 -9
- package/dist/src/options.d.ts.map +1 -1
- package/dist/src/proto/livekit_models_pb.d.ts +47 -0
- package/dist/src/proto/livekit_models_pb.d.ts.map +1 -1
- package/dist/src/room/RTCEngine.d.ts +1 -0
- package/dist/src/room/RTCEngine.d.ts.map +1 -1
- package/dist/src/room/Room.d.ts +14 -16
- package/dist/src/room/Room.d.ts.map +1 -1
- package/dist/src/room/defaults.d.ts.map +1 -1
- package/dist/src/room/events.d.ts +0 -4
- package/dist/src/room/events.d.ts.map +1 -1
- package/dist/src/room/participant/LocalParticipant.d.ts +8 -25
- package/dist/src/room/participant/LocalParticipant.d.ts.map +1 -1
- package/dist/src/room/participant/Participant.d.ts +6 -10
- package/dist/src/room/participant/Participant.d.ts.map +1 -1
- package/dist/src/room/participant/RemoteParticipant.d.ts +9 -6
- package/dist/src/room/participant/RemoteParticipant.d.ts.map +1 -1
- package/dist/src/room/timers.d.ts +4 -5
- package/dist/src/room/timers.d.ts.map +1 -1
- package/dist/src/room/track/LocalVideoTrack.d.ts +3 -3
- package/dist/src/room/track/LocalVideoTrack.d.ts.map +1 -1
- package/dist/src/room/track/RemoteTrackPublication.d.ts +2 -2
- package/dist/src/room/track/RemoteTrackPublication.d.ts.map +1 -1
- package/dist/src/room/track/Track.d.ts +5 -0
- package/dist/src/room/track/Track.d.ts.map +1 -1
- package/dist/src/room/track/options.d.ts +0 -5
- package/dist/src/room/track/options.d.ts.map +1 -1
- package/dist/src/room/types.d.ts +11 -3
- package/dist/src/room/types.d.ts.map +1 -1
- package/dist/src/version.d.ts +1 -1
- package/dist/ts4.2/src/api/SignalClient.d.ts +1 -3
- package/dist/ts4.2/src/index.d.ts +3 -3
- package/dist/ts4.2/src/options.d.ts +3 -9
- package/dist/ts4.2/src/proto/livekit_models_pb.d.ts +47 -0
- package/dist/ts4.2/src/room/RTCEngine.d.ts +1 -0
- package/dist/ts4.2/src/room/Room.d.ts +14 -16
- package/dist/ts4.2/src/room/events.d.ts +0 -4
- package/dist/ts4.2/src/room/participant/LocalParticipant.d.ts +8 -25
- package/dist/ts4.2/src/room/participant/Participant.d.ts +6 -10
- package/dist/ts4.2/src/room/participant/RemoteParticipant.d.ts +9 -6
- package/dist/ts4.2/src/room/timers.d.ts +4 -5
- package/dist/ts4.2/src/room/track/LocalVideoTrack.d.ts +3 -3
- package/dist/ts4.2/src/room/track/RemoteTrackPublication.d.ts +2 -2
- package/dist/ts4.2/src/room/track/Track.d.ts +5 -0
- package/dist/ts4.2/src/room/track/options.d.ts +0 -5
- package/dist/ts4.2/src/room/types.d.ts +11 -3
- package/dist/ts4.2/src/version.d.ts +1 -1
- package/package.json +8 -7
- package/src/api/SignalClient.ts +10 -10
- package/src/e2ee/E2eeManager.ts +2 -2
- package/src/index.ts +2 -4
- package/src/options.ts +3 -10
- package/src/proto/livekit_models_pb.ts +66 -0
- package/src/room/RTCEngine.ts +6 -1
- package/src/room/Room.ts +169 -129
- package/src/room/defaults.ts +1 -5
- package/src/room/events.ts +0 -5
- package/src/room/participant/LocalParticipant.ts +36 -77
- package/src/room/participant/Participant.ts +23 -24
- package/src/room/participant/RemoteParticipant.ts +27 -24
- package/src/room/track/LocalVideoTrack.test.ts +1 -1
- package/src/room/track/LocalVideoTrack.ts +11 -7
- package/src/room/track/RemoteTrackPublication.ts +2 -7
- package/src/room/track/Track.ts +10 -1
- package/src/room/track/options.ts +0 -6
- package/src/room/types.ts +11 -4
- package/src/version.ts +1 -1
@@ -3739,7 +3739,7 @@ proto3.util.setEnumType(TrackSource, "livekit.TrackSource", [{
|
|
3739
3739
|
/**
|
3740
3740
|
* @generated from enum livekit.VideoQuality
|
3741
3741
|
*/
|
3742
|
-
var VideoQuality;
|
3742
|
+
var VideoQuality$1;
|
3743
3743
|
(function (VideoQuality) {
|
3744
3744
|
/**
|
3745
3745
|
* @generated from enum value: LOW = 0;
|
@@ -3757,9 +3757,9 @@ var VideoQuality;
|
|
3757
3757
|
* @generated from enum value: OFF = 3;
|
3758
3758
|
*/
|
3759
3759
|
VideoQuality[VideoQuality["OFF"] = 3] = "OFF";
|
3760
|
-
})(VideoQuality || (VideoQuality = {}));
|
3760
|
+
})(VideoQuality$1 || (VideoQuality$1 = {}));
|
3761
3761
|
// Retrieve enum metadata with: proto3.getEnumType(VideoQuality)
|
3762
|
-
proto3.util.setEnumType(VideoQuality, "livekit.VideoQuality", [{
|
3762
|
+
proto3.util.setEnumType(VideoQuality$1, "livekit.VideoQuality", [{
|
3763
3763
|
no: 0,
|
3764
3764
|
name: "LOW"
|
3765
3765
|
}, {
|
@@ -4096,6 +4096,11 @@ Room$1.fields = proto3.util.newFieldList(() => [{
|
|
4096
4096
|
name: "active_recording",
|
4097
4097
|
kind: "scalar",
|
4098
4098
|
T: 8 /* ScalarType.BOOL */
|
4099
|
+
}, {
|
4100
|
+
no: 13,
|
4101
|
+
name: "version",
|
4102
|
+
kind: "message",
|
4103
|
+
T: TimedVersion
|
4099
4104
|
}]);
|
4100
4105
|
/**
|
4101
4106
|
* @generated from message livekit.Codec
|
@@ -4354,6 +4359,10 @@ class ParticipantInfo extends Message {
|
|
4354
4359
|
* @generated from field: bool is_publisher = 13;
|
4355
4360
|
*/
|
4356
4361
|
this.isPublisher = false;
|
4362
|
+
/**
|
4363
|
+
* @generated from field: livekit.ParticipantInfo.Kind kind = 14;
|
4364
|
+
*/
|
4365
|
+
this.kind = ParticipantInfo_Kind.STANDARD;
|
4357
4366
|
proto3.util.initPartial(data, this);
|
4358
4367
|
}
|
4359
4368
|
static fromBinary(bytes, options) {
|
@@ -4427,6 +4436,11 @@ ParticipantInfo.fields = proto3.util.newFieldList(() => [{
|
|
4427
4436
|
name: "is_publisher",
|
4428
4437
|
kind: "scalar",
|
4429
4438
|
T: 8 /* ScalarType.BOOL */
|
4439
|
+
}, {
|
4440
|
+
no: 14,
|
4441
|
+
name: "kind",
|
4442
|
+
kind: "enum",
|
4443
|
+
T: proto3.getEnumType(ParticipantInfo_Kind)
|
4430
4444
|
}]);
|
4431
4445
|
/**
|
4432
4446
|
* @generated from enum livekit.ParticipantInfo.State
|
@@ -4472,6 +4486,59 @@ proto3.util.setEnumType(ParticipantInfo_State, "livekit.ParticipantInfo.State",
|
|
4472
4486
|
no: 3,
|
4473
4487
|
name: "DISCONNECTED"
|
4474
4488
|
}]);
|
4489
|
+
/**
|
4490
|
+
* @generated from enum livekit.ParticipantInfo.Kind
|
4491
|
+
*/
|
4492
|
+
var ParticipantInfo_Kind;
|
4493
|
+
(function (ParticipantInfo_Kind) {
|
4494
|
+
/**
|
4495
|
+
* standard participants, e.g. web clients
|
4496
|
+
*
|
4497
|
+
* @generated from enum value: STANDARD = 0;
|
4498
|
+
*/
|
4499
|
+
ParticipantInfo_Kind[ParticipantInfo_Kind["STANDARD"] = 0] = "STANDARD";
|
4500
|
+
/**
|
4501
|
+
* only ingests streams
|
4502
|
+
*
|
4503
|
+
* @generated from enum value: INGRESS = 1;
|
4504
|
+
*/
|
4505
|
+
ParticipantInfo_Kind[ParticipantInfo_Kind["INGRESS"] = 1] = "INGRESS";
|
4506
|
+
/**
|
4507
|
+
* only consumes streams
|
4508
|
+
*
|
4509
|
+
* @generated from enum value: EGRESS = 2;
|
4510
|
+
*/
|
4511
|
+
ParticipantInfo_Kind[ParticipantInfo_Kind["EGRESS"] = 2] = "EGRESS";
|
4512
|
+
/**
|
4513
|
+
* SIP participants
|
4514
|
+
*
|
4515
|
+
* @generated from enum value: SIP = 3;
|
4516
|
+
*/
|
4517
|
+
ParticipantInfo_Kind[ParticipantInfo_Kind["SIP"] = 3] = "SIP";
|
4518
|
+
/**
|
4519
|
+
* LiveKit agents
|
4520
|
+
*
|
4521
|
+
* @generated from enum value: AGENT = 4;
|
4522
|
+
*/
|
4523
|
+
ParticipantInfo_Kind[ParticipantInfo_Kind["AGENT"] = 4] = "AGENT";
|
4524
|
+
})(ParticipantInfo_Kind || (ParticipantInfo_Kind = {}));
|
4525
|
+
// Retrieve enum metadata with: proto3.getEnumType(ParticipantInfo_Kind)
|
4526
|
+
proto3.util.setEnumType(ParticipantInfo_Kind, "livekit.ParticipantInfo.Kind", [{
|
4527
|
+
no: 0,
|
4528
|
+
name: "STANDARD"
|
4529
|
+
}, {
|
4530
|
+
no: 1,
|
4531
|
+
name: "INGRESS"
|
4532
|
+
}, {
|
4533
|
+
no: 2,
|
4534
|
+
name: "EGRESS"
|
4535
|
+
}, {
|
4536
|
+
no: 3,
|
4537
|
+
name: "SIP"
|
4538
|
+
}, {
|
4539
|
+
no: 4,
|
4540
|
+
name: "AGENT"
|
4541
|
+
}]);
|
4475
4542
|
/**
|
4476
4543
|
* @generated from message livekit.Encryption
|
4477
4544
|
*/
|
@@ -4779,6 +4846,11 @@ TrackInfo.fields = proto3.util.newFieldList(() => [{
|
|
4779
4846
|
name: "stream",
|
4780
4847
|
kind: "scalar",
|
4781
4848
|
T: 9 /* ScalarType.STRING */
|
4849
|
+
}, {
|
4850
|
+
no: 18,
|
4851
|
+
name: "version",
|
4852
|
+
kind: "message",
|
4853
|
+
T: TimedVersion
|
4782
4854
|
}]);
|
4783
4855
|
/**
|
4784
4856
|
* provide information about available spatial layers
|
@@ -4793,7 +4865,7 @@ class VideoLayer extends Message {
|
|
4793
4865
|
*
|
4794
4866
|
* @generated from field: livekit.VideoQuality quality = 1;
|
4795
4867
|
*/
|
4796
|
-
this.quality = VideoQuality.LOW;
|
4868
|
+
this.quality = VideoQuality$1.LOW;
|
4797
4869
|
/**
|
4798
4870
|
* @generated from field: uint32 width = 2;
|
4799
4871
|
*/
|
@@ -4833,7 +4905,7 @@ VideoLayer.fields = proto3.util.newFieldList(() => [{
|
|
4833
4905
|
no: 1,
|
4834
4906
|
name: "quality",
|
4835
4907
|
kind: "enum",
|
4836
|
-
T: proto3.getEnumType(VideoQuality)
|
4908
|
+
T: proto3.getEnumType(VideoQuality$1)
|
4837
4909
|
}, {
|
4838
4910
|
no: 2,
|
4839
4911
|
name: "width",
|
@@ -10045,10 +10117,6 @@ var RoomEvent;
|
|
10045
10117
|
* args: ([[ConnectionState]])
|
10046
10118
|
*/
|
10047
10119
|
RoomEvent["ConnectionStateChanged"] = "connectionStateChanged";
|
10048
|
-
/**
|
10049
|
-
* @deprecated StateChanged has been renamed to ConnectionStateChanged
|
10050
|
-
*/
|
10051
|
-
RoomEvent["StateChanged"] = "connectionStateChanged";
|
10052
10120
|
/**
|
10053
10121
|
* When input or output devices on the machine have changed.
|
10054
10122
|
*/
|
@@ -10614,10 +10682,10 @@ function getMatch(exp, ua) {
|
|
10614
10682
|
return match && match.length >= id && match[id] || '';
|
10615
10683
|
}
|
10616
10684
|
|
10617
|
-
var version$1 = "
|
10685
|
+
var version$1 = "2.0.0";
|
10618
10686
|
|
10619
10687
|
const version = version$1;
|
10620
|
-
const protocolVersion =
|
10688
|
+
const protocolVersion = 12;
|
10621
10689
|
|
10622
10690
|
/**
|
10623
10691
|
* Timers that can be overridden with platform specific implementations
|
@@ -11783,7 +11851,7 @@ class UpdateTrackSettings extends Message {
|
|
11783
11851
|
*
|
11784
11852
|
* @generated from field: livekit.VideoQuality quality = 4;
|
11785
11853
|
*/
|
11786
|
-
this.quality = VideoQuality.LOW;
|
11854
|
+
this.quality = VideoQuality$1.LOW;
|
11787
11855
|
/**
|
11788
11856
|
* for video, width to receive
|
11789
11857
|
*
|
@@ -11844,7 +11912,7 @@ UpdateTrackSettings.fields = proto3.util.newFieldList(() => [{
|
|
11844
11912
|
no: 4,
|
11845
11913
|
name: "quality",
|
11846
11914
|
kind: "enum",
|
11847
|
-
T: proto3.getEnumType(VideoQuality)
|
11915
|
+
T: proto3.getEnumType(VideoQuality$1)
|
11848
11916
|
}, {
|
11849
11917
|
no: 5,
|
11850
11918
|
name: "width",
|
@@ -12292,7 +12360,7 @@ class SubscribedQuality extends Message {
|
|
12292
12360
|
/**
|
12293
12361
|
* @generated from field: livekit.VideoQuality quality = 1;
|
12294
12362
|
*/
|
12295
|
-
this.quality = VideoQuality.LOW;
|
12363
|
+
this.quality = VideoQuality$1.LOW;
|
12296
12364
|
/**
|
12297
12365
|
* @generated from field: bool enabled = 2;
|
12298
12366
|
*/
|
@@ -12318,7 +12386,7 @@ SubscribedQuality.fields = proto3.util.newFieldList(() => [{
|
|
12318
12386
|
no: 1,
|
12319
12387
|
name: "quality",
|
12320
12388
|
kind: "enum",
|
12321
|
-
T: proto3.getEnumType(VideoQuality)
|
12389
|
+
T: proto3.getEnumType(VideoQuality$1)
|
12322
12390
|
}, {
|
12323
12391
|
no: 2,
|
12324
12392
|
name: "enabled",
|
@@ -12997,6 +13065,12 @@ const BACKGROUND_REACTION_DELAY = 5000;
|
|
12997
13065
|
// keep old audio elements when detached, we would re-use them since on iOS
|
12998
13066
|
// Safari tracks which audio elements have been "blessed" by the user.
|
12999
13067
|
const recycledElements = [];
|
13068
|
+
var VideoQuality;
|
13069
|
+
(function (VideoQuality) {
|
13070
|
+
VideoQuality[VideoQuality["LOW"] = 0] = "LOW";
|
13071
|
+
VideoQuality[VideoQuality["MEDIUM"] = 1] = "MEDIUM";
|
13072
|
+
VideoQuality[VideoQuality["HIGH"] = 2] = "HIGH";
|
13073
|
+
})(VideoQuality || (VideoQuality = {}));
|
13000
13074
|
class Track extends eventsExports.EventEmitter {
|
13001
13075
|
constructor(mediaTrack, kind) {
|
13002
13076
|
let loggerOptions = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
|
@@ -14548,8 +14622,8 @@ class E2EEManager extends eventsExports.EventEmitter {
|
|
14548
14622
|
room.on(RoomEvent.TrackPublished, (pub, participant) => this.setParticipantCryptorEnabled(pub.trackInfo.encryption !== Encryption_Type.NONE, participant.identity));
|
14549
14623
|
room.on(RoomEvent.ConnectionStateChanged, state => {
|
14550
14624
|
if (state === ConnectionState.Connected) {
|
14551
|
-
room.
|
14552
|
-
participant.
|
14625
|
+
room.remoteParticipants.forEach(participant => {
|
14626
|
+
participant.trackPublications.forEach(pub => {
|
14553
14627
|
this.setParticipantCryptorEnabled(pub.trackInfo.encryption !== Encryption_Type.NONE, participant.identity);
|
14554
14628
|
});
|
14555
14629
|
});
|
@@ -14938,7 +15012,7 @@ class SignalClient {
|
|
14938
15012
|
abortSignal === null || abortSignal === void 0 ? void 0 : abortSignal.addEventListener('abort', abortHandler);
|
14939
15013
|
this.log.debug("connecting to ".concat(url + params), this.logContext);
|
14940
15014
|
if (this.ws) {
|
14941
|
-
yield this.close();
|
15015
|
+
yield this.close(false);
|
14942
15016
|
}
|
14943
15017
|
this.ws = new WebSocket(url + params);
|
14944
15018
|
this.ws.binaryType = 'arraybuffer';
|
@@ -15025,6 +15099,8 @@ class SignalClient {
|
|
15025
15099
|
}
|
15026
15100
|
this.log.warn("websocket closed", Object.assign(Object.assign({}, this.logContext), {
|
15027
15101
|
reason: ev.reason,
|
15102
|
+
code: ev.code,
|
15103
|
+
wasClean: ev.wasClean,
|
15028
15104
|
state: this.state
|
15029
15105
|
}));
|
15030
15106
|
this.handleOnClose(ev.reason);
|
@@ -15035,10 +15111,13 @@ class SignalClient {
|
|
15035
15111
|
}));
|
15036
15112
|
}
|
15037
15113
|
close() {
|
15114
|
+
let updateState = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
|
15038
15115
|
return __awaiter(this, void 0, void 0, function* () {
|
15039
15116
|
const unlock = yield this.closingLock.lock();
|
15040
15117
|
try {
|
15041
|
-
|
15118
|
+
if (updateState) {
|
15119
|
+
this.state = SignalConnectionState.DISCONNECTING;
|
15120
|
+
}
|
15042
15121
|
if (this.ws) {
|
15043
15122
|
this.ws.onmessage = null;
|
15044
15123
|
this.ws.onopen = null;
|
@@ -15061,7 +15140,9 @@ class SignalClient {
|
|
15061
15140
|
this.ws = undefined;
|
15062
15141
|
}
|
15063
15142
|
} finally {
|
15064
|
-
|
15143
|
+
if (updateState) {
|
15144
|
+
this.state = SignalConnectionState.DISCONNECTED;
|
15145
|
+
}
|
15065
15146
|
this.clearPingInterval();
|
15066
15147
|
unlock();
|
15067
15148
|
}
|
@@ -15436,9 +15517,6 @@ function createConnectionParams(token, info, opts) {
|
|
15436
15517
|
if (info.browserVersion) {
|
15437
15518
|
params.set('browser_version', info.browserVersion);
|
15438
15519
|
}
|
15439
|
-
if (opts.publishOnly !== undefined) {
|
15440
|
-
params.set('publish', opts.publishOnly);
|
15441
|
-
}
|
15442
15520
|
if (opts.adaptiveStream) {
|
15443
15521
|
params.set('adaptive_stream', '1');
|
15444
15522
|
}
|
@@ -16652,10 +16730,6 @@ function extractStereoAndNackAudioFromOffer(offer) {
|
|
16652
16730
|
|
16653
16731
|
const defaultVideoCodec = 'vp8';
|
16654
16732
|
const publishDefaults = {
|
16655
|
-
/**
|
16656
|
-
* @deprecated
|
16657
|
-
*/
|
16658
|
-
audioBitrate: AudioPresets.music.maxBitrate,
|
16659
16733
|
audioPreset: AudioPresets.music,
|
16660
16734
|
dtx: true,
|
16661
16735
|
red: true,
|
@@ -16680,7 +16754,7 @@ const roomOptionDefaults = {
|
|
16680
16754
|
stopLocalTrackOnUnpublish: true,
|
16681
16755
|
reconnectPolicy: new DefaultReconnectPolicy(),
|
16682
16756
|
disconnectOnPageLeave: true,
|
16683
|
-
|
16757
|
+
webAudioMix: true
|
16684
16758
|
};
|
16685
16759
|
const roomConnectOptionDefaults = {
|
16686
16760
|
autoSubscribe: true,
|
@@ -16984,6 +17058,9 @@ class RTCEngine extends eventsExports.EventEmitter {
|
|
16984
17058
|
get isClosed() {
|
16985
17059
|
return this._isClosed;
|
16986
17060
|
}
|
17061
|
+
get pendingReconnect() {
|
17062
|
+
return !!this.reconnectTimeout;
|
17063
|
+
}
|
16987
17064
|
constructor(options) {
|
16988
17065
|
var _a;
|
16989
17066
|
super();
|
@@ -17108,7 +17185,7 @@ class RTCEngine extends eventsExports.EventEmitter {
|
|
17108
17185
|
// since the current engine may have inherited a regional url
|
17109
17186
|
this.regionUrlProvider.updateToken(this.token);
|
17110
17187
|
}
|
17111
|
-
this.reconnectTimeout = CriticalTimers.setTimeout(() => this.attemptReconnect(disconnectReason), delay);
|
17188
|
+
this.reconnectTimeout = CriticalTimers.setTimeout(() => this.attemptReconnect(disconnectReason).finally(() => this.reconnectTimeout = undefined), delay);
|
17112
17189
|
};
|
17113
17190
|
this.waitForRestarted = () => {
|
17114
17191
|
return new Promise((resolve, reject) => {
|
@@ -18953,7 +19030,8 @@ class LocalVideoTrack extends LocalTrack {
|
|
18953
19030
|
}
|
18954
19031
|
addSimulcastTrack(codec, encodings) {
|
18955
19032
|
if (this.simulcastCodecs.has(codec)) {
|
18956
|
-
|
19033
|
+
this.log.error("".concat(codec, " already added, skipping adding simulcast codec"), this.logContext);
|
19034
|
+
return;
|
18957
19035
|
}
|
18958
19036
|
const simulcastCodecInfo = {
|
18959
19037
|
codec,
|
@@ -19982,7 +20060,7 @@ class Participant extends eventsExports.EventEmitter {
|
|
19982
20060
|
});
|
19983
20061
|
}
|
19984
20062
|
get isEncrypted() {
|
19985
|
-
return this.
|
20063
|
+
return this.trackPublications.size > 0 && Array.from(this.trackPublications.values()).every(tr => tr.isEncrypted);
|
19986
20064
|
}
|
19987
20065
|
get isAgent() {
|
19988
20066
|
var _a, _b;
|
@@ -20005,21 +20083,19 @@ class Participant extends eventsExports.EventEmitter {
|
|
20005
20083
|
this.identity = identity;
|
20006
20084
|
this.name = name;
|
20007
20085
|
this.metadata = metadata;
|
20008
|
-
this.
|
20009
|
-
this.
|
20010
|
-
this.
|
20086
|
+
this.audioTrackPublications = new Map();
|
20087
|
+
this.videoTrackPublications = new Map();
|
20088
|
+
this.trackPublications = new Map();
|
20011
20089
|
}
|
20012
|
-
|
20013
|
-
return Array.from(this.
|
20090
|
+
getTrackPublications() {
|
20091
|
+
return Array.from(this.trackPublications.values());
|
20014
20092
|
}
|
20015
20093
|
/**
|
20016
20094
|
* Finds the first track that matches the source filter, for example, getting
|
20017
20095
|
* the user's camera track with getTrackBySource(Track.Source.Camera).
|
20018
|
-
* @param source
|
20019
|
-
* @returns
|
20020
20096
|
*/
|
20021
|
-
|
20022
|
-
for (const [, pub] of this.
|
20097
|
+
getTrackPublication(source) {
|
20098
|
+
for (const [, pub] of this.trackPublications) {
|
20023
20099
|
if (pub.source === source) {
|
20024
20100
|
return pub;
|
20025
20101
|
}
|
@@ -20027,11 +20103,9 @@ class Participant extends eventsExports.EventEmitter {
|
|
20027
20103
|
}
|
20028
20104
|
/**
|
20029
20105
|
* Finds the first track that matches the track's name.
|
20030
|
-
* @param name
|
20031
|
-
* @returns
|
20032
20106
|
*/
|
20033
|
-
|
20034
|
-
for (const [, pub] of this.
|
20107
|
+
getTrackPublicationByName(name) {
|
20108
|
+
for (const [, pub] of this.trackPublications) {
|
20035
20109
|
if (pub.trackName === name) {
|
20036
20110
|
return pub;
|
20037
20111
|
}
|
@@ -20042,16 +20116,16 @@ class Participant extends eventsExports.EventEmitter {
|
|
20042
20116
|
}
|
20043
20117
|
get isCameraEnabled() {
|
20044
20118
|
var _a;
|
20045
|
-
const track = this.
|
20119
|
+
const track = this.getTrackPublication(Track.Source.Camera);
|
20046
20120
|
return !((_a = track === null || track === void 0 ? void 0 : track.isMuted) !== null && _a !== void 0 ? _a : true);
|
20047
20121
|
}
|
20048
20122
|
get isMicrophoneEnabled() {
|
20049
20123
|
var _a;
|
20050
|
-
const track = this.
|
20124
|
+
const track = this.getTrackPublication(Track.Source.Microphone);
|
20051
20125
|
return !((_a = track === null || track === void 0 ? void 0 : track.isMuted) !== null && _a !== void 0 ? _a : true);
|
20052
20126
|
}
|
20053
20127
|
get isScreenShareEnabled() {
|
20054
|
-
const track = this.
|
20128
|
+
const track = this.getTrackPublication(Track.Source.ScreenShare);
|
20055
20129
|
return !!track;
|
20056
20130
|
}
|
20057
20131
|
get isLocal() {
|
@@ -20145,7 +20219,7 @@ class Participant extends eventsExports.EventEmitter {
|
|
20145
20219
|
*/
|
20146
20220
|
setAudioContext(ctx) {
|
20147
20221
|
this.audioContext = ctx;
|
20148
|
-
this.
|
20222
|
+
this.audioTrackPublications.forEach(track => (track.track instanceof RemoteAudioTrack || track.track instanceof LocalAudioTrack) && track.track.setAudioContext(ctx));
|
20149
20223
|
}
|
20150
20224
|
addTrackPublication(publication) {
|
20151
20225
|
// forward publication driven events
|
@@ -20159,13 +20233,13 @@ class Participant extends eventsExports.EventEmitter {
|
|
20159
20233
|
if (pub.track) {
|
20160
20234
|
pub.track.sid = publication.trackSid;
|
20161
20235
|
}
|
20162
|
-
this.
|
20236
|
+
this.trackPublications.set(publication.trackSid, publication);
|
20163
20237
|
switch (publication.kind) {
|
20164
20238
|
case Track.Kind.Audio:
|
20165
|
-
this.
|
20239
|
+
this.audioTrackPublications.set(publication.trackSid, publication);
|
20166
20240
|
break;
|
20167
20241
|
case Track.Kind.Video:
|
20168
|
-
this.
|
20242
|
+
this.videoTrackPublications.set(publication.trackSid, publication);
|
20169
20243
|
break;
|
20170
20244
|
}
|
20171
20245
|
}
|
@@ -20184,993 +20258,502 @@ function trackPermissionToProto(perms) {
|
|
20184
20258
|
});
|
20185
20259
|
}
|
20186
20260
|
|
20187
|
-
class
|
20188
|
-
|
20189
|
-
|
20190
|
-
|
20261
|
+
class LocalParticipant extends Participant {
|
20262
|
+
/** @internal */
|
20263
|
+
constructor(sid, identity, engine, options) {
|
20264
|
+
super(sid, identity, undefined, undefined, {
|
20265
|
+
loggerName: options.loggerName,
|
20266
|
+
loggerContextCb: () => this.engine.logContext
|
20267
|
+
});
|
20268
|
+
this.pendingPublishing = new Set();
|
20269
|
+
this.pendingPublishPromises = new Map();
|
20270
|
+
this.participantTrackPermissions = [];
|
20271
|
+
this.allParticipantsAllowedToSubscribe = true;
|
20272
|
+
this.encryptionType = Encryption_Type.NONE;
|
20273
|
+
this.handleReconnecting = () => {
|
20274
|
+
if (!this.reconnectFuture) {
|
20275
|
+
this.reconnectFuture = new Future();
|
20276
|
+
}
|
20277
|
+
};
|
20278
|
+
this.handleReconnected = () => {
|
20279
|
+
var _a, _b;
|
20280
|
+
(_b = (_a = this.reconnectFuture) === null || _a === void 0 ? void 0 : _a.resolve) === null || _b === void 0 ? void 0 : _b.call(_a);
|
20281
|
+
this.reconnectFuture = undefined;
|
20282
|
+
this.updateTrackSubscriptionPermissions();
|
20283
|
+
};
|
20284
|
+
this.handleDisconnected = () => {
|
20285
|
+
var _a, _b;
|
20286
|
+
if (this.reconnectFuture) {
|
20287
|
+
this.reconnectFuture.promise.catch(e => this.log.warn(e.message, this.logContext));
|
20288
|
+
(_b = (_a = this.reconnectFuture) === null || _a === void 0 ? void 0 : _a.reject) === null || _b === void 0 ? void 0 : _b.call(_a, 'Got disconnected during reconnection attempt');
|
20289
|
+
this.reconnectFuture = undefined;
|
20290
|
+
}
|
20291
|
+
};
|
20292
|
+
this.updateTrackSubscriptionPermissions = () => {
|
20293
|
+
this.log.debug('updating track subscription permissions', Object.assign(Object.assign({}, this.logContext), {
|
20294
|
+
allParticipantsAllowed: this.allParticipantsAllowedToSubscribe,
|
20295
|
+
participantTrackPermissions: this.participantTrackPermissions
|
20296
|
+
}));
|
20297
|
+
this.engine.client.sendUpdateSubscriptionPermissions(this.allParticipantsAllowedToSubscribe, this.participantTrackPermissions.map(p => trackPermissionToProto(p)));
|
20298
|
+
};
|
20191
20299
|
/** @internal */
|
20192
|
-
this.
|
20193
|
-
|
20194
|
-
this.currentVideoQuality = VideoQuality.HIGH;
|
20195
|
-
this.handleEnded = track => {
|
20196
|
-
this.setTrack(undefined);
|
20197
|
-
this.emit(TrackEvent.Ended, track);
|
20300
|
+
this.onTrackUnmuted = track => {
|
20301
|
+
this.onTrackMuted(track, track.isUpstreamPaused);
|
20198
20302
|
};
|
20199
|
-
|
20200
|
-
|
20201
|
-
|
20202
|
-
|
20303
|
+
// when the local track changes in mute status, we'll notify server as such
|
20304
|
+
/** @internal */
|
20305
|
+
this.onTrackMuted = (track, muted) => {
|
20306
|
+
if (muted === undefined) {
|
20307
|
+
muted = true;
|
20308
|
+
}
|
20309
|
+
if (!track.sid) {
|
20310
|
+
this.log.error('could not update mute status for unpublished track', Object.assign(Object.assign({}, this.logContext), getLogContextFromTrack(track)));
|
20311
|
+
return;
|
20312
|
+
}
|
20313
|
+
this.engine.updateMuteStatus(track.sid, muted);
|
20203
20314
|
};
|
20204
|
-
this.
|
20205
|
-
this.log.debug(
|
20206
|
-
this.
|
20207
|
-
this.emitTrackUpdate();
|
20315
|
+
this.onTrackUpstreamPaused = track => {
|
20316
|
+
this.log.debug('upstream paused', Object.assign(Object.assign({}, this.logContext), getLogContextFromTrack(track)));
|
20317
|
+
this.onTrackMuted(track, true);
|
20208
20318
|
};
|
20209
|
-
this.
|
20210
|
-
|
20319
|
+
this.onTrackUpstreamResumed = track => {
|
20320
|
+
this.log.debug('upstream resumed', Object.assign(Object.assign({}, this.logContext), getLogContextFromTrack(track)));
|
20321
|
+
this.onTrackMuted(track, track.isMuted);
|
20322
|
+
};
|
20323
|
+
this.handleSubscribedQualityUpdate = update => __awaiter(this, void 0, void 0, function* () {
|
20324
|
+
var _a, e_1, _b, _c;
|
20325
|
+
var _d, _e;
|
20326
|
+
if (!((_d = this.roomOptions) === null || _d === void 0 ? void 0 : _d.dynacast)) {
|
20327
|
+
return;
|
20328
|
+
}
|
20329
|
+
const pub = this.videoTrackPublications.get(update.trackSid);
|
20330
|
+
if (!pub) {
|
20331
|
+
this.log.warn('received subscribed quality update for unknown track', Object.assign(Object.assign({}, this.logContext), {
|
20332
|
+
trackSid: update.trackSid
|
20333
|
+
}));
|
20334
|
+
return;
|
20335
|
+
}
|
20336
|
+
if (update.subscribedCodecs.length > 0) {
|
20337
|
+
if (!pub.videoTrack) {
|
20338
|
+
return;
|
20339
|
+
}
|
20340
|
+
const newCodecs = yield pub.videoTrack.setPublishingCodecs(update.subscribedCodecs);
|
20341
|
+
try {
|
20342
|
+
for (var _f = true, newCodecs_1 = __asyncValues(newCodecs), newCodecs_1_1; newCodecs_1_1 = yield newCodecs_1.next(), _a = newCodecs_1_1.done, !_a; _f = true) {
|
20343
|
+
_c = newCodecs_1_1.value;
|
20344
|
+
_f = false;
|
20345
|
+
const codec = _c;
|
20346
|
+
if (isBackupCodec(codec)) {
|
20347
|
+
this.log.debug("publish ".concat(codec, " for ").concat(pub.videoTrack.sid), Object.assign(Object.assign({}, this.logContext), getLogContextFromTrack(pub)));
|
20348
|
+
yield this.publishAdditionalCodecForTrack(pub.videoTrack, codec, pub.options);
|
20349
|
+
}
|
20350
|
+
}
|
20351
|
+
} catch (e_1_1) {
|
20352
|
+
e_1 = {
|
20353
|
+
error: e_1_1
|
20354
|
+
};
|
20355
|
+
} finally {
|
20356
|
+
try {
|
20357
|
+
if (!_f && !_a && (_b = newCodecs_1.return)) yield _b.call(newCodecs_1);
|
20358
|
+
} finally {
|
20359
|
+
if (e_1) throw e_1.error;
|
20360
|
+
}
|
20361
|
+
}
|
20362
|
+
} else if (update.subscribedQualities.length > 0) {
|
20363
|
+
yield (_e = pub.videoTrack) === null || _e === void 0 ? void 0 : _e.setPublishingLayers(update.subscribedQualities);
|
20364
|
+
}
|
20365
|
+
});
|
20366
|
+
this.handleLocalTrackUnpublished = unpublished => {
|
20367
|
+
const track = this.trackPublications.get(unpublished.trackSid);
|
20368
|
+
if (!track) {
|
20369
|
+
this.log.warn('received unpublished event for unknown track', Object.assign(Object.assign({}, this.logContext), {
|
20370
|
+
trackSid: unpublished.trackSid
|
20371
|
+
}));
|
20372
|
+
return;
|
20373
|
+
}
|
20374
|
+
this.unpublishTrack(track.track);
|
20375
|
+
};
|
20376
|
+
this.handleTrackEnded = track => __awaiter(this, void 0, void 0, function* () {
|
20377
|
+
if (track.source === Track.Source.ScreenShare || track.source === Track.Source.ScreenShareAudio) {
|
20378
|
+
this.log.debug('unpublishing local track due to TrackEnded', Object.assign(Object.assign({}, this.logContext), getLogContextFromTrack(track)));
|
20379
|
+
this.unpublishTrack(track);
|
20380
|
+
} else if (track.isUserProvided) {
|
20381
|
+
yield track.mute();
|
20382
|
+
} else if (track instanceof LocalAudioTrack || track instanceof LocalVideoTrack) {
|
20383
|
+
try {
|
20384
|
+
if (isWeb()) {
|
20385
|
+
try {
|
20386
|
+
const currentPermissions = yield navigator === null || navigator === void 0 ? void 0 : navigator.permissions.query({
|
20387
|
+
// the permission query for camera and microphone currently not supported in Safari and Firefox
|
20388
|
+
// @ts-ignore
|
20389
|
+
name: track.source === Track.Source.Camera ? 'camera' : 'microphone'
|
20390
|
+
});
|
20391
|
+
if (currentPermissions && currentPermissions.state === 'denied') {
|
20392
|
+
this.log.warn("user has revoked access to ".concat(track.source), Object.assign(Object.assign({}, this.logContext), getLogContextFromTrack(track)));
|
20393
|
+
// detect granted change after permissions were denied to try and resume then
|
20394
|
+
currentPermissions.onchange = () => {
|
20395
|
+
if (currentPermissions.state !== 'denied') {
|
20396
|
+
if (!track.isMuted) {
|
20397
|
+
track.restartTrack();
|
20398
|
+
}
|
20399
|
+
currentPermissions.onchange = null;
|
20400
|
+
}
|
20401
|
+
};
|
20402
|
+
throw new Error('GetUserMedia Permission denied');
|
20403
|
+
}
|
20404
|
+
} catch (e) {
|
20405
|
+
// permissions query fails for firefox, we continue and try to restart the track
|
20406
|
+
}
|
20407
|
+
}
|
20408
|
+
if (!track.isMuted) {
|
20409
|
+
this.log.debug('track ended, attempting to use a different device', Object.assign(Object.assign({}, this.logContext), getLogContextFromTrack(track)));
|
20410
|
+
yield track.restartTrack();
|
20411
|
+
}
|
20412
|
+
} catch (e) {
|
20413
|
+
this.log.warn("could not restart track, muting instead", Object.assign(Object.assign({}, this.logContext), getLogContextFromTrack(track)));
|
20414
|
+
yield track.mute();
|
20415
|
+
}
|
20416
|
+
}
|
20417
|
+
});
|
20418
|
+
this.audioTrackPublications = new Map();
|
20419
|
+
this.videoTrackPublications = new Map();
|
20420
|
+
this.trackPublications = new Map();
|
20421
|
+
this.engine = engine;
|
20422
|
+
this.roomOptions = options;
|
20423
|
+
this.setupEngine(engine);
|
20424
|
+
this.activeDeviceMap = new Map();
|
20425
|
+
}
|
20426
|
+
get lastCameraError() {
|
20427
|
+
return this.cameraError;
|
20428
|
+
}
|
20429
|
+
get lastMicrophoneError() {
|
20430
|
+
return this.microphoneError;
|
20431
|
+
}
|
20432
|
+
get isE2EEEnabled() {
|
20433
|
+
return this.encryptionType !== Encryption_Type.NONE;
|
20434
|
+
}
|
20435
|
+
getTrackPublication(source) {
|
20436
|
+
const track = super.getTrackPublication(source);
|
20437
|
+
if (track) {
|
20438
|
+
return track;
|
20439
|
+
}
|
20440
|
+
}
|
20441
|
+
getTrackPublicationByName(name) {
|
20442
|
+
const track = super.getTrackPublicationByName(name);
|
20443
|
+
if (track) {
|
20444
|
+
return track;
|
20445
|
+
}
|
20211
20446
|
}
|
20212
20447
|
/**
|
20213
|
-
*
|
20214
|
-
* @param subscribed true to subscribe to a track, false to unsubscribe
|
20448
|
+
* @internal
|
20215
20449
|
*/
|
20216
|
-
|
20217
|
-
|
20218
|
-
|
20219
|
-
|
20220
|
-
|
20221
|
-
|
20222
|
-
|
20223
|
-
|
20224
|
-
|
20225
|
-
|
20226
|
-
|
20227
|
-
|
20228
|
-
participantTracks: [new ParticipantTracks({
|
20229
|
-
// sending an empty participant id since TrackPublication doesn't keep it
|
20230
|
-
// this is filled in by the participant that receives this message
|
20231
|
-
participantSid: '',
|
20232
|
-
trackSids: [this.trackSid]
|
20233
|
-
})]
|
20450
|
+
setupEngine(engine) {
|
20451
|
+
this.engine = engine;
|
20452
|
+
this.engine.on(EngineEvent.RemoteMute, (trackSid, muted) => {
|
20453
|
+
const pub = this.trackPublications.get(trackSid);
|
20454
|
+
if (!pub || !pub.track) {
|
20455
|
+
return;
|
20456
|
+
}
|
20457
|
+
if (muted) {
|
20458
|
+
pub.mute();
|
20459
|
+
} else {
|
20460
|
+
pub.unmute();
|
20461
|
+
}
|
20234
20462
|
});
|
20235
|
-
this.
|
20236
|
-
this.emitSubscriptionUpdateIfChanged(prevStatus);
|
20237
|
-
this.emitPermissionUpdateIfChanged(prevPermission);
|
20238
|
-
}
|
20239
|
-
get subscriptionStatus() {
|
20240
|
-
if (this.subscribed === false) {
|
20241
|
-
return TrackPublication.SubscriptionStatus.Unsubscribed;
|
20242
|
-
}
|
20243
|
-
if (!super.isSubscribed) {
|
20244
|
-
return TrackPublication.SubscriptionStatus.Desired;
|
20245
|
-
}
|
20246
|
-
return TrackPublication.SubscriptionStatus.Subscribed;
|
20247
|
-
}
|
20248
|
-
get permissionStatus() {
|
20249
|
-
return this.allowed ? TrackPublication.PermissionStatus.Allowed : TrackPublication.PermissionStatus.NotAllowed;
|
20463
|
+
this.engine.on(EngineEvent.Connected, this.handleReconnected).on(EngineEvent.SignalRestarted, this.handleReconnected).on(EngineEvent.SignalResumed, this.handleReconnected).on(EngineEvent.Restarting, this.handleReconnecting).on(EngineEvent.Resuming, this.handleReconnecting).on(EngineEvent.LocalTrackUnpublished, this.handleLocalTrackUnpublished).on(EngineEvent.SubscribedQualityUpdate, this.handleSubscribedQualityUpdate).on(EngineEvent.Disconnected, this.handleDisconnected);
|
20250
20464
|
}
|
20251
20465
|
/**
|
20252
|
-
*
|
20466
|
+
* Sets and updates the metadata of the local participant.
|
20467
|
+
* The change does not take immediate effect.
|
20468
|
+
* If successful, a `ParticipantEvent.MetadataChanged` event will be emitted on the local participant.
|
20469
|
+
* Note: this requires `canUpdateOwnMetadata` permission.
|
20470
|
+
* @param metadata
|
20253
20471
|
*/
|
20254
|
-
|
20255
|
-
|
20256
|
-
|
20257
|
-
}
|
20258
|
-
return super.isSubscribed;
|
20259
|
-
}
|
20260
|
-
// returns client's desire to subscribe to a track, also true if autoSubscribe is enabled
|
20261
|
-
get isDesired() {
|
20262
|
-
return this.subscribed !== false;
|
20263
|
-
}
|
20264
|
-
get isEnabled() {
|
20265
|
-
return !this.disabled;
|
20472
|
+
setMetadata(metadata) {
|
20473
|
+
var _a;
|
20474
|
+
this.engine.client.sendUpdateLocalMetadata(metadata, (_a = this.name) !== null && _a !== void 0 ? _a : '');
|
20266
20475
|
}
|
20267
20476
|
/**
|
20268
|
-
*
|
20269
|
-
*
|
20270
|
-
*
|
20271
|
-
*
|
20477
|
+
* Sets and updates the name of the local participant.
|
20478
|
+
* The change does not take immediate effect.
|
20479
|
+
* If successful, a `ParticipantEvent.ParticipantNameChanged` event will be emitted on the local participant.
|
20480
|
+
* Note: this requires `canUpdateOwnMetadata` permission.
|
20481
|
+
* @param metadata
|
20272
20482
|
*/
|
20273
|
-
|
20274
|
-
|
20275
|
-
|
20276
|
-
}
|
20277
|
-
this.disabled = !enabled;
|
20278
|
-
this.emitTrackUpdate();
|
20483
|
+
setName(name) {
|
20484
|
+
var _a;
|
20485
|
+
this.engine.client.sendUpdateLocalMetadata((_a = this.metadata) !== null && _a !== void 0 ? _a : '', name);
|
20279
20486
|
}
|
20280
20487
|
/**
|
20281
|
-
*
|
20488
|
+
* Enable or disable a participant's camera track.
|
20282
20489
|
*
|
20283
|
-
*
|
20284
|
-
*
|
20285
|
-
* optimize for uninterrupted video
|
20490
|
+
* If a track has already published, it'll mute or unmute the track.
|
20491
|
+
* Resolves with a `LocalTrackPublication` instance if successful and `undefined` otherwise
|
20286
20492
|
*/
|
20287
|
-
|
20288
|
-
|
20289
|
-
return;
|
20290
|
-
}
|
20291
|
-
this.currentVideoQuality = quality;
|
20292
|
-
this.videoDimensions = undefined;
|
20293
|
-
this.emitTrackUpdate();
|
20294
|
-
}
|
20295
|
-
setVideoDimensions(dimensions) {
|
20296
|
-
var _a, _b;
|
20297
|
-
if (!this.isManualOperationAllowed()) {
|
20298
|
-
return;
|
20299
|
-
}
|
20300
|
-
if (((_a = this.videoDimensions) === null || _a === void 0 ? void 0 : _a.width) === dimensions.width && ((_b = this.videoDimensions) === null || _b === void 0 ? void 0 : _b.height) === dimensions.height) {
|
20301
|
-
return;
|
20302
|
-
}
|
20303
|
-
if (this.track instanceof RemoteVideoTrack) {
|
20304
|
-
this.videoDimensions = dimensions;
|
20305
|
-
}
|
20306
|
-
this.currentVideoQuality = undefined;
|
20307
|
-
this.emitTrackUpdate();
|
20493
|
+
setCameraEnabled(enabled, options, publishOptions) {
|
20494
|
+
return this.setTrackEnabled(Track.Source.Camera, enabled, options, publishOptions);
|
20308
20495
|
}
|
20309
|
-
|
20310
|
-
|
20311
|
-
|
20312
|
-
|
20313
|
-
|
20314
|
-
|
20315
|
-
|
20316
|
-
|
20317
|
-
return;
|
20318
|
-
}
|
20319
|
-
this.fps = fps;
|
20320
|
-
this.emitTrackUpdate();
|
20496
|
+
/**
|
20497
|
+
* Enable or disable a participant's microphone track.
|
20498
|
+
*
|
20499
|
+
* If a track has already published, it'll mute or unmute the track.
|
20500
|
+
* Resolves with a `LocalTrackPublication` instance if successful and `undefined` otherwise
|
20501
|
+
*/
|
20502
|
+
setMicrophoneEnabled(enabled, options, publishOptions) {
|
20503
|
+
return this.setTrackEnabled(Track.Source.Microphone, enabled, options, publishOptions);
|
20321
20504
|
}
|
20322
|
-
|
20323
|
-
|
20505
|
+
/**
|
20506
|
+
* Start or stop sharing a participant's screen
|
20507
|
+
* Resolves with a `LocalTrackPublication` instance if successful and `undefined` otherwise
|
20508
|
+
*/
|
20509
|
+
setScreenShareEnabled(enabled, options, publishOptions) {
|
20510
|
+
return this.setTrackEnabled(Track.Source.ScreenShare, enabled, options, publishOptions);
|
20324
20511
|
}
|
20325
20512
|
/** @internal */
|
20326
|
-
|
20327
|
-
const
|
20328
|
-
const
|
20329
|
-
|
20330
|
-
|
20331
|
-
return;
|
20332
|
-
}
|
20333
|
-
if (prevTrack) {
|
20334
|
-
// unregister listener
|
20335
|
-
prevTrack.off(TrackEvent.VideoDimensionsChanged, this.handleVideoDimensionsChange);
|
20336
|
-
prevTrack.off(TrackEvent.VisibilityChanged, this.handleVisibilityChange);
|
20337
|
-
prevTrack.off(TrackEvent.Ended, this.handleEnded);
|
20338
|
-
prevTrack.detach();
|
20339
|
-
prevTrack.stopMonitor();
|
20340
|
-
this.emit(TrackEvent.Unsubscribed, prevTrack);
|
20341
|
-
}
|
20342
|
-
super.setTrack(track);
|
20343
|
-
if (track) {
|
20344
|
-
track.sid = this.trackSid;
|
20345
|
-
track.on(TrackEvent.VideoDimensionsChanged, this.handleVideoDimensionsChange);
|
20346
|
-
track.on(TrackEvent.VisibilityChanged, this.handleVisibilityChange);
|
20347
|
-
track.on(TrackEvent.Ended, this.handleEnded);
|
20348
|
-
this.emit(TrackEvent.Subscribed, track);
|
20513
|
+
setPermissions(permissions) {
|
20514
|
+
const prevPermissions = this.permissions;
|
20515
|
+
const changed = super.setPermissions(permissions);
|
20516
|
+
if (changed && prevPermissions) {
|
20517
|
+
this.emit(ParticipantEvent.ParticipantPermissionsChanged, prevPermissions);
|
20349
20518
|
}
|
20350
|
-
|
20351
|
-
this.emitSubscriptionUpdateIfChanged(prevStatus);
|
20352
|
-
}
|
20353
|
-
/** @internal */
|
20354
|
-
setAllowed(allowed) {
|
20355
|
-
const prevStatus = this.subscriptionStatus;
|
20356
|
-
const prevPermission = this.permissionStatus;
|
20357
|
-
this.allowed = allowed;
|
20358
|
-
this.emitPermissionUpdateIfChanged(prevPermission);
|
20359
|
-
this.emitSubscriptionUpdateIfChanged(prevStatus);
|
20360
|
-
}
|
20361
|
-
/** @internal */
|
20362
|
-
setSubscriptionError(error) {
|
20363
|
-
this.emit(TrackEvent.SubscriptionFailed, error);
|
20519
|
+
return changed;
|
20364
20520
|
}
|
20365
20521
|
/** @internal */
|
20366
|
-
|
20367
|
-
|
20368
|
-
|
20369
|
-
|
20370
|
-
if (this.track) {
|
20371
|
-
this.track.setMuted(info.muted);
|
20372
|
-
} else if (prevMetadataMuted !== info.muted) {
|
20373
|
-
this.emit(info.muted ? TrackEvent.Muted : TrackEvent.Unmuted);
|
20374
|
-
}
|
20375
|
-
}
|
20376
|
-
emitSubscriptionUpdateIfChanged(previousStatus) {
|
20377
|
-
const currentStatus = this.subscriptionStatus;
|
20378
|
-
if (previousStatus === currentStatus) {
|
20379
|
-
return;
|
20380
|
-
}
|
20381
|
-
this.emit(TrackEvent.SubscriptionStatusChanged, currentStatus, previousStatus);
|
20382
|
-
}
|
20383
|
-
emitPermissionUpdateIfChanged(previousPermissionStatus) {
|
20384
|
-
const currentPermissionStatus = this.permissionStatus;
|
20385
|
-
if (currentPermissionStatus !== previousPermissionStatus) {
|
20386
|
-
this.emit(TrackEvent.SubscriptionPermissionChanged, this.permissionStatus, previousPermissionStatus);
|
20387
|
-
}
|
20388
|
-
}
|
20389
|
-
isManualOperationAllowed() {
|
20390
|
-
if (this.kind === Track.Kind.Video && this.isAdaptiveStream) {
|
20391
|
-
this.log.warn('adaptive stream is enabled, cannot change video track settings', this.logContext);
|
20392
|
-
return false;
|
20393
|
-
}
|
20394
|
-
if (!this.isDesired) {
|
20395
|
-
this.log.warn('cannot update track settings when not subscribed', this.logContext);
|
20396
|
-
return false;
|
20397
|
-
}
|
20398
|
-
return true;
|
20399
|
-
}
|
20400
|
-
get isAdaptiveStream() {
|
20401
|
-
return this.track instanceof RemoteVideoTrack && this.track.isAdaptiveStream;
|
20402
|
-
}
|
20403
|
-
/* @internal */
|
20404
|
-
emitTrackUpdate() {
|
20405
|
-
const settings = new UpdateTrackSettings({
|
20406
|
-
trackSids: [this.trackSid],
|
20407
|
-
disabled: this.disabled,
|
20408
|
-
fps: this.fps
|
20522
|
+
setE2EEEnabled(enabled) {
|
20523
|
+
return __awaiter(this, void 0, void 0, function* () {
|
20524
|
+
this.encryptionType = enabled ? Encryption_Type.GCM : Encryption_Type.NONE;
|
20525
|
+
yield this.republishAllTracks(undefined, false);
|
20409
20526
|
});
|
20410
|
-
if (this.videoDimensions) {
|
20411
|
-
settings.width = Math.ceil(this.videoDimensions.width);
|
20412
|
-
settings.height = Math.ceil(this.videoDimensions.height);
|
20413
|
-
} else if (this.currentVideoQuality !== undefined) {
|
20414
|
-
settings.quality = this.currentVideoQuality;
|
20415
|
-
} else {
|
20416
|
-
// defaults to high quality
|
20417
|
-
settings.quality = VideoQuality.HIGH;
|
20418
|
-
}
|
20419
|
-
this.emit(TrackEvent.UpdateSettings, settings);
|
20420
|
-
}
|
20421
|
-
}
|
20422
|
-
|
20423
|
-
class RemoteParticipant extends Participant {
|
20424
|
-
/** @internal */
|
20425
|
-
static fromParticipantInfo(signalClient, pi) {
|
20426
|
-
return new RemoteParticipant(signalClient, pi.sid, pi.identity, pi.name, pi.metadata);
|
20427
|
-
}
|
20428
|
-
/** @internal */
|
20429
|
-
constructor(signalClient, sid, identity, name, metadata, loggerOptions) {
|
20430
|
-
super(sid, identity || '', name, metadata, loggerOptions);
|
20431
|
-
this.signalClient = signalClient;
|
20432
|
-
this.tracks = new Map();
|
20433
|
-
this.audioTracks = new Map();
|
20434
|
-
this.videoTracks = new Map();
|
20435
|
-
this.volumeMap = new Map();
|
20436
20527
|
}
|
20437
|
-
|
20438
|
-
|
20439
|
-
|
20440
|
-
|
20441
|
-
|
20442
|
-
|
20443
|
-
|
20444
|
-
|
20445
|
-
|
20446
|
-
|
20447
|
-
|
20448
|
-
|
20449
|
-
|
20450
|
-
|
20451
|
-
|
20452
|
-
|
20453
|
-
|
20454
|
-
|
20455
|
-
|
20456
|
-
|
20457
|
-
|
20458
|
-
|
20459
|
-
|
20460
|
-
|
20461
|
-
|
20462
|
-
|
20463
|
-
|
20464
|
-
|
20465
|
-
|
20466
|
-
|
20467
|
-
|
20468
|
-
|
20469
|
-
|
20470
|
-
|
20471
|
-
|
20472
|
-
|
20473
|
-
|
20474
|
-
|
20528
|
+
setTrackEnabled(source, enabled, options, publishOptions) {
|
20529
|
+
var _a, _b;
|
20530
|
+
return __awaiter(this, void 0, void 0, function* () {
|
20531
|
+
this.log.debug('setTrackEnabled', Object.assign(Object.assign({}, this.logContext), {
|
20532
|
+
source,
|
20533
|
+
enabled
|
20534
|
+
}));
|
20535
|
+
let track = this.getTrackPublication(source);
|
20536
|
+
if (enabled) {
|
20537
|
+
if (track) {
|
20538
|
+
yield track.unmute();
|
20539
|
+
} else {
|
20540
|
+
let localTracks;
|
20541
|
+
if (this.pendingPublishing.has(source)) {
|
20542
|
+
this.log.info('skipping duplicate published source', Object.assign(Object.assign({}, this.logContext), {
|
20543
|
+
source
|
20544
|
+
}));
|
20545
|
+
// no-op it's already been requested
|
20546
|
+
return;
|
20547
|
+
}
|
20548
|
+
this.pendingPublishing.add(source);
|
20549
|
+
try {
|
20550
|
+
switch (source) {
|
20551
|
+
case Track.Source.Camera:
|
20552
|
+
localTracks = yield this.createTracks({
|
20553
|
+
video: (_a = options) !== null && _a !== void 0 ? _a : true
|
20554
|
+
});
|
20555
|
+
break;
|
20556
|
+
case Track.Source.Microphone:
|
20557
|
+
localTracks = yield this.createTracks({
|
20558
|
+
audio: (_b = options) !== null && _b !== void 0 ? _b : true
|
20559
|
+
});
|
20560
|
+
break;
|
20561
|
+
case Track.Source.ScreenShare:
|
20562
|
+
localTracks = yield this.createScreenTracks(Object.assign({}, options));
|
20563
|
+
break;
|
20564
|
+
default:
|
20565
|
+
throw new TrackInvalidError(source);
|
20566
|
+
}
|
20567
|
+
const publishPromises = [];
|
20568
|
+
for (const localTrack of localTracks) {
|
20569
|
+
this.log.info('publishing track', Object.assign(Object.assign({}, this.logContext), getLogContextFromTrack(localTrack)));
|
20570
|
+
publishPromises.push(this.publishTrack(localTrack, publishOptions));
|
20571
|
+
}
|
20572
|
+
const publishedTracks = yield Promise.all(publishPromises);
|
20573
|
+
// for screen share publications including audio, this will only return the screen share publication, not the screen share audio one
|
20574
|
+
// revisit if we want to return an array of tracks instead for v2
|
20575
|
+
[track] = publishedTracks;
|
20576
|
+
} catch (e) {
|
20577
|
+
localTracks === null || localTracks === void 0 ? void 0 : localTracks.forEach(tr => {
|
20578
|
+
tr.stop();
|
20579
|
+
});
|
20580
|
+
if (e instanceof Error && !(e instanceof TrackInvalidError)) {
|
20581
|
+
this.emit(ParticipantEvent.MediaDevicesError, e);
|
20582
|
+
}
|
20583
|
+
throw e;
|
20584
|
+
} finally {
|
20585
|
+
this.pendingPublishing.delete(source);
|
20586
|
+
}
|
20587
|
+
}
|
20588
|
+
} else if (track && track.track) {
|
20589
|
+
// screenshare cannot be muted, unpublish instead
|
20590
|
+
if (source === Track.Source.ScreenShare) {
|
20591
|
+
track = yield this.unpublishTrack(track.track);
|
20592
|
+
const screenAudioTrack = this.getTrackPublication(Track.Source.ScreenShareAudio);
|
20593
|
+
if (screenAudioTrack && screenAudioTrack.track) {
|
20594
|
+
this.unpublishTrack(screenAudioTrack.track);
|
20595
|
+
}
|
20596
|
+
} else {
|
20597
|
+
yield track.mute();
|
20598
|
+
}
|
20599
|
+
}
|
20475
20600
|
return track;
|
20476
|
-
}
|
20601
|
+
});
|
20477
20602
|
}
|
20478
20603
|
/**
|
20479
|
-
*
|
20480
|
-
*
|
20481
|
-
* a different source can be passed in as a second argument
|
20482
|
-
* if no track exists the volume will be applied when the microphone track is added
|
20604
|
+
* Publish both camera and microphone at the same time. This is useful for
|
20605
|
+
* displaying a single Permission Dialog box to the end user.
|
20483
20606
|
*/
|
20484
|
-
|
20485
|
-
|
20486
|
-
|
20487
|
-
|
20488
|
-
|
20489
|
-
|
20490
|
-
|
20607
|
+
enableCameraAndMicrophone() {
|
20608
|
+
return __awaiter(this, void 0, void 0, function* () {
|
20609
|
+
if (this.pendingPublishing.has(Track.Source.Camera) || this.pendingPublishing.has(Track.Source.Microphone)) {
|
20610
|
+
// no-op it's already been requested
|
20611
|
+
return;
|
20612
|
+
}
|
20613
|
+
this.pendingPublishing.add(Track.Source.Camera);
|
20614
|
+
this.pendingPublishing.add(Track.Source.Microphone);
|
20615
|
+
try {
|
20616
|
+
const tracks = yield this.createTracks({
|
20617
|
+
audio: true,
|
20618
|
+
video: true
|
20619
|
+
});
|
20620
|
+
yield Promise.all(tracks.map(track => this.publishTrack(track)));
|
20621
|
+
} finally {
|
20622
|
+
this.pendingPublishing.delete(Track.Source.Camera);
|
20623
|
+
this.pendingPublishing.delete(Track.Source.Microphone);
|
20624
|
+
}
|
20625
|
+
});
|
20491
20626
|
}
|
20492
20627
|
/**
|
20493
|
-
*
|
20628
|
+
* Create local camera and/or microphone tracks
|
20629
|
+
* @param options
|
20630
|
+
* @returns
|
20494
20631
|
*/
|
20495
|
-
|
20496
|
-
|
20497
|
-
|
20498
|
-
|
20499
|
-
|
20500
|
-
|
20501
|
-
|
20502
|
-
|
20503
|
-
|
20504
|
-
|
20505
|
-
|
20506
|
-
|
20507
|
-
let publication = this.getTrackPublication(sid);
|
20508
|
-
// it's also possible that the browser didn't honor our original track id
|
20509
|
-
// FireFox would use its own local uuid instead of server track id
|
20510
|
-
if (!publication) {
|
20511
|
-
if (!sid.startsWith('TR')) {
|
20512
|
-
// find the first track that matches type
|
20513
|
-
this.tracks.forEach(p => {
|
20514
|
-
if (!publication && mediaTrack.kind === p.kind.toString()) {
|
20515
|
-
publication = p;
|
20632
|
+
createTracks(options) {
|
20633
|
+
var _a, _b;
|
20634
|
+
return __awaiter(this, void 0, void 0, function* () {
|
20635
|
+
const opts = mergeDefaultOptions(options, (_a = this.roomOptions) === null || _a === void 0 ? void 0 : _a.audioCaptureDefaults, (_b = this.roomOptions) === null || _b === void 0 ? void 0 : _b.videoCaptureDefaults);
|
20636
|
+
const constraints = constraintsForOptions(opts);
|
20637
|
+
let stream;
|
20638
|
+
try {
|
20639
|
+
stream = yield navigator.mediaDevices.getUserMedia(constraints);
|
20640
|
+
} catch (err) {
|
20641
|
+
if (err instanceof Error) {
|
20642
|
+
if (constraints.audio) {
|
20643
|
+
this.microphoneError = err;
|
20516
20644
|
}
|
20517
|
-
|
20645
|
+
if (constraints.video) {
|
20646
|
+
this.cameraError = err;
|
20647
|
+
}
|
20648
|
+
}
|
20649
|
+
throw err;
|
20518
20650
|
}
|
20519
|
-
|
20520
|
-
|
20521
|
-
|
20522
|
-
if (!publication) {
|
20523
|
-
if (triesLeft === 0) {
|
20524
|
-
this.log.error('could not find published track', Object.assign(Object.assign({}, this.logContext), {
|
20525
|
-
trackSid: sid
|
20526
|
-
}));
|
20527
|
-
this.emit(ParticipantEvent.TrackSubscriptionFailed, sid);
|
20528
|
-
return;
|
20651
|
+
if (constraints.audio) {
|
20652
|
+
this.microphoneError = undefined;
|
20653
|
+
this.emit(ParticipantEvent.AudioStreamAcquired);
|
20529
20654
|
}
|
20530
|
-
if (
|
20531
|
-
|
20532
|
-
|
20533
|
-
|
20534
|
-
|
20535
|
-
|
20536
|
-
|
20537
|
-
|
20538
|
-
|
20539
|
-
|
20540
|
-
}
|
20541
|
-
const isVideo = mediaTrack.kind === 'video';
|
20542
|
-
let track;
|
20543
|
-
if (isVideo) {
|
20544
|
-
track = new RemoteVideoTrack(mediaTrack, sid, receiver, adaptiveStreamSettings);
|
20545
|
-
} else {
|
20546
|
-
track = new RemoteAudioTrack(mediaTrack, sid, receiver, this.audioContext, this.audioOutput);
|
20547
|
-
}
|
20548
|
-
// set track info
|
20549
|
-
track.source = publication.source;
|
20550
|
-
// keep publication's muted status
|
20551
|
-
track.isMuted = publication.isMuted;
|
20552
|
-
track.setMediaStream(mediaStream);
|
20553
|
-
track.start();
|
20554
|
-
publication.setTrack(track);
|
20555
|
-
// set participant volumes on new audio tracks
|
20556
|
-
if (this.volumeMap.has(publication.source) && track instanceof RemoteAudioTrack) {
|
20557
|
-
track.setVolume(this.volumeMap.get(publication.source));
|
20558
|
-
}
|
20559
|
-
return publication;
|
20560
|
-
}
|
20561
|
-
/** @internal */
|
20562
|
-
get hasMetadata() {
|
20563
|
-
return !!this.participantInfo;
|
20564
|
-
}
|
20565
|
-
getTrackPublication(sid) {
|
20566
|
-
return this.tracks.get(sid);
|
20567
|
-
}
|
20568
|
-
/** @internal */
|
20569
|
-
updateInfo(info) {
|
20570
|
-
if (!super.updateInfo(info)) {
|
20571
|
-
return false;
|
20572
|
-
}
|
20573
|
-
// we are getting a list of all available tracks, reconcile in here
|
20574
|
-
// and send out events for changes
|
20575
|
-
// reconcile track publications, publish events only if metadata is already there
|
20576
|
-
// i.e. changes since the local participant has joined
|
20577
|
-
const validTracks = new Map();
|
20578
|
-
const newTracks = new Map();
|
20579
|
-
info.tracks.forEach(ti => {
|
20580
|
-
var _a, _b;
|
20581
|
-
let publication = this.getTrackPublication(ti.sid);
|
20582
|
-
if (!publication) {
|
20583
|
-
// new publication
|
20584
|
-
const kind = Track.kindFromProto(ti.type);
|
20585
|
-
if (!kind) {
|
20586
|
-
return;
|
20655
|
+
if (constraints.video) {
|
20656
|
+
this.cameraError = undefined;
|
20657
|
+
}
|
20658
|
+
return stream.getTracks().map(mediaStreamTrack => {
|
20659
|
+
const isAudio = mediaStreamTrack.kind === 'audio';
|
20660
|
+
isAudio ? options.audio : options.video;
|
20661
|
+
let trackConstraints;
|
20662
|
+
const conOrBool = isAudio ? constraints.audio : constraints.video;
|
20663
|
+
if (typeof conOrBool !== 'boolean') {
|
20664
|
+
trackConstraints = conOrBool;
|
20587
20665
|
}
|
20588
|
-
|
20589
|
-
|
20590
|
-
|
20666
|
+
const track = mediaTrackToLocalTrack(mediaStreamTrack, trackConstraints, {
|
20667
|
+
loggerName: this.roomOptions.loggerName,
|
20668
|
+
loggerContextCb: () => this.logContext
|
20591
20669
|
});
|
20592
|
-
|
20593
|
-
|
20594
|
-
|
20595
|
-
|
20596
|
-
this.log.debug("received a second track publication for ".concat(this.identity, " with the same source: ").concat(publication.source), Object.assign(Object.assign({}, this.logContext), {
|
20597
|
-
oldTrack: getLogContextFromTrack(existingTrackOfSource),
|
20598
|
-
newTrack: getLogContextFromTrack(publication)
|
20599
|
-
}));
|
20670
|
+
if (track.kind === Track.Kind.Video) {
|
20671
|
+
track.source = Track.Source.Camera;
|
20672
|
+
} else if (track.kind === Track.Kind.Audio) {
|
20673
|
+
track.source = Track.Source.Microphone;
|
20600
20674
|
}
|
20601
|
-
|
20602
|
-
|
20603
|
-
|
20604
|
-
}
|
20605
|
-
validTracks.set(ti.sid, publication);
|
20606
|
-
});
|
20607
|
-
// detect removed tracks
|
20608
|
-
this.tracks.forEach(publication => {
|
20609
|
-
if (!validTracks.has(publication.trackSid)) {
|
20610
|
-
this.log.trace('detected removed track on remote participant, unpublishing', Object.assign(Object.assign({}, this.logContext), getLogContextFromTrack(publication)));
|
20611
|
-
this.unpublishTrack(publication.trackSid, true);
|
20612
|
-
}
|
20613
|
-
});
|
20614
|
-
// always emit events for new publications, Room will not forward them unless it's ready
|
20615
|
-
newTracks.forEach(publication => {
|
20616
|
-
this.emit(ParticipantEvent.TrackPublished, publication);
|
20675
|
+
track.mediaStream = stream;
|
20676
|
+
return track;
|
20677
|
+
});
|
20617
20678
|
});
|
20618
|
-
return true;
|
20619
|
-
}
|
20620
|
-
/** @internal */
|
20621
|
-
unpublishTrack(sid, sendUnpublish) {
|
20622
|
-
const publication = this.tracks.get(sid);
|
20623
|
-
if (!publication) {
|
20624
|
-
return;
|
20625
|
-
}
|
20626
|
-
// also send unsubscribe, if track is actively subscribed
|
20627
|
-
const {
|
20628
|
-
track
|
20629
|
-
} = publication;
|
20630
|
-
if (track) {
|
20631
|
-
track.stop();
|
20632
|
-
publication.setTrack(undefined);
|
20633
|
-
}
|
20634
|
-
// remove track from maps only after unsubscribed has been fired
|
20635
|
-
this.tracks.delete(sid);
|
20636
|
-
// remove from the right type map
|
20637
|
-
switch (publication.kind) {
|
20638
|
-
case Track.Kind.Audio:
|
20639
|
-
this.audioTracks.delete(sid);
|
20640
|
-
break;
|
20641
|
-
case Track.Kind.Video:
|
20642
|
-
this.videoTracks.delete(sid);
|
20643
|
-
break;
|
20644
|
-
}
|
20645
|
-
if (sendUnpublish) {
|
20646
|
-
this.emit(ParticipantEvent.TrackUnpublished, publication);
|
20647
|
-
}
|
20648
20679
|
}
|
20649
20680
|
/**
|
20650
|
-
*
|
20681
|
+
* Creates a screen capture tracks with getDisplayMedia().
|
20682
|
+
* A LocalVideoTrack is always created and returned.
|
20683
|
+
* If { audio: true }, and the browser supports audio capture, a LocalAudioTrack is also created.
|
20651
20684
|
*/
|
20652
|
-
|
20685
|
+
createScreenTracks(options) {
|
20653
20686
|
return __awaiter(this, void 0, void 0, function* () {
|
20654
|
-
|
20655
|
-
|
20656
|
-
this.audioTracks.forEach(pub => {
|
20657
|
-
var _a;
|
20658
|
-
if (pub.track instanceof RemoteAudioTrack) {
|
20659
|
-
promises.push(pub.track.setSinkId((_a = output.deviceId) !== null && _a !== void 0 ? _a : 'default'));
|
20660
|
-
}
|
20661
|
-
});
|
20662
|
-
yield Promise.all(promises);
|
20663
|
-
});
|
20664
|
-
}
|
20665
|
-
/** @internal */
|
20666
|
-
emit(event) {
|
20667
|
-
for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
|
20668
|
-
args[_key - 1] = arguments[_key];
|
20669
|
-
}
|
20670
|
-
this.log.trace('participant event', Object.assign(Object.assign({}, this.logContext), {
|
20671
|
-
event,
|
20672
|
-
args
|
20673
|
-
}));
|
20674
|
-
return super.emit(event, ...args);
|
20675
|
-
}
|
20676
|
-
}
|
20677
|
-
|
20678
|
-
class LocalParticipant extends Participant {
|
20679
|
-
/** @internal */
|
20680
|
-
constructor(sid, identity, engine, options) {
|
20681
|
-
super(sid, identity, undefined, undefined, {
|
20682
|
-
loggerName: options.loggerName,
|
20683
|
-
loggerContextCb: () => this.engine.logContext
|
20684
|
-
});
|
20685
|
-
this.pendingPublishing = new Set();
|
20686
|
-
this.pendingPublishPromises = new Map();
|
20687
|
-
this.participantTrackPermissions = [];
|
20688
|
-
this.allParticipantsAllowedToSubscribe = true;
|
20689
|
-
this.encryptionType = Encryption_Type.NONE;
|
20690
|
-
this.handleReconnecting = () => {
|
20691
|
-
if (!this.reconnectFuture) {
|
20692
|
-
this.reconnectFuture = new Future();
|
20687
|
+
if (options === undefined) {
|
20688
|
+
options = {};
|
20693
20689
|
}
|
20694
|
-
|
20695
|
-
|
20696
|
-
var _a, _b;
|
20697
|
-
(_b = (_a = this.reconnectFuture) === null || _a === void 0 ? void 0 : _a.resolve) === null || _b === void 0 ? void 0 : _b.call(_a);
|
20698
|
-
this.reconnectFuture = undefined;
|
20699
|
-
this.updateTrackSubscriptionPermissions();
|
20700
|
-
};
|
20701
|
-
this.handleDisconnected = () => {
|
20702
|
-
var _a, _b;
|
20703
|
-
if (this.reconnectFuture) {
|
20704
|
-
this.reconnectFuture.promise.catch(e => this.log.warn(e.message, this.logContext));
|
20705
|
-
(_b = (_a = this.reconnectFuture) === null || _a === void 0 ? void 0 : _a.reject) === null || _b === void 0 ? void 0 : _b.call(_a, 'Got disconnected during reconnection attempt');
|
20706
|
-
this.reconnectFuture = undefined;
|
20690
|
+
if (navigator.mediaDevices.getDisplayMedia === undefined) {
|
20691
|
+
throw new DeviceUnsupportedError('getDisplayMedia not supported');
|
20707
20692
|
}
|
20708
|
-
|
20709
|
-
|
20710
|
-
|
20711
|
-
|
20712
|
-
|
20713
|
-
}));
|
20714
|
-
this.engine.client.sendUpdateSubscriptionPermissions(this.allParticipantsAllowedToSubscribe, this.participantTrackPermissions.map(p => trackPermissionToProto(p)));
|
20715
|
-
};
|
20716
|
-
/** @internal */
|
20717
|
-
this.onTrackUnmuted = track => {
|
20718
|
-
this.onTrackMuted(track, track.isUpstreamPaused);
|
20719
|
-
};
|
20720
|
-
// when the local track changes in mute status, we'll notify server as such
|
20721
|
-
/** @internal */
|
20722
|
-
this.onTrackMuted = (track, muted) => {
|
20723
|
-
if (muted === undefined) {
|
20724
|
-
muted = true;
|
20693
|
+
if (options.resolution === undefined && !isSafari17()) {
|
20694
|
+
// we need to constrain the dimensions, otherwise it could lead to low bitrate
|
20695
|
+
// due to encoding a huge video. Encoding such large surfaces is really expensive
|
20696
|
+
// unfortunately Safari 17 has a but and cannot be constrained by default
|
20697
|
+
options.resolution = ScreenSharePresets.h1080fps30.resolution;
|
20725
20698
|
}
|
20726
|
-
|
20727
|
-
|
20728
|
-
|
20699
|
+
const constraints = screenCaptureToDisplayMediaStreamOptions(options);
|
20700
|
+
const stream = yield navigator.mediaDevices.getDisplayMedia(constraints);
|
20701
|
+
const tracks = stream.getVideoTracks();
|
20702
|
+
if (tracks.length === 0) {
|
20703
|
+
throw new TrackInvalidError('no video track found');
|
20729
20704
|
}
|
20730
|
-
|
20731
|
-
|
20732
|
-
|
20733
|
-
|
20734
|
-
|
20735
|
-
|
20736
|
-
|
20737
|
-
this.log.debug('upstream resumed', Object.assign(Object.assign({}, this.logContext), getLogContextFromTrack(track)));
|
20738
|
-
this.onTrackMuted(track, track.isMuted);
|
20739
|
-
};
|
20740
|
-
this.handleSubscribedQualityUpdate = update => __awaiter(this, void 0, void 0, function* () {
|
20741
|
-
var _a, e_1, _b, _c;
|
20742
|
-
var _d, _e;
|
20743
|
-
if (!((_d = this.roomOptions) === null || _d === void 0 ? void 0 : _d.dynacast)) {
|
20744
|
-
return;
|
20705
|
+
const screenVideo = new LocalVideoTrack(tracks[0], undefined, false, {
|
20706
|
+
loggerName: this.roomOptions.loggerName,
|
20707
|
+
loggerContextCb: () => this.logContext
|
20708
|
+
});
|
20709
|
+
screenVideo.source = Track.Source.ScreenShare;
|
20710
|
+
if (options.contentHint) {
|
20711
|
+
screenVideo.mediaStreamTrack.contentHint = options.contentHint;
|
20745
20712
|
}
|
20746
|
-
const
|
20747
|
-
if (
|
20748
|
-
this.
|
20749
|
-
|
20750
|
-
|
20751
|
-
|
20713
|
+
const localTracks = [screenVideo];
|
20714
|
+
if (stream.getAudioTracks().length > 0) {
|
20715
|
+
this.emit(ParticipantEvent.AudioStreamAcquired);
|
20716
|
+
const screenAudio = new LocalAudioTrack(stream.getAudioTracks()[0], undefined, false, this.audioContext, {
|
20717
|
+
loggerName: this.roomOptions.loggerName,
|
20718
|
+
loggerContextCb: () => this.logContext
|
20719
|
+
});
|
20720
|
+
screenAudio.source = Track.Source.ScreenShareAudio;
|
20721
|
+
localTracks.push(screenAudio);
|
20752
20722
|
}
|
20753
|
-
|
20754
|
-
|
20755
|
-
|
20723
|
+
return localTracks;
|
20724
|
+
});
|
20725
|
+
}
|
20726
|
+
/**
|
20727
|
+
* Publish a new track to the room
|
20728
|
+
* @param track
|
20729
|
+
* @param options
|
20730
|
+
*/
|
20731
|
+
publishTrack(track, options) {
|
20732
|
+
var _a, _b, _c, _d;
|
20733
|
+
return __awaiter(this, void 0, void 0, function* () {
|
20734
|
+
yield (_a = this.reconnectFuture) === null || _a === void 0 ? void 0 : _a.promise;
|
20735
|
+
if (track instanceof LocalTrack && this.pendingPublishPromises.has(track)) {
|
20736
|
+
yield this.pendingPublishPromises.get(track);
|
20737
|
+
}
|
20738
|
+
let defaultConstraints;
|
20739
|
+
if (track instanceof MediaStreamTrack) {
|
20740
|
+
defaultConstraints = track.getConstraints();
|
20741
|
+
} else {
|
20742
|
+
// we want to access constraints directly as `track.mediaStreamTrack`
|
20743
|
+
// might be pointing to a non-device track (e.g. processed track) already
|
20744
|
+
defaultConstraints = track.constraints;
|
20745
|
+
let deviceKind = undefined;
|
20746
|
+
switch (track.source) {
|
20747
|
+
case Track.Source.Microphone:
|
20748
|
+
deviceKind = 'audioinput';
|
20749
|
+
break;
|
20750
|
+
case Track.Source.Camera:
|
20751
|
+
deviceKind = 'videoinput';
|
20756
20752
|
}
|
20757
|
-
|
20758
|
-
|
20759
|
-
|
20760
|
-
|
20761
|
-
_f = false;
|
20762
|
-
const codec = _c;
|
20763
|
-
if (isBackupCodec(codec)) {
|
20764
|
-
this.log.debug("publish ".concat(codec, " for ").concat(pub.videoTrack.sid), Object.assign(Object.assign({}, this.logContext), getLogContextFromTrack(pub)));
|
20765
|
-
yield this.publishAdditionalCodecForTrack(pub.videoTrack, codec, pub.options);
|
20766
|
-
}
|
20767
|
-
}
|
20768
|
-
} catch (e_1_1) {
|
20769
|
-
e_1 = {
|
20770
|
-
error: e_1_1
|
20771
|
-
};
|
20772
|
-
} finally {
|
20773
|
-
try {
|
20774
|
-
if (!_f && !_a && (_b = newCodecs_1.return)) yield _b.call(newCodecs_1);
|
20775
|
-
} finally {
|
20776
|
-
if (e_1) throw e_1.error;
|
20777
|
-
}
|
20778
|
-
}
|
20779
|
-
} else if (update.subscribedQualities.length > 0) {
|
20780
|
-
yield (_e = pub.videoTrack) === null || _e === void 0 ? void 0 : _e.setPublishingLayers(update.subscribedQualities);
|
20781
|
-
}
|
20782
|
-
});
|
20783
|
-
this.handleLocalTrackUnpublished = unpublished => {
|
20784
|
-
const track = this.tracks.get(unpublished.trackSid);
|
20785
|
-
if (!track) {
|
20786
|
-
this.log.warn('received unpublished event for unknown track', Object.assign(Object.assign({}, this.logContext), {
|
20787
|
-
trackSid: unpublished.trackSid
|
20788
|
-
}));
|
20789
|
-
return;
|
20790
|
-
}
|
20791
|
-
this.unpublishTrack(track.track);
|
20792
|
-
};
|
20793
|
-
this.handleTrackEnded = track => __awaiter(this, void 0, void 0, function* () {
|
20794
|
-
if (track.source === Track.Source.ScreenShare || track.source === Track.Source.ScreenShareAudio) {
|
20795
|
-
this.log.debug('unpublishing local track due to TrackEnded', Object.assign(Object.assign({}, this.logContext), getLogContextFromTrack(track)));
|
20796
|
-
this.unpublishTrack(track);
|
20797
|
-
} else if (track.isUserProvided) {
|
20798
|
-
yield track.mute();
|
20799
|
-
} else if (track instanceof LocalAudioTrack || track instanceof LocalVideoTrack) {
|
20800
|
-
try {
|
20801
|
-
if (isWeb()) {
|
20802
|
-
try {
|
20803
|
-
const currentPermissions = yield navigator === null || navigator === void 0 ? void 0 : navigator.permissions.query({
|
20804
|
-
// the permission query for camera and microphone currently not supported in Safari and Firefox
|
20805
|
-
// @ts-ignore
|
20806
|
-
name: track.source === Track.Source.Camera ? 'camera' : 'microphone'
|
20807
|
-
});
|
20808
|
-
if (currentPermissions && currentPermissions.state === 'denied') {
|
20809
|
-
this.log.warn("user has revoked access to ".concat(track.source), Object.assign(Object.assign({}, this.logContext), getLogContextFromTrack(track)));
|
20810
|
-
// detect granted change after permissions were denied to try and resume then
|
20811
|
-
currentPermissions.onchange = () => {
|
20812
|
-
if (currentPermissions.state !== 'denied') {
|
20813
|
-
if (!track.isMuted) {
|
20814
|
-
track.restartTrack();
|
20815
|
-
}
|
20816
|
-
currentPermissions.onchange = null;
|
20817
|
-
}
|
20818
|
-
};
|
20819
|
-
throw new Error('GetUserMedia Permission denied');
|
20820
|
-
}
|
20821
|
-
} catch (e) {
|
20822
|
-
// permissions query fails for firefox, we continue and try to restart the track
|
20823
|
-
}
|
20824
|
-
}
|
20825
|
-
if (!track.isMuted) {
|
20826
|
-
this.log.debug('track ended, attempting to use a different device', Object.assign(Object.assign({}, this.logContext), getLogContextFromTrack(track)));
|
20827
|
-
yield track.restartTrack();
|
20828
|
-
}
|
20829
|
-
} catch (e) {
|
20830
|
-
this.log.warn("could not restart track, muting instead", Object.assign(Object.assign({}, this.logContext), getLogContextFromTrack(track)));
|
20831
|
-
yield track.mute();
|
20832
|
-
}
|
20833
|
-
}
|
20834
|
-
});
|
20835
|
-
this.audioTracks = new Map();
|
20836
|
-
this.videoTracks = new Map();
|
20837
|
-
this.tracks = new Map();
|
20838
|
-
this.engine = engine;
|
20839
|
-
this.roomOptions = options;
|
20840
|
-
this.setupEngine(engine);
|
20841
|
-
this.activeDeviceMap = new Map();
|
20842
|
-
}
|
20843
|
-
get lastCameraError() {
|
20844
|
-
return this.cameraError;
|
20845
|
-
}
|
20846
|
-
get lastMicrophoneError() {
|
20847
|
-
return this.microphoneError;
|
20848
|
-
}
|
20849
|
-
get isE2EEEnabled() {
|
20850
|
-
return this.encryptionType !== Encryption_Type.NONE;
|
20851
|
-
}
|
20852
|
-
getTrack(source) {
|
20853
|
-
const track = super.getTrack(source);
|
20854
|
-
if (track) {
|
20855
|
-
return track;
|
20856
|
-
}
|
20857
|
-
}
|
20858
|
-
getTrackByName(name) {
|
20859
|
-
const track = super.getTrackByName(name);
|
20860
|
-
if (track) {
|
20861
|
-
return track;
|
20862
|
-
}
|
20863
|
-
}
|
20864
|
-
/**
|
20865
|
-
* @internal
|
20866
|
-
*/
|
20867
|
-
setupEngine(engine) {
|
20868
|
-
this.engine = engine;
|
20869
|
-
this.engine.on(EngineEvent.RemoteMute, (trackSid, muted) => {
|
20870
|
-
const pub = this.tracks.get(trackSid);
|
20871
|
-
if (!pub || !pub.track) {
|
20872
|
-
return;
|
20873
|
-
}
|
20874
|
-
if (muted) {
|
20875
|
-
pub.mute();
|
20876
|
-
} else {
|
20877
|
-
pub.unmute();
|
20878
|
-
}
|
20879
|
-
});
|
20880
|
-
this.engine.on(EngineEvent.Connected, this.handleReconnected).on(EngineEvent.SignalRestarted, this.handleReconnected).on(EngineEvent.SignalResumed, this.handleReconnected).on(EngineEvent.Restarting, this.handleReconnecting).on(EngineEvent.Resuming, this.handleReconnecting).on(EngineEvent.LocalTrackUnpublished, this.handleLocalTrackUnpublished).on(EngineEvent.SubscribedQualityUpdate, this.handleSubscribedQualityUpdate).on(EngineEvent.Disconnected, this.handleDisconnected);
|
20881
|
-
}
|
20882
|
-
/**
|
20883
|
-
* Sets and updates the metadata of the local participant.
|
20884
|
-
* The change does not take immediate effect.
|
20885
|
-
* If successful, a `ParticipantEvent.MetadataChanged` event will be emitted on the local participant.
|
20886
|
-
* Note: this requires `canUpdateOwnMetadata` permission.
|
20887
|
-
* @param metadata
|
20888
|
-
*/
|
20889
|
-
setMetadata(metadata) {
|
20890
|
-
var _a;
|
20891
|
-
this.engine.client.sendUpdateLocalMetadata(metadata, (_a = this.name) !== null && _a !== void 0 ? _a : '');
|
20892
|
-
}
|
20893
|
-
/**
|
20894
|
-
* Sets and updates the name of the local participant.
|
20895
|
-
* The change does not take immediate effect.
|
20896
|
-
* If successful, a `ParticipantEvent.ParticipantNameChanged` event will be emitted on the local participant.
|
20897
|
-
* Note: this requires `canUpdateOwnMetadata` permission.
|
20898
|
-
* @param metadata
|
20899
|
-
*/
|
20900
|
-
setName(name) {
|
20901
|
-
var _a;
|
20902
|
-
this.engine.client.sendUpdateLocalMetadata((_a = this.metadata) !== null && _a !== void 0 ? _a : '', name);
|
20903
|
-
}
|
20904
|
-
/**
|
20905
|
-
* Enable or disable a participant's camera track.
|
20906
|
-
*
|
20907
|
-
* If a track has already published, it'll mute or unmute the track.
|
20908
|
-
* Resolves with a `LocalTrackPublication` instance if successful and `undefined` otherwise
|
20909
|
-
*/
|
20910
|
-
setCameraEnabled(enabled, options, publishOptions) {
|
20911
|
-
return this.setTrackEnabled(Track.Source.Camera, enabled, options, publishOptions);
|
20912
|
-
}
|
20913
|
-
/**
|
20914
|
-
* Enable or disable a participant's microphone track.
|
20915
|
-
*
|
20916
|
-
* If a track has already published, it'll mute or unmute the track.
|
20917
|
-
* Resolves with a `LocalTrackPublication` instance if successful and `undefined` otherwise
|
20918
|
-
*/
|
20919
|
-
setMicrophoneEnabled(enabled, options, publishOptions) {
|
20920
|
-
return this.setTrackEnabled(Track.Source.Microphone, enabled, options, publishOptions);
|
20921
|
-
}
|
20922
|
-
/**
|
20923
|
-
* Start or stop sharing a participant's screen
|
20924
|
-
* Resolves with a `LocalTrackPublication` instance if successful and `undefined` otherwise
|
20925
|
-
*/
|
20926
|
-
setScreenShareEnabled(enabled, options, publishOptions) {
|
20927
|
-
return this.setTrackEnabled(Track.Source.ScreenShare, enabled, options, publishOptions);
|
20928
|
-
}
|
20929
|
-
/** @internal */
|
20930
|
-
setPermissions(permissions) {
|
20931
|
-
const prevPermissions = this.permissions;
|
20932
|
-
const changed = super.setPermissions(permissions);
|
20933
|
-
if (changed && prevPermissions) {
|
20934
|
-
this.emit(ParticipantEvent.ParticipantPermissionsChanged, prevPermissions);
|
20935
|
-
}
|
20936
|
-
return changed;
|
20937
|
-
}
|
20938
|
-
/** @internal */
|
20939
|
-
setE2EEEnabled(enabled) {
|
20940
|
-
return __awaiter(this, void 0, void 0, function* () {
|
20941
|
-
this.encryptionType = enabled ? Encryption_Type.GCM : Encryption_Type.NONE;
|
20942
|
-
yield this.republishAllTracks(undefined, false);
|
20943
|
-
});
|
20944
|
-
}
|
20945
|
-
setTrackEnabled(source, enabled, options, publishOptions) {
|
20946
|
-
var _a, _b;
|
20947
|
-
return __awaiter(this, void 0, void 0, function* () {
|
20948
|
-
this.log.debug('setTrackEnabled', Object.assign(Object.assign({}, this.logContext), {
|
20949
|
-
source,
|
20950
|
-
enabled
|
20951
|
-
}));
|
20952
|
-
let track = this.getTrack(source);
|
20953
|
-
if (enabled) {
|
20954
|
-
if (track) {
|
20955
|
-
yield track.unmute();
|
20956
|
-
} else {
|
20957
|
-
let localTracks;
|
20958
|
-
if (this.pendingPublishing.has(source)) {
|
20959
|
-
this.log.info('skipping duplicate published source', Object.assign(Object.assign({}, this.logContext), {
|
20960
|
-
source
|
20961
|
-
}));
|
20962
|
-
// no-op it's already been requested
|
20963
|
-
return;
|
20964
|
-
}
|
20965
|
-
this.pendingPublishing.add(source);
|
20966
|
-
try {
|
20967
|
-
switch (source) {
|
20968
|
-
case Track.Source.Camera:
|
20969
|
-
localTracks = yield this.createTracks({
|
20970
|
-
video: (_a = options) !== null && _a !== void 0 ? _a : true
|
20971
|
-
});
|
20972
|
-
break;
|
20973
|
-
case Track.Source.Microphone:
|
20974
|
-
localTracks = yield this.createTracks({
|
20975
|
-
audio: (_b = options) !== null && _b !== void 0 ? _b : true
|
20976
|
-
});
|
20977
|
-
break;
|
20978
|
-
case Track.Source.ScreenShare:
|
20979
|
-
localTracks = yield this.createScreenTracks(Object.assign({}, options));
|
20980
|
-
break;
|
20981
|
-
default:
|
20982
|
-
throw new TrackInvalidError(source);
|
20983
|
-
}
|
20984
|
-
const publishPromises = [];
|
20985
|
-
for (const localTrack of localTracks) {
|
20986
|
-
this.log.info('publishing track', Object.assign(Object.assign({}, this.logContext), getLogContextFromTrack(localTrack)));
|
20987
|
-
publishPromises.push(this.publishTrack(localTrack, publishOptions));
|
20988
|
-
}
|
20989
|
-
const publishedTracks = yield Promise.all(publishPromises);
|
20990
|
-
// for screen share publications including audio, this will only return the screen share publication, not the screen share audio one
|
20991
|
-
// revisit if we want to return an array of tracks instead for v2
|
20992
|
-
[track] = publishedTracks;
|
20993
|
-
} catch (e) {
|
20994
|
-
localTracks === null || localTracks === void 0 ? void 0 : localTracks.forEach(tr => {
|
20995
|
-
tr.stop();
|
20996
|
-
});
|
20997
|
-
if (e instanceof Error && !(e instanceof TrackInvalidError)) {
|
20998
|
-
this.emit(ParticipantEvent.MediaDevicesError, e);
|
20999
|
-
}
|
21000
|
-
throw e;
|
21001
|
-
} finally {
|
21002
|
-
this.pendingPublishing.delete(source);
|
21003
|
-
}
|
21004
|
-
}
|
21005
|
-
} else if (track && track.track) {
|
21006
|
-
// screenshare cannot be muted, unpublish instead
|
21007
|
-
if (source === Track.Source.ScreenShare) {
|
21008
|
-
track = yield this.unpublishTrack(track.track);
|
21009
|
-
const screenAudioTrack = this.getTrack(Track.Source.ScreenShareAudio);
|
21010
|
-
if (screenAudioTrack && screenAudioTrack.track) {
|
21011
|
-
this.unpublishTrack(screenAudioTrack.track);
|
21012
|
-
}
|
21013
|
-
} else {
|
21014
|
-
yield track.mute();
|
21015
|
-
}
|
21016
|
-
}
|
21017
|
-
return track;
|
21018
|
-
});
|
21019
|
-
}
|
21020
|
-
/**
|
21021
|
-
* Publish both camera and microphone at the same time. This is useful for
|
21022
|
-
* displaying a single Permission Dialog box to the end user.
|
21023
|
-
*/
|
21024
|
-
enableCameraAndMicrophone() {
|
21025
|
-
return __awaiter(this, void 0, void 0, function* () {
|
21026
|
-
if (this.pendingPublishing.has(Track.Source.Camera) || this.pendingPublishing.has(Track.Source.Microphone)) {
|
21027
|
-
// no-op it's already been requested
|
21028
|
-
return;
|
21029
|
-
}
|
21030
|
-
this.pendingPublishing.add(Track.Source.Camera);
|
21031
|
-
this.pendingPublishing.add(Track.Source.Microphone);
|
21032
|
-
try {
|
21033
|
-
const tracks = yield this.createTracks({
|
21034
|
-
audio: true,
|
21035
|
-
video: true
|
21036
|
-
});
|
21037
|
-
yield Promise.all(tracks.map(track => this.publishTrack(track)));
|
21038
|
-
} finally {
|
21039
|
-
this.pendingPublishing.delete(Track.Source.Camera);
|
21040
|
-
this.pendingPublishing.delete(Track.Source.Microphone);
|
21041
|
-
}
|
21042
|
-
});
|
21043
|
-
}
|
21044
|
-
/**
|
21045
|
-
* Create local camera and/or microphone tracks
|
21046
|
-
* @param options
|
21047
|
-
* @returns
|
21048
|
-
*/
|
21049
|
-
createTracks(options) {
|
21050
|
-
var _a, _b;
|
21051
|
-
return __awaiter(this, void 0, void 0, function* () {
|
21052
|
-
const opts = mergeDefaultOptions(options, (_a = this.roomOptions) === null || _a === void 0 ? void 0 : _a.audioCaptureDefaults, (_b = this.roomOptions) === null || _b === void 0 ? void 0 : _b.videoCaptureDefaults);
|
21053
|
-
const constraints = constraintsForOptions(opts);
|
21054
|
-
let stream;
|
21055
|
-
try {
|
21056
|
-
stream = yield navigator.mediaDevices.getUserMedia(constraints);
|
21057
|
-
} catch (err) {
|
21058
|
-
if (err instanceof Error) {
|
21059
|
-
if (constraints.audio) {
|
21060
|
-
this.microphoneError = err;
|
21061
|
-
}
|
21062
|
-
if (constraints.video) {
|
21063
|
-
this.cameraError = err;
|
21064
|
-
}
|
21065
|
-
}
|
21066
|
-
throw err;
|
21067
|
-
}
|
21068
|
-
if (constraints.audio) {
|
21069
|
-
this.microphoneError = undefined;
|
21070
|
-
this.emit(ParticipantEvent.AudioStreamAcquired);
|
21071
|
-
}
|
21072
|
-
if (constraints.video) {
|
21073
|
-
this.cameraError = undefined;
|
21074
|
-
}
|
21075
|
-
return stream.getTracks().map(mediaStreamTrack => {
|
21076
|
-
const isAudio = mediaStreamTrack.kind === 'audio';
|
21077
|
-
isAudio ? options.audio : options.video;
|
21078
|
-
let trackConstraints;
|
21079
|
-
const conOrBool = isAudio ? constraints.audio : constraints.video;
|
21080
|
-
if (typeof conOrBool !== 'boolean') {
|
21081
|
-
trackConstraints = conOrBool;
|
21082
|
-
}
|
21083
|
-
const track = mediaTrackToLocalTrack(mediaStreamTrack, trackConstraints, {
|
21084
|
-
loggerName: this.roomOptions.loggerName,
|
21085
|
-
loggerContextCb: () => this.logContext
|
21086
|
-
});
|
21087
|
-
if (track.kind === Track.Kind.Video) {
|
21088
|
-
track.source = Track.Source.Camera;
|
21089
|
-
} else if (track.kind === Track.Kind.Audio) {
|
21090
|
-
track.source = Track.Source.Microphone;
|
21091
|
-
}
|
21092
|
-
track.mediaStream = stream;
|
21093
|
-
return track;
|
21094
|
-
});
|
21095
|
-
});
|
21096
|
-
}
|
21097
|
-
/**
|
21098
|
-
* Creates a screen capture tracks with getDisplayMedia().
|
21099
|
-
* A LocalVideoTrack is always created and returned.
|
21100
|
-
* If { audio: true }, and the browser supports audio capture, a LocalAudioTrack is also created.
|
21101
|
-
*/
|
21102
|
-
createScreenTracks(options) {
|
21103
|
-
return __awaiter(this, void 0, void 0, function* () {
|
21104
|
-
if (options === undefined) {
|
21105
|
-
options = {};
|
21106
|
-
}
|
21107
|
-
if (navigator.mediaDevices.getDisplayMedia === undefined) {
|
21108
|
-
throw new DeviceUnsupportedError('getDisplayMedia not supported');
|
21109
|
-
}
|
21110
|
-
if (options.resolution === undefined && !isSafari17()) {
|
21111
|
-
// we need to constrain the dimensions, otherwise it could lead to low bitrate
|
21112
|
-
// due to encoding a huge video. Encoding such large surfaces is really expensive
|
21113
|
-
// unfortunately Safari 17 has a but and cannot be constrained by default
|
21114
|
-
options.resolution = ScreenSharePresets.h1080fps30.resolution;
|
21115
|
-
}
|
21116
|
-
const constraints = screenCaptureToDisplayMediaStreamOptions(options);
|
21117
|
-
const stream = yield navigator.mediaDevices.getDisplayMedia(constraints);
|
21118
|
-
const tracks = stream.getVideoTracks();
|
21119
|
-
if (tracks.length === 0) {
|
21120
|
-
throw new TrackInvalidError('no video track found');
|
21121
|
-
}
|
21122
|
-
const screenVideo = new LocalVideoTrack(tracks[0], undefined, false, {
|
21123
|
-
loggerName: this.roomOptions.loggerName,
|
21124
|
-
loggerContextCb: () => this.logContext
|
21125
|
-
});
|
21126
|
-
screenVideo.source = Track.Source.ScreenShare;
|
21127
|
-
if (options.contentHint) {
|
21128
|
-
screenVideo.mediaStreamTrack.contentHint = options.contentHint;
|
21129
|
-
}
|
21130
|
-
const localTracks = [screenVideo];
|
21131
|
-
if (stream.getAudioTracks().length > 0) {
|
21132
|
-
this.emit(ParticipantEvent.AudioStreamAcquired);
|
21133
|
-
const screenAudio = new LocalAudioTrack(stream.getAudioTracks()[0], undefined, false, this.audioContext, {
|
21134
|
-
loggerName: this.roomOptions.loggerName,
|
21135
|
-
loggerContextCb: () => this.logContext
|
21136
|
-
});
|
21137
|
-
screenAudio.source = Track.Source.ScreenShareAudio;
|
21138
|
-
localTracks.push(screenAudio);
|
21139
|
-
}
|
21140
|
-
return localTracks;
|
21141
|
-
});
|
21142
|
-
}
|
21143
|
-
/**
|
21144
|
-
* Publish a new track to the room
|
21145
|
-
* @param track
|
21146
|
-
* @param options
|
21147
|
-
*/
|
21148
|
-
publishTrack(track, options) {
|
21149
|
-
var _a, _b, _c, _d;
|
21150
|
-
return __awaiter(this, void 0, void 0, function* () {
|
21151
|
-
yield (_a = this.reconnectFuture) === null || _a === void 0 ? void 0 : _a.promise;
|
21152
|
-
if (track instanceof LocalTrack && this.pendingPublishPromises.has(track)) {
|
21153
|
-
yield this.pendingPublishPromises.get(track);
|
21154
|
-
}
|
21155
|
-
let defaultConstraints;
|
21156
|
-
if (track instanceof MediaStreamTrack) {
|
21157
|
-
defaultConstraints = track.getConstraints();
|
21158
|
-
} else {
|
21159
|
-
// we want to access constraints directly as `track.mediaStreamTrack`
|
21160
|
-
// might be pointing to a non-device track (e.g. processed track) already
|
21161
|
-
defaultConstraints = track.constraints;
|
21162
|
-
let deviceKind = undefined;
|
21163
|
-
switch (track.source) {
|
21164
|
-
case Track.Source.Microphone:
|
21165
|
-
deviceKind = 'audioinput';
|
21166
|
-
break;
|
21167
|
-
case Track.Source.Camera:
|
21168
|
-
deviceKind = 'videoinput';
|
21169
|
-
}
|
21170
|
-
if (deviceKind && this.activeDeviceMap.has(deviceKind)) {
|
21171
|
-
defaultConstraints = Object.assign(Object.assign({}, defaultConstraints), {
|
21172
|
-
deviceId: this.activeDeviceMap.get(deviceKind)
|
21173
|
-
});
|
20753
|
+
if (deviceKind && this.activeDeviceMap.has(deviceKind)) {
|
20754
|
+
defaultConstraints = Object.assign(Object.assign({}, defaultConstraints), {
|
20755
|
+
deviceId: this.activeDeviceMap.get(deviceKind)
|
20756
|
+
});
|
21174
20757
|
}
|
21175
20758
|
}
|
21176
20759
|
// convert raw media track into audio or video track
|
@@ -21202,7 +20785,7 @@ class LocalParticipant extends Participant {
|
|
21202
20785
|
}
|
21203
20786
|
// is it already published? if so skip
|
21204
20787
|
let existingPublication;
|
21205
|
-
this.
|
20788
|
+
this.trackPublications.forEach(publication => {
|
21206
20789
|
if (!publication.track) {
|
21207
20790
|
return;
|
21208
20791
|
}
|
@@ -21254,9 +20837,9 @@ class LocalParticipant extends Participant {
|
|
21254
20837
|
});
|
21255
20838
|
}
|
21256
20839
|
publish(track, opts, isStereo) {
|
21257
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m
|
20840
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
|
21258
20841
|
return __awaiter(this, void 0, void 0, function* () {
|
21259
|
-
const existingTrackOfSource = Array.from(this.
|
20842
|
+
const existingTrackOfSource = Array.from(this.trackPublications.values()).find(publishedTrack => track instanceof LocalTrack && publishedTrack.source === track.source);
|
21260
20843
|
if (existingTrackOfSource && track.source !== Track.Source.Unknown) {
|
21261
20844
|
this.log.info("publishing a second track with the same source: ".concat(track.source), Object.assign(Object.assign({}, this.logContext), getLogContextFromTrack(track)));
|
21262
20845
|
}
|
@@ -21287,438 +20870,933 @@ class LocalParticipant extends Participant {
|
|
21287
20870
|
track.on(TrackEvent.UpstreamResumed, this.onTrackUpstreamResumed);
|
21288
20871
|
// create track publication from track
|
21289
20872
|
const req = new AddTrackRequest({
|
21290
|
-
// get local track id for use during publishing
|
21291
|
-
cid: track.mediaStreamTrack.id,
|
21292
|
-
name: opts.name,
|
20873
|
+
// get local track id for use during publishing
|
20874
|
+
cid: track.mediaStreamTrack.id,
|
20875
|
+
name: opts.name,
|
20876
|
+
type: Track.kindToProto(track.kind),
|
20877
|
+
muted: track.isMuted,
|
20878
|
+
source: Track.sourceToProto(track.source),
|
20879
|
+
disableDtx: !((_a = opts.dtx) !== null && _a !== void 0 ? _a : true),
|
20880
|
+
encryption: this.encryptionType,
|
20881
|
+
stereo: isStereo,
|
20882
|
+
disableRed: this.isE2EEEnabled || !((_b = opts.red) !== null && _b !== void 0 ? _b : true),
|
20883
|
+
stream: opts === null || opts === void 0 ? void 0 : opts.stream
|
20884
|
+
});
|
20885
|
+
// compute encodings and layers for video
|
20886
|
+
let encodings;
|
20887
|
+
if (track.kind === Track.Kind.Video) {
|
20888
|
+
let dims = {
|
20889
|
+
width: 0,
|
20890
|
+
height: 0
|
20891
|
+
};
|
20892
|
+
try {
|
20893
|
+
dims = yield track.waitForDimensions();
|
20894
|
+
} catch (e) {
|
20895
|
+
// use defaults, it's quite painful for congestion control without simulcast
|
20896
|
+
// so using default dims according to publish settings
|
20897
|
+
const defaultRes = (_d = (_c = this.roomOptions.videoCaptureDefaults) === null || _c === void 0 ? void 0 : _c.resolution) !== null && _d !== void 0 ? _d : VideoPresets.h720.resolution;
|
20898
|
+
dims = {
|
20899
|
+
width: defaultRes.width,
|
20900
|
+
height: defaultRes.height
|
20901
|
+
};
|
20902
|
+
// log failure
|
20903
|
+
this.log.error('could not determine track dimensions, using defaults', Object.assign(Object.assign(Object.assign({}, this.logContext), getLogContextFromTrack(track)), {
|
20904
|
+
dims
|
20905
|
+
}));
|
20906
|
+
}
|
20907
|
+
// width and height should be defined for video
|
20908
|
+
req.width = dims.width;
|
20909
|
+
req.height = dims.height;
|
20910
|
+
// for svc codecs, disable simulcast and use vp8 for backup codec
|
20911
|
+
if (track instanceof LocalVideoTrack) {
|
20912
|
+
if (isSVCCodec(videoCodec)) {
|
20913
|
+
// vp9 svc with screenshare has problem to encode, always use L1T3 here
|
20914
|
+
if (track.source === Track.Source.ScreenShare && videoCodec === 'vp9') {
|
20915
|
+
opts.scalabilityMode = 'L1T3';
|
20916
|
+
}
|
20917
|
+
// set scalabilityMode to 'L3T3_KEY' by default
|
20918
|
+
opts.scalabilityMode = (_e = opts.scalabilityMode) !== null && _e !== void 0 ? _e : 'L3T3_KEY';
|
20919
|
+
}
|
20920
|
+
req.simulcastCodecs = [new SimulcastCodec({
|
20921
|
+
codec: videoCodec,
|
20922
|
+
cid: track.mediaStreamTrack.id
|
20923
|
+
})];
|
20924
|
+
// set up backup
|
20925
|
+
if (opts.backupCodec === true) {
|
20926
|
+
opts.backupCodec = {
|
20927
|
+
codec: defaultVideoCodec
|
20928
|
+
};
|
20929
|
+
}
|
20930
|
+
if (opts.backupCodec && videoCodec !== opts.backupCodec.codec &&
|
20931
|
+
// TODO remove this once e2ee is supported for backup codecs
|
20932
|
+
req.encryption === Encryption_Type.NONE) {
|
20933
|
+
// multi-codec simulcast requires dynacast
|
20934
|
+
if (!this.roomOptions.dynacast) {
|
20935
|
+
this.roomOptions.dynacast = true;
|
20936
|
+
}
|
20937
|
+
req.simulcastCodecs.push(new SimulcastCodec({
|
20938
|
+
codec: opts.backupCodec.codec,
|
20939
|
+
cid: ''
|
20940
|
+
}));
|
20941
|
+
}
|
20942
|
+
}
|
20943
|
+
encodings = computeVideoEncodings(track.source === Track.Source.ScreenShare, req.width, req.height, opts);
|
20944
|
+
req.layers = videoLayersFromEncodings(req.width, req.height, encodings, isSVCCodec(opts.videoCodec));
|
20945
|
+
} else if (track.kind === Track.Kind.Audio) {
|
20946
|
+
encodings = [{
|
20947
|
+
maxBitrate: (_f = opts.audioPreset) === null || _f === void 0 ? void 0 : _f.maxBitrate,
|
20948
|
+
priority: (_h = (_g = opts.audioPreset) === null || _g === void 0 ? void 0 : _g.priority) !== null && _h !== void 0 ? _h : 'high',
|
20949
|
+
networkPriority: (_k = (_j = opts.audioPreset) === null || _j === void 0 ? void 0 : _j.priority) !== null && _k !== void 0 ? _k : 'high'
|
20950
|
+
}];
|
20951
|
+
}
|
20952
|
+
if (!this.engine || this.engine.isClosed) {
|
20953
|
+
throw new UnexpectedConnectionState('cannot publish track when not connected');
|
20954
|
+
}
|
20955
|
+
const ti = yield this.engine.addTrack(req);
|
20956
|
+
// server might not support the codec the client has requested, in that case, fallback
|
20957
|
+
// to a supported codec
|
20958
|
+
let primaryCodecMime;
|
20959
|
+
ti.codecs.forEach(codec => {
|
20960
|
+
if (primaryCodecMime === undefined) {
|
20961
|
+
primaryCodecMime = codec.mimeType;
|
20962
|
+
}
|
20963
|
+
});
|
20964
|
+
if (primaryCodecMime && track.kind === Track.Kind.Video) {
|
20965
|
+
const updatedCodec = mimeTypeToVideoCodecString(primaryCodecMime);
|
20966
|
+
if (updatedCodec !== videoCodec) {
|
20967
|
+
this.log.debug('falling back to server selected codec', Object.assign(Object.assign(Object.assign({}, this.logContext), getLogContextFromTrack(track)), {
|
20968
|
+
codec: updatedCodec
|
20969
|
+
}));
|
20970
|
+
/* @ts-ignore */
|
20971
|
+
opts.videoCodec = updatedCodec;
|
20972
|
+
// recompute encodings since bitrates/etc could have changed
|
20973
|
+
encodings = computeVideoEncodings(track.source === Track.Source.ScreenShare, req.width, req.height, opts);
|
20974
|
+
}
|
20975
|
+
}
|
20976
|
+
const publication = new LocalTrackPublication(track.kind, ti, track, {
|
20977
|
+
loggerName: this.roomOptions.loggerName,
|
20978
|
+
loggerContextCb: () => this.logContext
|
20979
|
+
});
|
20980
|
+
// save options for when it needs to be republished again
|
20981
|
+
publication.options = opts;
|
20982
|
+
track.sid = ti.sid;
|
20983
|
+
if (!this.engine.pcManager) {
|
20984
|
+
throw new UnexpectedConnectionState('pcManager is not ready');
|
20985
|
+
}
|
20986
|
+
this.log.debug("publishing ".concat(track.kind, " with encodings"), Object.assign(Object.assign({}, this.logContext), {
|
20987
|
+
encodings,
|
20988
|
+
trackInfo: ti
|
20989
|
+
}));
|
20990
|
+
track.sender = yield this.engine.createSender(track, opts, encodings);
|
20991
|
+
if (encodings) {
|
20992
|
+
if (isFireFox() && track.kind === Track.Kind.Audio) {
|
20993
|
+
/* Refer to RFC https://datatracker.ietf.org/doc/html/rfc7587#section-6.1,
|
20994
|
+
livekit-server uses maxaveragebitrate=510000 in the answer sdp to permit client to
|
20995
|
+
publish high quality audio track. But firefox always uses this value as the actual
|
20996
|
+
bitrates, causing the audio bitrates to rise to 510Kbps in any stereo case unexpectedly.
|
20997
|
+
So the client need to modify maxaverragebitrates in answer sdp to user provided value to
|
20998
|
+
fix the issue.
|
20999
|
+
*/
|
21000
|
+
let trackTransceiver = undefined;
|
21001
|
+
for (const transceiver of this.engine.pcManager.publisher.getTransceivers()) {
|
21002
|
+
if (transceiver.sender === track.sender) {
|
21003
|
+
trackTransceiver = transceiver;
|
21004
|
+
break;
|
21005
|
+
}
|
21006
|
+
}
|
21007
|
+
if (trackTransceiver) {
|
21008
|
+
this.engine.pcManager.publisher.setTrackCodecBitrate({
|
21009
|
+
transceiver: trackTransceiver,
|
21010
|
+
codec: 'opus',
|
21011
|
+
maxbr: ((_l = encodings[0]) === null || _l === void 0 ? void 0 : _l.maxBitrate) ? encodings[0].maxBitrate / 1000 : 0
|
21012
|
+
});
|
21013
|
+
}
|
21014
|
+
} else if (track.codec && isSVCCodec(track.codec) && ((_m = encodings[0]) === null || _m === void 0 ? void 0 : _m.maxBitrate)) {
|
21015
|
+
this.engine.pcManager.publisher.setTrackCodecBitrate({
|
21016
|
+
cid: req.cid,
|
21017
|
+
codec: track.codec,
|
21018
|
+
maxbr: encodings[0].maxBitrate / 1000
|
21019
|
+
});
|
21020
|
+
}
|
21021
|
+
}
|
21022
|
+
yield this.engine.negotiate();
|
21023
|
+
if (track instanceof LocalVideoTrack) {
|
21024
|
+
track.startMonitor(this.engine.client);
|
21025
|
+
} else if (track instanceof LocalAudioTrack) {
|
21026
|
+
track.startMonitor();
|
21027
|
+
}
|
21028
|
+
this.addTrackPublication(publication);
|
21029
|
+
// send event for publication
|
21030
|
+
this.emit(ParticipantEvent.LocalTrackPublished, publication);
|
21031
|
+
return publication;
|
21032
|
+
});
|
21033
|
+
}
|
21034
|
+
get isLocal() {
|
21035
|
+
return true;
|
21036
|
+
}
|
21037
|
+
/** @internal
|
21038
|
+
* publish additional codec to existing track
|
21039
|
+
*/
|
21040
|
+
publishAdditionalCodecForTrack(track, videoCodec, options) {
|
21041
|
+
var _a;
|
21042
|
+
return __awaiter(this, void 0, void 0, function* () {
|
21043
|
+
// TODO remove once e2ee is supported for backup tracks
|
21044
|
+
if (this.encryptionType !== Encryption_Type.NONE) {
|
21045
|
+
return;
|
21046
|
+
}
|
21047
|
+
// is it not published? if so skip
|
21048
|
+
let existingPublication;
|
21049
|
+
this.trackPublications.forEach(publication => {
|
21050
|
+
if (!publication.track) {
|
21051
|
+
return;
|
21052
|
+
}
|
21053
|
+
if (publication.track === track) {
|
21054
|
+
existingPublication = publication;
|
21055
|
+
}
|
21056
|
+
});
|
21057
|
+
if (!existingPublication) {
|
21058
|
+
throw new TrackInvalidError('track is not published');
|
21059
|
+
}
|
21060
|
+
if (!(track instanceof LocalVideoTrack)) {
|
21061
|
+
throw new TrackInvalidError('track is not a video track');
|
21062
|
+
}
|
21063
|
+
const opts = Object.assign(Object.assign({}, (_a = this.roomOptions) === null || _a === void 0 ? void 0 : _a.publishDefaults), options);
|
21064
|
+
const encodings = computeTrackBackupEncodings(track, videoCodec, opts);
|
21065
|
+
if (!encodings) {
|
21066
|
+
this.log.info("backup codec has been disabled, ignoring request to add additional codec for track", Object.assign(Object.assign({}, this.logContext), getLogContextFromTrack(track)));
|
21067
|
+
return;
|
21068
|
+
}
|
21069
|
+
const simulcastTrack = track.addSimulcastTrack(videoCodec, encodings);
|
21070
|
+
if (!simulcastTrack) {
|
21071
|
+
return;
|
21072
|
+
}
|
21073
|
+
const req = new AddTrackRequest({
|
21074
|
+
cid: simulcastTrack.mediaStreamTrack.id,
|
21293
21075
|
type: Track.kindToProto(track.kind),
|
21294
21076
|
muted: track.isMuted,
|
21295
21077
|
source: Track.sourceToProto(track.source),
|
21296
|
-
|
21297
|
-
|
21298
|
-
|
21299
|
-
|
21300
|
-
|
21078
|
+
sid: track.sid,
|
21079
|
+
simulcastCodecs: [{
|
21080
|
+
codec: opts.videoCodec,
|
21081
|
+
cid: simulcastTrack.mediaStreamTrack.id
|
21082
|
+
}]
|
21301
21083
|
});
|
21302
|
-
|
21303
|
-
|
21304
|
-
|
21305
|
-
|
21306
|
-
|
21307
|
-
|
21308
|
-
|
21084
|
+
req.layers = videoLayersFromEncodings(req.width, req.height, encodings);
|
21085
|
+
if (!this.engine || this.engine.isClosed) {
|
21086
|
+
throw new UnexpectedConnectionState('cannot publish track when not connected');
|
21087
|
+
}
|
21088
|
+
const ti = yield this.engine.addTrack(req);
|
21089
|
+
yield this.engine.createSimulcastSender(track, simulcastTrack, opts, encodings);
|
21090
|
+
yield this.engine.negotiate();
|
21091
|
+
this.log.debug("published ".concat(videoCodec, " for track ").concat(track.sid), Object.assign(Object.assign({}, this.logContext), {
|
21092
|
+
encodings,
|
21093
|
+
trackInfo: ti
|
21094
|
+
}));
|
21095
|
+
});
|
21096
|
+
}
|
21097
|
+
unpublishTrack(track, stopOnUnpublish) {
|
21098
|
+
var _a, _b;
|
21099
|
+
return __awaiter(this, void 0, void 0, function* () {
|
21100
|
+
// look through all published tracks to find the right ones
|
21101
|
+
const publication = this.getPublicationForTrack(track);
|
21102
|
+
const pubLogContext = publication ? getLogContextFromTrack(publication) : undefined;
|
21103
|
+
this.log.debug('unpublishing track', Object.assign(Object.assign({}, this.logContext), pubLogContext));
|
21104
|
+
if (!publication || !publication.track) {
|
21105
|
+
this.log.warn('track was not unpublished because no publication was found', Object.assign(Object.assign({}, this.logContext), pubLogContext));
|
21106
|
+
return undefined;
|
21107
|
+
}
|
21108
|
+
track = publication.track;
|
21109
|
+
track.off(TrackEvent.Muted, this.onTrackMuted);
|
21110
|
+
track.off(TrackEvent.Unmuted, this.onTrackUnmuted);
|
21111
|
+
track.off(TrackEvent.Ended, this.handleTrackEnded);
|
21112
|
+
track.off(TrackEvent.UpstreamPaused, this.onTrackUpstreamPaused);
|
21113
|
+
track.off(TrackEvent.UpstreamResumed, this.onTrackUpstreamResumed);
|
21114
|
+
if (stopOnUnpublish === undefined) {
|
21115
|
+
stopOnUnpublish = (_b = (_a = this.roomOptions) === null || _a === void 0 ? void 0 : _a.stopLocalTrackOnUnpublish) !== null && _b !== void 0 ? _b : true;
|
21116
|
+
}
|
21117
|
+
if (stopOnUnpublish) {
|
21118
|
+
track.stop();
|
21119
|
+
}
|
21120
|
+
let negotiationNeeded = false;
|
21121
|
+
const trackSender = track.sender;
|
21122
|
+
track.sender = undefined;
|
21123
|
+
if (this.engine.pcManager && this.engine.pcManager.currentState < PCTransportState.FAILED && trackSender) {
|
21309
21124
|
try {
|
21310
|
-
|
21311
|
-
|
21312
|
-
|
21313
|
-
|
21314
|
-
|
21315
|
-
|
21316
|
-
|
21317
|
-
|
21318
|
-
};
|
21319
|
-
// log failure
|
21320
|
-
this.log.error('could not determine track dimensions, using defaults', Object.assign(Object.assign(Object.assign({}, this.logContext), getLogContextFromTrack(track)), {
|
21321
|
-
dims
|
21322
|
-
}));
|
21323
|
-
}
|
21324
|
-
// width and height should be defined for video
|
21325
|
-
req.width = dims.width;
|
21326
|
-
req.height = dims.height;
|
21327
|
-
// for svc codecs, disable simulcast and use vp8 for backup codec
|
21328
|
-
if (track instanceof LocalVideoTrack) {
|
21329
|
-
if (isSVCCodec(videoCodec)) {
|
21330
|
-
// vp9 svc with screenshare has problem to encode, always use L1T3 here
|
21331
|
-
if (track.source === Track.Source.ScreenShare && videoCodec === 'vp9') {
|
21332
|
-
opts.scalabilityMode = 'L1T3';
|
21125
|
+
for (const transceiver of this.engine.pcManager.publisher.getTransceivers()) {
|
21126
|
+
// if sender is not currently sending (after replaceTrack(null))
|
21127
|
+
// removeTrack would have no effect.
|
21128
|
+
// to ensure we end up successfully removing the track, manually set
|
21129
|
+
// the transceiver to inactive
|
21130
|
+
if (transceiver.sender === trackSender) {
|
21131
|
+
transceiver.direction = 'inactive';
|
21132
|
+
negotiationNeeded = true;
|
21333
21133
|
}
|
21334
|
-
// set scalabilityMode to 'L3T3_KEY' by default
|
21335
|
-
opts.scalabilityMode = (_e = opts.scalabilityMode) !== null && _e !== void 0 ? _e : 'L3T3_KEY';
|
21336
21134
|
}
|
21337
|
-
|
21338
|
-
|
21339
|
-
cid: track.mediaStreamTrack.id
|
21340
|
-
})];
|
21341
|
-
// set up backup
|
21342
|
-
if (opts.backupCodec === true) {
|
21343
|
-
opts.backupCodec = {
|
21344
|
-
codec: defaultVideoCodec
|
21345
|
-
};
|
21135
|
+
if (this.engine.removeTrack(trackSender)) {
|
21136
|
+
negotiationNeeded = true;
|
21346
21137
|
}
|
21347
|
-
if (
|
21348
|
-
|
21349
|
-
|
21350
|
-
|
21351
|
-
|
21352
|
-
|
21138
|
+
if (track instanceof LocalVideoTrack) {
|
21139
|
+
for (const [, trackInfo] of track.simulcastCodecs) {
|
21140
|
+
if (trackInfo.sender) {
|
21141
|
+
if (this.engine.removeTrack(trackInfo.sender)) {
|
21142
|
+
negotiationNeeded = true;
|
21143
|
+
}
|
21144
|
+
trackInfo.sender = undefined;
|
21145
|
+
}
|
21353
21146
|
}
|
21354
|
-
|
21355
|
-
codec: opts.backupCodec.codec,
|
21356
|
-
cid: ''
|
21357
|
-
}));
|
21147
|
+
track.simulcastCodecs.clear();
|
21358
21148
|
}
|
21149
|
+
} catch (e) {
|
21150
|
+
this.log.warn('failed to unpublish track', Object.assign(Object.assign(Object.assign({}, this.logContext), pubLogContext), {
|
21151
|
+
error: e
|
21152
|
+
}));
|
21359
21153
|
}
|
21360
|
-
encodings = computeVideoEncodings(track.source === Track.Source.ScreenShare, req.width, req.height, opts);
|
21361
|
-
req.layers = videoLayersFromEncodings(req.width, req.height, encodings, isSVCCodec(opts.videoCodec));
|
21362
|
-
} else if (track.kind === Track.Kind.Audio) {
|
21363
|
-
encodings = [{
|
21364
|
-
maxBitrate: (_g = (_f = opts.audioPreset) === null || _f === void 0 ? void 0 : _f.maxBitrate) !== null && _g !== void 0 ? _g : opts.audioBitrate,
|
21365
|
-
priority: (_j = (_h = opts.audioPreset) === null || _h === void 0 ? void 0 : _h.priority) !== null && _j !== void 0 ? _j : 'high',
|
21366
|
-
networkPriority: (_l = (_k = opts.audioPreset) === null || _k === void 0 ? void 0 : _k.priority) !== null && _l !== void 0 ? _l : 'high'
|
21367
|
-
}];
|
21368
21154
|
}
|
21369
|
-
|
21370
|
-
|
21155
|
+
// remove from our maps
|
21156
|
+
this.trackPublications.delete(publication.trackSid);
|
21157
|
+
switch (publication.kind) {
|
21158
|
+
case Track.Kind.Audio:
|
21159
|
+
this.audioTrackPublications.delete(publication.trackSid);
|
21160
|
+
break;
|
21161
|
+
case Track.Kind.Video:
|
21162
|
+
this.videoTrackPublications.delete(publication.trackSid);
|
21163
|
+
break;
|
21371
21164
|
}
|
21372
|
-
|
21373
|
-
|
21374
|
-
|
21375
|
-
|
21376
|
-
|
21377
|
-
|
21378
|
-
|
21165
|
+
this.emit(ParticipantEvent.LocalTrackUnpublished, publication);
|
21166
|
+
publication.setTrack(undefined);
|
21167
|
+
if (negotiationNeeded) {
|
21168
|
+
yield this.engine.negotiate();
|
21169
|
+
}
|
21170
|
+
return publication;
|
21171
|
+
});
|
21172
|
+
}
|
21173
|
+
unpublishTracks(tracks) {
|
21174
|
+
return __awaiter(this, void 0, void 0, function* () {
|
21175
|
+
const results = yield Promise.all(tracks.map(track => this.unpublishTrack(track)));
|
21176
|
+
return results.filter(track => track instanceof LocalTrackPublication);
|
21177
|
+
});
|
21178
|
+
}
|
21179
|
+
republishAllTracks(options) {
|
21180
|
+
let restartTracks = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
|
21181
|
+
return __awaiter(this, void 0, void 0, function* () {
|
21182
|
+
const localPubs = [];
|
21183
|
+
this.trackPublications.forEach(pub => {
|
21184
|
+
if (pub.track) {
|
21185
|
+
if (options) {
|
21186
|
+
pub.options = Object.assign(Object.assign({}, pub.options), options);
|
21187
|
+
}
|
21188
|
+
localPubs.push(pub);
|
21379
21189
|
}
|
21380
21190
|
});
|
21381
|
-
|
21382
|
-
const
|
21383
|
-
|
21384
|
-
|
21385
|
-
|
21191
|
+
yield Promise.all(localPubs.map(pub => __awaiter(this, void 0, void 0, function* () {
|
21192
|
+
const track = pub.track;
|
21193
|
+
yield this.unpublishTrack(track, false);
|
21194
|
+
if (restartTracks && !track.isMuted && track.source !== Track.Source.ScreenShare && track.source !== Track.Source.ScreenShareAudio && (track instanceof LocalAudioTrack || track instanceof LocalVideoTrack) && !track.isUserProvided) {
|
21195
|
+
// generally we need to restart the track before publishing, often a full reconnect
|
21196
|
+
// is necessary because computer had gone to sleep.
|
21197
|
+
this.log.debug('restarting existing track', Object.assign(Object.assign({}, this.logContext), {
|
21198
|
+
track: pub.trackSid
|
21386
21199
|
}));
|
21387
|
-
|
21388
|
-
|
21389
|
-
|
21390
|
-
|
21200
|
+
yield track.restartTrack();
|
21201
|
+
}
|
21202
|
+
yield this.publishTrack(track, pub.options);
|
21203
|
+
})));
|
21204
|
+
});
|
21205
|
+
}
|
21206
|
+
/**
|
21207
|
+
* Publish a new data payload to the room. Data will be forwarded to each
|
21208
|
+
* participant in the room if the destination field in publishOptions is empty
|
21209
|
+
*
|
21210
|
+
* @param data Uint8Array of the payload. To send string data, use TextEncoder.encode
|
21211
|
+
* @param options optionally specify a `reliable`, `topic` and `destination`
|
21212
|
+
*/
|
21213
|
+
publishData(data) {
|
21214
|
+
let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
21215
|
+
return __awaiter(this, void 0, void 0, function* () {
|
21216
|
+
const kind = options.reliable ? DataPacket_Kind.RELIABLE : DataPacket_Kind.LOSSY;
|
21217
|
+
const destinationIdentities = options.destinationIdentities;
|
21218
|
+
const topic = options.topic;
|
21219
|
+
const packet = new DataPacket({
|
21220
|
+
kind: kind,
|
21221
|
+
value: {
|
21222
|
+
case: 'user',
|
21223
|
+
value: new UserPacket({
|
21224
|
+
participantIdentity: this.identity,
|
21225
|
+
payload: data,
|
21226
|
+
destinationIdentities,
|
21227
|
+
topic
|
21228
|
+
})
|
21229
|
+
}
|
21230
|
+
});
|
21231
|
+
yield this.engine.sendDataPacket(packet, kind);
|
21232
|
+
});
|
21233
|
+
}
|
21234
|
+
/**
|
21235
|
+
* Control who can subscribe to LocalParticipant's published tracks.
|
21236
|
+
*
|
21237
|
+
* By default, all participants can subscribe. This allows fine-grained control over
|
21238
|
+
* who is able to subscribe at a participant and track level.
|
21239
|
+
*
|
21240
|
+
* Note: if access is given at a track-level (i.e. both [allParticipantsAllowed] and
|
21241
|
+
* [ParticipantTrackPermission.allTracksAllowed] are false), any newer published tracks
|
21242
|
+
* will not grant permissions to any participants and will require a subsequent
|
21243
|
+
* permissions update to allow subscription.
|
21244
|
+
*
|
21245
|
+
* @param allParticipantsAllowed Allows all participants to subscribe all tracks.
|
21246
|
+
* Takes precedence over [[participantTrackPermissions]] if set to true.
|
21247
|
+
* By default this is set to true.
|
21248
|
+
* @param participantTrackPermissions Full list of individual permissions per
|
21249
|
+
* participant/track. Any omitted participants will not receive any permissions.
|
21250
|
+
*/
|
21251
|
+
setTrackSubscriptionPermissions(allParticipantsAllowed) {
|
21252
|
+
let participantTrackPermissions = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
|
21253
|
+
this.participantTrackPermissions = participantTrackPermissions;
|
21254
|
+
this.allParticipantsAllowedToSubscribe = allParticipantsAllowed;
|
21255
|
+
if (!this.engine.client.isDisconnected) {
|
21256
|
+
this.updateTrackSubscriptionPermissions();
|
21257
|
+
}
|
21258
|
+
}
|
21259
|
+
/** @internal */
|
21260
|
+
updateInfo(info) {
|
21261
|
+
if (info.sid !== this.sid) {
|
21262
|
+
// drop updates that specify a wrong sid.
|
21263
|
+
// the sid for local participant is only explicitly set on join and full reconnect
|
21264
|
+
return false;
|
21265
|
+
}
|
21266
|
+
if (!super.updateInfo(info)) {
|
21267
|
+
return false;
|
21268
|
+
}
|
21269
|
+
// reconcile track mute status.
|
21270
|
+
// if server's track mute status doesn't match actual, we'll have to update
|
21271
|
+
// the server's copy
|
21272
|
+
info.tracks.forEach(ti => {
|
21273
|
+
var _a, _b;
|
21274
|
+
const pub = this.trackPublications.get(ti.sid);
|
21275
|
+
if (pub) {
|
21276
|
+
const mutedOnServer = pub.isMuted || ((_b = (_a = pub.track) === null || _a === void 0 ? void 0 : _a.isUpstreamPaused) !== null && _b !== void 0 ? _b : false);
|
21277
|
+
if (mutedOnServer !== ti.muted) {
|
21278
|
+
this.log.debug('updating server mute state after reconcile', Object.assign(Object.assign(Object.assign({}, this.logContext), getLogContextFromTrack(pub)), {
|
21279
|
+
mutedOnServer
|
21280
|
+
}));
|
21281
|
+
this.engine.client.sendMuteTrack(ti.sid, mutedOnServer);
|
21391
21282
|
}
|
21392
21283
|
}
|
21393
|
-
|
21394
|
-
|
21395
|
-
|
21396
|
-
|
21397
|
-
|
21398
|
-
|
21399
|
-
|
21400
|
-
if (!
|
21401
|
-
|
21284
|
+
});
|
21285
|
+
return true;
|
21286
|
+
}
|
21287
|
+
getPublicationForTrack(track) {
|
21288
|
+
let publication;
|
21289
|
+
this.trackPublications.forEach(pub => {
|
21290
|
+
const localTrack = pub.track;
|
21291
|
+
if (!localTrack) {
|
21292
|
+
return;
|
21402
21293
|
}
|
21403
|
-
this
|
21404
|
-
|
21405
|
-
|
21406
|
-
|
21407
|
-
|
21408
|
-
if (encodings) {
|
21409
|
-
if (isFireFox() && track.kind === Track.Kind.Audio) {
|
21410
|
-
/* Refer to RFC https://datatracker.ietf.org/doc/html/rfc7587#section-6.1,
|
21411
|
-
livekit-server uses maxaveragebitrate=510000 in the answer sdp to permit client to
|
21412
|
-
publish high quality audio track. But firefox always uses this value as the actual
|
21413
|
-
bitrates, causing the audio bitrates to rise to 510Kbps in any stereo case unexpectedly.
|
21414
|
-
So the client need to modify maxaverragebitrates in answer sdp to user provided value to
|
21415
|
-
fix the issue.
|
21416
|
-
*/
|
21417
|
-
let trackTransceiver = undefined;
|
21418
|
-
for (const transceiver of this.engine.pcManager.publisher.getTransceivers()) {
|
21419
|
-
if (transceiver.sender === track.sender) {
|
21420
|
-
trackTransceiver = transceiver;
|
21421
|
-
break;
|
21422
|
-
}
|
21423
|
-
}
|
21424
|
-
if (trackTransceiver) {
|
21425
|
-
this.engine.pcManager.publisher.setTrackCodecBitrate({
|
21426
|
-
transceiver: trackTransceiver,
|
21427
|
-
codec: 'opus',
|
21428
|
-
maxbr: ((_m = encodings[0]) === null || _m === void 0 ? void 0 : _m.maxBitrate) ? encodings[0].maxBitrate / 1000 : 0
|
21429
|
-
});
|
21294
|
+
// this looks overly complicated due to this object tree
|
21295
|
+
if (track instanceof MediaStreamTrack) {
|
21296
|
+
if (localTrack instanceof LocalAudioTrack || localTrack instanceof LocalVideoTrack) {
|
21297
|
+
if (localTrack.mediaStreamTrack === track) {
|
21298
|
+
publication = pub;
|
21430
21299
|
}
|
21431
|
-
} else if (track.codec && isSVCCodec(track.codec) && ((_o = encodings[0]) === null || _o === void 0 ? void 0 : _o.maxBitrate)) {
|
21432
|
-
this.engine.pcManager.publisher.setTrackCodecBitrate({
|
21433
|
-
cid: req.cid,
|
21434
|
-
codec: track.codec,
|
21435
|
-
maxbr: encodings[0].maxBitrate / 1000
|
21436
|
-
});
|
21437
21300
|
}
|
21301
|
+
} else if (track === localTrack) {
|
21302
|
+
publication = pub;
|
21438
21303
|
}
|
21439
|
-
yield this.engine.negotiate();
|
21440
|
-
if (track instanceof LocalVideoTrack) {
|
21441
|
-
track.startMonitor(this.engine.client);
|
21442
|
-
} else if (track instanceof LocalAudioTrack) {
|
21443
|
-
track.startMonitor();
|
21444
|
-
}
|
21445
|
-
this.addTrackPublication(publication);
|
21446
|
-
// send event for publication
|
21447
|
-
this.emit(ParticipantEvent.LocalTrackPublished, publication);
|
21448
|
-
return publication;
|
21449
21304
|
});
|
21305
|
+
return publication;
|
21306
|
+
}
|
21307
|
+
}
|
21308
|
+
|
21309
|
+
class RemoteTrackPublication extends TrackPublication {
|
21310
|
+
constructor(kind, ti, autoSubscribe, loggerOptions) {
|
21311
|
+
super(kind, ti.sid, ti.name, loggerOptions);
|
21312
|
+
this.track = undefined;
|
21313
|
+
/** @internal */
|
21314
|
+
this.allowed = true;
|
21315
|
+
this.disabled = false;
|
21316
|
+
this.currentVideoQuality = VideoQuality.HIGH;
|
21317
|
+
this.handleEnded = track => {
|
21318
|
+
this.setTrack(undefined);
|
21319
|
+
this.emit(TrackEvent.Ended, track);
|
21320
|
+
};
|
21321
|
+
this.handleVisibilityChange = visible => {
|
21322
|
+
this.log.debug("adaptivestream video visibility ".concat(this.trackSid, ", visible=").concat(visible), this.logContext);
|
21323
|
+
this.disabled = !visible;
|
21324
|
+
this.emitTrackUpdate();
|
21325
|
+
};
|
21326
|
+
this.handleVideoDimensionsChange = dimensions => {
|
21327
|
+
this.log.debug("adaptivestream video dimensions ".concat(dimensions.width, "x").concat(dimensions.height), this.logContext);
|
21328
|
+
this.videoDimensions = dimensions;
|
21329
|
+
this.emitTrackUpdate();
|
21330
|
+
};
|
21331
|
+
this.subscribed = autoSubscribe;
|
21332
|
+
this.updateInfo(ti);
|
21333
|
+
}
|
21334
|
+
/**
|
21335
|
+
* Subscribe or unsubscribe to this remote track
|
21336
|
+
* @param subscribed true to subscribe to a track, false to unsubscribe
|
21337
|
+
*/
|
21338
|
+
setSubscribed(subscribed) {
|
21339
|
+
const prevStatus = this.subscriptionStatus;
|
21340
|
+
const prevPermission = this.permissionStatus;
|
21341
|
+
this.subscribed = subscribed;
|
21342
|
+
// reset allowed status when desired subscription state changes
|
21343
|
+
// server will notify client via signal message if it's not allowed
|
21344
|
+
if (subscribed) {
|
21345
|
+
this.allowed = true;
|
21346
|
+
}
|
21347
|
+
const sub = new UpdateSubscription({
|
21348
|
+
trackSids: [this.trackSid],
|
21349
|
+
subscribe: this.subscribed,
|
21350
|
+
participantTracks: [new ParticipantTracks({
|
21351
|
+
// sending an empty participant id since TrackPublication doesn't keep it
|
21352
|
+
// this is filled in by the participant that receives this message
|
21353
|
+
participantSid: '',
|
21354
|
+
trackSids: [this.trackSid]
|
21355
|
+
})]
|
21356
|
+
});
|
21357
|
+
this.emit(TrackEvent.UpdateSubscription, sub);
|
21358
|
+
this.emitSubscriptionUpdateIfChanged(prevStatus);
|
21359
|
+
this.emitPermissionUpdateIfChanged(prevPermission);
|
21360
|
+
}
|
21361
|
+
get subscriptionStatus() {
|
21362
|
+
if (this.subscribed === false) {
|
21363
|
+
return TrackPublication.SubscriptionStatus.Unsubscribed;
|
21364
|
+
}
|
21365
|
+
if (!super.isSubscribed) {
|
21366
|
+
return TrackPublication.SubscriptionStatus.Desired;
|
21367
|
+
}
|
21368
|
+
return TrackPublication.SubscriptionStatus.Subscribed;
|
21369
|
+
}
|
21370
|
+
get permissionStatus() {
|
21371
|
+
return this.allowed ? TrackPublication.PermissionStatus.Allowed : TrackPublication.PermissionStatus.NotAllowed;
|
21372
|
+
}
|
21373
|
+
/**
|
21374
|
+
* Returns true if track is subscribed, and ready for playback
|
21375
|
+
*/
|
21376
|
+
get isSubscribed() {
|
21377
|
+
if (this.subscribed === false) {
|
21378
|
+
return false;
|
21379
|
+
}
|
21380
|
+
return super.isSubscribed;
|
21381
|
+
}
|
21382
|
+
// returns client's desire to subscribe to a track, also true if autoSubscribe is enabled
|
21383
|
+
get isDesired() {
|
21384
|
+
return this.subscribed !== false;
|
21385
|
+
}
|
21386
|
+
get isEnabled() {
|
21387
|
+
return !this.disabled;
|
21388
|
+
}
|
21389
|
+
/**
|
21390
|
+
* disable server from sending down data for this track. this is useful when
|
21391
|
+
* the participant is off screen, you may disable streaming down their video
|
21392
|
+
* to reduce bandwidth requirements
|
21393
|
+
* @param enabled
|
21394
|
+
*/
|
21395
|
+
setEnabled(enabled) {
|
21396
|
+
if (!this.isManualOperationAllowed() || this.disabled === !enabled) {
|
21397
|
+
return;
|
21398
|
+
}
|
21399
|
+
this.disabled = !enabled;
|
21400
|
+
this.emitTrackUpdate();
|
21401
|
+
}
|
21402
|
+
/**
|
21403
|
+
* for tracks that support simulcasting, adjust subscribed quality
|
21404
|
+
*
|
21405
|
+
* This indicates the highest quality the client can accept. if network
|
21406
|
+
* bandwidth does not allow, server will automatically reduce quality to
|
21407
|
+
* optimize for uninterrupted video
|
21408
|
+
*/
|
21409
|
+
setVideoQuality(quality) {
|
21410
|
+
if (!this.isManualOperationAllowed() || this.currentVideoQuality === quality) {
|
21411
|
+
return;
|
21412
|
+
}
|
21413
|
+
this.currentVideoQuality = quality;
|
21414
|
+
this.videoDimensions = undefined;
|
21415
|
+
this.emitTrackUpdate();
|
21416
|
+
}
|
21417
|
+
setVideoDimensions(dimensions) {
|
21418
|
+
var _a, _b;
|
21419
|
+
if (!this.isManualOperationAllowed()) {
|
21420
|
+
return;
|
21421
|
+
}
|
21422
|
+
if (((_a = this.videoDimensions) === null || _a === void 0 ? void 0 : _a.width) === dimensions.width && ((_b = this.videoDimensions) === null || _b === void 0 ? void 0 : _b.height) === dimensions.height) {
|
21423
|
+
return;
|
21424
|
+
}
|
21425
|
+
if (this.track instanceof RemoteVideoTrack) {
|
21426
|
+
this.videoDimensions = dimensions;
|
21427
|
+
}
|
21428
|
+
this.currentVideoQuality = undefined;
|
21429
|
+
this.emitTrackUpdate();
|
21430
|
+
}
|
21431
|
+
setVideoFPS(fps) {
|
21432
|
+
if (!this.isManualOperationAllowed()) {
|
21433
|
+
return;
|
21434
|
+
}
|
21435
|
+
if (!(this.track instanceof RemoteVideoTrack)) {
|
21436
|
+
return;
|
21437
|
+
}
|
21438
|
+
if (this.fps === fps) {
|
21439
|
+
return;
|
21440
|
+
}
|
21441
|
+
this.fps = fps;
|
21442
|
+
this.emitTrackUpdate();
|
21443
|
+
}
|
21444
|
+
get videoQuality() {
|
21445
|
+
return this.currentVideoQuality;
|
21446
|
+
}
|
21447
|
+
/** @internal */
|
21448
|
+
setTrack(track) {
|
21449
|
+
const prevStatus = this.subscriptionStatus;
|
21450
|
+
const prevPermission = this.permissionStatus;
|
21451
|
+
const prevTrack = this.track;
|
21452
|
+
if (prevTrack === track) {
|
21453
|
+
return;
|
21454
|
+
}
|
21455
|
+
if (prevTrack) {
|
21456
|
+
// unregister listener
|
21457
|
+
prevTrack.off(TrackEvent.VideoDimensionsChanged, this.handleVideoDimensionsChange);
|
21458
|
+
prevTrack.off(TrackEvent.VisibilityChanged, this.handleVisibilityChange);
|
21459
|
+
prevTrack.off(TrackEvent.Ended, this.handleEnded);
|
21460
|
+
prevTrack.detach();
|
21461
|
+
prevTrack.stopMonitor();
|
21462
|
+
this.emit(TrackEvent.Unsubscribed, prevTrack);
|
21463
|
+
}
|
21464
|
+
super.setTrack(track);
|
21465
|
+
if (track) {
|
21466
|
+
track.sid = this.trackSid;
|
21467
|
+
track.on(TrackEvent.VideoDimensionsChanged, this.handleVideoDimensionsChange);
|
21468
|
+
track.on(TrackEvent.VisibilityChanged, this.handleVisibilityChange);
|
21469
|
+
track.on(TrackEvent.Ended, this.handleEnded);
|
21470
|
+
this.emit(TrackEvent.Subscribed, track);
|
21471
|
+
}
|
21472
|
+
this.emitPermissionUpdateIfChanged(prevPermission);
|
21473
|
+
this.emitSubscriptionUpdateIfChanged(prevStatus);
|
21474
|
+
}
|
21475
|
+
/** @internal */
|
21476
|
+
setAllowed(allowed) {
|
21477
|
+
const prevStatus = this.subscriptionStatus;
|
21478
|
+
const prevPermission = this.permissionStatus;
|
21479
|
+
this.allowed = allowed;
|
21480
|
+
this.emitPermissionUpdateIfChanged(prevPermission);
|
21481
|
+
this.emitSubscriptionUpdateIfChanged(prevStatus);
|
21482
|
+
}
|
21483
|
+
/** @internal */
|
21484
|
+
setSubscriptionError(error) {
|
21485
|
+
this.emit(TrackEvent.SubscriptionFailed, error);
|
21486
|
+
}
|
21487
|
+
/** @internal */
|
21488
|
+
updateInfo(info) {
|
21489
|
+
super.updateInfo(info);
|
21490
|
+
const prevMetadataMuted = this.metadataMuted;
|
21491
|
+
this.metadataMuted = info.muted;
|
21492
|
+
if (this.track) {
|
21493
|
+
this.track.setMuted(info.muted);
|
21494
|
+
} else if (prevMetadataMuted !== info.muted) {
|
21495
|
+
this.emit(info.muted ? TrackEvent.Muted : TrackEvent.Unmuted);
|
21496
|
+
}
|
21450
21497
|
}
|
21451
|
-
|
21498
|
+
emitSubscriptionUpdateIfChanged(previousStatus) {
|
21499
|
+
const currentStatus = this.subscriptionStatus;
|
21500
|
+
if (previousStatus === currentStatus) {
|
21501
|
+
return;
|
21502
|
+
}
|
21503
|
+
this.emit(TrackEvent.SubscriptionStatusChanged, currentStatus, previousStatus);
|
21504
|
+
}
|
21505
|
+
emitPermissionUpdateIfChanged(previousPermissionStatus) {
|
21506
|
+
const currentPermissionStatus = this.permissionStatus;
|
21507
|
+
if (currentPermissionStatus !== previousPermissionStatus) {
|
21508
|
+
this.emit(TrackEvent.SubscriptionPermissionChanged, this.permissionStatus, previousPermissionStatus);
|
21509
|
+
}
|
21510
|
+
}
|
21511
|
+
isManualOperationAllowed() {
|
21512
|
+
if (this.kind === Track.Kind.Video && this.isAdaptiveStream) {
|
21513
|
+
this.log.warn('adaptive stream is enabled, cannot change video track settings', this.logContext);
|
21514
|
+
return false;
|
21515
|
+
}
|
21516
|
+
if (!this.isDesired) {
|
21517
|
+
this.log.warn('cannot update track settings when not subscribed', this.logContext);
|
21518
|
+
return false;
|
21519
|
+
}
|
21452
21520
|
return true;
|
21453
21521
|
}
|
21454
|
-
|
21455
|
-
|
21456
|
-
*/
|
21457
|
-
publishAdditionalCodecForTrack(track, videoCodec, options) {
|
21458
|
-
var _a;
|
21459
|
-
return __awaiter(this, void 0, void 0, function* () {
|
21460
|
-
// TODO remove once e2ee is supported for backup tracks
|
21461
|
-
if (this.encryptionType !== Encryption_Type.NONE) {
|
21462
|
-
return;
|
21463
|
-
}
|
21464
|
-
// is it not published? if so skip
|
21465
|
-
let existingPublication;
|
21466
|
-
this.tracks.forEach(publication => {
|
21467
|
-
if (!publication.track) {
|
21468
|
-
return;
|
21469
|
-
}
|
21470
|
-
if (publication.track === track) {
|
21471
|
-
existingPublication = publication;
|
21472
|
-
}
|
21473
|
-
});
|
21474
|
-
if (!existingPublication) {
|
21475
|
-
throw new TrackInvalidError('track is not published');
|
21476
|
-
}
|
21477
|
-
if (!(track instanceof LocalVideoTrack)) {
|
21478
|
-
throw new TrackInvalidError('track is not a video track');
|
21479
|
-
}
|
21480
|
-
const opts = Object.assign(Object.assign({}, (_a = this.roomOptions) === null || _a === void 0 ? void 0 : _a.publishDefaults), options);
|
21481
|
-
const encodings = computeTrackBackupEncodings(track, videoCodec, opts);
|
21482
|
-
if (!encodings) {
|
21483
|
-
this.log.info("backup codec has been disabled, ignoring request to add additional codec for track", Object.assign(Object.assign({}, this.logContext), getLogContextFromTrack(track)));
|
21484
|
-
return;
|
21485
|
-
}
|
21486
|
-
const simulcastTrack = track.addSimulcastTrack(videoCodec, encodings);
|
21487
|
-
const req = new AddTrackRequest({
|
21488
|
-
cid: simulcastTrack.mediaStreamTrack.id,
|
21489
|
-
type: Track.kindToProto(track.kind),
|
21490
|
-
muted: track.isMuted,
|
21491
|
-
source: Track.sourceToProto(track.source),
|
21492
|
-
sid: track.sid,
|
21493
|
-
simulcastCodecs: [{
|
21494
|
-
codec: opts.videoCodec,
|
21495
|
-
cid: simulcastTrack.mediaStreamTrack.id
|
21496
|
-
}]
|
21497
|
-
});
|
21498
|
-
req.layers = videoLayersFromEncodings(req.width, req.height, encodings);
|
21499
|
-
if (!this.engine || this.engine.isClosed) {
|
21500
|
-
throw new UnexpectedConnectionState('cannot publish track when not connected');
|
21501
|
-
}
|
21502
|
-
const ti = yield this.engine.addTrack(req);
|
21503
|
-
yield this.engine.createSimulcastSender(track, simulcastTrack, opts, encodings);
|
21504
|
-
yield this.engine.negotiate();
|
21505
|
-
this.log.debug("published ".concat(videoCodec, " for track ").concat(track.sid), Object.assign(Object.assign({}, this.logContext), {
|
21506
|
-
encodings,
|
21507
|
-
trackInfo: ti
|
21508
|
-
}));
|
21509
|
-
});
|
21522
|
+
get isAdaptiveStream() {
|
21523
|
+
return this.track instanceof RemoteVideoTrack && this.track.isAdaptiveStream;
|
21510
21524
|
}
|
21511
|
-
|
21512
|
-
|
21513
|
-
|
21514
|
-
|
21515
|
-
|
21516
|
-
|
21517
|
-
this.log.debug('unpublishing track', Object.assign(Object.assign({}, this.logContext), pubLogContext));
|
21518
|
-
if (!publication || !publication.track) {
|
21519
|
-
this.log.warn('track was not unpublished because no publication was found', Object.assign(Object.assign({}, this.logContext), pubLogContext));
|
21520
|
-
return undefined;
|
21521
|
-
}
|
21522
|
-
track = publication.track;
|
21523
|
-
track.off(TrackEvent.Muted, this.onTrackMuted);
|
21524
|
-
track.off(TrackEvent.Unmuted, this.onTrackUnmuted);
|
21525
|
-
track.off(TrackEvent.Ended, this.handleTrackEnded);
|
21526
|
-
track.off(TrackEvent.UpstreamPaused, this.onTrackUpstreamPaused);
|
21527
|
-
track.off(TrackEvent.UpstreamResumed, this.onTrackUpstreamResumed);
|
21528
|
-
if (stopOnUnpublish === undefined) {
|
21529
|
-
stopOnUnpublish = (_b = (_a = this.roomOptions) === null || _a === void 0 ? void 0 : _a.stopLocalTrackOnUnpublish) !== null && _b !== void 0 ? _b : true;
|
21530
|
-
}
|
21531
|
-
if (stopOnUnpublish) {
|
21532
|
-
track.stop();
|
21533
|
-
}
|
21534
|
-
let negotiationNeeded = false;
|
21535
|
-
const trackSender = track.sender;
|
21536
|
-
track.sender = undefined;
|
21537
|
-
if (this.engine.pcManager && this.engine.pcManager.currentState < PCTransportState.FAILED && trackSender) {
|
21538
|
-
try {
|
21539
|
-
for (const transceiver of this.engine.pcManager.publisher.getTransceivers()) {
|
21540
|
-
// if sender is not currently sending (after replaceTrack(null))
|
21541
|
-
// removeTrack would have no effect.
|
21542
|
-
// to ensure we end up successfully removing the track, manually set
|
21543
|
-
// the transceiver to inactive
|
21544
|
-
if (transceiver.sender === trackSender) {
|
21545
|
-
transceiver.direction = 'inactive';
|
21546
|
-
negotiationNeeded = true;
|
21547
|
-
}
|
21548
|
-
}
|
21549
|
-
if (this.engine.removeTrack(trackSender)) {
|
21550
|
-
negotiationNeeded = true;
|
21551
|
-
}
|
21552
|
-
if (track instanceof LocalVideoTrack) {
|
21553
|
-
for (const [, trackInfo] of track.simulcastCodecs) {
|
21554
|
-
if (trackInfo.sender) {
|
21555
|
-
if (this.engine.removeTrack(trackInfo.sender)) {
|
21556
|
-
negotiationNeeded = true;
|
21557
|
-
}
|
21558
|
-
trackInfo.sender = undefined;
|
21559
|
-
}
|
21560
|
-
}
|
21561
|
-
track.simulcastCodecs.clear();
|
21562
|
-
}
|
21563
|
-
} catch (e) {
|
21564
|
-
this.log.warn('failed to unpublish track', Object.assign(Object.assign(Object.assign({}, this.logContext), pubLogContext), {
|
21565
|
-
error: e
|
21566
|
-
}));
|
21567
|
-
}
|
21568
|
-
}
|
21569
|
-
// remove from our maps
|
21570
|
-
this.tracks.delete(publication.trackSid);
|
21571
|
-
switch (publication.kind) {
|
21572
|
-
case Track.Kind.Audio:
|
21573
|
-
this.audioTracks.delete(publication.trackSid);
|
21574
|
-
break;
|
21575
|
-
case Track.Kind.Video:
|
21576
|
-
this.videoTracks.delete(publication.trackSid);
|
21577
|
-
break;
|
21578
|
-
}
|
21579
|
-
this.emit(ParticipantEvent.LocalTrackUnpublished, publication);
|
21580
|
-
publication.setTrack(undefined);
|
21581
|
-
if (negotiationNeeded) {
|
21582
|
-
yield this.engine.negotiate();
|
21583
|
-
}
|
21584
|
-
return publication;
|
21525
|
+
/* @internal */
|
21526
|
+
emitTrackUpdate() {
|
21527
|
+
const settings = new UpdateTrackSettings({
|
21528
|
+
trackSids: [this.trackSid],
|
21529
|
+
disabled: this.disabled,
|
21530
|
+
fps: this.fps
|
21585
21531
|
});
|
21532
|
+
if (this.videoDimensions) {
|
21533
|
+
settings.width = Math.ceil(this.videoDimensions.width);
|
21534
|
+
settings.height = Math.ceil(this.videoDimensions.height);
|
21535
|
+
} else if (this.currentVideoQuality !== undefined) {
|
21536
|
+
settings.quality = this.currentVideoQuality;
|
21537
|
+
} else {
|
21538
|
+
// defaults to high quality
|
21539
|
+
settings.quality = VideoQuality.HIGH;
|
21540
|
+
}
|
21541
|
+
this.emit(TrackEvent.UpdateSettings, settings);
|
21586
21542
|
}
|
21587
|
-
|
21588
|
-
|
21589
|
-
|
21590
|
-
|
21591
|
-
|
21543
|
+
}
|
21544
|
+
|
21545
|
+
class RemoteParticipant extends Participant {
|
21546
|
+
/** @internal */
|
21547
|
+
static fromParticipantInfo(signalClient, pi) {
|
21548
|
+
return new RemoteParticipant(signalClient, pi.sid, pi.identity, pi.name, pi.metadata);
|
21592
21549
|
}
|
21593
|
-
|
21594
|
-
|
21595
|
-
|
21596
|
-
|
21597
|
-
|
21598
|
-
|
21599
|
-
|
21600
|
-
|
21601
|
-
|
21602
|
-
|
21603
|
-
|
21550
|
+
/** @internal */
|
21551
|
+
constructor(signalClient, sid, identity, name, metadata, loggerOptions) {
|
21552
|
+
super(sid, identity || '', name, metadata, loggerOptions);
|
21553
|
+
this.signalClient = signalClient;
|
21554
|
+
this.trackPublications = new Map();
|
21555
|
+
this.audioTrackPublications = new Map();
|
21556
|
+
this.videoTrackPublications = new Map();
|
21557
|
+
this.volumeMap = new Map();
|
21558
|
+
}
|
21559
|
+
addTrackPublication(publication) {
|
21560
|
+
super.addTrackPublication(publication);
|
21561
|
+
// register action events
|
21562
|
+
publication.on(TrackEvent.UpdateSettings, settings => {
|
21563
|
+
this.log.debug('send update settings', Object.assign(Object.assign({}, this.logContext), getLogContextFromTrack(publication)));
|
21564
|
+
this.signalClient.sendUpdateTrackSettings(settings);
|
21565
|
+
});
|
21566
|
+
publication.on(TrackEvent.UpdateSubscription, sub => {
|
21567
|
+
sub.participantTracks.forEach(pt => {
|
21568
|
+
pt.participantSid = this.sid;
|
21604
21569
|
});
|
21605
|
-
|
21606
|
-
|
21607
|
-
|
21608
|
-
|
21609
|
-
// generally we need to restart the track before publishing, often a full reconnect
|
21610
|
-
// is necessary because computer had gone to sleep.
|
21611
|
-
this.log.debug('restarting existing track', Object.assign(Object.assign({}, this.logContext), {
|
21612
|
-
track: pub.trackSid
|
21613
|
-
}));
|
21614
|
-
yield track.restartTrack();
|
21615
|
-
}
|
21616
|
-
yield this.publishTrack(track, pub.options);
|
21617
|
-
})));
|
21570
|
+
this.signalClient.sendUpdateSubscription(sub);
|
21571
|
+
});
|
21572
|
+
publication.on(TrackEvent.SubscriptionPermissionChanged, status => {
|
21573
|
+
this.emit(ParticipantEvent.TrackSubscriptionPermissionChanged, publication, status);
|
21618
21574
|
});
|
21575
|
+
publication.on(TrackEvent.SubscriptionStatusChanged, status => {
|
21576
|
+
this.emit(ParticipantEvent.TrackSubscriptionStatusChanged, publication, status);
|
21577
|
+
});
|
21578
|
+
publication.on(TrackEvent.Subscribed, track => {
|
21579
|
+
this.emit(ParticipantEvent.TrackSubscribed, track, publication);
|
21580
|
+
});
|
21581
|
+
publication.on(TrackEvent.Unsubscribed, previousTrack => {
|
21582
|
+
this.emit(ParticipantEvent.TrackUnsubscribed, previousTrack, publication);
|
21583
|
+
});
|
21584
|
+
publication.on(TrackEvent.SubscriptionFailed, error => {
|
21585
|
+
this.emit(ParticipantEvent.TrackSubscriptionFailed, publication.trackSid, error);
|
21586
|
+
});
|
21587
|
+
}
|
21588
|
+
getTrackPublication(source) {
|
21589
|
+
const track = super.getTrackPublication(source);
|
21590
|
+
if (track) {
|
21591
|
+
return track;
|
21592
|
+
}
|
21593
|
+
}
|
21594
|
+
getTrackPublicationByName(name) {
|
21595
|
+
const track = super.getTrackPublicationByName(name);
|
21596
|
+
if (track) {
|
21597
|
+
return track;
|
21598
|
+
}
|
21599
|
+
}
|
21600
|
+
/**
|
21601
|
+
* sets the volume on the participant's audio track
|
21602
|
+
* by default, this affects the microphone publication
|
21603
|
+
* a different source can be passed in as a second argument
|
21604
|
+
* if no track exists the volume will be applied when the microphone track is added
|
21605
|
+
*/
|
21606
|
+
setVolume(volume) {
|
21607
|
+
let source = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : Track.Source.Microphone;
|
21608
|
+
this.volumeMap.set(source, volume);
|
21609
|
+
const audioPublication = this.getTrackPublication(source);
|
21610
|
+
if (audioPublication && audioPublication.track) {
|
21611
|
+
audioPublication.track.setVolume(volume);
|
21612
|
+
}
|
21619
21613
|
}
|
21620
|
-
|
21621
|
-
|
21622
|
-
|
21623
|
-
|
21624
|
-
|
21625
|
-
|
21626
|
-
|
21627
|
-
|
21628
|
-
|
21629
|
-
|
21630
|
-
|
21631
|
-
|
21614
|
+
/**
|
21615
|
+
* gets the volume on the participant's microphone track
|
21616
|
+
*/
|
21617
|
+
getVolume() {
|
21618
|
+
let source = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : Track.Source.Microphone;
|
21619
|
+
const audioPublication = this.getTrackPublication(source);
|
21620
|
+
if (audioPublication && audioPublication.track) {
|
21621
|
+
return audioPublication.track.getVolume();
|
21622
|
+
}
|
21623
|
+
return this.volumeMap.get(source);
|
21624
|
+
}
|
21625
|
+
/** @internal */
|
21626
|
+
addSubscribedMediaTrack(mediaTrack, sid, mediaStream, receiver, adaptiveStreamSettings, triesLeft) {
|
21627
|
+
// find the track publication
|
21628
|
+
// it's possible for the media track to arrive before participant info
|
21629
|
+
let publication = this.getTrackPublicationBySid(sid);
|
21630
|
+
// it's also possible that the browser didn't honor our original track id
|
21631
|
+
// FireFox would use its own local uuid instead of server track id
|
21632
|
+
if (!publication) {
|
21633
|
+
if (!sid.startsWith('TR')) {
|
21634
|
+
// find the first track that matches type
|
21635
|
+
this.trackPublications.forEach(p => {
|
21636
|
+
if (!publication && mediaTrack.kind === p.kind.toString()) {
|
21637
|
+
publication = p;
|
21632
21638
|
}
|
21633
21639
|
});
|
21634
21640
|
}
|
21635
|
-
|
21636
|
-
|
21637
|
-
|
21638
|
-
|
21639
|
-
|
21640
|
-
|
21641
|
-
|
21642
|
-
|
21643
|
-
|
21644
|
-
|
21645
|
-
|
21646
|
-
|
21647
|
-
|
21648
|
-
|
21641
|
+
}
|
21642
|
+
// when we couldn't locate the track, it's possible that the metadata hasn't
|
21643
|
+
// yet arrived. Wait a bit longer for it to arrive, or fire an error
|
21644
|
+
if (!publication) {
|
21645
|
+
if (triesLeft === 0) {
|
21646
|
+
this.log.error('could not find published track', Object.assign(Object.assign({}, this.logContext), {
|
21647
|
+
trackSid: sid
|
21648
|
+
}));
|
21649
|
+
this.emit(ParticipantEvent.TrackSubscriptionFailed, sid);
|
21650
|
+
return;
|
21651
|
+
}
|
21652
|
+
if (triesLeft === undefined) triesLeft = 20;
|
21653
|
+
setTimeout(() => {
|
21654
|
+
this.addSubscribedMediaTrack(mediaTrack, sid, mediaStream, receiver, adaptiveStreamSettings, triesLeft - 1);
|
21655
|
+
}, 150);
|
21656
|
+
return;
|
21657
|
+
}
|
21658
|
+
if (mediaTrack.readyState === 'ended') {
|
21659
|
+
this.log.error('unable to subscribe because MediaStreamTrack is ended. Do not call MediaStreamTrack.stop()', Object.assign(Object.assign({}, this.logContext), getLogContextFromTrack(publication)));
|
21660
|
+
this.emit(ParticipantEvent.TrackSubscriptionFailed, sid);
|
21661
|
+
return;
|
21662
|
+
}
|
21663
|
+
const isVideo = mediaTrack.kind === 'video';
|
21664
|
+
let track;
|
21665
|
+
if (isVideo) {
|
21666
|
+
track = new RemoteVideoTrack(mediaTrack, sid, receiver, adaptiveStreamSettings);
|
21667
|
+
} else {
|
21668
|
+
track = new RemoteAudioTrack(mediaTrack, sid, receiver, this.audioContext, this.audioOutput);
|
21669
|
+
}
|
21670
|
+
// set track info
|
21671
|
+
track.source = publication.source;
|
21672
|
+
// keep publication's muted status
|
21673
|
+
track.isMuted = publication.isMuted;
|
21674
|
+
track.setMediaStream(mediaStream);
|
21675
|
+
track.start();
|
21676
|
+
publication.setTrack(track);
|
21677
|
+
// set participant volumes on new audio tracks
|
21678
|
+
if (this.volumeMap.has(publication.source) && track instanceof RemoteAudioTrack) {
|
21679
|
+
track.setVolume(this.volumeMap.get(publication.source));
|
21680
|
+
}
|
21681
|
+
return publication;
|
21682
|
+
}
|
21683
|
+
/** @internal */
|
21684
|
+
get hasMetadata() {
|
21685
|
+
return !!this.participantInfo;
|
21649
21686
|
}
|
21650
21687
|
/**
|
21651
|
-
*
|
21652
|
-
*
|
21653
|
-
* By default, all participants can subscribe. This allows fine-grained control over
|
21654
|
-
* who is able to subscribe at a participant and track level.
|
21655
|
-
*
|
21656
|
-
* Note: if access is given at a track-level (i.e. both [allParticipantsAllowed] and
|
21657
|
-
* [ParticipantTrackPermission.allTracksAllowed] are false), any newer published tracks
|
21658
|
-
* will not grant permissions to any participants and will require a subsequent
|
21659
|
-
* permissions update to allow subscription.
|
21660
|
-
*
|
21661
|
-
* @param allParticipantsAllowed Allows all participants to subscribe all tracks.
|
21662
|
-
* Takes precedence over [[participantTrackPermissions]] if set to true.
|
21663
|
-
* By default this is set to true.
|
21664
|
-
* @param participantTrackPermissions Full list of individual permissions per
|
21665
|
-
* participant/track. Any omitted participants will not receive any permissions.
|
21688
|
+
* @internal
|
21666
21689
|
*/
|
21667
|
-
|
21668
|
-
|
21669
|
-
this.participantTrackPermissions = participantTrackPermissions;
|
21670
|
-
this.allParticipantsAllowedToSubscribe = allParticipantsAllowed;
|
21671
|
-
if (!this.engine.client.isDisconnected) {
|
21672
|
-
this.updateTrackSubscriptionPermissions();
|
21673
|
-
}
|
21690
|
+
getTrackPublicationBySid(sid) {
|
21691
|
+
return this.trackPublications.get(sid);
|
21674
21692
|
}
|
21675
21693
|
/** @internal */
|
21676
21694
|
updateInfo(info) {
|
21677
|
-
if (info.sid !== this.sid) {
|
21678
|
-
// drop updates that specify a wrong sid.
|
21679
|
-
// the sid for local participant is only explicitly set on join and full reconnect
|
21680
|
-
return false;
|
21681
|
-
}
|
21682
21695
|
if (!super.updateInfo(info)) {
|
21683
21696
|
return false;
|
21684
21697
|
}
|
21685
|
-
// reconcile
|
21686
|
-
//
|
21687
|
-
//
|
21698
|
+
// we are getting a list of all available tracks, reconcile in here
|
21699
|
+
// and send out events for changes
|
21700
|
+
// reconcile track publications, publish events only if metadata is already there
|
21701
|
+
// i.e. changes since the local participant has joined
|
21702
|
+
const validTracks = new Map();
|
21703
|
+
const newTracks = new Map();
|
21688
21704
|
info.tracks.forEach(ti => {
|
21689
21705
|
var _a, _b;
|
21690
|
-
|
21691
|
-
if (
|
21692
|
-
|
21693
|
-
|
21694
|
-
|
21695
|
-
|
21706
|
+
let publication = this.getTrackPublicationBySid(ti.sid);
|
21707
|
+
if (!publication) {
|
21708
|
+
// new publication
|
21709
|
+
const kind = Track.kindFromProto(ti.type);
|
21710
|
+
if (!kind) {
|
21711
|
+
return;
|
21712
|
+
}
|
21713
|
+
publication = new RemoteTrackPublication(kind, ti, (_a = this.signalClient.connectOptions) === null || _a === void 0 ? void 0 : _a.autoSubscribe, {
|
21714
|
+
loggerContextCb: () => this.logContext,
|
21715
|
+
loggerName: (_b = this.loggerOptions) === null || _b === void 0 ? void 0 : _b.loggerName
|
21716
|
+
});
|
21717
|
+
publication.updateInfo(ti);
|
21718
|
+
newTracks.set(ti.sid, publication);
|
21719
|
+
const existingTrackOfSource = Array.from(this.trackPublications.values()).find(publishedTrack => publishedTrack.source === (publication === null || publication === void 0 ? void 0 : publication.source));
|
21720
|
+
if (existingTrackOfSource && publication.source !== Track.Source.Unknown) {
|
21721
|
+
this.log.debug("received a second track publication for ".concat(this.identity, " with the same source: ").concat(publication.source), Object.assign(Object.assign({}, this.logContext), {
|
21722
|
+
oldTrack: getLogContextFromTrack(existingTrackOfSource),
|
21723
|
+
newTrack: getLogContextFromTrack(publication)
|
21696
21724
|
}));
|
21697
|
-
this.engine.client.sendMuteTrack(ti.sid, mutedOnServer);
|
21698
21725
|
}
|
21726
|
+
this.addTrackPublication(publication);
|
21727
|
+
} else {
|
21728
|
+
publication.updateInfo(ti);
|
21729
|
+
}
|
21730
|
+
validTracks.set(ti.sid, publication);
|
21731
|
+
});
|
21732
|
+
// detect removed tracks
|
21733
|
+
this.trackPublications.forEach(publication => {
|
21734
|
+
if (!validTracks.has(publication.trackSid)) {
|
21735
|
+
this.log.trace('detected removed track on remote participant, unpublishing', Object.assign(Object.assign({}, this.logContext), getLogContextFromTrack(publication)));
|
21736
|
+
this.unpublishTrack(publication.trackSid, true);
|
21699
21737
|
}
|
21700
21738
|
});
|
21739
|
+
// always emit events for new publications, Room will not forward them unless it's ready
|
21740
|
+
newTracks.forEach(publication => {
|
21741
|
+
this.emit(ParticipantEvent.TrackPublished, publication);
|
21742
|
+
});
|
21701
21743
|
return true;
|
21702
21744
|
}
|
21703
|
-
|
21704
|
-
|
21705
|
-
this.
|
21706
|
-
|
21707
|
-
|
21708
|
-
|
21709
|
-
|
21710
|
-
|
21711
|
-
|
21712
|
-
|
21713
|
-
|
21714
|
-
|
21715
|
-
|
21745
|
+
/** @internal */
|
21746
|
+
unpublishTrack(sid, sendUnpublish) {
|
21747
|
+
const publication = this.trackPublications.get(sid);
|
21748
|
+
if (!publication) {
|
21749
|
+
return;
|
21750
|
+
}
|
21751
|
+
// also send unsubscribe, if track is actively subscribed
|
21752
|
+
const {
|
21753
|
+
track
|
21754
|
+
} = publication;
|
21755
|
+
if (track) {
|
21756
|
+
track.stop();
|
21757
|
+
publication.setTrack(undefined);
|
21758
|
+
}
|
21759
|
+
// remove track from maps only after unsubscribed has been fired
|
21760
|
+
this.trackPublications.delete(sid);
|
21761
|
+
// remove from the right type map
|
21762
|
+
switch (publication.kind) {
|
21763
|
+
case Track.Kind.Audio:
|
21764
|
+
this.audioTrackPublications.delete(sid);
|
21765
|
+
break;
|
21766
|
+
case Track.Kind.Video:
|
21767
|
+
this.videoTrackPublications.delete(sid);
|
21768
|
+
break;
|
21769
|
+
}
|
21770
|
+
if (sendUnpublish) {
|
21771
|
+
this.emit(ParticipantEvent.TrackUnpublished, publication);
|
21772
|
+
}
|
21773
|
+
}
|
21774
|
+
/**
|
21775
|
+
* @internal
|
21776
|
+
*/
|
21777
|
+
setAudioOutput(output) {
|
21778
|
+
return __awaiter(this, void 0, void 0, function* () {
|
21779
|
+
this.audioOutput = output;
|
21780
|
+
const promises = [];
|
21781
|
+
this.audioTrackPublications.forEach(pub => {
|
21782
|
+
var _a;
|
21783
|
+
if (pub.track instanceof RemoteAudioTrack) {
|
21784
|
+
promises.push(pub.track.setSinkId((_a = output.deviceId) !== null && _a !== void 0 ? _a : 'default'));
|
21716
21785
|
}
|
21717
|
-
}
|
21718
|
-
|
21719
|
-
}
|
21786
|
+
});
|
21787
|
+
yield Promise.all(promises);
|
21720
21788
|
});
|
21721
|
-
|
21789
|
+
}
|
21790
|
+
/** @internal */
|
21791
|
+
emit(event) {
|
21792
|
+
for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
|
21793
|
+
args[_key - 1] = arguments[_key];
|
21794
|
+
}
|
21795
|
+
this.log.trace('participant event', Object.assign(Object.assign({}, this.logContext), {
|
21796
|
+
event,
|
21797
|
+
args
|
21798
|
+
}));
|
21799
|
+
return super.emit(event, ...args);
|
21722
21800
|
}
|
21723
21801
|
}
|
21724
21802
|
|
@@ -21730,8 +21808,6 @@ var ConnectionState;
|
|
21730
21808
|
ConnectionState["Reconnecting"] = "reconnecting";
|
21731
21809
|
})(ConnectionState || (ConnectionState = {}));
|
21732
21810
|
const connectionReconcileFrequency = 2 * 1000;
|
21733
|
-
/** @deprecated RoomState has been renamed to [[ConnectionState]] */
|
21734
|
-
const RoomState = ConnectionState;
|
21735
21811
|
/**
|
21736
21812
|
* In LiveKit, a room is the logical grouping for a list of participants.
|
21737
21813
|
* Participants in a room can publish tracks, and subscribe to others' tracks.
|
@@ -21761,6 +21837,8 @@ class Room extends eventsExports.EventEmitter {
|
|
21761
21837
|
this.audioEnabled = true;
|
21762
21838
|
this.isVideoPlaybackBlocked = false;
|
21763
21839
|
this.log = livekitLogger;
|
21840
|
+
this.bufferedEvents = [];
|
21841
|
+
this.isResuming = false;
|
21764
21842
|
this.connect = (url, token, opts) => __awaiter(this, void 0, void 0, function* () {
|
21765
21843
|
var _c;
|
21766
21844
|
// In case a disconnect called happened right before the connect call, make sure the disconnect is completed first by awaiting its lock
|
@@ -21847,7 +21925,6 @@ class Room extends eventsExports.EventEmitter {
|
|
21847
21925
|
var _e, _f, _g;
|
21848
21926
|
const joinResponse = yield engine.join(url, token, {
|
21849
21927
|
autoSubscribe: connectOptions.autoSubscribe,
|
21850
|
-
publishOnly: connectOptions.publishOnly,
|
21851
21928
|
adaptiveStream: typeof roomOptions.adaptiveStream === 'object' ? true : roomOptions.adaptiveStream,
|
21852
21929
|
maxRetries: connectOptions.maxRetries,
|
21853
21930
|
e2eeEnabled: !!this.e2eeManager,
|
@@ -21892,8 +21969,8 @@ class Room extends eventsExports.EventEmitter {
|
|
21892
21969
|
}
|
21893
21970
|
};
|
21894
21971
|
this.attemptConnection = (url, token, opts, abortController) => __awaiter(this, void 0, void 0, function* () {
|
21895
|
-
var _h, _j;
|
21896
|
-
if (this.state === ConnectionState.Reconnecting) {
|
21972
|
+
var _h, _j, _k;
|
21973
|
+
if (this.state === ConnectionState.Reconnecting || this.isResuming || ((_h = this.engine) === null || _h === void 0 ? void 0 : _h.pendingReconnect)) {
|
21897
21974
|
this.log.info('Reconnection attempt replaced by new connection attempt', this.logContext);
|
21898
21975
|
// make sure we close and recreate the existing engine in order to get rid of any potentially ongoing reconnection attempts
|
21899
21976
|
this.recreateEngine();
|
@@ -21901,7 +21978,7 @@ class Room extends eventsExports.EventEmitter {
|
|
21901
21978
|
// create engine if previously disconnected
|
21902
21979
|
this.maybeCreateEngine();
|
21903
21980
|
}
|
21904
|
-
if ((
|
21981
|
+
if ((_j = this.regionUrlProvider) === null || _j === void 0 ? void 0 : _j.isCloud()) {
|
21905
21982
|
this.engine.setRegionUrlProvider(this.regionUrlProvider);
|
21906
21983
|
}
|
21907
21984
|
this.acquireAudioContext();
|
@@ -21954,7 +22031,7 @@ class Room extends eventsExports.EventEmitter {
|
|
21954
22031
|
}
|
21955
22032
|
if (isWeb()) {
|
21956
22033
|
document.addEventListener('freeze', this.onPageLeave);
|
21957
|
-
(
|
22034
|
+
(_k = navigator.mediaDevices) === null || _k === void 0 ? void 0 : _k.addEventListener('devicechange', this.handleDeviceChange);
|
21958
22035
|
}
|
21959
22036
|
this.setAndEmitConnectionState(ConnectionState.Connected);
|
21960
22037
|
this.emit(RoomEvent.Connected);
|
@@ -21966,7 +22043,7 @@ class Room extends eventsExports.EventEmitter {
|
|
21966
22043
|
this.disconnect = function () {
|
21967
22044
|
let stopTracks = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
|
21968
22045
|
return __awaiter(_this, void 0, void 0, function* () {
|
21969
|
-
var
|
22046
|
+
var _l, _m, _o, _p;
|
21970
22047
|
const unlock = yield this.disconnectLock.lock();
|
21971
22048
|
try {
|
21972
22049
|
if (this.state === ConnectionState.Disconnected) {
|
@@ -21974,16 +22051,16 @@ class Room extends eventsExports.EventEmitter {
|
|
21974
22051
|
return;
|
21975
22052
|
}
|
21976
22053
|
this.log.info('disconnect from room', Object.assign({}, this.logContext));
|
21977
|
-
if (this.state === ConnectionState.Connecting || this.state === ConnectionState.Reconnecting) {
|
22054
|
+
if (this.state === ConnectionState.Connecting || this.state === ConnectionState.Reconnecting || this.isResuming) {
|
21978
22055
|
// try aborting pending connection attempt
|
21979
22056
|
this.log.warn('abort connection attempt', this.logContext);
|
21980
|
-
(
|
22057
|
+
(_l = this.abortController) === null || _l === void 0 ? void 0 : _l.abort();
|
21981
22058
|
// in case the abort controller didn't manage to cancel the connection attempt, reject the connect promise explicitly
|
21982
|
-
(
|
22059
|
+
(_o = (_m = this.connectFuture) === null || _m === void 0 ? void 0 : _m.reject) === null || _o === void 0 ? void 0 : _o.call(_m, new ConnectionError('Client initiated disconnect'));
|
21983
22060
|
this.connectFuture = undefined;
|
21984
22061
|
}
|
21985
22062
|
// send leave
|
21986
|
-
if (!((
|
22063
|
+
if (!((_p = this.engine) === null || _p === void 0 ? void 0 : _p.client.isDisconnected)) {
|
21987
22064
|
yield this.engine.client.sendLeave();
|
21988
22065
|
}
|
21989
22066
|
// close engine (also closes client)
|
@@ -22037,16 +22114,21 @@ class Room extends eventsExports.EventEmitter {
|
|
22037
22114
|
}
|
22038
22115
|
// set the srcObject to null on page hide in order to prevent lock screen controls to show up for it
|
22039
22116
|
dummyAudioEl.srcObject = document.hidden ? null : stream;
|
22117
|
+
if (!document.hidden) {
|
22118
|
+
this.log.debug('page visible again, triggering startAudio to resume playback and update playback status', this.logContext);
|
22119
|
+
this.startAudio();
|
22120
|
+
}
|
22040
22121
|
});
|
22041
22122
|
document.body.append(dummyAudioEl);
|
22042
22123
|
this.once(RoomEvent.Disconnected, () => {
|
22043
22124
|
dummyAudioEl === null || dummyAudioEl === void 0 ? void 0 : dummyAudioEl.remove();
|
22125
|
+
dummyAudioEl = null;
|
22044
22126
|
});
|
22045
22127
|
}
|
22046
22128
|
elements.push(dummyAudioEl);
|
22047
22129
|
}
|
22048
|
-
this.
|
22049
|
-
p.
|
22130
|
+
this.remoteParticipants.forEach(p => {
|
22131
|
+
p.audioTrackPublications.forEach(t => {
|
22050
22132
|
if (t.track) {
|
22051
22133
|
t.track.attachedElements.forEach(e => {
|
22052
22134
|
elements.push(e);
|
@@ -22067,8 +22149,8 @@ class Room extends eventsExports.EventEmitter {
|
|
22067
22149
|
});
|
22068
22150
|
this.startVideo = () => __awaiter(this, void 0, void 0, function* () {
|
22069
22151
|
const elements = [];
|
22070
|
-
for (const p of this.
|
22071
|
-
p.
|
22152
|
+
for (const p of this.remoteParticipants.values()) {
|
22153
|
+
p.videoTrackPublications.forEach(tr => {
|
22072
22154
|
var _a;
|
22073
22155
|
(_a = tr.track) === null || _a === void 0 ? void 0 : _a.attachedElements.forEach(el => {
|
22074
22156
|
if (!elements.includes(el)) {
|
@@ -22089,9 +22171,11 @@ class Room extends eventsExports.EventEmitter {
|
|
22089
22171
|
});
|
22090
22172
|
this.handleRestarting = () => {
|
22091
22173
|
this.clearConnectionReconcile();
|
22174
|
+
// in case we went from resuming to full-reconnect, make sure to reflect it on the isResuming flag
|
22175
|
+
this.isResuming = false;
|
22092
22176
|
// also unwind existing participants & existing subscriptions
|
22093
|
-
for (const p of this.
|
22094
|
-
this.handleParticipantDisconnected(p.
|
22177
|
+
for (const p of this.remoteParticipants.values()) {
|
22178
|
+
this.handleParticipantDisconnected(p.identity, p);
|
22095
22179
|
}
|
22096
22180
|
if (this.setAndEmitConnectionState(ConnectionState.Reconnecting)) {
|
22097
22181
|
this.emit(RoomEvent.Reconnecting);
|
@@ -22101,7 +22185,7 @@ class Room extends eventsExports.EventEmitter {
|
|
22101
22185
|
this.log.debug("signal reconnected to server, region ".concat(joinResponse.serverRegion), Object.assign(Object.assign({}, this.logContext), {
|
22102
22186
|
region: joinResponse.serverRegion
|
22103
22187
|
}));
|
22104
|
-
this.
|
22188
|
+
this.bufferedEvents = [];
|
22105
22189
|
this.applyJoinResponse(joinResponse);
|
22106
22190
|
try {
|
22107
22191
|
// unpublish & republish tracks
|
@@ -22116,43 +22200,35 @@ class Room extends eventsExports.EventEmitter {
|
|
22116
22200
|
this.log.debug("fully reconnected to server", Object.assign(Object.assign({}, this.logContext), {
|
22117
22201
|
region: joinResponse.serverRegion
|
22118
22202
|
}));
|
22119
|
-
} catch (
|
22203
|
+
} catch (_q) {
|
22120
22204
|
// reconnection failed, handleDisconnect is being invoked already, just return here
|
22121
22205
|
return;
|
22122
22206
|
}
|
22123
22207
|
this.setAndEmitConnectionState(ConnectionState.Connected);
|
22124
22208
|
this.emit(RoomEvent.Reconnected);
|
22125
22209
|
this.registerConnectionReconcile();
|
22126
|
-
|
22127
|
-
this.participants.forEach(participant => {
|
22128
|
-
this.emit(RoomEvent.ParticipantConnected, participant);
|
22129
|
-
});
|
22210
|
+
this.emitBufferedEvents();
|
22130
22211
|
});
|
22131
22212
|
this.handleParticipantUpdates = participantInfos => {
|
22132
22213
|
// handle changes to participant state, and send events
|
22133
22214
|
participantInfos.forEach(info => {
|
22215
|
+
var _a;
|
22134
22216
|
if (info.identity === this.localParticipant.identity) {
|
22135
22217
|
this.localParticipant.updateInfo(info);
|
22136
22218
|
return;
|
22137
22219
|
}
|
22138
|
-
//
|
22139
|
-
|
22140
|
-
if (
|
22141
|
-
|
22142
|
-
this.handleParticipantDisconnected(sid, this.participants.get(sid));
|
22220
|
+
// LiveKit server doesn't send identity info prior to version 1.5.2 in disconnect updates
|
22221
|
+
// so we try to map an empty identity to an already known sID manually
|
22222
|
+
if (info.identity === '') {
|
22223
|
+
info.identity = (_a = this.sidToIdentity.get(info.sid)) !== null && _a !== void 0 ? _a : '';
|
22143
22224
|
}
|
22144
|
-
let remoteParticipant = this.
|
22145
|
-
const isNewParticipant = !remoteParticipant;
|
22225
|
+
let remoteParticipant = this.remoteParticipants.get(info.identity);
|
22146
22226
|
// when it's disconnected, send updates
|
22147
22227
|
if (info.state === ParticipantInfo_State.DISCONNECTED) {
|
22148
|
-
this.handleParticipantDisconnected(info.
|
22228
|
+
this.handleParticipantDisconnected(info.identity, remoteParticipant);
|
22149
22229
|
} else {
|
22150
22230
|
// create participant if doesn't exist
|
22151
|
-
remoteParticipant = this.getOrCreateParticipant(info.
|
22152
|
-
if (!isNewParticipant) {
|
22153
|
-
// just update, no events
|
22154
|
-
remoteParticipant.updateInfo(info);
|
22155
|
-
}
|
22231
|
+
remoteParticipant = this.getOrCreateParticipant(info.identity, info);
|
22156
22232
|
}
|
22157
22233
|
});
|
22158
22234
|
};
|
@@ -22167,7 +22243,7 @@ class Room extends eventsExports.EventEmitter {
|
|
22167
22243
|
this.localParticipant.setIsSpeaking(true);
|
22168
22244
|
activeSpeakers.push(this.localParticipant);
|
22169
22245
|
} else {
|
22170
|
-
const p = this.
|
22246
|
+
const p = this.getRemoteParticipantBySid(speaker.sid);
|
22171
22247
|
if (p) {
|
22172
22248
|
p.audioLevel = speaker.level;
|
22173
22249
|
p.setIsSpeaking(true);
|
@@ -22179,7 +22255,7 @@ class Room extends eventsExports.EventEmitter {
|
|
22179
22255
|
this.localParticipant.audioLevel = 0;
|
22180
22256
|
this.localParticipant.setIsSpeaking(false);
|
22181
22257
|
}
|
22182
|
-
this.
|
22258
|
+
this.remoteParticipants.forEach(p => {
|
22183
22259
|
if (!seenSids[p.sid]) {
|
22184
22260
|
p.audioLevel = 0;
|
22185
22261
|
p.setIsSpeaking(false);
|
@@ -22195,7 +22271,7 @@ class Room extends eventsExports.EventEmitter {
|
|
22195
22271
|
lastSpeakers.set(p.sid, p);
|
22196
22272
|
});
|
22197
22273
|
speakerUpdates.forEach(speaker => {
|
22198
|
-
let p = this.
|
22274
|
+
let p = this.getRemoteParticipantBySid(speaker.sid);
|
22199
22275
|
if (speaker.sid === this.localParticipant.sid) {
|
22200
22276
|
p = this.localParticipant;
|
22201
22277
|
}
|
@@ -22217,11 +22293,11 @@ class Room extends eventsExports.EventEmitter {
|
|
22217
22293
|
};
|
22218
22294
|
this.handleStreamStateUpdate = streamStateUpdate => {
|
22219
22295
|
streamStateUpdate.streamStates.forEach(streamState => {
|
22220
|
-
const participant = this.
|
22296
|
+
const participant = this.getRemoteParticipantBySid(streamState.participantSid);
|
22221
22297
|
if (!participant) {
|
22222
22298
|
return;
|
22223
22299
|
}
|
22224
|
-
const pub = participant.
|
22300
|
+
const pub = participant.getTrackPublicationBySid(streamState.trackSid);
|
22225
22301
|
if (!pub || !pub.track) {
|
22226
22302
|
return;
|
22227
22303
|
}
|
@@ -22231,22 +22307,22 @@ class Room extends eventsExports.EventEmitter {
|
|
22231
22307
|
});
|
22232
22308
|
};
|
22233
22309
|
this.handleSubscriptionPermissionUpdate = update => {
|
22234
|
-
const participant = this.
|
22310
|
+
const participant = this.getRemoteParticipantBySid(update.participantSid);
|
22235
22311
|
if (!participant) {
|
22236
22312
|
return;
|
22237
22313
|
}
|
22238
|
-
const pub = participant.
|
22314
|
+
const pub = participant.getTrackPublicationBySid(update.trackSid);
|
22239
22315
|
if (!pub) {
|
22240
22316
|
return;
|
22241
22317
|
}
|
22242
22318
|
pub.setAllowed(update.allowed);
|
22243
22319
|
};
|
22244
22320
|
this.handleSubscriptionError = update => {
|
22245
|
-
const participant = Array.from(this.
|
22321
|
+
const participant = Array.from(this.remoteParticipants.values()).find(p => p.trackPublications.has(update.trackSid));
|
22246
22322
|
if (!participant) {
|
22247
22323
|
return;
|
22248
22324
|
}
|
22249
|
-
const pub = participant.
|
22325
|
+
const pub = participant.getTrackPublicationBySid(update.trackSid);
|
22250
22326
|
if (!pub) {
|
22251
22327
|
return;
|
22252
22328
|
}
|
@@ -22254,7 +22330,7 @@ class Room extends eventsExports.EventEmitter {
|
|
22254
22330
|
};
|
22255
22331
|
this.handleDataPacket = (userPacket, kind) => {
|
22256
22332
|
// find the participant
|
22257
|
-
const participant = this.
|
22333
|
+
const participant = this.remoteParticipants.get(userPacket.participantIdentity);
|
22258
22334
|
this.emit(RoomEvent.DataReceived, userPacket.payload, participant, kind, userPacket.topic);
|
22259
22335
|
// also emit on the participant
|
22260
22336
|
participant === null || participant === void 0 ? void 0 : participant.emit(ParticipantEvent.DataReceived, userPacket.payload, kind);
|
@@ -22307,7 +22383,7 @@ class Room extends eventsExports.EventEmitter {
|
|
22307
22383
|
this.localParticipant.setConnectionQuality(info.quality);
|
22308
22384
|
return;
|
22309
22385
|
}
|
22310
|
-
const participant = this.
|
22386
|
+
const participant = this.getRemoteParticipantBySid(info.participantSid);
|
22311
22387
|
if (participant) {
|
22312
22388
|
participant.setConnectionQuality(info.quality);
|
22313
22389
|
}
|
@@ -22326,7 +22402,7 @@ class Room extends eventsExports.EventEmitter {
|
|
22326
22402
|
this.emit(RoomEvent.TrackUnmuted, pub, this.localParticipant);
|
22327
22403
|
};
|
22328
22404
|
this.onLocalTrackPublished = pub => __awaiter(this, void 0, void 0, function* () {
|
22329
|
-
var
|
22405
|
+
var _r;
|
22330
22406
|
this.emit(RoomEvent.LocalTrackPublished, pub, this.localParticipant);
|
22331
22407
|
if (pub.track instanceof LocalAudioTrack) {
|
22332
22408
|
const trackIsSilent = yield pub.track.checkForSilence();
|
@@ -22334,7 +22410,7 @@ class Room extends eventsExports.EventEmitter {
|
|
22334
22410
|
this.emit(RoomEvent.LocalAudioSilenceDetected, pub);
|
22335
22411
|
}
|
22336
22412
|
}
|
22337
|
-
const deviceId = yield (
|
22413
|
+
const deviceId = yield (_r = pub.track) === null || _r === void 0 ? void 0 : _r.getDeviceId();
|
22338
22414
|
const deviceKind = sourceToKind(pub.source);
|
22339
22415
|
if (deviceKind && deviceId && deviceId !== this.localParticipant.activeDeviceMap.get(deviceKind)) {
|
22340
22416
|
this.localParticipant.activeDeviceMap.set(deviceKind, deviceId);
|
@@ -22354,9 +22430,8 @@ class Room extends eventsExports.EventEmitter {
|
|
22354
22430
|
this.emit(RoomEvent.ParticipantPermissionsChanged, prevPermissions, this.localParticipant);
|
22355
22431
|
};
|
22356
22432
|
this.setMaxListeners(100);
|
22357
|
-
this.
|
22358
|
-
this.
|
22359
|
-
this.identityToSid = new Map();
|
22433
|
+
this.remoteParticipants = new Map();
|
22434
|
+
this.sidToIdentity = new Map();
|
22360
22435
|
this.options = Object.assign(Object.assign({}, roomOptionDefaults), options);
|
22361
22436
|
this.log = getLogger((_a = this.options.loggerName) !== null && _a !== void 0 ? _a : LoggerNames.Room);
|
22362
22437
|
this.options.audioCaptureDefaults = Object.assign(Object.assign({}, audioDefaults), options === null || options === void 0 ? void 0 : options.audioCaptureDefaults);
|
@@ -22408,9 +22483,10 @@ class Room extends eventsExports.EventEmitter {
|
|
22408
22483
|
}
|
22409
22484
|
}
|
22410
22485
|
get logContext() {
|
22486
|
+
var _a;
|
22411
22487
|
return {
|
22412
22488
|
room: this.name,
|
22413
|
-
roomSid: this.sid,
|
22489
|
+
roomSid: (_a = this.roomInfo) === null || _a === void 0 ? void 0 : _a.sid,
|
22414
22490
|
identity: this.localParticipant.identity
|
22415
22491
|
};
|
22416
22492
|
}
|
@@ -22421,10 +22497,32 @@ class Room extends eventsExports.EventEmitter {
|
|
22421
22497
|
var _a, _b;
|
22422
22498
|
return (_b = (_a = this.roomInfo) === null || _a === void 0 ? void 0 : _a.activeRecording) !== null && _b !== void 0 ? _b : false;
|
22423
22499
|
}
|
22424
|
-
/**
|
22425
|
-
|
22426
|
-
|
22427
|
-
|
22500
|
+
/**
|
22501
|
+
* server assigned unique room id.
|
22502
|
+
* returns once a sid has been issued by the server.
|
22503
|
+
*/
|
22504
|
+
getSid() {
|
22505
|
+
return __awaiter(this, void 0, void 0, function* () {
|
22506
|
+
if (this.state === ConnectionState.Disconnected) {
|
22507
|
+
return '';
|
22508
|
+
}
|
22509
|
+
if (this.roomInfo && this.roomInfo.sid !== '') {
|
22510
|
+
return this.roomInfo.sid;
|
22511
|
+
}
|
22512
|
+
return new Promise((resolve, reject) => {
|
22513
|
+
const handleRoomUpdate = roomInfo => {
|
22514
|
+
if (roomInfo.sid !== '') {
|
22515
|
+
this.engine.off(EngineEvent.RoomUpdate, handleRoomUpdate);
|
22516
|
+
resolve(roomInfo.sid);
|
22517
|
+
}
|
22518
|
+
};
|
22519
|
+
this.engine.on(EngineEvent.RoomUpdate, handleRoomUpdate);
|
22520
|
+
this.once(RoomEvent.Disconnected, () => {
|
22521
|
+
this.engine.off(EngineEvent.RoomUpdate, handleRoomUpdate);
|
22522
|
+
reject('Room disconnected before room server id was available');
|
22523
|
+
});
|
22524
|
+
});
|
22525
|
+
});
|
22428
22526
|
}
|
22429
22527
|
/** user assigned name, derived from JWT token */
|
22430
22528
|
get name() {
|
@@ -22455,21 +22553,17 @@ class Room extends eventsExports.EventEmitter {
|
|
22455
22553
|
this.handleDisconnect(this.options.stopLocalTrackOnUnpublish, reason);
|
22456
22554
|
}).on(EngineEvent.ActiveSpeakersUpdate, this.handleActiveSpeakersUpdate).on(EngineEvent.DataPacketReceived, this.handleDataPacket).on(EngineEvent.Resuming, () => {
|
22457
22555
|
this.clearConnectionReconcile();
|
22458
|
-
|
22459
|
-
|
22460
|
-
}
|
22461
|
-
this.cachedParticipantSids = Array.from(this.participants.keys());
|
22556
|
+
this.isResuming = true;
|
22557
|
+
this.log.info('Resuming signal connection', this.logContext);
|
22462
22558
|
}).on(EngineEvent.Resumed, () => {
|
22463
|
-
this.setAndEmitConnectionState(ConnectionState.Connected);
|
22464
|
-
this.emit(RoomEvent.Reconnected);
|
22465
22559
|
this.registerConnectionReconcile();
|
22560
|
+
this.isResuming = false;
|
22561
|
+
this.log.info('Resumed signal connection', this.logContext);
|
22466
22562
|
this.updateSubscriptions();
|
22467
|
-
|
22468
|
-
const diffParticipants = Array.from(this.participants.values()).filter(p => !this.cachedParticipantSids.includes(p.sid));
|
22469
|
-
diffParticipants.forEach(p => this.emit(RoomEvent.ParticipantConnected, p));
|
22470
|
-
this.cachedParticipantSids = [];
|
22563
|
+
this.emitBufferedEvents();
|
22471
22564
|
}).on(EngineEvent.SignalResumed, () => {
|
22472
|
-
|
22565
|
+
this.bufferedEvents = [];
|
22566
|
+
if (this.state === ConnectionState.Reconnecting || this.isResuming) {
|
22473
22567
|
this.sendSyncState();
|
22474
22568
|
}
|
22475
22569
|
}).on(EngineEvent.Restarting, this.handleRestarting).on(EngineEvent.SignalRestarted, this.handleSignalRestarted).on(EngineEvent.DCBufferStatusChanged, (status, kind) => {
|
@@ -22543,10 +22637,7 @@ class Room extends eventsExports.EventEmitter {
|
|
22543
22637
|
if (this.localParticipant.identity === identity) {
|
22544
22638
|
return this.localParticipant;
|
22545
22639
|
}
|
22546
|
-
|
22547
|
-
if (sid) {
|
22548
|
-
return this.participants.get(sid);
|
22549
|
-
}
|
22640
|
+
return this.remoteParticipants.get(identity);
|
22550
22641
|
}
|
22551
22642
|
clearConnectionFutures() {
|
22552
22643
|
this.connectFuture = undefined;
|
@@ -22677,15 +22768,6 @@ class Room extends eventsExports.EventEmitter {
|
|
22677
22768
|
get canPlaybackVideo() {
|
22678
22769
|
return !this.isVideoPlaybackBlocked;
|
22679
22770
|
}
|
22680
|
-
/**
|
22681
|
-
* Returns the active audio output device used in this room.
|
22682
|
-
* @return the previously successfully set audio output device ID or an empty string if the default device is used.
|
22683
|
-
* @deprecated use `getActiveDevice('audiooutput')` instead
|
22684
|
-
*/
|
22685
|
-
getActiveAudioOutputDevice() {
|
22686
|
-
var _a, _b;
|
22687
|
-
return (_b = (_a = this.options.audioOutput) === null || _a === void 0 ? void 0 : _a.deviceId) !== null && _b !== void 0 ? _b : '';
|
22688
|
-
}
|
22689
22771
|
getActiveDevice(kind) {
|
22690
22772
|
return this.localParticipant.activeDeviceMap.get(kind);
|
22691
22773
|
}
|
@@ -22713,7 +22795,7 @@ class Room extends eventsExports.EventEmitter {
|
|
22713
22795
|
const prevDeviceId = this.options.audioCaptureDefaults.deviceId;
|
22714
22796
|
this.options.audioCaptureDefaults.deviceId = deviceConstraint;
|
22715
22797
|
deviceHasChanged = prevDeviceId !== deviceConstraint;
|
22716
|
-
const tracks = Array.from(this.localParticipant.
|
22798
|
+
const tracks = Array.from(this.localParticipant.audioTrackPublications.values()).filter(track => track.source === Track.Source.Microphone);
|
22717
22799
|
try {
|
22718
22800
|
success = (yield Promise.all(tracks.map(t => {
|
22719
22801
|
var _a;
|
@@ -22727,7 +22809,7 @@ class Room extends eventsExports.EventEmitter {
|
|
22727
22809
|
const prevDeviceId = this.options.videoCaptureDefaults.deviceId;
|
22728
22810
|
this.options.videoCaptureDefaults.deviceId = deviceConstraint;
|
22729
22811
|
deviceHasChanged = prevDeviceId !== deviceConstraint;
|
22730
|
-
const tracks = Array.from(this.localParticipant.
|
22812
|
+
const tracks = Array.from(this.localParticipant.videoTrackPublications.values()).filter(track => track.source === Track.Source.Camera);
|
22731
22813
|
try {
|
22732
22814
|
success = (yield Promise.all(tracks.map(t => {
|
22733
22815
|
var _a;
|
@@ -22738,7 +22820,7 @@ class Room extends eventsExports.EventEmitter {
|
|
22738
22820
|
throw e;
|
22739
22821
|
}
|
22740
22822
|
} else if (kind === 'audiooutput') {
|
22741
|
-
if (!supportsSetSinkId() && !this.options.
|
22823
|
+
if (!supportsSetSinkId() && !this.options.webAudioMix || this.options.webAudioMix && this.audioContext && !('setSinkId' in this.audioContext)) {
|
22742
22824
|
throw new Error('cannot switch audio output, setSinkId not supported');
|
22743
22825
|
}
|
22744
22826
|
(_a = (_c = this.options).audioOutput) !== null && _a !== void 0 ? _a : _c.audioOutput = {};
|
@@ -22746,11 +22828,11 @@ class Room extends eventsExports.EventEmitter {
|
|
22746
22828
|
this.options.audioOutput.deviceId = deviceId;
|
22747
22829
|
deviceHasChanged = prevDeviceId !== deviceConstraint;
|
22748
22830
|
try {
|
22749
|
-
if (this.options.
|
22831
|
+
if (this.options.webAudioMix) {
|
22750
22832
|
// @ts-expect-error setSinkId is not yet in the typescript type of AudioContext
|
22751
22833
|
(_b = this.audioContext) === null || _b === void 0 ? void 0 : _b.setSinkId(deviceId);
|
22752
22834
|
} else {
|
22753
|
-
yield Promise.all(Array.from(this.
|
22835
|
+
yield Promise.all(Array.from(this.remoteParticipants.values()).map(p => p.setAudioOutput({
|
22754
22836
|
deviceId
|
22755
22837
|
})));
|
22756
22838
|
}
|
@@ -22774,9 +22856,12 @@ class Room extends eventsExports.EventEmitter {
|
|
22774
22856
|
(_a = this.engine) === null || _a === void 0 ? void 0 : _a.close();
|
22775
22857
|
/* @ts-ignore */
|
22776
22858
|
this.engine = undefined;
|
22859
|
+
this.isResuming = false;
|
22777
22860
|
// clear out existing remote participants, since they may have attached
|
22778
22861
|
// the old engine
|
22779
|
-
this.
|
22862
|
+
this.remoteParticipants.clear();
|
22863
|
+
this.sidToIdentity.clear();
|
22864
|
+
this.bufferedEvents = [];
|
22780
22865
|
this.maybeCreateEngine();
|
22781
22866
|
}
|
22782
22867
|
onTrackAdded(mediaTrack, stream, receiver) {
|
@@ -22805,19 +22890,19 @@ class Room extends eventsExports.EventEmitter {
|
|
22805
22890
|
return;
|
22806
22891
|
}
|
22807
22892
|
const parts = unpackStreamId(stream.id);
|
22808
|
-
const
|
22893
|
+
const participantSid = parts[0];
|
22809
22894
|
let streamId = parts[1];
|
22810
22895
|
let trackId = mediaTrack.id;
|
22811
22896
|
// firefox will get streamId (pID|trackId) instead of (pID|streamId) as it doesn't support sync tracks by stream
|
22812
22897
|
// and generates its own track id instead of infer from sdp track id.
|
22813
22898
|
if (streamId && streamId.startsWith('TR')) trackId = streamId;
|
22814
|
-
if (
|
22899
|
+
if (participantSid === this.localParticipant.sid) {
|
22815
22900
|
this.log.warn('tried to create RemoteParticipant for local participant', this.logContext);
|
22816
22901
|
return;
|
22817
22902
|
}
|
22818
|
-
const participant = this.
|
22903
|
+
const participant = Array.from(this.remoteParticipants.values()).find(p => p.sid === participantSid);
|
22819
22904
|
if (!participant) {
|
22820
|
-
this.log.error("Tried to add a track for a participant, that's not present. Sid: ".concat(
|
22905
|
+
this.log.error("Tried to add a track for a participant, that's not present. Sid: ".concat(participantSid), this.logContext);
|
22821
22906
|
return;
|
22822
22907
|
}
|
22823
22908
|
let adaptiveStreamSettings;
|
@@ -22835,17 +22920,19 @@ class Room extends eventsExports.EventEmitter {
|
|
22835
22920
|
let reason = arguments.length > 1 ? arguments[1] : undefined;
|
22836
22921
|
var _a;
|
22837
22922
|
this.clearConnectionReconcile();
|
22923
|
+
this.isResuming = false;
|
22924
|
+
this.bufferedEvents = [];
|
22838
22925
|
if (this.state === ConnectionState.Disconnected) {
|
22839
22926
|
return;
|
22840
22927
|
}
|
22841
22928
|
this.regionUrl = undefined;
|
22842
22929
|
try {
|
22843
|
-
this.
|
22844
|
-
p.
|
22930
|
+
this.remoteParticipants.forEach(p => {
|
22931
|
+
p.trackPublications.forEach(pub => {
|
22845
22932
|
p.unpublishTrack(pub.trackSid);
|
22846
22933
|
});
|
22847
22934
|
});
|
22848
|
-
this.localParticipant.
|
22935
|
+
this.localParticipant.trackPublications.forEach(pub => {
|
22849
22936
|
var _a, _b;
|
22850
22937
|
if (pub.track) {
|
22851
22938
|
this.localParticipant.unpublishTrack(pub.track, shouldStopTracks);
|
@@ -22856,12 +22943,13 @@ class Room extends eventsExports.EventEmitter {
|
|
22856
22943
|
}
|
22857
22944
|
});
|
22858
22945
|
this.localParticipant.off(ParticipantEvent.ParticipantMetadataChanged, this.onLocalParticipantMetadataChanged).off(ParticipantEvent.ParticipantNameChanged, this.onLocalParticipantNameChanged).off(ParticipantEvent.TrackMuted, this.onLocalTrackMuted).off(ParticipantEvent.TrackUnmuted, this.onLocalTrackUnmuted).off(ParticipantEvent.LocalTrackPublished, this.onLocalTrackPublished).off(ParticipantEvent.LocalTrackUnpublished, this.onLocalTrackUnpublished).off(ParticipantEvent.ConnectionQualityChanged, this.onLocalConnectionQualityChanged).off(ParticipantEvent.MediaDevicesError, this.onMediaDevicesError).off(ParticipantEvent.AudioStreamAcquired, this.startAudio).off(ParticipantEvent.ParticipantPermissionsChanged, this.onLocalParticipantPermissionsChanged);
|
22859
|
-
this.localParticipant.
|
22860
|
-
this.localParticipant.
|
22861
|
-
this.localParticipant.
|
22862
|
-
this.
|
22946
|
+
this.localParticipant.trackPublications.clear();
|
22947
|
+
this.localParticipant.videoTrackPublications.clear();
|
22948
|
+
this.localParticipant.audioTrackPublications.clear();
|
22949
|
+
this.remoteParticipants.clear();
|
22950
|
+
this.sidToIdentity.clear();
|
22863
22951
|
this.activeSpeakers = [];
|
22864
|
-
if (this.audioContext && typeof this.options.
|
22952
|
+
if (this.audioContext && typeof this.options.webAudioMix === 'boolean') {
|
22865
22953
|
this.audioContext.close();
|
22866
22954
|
this.audioContext = undefined;
|
22867
22955
|
}
|
@@ -22876,14 +22964,13 @@ class Room extends eventsExports.EventEmitter {
|
|
22876
22964
|
this.emit(RoomEvent.Disconnected, reason);
|
22877
22965
|
}
|
22878
22966
|
}
|
22879
|
-
handleParticipantDisconnected(
|
22967
|
+
handleParticipantDisconnected(identity, participant) {
|
22880
22968
|
// remove and send event
|
22881
|
-
this.
|
22969
|
+
this.remoteParticipants.delete(identity);
|
22882
22970
|
if (!participant) {
|
22883
22971
|
return;
|
22884
22972
|
}
|
22885
|
-
|
22886
|
-
participant.tracks.forEach(publication => {
|
22973
|
+
participant.trackPublications.forEach(publication => {
|
22887
22974
|
participant.unpublishTrack(publication.trackSid, true);
|
22888
22975
|
});
|
22889
22976
|
this.emit(RoomEvent.ParticipantDisconnected, participant);
|
@@ -22891,9 +22978,9 @@ class Room extends eventsExports.EventEmitter {
|
|
22891
22978
|
acquireAudioContext() {
|
22892
22979
|
var _a, _b;
|
22893
22980
|
return __awaiter(this, void 0, void 0, function* () {
|
22894
|
-
if (typeof this.options.
|
22981
|
+
if (typeof this.options.webAudioMix !== 'boolean' && this.options.webAudioMix.audioContext) {
|
22895
22982
|
// override audio context with custom audio context if supplied by user
|
22896
|
-
this.audioContext = this.options.
|
22983
|
+
this.audioContext = this.options.webAudioMix.audioContext;
|
22897
22984
|
} else if (!this.audioContext || this.audioContext.state === 'closed') {
|
22898
22985
|
// by using an AudioContext, it reduces lag on audio elements
|
22899
22986
|
// https://stackoverflow.com/questions/9811429/html5-audio-tag-on-safari-has-a-delay/54119854#54119854
|
@@ -22910,8 +22997,8 @@ class Room extends eventsExports.EventEmitter {
|
|
22910
22997
|
}));
|
22911
22998
|
}
|
22912
22999
|
}
|
22913
|
-
if (this.options.
|
22914
|
-
this.
|
23000
|
+
if (this.options.webAudioMix) {
|
23001
|
+
this.remoteParticipants.forEach(participant => participant.setAudioContext(this.audioContext));
|
22915
23002
|
}
|
22916
23003
|
this.localParticipant.setAudioContext(this.audioContext);
|
22917
23004
|
const newContextIsRunning = ((_b = this.audioContext) === null || _b === void 0 ? void 0 : _b.state) === 'running';
|
@@ -22921,18 +23008,18 @@ class Room extends eventsExports.EventEmitter {
|
|
22921
23008
|
}
|
22922
23009
|
});
|
22923
23010
|
}
|
22924
|
-
createParticipant(
|
23011
|
+
createParticipant(identity, info) {
|
22925
23012
|
var _a;
|
22926
23013
|
let participant;
|
22927
23014
|
if (info) {
|
22928
23015
|
participant = RemoteParticipant.fromParticipantInfo(this.engine.client, info);
|
22929
23016
|
} else {
|
22930
|
-
participant = new RemoteParticipant(this.engine.client,
|
23017
|
+
participant = new RemoteParticipant(this.engine.client, '', identity, undefined, undefined, {
|
22931
23018
|
loggerContextCb: () => this.logContext,
|
22932
23019
|
loggerName: this.options.loggerName
|
22933
23020
|
});
|
22934
23021
|
}
|
22935
|
-
if (this.options.
|
23022
|
+
if (this.options.webAudioMix) {
|
22936
23023
|
participant.setAudioContext(this.audioContext);
|
22937
23024
|
}
|
22938
23025
|
if ((_a = this.options.audioOutput) === null || _a === void 0 ? void 0 : _a.deviceId) {
|
@@ -22940,13 +23027,20 @@ class Room extends eventsExports.EventEmitter {
|
|
22940
23027
|
}
|
22941
23028
|
return participant;
|
22942
23029
|
}
|
22943
|
-
getOrCreateParticipant(
|
22944
|
-
if (this.
|
22945
|
-
|
23030
|
+
getOrCreateParticipant(identity, info) {
|
23031
|
+
if (this.remoteParticipants.has(identity)) {
|
23032
|
+
const existingParticipant = this.remoteParticipants.get(identity);
|
23033
|
+
if (info) {
|
23034
|
+
const wasUpdated = existingParticipant.updateInfo(info);
|
23035
|
+
if (wasUpdated) {
|
23036
|
+
this.sidToIdentity.set(info.sid, info.identity);
|
23037
|
+
}
|
23038
|
+
}
|
23039
|
+
return existingParticipant;
|
22946
23040
|
}
|
22947
|
-
const participant = this.createParticipant(
|
22948
|
-
this.
|
22949
|
-
this.
|
23041
|
+
const participant = this.createParticipant(identity, info);
|
23042
|
+
this.remoteParticipants.set(identity, participant);
|
23043
|
+
this.sidToIdentity.set(info.sid, info.identity);
|
22950
23044
|
// if we have valid info and the participant wasn't in the map before, we can assume the participant is new
|
22951
23045
|
// firing here to make sure that `ParticipantConnected` fires before the initial track events
|
22952
23046
|
this.emitWhenConnected(RoomEvent.ParticipantConnected, participant);
|
@@ -22997,11 +23091,11 @@ class Room extends eventsExports.EventEmitter {
|
|
22997
23091
|
return participant;
|
22998
23092
|
}
|
22999
23093
|
sendSyncState() {
|
23000
|
-
const remoteTracks = Array.from(this.
|
23001
|
-
acc.push(...participant.
|
23094
|
+
const remoteTracks = Array.from(this.remoteParticipants.values()).reduce((acc, participant) => {
|
23095
|
+
acc.push(...participant.getTrackPublications()); // FIXME would be nice to have this return RemoteTrackPublications directly instead of the type cast
|
23002
23096
|
return acc;
|
23003
23097
|
}, []);
|
23004
|
-
const localTracks = this.localParticipant.
|
23098
|
+
const localTracks = this.localParticipant.getTrackPublications(); // FIXME would be nice to have this return LocalTrackPublications directly instead of the type cast
|
23005
23099
|
this.engine.sendSyncState(remoteTracks, localTracks);
|
23006
23100
|
}
|
23007
23101
|
/**
|
@@ -23009,14 +23103,20 @@ class Room extends eventsExports.EventEmitter {
|
|
23009
23103
|
* subscription settings.
|
23010
23104
|
*/
|
23011
23105
|
updateSubscriptions() {
|
23012
|
-
for (const p of this.
|
23013
|
-
for (const pub of p.
|
23106
|
+
for (const p of this.remoteParticipants.values()) {
|
23107
|
+
for (const pub of p.videoTrackPublications.values()) {
|
23014
23108
|
if (pub.isSubscribed && pub instanceof RemoteTrackPublication) {
|
23015
23109
|
pub.emitTrackUpdate();
|
23016
23110
|
}
|
23017
23111
|
}
|
23018
23112
|
}
|
23019
23113
|
}
|
23114
|
+
getRemoteParticipantBySid(sid) {
|
23115
|
+
const identity = this.sidToIdentity.get(sid);
|
23116
|
+
if (identity) {
|
23117
|
+
return this.remoteParticipants.get(identity);
|
23118
|
+
}
|
23119
|
+
}
|
23020
23120
|
registerConnectionReconcile() {
|
23021
23121
|
this.clearConnectionReconcile();
|
23022
23122
|
let consecutiveFailures = 0;
|
@@ -23059,11 +23159,21 @@ class Room extends eventsExports.EventEmitter {
|
|
23059
23159
|
this.emit(RoomEvent.ConnectionStateChanged, this.state);
|
23060
23160
|
return true;
|
23061
23161
|
}
|
23162
|
+
emitBufferedEvents() {
|
23163
|
+
this.bufferedEvents.forEach(_ref2 => {
|
23164
|
+
let [ev, args] = _ref2;
|
23165
|
+
this.emit(ev, ...args);
|
23166
|
+
});
|
23167
|
+
this.bufferedEvents = [];
|
23168
|
+
}
|
23062
23169
|
emitWhenConnected(event) {
|
23063
|
-
|
23064
|
-
|
23065
|
-
|
23066
|
-
|
23170
|
+
for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
|
23171
|
+
args[_key - 1] = arguments[_key];
|
23172
|
+
}
|
23173
|
+
if (this.state === ConnectionState.Reconnecting || this.isResuming || !this.engine || this.engine.pendingReconnect) {
|
23174
|
+
// in case the room is reconnecting, buffer the events by firing them later after emitting RoomEvent.Reconnected
|
23175
|
+
this.bufferedEvents.push([event, args]);
|
23176
|
+
} else if (this.state === ConnectionState.Connected) {
|
23067
23177
|
return this.emit(event, ...args);
|
23068
23178
|
}
|
23069
23179
|
return false;
|
@@ -23843,5 +23953,5 @@ function isFacingModeValue(item) {
|
|
23843
23953
|
return item === undefined || allowedValues.includes(item);
|
23844
23954
|
}
|
23845
23955
|
|
23846
|
-
export { AudioPresets, BaseKeyProvider, ConnectionCheck, ConnectionError, ConnectionQuality, ConnectionState, CriticalTimers, CryptorEvent, DataPacket_Kind, DefaultReconnectPolicy, DeviceUnsupportedError, DisconnectReason, EncryptionEvent, EngineEvent, ExternalE2EEKeyProvider, KeyHandlerEvent, KeyProviderEvent, LivekitError, LocalAudioTrack, LocalParticipant, LocalTrack, LocalTrackPublication, LocalVideoTrack, LogLevel, MediaDeviceFailure, NegotiationError, Participant, ParticipantEvent, PublishDataError, RemoteAudioTrack, RemoteParticipant, RemoteTrack, RemoteTrackPublication, RemoteVideoTrack, Room, RoomEvent,
|
23956
|
+
export { AudioPresets, BaseKeyProvider, ConnectionCheck, ConnectionError, ConnectionQuality, ConnectionState, CriticalTimers, CryptorEvent, DataPacket_Kind, DefaultReconnectPolicy, DeviceUnsupportedError, DisconnectReason, EncryptionEvent, EngineEvent, ExternalE2EEKeyProvider, KeyHandlerEvent, KeyProviderEvent, LivekitError, LocalAudioTrack, LocalParticipant, LocalTrack, LocalTrackPublication, LocalVideoTrack, LogLevel, MediaDeviceFailure, NegotiationError, Participant, ParticipantEvent, PublishDataError, RemoteAudioTrack, RemoteParticipant, RemoteTrack, RemoteTrackPublication, RemoteVideoTrack, Room, RoomEvent, ScreenSharePresets, Track, TrackEvent, TrackInvalidError, TrackPublication, UnexpectedConnectionState, UnsupportedServer, VideoPreset, VideoPresets, VideoPresets43, VideoQuality, attachToElement, createAudioAnalyser, createE2EEKey, createKeyMaterialFromBuffer, createKeyMaterialFromString, createLocalAudioTrack, createLocalScreenTracks, createLocalTracks, createLocalVideoTrack, deriveKeys, detachTrack, facingModeFromDeviceLabel, facingModeFromLocalTrack, getEmptyAudioStreamTrack, getEmptyVideoStreamTrack, getLogger, importKey, isBackupCodec, isBrowserSupported, isE2EESupported, isInsertableStreamSupported, isScriptTransformSupported, isVideoFrame, needsRbspUnescaping, parseRbsp, protocolVersion, ratchet, setLogExtension, setLogLevel, supportsAV1, supportsAdaptiveStream, supportsDynacast, supportsVP9, version, videoCodecs, writeRbsp };
|
23847
23957
|
//# sourceMappingURL=livekit-client.esm.mjs.map
|