Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,9 @@
- Build: chiaki's build script starts the native build clean when chiaki's build files change. The OpenSSL upgrade otherwise failed on a machine that had built before (old configured headers left in the cache).

## Unreleased
- **PS5 decode latency (Saleh: PXPlay decodes in under 1 ms on the Odin 2, MoonCast showed up to 5 ms):** the Odin's Snapdragon 8 Gen 2 has a separate low-latency HEVC/H.264 decoder (`c2.qti.hevc.decoder.low_latency`) next to the default one, and the PS5 player always got the default one, which holds frames back. With Decoder set to Low latency or Ultra low latency, MoonCast now uses the low-latency decoder when the device has one (Snapdragon `.low_latency`, MediaTek `.lowlatency`). Also: Qualcomm decoders run at full speed (operating-rate), and the thread that hands frames to the screen has display priority.
- **Measured the same way as other players now:** Decode time is from a frame being queued to the decoder until the decoder hands it back, taken before the frame goes to the screen (it included the hand-off before). Detailed stats add **Render wait**: from decoded to really on screen (the wait for the next refresh, plus Frame pacing's hold), on Android 13 and newer.
- Measured on the Odin 2 Portal (PS5-423 home screen, 1080p60 HEVC, mostly still, ~0.3 Mbps): decode Standard 3.9 ms, Low 3.7 ms (low-latency decoder), Ultra 2.8 ms; render wait 0.2 ms with Frame pacing Lowest delay, 7.4 ms Balanced, 12.3 ms Smoothest. The PS5's earlier "up to 5 ms" also counted handing the frame to the screen. Frame pacing is the bigger delay: keep Lowest delay for the quickest picture.
- **PS5 up to 100 Mbps (Saleh: PXPlay does 100, MoonCast stopped at 50):** Settings, PlayStation: Bitrate has 50, 80 and 100 too, and Custom goes from 2 to 100 Mbps (home and away), on the handheld and the TV. MoonCast's own 50 Mbps cap is gone; chiaki hands the number to the PS5 as it is. The row warns that 80–100 Mbps needs a strong 5 GHz / 6 GHz link (or cable). Auto is unchanged (30 or 20 Mbps by the link).
- CONTRIBUTING.md: how to report problems, build, and send changes (one topic per pull request, CHANGELOG in each, lint and CI, the chiaki-ng submodule, no game pictures or secrets, licences). SECURITY.md: report privately through GitHub's security advisories, and what MoonCast keeps where.
- README: rewritten for MoonCast (it was still Moonlight's). It covers what the two apps play and how, the features, install and Sunshine's gamepad setting, building with the chiaki-ng submodule, the licences and the credits.
Expand Down
6 changes: 4 additions & 2 deletions app/src/main/java/com/limelight/Ps5StreamActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -806,15 +806,17 @@ class Ps5StreamActivity : Activity(), SurfaceHolder.Callback, com.limelight.home
lastLossPct = 100.0 * (v[5] - lastLost) / (v[4] + v[5] - lastShown - lastLost)
lastShown = v[4]; lastLost = v[5]; lastAt = now
val decodeMs = if (v.size > 6) v[6] else 0.0
Log.d(TAG, "stats mbps=${v[0]} loss=${v[1]} rtt=${v[2]} rtt0=${v[3]} shown=${v[4]} lost=${v[5]} decode=${"%.2f".format(java.util.Locale.US, decodeMs)}ms")
val renderMs = if (v.size > 7) v[7] else 0.0 // decoded -> on screen (Android 13+); 0 = not known
Log.d(TAG, "stats mbps=${v[0]} loss=${v[1]} rtt=${v[2]} rtt0=${v[3]} shown=${v[4]} lost=${v[5]} decode=${"%.2f".format(java.util.Locale.US, decodeMs)}ms render=${"%.2f".format(java.util.Locale.US, renderMs)}ms")
val loss = maxOf(v[1] * 100.0, lastLossPct)
val rtt = if (v[3] > 1.0) v[3] else 0.0 // chiaki uses 1 ms when it couldn't measure
return listOf(
(if (fps < 0) "…" else "%.0f".format(java.util.Locale.US, minOf(fps, 60.0))) to "Frames per second",
(if (rtt > 0) "%.0f ms".format(java.util.Locale.US, rtt) else "…") to "Network delay",
(if (v[0] > 0) "%.1f Mbps".format(java.util.Locale.US, v[0]) else "…") to "Picture quality",
"%.1f%%".format(java.util.Locale.US, loss) to "Lost packets",
(if (decodeMs > 0) "%.1f ms".format(java.util.Locale.US, decodeMs) else "…") to "Decode time")
(if (decodeMs > 0) "%.1f ms".format(java.util.Locale.US, decodeMs) else "…") to "Decode time",
(if (renderMs > 0) "%.1f ms".format(java.util.Locale.US, renderMs) else "…") to "Render wait")
}

private fun openMenu() {
Expand Down
6 changes: 4 additions & 2 deletions app/src/main/java/com/limelight/home/StatusCorner.kt
Original file line number Diff line number Diff line change
Expand Up @@ -76,14 +76,16 @@ class StatusCorner(private val act: Activity, private val info: () -> String = {
"Network delay" -> StatsPill.Chip(v, "network delay")
"Picture quality" -> StatsPill.Chip(v, "bitrate")
"Lost packets" -> StatsPill.Chip(v, "packets lost on the network")
"Decode time" -> StatsPill.Chip(v, "decode")
"Decode time" -> StatsPill.Chip(v, "decode (queued to decoded)")
"Render wait" -> StatsPill.Chip(v, "render wait (decoded to on screen)")
else -> StatsPill.Chip(v, label.lowercase())
}
}
return StatsPill.detailed(info().ifEmpty { "Streaming" }, rows, StatsPill.health(rtt, loss))
}
val chips = known.map { (v, label) ->
val chips = known.mapNotNull { (v, label) ->
when (label) {
"Render wait" -> null // Lite keeps to one row; Detailed shows it
"Frames per second" -> StatsPill.Chip(v, "fps")
"Network delay" -> { rtt = StatsPill.num(v); StatsPill.Chip(v.removeSuffix(" ms"), "ms delay") }
"Lost packets" -> { loss = StatsPill.num(v); StatsPill.Chip(v, "lost") }
Expand Down