From 668e22fd24ba752447679ad95c0e2c61ecd2cdfe Mon Sep 17 00:00:00 2001 From: CloudWebRTC Date: Mon, 8 Jan 2024 19:07:25 +0800 Subject: [PATCH] Rename some parameters. (#435) * rename some parameters. * fix. * fix. * update. * mapping RemoteParticipant by identites. --- example/lib/pages/room.dart | 2 +- lib/src/core/room.dart | 108 +++++++++++++++++---------- lib/src/events.dart | 4 +- lib/src/participant/local.dart | 42 ++++++++++- lib/src/participant/participant.dart | 69 ++++++++++------- lib/src/participant/remote.dart | 47 ++++++++++-- lib/src/types/other.dart | 1 + test/core/room_e2e_test.dart | 10 +-- 8 files changed, 201 insertions(+), 82 deletions(-) diff --git a/example/lib/pages/room.dart b/example/lib/pages/room.dart index e0137ee38..d6998e2ff 100644 --- a/example/lib/pages/room.dart +++ b/example/lib/pages/room.dart @@ -164,7 +164,7 @@ class _RoomPageState extends State { void _sortParticipants() { List userMediaTracks = []; List screenTracks = []; - for (var participant in widget.room.participants.values) { + for (var participant in widget.room.remoteParticipants.values) { for (var t in participant.videoTracks) { if (t.isScreenShare) { screenTracks.add(ParticipantTrack( diff --git a/lib/src/core/room.dart b/lib/src/core/room.dart index c4a880ada..cbee89ad2 100644 --- a/lib/src/core/room.dart +++ b/lib/src/core/room.dart @@ -61,10 +61,11 @@ class Room extends DisposableChangeNotifier with EventsEmittable { ConnectOptions get connectOptions => engine.connectOptions; RoomOptions get roomOptions => engine.roomOptions; - /// map of SID to RemoteParticipant - UnmodifiableMapView get participants => - UnmodifiableMapView(_participants); - final _participants = {}; + /// map of identity: [[RemoteParticipant]] + UnmodifiableMapView get remoteParticipants => + UnmodifiableMapView(_remoteParticipants); + final _remoteParticipants = {}; + final Map _sidToIdentity = {}; /// the current participant LocalParticipant? get localParticipant => _localParticipant; @@ -250,9 +251,10 @@ class Room extends DisposableChangeNotifier with EventsEmittable { } for (final info in event.response.otherParticipants) { - logger.fine('Creating RemoteParticipant: ${info.sid}(${info.identity}) ' + logger.fine( + 'Creating RemoteParticipant: sid = ${info.sid}(identity:${info.identity}) ' 'tracks:${info.tracks.map((e) => e.sid)}'); - _getOrCreateRemoteParticipant(info.sid, info); + _getOrCreateRemoteParticipant(info.identity, info); } if (e2eeManager != null && event.response.sifTrailer.isNotEmpty) { @@ -314,12 +316,12 @@ class Room extends DisposableChangeNotifier with EventsEmittable { 'allowed:${event.allowed}'); // find participant - final participant = _participants[event.participantSid]; + final participant = _getRemoteParticipantBySid(event.participantSid); if (participant == null) { return; } // find track - final publication = participant.trackPublications[event.trackSid]; + final publication = participant.getTrackPublicationBySid(event.trackSid); if (publication == null) { return; } @@ -365,9 +367,10 @@ class Room extends DisposableChangeNotifier with EventsEmittable { events.emit(const RoomRestartingEvent()); // clean up RemoteParticipants - var copy = _participants.values.toList(); + var copy = _remoteParticipants.values.toList(); - _participants.clear(); + _remoteParticipants.clear(); + _sidToIdentity.clear(); _activeSpeakers.clear(); // reset params _name = null; @@ -388,7 +391,7 @@ class Room extends DisposableChangeNotifier with EventsEmittable { // re-publish all tracks await localParticipant?.rePublishAllTracks(); - for (var participant in participants.values) { + for (var participant in remoteParticipants.values) { for (var pub in participant.trackPublications.values) { if (pub.subscribed) { pub.sendUpdateTrackSettings(); @@ -437,7 +440,7 @@ class Room extends DisposableChangeNotifier with EventsEmittable { final idParts = event.stream.id.split('|'); final participantSid = idParts[0]; final trackSid = idParts.elementAtOrNull(1) ?? event.track.id; - final participant = _getOrCreateRemoteParticipant(participantSid, null); + final participant = _getRemoteParticipantBySid(participantSid); try { if (trackSid == null || trackSid.isEmpty) { throw TrackSubscriptionExceptionEvent( @@ -445,6 +448,13 @@ class Room extends DisposableChangeNotifier with EventsEmittable { reason: TrackSubscribeFailReason.invalidServerResponse, ); } + if (participant == null) { + throw TrackSubscriptionExceptionEvent( + participant: participant, + sid: trackSid, + reason: TrackSubscribeFailReason.noParticipantFound, + ); + } await participant.addSubscribedMediaTrack( event.track, event.stream, @@ -454,7 +464,7 @@ class Room extends DisposableChangeNotifier with EventsEmittable { ); } on TrackSubscriptionExceptionEvent catch (event) { logger.severe('addSubscribedMediaTrack() throwed ${event}'); - [participant.room.events, participant.events].emit(event); + events.emit(event); } catch (exception) { // We don't want to pass up any exception so catch everything here. logger.warning( @@ -480,9 +490,25 @@ class Room extends DisposableChangeNotifier with EventsEmittable { } } + /// retrieves a participant by identity + Participant? getParticipantByIdentity(String identity) { + if (_localParticipant?.identity == identity) { + return _localParticipant; + } + return remoteParticipants[identity]; + } + + RemoteParticipant? _getRemoteParticipantBySid(String sid) { + final identity = _sidToIdentity[sid]; + if (identity != null) { + return remoteParticipants[identity]; + } + return null; + } + RemoteParticipant _getOrCreateRemoteParticipant( - String sid, lk_models.ParticipantInfo? info) { - RemoteParticipant? participant = _participants[sid]; + String identity, lk_models.ParticipantInfo? info) { + RemoteParticipant? participant = _remoteParticipants[identity]; if (participant != null) { if (info != null) { participant.updateFromInfo(info); @@ -491,11 +517,11 @@ class Room extends DisposableChangeNotifier with EventsEmittable { } if (info == null) { - logger.warning('RemoteParticipant.info is null trackSid: $sid'); + logger.warning('RemoteParticipant.info is null identity: $identity'); participant = RemoteParticipant( room: this, - sid: sid, - identity: '', + sid: '', + identity: identity, name: '', ); } else { @@ -505,8 +531,8 @@ class Room extends DisposableChangeNotifier with EventsEmittable { ); } - _participants[sid] = participant; - + _remoteParticipants[identity] = participant; + _sidToIdentity[participant.sid] = identity; return participant; } @@ -515,28 +541,31 @@ class Room extends DisposableChangeNotifier with EventsEmittable { // trigger change notifier only if list of participants membership is changed var hasChanged = false; for (final info in updates) { - if (localParticipant?.sid == info.sid) { - localParticipant?.updateFromInfo(info); + if (localParticipant?.identity == info.identity) { + await localParticipant?.updateFromInfo(info); continue; } - final isNew = !_participants.containsKey(info.sid); + final isNew = !_remoteParticipants.containsKey(info.identity); if (info.state == lk_models.ParticipantInfo_State.DISCONNECTED && !isNew) { hasChanged = true; - await _handleParticipantDisconnect(info.sid); + await _handleParticipantDisconnect(info.identity); continue; } - final participant = _getOrCreateRemoteParticipant(info.sid, info); + final participant = _getOrCreateRemoteParticipant(info.identity, info); if (isNew) { hasChanged = true; // fire connected event emitWhenConnected(ParticipantConnectedEvent(participant: participant)); } else { - await participant.updateFromInfo(info); + final wasUpdated = await participant.updateFromInfo(info); + if (wasUpdated) { + _sidToIdentity[info.sid] = info.identity; + } } } @@ -546,13 +575,12 @@ class Room extends DisposableChangeNotifier with EventsEmittable { } void _onSignalSpeakersChangedEvent(List speakers) { - // final lastSpeakers = { for (final p in _activeSpeakers) p.sid: p, }; for (final speaker in speakers) { - Participant? p = _participants[speaker.sid]; + Participant? p = _getRemoteParticipantBySid(speaker.sid); if (speaker.sid == localParticipant?.sid) p = localParticipant; if (p == null) continue; @@ -580,7 +608,7 @@ class Room extends DisposableChangeNotifier with EventsEmittable { // localParticipant & remote participants final allParticipants = { if (localParticipant != null) localParticipant!.sid: localParticipant!, - ..._participants, + ..._remoteParticipants, }; for (final speaker in speakers) { @@ -612,7 +640,7 @@ class Room extends DisposableChangeNotifier with EventsEmittable { if (entry.participantSid == localParticipant?.sid) { participant = localParticipant; } else { - participant = _participants[entry.participantSid]; + participant = _getRemoteParticipantBySid(entry.participantSid); } if (participant != null) { @@ -626,7 +654,7 @@ class Room extends DisposableChangeNotifier with EventsEmittable { List updates) async { for (final update in updates) { // try to find RemoteParticipant - final participant = participants[update.participantSid]; + final participant = remoteParticipants[update.participantSid]; if (participant == null) continue; // try to find RemoteTrackPublication final trackPublication = participant.trackPublications[update.trackSid]; @@ -646,7 +674,8 @@ class Room extends DisposableChangeNotifier with EventsEmittable { final senderSid = dataPacketEvent.packet.participantSid; RemoteParticipant? senderParticipant; if (senderSid.isNotEmpty) { - senderParticipant = participants[dataPacketEvent.packet.participantSid]; + senderParticipant = + _getRemoteParticipantBySid(dataPacketEvent.packet.participantSid); } // participant.delegate?.onDataReceived(participant, event.packet.payload); @@ -661,13 +690,13 @@ class Room extends DisposableChangeNotifier with EventsEmittable { events.emit(event); } - Future _handleParticipantDisconnect(String sid) async { - final participant = _participants.remove(sid); + Future _handleParticipantDisconnect(String identity) async { + final participant = _remoteParticipants.remove(identity); if (participant == null) { return; } - await participant.unpublishAllTracks(notify: true); + await participant.removeAllPublishedTracks(notify: true); emitWhenConnected(ParticipantDisconnectedEvent(participant: participant)); } @@ -675,7 +704,7 @@ class Room extends DisposableChangeNotifier with EventsEmittable { Future _sendSyncState() async { final sendUnSub = connectOptions.autoSubscribe; final participantTracks = - participants.values.map((e) => e.participantTracks()); + remoteParticipants.values.map((e) => e.participantTracks()); engine.sendSyncState( subscription: lk_rtc.UpdateSubscription( participantTracks: participantTracks, @@ -693,12 +722,13 @@ extension RoomPrivateMethods on Room { logger.fine('[${objectId}] cleanUp()'); // clean up RemoteParticipants - var participants = _participants.values.toList(); + var participants = _remoteParticipants.values.toList(); for (final participant in participants) { // RemoteParticipant is responsible for disposing resources await participant.dispose(); } - _participants.clear(); + _remoteParticipants.clear(); + _sidToIdentity.clear(); // clean up LocalParticipant await localParticipant?.unpublishAllTracks(); @@ -777,7 +807,7 @@ extension RoomHardwareManagementMethods on Room { /// Set audio output device. Future setAudioOutputDevice(MediaDevice device) async { if (lkPlatformIs(PlatformType.web)) { - participants.forEach((_, participant) { + remoteParticipants.forEach((_, participant) { for (var audioTrack in participant.audioTracks) { audioTrack.track?.setSinkId(device.deviceId); } diff --git a/lib/src/events.dart b/lib/src/events.dart index ce2513db9..494f8d49b 100644 --- a/lib/src/events.dart +++ b/lib/src/events.dart @@ -264,11 +264,11 @@ class TrackSubscribedEvent with RoomEvent, ParticipantEvent { /// An error has occured during track subscription. /// Emitted by [Room] and [RemoteParticipant]. class TrackSubscriptionExceptionEvent with RoomEvent, ParticipantEvent { - final RemoteParticipant participant; + final RemoteParticipant? participant; final String? sid; final TrackSubscribeFailReason reason; const TrackSubscriptionExceptionEvent({ - required this.participant, + this.participant, this.sid, required this.reason, }); diff --git a/lib/src/participant/local.dart b/lib/src/participant/local.dart index ab5cce43a..a05249f69 100644 --- a/lib/src/participant/local.dart +++ b/lib/src/participant/local.dart @@ -55,6 +55,10 @@ class LocalParticipant extends Participant { name: info.name, ) { updateFromInfo(info); + + onDispose(() async { + await unpublishAllTracks(); + }); } /// Publish an [AudioTrack] to the [Room]. @@ -333,8 +337,6 @@ class LocalParticipant extends Participant { return pub; } - /// Unpublish a [LocalTrackPublication] that's already published by this [LocalParticipant]. - @override Future removePublishedTrack(String trackSid, {bool notify = true}) async { logger.finer('Unpublish track sid: $trackSid, notify: $notify'); @@ -387,6 +389,15 @@ class LocalParticipant extends Participant { await pub.dispose(); } + /// Convenience method to unpublish all tracks. + Future unpublishAllTracks( + {bool notify = true, bool? stopOnUnpublish}) async { + final trackSids = trackPublications.keys.toSet(); + for (final trackid in trackSids) { + await removePublishedTrack(trackid, notify: notify); + } + } + Future rePublishAllTracks() async { final tracks = trackPublications.values.toList(); trackPublications.clear(); @@ -461,6 +472,33 @@ class LocalParticipant extends Participant { .whereType>() .toList(); + @override + LocalTrackPublication? getTrackPublicationByName(String name) { + final track = super.getTrackPublicationByName(name); + if (track != null) { + return track; + } + return null; + } + + @override + LocalTrackPublication? getTrackPublicationBySid(String sid) { + final track = super.getTrackPublicationBySid(sid); + if (track != null) { + return track; + } + return null; + } + + @override + LocalTrackPublication? getTrackPublicationBySource(TrackSource source) { + final track = super.getTrackPublicationBySource(source); + if (track != null) { + return track; + } + return null; + } + /// Shortcut for publishing a [TrackSource.camera] Future setCameraEnabled(bool enabled, {CameraCaptureOptions? cameraCaptureOptions}) async { diff --git a/lib/src/participant/participant.dart b/lib/src/participant/participant.dart index a30b1dd61..0acb0a559 100644 --- a/lib/src/participant/participant.dart +++ b/lib/src/participant/participant.dart @@ -136,7 +136,6 @@ abstract class Participant onDispose(() async { await events.dispose(); - await unpublishAllTracks(); }); } @@ -182,7 +181,13 @@ abstract class Participant /// for internal use /// {@nodoc} @internal - void updateFromInfo(lk_models.ParticipantInfo info) { + Future updateFromInfo(lk_models.ParticipantInfo info) async { + if (_participantInfo != null && + _participantInfo!.sid == info.sid && + _participantInfo!.version > info.version) { + return false; + } + identity = info.identity; sid = info.sid; updateName(info.name); @@ -191,6 +196,8 @@ abstract class Participant } _participantInfo = info; setPermissions(info.permission.toLKType()); + + return true; } @internal @@ -220,33 +227,28 @@ abstract class Participant trackPublications[pub.sid] = pub; } - // Must be implemented by subclasses. - Future removePublishedTrack(String trackSid, {bool notify = true}); - - /// Convenience method to unpublish all tracks. - Future unpublishAllTracks( - {bool notify = true, bool? stopOnUnpublish}) async { - final trackSids = trackPublications.keys.toSet(); - for (final trackid in trackSids) { - await removePublishedTrack(trackid, notify: notify); - } - } - - /// Convenience property to check whether [TrackSource.camera] is published or not. - bool isCameraEnabled() { - return !(getTrackPublicationBySource(TrackSource.camera)?.muted ?? true); + /// get a [TrackPublication] by its sid. + /// returns null when not found. + T? getTrackPublicationBySid(String sid) { + final pub = trackPublications[sid]; + if (pub is T) return pub; + return null; } - /// Convenience property to check whether [TrackSource.microphone] is published or not. - bool isMicrophoneEnabled() { - return !(getTrackPublicationBySource(TrackSource.microphone)?.muted ?? - true); + /// get a [TrackPublication] by its name. + /// returns null when not found. + T? getTrackPublicationByName(String name) { + for (final pub in trackPublications.values) { + if (pub.name == name) { + return pub; + } + } + return null; } - /// Convenience property to check whether [TrackSource.screenShareVideo] is published or not. - bool isScreenShareEnabled() { - return !(getTrackPublicationBySource(TrackSource.screenShareVideo)?.muted ?? - true); + /// get all [TrackPublication]s. + List getTrackPublications() { + return trackPublications.values.toList(); } /// Tries to find a [TrackPublication] by its [TrackSource]. Otherwise, will @@ -272,6 +274,23 @@ abstract class Participant e.kind == lk_models.TrackType.AUDIO)); } + /// Convenience property to check whether [TrackSource.camera] is published or not. + bool isCameraEnabled() { + return !(getTrackPublicationBySource(TrackSource.camera)?.muted ?? true); + } + + /// Convenience property to check whether [TrackSource.microphone] is published or not. + bool isMicrophoneEnabled() { + return !(getTrackPublicationBySource(TrackSource.microphone)?.muted ?? + true); + } + + /// Convenience property to check whether [TrackSource.screenShareVideo] is published or not. + bool isScreenShareEnabled() { + return !(getTrackPublicationBySource(TrackSource.screenShareVideo)?.muted ?? + true); + } + /// (Equality operator) [Participant.hashCode] is same as [sid.hashCode]. @override int get hashCode => sid.hashCode; diff --git a/lib/src/participant/remote.dart b/lib/src/participant/remote.dart index e9f183a72..4cef410b4 100644 --- a/lib/src/participant/remote.dart +++ b/lib/src/participant/remote.dart @@ -78,9 +78,30 @@ class RemoteParticipant extends Participant { .cast() .toList(); - RemoteTrackPublication? getTrackPublication(String sid) { - final pub = trackPublications[sid]; - if (pub is RemoteTrackPublication) return pub; + @override + RemoteTrackPublication? getTrackPublicationByName(String name) { + final track = super.getTrackPublicationByName(name); + if (track != null) { + return track; + } + return null; + } + + @override + RemoteTrackPublication? getTrackPublicationBySid(String sid) { + final track = super.getTrackPublicationBySid(sid); + if (track != null) { + return track; + } + return null; + } + + @override + RemoteTrackPublication? getTrackPublicationBySource(TrackSource source) { + final track = super.getTrackPublicationBySource(source); + if (track != null) { + return track; + } return null; } @@ -97,7 +118,7 @@ class RemoteParticipant extends Participant { logger.fine('addSubscribedMediaTrack()'); // If publication doesn't exist yet... - RemoteTrackPublication? pub = getTrackPublication(trackSid); + RemoteTrackPublication? pub = getTrackPublicationBySid(trackSid); if (pub == null) { logger.fine('addSubscribedMediaTrack() pub is null, will wait...'); logger.fine('addSubscribedMediaTrack() tracks: $trackPublications'); @@ -177,15 +198,17 @@ class RemoteParticipant extends Participant { /// {@nodoc} @override @internal - Future updateFromInfo(lk_models.ParticipantInfo info) async { + Future updateFromInfo(lk_models.ParticipantInfo info) async { logger.fine('RemoteParticipant.updateFromInfo(info: $info)'); - super.updateFromInfo(info); + if (!await super.updateFromInfo(info)) { + //return false; + } // figuring out deltas between tracks final newPubs = {}; for (final trackInfo in info.tracks) { - RemoteTrackPublication? pub = getTrackPublication(trackInfo.sid); + RemoteTrackPublication? pub = getTrackPublicationBySid(trackInfo.sid); if (pub == null) { final RemoteTrackPublication pub; if (trackInfo.type == lk_models.TrackType.VIDEO) { @@ -226,9 +249,10 @@ class RemoteParticipant extends Participant { for (final sid in removeSids) { await removePublishedTrack(sid); } + + return true; } - @override Future removePublishedTrack(String trackSid, {bool notify = true}) async { logger.finer('removePublishedTrack track sid: $trackSid, notify: $notify'); @@ -260,6 +284,13 @@ class RemoteParticipant extends Participant { await pub.dispose(); } + Future removeAllPublishedTracks({bool notify = true}) async { + final sids = trackPublications.keys.toList(); + for (final sid in sids) { + await removePublishedTrack(sid, notify: notify); + } + } + @internal lk_models.ParticipantTracks participantTracks() => lk_models.ParticipantTracks( diff --git a/lib/src/types/other.dart b/lib/src/types/other.dart index 28ea642da..92a390f3f 100644 --- a/lib/src/types/other.dart +++ b/lib/src/types/other.dart @@ -95,6 +95,7 @@ enum TrackSubscribeFailReason { invalidServerResponse, notTrackMetadataFound, unsupportedTrackType, + noParticipantFound, // ... } diff --git a/test/core/room_e2e_test.dart b/test/core/room_e2e_test.dart index b064585bd..5287d747f 100644 --- a/test/core/room_e2e_test.dart +++ b/test/core/room_e2e_test.dart @@ -51,12 +51,12 @@ void main() { expect( room.events.streamCtrl.stream, emitsInOrder([ - predicate( + predicate( (event) => event.participant.sid == remoteParticipantData.sid, ), - predicate( + predicate( (event) => event.participant.sid == remoteParticipantData.sid, - ) + ), ]), ); @@ -64,7 +64,7 @@ void main() { await room.events.waitFor( duration: const Duration(seconds: 1)); - expect(room.participants.length, 1); + expect(room.remoteParticipants.length, 1); }); test('participant disconnect', () async { @@ -85,7 +85,7 @@ void main() { await room.events.waitFor( duration: const Duration(seconds: 1)); - expect(room.participants.length, 0); + expect(room.remoteParticipants.length, 0); }); test('participant metadata changed', () async {