Support resolution switching (#597)

## Description
Adds a settings for automatic resolution switching. When enabled, the
app tries to output content at the video stream's resolution.

For example, if playing 1080p content on a 4K TV, the actual output by
the Android TV device will be 1080p.

This works in hand-in-hand with automatic refresh rate switching. So
enabling both allows for the output to match both refresh rate and
content resolution. Outputting the best refresh rate is preferred over
best resolution.

Also, shows the current display mode in the playback debug info.

### Related issues
Closes #428 
Fixes #531
This commit is contained in:
Ray 2025-12-31 17:11:30 -05:00 committed by GitHub
parent 38697b011d
commit 26a913b05e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 319 additions and 44 deletions

View file

@ -0,0 +1,124 @@
package com.github.damontecres.wholphin.test
import com.github.damontecres.wholphin.services.DisplayMode
import com.github.damontecres.wholphin.services.RefreshRateService
import org.junit.Assert
import org.junit.Test
class TestDisplayModeChoice {
companion object {
val HD_60 = DisplayMode(0, 1920, 1080, 60f)
val HD_30 = DisplayMode(1, 1920, 1080, 30f)
val HD_24 = DisplayMode(2, 1920, 1080, 24f)
val UHD_60 = DisplayMode(3, 3840, 2160, 60f)
val UHD_30 = DisplayMode(4, 3840, 2160, 30f)
val UHD_24 = DisplayMode(5, 3840, 2160, 24f)
val ALL_MODES = listOf(UHD_24, UHD_30, UHD_60, HD_24, HD_30, HD_60)
}
@Test
fun test1() {
val streamWidth = 1920
val streamHeight = 1080
val streamRealFrameRate = 60f
val result =
RefreshRateService.findDisplayMode(
displayModes = ALL_MODES,
streamWidth = streamWidth,
streamHeight = streamHeight,
targetFrameRate = streamRealFrameRate,
refreshRateSwitch = true,
resolutionSwitch = false,
)
Assert.assertEquals(3, result?.modeId)
}
@Test
fun test2() {
val streamWidth = 1920
val streamHeight = 1080
val streamRealFrameRate = 60f
val result =
RefreshRateService.findDisplayMode(
displayModes = ALL_MODES,
streamWidth = streamWidth,
streamHeight = streamHeight,
targetFrameRate = streamRealFrameRate,
refreshRateSwitch = true,
resolutionSwitch = true,
)
Assert.assertEquals(0, result?.modeId)
}
@Test
fun test3() {
val streamWidth = 1920
val streamHeight = 1080
val streamRealFrameRate = 30f
val result =
RefreshRateService.findDisplayMode(
displayModes = ALL_MODES,
streamWidth = streamWidth,
streamHeight = streamHeight,
targetFrameRate = streamRealFrameRate,
refreshRateSwitch = true,
resolutionSwitch = false,
)
Assert.assertEquals(4, result?.modeId)
}
@Test
fun test4() {
val streamWidth = 1920
val streamHeight = 804
val streamRealFrameRate = 30f
val result =
RefreshRateService.findDisplayMode(
displayModes = ALL_MODES,
streamWidth = streamWidth,
streamHeight = streamHeight,
targetFrameRate = streamRealFrameRate,
refreshRateSwitch = false,
resolutionSwitch = true,
)
Assert.assertEquals(1, result?.modeId)
}
@Test
fun testFraction() {
val streamWidth = 1920
val streamHeight = 1080
val streamRealFrameRate = 30f
val displayModes =
listOf(
DisplayMode(0, 1920, 1080, 59.940f),
DisplayMode(1, 1920, 1080, 60f),
// DisplayMode(2, 1920, 1080, 29.970f),
)
val result =
RefreshRateService.findDisplayMode(
displayModes = displayModes,
streamWidth = streamWidth,
streamHeight = streamHeight,
targetFrameRate = 29.970f,
refreshRateSwitch = true,
resolutionSwitch = false,
)
Assert.assertEquals(0, result?.modeId)
val result2 =
RefreshRateService.findDisplayMode(
displayModes = displayModes,
streamWidth = streamWidth,
streamHeight = streamHeight,
targetFrameRate = 24f,
refreshRateSwitch = true,
resolutionSwitch = false,
)
Assert.assertEquals(1, result2?.modeId)
}
}