livekit-client 1.15.11 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -17
- package/dist/livekit-client.esm.mjs +1573 -1479
- 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 +0 -2
- 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 +12 -15
- 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 +0 -2
- 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 +12 -15
- 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 +2 -6
- 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 +145 -114
- 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
|
});
|
@@ -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);
|
@@ -15441,9 +15517,6 @@ function createConnectionParams(token, info, opts) {
|
|
15441
15517
|
if (info.browserVersion) {
|
15442
15518
|
params.set('browser_version', info.browserVersion);
|
15443
15519
|
}
|
15444
|
-
if (opts.publishOnly !== undefined) {
|
15445
|
-
params.set('publish', opts.publishOnly);
|
15446
|
-
}
|
15447
15520
|
if (opts.adaptiveStream) {
|
15448
15521
|
params.set('adaptive_stream', '1');
|
15449
15522
|
}
|
@@ -16657,10 +16730,6 @@ function extractStereoAndNackAudioFromOffer(offer) {
|
|
16657
16730
|
|
16658
16731
|
const defaultVideoCodec = 'vp8';
|
16659
16732
|
const publishDefaults = {
|
16660
|
-
/**
|
16661
|
-
* @deprecated
|
16662
|
-
*/
|
16663
|
-
audioBitrate: AudioPresets.music.maxBitrate,
|
16664
16733
|
audioPreset: AudioPresets.music,
|
16665
16734
|
dtx: true,
|
16666
16735
|
red: true,
|
@@ -16685,7 +16754,7 @@ const roomOptionDefaults = {
|
|
16685
16754
|
stopLocalTrackOnUnpublish: true,
|
16686
16755
|
reconnectPolicy: new DefaultReconnectPolicy(),
|
16687
16756
|
disconnectOnPageLeave: true,
|
16688
|
-
|
16757
|
+
webAudioMix: true
|
16689
16758
|
};
|
16690
16759
|
const roomConnectOptionDefaults = {
|
16691
16760
|
autoSubscribe: true,
|
@@ -16989,6 +17058,9 @@ class RTCEngine extends eventsExports.EventEmitter {
|
|
16989
17058
|
get isClosed() {
|
16990
17059
|
return this._isClosed;
|
16991
17060
|
}
|
17061
|
+
get pendingReconnect() {
|
17062
|
+
return !!this.reconnectTimeout;
|
17063
|
+
}
|
16992
17064
|
constructor(options) {
|
16993
17065
|
var _a;
|
16994
17066
|
super();
|
@@ -17113,7 +17185,7 @@ class RTCEngine extends eventsExports.EventEmitter {
|
|
17113
17185
|
// since the current engine may have inherited a regional url
|
17114
17186
|
this.regionUrlProvider.updateToken(this.token);
|
17115
17187
|
}
|
17116
|
-
this.reconnectTimeout = CriticalTimers.setTimeout(() => this.attemptReconnect(disconnectReason), delay);
|
17188
|
+
this.reconnectTimeout = CriticalTimers.setTimeout(() => this.attemptReconnect(disconnectReason).finally(() => this.reconnectTimeout = undefined), delay);
|
17117
17189
|
};
|
17118
17190
|
this.waitForRestarted = () => {
|
17119
17191
|
return new Promise((resolve, reject) => {
|
@@ -18958,7 +19030,8 @@ class LocalVideoTrack extends LocalTrack {
|
|
18958
19030
|
}
|
18959
19031
|
addSimulcastTrack(codec, encodings) {
|
18960
19032
|
if (this.simulcastCodecs.has(codec)) {
|
18961
|
-
|
19033
|
+
this.log.error("".concat(codec, " already added, skipping adding simulcast codec"), this.logContext);
|
19034
|
+
return;
|
18962
19035
|
}
|
18963
19036
|
const simulcastCodecInfo = {
|
18964
19037
|
codec,
|
@@ -19987,7 +20060,7 @@ class Participant extends eventsExports.EventEmitter {
|
|
19987
20060
|
});
|
19988
20061
|
}
|
19989
20062
|
get isEncrypted() {
|
19990
|
-
return this.
|
20063
|
+
return this.trackPublications.size > 0 && Array.from(this.trackPublications.values()).every(tr => tr.isEncrypted);
|
19991
20064
|
}
|
19992
20065
|
get isAgent() {
|
19993
20066
|
var _a, _b;
|
@@ -20010,21 +20083,19 @@ class Participant extends eventsExports.EventEmitter {
|
|
20010
20083
|
this.identity = identity;
|
20011
20084
|
this.name = name;
|
20012
20085
|
this.metadata = metadata;
|
20013
|
-
this.
|
20014
|
-
this.
|
20015
|
-
this.
|
20086
|
+
this.audioTrackPublications = new Map();
|
20087
|
+
this.videoTrackPublications = new Map();
|
20088
|
+
this.trackPublications = new Map();
|
20016
20089
|
}
|
20017
|
-
|
20018
|
-
return Array.from(this.
|
20090
|
+
getTrackPublications() {
|
20091
|
+
return Array.from(this.trackPublications.values());
|
20019
20092
|
}
|
20020
20093
|
/**
|
20021
20094
|
* Finds the first track that matches the source filter, for example, getting
|
20022
20095
|
* the user's camera track with getTrackBySource(Track.Source.Camera).
|
20023
|
-
* @param source
|
20024
|
-
* @returns
|
20025
20096
|
*/
|
20026
|
-
|
20027
|
-
for (const [, pub] of this.
|
20097
|
+
getTrackPublication(source) {
|
20098
|
+
for (const [, pub] of this.trackPublications) {
|
20028
20099
|
if (pub.source === source) {
|
20029
20100
|
return pub;
|
20030
20101
|
}
|
@@ -20032,11 +20103,9 @@ class Participant extends eventsExports.EventEmitter {
|
|
20032
20103
|
}
|
20033
20104
|
/**
|
20034
20105
|
* Finds the first track that matches the track's name.
|
20035
|
-
* @param name
|
20036
|
-
* @returns
|
20037
20106
|
*/
|
20038
|
-
|
20039
|
-
for (const [, pub] of this.
|
20107
|
+
getTrackPublicationByName(name) {
|
20108
|
+
for (const [, pub] of this.trackPublications) {
|
20040
20109
|
if (pub.trackName === name) {
|
20041
20110
|
return pub;
|
20042
20111
|
}
|
@@ -20047,16 +20116,16 @@ class Participant extends eventsExports.EventEmitter {
|
|
20047
20116
|
}
|
20048
20117
|
get isCameraEnabled() {
|
20049
20118
|
var _a;
|
20050
|
-
const track = this.
|
20119
|
+
const track = this.getTrackPublication(Track.Source.Camera);
|
20051
20120
|
return !((_a = track === null || track === void 0 ? void 0 : track.isMuted) !== null && _a !== void 0 ? _a : true);
|
20052
20121
|
}
|
20053
20122
|
get isMicrophoneEnabled() {
|
20054
20123
|
var _a;
|
20055
|
-
const track = this.
|
20124
|
+
const track = this.getTrackPublication(Track.Source.Microphone);
|
20056
20125
|
return !((_a = track === null || track === void 0 ? void 0 : track.isMuted) !== null && _a !== void 0 ? _a : true);
|
20057
20126
|
}
|
20058
20127
|
get isScreenShareEnabled() {
|
20059
|
-
const track = this.
|
20128
|
+
const track = this.getTrackPublication(Track.Source.ScreenShare);
|
20060
20129
|
return !!track;
|
20061
20130
|
}
|
20062
20131
|
get isLocal() {
|
@@ -20150,7 +20219,7 @@ class Participant extends eventsExports.EventEmitter {
|
|
20150
20219
|
*/
|
20151
20220
|
setAudioContext(ctx) {
|
20152
20221
|
this.audioContext = ctx;
|
20153
|
-
this.
|
20222
|
+
this.audioTrackPublications.forEach(track => (track.track instanceof RemoteAudioTrack || track.track instanceof LocalAudioTrack) && track.track.setAudioContext(ctx));
|
20154
20223
|
}
|
20155
20224
|
addTrackPublication(publication) {
|
20156
20225
|
// forward publication driven events
|
@@ -20164,13 +20233,13 @@ class Participant extends eventsExports.EventEmitter {
|
|
20164
20233
|
if (pub.track) {
|
20165
20234
|
pub.track.sid = publication.trackSid;
|
20166
20235
|
}
|
20167
|
-
this.
|
20236
|
+
this.trackPublications.set(publication.trackSid, publication);
|
20168
20237
|
switch (publication.kind) {
|
20169
20238
|
case Track.Kind.Audio:
|
20170
|
-
this.
|
20239
|
+
this.audioTrackPublications.set(publication.trackSid, publication);
|
20171
20240
|
break;
|
20172
20241
|
case Track.Kind.Video:
|
20173
|
-
this.
|
20242
|
+
this.videoTrackPublications.set(publication.trackSid, publication);
|
20174
20243
|
break;
|
20175
20244
|
}
|
20176
20245
|
}
|
@@ -20189,993 +20258,502 @@ function trackPermissionToProto(perms) {
|
|
20189
20258
|
});
|
20190
20259
|
}
|
20191
20260
|
|
20192
|
-
class
|
20193
|
-
|
20194
|
-
|
20195
|
-
|
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
|
+
};
|
20196
20299
|
/** @internal */
|
20197
|
-
this.
|
20198
|
-
|
20199
|
-
this.currentVideoQuality = VideoQuality.HIGH;
|
20200
|
-
this.handleEnded = track => {
|
20201
|
-
this.setTrack(undefined);
|
20202
|
-
this.emit(TrackEvent.Ended, track);
|
20300
|
+
this.onTrackUnmuted = track => {
|
20301
|
+
this.onTrackMuted(track, track.isUpstreamPaused);
|
20203
20302
|
};
|
20204
|
-
|
20205
|
-
|
20206
|
-
|
20207
|
-
|
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);
|
20208
20314
|
};
|
20209
|
-
this.
|
20210
|
-
this.log.debug(
|
20211
|
-
this.
|
20212
|
-
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);
|
20213
20318
|
};
|
20214
|
-
this.
|
20215
|
-
|
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
|
+
}
|
20216
20446
|
}
|
20217
20447
|
/**
|
20218
|
-
*
|
20219
|
-
* @param subscribed true to subscribe to a track, false to unsubscribe
|
20448
|
+
* @internal
|
20220
20449
|
*/
|
20221
|
-
|
20222
|
-
|
20223
|
-
|
20224
|
-
|
20225
|
-
|
20226
|
-
|
20227
|
-
|
20228
|
-
|
20229
|
-
|
20230
|
-
|
20231
|
-
|
20232
|
-
|
20233
|
-
participantTracks: [new ParticipantTracks({
|
20234
|
-
// sending an empty participant id since TrackPublication doesn't keep it
|
20235
|
-
// this is filled in by the participant that receives this message
|
20236
|
-
participantSid: '',
|
20237
|
-
trackSids: [this.trackSid]
|
20238
|
-
})]
|
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
|
+
}
|
20239
20462
|
});
|
20240
|
-
this.
|
20241
|
-
this.emitSubscriptionUpdateIfChanged(prevStatus);
|
20242
|
-
this.emitPermissionUpdateIfChanged(prevPermission);
|
20243
|
-
}
|
20244
|
-
get subscriptionStatus() {
|
20245
|
-
if (this.subscribed === false) {
|
20246
|
-
return TrackPublication.SubscriptionStatus.Unsubscribed;
|
20247
|
-
}
|
20248
|
-
if (!super.isSubscribed) {
|
20249
|
-
return TrackPublication.SubscriptionStatus.Desired;
|
20250
|
-
}
|
20251
|
-
return TrackPublication.SubscriptionStatus.Subscribed;
|
20252
|
-
}
|
20253
|
-
get permissionStatus() {
|
20254
|
-
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);
|
20255
20464
|
}
|
20256
20465
|
/**
|
20257
|
-
*
|
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
|
20258
20471
|
*/
|
20259
|
-
|
20260
|
-
|
20261
|
-
|
20262
|
-
}
|
20263
|
-
return super.isSubscribed;
|
20264
|
-
}
|
20265
|
-
// returns client's desire to subscribe to a track, also true if autoSubscribe is enabled
|
20266
|
-
get isDesired() {
|
20267
|
-
return this.subscribed !== false;
|
20268
|
-
}
|
20269
|
-
get isEnabled() {
|
20270
|
-
return !this.disabled;
|
20472
|
+
setMetadata(metadata) {
|
20473
|
+
var _a;
|
20474
|
+
this.engine.client.sendUpdateLocalMetadata(metadata, (_a = this.name) !== null && _a !== void 0 ? _a : '');
|
20271
20475
|
}
|
20272
20476
|
/**
|
20273
|
-
*
|
20274
|
-
*
|
20275
|
-
*
|
20276
|
-
*
|
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
|
20277
20482
|
*/
|
20278
|
-
|
20279
|
-
|
20280
|
-
|
20281
|
-
}
|
20282
|
-
this.disabled = !enabled;
|
20283
|
-
this.emitTrackUpdate();
|
20483
|
+
setName(name) {
|
20484
|
+
var _a;
|
20485
|
+
this.engine.client.sendUpdateLocalMetadata((_a = this.metadata) !== null && _a !== void 0 ? _a : '', name);
|
20284
20486
|
}
|
20285
20487
|
/**
|
20286
|
-
*
|
20488
|
+
* Enable or disable a participant's camera track.
|
20287
20489
|
*
|
20288
|
-
*
|
20289
|
-
*
|
20290
|
-
* 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
|
20291
20492
|
*/
|
20292
|
-
|
20293
|
-
|
20294
|
-
return;
|
20295
|
-
}
|
20296
|
-
this.currentVideoQuality = quality;
|
20297
|
-
this.videoDimensions = undefined;
|
20298
|
-
this.emitTrackUpdate();
|
20299
|
-
}
|
20300
|
-
setVideoDimensions(dimensions) {
|
20301
|
-
var _a, _b;
|
20302
|
-
if (!this.isManualOperationAllowed()) {
|
20303
|
-
return;
|
20304
|
-
}
|
20305
|
-
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) {
|
20306
|
-
return;
|
20307
|
-
}
|
20308
|
-
if (this.track instanceof RemoteVideoTrack) {
|
20309
|
-
this.videoDimensions = dimensions;
|
20310
|
-
}
|
20311
|
-
this.currentVideoQuality = undefined;
|
20312
|
-
this.emitTrackUpdate();
|
20493
|
+
setCameraEnabled(enabled, options, publishOptions) {
|
20494
|
+
return this.setTrackEnabled(Track.Source.Camera, enabled, options, publishOptions);
|
20313
20495
|
}
|
20314
|
-
|
20315
|
-
|
20316
|
-
|
20317
|
-
|
20318
|
-
|
20319
|
-
|
20320
|
-
|
20321
|
-
|
20322
|
-
return;
|
20323
|
-
}
|
20324
|
-
this.fps = fps;
|
20325
|
-
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);
|
20326
20504
|
}
|
20327
|
-
|
20328
|
-
|
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);
|
20329
20511
|
}
|
20330
20512
|
/** @internal */
|
20331
|
-
|
20332
|
-
const
|
20333
|
-
const
|
20334
|
-
|
20335
|
-
|
20336
|
-
return;
|
20337
|
-
}
|
20338
|
-
if (prevTrack) {
|
20339
|
-
// unregister listener
|
20340
|
-
prevTrack.off(TrackEvent.VideoDimensionsChanged, this.handleVideoDimensionsChange);
|
20341
|
-
prevTrack.off(TrackEvent.VisibilityChanged, this.handleVisibilityChange);
|
20342
|
-
prevTrack.off(TrackEvent.Ended, this.handleEnded);
|
20343
|
-
prevTrack.detach();
|
20344
|
-
prevTrack.stopMonitor();
|
20345
|
-
this.emit(TrackEvent.Unsubscribed, prevTrack);
|
20346
|
-
}
|
20347
|
-
super.setTrack(track);
|
20348
|
-
if (track) {
|
20349
|
-
track.sid = this.trackSid;
|
20350
|
-
track.on(TrackEvent.VideoDimensionsChanged, this.handleVideoDimensionsChange);
|
20351
|
-
track.on(TrackEvent.VisibilityChanged, this.handleVisibilityChange);
|
20352
|
-
track.on(TrackEvent.Ended, this.handleEnded);
|
20353
|
-
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);
|
20354
20518
|
}
|
20355
|
-
|
20356
|
-
this.emitSubscriptionUpdateIfChanged(prevStatus);
|
20357
|
-
}
|
20358
|
-
/** @internal */
|
20359
|
-
setAllowed(allowed) {
|
20360
|
-
const prevStatus = this.subscriptionStatus;
|
20361
|
-
const prevPermission = this.permissionStatus;
|
20362
|
-
this.allowed = allowed;
|
20363
|
-
this.emitPermissionUpdateIfChanged(prevPermission);
|
20364
|
-
this.emitSubscriptionUpdateIfChanged(prevStatus);
|
20365
|
-
}
|
20366
|
-
/** @internal */
|
20367
|
-
setSubscriptionError(error) {
|
20368
|
-
this.emit(TrackEvent.SubscriptionFailed, error);
|
20519
|
+
return changed;
|
20369
20520
|
}
|
20370
20521
|
/** @internal */
|
20371
|
-
|
20372
|
-
|
20373
|
-
|
20374
|
-
|
20375
|
-
if (this.track) {
|
20376
|
-
this.track.setMuted(info.muted);
|
20377
|
-
} else if (prevMetadataMuted !== info.muted) {
|
20378
|
-
this.emit(info.muted ? TrackEvent.Muted : TrackEvent.Unmuted);
|
20379
|
-
}
|
20380
|
-
}
|
20381
|
-
emitSubscriptionUpdateIfChanged(previousStatus) {
|
20382
|
-
const currentStatus = this.subscriptionStatus;
|
20383
|
-
if (previousStatus === currentStatus) {
|
20384
|
-
return;
|
20385
|
-
}
|
20386
|
-
this.emit(TrackEvent.SubscriptionStatusChanged, currentStatus, previousStatus);
|
20387
|
-
}
|
20388
|
-
emitPermissionUpdateIfChanged(previousPermissionStatus) {
|
20389
|
-
const currentPermissionStatus = this.permissionStatus;
|
20390
|
-
if (currentPermissionStatus !== previousPermissionStatus) {
|
20391
|
-
this.emit(TrackEvent.SubscriptionPermissionChanged, this.permissionStatus, previousPermissionStatus);
|
20392
|
-
}
|
20393
|
-
}
|
20394
|
-
isManualOperationAllowed() {
|
20395
|
-
if (this.kind === Track.Kind.Video && this.isAdaptiveStream) {
|
20396
|
-
this.log.warn('adaptive stream is enabled, cannot change video track settings', this.logContext);
|
20397
|
-
return false;
|
20398
|
-
}
|
20399
|
-
if (!this.isDesired) {
|
20400
|
-
this.log.warn('cannot update track settings when not subscribed', this.logContext);
|
20401
|
-
return false;
|
20402
|
-
}
|
20403
|
-
return true;
|
20404
|
-
}
|
20405
|
-
get isAdaptiveStream() {
|
20406
|
-
return this.track instanceof RemoteVideoTrack && this.track.isAdaptiveStream;
|
20407
|
-
}
|
20408
|
-
/* @internal */
|
20409
|
-
emitTrackUpdate() {
|
20410
|
-
const settings = new UpdateTrackSettings({
|
20411
|
-
trackSids: [this.trackSid],
|
20412
|
-
disabled: this.disabled,
|
20413
|
-
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);
|
20414
20526
|
});
|
20415
|
-
if (this.videoDimensions) {
|
20416
|
-
settings.width = Math.ceil(this.videoDimensions.width);
|
20417
|
-
settings.height = Math.ceil(this.videoDimensions.height);
|
20418
|
-
} else if (this.currentVideoQuality !== undefined) {
|
20419
|
-
settings.quality = this.currentVideoQuality;
|
20420
|
-
} else {
|
20421
|
-
// defaults to high quality
|
20422
|
-
settings.quality = VideoQuality.HIGH;
|
20423
|
-
}
|
20424
|
-
this.emit(TrackEvent.UpdateSettings, settings);
|
20425
20527
|
}
|
20426
|
-
|
20427
|
-
|
20428
|
-
|
20429
|
-
|
20430
|
-
|
20431
|
-
|
20432
|
-
|
20433
|
-
|
20434
|
-
|
20435
|
-
|
20436
|
-
|
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
|
-
|
20475
|
-
|
20476
|
-
|
20477
|
-
|
20478
|
-
|
20479
|
-
|
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
|
+
}
|
20480
20600
|
return track;
|
20481
|
-
}
|
20601
|
+
});
|
20482
20602
|
}
|
20483
20603
|
/**
|
20484
|
-
*
|
20485
|
-
*
|
20486
|
-
* a different source can be passed in as a second argument
|
20487
|
-
* 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.
|
20488
20606
|
*/
|
20489
|
-
|
20490
|
-
|
20491
|
-
|
20492
|
-
|
20493
|
-
|
20494
|
-
|
20495
|
-
|
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
|
+
});
|
20496
20626
|
}
|
20497
20627
|
/**
|
20498
|
-
*
|
20628
|
+
* Create local camera and/or microphone tracks
|
20629
|
+
* @param options
|
20630
|
+
* @returns
|
20499
20631
|
*/
|
20500
|
-
|
20501
|
-
|
20502
|
-
|
20503
|
-
|
20504
|
-
|
20505
|
-
|
20506
|
-
|
20507
|
-
|
20508
|
-
|
20509
|
-
|
20510
|
-
|
20511
|
-
|
20512
|
-
let publication = this.getTrackPublication(sid);
|
20513
|
-
// it's also possible that the browser didn't honor our original track id
|
20514
|
-
// FireFox would use its own local uuid instead of server track id
|
20515
|
-
if (!publication) {
|
20516
|
-
if (!sid.startsWith('TR')) {
|
20517
|
-
// find the first track that matches type
|
20518
|
-
this.tracks.forEach(p => {
|
20519
|
-
if (!publication && mediaTrack.kind === p.kind.toString()) {
|
20520
|
-
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;
|
20521
20644
|
}
|
20522
|
-
|
20645
|
+
if (constraints.video) {
|
20646
|
+
this.cameraError = err;
|
20647
|
+
}
|
20648
|
+
}
|
20649
|
+
throw err;
|
20523
20650
|
}
|
20524
|
-
|
20525
|
-
|
20526
|
-
|
20527
|
-
if (!publication) {
|
20528
|
-
if (triesLeft === 0) {
|
20529
|
-
this.log.error('could not find published track', Object.assign(Object.assign({}, this.logContext), {
|
20530
|
-
trackSid: sid
|
20531
|
-
}));
|
20532
|
-
this.emit(ParticipantEvent.TrackSubscriptionFailed, sid);
|
20533
|
-
return;
|
20651
|
+
if (constraints.audio) {
|
20652
|
+
this.microphoneError = undefined;
|
20653
|
+
this.emit(ParticipantEvent.AudioStreamAcquired);
|
20534
20654
|
}
|
20535
|
-
if (
|
20536
|
-
|
20537
|
-
|
20538
|
-
|
20539
|
-
|
20540
|
-
|
20541
|
-
|
20542
|
-
|
20543
|
-
|
20544
|
-
|
20545
|
-
}
|
20546
|
-
const isVideo = mediaTrack.kind === 'video';
|
20547
|
-
let track;
|
20548
|
-
if (isVideo) {
|
20549
|
-
track = new RemoteVideoTrack(mediaTrack, sid, receiver, adaptiveStreamSettings);
|
20550
|
-
} else {
|
20551
|
-
track = new RemoteAudioTrack(mediaTrack, sid, receiver, this.audioContext, this.audioOutput);
|
20552
|
-
}
|
20553
|
-
// set track info
|
20554
|
-
track.source = publication.source;
|
20555
|
-
// keep publication's muted status
|
20556
|
-
track.isMuted = publication.isMuted;
|
20557
|
-
track.setMediaStream(mediaStream);
|
20558
|
-
track.start();
|
20559
|
-
publication.setTrack(track);
|
20560
|
-
// set participant volumes on new audio tracks
|
20561
|
-
if (this.volumeMap.has(publication.source) && track instanceof RemoteAudioTrack) {
|
20562
|
-
track.setVolume(this.volumeMap.get(publication.source));
|
20563
|
-
}
|
20564
|
-
return publication;
|
20565
|
-
}
|
20566
|
-
/** @internal */
|
20567
|
-
get hasMetadata() {
|
20568
|
-
return !!this.participantInfo;
|
20569
|
-
}
|
20570
|
-
getTrackPublication(sid) {
|
20571
|
-
return this.tracks.get(sid);
|
20572
|
-
}
|
20573
|
-
/** @internal */
|
20574
|
-
updateInfo(info) {
|
20575
|
-
if (!super.updateInfo(info)) {
|
20576
|
-
return false;
|
20577
|
-
}
|
20578
|
-
// we are getting a list of all available tracks, reconcile in here
|
20579
|
-
// and send out events for changes
|
20580
|
-
// reconcile track publications, publish events only if metadata is already there
|
20581
|
-
// i.e. changes since the local participant has joined
|
20582
|
-
const validTracks = new Map();
|
20583
|
-
const newTracks = new Map();
|
20584
|
-
info.tracks.forEach(ti => {
|
20585
|
-
var _a, _b;
|
20586
|
-
let publication = this.getTrackPublication(ti.sid);
|
20587
|
-
if (!publication) {
|
20588
|
-
// new publication
|
20589
|
-
const kind = Track.kindFromProto(ti.type);
|
20590
|
-
if (!kind) {
|
20591
|
-
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;
|
20592
20665
|
}
|
20593
|
-
|
20594
|
-
|
20595
|
-
|
20596
|
-
});
|
20597
|
-
|
20598
|
-
|
20599
|
-
|
20600
|
-
|
20601
|
-
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), {
|
20602
|
-
oldTrack: getLogContextFromTrack(existingTrackOfSource),
|
20603
|
-
newTrack: getLogContextFromTrack(publication)
|
20604
|
-
}));
|
20666
|
+
const track = mediaTrackToLocalTrack(mediaStreamTrack, trackConstraints, {
|
20667
|
+
loggerName: this.roomOptions.loggerName,
|
20668
|
+
loggerContextCb: () => this.logContext
|
20669
|
+
});
|
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;
|
20605
20674
|
}
|
20606
|
-
|
20607
|
-
|
20608
|
-
|
20609
|
-
}
|
20610
|
-
validTracks.set(ti.sid, publication);
|
20611
|
-
});
|
20612
|
-
// detect removed tracks
|
20613
|
-
this.tracks.forEach(publication => {
|
20614
|
-
if (!validTracks.has(publication.trackSid)) {
|
20615
|
-
this.log.trace('detected removed track on remote participant, unpublishing', Object.assign(Object.assign({}, this.logContext), getLogContextFromTrack(publication)));
|
20616
|
-
this.unpublishTrack(publication.trackSid, true);
|
20617
|
-
}
|
20618
|
-
});
|
20619
|
-
// always emit events for new publications, Room will not forward them unless it's ready
|
20620
|
-
newTracks.forEach(publication => {
|
20621
|
-
this.emit(ParticipantEvent.TrackPublished, publication);
|
20675
|
+
track.mediaStream = stream;
|
20676
|
+
return track;
|
20677
|
+
});
|
20622
20678
|
});
|
20623
|
-
return true;
|
20624
|
-
}
|
20625
|
-
/** @internal */
|
20626
|
-
unpublishTrack(sid, sendUnpublish) {
|
20627
|
-
const publication = this.tracks.get(sid);
|
20628
|
-
if (!publication) {
|
20629
|
-
return;
|
20630
|
-
}
|
20631
|
-
// also send unsubscribe, if track is actively subscribed
|
20632
|
-
const {
|
20633
|
-
track
|
20634
|
-
} = publication;
|
20635
|
-
if (track) {
|
20636
|
-
track.stop();
|
20637
|
-
publication.setTrack(undefined);
|
20638
|
-
}
|
20639
|
-
// remove track from maps only after unsubscribed has been fired
|
20640
|
-
this.tracks.delete(sid);
|
20641
|
-
// remove from the right type map
|
20642
|
-
switch (publication.kind) {
|
20643
|
-
case Track.Kind.Audio:
|
20644
|
-
this.audioTracks.delete(sid);
|
20645
|
-
break;
|
20646
|
-
case Track.Kind.Video:
|
20647
|
-
this.videoTracks.delete(sid);
|
20648
|
-
break;
|
20649
|
-
}
|
20650
|
-
if (sendUnpublish) {
|
20651
|
-
this.emit(ParticipantEvent.TrackUnpublished, publication);
|
20652
|
-
}
|
20653
20679
|
}
|
20654
20680
|
/**
|
20655
|
-
*
|
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.
|
20656
20684
|
*/
|
20657
|
-
|
20685
|
+
createScreenTracks(options) {
|
20658
20686
|
return __awaiter(this, void 0, void 0, function* () {
|
20659
|
-
|
20660
|
-
|
20661
|
-
this.audioTracks.forEach(pub => {
|
20662
|
-
var _a;
|
20663
|
-
if (pub.track instanceof RemoteAudioTrack) {
|
20664
|
-
promises.push(pub.track.setSinkId((_a = output.deviceId) !== null && _a !== void 0 ? _a : 'default'));
|
20665
|
-
}
|
20666
|
-
});
|
20667
|
-
yield Promise.all(promises);
|
20668
|
-
});
|
20669
|
-
}
|
20670
|
-
/** @internal */
|
20671
|
-
emit(event) {
|
20672
|
-
for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
|
20673
|
-
args[_key - 1] = arguments[_key];
|
20674
|
-
}
|
20675
|
-
this.log.trace('participant event', Object.assign(Object.assign({}, this.logContext), {
|
20676
|
-
event,
|
20677
|
-
args
|
20678
|
-
}));
|
20679
|
-
return super.emit(event, ...args);
|
20680
|
-
}
|
20681
|
-
}
|
20682
|
-
|
20683
|
-
class LocalParticipant extends Participant {
|
20684
|
-
/** @internal */
|
20685
|
-
constructor(sid, identity, engine, options) {
|
20686
|
-
super(sid, identity, undefined, undefined, {
|
20687
|
-
loggerName: options.loggerName,
|
20688
|
-
loggerContextCb: () => this.engine.logContext
|
20689
|
-
});
|
20690
|
-
this.pendingPublishing = new Set();
|
20691
|
-
this.pendingPublishPromises = new Map();
|
20692
|
-
this.participantTrackPermissions = [];
|
20693
|
-
this.allParticipantsAllowedToSubscribe = true;
|
20694
|
-
this.encryptionType = Encryption_Type.NONE;
|
20695
|
-
this.handleReconnecting = () => {
|
20696
|
-
if (!this.reconnectFuture) {
|
20697
|
-
this.reconnectFuture = new Future();
|
20687
|
+
if (options === undefined) {
|
20688
|
+
options = {};
|
20698
20689
|
}
|
20699
|
-
|
20700
|
-
|
20701
|
-
var _a, _b;
|
20702
|
-
(_b = (_a = this.reconnectFuture) === null || _a === void 0 ? void 0 : _a.resolve) === null || _b === void 0 ? void 0 : _b.call(_a);
|
20703
|
-
this.reconnectFuture = undefined;
|
20704
|
-
this.updateTrackSubscriptionPermissions();
|
20705
|
-
};
|
20706
|
-
this.handleDisconnected = () => {
|
20707
|
-
var _a, _b;
|
20708
|
-
if (this.reconnectFuture) {
|
20709
|
-
this.reconnectFuture.promise.catch(e => this.log.warn(e.message, this.logContext));
|
20710
|
-
(_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');
|
20711
|
-
this.reconnectFuture = undefined;
|
20690
|
+
if (navigator.mediaDevices.getDisplayMedia === undefined) {
|
20691
|
+
throw new DeviceUnsupportedError('getDisplayMedia not supported');
|
20712
20692
|
}
|
20713
|
-
|
20714
|
-
|
20715
|
-
|
20716
|
-
|
20717
|
-
|
20718
|
-
}));
|
20719
|
-
this.engine.client.sendUpdateSubscriptionPermissions(this.allParticipantsAllowedToSubscribe, this.participantTrackPermissions.map(p => trackPermissionToProto(p)));
|
20720
|
-
};
|
20721
|
-
/** @internal */
|
20722
|
-
this.onTrackUnmuted = track => {
|
20723
|
-
this.onTrackMuted(track, track.isUpstreamPaused);
|
20724
|
-
};
|
20725
|
-
// when the local track changes in mute status, we'll notify server as such
|
20726
|
-
/** @internal */
|
20727
|
-
this.onTrackMuted = (track, muted) => {
|
20728
|
-
if (muted === undefined) {
|
20729
|
-
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;
|
20730
20698
|
}
|
20731
|
-
|
20732
|
-
|
20733
|
-
|
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');
|
20734
20704
|
}
|
20735
|
-
|
20736
|
-
|
20737
|
-
|
20738
|
-
|
20739
|
-
|
20740
|
-
|
20741
|
-
|
20742
|
-
this.log.debug('upstream resumed', Object.assign(Object.assign({}, this.logContext), getLogContextFromTrack(track)));
|
20743
|
-
this.onTrackMuted(track, track.isMuted);
|
20744
|
-
};
|
20745
|
-
this.handleSubscribedQualityUpdate = update => __awaiter(this, void 0, void 0, function* () {
|
20746
|
-
var _a, e_1, _b, _c;
|
20747
|
-
var _d, _e;
|
20748
|
-
if (!((_d = this.roomOptions) === null || _d === void 0 ? void 0 : _d.dynacast)) {
|
20749
|
-
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;
|
20750
20712
|
}
|
20751
|
-
const
|
20752
|
-
if (
|
20753
|
-
this.
|
20754
|
-
|
20755
|
-
|
20756
|
-
|
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);
|
20757
20722
|
}
|
20758
|
-
|
20759
|
-
|
20760
|
-
|
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';
|
20761
20752
|
}
|
20762
|
-
|
20763
|
-
|
20764
|
-
|
20765
|
-
|
20766
|
-
_f = false;
|
20767
|
-
const codec = _c;
|
20768
|
-
if (isBackupCodec(codec)) {
|
20769
|
-
this.log.debug("publish ".concat(codec, " for ").concat(pub.videoTrack.sid), Object.assign(Object.assign({}, this.logContext), getLogContextFromTrack(pub)));
|
20770
|
-
yield this.publishAdditionalCodecForTrack(pub.videoTrack, codec, pub.options);
|
20771
|
-
}
|
20772
|
-
}
|
20773
|
-
} catch (e_1_1) {
|
20774
|
-
e_1 = {
|
20775
|
-
error: e_1_1
|
20776
|
-
};
|
20777
|
-
} finally {
|
20778
|
-
try {
|
20779
|
-
if (!_f && !_a && (_b = newCodecs_1.return)) yield _b.call(newCodecs_1);
|
20780
|
-
} finally {
|
20781
|
-
if (e_1) throw e_1.error;
|
20782
|
-
}
|
20783
|
-
}
|
20784
|
-
} else if (update.subscribedQualities.length > 0) {
|
20785
|
-
yield (_e = pub.videoTrack) === null || _e === void 0 ? void 0 : _e.setPublishingLayers(update.subscribedQualities);
|
20786
|
-
}
|
20787
|
-
});
|
20788
|
-
this.handleLocalTrackUnpublished = unpublished => {
|
20789
|
-
const track = this.tracks.get(unpublished.trackSid);
|
20790
|
-
if (!track) {
|
20791
|
-
this.log.warn('received unpublished event for unknown track', Object.assign(Object.assign({}, this.logContext), {
|
20792
|
-
trackSid: unpublished.trackSid
|
20793
|
-
}));
|
20794
|
-
return;
|
20795
|
-
}
|
20796
|
-
this.unpublishTrack(track.track);
|
20797
|
-
};
|
20798
|
-
this.handleTrackEnded = track => __awaiter(this, void 0, void 0, function* () {
|
20799
|
-
if (track.source === Track.Source.ScreenShare || track.source === Track.Source.ScreenShareAudio) {
|
20800
|
-
this.log.debug('unpublishing local track due to TrackEnded', Object.assign(Object.assign({}, this.logContext), getLogContextFromTrack(track)));
|
20801
|
-
this.unpublishTrack(track);
|
20802
|
-
} else if (track.isUserProvided) {
|
20803
|
-
yield track.mute();
|
20804
|
-
} else if (track instanceof LocalAudioTrack || track instanceof LocalVideoTrack) {
|
20805
|
-
try {
|
20806
|
-
if (isWeb()) {
|
20807
|
-
try {
|
20808
|
-
const currentPermissions = yield navigator === null || navigator === void 0 ? void 0 : navigator.permissions.query({
|
20809
|
-
// the permission query for camera and microphone currently not supported in Safari and Firefox
|
20810
|
-
// @ts-ignore
|
20811
|
-
name: track.source === Track.Source.Camera ? 'camera' : 'microphone'
|
20812
|
-
});
|
20813
|
-
if (currentPermissions && currentPermissions.state === 'denied') {
|
20814
|
-
this.log.warn("user has revoked access to ".concat(track.source), Object.assign(Object.assign({}, this.logContext), getLogContextFromTrack(track)));
|
20815
|
-
// detect granted change after permissions were denied to try and resume then
|
20816
|
-
currentPermissions.onchange = () => {
|
20817
|
-
if (currentPermissions.state !== 'denied') {
|
20818
|
-
if (!track.isMuted) {
|
20819
|
-
track.restartTrack();
|
20820
|
-
}
|
20821
|
-
currentPermissions.onchange = null;
|
20822
|
-
}
|
20823
|
-
};
|
20824
|
-
throw new Error('GetUserMedia Permission denied');
|
20825
|
-
}
|
20826
|
-
} catch (e) {
|
20827
|
-
// permissions query fails for firefox, we continue and try to restart the track
|
20828
|
-
}
|
20829
|
-
}
|
20830
|
-
if (!track.isMuted) {
|
20831
|
-
this.log.debug('track ended, attempting to use a different device', Object.assign(Object.assign({}, this.logContext), getLogContextFromTrack(track)));
|
20832
|
-
yield track.restartTrack();
|
20833
|
-
}
|
20834
|
-
} catch (e) {
|
20835
|
-
this.log.warn("could not restart track, muting instead", Object.assign(Object.assign({}, this.logContext), getLogContextFromTrack(track)));
|
20836
|
-
yield track.mute();
|
20837
|
-
}
|
20838
|
-
}
|
20839
|
-
});
|
20840
|
-
this.audioTracks = new Map();
|
20841
|
-
this.videoTracks = new Map();
|
20842
|
-
this.tracks = new Map();
|
20843
|
-
this.engine = engine;
|
20844
|
-
this.roomOptions = options;
|
20845
|
-
this.setupEngine(engine);
|
20846
|
-
this.activeDeviceMap = new Map();
|
20847
|
-
}
|
20848
|
-
get lastCameraError() {
|
20849
|
-
return this.cameraError;
|
20850
|
-
}
|
20851
|
-
get lastMicrophoneError() {
|
20852
|
-
return this.microphoneError;
|
20853
|
-
}
|
20854
|
-
get isE2EEEnabled() {
|
20855
|
-
return this.encryptionType !== Encryption_Type.NONE;
|
20856
|
-
}
|
20857
|
-
getTrack(source) {
|
20858
|
-
const track = super.getTrack(source);
|
20859
|
-
if (track) {
|
20860
|
-
return track;
|
20861
|
-
}
|
20862
|
-
}
|
20863
|
-
getTrackByName(name) {
|
20864
|
-
const track = super.getTrackByName(name);
|
20865
|
-
if (track) {
|
20866
|
-
return track;
|
20867
|
-
}
|
20868
|
-
}
|
20869
|
-
/**
|
20870
|
-
* @internal
|
20871
|
-
*/
|
20872
|
-
setupEngine(engine) {
|
20873
|
-
this.engine = engine;
|
20874
|
-
this.engine.on(EngineEvent.RemoteMute, (trackSid, muted) => {
|
20875
|
-
const pub = this.tracks.get(trackSid);
|
20876
|
-
if (!pub || !pub.track) {
|
20877
|
-
return;
|
20878
|
-
}
|
20879
|
-
if (muted) {
|
20880
|
-
pub.mute();
|
20881
|
-
} else {
|
20882
|
-
pub.unmute();
|
20883
|
-
}
|
20884
|
-
});
|
20885
|
-
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);
|
20886
|
-
}
|
20887
|
-
/**
|
20888
|
-
* Sets and updates the metadata of the local participant.
|
20889
|
-
* The change does not take immediate effect.
|
20890
|
-
* If successful, a `ParticipantEvent.MetadataChanged` event will be emitted on the local participant.
|
20891
|
-
* Note: this requires `canUpdateOwnMetadata` permission.
|
20892
|
-
* @param metadata
|
20893
|
-
*/
|
20894
|
-
setMetadata(metadata) {
|
20895
|
-
var _a;
|
20896
|
-
this.engine.client.sendUpdateLocalMetadata(metadata, (_a = this.name) !== null && _a !== void 0 ? _a : '');
|
20897
|
-
}
|
20898
|
-
/**
|
20899
|
-
* Sets and updates the name of the local participant.
|
20900
|
-
* The change does not take immediate effect.
|
20901
|
-
* If successful, a `ParticipantEvent.ParticipantNameChanged` event will be emitted on the local participant.
|
20902
|
-
* Note: this requires `canUpdateOwnMetadata` permission.
|
20903
|
-
* @param metadata
|
20904
|
-
*/
|
20905
|
-
setName(name) {
|
20906
|
-
var _a;
|
20907
|
-
this.engine.client.sendUpdateLocalMetadata((_a = this.metadata) !== null && _a !== void 0 ? _a : '', name);
|
20908
|
-
}
|
20909
|
-
/**
|
20910
|
-
* Enable or disable a participant's camera track.
|
20911
|
-
*
|
20912
|
-
* If a track has already published, it'll mute or unmute the track.
|
20913
|
-
* Resolves with a `LocalTrackPublication` instance if successful and `undefined` otherwise
|
20914
|
-
*/
|
20915
|
-
setCameraEnabled(enabled, options, publishOptions) {
|
20916
|
-
return this.setTrackEnabled(Track.Source.Camera, enabled, options, publishOptions);
|
20917
|
-
}
|
20918
|
-
/**
|
20919
|
-
* Enable or disable a participant's microphone track.
|
20920
|
-
*
|
20921
|
-
* If a track has already published, it'll mute or unmute the track.
|
20922
|
-
* Resolves with a `LocalTrackPublication` instance if successful and `undefined` otherwise
|
20923
|
-
*/
|
20924
|
-
setMicrophoneEnabled(enabled, options, publishOptions) {
|
20925
|
-
return this.setTrackEnabled(Track.Source.Microphone, enabled, options, publishOptions);
|
20926
|
-
}
|
20927
|
-
/**
|
20928
|
-
* Start or stop sharing a participant's screen
|
20929
|
-
* Resolves with a `LocalTrackPublication` instance if successful and `undefined` otherwise
|
20930
|
-
*/
|
20931
|
-
setScreenShareEnabled(enabled, options, publishOptions) {
|
20932
|
-
return this.setTrackEnabled(Track.Source.ScreenShare, enabled, options, publishOptions);
|
20933
|
-
}
|
20934
|
-
/** @internal */
|
20935
|
-
setPermissions(permissions) {
|
20936
|
-
const prevPermissions = this.permissions;
|
20937
|
-
const changed = super.setPermissions(permissions);
|
20938
|
-
if (changed && prevPermissions) {
|
20939
|
-
this.emit(ParticipantEvent.ParticipantPermissionsChanged, prevPermissions);
|
20940
|
-
}
|
20941
|
-
return changed;
|
20942
|
-
}
|
20943
|
-
/** @internal */
|
20944
|
-
setE2EEEnabled(enabled) {
|
20945
|
-
return __awaiter(this, void 0, void 0, function* () {
|
20946
|
-
this.encryptionType = enabled ? Encryption_Type.GCM : Encryption_Type.NONE;
|
20947
|
-
yield this.republishAllTracks(undefined, false);
|
20948
|
-
});
|
20949
|
-
}
|
20950
|
-
setTrackEnabled(source, enabled, options, publishOptions) {
|
20951
|
-
var _a, _b;
|
20952
|
-
return __awaiter(this, void 0, void 0, function* () {
|
20953
|
-
this.log.debug('setTrackEnabled', Object.assign(Object.assign({}, this.logContext), {
|
20954
|
-
source,
|
20955
|
-
enabled
|
20956
|
-
}));
|
20957
|
-
let track = this.getTrack(source);
|
20958
|
-
if (enabled) {
|
20959
|
-
if (track) {
|
20960
|
-
yield track.unmute();
|
20961
|
-
} else {
|
20962
|
-
let localTracks;
|
20963
|
-
if (this.pendingPublishing.has(source)) {
|
20964
|
-
this.log.info('skipping duplicate published source', Object.assign(Object.assign({}, this.logContext), {
|
20965
|
-
source
|
20966
|
-
}));
|
20967
|
-
// no-op it's already been requested
|
20968
|
-
return;
|
20969
|
-
}
|
20970
|
-
this.pendingPublishing.add(source);
|
20971
|
-
try {
|
20972
|
-
switch (source) {
|
20973
|
-
case Track.Source.Camera:
|
20974
|
-
localTracks = yield this.createTracks({
|
20975
|
-
video: (_a = options) !== null && _a !== void 0 ? _a : true
|
20976
|
-
});
|
20977
|
-
break;
|
20978
|
-
case Track.Source.Microphone:
|
20979
|
-
localTracks = yield this.createTracks({
|
20980
|
-
audio: (_b = options) !== null && _b !== void 0 ? _b : true
|
20981
|
-
});
|
20982
|
-
break;
|
20983
|
-
case Track.Source.ScreenShare:
|
20984
|
-
localTracks = yield this.createScreenTracks(Object.assign({}, options));
|
20985
|
-
break;
|
20986
|
-
default:
|
20987
|
-
throw new TrackInvalidError(source);
|
20988
|
-
}
|
20989
|
-
const publishPromises = [];
|
20990
|
-
for (const localTrack of localTracks) {
|
20991
|
-
this.log.info('publishing track', Object.assign(Object.assign({}, this.logContext), getLogContextFromTrack(localTrack)));
|
20992
|
-
publishPromises.push(this.publishTrack(localTrack, publishOptions));
|
20993
|
-
}
|
20994
|
-
const publishedTracks = yield Promise.all(publishPromises);
|
20995
|
-
// for screen share publications including audio, this will only return the screen share publication, not the screen share audio one
|
20996
|
-
// revisit if we want to return an array of tracks instead for v2
|
20997
|
-
[track] = publishedTracks;
|
20998
|
-
} catch (e) {
|
20999
|
-
localTracks === null || localTracks === void 0 ? void 0 : localTracks.forEach(tr => {
|
21000
|
-
tr.stop();
|
21001
|
-
});
|
21002
|
-
if (e instanceof Error && !(e instanceof TrackInvalidError)) {
|
21003
|
-
this.emit(ParticipantEvent.MediaDevicesError, e);
|
21004
|
-
}
|
21005
|
-
throw e;
|
21006
|
-
} finally {
|
21007
|
-
this.pendingPublishing.delete(source);
|
21008
|
-
}
|
21009
|
-
}
|
21010
|
-
} else if (track && track.track) {
|
21011
|
-
// screenshare cannot be muted, unpublish instead
|
21012
|
-
if (source === Track.Source.ScreenShare) {
|
21013
|
-
track = yield this.unpublishTrack(track.track);
|
21014
|
-
const screenAudioTrack = this.getTrack(Track.Source.ScreenShareAudio);
|
21015
|
-
if (screenAudioTrack && screenAudioTrack.track) {
|
21016
|
-
this.unpublishTrack(screenAudioTrack.track);
|
21017
|
-
}
|
21018
|
-
} else {
|
21019
|
-
yield track.mute();
|
21020
|
-
}
|
21021
|
-
}
|
21022
|
-
return track;
|
21023
|
-
});
|
21024
|
-
}
|
21025
|
-
/**
|
21026
|
-
* Publish both camera and microphone at the same time. This is useful for
|
21027
|
-
* displaying a single Permission Dialog box to the end user.
|
21028
|
-
*/
|
21029
|
-
enableCameraAndMicrophone() {
|
21030
|
-
return __awaiter(this, void 0, void 0, function* () {
|
21031
|
-
if (this.pendingPublishing.has(Track.Source.Camera) || this.pendingPublishing.has(Track.Source.Microphone)) {
|
21032
|
-
// no-op it's already been requested
|
21033
|
-
return;
|
21034
|
-
}
|
21035
|
-
this.pendingPublishing.add(Track.Source.Camera);
|
21036
|
-
this.pendingPublishing.add(Track.Source.Microphone);
|
21037
|
-
try {
|
21038
|
-
const tracks = yield this.createTracks({
|
21039
|
-
audio: true,
|
21040
|
-
video: true
|
21041
|
-
});
|
21042
|
-
yield Promise.all(tracks.map(track => this.publishTrack(track)));
|
21043
|
-
} finally {
|
21044
|
-
this.pendingPublishing.delete(Track.Source.Camera);
|
21045
|
-
this.pendingPublishing.delete(Track.Source.Microphone);
|
21046
|
-
}
|
21047
|
-
});
|
21048
|
-
}
|
21049
|
-
/**
|
21050
|
-
* Create local camera and/or microphone tracks
|
21051
|
-
* @param options
|
21052
|
-
* @returns
|
21053
|
-
*/
|
21054
|
-
createTracks(options) {
|
21055
|
-
var _a, _b;
|
21056
|
-
return __awaiter(this, void 0, void 0, function* () {
|
21057
|
-
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);
|
21058
|
-
const constraints = constraintsForOptions(opts);
|
21059
|
-
let stream;
|
21060
|
-
try {
|
21061
|
-
stream = yield navigator.mediaDevices.getUserMedia(constraints);
|
21062
|
-
} catch (err) {
|
21063
|
-
if (err instanceof Error) {
|
21064
|
-
if (constraints.audio) {
|
21065
|
-
this.microphoneError = err;
|
21066
|
-
}
|
21067
|
-
if (constraints.video) {
|
21068
|
-
this.cameraError = err;
|
21069
|
-
}
|
21070
|
-
}
|
21071
|
-
throw err;
|
21072
|
-
}
|
21073
|
-
if (constraints.audio) {
|
21074
|
-
this.microphoneError = undefined;
|
21075
|
-
this.emit(ParticipantEvent.AudioStreamAcquired);
|
21076
|
-
}
|
21077
|
-
if (constraints.video) {
|
21078
|
-
this.cameraError = undefined;
|
21079
|
-
}
|
21080
|
-
return stream.getTracks().map(mediaStreamTrack => {
|
21081
|
-
const isAudio = mediaStreamTrack.kind === 'audio';
|
21082
|
-
isAudio ? options.audio : options.video;
|
21083
|
-
let trackConstraints;
|
21084
|
-
const conOrBool = isAudio ? constraints.audio : constraints.video;
|
21085
|
-
if (typeof conOrBool !== 'boolean') {
|
21086
|
-
trackConstraints = conOrBool;
|
21087
|
-
}
|
21088
|
-
const track = mediaTrackToLocalTrack(mediaStreamTrack, trackConstraints, {
|
21089
|
-
loggerName: this.roomOptions.loggerName,
|
21090
|
-
loggerContextCb: () => this.logContext
|
21091
|
-
});
|
21092
|
-
if (track.kind === Track.Kind.Video) {
|
21093
|
-
track.source = Track.Source.Camera;
|
21094
|
-
} else if (track.kind === Track.Kind.Audio) {
|
21095
|
-
track.source = Track.Source.Microphone;
|
21096
|
-
}
|
21097
|
-
track.mediaStream = stream;
|
21098
|
-
return track;
|
21099
|
-
});
|
21100
|
-
});
|
21101
|
-
}
|
21102
|
-
/**
|
21103
|
-
* Creates a screen capture tracks with getDisplayMedia().
|
21104
|
-
* A LocalVideoTrack is always created and returned.
|
21105
|
-
* If { audio: true }, and the browser supports audio capture, a LocalAudioTrack is also created.
|
21106
|
-
*/
|
21107
|
-
createScreenTracks(options) {
|
21108
|
-
return __awaiter(this, void 0, void 0, function* () {
|
21109
|
-
if (options === undefined) {
|
21110
|
-
options = {};
|
21111
|
-
}
|
21112
|
-
if (navigator.mediaDevices.getDisplayMedia === undefined) {
|
21113
|
-
throw new DeviceUnsupportedError('getDisplayMedia not supported');
|
21114
|
-
}
|
21115
|
-
if (options.resolution === undefined && !isSafari17()) {
|
21116
|
-
// we need to constrain the dimensions, otherwise it could lead to low bitrate
|
21117
|
-
// due to encoding a huge video. Encoding such large surfaces is really expensive
|
21118
|
-
// unfortunately Safari 17 has a but and cannot be constrained by default
|
21119
|
-
options.resolution = ScreenSharePresets.h1080fps30.resolution;
|
21120
|
-
}
|
21121
|
-
const constraints = screenCaptureToDisplayMediaStreamOptions(options);
|
21122
|
-
const stream = yield navigator.mediaDevices.getDisplayMedia(constraints);
|
21123
|
-
const tracks = stream.getVideoTracks();
|
21124
|
-
if (tracks.length === 0) {
|
21125
|
-
throw new TrackInvalidError('no video track found');
|
21126
|
-
}
|
21127
|
-
const screenVideo = new LocalVideoTrack(tracks[0], undefined, false, {
|
21128
|
-
loggerName: this.roomOptions.loggerName,
|
21129
|
-
loggerContextCb: () => this.logContext
|
21130
|
-
});
|
21131
|
-
screenVideo.source = Track.Source.ScreenShare;
|
21132
|
-
if (options.contentHint) {
|
21133
|
-
screenVideo.mediaStreamTrack.contentHint = options.contentHint;
|
21134
|
-
}
|
21135
|
-
const localTracks = [screenVideo];
|
21136
|
-
if (stream.getAudioTracks().length > 0) {
|
21137
|
-
this.emit(ParticipantEvent.AudioStreamAcquired);
|
21138
|
-
const screenAudio = new LocalAudioTrack(stream.getAudioTracks()[0], undefined, false, this.audioContext, {
|
21139
|
-
loggerName: this.roomOptions.loggerName,
|
21140
|
-
loggerContextCb: () => this.logContext
|
21141
|
-
});
|
21142
|
-
screenAudio.source = Track.Source.ScreenShareAudio;
|
21143
|
-
localTracks.push(screenAudio);
|
21144
|
-
}
|
21145
|
-
return localTracks;
|
21146
|
-
});
|
21147
|
-
}
|
21148
|
-
/**
|
21149
|
-
* Publish a new track to the room
|
21150
|
-
* @param track
|
21151
|
-
* @param options
|
21152
|
-
*/
|
21153
|
-
publishTrack(track, options) {
|
21154
|
-
var _a, _b, _c, _d;
|
21155
|
-
return __awaiter(this, void 0, void 0, function* () {
|
21156
|
-
yield (_a = this.reconnectFuture) === null || _a === void 0 ? void 0 : _a.promise;
|
21157
|
-
if (track instanceof LocalTrack && this.pendingPublishPromises.has(track)) {
|
21158
|
-
yield this.pendingPublishPromises.get(track);
|
21159
|
-
}
|
21160
|
-
let defaultConstraints;
|
21161
|
-
if (track instanceof MediaStreamTrack) {
|
21162
|
-
defaultConstraints = track.getConstraints();
|
21163
|
-
} else {
|
21164
|
-
// we want to access constraints directly as `track.mediaStreamTrack`
|
21165
|
-
// might be pointing to a non-device track (e.g. processed track) already
|
21166
|
-
defaultConstraints = track.constraints;
|
21167
|
-
let deviceKind = undefined;
|
21168
|
-
switch (track.source) {
|
21169
|
-
case Track.Source.Microphone:
|
21170
|
-
deviceKind = 'audioinput';
|
21171
|
-
break;
|
21172
|
-
case Track.Source.Camera:
|
21173
|
-
deviceKind = 'videoinput';
|
21174
|
-
}
|
21175
|
-
if (deviceKind && this.activeDeviceMap.has(deviceKind)) {
|
21176
|
-
defaultConstraints = Object.assign(Object.assign({}, defaultConstraints), {
|
21177
|
-
deviceId: this.activeDeviceMap.get(deviceKind)
|
21178
|
-
});
|
20753
|
+
if (deviceKind && this.activeDeviceMap.has(deviceKind)) {
|
20754
|
+
defaultConstraints = Object.assign(Object.assign({}, defaultConstraints), {
|
20755
|
+
deviceId: this.activeDeviceMap.get(deviceKind)
|
20756
|
+
});
|
21179
20757
|
}
|
21180
20758
|
}
|
21181
20759
|
// convert raw media track into audio or video track
|
@@ -21207,7 +20785,7 @@ class LocalParticipant extends Participant {
|
|
21207
20785
|
}
|
21208
20786
|
// is it already published? if so skip
|
21209
20787
|
let existingPublication;
|
21210
|
-
this.
|
20788
|
+
this.trackPublications.forEach(publication => {
|
21211
20789
|
if (!publication.track) {
|
21212
20790
|
return;
|
21213
20791
|
}
|
@@ -21259,9 +20837,9 @@ class LocalParticipant extends Participant {
|
|
21259
20837
|
});
|
21260
20838
|
}
|
21261
20839
|
publish(track, opts, isStereo) {
|
21262
|
-
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;
|
21263
20841
|
return __awaiter(this, void 0, void 0, function* () {
|
21264
|
-
const existingTrackOfSource = Array.from(this.
|
20842
|
+
const existingTrackOfSource = Array.from(this.trackPublications.values()).find(publishedTrack => track instanceof LocalTrack && publishedTrack.source === track.source);
|
21265
20843
|
if (existingTrackOfSource && track.source !== Track.Source.Unknown) {
|
21266
20844
|
this.log.info("publishing a second track with the same source: ".concat(track.source), Object.assign(Object.assign({}, this.logContext), getLogContextFromTrack(track)));
|
21267
20845
|
}
|
@@ -21292,438 +20870,933 @@ class LocalParticipant extends Participant {
|
|
21292
20870
|
track.on(TrackEvent.UpstreamResumed, this.onTrackUpstreamResumed);
|
21293
20871
|
// create track publication from track
|
21294
20872
|
const req = new AddTrackRequest({
|
21295
|
-
// get local track id for use during publishing
|
21296
|
-
cid: track.mediaStreamTrack.id,
|
21297
|
-
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,
|
21298
21075
|
type: Track.kindToProto(track.kind),
|
21299
21076
|
muted: track.isMuted,
|
21300
21077
|
source: Track.sourceToProto(track.source),
|
21301
|
-
|
21302
|
-
|
21303
|
-
|
21304
|
-
|
21305
|
-
|
21078
|
+
sid: track.sid,
|
21079
|
+
simulcastCodecs: [{
|
21080
|
+
codec: opts.videoCodec,
|
21081
|
+
cid: simulcastTrack.mediaStreamTrack.id
|
21082
|
+
}]
|
21306
21083
|
});
|
21307
|
-
|
21308
|
-
|
21309
|
-
|
21310
|
-
|
21311
|
-
|
21312
|
-
|
21313
|
-
|
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) {
|
21314
21124
|
try {
|
21315
|
-
|
21316
|
-
|
21317
|
-
|
21318
|
-
|
21319
|
-
|
21320
|
-
|
21321
|
-
|
21322
|
-
|
21323
|
-
};
|
21324
|
-
// log failure
|
21325
|
-
this.log.error('could not determine track dimensions, using defaults', Object.assign(Object.assign(Object.assign({}, this.logContext), getLogContextFromTrack(track)), {
|
21326
|
-
dims
|
21327
|
-
}));
|
21328
|
-
}
|
21329
|
-
// width and height should be defined for video
|
21330
|
-
req.width = dims.width;
|
21331
|
-
req.height = dims.height;
|
21332
|
-
// for svc codecs, disable simulcast and use vp8 for backup codec
|
21333
|
-
if (track instanceof LocalVideoTrack) {
|
21334
|
-
if (isSVCCodec(videoCodec)) {
|
21335
|
-
// vp9 svc with screenshare has problem to encode, always use L1T3 here
|
21336
|
-
if (track.source === Track.Source.ScreenShare && videoCodec === 'vp9') {
|
21337
|
-
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;
|
21338
21133
|
}
|
21339
|
-
// set scalabilityMode to 'L3T3_KEY' by default
|
21340
|
-
opts.scalabilityMode = (_e = opts.scalabilityMode) !== null && _e !== void 0 ? _e : 'L3T3_KEY';
|
21341
21134
|
}
|
21342
|
-
|
21343
|
-
|
21344
|
-
cid: track.mediaStreamTrack.id
|
21345
|
-
})];
|
21346
|
-
// set up backup
|
21347
|
-
if (opts.backupCodec === true) {
|
21348
|
-
opts.backupCodec = {
|
21349
|
-
codec: defaultVideoCodec
|
21350
|
-
};
|
21135
|
+
if (this.engine.removeTrack(trackSender)) {
|
21136
|
+
negotiationNeeded = true;
|
21351
21137
|
}
|
21352
|
-
if (
|
21353
|
-
|
21354
|
-
|
21355
|
-
|
21356
|
-
|
21357
|
-
|
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
|
+
}
|
21358
21146
|
}
|
21359
|
-
|
21360
|
-
codec: opts.backupCodec.codec,
|
21361
|
-
cid: ''
|
21362
|
-
}));
|
21147
|
+
track.simulcastCodecs.clear();
|
21363
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
|
+
}));
|
21364
21153
|
}
|
21365
|
-
encodings = computeVideoEncodings(track.source === Track.Source.ScreenShare, req.width, req.height, opts);
|
21366
|
-
req.layers = videoLayersFromEncodings(req.width, req.height, encodings, isSVCCodec(opts.videoCodec));
|
21367
|
-
} else if (track.kind === Track.Kind.Audio) {
|
21368
|
-
encodings = [{
|
21369
|
-
maxBitrate: (_g = (_f = opts.audioPreset) === null || _f === void 0 ? void 0 : _f.maxBitrate) !== null && _g !== void 0 ? _g : opts.audioBitrate,
|
21370
|
-
priority: (_j = (_h = opts.audioPreset) === null || _h === void 0 ? void 0 : _h.priority) !== null && _j !== void 0 ? _j : 'high',
|
21371
|
-
networkPriority: (_l = (_k = opts.audioPreset) === null || _k === void 0 ? void 0 : _k.priority) !== null && _l !== void 0 ? _l : 'high'
|
21372
|
-
}];
|
21373
21154
|
}
|
21374
|
-
|
21375
|
-
|
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;
|
21376
21164
|
}
|
21377
|
-
|
21378
|
-
|
21379
|
-
|
21380
|
-
|
21381
|
-
|
21382
|
-
|
21383
|
-
|
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);
|
21384
21189
|
}
|
21385
21190
|
});
|
21386
|
-
|
21387
|
-
const
|
21388
|
-
|
21389
|
-
|
21390
|
-
|
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
|
21199
|
+
}));
|
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
|
21391
21280
|
}));
|
21392
|
-
|
21393
|
-
opts.videoCodec = updatedCodec;
|
21394
|
-
// recompute encodings since bitrates/etc could have changed
|
21395
|
-
encodings = computeVideoEncodings(track.source === Track.Source.ScreenShare, req.width, req.height, opts);
|
21281
|
+
this.engine.client.sendMuteTrack(ti.sid, mutedOnServer);
|
21396
21282
|
}
|
21397
21283
|
}
|
21398
|
-
|
21399
|
-
|
21400
|
-
|
21401
|
-
|
21402
|
-
|
21403
|
-
|
21404
|
-
|
21405
|
-
if (!
|
21406
|
-
|
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;
|
21407
21293
|
}
|
21408
|
-
this
|
21409
|
-
|
21410
|
-
|
21411
|
-
|
21412
|
-
|
21413
|
-
if (encodings) {
|
21414
|
-
if (isFireFox() && track.kind === Track.Kind.Audio) {
|
21415
|
-
/* Refer to RFC https://datatracker.ietf.org/doc/html/rfc7587#section-6.1,
|
21416
|
-
livekit-server uses maxaveragebitrate=510000 in the answer sdp to permit client to
|
21417
|
-
publish high quality audio track. But firefox always uses this value as the actual
|
21418
|
-
bitrates, causing the audio bitrates to rise to 510Kbps in any stereo case unexpectedly.
|
21419
|
-
So the client need to modify maxaverragebitrates in answer sdp to user provided value to
|
21420
|
-
fix the issue.
|
21421
|
-
*/
|
21422
|
-
let trackTransceiver = undefined;
|
21423
|
-
for (const transceiver of this.engine.pcManager.publisher.getTransceivers()) {
|
21424
|
-
if (transceiver.sender === track.sender) {
|
21425
|
-
trackTransceiver = transceiver;
|
21426
|
-
break;
|
21427
|
-
}
|
21428
|
-
}
|
21429
|
-
if (trackTransceiver) {
|
21430
|
-
this.engine.pcManager.publisher.setTrackCodecBitrate({
|
21431
|
-
transceiver: trackTransceiver,
|
21432
|
-
codec: 'opus',
|
21433
|
-
maxbr: ((_m = encodings[0]) === null || _m === void 0 ? void 0 : _m.maxBitrate) ? encodings[0].maxBitrate / 1000 : 0
|
21434
|
-
});
|
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;
|
21435
21299
|
}
|
21436
|
-
} else if (track.codec && isSVCCodec(track.codec) && ((_o = encodings[0]) === null || _o === void 0 ? void 0 : _o.maxBitrate)) {
|
21437
|
-
this.engine.pcManager.publisher.setTrackCodecBitrate({
|
21438
|
-
cid: req.cid,
|
21439
|
-
codec: track.codec,
|
21440
|
-
maxbr: encodings[0].maxBitrate / 1000
|
21441
|
-
});
|
21442
21300
|
}
|
21301
|
+
} else if (track === localTrack) {
|
21302
|
+
publication = pub;
|
21443
21303
|
}
|
21444
|
-
yield this.engine.negotiate();
|
21445
|
-
if (track instanceof LocalVideoTrack) {
|
21446
|
-
track.startMonitor(this.engine.client);
|
21447
|
-
} else if (track instanceof LocalAudioTrack) {
|
21448
|
-
track.startMonitor();
|
21449
|
-
}
|
21450
|
-
this.addTrackPublication(publication);
|
21451
|
-
// send event for publication
|
21452
|
-
this.emit(ParticipantEvent.LocalTrackPublished, publication);
|
21453
|
-
return publication;
|
21454
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
|
+
}
|
21455
21497
|
}
|
21456
|
-
|
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
|
+
}
|
21457
21520
|
return true;
|
21458
21521
|
}
|
21459
|
-
|
21460
|
-
|
21461
|
-
*/
|
21462
|
-
publishAdditionalCodecForTrack(track, videoCodec, options) {
|
21463
|
-
var _a;
|
21464
|
-
return __awaiter(this, void 0, void 0, function* () {
|
21465
|
-
// TODO remove once e2ee is supported for backup tracks
|
21466
|
-
if (this.encryptionType !== Encryption_Type.NONE) {
|
21467
|
-
return;
|
21468
|
-
}
|
21469
|
-
// is it not published? if so skip
|
21470
|
-
let existingPublication;
|
21471
|
-
this.tracks.forEach(publication => {
|
21472
|
-
if (!publication.track) {
|
21473
|
-
return;
|
21474
|
-
}
|
21475
|
-
if (publication.track === track) {
|
21476
|
-
existingPublication = publication;
|
21477
|
-
}
|
21478
|
-
});
|
21479
|
-
if (!existingPublication) {
|
21480
|
-
throw new TrackInvalidError('track is not published');
|
21481
|
-
}
|
21482
|
-
if (!(track instanceof LocalVideoTrack)) {
|
21483
|
-
throw new TrackInvalidError('track is not a video track');
|
21484
|
-
}
|
21485
|
-
const opts = Object.assign(Object.assign({}, (_a = this.roomOptions) === null || _a === void 0 ? void 0 : _a.publishDefaults), options);
|
21486
|
-
const encodings = computeTrackBackupEncodings(track, videoCodec, opts);
|
21487
|
-
if (!encodings) {
|
21488
|
-
this.log.info("backup codec has been disabled, ignoring request to add additional codec for track", Object.assign(Object.assign({}, this.logContext), getLogContextFromTrack(track)));
|
21489
|
-
return;
|
21490
|
-
}
|
21491
|
-
const simulcastTrack = track.addSimulcastTrack(videoCodec, encodings);
|
21492
|
-
const req = new AddTrackRequest({
|
21493
|
-
cid: simulcastTrack.mediaStreamTrack.id,
|
21494
|
-
type: Track.kindToProto(track.kind),
|
21495
|
-
muted: track.isMuted,
|
21496
|
-
source: Track.sourceToProto(track.source),
|
21497
|
-
sid: track.sid,
|
21498
|
-
simulcastCodecs: [{
|
21499
|
-
codec: opts.videoCodec,
|
21500
|
-
cid: simulcastTrack.mediaStreamTrack.id
|
21501
|
-
}]
|
21502
|
-
});
|
21503
|
-
req.layers = videoLayersFromEncodings(req.width, req.height, encodings);
|
21504
|
-
if (!this.engine || this.engine.isClosed) {
|
21505
|
-
throw new UnexpectedConnectionState('cannot publish track when not connected');
|
21506
|
-
}
|
21507
|
-
const ti = yield this.engine.addTrack(req);
|
21508
|
-
yield this.engine.createSimulcastSender(track, simulcastTrack, opts, encodings);
|
21509
|
-
yield this.engine.negotiate();
|
21510
|
-
this.log.debug("published ".concat(videoCodec, " for track ").concat(track.sid), Object.assign(Object.assign({}, this.logContext), {
|
21511
|
-
encodings,
|
21512
|
-
trackInfo: ti
|
21513
|
-
}));
|
21514
|
-
});
|
21522
|
+
get isAdaptiveStream() {
|
21523
|
+
return this.track instanceof RemoteVideoTrack && this.track.isAdaptiveStream;
|
21515
21524
|
}
|
21516
|
-
|
21517
|
-
|
21518
|
-
|
21519
|
-
|
21520
|
-
|
21521
|
-
|
21522
|
-
this.log.debug('unpublishing track', Object.assign(Object.assign({}, this.logContext), pubLogContext));
|
21523
|
-
if (!publication || !publication.track) {
|
21524
|
-
this.log.warn('track was not unpublished because no publication was found', Object.assign(Object.assign({}, this.logContext), pubLogContext));
|
21525
|
-
return undefined;
|
21526
|
-
}
|
21527
|
-
track = publication.track;
|
21528
|
-
track.off(TrackEvent.Muted, this.onTrackMuted);
|
21529
|
-
track.off(TrackEvent.Unmuted, this.onTrackUnmuted);
|
21530
|
-
track.off(TrackEvent.Ended, this.handleTrackEnded);
|
21531
|
-
track.off(TrackEvent.UpstreamPaused, this.onTrackUpstreamPaused);
|
21532
|
-
track.off(TrackEvent.UpstreamResumed, this.onTrackUpstreamResumed);
|
21533
|
-
if (stopOnUnpublish === undefined) {
|
21534
|
-
stopOnUnpublish = (_b = (_a = this.roomOptions) === null || _a === void 0 ? void 0 : _a.stopLocalTrackOnUnpublish) !== null && _b !== void 0 ? _b : true;
|
21535
|
-
}
|
21536
|
-
if (stopOnUnpublish) {
|
21537
|
-
track.stop();
|
21538
|
-
}
|
21539
|
-
let negotiationNeeded = false;
|
21540
|
-
const trackSender = track.sender;
|
21541
|
-
track.sender = undefined;
|
21542
|
-
if (this.engine.pcManager && this.engine.pcManager.currentState < PCTransportState.FAILED && trackSender) {
|
21543
|
-
try {
|
21544
|
-
for (const transceiver of this.engine.pcManager.publisher.getTransceivers()) {
|
21545
|
-
// if sender is not currently sending (after replaceTrack(null))
|
21546
|
-
// removeTrack would have no effect.
|
21547
|
-
// to ensure we end up successfully removing the track, manually set
|
21548
|
-
// the transceiver to inactive
|
21549
|
-
if (transceiver.sender === trackSender) {
|
21550
|
-
transceiver.direction = 'inactive';
|
21551
|
-
negotiationNeeded = true;
|
21552
|
-
}
|
21553
|
-
}
|
21554
|
-
if (this.engine.removeTrack(trackSender)) {
|
21555
|
-
negotiationNeeded = true;
|
21556
|
-
}
|
21557
|
-
if (track instanceof LocalVideoTrack) {
|
21558
|
-
for (const [, trackInfo] of track.simulcastCodecs) {
|
21559
|
-
if (trackInfo.sender) {
|
21560
|
-
if (this.engine.removeTrack(trackInfo.sender)) {
|
21561
|
-
negotiationNeeded = true;
|
21562
|
-
}
|
21563
|
-
trackInfo.sender = undefined;
|
21564
|
-
}
|
21565
|
-
}
|
21566
|
-
track.simulcastCodecs.clear();
|
21567
|
-
}
|
21568
|
-
} catch (e) {
|
21569
|
-
this.log.warn('failed to unpublish track', Object.assign(Object.assign(Object.assign({}, this.logContext), pubLogContext), {
|
21570
|
-
error: e
|
21571
|
-
}));
|
21572
|
-
}
|
21573
|
-
}
|
21574
|
-
// remove from our maps
|
21575
|
-
this.tracks.delete(publication.trackSid);
|
21576
|
-
switch (publication.kind) {
|
21577
|
-
case Track.Kind.Audio:
|
21578
|
-
this.audioTracks.delete(publication.trackSid);
|
21579
|
-
break;
|
21580
|
-
case Track.Kind.Video:
|
21581
|
-
this.videoTracks.delete(publication.trackSid);
|
21582
|
-
break;
|
21583
|
-
}
|
21584
|
-
this.emit(ParticipantEvent.LocalTrackUnpublished, publication);
|
21585
|
-
publication.setTrack(undefined);
|
21586
|
-
if (negotiationNeeded) {
|
21587
|
-
yield this.engine.negotiate();
|
21588
|
-
}
|
21589
|
-
return publication;
|
21525
|
+
/* @internal */
|
21526
|
+
emitTrackUpdate() {
|
21527
|
+
const settings = new UpdateTrackSettings({
|
21528
|
+
trackSids: [this.trackSid],
|
21529
|
+
disabled: this.disabled,
|
21530
|
+
fps: this.fps
|
21590
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);
|
21591
21542
|
}
|
21592
|
-
|
21593
|
-
|
21594
|
-
|
21595
|
-
|
21596
|
-
|
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);
|
21597
21549
|
}
|
21598
|
-
|
21599
|
-
|
21600
|
-
|
21601
|
-
|
21602
|
-
|
21603
|
-
|
21604
|
-
|
21605
|
-
|
21606
|
-
|
21607
|
-
|
21608
|
-
|
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;
|
21609
21569
|
});
|
21610
|
-
|
21611
|
-
|
21612
|
-
|
21613
|
-
|
21614
|
-
|
21615
|
-
|
21616
|
-
|
21617
|
-
|
21618
|
-
|
21619
|
-
|
21620
|
-
|
21621
|
-
|
21622
|
-
|
21570
|
+
this.signalClient.sendUpdateSubscription(sub);
|
21571
|
+
});
|
21572
|
+
publication.on(TrackEvent.SubscriptionPermissionChanged, status => {
|
21573
|
+
this.emit(ParticipantEvent.TrackSubscriptionPermissionChanged, publication, status);
|
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);
|
21623
21586
|
});
|
21624
21587
|
}
|
21625
|
-
|
21626
|
-
|
21627
|
-
|
21628
|
-
|
21629
|
-
|
21630
|
-
|
21631
|
-
|
21632
|
-
|
21633
|
-
|
21634
|
-
|
21635
|
-
|
21636
|
-
|
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
|
+
}
|
21613
|
+
}
|
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;
|
21637
21638
|
}
|
21638
21639
|
});
|
21639
21640
|
}
|
21640
|
-
|
21641
|
-
|
21642
|
-
|
21643
|
-
|
21644
|
-
|
21645
|
-
|
21646
|
-
|
21647
|
-
|
21648
|
-
|
21649
|
-
|
21650
|
-
|
21651
|
-
|
21652
|
-
|
21653
|
-
|
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;
|
21654
21686
|
}
|
21655
21687
|
/**
|
21656
|
-
*
|
21657
|
-
*
|
21658
|
-
* By default, all participants can subscribe. This allows fine-grained control over
|
21659
|
-
* who is able to subscribe at a participant and track level.
|
21660
|
-
*
|
21661
|
-
* Note: if access is given at a track-level (i.e. both [allParticipantsAllowed] and
|
21662
|
-
* [ParticipantTrackPermission.allTracksAllowed] are false), any newer published tracks
|
21663
|
-
* will not grant permissions to any participants and will require a subsequent
|
21664
|
-
* permissions update to allow subscription.
|
21665
|
-
*
|
21666
|
-
* @param allParticipantsAllowed Allows all participants to subscribe all tracks.
|
21667
|
-
* Takes precedence over [[participantTrackPermissions]] if set to true.
|
21668
|
-
* By default this is set to true.
|
21669
|
-
* @param participantTrackPermissions Full list of individual permissions per
|
21670
|
-
* participant/track. Any omitted participants will not receive any permissions.
|
21688
|
+
* @internal
|
21671
21689
|
*/
|
21672
|
-
|
21673
|
-
|
21674
|
-
this.participantTrackPermissions = participantTrackPermissions;
|
21675
|
-
this.allParticipantsAllowedToSubscribe = allParticipantsAllowed;
|
21676
|
-
if (!this.engine.client.isDisconnected) {
|
21677
|
-
this.updateTrackSubscriptionPermissions();
|
21678
|
-
}
|
21690
|
+
getTrackPublicationBySid(sid) {
|
21691
|
+
return this.trackPublications.get(sid);
|
21679
21692
|
}
|
21680
21693
|
/** @internal */
|
21681
21694
|
updateInfo(info) {
|
21682
|
-
if (info.sid !== this.sid) {
|
21683
|
-
// drop updates that specify a wrong sid.
|
21684
|
-
// the sid for local participant is only explicitly set on join and full reconnect
|
21685
|
-
return false;
|
21686
|
-
}
|
21687
21695
|
if (!super.updateInfo(info)) {
|
21688
21696
|
return false;
|
21689
21697
|
}
|
21690
|
-
// reconcile
|
21691
|
-
//
|
21692
|
-
//
|
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();
|
21693
21704
|
info.tracks.forEach(ti => {
|
21694
21705
|
var _a, _b;
|
21695
|
-
|
21696
|
-
if (
|
21697
|
-
|
21698
|
-
|
21699
|
-
|
21700
|
-
|
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)
|
21701
21724
|
}));
|
21702
|
-
this.engine.client.sendMuteTrack(ti.sid, mutedOnServer);
|
21703
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);
|
21704
21737
|
}
|
21705
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
|
+
});
|
21706
21743
|
return true;
|
21707
21744
|
}
|
21708
|
-
|
21709
|
-
|
21710
|
-
this.
|
21711
|
-
|
21712
|
-
|
21713
|
-
|
21714
|
-
|
21715
|
-
|
21716
|
-
|
21717
|
-
|
21718
|
-
|
21719
|
-
|
21720
|
-
|
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'));
|
21721
21785
|
}
|
21722
|
-
}
|
21723
|
-
|
21724
|
-
}
|
21786
|
+
});
|
21787
|
+
yield Promise.all(promises);
|
21725
21788
|
});
|
21726
|
-
|
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);
|
21727
21800
|
}
|
21728
21801
|
}
|
21729
21802
|
|
@@ -21735,8 +21808,6 @@ var ConnectionState;
|
|
21735
21808
|
ConnectionState["Reconnecting"] = "reconnecting";
|
21736
21809
|
})(ConnectionState || (ConnectionState = {}));
|
21737
21810
|
const connectionReconcileFrequency = 2 * 1000;
|
21738
|
-
/** @deprecated RoomState has been renamed to [[ConnectionState]] */
|
21739
|
-
const RoomState = ConnectionState;
|
21740
21811
|
/**
|
21741
21812
|
* In LiveKit, a room is the logical grouping for a list of participants.
|
21742
21813
|
* Participants in a room can publish tracks, and subscribe to others' tracks.
|
@@ -21767,6 +21838,7 @@ class Room extends eventsExports.EventEmitter {
|
|
21767
21838
|
this.isVideoPlaybackBlocked = false;
|
21768
21839
|
this.log = livekitLogger;
|
21769
21840
|
this.bufferedEvents = [];
|
21841
|
+
this.isResuming = false;
|
21770
21842
|
this.connect = (url, token, opts) => __awaiter(this, void 0, void 0, function* () {
|
21771
21843
|
var _c;
|
21772
21844
|
// In case a disconnect called happened right before the connect call, make sure the disconnect is completed first by awaiting its lock
|
@@ -21853,7 +21925,6 @@ class Room extends eventsExports.EventEmitter {
|
|
21853
21925
|
var _e, _f, _g;
|
21854
21926
|
const joinResponse = yield engine.join(url, token, {
|
21855
21927
|
autoSubscribe: connectOptions.autoSubscribe,
|
21856
|
-
publishOnly: connectOptions.publishOnly,
|
21857
21928
|
adaptiveStream: typeof roomOptions.adaptiveStream === 'object' ? true : roomOptions.adaptiveStream,
|
21858
21929
|
maxRetries: connectOptions.maxRetries,
|
21859
21930
|
e2eeEnabled: !!this.e2eeManager,
|
@@ -21898,8 +21969,8 @@ class Room extends eventsExports.EventEmitter {
|
|
21898
21969
|
}
|
21899
21970
|
};
|
21900
21971
|
this.attemptConnection = (url, token, opts, abortController) => __awaiter(this, void 0, void 0, function* () {
|
21901
|
-
var _h, _j;
|
21902
|
-
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)) {
|
21903
21974
|
this.log.info('Reconnection attempt replaced by new connection attempt', this.logContext);
|
21904
21975
|
// make sure we close and recreate the existing engine in order to get rid of any potentially ongoing reconnection attempts
|
21905
21976
|
this.recreateEngine();
|
@@ -21907,7 +21978,7 @@ class Room extends eventsExports.EventEmitter {
|
|
21907
21978
|
// create engine if previously disconnected
|
21908
21979
|
this.maybeCreateEngine();
|
21909
21980
|
}
|
21910
|
-
if ((
|
21981
|
+
if ((_j = this.regionUrlProvider) === null || _j === void 0 ? void 0 : _j.isCloud()) {
|
21911
21982
|
this.engine.setRegionUrlProvider(this.regionUrlProvider);
|
21912
21983
|
}
|
21913
21984
|
this.acquireAudioContext();
|
@@ -21960,7 +22031,7 @@ class Room extends eventsExports.EventEmitter {
|
|
21960
22031
|
}
|
21961
22032
|
if (isWeb()) {
|
21962
22033
|
document.addEventListener('freeze', this.onPageLeave);
|
21963
|
-
(
|
22034
|
+
(_k = navigator.mediaDevices) === null || _k === void 0 ? void 0 : _k.addEventListener('devicechange', this.handleDeviceChange);
|
21964
22035
|
}
|
21965
22036
|
this.setAndEmitConnectionState(ConnectionState.Connected);
|
21966
22037
|
this.emit(RoomEvent.Connected);
|
@@ -21972,7 +22043,7 @@ class Room extends eventsExports.EventEmitter {
|
|
21972
22043
|
this.disconnect = function () {
|
21973
22044
|
let stopTracks = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
|
21974
22045
|
return __awaiter(_this, void 0, void 0, function* () {
|
21975
|
-
var
|
22046
|
+
var _l, _m, _o, _p;
|
21976
22047
|
const unlock = yield this.disconnectLock.lock();
|
21977
22048
|
try {
|
21978
22049
|
if (this.state === ConnectionState.Disconnected) {
|
@@ -21980,16 +22051,16 @@ class Room extends eventsExports.EventEmitter {
|
|
21980
22051
|
return;
|
21981
22052
|
}
|
21982
22053
|
this.log.info('disconnect from room', Object.assign({}, this.logContext));
|
21983
|
-
if (this.state === ConnectionState.Connecting || this.state === ConnectionState.Reconnecting) {
|
22054
|
+
if (this.state === ConnectionState.Connecting || this.state === ConnectionState.Reconnecting || this.isResuming) {
|
21984
22055
|
// try aborting pending connection attempt
|
21985
22056
|
this.log.warn('abort connection attempt', this.logContext);
|
21986
|
-
(
|
22057
|
+
(_l = this.abortController) === null || _l === void 0 ? void 0 : _l.abort();
|
21987
22058
|
// in case the abort controller didn't manage to cancel the connection attempt, reject the connect promise explicitly
|
21988
|
-
(
|
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'));
|
21989
22060
|
this.connectFuture = undefined;
|
21990
22061
|
}
|
21991
22062
|
// send leave
|
21992
|
-
if (!((
|
22063
|
+
if (!((_p = this.engine) === null || _p === void 0 ? void 0 : _p.client.isDisconnected)) {
|
21993
22064
|
yield this.engine.client.sendLeave();
|
21994
22065
|
}
|
21995
22066
|
// close engine (also closes client)
|
@@ -22056,8 +22127,8 @@ class Room extends eventsExports.EventEmitter {
|
|
22056
22127
|
}
|
22057
22128
|
elements.push(dummyAudioEl);
|
22058
22129
|
}
|
22059
|
-
this.
|
22060
|
-
p.
|
22130
|
+
this.remoteParticipants.forEach(p => {
|
22131
|
+
p.audioTrackPublications.forEach(t => {
|
22061
22132
|
if (t.track) {
|
22062
22133
|
t.track.attachedElements.forEach(e => {
|
22063
22134
|
elements.push(e);
|
@@ -22078,8 +22149,8 @@ class Room extends eventsExports.EventEmitter {
|
|
22078
22149
|
});
|
22079
22150
|
this.startVideo = () => __awaiter(this, void 0, void 0, function* () {
|
22080
22151
|
const elements = [];
|
22081
|
-
for (const p of this.
|
22082
|
-
p.
|
22152
|
+
for (const p of this.remoteParticipants.values()) {
|
22153
|
+
p.videoTrackPublications.forEach(tr => {
|
22083
22154
|
var _a;
|
22084
22155
|
(_a = tr.track) === null || _a === void 0 ? void 0 : _a.attachedElements.forEach(el => {
|
22085
22156
|
if (!elements.includes(el)) {
|
@@ -22100,9 +22171,11 @@ class Room extends eventsExports.EventEmitter {
|
|
22100
22171
|
});
|
22101
22172
|
this.handleRestarting = () => {
|
22102
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;
|
22103
22176
|
// also unwind existing participants & existing subscriptions
|
22104
|
-
for (const p of this.
|
22105
|
-
this.handleParticipantDisconnected(p.
|
22177
|
+
for (const p of this.remoteParticipants.values()) {
|
22178
|
+
this.handleParticipantDisconnected(p.identity, p);
|
22106
22179
|
}
|
22107
22180
|
if (this.setAndEmitConnectionState(ConnectionState.Reconnecting)) {
|
22108
22181
|
this.emit(RoomEvent.Reconnecting);
|
@@ -22127,7 +22200,7 @@ class Room extends eventsExports.EventEmitter {
|
|
22127
22200
|
this.log.debug("fully reconnected to server", Object.assign(Object.assign({}, this.logContext), {
|
22128
22201
|
region: joinResponse.serverRegion
|
22129
22202
|
}));
|
22130
|
-
} catch (
|
22203
|
+
} catch (_q) {
|
22131
22204
|
// reconnection failed, handleDisconnect is being invoked already, just return here
|
22132
22205
|
return;
|
22133
22206
|
}
|
@@ -22139,28 +22212,23 @@ class Room extends eventsExports.EventEmitter {
|
|
22139
22212
|
this.handleParticipantUpdates = participantInfos => {
|
22140
22213
|
// handle changes to participant state, and send events
|
22141
22214
|
participantInfos.forEach(info => {
|
22215
|
+
var _a;
|
22142
22216
|
if (info.identity === this.localParticipant.identity) {
|
22143
22217
|
this.localParticipant.updateInfo(info);
|
22144
22218
|
return;
|
22145
22219
|
}
|
22146
|
-
//
|
22147
|
-
|
22148
|
-
if (
|
22149
|
-
|
22150
|
-
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 : '';
|
22151
22224
|
}
|
22152
|
-
let remoteParticipant = this.
|
22153
|
-
const isNewParticipant = !remoteParticipant;
|
22225
|
+
let remoteParticipant = this.remoteParticipants.get(info.identity);
|
22154
22226
|
// when it's disconnected, send updates
|
22155
22227
|
if (info.state === ParticipantInfo_State.DISCONNECTED) {
|
22156
|
-
this.handleParticipantDisconnected(info.
|
22228
|
+
this.handleParticipantDisconnected(info.identity, remoteParticipant);
|
22157
22229
|
} else {
|
22158
22230
|
// create participant if doesn't exist
|
22159
|
-
remoteParticipant = this.getOrCreateParticipant(info.
|
22160
|
-
if (!isNewParticipant) {
|
22161
|
-
// just update, no events
|
22162
|
-
remoteParticipant.updateInfo(info);
|
22163
|
-
}
|
22231
|
+
remoteParticipant = this.getOrCreateParticipant(info.identity, info);
|
22164
22232
|
}
|
22165
22233
|
});
|
22166
22234
|
};
|
@@ -22175,7 +22243,7 @@ class Room extends eventsExports.EventEmitter {
|
|
22175
22243
|
this.localParticipant.setIsSpeaking(true);
|
22176
22244
|
activeSpeakers.push(this.localParticipant);
|
22177
22245
|
} else {
|
22178
|
-
const p = this.
|
22246
|
+
const p = this.getRemoteParticipantBySid(speaker.sid);
|
22179
22247
|
if (p) {
|
22180
22248
|
p.audioLevel = speaker.level;
|
22181
22249
|
p.setIsSpeaking(true);
|
@@ -22187,7 +22255,7 @@ class Room extends eventsExports.EventEmitter {
|
|
22187
22255
|
this.localParticipant.audioLevel = 0;
|
22188
22256
|
this.localParticipant.setIsSpeaking(false);
|
22189
22257
|
}
|
22190
|
-
this.
|
22258
|
+
this.remoteParticipants.forEach(p => {
|
22191
22259
|
if (!seenSids[p.sid]) {
|
22192
22260
|
p.audioLevel = 0;
|
22193
22261
|
p.setIsSpeaking(false);
|
@@ -22203,7 +22271,7 @@ class Room extends eventsExports.EventEmitter {
|
|
22203
22271
|
lastSpeakers.set(p.sid, p);
|
22204
22272
|
});
|
22205
22273
|
speakerUpdates.forEach(speaker => {
|
22206
|
-
let p = this.
|
22274
|
+
let p = this.getRemoteParticipantBySid(speaker.sid);
|
22207
22275
|
if (speaker.sid === this.localParticipant.sid) {
|
22208
22276
|
p = this.localParticipant;
|
22209
22277
|
}
|
@@ -22225,11 +22293,11 @@ class Room extends eventsExports.EventEmitter {
|
|
22225
22293
|
};
|
22226
22294
|
this.handleStreamStateUpdate = streamStateUpdate => {
|
22227
22295
|
streamStateUpdate.streamStates.forEach(streamState => {
|
22228
|
-
const participant = this.
|
22296
|
+
const participant = this.getRemoteParticipantBySid(streamState.participantSid);
|
22229
22297
|
if (!participant) {
|
22230
22298
|
return;
|
22231
22299
|
}
|
22232
|
-
const pub = participant.
|
22300
|
+
const pub = participant.getTrackPublicationBySid(streamState.trackSid);
|
22233
22301
|
if (!pub || !pub.track) {
|
22234
22302
|
return;
|
22235
22303
|
}
|
@@ -22239,22 +22307,22 @@ class Room extends eventsExports.EventEmitter {
|
|
22239
22307
|
});
|
22240
22308
|
};
|
22241
22309
|
this.handleSubscriptionPermissionUpdate = update => {
|
22242
|
-
const participant = this.
|
22310
|
+
const participant = this.getRemoteParticipantBySid(update.participantSid);
|
22243
22311
|
if (!participant) {
|
22244
22312
|
return;
|
22245
22313
|
}
|
22246
|
-
const pub = participant.
|
22314
|
+
const pub = participant.getTrackPublicationBySid(update.trackSid);
|
22247
22315
|
if (!pub) {
|
22248
22316
|
return;
|
22249
22317
|
}
|
22250
22318
|
pub.setAllowed(update.allowed);
|
22251
22319
|
};
|
22252
22320
|
this.handleSubscriptionError = update => {
|
22253
|
-
const participant = Array.from(this.
|
22321
|
+
const participant = Array.from(this.remoteParticipants.values()).find(p => p.trackPublications.has(update.trackSid));
|
22254
22322
|
if (!participant) {
|
22255
22323
|
return;
|
22256
22324
|
}
|
22257
|
-
const pub = participant.
|
22325
|
+
const pub = participant.getTrackPublicationBySid(update.trackSid);
|
22258
22326
|
if (!pub) {
|
22259
22327
|
return;
|
22260
22328
|
}
|
@@ -22262,7 +22330,7 @@ class Room extends eventsExports.EventEmitter {
|
|
22262
22330
|
};
|
22263
22331
|
this.handleDataPacket = (userPacket, kind) => {
|
22264
22332
|
// find the participant
|
22265
|
-
const participant = this.
|
22333
|
+
const participant = this.remoteParticipants.get(userPacket.participantIdentity);
|
22266
22334
|
this.emit(RoomEvent.DataReceived, userPacket.payload, participant, kind, userPacket.topic);
|
22267
22335
|
// also emit on the participant
|
22268
22336
|
participant === null || participant === void 0 ? void 0 : participant.emit(ParticipantEvent.DataReceived, userPacket.payload, kind);
|
@@ -22315,7 +22383,7 @@ class Room extends eventsExports.EventEmitter {
|
|
22315
22383
|
this.localParticipant.setConnectionQuality(info.quality);
|
22316
22384
|
return;
|
22317
22385
|
}
|
22318
|
-
const participant = this.
|
22386
|
+
const participant = this.getRemoteParticipantBySid(info.participantSid);
|
22319
22387
|
if (participant) {
|
22320
22388
|
participant.setConnectionQuality(info.quality);
|
22321
22389
|
}
|
@@ -22334,7 +22402,7 @@ class Room extends eventsExports.EventEmitter {
|
|
22334
22402
|
this.emit(RoomEvent.TrackUnmuted, pub, this.localParticipant);
|
22335
22403
|
};
|
22336
22404
|
this.onLocalTrackPublished = pub => __awaiter(this, void 0, void 0, function* () {
|
22337
|
-
var
|
22405
|
+
var _r;
|
22338
22406
|
this.emit(RoomEvent.LocalTrackPublished, pub, this.localParticipant);
|
22339
22407
|
if (pub.track instanceof LocalAudioTrack) {
|
22340
22408
|
const trackIsSilent = yield pub.track.checkForSilence();
|
@@ -22342,7 +22410,7 @@ class Room extends eventsExports.EventEmitter {
|
|
22342
22410
|
this.emit(RoomEvent.LocalAudioSilenceDetected, pub);
|
22343
22411
|
}
|
22344
22412
|
}
|
22345
|
-
const deviceId = yield (
|
22413
|
+
const deviceId = yield (_r = pub.track) === null || _r === void 0 ? void 0 : _r.getDeviceId();
|
22346
22414
|
const deviceKind = sourceToKind(pub.source);
|
22347
22415
|
if (deviceKind && deviceId && deviceId !== this.localParticipant.activeDeviceMap.get(deviceKind)) {
|
22348
22416
|
this.localParticipant.activeDeviceMap.set(deviceKind, deviceId);
|
@@ -22362,8 +22430,8 @@ class Room extends eventsExports.EventEmitter {
|
|
22362
22430
|
this.emit(RoomEvent.ParticipantPermissionsChanged, prevPermissions, this.localParticipant);
|
22363
22431
|
};
|
22364
22432
|
this.setMaxListeners(100);
|
22365
|
-
this.
|
22366
|
-
this.
|
22433
|
+
this.remoteParticipants = new Map();
|
22434
|
+
this.sidToIdentity = new Map();
|
22367
22435
|
this.options = Object.assign(Object.assign({}, roomOptionDefaults), options);
|
22368
22436
|
this.log = getLogger((_a = this.options.loggerName) !== null && _a !== void 0 ? _a : LoggerNames.Room);
|
22369
22437
|
this.options.audioCaptureDefaults = Object.assign(Object.assign({}, audioDefaults), options === null || options === void 0 ? void 0 : options.audioCaptureDefaults);
|
@@ -22415,9 +22483,10 @@ class Room extends eventsExports.EventEmitter {
|
|
22415
22483
|
}
|
22416
22484
|
}
|
22417
22485
|
get logContext() {
|
22486
|
+
var _a;
|
22418
22487
|
return {
|
22419
22488
|
room: this.name,
|
22420
|
-
roomSid: this.sid,
|
22489
|
+
roomSid: (_a = this.roomInfo) === null || _a === void 0 ? void 0 : _a.sid,
|
22421
22490
|
identity: this.localParticipant.identity
|
22422
22491
|
};
|
22423
22492
|
}
|
@@ -22428,10 +22497,32 @@ class Room extends eventsExports.EventEmitter {
|
|
22428
22497
|
var _a, _b;
|
22429
22498
|
return (_b = (_a = this.roomInfo) === null || _a === void 0 ? void 0 : _a.activeRecording) !== null && _b !== void 0 ? _b : false;
|
22430
22499
|
}
|
22431
|
-
/**
|
22432
|
-
|
22433
|
-
|
22434
|
-
|
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
|
+
});
|
22435
22526
|
}
|
22436
22527
|
/** user assigned name, derived from JWT token */
|
22437
22528
|
get name() {
|
@@ -22462,18 +22553,17 @@ class Room extends eventsExports.EventEmitter {
|
|
22462
22553
|
this.handleDisconnect(this.options.stopLocalTrackOnUnpublish, reason);
|
22463
22554
|
}).on(EngineEvent.ActiveSpeakersUpdate, this.handleActiveSpeakersUpdate).on(EngineEvent.DataPacketReceived, this.handleDataPacket).on(EngineEvent.Resuming, () => {
|
22464
22555
|
this.clearConnectionReconcile();
|
22465
|
-
|
22466
|
-
|
22467
|
-
}
|
22556
|
+
this.isResuming = true;
|
22557
|
+
this.log.info('Resuming signal connection', this.logContext);
|
22468
22558
|
}).on(EngineEvent.Resumed, () => {
|
22469
|
-
this.setAndEmitConnectionState(ConnectionState.Connected);
|
22470
|
-
this.emit(RoomEvent.Reconnected);
|
22471
22559
|
this.registerConnectionReconcile();
|
22560
|
+
this.isResuming = false;
|
22561
|
+
this.log.info('Resumed signal connection', this.logContext);
|
22472
22562
|
this.updateSubscriptions();
|
22473
22563
|
this.emitBufferedEvents();
|
22474
22564
|
}).on(EngineEvent.SignalResumed, () => {
|
22475
22565
|
this.bufferedEvents = [];
|
22476
|
-
if (this.state === ConnectionState.Reconnecting) {
|
22566
|
+
if (this.state === ConnectionState.Reconnecting || this.isResuming) {
|
22477
22567
|
this.sendSyncState();
|
22478
22568
|
}
|
22479
22569
|
}).on(EngineEvent.Restarting, this.handleRestarting).on(EngineEvent.SignalRestarted, this.handleSignalRestarted).on(EngineEvent.DCBufferStatusChanged, (status, kind) => {
|
@@ -22547,10 +22637,7 @@ class Room extends eventsExports.EventEmitter {
|
|
22547
22637
|
if (this.localParticipant.identity === identity) {
|
22548
22638
|
return this.localParticipant;
|
22549
22639
|
}
|
22550
|
-
|
22551
|
-
if (sid) {
|
22552
|
-
return this.participants.get(sid);
|
22553
|
-
}
|
22640
|
+
return this.remoteParticipants.get(identity);
|
22554
22641
|
}
|
22555
22642
|
clearConnectionFutures() {
|
22556
22643
|
this.connectFuture = undefined;
|
@@ -22681,15 +22768,6 @@ class Room extends eventsExports.EventEmitter {
|
|
22681
22768
|
get canPlaybackVideo() {
|
22682
22769
|
return !this.isVideoPlaybackBlocked;
|
22683
22770
|
}
|
22684
|
-
/**
|
22685
|
-
* Returns the active audio output device used in this room.
|
22686
|
-
* @return the previously successfully set audio output device ID or an empty string if the default device is used.
|
22687
|
-
* @deprecated use `getActiveDevice('audiooutput')` instead
|
22688
|
-
*/
|
22689
|
-
getActiveAudioOutputDevice() {
|
22690
|
-
var _a, _b;
|
22691
|
-
return (_b = (_a = this.options.audioOutput) === null || _a === void 0 ? void 0 : _a.deviceId) !== null && _b !== void 0 ? _b : '';
|
22692
|
-
}
|
22693
22771
|
getActiveDevice(kind) {
|
22694
22772
|
return this.localParticipant.activeDeviceMap.get(kind);
|
22695
22773
|
}
|
@@ -22717,7 +22795,7 @@ class Room extends eventsExports.EventEmitter {
|
|
22717
22795
|
const prevDeviceId = this.options.audioCaptureDefaults.deviceId;
|
22718
22796
|
this.options.audioCaptureDefaults.deviceId = deviceConstraint;
|
22719
22797
|
deviceHasChanged = prevDeviceId !== deviceConstraint;
|
22720
|
-
const tracks = Array.from(this.localParticipant.
|
22798
|
+
const tracks = Array.from(this.localParticipant.audioTrackPublications.values()).filter(track => track.source === Track.Source.Microphone);
|
22721
22799
|
try {
|
22722
22800
|
success = (yield Promise.all(tracks.map(t => {
|
22723
22801
|
var _a;
|
@@ -22731,7 +22809,7 @@ class Room extends eventsExports.EventEmitter {
|
|
22731
22809
|
const prevDeviceId = this.options.videoCaptureDefaults.deviceId;
|
22732
22810
|
this.options.videoCaptureDefaults.deviceId = deviceConstraint;
|
22733
22811
|
deviceHasChanged = prevDeviceId !== deviceConstraint;
|
22734
|
-
const tracks = Array.from(this.localParticipant.
|
22812
|
+
const tracks = Array.from(this.localParticipant.videoTrackPublications.values()).filter(track => track.source === Track.Source.Camera);
|
22735
22813
|
try {
|
22736
22814
|
success = (yield Promise.all(tracks.map(t => {
|
22737
22815
|
var _a;
|
@@ -22742,7 +22820,7 @@ class Room extends eventsExports.EventEmitter {
|
|
22742
22820
|
throw e;
|
22743
22821
|
}
|
22744
22822
|
} else if (kind === 'audiooutput') {
|
22745
|
-
if (!supportsSetSinkId() && !this.options.
|
22823
|
+
if (!supportsSetSinkId() && !this.options.webAudioMix || this.options.webAudioMix && this.audioContext && !('setSinkId' in this.audioContext)) {
|
22746
22824
|
throw new Error('cannot switch audio output, setSinkId not supported');
|
22747
22825
|
}
|
22748
22826
|
(_a = (_c = this.options).audioOutput) !== null && _a !== void 0 ? _a : _c.audioOutput = {};
|
@@ -22750,11 +22828,11 @@ class Room extends eventsExports.EventEmitter {
|
|
22750
22828
|
this.options.audioOutput.deviceId = deviceId;
|
22751
22829
|
deviceHasChanged = prevDeviceId !== deviceConstraint;
|
22752
22830
|
try {
|
22753
|
-
if (this.options.
|
22831
|
+
if (this.options.webAudioMix) {
|
22754
22832
|
// @ts-expect-error setSinkId is not yet in the typescript type of AudioContext
|
22755
22833
|
(_b = this.audioContext) === null || _b === void 0 ? void 0 : _b.setSinkId(deviceId);
|
22756
22834
|
} else {
|
22757
|
-
yield Promise.all(Array.from(this.
|
22835
|
+
yield Promise.all(Array.from(this.remoteParticipants.values()).map(p => p.setAudioOutput({
|
22758
22836
|
deviceId
|
22759
22837
|
})));
|
22760
22838
|
}
|
@@ -22778,9 +22856,11 @@ class Room extends eventsExports.EventEmitter {
|
|
22778
22856
|
(_a = this.engine) === null || _a === void 0 ? void 0 : _a.close();
|
22779
22857
|
/* @ts-ignore */
|
22780
22858
|
this.engine = undefined;
|
22859
|
+
this.isResuming = false;
|
22781
22860
|
// clear out existing remote participants, since they may have attached
|
22782
22861
|
// the old engine
|
22783
|
-
this.
|
22862
|
+
this.remoteParticipants.clear();
|
22863
|
+
this.sidToIdentity.clear();
|
22784
22864
|
this.bufferedEvents = [];
|
22785
22865
|
this.maybeCreateEngine();
|
22786
22866
|
}
|
@@ -22810,19 +22890,19 @@ class Room extends eventsExports.EventEmitter {
|
|
22810
22890
|
return;
|
22811
22891
|
}
|
22812
22892
|
const parts = unpackStreamId(stream.id);
|
22813
|
-
const
|
22893
|
+
const participantSid = parts[0];
|
22814
22894
|
let streamId = parts[1];
|
22815
22895
|
let trackId = mediaTrack.id;
|
22816
22896
|
// firefox will get streamId (pID|trackId) instead of (pID|streamId) as it doesn't support sync tracks by stream
|
22817
22897
|
// and generates its own track id instead of infer from sdp track id.
|
22818
22898
|
if (streamId && streamId.startsWith('TR')) trackId = streamId;
|
22819
|
-
if (
|
22899
|
+
if (participantSid === this.localParticipant.sid) {
|
22820
22900
|
this.log.warn('tried to create RemoteParticipant for local participant', this.logContext);
|
22821
22901
|
return;
|
22822
22902
|
}
|
22823
|
-
const participant = this.
|
22903
|
+
const participant = Array.from(this.remoteParticipants.values()).find(p => p.sid === participantSid);
|
22824
22904
|
if (!participant) {
|
22825
|
-
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);
|
22826
22906
|
return;
|
22827
22907
|
}
|
22828
22908
|
let adaptiveStreamSettings;
|
@@ -22840,18 +22920,19 @@ class Room extends eventsExports.EventEmitter {
|
|
22840
22920
|
let reason = arguments.length > 1 ? arguments[1] : undefined;
|
22841
22921
|
var _a;
|
22842
22922
|
this.clearConnectionReconcile();
|
22923
|
+
this.isResuming = false;
|
22843
22924
|
this.bufferedEvents = [];
|
22844
22925
|
if (this.state === ConnectionState.Disconnected) {
|
22845
22926
|
return;
|
22846
22927
|
}
|
22847
22928
|
this.regionUrl = undefined;
|
22848
22929
|
try {
|
22849
|
-
this.
|
22850
|
-
p.
|
22930
|
+
this.remoteParticipants.forEach(p => {
|
22931
|
+
p.trackPublications.forEach(pub => {
|
22851
22932
|
p.unpublishTrack(pub.trackSid);
|
22852
22933
|
});
|
22853
22934
|
});
|
22854
|
-
this.localParticipant.
|
22935
|
+
this.localParticipant.trackPublications.forEach(pub => {
|
22855
22936
|
var _a, _b;
|
22856
22937
|
if (pub.track) {
|
22857
22938
|
this.localParticipant.unpublishTrack(pub.track, shouldStopTracks);
|
@@ -22862,12 +22943,13 @@ class Room extends eventsExports.EventEmitter {
|
|
22862
22943
|
}
|
22863
22944
|
});
|
22864
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);
|
22865
|
-
this.localParticipant.
|
22866
|
-
this.localParticipant.
|
22867
|
-
this.localParticipant.
|
22868
|
-
this.
|
22946
|
+
this.localParticipant.trackPublications.clear();
|
22947
|
+
this.localParticipant.videoTrackPublications.clear();
|
22948
|
+
this.localParticipant.audioTrackPublications.clear();
|
22949
|
+
this.remoteParticipants.clear();
|
22950
|
+
this.sidToIdentity.clear();
|
22869
22951
|
this.activeSpeakers = [];
|
22870
|
-
if (this.audioContext && typeof this.options.
|
22952
|
+
if (this.audioContext && typeof this.options.webAudioMix === 'boolean') {
|
22871
22953
|
this.audioContext.close();
|
22872
22954
|
this.audioContext = undefined;
|
22873
22955
|
}
|
@@ -22882,14 +22964,13 @@ class Room extends eventsExports.EventEmitter {
|
|
22882
22964
|
this.emit(RoomEvent.Disconnected, reason);
|
22883
22965
|
}
|
22884
22966
|
}
|
22885
|
-
handleParticipantDisconnected(
|
22967
|
+
handleParticipantDisconnected(identity, participant) {
|
22886
22968
|
// remove and send event
|
22887
|
-
this.
|
22969
|
+
this.remoteParticipants.delete(identity);
|
22888
22970
|
if (!participant) {
|
22889
22971
|
return;
|
22890
22972
|
}
|
22891
|
-
|
22892
|
-
participant.tracks.forEach(publication => {
|
22973
|
+
participant.trackPublications.forEach(publication => {
|
22893
22974
|
participant.unpublishTrack(publication.trackSid, true);
|
22894
22975
|
});
|
22895
22976
|
this.emit(RoomEvent.ParticipantDisconnected, participant);
|
@@ -22897,9 +22978,9 @@ class Room extends eventsExports.EventEmitter {
|
|
22897
22978
|
acquireAudioContext() {
|
22898
22979
|
var _a, _b;
|
22899
22980
|
return __awaiter(this, void 0, void 0, function* () {
|
22900
|
-
if (typeof this.options.
|
22981
|
+
if (typeof this.options.webAudioMix !== 'boolean' && this.options.webAudioMix.audioContext) {
|
22901
22982
|
// override audio context with custom audio context if supplied by user
|
22902
|
-
this.audioContext = this.options.
|
22983
|
+
this.audioContext = this.options.webAudioMix.audioContext;
|
22903
22984
|
} else if (!this.audioContext || this.audioContext.state === 'closed') {
|
22904
22985
|
// by using an AudioContext, it reduces lag on audio elements
|
22905
22986
|
// https://stackoverflow.com/questions/9811429/html5-audio-tag-on-safari-has-a-delay/54119854#54119854
|
@@ -22916,8 +22997,8 @@ class Room extends eventsExports.EventEmitter {
|
|
22916
22997
|
}));
|
22917
22998
|
}
|
22918
22999
|
}
|
22919
|
-
if (this.options.
|
22920
|
-
this.
|
23000
|
+
if (this.options.webAudioMix) {
|
23001
|
+
this.remoteParticipants.forEach(participant => participant.setAudioContext(this.audioContext));
|
22921
23002
|
}
|
22922
23003
|
this.localParticipant.setAudioContext(this.audioContext);
|
22923
23004
|
const newContextIsRunning = ((_b = this.audioContext) === null || _b === void 0 ? void 0 : _b.state) === 'running';
|
@@ -22927,18 +23008,18 @@ class Room extends eventsExports.EventEmitter {
|
|
22927
23008
|
}
|
22928
23009
|
});
|
22929
23010
|
}
|
22930
|
-
createParticipant(
|
23011
|
+
createParticipant(identity, info) {
|
22931
23012
|
var _a;
|
22932
23013
|
let participant;
|
22933
23014
|
if (info) {
|
22934
23015
|
participant = RemoteParticipant.fromParticipantInfo(this.engine.client, info);
|
22935
23016
|
} else {
|
22936
|
-
participant = new RemoteParticipant(this.engine.client,
|
23017
|
+
participant = new RemoteParticipant(this.engine.client, '', identity, undefined, undefined, {
|
22937
23018
|
loggerContextCb: () => this.logContext,
|
22938
23019
|
loggerName: this.options.loggerName
|
22939
23020
|
});
|
22940
23021
|
}
|
22941
|
-
if (this.options.
|
23022
|
+
if (this.options.webAudioMix) {
|
22942
23023
|
participant.setAudioContext(this.audioContext);
|
22943
23024
|
}
|
22944
23025
|
if ((_a = this.options.audioOutput) === null || _a === void 0 ? void 0 : _a.deviceId) {
|
@@ -22946,13 +23027,20 @@ class Room extends eventsExports.EventEmitter {
|
|
22946
23027
|
}
|
22947
23028
|
return participant;
|
22948
23029
|
}
|
22949
|
-
getOrCreateParticipant(
|
22950
|
-
if (this.
|
22951
|
-
|
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;
|
22952
23040
|
}
|
22953
|
-
const participant = this.createParticipant(
|
22954
|
-
this.
|
22955
|
-
this.
|
23041
|
+
const participant = this.createParticipant(identity, info);
|
23042
|
+
this.remoteParticipants.set(identity, participant);
|
23043
|
+
this.sidToIdentity.set(info.sid, info.identity);
|
22956
23044
|
// if we have valid info and the participant wasn't in the map before, we can assume the participant is new
|
22957
23045
|
// firing here to make sure that `ParticipantConnected` fires before the initial track events
|
22958
23046
|
this.emitWhenConnected(RoomEvent.ParticipantConnected, participant);
|
@@ -23003,11 +23091,11 @@ class Room extends eventsExports.EventEmitter {
|
|
23003
23091
|
return participant;
|
23004
23092
|
}
|
23005
23093
|
sendSyncState() {
|
23006
|
-
const remoteTracks = Array.from(this.
|
23007
|
-
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
|
23008
23096
|
return acc;
|
23009
23097
|
}, []);
|
23010
|
-
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
|
23011
23099
|
this.engine.sendSyncState(remoteTracks, localTracks);
|
23012
23100
|
}
|
23013
23101
|
/**
|
@@ -23015,14 +23103,20 @@ class Room extends eventsExports.EventEmitter {
|
|
23015
23103
|
* subscription settings.
|
23016
23104
|
*/
|
23017
23105
|
updateSubscriptions() {
|
23018
|
-
for (const p of this.
|
23019
|
-
for (const pub of p.
|
23106
|
+
for (const p of this.remoteParticipants.values()) {
|
23107
|
+
for (const pub of p.videoTrackPublications.values()) {
|
23020
23108
|
if (pub.isSubscribed && pub instanceof RemoteTrackPublication) {
|
23021
23109
|
pub.emitTrackUpdate();
|
23022
23110
|
}
|
23023
23111
|
}
|
23024
23112
|
}
|
23025
23113
|
}
|
23114
|
+
getRemoteParticipantBySid(sid) {
|
23115
|
+
const identity = this.sidToIdentity.get(sid);
|
23116
|
+
if (identity) {
|
23117
|
+
return this.remoteParticipants.get(identity);
|
23118
|
+
}
|
23119
|
+
}
|
23026
23120
|
registerConnectionReconcile() {
|
23027
23121
|
this.clearConnectionReconcile();
|
23028
23122
|
let consecutiveFailures = 0;
|
@@ -23076,11 +23170,11 @@ class Room extends eventsExports.EventEmitter {
|
|
23076
23170
|
for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
|
23077
23171
|
args[_key - 1] = arguments[_key];
|
23078
23172
|
}
|
23079
|
-
if (this.state === ConnectionState.
|
23080
|
-
return this.emit(event, ...args);
|
23081
|
-
} else if (this.state === ConnectionState.Reconnecting) {
|
23173
|
+
if (this.state === ConnectionState.Reconnecting || this.isResuming || !this.engine || this.engine.pendingReconnect) {
|
23082
23174
|
// in case the room is reconnecting, buffer the events by firing them later after emitting RoomEvent.Reconnected
|
23083
23175
|
this.bufferedEvents.push([event, args]);
|
23176
|
+
} else if (this.state === ConnectionState.Connected) {
|
23177
|
+
return this.emit(event, ...args);
|
23084
23178
|
}
|
23085
23179
|
return false;
|
23086
23180
|
}
|
@@ -23859,5 +23953,5 @@ function isFacingModeValue(item) {
|
|
23859
23953
|
return item === undefined || allowedValues.includes(item);
|
23860
23954
|
}
|
23861
23955
|
|
23862
|
-
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 };
|
23863
23957
|
//# sourceMappingURL=livekit-client.esm.mjs.map
|