Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
b92e60c
Harden Switch input, media recovery, persistence and launcher layout
zortos293 Oct 1, 2026
3d6ebc6
Recover native UI verification harness after workspace rollback
zortos293 Oct 2, 2026
8b9aaab
Merge branch 'main' of https://github.com/opencloudgaming/opennow-swi…
zortos293 Oct 2, 2026
448cf28
Recover native UI, bundled typography and captured stream configuration
zortos293 Oct 2, 2026
6219c06
Recover queue lifecycle and live stream overlay implementation
zortos293 Oct 2, 2026
3eeb445
Refresh retained shell tabs after interface language changes
zortos293 Oct 2, 2026
b072ba1
Restore configured server labels in the live stream overlay
zortos293 Oct 2, 2026
cc916b0
Verify real language-save refresh and per-image preview identity
zortos293 Oct 2, 2026
3689244
Fix clipped captions, focus borders and scrolling content
zortos293 Oct 2, 2026
e380f27
Verify rendered captions, glyph bounds and scroll viewport clearance
zortos293 Oct 2, 2026
7450cb6
Show number cards only during the active queue phase
zortos293 Oct 3, 2026
5cc353b
Retire the Zortos community proxy and use direct NVIDIA requests
zortos293 Oct 3, 2026
d83d54b
Advertise the bounded SCTP receive limit in SDP
zortos293 Oct 3, 2026
96b3f49
Keep fresh video alive across advisory network-status changes
zortos293 Oct 3, 2026
794d9ca
Count published video frames even when a later decode step fails
zortos293 Oct 3, 2026
aec68ff
Keep video progressing while audio output re-primes
zortos293 Oct 3, 2026
e26b66d
Preserve SCTP connect results across diagnostic logging
zortos293 Oct 3, 2026
47413b0
Send ICE consent checks after the stream connects
zortos293 Oct 14, 2026
f2612a7
Fix byte-swapped SCTP checksums in the Switch usrsctp build
zortos293 Oct 14, 2026
aea9aa4
Stop requesting server-side resolution and bandwidth adaptation
zortos293 Oct 14, 2026
db237b5
Start the stream at the requested bitrate
zortos293 Oct 14, 2026
f791bd7
Use the NVTEGRA hwaccel through the standard H.264 decoder
zortos293 Oct 14, 2026
4a4b4b3
Send transport-wide congestion control feedback
zortos293 Oct 14, 2026
fc0096c
Recommend overclocking for full-rate streaming
zortos293 Oct 14, 2026
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: 2 additions & 1 deletion .github/workflows/host-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ jobs:
run: |
sudo apt-get update -qq
sudo apt-get install -y --no-install-recommends \
g++ cmake ninja-build libsdl2-dev libgl1-mesa-dev libcurl4-openssl-dev xvfb xauth
g++ cmake ninja-build pkg-config python3 imagemagick libjansson-dev \
libsdl2-dev libgl1-mesa-dev libcurl4-openssl-dev xvfb xauth

- name: Build native UI layout test
run: |
Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,10 @@ Install and setup guidance lives in the
[OpenNOW documentation](https://opennow.zortos.me). Nintendo Switch installation
requires custom firmware and Homebrew Menu.

> [!IMPORTANT]
> Overclock the Switch for the best streaming performance. At stock clocks the
> stream hovers between 45 and 50 FPS instead of a steady 60 FPS.

> [!NOTE]
> The application currently retains the inherited `SwitchNOW.nro` filename and
> `sdmc:/switch/SwitchNOW/` data directory for compatibility. Renaming either
Expand Down
38 changes: 33 additions & 5 deletions app/src/StreamView.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,12 @@ StreamView::StreamView(
const std::vector<opennow::IceServerInfo>& ice_servers,
const opennow::GfnClient& client,
const opennow::AuthSession& auth,
const std::string& game_title)
const std::string& game_title,
const opennow::StreamSettings& stream_settings)
: client_(client)
, auth_(auth)
, cloud_session_id_(session_id)
, game_title_(game_title) {
const auto stream_settings = opennow::LoadStreamSettings();
debug_diagnostics_ = stream_settings.debug_diagnostics;
stats_overlay_enabled_ = stream_settings.stats_overlay_enabled;
stream_codec_ = stream_settings.codec;
Expand Down Expand Up @@ -66,7 +66,8 @@ StreamView::StreamView(
session_id,
media_ip,
media_port,
ice_servers);
ice_servers,
stream_settings);
session_->start();
network_monitor_.start();
RefreshNetworkInfo(stream_started_at_);
Expand All @@ -82,6 +83,7 @@ StreamView::StreamView(
}

StreamView::~StreamView() {
RecordLifecycleEvent("destroy");
network_monitor_.request_stop();
opennow::SetSensitiveInputLoggingSuppressed(false);
std::fill(nte_credentials_.password.begin(), nte_credentials_.password.end(), '\0');
Expand Down Expand Up @@ -123,6 +125,7 @@ void StreamView::onFocusLost() {
void StreamView::ExitStream() {
if (exit_requested_)
return;
RecordLifecycleEvent("exit");
exit_requested_ = true;
network_monitor_.request_stop();
if (session_)
Expand All @@ -139,6 +142,30 @@ void StreamView::ExitStream() {
}
}

void StreamView::RecordLifecycleEvent(const char* event) {
if (!debug_diagnostics_ || !session_)
return;

const auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now() - stream_started_at_).count();
const auto network = network_monitor_.snapshot();
const auto health = session_->get_transport_health();
const auto video = session_->get_video_performance();
session_->record_ui_event(
std::string("stream_lifecycle event=") + event +
" reason=" + std::to_string(static_cast<int>(stream_end_reason_)) +
" elapsedMs=" + std::to_string(elapsed) +
" internetConnected=" + std::to_string(network.internet_connected) +
" peerCompleted=" + std::to_string(health.peer_completed) +
" peerTerminal=" + std::to_string(static_cast<int>(health.peer_terminal)) +
" signalingConnected=" + std::to_string(health.signaling_connected) +
" videoStarted=" + std::to_string(health.video_started) +
" accessUnitIdleMs=" + std::to_string(health.video_idle.count()) +
" accessUnits=" + std::to_string(video.access_units) +
" decodedFrames=" + std::to_string(video.decoded_frames) +
" presentedFrames=" + std::to_string(video.presented_frames));
}

void StreamView::StopCloudSessionAsync() {
if (cloud_stop_requested_ || cloud_session_id_.empty())
return;
Expand Down Expand Up @@ -414,7 +441,8 @@ brls::View* StreamView::create(
const std::vector<opennow::IceServerInfo>& ice_servers,
const opennow::GfnClient& client,
const opennow::AuthSession& auth,
const std::string& game_title) {
const std::string& game_title,
const opennow::StreamSettings& settings) {
return new StreamView(signaling_url, jwt_token, session_id, media_ip, media_port,
ice_servers, client, auth, game_title);
ice_servers, client, auth, game_title, settings);
}
12 changes: 10 additions & 2 deletions app/src/StreamView.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
#include "nte_credentials.hpp"
#include "stream_end_policy.hpp"
#include "stream_overlay_policy.hpp"
#include "stream_overlay_view.hpp"
#include "stream_settings.hpp"
#include "webrtc_session.hpp"

#include <array>
Expand All @@ -33,7 +35,8 @@ class StreamView : public brls::Box {
const std::vector<opennow::IceServerInfo>& ice_servers,
const opennow::GfnClient& client,
const opennow::AuthSession& auth,
const std::string& game_title);
const std::string& game_title,
const opennow::StreamSettings& settings);
~StreamView() override;

void draw(NVGcontext* vg, float x, float y, float width, float height, brls::Style style, brls::FrameContext* ctx) override;
Expand All @@ -49,11 +52,13 @@ class StreamView : public brls::Box {
const std::vector<opennow::IceServerInfo>& ice_servers,
const opennow::GfnClient& client,
const opennow::AuthSession& auth,
const std::string& game_title);
const std::string& game_title,
const opennow::StreamSettings& settings);

private:
void ExitStream();
void StopCloudSessionAsync();
void RecordLifecycleEvent(const char* event);
void DrawDebugOverlay(NVGcontext* vg, float x, float y, float width);
void DrawPreparingStream(NVGcontext* vg, float x, float y, float width, float height);
void UpdatePerformanceCounter();
Expand Down Expand Up @@ -141,6 +146,9 @@ class StreamView : public brls::Box {
bool debug_diagnostics_ = false;
bool stats_overlay_enabled_ = false;
bool stream_overlay_visible_ = false;
std::unique_ptr<opennow::StreamOverlayView> stream_overlay_view_;
std::chrono::steady_clock::time_point stream_overlay_next_sample_ {};
std::int64_t stream_overlay_elapsed_seconds_ = -1;
bool stream_overlay_b_was_down_ = false;
bool overlay_chord_latched_ = false;
opennow::input::OverlayChordState overlay_chord_state_;
Expand Down
Loading
Loading