Skip to content

Commit 87250d5

Browse files
committed
imp1
1 parent db4ad8c commit 87250d5

File tree

2 files changed

+83
-47
lines changed

2 files changed

+83
-47
lines changed
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// Copyright 2024 LiveKit, Inc.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
import 'dart:collection';
16+
17+
import '../participant/participant.dart';
18+
19+
/// Small helper to keep participant lookups by identity and sid in sync.
20+
class ParticipantCollection<T extends Participant> extends IterableBase<T> {
21+
final Map<String, T> _byIdentity = {};
22+
final Map<String, T> _bySid = {};
23+
24+
// Read-only views of the collections
25+
UnmodifiableMapView<String, T> get byIdentity => UnmodifiableMapView(_byIdentity);
26+
UnmodifiableMapView<String, T> get bySid => UnmodifiableMapView(_bySid);
27+
28+
// Update methods
29+
void set(T participant) {
30+
_byIdentity[participant.identity] = participant;
31+
_bySid[participant.sid] = participant;
32+
}
33+
34+
// Contains methods
35+
bool containsIdentity(String identity) => _byIdentity.containsKey(identity);
36+
bool containsSid(String sid) => _bySid.containsKey(sid);
37+
38+
Iterable<T> removeAll() {
39+
final copy = _byIdentity.values.toList();
40+
_byIdentity.clear();
41+
_bySid.clear();
42+
return copy;
43+
}
44+
45+
bool removeByIdentity(String identity) {
46+
final participant = _byIdentity.remove(identity);
47+
if (participant != null) {
48+
_bySid.remove(participant.sid);
49+
return true;
50+
}
51+
return false;
52+
}
53+
54+
@override
55+
Iterator<T> get iterator => _byIdentity.values.iterator;
56+
}

lib/src/core/room.dart

Lines changed: 27 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ import '../types/rpc.dart';
5454
import '../types/transcription_segment.dart';
5555
import '../utils.dart' show unpackStreamId;
5656
import 'engine.dart';
57+
import 'participant_collection.dart';
5758
import 'pending_track_queue.dart';
5859

5960
/// Room is the primary construct for LiveKit conferences. It contains a
@@ -72,10 +73,9 @@ class Room extends DisposableChangeNotifier with EventsEmittable<RoomEvent> {
7273
ConnectOptions get connectOptions => engine.connectOptions;
7374
RoomOptions get roomOptions => engine.roomOptions;
7475

75-
/// map of identity: [[RemoteParticipant]]
76-
UnmodifiableMapView<String, RemoteParticipant> get remoteParticipants => UnmodifiableMapView(_remoteParticipants);
77-
final _remoteParticipants = <String, RemoteParticipant>{};
78-
final Map<String, String> _sidToIdentity = <String, String>{};
76+
final ParticipantCollection<RemoteParticipant> _remoteParticipants = ParticipantCollection();
77+
UnmodifiableMapView<String, RemoteParticipant> get remoteParticipants =>
78+
UnmodifiableMapView(_remoteParticipants.byIdentity);
7979

8080
/// the current participant
8181
LocalParticipant? get localParticipant => _localParticipant;
@@ -375,7 +375,7 @@ class Room extends DisposableChangeNotifier with EventsEmittable<RoomEvent> {
375375
'allowed:${event.allowed}');
376376

377377
// find participant
378-
final participant = _getRemoteParticipantBySid(event.participantSid);
378+
final participant = _remoteParticipants.bySid[event.participantSid];
379379
if (participant == null) {
380380
return;
381381
}
@@ -522,10 +522,7 @@ class Room extends DisposableChangeNotifier with EventsEmittable<RoomEvent> {
522522
events.emit(const RoomReconnectingEvent());
523523

524524
// clean up RemoteParticipants
525-
final copy = _remoteParticipants.values.toList();
526-
527-
_remoteParticipants.clear();
528-
_sidToIdentity.clear();
525+
final copy = _remoteParticipants.removeAll().toList();
529526
_activeSpeakers.clear();
530527
// reset params
531528
_name = null;
@@ -544,7 +541,7 @@ class Room extends DisposableChangeNotifier with EventsEmittable<RoomEvent> {
544541
// re-publish all tracks
545542
await localParticipant?.rePublishAllTracks();
546543

547-
for (var participant in remoteParticipants.values) {
544+
for (var participant in _remoteParticipants) {
548545
for (var pub in participant.trackPublications.values) {
549546
if (pub.subscribed) {
550547
pub.sendUpdateTrackSettings();
@@ -603,7 +600,7 @@ class Room extends DisposableChangeNotifier with EventsEmittable<RoomEvent> {
603600
trackSid = streamId;
604601
}
605602

606-
final participant = _getRemoteParticipantBySid(participantSid);
603+
final participant = _remoteParticipants.bySid[participantSid];
607604
try {
608605
if (trackSid == null || trackSid.isEmpty) {
609606
throw TrackSubscriptionExceptionEvent(
@@ -667,23 +664,15 @@ class Room extends DisposableChangeNotifier with EventsEmittable<RoomEvent> {
667664
if (_localParticipant?.identity == identity) {
668665
return _localParticipant;
669666
}
670-
return remoteParticipants[identity];
671-
}
672-
673-
RemoteParticipant? _getRemoteParticipantBySid(String sid) {
674-
final identity = _sidToIdentity[sid];
675-
if (identity != null) {
676-
return remoteParticipants[identity];
677-
}
678-
return null;
667+
return _remoteParticipants.byIdentity[identity];
679668
}
680669

681670
Future<ParticipantCreationResult> _getOrCreateRemoteParticipant(lk_models.ParticipantInfo info) async {
682671
if (!info.hasIdentity() || !info.hasSid()) {
683672
throw Exception('ParticipantInfo must have identity and sid');
684673
}
685674

686-
final participant = _remoteParticipants[info.identity];
675+
final participant = _remoteParticipants.byIdentity[info.identity];
687676
if (participant != null) {
688677
// Return existing participant with no new publications; caller handles updates.
689678
return ParticipantCreationResult(
@@ -697,8 +686,7 @@ class Room extends DisposableChangeNotifier with EventsEmittable<RoomEvent> {
697686
info: info,
698687
);
699688

700-
_remoteParticipants[result.participant.identity] = result.participant;
701-
_sidToIdentity[result.participant.sid] = result.participant.identity;
689+
_remoteParticipants.set(result.participant);
702690
events.emit(InternalParticipantAvailableEvent(participant: result.participant));
703691
return result;
704692
}
@@ -720,7 +708,7 @@ class Room extends DisposableChangeNotifier with EventsEmittable<RoomEvent> {
720708
continue;
721709
}
722710

723-
final isNew = !_remoteParticipants.containsKey(info.identity);
711+
final isNew = !_remoteParticipants.containsIdentity(info.identity);
724712

725713
if (info.state == lk_models.ParticipantInfo_State.DISCONNECTED) {
726714
hasChanged = await _handleParticipantDisconnect(info.identity);
@@ -743,12 +731,12 @@ class Room extends DisposableChangeNotifier with EventsEmittable<RoomEvent> {
743731
[result.participant.events, events].emit(event);
744732
}
745733
}
746-
_sidToIdentity[info.sid] = info.identity;
734+
_remoteParticipants.set(result.participant);
747735
events.emit(InternalParticipantAvailableEvent(participant: result.participant));
748736
} else {
749737
final wasUpdated = await result.participant.updateFromInfo(info);
750738
if (wasUpdated) {
751-
_sidToIdentity[info.sid] = info.identity;
739+
_remoteParticipants.set(result.participant);
752740
events.emit(InternalParticipantAvailableEvent(participant: result.participant));
753741
}
754742
}
@@ -765,7 +753,7 @@ class Room extends DisposableChangeNotifier with EventsEmittable<RoomEvent> {
765753
};
766754

767755
for (final speaker in speakers) {
768-
Participant? p = _getRemoteParticipantBySid(speaker.sid);
756+
Participant? p = _remoteParticipants.bySid[speaker.sid];
769757
if (speaker.sid == localParticipant?.sid) p = localParticipant;
770758
if (p == null) continue;
771759

@@ -788,7 +776,7 @@ class Room extends DisposableChangeNotifier with EventsEmittable<RoomEvent> {
788776
isConnected: connectionState == ConnectionState.connected,
789777
participantSid: participant?.sid,
790778
subscriber: (pending) async {
791-
final target = participant ?? _getRemoteParticipantBySid(pending.participantSid);
779+
final target = participant ?? _remoteParticipants.bySid[pending.participantSid];
792780
if (target == null) return false;
793781
try {
794782
await target.addSubscribedMediaTrack(
@@ -818,7 +806,7 @@ class Room extends DisposableChangeNotifier with EventsEmittable<RoomEvent> {
818806
// localParticipant & remote participants
819807
final allParticipants = <String, Participant>{
820808
if (localParticipant != null) localParticipant!.sid: localParticipant!,
821-
..._remoteParticipants,
809+
..._remoteParticipants.bySid,
822810
};
823811

824812
for (final speaker in speakers) {
@@ -849,7 +837,7 @@ class Room extends DisposableChangeNotifier with EventsEmittable<RoomEvent> {
849837
if (entry.participantSid == localParticipant?.sid) {
850838
participant = localParticipant;
851839
} else {
852-
participant = _getRemoteParticipantBySid(entry.participantSid);
840+
participant = _remoteParticipants.bySid[entry.participantSid];
853841
}
854842

855843
if (participant != null) {
@@ -861,13 +849,8 @@ class Room extends DisposableChangeNotifier with EventsEmittable<RoomEvent> {
861849

862850
void _onSignalStreamStateUpdateEvent(List<lk_rtc.StreamStateInfo> updates) async {
863851
for (final update in updates) {
864-
final identity = _sidToIdentity[update.participantSid];
865-
if (identity == null) {
866-
logger.warning('participant not found for sid ${update.participantSid}');
867-
continue;
868-
}
869852
// try to find RemoteParticipant
870-
final participant = remoteParticipants[identity];
853+
final participant = _remoteParticipants.bySid[update.participantSid];
871854
if (participant == null) continue;
872855
// try to find RemoteTrackPublication
873856
final trackPublication = participant.trackPublications[update.trackSid];
@@ -935,10 +918,9 @@ class Room extends DisposableChangeNotifier with EventsEmittable<RoomEvent> {
935918
}
936919

937920
Future<bool> _handleParticipantDisconnect(String identity) async {
938-
final participant = _remoteParticipants.remove(identity);
939-
if (participant == null) {
940-
return false;
941-
}
921+
final participant = _remoteParticipants.byIdentity[identity];
922+
final removed = _remoteParticipants.removeByIdentity(identity);
923+
if (!removed || participant == null) return false;
942924

943925
validateParticipantHasNoActiveDataStreams(identity);
944926

@@ -954,7 +936,7 @@ class Room extends DisposableChangeNotifier with EventsEmittable<RoomEvent> {
954936
final trackSids = <String>[];
955937
final trackSidsDisabled = <String>[];
956938

957-
for (var participant in remoteParticipants.values) {
939+
for (var participant in _remoteParticipants) {
958940
for (var track in participant.trackPublications.values) {
959941
if (track.subscribed != autoSubscribe) {
960942
trackSids.add(track.sid);
@@ -983,14 +965,12 @@ extension RoomPrivateMethods on Room {
983965
logger.fine('[${objectId}] cleanUp()');
984966

985967
// clean up RemoteParticipants
986-
final participants = _remoteParticipants.values.toList();
968+
final participants = _remoteParticipants.removeAll().toList();
987969
for (final participant in participants) {
988970
await participant.removeAllPublishedTracks(notify: false);
989971
// RemoteParticipant is responsible for disposing resources
990972
await participant.dispose();
991973
}
992-
_remoteParticipants.clear();
993-
_sidToIdentity.clear();
994974

995975
// clean up LocalParticipant
996976
await localParticipant?.unpublishAllTracks();
@@ -1102,11 +1082,11 @@ extension RoomHardwareManagementMethods on Room {
11021082
/// Set audio output device.
11031083
Future<void> setAudioOutputDevice(MediaDevice device) async {
11041084
if (lkPlatformIs(PlatformType.web)) {
1105-
remoteParticipants.forEach((_, participant) {
1106-
for (var audioTrack in participant.audioTrackPublications) {
1085+
for (final participant in _remoteParticipants) {
1086+
for (final audioTrack in participant.audioTrackPublications) {
11071087
audioTrack.track?.setSinkId(device.deviceId);
11081088
}
1109-
});
1089+
}
11101090
Hardware.instance.selectedAudioOutput = device;
11111091
} else {
11121092
await Hardware.instance.selectAudioOutput(device);

0 commit comments

Comments
 (0)