livekit-client 1.15.10 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|