diff --git a/.changeset/bright-bitrates-start.md b/.changeset/bright-bitrates-start.md new file mode 100644 index 000000000..be0ae0a25 --- /dev/null +++ b/.changeset/bright-bitrates-start.md @@ -0,0 +1,5 @@ +--- +"client-sdk-android": minor +--- + +Add `Room.setPublisherInitialVideoBitrate` to configure the publisher PeerConnection's current and maximum video bitrate after connecting. diff --git a/livekit-android-sdk/src/main/java/io/livekit/android/room/RTCEngine.kt b/livekit-android-sdk/src/main/java/io/livekit/android/room/RTCEngine.kt index e0e64e7db..7a7c5bff4 100644 --- a/livekit-android-sdk/src/main/java/io/livekit/android/room/RTCEngine.kt +++ b/livekit-android-sdk/src/main/java/io/livekit/android/room/RTCEngine.kt @@ -443,6 +443,26 @@ internal constructor( return SenderTransceiverHandle(publisher, transceiver, sessionState) } + /** + * Sets the publisher PeerConnection's initial video bitrate. + */ + internal suspend fun setPublisherInitialVideoBitrate( + initialBitrateBps: Int, + maxBitrateBps: Int, + ): Boolean { + require(initialBitrateBps > 0) + require(maxBitrateBps >= initialBitrateBps) + + val publisherTransport = publisher ?: return false + return publisherTransport.withPeerConnection { + setBitrate( + null, + initialBitrateBps, + maxBitrateBps, + ) + } == true + } + fun updateSubscriptionPermissions( allParticipants: Boolean, participantTrackPermissions: List, diff --git a/livekit-android-sdk/src/main/java/io/livekit/android/room/Room.kt b/livekit-android-sdk/src/main/java/io/livekit/android/room/Room.kt index 10aa87821..6575d641f 100644 --- a/livekit-android-sdk/src/main/java/io/livekit/android/room/Room.kt +++ b/livekit-android-sdk/src/main/java/io/livekit/android/room/Room.kt @@ -1623,6 +1623,31 @@ constructor( */ fun getPublisherRTCStats(callback: RTCStatsCollectorCallback) = engine.getPublisherRTCStats(callback) + /** + * Sets the local publisher's initial video bitrate. + * + * Call this after the room has connected and before or immediately after publishing video. + * For example: + * ``` + * room.setPublisherInitialVideoBitrate( + * initialBitrateBps = 1_500_000, + * maxBitrateBps = 3_500_000, + * ) + * ``` + * + * @return `true` when WebRTC accepts the bitrate configuration, otherwise `false`. + * @throws IllegalArgumentException if either bitrate is invalid. + */ + suspend fun setPublisherInitialVideoBitrate( + initialBitrateBps: Int, + maxBitrateBps: Int, + ): Boolean { + return engine.setPublisherInitialVideoBitrate( + initialBitrateBps = initialBitrateBps, + maxBitrateBps = maxBitrateBps, + ) + } + /** * Get stats for the subscriber peer connection. * diff --git a/livekit-android-test/src/main/java/io/livekit/android/test/mock/MockPeerConnection.kt b/livekit-android-test/src/main/java/io/livekit/android/test/mock/MockPeerConnection.kt index 6a51cfebe..5a9695703 100644 --- a/livekit-android-test/src/main/java/io/livekit/android/test/mock/MockPeerConnection.kt +++ b/livekit-android-test/src/main/java/io/livekit/android/test/mock/MockPeerConnection.kt @@ -46,6 +46,7 @@ class MockPeerConnection( private var closed = false var localDesc: SessionDescription? = null var remoteDesc: SessionDescription? = null + var lastBitrateSettings: Triple? = null private val signalStateMachine = SignalStateMachine { newState -> observer?.onSignalingChange(newState) @@ -210,6 +211,7 @@ class MockPeerConnection( } override fun setBitrate(min: Int?, current: Int?, max: Int?): Boolean { + lastBitrateSettings = Triple(min, current, max) return true } diff --git a/livekit-android-test/src/test/java/io/livekit/android/room/RTCEngineMockE2ETest.kt b/livekit-android-test/src/test/java/io/livekit/android/room/RTCEngineMockE2ETest.kt index 6225fb1ec..0df8115f6 100644 --- a/livekit-android-test/src/test/java/io/livekit/android/room/RTCEngineMockE2ETest.kt +++ b/livekit-android-test/src/test/java/io/livekit/android/room/RTCEngineMockE2ETest.kt @@ -149,6 +149,22 @@ class RTCEngineMockE2ETest : MockE2ETest() { ) } + @Test + fun setPublisherInitialVideoBitrate() = runTest { + connect() + + val applied = room.setPublisherInitialVideoBitrate( + initialBitrateBps = 1_500_000, + maxBitrateBps = 3_500_000, + ) + + assertTrue(applied) + assertEquals( + Triple(null, 1_500_000, 3_500_000), + getPublisherPeerConnection().lastBitrateSettings, + ) + } + @Test fun iceSubscriberConnect() = runTest { connect()