# Conflicts:
#	.github/workflows/ci-copr.yml
#	.github/workflows/ci-flatpak.yml
#	.github/workflows/ci-homebrew.yml
#	.github/workflows/ci-linux.yml
#	.github/workflows/ci-windows.yml
#	.github/workflows/ci.yml
#	.github/workflows/localize.yml
#	README.md
#	scripts/icons/convert_and_pack.sh
#	src/config.cpp
#	src/config.h
#	src/confighttp.cpp
#	src/logging.cpp
#	src/video.cpp
#	src_assets/common/assets/web/config.html
#	src_assets/common/assets/web/public/assets/locale/en.json
#	src_assets/common/assets/web/public/assets/locale/it.json
#	src_assets/windows/misc/gamepad/install-gamepad.bat
#	third-party/build-deps
#	third-party/moonlight-common-c
#	tools/CMakeLists.txt
This commit is contained in:
Yukino Song
2025-08-03 13:52:19 +08:00
43 changed files with 487 additions and 486 deletions

View File

@@ -34,7 +34,7 @@
#include "platform/windows/utils.h"
#endif
#ifndef __APPLE__
#if !defined(__ANDROID__) && !defined(__APPLE__)
// For NVENC legacy constants
#include <ffnvcodec/nvEncodeAPI.h>
#endif
@@ -511,6 +511,7 @@ namespace config {
}, // display_device
0, // max_bitrate
0, // minimum_fps_target (0 = framerate)
"1920x1080x60", // fallback_mode
false, // isolated Display
@@ -1085,9 +1086,12 @@ namespace config {
}
void apply_config(std::unordered_map<std::string, std::string> &&vars) {
#ifndef __ANDROID__
// TODO: Android can possibly support this
if (!fs::exists(stream.file_apps.c_str())) {
fs::copy_file(SUNSHINE_ASSETS_DIR "/apps.json", stream.file_apps);
}
#endif
for (auto &[name, val] : vars) {
#ifdef _WIN32
@@ -1121,7 +1125,7 @@ namespace config {
bool_f(vars, "nvenc_opengl_vulkan_on_dxgi", video.nv_opengl_vulkan_on_dxgi);
bool_f(vars, "nvenc_latency_over_power", video.nv_sunshine_high_power_mode);
#ifndef __APPLE__
#if !defined(__ANDROID__) && !defined(__APPLE__)
video.nv_legacy.preset = video.nv.quality_preset + 11;
video.nv_legacy.multipass = video.nv.two_pass == nvenc::nvenc_two_pass::quarter_resolution ? NV_ENC_TWO_PASS_QUARTER_RESOLUTION :
video.nv.two_pass == nvenc::nvenc_two_pass::full_resolution ? NV_ENC_TWO_PASS_FULL_RESOLUTION :
@@ -1198,6 +1202,8 @@ namespace config {
}
int_f(vars, "max_bitrate", video.max_bitrate);
double_between_f(vars, "minimum_fps_target", video.minimum_fps_target, {0.0, 1000.0});
string_f(vars, "fallback_mode", video.fallback_mode);
bool_f(vars, "isolated_virtual_display_option", video.isolated_virtual_display_option);
bool_f(vars, "ignore_encoder_probe_failure", video.ignore_encoder_probe_failure);

View File

@@ -144,6 +144,7 @@ namespace config {
} dd;
int max_bitrate; // Maximum bitrate, sets ceiling in kbps for bitrate requested from client
double minimum_fps_target; ///< Lowest framerate that will be used when streaming. Range 0-1000, 0 = half of client's requested framerate.
std::string fallback_mode;
bool isolated_virtual_display_option;

View File

@@ -16,11 +16,17 @@
#include <boost/log/expressions.hpp>
#include <boost/log/sinks.hpp>
#include <boost/log/sources/severity_logger.hpp>
#include <display_device/logging.h>
// local includes
#include "logging.h"
// conditional includes
#ifdef __ANDROID__
#include <android/log.h>
#else
#include <display_device/logging.h>
#endif
extern "C" {
#include <libavutil/log.h>
}
@@ -98,6 +104,48 @@ namespace logging {
os << "["sv << std::put_time(&lt, "%Y-%m-%d %H:%M:%S.") << boost::format("%03u") % ms.count() << "]: "sv
<< log_type << view.attribute_values()[message].extract<std::string>();
}
#ifdef __ANDROID__
namespace sinks = boost::log::sinks;
namespace expr = boost::log::expressions;
void android_log(const std::string &message, int severity) {
android_LogPriority android_priority;
switch (severity) {
case 0:
android_priority = ANDROID_LOG_VERBOSE;
break;
case 1:
android_priority = ANDROID_LOG_DEBUG;
break;
case 2:
android_priority = ANDROID_LOG_INFO;
break;
case 3:
android_priority = ANDROID_LOG_WARN;
break;
case 4:
android_priority = ANDROID_LOG_ERROR;
break;
case 5:
android_priority = ANDROID_LOG_FATAL;
break;
default:
android_priority = ANDROID_LOG_UNKNOWN;
break;
}
__android_log_print(android_priority, "Sunshine", "%s", message.c_str());
}
// custom sink backend for android
struct android_sink_backend: public sinks::basic_sink_backend<sinks::concurrent_feeding> {
void consume(const bl::record_view &rec) {
int log_sev = rec[severity].get();
const std::string log_msg = rec[expr::smessage].get();
// log to android
android_log(log_msg, log_sev);
}
};
#endif
[[nodiscard]] std::unique_ptr<deinit_t> init(int min_log_level, const std::string &log_file) {
if (sink) {
@@ -120,15 +168,18 @@ namespace logging {
}
}
#ifndef __ANDROID__
setup_av_logging(min_log_level);
setup_libdisplaydevice_logging(min_log_level);
#endif
sink = boost::make_shared<text_sink>();
#ifndef SUNSHINE_TESTS
boost::shared_ptr<std::ostream> stream {&std::cout, boost::null_deleter()};
sink->locked_backend()->add_stream(stream);
#endif
#endif
sink->locked_backend()->add_stream(boost::make_shared<std::ofstream>(log_file));
sink->set_filter(severity >= min_log_level);
sink->set_formatter(&formatter);
@@ -138,9 +189,15 @@ namespace logging {
sink->locked_backend()->auto_flush(true);
bl::core::get()->add_sink(sink);
#ifdef __ANDROID__
auto android_sink = boost::make_shared<sinks::synchronous_sink<android_sink_backend>>();
bl::core::get()->add_sink(android_sink);
#endif
return std::make_unique<deinit_t>();
}
#ifndef __ANDROID__
void setup_av_logging(int min_log_level) {
if (min_log_level >= 1) {
av_log_set_level(AV_LOG_QUIET);
@@ -198,6 +255,7 @@ namespace logging {
}
});
}
#endif
void log_flush() {
if (sink) {

View File

@@ -1922,10 +1922,12 @@ namespace video {
}
});
// set minimum frame time based on client-requested target framerate
auto minimum_frame_time = std::chrono::nanoseconds(1000ms) * 1000 / config.encodingFramerate;
// set minimum frame time based on client-requested target framerate// set max frame time based on client-requested target framerate.
double minimum_fps_target = (config::video.minimum_fps_target > 0.0) ? config::video.minimum_fps_target : config.encodingFramerate;
auto max_frametime = std::chrono::nanoseconds(1000ms) * 1000 / minimum_fps_target;
auto encode_frame_threshold = std::chrono::nanoseconds(1000ms) * 1000 / config.encodingFramerate;
auto frame_variation_threshold = encode_frame_threshold / 4;
BOOST_LOG(info) << "Minimum FPS target set to ~"sv << (minimum_fps_target / 2) << "fps ("sv << max_frametime.count() * 2 << "ns)"sv;
BOOST_LOG(info) << "Encoding Frame threshold: "sv << encode_frame_threshold;
auto shutdown_event = mail->event<bool>(mail::shutdown);
@@ -1997,7 +1999,7 @@ namespace video {
// Encode at a minimum FPS to avoid image quality issues with static content
if (!requested_idr_frame || images->peek()) {
if (auto img = images->pop(minimum_frame_time)) {
if (auto img = images->pop(max_frametime)) {
frame_timestamp = img->frame_timestamp;
// If new frame comes in way too fast, just drop
if (*frame_timestamp < (next_frame_start - frame_variation_threshold)) {