livekit-client 1.15.12 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +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
|