From 03236a50e555fefd0417a35ff6f13159871b79ea Mon Sep 17 00:00:00 2001 From: Elia Zammuto Date: Sun, 16 May 2021 20:06:06 +0200 Subject: [PATCH] UI for Application Config --- assets/web/config.html | 414 +++++++++++++++++++++++++++++++++++++++- assets/web/header.html | 5 +- sunshine/config.cpp | 1 - sunshine/config.h | 2 + sunshine/confighttp.cpp | 79 ++++++++ 5 files changed, 492 insertions(+), 9 deletions(-) diff --git a/assets/web/config.html b/assets/web/config.html index c63ec39d..eb15bfe2 100644 --- a/assets/web/config.html +++ b/assets/web/config.html @@ -1,4 +1,410 @@ -
-

Hello, Sunshine!

-

Placeholer for config page

-
\ No newline at end of file +
+

Configuration

+
+ + + +
+ +
+ + +
The name displayed by Moonlight. If not specified, the PC's hostname is used +
+
+ +
+ + +
The minimum log level printed to standard out
+
+ +
+ + +
The origin of the remote endpoint address that is not denied for HTTP method /pin +
+
+ +
+ + +
If no external IP address is given, the local IP address is used
+
+ +
+ + +
How long to wait in milliseconds for data from moonlight before shutting down the + stream
+
+
+ +
+ +
+ + +
The private key must be 2048 bits
+
+ +
+ + +
The certificate must be signed with a 2048 bit key
+
+ + +
+ + +
The file where current state of Sunshine is stored
+
+ +
+ + +
The file where current apps of Sunshine are stored
+
+
+
+ +
+ + +
+ The back/select button on the controller.
+ On the Shield, the home and powerbutton are not passed to Moonlight.
+ If, after the timeout, the back button is still pressed down, Home/Guide button press is + emulated.
+ If back_button_timeout < 0, then the Home/Guide button will not be emulated
+
+
+ +
+ + +
+ Control how fast keys will repeat themselves
+ The initial delay in milliseconds before repeating keys +
+
+ +
+ + +
+ How often keys repeat every second
+ This configurable option supports decimals +
+
+
+ +
+ +
+ + +
+ The name of the audio sink used for Audio Loopback
+ You can find the name of the audio sink using the following command:
+ tools\audio-info.exe +
+
+ The name of the audio sink used for Audio Loopback
+ If you do not specify this variable, pulseaudio will select the default monitor device.
+
+ You can find the name of the audio sink using the following command:
+ pacmd list-sources | grep "name:"
+
+
+ +
+ + +
+ You can select the video card you want to stream:
+ The appropriate values can be found using the following command:
+ tools\dxgi-info.exe +
+
+ +
+ + +
+ You can select the video card you want to stream:
+ The appropriate values can be found using the following command:
+ tools\dxgi-info.exe
+ !! Linux only !!
+ Set the display number to stream. I have no idea how they are numbered. They start from 1, + usually.
+ output_name = 1
+
+
+
+
+ +
+ + +
+ Constant Rate Factor. Between 1 and 52. It allows QP to go up during motion and down with still + image, + resulting in constant perceived quality
+ Higher value means more compression, but less quality
+ If crf == 0, then use QP directly instead +
+
+ +
+ + +
+ Quantitization Parameter
+ Higher value means more compression, but less quality
+ If crf != 0, then this parameter is ignored +
+
+ +
+ + +
+ Minimum number of threads used by ffmpeg to encode the video.
+ Increasing the value slightly reduces encoding efficiency, but the tradeoff is usually
+ worth it to gain the use of more CPU cores for encoding. The ideal value is the lowest
+ value that can reliably encode at your desired streaming settings on your hardware. +
+
+ +
+ + +
+ Allows the client to request HEVC Main or HEVC Main10 video streams.
+ HEVC is more CPU-intensive to encode, so enabling this may reduce performance when using software + encoding. +
+
+ +
+ + +
+ Force a specific encoder, otherwise Sunshine will use the first encoder that is available +
+
+ +
+ + +
+ How much error correcting packets must be send for every video.
+ This is just some random number, don't know the optimal value.
+ The higher fec_percentage, the lower space for the actual data to send per frame there is +
+
+ +
+ + +
+ When multicasting, it could be useful to have different configurations for each connected Client. + For example: +
    +
  • Clients connected through WAN and LAN have different bitrate contstraints.
  • +
  • Decoders may require different settings for color
  • +
+ Unlike simply broadcasting to multiple Client, this will generate distinct video streams.
+ Note, CPU usage increases for each distinct video stream generated +
+
+
+ +
+
+ + +
+
+ + +
+
+ +
+ +
+ + +
+
+ + +
+
+ + +
+
+ +
+ +
+ + +
+
+ + +
+
+ + +
+
+
+
+ +
+
+ + + + \ No newline at end of file diff --git a/assets/web/header.html b/assets/web/header.html index 59c11c2f..9c621101 100644 --- a/assets/web/header.html +++ b/assets/web/header.html @@ -11,7 +11,7 @@ - + @@ -34,9 +34,6 @@ - diff --git a/sunshine/config.cpp b/sunshine/config.cpp index 9408578c..d2a97d2e 100644 --- a/sunshine/config.cpp +++ b/sunshine/config.cpp @@ -1,7 +1,6 @@ #include #include #include -#include #include diff --git a/sunshine/config.h b/sunshine/config.h index f67ac12b..1dbcabbc 100644 --- a/sunshine/config.h +++ b/sunshine/config.h @@ -5,6 +5,7 @@ #include #include #include +#include namespace config { struct video_t { @@ -96,6 +97,7 @@ extern input_t input; extern sunshine_t sunshine; int parse(int argc, char *argv[]); +std::unordered_map parse_config(std::string_view file_content); } #endif diff --git a/sunshine/confighttp.cpp b/sunshine/confighttp.cpp index c71fc0f7..670b9204 100644 --- a/sunshine/confighttp.cpp +++ b/sunshine/confighttp.cpp @@ -21,6 +21,7 @@ #include "crypto.h" #include "confighttp.h" #include "platform/common.h" +#include "httpcommon.h" #include "network.h" #include "nvhttp.h" #include "uuid.h" @@ -219,6 +220,82 @@ void deleteApp(resp_https_t response, req_https_t request) } } +void getConfig(resp_https_t response, req_https_t request) +{ + pt::ptree outputTree; + auto g = util::fail_guard([&]() { + std::ostringstream data; + + pt::write_json(data, outputTree); + response->write(data.str()); + }); + try + { + outputTree.put("status","true"); + #ifdef _WIN32 + outputTree.put("platform","windows"); + #elif + outputTree.put("platform","linux"); + #endif + const char *config_file = SUNSHINE_ASSETS_DIR "/sunshine.conf"; + std::ifstream in { config_file }; + + if(!in.is_open()) { + std::cout << "Error: Couldn't open "sv << config_file << std::endl; + } + + auto vars = config::parse_config(std::string { + // Quick and dirty + std::istreambuf_iterator(in), + std::istreambuf_iterator() + }); + + for(auto &[name,value] : vars) { + outputTree.put(std::move(name), std::move(value)); + } + } + catch (std::exception &e) + { + BOOST_LOG(warning) << e.what(); + outputTree.put("status", "false"); + outputTree.put("error", "Invalid File JSON"); + return; + } +} + +void saveConfig(resp_https_t response, req_https_t request){ + std::stringstream ss; + std::stringstream configStream; + ss << request->content.rdbuf(); + pt::ptree outputTree; + auto g = util::fail_guard([&]() { + std::ostringstream data; + + pt::write_json(data, outputTree); + response->write(data.str()); + }); + pt::ptree inputTree; + try + { + //TODO: Input Validation + pt::read_json(ss, inputTree); + for (const auto &kv : inputTree) + { + std::string value = inputTree.get(kv.first); + if(value.length() == 0 || value.compare("null") == 0)continue; + configStream << kv.first << " = " << value << std::endl; + } + http::write_file(SUNSHINE_ASSETS_DIR "/sunshine.conf",configStream.str()); + } + catch (std::exception &e) + { + BOOST_LOG(warning) << e.what(); + outputTree.put("status", "false"); + outputTree.put("error", e.what()); + return; + } +} + void start(std::shared_ptr shutdown_event) { auto ctx = std::make_shared(boost::asio::ssl::context::tls); @@ -231,6 +308,8 @@ void start(std::shared_ptr shutdown_event) http_server.resource["^/apps$"]["GET"] = getAppsPage; http_server.resource["^/api/apps$"]["GET"] = getApps; http_server.resource["^/api/apps$"]["POST"] = saveApp; + http_server.resource["^/api/config$"]["GET"] = getConfig; + http_server.resource["^/api/config$"]["POST"] = saveConfig; http_server.resource["^/api/apps/([0-9]+)$"]["DELETE"] = deleteApp; http_server.resource["^/clients$"]["GET"] = getClientsPage; http_server.resource["^/config$"]["GET"] = getConfigPage;