livekit-client 1.15.12 → 2.0.1
Sign up to get free protection for your applications and to get access to all the features.
- package/README.md +27 -15
- package/dist/livekit-client.esm.mjs +1750 -1581
- 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 +56 -1
- package/dist/src/proto/livekit_models_pb.d.ts.map +1 -1
- package/dist/src/proto/livekit_rtc_pb.d.ts +38 -0
- package/dist/src/proto/livekit_rtc_pb.d.ts.map +1 -1
- package/dist/src/room/PCTransport.d.ts +1 -2
- package/dist/src/room/PCTransport.d.ts.map +1 -1
- package/dist/src/room/PCTransportManager.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 +8 -5
- 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 +56 -1
- package/dist/ts4.2/src/proto/livekit_rtc_pb.d.ts +38 -0
- package/dist/ts4.2/src/room/PCTransport.d.ts +1 -2
- 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 +8 -5
- 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 +10 -9
- 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 +78 -0
- package/src/proto/livekit_rtc_pb.ts +53 -0
- package/src/room/PCTransport.ts +3 -13
- package/src/room/PCTransportManager.ts +2 -3
- package/src/room/RTCEngine.ts +11 -1
- package/src/room/Room.ts +145 -114
- package/src/room/defaults.ts +1 -5
- package/src/room/events.ts +8 -6
- 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
|
}, {
|
@@ -3874,6 +3874,14 @@ var DisconnectReason;
|
|
3874
3874
|
* @generated from enum value: JOIN_FAILURE = 7;
|
3875
3875
|
*/
|
3876
3876
|
DisconnectReason[DisconnectReason["JOIN_FAILURE"] = 7] = "JOIN_FAILURE";
|
3877
|
+
/**
|
3878
|
+
* @generated from enum value: MIGRATION = 8;
|
3879
|
+
*/
|
3880
|
+
DisconnectReason[DisconnectReason["MIGRATION"] = 8] = "MIGRATION";
|
3881
|
+
/**
|
3882
|
+
* @generated from enum value: SIGNAL_CLOSE = 9;
|
3883
|
+
*/
|
3884
|
+
DisconnectReason[DisconnectReason["SIGNAL_CLOSE"] = 9] = "SIGNAL_CLOSE";
|
3877
3885
|
})(DisconnectReason || (DisconnectReason = {}));
|
3878
3886
|
// Retrieve enum metadata with: proto3.getEnumType(DisconnectReason)
|
3879
3887
|
proto3.util.setEnumType(DisconnectReason, "livekit.DisconnectReason", [{
|
@@ -3900,6 +3908,12 @@ proto3.util.setEnumType(DisconnectReason, "livekit.DisconnectReason", [{
|
|
3900
3908
|
}, {
|
3901
3909
|
no: 7,
|
3902
3910
|
name: "JOIN_FAILURE"
|
3911
|
+
}, {
|
3912
|
+
no: 8,
|
3913
|
+
name: "MIGRATION"
|
3914
|
+
}, {
|
3915
|
+
no: 9,
|
3916
|
+
name: "SIGNAL_CLOSE"
|
3903
3917
|
}]);
|
3904
3918
|
/**
|
3905
3919
|
* @generated from enum livekit.ReconnectReason
|
@@ -4096,6 +4110,11 @@ Room$1.fields = proto3.util.newFieldList(() => [{
|
|
4096
4110
|
name: "active_recording",
|
4097
4111
|
kind: "scalar",
|
4098
4112
|
T: 8 /* ScalarType.BOOL */
|
4113
|
+
}, {
|
4114
|
+
no: 13,
|
4115
|
+
name: "version",
|
4116
|
+
kind: "message",
|
4117
|
+
T: TimedVersion
|
4099
4118
|
}]);
|
4100
4119
|
/**
|
4101
4120
|
* @generated from message livekit.Codec
|
@@ -4354,6 +4373,10 @@ class ParticipantInfo extends Message {
|
|
4354
4373
|
* @generated from field: bool is_publisher = 13;
|
4355
4374
|
*/
|
4356
4375
|
this.isPublisher = false;
|
4376
|
+
/**
|
4377
|
+
* @generated from field: livekit.ParticipantInfo.Kind kind = 14;
|
4378
|
+
*/
|
4379
|
+
this.kind = ParticipantInfo_Kind.STANDARD;
|
4357
4380
|
proto3.util.initPartial(data, this);
|
4358
4381
|
}
|
4359
4382
|
static fromBinary(bytes, options) {
|
@@ -4427,6 +4450,11 @@ ParticipantInfo.fields = proto3.util.newFieldList(() => [{
|
|
4427
4450
|
name: "is_publisher",
|
4428
4451
|
kind: "scalar",
|
4429
4452
|
T: 8 /* ScalarType.BOOL */
|
4453
|
+
}, {
|
4454
|
+
no: 14,
|
4455
|
+
name: "kind",
|
4456
|
+
kind: "enum",
|
4457
|
+
T: proto3.getEnumType(ParticipantInfo_Kind)
|
4430
4458
|
}]);
|
4431
4459
|
/**
|
4432
4460
|
* @generated from enum livekit.ParticipantInfo.State
|
@@ -4472,6 +4500,59 @@ proto3.util.setEnumType(ParticipantInfo_State, "livekit.ParticipantInfo.State",
|
|
4472
4500
|
no: 3,
|
4473
4501
|
name: "DISCONNECTED"
|
4474
4502
|
}]);
|
4503
|
+
/**
|
4504
|
+
* @generated from enum livekit.ParticipantInfo.Kind
|
4505
|
+
*/
|
4506
|
+
var ParticipantInfo_Kind;
|
4507
|
+
(function (ParticipantInfo_Kind) {
|
4508
|
+
/**
|
4509
|
+
* standard participants, e.g. web clients
|
4510
|
+
*
|
4511
|
+
* @generated from enum value: STANDARD = 0;
|
4512
|
+
*/
|
4513
|
+
ParticipantInfo_Kind[ParticipantInfo_Kind["STANDARD"] = 0] = "STANDARD";
|
4514
|
+
/**
|
4515
|
+
* only ingests streams
|
4516
|
+
*
|
4517
|
+
* @generated from enum value: INGRESS = 1;
|
4518
|
+
*/
|
4519
|
+
ParticipantInfo_Kind[ParticipantInfo_Kind["INGRESS"] = 1] = "INGRESS";
|
4520
|
+
/**
|
4521
|
+
* only consumes streams
|
4522
|
+
*
|
4523
|
+
* @generated from enum value: EGRESS = 2;
|
4524
|
+
*/
|
4525
|
+
ParticipantInfo_Kind[ParticipantInfo_Kind["EGRESS"] = 2] = "EGRESS";
|
4526
|
+
/**
|
4527
|
+
* SIP participants
|
4528
|
+
*
|
4529
|
+
* @generated from enum value: SIP = 3;
|
4530
|
+
*/
|
4531
|
+
ParticipantInfo_Kind[ParticipantInfo_Kind["SIP"] = 3] = "SIP";
|
4532
|
+
/**
|
4533
|
+
* LiveKit agents
|
4534
|
+
*
|
4535
|
+
* @generated from enum value: AGENT = 4;
|
4536
|
+
*/
|
4537
|
+
ParticipantInfo_Kind[ParticipantInfo_Kind["AGENT"] = 4] = "AGENT";
|
4538
|
+
})(ParticipantInfo_Kind || (ParticipantInfo_Kind = {}));
|
4539
|
+
// Retrieve enum metadata with: proto3.getEnumType(ParticipantInfo_Kind)
|
4540
|
+
proto3.util.setEnumType(ParticipantInfo_Kind, "livekit.ParticipantInfo.Kind", [{
|
4541
|
+
no: 0,
|
4542
|
+
name: "STANDARD"
|
4543
|
+
}, {
|
4544
|
+
no: 1,
|
4545
|
+
name: "INGRESS"
|
4546
|
+
}, {
|
4547
|
+
no: 2,
|
4548
|
+
name: "EGRESS"
|
4549
|
+
}, {
|
4550
|
+
no: 3,
|
4551
|
+
name: "SIP"
|
4552
|
+
}, {
|
4553
|
+
no: 4,
|
4554
|
+
name: "AGENT"
|
4555
|
+
}]);
|
4475
4556
|
/**
|
4476
4557
|
* @generated from message livekit.Encryption
|
4477
4558
|
*/
|
@@ -4779,6 +4860,11 @@ TrackInfo.fields = proto3.util.newFieldList(() => [{
|
|
4779
4860
|
name: "stream",
|
4780
4861
|
kind: "scalar",
|
4781
4862
|
T: 9 /* ScalarType.STRING */
|
4863
|
+
}, {
|
4864
|
+
no: 18,
|
4865
|
+
name: "version",
|
4866
|
+
kind: "message",
|
4867
|
+
T: TimedVersion
|
4782
4868
|
}]);
|
4783
4869
|
/**
|
4784
4870
|
* provide information about available spatial layers
|
@@ -4793,7 +4879,7 @@ class VideoLayer extends Message {
|
|
4793
4879
|
*
|
4794
4880
|
* @generated from field: livekit.VideoQuality quality = 1;
|
4795
4881
|
*/
|
4796
|
-
this.quality = VideoQuality.LOW;
|
4882
|
+
this.quality = VideoQuality$1.LOW;
|
4797
4883
|
/**
|
4798
4884
|
* @generated from field: uint32 width = 2;
|
4799
4885
|
*/
|
@@ -4833,7 +4919,7 @@ VideoLayer.fields = proto3.util.newFieldList(() => [{
|
|
4833
4919
|
no: 1,
|
4834
4920
|
name: "quality",
|
4835
4921
|
kind: "enum",
|
4836
|
-
T: proto3.getEnumType(VideoQuality)
|
4922
|
+
T: proto3.getEnumType(VideoQuality$1)
|
4837
4923
|
}, {
|
4838
4924
|
no: 2,
|
4839
4925
|
name: "width",
|
@@ -10036,7 +10122,14 @@ var RoomEvent;
|
|
10036
10122
|
RoomEvent["Reconnected"] = "reconnected";
|
10037
10123
|
/**
|
10038
10124
|
* When disconnected from room. This fires when room.disconnect() is called or
|
10039
|
-
* when an unrecoverable connection issue had occured
|
10125
|
+
* when an unrecoverable connection issue had occured.
|
10126
|
+
*
|
10127
|
+
* DisconnectReason can be used to determine why the participant was disconnected. Notable reasons are
|
10128
|
+
* - DUPLICATE_IDENTITY: another client with the same identity has joined the room
|
10129
|
+
* - PARTICIPANT_REMOVED: participant was removed by RemoveParticipant API
|
10130
|
+
* - ROOM_DELETED: the room has ended via DeleteRoom API
|
10131
|
+
*
|
10132
|
+
* args: ([[DisconnectReason]])
|
10040
10133
|
*/
|
10041
10134
|
RoomEvent["Disconnected"] = "disconnected";
|
10042
10135
|
/**
|
@@ -10045,10 +10138,6 @@ var RoomEvent;
|
|
10045
10138
|
* args: ([[ConnectionState]])
|
10046
10139
|
*/
|
10047
10140
|
RoomEvent["ConnectionStateChanged"] = "connectionStateChanged";
|
10048
|
-
/**
|
10049
|
-
* @deprecated StateChanged has been renamed to ConnectionStateChanged
|
10050
|
-
*/
|
10051
|
-
RoomEvent["StateChanged"] = "connectionStateChanged";
|
10052
10141
|
/**
|
10053
10142
|
* When input or output devices on the machine have changed.
|
10054
10143
|
*/
|
@@ -10614,10 +10703,10 @@ function getMatch(exp, ua) {
|
|
10614
10703
|
return match && match.length >= id && match[id] || '';
|
10615
10704
|
}
|
10616
10705
|
|
10617
|
-
var version$1 = "
|
10706
|
+
var version$1 = "2.0.1";
|
10618
10707
|
|
10619
10708
|
const version = version$1;
|
10620
|
-
const protocolVersion =
|
10709
|
+
const protocolVersion = 12;
|
10621
10710
|
|
10622
10711
|
/**
|
10623
10712
|
* Timers that can be overridden with platform specific implementations
|
@@ -11783,7 +11872,7 @@ class UpdateTrackSettings extends Message {
|
|
11783
11872
|
*
|
11784
11873
|
* @generated from field: livekit.VideoQuality quality = 4;
|
11785
11874
|
*/
|
11786
|
-
this.quality = VideoQuality.LOW;
|
11875
|
+
this.quality = VideoQuality$1.LOW;
|
11787
11876
|
/**
|
11788
11877
|
* for video, width to receive
|
11789
11878
|
*
|
@@ -11844,7 +11933,7 @@ UpdateTrackSettings.fields = proto3.util.newFieldList(() => [{
|
|
11844
11933
|
no: 4,
|
11845
11934
|
name: "quality",
|
11846
11935
|
kind: "enum",
|
11847
|
-
T: proto3.getEnumType(VideoQuality)
|
11936
|
+
T: proto3.getEnumType(VideoQuality$1)
|
11848
11937
|
}, {
|
11849
11938
|
no: 5,
|
11850
11939
|
name: "width",
|
@@ -11875,6 +11964,7 @@ class LeaveRequest extends Message {
|
|
11875
11964
|
/**
|
11876
11965
|
* sent when server initiates the disconnect due to server-restart
|
11877
11966
|
* indicates clients should attempt full-reconnect sequence
|
11967
|
+
* NOTE: `can_reconnect` obsoleted by `action` starting in protocol version 13
|
11878
11968
|
*
|
11879
11969
|
* @generated from field: bool can_reconnect = 1;
|
11880
11970
|
*/
|
@@ -11883,6 +11973,10 @@ class LeaveRequest extends Message {
|
|
11883
11973
|
* @generated from field: livekit.DisconnectReason reason = 2;
|
11884
11974
|
*/
|
11885
11975
|
this.reason = DisconnectReason.UNKNOWN_REASON;
|
11976
|
+
/**
|
11977
|
+
* @generated from field: livekit.LeaveRequest.Action action = 3;
|
11978
|
+
*/
|
11979
|
+
this.action = LeaveRequest_Action.DISCONNECT;
|
11886
11980
|
proto3.util.initPartial(data, this);
|
11887
11981
|
}
|
11888
11982
|
static fromBinary(bytes, options) {
|
@@ -11910,6 +12004,53 @@ LeaveRequest.fields = proto3.util.newFieldList(() => [{
|
|
11910
12004
|
name: "reason",
|
11911
12005
|
kind: "enum",
|
11912
12006
|
T: proto3.getEnumType(DisconnectReason)
|
12007
|
+
}, {
|
12008
|
+
no: 3,
|
12009
|
+
name: "action",
|
12010
|
+
kind: "enum",
|
12011
|
+
T: proto3.getEnumType(LeaveRequest_Action)
|
12012
|
+
}, {
|
12013
|
+
no: 4,
|
12014
|
+
name: "regions",
|
12015
|
+
kind: "message",
|
12016
|
+
T: RegionSettings
|
12017
|
+
}]);
|
12018
|
+
/**
|
12019
|
+
* indicates action clients should take on receiving this message
|
12020
|
+
*
|
12021
|
+
* @generated from enum livekit.LeaveRequest.Action
|
12022
|
+
*/
|
12023
|
+
var LeaveRequest_Action;
|
12024
|
+
(function (LeaveRequest_Action) {
|
12025
|
+
/**
|
12026
|
+
* should disconnect
|
12027
|
+
*
|
12028
|
+
* @generated from enum value: DISCONNECT = 0;
|
12029
|
+
*/
|
12030
|
+
LeaveRequest_Action[LeaveRequest_Action["DISCONNECT"] = 0] = "DISCONNECT";
|
12031
|
+
/**
|
12032
|
+
* should attempt a resume with `reconnect=1` in join URL
|
12033
|
+
*
|
12034
|
+
* @generated from enum value: RESUME = 1;
|
12035
|
+
*/
|
12036
|
+
LeaveRequest_Action[LeaveRequest_Action["RESUME"] = 1] = "RESUME";
|
12037
|
+
/**
|
12038
|
+
* should attempt a reconnect, i. e. no `reconnect=1`
|
12039
|
+
*
|
12040
|
+
* @generated from enum value: RECONNECT = 2;
|
12041
|
+
*/
|
12042
|
+
LeaveRequest_Action[LeaveRequest_Action["RECONNECT"] = 2] = "RECONNECT";
|
12043
|
+
})(LeaveRequest_Action || (LeaveRequest_Action = {}));
|
12044
|
+
// Retrieve enum metadata with: proto3.getEnumType(LeaveRequest_Action)
|
12045
|
+
proto3.util.setEnumType(LeaveRequest_Action, "livekit.LeaveRequest.Action", [{
|
12046
|
+
no: 0,
|
12047
|
+
name: "DISCONNECT"
|
12048
|
+
}, {
|
12049
|
+
no: 1,
|
12050
|
+
name: "RESUME"
|
12051
|
+
}, {
|
12052
|
+
no: 2,
|
12053
|
+
name: "RECONNECT"
|
11913
12054
|
}]);
|
11914
12055
|
/**
|
11915
12056
|
* message to indicate published video track dimensions are changing
|
@@ -12292,7 +12433,7 @@ class SubscribedQuality extends Message {
|
|
12292
12433
|
/**
|
12293
12434
|
* @generated from field: livekit.VideoQuality quality = 1;
|
12294
12435
|
*/
|
12295
|
-
this.quality = VideoQuality.LOW;
|
12436
|
+
this.quality = VideoQuality$1.LOW;
|
12296
12437
|
/**
|
12297
12438
|
* @generated from field: bool enabled = 2;
|
12298
12439
|
*/
|
@@ -12318,7 +12459,7 @@ SubscribedQuality.fields = proto3.util.newFieldList(() => [{
|
|
12318
12459
|
no: 1,
|
12319
12460
|
name: "quality",
|
12320
12461
|
kind: "enum",
|
12321
|
-
T: proto3.getEnumType(VideoQuality)
|
12462
|
+
T: proto3.getEnumType(VideoQuality$1)
|
12322
12463
|
}, {
|
12323
12464
|
no: 2,
|
12324
12465
|
name: "enabled",
|
@@ -12592,6 +12733,10 @@ class SyncState extends Message {
|
|
12592
12733
|
* @generated from field: repeated livekit.DataChannelInfo data_channels = 4;
|
12593
12734
|
*/
|
12594
12735
|
this.dataChannels = [];
|
12736
|
+
/**
|
12737
|
+
* @generated from field: repeated string track_sids_disabled = 6;
|
12738
|
+
*/
|
12739
|
+
this.trackSidsDisabled = [];
|
12595
12740
|
proto3.util.initPartial(data, this);
|
12596
12741
|
}
|
12597
12742
|
static fromBinary(bytes, options) {
|
@@ -12636,6 +12781,12 @@ SyncState.fields = proto3.util.newFieldList(() => [{
|
|
12636
12781
|
name: "offer",
|
12637
12782
|
kind: "message",
|
12638
12783
|
T: SessionDescription
|
12784
|
+
}, {
|
12785
|
+
no: 6,
|
12786
|
+
name: "track_sids_disabled",
|
12787
|
+
kind: "scalar",
|
12788
|
+
T: 9 /* ScalarType.STRING */,
|
12789
|
+
repeated: true
|
12639
12790
|
}]);
|
12640
12791
|
/**
|
12641
12792
|
* @generated from message livekit.DataChannelInfo
|
@@ -12997,6 +13148,12 @@ const BACKGROUND_REACTION_DELAY = 5000;
|
|
12997
13148
|
// keep old audio elements when detached, we would re-use them since on iOS
|
12998
13149
|
// Safari tracks which audio elements have been "blessed" by the user.
|
12999
13150
|
const recycledElements = [];
|
13151
|
+
var VideoQuality;
|
13152
|
+
(function (VideoQuality) {
|
13153
|
+
VideoQuality[VideoQuality["LOW"] = 0] = "LOW";
|
13154
|
+
VideoQuality[VideoQuality["MEDIUM"] = 1] = "MEDIUM";
|
13155
|
+
VideoQuality[VideoQuality["HIGH"] = 2] = "HIGH";
|
13156
|
+
})(VideoQuality || (VideoQuality = {}));
|
13000
13157
|
class Track extends eventsExports.EventEmitter {
|
13001
13158
|
constructor(mediaTrack, kind) {
|
13002
13159
|
let loggerOptions = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
|
@@ -13657,10 +13814,6 @@ function isFireFox() {
|
|
13657
13814
|
var _a;
|
13658
13815
|
return ((_a = getBrowser()) === null || _a === void 0 ? void 0 : _a.name) === 'Firefox';
|
13659
13816
|
}
|
13660
|
-
function isChromiumBased() {
|
13661
|
-
var _a;
|
13662
|
-
return ((_a = getBrowser()) === null || _a === void 0 ? void 0 : _a.name) === 'Chrome';
|
13663
|
-
}
|
13664
13817
|
function isSafari() {
|
13665
13818
|
var _a;
|
13666
13819
|
return ((_a = getBrowser()) === null || _a === void 0 ? void 0 : _a.name) === 'Safari';
|
@@ -14548,8 +14701,8 @@ class E2EEManager extends eventsExports.EventEmitter {
|
|
14548
14701
|
room.on(RoomEvent.TrackPublished, (pub, participant) => this.setParticipantCryptorEnabled(pub.trackInfo.encryption !== Encryption_Type.NONE, participant.identity));
|
14549
14702
|
room.on(RoomEvent.ConnectionStateChanged, state => {
|
14550
14703
|
if (state === ConnectionState.Connected) {
|
14551
|
-
room.
|
14552
|
-
participant.
|
14704
|
+
room.remoteParticipants.forEach(participant => {
|
14705
|
+
participant.trackPublications.forEach(pub => {
|
14553
14706
|
this.setParticipantCryptorEnabled(pub.trackInfo.encryption !== Encryption_Type.NONE, participant.identity);
|
14554
14707
|
});
|
14555
14708
|
});
|
@@ -15025,6 +15178,8 @@ class SignalClient {
|
|
15025
15178
|
}
|
15026
15179
|
this.log.warn("websocket closed", Object.assign(Object.assign({}, this.logContext), {
|
15027
15180
|
reason: ev.reason,
|
15181
|
+
code: ev.code,
|
15182
|
+
wasClean: ev.wasClean,
|
15028
15183
|
state: this.state
|
15029
15184
|
}));
|
15030
15185
|
this.handleOnClose(ev.reason);
|
@@ -15441,9 +15596,6 @@ function createConnectionParams(token, info, opts) {
|
|
15441
15596
|
if (info.browserVersion) {
|
15442
15597
|
params.set('browser_version', info.browserVersion);
|
15443
15598
|
}
|
15444
|
-
if (opts.publishOnly !== undefined) {
|
15445
|
-
params.set('publish', opts.publishOnly);
|
15446
|
-
}
|
15447
15599
|
if (opts.adaptiveStream) {
|
15448
15600
|
params.set('adaptive_stream', '1');
|
15449
15601
|
}
|
@@ -16145,8 +16297,7 @@ class PCTransport extends eventsExports.EventEmitter {
|
|
16145
16297
|
return this._pc;
|
16146
16298
|
}
|
16147
16299
|
constructor(config) {
|
16148
|
-
let
|
16149
|
-
let loggerOptions = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
|
16300
|
+
let loggerOptions = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
16150
16301
|
var _a;
|
16151
16302
|
super();
|
16152
16303
|
this.log = livekitLogger;
|
@@ -16190,13 +16341,10 @@ class PCTransport extends eventsExports.EventEmitter {
|
|
16190
16341
|
this.log = getLogger((_a = loggerOptions.loggerName) !== null && _a !== void 0 ? _a : LoggerNames.PCTransport);
|
16191
16342
|
this.loggerOptions = loggerOptions;
|
16192
16343
|
this.config = config;
|
16193
|
-
this.mediaConstraints = mediaConstraints;
|
16194
16344
|
this._pc = this.createPC();
|
16195
16345
|
}
|
16196
16346
|
createPC() {
|
16197
|
-
const pc =
|
16198
|
-
// @ts-expect-error chrome allows additional media constraints to be passed into the RTCPeerConnection constructor
|
16199
|
-
new RTCPeerConnection(this.config, this.mediaConstraints) : new RTCPeerConnection(this.config);
|
16347
|
+
const pc = new RTCPeerConnection(this.config);
|
16200
16348
|
pc.onicecandidate = ev => {
|
16201
16349
|
var _a;
|
16202
16350
|
if (!ev.candidate) return;
|
@@ -16657,10 +16805,6 @@ function extractStereoAndNackAudioFromOffer(offer) {
|
|
16657
16805
|
|
16658
16806
|
const defaultVideoCodec = 'vp8';
|
16659
16807
|
const publishDefaults = {
|
16660
|
-
/**
|
16661
|
-
* @deprecated
|
16662
|
-
*/
|
16663
|
-
audioBitrate: AudioPresets.music.maxBitrate,
|
16664
16808
|
audioPreset: AudioPresets.music,
|
16665
16809
|
dtx: true,
|
16666
16810
|
red: true,
|
@@ -16685,7 +16829,7 @@ const roomOptionDefaults = {
|
|
16685
16829
|
stopLocalTrackOnUnpublish: true,
|
16686
16830
|
reconnectPolicy: new DefaultReconnectPolicy(),
|
16687
16831
|
disconnectOnPageLeave: true,
|
16688
|
-
|
16832
|
+
webAudioMix: true
|
16689
16833
|
};
|
16690
16834
|
const roomConnectOptionDefaults = {
|
16691
16835
|
autoSubscribe: true,
|
@@ -16743,13 +16887,8 @@ class PCTransportManager {
|
|
16743
16887
|
this.loggerOptions = loggerOptions;
|
16744
16888
|
this.isPublisherConnectionRequired = !subscriberPrimary;
|
16745
16889
|
this.isSubscriberConnectionRequired = subscriberPrimary;
|
16746
|
-
|
16747
|
-
|
16748
|
-
googDscp: true
|
16749
|
-
}]
|
16750
|
-
};
|
16751
|
-
this.publisher = new PCTransport(rtcConfig, googConstraints, loggerOptions);
|
16752
|
-
this.subscriber = new PCTransport(rtcConfig, undefined, loggerOptions);
|
16890
|
+
this.publisher = new PCTransport(rtcConfig, loggerOptions);
|
16891
|
+
this.subscriber = new PCTransport(rtcConfig, loggerOptions);
|
16753
16892
|
this.publisher.onConnectionStateChange = this.updateState;
|
16754
16893
|
this.subscriber.onConnectionStateChange = this.updateState;
|
16755
16894
|
this.publisher.onIceConnectionStateChange = this.updateState;
|
@@ -16989,6 +17128,9 @@ class RTCEngine extends eventsExports.EventEmitter {
|
|
16989
17128
|
get isClosed() {
|
16990
17129
|
return this._isClosed;
|
16991
17130
|
}
|
17131
|
+
get pendingReconnect() {
|
17132
|
+
return !!this.reconnectTimeout;
|
17133
|
+
}
|
16992
17134
|
constructor(options) {
|
16993
17135
|
var _a;
|
16994
17136
|
super();
|
@@ -17113,7 +17255,7 @@ class RTCEngine extends eventsExports.EventEmitter {
|
|
17113
17255
|
// since the current engine may have inherited a regional url
|
17114
17256
|
this.regionUrlProvider.updateToken(this.token);
|
17115
17257
|
}
|
17116
|
-
this.reconnectTimeout = CriticalTimers.setTimeout(() => this.attemptReconnect(disconnectReason), delay);
|
17258
|
+
this.reconnectTimeout = CriticalTimers.setTimeout(() => this.attemptReconnect(disconnectReason).finally(() => this.reconnectTimeout = undefined), delay);
|
17117
17259
|
};
|
17118
17260
|
this.waitForRestarted = () => {
|
17119
17261
|
return new Promise((resolve, reject) => {
|
@@ -17984,10 +18126,14 @@ class RTCEngine extends eventsExports.EventEmitter {
|
|
17984
18126
|
*/
|
17985
18127
|
const autoSubscribe = (_b = (_a = this.signalOpts) === null || _a === void 0 ? void 0 : _a.autoSubscribe) !== null && _b !== void 0 ? _b : true;
|
17986
18128
|
const trackSids = new Array();
|
18129
|
+
const trackSidsDisabled = new Array();
|
17987
18130
|
remoteTracks.forEach(track => {
|
17988
18131
|
if (track.isDesired !== autoSubscribe) {
|
17989
18132
|
trackSids.push(track.trackSid);
|
17990
18133
|
}
|
18134
|
+
if (!track.isEnabled) {
|
18135
|
+
trackSidsDisabled.push(track.trackSid);
|
18136
|
+
}
|
17991
18137
|
});
|
17992
18138
|
this.client.sendSyncState(new SyncState({
|
17993
18139
|
answer: previousAnswer ? toProtoSessionDescription({
|
@@ -18004,7 +18150,8 @@ class RTCEngine extends eventsExports.EventEmitter {
|
|
18004
18150
|
participantTracks: []
|
18005
18151
|
}),
|
18006
18152
|
publishTracks: getTrackPublicationInfo(localTracks),
|
18007
|
-
dataChannels: this.dataChannelsInfo()
|
18153
|
+
dataChannels: this.dataChannelsInfo(),
|
18154
|
+
trackSidsDisabled
|
18008
18155
|
}));
|
18009
18156
|
}
|
18010
18157
|
/* @internal */
|
@@ -18958,7 +19105,8 @@ class LocalVideoTrack extends LocalTrack {
|
|
18958
19105
|
}
|
18959
19106
|
addSimulcastTrack(codec, encodings) {
|
18960
19107
|
if (this.simulcastCodecs.has(codec)) {
|
18961
|
-
|
19108
|
+
this.log.error("".concat(codec, " already added, skipping adding simulcast codec"), this.logContext);
|
19109
|
+
return;
|
18962
19110
|
}
|
18963
19111
|
const simulcastCodecInfo = {
|
18964
19112
|
codec,
|
@@ -19987,7 +20135,7 @@ class Participant extends eventsExports.EventEmitter {
|
|
19987
20135
|
});
|
19988
20136
|
}
|
19989
20137
|
get isEncrypted() {
|
19990
|
-
return this.
|
20138
|
+
return this.trackPublications.size > 0 && Array.from(this.trackPublications.values()).every(tr => tr.isEncrypted);
|
19991
20139
|
}
|
19992
20140
|
get isAgent() {
|
19993
20141
|
var _a, _b;
|
@@ -20010,21 +20158,19 @@ class Participant extends eventsExports.EventEmitter {
|
|
20010
20158
|
this.identity = identity;
|
20011
20159
|
this.name = name;
|
20012
20160
|
this.metadata = metadata;
|
20013
|
-
this.
|
20014
|
-
this.
|
20015
|
-
this.
|
20161
|
+
this.audioTrackPublications = new Map();
|
20162
|
+
this.videoTrackPublications = new Map();
|
20163
|
+
this.trackPublications = new Map();
|
20016
20164
|
}
|
20017
|
-
|
20018
|
-
return Array.from(this.
|
20165
|
+
getTrackPublications() {
|
20166
|
+
return Array.from(this.trackPublications.values());
|
20019
20167
|
}
|
20020
20168
|
/**
|
20021
20169
|
* Finds the first track that matches the source filter, for example, getting
|
20022
20170
|
* the user's camera track with getTrackBySource(Track.Source.Camera).
|
20023
|
-
* @param source
|
20024
|
-
* @returns
|
20025
20171
|
*/
|
20026
|
-
|
20027
|
-
for (const [, pub] of this.
|
20172
|
+
getTrackPublication(source) {
|
20173
|
+
for (const [, pub] of this.trackPublications) {
|
20028
20174
|
if (pub.source === source) {
|
20029
20175
|
return pub;
|
20030
20176
|
}
|
@@ -20032,11 +20178,9 @@ class Participant extends eventsExports.EventEmitter {
|
|
20032
20178
|
}
|
20033
20179
|
/**
|
20034
20180
|
* Finds the first track that matches the track's name.
|
20035
|
-
* @param name
|
20036
|
-
* @returns
|
20037
20181
|
*/
|
20038
|
-
|
20039
|
-
for (const [, pub] of this.
|
20182
|
+
getTrackPublicationByName(name) {
|
20183
|
+
for (const [, pub] of this.trackPublications) {
|
20040
20184
|
if (pub.trackName === name) {
|
20041
20185
|
return pub;
|
20042
20186
|
}
|
@@ -20047,16 +20191,16 @@ class Participant extends eventsExports.EventEmitter {
|
|
20047
20191
|
}
|
20048
20192
|
get isCameraEnabled() {
|
20049
20193
|
var _a;
|
20050
|
-
const track = this.
|
20194
|
+
const track = this.getTrackPublication(Track.Source.Camera);
|
20051
20195
|
return !((_a = track === null || track === void 0 ? void 0 : track.isMuted) !== null && _a !== void 0 ? _a : true);
|
20052
20196
|
}
|
20053
20197
|
get isMicrophoneEnabled() {
|
20054
20198
|
var _a;
|
20055
|
-
const track = this.
|
20199
|
+
const track = this.getTrackPublication(Track.Source.Microphone);
|
20056
20200
|
return !((_a = track === null || track === void 0 ? void 0 : track.isMuted) !== null && _a !== void 0 ? _a : true);
|
20057
20201
|
}
|
20058
20202
|
get isScreenShareEnabled() {
|
20059
|
-
const track = this.
|
20203
|
+
const track = this.getTrackPublication(Track.Source.ScreenShare);
|
20060
20204
|
return !!track;
|
20061
20205
|
}
|
20062
20206
|
get isLocal() {
|
@@ -20150,7 +20294,7 @@ class Participant extends eventsExports.EventEmitter {
|
|
20150
20294
|
*/
|
20151
20295
|
setAudioContext(ctx) {
|
20152
20296
|
this.audioContext = ctx;
|
20153
|
-
this.
|
20297
|
+
this.audioTrackPublications.forEach(track => (track.track instanceof RemoteAudioTrack || track.track instanceof LocalAudioTrack) && track.track.setAudioContext(ctx));
|
20154
20298
|
}
|
20155
20299
|
addTrackPublication(publication) {
|
20156
20300
|
// forward publication driven events
|
@@ -20164,13 +20308,13 @@ class Participant extends eventsExports.EventEmitter {
|
|
20164
20308
|
if (pub.track) {
|
20165
20309
|
pub.track.sid = publication.trackSid;
|
20166
20310
|
}
|
20167
|
-
this.
|
20311
|
+
this.trackPublications.set(publication.trackSid, publication);
|
20168
20312
|
switch (publication.kind) {
|
20169
20313
|
case Track.Kind.Audio:
|
20170
|
-
this.
|
20314
|
+
this.audioTrackPublications.set(publication.trackSid, publication);
|
20171
20315
|
break;
|
20172
20316
|
case Track.Kind.Video:
|
20173
|
-
this.
|
20317
|
+
this.videoTrackPublications.set(publication.trackSid, publication);
|
20174
20318
|
break;
|
20175
20319
|
}
|
20176
20320
|
}
|
@@ -20189,1541 +20333,1545 @@ function trackPermissionToProto(perms) {
|
|
20189
20333
|
});
|
20190
20334
|
}
|
20191
20335
|
|
20192
|
-
class
|
20193
|
-
|
20194
|
-
|
20195
|
-
|
20336
|
+
class LocalParticipant extends Participant {
|
20337
|
+
/** @internal */
|
20338
|
+
constructor(sid, identity, engine, options) {
|
20339
|
+
super(sid, identity, undefined, undefined, {
|
20340
|
+
loggerName: options.loggerName,
|
20341
|
+
loggerContextCb: () => this.engine.logContext
|
20342
|
+
});
|
20343
|
+
this.pendingPublishing = new Set();
|
20344
|
+
this.pendingPublishPromises = new Map();
|
20345
|
+
this.participantTrackPermissions = [];
|
20346
|
+
this.allParticipantsAllowedToSubscribe = true;
|
20347
|
+
this.encryptionType = Encryption_Type.NONE;
|
20348
|
+
this.handleReconnecting = () => {
|
20349
|
+
if (!this.reconnectFuture) {
|
20350
|
+
this.reconnectFuture = new Future();
|
20351
|
+
}
|
20352
|
+
};
|
20353
|
+
this.handleReconnected = () => {
|
20354
|
+
var _a, _b;
|
20355
|
+
(_b = (_a = this.reconnectFuture) === null || _a === void 0 ? void 0 : _a.resolve) === null || _b === void 0 ? void 0 : _b.call(_a);
|
20356
|
+
this.reconnectFuture = undefined;
|
20357
|
+
this.updateTrackSubscriptionPermissions();
|
20358
|
+
};
|
20359
|
+
this.handleDisconnected = () => {
|
20360
|
+
var _a, _b;
|
20361
|
+
if (this.reconnectFuture) {
|
20362
|
+
this.reconnectFuture.promise.catch(e => this.log.warn(e.message, this.logContext));
|
20363
|
+
(_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');
|
20364
|
+
this.reconnectFuture = undefined;
|
20365
|
+
}
|
20366
|
+
};
|
20367
|
+
this.updateTrackSubscriptionPermissions = () => {
|
20368
|
+
this.log.debug('updating track subscription permissions', Object.assign(Object.assign({}, this.logContext), {
|
20369
|
+
allParticipantsAllowed: this.allParticipantsAllowedToSubscribe,
|
20370
|
+
participantTrackPermissions: this.participantTrackPermissions
|
20371
|
+
}));
|
20372
|
+
this.engine.client.sendUpdateSubscriptionPermissions(this.allParticipantsAllowedToSubscribe, this.participantTrackPermissions.map(p => trackPermissionToProto(p)));
|
20373
|
+
};
|
20196
20374
|
/** @internal */
|
20197
|
-
this.
|
20198
|
-
|
20199
|
-
this.currentVideoQuality = VideoQuality.HIGH;
|
20200
|
-
this.handleEnded = track => {
|
20201
|
-
this.setTrack(undefined);
|
20202
|
-
this.emit(TrackEvent.Ended, track);
|
20375
|
+
this.onTrackUnmuted = track => {
|
20376
|
+
this.onTrackMuted(track, track.isUpstreamPaused);
|
20203
20377
|
};
|
20204
|
-
|
20205
|
-
|
20206
|
-
|
20207
|
-
|
20378
|
+
// when the local track changes in mute status, we'll notify server as such
|
20379
|
+
/** @internal */
|
20380
|
+
this.onTrackMuted = (track, muted) => {
|
20381
|
+
if (muted === undefined) {
|
20382
|
+
muted = true;
|
20383
|
+
}
|
20384
|
+
if (!track.sid) {
|
20385
|
+
this.log.error('could not update mute status for unpublished track', Object.assign(Object.assign({}, this.logContext), getLogContextFromTrack(track)));
|
20386
|
+
return;
|
20387
|
+
}
|
20388
|
+
this.engine.updateMuteStatus(track.sid, muted);
|
20208
20389
|
};
|
20209
|
-
this.
|
20210
|
-
this.log.debug(
|
20211
|
-
this.
|
20212
|
-
this.emitTrackUpdate();
|
20390
|
+
this.onTrackUpstreamPaused = track => {
|
20391
|
+
this.log.debug('upstream paused', Object.assign(Object.assign({}, this.logContext), getLogContextFromTrack(track)));
|
20392
|
+
this.onTrackMuted(track, true);
|
20213
20393
|
};
|
20214
|
-
this.
|
20215
|
-
|
20216
|
-
|
20217
|
-
|
20218
|
-
|
20219
|
-
|
20220
|
-
|
20221
|
-
|
20222
|
-
|
20223
|
-
|
20224
|
-
|
20225
|
-
|
20226
|
-
|
20227
|
-
|
20228
|
-
|
20229
|
-
|
20230
|
-
|
20231
|
-
|
20232
|
-
|
20233
|
-
|
20234
|
-
|
20235
|
-
|
20236
|
-
|
20237
|
-
|
20238
|
-
|
20394
|
+
this.onTrackUpstreamResumed = track => {
|
20395
|
+
this.log.debug('upstream resumed', Object.assign(Object.assign({}, this.logContext), getLogContextFromTrack(track)));
|
20396
|
+
this.onTrackMuted(track, track.isMuted);
|
20397
|
+
};
|
20398
|
+
this.handleSubscribedQualityUpdate = update => __awaiter(this, void 0, void 0, function* () {
|
20399
|
+
var _a, e_1, _b, _c;
|
20400
|
+
var _d, _e;
|
20401
|
+
if (!((_d = this.roomOptions) === null || _d === void 0 ? void 0 : _d.dynacast)) {
|
20402
|
+
return;
|
20403
|
+
}
|
20404
|
+
const pub = this.videoTrackPublications.get(update.trackSid);
|
20405
|
+
if (!pub) {
|
20406
|
+
this.log.warn('received subscribed quality update for unknown track', Object.assign(Object.assign({}, this.logContext), {
|
20407
|
+
trackSid: update.trackSid
|
20408
|
+
}));
|
20409
|
+
return;
|
20410
|
+
}
|
20411
|
+
if (update.subscribedCodecs.length > 0) {
|
20412
|
+
if (!pub.videoTrack) {
|
20413
|
+
return;
|
20414
|
+
}
|
20415
|
+
const newCodecs = yield pub.videoTrack.setPublishingCodecs(update.subscribedCodecs);
|
20416
|
+
try {
|
20417
|
+
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) {
|
20418
|
+
_c = newCodecs_1_1.value;
|
20419
|
+
_f = false;
|
20420
|
+
const codec = _c;
|
20421
|
+
if (isBackupCodec(codec)) {
|
20422
|
+
this.log.debug("publish ".concat(codec, " for ").concat(pub.videoTrack.sid), Object.assign(Object.assign({}, this.logContext), getLogContextFromTrack(pub)));
|
20423
|
+
yield this.publishAdditionalCodecForTrack(pub.videoTrack, codec, pub.options);
|
20424
|
+
}
|
20425
|
+
}
|
20426
|
+
} catch (e_1_1) {
|
20427
|
+
e_1 = {
|
20428
|
+
error: e_1_1
|
20429
|
+
};
|
20430
|
+
} finally {
|
20431
|
+
try {
|
20432
|
+
if (!_f && !_a && (_b = newCodecs_1.return)) yield _b.call(newCodecs_1);
|
20433
|
+
} finally {
|
20434
|
+
if (e_1) throw e_1.error;
|
20435
|
+
}
|
20436
|
+
}
|
20437
|
+
} else if (update.subscribedQualities.length > 0) {
|
20438
|
+
yield (_e = pub.videoTrack) === null || _e === void 0 ? void 0 : _e.setPublishingLayers(update.subscribedQualities);
|
20439
|
+
}
|
20239
20440
|
});
|
20240
|
-
this.
|
20241
|
-
|
20242
|
-
|
20243
|
-
|
20244
|
-
|
20245
|
-
|
20246
|
-
|
20247
|
-
|
20248
|
-
|
20249
|
-
|
20441
|
+
this.handleLocalTrackUnpublished = unpublished => {
|
20442
|
+
const track = this.trackPublications.get(unpublished.trackSid);
|
20443
|
+
if (!track) {
|
20444
|
+
this.log.warn('received unpublished event for unknown track', Object.assign(Object.assign({}, this.logContext), {
|
20445
|
+
trackSid: unpublished.trackSid
|
20446
|
+
}));
|
20447
|
+
return;
|
20448
|
+
}
|
20449
|
+
this.unpublishTrack(track.track);
|
20450
|
+
};
|
20451
|
+
this.handleTrackEnded = track => __awaiter(this, void 0, void 0, function* () {
|
20452
|
+
if (track.source === Track.Source.ScreenShare || track.source === Track.Source.ScreenShareAudio) {
|
20453
|
+
this.log.debug('unpublishing local track due to TrackEnded', Object.assign(Object.assign({}, this.logContext), getLogContextFromTrack(track)));
|
20454
|
+
this.unpublishTrack(track);
|
20455
|
+
} else if (track.isUserProvided) {
|
20456
|
+
yield track.mute();
|
20457
|
+
} else if (track instanceof LocalAudioTrack || track instanceof LocalVideoTrack) {
|
20458
|
+
try {
|
20459
|
+
if (isWeb()) {
|
20460
|
+
try {
|
20461
|
+
const currentPermissions = yield navigator === null || navigator === void 0 ? void 0 : navigator.permissions.query({
|
20462
|
+
// the permission query for camera and microphone currently not supported in Safari and Firefox
|
20463
|
+
// @ts-ignore
|
20464
|
+
name: track.source === Track.Source.Camera ? 'camera' : 'microphone'
|
20465
|
+
});
|
20466
|
+
if (currentPermissions && currentPermissions.state === 'denied') {
|
20467
|
+
this.log.warn("user has revoked access to ".concat(track.source), Object.assign(Object.assign({}, this.logContext), getLogContextFromTrack(track)));
|
20468
|
+
// detect granted change after permissions were denied to try and resume then
|
20469
|
+
currentPermissions.onchange = () => {
|
20470
|
+
if (currentPermissions.state !== 'denied') {
|
20471
|
+
if (!track.isMuted) {
|
20472
|
+
track.restartTrack();
|
20473
|
+
}
|
20474
|
+
currentPermissions.onchange = null;
|
20475
|
+
}
|
20476
|
+
};
|
20477
|
+
throw new Error('GetUserMedia Permission denied');
|
20478
|
+
}
|
20479
|
+
} catch (e) {
|
20480
|
+
// permissions query fails for firefox, we continue and try to restart the track
|
20481
|
+
}
|
20482
|
+
}
|
20483
|
+
if (!track.isMuted) {
|
20484
|
+
this.log.debug('track ended, attempting to use a different device', Object.assign(Object.assign({}, this.logContext), getLogContextFromTrack(track)));
|
20485
|
+
yield track.restartTrack();
|
20486
|
+
}
|
20487
|
+
} catch (e) {
|
20488
|
+
this.log.warn("could not restart track, muting instead", Object.assign(Object.assign({}, this.logContext), getLogContextFromTrack(track)));
|
20489
|
+
yield track.mute();
|
20490
|
+
}
|
20491
|
+
}
|
20492
|
+
});
|
20493
|
+
this.audioTrackPublications = new Map();
|
20494
|
+
this.videoTrackPublications = new Map();
|
20495
|
+
this.trackPublications = new Map();
|
20496
|
+
this.engine = engine;
|
20497
|
+
this.roomOptions = options;
|
20498
|
+
this.setupEngine(engine);
|
20499
|
+
this.activeDeviceMap = new Map();
|
20500
|
+
}
|
20501
|
+
get lastCameraError() {
|
20502
|
+
return this.cameraError;
|
20503
|
+
}
|
20504
|
+
get lastMicrophoneError() {
|
20505
|
+
return this.microphoneError;
|
20506
|
+
}
|
20507
|
+
get isE2EEEnabled() {
|
20508
|
+
return this.encryptionType !== Encryption_Type.NONE;
|
20509
|
+
}
|
20510
|
+
getTrackPublication(source) {
|
20511
|
+
const track = super.getTrackPublication(source);
|
20512
|
+
if (track) {
|
20513
|
+
return track;
|
20250
20514
|
}
|
20251
|
-
return TrackPublication.SubscriptionStatus.Subscribed;
|
20252
20515
|
}
|
20253
|
-
|
20254
|
-
|
20516
|
+
getTrackPublicationByName(name) {
|
20517
|
+
const track = super.getTrackPublicationByName(name);
|
20518
|
+
if (track) {
|
20519
|
+
return track;
|
20520
|
+
}
|
20255
20521
|
}
|
20256
20522
|
/**
|
20257
|
-
*
|
20523
|
+
* @internal
|
20258
20524
|
*/
|
20259
|
-
|
20260
|
-
|
20261
|
-
|
20262
|
-
|
20263
|
-
|
20264
|
-
|
20265
|
-
|
20266
|
-
|
20267
|
-
|
20525
|
+
setupEngine(engine) {
|
20526
|
+
this.engine = engine;
|
20527
|
+
this.engine.on(EngineEvent.RemoteMute, (trackSid, muted) => {
|
20528
|
+
const pub = this.trackPublications.get(trackSid);
|
20529
|
+
if (!pub || !pub.track) {
|
20530
|
+
return;
|
20531
|
+
}
|
20532
|
+
if (muted) {
|
20533
|
+
pub.mute();
|
20534
|
+
} else {
|
20535
|
+
pub.unmute();
|
20536
|
+
}
|
20537
|
+
});
|
20538
|
+
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);
|
20268
20539
|
}
|
20269
|
-
|
20270
|
-
|
20540
|
+
/**
|
20541
|
+
* Sets and updates the metadata of the local participant.
|
20542
|
+
* The change does not take immediate effect.
|
20543
|
+
* If successful, a `ParticipantEvent.MetadataChanged` event will be emitted on the local participant.
|
20544
|
+
* Note: this requires `canUpdateOwnMetadata` permission.
|
20545
|
+
* @param metadata
|
20546
|
+
*/
|
20547
|
+
setMetadata(metadata) {
|
20548
|
+
var _a;
|
20549
|
+
this.engine.client.sendUpdateLocalMetadata(metadata, (_a = this.name) !== null && _a !== void 0 ? _a : '');
|
20271
20550
|
}
|
20272
20551
|
/**
|
20273
|
-
*
|
20274
|
-
*
|
20275
|
-
*
|
20276
|
-
*
|
20552
|
+
* Sets and updates the name of the local participant.
|
20553
|
+
* The change does not take immediate effect.
|
20554
|
+
* If successful, a `ParticipantEvent.ParticipantNameChanged` event will be emitted on the local participant.
|
20555
|
+
* Note: this requires `canUpdateOwnMetadata` permission.
|
20556
|
+
* @param metadata
|
20277
20557
|
*/
|
20278
|
-
|
20279
|
-
|
20280
|
-
|
20281
|
-
}
|
20282
|
-
this.disabled = !enabled;
|
20283
|
-
this.emitTrackUpdate();
|
20558
|
+
setName(name) {
|
20559
|
+
var _a;
|
20560
|
+
this.engine.client.sendUpdateLocalMetadata((_a = this.metadata) !== null && _a !== void 0 ? _a : '', name);
|
20284
20561
|
}
|
20285
20562
|
/**
|
20286
|
-
*
|
20563
|
+
* Enable or disable a participant's camera track.
|
20287
20564
|
*
|
20288
|
-
*
|
20289
|
-
*
|
20290
|
-
* optimize for uninterrupted video
|
20565
|
+
* If a track has already published, it'll mute or unmute the track.
|
20566
|
+
* Resolves with a `LocalTrackPublication` instance if successful and `undefined` otherwise
|
20291
20567
|
*/
|
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();
|
20568
|
+
setCameraEnabled(enabled, options, publishOptions) {
|
20569
|
+
return this.setTrackEnabled(Track.Source.Camera, enabled, options, publishOptions);
|
20313
20570
|
}
|
20314
|
-
|
20315
|
-
|
20316
|
-
|
20317
|
-
|
20318
|
-
|
20319
|
-
|
20320
|
-
|
20321
|
-
|
20322
|
-
return;
|
20323
|
-
}
|
20324
|
-
this.fps = fps;
|
20325
|
-
this.emitTrackUpdate();
|
20571
|
+
/**
|
20572
|
+
* Enable or disable a participant's microphone track.
|
20573
|
+
*
|
20574
|
+
* If a track has already published, it'll mute or unmute the track.
|
20575
|
+
* Resolves with a `LocalTrackPublication` instance if successful and `undefined` otherwise
|
20576
|
+
*/
|
20577
|
+
setMicrophoneEnabled(enabled, options, publishOptions) {
|
20578
|
+
return this.setTrackEnabled(Track.Source.Microphone, enabled, options, publishOptions);
|
20326
20579
|
}
|
20327
|
-
|
20328
|
-
|
20580
|
+
/**
|
20581
|
+
* Start or stop sharing a participant's screen
|
20582
|
+
* Resolves with a `LocalTrackPublication` instance if successful and `undefined` otherwise
|
20583
|
+
*/
|
20584
|
+
setScreenShareEnabled(enabled, options, publishOptions) {
|
20585
|
+
return this.setTrackEnabled(Track.Source.ScreenShare, enabled, options, publishOptions);
|
20329
20586
|
}
|
20330
20587
|
/** @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);
|
20588
|
+
setPermissions(permissions) {
|
20589
|
+
const prevPermissions = this.permissions;
|
20590
|
+
const changed = super.setPermissions(permissions);
|
20591
|
+
if (changed && prevPermissions) {
|
20592
|
+
this.emit(ParticipantEvent.ParticipantPermissionsChanged, prevPermissions);
|
20354
20593
|
}
|
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);
|
20594
|
+
return changed;
|
20369
20595
|
}
|
20370
20596
|
/** @internal */
|
20371
|
-
|
20372
|
-
|
20373
|
-
|
20374
|
-
|
20375
|
-
|
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);
|
20597
|
+
setE2EEEnabled(enabled) {
|
20598
|
+
return __awaiter(this, void 0, void 0, function* () {
|
20599
|
+
this.encryptionType = enabled ? Encryption_Type.GCM : Encryption_Type.NONE;
|
20600
|
+
yield this.republishAllTracks(undefined, false);
|
20601
|
+
});
|
20387
20602
|
}
|
20388
|
-
|
20389
|
-
|
20390
|
-
|
20391
|
-
this.
|
20392
|
-
|
20393
|
-
|
20394
|
-
|
20395
|
-
|
20396
|
-
|
20397
|
-
|
20398
|
-
|
20399
|
-
|
20400
|
-
|
20401
|
-
|
20402
|
-
|
20403
|
-
|
20404
|
-
|
20405
|
-
|
20406
|
-
|
20407
|
-
|
20408
|
-
|
20409
|
-
|
20410
|
-
|
20411
|
-
|
20412
|
-
|
20413
|
-
|
20414
|
-
|
20415
|
-
|
20416
|
-
|
20417
|
-
|
20418
|
-
|
20419
|
-
|
20420
|
-
|
20421
|
-
|
20422
|
-
|
20423
|
-
|
20424
|
-
|
20425
|
-
|
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
|
-
publication.on(TrackEvent.Subscribed, track => {
|
20462
|
-
this.emit(ParticipantEvent.TrackSubscribed, track, publication);
|
20463
|
-
});
|
20464
|
-
publication.on(TrackEvent.Unsubscribed, previousTrack => {
|
20465
|
-
this.emit(ParticipantEvent.TrackUnsubscribed, previousTrack, publication);
|
20466
|
-
});
|
20467
|
-
publication.on(TrackEvent.SubscriptionFailed, error => {
|
20468
|
-
this.emit(ParticipantEvent.TrackSubscriptionFailed, publication.trackSid, error);
|
20469
|
-
});
|
20470
|
-
}
|
20471
|
-
getTrack(source) {
|
20472
|
-
const track = super.getTrack(source);
|
20473
|
-
if (track) {
|
20474
|
-
return track;
|
20475
|
-
}
|
20476
|
-
}
|
20477
|
-
getTrackByName(name) {
|
20478
|
-
const track = super.getTrackByName(name);
|
20479
|
-
if (track) {
|
20603
|
+
setTrackEnabled(source, enabled, options, publishOptions) {
|
20604
|
+
var _a, _b;
|
20605
|
+
return __awaiter(this, void 0, void 0, function* () {
|
20606
|
+
this.log.debug('setTrackEnabled', Object.assign(Object.assign({}, this.logContext), {
|
20607
|
+
source,
|
20608
|
+
enabled
|
20609
|
+
}));
|
20610
|
+
let track = this.getTrackPublication(source);
|
20611
|
+
if (enabled) {
|
20612
|
+
if (track) {
|
20613
|
+
yield track.unmute();
|
20614
|
+
} else {
|
20615
|
+
let localTracks;
|
20616
|
+
if (this.pendingPublishing.has(source)) {
|
20617
|
+
this.log.info('skipping duplicate published source', Object.assign(Object.assign({}, this.logContext), {
|
20618
|
+
source
|
20619
|
+
}));
|
20620
|
+
// no-op it's already been requested
|
20621
|
+
return;
|
20622
|
+
}
|
20623
|
+
this.pendingPublishing.add(source);
|
20624
|
+
try {
|
20625
|
+
switch (source) {
|
20626
|
+
case Track.Source.Camera:
|
20627
|
+
localTracks = yield this.createTracks({
|
20628
|
+
video: (_a = options) !== null && _a !== void 0 ? _a : true
|
20629
|
+
});
|
20630
|
+
break;
|
20631
|
+
case Track.Source.Microphone:
|
20632
|
+
localTracks = yield this.createTracks({
|
20633
|
+
audio: (_b = options) !== null && _b !== void 0 ? _b : true
|
20634
|
+
});
|
20635
|
+
break;
|
20636
|
+
case Track.Source.ScreenShare:
|
20637
|
+
localTracks = yield this.createScreenTracks(Object.assign({}, options));
|
20638
|
+
break;
|
20639
|
+
default:
|
20640
|
+
throw new TrackInvalidError(source);
|
20641
|
+
}
|
20642
|
+
const publishPromises = [];
|
20643
|
+
for (const localTrack of localTracks) {
|
20644
|
+
this.log.info('publishing track', Object.assign(Object.assign({}, this.logContext), getLogContextFromTrack(localTrack)));
|
20645
|
+
publishPromises.push(this.publishTrack(localTrack, publishOptions));
|
20646
|
+
}
|
20647
|
+
const publishedTracks = yield Promise.all(publishPromises);
|
20648
|
+
// for screen share publications including audio, this will only return the screen share publication, not the screen share audio one
|
20649
|
+
// revisit if we want to return an array of tracks instead for v2
|
20650
|
+
[track] = publishedTracks;
|
20651
|
+
} catch (e) {
|
20652
|
+
localTracks === null || localTracks === void 0 ? void 0 : localTracks.forEach(tr => {
|
20653
|
+
tr.stop();
|
20654
|
+
});
|
20655
|
+
if (e instanceof Error && !(e instanceof TrackInvalidError)) {
|
20656
|
+
this.emit(ParticipantEvent.MediaDevicesError, e);
|
20657
|
+
}
|
20658
|
+
throw e;
|
20659
|
+
} finally {
|
20660
|
+
this.pendingPublishing.delete(source);
|
20661
|
+
}
|
20662
|
+
}
|
20663
|
+
} else if (track && track.track) {
|
20664
|
+
// screenshare cannot be muted, unpublish instead
|
20665
|
+
if (source === Track.Source.ScreenShare) {
|
20666
|
+
track = yield this.unpublishTrack(track.track);
|
20667
|
+
const screenAudioTrack = this.getTrackPublication(Track.Source.ScreenShareAudio);
|
20668
|
+
if (screenAudioTrack && screenAudioTrack.track) {
|
20669
|
+
this.unpublishTrack(screenAudioTrack.track);
|
20670
|
+
}
|
20671
|
+
} else {
|
20672
|
+
yield track.mute();
|
20673
|
+
}
|
20674
|
+
}
|
20480
20675
|
return track;
|
20481
|
-
}
|
20676
|
+
});
|
20482
20677
|
}
|
20483
20678
|
/**
|
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
|
20679
|
+
* Publish both camera and microphone at the same time. This is useful for
|
20680
|
+
* displaying a single Permission Dialog box to the end user.
|
20488
20681
|
*/
|
20489
|
-
|
20490
|
-
|
20491
|
-
|
20492
|
-
|
20493
|
-
|
20494
|
-
|
20495
|
-
|
20682
|
+
enableCameraAndMicrophone() {
|
20683
|
+
return __awaiter(this, void 0, void 0, function* () {
|
20684
|
+
if (this.pendingPublishing.has(Track.Source.Camera) || this.pendingPublishing.has(Track.Source.Microphone)) {
|
20685
|
+
// no-op it's already been requested
|
20686
|
+
return;
|
20687
|
+
}
|
20688
|
+
this.pendingPublishing.add(Track.Source.Camera);
|
20689
|
+
this.pendingPublishing.add(Track.Source.Microphone);
|
20690
|
+
try {
|
20691
|
+
const tracks = yield this.createTracks({
|
20692
|
+
audio: true,
|
20693
|
+
video: true
|
20694
|
+
});
|
20695
|
+
yield Promise.all(tracks.map(track => this.publishTrack(track)));
|
20696
|
+
} finally {
|
20697
|
+
this.pendingPublishing.delete(Track.Source.Camera);
|
20698
|
+
this.pendingPublishing.delete(Track.Source.Microphone);
|
20699
|
+
}
|
20700
|
+
});
|
20496
20701
|
}
|
20497
20702
|
/**
|
20498
|
-
*
|
20703
|
+
* Create local camera and/or microphone tracks
|
20704
|
+
* @param options
|
20705
|
+
* @returns
|
20499
20706
|
*/
|
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;
|
20707
|
+
createTracks(options) {
|
20708
|
+
var _a, _b;
|
20709
|
+
return __awaiter(this, void 0, void 0, function* () {
|
20710
|
+
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);
|
20711
|
+
const constraints = constraintsForOptions(opts);
|
20712
|
+
let stream;
|
20713
|
+
try {
|
20714
|
+
stream = yield navigator.mediaDevices.getUserMedia(constraints);
|
20715
|
+
} catch (err) {
|
20716
|
+
if (err instanceof Error) {
|
20717
|
+
if (constraints.audio) {
|
20718
|
+
this.microphoneError = err;
|
20521
20719
|
}
|
20522
|
-
|
20720
|
+
if (constraints.video) {
|
20721
|
+
this.cameraError = err;
|
20722
|
+
}
|
20723
|
+
}
|
20724
|
+
throw err;
|
20523
20725
|
}
|
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;
|
20726
|
+
if (constraints.audio) {
|
20727
|
+
this.microphoneError = undefined;
|
20728
|
+
this.emit(ParticipantEvent.AudioStreamAcquired);
|
20534
20729
|
}
|
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;
|
20730
|
+
if (constraints.video) {
|
20731
|
+
this.cameraError = undefined;
|
20732
|
+
}
|
20733
|
+
return stream.getTracks().map(mediaStreamTrack => {
|
20734
|
+
const isAudio = mediaStreamTrack.kind === 'audio';
|
20735
|
+
isAudio ? options.audio : options.video;
|
20736
|
+
let trackConstraints;
|
20737
|
+
const conOrBool = isAudio ? constraints.audio : constraints.video;
|
20738
|
+
if (typeof conOrBool !== 'boolean') {
|
20739
|
+
trackConstraints = conOrBool;
|
20592
20740
|
}
|
20593
|
-
|
20594
|
-
|
20595
|
-
|
20741
|
+
const track = mediaTrackToLocalTrack(mediaStreamTrack, trackConstraints, {
|
20742
|
+
loggerName: this.roomOptions.loggerName,
|
20743
|
+
loggerContextCb: () => this.logContext
|
20596
20744
|
});
|
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
|
-
}));
|
20745
|
+
if (track.kind === Track.Kind.Video) {
|
20746
|
+
track.source = Track.Source.Camera;
|
20747
|
+
} else if (track.kind === Track.Kind.Audio) {
|
20748
|
+
track.source = Track.Source.Microphone;
|
20605
20749
|
}
|
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);
|
20750
|
+
track.mediaStream = stream;
|
20751
|
+
return track;
|
20752
|
+
});
|
20622
20753
|
});
|
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
20754
|
}
|
20654
20755
|
/**
|
20655
|
-
*
|
20756
|
+
* Creates a screen capture tracks with getDisplayMedia().
|
20757
|
+
* A LocalVideoTrack is always created and returned.
|
20758
|
+
* If { audio: true }, and the browser supports audio capture, a LocalAudioTrack is also created.
|
20656
20759
|
*/
|
20657
|
-
|
20760
|
+
createScreenTracks(options) {
|
20658
20761
|
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();
|
20762
|
+
if (options === undefined) {
|
20763
|
+
options = {};
|
20698
20764
|
}
|
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;
|
20765
|
+
if (navigator.mediaDevices.getDisplayMedia === undefined) {
|
20766
|
+
throw new DeviceUnsupportedError('getDisplayMedia not supported');
|
20712
20767
|
}
|
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;
|
20768
|
+
if (options.resolution === undefined && !isSafari17()) {
|
20769
|
+
// we need to constrain the dimensions, otherwise it could lead to low bitrate
|
20770
|
+
// due to encoding a huge video. Encoding such large surfaces is really expensive
|
20771
|
+
// unfortunately Safari 17 has a but and cannot be constrained by default
|
20772
|
+
options.resolution = ScreenSharePresets.h1080fps30.resolution;
|
20730
20773
|
}
|
20731
|
-
|
20732
|
-
|
20733
|
-
|
20774
|
+
const constraints = screenCaptureToDisplayMediaStreamOptions(options);
|
20775
|
+
const stream = yield navigator.mediaDevices.getDisplayMedia(constraints);
|
20776
|
+
const tracks = stream.getVideoTracks();
|
20777
|
+
if (tracks.length === 0) {
|
20778
|
+
throw new TrackInvalidError('no video track found');
|
20734
20779
|
}
|
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;
|
20780
|
+
const screenVideo = new LocalVideoTrack(tracks[0], undefined, false, {
|
20781
|
+
loggerName: this.roomOptions.loggerName,
|
20782
|
+
loggerContextCb: () => this.logContext
|
20783
|
+
});
|
20784
|
+
screenVideo.source = Track.Source.ScreenShare;
|
20785
|
+
if (options.contentHint) {
|
20786
|
+
screenVideo.mediaStreamTrack.contentHint = options.contentHint;
|
20750
20787
|
}
|
20751
|
-
const
|
20752
|
-
if (
|
20753
|
-
this.
|
20754
|
-
|
20755
|
-
|
20756
|
-
|
20757
|
-
|
20758
|
-
|
20759
|
-
|
20760
|
-
return;
|
20761
|
-
}
|
20762
|
-
const newCodecs = yield pub.videoTrack.setPublishingCodecs(update.subscribedCodecs);
|
20763
|
-
try {
|
20764
|
-
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) {
|
20765
|
-
_c = newCodecs_1_1.value;
|
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
|
-
}
|
20788
|
+
const localTracks = [screenVideo];
|
20789
|
+
if (stream.getAudioTracks().length > 0) {
|
20790
|
+
this.emit(ParticipantEvent.AudioStreamAcquired);
|
20791
|
+
const screenAudio = new LocalAudioTrack(stream.getAudioTracks()[0], undefined, false, this.audioContext, {
|
20792
|
+
loggerName: this.roomOptions.loggerName,
|
20793
|
+
loggerContextCb: () => this.logContext
|
20794
|
+
});
|
20795
|
+
screenAudio.source = Track.Source.ScreenShareAudio;
|
20796
|
+
localTracks.push(screenAudio);
|
20838
20797
|
}
|
20798
|
+
return localTracks;
|
20839
20799
|
});
|
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
20800
|
}
|
20869
20801
|
/**
|
20870
|
-
*
|
20802
|
+
* Publish a new track to the room
|
20803
|
+
* @param track
|
20804
|
+
* @param options
|
20871
20805
|
*/
|
20872
|
-
|
20873
|
-
|
20874
|
-
this
|
20875
|
-
|
20876
|
-
if (
|
20877
|
-
|
20806
|
+
publishTrack(track, options) {
|
20807
|
+
var _a, _b, _c, _d;
|
20808
|
+
return __awaiter(this, void 0, void 0, function* () {
|
20809
|
+
yield (_a = this.reconnectFuture) === null || _a === void 0 ? void 0 : _a.promise;
|
20810
|
+
if (track instanceof LocalTrack && this.pendingPublishPromises.has(track)) {
|
20811
|
+
yield this.pendingPublishPromises.get(track);
|
20878
20812
|
}
|
20879
|
-
|
20880
|
-
|
20813
|
+
let defaultConstraints;
|
20814
|
+
if (track instanceof MediaStreamTrack) {
|
20815
|
+
defaultConstraints = track.getConstraints();
|
20881
20816
|
} else {
|
20882
|
-
|
20817
|
+
// we want to access constraints directly as `track.mediaStreamTrack`
|
20818
|
+
// might be pointing to a non-device track (e.g. processed track) already
|
20819
|
+
defaultConstraints = track.constraints;
|
20820
|
+
let deviceKind = undefined;
|
20821
|
+
switch (track.source) {
|
20822
|
+
case Track.Source.Microphone:
|
20823
|
+
deviceKind = 'audioinput';
|
20824
|
+
break;
|
20825
|
+
case Track.Source.Camera:
|
20826
|
+
deviceKind = 'videoinput';
|
20827
|
+
}
|
20828
|
+
if (deviceKind && this.activeDeviceMap.has(deviceKind)) {
|
20829
|
+
defaultConstraints = Object.assign(Object.assign({}, defaultConstraints), {
|
20830
|
+
deviceId: this.activeDeviceMap.get(deviceKind)
|
20831
|
+
});
|
20832
|
+
}
|
20833
|
+
}
|
20834
|
+
// convert raw media track into audio or video track
|
20835
|
+
if (track instanceof MediaStreamTrack) {
|
20836
|
+
switch (track.kind) {
|
20837
|
+
case 'audio':
|
20838
|
+
track = new LocalAudioTrack(track, defaultConstraints, true, this.audioContext, {
|
20839
|
+
loggerName: this.roomOptions.loggerName,
|
20840
|
+
loggerContextCb: () => this.logContext
|
20841
|
+
});
|
20842
|
+
break;
|
20843
|
+
case 'video':
|
20844
|
+
track = new LocalVideoTrack(track, defaultConstraints, true, {
|
20845
|
+
loggerName: this.roomOptions.loggerName,
|
20846
|
+
loggerContextCb: () => this.logContext
|
20847
|
+
});
|
20848
|
+
break;
|
20849
|
+
default:
|
20850
|
+
throw new TrackInvalidError("unsupported MediaStreamTrack kind ".concat(track.kind));
|
20851
|
+
}
|
20852
|
+
} else {
|
20853
|
+
track.updateLoggerOptions({
|
20854
|
+
loggerName: this.roomOptions.loggerName,
|
20855
|
+
loggerContextCb: () => this.logContext
|
20856
|
+
});
|
20857
|
+
}
|
20858
|
+
if (track instanceof LocalAudioTrack) {
|
20859
|
+
track.setAudioContext(this.audioContext);
|
20860
|
+
}
|
20861
|
+
// is it already published? if so skip
|
20862
|
+
let existingPublication;
|
20863
|
+
this.trackPublications.forEach(publication => {
|
20864
|
+
if (!publication.track) {
|
20865
|
+
return;
|
20866
|
+
}
|
20867
|
+
if (publication.track === track) {
|
20868
|
+
existingPublication = publication;
|
20869
|
+
}
|
20870
|
+
});
|
20871
|
+
if (existingPublication) {
|
20872
|
+
this.log.warn('track has already been published, skipping', Object.assign(Object.assign({}, this.logContext), getLogContextFromTrack(existingPublication)));
|
20873
|
+
return existingPublication;
|
20874
|
+
}
|
20875
|
+
const isStereoInput = 'channelCount' in track.mediaStreamTrack.getSettings() &&
|
20876
|
+
// @ts-ignore `channelCount` on getSettings() is currently only available for Safari, but is generally the best way to determine a stereo track https://developer.mozilla.org/en-US/docs/Web/API/MediaTrackSettings/channelCount
|
20877
|
+
track.mediaStreamTrack.getSettings().channelCount === 2 || track.mediaStreamTrack.getConstraints().channelCount === 2;
|
20878
|
+
const isStereo = (_b = options === null || options === void 0 ? void 0 : options.forceStereo) !== null && _b !== void 0 ? _b : isStereoInput;
|
20879
|
+
// disable dtx for stereo track if not enabled explicitly
|
20880
|
+
if (isStereo) {
|
20881
|
+
if (!options) {
|
20882
|
+
options = {};
|
20883
|
+
}
|
20884
|
+
if (options.dtx === undefined) {
|
20885
|
+
this.log.info("Opus DTX will be disabled for stereo tracks by default. Enable them explicitly to make it work.", Object.assign(Object.assign({}, this.logContext), getLogContextFromTrack(track)));
|
20886
|
+
}
|
20887
|
+
if (options.red === undefined) {
|
20888
|
+
this.log.info("Opus RED will be disabled for stereo tracks by default. Enable them explicitly to make it work.");
|
20889
|
+
}
|
20890
|
+
(_c = options.dtx) !== null && _c !== void 0 ? _c : options.dtx = false;
|
20891
|
+
(_d = options.red) !== null && _d !== void 0 ? _d : options.red = false;
|
20892
|
+
}
|
20893
|
+
const opts = Object.assign(Object.assign({}, this.roomOptions.publishDefaults), options);
|
20894
|
+
// disable simulcast if e2ee is set on safari
|
20895
|
+
if (isSafari() && this.roomOptions.e2ee) {
|
20896
|
+
this.log.info("End-to-end encryption is set up, simulcast publishing will be disabled on Safari", Object.assign({}, this.logContext));
|
20897
|
+
opts.simulcast = false;
|
20898
|
+
}
|
20899
|
+
if (opts.source) {
|
20900
|
+
track.source = opts.source;
|
20901
|
+
}
|
20902
|
+
const publishPromise = this.publish(track, opts, isStereo);
|
20903
|
+
this.pendingPublishPromises.set(track, publishPromise);
|
20904
|
+
try {
|
20905
|
+
const publication = yield publishPromise;
|
20906
|
+
return publication;
|
20907
|
+
} catch (e) {
|
20908
|
+
throw e;
|
20909
|
+
} finally {
|
20910
|
+
this.pendingPublishPromises.delete(track);
|
20883
20911
|
}
|
20884
20912
|
});
|
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
20913
|
}
|
20943
|
-
|
20944
|
-
|
20914
|
+
publish(track, opts, isStereo) {
|
20915
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
|
20945
20916
|
return __awaiter(this, void 0, void 0, function* () {
|
20946
|
-
this.
|
20947
|
-
|
20948
|
-
|
20949
|
-
|
20950
|
-
|
20951
|
-
|
20952
|
-
|
20953
|
-
|
20954
|
-
|
20955
|
-
enabled
|
20956
|
-
|
20957
|
-
|
20958
|
-
|
20959
|
-
|
20960
|
-
|
20961
|
-
|
20962
|
-
|
20963
|
-
|
20964
|
-
|
20965
|
-
|
20966
|
-
|
20967
|
-
|
20968
|
-
|
20969
|
-
|
20970
|
-
|
20971
|
-
|
20972
|
-
|
20973
|
-
|
20974
|
-
|
20975
|
-
|
20976
|
-
|
20977
|
-
|
20978
|
-
|
20979
|
-
|
20980
|
-
|
20981
|
-
|
20982
|
-
|
20983
|
-
|
20984
|
-
|
20985
|
-
|
20986
|
-
|
20987
|
-
|
20988
|
-
|
20989
|
-
|
20990
|
-
|
20991
|
-
|
20992
|
-
|
20917
|
+
const existingTrackOfSource = Array.from(this.trackPublications.values()).find(publishedTrack => track instanceof LocalTrack && publishedTrack.source === track.source);
|
20918
|
+
if (existingTrackOfSource && track.source !== Track.Source.Unknown) {
|
20919
|
+
this.log.info("publishing a second track with the same source: ".concat(track.source), Object.assign(Object.assign({}, this.logContext), getLogContextFromTrack(track)));
|
20920
|
+
}
|
20921
|
+
if (opts.stopMicTrackOnMute && track instanceof LocalAudioTrack) {
|
20922
|
+
track.stopOnMute = true;
|
20923
|
+
}
|
20924
|
+
if (track.source === Track.Source.ScreenShare && isFireFox()) {
|
20925
|
+
// Firefox does not work well with simulcasted screen share
|
20926
|
+
// we frequently get no data on layer 0 when enabled
|
20927
|
+
opts.simulcast = false;
|
20928
|
+
}
|
20929
|
+
// require full AV1/VP9 SVC support prior to using it
|
20930
|
+
if (opts.videoCodec === 'av1' && !supportsAV1()) {
|
20931
|
+
opts.videoCodec = undefined;
|
20932
|
+
}
|
20933
|
+
if (opts.videoCodec === 'vp9' && !supportsVP9()) {
|
20934
|
+
opts.videoCodec = undefined;
|
20935
|
+
}
|
20936
|
+
if (opts.videoCodec === undefined) {
|
20937
|
+
opts.videoCodec = defaultVideoCodec;
|
20938
|
+
}
|
20939
|
+
const videoCodec = opts.videoCodec;
|
20940
|
+
// handle track actions
|
20941
|
+
track.on(TrackEvent.Muted, this.onTrackMuted);
|
20942
|
+
track.on(TrackEvent.Unmuted, this.onTrackUnmuted);
|
20943
|
+
track.on(TrackEvent.Ended, this.handleTrackEnded);
|
20944
|
+
track.on(TrackEvent.UpstreamPaused, this.onTrackUpstreamPaused);
|
20945
|
+
track.on(TrackEvent.UpstreamResumed, this.onTrackUpstreamResumed);
|
20946
|
+
// create track publication from track
|
20947
|
+
const req = new AddTrackRequest({
|
20948
|
+
// get local track id for use during publishing
|
20949
|
+
cid: track.mediaStreamTrack.id,
|
20950
|
+
name: opts.name,
|
20951
|
+
type: Track.kindToProto(track.kind),
|
20952
|
+
muted: track.isMuted,
|
20953
|
+
source: Track.sourceToProto(track.source),
|
20954
|
+
disableDtx: !((_a = opts.dtx) !== null && _a !== void 0 ? _a : true),
|
20955
|
+
encryption: this.encryptionType,
|
20956
|
+
stereo: isStereo,
|
20957
|
+
disableRed: this.isE2EEEnabled || !((_b = opts.red) !== null && _b !== void 0 ? _b : true),
|
20958
|
+
stream: opts === null || opts === void 0 ? void 0 : opts.stream
|
20959
|
+
});
|
20960
|
+
// compute encodings and layers for video
|
20961
|
+
let encodings;
|
20962
|
+
if (track.kind === Track.Kind.Video) {
|
20963
|
+
let dims = {
|
20964
|
+
width: 0,
|
20965
|
+
height: 0
|
20966
|
+
};
|
20967
|
+
try {
|
20968
|
+
dims = yield track.waitForDimensions();
|
20969
|
+
} catch (e) {
|
20970
|
+
// use defaults, it's quite painful for congestion control without simulcast
|
20971
|
+
// so using default dims according to publish settings
|
20972
|
+
const defaultRes = (_d = (_c = this.roomOptions.videoCaptureDefaults) === null || _c === void 0 ? void 0 : _c.resolution) !== null && _d !== void 0 ? _d : VideoPresets.h720.resolution;
|
20973
|
+
dims = {
|
20974
|
+
width: defaultRes.width,
|
20975
|
+
height: defaultRes.height
|
20976
|
+
};
|
20977
|
+
// log failure
|
20978
|
+
this.log.error('could not determine track dimensions, using defaults', Object.assign(Object.assign(Object.assign({}, this.logContext), getLogContextFromTrack(track)), {
|
20979
|
+
dims
|
20980
|
+
}));
|
20981
|
+
}
|
20982
|
+
// width and height should be defined for video
|
20983
|
+
req.width = dims.width;
|
20984
|
+
req.height = dims.height;
|
20985
|
+
// for svc codecs, disable simulcast and use vp8 for backup codec
|
20986
|
+
if (track instanceof LocalVideoTrack) {
|
20987
|
+
if (isSVCCodec(videoCodec)) {
|
20988
|
+
// vp9 svc with screenshare has problem to encode, always use L1T3 here
|
20989
|
+
if (track.source === Track.Source.ScreenShare && videoCodec === 'vp9') {
|
20990
|
+
opts.scalabilityMode = 'L1T3';
|
20993
20991
|
}
|
20994
|
-
|
20995
|
-
|
20996
|
-
|
20997
|
-
|
20998
|
-
|
20999
|
-
|
21000
|
-
|
21001
|
-
|
21002
|
-
|
21003
|
-
|
20992
|
+
// set scalabilityMode to 'L3T3_KEY' by default
|
20993
|
+
opts.scalabilityMode = (_e = opts.scalabilityMode) !== null && _e !== void 0 ? _e : 'L3T3_KEY';
|
20994
|
+
}
|
20995
|
+
req.simulcastCodecs = [new SimulcastCodec({
|
20996
|
+
codec: videoCodec,
|
20997
|
+
cid: track.mediaStreamTrack.id
|
20998
|
+
})];
|
20999
|
+
// set up backup
|
21000
|
+
if (opts.backupCodec === true) {
|
21001
|
+
opts.backupCodec = {
|
21002
|
+
codec: defaultVideoCodec
|
21003
|
+
};
|
21004
|
+
}
|
21005
|
+
if (opts.backupCodec && videoCodec !== opts.backupCodec.codec &&
|
21006
|
+
// TODO remove this once e2ee is supported for backup codecs
|
21007
|
+
req.encryption === Encryption_Type.NONE) {
|
21008
|
+
// multi-codec simulcast requires dynacast
|
21009
|
+
if (!this.roomOptions.dynacast) {
|
21010
|
+
this.roomOptions.dynacast = true;
|
21004
21011
|
}
|
21005
|
-
|
21006
|
-
|
21007
|
-
|
21012
|
+
req.simulcastCodecs.push(new SimulcastCodec({
|
21013
|
+
codec: opts.backupCodec.codec,
|
21014
|
+
cid: ''
|
21015
|
+
}));
|
21008
21016
|
}
|
21009
21017
|
}
|
21010
|
-
|
21011
|
-
|
21012
|
-
|
21013
|
-
|
21014
|
-
|
21015
|
-
|
21016
|
-
|
21018
|
+
encodings = computeVideoEncodings(track.source === Track.Source.ScreenShare, req.width, req.height, opts);
|
21019
|
+
req.layers = videoLayersFromEncodings(req.width, req.height, encodings, isSVCCodec(opts.videoCodec));
|
21020
|
+
} else if (track.kind === Track.Kind.Audio) {
|
21021
|
+
encodings = [{
|
21022
|
+
maxBitrate: (_f = opts.audioPreset) === null || _f === void 0 ? void 0 : _f.maxBitrate,
|
21023
|
+
priority: (_h = (_g = opts.audioPreset) === null || _g === void 0 ? void 0 : _g.priority) !== null && _h !== void 0 ? _h : 'high',
|
21024
|
+
networkPriority: (_k = (_j = opts.audioPreset) === null || _j === void 0 ? void 0 : _j.priority) !== null && _k !== void 0 ? _k : 'high'
|
21025
|
+
}];
|
21026
|
+
}
|
21027
|
+
if (!this.engine || this.engine.isClosed) {
|
21028
|
+
throw new UnexpectedConnectionState('cannot publish track when not connected');
|
21029
|
+
}
|
21030
|
+
const ti = yield this.engine.addTrack(req);
|
21031
|
+
// server might not support the codec the client has requested, in that case, fallback
|
21032
|
+
// to a supported codec
|
21033
|
+
let primaryCodecMime;
|
21034
|
+
ti.codecs.forEach(codec => {
|
21035
|
+
if (primaryCodecMime === undefined) {
|
21036
|
+
primaryCodecMime = codec.mimeType;
|
21037
|
+
}
|
21038
|
+
});
|
21039
|
+
if (primaryCodecMime && track.kind === Track.Kind.Video) {
|
21040
|
+
const updatedCodec = mimeTypeToVideoCodecString(primaryCodecMime);
|
21041
|
+
if (updatedCodec !== videoCodec) {
|
21042
|
+
this.log.debug('falling back to server selected codec', Object.assign(Object.assign(Object.assign({}, this.logContext), getLogContextFromTrack(track)), {
|
21043
|
+
codec: updatedCodec
|
21044
|
+
}));
|
21045
|
+
/* @ts-ignore */
|
21046
|
+
opts.videoCodec = updatedCodec;
|
21047
|
+
// recompute encodings since bitrates/etc could have changed
|
21048
|
+
encodings = computeVideoEncodings(track.source === Track.Source.ScreenShare, req.width, req.height, opts);
|
21049
|
+
}
|
21050
|
+
}
|
21051
|
+
const publication = new LocalTrackPublication(track.kind, ti, track, {
|
21052
|
+
loggerName: this.roomOptions.loggerName,
|
21053
|
+
loggerContextCb: () => this.logContext
|
21054
|
+
});
|
21055
|
+
// save options for when it needs to be republished again
|
21056
|
+
publication.options = opts;
|
21057
|
+
track.sid = ti.sid;
|
21058
|
+
if (!this.engine.pcManager) {
|
21059
|
+
throw new UnexpectedConnectionState('pcManager is not ready');
|
21060
|
+
}
|
21061
|
+
this.log.debug("publishing ".concat(track.kind, " with encodings"), Object.assign(Object.assign({}, this.logContext), {
|
21062
|
+
encodings,
|
21063
|
+
trackInfo: ti
|
21064
|
+
}));
|
21065
|
+
track.sender = yield this.engine.createSender(track, opts, encodings);
|
21066
|
+
if (encodings) {
|
21067
|
+
if (isFireFox() && track.kind === Track.Kind.Audio) {
|
21068
|
+
/* Refer to RFC https://datatracker.ietf.org/doc/html/rfc7587#section-6.1,
|
21069
|
+
livekit-server uses maxaveragebitrate=510000 in the answer sdp to permit client to
|
21070
|
+
publish high quality audio track. But firefox always uses this value as the actual
|
21071
|
+
bitrates, causing the audio bitrates to rise to 510Kbps in any stereo case unexpectedly.
|
21072
|
+
So the client need to modify maxaverragebitrates in answer sdp to user provided value to
|
21073
|
+
fix the issue.
|
21074
|
+
*/
|
21075
|
+
let trackTransceiver = undefined;
|
21076
|
+
for (const transceiver of this.engine.pcManager.publisher.getTransceivers()) {
|
21077
|
+
if (transceiver.sender === track.sender) {
|
21078
|
+
trackTransceiver = transceiver;
|
21079
|
+
break;
|
21080
|
+
}
|
21017
21081
|
}
|
21018
|
-
|
21019
|
-
|
21082
|
+
if (trackTransceiver) {
|
21083
|
+
this.engine.pcManager.publisher.setTrackCodecBitrate({
|
21084
|
+
transceiver: trackTransceiver,
|
21085
|
+
codec: 'opus',
|
21086
|
+
maxbr: ((_l = encodings[0]) === null || _l === void 0 ? void 0 : _l.maxBitrate) ? encodings[0].maxBitrate / 1000 : 0
|
21087
|
+
});
|
21088
|
+
}
|
21089
|
+
} else if (track.codec && isSVCCodec(track.codec) && ((_m = encodings[0]) === null || _m === void 0 ? void 0 : _m.maxBitrate)) {
|
21090
|
+
this.engine.pcManager.publisher.setTrackCodecBitrate({
|
21091
|
+
cid: req.cid,
|
21092
|
+
codec: track.codec,
|
21093
|
+
maxbr: encodings[0].maxBitrate / 1000
|
21094
|
+
});
|
21020
21095
|
}
|
21021
21096
|
}
|
21022
|
-
|
21097
|
+
yield this.engine.negotiate();
|
21098
|
+
if (track instanceof LocalVideoTrack) {
|
21099
|
+
track.startMonitor(this.engine.client);
|
21100
|
+
} else if (track instanceof LocalAudioTrack) {
|
21101
|
+
track.startMonitor();
|
21102
|
+
}
|
21103
|
+
this.addTrackPublication(publication);
|
21104
|
+
// send event for publication
|
21105
|
+
this.emit(ParticipantEvent.LocalTrackPublished, publication);
|
21106
|
+
return publication;
|
21023
21107
|
});
|
21024
21108
|
}
|
21025
|
-
|
21026
|
-
|
21027
|
-
|
21109
|
+
get isLocal() {
|
21110
|
+
return true;
|
21111
|
+
}
|
21112
|
+
/** @internal
|
21113
|
+
* publish additional codec to existing track
|
21028
21114
|
*/
|
21029
|
-
|
21115
|
+
publishAdditionalCodecForTrack(track, videoCodec, options) {
|
21116
|
+
var _a;
|
21030
21117
|
return __awaiter(this, void 0, void 0, function* () {
|
21031
|
-
|
21032
|
-
|
21118
|
+
// TODO remove once e2ee is supported for backup tracks
|
21119
|
+
if (this.encryptionType !== Encryption_Type.NONE) {
|
21033
21120
|
return;
|
21034
21121
|
}
|
21035
|
-
|
21036
|
-
|
21037
|
-
|
21038
|
-
|
21039
|
-
|
21040
|
-
|
21041
|
-
|
21042
|
-
|
21043
|
-
|
21044
|
-
|
21045
|
-
|
21122
|
+
// is it not published? if so skip
|
21123
|
+
let existingPublication;
|
21124
|
+
this.trackPublications.forEach(publication => {
|
21125
|
+
if (!publication.track) {
|
21126
|
+
return;
|
21127
|
+
}
|
21128
|
+
if (publication.track === track) {
|
21129
|
+
existingPublication = publication;
|
21130
|
+
}
|
21131
|
+
});
|
21132
|
+
if (!existingPublication) {
|
21133
|
+
throw new TrackInvalidError('track is not published');
|
21134
|
+
}
|
21135
|
+
if (!(track instanceof LocalVideoTrack)) {
|
21136
|
+
throw new TrackInvalidError('track is not a video track');
|
21137
|
+
}
|
21138
|
+
const opts = Object.assign(Object.assign({}, (_a = this.roomOptions) === null || _a === void 0 ? void 0 : _a.publishDefaults), options);
|
21139
|
+
const encodings = computeTrackBackupEncodings(track, videoCodec, opts);
|
21140
|
+
if (!encodings) {
|
21141
|
+
this.log.info("backup codec has been disabled, ignoring request to add additional codec for track", Object.assign(Object.assign({}, this.logContext), getLogContextFromTrack(track)));
|
21142
|
+
return;
|
21143
|
+
}
|
21144
|
+
const simulcastTrack = track.addSimulcastTrack(videoCodec, encodings);
|
21145
|
+
if (!simulcastTrack) {
|
21146
|
+
return;
|
21147
|
+
}
|
21148
|
+
const req = new AddTrackRequest({
|
21149
|
+
cid: simulcastTrack.mediaStreamTrack.id,
|
21150
|
+
type: Track.kindToProto(track.kind),
|
21151
|
+
muted: track.isMuted,
|
21152
|
+
source: Track.sourceToProto(track.source),
|
21153
|
+
sid: track.sid,
|
21154
|
+
simulcastCodecs: [{
|
21155
|
+
codec: opts.videoCodec,
|
21156
|
+
cid: simulcastTrack.mediaStreamTrack.id
|
21157
|
+
}]
|
21158
|
+
});
|
21159
|
+
req.layers = videoLayersFromEncodings(req.width, req.height, encodings);
|
21160
|
+
if (!this.engine || this.engine.isClosed) {
|
21161
|
+
throw new UnexpectedConnectionState('cannot publish track when not connected');
|
21046
21162
|
}
|
21163
|
+
const ti = yield this.engine.addTrack(req);
|
21164
|
+
yield this.engine.createSimulcastSender(track, simulcastTrack, opts, encodings);
|
21165
|
+
yield this.engine.negotiate();
|
21166
|
+
this.log.debug("published ".concat(videoCodec, " for track ").concat(track.sid), Object.assign(Object.assign({}, this.logContext), {
|
21167
|
+
encodings,
|
21168
|
+
trackInfo: ti
|
21169
|
+
}));
|
21047
21170
|
});
|
21048
21171
|
}
|
21049
|
-
|
21050
|
-
* Create local camera and/or microphone tracks
|
21051
|
-
* @param options
|
21052
|
-
* @returns
|
21053
|
-
*/
|
21054
|
-
createTracks(options) {
|
21172
|
+
unpublishTrack(track, stopOnUnpublish) {
|
21055
21173
|
var _a, _b;
|
21056
21174
|
return __awaiter(this, void 0, void 0, function* () {
|
21057
|
-
|
21058
|
-
const
|
21059
|
-
|
21060
|
-
|
21061
|
-
|
21062
|
-
|
21063
|
-
|
21064
|
-
|
21065
|
-
|
21175
|
+
// look through all published tracks to find the right ones
|
21176
|
+
const publication = this.getPublicationForTrack(track);
|
21177
|
+
const pubLogContext = publication ? getLogContextFromTrack(publication) : undefined;
|
21178
|
+
this.log.debug('unpublishing track', Object.assign(Object.assign({}, this.logContext), pubLogContext));
|
21179
|
+
if (!publication || !publication.track) {
|
21180
|
+
this.log.warn('track was not unpublished because no publication was found', Object.assign(Object.assign({}, this.logContext), pubLogContext));
|
21181
|
+
return undefined;
|
21182
|
+
}
|
21183
|
+
track = publication.track;
|
21184
|
+
track.off(TrackEvent.Muted, this.onTrackMuted);
|
21185
|
+
track.off(TrackEvent.Unmuted, this.onTrackUnmuted);
|
21186
|
+
track.off(TrackEvent.Ended, this.handleTrackEnded);
|
21187
|
+
track.off(TrackEvent.UpstreamPaused, this.onTrackUpstreamPaused);
|
21188
|
+
track.off(TrackEvent.UpstreamResumed, this.onTrackUpstreamResumed);
|
21189
|
+
if (stopOnUnpublish === undefined) {
|
21190
|
+
stopOnUnpublish = (_b = (_a = this.roomOptions) === null || _a === void 0 ? void 0 : _a.stopLocalTrackOnUnpublish) !== null && _b !== void 0 ? _b : true;
|
21191
|
+
}
|
21192
|
+
if (stopOnUnpublish) {
|
21193
|
+
track.stop();
|
21194
|
+
}
|
21195
|
+
let negotiationNeeded = false;
|
21196
|
+
const trackSender = track.sender;
|
21197
|
+
track.sender = undefined;
|
21198
|
+
if (this.engine.pcManager && this.engine.pcManager.currentState < PCTransportState.FAILED && trackSender) {
|
21199
|
+
try {
|
21200
|
+
for (const transceiver of this.engine.pcManager.publisher.getTransceivers()) {
|
21201
|
+
// if sender is not currently sending (after replaceTrack(null))
|
21202
|
+
// removeTrack would have no effect.
|
21203
|
+
// to ensure we end up successfully removing the track, manually set
|
21204
|
+
// the transceiver to inactive
|
21205
|
+
if (transceiver.sender === trackSender) {
|
21206
|
+
transceiver.direction = 'inactive';
|
21207
|
+
negotiationNeeded = true;
|
21208
|
+
}
|
21066
21209
|
}
|
21067
|
-
if (
|
21068
|
-
|
21210
|
+
if (this.engine.removeTrack(trackSender)) {
|
21211
|
+
negotiationNeeded = true;
|
21212
|
+
}
|
21213
|
+
if (track instanceof LocalVideoTrack) {
|
21214
|
+
for (const [, trackInfo] of track.simulcastCodecs) {
|
21215
|
+
if (trackInfo.sender) {
|
21216
|
+
if (this.engine.removeTrack(trackInfo.sender)) {
|
21217
|
+
negotiationNeeded = true;
|
21218
|
+
}
|
21219
|
+
trackInfo.sender = undefined;
|
21220
|
+
}
|
21221
|
+
}
|
21222
|
+
track.simulcastCodecs.clear();
|
21069
21223
|
}
|
21224
|
+
} catch (e) {
|
21225
|
+
this.log.warn('failed to unpublish track', Object.assign(Object.assign(Object.assign({}, this.logContext), pubLogContext), {
|
21226
|
+
error: e
|
21227
|
+
}));
|
21070
21228
|
}
|
21071
|
-
throw err;
|
21072
21229
|
}
|
21073
|
-
|
21074
|
-
|
21075
|
-
|
21230
|
+
// remove from our maps
|
21231
|
+
this.trackPublications.delete(publication.trackSid);
|
21232
|
+
switch (publication.kind) {
|
21233
|
+
case Track.Kind.Audio:
|
21234
|
+
this.audioTrackPublications.delete(publication.trackSid);
|
21235
|
+
break;
|
21236
|
+
case Track.Kind.Video:
|
21237
|
+
this.videoTrackPublications.delete(publication.trackSid);
|
21238
|
+
break;
|
21076
21239
|
}
|
21077
|
-
|
21078
|
-
|
21240
|
+
this.emit(ParticipantEvent.LocalTrackUnpublished, publication);
|
21241
|
+
publication.setTrack(undefined);
|
21242
|
+
if (negotiationNeeded) {
|
21243
|
+
yield this.engine.negotiate();
|
21079
21244
|
}
|
21080
|
-
return
|
21081
|
-
|
21082
|
-
|
21083
|
-
|
21084
|
-
|
21085
|
-
|
21086
|
-
|
21087
|
-
|
21088
|
-
|
21089
|
-
|
21090
|
-
|
21091
|
-
|
21092
|
-
|
21093
|
-
|
21094
|
-
|
21095
|
-
|
21245
|
+
return publication;
|
21246
|
+
});
|
21247
|
+
}
|
21248
|
+
unpublishTracks(tracks) {
|
21249
|
+
return __awaiter(this, void 0, void 0, function* () {
|
21250
|
+
const results = yield Promise.all(tracks.map(track => this.unpublishTrack(track)));
|
21251
|
+
return results.filter(track => track instanceof LocalTrackPublication);
|
21252
|
+
});
|
21253
|
+
}
|
21254
|
+
republishAllTracks(options) {
|
21255
|
+
let restartTracks = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
|
21256
|
+
return __awaiter(this, void 0, void 0, function* () {
|
21257
|
+
const localPubs = [];
|
21258
|
+
this.trackPublications.forEach(pub => {
|
21259
|
+
if (pub.track) {
|
21260
|
+
if (options) {
|
21261
|
+
pub.options = Object.assign(Object.assign({}, pub.options), options);
|
21262
|
+
}
|
21263
|
+
localPubs.push(pub);
|
21096
21264
|
}
|
21097
|
-
track.mediaStream = stream;
|
21098
|
-
return track;
|
21099
21265
|
});
|
21266
|
+
yield Promise.all(localPubs.map(pub => __awaiter(this, void 0, void 0, function* () {
|
21267
|
+
const track = pub.track;
|
21268
|
+
yield this.unpublishTrack(track, false);
|
21269
|
+
if (restartTracks && !track.isMuted && track.source !== Track.Source.ScreenShare && track.source !== Track.Source.ScreenShareAudio && (track instanceof LocalAudioTrack || track instanceof LocalVideoTrack) && !track.isUserProvided) {
|
21270
|
+
// generally we need to restart the track before publishing, often a full reconnect
|
21271
|
+
// is necessary because computer had gone to sleep.
|
21272
|
+
this.log.debug('restarting existing track', Object.assign(Object.assign({}, this.logContext), {
|
21273
|
+
track: pub.trackSid
|
21274
|
+
}));
|
21275
|
+
yield track.restartTrack();
|
21276
|
+
}
|
21277
|
+
yield this.publishTrack(track, pub.options);
|
21278
|
+
})));
|
21100
21279
|
});
|
21101
21280
|
}
|
21102
21281
|
/**
|
21103
|
-
*
|
21104
|
-
*
|
21105
|
-
*
|
21282
|
+
* Publish a new data payload to the room. Data will be forwarded to each
|
21283
|
+
* participant in the room if the destination field in publishOptions is empty
|
21284
|
+
*
|
21285
|
+
* @param data Uint8Array of the payload. To send string data, use TextEncoder.encode
|
21286
|
+
* @param options optionally specify a `reliable`, `topic` and `destination`
|
21106
21287
|
*/
|
21107
|
-
|
21288
|
+
publishData(data) {
|
21289
|
+
let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
21108
21290
|
return __awaiter(this, void 0, void 0, function* () {
|
21109
|
-
|
21110
|
-
|
21111
|
-
|
21112
|
-
|
21113
|
-
|
21114
|
-
|
21115
|
-
|
21116
|
-
|
21117
|
-
|
21118
|
-
|
21119
|
-
|
21120
|
-
|
21121
|
-
|
21122
|
-
|
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
|
21291
|
+
const kind = options.reliable ? DataPacket_Kind.RELIABLE : DataPacket_Kind.LOSSY;
|
21292
|
+
const destinationIdentities = options.destinationIdentities;
|
21293
|
+
const topic = options.topic;
|
21294
|
+
const packet = new DataPacket({
|
21295
|
+
kind: kind,
|
21296
|
+
value: {
|
21297
|
+
case: 'user',
|
21298
|
+
value: new UserPacket({
|
21299
|
+
participantIdentity: this.identity,
|
21300
|
+
payload: data,
|
21301
|
+
destinationIdentities,
|
21302
|
+
topic
|
21303
|
+
})
|
21304
|
+
}
|
21130
21305
|
});
|
21131
|
-
|
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;
|
21306
|
+
yield this.engine.sendDataPacket(packet, kind);
|
21146
21307
|
});
|
21147
21308
|
}
|
21148
21309
|
/**
|
21149
|
-
*
|
21150
|
-
*
|
21151
|
-
*
|
21152
|
-
|
21153
|
-
|
21154
|
-
|
21155
|
-
|
21156
|
-
|
21157
|
-
|
21158
|
-
|
21159
|
-
|
21160
|
-
|
21161
|
-
|
21162
|
-
|
21163
|
-
|
21164
|
-
|
21165
|
-
|
21166
|
-
|
21167
|
-
|
21168
|
-
|
21169
|
-
|
21170
|
-
|
21171
|
-
|
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
|
-
});
|
21179
|
-
}
|
21180
|
-
}
|
21181
|
-
// convert raw media track into audio or video track
|
21182
|
-
if (track instanceof MediaStreamTrack) {
|
21183
|
-
switch (track.kind) {
|
21184
|
-
case 'audio':
|
21185
|
-
track = new LocalAudioTrack(track, defaultConstraints, true, this.audioContext, {
|
21186
|
-
loggerName: this.roomOptions.loggerName,
|
21187
|
-
loggerContextCb: () => this.logContext
|
21188
|
-
});
|
21189
|
-
break;
|
21190
|
-
case 'video':
|
21191
|
-
track = new LocalVideoTrack(track, defaultConstraints, true, {
|
21192
|
-
loggerName: this.roomOptions.loggerName,
|
21193
|
-
loggerContextCb: () => this.logContext
|
21194
|
-
});
|
21195
|
-
break;
|
21196
|
-
default:
|
21197
|
-
throw new TrackInvalidError("unsupported MediaStreamTrack kind ".concat(track.kind));
|
21198
|
-
}
|
21199
|
-
} else {
|
21200
|
-
track.updateLoggerOptions({
|
21201
|
-
loggerName: this.roomOptions.loggerName,
|
21202
|
-
loggerContextCb: () => this.logContext
|
21203
|
-
});
|
21204
|
-
}
|
21205
|
-
if (track instanceof LocalAudioTrack) {
|
21206
|
-
track.setAudioContext(this.audioContext);
|
21207
|
-
}
|
21208
|
-
// is it already published? if so skip
|
21209
|
-
let existingPublication;
|
21210
|
-
this.tracks.forEach(publication => {
|
21211
|
-
if (!publication.track) {
|
21212
|
-
return;
|
21213
|
-
}
|
21214
|
-
if (publication.track === track) {
|
21215
|
-
existingPublication = publication;
|
21216
|
-
}
|
21217
|
-
});
|
21218
|
-
if (existingPublication) {
|
21219
|
-
this.log.warn('track has already been published, skipping', Object.assign(Object.assign({}, this.logContext), getLogContextFromTrack(existingPublication)));
|
21220
|
-
return existingPublication;
|
21221
|
-
}
|
21222
|
-
const isStereoInput = 'channelCount' in track.mediaStreamTrack.getSettings() &&
|
21223
|
-
// @ts-ignore `channelCount` on getSettings() is currently only available for Safari, but is generally the best way to determine a stereo track https://developer.mozilla.org/en-US/docs/Web/API/MediaTrackSettings/channelCount
|
21224
|
-
track.mediaStreamTrack.getSettings().channelCount === 2 || track.mediaStreamTrack.getConstraints().channelCount === 2;
|
21225
|
-
const isStereo = (_b = options === null || options === void 0 ? void 0 : options.forceStereo) !== null && _b !== void 0 ? _b : isStereoInput;
|
21226
|
-
// disable dtx for stereo track if not enabled explicitly
|
21227
|
-
if (isStereo) {
|
21228
|
-
if (!options) {
|
21229
|
-
options = {};
|
21230
|
-
}
|
21231
|
-
if (options.dtx === undefined) {
|
21232
|
-
this.log.info("Opus DTX will be disabled for stereo tracks by default. Enable them explicitly to make it work.", Object.assign(Object.assign({}, this.logContext), getLogContextFromTrack(track)));
|
21233
|
-
}
|
21234
|
-
if (options.red === undefined) {
|
21235
|
-
this.log.info("Opus RED will be disabled for stereo tracks by default. Enable them explicitly to make it work.");
|
21236
|
-
}
|
21237
|
-
(_c = options.dtx) !== null && _c !== void 0 ? _c : options.dtx = false;
|
21238
|
-
(_d = options.red) !== null && _d !== void 0 ? _d : options.red = false;
|
21239
|
-
}
|
21240
|
-
const opts = Object.assign(Object.assign({}, this.roomOptions.publishDefaults), options);
|
21241
|
-
// disable simulcast if e2ee is set on safari
|
21242
|
-
if (isSafari() && this.roomOptions.e2ee) {
|
21243
|
-
this.log.info("End-to-end encryption is set up, simulcast publishing will be disabled on Safari", Object.assign({}, this.logContext));
|
21244
|
-
opts.simulcast = false;
|
21245
|
-
}
|
21246
|
-
if (opts.source) {
|
21247
|
-
track.source = opts.source;
|
21248
|
-
}
|
21249
|
-
const publishPromise = this.publish(track, opts, isStereo);
|
21250
|
-
this.pendingPublishPromises.set(track, publishPromise);
|
21251
|
-
try {
|
21252
|
-
const publication = yield publishPromise;
|
21253
|
-
return publication;
|
21254
|
-
} catch (e) {
|
21255
|
-
throw e;
|
21256
|
-
} finally {
|
21257
|
-
this.pendingPublishPromises.delete(track);
|
21258
|
-
}
|
21259
|
-
});
|
21310
|
+
* Control who can subscribe to LocalParticipant's published tracks.
|
21311
|
+
*
|
21312
|
+
* By default, all participants can subscribe. This allows fine-grained control over
|
21313
|
+
* who is able to subscribe at a participant and track level.
|
21314
|
+
*
|
21315
|
+
* Note: if access is given at a track-level (i.e. both [allParticipantsAllowed] and
|
21316
|
+
* [ParticipantTrackPermission.allTracksAllowed] are false), any newer published tracks
|
21317
|
+
* will not grant permissions to any participants and will require a subsequent
|
21318
|
+
* permissions update to allow subscription.
|
21319
|
+
*
|
21320
|
+
* @param allParticipantsAllowed Allows all participants to subscribe all tracks.
|
21321
|
+
* Takes precedence over [[participantTrackPermissions]] if set to true.
|
21322
|
+
* By default this is set to true.
|
21323
|
+
* @param participantTrackPermissions Full list of individual permissions per
|
21324
|
+
* participant/track. Any omitted participants will not receive any permissions.
|
21325
|
+
*/
|
21326
|
+
setTrackSubscriptionPermissions(allParticipantsAllowed) {
|
21327
|
+
let participantTrackPermissions = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
|
21328
|
+
this.participantTrackPermissions = participantTrackPermissions;
|
21329
|
+
this.allParticipantsAllowedToSubscribe = allParticipantsAllowed;
|
21330
|
+
if (!this.engine.client.isDisconnected) {
|
21331
|
+
this.updateTrackSubscriptionPermissions();
|
21332
|
+
}
|
21260
21333
|
}
|
21261
|
-
|
21262
|
-
|
21263
|
-
|
21264
|
-
|
21265
|
-
|
21266
|
-
|
21267
|
-
|
21268
|
-
|
21269
|
-
|
21270
|
-
|
21271
|
-
|
21272
|
-
|
21273
|
-
|
21274
|
-
|
21275
|
-
|
21276
|
-
|
21277
|
-
if (
|
21278
|
-
|
21279
|
-
|
21280
|
-
|
21281
|
-
|
21282
|
-
}
|
21283
|
-
if (opts.videoCodec === undefined) {
|
21284
|
-
opts.videoCodec = defaultVideoCodec;
|
21285
|
-
}
|
21286
|
-
const videoCodec = opts.videoCodec;
|
21287
|
-
// handle track actions
|
21288
|
-
track.on(TrackEvent.Muted, this.onTrackMuted);
|
21289
|
-
track.on(TrackEvent.Unmuted, this.onTrackUnmuted);
|
21290
|
-
track.on(TrackEvent.Ended, this.handleTrackEnded);
|
21291
|
-
track.on(TrackEvent.UpstreamPaused, this.onTrackUpstreamPaused);
|
21292
|
-
track.on(TrackEvent.UpstreamResumed, this.onTrackUpstreamResumed);
|
21293
|
-
// create track publication from track
|
21294
|
-
const req = new AddTrackRequest({
|
21295
|
-
// get local track id for use during publishing
|
21296
|
-
cid: track.mediaStreamTrack.id,
|
21297
|
-
name: opts.name,
|
21298
|
-
type: Track.kindToProto(track.kind),
|
21299
|
-
muted: track.isMuted,
|
21300
|
-
source: Track.sourceToProto(track.source),
|
21301
|
-
disableDtx: !((_a = opts.dtx) !== null && _a !== void 0 ? _a : true),
|
21302
|
-
encryption: this.encryptionType,
|
21303
|
-
stereo: isStereo,
|
21304
|
-
disableRed: this.isE2EEEnabled || !((_b = opts.red) !== null && _b !== void 0 ? _b : true),
|
21305
|
-
stream: opts === null || opts === void 0 ? void 0 : opts.stream
|
21306
|
-
});
|
21307
|
-
// compute encodings and layers for video
|
21308
|
-
let encodings;
|
21309
|
-
if (track.kind === Track.Kind.Video) {
|
21310
|
-
let dims = {
|
21311
|
-
width: 0,
|
21312
|
-
height: 0
|
21313
|
-
};
|
21314
|
-
try {
|
21315
|
-
dims = yield track.waitForDimensions();
|
21316
|
-
} catch (e) {
|
21317
|
-
// use defaults, it's quite painful for congestion control without simulcast
|
21318
|
-
// so using default dims according to publish settings
|
21319
|
-
const defaultRes = (_d = (_c = this.roomOptions.videoCaptureDefaults) === null || _c === void 0 ? void 0 : _c.resolution) !== null && _d !== void 0 ? _d : VideoPresets.h720.resolution;
|
21320
|
-
dims = {
|
21321
|
-
width: defaultRes.width,
|
21322
|
-
height: defaultRes.height
|
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';
|
21338
|
-
}
|
21339
|
-
// set scalabilityMode to 'L3T3_KEY' by default
|
21340
|
-
opts.scalabilityMode = (_e = opts.scalabilityMode) !== null && _e !== void 0 ? _e : 'L3T3_KEY';
|
21341
|
-
}
|
21342
|
-
req.simulcastCodecs = [new SimulcastCodec({
|
21343
|
-
codec: videoCodec,
|
21344
|
-
cid: track.mediaStreamTrack.id
|
21345
|
-
})];
|
21346
|
-
// set up backup
|
21347
|
-
if (opts.backupCodec === true) {
|
21348
|
-
opts.backupCodec = {
|
21349
|
-
codec: defaultVideoCodec
|
21350
|
-
};
|
21351
|
-
}
|
21352
|
-
if (opts.backupCodec && videoCodec !== opts.backupCodec.codec &&
|
21353
|
-
// TODO remove this once e2ee is supported for backup codecs
|
21354
|
-
req.encryption === Encryption_Type.NONE) {
|
21355
|
-
// multi-codec simulcast requires dynacast
|
21356
|
-
if (!this.roomOptions.dynacast) {
|
21357
|
-
this.roomOptions.dynacast = true;
|
21358
|
-
}
|
21359
|
-
req.simulcastCodecs.push(new SimulcastCodec({
|
21360
|
-
codec: opts.backupCodec.codec,
|
21361
|
-
cid: ''
|
21362
|
-
}));
|
21363
|
-
}
|
21364
|
-
}
|
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
|
-
}
|
21374
|
-
if (!this.engine || this.engine.isClosed) {
|
21375
|
-
throw new UnexpectedConnectionState('cannot publish track when not connected');
|
21376
|
-
}
|
21377
|
-
const ti = yield this.engine.addTrack(req);
|
21378
|
-
// server might not support the codec the client has requested, in that case, fallback
|
21379
|
-
// to a supported codec
|
21380
|
-
let primaryCodecMime;
|
21381
|
-
ti.codecs.forEach(codec => {
|
21382
|
-
if (primaryCodecMime === undefined) {
|
21383
|
-
primaryCodecMime = codec.mimeType;
|
21384
|
-
}
|
21385
|
-
});
|
21386
|
-
if (primaryCodecMime && track.kind === Track.Kind.Video) {
|
21387
|
-
const updatedCodec = mimeTypeToVideoCodecString(primaryCodecMime);
|
21388
|
-
if (updatedCodec !== videoCodec) {
|
21389
|
-
this.log.debug('falling back to server selected codec', Object.assign(Object.assign(Object.assign({}, this.logContext), getLogContextFromTrack(track)), {
|
21390
|
-
codec: updatedCodec
|
21334
|
+
/** @internal */
|
21335
|
+
updateInfo(info) {
|
21336
|
+
if (info.sid !== this.sid) {
|
21337
|
+
// drop updates that specify a wrong sid.
|
21338
|
+
// the sid for local participant is only explicitly set on join and full reconnect
|
21339
|
+
return false;
|
21340
|
+
}
|
21341
|
+
if (!super.updateInfo(info)) {
|
21342
|
+
return false;
|
21343
|
+
}
|
21344
|
+
// reconcile track mute status.
|
21345
|
+
// if server's track mute status doesn't match actual, we'll have to update
|
21346
|
+
// the server's copy
|
21347
|
+
info.tracks.forEach(ti => {
|
21348
|
+
var _a, _b;
|
21349
|
+
const pub = this.trackPublications.get(ti.sid);
|
21350
|
+
if (pub) {
|
21351
|
+
const mutedOnServer = pub.isMuted || ((_b = (_a = pub.track) === null || _a === void 0 ? void 0 : _a.isUpstreamPaused) !== null && _b !== void 0 ? _b : false);
|
21352
|
+
if (mutedOnServer !== ti.muted) {
|
21353
|
+
this.log.debug('updating server mute state after reconcile', Object.assign(Object.assign(Object.assign({}, this.logContext), getLogContextFromTrack(pub)), {
|
21354
|
+
mutedOnServer
|
21391
21355
|
}));
|
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);
|
21356
|
+
this.engine.client.sendMuteTrack(ti.sid, mutedOnServer);
|
21396
21357
|
}
|
21397
21358
|
}
|
21398
|
-
|
21399
|
-
|
21400
|
-
|
21401
|
-
|
21402
|
-
|
21403
|
-
|
21404
|
-
|
21405
|
-
if (!
|
21406
|
-
|
21359
|
+
});
|
21360
|
+
return true;
|
21361
|
+
}
|
21362
|
+
getPublicationForTrack(track) {
|
21363
|
+
let publication;
|
21364
|
+
this.trackPublications.forEach(pub => {
|
21365
|
+
const localTrack = pub.track;
|
21366
|
+
if (!localTrack) {
|
21367
|
+
return;
|
21407
21368
|
}
|
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
|
-
});
|
21369
|
+
// this looks overly complicated due to this object tree
|
21370
|
+
if (track instanceof MediaStreamTrack) {
|
21371
|
+
if (localTrack instanceof LocalAudioTrack || localTrack instanceof LocalVideoTrack) {
|
21372
|
+
if (localTrack.mediaStreamTrack === track) {
|
21373
|
+
publication = pub;
|
21435
21374
|
}
|
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
21375
|
}
|
21376
|
+
} else if (track === localTrack) {
|
21377
|
+
publication = pub;
|
21443
21378
|
}
|
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
21379
|
});
|
21380
|
+
return publication;
|
21381
|
+
}
|
21382
|
+
}
|
21383
|
+
|
21384
|
+
class RemoteTrackPublication extends TrackPublication {
|
21385
|
+
constructor(kind, ti, autoSubscribe, loggerOptions) {
|
21386
|
+
super(kind, ti.sid, ti.name, loggerOptions);
|
21387
|
+
this.track = undefined;
|
21388
|
+
/** @internal */
|
21389
|
+
this.allowed = true;
|
21390
|
+
this.disabled = false;
|
21391
|
+
this.currentVideoQuality = VideoQuality.HIGH;
|
21392
|
+
this.handleEnded = track => {
|
21393
|
+
this.setTrack(undefined);
|
21394
|
+
this.emit(TrackEvent.Ended, track);
|
21395
|
+
};
|
21396
|
+
this.handleVisibilityChange = visible => {
|
21397
|
+
this.log.debug("adaptivestream video visibility ".concat(this.trackSid, ", visible=").concat(visible), this.logContext);
|
21398
|
+
this.disabled = !visible;
|
21399
|
+
this.emitTrackUpdate();
|
21400
|
+
};
|
21401
|
+
this.handleVideoDimensionsChange = dimensions => {
|
21402
|
+
this.log.debug("adaptivestream video dimensions ".concat(dimensions.width, "x").concat(dimensions.height), this.logContext);
|
21403
|
+
this.videoDimensions = dimensions;
|
21404
|
+
this.emitTrackUpdate();
|
21405
|
+
};
|
21406
|
+
this.subscribed = autoSubscribe;
|
21407
|
+
this.updateInfo(ti);
|
21408
|
+
}
|
21409
|
+
/**
|
21410
|
+
* Subscribe or unsubscribe to this remote track
|
21411
|
+
* @param subscribed true to subscribe to a track, false to unsubscribe
|
21412
|
+
*/
|
21413
|
+
setSubscribed(subscribed) {
|
21414
|
+
const prevStatus = this.subscriptionStatus;
|
21415
|
+
const prevPermission = this.permissionStatus;
|
21416
|
+
this.subscribed = subscribed;
|
21417
|
+
// reset allowed status when desired subscription state changes
|
21418
|
+
// server will notify client via signal message if it's not allowed
|
21419
|
+
if (subscribed) {
|
21420
|
+
this.allowed = true;
|
21421
|
+
}
|
21422
|
+
const sub = new UpdateSubscription({
|
21423
|
+
trackSids: [this.trackSid],
|
21424
|
+
subscribe: this.subscribed,
|
21425
|
+
participantTracks: [new ParticipantTracks({
|
21426
|
+
// sending an empty participant id since TrackPublication doesn't keep it
|
21427
|
+
// this is filled in by the participant that receives this message
|
21428
|
+
participantSid: '',
|
21429
|
+
trackSids: [this.trackSid]
|
21430
|
+
})]
|
21431
|
+
});
|
21432
|
+
this.emit(TrackEvent.UpdateSubscription, sub);
|
21433
|
+
this.emitSubscriptionUpdateIfChanged(prevStatus);
|
21434
|
+
this.emitPermissionUpdateIfChanged(prevPermission);
|
21435
|
+
}
|
21436
|
+
get subscriptionStatus() {
|
21437
|
+
if (this.subscribed === false) {
|
21438
|
+
return TrackPublication.SubscriptionStatus.Unsubscribed;
|
21439
|
+
}
|
21440
|
+
if (!super.isSubscribed) {
|
21441
|
+
return TrackPublication.SubscriptionStatus.Desired;
|
21442
|
+
}
|
21443
|
+
return TrackPublication.SubscriptionStatus.Subscribed;
|
21444
|
+
}
|
21445
|
+
get permissionStatus() {
|
21446
|
+
return this.allowed ? TrackPublication.PermissionStatus.Allowed : TrackPublication.PermissionStatus.NotAllowed;
|
21447
|
+
}
|
21448
|
+
/**
|
21449
|
+
* Returns true if track is subscribed, and ready for playback
|
21450
|
+
*/
|
21451
|
+
get isSubscribed() {
|
21452
|
+
if (this.subscribed === false) {
|
21453
|
+
return false;
|
21454
|
+
}
|
21455
|
+
return super.isSubscribed;
|
21456
|
+
}
|
21457
|
+
// returns client's desire to subscribe to a track, also true if autoSubscribe is enabled
|
21458
|
+
get isDesired() {
|
21459
|
+
return this.subscribed !== false;
|
21460
|
+
}
|
21461
|
+
get isEnabled() {
|
21462
|
+
return !this.disabled;
|
21463
|
+
}
|
21464
|
+
/**
|
21465
|
+
* disable server from sending down data for this track. this is useful when
|
21466
|
+
* the participant is off screen, you may disable streaming down their video
|
21467
|
+
* to reduce bandwidth requirements
|
21468
|
+
* @param enabled
|
21469
|
+
*/
|
21470
|
+
setEnabled(enabled) {
|
21471
|
+
if (!this.isManualOperationAllowed() || this.disabled === !enabled) {
|
21472
|
+
return;
|
21473
|
+
}
|
21474
|
+
this.disabled = !enabled;
|
21475
|
+
this.emitTrackUpdate();
|
21476
|
+
}
|
21477
|
+
/**
|
21478
|
+
* for tracks that support simulcasting, adjust subscribed quality
|
21479
|
+
*
|
21480
|
+
* This indicates the highest quality the client can accept. if network
|
21481
|
+
* bandwidth does not allow, server will automatically reduce quality to
|
21482
|
+
* optimize for uninterrupted video
|
21483
|
+
*/
|
21484
|
+
setVideoQuality(quality) {
|
21485
|
+
if (!this.isManualOperationAllowed() || this.currentVideoQuality === quality) {
|
21486
|
+
return;
|
21487
|
+
}
|
21488
|
+
this.currentVideoQuality = quality;
|
21489
|
+
this.videoDimensions = undefined;
|
21490
|
+
this.emitTrackUpdate();
|
21491
|
+
}
|
21492
|
+
setVideoDimensions(dimensions) {
|
21493
|
+
var _a, _b;
|
21494
|
+
if (!this.isManualOperationAllowed()) {
|
21495
|
+
return;
|
21496
|
+
}
|
21497
|
+
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) {
|
21498
|
+
return;
|
21499
|
+
}
|
21500
|
+
if (this.track instanceof RemoteVideoTrack) {
|
21501
|
+
this.videoDimensions = dimensions;
|
21502
|
+
}
|
21503
|
+
this.currentVideoQuality = undefined;
|
21504
|
+
this.emitTrackUpdate();
|
21505
|
+
}
|
21506
|
+
setVideoFPS(fps) {
|
21507
|
+
if (!this.isManualOperationAllowed()) {
|
21508
|
+
return;
|
21509
|
+
}
|
21510
|
+
if (!(this.track instanceof RemoteVideoTrack)) {
|
21511
|
+
return;
|
21512
|
+
}
|
21513
|
+
if (this.fps === fps) {
|
21514
|
+
return;
|
21515
|
+
}
|
21516
|
+
this.fps = fps;
|
21517
|
+
this.emitTrackUpdate();
|
21518
|
+
}
|
21519
|
+
get videoQuality() {
|
21520
|
+
return this.currentVideoQuality;
|
21521
|
+
}
|
21522
|
+
/** @internal */
|
21523
|
+
setTrack(track) {
|
21524
|
+
const prevStatus = this.subscriptionStatus;
|
21525
|
+
const prevPermission = this.permissionStatus;
|
21526
|
+
const prevTrack = this.track;
|
21527
|
+
if (prevTrack === track) {
|
21528
|
+
return;
|
21529
|
+
}
|
21530
|
+
if (prevTrack) {
|
21531
|
+
// unregister listener
|
21532
|
+
prevTrack.off(TrackEvent.VideoDimensionsChanged, this.handleVideoDimensionsChange);
|
21533
|
+
prevTrack.off(TrackEvent.VisibilityChanged, this.handleVisibilityChange);
|
21534
|
+
prevTrack.off(TrackEvent.Ended, this.handleEnded);
|
21535
|
+
prevTrack.detach();
|
21536
|
+
prevTrack.stopMonitor();
|
21537
|
+
this.emit(TrackEvent.Unsubscribed, prevTrack);
|
21538
|
+
}
|
21539
|
+
super.setTrack(track);
|
21540
|
+
if (track) {
|
21541
|
+
track.sid = this.trackSid;
|
21542
|
+
track.on(TrackEvent.VideoDimensionsChanged, this.handleVideoDimensionsChange);
|
21543
|
+
track.on(TrackEvent.VisibilityChanged, this.handleVisibilityChange);
|
21544
|
+
track.on(TrackEvent.Ended, this.handleEnded);
|
21545
|
+
this.emit(TrackEvent.Subscribed, track);
|
21546
|
+
}
|
21547
|
+
this.emitPermissionUpdateIfChanged(prevPermission);
|
21548
|
+
this.emitSubscriptionUpdateIfChanged(prevStatus);
|
21549
|
+
}
|
21550
|
+
/** @internal */
|
21551
|
+
setAllowed(allowed) {
|
21552
|
+
const prevStatus = this.subscriptionStatus;
|
21553
|
+
const prevPermission = this.permissionStatus;
|
21554
|
+
this.allowed = allowed;
|
21555
|
+
this.emitPermissionUpdateIfChanged(prevPermission);
|
21556
|
+
this.emitSubscriptionUpdateIfChanged(prevStatus);
|
21557
|
+
}
|
21558
|
+
/** @internal */
|
21559
|
+
setSubscriptionError(error) {
|
21560
|
+
this.emit(TrackEvent.SubscriptionFailed, error);
|
21561
|
+
}
|
21562
|
+
/** @internal */
|
21563
|
+
updateInfo(info) {
|
21564
|
+
super.updateInfo(info);
|
21565
|
+
const prevMetadataMuted = this.metadataMuted;
|
21566
|
+
this.metadataMuted = info.muted;
|
21567
|
+
if (this.track) {
|
21568
|
+
this.track.setMuted(info.muted);
|
21569
|
+
} else if (prevMetadataMuted !== info.muted) {
|
21570
|
+
this.emit(info.muted ? TrackEvent.Muted : TrackEvent.Unmuted);
|
21571
|
+
}
|
21455
21572
|
}
|
21456
|
-
|
21573
|
+
emitSubscriptionUpdateIfChanged(previousStatus) {
|
21574
|
+
const currentStatus = this.subscriptionStatus;
|
21575
|
+
if (previousStatus === currentStatus) {
|
21576
|
+
return;
|
21577
|
+
}
|
21578
|
+
this.emit(TrackEvent.SubscriptionStatusChanged, currentStatus, previousStatus);
|
21579
|
+
}
|
21580
|
+
emitPermissionUpdateIfChanged(previousPermissionStatus) {
|
21581
|
+
const currentPermissionStatus = this.permissionStatus;
|
21582
|
+
if (currentPermissionStatus !== previousPermissionStatus) {
|
21583
|
+
this.emit(TrackEvent.SubscriptionPermissionChanged, this.permissionStatus, previousPermissionStatus);
|
21584
|
+
}
|
21585
|
+
}
|
21586
|
+
isManualOperationAllowed() {
|
21587
|
+
if (this.kind === Track.Kind.Video && this.isAdaptiveStream) {
|
21588
|
+
this.log.warn('adaptive stream is enabled, cannot change video track settings', this.logContext);
|
21589
|
+
return false;
|
21590
|
+
}
|
21591
|
+
if (!this.isDesired) {
|
21592
|
+
this.log.warn('cannot update track settings when not subscribed', this.logContext);
|
21593
|
+
return false;
|
21594
|
+
}
|
21457
21595
|
return true;
|
21458
21596
|
}
|
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
|
-
});
|
21597
|
+
get isAdaptiveStream() {
|
21598
|
+
return this.track instanceof RemoteVideoTrack && this.track.isAdaptiveStream;
|
21515
21599
|
}
|
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;
|
21600
|
+
/* @internal */
|
21601
|
+
emitTrackUpdate() {
|
21602
|
+
const settings = new UpdateTrackSettings({
|
21603
|
+
trackSids: [this.trackSid],
|
21604
|
+
disabled: this.disabled,
|
21605
|
+
fps: this.fps
|
21590
21606
|
});
|
21607
|
+
if (this.videoDimensions) {
|
21608
|
+
settings.width = Math.ceil(this.videoDimensions.width);
|
21609
|
+
settings.height = Math.ceil(this.videoDimensions.height);
|
21610
|
+
} else if (this.currentVideoQuality !== undefined) {
|
21611
|
+
settings.quality = this.currentVideoQuality;
|
21612
|
+
} else {
|
21613
|
+
// defaults to high quality
|
21614
|
+
settings.quality = VideoQuality.HIGH;
|
21615
|
+
}
|
21616
|
+
this.emit(TrackEvent.UpdateSettings, settings);
|
21591
21617
|
}
|
21592
|
-
|
21593
|
-
|
21594
|
-
|
21595
|
-
|
21596
|
-
|
21618
|
+
}
|
21619
|
+
|
21620
|
+
class RemoteParticipant extends Participant {
|
21621
|
+
/** @internal */
|
21622
|
+
static fromParticipantInfo(signalClient, pi) {
|
21623
|
+
return new RemoteParticipant(signalClient, pi.sid, pi.identity, pi.name, pi.metadata);
|
21597
21624
|
}
|
21598
|
-
|
21599
|
-
|
21600
|
-
|
21601
|
-
|
21602
|
-
|
21603
|
-
|
21604
|
-
|
21605
|
-
|
21606
|
-
|
21607
|
-
|
21608
|
-
|
21625
|
+
/** @internal */
|
21626
|
+
constructor(signalClient, sid, identity, name, metadata, loggerOptions) {
|
21627
|
+
super(sid, identity || '', name, metadata, loggerOptions);
|
21628
|
+
this.signalClient = signalClient;
|
21629
|
+
this.trackPublications = new Map();
|
21630
|
+
this.audioTrackPublications = new Map();
|
21631
|
+
this.videoTrackPublications = new Map();
|
21632
|
+
this.volumeMap = new Map();
|
21633
|
+
}
|
21634
|
+
addTrackPublication(publication) {
|
21635
|
+
super.addTrackPublication(publication);
|
21636
|
+
// register action events
|
21637
|
+
publication.on(TrackEvent.UpdateSettings, settings => {
|
21638
|
+
this.log.debug('send update settings', Object.assign(Object.assign({}, this.logContext), getLogContextFromTrack(publication)));
|
21639
|
+
this.signalClient.sendUpdateTrackSettings(settings);
|
21640
|
+
});
|
21641
|
+
publication.on(TrackEvent.UpdateSubscription, sub => {
|
21642
|
+
sub.participantTracks.forEach(pt => {
|
21643
|
+
pt.participantSid = this.sid;
|
21609
21644
|
});
|
21610
|
-
|
21611
|
-
|
21612
|
-
|
21613
|
-
|
21614
|
-
|
21615
|
-
|
21616
|
-
|
21617
|
-
|
21618
|
-
|
21619
|
-
|
21620
|
-
|
21621
|
-
|
21622
|
-
|
21645
|
+
this.signalClient.sendUpdateSubscription(sub);
|
21646
|
+
});
|
21647
|
+
publication.on(TrackEvent.SubscriptionPermissionChanged, status => {
|
21648
|
+
this.emit(ParticipantEvent.TrackSubscriptionPermissionChanged, publication, status);
|
21649
|
+
});
|
21650
|
+
publication.on(TrackEvent.SubscriptionStatusChanged, status => {
|
21651
|
+
this.emit(ParticipantEvent.TrackSubscriptionStatusChanged, publication, status);
|
21652
|
+
});
|
21653
|
+
publication.on(TrackEvent.Subscribed, track => {
|
21654
|
+
this.emit(ParticipantEvent.TrackSubscribed, track, publication);
|
21655
|
+
});
|
21656
|
+
publication.on(TrackEvent.Unsubscribed, previousTrack => {
|
21657
|
+
this.emit(ParticipantEvent.TrackUnsubscribed, previousTrack, publication);
|
21658
|
+
});
|
21659
|
+
publication.on(TrackEvent.SubscriptionFailed, error => {
|
21660
|
+
this.emit(ParticipantEvent.TrackSubscriptionFailed, publication.trackSid, error);
|
21623
21661
|
});
|
21624
21662
|
}
|
21625
|
-
|
21626
|
-
|
21627
|
-
|
21628
|
-
|
21629
|
-
|
21630
|
-
|
21631
|
-
|
21632
|
-
|
21633
|
-
|
21634
|
-
|
21635
|
-
|
21636
|
-
|
21663
|
+
getTrackPublication(source) {
|
21664
|
+
const track = super.getTrackPublication(source);
|
21665
|
+
if (track) {
|
21666
|
+
return track;
|
21667
|
+
}
|
21668
|
+
}
|
21669
|
+
getTrackPublicationByName(name) {
|
21670
|
+
const track = super.getTrackPublicationByName(name);
|
21671
|
+
if (track) {
|
21672
|
+
return track;
|
21673
|
+
}
|
21674
|
+
}
|
21675
|
+
/**
|
21676
|
+
* sets the volume on the participant's audio track
|
21677
|
+
* by default, this affects the microphone publication
|
21678
|
+
* a different source can be passed in as a second argument
|
21679
|
+
* if no track exists the volume will be applied when the microphone track is added
|
21680
|
+
*/
|
21681
|
+
setVolume(volume) {
|
21682
|
+
let source = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : Track.Source.Microphone;
|
21683
|
+
this.volumeMap.set(source, volume);
|
21684
|
+
const audioPublication = this.getTrackPublication(source);
|
21685
|
+
if (audioPublication && audioPublication.track) {
|
21686
|
+
audioPublication.track.setVolume(volume);
|
21687
|
+
}
|
21688
|
+
}
|
21689
|
+
/**
|
21690
|
+
* gets the volume on the participant's microphone track
|
21691
|
+
*/
|
21692
|
+
getVolume() {
|
21693
|
+
let source = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : Track.Source.Microphone;
|
21694
|
+
const audioPublication = this.getTrackPublication(source);
|
21695
|
+
if (audioPublication && audioPublication.track) {
|
21696
|
+
return audioPublication.track.getVolume();
|
21697
|
+
}
|
21698
|
+
return this.volumeMap.get(source);
|
21699
|
+
}
|
21700
|
+
/** @internal */
|
21701
|
+
addSubscribedMediaTrack(mediaTrack, sid, mediaStream, receiver, adaptiveStreamSettings, triesLeft) {
|
21702
|
+
// find the track publication
|
21703
|
+
// it's possible for the media track to arrive before participant info
|
21704
|
+
let publication = this.getTrackPublicationBySid(sid);
|
21705
|
+
// it's also possible that the browser didn't honor our original track id
|
21706
|
+
// FireFox would use its own local uuid instead of server track id
|
21707
|
+
if (!publication) {
|
21708
|
+
if (!sid.startsWith('TR')) {
|
21709
|
+
// find the first track that matches type
|
21710
|
+
this.trackPublications.forEach(p => {
|
21711
|
+
if (!publication && mediaTrack.kind === p.kind.toString()) {
|
21712
|
+
publication = p;
|
21637
21713
|
}
|
21638
21714
|
});
|
21639
21715
|
}
|
21640
|
-
|
21641
|
-
|
21642
|
-
|
21643
|
-
|
21644
|
-
|
21645
|
-
|
21646
|
-
|
21647
|
-
|
21648
|
-
|
21649
|
-
|
21650
|
-
|
21651
|
-
|
21652
|
-
|
21653
|
-
|
21716
|
+
}
|
21717
|
+
// when we couldn't locate the track, it's possible that the metadata hasn't
|
21718
|
+
// yet arrived. Wait a bit longer for it to arrive, or fire an error
|
21719
|
+
if (!publication) {
|
21720
|
+
if (triesLeft === 0) {
|
21721
|
+
this.log.error('could not find published track', Object.assign(Object.assign({}, this.logContext), {
|
21722
|
+
trackSid: sid
|
21723
|
+
}));
|
21724
|
+
this.emit(ParticipantEvent.TrackSubscriptionFailed, sid);
|
21725
|
+
return;
|
21726
|
+
}
|
21727
|
+
if (triesLeft === undefined) triesLeft = 20;
|
21728
|
+
setTimeout(() => {
|
21729
|
+
this.addSubscribedMediaTrack(mediaTrack, sid, mediaStream, receiver, adaptiveStreamSettings, triesLeft - 1);
|
21730
|
+
}, 150);
|
21731
|
+
return;
|
21732
|
+
}
|
21733
|
+
if (mediaTrack.readyState === 'ended') {
|
21734
|
+
this.log.error('unable to subscribe because MediaStreamTrack is ended. Do not call MediaStreamTrack.stop()', Object.assign(Object.assign({}, this.logContext), getLogContextFromTrack(publication)));
|
21735
|
+
this.emit(ParticipantEvent.TrackSubscriptionFailed, sid);
|
21736
|
+
return;
|
21737
|
+
}
|
21738
|
+
const isVideo = mediaTrack.kind === 'video';
|
21739
|
+
let track;
|
21740
|
+
if (isVideo) {
|
21741
|
+
track = new RemoteVideoTrack(mediaTrack, sid, receiver, adaptiveStreamSettings);
|
21742
|
+
} else {
|
21743
|
+
track = new RemoteAudioTrack(mediaTrack, sid, receiver, this.audioContext, this.audioOutput);
|
21744
|
+
}
|
21745
|
+
// set track info
|
21746
|
+
track.source = publication.source;
|
21747
|
+
// keep publication's muted status
|
21748
|
+
track.isMuted = publication.isMuted;
|
21749
|
+
track.setMediaStream(mediaStream);
|
21750
|
+
track.start();
|
21751
|
+
publication.setTrack(track);
|
21752
|
+
// set participant volumes on new audio tracks
|
21753
|
+
if (this.volumeMap.has(publication.source) && track instanceof RemoteAudioTrack) {
|
21754
|
+
track.setVolume(this.volumeMap.get(publication.source));
|
21755
|
+
}
|
21756
|
+
return publication;
|
21757
|
+
}
|
21758
|
+
/** @internal */
|
21759
|
+
get hasMetadata() {
|
21760
|
+
return !!this.participantInfo;
|
21654
21761
|
}
|
21655
21762
|
/**
|
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.
|
21763
|
+
* @internal
|
21671
21764
|
*/
|
21672
|
-
|
21673
|
-
|
21674
|
-
this.participantTrackPermissions = participantTrackPermissions;
|
21675
|
-
this.allParticipantsAllowedToSubscribe = allParticipantsAllowed;
|
21676
|
-
if (!this.engine.client.isDisconnected) {
|
21677
|
-
this.updateTrackSubscriptionPermissions();
|
21678
|
-
}
|
21765
|
+
getTrackPublicationBySid(sid) {
|
21766
|
+
return this.trackPublications.get(sid);
|
21679
21767
|
}
|
21680
21768
|
/** @internal */
|
21681
21769
|
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
21770
|
if (!super.updateInfo(info)) {
|
21688
21771
|
return false;
|
21689
21772
|
}
|
21690
|
-
// reconcile
|
21691
|
-
//
|
21692
|
-
//
|
21773
|
+
// we are getting a list of all available tracks, reconcile in here
|
21774
|
+
// and send out events for changes
|
21775
|
+
// reconcile track publications, publish events only if metadata is already there
|
21776
|
+
// i.e. changes since the local participant has joined
|
21777
|
+
const validTracks = new Map();
|
21778
|
+
const newTracks = new Map();
|
21693
21779
|
info.tracks.forEach(ti => {
|
21694
21780
|
var _a, _b;
|
21695
|
-
|
21696
|
-
if (
|
21697
|
-
|
21698
|
-
|
21699
|
-
|
21700
|
-
|
21781
|
+
let publication = this.getTrackPublicationBySid(ti.sid);
|
21782
|
+
if (!publication) {
|
21783
|
+
// new publication
|
21784
|
+
const kind = Track.kindFromProto(ti.type);
|
21785
|
+
if (!kind) {
|
21786
|
+
return;
|
21787
|
+
}
|
21788
|
+
publication = new RemoteTrackPublication(kind, ti, (_a = this.signalClient.connectOptions) === null || _a === void 0 ? void 0 : _a.autoSubscribe, {
|
21789
|
+
loggerContextCb: () => this.logContext,
|
21790
|
+
loggerName: (_b = this.loggerOptions) === null || _b === void 0 ? void 0 : _b.loggerName
|
21791
|
+
});
|
21792
|
+
publication.updateInfo(ti);
|
21793
|
+
newTracks.set(ti.sid, publication);
|
21794
|
+
const existingTrackOfSource = Array.from(this.trackPublications.values()).find(publishedTrack => publishedTrack.source === (publication === null || publication === void 0 ? void 0 : publication.source));
|
21795
|
+
if (existingTrackOfSource && publication.source !== Track.Source.Unknown) {
|
21796
|
+
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), {
|
21797
|
+
oldTrack: getLogContextFromTrack(existingTrackOfSource),
|
21798
|
+
newTrack: getLogContextFromTrack(publication)
|
21701
21799
|
}));
|
21702
|
-
this.engine.client.sendMuteTrack(ti.sid, mutedOnServer);
|
21703
21800
|
}
|
21801
|
+
this.addTrackPublication(publication);
|
21802
|
+
} else {
|
21803
|
+
publication.updateInfo(ti);
|
21804
|
+
}
|
21805
|
+
validTracks.set(ti.sid, publication);
|
21806
|
+
});
|
21807
|
+
// detect removed tracks
|
21808
|
+
this.trackPublications.forEach(publication => {
|
21809
|
+
if (!validTracks.has(publication.trackSid)) {
|
21810
|
+
this.log.trace('detected removed track on remote participant, unpublishing', Object.assign(Object.assign({}, this.logContext), getLogContextFromTrack(publication)));
|
21811
|
+
this.unpublishTrack(publication.trackSid, true);
|
21704
21812
|
}
|
21705
21813
|
});
|
21814
|
+
// always emit events for new publications, Room will not forward them unless it's ready
|
21815
|
+
newTracks.forEach(publication => {
|
21816
|
+
this.emit(ParticipantEvent.TrackPublished, publication);
|
21817
|
+
});
|
21706
21818
|
return true;
|
21707
21819
|
}
|
21708
|
-
|
21709
|
-
|
21710
|
-
this.
|
21711
|
-
|
21712
|
-
|
21713
|
-
|
21714
|
-
|
21715
|
-
|
21716
|
-
|
21717
|
-
|
21718
|
-
|
21719
|
-
|
21720
|
-
|
21820
|
+
/** @internal */
|
21821
|
+
unpublishTrack(sid, sendUnpublish) {
|
21822
|
+
const publication = this.trackPublications.get(sid);
|
21823
|
+
if (!publication) {
|
21824
|
+
return;
|
21825
|
+
}
|
21826
|
+
// also send unsubscribe, if track is actively subscribed
|
21827
|
+
const {
|
21828
|
+
track
|
21829
|
+
} = publication;
|
21830
|
+
if (track) {
|
21831
|
+
track.stop();
|
21832
|
+
publication.setTrack(undefined);
|
21833
|
+
}
|
21834
|
+
// remove track from maps only after unsubscribed has been fired
|
21835
|
+
this.trackPublications.delete(sid);
|
21836
|
+
// remove from the right type map
|
21837
|
+
switch (publication.kind) {
|
21838
|
+
case Track.Kind.Audio:
|
21839
|
+
this.audioTrackPublications.delete(sid);
|
21840
|
+
break;
|
21841
|
+
case Track.Kind.Video:
|
21842
|
+
this.videoTrackPublications.delete(sid);
|
21843
|
+
break;
|
21844
|
+
}
|
21845
|
+
if (sendUnpublish) {
|
21846
|
+
this.emit(ParticipantEvent.TrackUnpublished, publication);
|
21847
|
+
}
|
21848
|
+
}
|
21849
|
+
/**
|
21850
|
+
* @internal
|
21851
|
+
*/
|
21852
|
+
setAudioOutput(output) {
|
21853
|
+
return __awaiter(this, void 0, void 0, function* () {
|
21854
|
+
this.audioOutput = output;
|
21855
|
+
const promises = [];
|
21856
|
+
this.audioTrackPublications.forEach(pub => {
|
21857
|
+
var _a;
|
21858
|
+
if (pub.track instanceof RemoteAudioTrack) {
|
21859
|
+
promises.push(pub.track.setSinkId((_a = output.deviceId) !== null && _a !== void 0 ? _a : 'default'));
|
21721
21860
|
}
|
21722
|
-
}
|
21723
|
-
|
21724
|
-
}
|
21861
|
+
});
|
21862
|
+
yield Promise.all(promises);
|
21725
21863
|
});
|
21726
|
-
|
21864
|
+
}
|
21865
|
+
/** @internal */
|
21866
|
+
emit(event) {
|
21867
|
+
for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
|
21868
|
+
args[_key - 1] = arguments[_key];
|
21869
|
+
}
|
21870
|
+
this.log.trace('participant event', Object.assign(Object.assign({}, this.logContext), {
|
21871
|
+
event,
|
21872
|
+
args
|
21873
|
+
}));
|
21874
|
+
return super.emit(event, ...args);
|
21727
21875
|
}
|
21728
21876
|
}
|
21729
21877
|
|
@@ -21735,8 +21883,6 @@ var ConnectionState;
|
|
21735
21883
|
ConnectionState["Reconnecting"] = "reconnecting";
|
21736
21884
|
})(ConnectionState || (ConnectionState = {}));
|
21737
21885
|
const connectionReconcileFrequency = 2 * 1000;
|
21738
|
-
/** @deprecated RoomState has been renamed to [[ConnectionState]] */
|
21739
|
-
const RoomState = ConnectionState;
|
21740
21886
|
/**
|
21741
21887
|
* In LiveKit, a room is the logical grouping for a list of participants.
|
21742
21888
|
* Participants in a room can publish tracks, and subscribe to others' tracks.
|
@@ -21767,6 +21913,7 @@ class Room extends eventsExports.EventEmitter {
|
|
21767
21913
|
this.isVideoPlaybackBlocked = false;
|
21768
21914
|
this.log = livekitLogger;
|
21769
21915
|
this.bufferedEvents = [];
|
21916
|
+
this.isResuming = false;
|
21770
21917
|
this.connect = (url, token, opts) => __awaiter(this, void 0, void 0, function* () {
|
21771
21918
|
var _c;
|
21772
21919
|
// In case a disconnect called happened right before the connect call, make sure the disconnect is completed first by awaiting its lock
|
@@ -21853,7 +22000,6 @@ class Room extends eventsExports.EventEmitter {
|
|
21853
22000
|
var _e, _f, _g;
|
21854
22001
|
const joinResponse = yield engine.join(url, token, {
|
21855
22002
|
autoSubscribe: connectOptions.autoSubscribe,
|
21856
|
-
publishOnly: connectOptions.publishOnly,
|
21857
22003
|
adaptiveStream: typeof roomOptions.adaptiveStream === 'object' ? true : roomOptions.adaptiveStream,
|
21858
22004
|
maxRetries: connectOptions.maxRetries,
|
21859
22005
|
e2eeEnabled: !!this.e2eeManager,
|
@@ -21898,8 +22044,8 @@ class Room extends eventsExports.EventEmitter {
|
|
21898
22044
|
}
|
21899
22045
|
};
|
21900
22046
|
this.attemptConnection = (url, token, opts, abortController) => __awaiter(this, void 0, void 0, function* () {
|
21901
|
-
var _h, _j;
|
21902
|
-
if (this.state === ConnectionState.Reconnecting) {
|
22047
|
+
var _h, _j, _k;
|
22048
|
+
if (this.state === ConnectionState.Reconnecting || this.isResuming || ((_h = this.engine) === null || _h === void 0 ? void 0 : _h.pendingReconnect)) {
|
21903
22049
|
this.log.info('Reconnection attempt replaced by new connection attempt', this.logContext);
|
21904
22050
|
// make sure we close and recreate the existing engine in order to get rid of any potentially ongoing reconnection attempts
|
21905
22051
|
this.recreateEngine();
|
@@ -21907,7 +22053,7 @@ class Room extends eventsExports.EventEmitter {
|
|
21907
22053
|
// create engine if previously disconnected
|
21908
22054
|
this.maybeCreateEngine();
|
21909
22055
|
}
|
21910
|
-
if ((
|
22056
|
+
if ((_j = this.regionUrlProvider) === null || _j === void 0 ? void 0 : _j.isCloud()) {
|
21911
22057
|
this.engine.setRegionUrlProvider(this.regionUrlProvider);
|
21912
22058
|
}
|
21913
22059
|
this.acquireAudioContext();
|
@@ -21960,7 +22106,7 @@ class Room extends eventsExports.EventEmitter {
|
|
21960
22106
|
}
|
21961
22107
|
if (isWeb()) {
|
21962
22108
|
document.addEventListener('freeze', this.onPageLeave);
|
21963
|
-
(
|
22109
|
+
(_k = navigator.mediaDevices) === null || _k === void 0 ? void 0 : _k.addEventListener('devicechange', this.handleDeviceChange);
|
21964
22110
|
}
|
21965
22111
|
this.setAndEmitConnectionState(ConnectionState.Connected);
|
21966
22112
|
this.emit(RoomEvent.Connected);
|
@@ -21972,7 +22118,7 @@ class Room extends eventsExports.EventEmitter {
|
|
21972
22118
|
this.disconnect = function () {
|
21973
22119
|
let stopTracks = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
|
21974
22120
|
return __awaiter(_this, void 0, void 0, function* () {
|
21975
|
-
var
|
22121
|
+
var _l, _m, _o, _p;
|
21976
22122
|
const unlock = yield this.disconnectLock.lock();
|
21977
22123
|
try {
|
21978
22124
|
if (this.state === ConnectionState.Disconnected) {
|
@@ -21980,16 +22126,16 @@ class Room extends eventsExports.EventEmitter {
|
|
21980
22126
|
return;
|
21981
22127
|
}
|
21982
22128
|
this.log.info('disconnect from room', Object.assign({}, this.logContext));
|
21983
|
-
if (this.state === ConnectionState.Connecting || this.state === ConnectionState.Reconnecting) {
|
22129
|
+
if (this.state === ConnectionState.Connecting || this.state === ConnectionState.Reconnecting || this.isResuming) {
|
21984
22130
|
// try aborting pending connection attempt
|
21985
22131
|
this.log.warn('abort connection attempt', this.logContext);
|
21986
|
-
(
|
22132
|
+
(_l = this.abortController) === null || _l === void 0 ? void 0 : _l.abort();
|
21987
22133
|
// in case the abort controller didn't manage to cancel the connection attempt, reject the connect promise explicitly
|
21988
|
-
(
|
22134
|
+
(_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
22135
|
this.connectFuture = undefined;
|
21990
22136
|
}
|
21991
22137
|
// send leave
|
21992
|
-
if (!((
|
22138
|
+
if (!((_p = this.engine) === null || _p === void 0 ? void 0 : _p.client.isDisconnected)) {
|
21993
22139
|
yield this.engine.client.sendLeave();
|
21994
22140
|
}
|
21995
22141
|
// close engine (also closes client)
|
@@ -22056,8 +22202,8 @@ class Room extends eventsExports.EventEmitter {
|
|
22056
22202
|
}
|
22057
22203
|
elements.push(dummyAudioEl);
|
22058
22204
|
}
|
22059
|
-
this.
|
22060
|
-
p.
|
22205
|
+
this.remoteParticipants.forEach(p => {
|
22206
|
+
p.audioTrackPublications.forEach(t => {
|
22061
22207
|
if (t.track) {
|
22062
22208
|
t.track.attachedElements.forEach(e => {
|
22063
22209
|
elements.push(e);
|
@@ -22078,8 +22224,8 @@ class Room extends eventsExports.EventEmitter {
|
|
22078
22224
|
});
|
22079
22225
|
this.startVideo = () => __awaiter(this, void 0, void 0, function* () {
|
22080
22226
|
const elements = [];
|
22081
|
-
for (const p of this.
|
22082
|
-
p.
|
22227
|
+
for (const p of this.remoteParticipants.values()) {
|
22228
|
+
p.videoTrackPublications.forEach(tr => {
|
22083
22229
|
var _a;
|
22084
22230
|
(_a = tr.track) === null || _a === void 0 ? void 0 : _a.attachedElements.forEach(el => {
|
22085
22231
|
if (!elements.includes(el)) {
|
@@ -22100,9 +22246,11 @@ class Room extends eventsExports.EventEmitter {
|
|
22100
22246
|
});
|
22101
22247
|
this.handleRestarting = () => {
|
22102
22248
|
this.clearConnectionReconcile();
|
22249
|
+
// in case we went from resuming to full-reconnect, make sure to reflect it on the isResuming flag
|
22250
|
+
this.isResuming = false;
|
22103
22251
|
// also unwind existing participants & existing subscriptions
|
22104
|
-
for (const p of this.
|
22105
|
-
this.handleParticipantDisconnected(p.
|
22252
|
+
for (const p of this.remoteParticipants.values()) {
|
22253
|
+
this.handleParticipantDisconnected(p.identity, p);
|
22106
22254
|
}
|
22107
22255
|
if (this.setAndEmitConnectionState(ConnectionState.Reconnecting)) {
|
22108
22256
|
this.emit(RoomEvent.Reconnecting);
|
@@ -22127,7 +22275,7 @@ class Room extends eventsExports.EventEmitter {
|
|
22127
22275
|
this.log.debug("fully reconnected to server", Object.assign(Object.assign({}, this.logContext), {
|
22128
22276
|
region: joinResponse.serverRegion
|
22129
22277
|
}));
|
22130
|
-
} catch (
|
22278
|
+
} catch (_q) {
|
22131
22279
|
// reconnection failed, handleDisconnect is being invoked already, just return here
|
22132
22280
|
return;
|
22133
22281
|
}
|
@@ -22139,28 +22287,23 @@ class Room extends eventsExports.EventEmitter {
|
|
22139
22287
|
this.handleParticipantUpdates = participantInfos => {
|
22140
22288
|
// handle changes to participant state, and send events
|
22141
22289
|
participantInfos.forEach(info => {
|
22290
|
+
var _a;
|
22142
22291
|
if (info.identity === this.localParticipant.identity) {
|
22143
22292
|
this.localParticipant.updateInfo(info);
|
22144
22293
|
return;
|
22145
22294
|
}
|
22146
|
-
//
|
22147
|
-
|
22148
|
-
if (
|
22149
|
-
|
22150
|
-
this.handleParticipantDisconnected(sid, this.participants.get(sid));
|
22295
|
+
// LiveKit server doesn't send identity info prior to version 1.5.2 in disconnect updates
|
22296
|
+
// so we try to map an empty identity to an already known sID manually
|
22297
|
+
if (info.identity === '') {
|
22298
|
+
info.identity = (_a = this.sidToIdentity.get(info.sid)) !== null && _a !== void 0 ? _a : '';
|
22151
22299
|
}
|
22152
|
-
let remoteParticipant = this.
|
22153
|
-
const isNewParticipant = !remoteParticipant;
|
22300
|
+
let remoteParticipant = this.remoteParticipants.get(info.identity);
|
22154
22301
|
// when it's disconnected, send updates
|
22155
22302
|
if (info.state === ParticipantInfo_State.DISCONNECTED) {
|
22156
|
-
this.handleParticipantDisconnected(info.
|
22303
|
+
this.handleParticipantDisconnected(info.identity, remoteParticipant);
|
22157
22304
|
} else {
|
22158
22305
|
// create participant if doesn't exist
|
22159
|
-
remoteParticipant = this.getOrCreateParticipant(info.
|
22160
|
-
if (!isNewParticipant) {
|
22161
|
-
// just update, no events
|
22162
|
-
remoteParticipant.updateInfo(info);
|
22163
|
-
}
|
22306
|
+
remoteParticipant = this.getOrCreateParticipant(info.identity, info);
|
22164
22307
|
}
|
22165
22308
|
});
|
22166
22309
|
};
|
@@ -22175,7 +22318,7 @@ class Room extends eventsExports.EventEmitter {
|
|
22175
22318
|
this.localParticipant.setIsSpeaking(true);
|
22176
22319
|
activeSpeakers.push(this.localParticipant);
|
22177
22320
|
} else {
|
22178
|
-
const p = this.
|
22321
|
+
const p = this.getRemoteParticipantBySid(speaker.sid);
|
22179
22322
|
if (p) {
|
22180
22323
|
p.audioLevel = speaker.level;
|
22181
22324
|
p.setIsSpeaking(true);
|
@@ -22187,7 +22330,7 @@ class Room extends eventsExports.EventEmitter {
|
|
22187
22330
|
this.localParticipant.audioLevel = 0;
|
22188
22331
|
this.localParticipant.setIsSpeaking(false);
|
22189
22332
|
}
|
22190
|
-
this.
|
22333
|
+
this.remoteParticipants.forEach(p => {
|
22191
22334
|
if (!seenSids[p.sid]) {
|
22192
22335
|
p.audioLevel = 0;
|
22193
22336
|
p.setIsSpeaking(false);
|
@@ -22203,7 +22346,7 @@ class Room extends eventsExports.EventEmitter {
|
|
22203
22346
|
lastSpeakers.set(p.sid, p);
|
22204
22347
|
});
|
22205
22348
|
speakerUpdates.forEach(speaker => {
|
22206
|
-
let p = this.
|
22349
|
+
let p = this.getRemoteParticipantBySid(speaker.sid);
|
22207
22350
|
if (speaker.sid === this.localParticipant.sid) {
|
22208
22351
|
p = this.localParticipant;
|
22209
22352
|
}
|
@@ -22225,11 +22368,11 @@ class Room extends eventsExports.EventEmitter {
|
|
22225
22368
|
};
|
22226
22369
|
this.handleStreamStateUpdate = streamStateUpdate => {
|
22227
22370
|
streamStateUpdate.streamStates.forEach(streamState => {
|
22228
|
-
const participant = this.
|
22371
|
+
const participant = this.getRemoteParticipantBySid(streamState.participantSid);
|
22229
22372
|
if (!participant) {
|
22230
22373
|
return;
|
22231
22374
|
}
|
22232
|
-
const pub = participant.
|
22375
|
+
const pub = participant.getTrackPublicationBySid(streamState.trackSid);
|
22233
22376
|
if (!pub || !pub.track) {
|
22234
22377
|
return;
|
22235
22378
|
}
|
@@ -22239,22 +22382,22 @@ class Room extends eventsExports.EventEmitter {
|
|
22239
22382
|
});
|
22240
22383
|
};
|
22241
22384
|
this.handleSubscriptionPermissionUpdate = update => {
|
22242
|
-
const participant = this.
|
22385
|
+
const participant = this.getRemoteParticipantBySid(update.participantSid);
|
22243
22386
|
if (!participant) {
|
22244
22387
|
return;
|
22245
22388
|
}
|
22246
|
-
const pub = participant.
|
22389
|
+
const pub = participant.getTrackPublicationBySid(update.trackSid);
|
22247
22390
|
if (!pub) {
|
22248
22391
|
return;
|
22249
22392
|
}
|
22250
22393
|
pub.setAllowed(update.allowed);
|
22251
22394
|
};
|
22252
22395
|
this.handleSubscriptionError = update => {
|
22253
|
-
const participant = Array.from(this.
|
22396
|
+
const participant = Array.from(this.remoteParticipants.values()).find(p => p.trackPublications.has(update.trackSid));
|
22254
22397
|
if (!participant) {
|
22255
22398
|
return;
|
22256
22399
|
}
|
22257
|
-
const pub = participant.
|
22400
|
+
const pub = participant.getTrackPublicationBySid(update.trackSid);
|
22258
22401
|
if (!pub) {
|
22259
22402
|
return;
|
22260
22403
|
}
|
@@ -22262,7 +22405,7 @@ class Room extends eventsExports.EventEmitter {
|
|
22262
22405
|
};
|
22263
22406
|
this.handleDataPacket = (userPacket, kind) => {
|
22264
22407
|
// find the participant
|
22265
|
-
const participant = this.
|
22408
|
+
const participant = this.remoteParticipants.get(userPacket.participantIdentity);
|
22266
22409
|
this.emit(RoomEvent.DataReceived, userPacket.payload, participant, kind, userPacket.topic);
|
22267
22410
|
// also emit on the participant
|
22268
22411
|
participant === null || participant === void 0 ? void 0 : participant.emit(ParticipantEvent.DataReceived, userPacket.payload, kind);
|
@@ -22315,7 +22458,7 @@ class Room extends eventsExports.EventEmitter {
|
|
22315
22458
|
this.localParticipant.setConnectionQuality(info.quality);
|
22316
22459
|
return;
|
22317
22460
|
}
|
22318
|
-
const participant = this.
|
22461
|
+
const participant = this.getRemoteParticipantBySid(info.participantSid);
|
22319
22462
|
if (participant) {
|
22320
22463
|
participant.setConnectionQuality(info.quality);
|
22321
22464
|
}
|
@@ -22334,7 +22477,7 @@ class Room extends eventsExports.EventEmitter {
|
|
22334
22477
|
this.emit(RoomEvent.TrackUnmuted, pub, this.localParticipant);
|
22335
22478
|
};
|
22336
22479
|
this.onLocalTrackPublished = pub => __awaiter(this, void 0, void 0, function* () {
|
22337
|
-
var
|
22480
|
+
var _r;
|
22338
22481
|
this.emit(RoomEvent.LocalTrackPublished, pub, this.localParticipant);
|
22339
22482
|
if (pub.track instanceof LocalAudioTrack) {
|
22340
22483
|
const trackIsSilent = yield pub.track.checkForSilence();
|
@@ -22342,7 +22485,7 @@ class Room extends eventsExports.EventEmitter {
|
|
22342
22485
|
this.emit(RoomEvent.LocalAudioSilenceDetected, pub);
|
22343
22486
|
}
|
22344
22487
|
}
|
22345
|
-
const deviceId = yield (
|
22488
|
+
const deviceId = yield (_r = pub.track) === null || _r === void 0 ? void 0 : _r.getDeviceId();
|
22346
22489
|
const deviceKind = sourceToKind(pub.source);
|
22347
22490
|
if (deviceKind && deviceId && deviceId !== this.localParticipant.activeDeviceMap.get(deviceKind)) {
|
22348
22491
|
this.localParticipant.activeDeviceMap.set(deviceKind, deviceId);
|
@@ -22362,8 +22505,8 @@ class Room extends eventsExports.EventEmitter {
|
|
22362
22505
|
this.emit(RoomEvent.ParticipantPermissionsChanged, prevPermissions, this.localParticipant);
|
22363
22506
|
};
|
22364
22507
|
this.setMaxListeners(100);
|
22365
|
-
this.
|
22366
|
-
this.
|
22508
|
+
this.remoteParticipants = new Map();
|
22509
|
+
this.sidToIdentity = new Map();
|
22367
22510
|
this.options = Object.assign(Object.assign({}, roomOptionDefaults), options);
|
22368
22511
|
this.log = getLogger((_a = this.options.loggerName) !== null && _a !== void 0 ? _a : LoggerNames.Room);
|
22369
22512
|
this.options.audioCaptureDefaults = Object.assign(Object.assign({}, audioDefaults), options === null || options === void 0 ? void 0 : options.audioCaptureDefaults);
|
@@ -22415,9 +22558,10 @@ class Room extends eventsExports.EventEmitter {
|
|
22415
22558
|
}
|
22416
22559
|
}
|
22417
22560
|
get logContext() {
|
22561
|
+
var _a;
|
22418
22562
|
return {
|
22419
22563
|
room: this.name,
|
22420
|
-
roomSid: this.sid,
|
22564
|
+
roomSid: (_a = this.roomInfo) === null || _a === void 0 ? void 0 : _a.sid,
|
22421
22565
|
identity: this.localParticipant.identity
|
22422
22566
|
};
|
22423
22567
|
}
|
@@ -22428,10 +22572,32 @@ class Room extends eventsExports.EventEmitter {
|
|
22428
22572
|
var _a, _b;
|
22429
22573
|
return (_b = (_a = this.roomInfo) === null || _a === void 0 ? void 0 : _a.activeRecording) !== null && _b !== void 0 ? _b : false;
|
22430
22574
|
}
|
22431
|
-
/**
|
22432
|
-
|
22433
|
-
|
22434
|
-
|
22575
|
+
/**
|
22576
|
+
* server assigned unique room id.
|
22577
|
+
* returns once a sid has been issued by the server.
|
22578
|
+
*/
|
22579
|
+
getSid() {
|
22580
|
+
return __awaiter(this, void 0, void 0, function* () {
|
22581
|
+
if (this.state === ConnectionState.Disconnected) {
|
22582
|
+
return '';
|
22583
|
+
}
|
22584
|
+
if (this.roomInfo && this.roomInfo.sid !== '') {
|
22585
|
+
return this.roomInfo.sid;
|
22586
|
+
}
|
22587
|
+
return new Promise((resolve, reject) => {
|
22588
|
+
const handleRoomUpdate = roomInfo => {
|
22589
|
+
if (roomInfo.sid !== '') {
|
22590
|
+
this.engine.off(EngineEvent.RoomUpdate, handleRoomUpdate);
|
22591
|
+
resolve(roomInfo.sid);
|
22592
|
+
}
|
22593
|
+
};
|
22594
|
+
this.engine.on(EngineEvent.RoomUpdate, handleRoomUpdate);
|
22595
|
+
this.once(RoomEvent.Disconnected, () => {
|
22596
|
+
this.engine.off(EngineEvent.RoomUpdate, handleRoomUpdate);
|
22597
|
+
reject('Room disconnected before room server id was available');
|
22598
|
+
});
|
22599
|
+
});
|
22600
|
+
});
|
22435
22601
|
}
|
22436
22602
|
/** user assigned name, derived from JWT token */
|
22437
22603
|
get name() {
|
@@ -22462,18 +22628,17 @@ class Room extends eventsExports.EventEmitter {
|
|
22462
22628
|
this.handleDisconnect(this.options.stopLocalTrackOnUnpublish, reason);
|
22463
22629
|
}).on(EngineEvent.ActiveSpeakersUpdate, this.handleActiveSpeakersUpdate).on(EngineEvent.DataPacketReceived, this.handleDataPacket).on(EngineEvent.Resuming, () => {
|
22464
22630
|
this.clearConnectionReconcile();
|
22465
|
-
|
22466
|
-
|
22467
|
-
}
|
22631
|
+
this.isResuming = true;
|
22632
|
+
this.log.info('Resuming signal connection', this.logContext);
|
22468
22633
|
}).on(EngineEvent.Resumed, () => {
|
22469
|
-
this.setAndEmitConnectionState(ConnectionState.Connected);
|
22470
|
-
this.emit(RoomEvent.Reconnected);
|
22471
22634
|
this.registerConnectionReconcile();
|
22635
|
+
this.isResuming = false;
|
22636
|
+
this.log.info('Resumed signal connection', this.logContext);
|
22472
22637
|
this.updateSubscriptions();
|
22473
22638
|
this.emitBufferedEvents();
|
22474
22639
|
}).on(EngineEvent.SignalResumed, () => {
|
22475
22640
|
this.bufferedEvents = [];
|
22476
|
-
if (this.state === ConnectionState.Reconnecting) {
|
22641
|
+
if (this.state === ConnectionState.Reconnecting || this.isResuming) {
|
22477
22642
|
this.sendSyncState();
|
22478
22643
|
}
|
22479
22644
|
}).on(EngineEvent.Restarting, this.handleRestarting).on(EngineEvent.SignalRestarted, this.handleSignalRestarted).on(EngineEvent.DCBufferStatusChanged, (status, kind) => {
|
@@ -22547,10 +22712,7 @@ class Room extends eventsExports.EventEmitter {
|
|
22547
22712
|
if (this.localParticipant.identity === identity) {
|
22548
22713
|
return this.localParticipant;
|
22549
22714
|
}
|
22550
|
-
|
22551
|
-
if (sid) {
|
22552
|
-
return this.participants.get(sid);
|
22553
|
-
}
|
22715
|
+
return this.remoteParticipants.get(identity);
|
22554
22716
|
}
|
22555
22717
|
clearConnectionFutures() {
|
22556
22718
|
this.connectFuture = undefined;
|
@@ -22681,15 +22843,6 @@ class Room extends eventsExports.EventEmitter {
|
|
22681
22843
|
get canPlaybackVideo() {
|
22682
22844
|
return !this.isVideoPlaybackBlocked;
|
22683
22845
|
}
|
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
22846
|
getActiveDevice(kind) {
|
22694
22847
|
return this.localParticipant.activeDeviceMap.get(kind);
|
22695
22848
|
}
|
@@ -22717,7 +22870,7 @@ class Room extends eventsExports.EventEmitter {
|
|
22717
22870
|
const prevDeviceId = this.options.audioCaptureDefaults.deviceId;
|
22718
22871
|
this.options.audioCaptureDefaults.deviceId = deviceConstraint;
|
22719
22872
|
deviceHasChanged = prevDeviceId !== deviceConstraint;
|
22720
|
-
const tracks = Array.from(this.localParticipant.
|
22873
|
+
const tracks = Array.from(this.localParticipant.audioTrackPublications.values()).filter(track => track.source === Track.Source.Microphone);
|
22721
22874
|
try {
|
22722
22875
|
success = (yield Promise.all(tracks.map(t => {
|
22723
22876
|
var _a;
|
@@ -22731,7 +22884,7 @@ class Room extends eventsExports.EventEmitter {
|
|
22731
22884
|
const prevDeviceId = this.options.videoCaptureDefaults.deviceId;
|
22732
22885
|
this.options.videoCaptureDefaults.deviceId = deviceConstraint;
|
22733
22886
|
deviceHasChanged = prevDeviceId !== deviceConstraint;
|
22734
|
-
const tracks = Array.from(this.localParticipant.
|
22887
|
+
const tracks = Array.from(this.localParticipant.videoTrackPublications.values()).filter(track => track.source === Track.Source.Camera);
|
22735
22888
|
try {
|
22736
22889
|
success = (yield Promise.all(tracks.map(t => {
|
22737
22890
|
var _a;
|
@@ -22742,7 +22895,7 @@ class Room extends eventsExports.EventEmitter {
|
|
22742
22895
|
throw e;
|
22743
22896
|
}
|
22744
22897
|
} else if (kind === 'audiooutput') {
|
22745
|
-
if (!supportsSetSinkId() && !this.options.
|
22898
|
+
if (!supportsSetSinkId() && !this.options.webAudioMix || this.options.webAudioMix && this.audioContext && !('setSinkId' in this.audioContext)) {
|
22746
22899
|
throw new Error('cannot switch audio output, setSinkId not supported');
|
22747
22900
|
}
|
22748
22901
|
(_a = (_c = this.options).audioOutput) !== null && _a !== void 0 ? _a : _c.audioOutput = {};
|
@@ -22750,11 +22903,11 @@ class Room extends eventsExports.EventEmitter {
|
|
22750
22903
|
this.options.audioOutput.deviceId = deviceId;
|
22751
22904
|
deviceHasChanged = prevDeviceId !== deviceConstraint;
|
22752
22905
|
try {
|
22753
|
-
if (this.options.
|
22906
|
+
if (this.options.webAudioMix) {
|
22754
22907
|
// @ts-expect-error setSinkId is not yet in the typescript type of AudioContext
|
22755
22908
|
(_b = this.audioContext) === null || _b === void 0 ? void 0 : _b.setSinkId(deviceId);
|
22756
22909
|
} else {
|
22757
|
-
yield Promise.all(Array.from(this.
|
22910
|
+
yield Promise.all(Array.from(this.remoteParticipants.values()).map(p => p.setAudioOutput({
|
22758
22911
|
deviceId
|
22759
22912
|
})));
|
22760
22913
|
}
|
@@ -22778,9 +22931,11 @@ class Room extends eventsExports.EventEmitter {
|
|
22778
22931
|
(_a = this.engine) === null || _a === void 0 ? void 0 : _a.close();
|
22779
22932
|
/* @ts-ignore */
|
22780
22933
|
this.engine = undefined;
|
22934
|
+
this.isResuming = false;
|
22781
22935
|
// clear out existing remote participants, since they may have attached
|
22782
22936
|
// the old engine
|
22783
|
-
this.
|
22937
|
+
this.remoteParticipants.clear();
|
22938
|
+
this.sidToIdentity.clear();
|
22784
22939
|
this.bufferedEvents = [];
|
22785
22940
|
this.maybeCreateEngine();
|
22786
22941
|
}
|
@@ -22810,19 +22965,19 @@ class Room extends eventsExports.EventEmitter {
|
|
22810
22965
|
return;
|
22811
22966
|
}
|
22812
22967
|
const parts = unpackStreamId(stream.id);
|
22813
|
-
const
|
22968
|
+
const participantSid = parts[0];
|
22814
22969
|
let streamId = parts[1];
|
22815
22970
|
let trackId = mediaTrack.id;
|
22816
22971
|
// firefox will get streamId (pID|trackId) instead of (pID|streamId) as it doesn't support sync tracks by stream
|
22817
22972
|
// and generates its own track id instead of infer from sdp track id.
|
22818
22973
|
if (streamId && streamId.startsWith('TR')) trackId = streamId;
|
22819
|
-
if (
|
22974
|
+
if (participantSid === this.localParticipant.sid) {
|
22820
22975
|
this.log.warn('tried to create RemoteParticipant for local participant', this.logContext);
|
22821
22976
|
return;
|
22822
22977
|
}
|
22823
|
-
const participant = this.
|
22978
|
+
const participant = Array.from(this.remoteParticipants.values()).find(p => p.sid === participantSid);
|
22824
22979
|
if (!participant) {
|
22825
|
-
this.log.error("Tried to add a track for a participant, that's not present. Sid: ".concat(
|
22980
|
+
this.log.error("Tried to add a track for a participant, that's not present. Sid: ".concat(participantSid), this.logContext);
|
22826
22981
|
return;
|
22827
22982
|
}
|
22828
22983
|
let adaptiveStreamSettings;
|
@@ -22840,18 +22995,19 @@ class Room extends eventsExports.EventEmitter {
|
|
22840
22995
|
let reason = arguments.length > 1 ? arguments[1] : undefined;
|
22841
22996
|
var _a;
|
22842
22997
|
this.clearConnectionReconcile();
|
22998
|
+
this.isResuming = false;
|
22843
22999
|
this.bufferedEvents = [];
|
22844
23000
|
if (this.state === ConnectionState.Disconnected) {
|
22845
23001
|
return;
|
22846
23002
|
}
|
22847
23003
|
this.regionUrl = undefined;
|
22848
23004
|
try {
|
22849
|
-
this.
|
22850
|
-
p.
|
23005
|
+
this.remoteParticipants.forEach(p => {
|
23006
|
+
p.trackPublications.forEach(pub => {
|
22851
23007
|
p.unpublishTrack(pub.trackSid);
|
22852
23008
|
});
|
22853
23009
|
});
|
22854
|
-
this.localParticipant.
|
23010
|
+
this.localParticipant.trackPublications.forEach(pub => {
|
22855
23011
|
var _a, _b;
|
22856
23012
|
if (pub.track) {
|
22857
23013
|
this.localParticipant.unpublishTrack(pub.track, shouldStopTracks);
|
@@ -22862,12 +23018,13 @@ class Room extends eventsExports.EventEmitter {
|
|
22862
23018
|
}
|
22863
23019
|
});
|
22864
23020
|
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.
|
23021
|
+
this.localParticipant.trackPublications.clear();
|
23022
|
+
this.localParticipant.videoTrackPublications.clear();
|
23023
|
+
this.localParticipant.audioTrackPublications.clear();
|
23024
|
+
this.remoteParticipants.clear();
|
23025
|
+
this.sidToIdentity.clear();
|
22869
23026
|
this.activeSpeakers = [];
|
22870
|
-
if (this.audioContext && typeof this.options.
|
23027
|
+
if (this.audioContext && typeof this.options.webAudioMix === 'boolean') {
|
22871
23028
|
this.audioContext.close();
|
22872
23029
|
this.audioContext = undefined;
|
22873
23030
|
}
|
@@ -22882,14 +23039,13 @@ class Room extends eventsExports.EventEmitter {
|
|
22882
23039
|
this.emit(RoomEvent.Disconnected, reason);
|
22883
23040
|
}
|
22884
23041
|
}
|
22885
|
-
handleParticipantDisconnected(
|
23042
|
+
handleParticipantDisconnected(identity, participant) {
|
22886
23043
|
// remove and send event
|
22887
|
-
this.
|
23044
|
+
this.remoteParticipants.delete(identity);
|
22888
23045
|
if (!participant) {
|
22889
23046
|
return;
|
22890
23047
|
}
|
22891
|
-
|
22892
|
-
participant.tracks.forEach(publication => {
|
23048
|
+
participant.trackPublications.forEach(publication => {
|
22893
23049
|
participant.unpublishTrack(publication.trackSid, true);
|
22894
23050
|
});
|
22895
23051
|
this.emit(RoomEvent.ParticipantDisconnected, participant);
|
@@ -22897,9 +23053,9 @@ class Room extends eventsExports.EventEmitter {
|
|
22897
23053
|
acquireAudioContext() {
|
22898
23054
|
var _a, _b;
|
22899
23055
|
return __awaiter(this, void 0, void 0, function* () {
|
22900
|
-
if (typeof this.options.
|
23056
|
+
if (typeof this.options.webAudioMix !== 'boolean' && this.options.webAudioMix.audioContext) {
|
22901
23057
|
// override audio context with custom audio context if supplied by user
|
22902
|
-
this.audioContext = this.options.
|
23058
|
+
this.audioContext = this.options.webAudioMix.audioContext;
|
22903
23059
|
} else if (!this.audioContext || this.audioContext.state === 'closed') {
|
22904
23060
|
// by using an AudioContext, it reduces lag on audio elements
|
22905
23061
|
// https://stackoverflow.com/questions/9811429/html5-audio-tag-on-safari-has-a-delay/54119854#54119854
|
@@ -22916,8 +23072,8 @@ class Room extends eventsExports.EventEmitter {
|
|
22916
23072
|
}));
|
22917
23073
|
}
|
22918
23074
|
}
|
22919
|
-
if (this.options.
|
22920
|
-
this.
|
23075
|
+
if (this.options.webAudioMix) {
|
23076
|
+
this.remoteParticipants.forEach(participant => participant.setAudioContext(this.audioContext));
|
22921
23077
|
}
|
22922
23078
|
this.localParticipant.setAudioContext(this.audioContext);
|
22923
23079
|
const newContextIsRunning = ((_b = this.audioContext) === null || _b === void 0 ? void 0 : _b.state) === 'running';
|
@@ -22927,18 +23083,18 @@ class Room extends eventsExports.EventEmitter {
|
|
22927
23083
|
}
|
22928
23084
|
});
|
22929
23085
|
}
|
22930
|
-
createParticipant(
|
23086
|
+
createParticipant(identity, info) {
|
22931
23087
|
var _a;
|
22932
23088
|
let participant;
|
22933
23089
|
if (info) {
|
22934
23090
|
participant = RemoteParticipant.fromParticipantInfo(this.engine.client, info);
|
22935
23091
|
} else {
|
22936
|
-
participant = new RemoteParticipant(this.engine.client,
|
23092
|
+
participant = new RemoteParticipant(this.engine.client, '', identity, undefined, undefined, {
|
22937
23093
|
loggerContextCb: () => this.logContext,
|
22938
23094
|
loggerName: this.options.loggerName
|
22939
23095
|
});
|
22940
23096
|
}
|
22941
|
-
if (this.options.
|
23097
|
+
if (this.options.webAudioMix) {
|
22942
23098
|
participant.setAudioContext(this.audioContext);
|
22943
23099
|
}
|
22944
23100
|
if ((_a = this.options.audioOutput) === null || _a === void 0 ? void 0 : _a.deviceId) {
|
@@ -22946,13 +23102,20 @@ class Room extends eventsExports.EventEmitter {
|
|
22946
23102
|
}
|
22947
23103
|
return participant;
|
22948
23104
|
}
|
22949
|
-
getOrCreateParticipant(
|
22950
|
-
if (this.
|
22951
|
-
|
23105
|
+
getOrCreateParticipant(identity, info) {
|
23106
|
+
if (this.remoteParticipants.has(identity)) {
|
23107
|
+
const existingParticipant = this.remoteParticipants.get(identity);
|
23108
|
+
if (info) {
|
23109
|
+
const wasUpdated = existingParticipant.updateInfo(info);
|
23110
|
+
if (wasUpdated) {
|
23111
|
+
this.sidToIdentity.set(info.sid, info.identity);
|
23112
|
+
}
|
23113
|
+
}
|
23114
|
+
return existingParticipant;
|
22952
23115
|
}
|
22953
|
-
const participant = this.createParticipant(
|
22954
|
-
this.
|
22955
|
-
this.
|
23116
|
+
const participant = this.createParticipant(identity, info);
|
23117
|
+
this.remoteParticipants.set(identity, participant);
|
23118
|
+
this.sidToIdentity.set(info.sid, info.identity);
|
22956
23119
|
// if we have valid info and the participant wasn't in the map before, we can assume the participant is new
|
22957
23120
|
// firing here to make sure that `ParticipantConnected` fires before the initial track events
|
22958
23121
|
this.emitWhenConnected(RoomEvent.ParticipantConnected, participant);
|
@@ -23003,11 +23166,11 @@ class Room extends eventsExports.EventEmitter {
|
|
23003
23166
|
return participant;
|
23004
23167
|
}
|
23005
23168
|
sendSyncState() {
|
23006
|
-
const remoteTracks = Array.from(this.
|
23007
|
-
acc.push(...participant.
|
23169
|
+
const remoteTracks = Array.from(this.remoteParticipants.values()).reduce((acc, participant) => {
|
23170
|
+
acc.push(...participant.getTrackPublications()); // FIXME would be nice to have this return RemoteTrackPublications directly instead of the type cast
|
23008
23171
|
return acc;
|
23009
23172
|
}, []);
|
23010
|
-
const localTracks = this.localParticipant.
|
23173
|
+
const localTracks = this.localParticipant.getTrackPublications(); // FIXME would be nice to have this return LocalTrackPublications directly instead of the type cast
|
23011
23174
|
this.engine.sendSyncState(remoteTracks, localTracks);
|
23012
23175
|
}
|
23013
23176
|
/**
|
@@ -23015,14 +23178,20 @@ class Room extends eventsExports.EventEmitter {
|
|
23015
23178
|
* subscription settings.
|
23016
23179
|
*/
|
23017
23180
|
updateSubscriptions() {
|
23018
|
-
for (const p of this.
|
23019
|
-
for (const pub of p.
|
23181
|
+
for (const p of this.remoteParticipants.values()) {
|
23182
|
+
for (const pub of p.videoTrackPublications.values()) {
|
23020
23183
|
if (pub.isSubscribed && pub instanceof RemoteTrackPublication) {
|
23021
23184
|
pub.emitTrackUpdate();
|
23022
23185
|
}
|
23023
23186
|
}
|
23024
23187
|
}
|
23025
23188
|
}
|
23189
|
+
getRemoteParticipantBySid(sid) {
|
23190
|
+
const identity = this.sidToIdentity.get(sid);
|
23191
|
+
if (identity) {
|
23192
|
+
return this.remoteParticipants.get(identity);
|
23193
|
+
}
|
23194
|
+
}
|
23026
23195
|
registerConnectionReconcile() {
|
23027
23196
|
this.clearConnectionReconcile();
|
23028
23197
|
let consecutiveFailures = 0;
|
@@ -23076,11 +23245,11 @@ class Room extends eventsExports.EventEmitter {
|
|
23076
23245
|
for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
|
23077
23246
|
args[_key - 1] = arguments[_key];
|
23078
23247
|
}
|
23079
|
-
if (this.state === ConnectionState.
|
23080
|
-
return this.emit(event, ...args);
|
23081
|
-
} else if (this.state === ConnectionState.Reconnecting) {
|
23248
|
+
if (this.state === ConnectionState.Reconnecting || this.isResuming || !this.engine || this.engine.pendingReconnect) {
|
23082
23249
|
// in case the room is reconnecting, buffer the events by firing them later after emitting RoomEvent.Reconnected
|
23083
23250
|
this.bufferedEvents.push([event, args]);
|
23251
|
+
} else if (this.state === ConnectionState.Connected) {
|
23252
|
+
return this.emit(event, ...args);
|
23084
23253
|
}
|
23085
23254
|
return false;
|
23086
23255
|
}
|
@@ -23859,5 +24028,5 @@ function isFacingModeValue(item) {
|
|
23859
24028
|
return item === undefined || allowedValues.includes(item);
|
23860
24029
|
}
|
23861
24030
|
|
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,
|
24031
|
+
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
24032
|
//# sourceMappingURL=livekit-client.esm.mjs.map
|