diff --git a/.version b/.version index 834f26295..dbe590065 100644 --- a/.version +++ b/.version @@ -1 +1 @@ -2.8.0 +2.8.1 diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f1778128..e9247553a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # CHANGELOG +## 2.8.1 + +* Added: Add agent deployment targeting to token source options +* Fixed: Android plugin compatibility with AGP 9 built-in Kotlin +* Fixed: Use maintain-resolution as the default video degradation preference for local video publishing + ## 2.8.0 * Added: Session API support for simpler E2EE setup diff --git a/README.md b/README.md index 9a80d3dfb..93316d167 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,7 @@ Include this package to your `pubspec.yaml` ```yaml --- dependencies: - livekit_client: ^2.8.0 + livekit_client: ^2.8.1 ``` ### iOS diff --git a/android/build.gradle b/android/build.gradle index dbc0cb96b..17fa1f448 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -2,7 +2,7 @@ group = "io.livekit.plugin" version = "1.0-SNAPSHOT" buildscript { - ext.kotlin_version = "1.8.22" + ext.kotlin_version = "2.1.0" repositories { google() mavenCentral() @@ -22,7 +22,18 @@ allprojects { } apply plugin: "com.android.library" -apply plugin: "kotlin-android" + +// AGP 9's built-in Kotlin compiles Kotlin itself and rejects the Kotlin Gradle +// Plugin. Apply KGP only when built-in Kotlin is NOT active: that means AGP < 9, +// or AGP 9 with android.builtInKotlin=false (the configuration Flutter currently +// ships by default while the ecosystem migrates). +def agpMajor = com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION.tokenize(".")[0] as int +def builtInKotlinActive = agpMajor >= 9 && + (!project.hasProperty("android.builtInKotlin") || + Boolean.parseBoolean(project.property("android.builtInKotlin").toString())) +if (!builtInKotlinActive) { + apply plugin: "kotlin-android" +} android { if (project.android.hasProperty("namespace")) { @@ -36,10 +47,6 @@ android { targetCompatibility = JavaVersion.VERSION_1_8 } - kotlinOptions { - jvmTarget = JavaVersion.VERSION_1_8 - } - sourceSets { main.java.srcDirs += "src/main/kotlin" test.java.srcDirs += "src/test/kotlin" @@ -68,3 +75,19 @@ android { } } } + +// Configure the Kotlin JVM target. The compilerOptions DSL requires KGP 1.9+ or +// AGP 9 built-in Kotlin; older Flutter app templates ship KGP 1.8.x, which only +// supports the legacy kotlinOptions DSL. +def kotlinExt = project.extensions.findByName("kotlin") +if (kotlinExt?.hasProperty("compilerOptions")) { + kotlin { + compilerOptions { + jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_1_8 + } + } +} else { + android.kotlinOptions { + jvmTarget = JavaVersion.VERSION_1_8.toString() + } +} diff --git a/ios/livekit_client.podspec b/ios/livekit_client.podspec index e9b3032eb..dc471f74d 100644 --- a/ios/livekit_client.podspec +++ b/ios/livekit_client.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'livekit_client' - s.version = '2.8.0' + s.version = '2.8.1' s.summary = 'Open source platform for real-time audio and video.' s.description = 'Open source platform for real-time audio and video.' s.homepage = 'https://livekit.io/' diff --git a/lib/src/livekit.dart b/lib/src/livekit.dart index 6cdc2cba8..9b86d8578 100644 --- a/lib/src/livekit.dart +++ b/lib/src/livekit.dart @@ -20,7 +20,7 @@ import 'support/platform.dart' show lkPlatformIsMobile; /// Main entry point to connect to a room. /// {@category Room} class LiveKitClient { - static const version = '2.8.0'; + static const version = '2.8.1'; /// Initialize the WebRTC plugin. If this is not manually called, will be /// initialized with default settings. diff --git a/lib/src/participant/local.dart b/lib/src/participant/local.dart index e785b72fa..1d2781d91 100644 --- a/lib/src/participant/local.dart +++ b/lib/src/participant/local.dart @@ -386,10 +386,7 @@ class LocalParticipant extends Participant { } if ([TrackSource.camera, TrackSource.screenShareVideo].contains(track.source)) { - final degradationPreference = publishOptions.degradationPreference ?? - getDefaultDegradationPreference( - track, - ); + final degradationPreference = publishOptions.degradationPreference ?? DegradationPreference.maintainResolution; await track.setDegradationPreference(degradationPreference); } @@ -487,10 +484,7 @@ class LocalParticipant extends Participant { } if ([TrackSource.camera, TrackSource.screenShareVideo].contains(track.source)) { - final degradationPreference = publishOptions.degradationPreference ?? - getDefaultDegradationPreference( - track, - ); + final degradationPreference = publishOptions.degradationPreference ?? DegradationPreference.maintainResolution; await track.setDegradationPreference(degradationPreference); } @@ -593,17 +587,6 @@ class LocalParticipant extends Participant { await pub.dispose(); } - DegradationPreference getDefaultDegradationPreference(LocalVideoTrack track) { - // a few of reasons we have different default paths: - // 1. without this, Chrome seems to aggressively resize the SVC video stating `quality-limitation: bandwidth` even when BW isn't an issue - // 2. since we are overriding contentHint to motion (to workaround L1T3 publishing), it overrides the default degradationPreference to `balanced` - final VideoDimensions dimensions = track.currentOptions.params.dimensions; - if (track.source == TrackSource.screenShareVideo || dimensions.height >= 1080) { - return DegradationPreference.maintainResolution; - } - return DegradationPreference.balanced; - } - /// Convenience method to unpublish all tracks. Future unpublishAllTracks({bool notify = true, bool? stopOnUnpublish}) async { final trackSids = trackPublications.keys.toSet(); diff --git a/lib/src/token_source/room_configuration.dart b/lib/src/token_source/room_configuration.dart index 24ce8af8b..82a3898b1 100644 --- a/lib/src/token_source/room_configuration.dart +++ b/lib/src/token_source/room_configuration.dart @@ -26,9 +26,13 @@ class RoomAgentDispatch { /// Metadata for the agent. final String? metadata; + /// Optional deployment to target. Leave empty to target the production deployment. + final String? deployment; + const RoomAgentDispatch({ this.agentName, this.metadata, + this.deployment, }); factory RoomAgentDispatch.fromJson(Map json) => _$RoomAgentDispatchFromJson(json); diff --git a/lib/src/token_source/room_configuration.g.dart b/lib/src/token_source/room_configuration.g.dart index c7517a575..9bfaee4d2 100644 --- a/lib/src/token_source/room_configuration.g.dart +++ b/lib/src/token_source/room_configuration.g.dart @@ -9,11 +9,13 @@ part of 'room_configuration.dart'; RoomAgentDispatch _$RoomAgentDispatchFromJson(Map json) => RoomAgentDispatch( agentName: json['agent_name'] as String?, metadata: json['metadata'] as String?, + deployment: json['deployment'] as String?, ); Map _$RoomAgentDispatchToJson(RoomAgentDispatch instance) => { if (instance.agentName case final value?) 'agent_name': value, if (instance.metadata case final value?) 'metadata': value, + if (instance.deployment case final value?) 'deployment': value, }; RoomConfiguration _$RoomConfigurationFromJson(Map json) => RoomConfiguration( diff --git a/lib/src/token_source/token_source.dart b/lib/src/token_source/token_source.dart index f2f4e8304..392c62557 100644 --- a/lib/src/token_source/token_source.dart +++ b/lib/src/token_source/token_source.dart @@ -43,6 +43,9 @@ class TokenRequestOptions { /// Metadata passed to the agent job. final String? agentMetadata; + /// Optional deployment to target. Leave empty to target the production deployment. + final String? agentDeployment; + const TokenRequestOptions({ this.roomName, this.participantName, @@ -51,6 +54,7 @@ class TokenRequestOptions { this.participantAttributes, this.agentName, this.agentMetadata, + this.agentDeployment, }); factory TokenRequestOptions.fromJson(Map json) => _$TokenRequestOptionsFromJson(json); @@ -58,8 +62,8 @@ class TokenRequestOptions { /// Converts this options object to a wire-format request. TokenSourceRequest toRequest() { - final List? agents = (agentName != null || agentMetadata != null) - ? [RoomAgentDispatch(agentName: agentName, metadata: agentMetadata)] + final List? agents = (agentName != null || agentMetadata != null || agentDeployment != null) + ? [RoomAgentDispatch(agentName: agentName, metadata: agentMetadata, deployment: agentDeployment)] : null; return TokenSourceRequest( @@ -83,6 +87,7 @@ class TokenRequestOptions { other.participantMetadata == participantMetadata && other.agentName == agentName && other.agentMetadata == agentMetadata && + other.agentDeployment == agentDeployment && const MapEquality().equals(other.participantAttributes, participantAttributes); } @@ -95,6 +100,7 @@ class TokenRequestOptions { participantMetadata, agentName, agentMetadata, + agentDeployment, const MapEquality().hash(participantAttributes), ); } diff --git a/lib/src/token_source/token_source.g.dart b/lib/src/token_source/token_source.g.dart index a5ac7308e..ec930e18c 100644 --- a/lib/src/token_source/token_source.g.dart +++ b/lib/src/token_source/token_source.g.dart @@ -16,6 +16,7 @@ TokenRequestOptions _$TokenRequestOptionsFromJson(Map json) => ), agentName: json['agentName'] as String?, agentMetadata: json['agentMetadata'] as String?, + agentDeployment: json['agentDeployment'] as String?, ); Map _$TokenRequestOptionsToJson(TokenRequestOptions instance) => { @@ -26,6 +27,7 @@ Map _$TokenRequestOptionsToJson(TokenRequestOptions instance) = if (instance.participantAttributes case final value?) 'participantAttributes': value, if (instance.agentName case final value?) 'agentName': value, if (instance.agentMetadata case final value?) 'agentMetadata': value, + if (instance.agentDeployment case final value?) 'agentDeployment': value, }; TokenSourceRequest _$TokenSourceRequestFromJson(Map json) => TokenSourceRequest( diff --git a/macos/livekit_client.podspec b/macos/livekit_client.podspec index 16c318b85..cdabf2d49 100644 --- a/macos/livekit_client.podspec +++ b/macos/livekit_client.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'livekit_client' - s.version = '2.8.0' + s.version = '2.8.1' s.summary = 'Open source platform for real-time audio and video.' s.description = 'Open source platform for real-time audio and video.' s.homepage = 'https://livekit.io/' diff --git a/pubspec.yaml b/pubspec.yaml index 2c8783473..e153dc5f0 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -15,7 +15,7 @@ name: livekit_client description: Flutter Client SDK for LiveKit. Build real-time video and audio into your apps. Supports iOS, Android, and Web. -version: 2.8.0 +version: 2.8.1 homepage: https://github.com/livekit/client-sdk-flutter environment: