Table of contents
Artemis-Switch: A streamed frame from host to screen
Artemis-Switch is a fork in the Moonlight streaming family. The host runs the game. The client receives compressed video and audio, renders frames and sends controller input back.
- Languages
- 11Code breakdown
- Documented components
- 2Explore modules
- Source documents
- 6Read the instructions
Repository languagesC++ 53.78%11
Shares calculated from GitHub Linguist bytes.
| Language | % | Bytes |
|---|---|---|
| C++ | 2,312,036 | |
| C | 1,428,795 | |
| Java | 244,691 | |
| CMake | 150,826 | |
| Objective-C++ | 83,846 | |
| Shell | 32,086 |
5 more languages
| Swift | 0.38% | 16,275 |
| Python | 0.35% | 15,226 |
| GLSL | 0.34% | 14,440 |
| Makefile | 0.02% | 662 |
| PowerShell | 0.01% | 271 |
1. Overview
1.1. Why it exists
Hardware-accelerated GameStream/Moonlight client in C++ with custom NVN display frame pacing for zero-stutter video.
1.2. Best fit
Ultra-low latency PC game streaming directly to Nintendo Switch handheld mode.
2. Use cases
Moonlight-Switch fork that streams PC games to a Nintendo Switch, with FSR upscaling, stream profiles and telemetry.
- Pair and stream from a host
- Switch playerGameStream host PC
GameStreamClient.cpp and MoonlightSession.cpp handle PIN pairing and sessions with Sunshine, Apollo, Vibeshine or Punktfunk; DiscoverManager finds hosts and WakeOnLanManager can wake them.
- Tune video presentation
- Switch player
Fit, Fill, Stretch, Zoom/Pan, rotation and full range share one deko3d presentation path, with FSR/EASU upscaling, RCAS sharpening and dithering on top of NVDEC decode.
- Save stream profiles
- Switch playerGameStream host PC
Named profiles store FPS (30 to 120), resolution, bitrate, codec and presentation in profile.json; they can be assigned per host, edited and imported or exported (StreamProfileStore.cpp, ProfileEditorDialog.cpp).
- Export a benchmark
- Switch player
The Performance page shows bitrate, latency, frame queue, GPU time and Switch clocks; Benchmark start, stop and save write a file through app/src/benchmark/BenchmarkExport.cpp.
- Build and test the app
- Developer
With devkitA64 and CMake, the Moonlight.nro target builds the Switch binary, exported by CI as Artemis-Switch.nro; tests/ holds portable unit tests run with ctest, plus Python i18n and release checks.
2.1. Scenario: Stream a PC game to a Switch
- Copy Artemis-Switch.nro to sdmc:/switch/Artemis-Switch/ and start hbmenu through title redirection.
- Open Artemis Switch, select the PC host and finish PIN pairing in the host’s web UI.
- Create a stream profile (for example 60 FPS, H.265, Fit with FSR) and assign it to that host.
- Launch an app and press - and + together to open the in-stream overlay.
- Open Performance to watch bitrate and pipeline latency, then run and save a Benchmark.
Files readREADME.mdapp/src/streaming/GameStreamClient.cppapp/src/streaming/MoonlightSession.cppapp/src/streaming/StreamProfileStore.cppapp/src/benchmark/BenchmarkExport.cpptests/CMakeLists.txtCMakeLists.txt
3. Getting started
These commands come from the repository documentation. They were not run during this review.
git clone --recursive https://github.com/antoinebou12/Artemis-Switch.git
cd Artemis-Switch
cmake -B build/switch -DCMAKE_BUILD_TYPE=Release -DPLATFORM_SWITCH=ON -DUSE_DEKO3D=ON
cmake --build build/switch --target Moonlight.nro --parallelpython tests/i18n_consistency_test.py
python tests/release_contract_test.py
cmake -S tests -B build/tests -DCMAKE_BUILD_TYPE=Debug
cmake --build build/tests --parallel
ctest --test-dir build/tests --output-on-failurepython -m pip install ruff==0.12.10
python scripts/lint.py
cmake -S tests -B build/lint -DCMAKE_BUILD_TYPE=Debug -DARTEMIS_STRICT_WARNINGS=ON -DARTEMIS_ENABLE_CLANG_TIDY=ON
cmake --build build/lint --parallelcmake -S ci/integration -B build/integration -DCMAKE_BUILD_TYPE=Debug
cmake --build build/integration --parallel
ctest --test-dir build/integration --output-on-failurecd android-project./build_libromfs_generator.shexport JAVA_HOME=/Applications/Android\ Studio.app/Contents/jbr/Contents/Home
export ANDROID_SDK_ROOT=~/Library/Android/sdk
./gradlew assembleDebug
./gradlew installDebug4. Repository references
Excerpts from the repository’s guides, in their original language. Links open the inspected source revision.
Prerequisites and first run
4.1. Install the Artemis Switch NRO
docs/NRO_INSTALL.md
Use one of these files from dist/nro:
- Easy install: extract Artemis-Switch-SD.zip to the root of the Switch SD card.
- Manual install: copy Artemis-Switch.nro to sdmc:/switch/Artemis-Switch/Artemis-Switch.nro. Launch the Homebrew Menu with title redirection/full RAM mode, then select Artemis Switch. Applet mode is not supported for reliable streaming. The ZIP contains only the ready-to-copy switch/Artemis-Switch/Artemis-Switch.nro layout. Apollo and Sunshine both use the normal Moonlight/GameStream pairing flow.
4.2. Install
docs/source/install.md
4.3. Easy install
docs/source/install.md
4.4. Manual install
docs/source/install.md
Configuration and usage
4.5. Recommended host and clients
README.md
4.6. Client (device)
README.md
| Client | Device | Notes |
|---|---|---|
| Artemis Switch (this app) | Nintendo Switch | Moonlight-Switch fork with FSR/RCAS, stream profiles, and Switch presentation. |
| Artemide | Android | Highly recommended on Android: ultra-low latency (ULL), direct presentation, built-in FSR upscaling. FSR Performance / Balanced / Quality presets inspired this fork. |
| Artemis (Moonlight Noir) | Android | Apollo’s companion client (virtual display / host UX inspiration). |
| Moonlight | PC, Android, iOS, … | Official clients. |
| Moonlight V+ | Android | Alternative Android fork (frame-gen and extra host tools). |
Development and contributions
4.7. Developer build
README.md
Prerequisites: Git, CMake 3.20 or newer, and a standard devkitPro devkitA64 environment. Clone recursively so every dependency under extern/ is initialized: Repository layout
| Path | Purpose |
|---|---|
| app/src/ | Application, streaming, renderer, input, and feature implementation |
| app/include/ | UI and application declarations |
| resources/ | Borealis XML, images, shaders, and translations |
| tests/ | Portable unit and contract tests |
| ci/integration/ | Cross-feature integration test build |
| docs/ | GitHub Pages source and engineering notes |
| .github/workflows/ | Build, test, sanitizer, and release automation |
| extern/ | Pinned third-party dependencies and Git submodules |
| Keep Switch-specific behavior behind the existing platform and capability gates. | |
| Do not make project-specific changes inside vendored extern/ dependencies. |
4.8. Developer validation
README.md
Source documentation
README.mddocs/README.mddocs/NRO_INSTALL.mddocs/source/install.mdapp/platforms/android-project/README.mdapp/platforms/android-project/README_ZH.md
6 documents read from 6 candidates.
5. Features and components 2
- Session and interface
- The Borealis interface handles host selection and settings. MoonlightSession coordinates streaming, while the Switch decoder-and-render provider chooses concrete video and audio implementations. UI state and media processing have different lifecycles.
- Platform-specific output
- The provider constructs FFmpegVideoDecoder. With BOREALIS_USE_DEKO3D it selects ArtemisDKVideoRenderer, while other build flags select other renderers. Switch audio uses AudrenAudioRenderer. These are explicit build-time choices.
6. Architecture
Expand a component to follow its connections, or explore the full diagram.
GameStream host
Streaming session
FFmpeg video decoder
- From Streaming session
- To deko3d renderer
deko3d renderer
Switch display
- From deko3d renderer
Controller input
Mermaid Interactive diagram
System overview of the components and connections verified in the repository.

flowchart LR
n0["GameStream host"]
n1["Streaming session"]
n2["FFmpeg video decoder"]
n3["deko3d renderer"]
n4["Switch display"]
n5["Controller input"]
n0 --> n1
n1 --> n2
n2 --> n3
n3 --> n4
n5 --> n1
n1 --> n0
Text version of the diagram
- GameStream host → Streaming session
- Streaming session → FFmpeg video decoder
- FFmpeg video decoder → deko3d renderer
- deko3d renderer → Switch display
- Controller input → Streaming session
- Streaming session → GameStream host
Follow one path through the code
Read the provider methods to see which backend is actually compiled, then follow the session’s decoded frames into that renderer. Network, decode and presentation delays need separate measurements to explain a slow stream.
7. Technical dossier
Built from the source at commit 33e6854 (634 files). Each item comes from a pattern match in the code, so open the linked file before relying on it.
7.1. Layers and modules
Folders are sorted into layers by name, from the user interface down to data access. Because the sorting uses names only, the diagram says nothing about calls between layers.
Mermaid Layered view
flowchart TB
subgraph L_presentation["Presentation"]
direction TB
presentation_0["resources/xml<br/>19 files"]
presentation_1["features/ui<br/>9 files"]
presentation_2["platforms/android-project<br/>5 files"]
presentation_3["views<br/>2 files"]
end
subgraph L_application["Application services"]
direction TB
application_0["src<br/>51 files"]
application_1["remote_access/tailscale<br/>41 files"]
application_2["streaming<br/>37 files"]
application_3["include<br/>27 files"]
application_more["+28"]
end
subgraph L_infrastructure["Data and infrastructure"]
direction TB
infrastructure_0["switch/libavutil<br/>100 files"]
infrastructure_1["switch/libavcodec<br/>25 files"]
infrastructure_2["switch/curl<br/>12 files"]
infrastructure_3["remote_access/providers<br/>6 files"]
infrastructure_more["+2"]
end
subgraph L_core["Core source"]
direction TB
core_0["(root)<br/>1 file"]
core_1["docs/source<br/>1 file"]
end
subgraph L_tests["Tests"]
direction TB
tests_0["tests<br/>78 files"]
end
subgraph L_hosting["Build, CI and hosting"]
direction TB
hosting_0[".github<br/>15 files"]
hosting_1["(root)<br/>2 files"]
hosting_2["platforms<br/>2 files"]
hosting_3["ci<br/>1 file"]
hosting_more["+2"]
end
L_presentation ~~~ L_application
L_application ~~~ L_infrastructure
L_infrastructure ~~~ L_core
L_core ~~~ L_tests
L_core ~~~ L_hosting
| Module | Layer | Files | Types | Extensions |
|---|---|---|---|---|
switch/libavutil | Data and infrastructure | 100 | 195 | .h |
src | Application services | 51 | 39 | .cpp |
remote_access/tailscale | Application services | 41 | 62 | .cpp .hpp .h |
streaming | Application services | 37 | 32 | .hpp .cpp |
include | Application services | 27 | 30 | .hpp |
switch/libavcodec | Data and infrastructure | 25 | 30 | .h |
platforms/android-project | Application services | 24 | 35 | .java .xml .gradle |
streaming/video | Application services | 24 | 16 | .hpp .glsl .cpp |
features/input | Application services | 21 | 22 | .hpp .cpp |
resources/xml | Presentation | 19 | 0 | .xml |
remote_access | Application services | 17 | 20 | .hpp .cpp |
features/video | Application services | 13 | 8 | .hpp .cpp |
switch/curl | Data and infrastructure | 12 | 18 | .h |
benchmark | Application services | 11 | 13 | .hpp .cpp |
29 smaller modules are not shown.
7.2. C4 model
From system context to code declarations. Each level reflects the available source evidence.
UML 1 · System context
The widest C4 view: who uses the project and which outside systems and data stores it relies on. Rendered with uml-mcp.
PlantUML source
@startuml
!include <C4/C4_Context>
LAYOUT_LEFT_RIGHT()
Person(P_cli, "CLI user")
System(SYS, "Artemis-Switch", "Artemis-Switch is a fork in the Moonlight streaming family")
SystemDb(D0, "Active Record")
Rel(P_cli, SYS, "Uses")
Rel(SYS, D0, "Reads and writes")
@enduml
UML 2 · Containers
One level closer: the parts that run separately inside the project, their main language and how calls flow between them. Rendered with uml-mcp.
PlantUML source
@startuml
!include <C4/C4_Container>
LAYOUT_LEFT_RIGHT()
Person(P_cli, "CLI user")
System_Boundary(SYS, "Artemis-Switch") {
Container(C0, "artemis-switch-build", "Docker")
ContainerDb(DB0, "Active Record", "")
}
Rel(P_cli, C0, "Uses")
Rel(C0, DB0, "Reads and writes")
@enduml
UML 3 · Components and layers
Modules grouped by source-layer classification. Boundaries show organization, not runtime isolation or call relationships. Rendered with uml-mcp.
PlantUML source
@startuml
!include <C4/C4_Component>
LAYOUT_TOP_DOWN()
System_Boundary(SYS, "Artemis-Switch") {
Boundary(L0, "Presentation", "layer") {
Component(M0_0, "resources/xml", ".xml", "19 files")
Component(M0_1, "features/ui", "C++", "9 files")
Component(M0_2, "platforms/android-project", ".xml", "5 files")
Component(M0_3, "views", "C++", "2 files")
Component(M0_4, "docs/_static", ".css", "1 files")
}
Boundary(L1, "Application services", "layer") {
Component(M1_0, "src", "C++", "51 files")
Component(M1_1, "remote_access/tailscale", "C++, C/C++", "41 files")
Component(M1_2, "streaming", "C++", "37 files")
Component(M1_3, "include", "C++", "27 files")
Component(M1_4, "streaming/video", "C++, .glsl", "24 files")
Component(M1_5, "features/input", "C++", "21 files")
Component(M1_6, "platforms/android-project", "Java, .gradle, .xml", "19 files")
Component(M1_7, "remote_access", "C++", "17 files")
Component(M1_more, "+24 modules", "", "")
}
Boundary(L2, "Data and infrastructure", "layer") {
Component(M2_0, "switch/libavutil", "C/C++", "100 files")
Component(M2_1, "switch/libavcodec", "C/C++", "25 files")
Component(M2_2, "switch/curl", "C/C++", "12 files")
Component(M2_3, "remote_access/providers", "C++", "6 files")
Component(M2_4, "switch/libavformat", "C/C++", "4 files")
Component(M2_5, "switch/libswresample", "C/C++", "3 files")
}
Boundary(L3, "Core source", "layer") {
Component(M3_0, "(root)", ".sh", "1 files")
Component(M3_1, "docs/source", "Python", "1 files")
}
Boundary(L4, "Tests", "layer") {
Component(M4_0, "tests", "C++, Python", "78 files")
}
Boundary(L5, "Build, CI and hosting", "layer") {
Component(M5_0, ".github", ".yml, .md", "15 files")
Component(M5_1, "(root)", ".txt, .yml", "2 files")
Component(M5_2, "platforms", ".txt", "2 files")
Component(M5_3, "ci", ".txt", "1 files")
Component(M5_4, "docs", "Makefile", "1 files")
Component(M5_5, "extern", ".txt", "1 files")
}
}
@enduml
UML 4 · Code
Selected declarations, fields and methods grouped by source file. No inheritance or call edges are inferred. Rendered with uml-mcp.
PlantUML source
@startuml
skinparam backgroundColor transparent
skinparam shadowing false
skinparam roundCorner 10
skinparam defaultFontName Arial
skinparam defaultFontSize 13
skinparam ArrowColor #4e6070
skinparam ArrowFontColor #3d4f5f
skinparam titleFontSize 14
skinparam actorBorderColor #006a62
skinparam actorBackgroundColor #e9f3f4
skinparam usecaseBorderColor #006a62
skinparam usecaseBackgroundColor #e9f3f4
skinparam componentBorderColor #006a62
skinparam componentBackgroundColor #e9f3f4
skinparam interfaceBorderColor #006a62
skinparam interfaceBackgroundColor #ffffff
skinparam nodeBorderColor #4e6070
skinparam nodeBackgroundColor #f4f8fb
skinparam databaseBorderColor #006a62
skinparam databaseBackgroundColor #ffffff
skinparam cloudBorderColor #4e6070
skinparam cloudBackgroundColor #f0f5fa
skinparam artifactBorderColor #006a62
skinparam artifactBackgroundColor #ffffff
skinparam rectangleBorderColor #b0c2d3
skinparam activityBorderColor #006a62
skinparam activityBackgroundColor #e9f3f4
skinparam partitionBorderColor #b0c2d3
skinparam stereotypeCBackgroundColor #ffffff
top to bottom direction
hide empty members
package "Artemis-Switch" {
package "app/src/utils/Settings.hpp" as F0 {
class "Settings" as T0_0 {
working_dir()
set_working_dir()
launch_path()
set_launch_path()
key_dir()
boxart_dir()
}
}
package "app/platforms/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java" as F1 {
class "SDLActivity" as T1_0 {
getMotionListener()
main()
getMainSharedObject()
getMainFunction()
getLibraries()
loadLibraries()
}
}
package "app/src/remote_access/tailscale/TailscaleWgxRoute.cpp" as F2 {
class "RealWgxBackend" as T2_0 {
realEgressCallback()
sendEgress()
toSockaddr()
fromSockaddr()
discoverLanAddress()
startDirectPath()
}
}
package "app/src/streaming/MoonlightSession.hpp" as F3 {
class "MoonlightSession" as T3_0 {
set_provider()
start()
stop()
stopForApplicationExit()
set_address()
setApolloLaunchOptions()
}
}
package "app/src/streaming/AVFrameHolder.hpp" as F4 {
class "AVFrameQueue" as T4_0 {
push()
pushTransferred()
recycleWriteFrame()
configure()
capacityFor()
size()
}
class "AVFrameHolder" as T4_1 {
push()
pushTransferred()
get()
acquireWriteFrame()
recycleWriteFrame()
prepare()
}
}
package "app/src/streaming/FramePipelineTelemetry.hpp" as F5 {
class "FramePipelineTelemetry" as T5_0 {
reset()
noteStreamStart()
noteFirstPacket()
noteDecodedFrame()
consumePendingDecodeTiming()
noteFrameQueued()
}
}
package "app/src/remote_access/tailscale/TailscaleCore.hpp" as F6 {
class "TailscaleCore" as T6_0 {
start()
stop()
snapshot()
resolveRoute()
activateRoute()
prepareRouteForStreaming()
}
}
package "app/platforms/android-project/app/src/main/java/org/libsdl/app/SDLSurface.java" as F7 {
class "SDLSurface" as T7_0 {
handlePause()
handleResume()
getNativeSurface()
enableMediaCodecOverlay()
surfaceCreated()
surfaceDestroyed()
}
}
package "app/src/remote_access/tailscale/relay/TsLwipRelay.hpp" as F8 {
class "TsLwipRelay" as T8_0 {
start()
stop()
addTcp()
addUdp()
isRunning()
log()
}
}
package "lib/switch/include/libavcodec/codec.h" as F9 {
class "AVCodec" as T9_0 {
name
long_name
type
id
capabilities
max_lowres
supported_framerates
pix_fmts
}
}
package "lib/switch/include/libavcodec/avcodec.h" as F10 {
class "AVCodecContext" as T10_0 {
av_class
log_level_offset
codec_type
codec
codec_id
codec_tag
priv_data
internal
}
class "AVCodecParserContext" as T10_1 {
priv_data
parser
frame_offset
cur_offset
next_frame_offset
pict_type
repeat_pict
pts
}
}
package "lib/switch/include/libavcodec/codec_desc.h" as F11 {
class "AVCodecDescriptor" as T11_0 {
id
type
name
long_name
props
NULL
mime_types
profiles
}
}
package "lib/switch/include/libavutil/dovi_meta.h" as F12 {
class "AVDOVIColorMetadata" as T12_0 {
dm_metadata_id
scene_refresh_flag
ycc_to_rgb_matrix
ycc_to_rgb_offset
rgb_to_lms_matrix
signal_eotf
signal_eotf_param0
signal_eotf_param1
}
class "AVDOVIDataMapping" as T12_1 {
vdr_rpu_id
mapping_color_space
mapping_chroma_format_idc
curves
nlq_method_idc
num_x_partitions
num_y_partitions
nlq
}
class "AVDOVIDecoderConfigurationRecord" as T12_2 {
dv_version_major
dv_version_minor
dv_profile
dv_level
rpu_present_flag
el_present_flag
bl_present_flag
dv_bl_signal_compatibility_id
}
class "AVDOVIDmData" as T12_3 {
level
l1
l2
l3
l4
l5
l6
l8
}
class "AVDOVIDmLevel1" as T12_4 {
min_pq
max_pq
avg_pq
target_max_pq
trim_slope
trim_offset
trim_power
trim_chroma_weight
}
class "AVDOVIDmLevel10" as T12_5 {
target_display_index
target_max_pq
target_min_pq
target_primary_index
target_display_primaries
content_type
whitepoint
reference_mode_flag
}
class "AVDOVIDmLevel11" as T12_6 {
content_type
whitepoint
reference_mode_flag
sharpness
noise_reduction
mpeg_noise_reduction
frame_rate_conversion
brightness
}
class "AVDOVIDmLevel2" as T12_7 {
target_max_pq
trim_slope
trim_offset
trim_power
trim_chroma_weight
trim_saturation_gain
ms_weight
min_pq_offset
}
class "AVDOVIDmLevel254" as T12_8 {
dm_mode
dm_version_index
dm_run_mode
dm_run_version
dm_debug
level
l1
l2
}
class "AVDOVIDmLevel255" as T12_9 {
dm_run_mode
dm_run_version
dm_debug
level
l1
l2
l3
l4
}
}
}
@enduml
7.3. Public interface
Outside code can use everything listed here. The project also declares 637 public types.
Command line 2
Entry points
app/src/main.cppentry file
Public headers 175
app/include/about_tab.hpp app/include/active_remote_route.hpp app/include/add_host_tab.hpp app/include/app_cell.hpp app/include/app_list_view.hpp app/include/artemis_settings_tab.hpp app/include/button_selecting_dialog.hpp app/include/discovery_candidate.hpp app/include/favorite_tab.hpp app/include/forwarder_maker.hpp
7.4. Private interface
These names are internal by the language’s conventions: a leading underscore, private, static or pub(crate).
- anonymous namespace
- 98
- private / internal method
- 65
- static (file-local) function
- 35
onDestroyapp/platforms/android-project/app/src/main/java/com/borealis/demo/DemoActivity.java:8getLibrariesapp/platforms/android-project/app/src/main/java/com/borealis/demo/DemoActivity.java:20GattOperationapp/platforms/android-project/app/src/main/java/org/libsdl/app/HIDDeviceBLESteamController.java:82namespace { }app/src/add_host_tab.cpp:18namespace { }app/src/add_host_tab.cpp:252namespace { }app/src/add_host_tab.cpp:516_mbedtls_sha1app/src/crypto/MbedTLSCryptoManager.cpp:21_mbedtls_sha256app/src/crypto/MbedTLSCryptoManager.cpp:30_mbedtls_cert_signatureapp/src/crypto/MbedTLSCryptoManager.cpp:39
7.5. Services
Service 25
HIDDeviceManageracquire, release, getContext and 32 moreMoonlightInputManagerdropInput, resetForNewSession, stopAllRumble and 19 moreRemoteAccessManagerregisterProvider, providers, availableProviders and 18 moreSDLControllerManagernativeSetupJNI, nativeAddJoystick, nativeRemoveJoystick and 16 moreMbedTLSCryptoManagerload_cert_key_pair, generate_new_cert_key_pair, remove_cert_key_pair and 11 moreOpenSSLCryptoManagerload_cert_key_pair, generate_new_cert_key_pair, remove_cert_key_pair and 11 moreWireGuardManagerenable_from_settings, disable, default_config_path and 10 moreD3D11VideoRendererdraw, ensureContext, cleanup and 9 more
Store 13
StreamConfigProfileStoreget, create, rename and 20 moreWakeOnLanOverridesStoreget, set, clear and 3 moreDisplayTransformStoreget, set, setRotation and 3 moreZoomPanStoresetRemember, setState, reset and 3 moreApolloHostOptionsStoreget, set, reload and 2 moreInputSettingsStoresetPointer, setShortcuts, reload and 2 moreVideoScaleStoreget, set, reload and 2 moreSwitchMotionPolicyStoreset, reload, save and 1 more
Provider 7
NetBirdProviderid, name, available and 18 moreTailscaleProviderid, name, available and 16 moreIRemoteAccessProviderid, name, available and 15 moreWireGuardProviderid, name, available and 12 moreMoonlightShortcutsProviderMoonlightSessionDecoderAndRenderProviderSwitchMoonlightSessionDecoderAndRenderProvider
Handler 5
SDLJoystickHandlerpollInputDevices, getJoystick, handleMotionEvent and 7 moreSDLHapticHandlerrun, rumble, stop and 2 moreSDLClipboardHandlerclipboardHasText, clipboardGetText, clipboardSetText and 1 moreBorealisHandlerhandleMessageSDLCommandHandlerhandleMessage
Client 2
GameStreamClientserver_data, active_address, start and 18 moreNoiseClientbegin, complete, ready and 7 more
Controller 1
HIDDeviceBLESteamControllergetIdentifier, getGatt, connectGatt and 37 more
7.6. Entities and data model 436
Mermaid Entity class diagram
classDiagram
direction LR
class E0["AVCodec"] {
+char name
+char long_name
+enum type
+enum id
+int capabilities
+uint8_t max_lowres
}
class E1["AVCodecContext"] {
+AVClass av_class
+int log_level_offset
+enum codec_type
+struct codec
+enum codec_id
+unsigned codec_tag
}
class E2["AVCodecDescriptor"] {
+enum id
+enum type
+char name
+char long_name
+int props
+NULL
}
class E3["AVCodecParserContext"] {
+void priv_data
+struct parser
+int64_t frame_offset
+int64_t cur_offset
+int64_t next_frame_offset
+int pict_type
}
class E4["AVDOVIColorMetadata"] {
+uint8_t dm_metadata_id
+uint8_t scene_refresh_flag
+AVRational ycc_to_rgb_matrix
+AVRational ycc_to_rgb_offset
+AVRational rgb_to_lms_matrix
+uint16_t signal_eotf
}
class E5["AVDOVIDataMapping"] {
+uint8_t vdr_rpu_id
+uint8_t mapping_color_space
+uint8_t mapping_chroma_format_idc
+AVDOVIReshapingCurve curves
+enum nlq_method_idc
+uint32_t num_x_partitions
}
class E6["AVDOVIDecoderConfigurationRecord"] {
+uint8_t dv_version_major
+uint8_t dv_version_minor
+uint8_t dv_profile
+uint8_t dv_level
+uint8_t rpu_present_flag
+uint8_t el_present_flag
}
class E7["AVDOVIDmData"] {
+uint8_t level
+AVDOVIDmLevel1 l1
+AVDOVIDmLevel2 l2
+AVDOVIDmLevel3 l3
+AVDOVIDmLevel4 l4
+AVDOVIDmLevel5 l5
}
class E8["AVDOVIDmLevel1"] {
+uint16_t min_pq
+uint16_t max_pq
+uint16_t avg_pq
+uint16_t target_max_pq
+uint16_t trim_slope
+uint16_t trim_offset
}
class E9["AVDOVIDmLevel10"] {
+uint8_t target_display_index
+uint16_t target_max_pq
+uint16_t target_min_pq
+uint8_t target_primary_index
+AVColorPrimariesDesc target_display_primaries
+uint8_t content_type
}
E1 --> E0 : codec
E7 --> E8 : l1
An arrow links a field to the entity its type names.
| Entity | Kind | First fields |
|---|---|---|
AVCodec | Data struct | name, long_name, type, id, capabilities, max_lowres, supported_framerates, pix_fmts |
AVCodecContext | Data struct | av_class, log_level_offset, codec_type, codec, codec_id, codec_tag, priv_data, internal |
AVCodecDescriptor | Data struct | id, type, name, long_name, props, NULL, mime_types, profiles |
AVCodecParserContext | Data struct | priv_data, parser, frame_offset, cur_offset, next_frame_offset, pict_type, repeat_pict, pts |
AVDOVIColorMetadata | Data struct | dm_metadata_id, scene_refresh_flag, ycc_to_rgb_matrix, ycc_to_rgb_offset, rgb_to_lms_matrix, signal_eotf, signal_eotf_param0, signal_eotf_param1 |
AVDOVIDataMapping | Data struct | vdr_rpu_id, mapping_color_space, mapping_chroma_format_idc, curves, nlq_method_idc, num_x_partitions, num_y_partitions, nlq |
AVDOVIDecoderConfigurationRecord | Data struct | dv_version_major, dv_version_minor, dv_profile, dv_level, rpu_present_flag, el_present_flag, bl_present_flag, dv_bl_signal_compatibility_id |
AVDOVIDmData | Data struct | level, l1, l2, l3, l4, l5, l6, l8 |
Show 6 more entities
AVDOVIDmLevel1 | Data struct | min_pq, max_pq, avg_pq, target_max_pq, trim_slope, trim_offset, trim_power, trim_chroma_weight |
|---|---|---|
AVDOVIDmLevel10 | Data struct | target_display_index, target_max_pq, target_min_pq, target_primary_index, target_display_primaries, content_type, whitepoint, reference_mode_flag |
AVDOVIDmLevel11 | Data struct | content_type, whitepoint, reference_mode_flag, sharpness, noise_reduction, mpeg_noise_reduction, frame_rate_conversion, brightness |
AVDOVIDmLevel2 | Data struct | target_max_pq, trim_slope, trim_offset, trim_power, trim_chroma_weight, trim_saturation_gain, ms_weight, min_pq_offset |
AVDOVIDmLevel254 | Data struct | dm_mode, dm_version_index, dm_run_mode, dm_run_version, dm_debug, level, l1, l2 |
AVDOVIDmLevel255 | Data struct | dm_run_mode, dm_run_version, dm_debug, level, l1, l2, l3, l4 |
7.7. Business logic
Once UI code and the services and entities above are set aside, these classes have the most methods.
SettingsApplication services, 157 methodsworking_dir, set_working_dir, launch_path, set_launch_path, key_dir, boxart_dirSDLActivityApplication services, 116 methodsgetMotionListener, main, getMainSharedObject, getMainFunction, getLibraries, loadLibrariesRealWgxBackendApplication services, 37 methodsrealEgressCallback, sendEgress, toSockaddr, fromSockaddr, discoverLanAddress, startDirectPathMoonlightSessionApplication services, 35 methodsset_provider, start, stop, stopForApplicationExit, set_address, setApolloLaunchOptionsAVFrameQueueApplication services, 28 methodspush, pushTransferred, recycleWriteFrame, configure, capacityFor, sizeAVFrameHolderApplication services, 23 methodspush, pushTransferred, get, acquireWriteFrame, recycleWriteFrame, prepareFramePipelineTelemetryApplication services, 16 methodsreset, noteStreamStart, noteFirstPacket, noteDecodedFrame, consumePendingDecodeTiming, noteFrameQueuedTailscaleCoreApplication services, 21 methodsstart, stop, snapshot, resolveRoute, activateRoute, prepareRouteForStreamingSDLSurfaceApplication services, 20 methodshandlePause, handleResume, getNativeSurface, enableMediaCodecOverlay, surfaceCreated, surfaceDestroyedTsLwipRelayApplication services, 20 methodsstart, stop, addTcp, addUdp, isRunning, log
7.8. Database and persistence
7.9. External integrations
HTTP clients
Hosts named in the code
api.netbird.io2 filesapp/src/artemis_settings_tab.cpp:616aomediacodec.github.io1 fileslib/switch/include/libavutil/iamf.h:27gbatemp.net1 filesapp/src/about_tab.cpp:80www.patreon.com1 filesapp/src/about_tab.cpp:69
7.10. Stack and dependencies
JVM (Gradle) app/platforms/android-project/app/build.gradle
compileSdk Version minSdk Version targetSdk Version
External dependencies
8 declared packages (8 runtime, 0 development) in CMakeLists.txt, app/platforms/android-project/app/build.gradle.
Package table 8
| Package | Version | Scope | Purpose | Registry |
|---|---|---|---|---|
PkgConfig | — | runtime | — | C/C++ (CMake) |
MbedTLS | — | runtime | — | C/C++ (CMake) |
OpenSSL | — | runtime | — | C/C++ (CMake) |
SDL2 | — | runtime | Graphics, imaging and 3D | C/C++ (CMake) |
CURL | — | runtime | — | C/C++ (CMake) |
Jansson | — | runtime | — | C/C++ (CMake) |
EXPAT | — | runtime | — | C/C++ (CMake) |
PNG | — | runtime | — | C/C++ (CMake) |
7.11. CI/CD pipeline
14 GitHub Actions workflows, 27 jobs. Each box is a job; double-bordered boxes publish or deploy, and arrows follow needs.
Mermaid Pipeline flow
flowchart LR
subgraph wf0["All Builds"]
direction LR
wf0_switch["Nintendo Switch<br/>reuses docker-image.yml"]
wf0_psvita["PS Vita<br/>reuses psvita.yml"]
wf0_android["Android<br/>reuses android-apk.yml"]
wf0_macos["macOS<br/>reuses macos.yml"]
wf0_windows["Windows<br/>reuses windows.yml"]
wf0_linux["Linux<br/>reuses linux.yml"]
end
subgraph wf1["Android APK"]
direction LR
wf1_build["build<br/>ubuntu-latest"]
end
subgraph wf2["Build Artemis Switch NRO into dist (one-sho…"]
direction LR
wf2_build_and_commit[["build-and-commit<br/>ubuntu-latest"]]
end
subgraph wf3["Build Artemis Switch"]
direction LR
wf3_build[["build<br/>ubuntu-latest"]]
end
subgraph wf4["Docs"]
direction LR
wf4_deploy[["deploy<br/>ubuntu-latest"]]
end
subgraph wf5["Feature and Integration CI"]
direction LR
wf5_portable_integration[["Consolidated portable integration<br/>ubuntu-latest"]]
wf5_release_package_contract["Release source package contract<br/>ubuntu-latest"]
wf5_sanitizer_integration["ASan + UBSan integration<br/>ubuntu-latest"]
wf5_ci_summary["Artemis CI summary<br/>ubuntu-latest"]
end
t0(["This · workflow · fans · out · to · six"])
t1(["called by a workflow"])
t2(["push"])
t3(["manual run · called by a workflow"])
t4(["push · manual run"])
t5(["Topic · branches · run · through · pull req…"])
d0[("GitHub Pages")]
t0 --> wf0_switch
t0 --> wf0_psvita
t0 --> wf0_android
t0 --> wf0_macos
t0 --> wf0_windows
t0 --> wf0_linux
t1 --> wf1_build
t2 --> wf2_build_and_commit
t3 --> wf3_build
t4 --> wf4_deploy
wf4_deploy -.-> d0
t5 --> wf5_portable_integration
t5 --> wf5_release_package_contract
t5 --> wf5_sanitizer_integration
wf5_portable_integration --> wf5_ci_summary
wf5_release_package_contract --> wf5_ci_summary
wf5_sanitizer_integration --> wf5_ci_summary
| Workflow | Triggers | Jobs | Publishes to |
|---|---|---|---|
All Builds.github/workflows/all-builds.yml | This, workflow, fans, out, to, six |
| — |
Android APK.github/workflows/android-apk.yml | called by a workflow |
| — |
Build Artemis Switch NRO into dist (one-shot).github/workflows/build-dist-once.yml | push |
| — |
Build Artemis Switch.github/workflows/docker-image.yml | manual run, called by a workflow |
| — |
Docs.github/workflows/docs.yml | push, manual run |
| GitHub Pages |
Feature and Integration CI.github/workflows/feature-integration-ci.yml | Topic, branches, run, through, pull request, Keeping |
| — |
Lint.github/workflows/lint.yml | Topic, branches, are, linted, through, pull request |
| — |
Linux Builds.github/workflows/linux.yml | called by a workflow, manual run |
| — |
6 more workflows are not shown.
7.12. Deployment
Mermaid Deployment diagram
flowchart LR
repo[("GitHub repository")]
ci[["GitHub Actions<br/>14 workflows"]]
pub_GitHub_Pages[("GitHub Pages")]
pub_GitHub_Releases[("GitHub Releases")]
subgraph compose["Docker Compose"]
svc_artemis_switch_build["artemis-switch-build"]
end
repo -->|"push or pull request"| ci
ci -->|"publishes"| pub_GitHub_Pages
ci -->|"publishes"| pub_GitHub_Releases
repo -->|"docker compose up"| compose
UML Deployment
The outer node is where the code runs. Each base image points to what it builds, solid arrows lead to databases and dashed ones to outside services. Rendered with uml-mcp.
PlantUML source
@startuml
skinparam backgroundColor transparent
skinparam shadowing false
skinparam roundCorner 10
skinparam defaultFontName Arial
skinparam defaultFontSize 13
skinparam ArrowColor #4e6070
skinparam ArrowFontColor #3d4f5f
skinparam titleFontSize 14
skinparam actorBorderColor #006a62
skinparam actorBackgroundColor #e9f3f4
skinparam usecaseBorderColor #006a62
skinparam usecaseBackgroundColor #e9f3f4
skinparam componentBorderColor #006a62
skinparam componentBackgroundColor #e9f3f4
skinparam interfaceBorderColor #006a62
skinparam interfaceBackgroundColor #ffffff
skinparam nodeBorderColor #4e6070
skinparam nodeBackgroundColor #f4f8fb
skinparam databaseBorderColor #006a62
skinparam databaseBackgroundColor #ffffff
skinparam cloudBorderColor #4e6070
skinparam cloudBackgroundColor #f0f5fa
skinparam artifactBorderColor #006a62
skinparam artifactBackgroundColor #ffffff
skinparam rectangleBorderColor #b0c2d3
skinparam activityBorderColor #006a62
skinparam activityBackgroundColor #e9f3f4
skinparam partitionBorderColor #b0c2d3
skinparam stereotypeCBackgroundColor #ffffff
node "Docker Compose" as H {
node "artemis-switch-build" as N0 <<container>>
}
database "Active Record" as DB0
N0 --> DB0
cloud "Console network stack (libnx/…" as E0
N0 ..> E0
cloud "WebSocket" as E1
N0 ..> E1
@enduml
Where it runs
Published to
Compose services
artemis-switch-build
7.13. Monitoring
What the code logs, measures and exposes to tell whether it is working.
7.14. Security
Controls
Supply chain
Environment variables 3
The page lists names without their values and underlines the ones that look like secrets.
7.15. Testing
- Test files
- 78
- Source files
- 567
- Unit tests
- 75
- Benchmark tests
- 2
- Integration tests
- 1
Code coverage
No measured coverage report in the analyzed sources.
Where the tests are
tests 78
Continuous integration
- Lint
ruff- Unit tests
ctest
UML CI pipeline
The main GitHub Actions workflow runs these steps. Jobs that run in parallel sit side by side. Rendered with uml-mcp.
PlantUML source
@startuml
skinparam backgroundColor transparent
skinparam shadowing false
skinparam roundCorner 10
skinparam defaultFontName Arial
skinparam defaultFontSize 13
skinparam ArrowColor #4e6070
skinparam ArrowFontColor #3d4f5f
skinparam titleFontSize 14
skinparam actorBorderColor #006a62
skinparam actorBackgroundColor #e9f3f4
skinparam usecaseBorderColor #006a62
skinparam usecaseBackgroundColor #e9f3f4
skinparam componentBorderColor #006a62
skinparam componentBackgroundColor #e9f3f4
skinparam interfaceBorderColor #006a62
skinparam interfaceBackgroundColor #ffffff
skinparam nodeBorderColor #4e6070
skinparam nodeBackgroundColor #f4f8fb
skinparam databaseBorderColor #006a62
skinparam databaseBackgroundColor #ffffff
skinparam cloudBorderColor #4e6070
skinparam cloudBackgroundColor #f0f5fa
skinparam artifactBorderColor #006a62
skinparam artifactBackgroundColor #ffffff
skinparam rectangleBorderColor #b0c2d3
skinparam activityBorderColor #006a62
skinparam activityBackgroundColor #e9f3f4
skinparam partitionBorderColor #b0c2d3
skinparam stereotypeCBackgroundColor #ffffff
title Feature and Integration CI
start
fork
partition "Consolidated portable integra…" {
:Checkout;
:Validate localization JSON syntax;
:Validate Artemis English/French translation…;
:Validate release/CD contract;
:Validate isolated VPN build contract;
:Configure unit suite;
:Build unit suite;
:Run unit suite;
}
fork again
partition "Release source package contra…" {
:Checkout repository and submodules;
:Build dry-run source packages;
:Inspect source packages;
}
fork again
partition "ASan + UBSan integration" {
:Checkout;
:Configure;
:Build;
:Test;
}
fork again
partition "Artemis CI summary" {
:Require all integration jobs;
}
end fork
stop
@enduml
8. Types and file structure
651 declared types, with 12 in the base-relationship overview.
Type diagram
classDiagram
direction TB
class t27b200e5e641["AboutTab"] {
}
class t1d8fa37de9f9["ActiveRemoteRoute"] {
<<struct>>
}
class t76b3bc05a24d["AddHostTab"] {
findHost()
stopSearchHost()
connectHost()
fillSearchBox()
}
class t71f8fd94394b["AppCell"] {
setFavorite()
updateFavoriteAction()
}
class tcb6c23872ee9["AppListView"] {
onLayout()
willAppear()
draw()
blockInput()
}
class t5416fcf29d4d["ArtemisSettingsTab"] {
refreshValues()
editWidth()
editHeight()
refreshRemoteAccessRows()
}
class taa0a005beea4["ButtonSelectingDialog"] {
open()
reloadLabel()
buttonsText()
draw()
}
class t0cab5ff3d09b["DiscoveryCandidate"] {
<<struct>>
isDirect()
}
class t9649c53d1d2d["FavoriteTab"] {
refreshIfNeeded()
setRefreshNeeded()
updateAppList()
}
class tae773a498879["ClickGestureRecognizer"] {
recognitionLoop()
}
class t03d6e2ef334c["FingersGestureRecognizer"] {
recognitionLoop()
}
class t96b64c4eb4d8["TwoFingerScrollState"] {
<<struct>>
}
class b66d2434ea20a["brls::Box"] {
<<unresolved_base>>
}
b66d2434ea20a <|-- t27b200e5e641
class b12e371ab4244["brls::Box"] {
<<unresolved_base>>
}
b12e371ab4244 <|-- t76b3bc05a24d
class b9fb6426c89d6["Box"] {
<<unresolved_base>>
}
b9fb6426c89d6 <|-- t71f8fd94394b
class bd9fae1c39e24["Box"] {
<<unresolved_base>>
}
bd9fae1c39e24 <|-- tcb6c23872ee9
class bc8de2cffa73c["brls::Box"] {
<<unresolved_base>>
}
bc8de2cffa73c <|-- t5416fcf29d4d
class b4629afd0f9ec["Dialog"] {
<<unresolved_base>>
}
b4629afd0f9ec <|-- taa0a005beea4
class b3def5b4c744e["brls::Box"] {
<<unresolved_base>>
}
b3def5b4c744e <|-- t9649c53d1d2d
class b1c2ec6b5bcbd["brls::GestureRecognizer"] {
<<unresolved_base>>
}
b1c2ec6b5bcbd <|-- tae773a498879
class b905c043cc5ee["brls::GestureRecognizer"] {
<<unresolved_base>>
}
b905c043cc5ee <|-- t03d6e2ef334c
Unresolved bases remain labelled; full signatures are not reconstructed.
9. Reference
616 / 616 eligible files read (962 in the tree); 0 oversized, 0 failed reads and 88 syntax diagnostics.
What the scan covers
Known dependency/build folders, credential files and binaries are excluded. The scan covers READMEs, recognized manifests and parser-supported source files.
Linguist statistics cover the whole repository and may include other languages.
11. Files to read 3
12. Glossary
Repository
The versioned source files, configuration, and history of a project.
README
The repository’s entry document, usually covering its purpose and initial setup.
Dependency
A library, tool, or service that another component needs to build or run.
Session
State kept across related requests, often to track a signed-in user. Authentication and permission checks remain separate concerns.
Published port
A host port mapped to a service’s container port. Publishing a port can expose that service beyond its container network.
CLI
A text interface in which commands and arguments select operations. Exit codes let scripts detect success or failure.
Build
The steps that turn source files and dependencies into a runnable program or distributable artifact.
GPU
A processor designed for parallel workloads, including graphics rendering and many numerical operations.
Shader
A program executed at a stage of a graphics or compute pipeline, for example to transform vertices or calculate pixel colors.
Video decoder
A component that reconstructs video frames from a compressed stream. Decoding and displaying a frame are separate stages.
Backend
An implementation behind a shared interface. Depending on context, it can mean a server-side service or a graphics, input, or storage implementation.
Firmware
Software running on a device or microcontroller to control its hardware and communication.
Fork
A separate repository derived from another project. A change in a fork is not automatically a change in the upstream project.
13. References
- antoinebou12. (2026, November 5). Artemis-Switch. [Software repository] github.com.
- antoinebou12. (2026, November 5). Artemis-Switch: README. [Repository documentation] github.com.
- antoinebou12. (2026, November 5). app/src/main.cpp. [Source file] github.com.
- antoinebou12. (2026, November 5). app/src/streaming/MoonlightSession.cpp. [Source file] github.com.
- antoinebou12. (2026, November 5). app/src/streaming/switch/SwitchMoonlightSessionDecoderAndRenderProvider.cpp. [Source file] github.com.
- Boucher, A. (2026, October 20). Artemis Switch: following a frame from the network to the screen. [Project article] antoineboucher.info.
- Boucher, A. (2026, September 2). Handheld streaming and homebrew — Artemis-Switch, moonlight-N3DS, sm64coopdx-switch. [Project article] antoineboucher.info.
- Boucher, A. (2026, September 2). Artemis-Switch. [Project article] antoineboucher.info.
- Boucher, A. (2026, October 23). Artemis-Switch: A streamed frame from host to screen. [Project article] antoineboucher.info.
Sources without a publication date show the date they were last checked, November 5, 2026. File links retain the inspected revision where available.