From df9d9d86735536d10a76ed80a8656052128cf3a6 Mon Sep 17 00:00:00 2001 From: bicarus-dev <202771338+bicarus-dev@users.noreply.github.com> Date: Sun, 4 May 2025 20:10:56 -0700 Subject: [PATCH] api: quality options for screen mirroring (#313) ## Link to GitHub Issue, if one exists n/a ## Description of change Allow the server to override client's quality options for screen mirroring. On Windows / Android versions (flutter-based) clients, this is already in the options of those apps. However, the iOS version lacks the quality options. ## Testing Tested with iOS and Windows clients. --- src/spice2x/api/modules/capture.cpp | 18 +++++++++++++++--- src/spice2x/api/modules/capture.h | 5 +++++ src/spice2x/launcher/launcher.cpp | 9 +++++++++ src/spice2x/launcher/options.cpp | 22 ++++++++++++++++++++++ src/spice2x/launcher/options.h | 2 ++ 5 files changed, 53 insertions(+), 3 deletions(-) diff --git a/src/spice2x/api/modules/capture.cpp b/src/spice2x/api/modules/capture.cpp index 9cab7e7..5bef6a3 100644 --- a/src/spice2x/api/modules/capture.cpp +++ b/src/spice2x/api/modules/capture.cpp @@ -9,6 +9,9 @@ using namespace rapidjson; namespace api::modules { + std::optional CAPTURE_QUALITY; + std::optional CAPTURE_DIVIDE; + static thread_local std::vector CAPTURE_BUFFER; Capture::Capture() : Module("capture") { @@ -44,12 +47,21 @@ namespace api::modules { int screen = 0; int quality = 70; int divide = 1; - if (req.params.Size() > 0 && req.params[0].IsUint()) + if (req.params.Size() > 0 && req.params[0].IsUint()) { screen = req.params[0].GetUint(); - if (req.params.Size() > 1 && req.params[1].IsUint()) + } + + if (CAPTURE_QUALITY.has_value()) { + quality = CAPTURE_QUALITY.value(); + } else if (req.params.Size() > 1 && req.params[1].IsUint()) { quality = req.params[1].GetUint(); - if (req.params.Size() > 2 && req.params[2].IsUint()) + } + + if (CAPTURE_DIVIDE.has_value()) { + divide = CAPTURE_DIVIDE.value(); + } else if (req.params.Size() > 2 && req.params[2].IsUint()) { divide = req.params[2].GetUint(); + } // receive JPEG data uint64_t timestamp = 0; diff --git a/src/spice2x/api/modules/capture.h b/src/spice2x/api/modules/capture.h index 8b02b02..fb8c55d 100644 --- a/src/spice2x/api/modules/capture.h +++ b/src/spice2x/api/modules/capture.h @@ -1,10 +1,15 @@ #pragma once +#include + #include "api/module.h" #include "api/request.h" namespace api::modules { + extern std::optional CAPTURE_QUALITY; + extern std::optional CAPTURE_DIVIDE; + class Capture : public Module { public: Capture(); diff --git a/src/spice2x/launcher/launcher.cpp b/src/spice2x/launcher/launcher.cpp index 82215f9..fc6210c 100644 --- a/src/spice2x/launcher/launcher.cpp +++ b/src/spice2x/launcher/launcher.cpp @@ -6,6 +6,7 @@ #include #include +#include "api/modules/capture.h" #include "acio/acio.h" #include "acio/icca/icca.h" #include "api/controller.h" @@ -813,6 +814,7 @@ int main_implementation(int argc, char *argv[]) { if (options[launcher::Options::AdapterSubnet].is_active()) { NETWORK_SUBNET = options[launcher::Options::AdapterSubnet].value_text(); } + if (options[launcher::Options::APITCPPort].is_active()) { api_enable = true; api_port = options[launcher::Options::APITCPPort].value_uint32(); @@ -835,6 +837,13 @@ int main_implementation(int argc, char *argv[]) { if (options[launcher::Options::APIDebugMode].value_bool()) { api_debug = true; } + if (options[launcher::Options::APIScreenMirrorQuality].is_active()) { + api::modules::CAPTURE_QUALITY = options[launcher::Options::APIScreenMirrorQuality].value_uint32(); + } + if (options[launcher::Options::APIScreenMirrorDivide].is_active()) { + api::modules::CAPTURE_DIVIDE = options[launcher::Options::APIScreenMirrorDivide].value_uint32(); + } + if (options[launcher::Options::DisableDebugHooks].value_bool()) { debughook::DEBUGHOOK_LOGGING = false; } diff --git a/src/spice2x/launcher/options.cpp b/src/spice2x/launcher/options.cpp index 5549666..76fe080 100644 --- a/src/spice2x/launcher/options.cpp +++ b/src/spice2x/launcher/options.cpp @@ -1194,6 +1194,28 @@ static const std::vector OPTION_DEFINITIONS = { .type = OptionType::Bool, .category = "API (Dev)", }, + { + // APIScreenMirrorQuality + .title = "API Screen Mirror Quality Override", + .name = "apiscreenq", + .desc = "JPEG compression level of mirrored images, overriding any client request. " + "Value must be between 0 (poor quality) and 100 (best quality), inclusive. Default: 70", + .type = OptionType::Integer, + .setting_name = "(0-100)", + .category = "SpiceCompanion and API", + }, + { + // APIScreenMirrorDivide + .title = "API Screen Mirror Divide Override", + .name = "apiscreendiv", + .desc = "Divide value of mirrored images, overriding any client request. " + "Divide of 1 means send every pixel, 2 means every other pixel, 3 means every third pixel, and so on; " + "increasing the value drastically reduces transfer size at the cost of image quality. " + "Value must be 1 or greater. Default: 1", + .type = OptionType::Integer, + .setting_name = "1", + .category = "SpiceCompanion and API", + }, { .title = "Enable All IO Modules", .name = "io", diff --git a/src/spice2x/launcher/options.h b/src/spice2x/launcher/options.h index 46d9ec9..94c484d 100644 --- a/src/spice2x/launcher/options.h +++ b/src/spice2x/launcher/options.h @@ -136,6 +136,8 @@ namespace launcher { APISerialBaud, APIPretty, APIDebugMode, + APIScreenMirrorQuality, + APIScreenMirrorDivide, EnableAllIOModules, EnableACIOModule, EnableICCAModule,