diff --git a/lib/src/core/room.dart b/lib/src/core/room.dart index 3f3c84e44..1fd102e69 100644 --- a/lib/src/core/room.dart +++ b/lib/src/core/room.dart @@ -211,6 +211,10 @@ class Room extends DisposableChangeNotifier with EventsEmittable { } else { await publication?.unmute(); } + }) + ..on((event) async { + // unpublish local track + await localParticipant?.unpublishTrack(event.trackSid); }); void _setUpEngineListeners() => _engineListener diff --git a/lib/src/core/signal_client.dart b/lib/src/core/signal_client.dart index c7ed5dcc0..1454a1c70 100644 --- a/lib/src/core/signal_client.dart +++ b/lib/src/core/signal_client.dart @@ -170,6 +170,11 @@ class SignalClient extends Disposable with EventsEmittable { track: msg.trackPublished.track, )); break; + case lk_rtc.SignalResponse_Message.trackUnpublished: + events.emit(SignalTrackUnpublishedEvent( + trackSid: msg.trackUnpublished.trackSid, + )); + break; case lk_rtc.SignalResponse_Message.speakersChanged: events.emit( SignalSpeakersChangedEvent(speakers: msg.speakersChanged.speakers)); diff --git a/lib/src/events.dart b/lib/src/events.dart index efaaed8de..05d50735d 100644 --- a/lib/src/events.dart +++ b/lib/src/events.dart @@ -9,6 +9,7 @@ import 'publication/remote.dart'; import 'publication/track_publication.dart'; import 'track/track.dart'; import 'types/other.dart'; +import 'types/participant_permissions.dart'; /// Base type for all LiveKit events. mixin LiveKitEvent {} @@ -360,3 +361,21 @@ class TrackSubscriptionPermissionChangedEvent with RoomEvent, ParticipantEvent { '(participant: ${participant}, publication: ${publication}, ' 'state: ${state})'; } + +/// The [ParticipantPermissions] updated for the [Participant]. +/// Currently, only for [LocalParticipant]. +/// Emitted by [Room] and [LocalParticipant]. +class ParticipantPermissionsUpdatedEvent with RoomEvent, ParticipantEvent { + final Participant participant; + final ParticipantPermissions permissions; + final ParticipantPermissions oldPermissions; + const ParticipantPermissionsUpdatedEvent({ + required this.participant, + required this.permissions, + required this.oldPermissions, + }); + + @override + String toString() => '${runtimeType}' + '(participant: ${participant}, permissions: ${permissions})'; +} diff --git a/lib/src/extensions.dart b/lib/src/extensions.dart index 0c0f7e1a8..80ac9e29b 100644 --- a/lib/src/extensions.dart +++ b/lib/src/extensions.dart @@ -43,6 +43,7 @@ extension ProtocolVersionExt on ProtocolVersion { ProtocolVersion.v4: '4', ProtocolVersion.v5: '5', ProtocolVersion.v6: '6', + ProtocolVersion.v7: '7', }[this]!; } diff --git a/lib/src/internal/events.dart b/lib/src/internal/events.dart index 6fadc8b57..7b223a02c 100644 --- a/lib/src/internal/events.dart +++ b/lib/src/internal/events.dart @@ -194,6 +194,15 @@ class SignalLocalTrackPublishedEvent with SignalEvent, InternalEvent { }); } +@internal +class SignalTrackUnpublishedEvent with SignalEvent, InternalEvent { + final String trackSid; + + const SignalTrackUnpublishedEvent({ + required this.trackSid, + }); +} + @internal class SignalRoomUpdateEvent with SignalEvent, InternalEvent { final lk_models.Room room; diff --git a/lib/src/options.dart b/lib/src/options.dart index 33a8e6ec5..30e115b87 100644 --- a/lib/src/options.dart +++ b/lib/src/options.dart @@ -24,7 +24,7 @@ class ConnectOptions { const ConnectOptions({ this.autoSubscribe = true, this.rtcConfiguration = const RTCConfiguration(), - this.protocolVersion = ProtocolVersion.v6, + this.protocolVersion = ProtocolVersion.v7, }); } diff --git a/lib/src/participant/local.dart b/lib/src/participant/local.dart index d76fb145b..248a82c4b 100644 --- a/lib/src/participant/local.dart +++ b/lib/src/participant/local.dart @@ -1,5 +1,4 @@ import 'package:flutter/foundation.dart'; - import 'package:flutter_webrtc/flutter_webrtc.dart' as rtc; import 'package:meta/meta.dart'; @@ -17,6 +16,7 @@ import '../track/local/audio.dart'; import '../track/local/local.dart'; import '../track/local/video.dart'; import '../types/other.dart'; +import '../types/participant_permissions.dart'; import '../types/video_dimensions.dart'; import '../utils.dart'; import 'participant.dart'; @@ -339,4 +339,19 @@ class LocalParticipant extends Participant { @internal Iterable publishedTracksInfo() => trackPublications.values.map((e) => e.toPBTrackPublishedResponse()); + + @internal + @override + ParticipantPermissions? setPermissions(ParticipantPermissions newValue) { + final oldValue = super.setPermissions(newValue); + if (oldValue != null) { + // notify + [events, room.events].emit(ParticipantPermissionsUpdatedEvent( + participant: this, + permissions: newValue, + oldPermissions: oldValue, + )); + } + return oldValue; + } } diff --git a/lib/src/participant/participant.dart b/lib/src/participant/participant.dart index c6090e714..aa9dbaa1b 100644 --- a/lib/src/participant/participant.dart +++ b/lib/src/participant/participant.dart @@ -12,6 +12,7 @@ import '../support/disposable.dart'; import '../track/local/local.dart'; import '../track/track.dart'; import '../types/other.dart'; +import '../types/participant_permissions.dart'; import 'local.dart'; import 'remote.dart'; @@ -59,6 +60,9 @@ abstract class Participant /// Connection quality between the [Participant] and the server. ConnectionQuality _connectionQuality = ConnectionQuality.unknown; + ParticipantPermissions _permissions = const ParticipantPermissions(); + ParticipantPermissions get permissions => _permissions; + /// when the participant joined the room DateTime get joinedAt { final pi = _participantInfo; @@ -161,6 +165,16 @@ abstract class Participant _setMetadata(info.metadata); } _participantInfo = info; + setPermissions(info.permission.toLKType()); + } + + @internal + // returns oldValue (if updated) + ParticipantPermissions? setPermissions(ParticipantPermissions newValue) { + if (_permissions == newValue) return null; + final oldValue = _permissions; + _permissions = newValue; + return oldValue; } /// for internal use diff --git a/lib/src/types/other.dart b/lib/src/types/other.dart index 918db8681..369d82c56 100644 --- a/lib/src/types/other.dart +++ b/lib/src/types/other.dart @@ -13,6 +13,7 @@ enum ProtocolVersion { v4, v5, v6, // Session migration + v7, // Remote unpublish } /// Connection state type used throughout the SDK. diff --git a/lib/src/types/participant_permissions.dart b/lib/src/types/participant_permissions.dart new file mode 100644 index 000000000..f8f2b7a06 --- /dev/null +++ b/lib/src/types/participant_permissions.dart @@ -0,0 +1,30 @@ +import 'package:meta/meta.dart'; + +import '../proto/livekit_models.pb.dart' as lk_models; + +@immutable +class ParticipantPermissions { + final bool canSubscribe; + final bool canPublish; + final bool canPublishData; + final bool hidden; + final bool recorder; + + const ParticipantPermissions({ + this.canSubscribe = false, + this.canPublish = false, + this.canPublishData = false, + this.hidden = false, + this.recorder = false, + }); +} + +extension ParticipantPermissionExt on lk_models.ParticipantPermission { + ParticipantPermissions toLKType() => ParticipantPermissions( + canSubscribe: canSubscribe, + canPublish: canPublish, + canPublishData: canPublishData, + hidden: hidden, + recorder: recorder, + ); +}