Switch to new event system (#9)

* `SignalEvents` and `synchronized` mode for `EventsListenable`

* cascade syntax

* clean up

* `RoomEvents`

* `ParticipantEvents`

* all events implemented

* make it compile with M1 macs

* dispose logic

* cleaner connect logic

* ask to publish

* fix initial EngineTrackAddedEvent glitch

* fix unpublishTrack bug

* clean up

* better events docs

* organize event types

* clean up

* cleaner wait logic

* wait for event instead of using Completer

* notifyListeners
This commit is contained in:
Hiroshi Horie
2021-09-24 23:37:26 +09:00
committed by GitHub
parent f4bd82a919
commit 6445b8d2a8
36 changed files with 1270 additions and 948 deletions
+67 -69
View File
@@ -1,50 +1,16 @@
import 'package:collection/collection.dart';
import 'package:flutter/foundation.dart';
import 'package:meta/meta.dart';
import '../classes/change_notifier.dart';
import '../events.dart';
import '../extensions.dart';
import '../logger.dart';
import '../managers/event.dart';
import '../proto/livekit_models.pb.dart' as lk_models;
import '../track/remote_track_publication.dart';
import '../track/track.dart';
import '../track/track_publication.dart';
import 'remote_participant.dart';
/// Callbacks for participant changes
mixin ParticipantDelegate {
/// The participant's metadata has changed
void onMetadataChanged(Participant participant) {}
/// The participant's isSpeaking property has changed
void onSpeakingChanged(Participant participant, bool speaking) {}
/// This participant has muted one of their tracks
void onTrackMuted(Participant participant, TrackPublication publication) {}
/// This participant has unmuted one of their tracks
void onTrackUnmuted(Participant participant, TrackPublication publication) {}
/// This participant has published a new [Track] to the [Room].
void onTrackPublished(RemoteParticipant participant, RemoteTrackPublication publication) {}
/// This participant has unpublished one of their [Track].
void onTrackUnpublished(RemoteParticipant participant, RemoteTrackPublication publication) {}
/// The [LocalParticipant] has subscribed to a new track published by this
/// [RemoteParticipant]
void onTrackSubscribed(
RemoteParticipant participant, Track track, RemoteTrackPublication publication) {}
/// The [LocalParticipant] has unsubscribed from a track published by this
/// [RemoteParticipant]. This event is fired when the track was unpublished
void onTrackUnsubscribed(
RemoteParticipant participant, Track track, RemoteTrackPublication publication) {}
/// Data received from this [RemoteParticipant].
void onDataReceived(RemoteParticipant participant, List<int> data) {}
/// An error has occured during track subscription.
void onTrackSubscriptionFailed(RemoteParticipant participant, String sid, String? message) {}
}
/// Represents a Participant in the room, notifies changes via delegates as
/// well as ChangeNotifier/providers.
/// A change notification is triggered when
@@ -52,15 +18,18 @@ mixin ParticipantDelegate {
/// - mute status changed
/// - added/removed subscribed tracks
/// - metadata changed
class Participant extends ChangeNotifier {
/// Base for [RemoteParticipant] and [LocalParticipant],
/// can not be instantiated directly.
abstract class Participant extends LKChangeNotifier {
/// map of track sid => published track
Map<String, TrackPublication> tracks = {};
final trackPublications = <String, TrackPublication>{};
/// audio level between 0-1, 1 being the loudest
double audioLevel = 0;
/// server assigned unique id
String sid;
final String sid;
/// user-assigned identity
String identity;
@@ -71,16 +40,12 @@ class Participant extends ChangeNotifier {
/// when the participant had last spoken
DateTime? lastSpokeAt;
ParticipantDelegate? roomDelegate;
/// delegate to receive participant callbacks
ParticipantDelegate? delegate;
lk_models.ParticipantInfo? _participantInfo;
bool _isSpeaking = false;
// suppport for multiple event listeners
final events = EventsEmitter<ParticipantEvent>();
final EventsEmitter<RoomEvent> roomEvents;
/// when the participant joined the room
DateTime get joinedAt {
@@ -95,23 +60,40 @@ class Participant extends ChangeNotifier {
bool get isSpeaking => _isSpeaking;
/// true if participant is publishing an audio track and is muted
bool get isMuted {
if (audioTracks.isEmpty) return false;
return audioTracks.first.muted;
}
bool get isMuted => audioTracks.firstOrNull?.muted ?? true;
bool get hasAudio => audioTracks.isNotEmpty;
bool get hasVideo => videoTracks.isNotEmpty;
/// tracks that are subscribed to
List<TrackPublication> get subscribedTracks => tracks.values.where((e) => e.subscribed).toList();
List<TrackPublication> get subscribedTracks =>
trackPublications.values.where((e) => e.subscribed).toList();
/// for internal use
/// {@nodoc}
@internal
bool get hasInfo => _participantInfo != null;
Participant(this.sid, this.identity);
Participant(
this.sid,
this.identity, {
required this.roomEvents,
}) {
// Any event emitted will trigger ChangeNotifier
events.listen((event) {
logger.fine('[ParticipantEvent] $event, will notifyListeners()');
notifyListeners();
});
}
@override
@mustCallSuper
Future<void> dispose() async {
logger.fine('$objectId dispose()');
await events.dispose();
super.dispose();
}
/// for internal use
/// {@nodoc}
@@ -123,26 +105,29 @@ class Participant extends ChangeNotifier {
if (speaking) {
lastSpokeAt = DateTime.now();
}
delegate?.onSpeakingChanged(this, speaking);
roomDelegate?.onSpeakingChanged(this, speaking);
notifyListeners();
[events, roomEvents].emit(SpeakingChangedEvent(
participant: this,
speaking: speaking,
));
}
void _setMetadata(String md) {
final changed = _participantInfo?.metadata != md;
metadata = md;
if (changed) {
delegate?.onMetadataChanged(this);
roomDelegate?.onMetadataChanged(this);
notifyListeners();
[events, roomEvents].emit(ParticipantMetadataUpdatedEvent(
participant: this,
));
}
}
/// for internal use
/// {@nodoc}
@internal
void updateFromInfo(lk_models.ParticipantInfo info) {
identity = info.identity;
sid = info.sid;
// participantSid = info.sid;
if (info.metadata.isNotEmpty) {
_setMetadata(info.metadata);
}
@@ -151,23 +136,36 @@ class Participant extends ChangeNotifier {
/// for internal use
/// {@nodoc}
void muteChanged() {
notifyListeners();
}
/// for internal use
/// {@nodoc}
@internal
void addTrackPublication(TrackPublication pub) {
pub.track?.sid = pub.sid;
tracks[pub.sid] = pub;
trackPublications[pub.sid] = pub;
}
// Must implement
Future<void> unpublishTrack(String trackSid, {bool notify = false});
Future<void> unpublishAllTracks() async {
final _ = List<TrackPublication>.from(trackPublications.values);
for (final track in _) {
await unpublishTrack(track.sid);
}
}
// Equality operators
// Object is considered equal when sid is equal
@override
int get hashCode => sid.hashCode;
@override
bool operator ==(Object other) => other is Participant && sid == other.sid;
}
// Convenience extension
extension ParticipantExt on Participant {
List<TrackPublication> get videoTracks =>
tracks.values.where((e) => e.kind == lk_models.TrackType.VIDEO).toList();
trackPublications.values.where((e) => e.kind == lk_models.TrackType.VIDEO).toList();
List<TrackPublication> get audioTracks =>
tracks.values.where((e) => e.kind == lk_models.TrackType.AUDIO).toList();
trackPublications.values.where((e) => e.kind == lk_models.TrackType.AUDIO).toList();
}