feat(player): support playback speeds up to 8x

close #1545
This commit is contained in:
edde746
2026-07-26 04:24:55 +02:00
parent 2a25f21e27
commit 3b1e71b3fa
12 changed files with 194 additions and 16 deletions
@@ -111,6 +111,8 @@ class ExoPlayerCore(private val activity: Activity) : Player.Listener {
private const val WATCHDOG_TIMEOUT_MS = 8000L
private const val DECODER_HANG_TIMEOUT_MS = 5000L
private const val MAX_AUDIO_RECOVERY_ATTEMPTS = 2
private const val MIN_PLAYBACK_SPEED = 0.25f
private const val MAX_PLAYBACK_SPEED = 8f
private const val FPS_SAMPLE_COUNT = 8
private const val AUDIO_BOUNCE_TIMEOUT_MS = 1000L
@@ -3270,10 +3272,10 @@ class ExoPlayerCore(private val activity: Activity) : Player.Listener {
}
fun setPlaybackSpeed(speed: Float) {
val clampedSpeed = speed.coerceIn(0.25f, 4f)
val clampedSpeed = speed.coerceIn(MIN_PLAYBACK_SPEED, MAX_PLAYBACK_SPEED)
exoPlayer?.setPlaybackSpeed(clampedSpeed)
updateTunnelingState("speed changed")
delegate?.onPropertyChange("speed", speed.toDouble())
delegate?.onPropertyChange("speed", clampedSpeed.toDouble())
}
fun selectAudioTrack(trackId: String) {
@@ -0,0 +1,55 @@
package com.edde746.plezy.exoplayer
import android.app.Activity
import android.os.Looper
import android.widget.FrameLayout
import androidx.media3.exoplayer.ExoPlayer
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.Robolectric
import org.robolectric.RobolectricTestRunner
import org.robolectric.Shadows.shadowOf
import org.robolectric.annotation.Config
@RunWith(RobolectricTestRunner::class)
@Config(sdk = [28])
class ExoPlayerPlaybackSpeedTest {
@Test
fun eightTimesPlaybackIsAppliedAndOutOfRangeRequestsReportTheClamp() {
val activity = Robolectric.buildActivity(Activity::class.java).setup().get()
activity.setContentView(FrameLayout(activity))
val core = ExoPlayerCore(activity)
val delegate = RecordingDelegate()
core.delegate = delegate
try {
assertTrue(core.initialize())
val player = core.getExoPlayer()
core.setPlaybackSpeed(8f)
assertEquals(8f, player.playbackParameters.speed, 0f)
core.setPlaybackSpeed(9f)
assertEquals(8f, player.playbackParameters.speed, 0f)
assertEquals(listOf(8.0, 8.0), delegate.reportedSpeeds)
} finally {
core.dispose()
shadowOf(Looper.getMainLooper()).idle()
}
}
private fun ExoPlayerCore.getExoPlayer(): ExoPlayer = javaClass.getDeclaredField("exoPlayer").apply { isAccessible = true }.get(this) as ExoPlayer
private class RecordingDelegate : ExoPlayerDelegate {
val reportedSpeeds = mutableListOf<Double>()
override fun onPropertyChange(name: String, value: Any?) {
if (name == "speed") reportedSpeeds += value as Double
}
override fun onEvent(name: String, data: Map<String, Any>?) = Unit
}
}