Merge branch 'master' of https://github.com/loki-47-6F-64/sunshine into loki-47-6F-64-master

This commit is contained in:
Elia Zammuto
2021-05-29 22:06:28 +02:00
55 changed files with 4731 additions and 2456 deletions

View File

@@ -1,11 +1,12 @@
#include <algorithm>
#include <fstream>
#include <iostream>
#include <functional>
#include <iostream>
#include <boost/asio.hpp>
#include "utility.h"
#include "config.h"
#include "utility.h"
#define CA_DIR "credentials"
#define PRIVATE_KEY_FILE CA_DIR "/cakey.pem"
@@ -32,12 +33,12 @@ enum preset_e : int {
};
enum rc_e : int {
constqp = 0x0, /**< Constant QP mode */
vbr = 0x1, /**< Variable bitrate mode */
cbr = 0x2, /**< Constant bitrate mode */
cbr_ld_hq = 0x8, /**< low-delay CBR, high quality */
cbr_hq = 0x10, /**< CBR, high quality (slower) */
vbr_hq = 0x20 /**< VBR, high quality (slower) */
constqp = 0x0, /**< Constant QP mode */
vbr = 0x1, /**< Variable bitrate mode */
cbr = 0x2, /**< Constant bitrate mode */
cbr_ld_hq = 0x8, /**< low-delay CBR, high quality */
cbr_hq = 0x10, /**< CBR, high quality (slower) */
vbr_hq = 0x20 /**< VBR, high quality (slower) */
};
enum coder_e : int {
@@ -47,7 +48,8 @@ enum coder_e : int {
};
std::optional<preset_e> preset_from_view(const std::string_view &preset) {
#define _CONVERT_(x) if(preset == #x##sv) return x
#define _CONVERT_(x) \
if(preset == #x##sv) return x
_CONVERT_(slow);
_CONVERT_(medium);
_CONVERT_(fast);
@@ -64,7 +66,8 @@ std::optional<preset_e> preset_from_view(const std::string_view &preset) {
}
std::optional<rc_e> rc_from_view(const std::string_view &rc) {
#define _CONVERT_(x) if(rc == #x##sv) return x
#define _CONVERT_(x) \
if(rc == #x##sv) return x
_CONVERT_(constqp);
_CONVERT_(vbr);
_CONVERT_(cbr);
@@ -77,12 +80,12 @@ std::optional<rc_e> rc_from_view(const std::string_view &rc) {
int coder_from_view(const std::string_view &coder) {
if(coder == "auto"sv) return _auto;
if(coder == "cabac"sv || coder == "ac"sv) return cabac;
if(coder == "cavlc"sv || coder == "vlc"sv) return cavlc;
if(coder == "cabac"sv || coder == "ac"sv) return cabac;
if(coder == "cavlc"sv || coder == "vlc"sv) return cavlc;
return -1;
}
}
} // namespace nv
namespace amd {
enum quality_e : int {
@@ -106,7 +109,8 @@ enum coder_e : int {
};
std::optional<quality_e> quality_from_view(const std::string_view &quality) {
#define _CONVERT_(x) if(quality == #x##sv) return x
#define _CONVERT_(x) \
if(quality == #x##sv) return x
_CONVERT_(speed);
_CONVERT_(balanced);
//_CONVERT_(quality2);
@@ -116,7 +120,8 @@ std::optional<quality_e> quality_from_view(const std::string_view &quality) {
}
std::optional<rc_e> rc_from_view(const std::string_view &rc) {
#define _CONVERT_(x) if(rc == #x##sv) return x
#define _CONVERT_(x) \
if(rc == #x##sv) return x
_CONVERT_(constqp);
_CONVERT_(vbr_latency);
_CONVERT_(vbr_peak);
@@ -127,40 +132,38 @@ std::optional<rc_e> rc_from_view(const std::string_view &rc) {
int coder_from_view(const std::string_view &coder) {
if(coder == "auto"sv) return _auto;
if(coder == "cabac"sv || coder == "ac"sv) return cabac;
if(coder == "cavlc"sv || coder == "vlc"sv) return cavlc;
if(coder == "cabac"sv || coder == "ac"sv) return cabac;
if(coder == "cavlc"sv || coder == "vlc"sv) return cavlc;
return -1;
}
}
} // namespace amd
video_t video {
0, // crf
0, // crf
28, // qp
0, // hevc_mode
1, // min_threads
{
"superfast"s, // preset
"superfast"s, // preset
"zerolatency"s, // tune
}, // software
}, // software
{
nv::llhq,
std::nullopt,
-1
}, // nv
-1 }, // nv
{
amd::balanced,
std::nullopt,
-1
}, // amd
-1 }, // amd
{}, // encoder
{}, // adapter_name
{}, // output_name
{}, // output_name
};
audio_t audio {};
@@ -171,7 +174,7 @@ stream_t stream {
APPS_JSON_PATH,
10, // fecPercentage
1 // channels
1 // channels
};
nvhttp_t nvhttp {
@@ -180,42 +183,113 @@ nvhttp_t nvhttp {
CERTIFICATE_FILE,
boost::asio::ip::host_name(), // sunshine_name,
"sunshine_state.json"s // file_state
"sunshine_state.json"s, // file_state
{}, // external_ip
{
"352x240"s,
"480x360"s,
"858x480"s,
"1280x720"s,
"1920x1080"s,
"2560x1080"s,
"3440x1440"s
"1920x1200"s,
"3860x2160"s,
"3840x1600"s,
}, // supported resolutions
{ 10, 30, 60, 90, 120 }, // supported fps
};
input_t input {
2s, // back_button_timeout
500ms, // key_repeat_delay
std::chrono::duration<double> { 1 / 24.9 } // key_repeat_period
2s, // back_button_timeout
500ms, // key_repeat_delay
std::chrono::duration<double> { 1 / 24.9 } // key_repeat_period
};
sunshine_t sunshine {
2, // min_log_level
0 // flags
0 // flags
};
bool whitespace(char ch) {
bool endline(char ch) {
return ch == '\r' || ch == '\n';
}
bool space_tab(char ch) {
return ch == ' ' || ch == '\t';
}
std::string to_string(const char *begin, const char *end) {
return { begin, (std::size_t)(end - begin) };
bool whitespace(char ch) {
return space_tab(ch) || endline(ch);
}
std::optional<std::pair<std::string, std::string>> parse_line(std::string_view::const_iterator begin, std::string_view::const_iterator end) {
begin = std::find_if(begin, end, std::not_fn(whitespace));
end = std::find(begin, end, '#');
end = std::find_if(std::make_reverse_iterator(end), std::make_reverse_iterator(begin), std::not_fn(whitespace)).base();
std::string to_string(const char *begin, const char *end) {
std::string result;
auto eq = std::find(begin, end, '=');
if(eq == end || eq == begin) {
return std::nullopt;
KITTY_WHILE_LOOP(auto pos = begin, pos != end, {
auto comment = std::find(pos, end, '#');
auto endl = std::find_if(comment, end, endline);
result.append(pos, comment);
pos = endl;
})
return result;
}
template<class It>
It skip_list(It skipper, It end) {
int stack = 1;
while(skipper != end && stack) {
if(*skipper == '[') {
++stack;
}
if(*skipper == ']') {
--stack;
}
++skipper;
}
auto end_name = std::find_if(std::make_reverse_iterator(eq), std::make_reverse_iterator(begin), std::not_fn(whitespace)).base();
auto begin_val = std::find_if(eq + 1, end, std::not_fn(whitespace));
return skipper;
}
return std::pair { to_string(begin, end_name), to_string(begin_val, end) };
std::pair<
std::string_view::const_iterator,
std::optional<std::pair<std::string, std::string>>>
parse_option(std::string_view::const_iterator begin, std::string_view::const_iterator end) {
begin = std::find_if_not(begin, end, whitespace);
auto endl = std::find_if(begin, end, endline);
auto endc = std::find(begin, endl, '#');
endc = std::find_if(std::make_reverse_iterator(endc), std::make_reverse_iterator(begin), std::not_fn(whitespace)).base();
auto eq = std::find(begin, endc, '=');
if(eq == endc || eq == begin) {
return std::make_pair(endl, std::nullopt);
}
auto end_name = std::find_if_not(std::make_reverse_iterator(eq), std::make_reverse_iterator(begin), space_tab).base();
auto begin_val = std::find_if_not(eq + 1, endc, space_tab);
if(begin_val == endl) {
return std::make_pair(endl, std::nullopt);
}
// Lists might contain newlines
if(*begin_val == '[') {
endl = skip_list(begin_val + 1, end);
if(endl == end) {
std::cout << "Warning: Config option ["sv << to_string(begin, end_name) << "] Missing ']'"sv;
return std::make_pair(endl, std::nullopt);
}
}
return std::make_pair(
endl,
std::make_pair(to_string(begin, end_name), to_string(begin_val, endl)));
}
std::unordered_map<std::string, std::string> parse_config(std::string_view file_content) {
@@ -225,10 +299,14 @@ std::unordered_map<std::string, std::string> parse_config(std::string_view file_
auto end = std::end(file_content);
while(pos < end) {
auto newline = std::find_if(pos, end, [](auto ch) { return ch == '\n' || ch == '\r'; });
auto var = parse_line(pos, newline);
// auto newline = std::find_if(pos, end, [](auto ch) { return ch == '\n' || ch == '\r'; });
TUPLE_2D(endl, var, parse_option(pos, end));
pos = endl;
if(pos != end) {
pos += (*pos == '\r') ? 2 : 1;
}
pos = (*newline == '\r') ? newline + 2 : newline + 1;
if(!var) {
continue;
}
@@ -270,7 +348,7 @@ void int_f(std::unordered_map<std::string, std::string> &vars, const std::string
}
auto &val = it->second;
input = util::from_chars(&val[0], &val[0] + val.size());
input = util::from_chars(&val[0], &val[0] + val.size());
vars.erase(it);
}
@@ -283,7 +361,7 @@ void int_f(std::unordered_map<std::string, std::string> &vars, const std::string
}
auto &val = it->second;
input = util::from_chars(&val[0], &val[0] + val.size());
input = util::from_chars(&val[0], &val[0] + val.size());
vars.erase(it);
}
@@ -318,15 +396,14 @@ void int_between_f(std::unordered_map<std::string, std::string> &vars, const std
}
bool to_bool(std::string &boolean) {
std::for_each(std::begin(boolean), std::end(boolean), [](char ch) { return (char)std::tolower(ch); });
std::for_each(std::begin(boolean), std::end(boolean), [](char ch) { return (char)std::tolower(ch); });
return
boolean == "true"sv ||
boolean == "yes"sv ||
boolean == "enable"sv ||
(std::find(std::begin(boolean), std::end(boolean), '1') != std::end(boolean));
return boolean == "true"sv ||
boolean == "yes"sv ||
boolean == "enable"sv ||
(std::find(std::begin(boolean), std::end(boolean), '1') != std::end(boolean));
}
void bool_f(std::unordered_map<std::string, std::string> &vars, const std::string &name, int &input) {
void bool_f(std::unordered_map<std::string, std::string> &vars, const std::string &name, int &input) {
std::string tmp;
string_f(vars, name, tmp);
@@ -337,7 +414,7 @@ void bool_f(std::unordered_map<std::string, std::string> &vars, const std::strin
input = to_bool(tmp) ? 1 : 0;
}
void double_f(std::unordered_map<std::string, std::string> &vars, const std::string &name, double &input) {
void double_f(std::unordered_map<std::string, std::string> &vars, const std::string &name, double &input) {
std::string tmp;
string_f(vars, name, tmp);
@@ -366,33 +443,86 @@ void double_between_f(std::unordered_map<std::string, std::string> &vars, const
}
}
void list_string_f(std::unordered_map<std::string, std::string> &vars, const std::string &name, std::vector<std::string> &input) {
std::string string;
string_f(vars, name, string);
if(string.empty()) {
return;
}
input.clear();
auto begin = std::cbegin(string);
if(*begin == '[') {
++begin;
}
begin = std::find_if_not(begin, std::cend(string), whitespace);
if(begin == std::cend(string)) {
return;
}
auto pos = begin;
while(pos < std::cend(string)) {
if(*pos == '[') {
pos = skip_list(pos + 1, std::cend(string)) + 1;
}
else if(*pos == ']') {
break;
}
else if(*pos == ',') {
input.emplace_back(begin, pos);
pos = begin = std::find_if_not(pos + 1, std::cend(string), whitespace);
}
else {
++pos;
}
}
if(pos != begin) {
input.emplace_back(begin, pos);
}
}
void list_int_f(std::unordered_map<std::string, std::string> &vars, const std::string &name, std::vector<int> &input) {
std::vector<std::string> list;
list_string_f(vars, name, list);
for(auto &el : list) {
input.emplace_back(util::from_view(el));
}
}
void print_help(const char *name) {
std::cout <<
"Usage: "sv << name << " [options] [/path/to/configuration_file]"sv << std::endl <<
" Any configurable option can be overwritten with: \"name=value\""sv << std::endl << std::endl <<
" --help | print help"sv << std::endl << std::endl <<
" flags"sv << std::endl <<
" -0 | Read PIN from stdin"sv << std::endl <<
" -1 | Do not load previously saved state and do retain any state after shutdown"sv << std::endl <<
" | Effectively starting as if for the first time without overwriting any pairings with your devices"sv;
std::cout
<< "Usage: "sv << name << " [options] [/path/to/configuration_file]"sv << std::endl
<< " Any configurable option can be overwritten with: \"name=value\""sv << std::endl
<< std::endl
<< " --help | print help"sv << std::endl
<< std::endl
<< " flags"sv << std::endl
<< " -0 | Read PIN from stdin"sv << std::endl
<< " -1 | Do not load previously saved state and do retain any state after shutdown"sv << std::endl
<< " | Effectively starting as if for the first time without overwriting any pairings with your devices"sv;
}
int apply_flags(const char *line) {
int ret = 0;
while(*line != '\0') {
switch(*line) {
case '0':
config::sunshine.flags[config::flag::PIN_STDIN].flip();
break;
case '1':
config::sunshine.flags[config::flag::FRESH_STATE].flip();
break;
case 'p':
config::sunshine.flags[config::flag::CONST_PIN].flip();
break;
default:
std::cout << "Warning: Unrecognized flag: ["sv << *line << ']' << std::endl;
ret = -1;
case '0':
config::sunshine.flags[config::flag::PIN_STDIN].flip();
break;
case '1':
config::sunshine.flags[config::flag::FRESH_STATE].flip();
break;
case 'p':
config::sunshine.flags[config::flag::CONST_PIN].flip();
break;
default:
std::cout << "Warning: Unrecognized flag: ["sv << *line << ']' << std::endl;
ret = -1;
}
++line;
@@ -409,9 +539,7 @@ void apply_config(std::unordered_map<std::string, std::string> &&vars) {
int_f(vars, "crf", video.crf);
int_f(vars, "qp", video.qp);
int_f(vars, "min_threads", video.min_threads);
int_between_f(vars, "hevc_mode", video.hevc_mode, {
0, 3
});
int_between_f(vars, "hevc_mode", video.hevc_mode, { 0, 3 });
string_f(vars, "sw_preset", video.sw.preset);
string_f(vars, "sw_tune", video.sw.tune);
int_f(vars, "nv_preset", video.nv.preset, nv::preset_from_view);
@@ -431,29 +559,24 @@ void apply_config(std::unordered_map<std::string, std::string> &&vars) {
string_f(vars, "sunshine_name", nvhttp.sunshine_name);
string_f(vars, "file_state", nvhttp.file_state);
string_f(vars, "external_ip", nvhttp.external_ip);
list_string_f(vars, "resolutions"s, nvhttp.resolutions);
list_int_f(vars, "fps"s, nvhttp.fps);
string_f(vars, "audio_sink", audio.sink);
string_f(vars, "virtual_sink", audio.virtual_sink);
string_restricted_f(vars, "origin_pin_allowed", nvhttp.origin_pin_allowed, {
"pc"sv, "lan"sv, "wan"sv
});
string_restricted_f(vars, "origin_pin_allowed", nvhttp.origin_pin_allowed, { "pc"sv, "lan"sv, "wan"sv });
int to = -1;
int_between_f(vars, "ping_timeout", to, {
-1, std::numeric_limits<int>::max()
});
int_between_f(vars, "ping_timeout", to, { -1, std::numeric_limits<int>::max() });
if(to != -1) {
stream.ping_timeout = std::chrono::milliseconds(to);
}
int_between_f(vars, "channels", stream.channels, {
1, std::numeric_limits<int>::max()
});
int_between_f(vars, "channels", stream.channels, { 1, std::numeric_limits<int>::max() });
string_f(vars, "file_apps", stream.file_apps);
int_between_f(vars, "fec_percentage", stream.fec_percentage, {
1, 100
});
int_between_f(vars, "fec_percentage", stream.fec_percentage, { 1, 100 });
to = std::numeric_limits<int>::min();
int_f(vars, "back_button_timeout", to);
@@ -463,12 +586,10 @@ void apply_config(std::unordered_map<std::string, std::string> &&vars) {
}
double repeat_frequency { 0 };
double_between_f(vars, "key_repeat_frequency", repeat_frequency, {
0, std::numeric_limits<double>::max()
});
double_between_f(vars, "key_repeat_frequency", repeat_frequency, { 0, std::numeric_limits<double>::max() });
if(repeat_frequency > 0) {
config::input.key_repeat_period = std::chrono::duration<double> {1 / repeat_frequency };
config::input.key_repeat_period = std::chrono::duration<double> { 1 / repeat_frequency };
}
to = -1;
@@ -478,9 +599,7 @@ void apply_config(std::unordered_map<std::string, std::string> &&vars) {
}
std::string log_level_string;
string_restricted_f(vars, "min_log_level", log_level_string, {
"verbose"sv, "debug"sv, "info"sv, "warning"sv, "error"sv, "fatal"sv, "none"sv
});
string_restricted_f(vars, "min_log_level", log_level_string, { "verbose"sv, "debug"sv, "info"sv, "warning"sv, "error"sv, "fatal"sv, "none"sv });
if(!log_level_string.empty()) {
if(log_level_string == "verbose"sv) {
@@ -514,7 +633,7 @@ void apply_config(std::unordered_map<std::string, std::string> &&vars) {
}
if(sunshine.min_log_level <= 3) {
for(auto &[var,_] : vars) {
for(auto &[var, _] : vars) {
std::cout << "Warning: Unrecognized configurable option ["sv << var << ']' << std::endl;
}
}
@@ -525,7 +644,7 @@ int parse(int argc, char *argv[]) {
std::unordered_map<std::string, std::string> cmd_vars;
for(auto x = argc -1; x > 0; --x) {
for(auto x = argc - 1; x > 0; --x) {
auto line = argv[x];
if(line == "--help"sv) {
@@ -546,7 +665,7 @@ int parse(int argc, char *argv[]) {
config_file = line;
}
else {
auto var = parse_line(line, line_end);
TUPLE_EL(var, 1, parse_option(line, line_end));
if(!var) {
print_help(*argv);
return -1;
@@ -568,10 +687,9 @@ int parse(int argc, char *argv[]) {
auto vars = parse_config(std::string {
// Quick and dirty
std::istreambuf_iterator<char>(in),
std::istreambuf_iterator<char>()
});
std::istreambuf_iterator<char>() });
for(auto &[name,value] : cmd_vars) {
for(auto &[name, value] : cmd_vars) {
vars.insert_or_assign(std::move(name), std::move(value));
}
@@ -579,4 +697,4 @@ int parse(int argc, char *argv[]) {
return 0;
}
}
} // namespace config