diff --git a/README.md b/README.md index e5425b091..227bc56c3 100644 --- a/README.md +++ b/README.md @@ -194,6 +194,9 @@ final roomOptions = RoomOptions( final room = Room(); +// you can use `prepareConnection` to speed up connection. +await room.prepareConnection(url, token); + await room.connect(url, token, roomOptions: roomOptions); try { diff --git a/example/lib/pages/prejoin.dart b/example/lib/pages/prejoin.dart index 2d9577228..fae8c9d7e 100644 --- a/example/lib/pages/prejoin.dart +++ b/example/lib/pages/prejoin.dart @@ -165,12 +165,12 @@ class _PreJoinPageState extends State { try { //create new room - var cameraEncoding = VideoEncoding( + var cameraEncoding = const VideoEncoding( maxBitrate: 5 * 1000 * 1000, maxFramerate: 30, ); - var screenEncoding = VideoEncoding( + var screenEncoding = const VideoEncoding( maxBitrate: 3 * 1000 * 1000, maxFramerate: 15, ); @@ -189,10 +189,10 @@ class _PreJoinPageState extends State { defaultAudioPublishOptions: const AudioPublishOptions( name: 'custom_audio_track_name', ), - defaultCameraCaptureOptions: CameraCaptureOptions( + defaultCameraCaptureOptions: const CameraCaptureOptions( maxFrameRate: 30, params: VideoParameters( - dimensions: const VideoDimensions(1280, 720), + dimensions: VideoDimensions(1280, 720), )), defaultScreenShareCaptureOptions: const ScreenShareCaptureOptions( useiOSBroadcastExtension: true, @@ -214,6 +214,8 @@ class _PreJoinPageState extends State { // Create a Listener before connecting final listener = room.createListener(); + await room.prepareConnection(args.url, args.token); + // Try to connect to the room // This will throw an Exception if it fails for any reason. await room.connect( diff --git a/example/lib/pages/room.dart b/example/lib/pages/room.dart index a138149a4..cfaeda79a 100644 --- a/example/lib/pages/room.dart +++ b/example/lib/pages/room.dart @@ -120,8 +120,8 @@ class _RoomPageState extends State { String decoded = 'Failed to decode'; try { decoded = utf8.decode(event.data); - } catch (_) { - print('Failed to decode: $_'); + } catch (err) { + print('Failed to decode: $err'); } context.showDataReceivedDialog(decoded); }) diff --git a/example/lib/widgets/controls.dart b/example/lib/widgets/controls.dart index db0698d83..ebde5a080 100644 --- a/example/lib/widgets/controls.dart +++ b/example/lib/widgets/controls.dart @@ -169,7 +169,7 @@ class _ControlsWidgetState extends State { const androidConfig = FlutterBackgroundAndroidConfig( notificationTitle: 'Screen Sharing', notificationText: 'LiveKit Example is sharing the screen.', - notificationImportance: AndroidNotificationImportance.Default, + notificationImportance: AndroidNotificationImportance.normal, notificationIcon: AndroidResource( name: 'livekit_ic_launcher', defType: 'mipmap'), ); diff --git a/lib/src/core/engine.dart b/lib/src/core/engine.dart index 925cd9a8e..8ad91fd0f 100644 --- a/lib/src/core/engine.dart +++ b/lib/src/core/engine.dart @@ -36,6 +36,7 @@ import '../proto/livekit_models.pb.dart' as lk_models; import '../proto/livekit_rtc.pb.dart' as lk_rtc; import '../publication/local.dart'; import '../support/disposable.dart'; +import '../support/region_url_provider.dart'; import '../support/websocket.dart'; import '../track/local/video.dart'; import '../types/internal.dart'; @@ -130,6 +131,8 @@ class Engine extends Disposable with EventsEmittable { bool attemptingReconnect = false; + RegionUrlProvider? _regionUrlProvider; + void clearReconnectTimeout() { if (reconnectTimeout != null) { reconnectTimeout?.cancel(); @@ -171,6 +174,7 @@ class Engine extends Disposable with EventsEmittable { ConnectOptions? connectOptions, RoomOptions? roomOptions, FastConnectOptions? fastConnectOptions, + RegionUrlProvider? regionUrlProvider, }) async { this.url = url; this.token = token; @@ -179,6 +183,10 @@ class Engine extends Disposable with EventsEmittable { this.roomOptions = roomOptions ?? this.roomOptions; this.fastConnectOptions = fastConnectOptions; + if (regionUrlProvider != null) { + _regionUrlProvider = regionUrlProvider; + } + try { // wait for socket to connect rtc server await signalClient.connect( @@ -192,7 +200,8 @@ class Engine extends Disposable with EventsEmittable { await _signalListener.waitFor( duration: this.connectOptions.timeouts.connection, onTimeout: () => throw ConnectException( - 'Timed out waiting for SignalJoinResponseEvent'), + 'Timed out waiting for SignalJoinResponseEvent', + reason: ConnectionErrorReason.Timeout), ); logger.fine('Waiting for engine to connect...'); @@ -663,6 +672,11 @@ class Engine extends Disposable with EventsEmittable { )); clearReconnectTimeout(); + if (token != null && _regionUrlProvider != null) { + // token may have been refreshed, we do not want to recreate the regionUrlProvider + // since the current engine may have inherited a regional url + _regionUrlProvider!.updateToken(token!); + } logger.fine( 'WebSocket reconnecting in $delay ms, retry times $reconnectAttempts'); reconnectTimeout = Timer(Duration(milliseconds: delay), () async { @@ -700,7 +714,8 @@ class Engine extends Disposable with EventsEmittable { duration: connectOptions.timeouts.connection * 10, filter: (event) => !event.state.contains(ConnectivityResult.none), onTimeout: () => throw ConnectException( - 'attemptReconnect: Timed out waiting for SignalConnectivityChangedEvent'), + 'attemptReconnect: Timed out waiting for SignalConnectivityChangedEvent', + reason: ConnectionErrorReason.Timeout), ); } @@ -756,7 +771,8 @@ class Engine extends Disposable with EventsEmittable { await events.waitFor( duration: connectOptions.timeouts.connection, onTimeout: () => throw ConnectException( - 'resumeConnection: Timed out waiting for SignalReconnectedEvent'), + 'resumeConnection: Timed out waiting for SignalReconnectedEvent', + reason: ConnectionErrorReason.Timeout), ); logger.fine('resumeConnection: reason: ${reason.name}'); @@ -789,53 +805,65 @@ class Engine extends Disposable with EventsEmittable { } @internal - Future restartConnection([bool signalEvents = false]) async { + Future restartConnection({String? regionUrl}) async { if (_isClosed) { return; } - events.emit(const EngineFullRestartingEvent()); + try { + events.emit(const EngineFullRestartingEvent()); - if (signalClient.connectionState == ConnectionState.connected) { - await signalClient.sendLeave(); - } + if (signalClient.connectionState == ConnectionState.connected) { + await signalClient.sendLeave(); + } - await publisher?.dispose(); - publisher = null; + await publisher?.dispose(); + publisher = null; - await subscriber?.dispose(); - subscriber = null; + await subscriber?.dispose(); + subscriber = null; - _reliableDCSub = null; - _reliableDCPub = null; - _lossyDCSub = null; - _lossyDCPub = null; + _reliableDCSub = null; + _reliableDCPub = null; + _lossyDCSub = null; + _lossyDCPub = null; - await _signalListener.cancelAll(); + await _signalListener.cancelAll(); - _signalListener = signalClient.createListener(synchronized: true); - _setUpSignalListeners(); + _signalListener = signalClient.createListener(synchronized: true); + _setUpSignalListeners(); - await connect( - url!, - token!, - roomOptions: roomOptions, - connectOptions: connectOptions, - fastConnectOptions: fastConnectOptions, - ); - - if (_hasPublished) { - await negotiate(); - logger.fine('restartConnection: Waiting for publisher to ice-connect...'); - await events.waitFor( - filter: (event) => event.state.isConnected(), - duration: connectOptions.timeouts.peerConnection, + await connect( + regionUrl ?? url!, + token!, + roomOptions: roomOptions, + connectOptions: connectOptions, + fastConnectOptions: fastConnectOptions, ); - } - - fullReconnectOnNext = false; - events.emit(const EngineRestartedEvent()); + if (_hasPublished) { + await negotiate(); + logger + .fine('restartConnection: Waiting for publisher to ice-connect...'); + await events.waitFor( + filter: (event) => event.state.isConnected(), + duration: connectOptions.timeouts.peerConnection, + ); + } + fullReconnectOnNext = false; + _regionUrlProvider?.resetAttempts(); + events.emit(const EngineRestartedEvent()); + } catch (error) { + final nextRegionUrl = await _regionUrlProvider?.getNextBestRegionUrl(); + if (nextRegionUrl != null) { + await restartConnection(regionUrl: nextRegionUrl); + return; + } else { + // no more regions to try (or we're not on cloud) + _regionUrlProvider?.resetAttempts(); + rethrow; + } + } } @internal @@ -992,19 +1020,32 @@ class Engine extends Disposable with EventsEmittable { token = event.token; }) ..on((event) async { - if (event.canReconnect) { - fullReconnectOnNext = true; - // reconnect immediately instead of waiting for next attempt - await handleDisconnect(ClientDisconnectReason.leaveReconnect); - } else { - if (connectionState == ConnectionState.reconnecting) { - logger.warning( - '[Signal] Received Leave while engine is reconnecting, ignoring...'); - return; - } - await signalClient.cleanUp(); - await cleanUp(); - events.emit(EngineDisconnectedEvent(reason: event.reason.toSDKType())); + if (event.regions != null && _regionUrlProvider != null) { + logger.fine('updating regions'); + _regionUrlProvider?.setServerReportedRegions(event.regions!); + } + switch (event.action) { + case lk_rtc.LeaveRequest_Action.DISCONNECT: + if (connectionState == ConnectionState.reconnecting) { + logger.warning( + '[Signal] Received Leave while engine is reconnecting, ignoring...'); + return; + } + await signalClient.cleanUp(); + await cleanUp(); + events + .emit(EngineDisconnectedEvent(reason: event.reason.toSDKType())); + break; + case lk_rtc.LeaveRequest_Action.RECONNECT: + fullReconnectOnNext = true; + // reconnect immediately instead of waiting for next attempt + await handleDisconnect(ClientDisconnectReason.leaveReconnect); + break; + case lk_rtc.LeaveRequest_Action.RESUME: + // reconnect immediately instead of waiting for next attempt + await handleDisconnect(ClientDisconnectReason.leaveReconnect); + default: + break; } }); @@ -1016,6 +1057,10 @@ class Engine extends Disposable with EventsEmittable { await cleanUp(); } } + + void setRegionUrlProvider(RegionUrlProvider provider) { + _regionUrlProvider = provider; + } } extension EnginePrivateMethods on Engine { diff --git a/lib/src/core/room.dart b/lib/src/core/room.dart index 5dec460c2..801bfe3c8 100644 --- a/lib/src/core/room.dart +++ b/lib/src/core/room.dart @@ -19,6 +19,7 @@ import 'dart:async'; import 'package:flutter/foundation.dart'; import 'package:collection/collection.dart'; +import 'package:http/http.dart' as http; import 'package:meta/meta.dart'; import '../core/signal_client.dart'; @@ -38,6 +39,8 @@ import '../proto/livekit_models.pb.dart' as lk_models; import '../proto/livekit_rtc.pb.dart' as lk_rtc; import '../support/disposable.dart'; import '../support/platform.dart'; +import '../support/region_url_provider.dart'; +import '../support/websocket.dart' show WebSocketException; import '../track/local/audio.dart'; import '../track/local/video.dart'; import '../track/track.dart'; @@ -110,6 +113,9 @@ class Room extends DisposableChangeNotifier with EventsEmittable { // late EventsListener _signalListener; + RegionUrlProvider? _regionUrlProvider; + String? _regionUrl; + Room({ @Deprecated('deprecated, please use connectOptions in room.connect()') ConnectOptions connectOptions = const ConnectOptions(), @@ -148,6 +154,39 @@ class Room extends DisposableChangeNotifier with EventsEmittable { }); } + /// prepareConnection should be called as soon as the page is loaded, in order + /// to speed up the connection attempt. This function will + /// - perform DNS resolution and pre-warm the DNS cache + /// - establish TLS connection and cache TLS keys + /// + /// With LiveKit Cloud, it will also determine the best edge data center for + /// the current client to connect to if a token is provided. + + Future prepareConnection(String url, String? token) async { + if (engine.connectionState != ConnectionState.disconnected) { + return; + } + logger.info('prepareConnection to $url'); + try { + if (isCloudUrl(Uri.parse(url)) && token != null) { + _regionUrlProvider = RegionUrlProvider(token: token, url: url); + final regionUrl = await _regionUrlProvider!.getNextBestRegionUrl(); + // we will not replace the regionUrl if an attempt had already started + // to avoid overriding regionUrl after a new connection attempt had started + if (regionUrl != null && + connectionState == ConnectionState.disconnected) { + _regionUrl = regionUrl; + await http.head(Uri.parse(toHttpUrl(regionUrl))); + logger.fine('prepared connection to ${regionUrl}'); + } + } else { + await http.head(Uri.parse(toHttpUrl(url))); + } + } catch (e) { + logger.warning('could not prepare connection'); + } + } + Future connect( String url, String token, { @@ -174,13 +213,65 @@ class Room extends DisposableChangeNotifier with EventsEmittable { ); } - await engine.connect( - url, - token, - connectOptions: connectOptions, - roomOptions: roomOptions, - fastConnectOptions: fastConnectOptions, - ); + if (_regionUrlProvider?.getServerUrl().toString() != url) { + _regionUrl = null; + _regionUrlProvider = null; + } + if (isCloudUrl(Uri.parse(url))) { + if (_regionUrlProvider == null) { + _regionUrlProvider = RegionUrlProvider(url: url, token: token); + } else { + _regionUrlProvider?.updateToken(token); + } + // trigger the first fetch without waiting for a response + // if initial connection fails, this will speed up picking regional url + // on subsequent runs + unawaited(_regionUrlProvider?.fetchRegionSettings().then((settings) { + _regionUrlProvider?.setServerReportedRegions(settings); + }).catchError((e) { + logger.warning('could not fetch region settings $e'); + })); + } + try { + await engine.connect( + _regionUrl ?? url, + token, + connectOptions: connectOptions, + roomOptions: roomOptions, + fastConnectOptions: fastConnectOptions, + regionUrlProvider: _regionUrlProvider, + ); + } catch (e) { + logger.warning('could not connect to $url $e'); + if (_regionUrlProvider != null && e is WebSocketException || + (e is ConnectException && + e.reason != ConnectionErrorReason.NotAllowed)) { + String? nextUrl; + try { + nextUrl = await _regionUrlProvider!.getNextBestRegionUrl(); + } catch (error) { + if (error is ConnectException && (error.statusCode == 401)) { + rethrow; + } + } + if (nextUrl != null) { + logger.fine( + 'Initial connection failed with ConnectionError: $e. Retrying with another region: ${nextUrl}'); + await engine.connect( + nextUrl, + token, + connectOptions: connectOptions, + roomOptions: roomOptions, + fastConnectOptions: fastConnectOptions, + regionUrlProvider: _regionUrlProvider, + ); + } else { + rethrow; + } + } else { + rethrow; + } + } } void _setUpSignalListeners() => _signalListener diff --git a/lib/src/core/signal_client.dart b/lib/src/core/signal_client.dart index 2e529eb0d..fdcb81a86 100644 --- a/lib/src/core/signal_client.dart +++ b/lib/src/core/signal_client.dart @@ -118,7 +118,8 @@ class SignalClient extends Disposable with EventsEmittable { logger.warning('no internet connection'); events.emit(SignalDisconnectedEvent( reason: DisconnectReason.noInternetConnection)); - throw ConnectException('no internet connection'); + throw ConnectException('no internet connection', + reason: ConnectionErrorReason.InternalError, statusCode: 503); } } @@ -176,7 +177,11 @@ class SignalClient extends Disposable with EventsEmittable { final validateResponse = await http.get(validateUri); if (validateResponse.statusCode != 200) { - finalError = ConnectException(validateResponse.body); + finalError = ConnectException(validateResponse.body, + reason: validateResponse.statusCode >= 400 + ? ConnectionErrorReason.NotAllowed + : ConnectionErrorReason.InternalError, + statusCode: validateResponse.statusCode); } } catch (error) { if (socketError.runtimeType != error.runtimeType) { @@ -289,8 +294,7 @@ class SignalClient extends Disposable with EventsEmittable { )); break; case lk_rtc.SignalResponse_Message.leave: - events.emit(SignalLeaveEvent( - canReconnect: msg.leave.canReconnect, reason: msg.leave.reason)); + events.emit(SignalLeaveEvent(request: msg.leave)); break; case lk_rtc.SignalResponse_Message.mute: events.emit(SignalRemoteMuteTrackEvent( diff --git a/lib/src/exceptions.dart b/lib/src/exceptions.dart index 97b914cd2..1779c4804 100644 --- a/lib/src/exceptions.dart +++ b/lib/src/exceptions.dart @@ -21,13 +21,25 @@ abstract class LiveKitException implements Exception { String toString() => 'LiveKit Exception: [$runtimeType] $message'; } +enum ConnectionErrorReason { + NotAllowed, + InternalError, + Timeout, +} + /// An exception occured while attempting to connect. /// Common reasons: /// - Invalid token (make sure your token is generated correctly) /// - Network condition is not good /// - Server not set up correctly (not responding) class ConnectException extends LiveKitException { - ConnectException([String msg = 'Failed to connect to server']) : super._(msg); + final ConnectionErrorReason reason; + final int statusCode; + ConnectException( + String msg, { + required this.reason, + this.statusCode = 0, + }) : super._(msg); } /// An exception occured while attempting to disconnect. diff --git a/lib/src/internal/events.dart b/lib/src/internal/events.dart index 392590d10..b6fc4c928 100644 --- a/lib/src/internal/events.dart +++ b/lib/src/internal/events.dart @@ -326,11 +326,14 @@ class EngineActiveSpeakersUpdateEvent with EngineEvent, InternalEvent { @internal class SignalLeaveEvent with SignalEvent, InternalEvent { - final bool canReconnect; - final lk_models.DisconnectReason reason; + bool get canReconnect => request.canReconnect; + lk_rtc.LeaveRequest_Action get action => request.action; + lk_models.DisconnectReason get reason => request.reason; + lk_rtc.RegionSettings? get regions => + request.hasReason() ? request.regions : null; + final lk_rtc.LeaveRequest request; const SignalLeaveEvent({ - required this.canReconnect, - required this.reason, + required this.request, }); } diff --git a/lib/src/proto/livekit_models.pb.dart b/lib/src/proto/livekit_models.pb.dart index cf1719b7b..38cb3e477 100644 --- a/lib/src/proto/livekit_models.pb.dart +++ b/lib/src/proto/livekit_models.pb.dart @@ -665,6 +665,7 @@ class ParticipantInfo extends $pb.GeneratedMessage { $core.bool? isPublisher, ParticipantInfo_Kind? kind, $core.Map<$core.String, $core.String>? attributes, + DisconnectReason? disconnectReason, }) { final $result = create(); if (sid != null) { @@ -706,6 +707,9 @@ class ParticipantInfo extends $pb.GeneratedMessage { if (attributes != null) { $result.attributes.addAll(attributes); } + if (disconnectReason != null) { + $result.disconnectReason = disconnectReason; + } return $result; } ParticipantInfo._() : super(); @@ -747,6 +751,11 @@ class ParticipantInfo extends $pb.GeneratedMessage { keyFieldType: $pb.PbFieldType.OS, valueFieldType: $pb.PbFieldType.OS, packageName: const $pb.PackageName('livekit')) + ..e( + 16, _omitFieldNames ? '' : 'disconnectReason', $pb.PbFieldType.OE, + defaultOrMaker: DisconnectReason.UNKNOWN_REASON, + valueOf: DisconnectReason.valueOf, + enumValues: DisconnectReason.values) ..hasRequiredFields = false; @$core.Deprecated('Using this can add significant overhead to your binary. ' @@ -914,6 +923,18 @@ class ParticipantInfo extends $pb.GeneratedMessage { @$pb.TagNumber(15) $core.Map<$core.String, $core.String> get attributes => $_getMap(12); + + @$pb.TagNumber(16) + DisconnectReason get disconnectReason => $_getN(13); + @$pb.TagNumber(16) + set disconnectReason(DisconnectReason v) { + setField(16, v); + } + + @$pb.TagNumber(16) + $core.bool hasDisconnectReason() => $_has(13); + @$pb.TagNumber(16) + void clearDisconnectReason() => clearField(16); } class Encryption extends $pb.GeneratedMessage { @@ -4197,6 +4218,511 @@ class RTPStats extends $pb.GeneratedMessage { RTPDrift ensureRebasedReportDrift() => $_ensure(43); } +enum RTPForwarderState_CodecMunger { vp8Munger, notSet } + +class RTPForwarderState extends $pb.GeneratedMessage { + factory RTPForwarderState({ + $core.bool? started, + $core.int? referenceLayerSpatial, + $fixnum.Int64? preStartTime, + $fixnum.Int64? extFirstTimestamp, + $fixnum.Int64? dummyStartTimestampOffset, + RTPMungerState? rtpMunger, + VP8MungerState? vp8Munger, + }) { + final $result = create(); + if (started != null) { + $result.started = started; + } + if (referenceLayerSpatial != null) { + $result.referenceLayerSpatial = referenceLayerSpatial; + } + if (preStartTime != null) { + $result.preStartTime = preStartTime; + } + if (extFirstTimestamp != null) { + $result.extFirstTimestamp = extFirstTimestamp; + } + if (dummyStartTimestampOffset != null) { + $result.dummyStartTimestampOffset = dummyStartTimestampOffset; + } + if (rtpMunger != null) { + $result.rtpMunger = rtpMunger; + } + if (vp8Munger != null) { + $result.vp8Munger = vp8Munger; + } + return $result; + } + RTPForwarderState._() : super(); + factory RTPForwarderState.fromBuffer($core.List<$core.int> i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(i, r); + factory RTPForwarderState.fromJson($core.String i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(i, r); + + static const $core.Map<$core.int, RTPForwarderState_CodecMunger> + _RTPForwarderState_CodecMungerByTag = { + 7: RTPForwarderState_CodecMunger.vp8Munger, + 0: RTPForwarderState_CodecMunger.notSet + }; + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'RTPForwarderState', + package: const $pb.PackageName(_omitMessageNames ? '' : 'livekit'), + createEmptyInstance: create) + ..oo(0, [7]) + ..aOB(1, _omitFieldNames ? '' : 'started') + ..a<$core.int>( + 2, _omitFieldNames ? '' : 'referenceLayerSpatial', $pb.PbFieldType.O3) + ..aInt64(3, _omitFieldNames ? '' : 'preStartTime') + ..a<$fixnum.Int64>( + 4, _omitFieldNames ? '' : 'extFirstTimestamp', $pb.PbFieldType.OU6, + defaultOrMaker: $fixnum.Int64.ZERO) + ..a<$fixnum.Int64>(5, _omitFieldNames ? '' : 'dummyStartTimestampOffset', + $pb.PbFieldType.OU6, + defaultOrMaker: $fixnum.Int64.ZERO) + ..aOM(6, _omitFieldNames ? '' : 'rtpMunger', + subBuilder: RTPMungerState.create) + ..aOM(7, _omitFieldNames ? '' : 'vp8Munger', + subBuilder: VP8MungerState.create) + ..hasRequiredFields = false; + + @$core.Deprecated('Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + RTPForwarderState clone() => RTPForwarderState()..mergeFromMessage(this); + @$core.Deprecated('Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + RTPForwarderState copyWith(void Function(RTPForwarderState) updates) => + super.copyWith((message) => updates(message as RTPForwarderState)) + as RTPForwarderState; + + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static RTPForwarderState create() => RTPForwarderState._(); + RTPForwarderState createEmptyInstance() => create(); + static $pb.PbList createRepeated() => + $pb.PbList(); + @$core.pragma('dart2js:noInline') + static RTPForwarderState getDefault() => _defaultInstance ??= + $pb.GeneratedMessage.$_defaultFor(create); + static RTPForwarderState? _defaultInstance; + + RTPForwarderState_CodecMunger whichCodecMunger() => + _RTPForwarderState_CodecMungerByTag[$_whichOneof(0)]!; + void clearCodecMunger() => clearField($_whichOneof(0)); + + @$pb.TagNumber(1) + $core.bool get started => $_getBF(0); + @$pb.TagNumber(1) + set started($core.bool v) { + $_setBool(0, v); + } + + @$pb.TagNumber(1) + $core.bool hasStarted() => $_has(0); + @$pb.TagNumber(1) + void clearStarted() => clearField(1); + + @$pb.TagNumber(2) + $core.int get referenceLayerSpatial => $_getIZ(1); + @$pb.TagNumber(2) + set referenceLayerSpatial($core.int v) { + $_setSignedInt32(1, v); + } + + @$pb.TagNumber(2) + $core.bool hasReferenceLayerSpatial() => $_has(1); + @$pb.TagNumber(2) + void clearReferenceLayerSpatial() => clearField(2); + + @$pb.TagNumber(3) + $fixnum.Int64 get preStartTime => $_getI64(2); + @$pb.TagNumber(3) + set preStartTime($fixnum.Int64 v) { + $_setInt64(2, v); + } + + @$pb.TagNumber(3) + $core.bool hasPreStartTime() => $_has(2); + @$pb.TagNumber(3) + void clearPreStartTime() => clearField(3); + + @$pb.TagNumber(4) + $fixnum.Int64 get extFirstTimestamp => $_getI64(3); + @$pb.TagNumber(4) + set extFirstTimestamp($fixnum.Int64 v) { + $_setInt64(3, v); + } + + @$pb.TagNumber(4) + $core.bool hasExtFirstTimestamp() => $_has(3); + @$pb.TagNumber(4) + void clearExtFirstTimestamp() => clearField(4); + + @$pb.TagNumber(5) + $fixnum.Int64 get dummyStartTimestampOffset => $_getI64(4); + @$pb.TagNumber(5) + set dummyStartTimestampOffset($fixnum.Int64 v) { + $_setInt64(4, v); + } + + @$pb.TagNumber(5) + $core.bool hasDummyStartTimestampOffset() => $_has(4); + @$pb.TagNumber(5) + void clearDummyStartTimestampOffset() => clearField(5); + + @$pb.TagNumber(6) + RTPMungerState get rtpMunger => $_getN(5); + @$pb.TagNumber(6) + set rtpMunger(RTPMungerState v) { + setField(6, v); + } + + @$pb.TagNumber(6) + $core.bool hasRtpMunger() => $_has(5); + @$pb.TagNumber(6) + void clearRtpMunger() => clearField(6); + @$pb.TagNumber(6) + RTPMungerState ensureRtpMunger() => $_ensure(5); + + @$pb.TagNumber(7) + VP8MungerState get vp8Munger => $_getN(6); + @$pb.TagNumber(7) + set vp8Munger(VP8MungerState v) { + setField(7, v); + } + + @$pb.TagNumber(7) + $core.bool hasVp8Munger() => $_has(6); + @$pb.TagNumber(7) + void clearVp8Munger() => clearField(7); + @$pb.TagNumber(7) + VP8MungerState ensureVp8Munger() => $_ensure(6); +} + +class RTPMungerState extends $pb.GeneratedMessage { + factory RTPMungerState({ + $fixnum.Int64? extLastSequenceNumber, + $fixnum.Int64? extSecondLastSequenceNumber, + $fixnum.Int64? extLastTimestamp, + $fixnum.Int64? extSecondLastTimestamp, + $core.bool? lastMarker, + $core.bool? secondLastMarker, + }) { + final $result = create(); + if (extLastSequenceNumber != null) { + $result.extLastSequenceNumber = extLastSequenceNumber; + } + if (extSecondLastSequenceNumber != null) { + $result.extSecondLastSequenceNumber = extSecondLastSequenceNumber; + } + if (extLastTimestamp != null) { + $result.extLastTimestamp = extLastTimestamp; + } + if (extSecondLastTimestamp != null) { + $result.extSecondLastTimestamp = extSecondLastTimestamp; + } + if (lastMarker != null) { + $result.lastMarker = lastMarker; + } + if (secondLastMarker != null) { + $result.secondLastMarker = secondLastMarker; + } + return $result; + } + RTPMungerState._() : super(); + factory RTPMungerState.fromBuffer($core.List<$core.int> i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(i, r); + factory RTPMungerState.fromJson($core.String i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'RTPMungerState', + package: const $pb.PackageName(_omitMessageNames ? '' : 'livekit'), + createEmptyInstance: create) + ..a<$fixnum.Int64>( + 1, _omitFieldNames ? '' : 'extLastSequenceNumber', $pb.PbFieldType.OU6, + defaultOrMaker: $fixnum.Int64.ZERO) + ..a<$fixnum.Int64>(2, _omitFieldNames ? '' : 'extSecondLastSequenceNumber', + $pb.PbFieldType.OU6, + defaultOrMaker: $fixnum.Int64.ZERO) + ..a<$fixnum.Int64>( + 3, _omitFieldNames ? '' : 'extLastTimestamp', $pb.PbFieldType.OU6, + defaultOrMaker: $fixnum.Int64.ZERO) + ..a<$fixnum.Int64>( + 4, _omitFieldNames ? '' : 'extSecondLastTimestamp', $pb.PbFieldType.OU6, + defaultOrMaker: $fixnum.Int64.ZERO) + ..aOB(5, _omitFieldNames ? '' : 'lastMarker') + ..aOB(6, _omitFieldNames ? '' : 'secondLastMarker') + ..hasRequiredFields = false; + + @$core.Deprecated('Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + RTPMungerState clone() => RTPMungerState()..mergeFromMessage(this); + @$core.Deprecated('Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + RTPMungerState copyWith(void Function(RTPMungerState) updates) => + super.copyWith((message) => updates(message as RTPMungerState)) + as RTPMungerState; + + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static RTPMungerState create() => RTPMungerState._(); + RTPMungerState createEmptyInstance() => create(); + static $pb.PbList createRepeated() => + $pb.PbList(); + @$core.pragma('dart2js:noInline') + static RTPMungerState getDefault() => _defaultInstance ??= + $pb.GeneratedMessage.$_defaultFor(create); + static RTPMungerState? _defaultInstance; + + @$pb.TagNumber(1) + $fixnum.Int64 get extLastSequenceNumber => $_getI64(0); + @$pb.TagNumber(1) + set extLastSequenceNumber($fixnum.Int64 v) { + $_setInt64(0, v); + } + + @$pb.TagNumber(1) + $core.bool hasExtLastSequenceNumber() => $_has(0); + @$pb.TagNumber(1) + void clearExtLastSequenceNumber() => clearField(1); + + @$pb.TagNumber(2) + $fixnum.Int64 get extSecondLastSequenceNumber => $_getI64(1); + @$pb.TagNumber(2) + set extSecondLastSequenceNumber($fixnum.Int64 v) { + $_setInt64(1, v); + } + + @$pb.TagNumber(2) + $core.bool hasExtSecondLastSequenceNumber() => $_has(1); + @$pb.TagNumber(2) + void clearExtSecondLastSequenceNumber() => clearField(2); + + @$pb.TagNumber(3) + $fixnum.Int64 get extLastTimestamp => $_getI64(2); + @$pb.TagNumber(3) + set extLastTimestamp($fixnum.Int64 v) { + $_setInt64(2, v); + } + + @$pb.TagNumber(3) + $core.bool hasExtLastTimestamp() => $_has(2); + @$pb.TagNumber(3) + void clearExtLastTimestamp() => clearField(3); + + @$pb.TagNumber(4) + $fixnum.Int64 get extSecondLastTimestamp => $_getI64(3); + @$pb.TagNumber(4) + set extSecondLastTimestamp($fixnum.Int64 v) { + $_setInt64(3, v); + } + + @$pb.TagNumber(4) + $core.bool hasExtSecondLastTimestamp() => $_has(3); + @$pb.TagNumber(4) + void clearExtSecondLastTimestamp() => clearField(4); + + @$pb.TagNumber(5) + $core.bool get lastMarker => $_getBF(4); + @$pb.TagNumber(5) + set lastMarker($core.bool v) { + $_setBool(4, v); + } + + @$pb.TagNumber(5) + $core.bool hasLastMarker() => $_has(4); + @$pb.TagNumber(5) + void clearLastMarker() => clearField(5); + + @$pb.TagNumber(6) + $core.bool get secondLastMarker => $_getBF(5); + @$pb.TagNumber(6) + set secondLastMarker($core.bool v) { + $_setBool(5, v); + } + + @$pb.TagNumber(6) + $core.bool hasSecondLastMarker() => $_has(5); + @$pb.TagNumber(6) + void clearSecondLastMarker() => clearField(6); +} + +class VP8MungerState extends $pb.GeneratedMessage { + factory VP8MungerState({ + $core.int? extLastPictureId, + $core.bool? pictureIdUsed, + $core.int? lastTl0PicIdx, + $core.bool? tl0PicIdxUsed, + $core.bool? tidUsed, + $core.int? lastKeyIdx, + $core.bool? keyIdxUsed, + }) { + final $result = create(); + if (extLastPictureId != null) { + $result.extLastPictureId = extLastPictureId; + } + if (pictureIdUsed != null) { + $result.pictureIdUsed = pictureIdUsed; + } + if (lastTl0PicIdx != null) { + $result.lastTl0PicIdx = lastTl0PicIdx; + } + if (tl0PicIdxUsed != null) { + $result.tl0PicIdxUsed = tl0PicIdxUsed; + } + if (tidUsed != null) { + $result.tidUsed = tidUsed; + } + if (lastKeyIdx != null) { + $result.lastKeyIdx = lastKeyIdx; + } + if (keyIdxUsed != null) { + $result.keyIdxUsed = keyIdxUsed; + } + return $result; + } + VP8MungerState._() : super(); + factory VP8MungerState.fromBuffer($core.List<$core.int> i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(i, r); + factory VP8MungerState.fromJson($core.String i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'VP8MungerState', + package: const $pb.PackageName(_omitMessageNames ? '' : 'livekit'), + createEmptyInstance: create) + ..a<$core.int>( + 1, _omitFieldNames ? '' : 'extLastPictureId', $pb.PbFieldType.O3) + ..aOB(2, _omitFieldNames ? '' : 'pictureIdUsed') + ..a<$core.int>( + 3, _omitFieldNames ? '' : 'lastTl0PicIdx', $pb.PbFieldType.OU3) + ..aOB(4, _omitFieldNames ? '' : 'tl0PicIdxUsed') + ..aOB(5, _omitFieldNames ? '' : 'tidUsed') + ..a<$core.int>(6, _omitFieldNames ? '' : 'lastKeyIdx', $pb.PbFieldType.OU3) + ..aOB(7, _omitFieldNames ? '' : 'keyIdxUsed') + ..hasRequiredFields = false; + + @$core.Deprecated('Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + VP8MungerState clone() => VP8MungerState()..mergeFromMessage(this); + @$core.Deprecated('Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + VP8MungerState copyWith(void Function(VP8MungerState) updates) => + super.copyWith((message) => updates(message as VP8MungerState)) + as VP8MungerState; + + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static VP8MungerState create() => VP8MungerState._(); + VP8MungerState createEmptyInstance() => create(); + static $pb.PbList createRepeated() => + $pb.PbList(); + @$core.pragma('dart2js:noInline') + static VP8MungerState getDefault() => _defaultInstance ??= + $pb.GeneratedMessage.$_defaultFor(create); + static VP8MungerState? _defaultInstance; + + @$pb.TagNumber(1) + $core.int get extLastPictureId => $_getIZ(0); + @$pb.TagNumber(1) + set extLastPictureId($core.int v) { + $_setSignedInt32(0, v); + } + + @$pb.TagNumber(1) + $core.bool hasExtLastPictureId() => $_has(0); + @$pb.TagNumber(1) + void clearExtLastPictureId() => clearField(1); + + @$pb.TagNumber(2) + $core.bool get pictureIdUsed => $_getBF(1); + @$pb.TagNumber(2) + set pictureIdUsed($core.bool v) { + $_setBool(1, v); + } + + @$pb.TagNumber(2) + $core.bool hasPictureIdUsed() => $_has(1); + @$pb.TagNumber(2) + void clearPictureIdUsed() => clearField(2); + + @$pb.TagNumber(3) + $core.int get lastTl0PicIdx => $_getIZ(2); + @$pb.TagNumber(3) + set lastTl0PicIdx($core.int v) { + $_setUnsignedInt32(2, v); + } + + @$pb.TagNumber(3) + $core.bool hasLastTl0PicIdx() => $_has(2); + @$pb.TagNumber(3) + void clearLastTl0PicIdx() => clearField(3); + + @$pb.TagNumber(4) + $core.bool get tl0PicIdxUsed => $_getBF(3); + @$pb.TagNumber(4) + set tl0PicIdxUsed($core.bool v) { + $_setBool(3, v); + } + + @$pb.TagNumber(4) + $core.bool hasTl0PicIdxUsed() => $_has(3); + @$pb.TagNumber(4) + void clearTl0PicIdxUsed() => clearField(4); + + @$pb.TagNumber(5) + $core.bool get tidUsed => $_getBF(4); + @$pb.TagNumber(5) + set tidUsed($core.bool v) { + $_setBool(4, v); + } + + @$pb.TagNumber(5) + $core.bool hasTidUsed() => $_has(4); + @$pb.TagNumber(5) + void clearTidUsed() => clearField(5); + + @$pb.TagNumber(6) + $core.int get lastKeyIdx => $_getIZ(5); + @$pb.TagNumber(6) + set lastKeyIdx($core.int v) { + $_setUnsignedInt32(5, v); + } + + @$pb.TagNumber(6) + $core.bool hasLastKeyIdx() => $_has(5); + @$pb.TagNumber(6) + void clearLastKeyIdx() => clearField(6); + + @$pb.TagNumber(7) + $core.bool get keyIdxUsed => $_getBF(6); + @$pb.TagNumber(7) + set keyIdxUsed($core.bool v) { + $_setBool(6, v); + } + + @$pb.TagNumber(7) + $core.bool hasKeyIdxUsed() => $_has(6); + @$pb.TagNumber(7) + void clearKeyIdxUsed() => clearField(7); +} + class TimedVersion extends $pb.GeneratedMessage { factory TimedVersion({ $fixnum.Int64? unixMicro, diff --git a/lib/src/proto/livekit_models.pbenum.dart b/lib/src/proto/livekit_models.pbenum.dart index 23ea2ca08..abab8debe 100644 --- a/lib/src/proto/livekit_models.pbenum.dart +++ b/lib/src/proto/livekit_models.pbenum.dart @@ -211,6 +211,8 @@ class DisconnectReason extends $pb.ProtobufEnum { DisconnectReason._(8, _omitEnumNames ? '' : 'MIGRATION'); static const DisconnectReason SIGNAL_CLOSE = DisconnectReason._(9, _omitEnumNames ? '' : 'SIGNAL_CLOSE'); + static const DisconnectReason ROOM_CLOSED = + DisconnectReason._(10, _omitEnumNames ? '' : 'ROOM_CLOSED'); static const $core.List values = [ UNKNOWN_REASON, @@ -223,6 +225,7 @@ class DisconnectReason extends $pb.ProtobufEnum { JOIN_FAILURE, MIGRATION, SIGNAL_CLOSE, + ROOM_CLOSED, ]; static final $core.Map<$core.int, DisconnectReason> _byValue = diff --git a/lib/src/proto/livekit_models.pbjson.dart b/lib/src/proto/livekit_models.pbjson.dart index 854b6a216..dc92ce4a6 100644 --- a/lib/src/proto/livekit_models.pbjson.dart +++ b/lib/src/proto/livekit_models.pbjson.dart @@ -148,6 +148,7 @@ const DisconnectReason$json = { {'1': 'JOIN_FAILURE', '2': 7}, {'1': 'MIGRATION', '2': 8}, {'1': 'SIGNAL_CLOSE', '2': 9}, + {'1': 'ROOM_CLOSED', '2': 10}, ], }; @@ -156,7 +157,8 @@ final $typed_data.Uint8List disconnectReasonDescriptor = $convert.base64Decode( 'ChBEaXNjb25uZWN0UmVhc29uEhIKDlVOS05PV05fUkVBU09OEAASFAoQQ0xJRU5UX0lOSVRJQV' 'RFRBABEhYKEkRVUExJQ0FURV9JREVOVElUWRACEhMKD1NFUlZFUl9TSFVURE9XThADEhcKE1BB' 'UlRJQ0lQQU5UX1JFTU9WRUQQBBIQCgxST09NX0RFTEVURUQQBRISCg5TVEFURV9NSVNNQVRDSB' - 'AGEhAKDEpPSU5fRkFJTFVSRRAHEg0KCU1JR1JBVElPThAIEhAKDFNJR05BTF9DTE9TRRAJ'); + 'AGEhAKDEpPSU5fRkFJTFVSRRAHEg0KCU1JR1JBVElPThAIEhAKDFNJR05BTF9DTE9TRRAJEg8K' + 'C1JPT01fQ0xPU0VEEAo='); @$core.Deprecated('Use reconnectReasonDescriptor instead') const ReconnectReason$json = { @@ -396,6 +398,14 @@ const ParticipantInfo$json = { '6': '.livekit.ParticipantInfo.AttributesEntry', '10': 'attributes' }, + { + '1': 'disconnect_reason', + '3': 16, + '4': 1, + '5': 14, + '6': '.livekit.DisconnectReason', + '10': 'disconnectReason' + }, ], '3': [ParticipantInfo_AttributesEntry$json], '4': [ParticipantInfo_State$json, ParticipantInfo_Kind$json], @@ -445,10 +455,12 @@ final $typed_data.Uint8List participantInfoDescriptor = $convert.base64Decode( 'ZWdpb24YDCABKAlSBnJlZ2lvbhIhCgxpc19wdWJsaXNoZXIYDSABKAhSC2lzUHVibGlzaGVyEj' 'EKBGtpbmQYDiABKA4yHS5saXZla2l0LlBhcnRpY2lwYW50SW5mby5LaW5kUgRraW5kEkgKCmF0' 'dHJpYnV0ZXMYDyADKAsyKC5saXZla2l0LlBhcnRpY2lwYW50SW5mby5BdHRyaWJ1dGVzRW50cn' - 'lSCmF0dHJpYnV0ZXMaPQoPQXR0cmlidXRlc0VudHJ5EhAKA2tleRgBIAEoCVIDa2V5EhQKBXZh' - 'bHVlGAIgASgJUgV2YWx1ZToCOAEiPgoFU3RhdGUSCwoHSk9JTklORxAAEgoKBkpPSU5FRBABEg' - 'oKBkFDVElWRRACEhAKDERJU0NPTk5FQ1RFRBADIkEKBEtpbmQSDAoIU1RBTkRBUkQQABILCgdJ' - 'TkdSRVNTEAESCgoGRUdSRVNTEAISBwoDU0lQEAMSCQoFQUdFTlQQBA=='); + 'lSCmF0dHJpYnV0ZXMSRgoRZGlzY29ubmVjdF9yZWFzb24YECABKA4yGS5saXZla2l0LkRpc2Nv' + 'bm5lY3RSZWFzb25SEGRpc2Nvbm5lY3RSZWFzb24aPQoPQXR0cmlidXRlc0VudHJ5EhAKA2tleR' + 'gBIAEoCVIDa2V5EhQKBXZhbHVlGAIgASgJUgV2YWx1ZToCOAEiPgoFU3RhdGUSCwoHSk9JTklO' + 'RxAAEgoKBkpPSU5FRBABEgoKBkFDVElWRRACEhAKDERJU0NPTk5FQ1RFRBADIkEKBEtpbmQSDA' + 'oIU1RBTkRBUkQQABILCgdJTkdSRVNTEAESCgoGRUdSRVNTEAISBwoDU0lQEAMSCQoFQUdFTlQQ' + 'BA=='); @$core.Deprecated('Use encryptionDescriptor instead') const Encryption$json = { @@ -1344,6 +1356,147 @@ final $typed_data.Uint8List rTPStatsDescriptor = $convert.base64Decode( 'NlZFJlcG9ydERyaWZ0Gj8KEUdhcEhpc3RvZ3JhbUVudHJ5EhAKA2tleRgBIAEoBVIDa2V5EhQK' 'BXZhbHVlGAIgASgNUgV2YWx1ZToCOAE='); +@$core.Deprecated('Use rTPForwarderStateDescriptor instead') +const RTPForwarderState$json = { + '1': 'RTPForwarderState', + '2': [ + {'1': 'started', '3': 1, '4': 1, '5': 8, '10': 'started'}, + { + '1': 'reference_layer_spatial', + '3': 2, + '4': 1, + '5': 5, + '10': 'referenceLayerSpatial' + }, + {'1': 'pre_start_time', '3': 3, '4': 1, '5': 3, '10': 'preStartTime'}, + { + '1': 'ext_first_timestamp', + '3': 4, + '4': 1, + '5': 4, + '10': 'extFirstTimestamp' + }, + { + '1': 'dummy_start_timestamp_offset', + '3': 5, + '4': 1, + '5': 4, + '10': 'dummyStartTimestampOffset' + }, + { + '1': 'rtp_munger', + '3': 6, + '4': 1, + '5': 11, + '6': '.livekit.RTPMungerState', + '10': 'rtpMunger' + }, + { + '1': 'vp8_munger', + '3': 7, + '4': 1, + '5': 11, + '6': '.livekit.VP8MungerState', + '9': 0, + '10': 'vp8Munger' + }, + ], + '8': [ + {'1': 'codec_munger'}, + ], +}; + +/// Descriptor for `RTPForwarderState`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List rTPForwarderStateDescriptor = $convert.base64Decode( + 'ChFSVFBGb3J3YXJkZXJTdGF0ZRIYCgdzdGFydGVkGAEgASgIUgdzdGFydGVkEjYKF3JlZmVyZW' + '5jZV9sYXllcl9zcGF0aWFsGAIgASgFUhVyZWZlcmVuY2VMYXllclNwYXRpYWwSJAoOcHJlX3N0' + 'YXJ0X3RpbWUYAyABKANSDHByZVN0YXJ0VGltZRIuChNleHRfZmlyc3RfdGltZXN0YW1wGAQgAS' + 'gEUhFleHRGaXJzdFRpbWVzdGFtcBI/ChxkdW1teV9zdGFydF90aW1lc3RhbXBfb2Zmc2V0GAUg' + 'ASgEUhlkdW1teVN0YXJ0VGltZXN0YW1wT2Zmc2V0EjYKCnJ0cF9tdW5nZXIYBiABKAsyFy5saX' + 'Zla2l0LlJUUE11bmdlclN0YXRlUglydHBNdW5nZXISOAoKdnA4X211bmdlchgHIAEoCzIXLmxp' + 'dmVraXQuVlA4TXVuZ2VyU3RhdGVIAFIJdnA4TXVuZ2VyQg4KDGNvZGVjX211bmdlcg=='); + +@$core.Deprecated('Use rTPMungerStateDescriptor instead') +const RTPMungerState$json = { + '1': 'RTPMungerState', + '2': [ + { + '1': 'ext_last_sequence_number', + '3': 1, + '4': 1, + '5': 4, + '10': 'extLastSequenceNumber' + }, + { + '1': 'ext_second_last_sequence_number', + '3': 2, + '4': 1, + '5': 4, + '10': 'extSecondLastSequenceNumber' + }, + { + '1': 'ext_last_timestamp', + '3': 3, + '4': 1, + '5': 4, + '10': 'extLastTimestamp' + }, + { + '1': 'ext_second_last_timestamp', + '3': 4, + '4': 1, + '5': 4, + '10': 'extSecondLastTimestamp' + }, + {'1': 'last_marker', '3': 5, '4': 1, '5': 8, '10': 'lastMarker'}, + { + '1': 'second_last_marker', + '3': 6, + '4': 1, + '5': 8, + '10': 'secondLastMarker' + }, + ], +}; + +/// Descriptor for `RTPMungerState`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List rTPMungerStateDescriptor = $convert.base64Decode( + 'Cg5SVFBNdW5nZXJTdGF0ZRI3ChhleHRfbGFzdF9zZXF1ZW5jZV9udW1iZXIYASABKARSFWV4dE' + 'xhc3RTZXF1ZW5jZU51bWJlchJECh9leHRfc2Vjb25kX2xhc3Rfc2VxdWVuY2VfbnVtYmVyGAIg' + 'ASgEUhtleHRTZWNvbmRMYXN0U2VxdWVuY2VOdW1iZXISLAoSZXh0X2xhc3RfdGltZXN0YW1wGA' + 'MgASgEUhBleHRMYXN0VGltZXN0YW1wEjkKGWV4dF9zZWNvbmRfbGFzdF90aW1lc3RhbXAYBCAB' + 'KARSFmV4dFNlY29uZExhc3RUaW1lc3RhbXASHwoLbGFzdF9tYXJrZXIYBSABKAhSCmxhc3RNYX' + 'JrZXISLAoSc2Vjb25kX2xhc3RfbWFya2VyGAYgASgIUhBzZWNvbmRMYXN0TWFya2Vy'); + +@$core.Deprecated('Use vP8MungerStateDescriptor instead') +const VP8MungerState$json = { + '1': 'VP8MungerState', + '2': [ + { + '1': 'ext_last_picture_id', + '3': 1, + '4': 1, + '5': 5, + '10': 'extLastPictureId' + }, + {'1': 'picture_id_used', '3': 2, '4': 1, '5': 8, '10': 'pictureIdUsed'}, + {'1': 'last_tl0_pic_idx', '3': 3, '4': 1, '5': 13, '10': 'lastTl0PicIdx'}, + {'1': 'tl0_pic_idx_used', '3': 4, '4': 1, '5': 8, '10': 'tl0PicIdxUsed'}, + {'1': 'tid_used', '3': 5, '4': 1, '5': 8, '10': 'tidUsed'}, + {'1': 'last_key_idx', '3': 6, '4': 1, '5': 13, '10': 'lastKeyIdx'}, + {'1': 'key_idx_used', '3': 7, '4': 1, '5': 8, '10': 'keyIdxUsed'}, + ], +}; + +/// Descriptor for `VP8MungerState`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List vP8MungerStateDescriptor = $convert.base64Decode( + 'Cg5WUDhNdW5nZXJTdGF0ZRItChNleHRfbGFzdF9waWN0dXJlX2lkGAEgASgFUhBleHRMYXN0UG' + 'ljdHVyZUlkEiYKD3BpY3R1cmVfaWRfdXNlZBgCIAEoCFINcGljdHVyZUlkVXNlZBInChBsYXN0' + 'X3RsMF9waWNfaWR4GAMgASgNUg1sYXN0VGwwUGljSWR4EicKEHRsMF9waWNfaWR4X3VzZWQYBC' + 'ABKAhSDXRsMFBpY0lkeFVzZWQSGQoIdGlkX3VzZWQYBSABKAhSB3RpZFVzZWQSIAoMbGFzdF9r' + 'ZXlfaWR4GAYgASgNUgpsYXN0S2V5SWR4EiAKDGtleV9pZHhfdXNlZBgHIAEoCFIKa2V5SWR4VX' + 'NlZA=='); + @$core.Deprecated('Use timedVersionDescriptor instead') const TimedVersion$json = { '1': 'TimedVersion', diff --git a/lib/src/proto/livekit_rtc.pb.dart b/lib/src/proto/livekit_rtc.pb.dart index ddf6ebeb5..3132b2193 100644 --- a/lib/src/proto/livekit_rtc.pb.dart +++ b/lib/src/proto/livekit_rtc.pb.dart @@ -492,7 +492,8 @@ enum SignalResponse_Message { reconnect, pongResp, subscriptionResponse, - errorResponse, + requestResponse, + trackSubscribed, notSet } @@ -518,7 +519,8 @@ class SignalResponse extends $pb.GeneratedMessage { ReconnectResponse? reconnect, Pong? pongResp, SubscriptionResponse? subscriptionResponse, - ErrorResponse? errorResponse, + RequestResponse? requestResponse, + TrackSubscribed? trackSubscribed, }) { final $result = create(); if (join != null) { @@ -581,8 +583,11 @@ class SignalResponse extends $pb.GeneratedMessage { if (subscriptionResponse != null) { $result.subscriptionResponse = subscriptionResponse; } - if (errorResponse != null) { - $result.errorResponse = errorResponse; + if (requestResponse != null) { + $result.requestResponse = requestResponse; + } + if (trackSubscribed != null) { + $result.trackSubscribed = trackSubscribed; } return $result; } @@ -616,7 +621,8 @@ class SignalResponse extends $pb.GeneratedMessage { 19: SignalResponse_Message.reconnect, 20: SignalResponse_Message.pongResp, 21: SignalResponse_Message.subscriptionResponse, - 22: SignalResponse_Message.errorResponse, + 22: SignalResponse_Message.requestResponse, + 23: SignalResponse_Message.trackSubscribed, 0: SignalResponse_Message.notSet }; static final $pb.BuilderInfo _i = $pb.BuilderInfo( @@ -644,7 +650,8 @@ class SignalResponse extends $pb.GeneratedMessage { 19, 20, 21, - 22 + 22, + 23 ]) ..aOM(1, _omitFieldNames ? '' : 'join', subBuilder: JoinResponse.create) @@ -688,8 +695,10 @@ class SignalResponse extends $pb.GeneratedMessage { ..aOM( 21, _omitFieldNames ? '' : 'subscriptionResponse', subBuilder: SubscriptionResponse.create) - ..aOM(22, _omitFieldNames ? '' : 'errorResponse', - subBuilder: ErrorResponse.create) + ..aOM(22, _omitFieldNames ? '' : 'requestResponse', + subBuilder: RequestResponse.create) + ..aOM(23, _omitFieldNames ? '' : 'trackSubscribed', + subBuilder: TrackSubscribed.create) ..hasRequiredFields = false; @$core.Deprecated('Using this can add significant overhead to your binary. ' @@ -1017,20 +1026,35 @@ class SignalResponse extends $pb.GeneratedMessage { @$pb.TagNumber(21) SubscriptionResponse ensureSubscriptionResponse() => $_ensure(19); - /// Errors relating to user inititated requests that carry a `request_id` + /// Response relating to user inititated requests that carry a `request_id` @$pb.TagNumber(22) - ErrorResponse get errorResponse => $_getN(20); + RequestResponse get requestResponse => $_getN(20); @$pb.TagNumber(22) - set errorResponse(ErrorResponse v) { + set requestResponse(RequestResponse v) { setField(22, v); } @$pb.TagNumber(22) - $core.bool hasErrorResponse() => $_has(20); + $core.bool hasRequestResponse() => $_has(20); @$pb.TagNumber(22) - void clearErrorResponse() => clearField(22); + void clearRequestResponse() => clearField(22); @$pb.TagNumber(22) - ErrorResponse ensureErrorResponse() => $_ensure(20); + RequestResponse ensureRequestResponse() => $_ensure(20); + + /// notify to the publisher when a published track has been subscribed for the first time + @$pb.TagNumber(23) + TrackSubscribed get trackSubscribed => $_getN(21); + @$pb.TagNumber(23) + set trackSubscribed(TrackSubscribed v) { + setField(23, v); + } + + @$pb.TagNumber(23) + $core.bool hasTrackSubscribed() => $_has(21); + @$pb.TagNumber(23) + void clearTrackSubscribed() => clearField(23); + @$pb.TagNumber(23) + TrackSubscribed ensureTrackSubscribed() => $_ensure(21); } class SimulcastCodec extends $pb.GeneratedMessage { @@ -1417,6 +1441,7 @@ class TrickleRequest extends $pb.GeneratedMessage { factory TrickleRequest({ $core.String? candidateInit, SignalTarget? target, + $core.bool? final_3, }) { final $result = create(); if (candidateInit != null) { @@ -1425,6 +1450,9 @@ class TrickleRequest extends $pb.GeneratedMessage { if (target != null) { $result.target = target; } + if (final_3 != null) { + $result.final_3 = final_3; + } return $result; } TrickleRequest._() : super(); @@ -1444,6 +1472,7 @@ class TrickleRequest extends $pb.GeneratedMessage { defaultOrMaker: SignalTarget.PUBLISHER, valueOf: SignalTarget.valueOf, enumValues: SignalTarget.values) + ..aOB(3, _omitFieldNames ? '' : 'final') ..hasRequiredFields = false; @$core.Deprecated('Using this can add significant overhead to your binary. ' @@ -1492,6 +1521,18 @@ class TrickleRequest extends $pb.GeneratedMessage { $core.bool hasTarget() => $_has(1); @$pb.TagNumber(2) void clearTarget() => clearField(2); + + @$pb.TagNumber(3) + $core.bool get final_3 => $_getBF(2); + @$pb.TagNumber(3) + set final_3($core.bool v) { + $_setBool(2, v); + } + + @$pb.TagNumber(3) + $core.bool hasFinal_3() => $_has(2); + @$pb.TagNumber(3) + void clearFinal_3() => clearField(3); } class MuteTrackRequest extends $pb.GeneratedMessage { @@ -4799,10 +4840,10 @@ class SubscriptionResponse extends $pb.GeneratedMessage { void clearErr() => clearField(2); } -class ErrorResponse extends $pb.GeneratedMessage { - factory ErrorResponse({ +class RequestResponse extends $pb.GeneratedMessage { + factory RequestResponse({ $core.int? requestId, - ErrorResponse_Reason? reason, + RequestResponse_Reason? reason, $core.String? message, }) { final $result = create(); @@ -4817,49 +4858,49 @@ class ErrorResponse extends $pb.GeneratedMessage { } return $result; } - ErrorResponse._() : super(); - factory ErrorResponse.fromBuffer($core.List<$core.int> i, + RequestResponse._() : super(); + factory RequestResponse.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); - factory ErrorResponse.fromJson($core.String i, + factory RequestResponse.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); static final $pb.BuilderInfo _i = $pb.BuilderInfo( - _omitMessageNames ? '' : 'ErrorResponse', + _omitMessageNames ? '' : 'RequestResponse', package: const $pb.PackageName(_omitMessageNames ? '' : 'livekit'), createEmptyInstance: create) ..a<$core.int>(1, _omitFieldNames ? '' : 'requestId', $pb.PbFieldType.OU3) - ..e( + ..e( 2, _omitFieldNames ? '' : 'reason', $pb.PbFieldType.OE, - defaultOrMaker: ErrorResponse_Reason.UNKNOWN, - valueOf: ErrorResponse_Reason.valueOf, - enumValues: ErrorResponse_Reason.values) + defaultOrMaker: RequestResponse_Reason.OK, + valueOf: RequestResponse_Reason.valueOf, + enumValues: RequestResponse_Reason.values) ..aOS(3, _omitFieldNames ? '' : 'message') ..hasRequiredFields = false; @$core.Deprecated('Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' 'Will be removed in next major version') - ErrorResponse clone() => ErrorResponse()..mergeFromMessage(this); + RequestResponse clone() => RequestResponse()..mergeFromMessage(this); @$core.Deprecated('Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') - ErrorResponse copyWith(void Function(ErrorResponse) updates) => - super.copyWith((message) => updates(message as ErrorResponse)) - as ErrorResponse; + RequestResponse copyWith(void Function(RequestResponse) updates) => + super.copyWith((message) => updates(message as RequestResponse)) + as RequestResponse; $pb.BuilderInfo get info_ => _i; @$core.pragma('dart2js:noInline') - static ErrorResponse create() => ErrorResponse._(); - ErrorResponse createEmptyInstance() => create(); - static $pb.PbList createRepeated() => - $pb.PbList(); + static RequestResponse create() => RequestResponse._(); + RequestResponse createEmptyInstance() => create(); + static $pb.PbList createRepeated() => + $pb.PbList(); @$core.pragma('dart2js:noInline') - static ErrorResponse getDefault() => _defaultInstance ??= - $pb.GeneratedMessage.$_defaultFor(create); - static ErrorResponse? _defaultInstance; + static RequestResponse getDefault() => _defaultInstance ??= + $pb.GeneratedMessage.$_defaultFor(create); + static RequestResponse? _defaultInstance; @$pb.TagNumber(1) $core.int get requestId => $_getIZ(0); @@ -4874,9 +4915,9 @@ class ErrorResponse extends $pb.GeneratedMessage { void clearRequestId() => clearField(1); @$pb.TagNumber(2) - ErrorResponse_Reason get reason => $_getN(1); + RequestResponse_Reason get reason => $_getN(1); @$pb.TagNumber(2) - set reason(ErrorResponse_Reason v) { + set reason(RequestResponse_Reason v) { setField(2, v); } @@ -4898,6 +4939,67 @@ class ErrorResponse extends $pb.GeneratedMessage { void clearMessage() => clearField(3); } +class TrackSubscribed extends $pb.GeneratedMessage { + factory TrackSubscribed({ + $core.String? trackSid, + }) { + final $result = create(); + if (trackSid != null) { + $result.trackSid = trackSid; + } + return $result; + } + TrackSubscribed._() : super(); + factory TrackSubscribed.fromBuffer($core.List<$core.int> i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(i, r); + factory TrackSubscribed.fromJson($core.String i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'TrackSubscribed', + package: const $pb.PackageName(_omitMessageNames ? '' : 'livekit'), + createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'trackSid') + ..hasRequiredFields = false; + + @$core.Deprecated('Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + TrackSubscribed clone() => TrackSubscribed()..mergeFromMessage(this); + @$core.Deprecated('Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + TrackSubscribed copyWith(void Function(TrackSubscribed) updates) => + super.copyWith((message) => updates(message as TrackSubscribed)) + as TrackSubscribed; + + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static TrackSubscribed create() => TrackSubscribed._(); + TrackSubscribed createEmptyInstance() => create(); + static $pb.PbList createRepeated() => + $pb.PbList(); + @$core.pragma('dart2js:noInline') + static TrackSubscribed getDefault() => _defaultInstance ??= + $pb.GeneratedMessage.$_defaultFor(create); + static TrackSubscribed? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get trackSid => $_getSZ(0); + @$pb.TagNumber(1) + set trackSid($core.String v) { + $_setString(0, v); + } + + @$pb.TagNumber(1) + $core.bool hasTrackSid() => $_has(0); + @$pb.TagNumber(1) + void clearTrackSid() => clearField(1); +} + const _omitFieldNames = $core.bool.fromEnvironment('protobuf.omit_field_names'); const _omitMessageNames = $core.bool.fromEnvironment('protobuf.omit_message_names'); diff --git a/lib/src/proto/livekit_rtc.pbenum.dart b/lib/src/proto/livekit_rtc.pbenum.dart index 33ec6fb9f..8c2d29624 100644 --- a/lib/src/proto/livekit_rtc.pbenum.dart +++ b/lib/src/proto/livekit_rtc.pbenum.dart @@ -92,28 +92,29 @@ class LeaveRequest_Action extends $pb.ProtobufEnum { const LeaveRequest_Action._($core.int v, $core.String n) : super(v, n); } -class ErrorResponse_Reason extends $pb.ProtobufEnum { - static const ErrorResponse_Reason UNKNOWN = - ErrorResponse_Reason._(0, _omitEnumNames ? '' : 'UNKNOWN'); - static const ErrorResponse_Reason NOT_FOUND = - ErrorResponse_Reason._(1, _omitEnumNames ? '' : 'NOT_FOUND'); - static const ErrorResponse_Reason NOT_ALLOWED = - ErrorResponse_Reason._(2, _omitEnumNames ? '' : 'NOT_ALLOWED'); - static const ErrorResponse_Reason INVALID_ARGUMENT = - ErrorResponse_Reason._(3, _omitEnumNames ? '' : 'INVALID_ARGUMENT'); - - static const $core.List values = [ - UNKNOWN, +class RequestResponse_Reason extends $pb.ProtobufEnum { + static const RequestResponse_Reason OK = + RequestResponse_Reason._(0, _omitEnumNames ? '' : 'OK'); + static const RequestResponse_Reason NOT_FOUND = + RequestResponse_Reason._(1, _omitEnumNames ? '' : 'NOT_FOUND'); + static const RequestResponse_Reason NOT_ALLOWED = + RequestResponse_Reason._(2, _omitEnumNames ? '' : 'NOT_ALLOWED'); + static const RequestResponse_Reason LIMIT_EXCEEDED = + RequestResponse_Reason._(3, _omitEnumNames ? '' : 'LIMIT_EXCEEDED'); + + static const $core.List values = + [ + OK, NOT_FOUND, NOT_ALLOWED, - INVALID_ARGUMENT, + LIMIT_EXCEEDED, ]; - static final $core.Map<$core.int, ErrorResponse_Reason> _byValue = + static final $core.Map<$core.int, RequestResponse_Reason> _byValue = $pb.ProtobufEnum.initByValue(values); - static ErrorResponse_Reason? valueOf($core.int value) => _byValue[value]; + static RequestResponse_Reason? valueOf($core.int value) => _byValue[value]; - const ErrorResponse_Reason._($core.int v, $core.String n) : super(v, n); + const RequestResponse_Reason._($core.int v, $core.String n) : super(v, n); } const _omitEnumNames = $core.bool.fromEnvironment('protobuf.omit_enum_names'); diff --git a/lib/src/proto/livekit_rtc.pbjson.dart b/lib/src/proto/livekit_rtc.pbjson.dart index 2a0da4602..1f295eca8 100644 --- a/lib/src/proto/livekit_rtc.pbjson.dart +++ b/lib/src/proto/livekit_rtc.pbjson.dart @@ -408,13 +408,22 @@ const SignalResponse$json = { '10': 'subscriptionResponse' }, { - '1': 'error_response', + '1': 'request_response', '3': 22, '4': 1, '5': 11, - '6': '.livekit.ErrorResponse', + '6': '.livekit.RequestResponse', '9': 0, - '10': 'errorResponse' + '10': 'requestResponse' + }, + { + '1': 'track_subscribed', + '3': 23, + '4': 1, + '5': 11, + '6': '.livekit.TrackSubscribed', + '9': 0, + '10': 'trackSubscribed' }, ], '8': [ @@ -447,8 +456,9 @@ final $typed_data.Uint8List signalResponseDescriptor = $convert.base64Decode( 'dmVraXQuUmVjb25uZWN0UmVzcG9uc2VIAFIJcmVjb25uZWN0EiwKCXBvbmdfcmVzcBgUIAEoCz' 'INLmxpdmVraXQuUG9uZ0gAUghwb25nUmVzcBJUChVzdWJzY3JpcHRpb25fcmVzcG9uc2UYFSAB' 'KAsyHS5saXZla2l0LlN1YnNjcmlwdGlvblJlc3BvbnNlSABSFHN1YnNjcmlwdGlvblJlc3Bvbn' - 'NlEj8KDmVycm9yX3Jlc3BvbnNlGBYgASgLMhYubGl2ZWtpdC5FcnJvclJlc3BvbnNlSABSDWVy' - 'cm9yUmVzcG9uc2VCCQoHbWVzc2FnZQ=='); + 'NlEkUKEHJlcXVlc3RfcmVzcG9uc2UYFiABKAsyGC5saXZla2l0LlJlcXVlc3RSZXNwb25zZUgA' + 'Ug9yZXF1ZXN0UmVzcG9uc2USRQoQdHJhY2tfc3Vic2NyaWJlZBgXIAEoCzIYLmxpdmVraXQuVH' + 'JhY2tTdWJzY3JpYmVkSABSD3RyYWNrU3Vic2NyaWJlZEIJCgdtZXNzYWdl'); @$core.Deprecated('Use simulcastCodecDescriptor instead') const SimulcastCodec$json = { @@ -547,13 +557,15 @@ const TrickleRequest$json = { '6': '.livekit.SignalTarget', '10': 'target' }, + {'1': 'final', '3': 3, '4': 1, '5': 8, '10': 'final'}, ], }; /// Descriptor for `TrickleRequest`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List trickleRequestDescriptor = $convert.base64Decode( 'Cg5Ucmlja2xlUmVxdWVzdBIkCg1jYW5kaWRhdGVJbml0GAEgASgJUg1jYW5kaWRhdGVJbml0Ei' - '0KBnRhcmdldBgCIAEoDjIVLmxpdmVraXQuU2lnbmFsVGFyZ2V0UgZ0YXJnZXQ='); + '0KBnRhcmdldBgCIAEoDjIVLmxpdmVraXQuU2lnbmFsVGFyZ2V0UgZ0YXJnZXQSFAoFZmluYWwY' + 'AyABKAhSBWZpbmFs'); @$core.Deprecated('Use muteTrackRequestDescriptor instead') const MuteTrackRequest$json = { @@ -1461,9 +1473,9 @@ final $typed_data.Uint8List subscriptionResponseDescriptor = $convert.base64Deco 'ChRTdWJzY3JpcHRpb25SZXNwb25zZRIbCgl0cmFja19zaWQYASABKAlSCHRyYWNrU2lkEiwKA2' 'VychgCIAEoDjIaLmxpdmVraXQuU3Vic2NyaXB0aW9uRXJyb3JSA2Vycg=='); -@$core.Deprecated('Use errorResponseDescriptor instead') -const ErrorResponse$json = { - '1': 'ErrorResponse', +@$core.Deprecated('Use requestResponseDescriptor instead') +const RequestResponse$json = { + '1': 'RequestResponse', '2': [ {'1': 'request_id', '3': 1, '4': 1, '5': 13, '10': 'requestId'}, { @@ -1471,28 +1483,40 @@ const ErrorResponse$json = { '3': 2, '4': 1, '5': 14, - '6': '.livekit.ErrorResponse.Reason', + '6': '.livekit.RequestResponse.Reason', '10': 'reason' }, {'1': 'message', '3': 3, '4': 1, '5': 9, '10': 'message'}, ], - '4': [ErrorResponse_Reason$json], + '4': [RequestResponse_Reason$json], }; -@$core.Deprecated('Use errorResponseDescriptor instead') -const ErrorResponse_Reason$json = { +@$core.Deprecated('Use requestResponseDescriptor instead') +const RequestResponse_Reason$json = { '1': 'Reason', '2': [ - {'1': 'UNKNOWN', '2': 0}, + {'1': 'OK', '2': 0}, {'1': 'NOT_FOUND', '2': 1}, {'1': 'NOT_ALLOWED', '2': 2}, - {'1': 'INVALID_ARGUMENT', '2': 3}, + {'1': 'LIMIT_EXCEEDED', '2': 3}, + ], +}; + +/// Descriptor for `RequestResponse`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List requestResponseDescriptor = $convert.base64Decode( + 'Cg9SZXF1ZXN0UmVzcG9uc2USHQoKcmVxdWVzdF9pZBgBIAEoDVIJcmVxdWVzdElkEjcKBnJlYX' + 'NvbhgCIAEoDjIfLmxpdmVraXQuUmVxdWVzdFJlc3BvbnNlLlJlYXNvblIGcmVhc29uEhgKB21l' + 'c3NhZ2UYAyABKAlSB21lc3NhZ2UiRAoGUmVhc29uEgYKAk9LEAASDQoJTk9UX0ZPVU5EEAESDw' + 'oLTk9UX0FMTE9XRUQQAhISCg5MSU1JVF9FWENFRURFRBAD'); + +@$core.Deprecated('Use trackSubscribedDescriptor instead') +const TrackSubscribed$json = { + '1': 'TrackSubscribed', + '2': [ + {'1': 'track_sid', '3': 1, '4': 1, '5': 9, '10': 'trackSid'}, ], }; -/// Descriptor for `ErrorResponse`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List errorResponseDescriptor = $convert.base64Decode( - 'Cg1FcnJvclJlc3BvbnNlEh0KCnJlcXVlc3RfaWQYASABKA1SCXJlcXVlc3RJZBI1CgZyZWFzb2' - '4YAiABKA4yHS5saXZla2l0LkVycm9yUmVzcG9uc2UuUmVhc29uUgZyZWFzb24SGAoHbWVzc2Fn' - 'ZRgDIAEoCVIHbWVzc2FnZSJLCgZSZWFzb24SCwoHVU5LTk9XThAAEg0KCU5PVF9GT1VORBABEg' - '8KC05PVF9BTExPV0VEEAISFAoQSU5WQUxJRF9BUkdVTUVOVBAD'); +/// Descriptor for `TrackSubscribed`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List trackSubscribedDescriptor = $convert.base64Decode( + 'Cg9UcmFja1N1YnNjcmliZWQSGwoJdHJhY2tfc2lkGAEgASgJUgh0cmFja1NpZA=='); diff --git a/lib/src/support/region_url_provider.dart b/lib/src/support/region_url_provider.dart new file mode 100644 index 000000000..ba92bdb97 --- /dev/null +++ b/lib/src/support/region_url_provider.dart @@ -0,0 +1,139 @@ +import 'dart:convert' show json; + +import 'package:fixnum/fixnum.dart'; +import 'package:http/http.dart' as http; + +import '../exceptions.dart'; +import '../logger.dart'; +import '../proto/livekit_rtc.pb.dart' as lk_models; + +class RegionUrlProvider { + Uri serverUrl; + + String token; + + lk_models.RegionSettings? regionSettings; + + List attemptedRegions = []; + + int lastUpdateAt = 0; + + int settingsCacheTime = 3000; + + RegionUrlProvider({required String url, required this.token}) + : serverUrl = Uri.parse(url); + + updateToken(String token) { + this.token = token; + } + + bool isCloud() => isCloudUrl(serverUrl); + + Uri getServerUrl() { + return serverUrl; + } + + Future getNextBestRegionUrl() async { + if (!isCloud()) { + throw Exception( + 'region availability is only supported for LiveKit Cloud domains'); + } + if (regionSettings == null || + DateTime.now().microsecondsSinceEpoch - lastUpdateAt > + settingsCacheTime) { + regionSettings = await fetchRegionSettings(); + } + final regionsLeft = regionSettings?.regions.where( + (region) => !attemptedRegions + .any((attempted) => attempted.region == region.region), + ); + if (regionsLeft?.isNotEmpty ?? false) { + final nextRegion = regionsLeft!.first; + attemptedRegions.add(nextRegion); + logger.fine('next region: ${nextRegion.region}'); + return nextRegion.url; + } else { + return null; + } + } + + resetAttempts() { + attemptedRegions = []; + } + + /* @internal */ + Future fetchRegionSettings() async { + var url = '${getCloudConfigUrl(serverUrl)}/regions'; + http.Response regionSettingsResponse = + await http.get(Uri.parse(url), headers: { + 'authorization': 'Bearer $token', + }); + if (regionSettingsResponse.statusCode == 200) { + var mapData = json.decode(regionSettingsResponse.body); + var regions = (mapData['regions'] as List) + .map((region) => lk_models.RegionInfo( + distance: Int64(int.parse(region['distance'])), + region: region['region'], + url: region['url'])) + .toList(); + var regionSettings = lk_models.RegionSettings( + regions: regions, + ); + lastUpdateAt = DateTime.now().microsecondsSinceEpoch; + return regionSettings; + } else { + throw ConnectException( + 'Could not fetch region settings: ${regionSettingsResponse.body}, status: ${regionSettingsResponse.statusCode}', + reason: regionSettingsResponse.statusCode == 401 + ? ConnectionErrorReason.NotAllowed + : ConnectionErrorReason.InternalError, + statusCode: regionSettingsResponse.statusCode); + } + } + + setServerReportedRegions(lk_models.RegionSettings regions) { + regionSettings = regions; + lastUpdateAt = DateTime.now().millisecondsSinceEpoch; + } + + String getCloudConfigUrl(Uri serverUrl) { + return '${serverUrl.scheme.replaceAll('ws', 'http')}://${serverUrl.host}/settings'; + } +} + +extension RegionInfoExtension on lk_models.RegionInfo { + lk_models.RegionInfo fromJson(Map json) => + lk_models.RegionInfo( + region: json['region'], + url: json['url'], + distance: json['distance'], + ); +} + +extension RegionSettingsExtension on lk_models.RegionSettings { + lk_models.RegionSettings fromJson(Map json) => + lk_models.RegionSettings( + regions: json['regions'] + .map((region) => lk_models.RegionInfo.fromJson(region)) + .toList(), + ); +} + +bool isCloudUrl(Uri uri) { + return uri.host.contains('.livekit.cloud') || + uri.host.contains('.livekit.run'); +} + +String toHttpUrl(String url) { + if (url.startsWith('ws')) { + return url.replaceFirst('ws', 'http'); + } + return url; +} + +String toWebsocketUrl(String url) { + if (url.startsWith('http')) { + return url.replaceFirst('http', 'ws'); + } + return url; +} diff --git a/lib/src/support/websocket.dart b/lib/src/support/websocket.dart index eeaa45e81..5e88ae4e4 100644 --- a/lib/src/support/websocket.dart +++ b/lib/src/support/websocket.dart @@ -16,17 +16,9 @@ import '../support/disposable.dart'; import 'websocket/io.dart' if (dart.library.html) 'websocket/web.dart'; class WebSocketException implements Exception { - final int code; - const WebSocketException._(this.code); - - static WebSocketException unknown() => const WebSocketException._(0); - static WebSocketException connect() => const WebSocketException._(1); - - @override - String toString() => { - WebSocketException.unknown(): 'Unknown error', - WebSocketException.connect(): 'Failed to connect', - }[this]!; + final String message; + final dynamic error; + const WebSocketException(this.message, [this.error]); } typedef WebSocketOnData = Function(dynamic data); diff --git a/lib/src/support/websocket/io.dart b/lib/src/support/websocket/io.dart index d2ef577f2..1e9dec1e8 100644 --- a/lib/src/support/websocket/io.dart +++ b/lib/src/support/websocket/io.dart @@ -78,9 +78,9 @@ class LiveKitWebSocketIO extends LiveKitWebSocket { final ws = await io.WebSocket.connect(uri.toString()); logger.fine('[WebSocketIO] Connected'); return LiveKitWebSocketIO._(ws, options); - } catch (_) { - logger.severe('[WebSocketIO] did throw ${_}'); - throw WebSocketException.connect(); + } catch (err) { + logger.severe('[WebSocketIO] did throw $err'); + throw WebSocketException('Failed to connect', err); } } } diff --git a/lib/src/support/websocket/web.dart b/lib/src/support/websocket/web.dart index 574e1afa1..bade1a8df 100644 --- a/lib/src/support/websocket/web.dart +++ b/lib/src/support/websocket/web.dart @@ -74,8 +74,8 @@ class LiveKitWebSocketWeb extends LiveKitWebSocket { final ws = web.WebSocket(uri.toString()); ws.onOpen .listen((_) => completer.complete(LiveKitWebSocketWeb._(ws, options))); - ws.onError - .listen((_) => completer.completeError(WebSocketException.connect())); + ws.onError.listen((e) => + completer.completeError(WebSocketException('Failed to connect', e))); return completer.future; } }