From d31daa5d8e094e48e67daf78accd0bef196e6c3d Mon Sep 17 00:00:00 2001 From: Hiroshi Horie <548776+hiroshihorie@users.noreply.github.com> Date: Sat, 6 Nov 2021 17:36:47 +0900 Subject: [PATCH] Connection quality (#27) * update protos * signal event * implemented * format * implement example * protocol 5 * icon size --- example/lib/widgets/participant.dart | 1 + example/lib/widgets/participant_info.dart | 18 ++ lib/src/events.dart | 19 +++ lib/src/extensions.dart | 11 ++ lib/src/participant/participant.dart | 16 ++ lib/src/proto/livekit_models.pb.dart | 2 +- lib/src/proto/livekit_models.pbenum.dart | 32 +++- lib/src/proto/livekit_models.pbjson.dart | 15 +- lib/src/proto/livekit_models.pbserver.dart | 2 +- lib/src/proto/livekit_rtc.pb.dart | 186 ++++++++++++++++++++- lib/src/proto/livekit_rtc.pbenum.dart | 2 +- lib/src/proto/livekit_rtc.pbjson.dart | 57 ++++++- lib/src/proto/livekit_rtc.pbserver.dart | 2 +- lib/src/room.dart | 19 +++ lib/src/rtc_engine.dart | 2 + lib/src/signal_client.dart | 7 +- lib/src/types.dart | 8 + 17 files changed, 388 insertions(+), 11 deletions(-) diff --git a/example/lib/widgets/participant.dart b/example/lib/widgets/participant.dart index 9234972..e44694e 100644 --- a/example/lib/widgets/participant.dart +++ b/example/lib/widgets/participant.dart @@ -125,6 +125,7 @@ class _ParticipantWidgetState extends State { title: widget.participant.identity, audioAvailable: firstAudioPub?.muted == false && firstAudioPub?.subscribed == true, + connectionQuality: widget.participant.connectionQuality, ), ], ), diff --git a/example/lib/widgets/participant_info.dart b/example/lib/widgets/participant_info.dart index 403f11e..56e822d 100644 --- a/example/lib/widgets/participant_info.dart +++ b/example/lib/widgets/participant_info.dart @@ -1,14 +1,17 @@ import 'package:eva_icons_flutter/eva_icons_flutter.dart'; import 'package:flutter/material.dart'; +import 'package:livekit_client/livekit_client.dart'; class ParticipantInfoWidget extends StatelessWidget { // final String? title; final bool audioAvailable; + final ConnectionQuality connectionQuality; const ParticipantInfoWidget({ this.title, this.audioAvailable = true, + this.connectionQuality = ConnectionQuality.unknown, Key? key, }) : super(key: key); @@ -38,6 +41,21 @@ class ParticipantInfoWidget extends StatelessWidget { size: 16, ), ), + if (connectionQuality != ConnectionQuality.unknown) + Padding( + padding: const EdgeInsets.only(left: 5), + child: Icon( + connectionQuality == ConnectionQuality.poor + ? EvaIcons.wifiOffOutline + : EvaIcons.wifi, + color: { + ConnectionQuality.excellent: Colors.green, + ConnectionQuality.good: Colors.orange, + ConnectionQuality.poor: Colors.red, + }[connectionQuality], + size: 16, + ), + ), ], ), ); diff --git a/lib/src/events.dart b/lib/src/events.dart index 4cb35aa..894a909 100644 --- a/lib/src/events.dart +++ b/lib/src/events.dart @@ -189,6 +189,18 @@ class ParticipantMetadataUpdatedEvent with RoomEvent, ParticipantEvent { }); } +/// [Pariticpant]'s [ConnectionQuality] has updated. +/// Emitted by [Room] and [Participant]. +class ParticipantConnectionQualityUpdatedEvent + with RoomEvent, ParticipantEvent { + final Participant participant; + final ConnectionQuality connectionQuality; + const ParticipantConnectionQualityUpdatedEvent({ + required this.participant, + required this.connectionQuality, + }); +} + /// Data received from [RemoteParticipant]. /// Data packets provides the ability to use LiveKit to send/receive arbitrary /// payloads. @@ -310,6 +322,13 @@ class SignalParticipantUpdateEvent with SignalEvent, EngineEvent { }); } +class SignalConnectionQualityUpdateEvent with SignalEvent, EngineEvent { + final List updates; + const SignalConnectionQualityUpdateEvent({ + required this.updates, + }); +} + class SignalLocalTrackPublishedEvent with SignalEvent { final String cid; final lk_models.TrackInfo track; diff --git a/lib/src/extensions.dart b/lib/src/extensions.dart index 0afc7c8..dbf6adf 100644 --- a/lib/src/extensions.dart +++ b/lib/src/extensions.dart @@ -41,6 +41,7 @@ extension ProtocolVersionExt on ProtocolVersion { ProtocolVersion.protocol2: '2', ProtocolVersion.protocol3: '3', ProtocolVersion.protocol4: '4', + ProtocolVersion.protocol5: '5', }[this]!; } @@ -91,3 +92,13 @@ extension SessionDescriptionExt on lk_rtc.SessionDescription { return rtc.RTCSessionDescription(sdp, type); } } + +extension ConnectionQualityExt on lk_models.ConnectionQuality { + ConnectionQuality toLKType() => + { + lk_models.ConnectionQuality.POOR: ConnectionQuality.poor, + lk_models.ConnectionQuality.GOOD: ConnectionQuality.good, + lk_models.ConnectionQuality.EXCELLENT: ConnectionQuality.excellent, + }[this] ?? + ConnectionQuality.unknown; +} diff --git a/lib/src/participant/participant.dart b/lib/src/participant/participant.dart index deb19cc..8afdee4 100644 --- a/lib/src/participant/participant.dart +++ b/lib/src/participant/participant.dart @@ -8,6 +8,7 @@ import '../managers/event.dart'; import '../proto/livekit_models.pb.dart' as lk_models; import '../support/disposable.dart'; import '../track/track_publication.dart'; +import '../types.dart'; import 'remote_participant.dart'; /// Represents a Participant in the room, notifies changes via delegates as @@ -43,6 +44,8 @@ abstract class Participant extends DisposableChangeNotifier lk_models.ParticipantInfo? _participantInfo; bool _isSpeaking = false; + ConnectionQuality _connectionQuality = ConnectionQuality.unknown; + // suppport for multiple event listeners final EventsEmitter roomEvents; @@ -66,6 +69,9 @@ abstract class Participant extends DisposableChangeNotifier bool get hasVideo => videoTracks.isNotEmpty; + /// Connection quality of the participant + ConnectionQuality get connectionQuality => _connectionQuality; + /// tracks that are subscribed to List get subscribedTracks => trackPublications.values.where((e) => e.subscribed).toList(); @@ -120,6 +126,16 @@ abstract class Participant extends DisposableChangeNotifier } } + @internal + void updateConnectionQuality(ConnectionQuality quality) { + if (_connectionQuality == quality) return; + _connectionQuality = quality; + [events, roomEvents].emit(ParticipantConnectionQualityUpdatedEvent( + participant: this, + connectionQuality: _connectionQuality, + )); + } + /// for internal use /// {@nodoc} @internal diff --git a/lib/src/proto/livekit_models.pb.dart b/lib/src/proto/livekit_models.pb.dart index d983227..c7c688e 100644 --- a/lib/src/proto/livekit_models.pb.dart +++ b/lib/src/proto/livekit_models.pb.dart @@ -3,7 +3,7 @@ // source: livekit_models.proto // // @dart = 2.12 -// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_this,unused_import,unused_shown_name +// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name import 'dart:core' as $core; diff --git a/lib/src/proto/livekit_models.pbenum.dart b/lib/src/proto/livekit_models.pbenum.dart index abc9091..e9f7034 100644 --- a/lib/src/proto/livekit_models.pbenum.dart +++ b/lib/src/proto/livekit_models.pbenum.dart @@ -3,7 +3,7 @@ // source: livekit_models.proto // // @dart = 2.12 -// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_this,unused_import,unused_shown_name +// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name // ignore_for_file: UNDEFINED_SHOWN_NAME import 'dart:core' as $core; @@ -75,6 +75,36 @@ class TrackSource extends $pb.ProtobufEnum { const TrackSource._($core.int v, $core.String n) : super(v, n); } +class ConnectionQuality extends $pb.ProtobufEnum { + static const ConnectionQuality POOR = ConnectionQuality._( + 0, + const $core.bool.fromEnvironment('protobuf.omit_enum_names') + ? '' + : 'POOR'); + static const ConnectionQuality GOOD = ConnectionQuality._( + 1, + const $core.bool.fromEnvironment('protobuf.omit_enum_names') + ? '' + : 'GOOD'); + static const ConnectionQuality EXCELLENT = ConnectionQuality._( + 2, + const $core.bool.fromEnvironment('protobuf.omit_enum_names') + ? '' + : 'EXCELLENT'); + + static const $core.List values = [ + POOR, + GOOD, + EXCELLENT, + ]; + + static final $core.Map<$core.int, ConnectionQuality> _byValue = + $pb.ProtobufEnum.initByValue(values); + static ConnectionQuality? valueOf($core.int value) => _byValue[value]; + + const ConnectionQuality._($core.int v, $core.String n) : super(v, n); +} + class ParticipantInfo_State extends $pb.ProtobufEnum { static const ParticipantInfo_State JOINING = ParticipantInfo_State._( 0, diff --git a/lib/src/proto/livekit_models.pbjson.dart b/lib/src/proto/livekit_models.pbjson.dart index d5d94f1..9b3a018 100644 --- a/lib/src/proto/livekit_models.pbjson.dart +++ b/lib/src/proto/livekit_models.pbjson.dart @@ -3,7 +3,7 @@ // source: livekit_models.proto // // @dart = 2.12 -// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,deprecated_member_use_from_same_package,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_this,unused_import,unused_shown_name +// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,deprecated_member_use_from_same_package,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name import 'dart:core' as $core; import 'dart:convert' as $convert; @@ -36,6 +36,19 @@ const TrackSource$json = const { /// Descriptor for `TrackSource`. Decode as a `google.protobuf.EnumDescriptorProto`. final $typed_data.Uint8List trackSourceDescriptor = $convert.base64Decode( 'CgtUcmFja1NvdXJjZRILCgdVTktOT1dOEAASCgoGQ0FNRVJBEAESDgoKTUlDUk9QSE9ORRACEhAKDFNDUkVFTl9TSEFSRRAD'); +@$core.Deprecated('Use connectionQualityDescriptor instead') +const ConnectionQuality$json = const { + '1': 'ConnectionQuality', + '2': const [ + const {'1': 'POOR', '2': 0}, + const {'1': 'GOOD', '2': 1}, + const {'1': 'EXCELLENT', '2': 2}, + ], +}; + +/// Descriptor for `ConnectionQuality`. Decode as a `google.protobuf.EnumDescriptorProto`. +final $typed_data.Uint8List connectionQualityDescriptor = $convert.base64Decode( + 'ChFDb25uZWN0aW9uUXVhbGl0eRIICgRQT09SEAASCAoER09PRBABEg0KCUVYQ0VMTEVOVBAC'); @$core.Deprecated('Use roomDescriptor instead') const Room$json = const { '1': 'Room', diff --git a/lib/src/proto/livekit_models.pbserver.dart b/lib/src/proto/livekit_models.pbserver.dart index c28eba2..aa472cd 100644 --- a/lib/src/proto/livekit_models.pbserver.dart +++ b/lib/src/proto/livekit_models.pbserver.dart @@ -3,6 +3,6 @@ // source: livekit_models.proto // // @dart = 2.12 -// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,deprecated_member_use_from_same_package,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_this,unused_import,unused_shown_name +// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,deprecated_member_use_from_same_package,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name export 'livekit_models.pb.dart'; diff --git a/lib/src/proto/livekit_rtc.pb.dart b/lib/src/proto/livekit_rtc.pb.dart index a1a1a6c..e8d3a3b 100644 --- a/lib/src/proto/livekit_rtc.pb.dart +++ b/lib/src/proto/livekit_rtc.pb.dart @@ -3,7 +3,7 @@ // source: livekit_rtc.proto // // @dart = 2.12 -// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_this,unused_import,unused_shown_name +// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name import 'dart:core' as $core; @@ -294,6 +294,7 @@ enum SignalResponse_Message { mute, speakersChanged, roomUpdate, + connectionQuality, notSet } @@ -310,6 +311,7 @@ class SignalResponse extends $pb.GeneratedMessage { 9: SignalResponse_Message.mute, 10: SignalResponse_Message.speakersChanged, 11: SignalResponse_Message.roomUpdate, + 12: SignalResponse_Message.connectionQuality, 0: SignalResponse_Message.notSet }; static final $pb.BuilderInfo _i = $pb.BuilderInfo( @@ -321,7 +323,7 @@ class SignalResponse extends $pb.GeneratedMessage { ? '' : 'livekit'), createEmptyInstance: create) - ..oo(0, [1, 2, 3, 4, 5, 6, 8, 9, 10, 11]) + ..oo(0, [1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12]) ..aOM( 1, const $core.bool.fromEnvironment('protobuf.omit_field_names') @@ -382,6 +384,12 @@ class SignalResponse extends $pb.GeneratedMessage { ? '' : 'roomUpdate', subBuilder: RoomUpdate.create) + ..aOM( + 12, + const $core.bool.fromEnvironment('protobuf.omit_field_names') + ? '' + : 'connectionQuality', + subBuilder: ConnectionQualityUpdate.create) ..hasRequiredFields = false; SignalResponse._() : super(); @@ -396,6 +404,7 @@ class SignalResponse extends $pb.GeneratedMessage { MuteTrackRequest? mute, SpeakersChanged? speakersChanged, RoomUpdate? roomUpdate, + ConnectionQualityUpdate? connectionQuality, }) { final _result = create(); if (join != null) { @@ -428,6 +437,9 @@ class SignalResponse extends $pb.GeneratedMessage { if (roomUpdate != null) { _result.roomUpdate = roomUpdate; } + if (connectionQuality != null) { + _result.connectionQuality = connectionQuality; + } return _result; } factory SignalResponse.fromBuffer($core.List<$core.int> i, @@ -600,6 +612,20 @@ class SignalResponse extends $pb.GeneratedMessage { void clearRoomUpdate() => clearField(11); @$pb.TagNumber(11) RoomUpdate ensureRoomUpdate() => $_ensure(9); + + @$pb.TagNumber(12) + ConnectionQualityUpdate get connectionQuality => $_getN(10); + @$pb.TagNumber(12) + set connectionQuality(ConnectionQualityUpdate v) { + setField(12, v); + } + + @$pb.TagNumber(12) + $core.bool hasConnectionQuality() => $_has(10); + @$pb.TagNumber(12) + void clearConnectionQuality() => clearField(12); + @$pb.TagNumber(12) + ConnectionQualityUpdate ensureConnectionQuality() => $_ensure(10); } class AddTrackRequest extends $pb.GeneratedMessage { @@ -1954,3 +1980,159 @@ class RoomUpdate extends $pb.GeneratedMessage { @$pb.TagNumber(1) $0.Room ensureRoom() => $_ensure(0); } + +class ConnectionQualityInfo extends $pb.GeneratedMessage { + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + const $core.bool.fromEnvironment('protobuf.omit_message_names') + ? '' + : 'ConnectionQualityInfo', + package: const $pb.PackageName( + const $core.bool.fromEnvironment('protobuf.omit_message_names') + ? '' + : 'livekit'), + createEmptyInstance: create) + ..aOS( + 1, + const $core.bool.fromEnvironment('protobuf.omit_field_names') + ? '' + : 'participantSid') + ..e<$0.ConnectionQuality>( + 2, + const $core.bool.fromEnvironment('protobuf.omit_field_names') + ? '' + : 'quality', + $pb.PbFieldType.OE, + defaultOrMaker: $0.ConnectionQuality.POOR, + valueOf: $0.ConnectionQuality.valueOf, + enumValues: $0.ConnectionQuality.values) + ..hasRequiredFields = false; + + ConnectionQualityInfo._() : super(); + factory ConnectionQualityInfo({ + $core.String? participantSid, + $0.ConnectionQuality? quality, + }) { + final _result = create(); + if (participantSid != null) { + _result.participantSid = participantSid; + } + if (quality != null) { + _result.quality = quality; + } + return _result; + } + factory ConnectionQualityInfo.fromBuffer($core.List<$core.int> i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(i, r); + factory ConnectionQualityInfo.fromJson($core.String i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(i, r); + @$core.Deprecated('Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + ConnectionQualityInfo clone() => + ConnectionQualityInfo()..mergeFromMessage(this); + @$core.Deprecated('Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + ConnectionQualityInfo copyWith( + void Function(ConnectionQualityInfo) updates) => + super.copyWith((message) => updates(message as ConnectionQualityInfo)) + as ConnectionQualityInfo; // ignore: deprecated_member_use + $pb.BuilderInfo get info_ => _i; + @$core.pragma('dart2js:noInline') + static ConnectionQualityInfo create() => ConnectionQualityInfo._(); + ConnectionQualityInfo createEmptyInstance() => create(); + static $pb.PbList createRepeated() => + $pb.PbList(); + @$core.pragma('dart2js:noInline') + static ConnectionQualityInfo getDefault() => _defaultInstance ??= + $pb.GeneratedMessage.$_defaultFor(create); + static ConnectionQualityInfo? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get participantSid => $_getSZ(0); + @$pb.TagNumber(1) + set participantSid($core.String v) { + $_setString(0, v); + } + + @$pb.TagNumber(1) + $core.bool hasParticipantSid() => $_has(0); + @$pb.TagNumber(1) + void clearParticipantSid() => clearField(1); + + @$pb.TagNumber(2) + $0.ConnectionQuality get quality => $_getN(1); + @$pb.TagNumber(2) + set quality($0.ConnectionQuality v) { + setField(2, v); + } + + @$pb.TagNumber(2) + $core.bool hasQuality() => $_has(1); + @$pb.TagNumber(2) + void clearQuality() => clearField(2); +} + +class ConnectionQualityUpdate extends $pb.GeneratedMessage { + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + const $core.bool.fromEnvironment('protobuf.omit_message_names') + ? '' + : 'ConnectionQualityUpdate', + package: const $pb.PackageName( + const $core.bool.fromEnvironment('protobuf.omit_message_names') + ? '' + : 'livekit'), + createEmptyInstance: create) + ..pc( + 1, + const $core.bool.fromEnvironment('protobuf.omit_field_names') + ? '' + : 'updates', + $pb.PbFieldType.PM, + subBuilder: ConnectionQualityInfo.create) + ..hasRequiredFields = false; + + ConnectionQualityUpdate._() : super(); + factory ConnectionQualityUpdate({ + $core.Iterable? updates, + }) { + final _result = create(); + if (updates != null) { + _result.updates.addAll(updates); + } + return _result; + } + factory ConnectionQualityUpdate.fromBuffer($core.List<$core.int> i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(i, r); + factory ConnectionQualityUpdate.fromJson($core.String i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(i, r); + @$core.Deprecated('Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + ConnectionQualityUpdate clone() => + ConnectionQualityUpdate()..mergeFromMessage(this); + @$core.Deprecated('Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + ConnectionQualityUpdate copyWith( + void Function(ConnectionQualityUpdate) updates) => + super.copyWith((message) => updates(message as ConnectionQualityUpdate)) + as ConnectionQualityUpdate; // ignore: deprecated_member_use + $pb.BuilderInfo get info_ => _i; + @$core.pragma('dart2js:noInline') + static ConnectionQualityUpdate create() => ConnectionQualityUpdate._(); + ConnectionQualityUpdate createEmptyInstance() => create(); + static $pb.PbList createRepeated() => + $pb.PbList(); + @$core.pragma('dart2js:noInline') + static ConnectionQualityUpdate getDefault() => _defaultInstance ??= + $pb.GeneratedMessage.$_defaultFor(create); + static ConnectionQualityUpdate? _defaultInstance; + + @$pb.TagNumber(1) + $core.List get updates => $_getList(0); +} diff --git a/lib/src/proto/livekit_rtc.pbenum.dart b/lib/src/proto/livekit_rtc.pbenum.dart index ac2bd47..029b5c6 100644 --- a/lib/src/proto/livekit_rtc.pbenum.dart +++ b/lib/src/proto/livekit_rtc.pbenum.dart @@ -3,7 +3,7 @@ // source: livekit_rtc.proto // // @dart = 2.12 -// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_this,unused_import,unused_shown_name +// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name // ignore_for_file: UNDEFINED_SHOWN_NAME import 'dart:core' as $core; diff --git a/lib/src/proto/livekit_rtc.pbjson.dart b/lib/src/proto/livekit_rtc.pbjson.dart index 992a19e..2829ca7 100644 --- a/lib/src/proto/livekit_rtc.pbjson.dart +++ b/lib/src/proto/livekit_rtc.pbjson.dart @@ -3,7 +3,7 @@ // source: livekit_rtc.proto // // @dart = 2.12 -// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,deprecated_member_use_from_same_package,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_this,unused_import,unused_shown_name +// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,deprecated_member_use_from_same_package,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name import 'dart:core' as $core; import 'dart:convert' as $convert; @@ -213,6 +213,15 @@ const SignalResponse$json = const { '9': 0, '10': 'roomUpdate' }, + const { + '1': 'connection_quality', + '3': 12, + '4': 1, + '5': 11, + '6': '.livekit.ConnectionQualityUpdate', + '9': 0, + '10': 'connectionQuality' + }, ], '8': const [ const {'1': 'message'}, @@ -221,7 +230,7 @@ const SignalResponse$json = const { /// Descriptor for `SignalResponse`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List signalResponseDescriptor = $convert.base64Decode( - 'Cg5TaWduYWxSZXNwb25zZRIrCgRqb2luGAEgASgLMhUubGl2ZWtpdC5Kb2luUmVzcG9uc2VIAFIEam9pbhI1CgZhbnN3ZXIYAiABKAsyGy5saXZla2l0LlNlc3Npb25EZXNjcmlwdGlvbkgAUgZhbnN3ZXISMwoFb2ZmZXIYAyABKAsyGy5saXZla2l0LlNlc3Npb25EZXNjcmlwdGlvbkgAUgVvZmZlchIzCgd0cmlja2xlGAQgASgLMhcubGl2ZWtpdC5Ucmlja2xlUmVxdWVzdEgAUgd0cmlja2xlEjQKBnVwZGF0ZRgFIAEoCzIaLmxpdmVraXQuUGFydGljaXBhbnRVcGRhdGVIAFIGdXBkYXRlEkoKD3RyYWNrX3B1Ymxpc2hlZBgGIAEoCzIfLmxpdmVraXQuVHJhY2tQdWJsaXNoZWRSZXNwb25zZUgAUg50cmFja1B1Ymxpc2hlZBItCgVsZWF2ZRgIIAEoCzIVLmxpdmVraXQuTGVhdmVSZXF1ZXN0SABSBWxlYXZlEi8KBG11dGUYCSABKAsyGS5saXZla2l0Lk11dGVUcmFja1JlcXVlc3RIAFIEbXV0ZRJFChBzcGVha2Vyc19jaGFuZ2VkGAogASgLMhgubGl2ZWtpdC5TcGVha2Vyc0NoYW5nZWRIAFIPc3BlYWtlcnNDaGFuZ2VkEjYKC3Jvb21fdXBkYXRlGAsgASgLMhMubGl2ZWtpdC5Sb29tVXBkYXRlSABSCnJvb21VcGRhdGVCCQoHbWVzc2FnZQ=='); + 'Cg5TaWduYWxSZXNwb25zZRIrCgRqb2luGAEgASgLMhUubGl2ZWtpdC5Kb2luUmVzcG9uc2VIAFIEam9pbhI1CgZhbnN3ZXIYAiABKAsyGy5saXZla2l0LlNlc3Npb25EZXNjcmlwdGlvbkgAUgZhbnN3ZXISMwoFb2ZmZXIYAyABKAsyGy5saXZla2l0LlNlc3Npb25EZXNjcmlwdGlvbkgAUgVvZmZlchIzCgd0cmlja2xlGAQgASgLMhcubGl2ZWtpdC5Ucmlja2xlUmVxdWVzdEgAUgd0cmlja2xlEjQKBnVwZGF0ZRgFIAEoCzIaLmxpdmVraXQuUGFydGljaXBhbnRVcGRhdGVIAFIGdXBkYXRlEkoKD3RyYWNrX3B1Ymxpc2hlZBgGIAEoCzIfLmxpdmVraXQuVHJhY2tQdWJsaXNoZWRSZXNwb25zZUgAUg50cmFja1B1Ymxpc2hlZBItCgVsZWF2ZRgIIAEoCzIVLmxpdmVraXQuTGVhdmVSZXF1ZXN0SABSBWxlYXZlEi8KBG11dGUYCSABKAsyGS5saXZla2l0Lk11dGVUcmFja1JlcXVlc3RIAFIEbXV0ZRJFChBzcGVha2Vyc19jaGFuZ2VkGAogASgLMhgubGl2ZWtpdC5TcGVha2Vyc0NoYW5nZWRIAFIPc3BlYWtlcnNDaGFuZ2VkEjYKC3Jvb21fdXBkYXRlGAsgASgLMhMubGl2ZWtpdC5Sb29tVXBkYXRlSABSCnJvb21VcGRhdGUSUQoSY29ubmVjdGlvbl9xdWFsaXR5GAwgASgLMiAubGl2ZWtpdC5Db25uZWN0aW9uUXVhbGl0eVVwZGF0ZUgAUhFjb25uZWN0aW9uUXVhbGl0eUIJCgdtZXNzYWdl'); @$core.Deprecated('Use addTrackRequestDescriptor instead') const AddTrackRequest$json = const { '1': 'AddTrackRequest', @@ -492,3 +501,47 @@ const RoomUpdate$json = const { /// Descriptor for `RoomUpdate`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List roomUpdateDescriptor = $convert.base64Decode( 'CgpSb29tVXBkYXRlEiEKBHJvb20YASABKAsyDS5saXZla2l0LlJvb21SBHJvb20='); +@$core.Deprecated('Use connectionQualityInfoDescriptor instead') +const ConnectionQualityInfo$json = const { + '1': 'ConnectionQualityInfo', + '2': const [ + const { + '1': 'participant_sid', + '3': 1, + '4': 1, + '5': 9, + '10': 'participantSid' + }, + const { + '1': 'quality', + '3': 2, + '4': 1, + '5': 14, + '6': '.livekit.ConnectionQuality', + '10': 'quality' + }, + ], +}; + +/// Descriptor for `ConnectionQualityInfo`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List connectionQualityInfoDescriptor = $convert.base64Decode( + 'ChVDb25uZWN0aW9uUXVhbGl0eUluZm8SJwoPcGFydGljaXBhbnRfc2lkGAEgASgJUg5wYXJ0aWNpcGFudFNpZBI0CgdxdWFsaXR5GAIgASgOMhoubGl2ZWtpdC5Db25uZWN0aW9uUXVhbGl0eVIHcXVhbGl0eQ=='); +@$core.Deprecated('Use connectionQualityUpdateDescriptor instead') +const ConnectionQualityUpdate$json = const { + '1': 'ConnectionQualityUpdate', + '2': const [ + const { + '1': 'updates', + '3': 1, + '4': 3, + '5': 11, + '6': '.livekit.ConnectionQualityInfo', + '10': 'updates' + }, + ], +}; + +/// Descriptor for `ConnectionQualityUpdate`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List connectionQualityUpdateDescriptor = + $convert.base64Decode( + 'ChdDb25uZWN0aW9uUXVhbGl0eVVwZGF0ZRI4Cgd1cGRhdGVzGAEgAygLMh4ubGl2ZWtpdC5Db25uZWN0aW9uUXVhbGl0eUluZm9SB3VwZGF0ZXM='); diff --git a/lib/src/proto/livekit_rtc.pbserver.dart b/lib/src/proto/livekit_rtc.pbserver.dart index 904b71c..34f635e 100644 --- a/lib/src/proto/livekit_rtc.pbserver.dart +++ b/lib/src/proto/livekit_rtc.pbserver.dart @@ -3,6 +3,6 @@ // source: livekit_rtc.proto // // @dart = 2.12 -// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,deprecated_member_use_from_same_package,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_this,unused_import,unused_shown_name +// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,deprecated_member_use_from_same_package,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name export 'livekit_rtc.pb.dart'; diff --git a/lib/src/room.dart b/lib/src/room.dart index 7e8905c..3c5a6f1 100644 --- a/lib/src/room.dart +++ b/lib/src/room.dart @@ -174,6 +174,8 @@ class Room extends DisposableChangeNotifier with EventsEmittable { (event) => _onEngineActiveSpeakersUpdateEvent(event.speakers)) ..on( (event) => _onSignalSpeakersChangedEvent(event.speakers)) + ..on( + (event) => _onSignalConnectionQualityUpdateEvent(event.updates)) ..on(_onDataMessageEvent) ..on((event) async { final track = localParticipant.trackPublications[event.sid]; @@ -366,6 +368,23 @@ class Room extends DisposableChangeNotifier with EventsEmittable { events.emit(ActiveSpeakersChangedEvent(speakers: activeSpeakers)); } + void _onSignalConnectionQualityUpdateEvent( + List updates) { + for (final entry in updates) { + Participant? participant; + if (entry.participantSid == localParticipant.sid) { + participant = localParticipant; + } else { + participant = _participants[entry.participantSid]; + } + + if (participant != null) { + // update the connection quality if the participant is found + participant.updateConnectionQuality(entry.quality.toLKType()); + } + } + } + void _onDataMessageEvent(EngineDataPacketReceivedEvent dataPacketEvent) { // participant may be null if data is sent from Server-API final senderSid = dataPacketEvent.packet.participantSid; diff --git a/lib/src/rtc_engine.dart b/lib/src/rtc_engine.dart index 4a4f9c9..ef37299 100644 --- a/lib/src/rtc_engine.dart +++ b/lib/src/rtc_engine.dart @@ -550,6 +550,8 @@ class RTCEngine extends Disposable with EventsEmittable { ..on((event) => events.emit(event)) // relay ..on((event) => events.emit(event)) + // relay + ..on((event) => events.emit(event)) ..on((event) async { await close(); events.emit(const EngineDisconnectedEvent()); diff --git a/lib/src/signal_client.dart b/lib/src/signal_client.dart index d37a547..9b9f437 100644 --- a/lib/src/signal_client.dart +++ b/lib/src/signal_client.dart @@ -24,7 +24,7 @@ class SignalClient extends Disposable with EventsEmittable { LiveKitWebSocket? _ws; SignalClient({ - this.protocol = ProtocolVersion.protocol4, + this.protocol = ProtocolVersion.protocol5, }) { events.listen((event) { logger.fine('[SignalEvent] $event'); @@ -247,6 +247,11 @@ class SignalClient extends Disposable with EventsEmittable { events.emit( SignalSpeakersChangedEvent(speakers: msg.speakersChanged.speakers)); break; + case lk_rtc.SignalResponse_Message.connectionQuality: + events.emit(SignalConnectionQualityUpdateEvent( + updates: msg.connectionQuality.updates, + )); + break; case lk_rtc.SignalResponse_Message.leave: events.emit(SignalLeaveEvent(canReconnect: msg.leave.canReconnect)); break; diff --git a/lib/src/types.dart b/lib/src/types.dart index 98888aa..a092ec1 100644 --- a/lib/src/types.dart +++ b/lib/src/types.dart @@ -8,6 +8,7 @@ enum ProtocolVersion { protocol2, protocol3, protocol4, + protocol5, } enum ConnectionState { @@ -16,6 +17,13 @@ enum ConnectionState { reconnecting, } +enum ConnectionQuality { + unknown, + poor, + good, + excellent, +} + enum Reliability { reliable, lossy,