Repository navigation
MoonCast 1.0 so far → master (the whole chain #1 → #104 in one merge) - #105
Open
xservzeroman wants to merge 211 commits into
Open
xservzeroman wants to merge 211 commits into
xservzeroman wants to merge 211 commits into
Conversation
applicationId me.mooncast.tv, app name MoonCast TV, our own id (as the upstream author asks forks to do). Adds doc/MOONCAST-TV.md: the plan to use Moonlight's native MediaCodec + Sunshine protocol for low latency, with the MoonCast PC helper as the strict-NAT fallback. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The native build OOM-restarted the 4 GB container: 3 GB Gradle heap plus ndk-build compiling every ABI in parallel. Lower the Gradle heap to 1.5 GB, single worker, no parallel; build arm64-v8a only (the stick and JAL TV are arm64) with -j2 native jobs. BUILD SUCCESSFUL in ~1 min. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replace Moonlight's grey Material look with MoonCast's: a night-sky void background with the copper blood-moon eclipse glow (mc_bg), copper-eclipse accent/focus, moonlight-silver text, void status/nav bars. Game tiles get a copper focus ring (mc_tile_focus). Verified on the JAL TV. Next: Continue-centric home (the live game as the hero tile, one press to resume on this screen - the phone<->TV handoff), rounded box art, Unbounded/ Figtree fonts, restyled settings. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…eaming
The native TV app now opens to a simple 'Ready to play - waiting for your
phone' splash (like the first WebView MoonCast TV), not Moonlight's host/
game grid. The phone drives everything: it POSTs the game to :8099 /play
{appId,appName} and the receiver fires ShortcutTrampoline to launch the
stream with NATIVE Moonlight (hardware MediaCodec + Sunshine protocol,
low latency) - no remote navigation on the TV. GET /mc-native identifies
it + names the paired PC. MoonCastReceiver is the leanback launcher;
PcView stays for setup/pairing.
Verified on the JAL TV: splash shows, phone /play ES-DE -> native stream,
Sunshine 1440p, MSTAR hardware decode 60 fps, Super Mario Odyssey live.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ssion
The native receiver streams direct from Sunshine and owns the gamepad the game
reads. PhoneGamepad connects to the phone's pad server (ws://phone:8099/pad),
parses {pads:[{b,a}]} (W3C order) and feeds pad 0 into this stream's own
NvConnection via sendControllerInput — so no second Sunshine session is needed
for the controller (which gave the game a gamepad it ignored and a second audio
stream that broke the sound). Phone address comes from the /play request.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Graft chiaki-ng's com.metallic.chiaki.lib JNI layer + libchiaki-jni.so/liboboe.so (arm64) into the fork (AGP 9 built-in Kotlin, @parcelize stripped). New Ps5StreamActivity runs a chiaki Session to a SurfaceView; Ps5Pad relays the phone's pad -> ControllerState; MoonCastReceiver.playPs5 launches it when the phone POSTs /play {ps5host,registKey,morning}. Phone stays the controller. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Ps5StreamActivity now derives the wake credential from the registration key (ASCII up to the null pad, hex->ULong, as chiaki does), sends DiscoveryService .wakeup(null, host, cred, ps5), then connects with retries while the console powers up. No manual wake needed.
… keys Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…S5 sessions) Ps5Pad.start() now stops any previous relay first; stop() interrupts the thread (with backoff so it can't busy-loop). Ps5StreamActivity always disposes the chiaki session on quit/destroy and bails the connect loop when closed - so a failed/ended session no longer leaves a half-open session on the console that makes the PS5 refuse the next connect from this device. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… relay is authorized (no 403) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ead freeze / deadlock) Session.dispose() joins native threads; doing it in onDestroy on the UI thread froze the main looper and stopped the receiver from launching the next stream. Also stop disposing inside the QuitEvent callback (runs on chiaki's own thread - would deadlock). Teardown now runs on a dedicated thread. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…x pads lack them Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ress L2 during PS combo Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…et self-heals The pad relays (Ps5Pad for PS5, PhoneGamepad for Moonlight) opened ws://phone:8099/pad with no read timeout, so a socket the phone could no longer reach (WiFi power-save / NAT dropped it with no FIN/RST) left the reader blocked forever - the controller froze on its last state. Set soTimeout=25s (> the phones 8s heartbeat) so a dead link times out and run() reconnects; skip empty ping/pong frames; and reset the pad to neutral on ANY exit (try/finally) so no button/stick sticks while reconnecting.
Bouncing to Moonlight PcView when no PC was paired made the TV look like plain Moonlight AND tore down the receiver (killing the :8099 pad server so casts could not connect). The phone drives everything and PS5 needs no PC pairing, so stay on "waiting for your phone"; MENU still opens add-PC for the rare case of pairing a game PC on the TV.
… no port-forward) - native/mc_jni_patch.py: patches chiaki-ng android chiaki-jni.c (sessionCreate) - when ConnectInfo carries psnToken + duid it runs chiaki-ng's PSN steps (session_init, upnp_discover, session_create, create_offer, session_start(duid), punch_hole CTRL) and hands the hole-punch session + PSN account id to chiaki_session_init; the lib punches the data hole itself. Optional fields: LAN unchanged. libchiaki-jni.so (arm64-v8a + armeabi-v7a) rebuilt on xserv with it (jniLibs are not in git). - ConnectInfo: optional psnToken / duid / psnAccountId. - Ps5StreamActivity: LAN when a PlayStation answers discovery at the saved address (also in standby), otherwise Remote Play via PSN when the phone sent a token; PSN wakes the console. - MoonCastReceiver: /play accepts psnToken/duid/accountId, reads the whole body, unescapes "\/". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
First live remote attempt failed at get_websocket_fqdn: "SSL peer certificate or SSH remote key was not OK" - Android's libcurl has no CA store. native/mc_curl_ca_patch.py wraps curl_easy_init in holepunch.c to set CURLOPT_CAINFO from MC_CA_BUNDLE; Ps5StreamActivity writes AndroidCAStore roots to filesDir/cacert.pem and exports that path before a remote connect. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The PSN push websocket (pushcl) sends an incomplete chain; chiaki-ng repairs it via AIA, but both the repair trust store (aia_openssl.c) and the CAINFO_BLOB merge (holepunch.c) read CURLINFO_CAINFO, which is curl's built-in default (empty on Android), so the repair failed closed. Fall back to the app's MC_CA_BUNDLE there (native/mc_curl_ca_patch2.py). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…how status A console in rest mode only joins the PSN session once awake (~60 s); chiaki-ng waited 30 s for MEMBER_CREATED, so the first remote attempt always timed out and only the retry connected. native/mc_wake_timeout_patch.py waits up to 90 s for that one notification. The stream screen now shows "Waking your PlayStation over the internet..." until the first frame (and a reason if it fails) instead of a black screen. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- No UPnP probe in mc_psn_connect: it only ever timed out (7 s per attempt) and the hole-punch works without it (chiaki-ng's own holepunch-test never calls it). - The stream screen follows the app's own logcat (Chiaki tag, no permission needed) and shows the real milestone: Reaching PlayStation Network -> Asking your PS5 to start Remote Play (wakes it) -> Your PS5 answered -> signing in -> opening the video channel -> Starting video; "Retrying (attempt N of 3)" when PSN drops an attempt. Stages only move forward within an attempt. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Over PSN the console sends the ctrl session id only after the data channel exists, so chiaki's SESSION_EXPECT_CTRL_START_MS (10 s) wait before the data punch always ran out (login arrived in 0.25 s, data offer exactly 10 s later). native/mc_ctrl_start_patch.py waits 2 s when a hole-punch session is used; LAN keeps 10 s. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s that fail before the picture - Revert c7d9aae (2 s ctrl-start wait): the console is not ready for the data-channel punch that early - it ignored 20 rounds of candidates, sent rudp finish and dropped the session. Back to chiaki's 10 s. - Ps5Pad: every controller update goes through push() under a lock that stop() clears, so nothing calls sessionSetControllerState on a session being disposed (native crash after a PS5 quit). - Remote attempts only succeed on the first picture; a quit before that (e.g. data hole-punch failed) is a failed attempt -> clean teardown + retry, instead of closing the stream screen. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…AT allocation Some JAL<->home hole-punches got no candidate response at all (20 rounds, then "Failed to find reachable candidate"), costing 30-50 s per retry. force_port_guessing makes a port-rewriting NAT (increment 0, local != STUN port) advertise 75 guessed ports and open 250 sockets; with a port-preserving NAT it does nothing. Also log the STUN allocation (increment / random). native/mc_port_guessing_patch.py (applies on top of mc_jni_patch.py). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Cast right after the PS5 entered rest mode: the start request arrived mid-transition and was ignored; the attempt waited its full 90 s for a reply that never came, while a fresh attempt woke it in 40 s. Waiting longer on one request never helps, a new request does: 60 s per attempt (a normal wake took ~46 s) and 4 attempts for remote. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…fails
The Android binding left packet_loss_max and enable_idr_on_fec_failure zeroed: chiaki always told the
PS5 0% loss ("measured=12.1% reported_max=0.0%"), so the console never lowered its bitrate on a lossy
link, and corrupt frames lingered until the next keyframe (111 corrupt-frame reports in one remote
session). native/mc_quality_patch.py sets chiaki-ng desktop's values: 0.05 and true.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Ps5Pad listens on UDP :8100 for {"seq","k","pads"} from the phone: accepted only from the phone IP with
our pad key and a newer seq (reset accepted after a phone restart / long gap). While UDP flows (<1 s
old) the WebSocket pad frames are ignored; if it stops, the WebSocket path takes over again. Avoids
TCP resend stalls on jittery Wi-Fi.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ndheld (Odin) - Ps5StreamActivity reads this device's own gamepad (buttons, sticks, analog triggers, d-pad / hat) and maps it to a DualSense state with the relay's combos (L2+Start = PS, Select+Start = touchpad); sent via Ps5Pad.pushLocal under the relay's lock (never into a disposing session). System Back (not the pad's B) leaves the stream. - Immersive full screen (no status/nav bars) when focused. - Exported, so the MoonCast app can open it on the same device. - Ps5Pad.start attaches the session even without a phone relay, so local input works on its own. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s busy - onStop finishes the player: on the Odin, switching back to MoonCast left the PS5 session streaming in the background, and the PS5 (one Remote Play session at a time) then refused the TV with 0x80108b10 "Remote is already in use". - QUIT_REASON_SESSION_REQUEST_RP_IN_USE before the picture: show "Your PS5 is already streaming to another device. Close it there, then try again." for 6 s (no pointless retries) instead of closing silently. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
One SRCH with a 1.5 s wait: on the home Mi stick (2.4 GHz, some loss) a single lost packet sent a home session over PSN (~40-80 s, "Holepunch session was deleted" at teardown) instead of the LAN (~9 s). Now up to 3 SRCH, 600 ms each, any reply = LAN. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
fix(pad): self-heal a dead controller socket + stay on the waiting screen (PS5 TV)
…ard) and Decode time in the Quick menu (test232) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…test233) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…illustration compare.html draws a made-up 720p frame, enlarges it 3.4x, and runs FSR 1.0's RCAS on the right half. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ening (test234) FsrRenderer: the decoder renders into a SurfaceTexture; copy, EASU and RCAS passes (ports of ffx_fsr1.h, MIT) draw to the screen surface with the decoder's presentation time. Only when the stream is clearly smaller than the screen; a fold moves only FSR's output. The mockup comparison is now rendered by these shaders in WebGL 2. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…t235) PC: MediaCodecDecoderRenderer.setSurfaceOverride, no setFixedSize while FSR is planned, off with HDR. Xbox: FsrGlDrawer as SurfaceViewRenderer's GlDrawer, hardware scaler off. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…atency keys stepwise (test236) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ed (test237) BatteryCap reads hh_battery_res and the plugged state; Game caps prefConfig keeping the shape and compares fold sizes with the uncapped size; Ps5StreamActivity takes the lower preset. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…PC (test238) Streaming status flips the stats corner on PC, PS5 and Xbox (new StatusCorner for PS5 and Xbox) and saves it as Stats while playing. PC only: Task Manager sends Ctrl+Shift+Esc; Hibernate PC asks first (Cancel picked), then sends Win+X, U, H, and the stream ending within a minute after that is not reported as an error. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…endent (test239) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…y (test240) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…video (test242) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…Off at every start (test241) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…n default in Settings, menu scrolls to the selection (test243) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…start, battery-cap Auto bitrate (test244) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… (test245) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… only), Obtainium links and Privacy in README (test246) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… PS5 and Xbox (test247) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rid (test249) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…apps starts on Done and says what each tap did (test251) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ings (test252) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…read picker, breath pause, a11y labels (test253) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…uick menu, All games scroll, TV re-fit and shared covers (test254) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…hint bar (test255) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…et Add apps tile, top pill ellipsis, group corners (test256) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Brings all of MoonCast so far into
masterin one merge, instead of merging the stacked chain one by one (#1 → #104). After this, the repo's front page, README and default branch are MoonCast instead of the old Moonlight.What's included
The whole main line of the chain, in order: #1, #4–#16, #23, #24, #30, #31, #33, #34, #36, #45–#55, #61–#64, #66, #67, #69–#73, #75–#91, #93, #94, #96, #97, #100–#104. That is: the rebrand, both apps (TV receiver, handheld sender), PS5 (chiaki-ng, PSN), Xbox Remote Play, the Feel engine, on-screen controllers, foldables, release signing and CI, OpenSSL 3, FSR, the Quick menu items, Touch screen, opt-in usage check-in, On this device, Change picture, Add to home screen, and the UI review rounds.
211 commits, 223 files.
masteris an ancestor offeat/home-shortcuts, so this merges with no conflicts.Not included (on purpose)
release/1.0-beta2, which only switches Hibernate PC off for the beta 2 build.After merging
I'll close the stacked PRs as merged-through-this-one (with a comment pointing here) and clean up the merged branches (issue #92).
🤖 Generated with Claude Code