davidliu
Committed by GitHub

Allow setting of degradation preference (#420)

* Allow setting of degradation preference

* fix tests

* spotless
@@ -465,7 +465,6 @@ internal fun isSVCCodec(codec: String?): Boolean { @@ -465,7 +465,6 @@ internal fun isSVCCodec(codec: String?): Boolean {
465 /** 465 /**
466 * @suppress 466 * @suppress
467 */ 467 */
468 -@VisibleForTesting  
469 data class TrackBitrateInfo( 468 data class TrackBitrateInfo(
470 val codec: String, 469 val codec: String,
471 val maxBitrate: Long, 470 val maxBitrate: Long,
@@ -474,7 +473,6 @@ data class TrackBitrateInfo( @@ -474,7 +473,6 @@ data class TrackBitrateInfo(
474 /** 473 /**
475 * @suppress 474 * @suppress
476 */ 475 */
477 -@VisibleForTesting  
478 sealed class TrackBitrateInfoKey { 476 sealed class TrackBitrateInfoKey {
479 data class Cid(val value: String) : TrackBitrateInfoKey() 477 data class Cid(val value: String) : TrackBitrateInfoKey()
480 data class Transceiver(val value: RtpTransceiver) : TrackBitrateInfoKey() 478 data class Transceiver(val value: RtpTransceiver) : TrackBitrateInfoKey()
@@ -31,7 +31,19 @@ import io.livekit.android.room.DefaultsManager @@ -31,7 +31,19 @@ import io.livekit.android.room.DefaultsManager
31 import io.livekit.android.room.RTCEngine 31 import io.livekit.android.room.RTCEngine
32 import io.livekit.android.room.TrackBitrateInfo 32 import io.livekit.android.room.TrackBitrateInfo
33 import io.livekit.android.room.isSVCCodec 33 import io.livekit.android.room.isSVCCodec
34 -import io.livekit.android.room.track.* 34 +import io.livekit.android.room.track.DataPublishReliability
  35 +import io.livekit.android.room.track.LocalAudioTrack
  36 +import io.livekit.android.room.track.LocalAudioTrackOptions
  37 +import io.livekit.android.room.track.LocalScreencastVideoTrack
  38 +import io.livekit.android.room.track.LocalTrackPublication
  39 +import io.livekit.android.room.track.LocalVideoTrack
  40 +import io.livekit.android.room.track.LocalVideoTrackOptions
  41 +import io.livekit.android.room.track.Track
  42 +import io.livekit.android.room.track.TrackException
  43 +import io.livekit.android.room.track.TrackPublication
  44 +import io.livekit.android.room.track.VideoCaptureParameter
  45 +import io.livekit.android.room.track.VideoCodec
  46 +import io.livekit.android.room.track.VideoEncoding
35 import io.livekit.android.room.util.EncodingUtils 47 import io.livekit.android.room.util.EncodingUtils
36 import io.livekit.android.util.LKLog 48 import io.livekit.android.util.LKLog
37 import io.livekit.android.webrtc.sortVideoCodecPreferences 49 import io.livekit.android.webrtc.sortVideoCodecPreferences
@@ -41,8 +53,14 @@ import livekit.LivekitModels @@ -41,8 +53,14 @@ import livekit.LivekitModels
41 import livekit.LivekitRtc 53 import livekit.LivekitRtc
42 import livekit.LivekitRtc.AddTrackRequest 54 import livekit.LivekitRtc.AddTrackRequest
43 import livekit.LivekitRtc.SimulcastCodec 55 import livekit.LivekitRtc.SimulcastCodec
44 -import livekit.org.webrtc.* 56 +import livekit.org.webrtc.EglBase
  57 +import livekit.org.webrtc.PeerConnectionFactory
  58 +import livekit.org.webrtc.RtpParameters
  59 +import livekit.org.webrtc.RtpTransceiver
45 import livekit.org.webrtc.RtpTransceiver.RtpTransceiverInit 60 import livekit.org.webrtc.RtpTransceiver.RtpTransceiverInit
  61 +import livekit.org.webrtc.SurfaceTextureHelper
  62 +import livekit.org.webrtc.VideoCapturer
  63 +import livekit.org.webrtc.VideoProcessor
46 import javax.inject.Named 64 import javax.inject.Named
47 import kotlin.math.max 65 import kotlin.math.max
48 66
@@ -412,10 +430,14 @@ internal constructor( @@ -412,10 +430,14 @@ internal constructor(
412 } 430 }
413 } 431 }
414 432
415 - // Set preferred video codec order  
416 if (options is VideoTrackPublishOptions) { 433 if (options is VideoTrackPublishOptions) {
  434 + // Set preferred video codec order
417 transceiver.sortVideoCodecPreferences(options.videoCodec, capabilitiesGetter) 435 transceiver.sortVideoCodecPreferences(options.videoCodec, capabilitiesGetter)
418 (track as LocalVideoTrack).codec = options.videoCodec 436 (track as LocalVideoTrack).codec = options.videoCodec
  437 +
  438 + val rtpParameters = transceiver.sender.parameters
  439 + rtpParameters.degradationPreference = options.degradationPreference
  440 + transceiver.sender.parameters = rtpParameters
419 } 441 }
420 442
421 val publication = LocalTrackPublication( 443 val publication = LocalTrackPublication(
@@ -877,6 +899,14 @@ abstract class BaseVideoTrackPublishOptions { @@ -877,6 +899,14 @@ abstract class BaseVideoTrackPublishOptions {
877 * will automatically publish a secondary track encoded with the backup codec. 899 * will automatically publish a secondary track encoded with the backup codec.
878 */ 900 */
879 abstract val backupCodec: BackupVideoCodec? 901 abstract val backupCodec: BackupVideoCodec?
  902 +
  903 + /**
  904 + * When bandwidth is constrained, this preference indicates which is preferred
  905 + * between degrading resolution vs. framerate.
  906 + *
  907 + * null value indicates default value (maintain framerate).
  908 + */
  909 + abstract val degradationPreference: RtpParameters.DegradationPreference?
880 } 910 }
881 911
882 data class VideoTrackPublishDefaults( 912 data class VideoTrackPublishDefaults(
@@ -885,6 +915,7 @@ data class VideoTrackPublishDefaults( @@ -885,6 +915,7 @@ data class VideoTrackPublishDefaults(
885 override val videoCodec: String = VideoCodec.VP8.codecName, 915 override val videoCodec: String = VideoCodec.VP8.codecName,
886 override val scalabilityMode: String? = null, 916 override val scalabilityMode: String? = null,
887 override val backupCodec: BackupVideoCodec? = null, 917 override val backupCodec: BackupVideoCodec? = null,
  918 + override val degradationPreference: RtpParameters.DegradationPreference? = null,
888 ) : BaseVideoTrackPublishOptions() 919 ) : BaseVideoTrackPublishOptions()
889 920
890 data class VideoTrackPublishOptions( 921 data class VideoTrackPublishOptions(
@@ -896,6 +927,7 @@ data class VideoTrackPublishOptions( @@ -896,6 +927,7 @@ data class VideoTrackPublishOptions(
896 override val backupCodec: BackupVideoCodec? = null, 927 override val backupCodec: BackupVideoCodec? = null,
897 override val source: Track.Source? = null, 928 override val source: Track.Source? = null,
898 override val stream: String? = null, 929 override val stream: String? = null,
  930 + override val degradationPreference: RtpParameters.DegradationPreference? = null,
899 ) : BaseVideoTrackPublishOptions(), TrackPublishOptions { 931 ) : BaseVideoTrackPublishOptions(), TrackPublishOptions {
900 constructor( 932 constructor(
901 name: String? = null, 933 name: String? = null,
@@ -911,6 +943,7 @@ data class VideoTrackPublishOptions( @@ -911,6 +943,7 @@ data class VideoTrackPublishOptions(
911 backupCodec = base.backupCodec, 943 backupCodec = base.backupCodec,
912 source = source, 944 source = source,
913 stream = stream, 945 stream = stream,
  946 + degradationPreference = base.degradationPreference,
914 ) 947 )
915 948
916 fun createBackupOptions(): VideoTrackPublishOptions? { 949 fun createBackupOptions(): VideoTrackPublishOptions? {
@@ -16,11 +16,30 @@ @@ -16,11 +16,30 @@
16 16
17 package io.livekit.android.test.mock 17 package io.livekit.android.test.mock
18 18
  19 +import livekit.org.webrtc.MockRtpParameters
  20 +import livekit.org.webrtc.RtpParameters
19 import livekit.org.webrtc.RtpSender 21 import livekit.org.webrtc.RtpSender
20 import org.mockito.Mockito 22 import org.mockito.Mockito
  23 +import org.mockito.kotlin.any
  24 +import org.mockito.kotlin.whenever
  25 +import java.util.UUID
21 26
22 object MockRtpSender { 27 object MockRtpSender {
23 fun create(): RtpSender { 28 fun create(): RtpSender {
24 - return Mockito.mock(RtpSender::class.java) 29 + var rtpParameters: RtpParameters = MockRtpParameters(
  30 + transactionId = UUID.randomUUID().toString(),
  31 + degradationPreference = null,
  32 + rtcp = MockRtpParameters.MockRtcp("", false),
  33 + headerExtensions = mutableListOf(),
  34 + encodings = mutableListOf(),
  35 + codecs = mutableListOf(),
  36 + )
  37 + return Mockito.mock(RtpSender::class.java).apply {
  38 + whenever(this.parameters).thenAnswer { rtpParameters }
  39 + whenever(this.setParameters(any())).thenAnswer {
  40 + rtpParameters = it.getArgument(0)
  41 + true
  42 + }
  43 + }
25 } 44 }
26 } 45 }
  1 +/*
  2 + * Copyright 2024 LiveKit, Inc.
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +
  17 +package livekit.org.webrtc
  18 +
  19 +class MockRtpParameters(
  20 + transactionId: String?,
  21 + degradationPreference: DegradationPreference?,
  22 + rtcp: Rtcp?,
  23 + headerExtensions: MutableList<HeaderExtension>?,
  24 + encodings: MutableList<Encoding>?,
  25 + codecs: MutableList<Codec>?,
  26 +) : RtpParameters(transactionId, degradationPreference, rtcp, headerExtensions, encodings, codecs) {
  27 + class MockRtcp(cname: String?, reducedSize: Boolean) : Rtcp(cname, reducedSize)
  28 + class MockHeaderExtension(uri: String?, id: Int, encrypted: Boolean) : HeaderExtension(uri, id, encrypted)
  29 + class MockCodec(payloadType: Int, name: String?, kind: MediaStreamTrack.MediaType?, clockRate: Int?, numChannels: Int?, parameters: MutableMap<String, String>?) :
  30 + Codec(payloadType, name, kind, clockRate, numChannels, parameters)
  31 +}
@@ -40,6 +40,7 @@ import livekit.LivekitModels @@ -40,6 +40,7 @@ import livekit.LivekitModels
40 import livekit.LivekitRtc 40 import livekit.LivekitRtc
41 import livekit.LivekitRtc.SubscribedCodec 41 import livekit.LivekitRtc.SubscribedCodec
42 import livekit.LivekitRtc.SubscribedQuality 42 import livekit.LivekitRtc.SubscribedQuality
  43 +import livekit.org.webrtc.RtpParameters
43 import livekit.org.webrtc.VideoSource 44 import livekit.org.webrtc.VideoSource
44 import org.junit.Assert.* 45 import org.junit.Assert.*
45 import org.junit.Test 46 import org.junit.Test
@@ -333,4 +334,18 @@ class LocalParticipantMockE2ETest : MockE2ETest() { @@ -333,4 +334,18 @@ class LocalParticipantMockE2ETest : MockE2ETest() {
333 }, 334 },
334 ) 335 )
335 } 336 }
  337 +
  338 + @Test
  339 + fun publishDegradationPreferences() = runTest {
  340 + val preference = RtpParameters.DegradationPreference.DISABLED
  341 + room.videoTrackPublishDefaults = room.videoTrackPublishDefaults.copy(degradationPreference = preference)
  342 + connect()
  343 +
  344 + room.localParticipant.publishVideoTrack(track = createLocalTrack())
  345 +
  346 + val peerConnection = getPublisherPeerConnection()
  347 + val transceiver = peerConnection.transceivers.first()
  348 +
  349 + assertEquals(preference, transceiver.sender.parameters.degradationPreference)
  350 + }
336 } 351 }