This branch adds optional stream capture for offline analysis:
- Raw encoded video units (
video.<ext>) - Per-sample index records (
sample_index.jsonl) - Per-frame stats records (
frame_stats.jsonl, optional) - Session metadata (
session.json)
- Ensure Android NDK is configured in
local.properties(replace{HOME}):
ndk.dir={HOME}/Android/Sdk/ndk/27.0.12077973
sdk.dir={HOME}/Android/Sdk
- Fetch submodules:
git submodule update --init --recursive- Build (JDK 17+):
./gradlew :app:assembleNonRootDebug # or :app:assembleRootDebug- Install:
adb install app/build/outputs/apk/nonRoot/debug/app-nonRoot-debug.apk- Open Moonlight on the device.
- In Settings, enable
Capture received video. - Set
Capture size limit per session. - Optional: keep
Save per-frame statisticsenabled forframe_stats.jsonl. - Start a stream.
Capture output location:
/storage/emulated/0/Android/data/com.limelight.debug/files/captures/
Each stream creates a session directory named like:
yyyyMMdd-HHmmss-<codec>-<width>x<height>@<fps>
One metadata file per session.
Fields:
created_wall_timecodec(h264,hevc,av1, orunknown)width,height,fpsvideo_format_maskcapture_mode(raw_only)bitstream_filesample_index_filesession_dir
Raw decode units appended in receive order.
Extension mapping:
.h264for H.264.h265for HEVC.av1for AV1.binfallback
One JSON object per line. Every record includes:
eventseq(monotonic index sequence)uptime_ms(Android uptime timestamp)
Event payloads:
session_start:codec,width,height,fps,cap_bytes,bitstream_file,capture_modesample:frame_number,frame_type,pts_us,receive_time_ms,enqueue_time_ms,file_offset,sample_size,bitstream_filecsd:decode_unit_type,frame_number,pts_us,file_offset,sample_size,bitstream_filesession_end:reason,estimated_video_bytes,cap_reached
Written only when Save per-frame statistics is enabled. Every record includes:
eventuptime_ms
Event payloads:
session_start:codec,width,height,fps,cap_bytes,video_file,sample_index_filecapture_mode:video_format,reasonframe_received:frame_number,frame_type,decode_unit_length,host_processing_latency_0_1ms,receive_time_ms,enqueue_time_ms,pts_usframe_decoded:frame_number,frame_type,pts_us,decoder_latency_ms,queue_delay_ms(or fallback-1for frame metadata when unmatched)capture_stopped:reasonsession_end:reason,estimated_video_bytes,cap_reached
frame_receivedand index records are emitted when decode units arrive at the decoder input path (onDecodeUnit).frame_decodedis emitted when the frame is released fromMediaCodecoutput (onFrameDecoded), using PTS to match received metadata.- Raw bytes are written continuously until the session ends or the configured size cap is reached.
- Hitting the cap stops capture only (streaming continues), records
capture_stopped/session_end, and shows a toast on device.
adb pull /storage/emulated/0/Android/data/com.limelight.debug/files/captures/WIP: Add tooling to reconstruct frames (for example YUV) using sample_index.jsonl + video.<ext>.
Moonlight for Android is an open source client for NVIDIA GameStream and Sunshine.
Moonlight for Android will allow you to stream your full collection of games from your Windows PC to your Android device, whether in your own home or over the internet.
Moonlight also has a PC client and iOS/tvOS client.
You can follow development on our Discord server and help translate Moonlight into your language on Weblate.
- Install Android Studio and the Android NDK
- Run ‘git submodule update --init --recursive’ from within moonlight-android/
- In moonlight-android/, create a file called ‘local.properties’. Add an ‘ndk.dir=’ property to the local.properties file and set it equal to your NDK directory.
- Build the APK using Android Studio or gradle
Moonlight is the work of students at Case Western and was started as a project at MHacks.