mirror of
https://github.com/LizardByte/Sunshine.git
synced 2026-02-05 02:05:26 +00:00
clang-format
This commit is contained in:
67
.clang-format
Normal file
67
.clang-format
Normal file
@ -0,0 +1,67 @@
|
||||
# Generated from CLion C/C++ Code Style settings
|
||||
BasedOnStyle: LLVM
|
||||
AccessModifierOffset: -2
|
||||
AlignAfterOpenBracket: DontAlign
|
||||
AlignConsecutiveAssignments: AcrossComments
|
||||
AlignOperands: Align
|
||||
AllowAllArgumentsOnNextLine: false
|
||||
AllowAllConstructorInitializersOnNextLine: false
|
||||
AllowAllParametersOfDeclarationOnNextLine: false
|
||||
AllowShortBlocksOnASingleLine: Always
|
||||
AllowShortCaseLabelsOnASingleLine: false
|
||||
AllowShortFunctionsOnASingleLine: All
|
||||
AllowShortIfStatementsOnASingleLine: Always
|
||||
AllowShortLambdasOnASingleLine: All
|
||||
AllowShortLoopsOnASingleLine: true
|
||||
AlwaysBreakAfterReturnType: None
|
||||
AlwaysBreakTemplateDeclarations: Yes
|
||||
BreakBeforeBraces: Custom
|
||||
BraceWrapping:
|
||||
AfterCaseLabel: false
|
||||
AfterClass: false
|
||||
AfterControlStatement: Never
|
||||
AfterEnum: false
|
||||
AfterFunction: false
|
||||
AfterNamespace: false
|
||||
AfterUnion: false
|
||||
BeforeCatch: true
|
||||
BeforeElse: true
|
||||
IndentBraces: false
|
||||
SplitEmptyFunction: false
|
||||
SplitEmptyRecord: true
|
||||
BreakBeforeBinaryOperators: None
|
||||
BreakBeforeTernaryOperators: false
|
||||
BreakConstructorInitializers: BeforeColon
|
||||
BreakInheritanceList: BeforeColon
|
||||
ColumnLimit: 0
|
||||
CompactNamespaces: false
|
||||
ContinuationIndentWidth: 2
|
||||
IndentCaseLabels: false
|
||||
IndentPPDirectives: None
|
||||
IndentWidth: 2
|
||||
KeepEmptyLinesAtTheStartOfBlocks: true
|
||||
MaxEmptyLinesToKeep: 2
|
||||
NamespaceIndentation: None
|
||||
ObjCSpaceAfterProperty: false
|
||||
ObjCSpaceBeforeProtocolList: true
|
||||
PointerAlignment: Right
|
||||
ReflowComments: false
|
||||
SpaceAfterCStyleCast: false
|
||||
SpaceAfterLogicalNot: false
|
||||
SpaceAfterTemplateKeyword: false
|
||||
SpaceBeforeAssignmentOperators: true
|
||||
SpaceBeforeCpp11BracedList: true
|
||||
SpaceBeforeCtorInitializerColon: true
|
||||
SpaceBeforeInheritanceColon: true
|
||||
SpaceBeforeParens: Never
|
||||
SpaceBeforeRangeBasedForLoopColon: true
|
||||
SpaceInEmptyParentheses: false
|
||||
SpacesBeforeTrailingComments: 1
|
||||
SpacesInAngles: false
|
||||
SpacesInCStyleCastParentheses: false
|
||||
SpacesInContainerLiterals: false
|
||||
SpacesInParentheses: false
|
||||
SpacesInSquareBrackets: false
|
||||
TabWidth: 2
|
||||
Cpp11BracedListStyle: false
|
||||
UseTab: Never
|
||||
@ -4,14 +4,14 @@
|
||||
|
||||
#include "platform/common.h"
|
||||
|
||||
#include "utility.h"
|
||||
#include "thread_safe.h"
|
||||
#include "audio.h"
|
||||
#include "main.h"
|
||||
#include "thread_safe.h"
|
||||
#include "utility.h"
|
||||
|
||||
namespace audio {
|
||||
using namespace std::literals;
|
||||
using opus_t = util::safe_ptr<OpusMSEncoder, opus_multistream_encoder_destroy>;
|
||||
using opus_t = util::safe_ptr<OpusMSEncoder, opus_multistream_encoder_destroy>;
|
||||
using sample_queue_t = std::shared_ptr<safe::queue_t<std::vector<std::int16_t>>>;
|
||||
|
||||
struct opus_stream_config_t {
|
||||
@ -23,31 +23,31 @@ struct opus_stream_config_t {
|
||||
};
|
||||
|
||||
constexpr std::uint8_t map_stereo[] { 0, 1 };
|
||||
constexpr std::uint8_t map_surround51[] {0, 4, 1, 5, 2, 3};
|
||||
constexpr std::uint8_t map_high_surround51[] {0, 1, 2, 3, 4, 5};
|
||||
constexpr auto SAMPLE_RATE = 48000;
|
||||
constexpr std::uint8_t map_surround51[] { 0, 4, 1, 5, 2, 3 };
|
||||
constexpr std::uint8_t map_high_surround51[] { 0, 1, 2, 3, 4, 5 };
|
||||
constexpr auto SAMPLE_RATE = 48000;
|
||||
static opus_stream_config_t stereo = {
|
||||
SAMPLE_RATE,
|
||||
2,
|
||||
1,
|
||||
1,
|
||||
map_stereo
|
||||
SAMPLE_RATE,
|
||||
2,
|
||||
1,
|
||||
1,
|
||||
map_stereo
|
||||
};
|
||||
|
||||
static opus_stream_config_t Surround51 = {
|
||||
SAMPLE_RATE,
|
||||
6,
|
||||
4,
|
||||
2,
|
||||
map_surround51
|
||||
SAMPLE_RATE,
|
||||
6,
|
||||
4,
|
||||
2,
|
||||
map_surround51
|
||||
};
|
||||
|
||||
static opus_stream_config_t HighSurround51 = {
|
||||
SAMPLE_RATE,
|
||||
6,
|
||||
6,
|
||||
0,
|
||||
map_high_surround51
|
||||
SAMPLE_RATE,
|
||||
6,
|
||||
6,
|
||||
0,
|
||||
map_high_surround51
|
||||
};
|
||||
|
||||
void encodeThread(packet_queue_t packets, sample_queue_t samples, config_t config, void *channel_data) {
|
||||
@ -60,12 +60,11 @@ void encodeThread(packet_queue_t packets, sample_queue_t samples, config_t confi
|
||||
stream->coupledStreams,
|
||||
stream->mapping,
|
||||
OPUS_APPLICATION_AUDIO,
|
||||
nullptr)
|
||||
};
|
||||
nullptr) };
|
||||
|
||||
auto frame_size = config.packetDuration * stream->sampleRate / 1000;
|
||||
while(auto sample = samples->pop()) {
|
||||
packet_t packet { 16*1024 }; // 16KB
|
||||
packet_t packet { 16 * 1024 }; // 16KB
|
||||
|
||||
int bytes = opus_multistream_encode(opus.get(), sample->data(), frame_size, std::begin(packet), packet.size());
|
||||
if(bytes < 0) {
|
||||
@ -94,12 +93,12 @@ void capture(safe::signal_t *shutdown_event, packet_queue_t packets, config_t co
|
||||
//FIXME: Pick correct opus_stream_config_t based on config.channels
|
||||
auto stream = &stereo;
|
||||
|
||||
auto frame_size = config.packetDuration * stream->sampleRate / 1000;
|
||||
auto frame_size = config.packetDuration * stream->sampleRate / 1000;
|
||||
int samples_per_frame = frame_size * stream->channelCount;
|
||||
|
||||
auto mic = platf::microphone(stream->sampleRate, frame_size);
|
||||
if(!mic) {
|
||||
BOOST_LOG(error) << "Couldn't create audio input"sv ;
|
||||
BOOST_LOG(error) << "Couldn't create audio input"sv;
|
||||
|
||||
return;
|
||||
}
|
||||
@ -110,24 +109,24 @@ void capture(safe::signal_t *shutdown_event, packet_queue_t packets, config_t co
|
||||
|
||||
auto status = mic->sample(sample_buffer);
|
||||
switch(status) {
|
||||
case platf::capture_e::ok:
|
||||
break;
|
||||
case platf::capture_e::timeout:
|
||||
continue;
|
||||
case platf::capture_e::reinit:
|
||||
mic.reset();
|
||||
mic = platf::microphone(stream->sampleRate, frame_size);
|
||||
if(!mic) {
|
||||
BOOST_LOG(error) << "Couldn't re-initialize audio input"sv ;
|
||||
case platf::capture_e::ok:
|
||||
break;
|
||||
case platf::capture_e::timeout:
|
||||
continue;
|
||||
case platf::capture_e::reinit:
|
||||
mic.reset();
|
||||
mic = platf::microphone(stream->sampleRate, frame_size);
|
||||
if(!mic) {
|
||||
BOOST_LOG(error) << "Couldn't re-initialize audio input"sv;
|
||||
|
||||
return;
|
||||
}
|
||||
return;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
return;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
samples->raise(std::move(sample_buffer));
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace audio
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
#ifndef SUNSHINE_AUDIO_H
|
||||
#define SUNSHINE_AUDIO_H
|
||||
|
||||
#include "utility.h"
|
||||
#include "thread_safe.h"
|
||||
#include "utility.h"
|
||||
namespace audio {
|
||||
struct config_t {
|
||||
int packetDuration;
|
||||
@ -10,9 +10,9 @@ struct config_t {
|
||||
int mask;
|
||||
};
|
||||
|
||||
using packet_t = util::buffer_t<std::uint8_t>;
|
||||
using packet_queue_t = std::shared_ptr<safe::queue_t<std::pair<void*, packet_t>>>;
|
||||
using packet_t = util::buffer_t<std::uint8_t>;
|
||||
using packet_queue_t = std::shared_ptr<safe::queue_t<std::pair<void *, packet_t>>>;
|
||||
void capture(safe::signal_t *shutdown_event, packet_queue_t packets, config_t config, void *channel_data);
|
||||
}
|
||||
} // namespace audio
|
||||
|
||||
#endif
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <unordered_map>
|
||||
|
||||
#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"
|
||||
@ -33,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 {
|
||||
@ -48,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);
|
||||
@ -65,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);
|
||||
@ -78,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 {
|
||||
@ -107,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);
|
||||
@ -117,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);
|
||||
@ -128,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 {};
|
||||
@ -172,7 +174,7 @@ stream_t stream {
|
||||
APPS_JSON_PATH,
|
||||
|
||||
10, // fecPercentage
|
||||
1 // channels
|
||||
1 // channels
|
||||
};
|
||||
|
||||
nvhttp_t nvhttp {
|
||||
@ -181,18 +183,18 @@ nvhttp_t nvhttp {
|
||||
CERTIFICATE_FILE,
|
||||
|
||||
boost::asio::ip::host_name(), // sunshine_name,
|
||||
"sunshine_state.json"s // file_state
|
||||
"sunshine_state.json"s // file_state
|
||||
};
|
||||
|
||||
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) {
|
||||
@ -213,7 +215,7 @@ std::optional<std::pair<std::string, std::string>> parse_line(std::string_view::
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto end_name = std::find_if(std::make_reverse_iterator(eq), std::make_reverse_iterator(begin), std::not_fn(whitespace)).base();
|
||||
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 std::pair { to_string(begin, end_name), to_string(begin_val, end) };
|
||||
@ -227,7 +229,7 @@ std::unordered_map<std::string, std::string> parse_config(std::string_view file_
|
||||
|
||||
while(pos < end) {
|
||||
auto newline = std::find_if(pos, end, [](auto ch) { return ch == '\n' || ch == '\r'; });
|
||||
auto var = parse_line(pos, newline);
|
||||
auto var = parse_line(pos, newline);
|
||||
|
||||
pos = (*newline == '\r') ? newline + 2 : newline + 1;
|
||||
if(!var) {
|
||||
@ -271,7 +273,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);
|
||||
}
|
||||
@ -284,7 +286,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);
|
||||
}
|
||||
@ -319,15 +321,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);
|
||||
|
||||
@ -338,7 +339,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);
|
||||
|
||||
@ -368,32 +369,33 @@ void double_between_f(std::unordered_map<std::string, std::string> &vars, const
|
||||
}
|
||||
|
||||
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;
|
||||
@ -410,9 +412,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);
|
||||
@ -435,26 +435,18 @@ void apply_config(std::unordered_map<std::string, std::string> &&vars) {
|
||||
|
||||
string_f(vars, "audio_sink", audio.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);
|
||||
@ -464,12 +456,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;
|
||||
@ -479,9 +469,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) {
|
||||
@ -515,7 +503,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;
|
||||
}
|
||||
}
|
||||
@ -526,7 +514,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) {
|
||||
@ -569,10 +557,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));
|
||||
}
|
||||
|
||||
@ -580,4 +567,4 @@ int parse(int argc, char *argv[]) {
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
} // namespace config
|
||||
|
||||
@ -1,16 +1,16 @@
|
||||
#ifndef SUNSHINE_CONFIG_H
|
||||
#define SUNSHINE_CONFIG_H
|
||||
|
||||
#include <chrono>
|
||||
#include <string>
|
||||
#include <bitset>
|
||||
#include <chrono>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
namespace config {
|
||||
struct video_t {
|
||||
// ffmpeg params
|
||||
int crf; // higher == more compression and less quality
|
||||
int qp; // higher == more compression and less quality, ignored if crf != 0
|
||||
int qp; // higher == more compression and less quality, ignored if crf != 0
|
||||
|
||||
int hevc_mode;
|
||||
|
||||
@ -76,9 +76,9 @@ struct input_t {
|
||||
namespace flag {
|
||||
enum flag_e : std::size_t {
|
||||
PIN_STDIN = 0, // Read PIN from stdin instead of http
|
||||
FRESH_STATE, // Do not load or save state
|
||||
FRESH_STATE, // Do not load or save state
|
||||
FLAG_SIZE,
|
||||
CONST_PIN= 4 // Use "universal" pin
|
||||
CONST_PIN = 4 // Use "universal" pin
|
||||
};
|
||||
}
|
||||
|
||||
@ -96,6 +96,6 @@ extern input_t input;
|
||||
extern sunshine_t sunshine;
|
||||
|
||||
int parse(int argc, char *argv[]);
|
||||
}
|
||||
} // namespace config
|
||||
|
||||
#endif
|
||||
|
||||
@ -2,14 +2,14 @@
|
||||
// Created by loki on 5/31/19.
|
||||
//
|
||||
|
||||
#include <openssl/pem.h>
|
||||
#include "crypto.h"
|
||||
#include <openssl/pem.h>
|
||||
namespace crypto {
|
||||
using big_num_t = util::safe_ptr<BIGNUM, BN_free>;
|
||||
using big_num_t = util::safe_ptr<BIGNUM, BN_free>;
|
||||
//using rsa_t = util::safe_ptr<RSA, RSA_free>;
|
||||
using asn1_string_t = util::safe_ptr<ASN1_STRING, ASN1_STRING_free>;
|
||||
|
||||
cert_chain_t::cert_chain_t() : _certs {}, _cert_ctx {X509_STORE_CTX_new() } {}
|
||||
cert_chain_t::cert_chain_t() : _certs {}, _cert_ctx { X509_STORE_CTX_new() } {}
|
||||
void cert_chain_t::add(x509_t &&cert) {
|
||||
x509_store_t x509_store { X509_STORE_new() };
|
||||
|
||||
@ -26,7 +26,7 @@ void cert_chain_t::add(x509_t &&cert) {
|
||||
*/
|
||||
const char *cert_chain_t::verify(x509_t::element_type *cert) {
|
||||
int err_code = 0;
|
||||
for(auto &[_,x509_store] : _certs) {
|
||||
for(auto &[_, x509_store] : _certs) {
|
||||
auto fg = util::fail_guard([this]() {
|
||||
X509_STORE_CTX_cleanup(_cert_ctx.get());
|
||||
});
|
||||
@ -36,7 +36,7 @@ const char *cert_chain_t::verify(x509_t::element_type *cert) {
|
||||
|
||||
auto err = X509_verify_cert(_cert_ctx.get());
|
||||
|
||||
if (err == 1) {
|
||||
if(err == 1) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@ -46,7 +46,7 @@ const char *cert_chain_t::verify(x509_t::element_type *cert) {
|
||||
if(err_code == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY) {
|
||||
return nullptr;
|
||||
}
|
||||
if (err_code != X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT && err_code != X509_V_ERR_INVALID_CA) {
|
||||
if(err_code != X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT && err_code != X509_V_ERR_INVALID_CA) {
|
||||
return X509_verify_cert_error_string(err_code);
|
||||
}
|
||||
}
|
||||
@ -63,7 +63,7 @@ int cipher_t::decrypt(const std::string_view &cipher, std::vector<std::uint8_t>
|
||||
});
|
||||
|
||||
// Gen 7 servers use 128-bit AES ECB
|
||||
if (EVP_DecryptInit_ex(ctx.get(), EVP_aes_128_ecb(), nullptr, key.data(), nullptr) != 1) {
|
||||
if(EVP_DecryptInit_ex(ctx.get(), EVP_aes_128_ecb(), nullptr, key.data(), nullptr) != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -72,11 +72,11 @@ int cipher_t::decrypt(const std::string_view &cipher, std::vector<std::uint8_t>
|
||||
plaintext.resize((cipher.size() + 15) / 16 * 16);
|
||||
auto size = (int)plaintext.size();
|
||||
// Encrypt into the caller's buffer, leaving room for the auth tag to be prepended
|
||||
if (EVP_DecryptUpdate(ctx.get(), plaintext.data(), &size, (const std::uint8_t*)cipher.data(), cipher.size()) != 1) {
|
||||
if(EVP_DecryptUpdate(ctx.get(), plaintext.data(), &size, (const std::uint8_t *)cipher.data(), cipher.size()) != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (EVP_DecryptFinal_ex(ctx.get(), plaintext.data(), &len) != 1) {
|
||||
if(EVP_DecryptFinal_ex(ctx.get(), plaintext.data(), &len) != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -85,7 +85,7 @@ int cipher_t::decrypt(const std::string_view &cipher, std::vector<std::uint8_t>
|
||||
}
|
||||
|
||||
int cipher_t::decrypt_gcm(aes_t &iv, const std::string_view &tagged_cipher,
|
||||
std::vector<std::uint8_t> &plaintext) {
|
||||
std::vector<std::uint8_t> &plaintext) {
|
||||
auto cipher = tagged_cipher.substr(16);
|
||||
auto tag = tagged_cipher.substr(0, 16);
|
||||
|
||||
@ -93,15 +93,15 @@ int cipher_t::decrypt_gcm(aes_t &iv, const std::string_view &tagged_cipher,
|
||||
EVP_CIPHER_CTX_reset(ctx.get());
|
||||
});
|
||||
|
||||
if (EVP_DecryptInit_ex(ctx.get(), EVP_aes_128_gcm(), nullptr, nullptr, nullptr) != 1) {
|
||||
if(EVP_DecryptInit_ex(ctx.get(), EVP_aes_128_gcm(), nullptr, nullptr, nullptr) != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_SET_IVLEN, iv.size(), nullptr) != 1) {
|
||||
if(EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_SET_IVLEN, iv.size(), nullptr) != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (EVP_DecryptInit_ex(ctx.get(), nullptr, nullptr, key.data(), iv.data()) != 1) {
|
||||
if(EVP_DecryptInit_ex(ctx.get(), nullptr, nullptr, key.data(), iv.data()) != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -109,16 +109,16 @@ int cipher_t::decrypt_gcm(aes_t &iv, const std::string_view &tagged_cipher,
|
||||
plaintext.resize((cipher.size() + 15) / 16 * 16);
|
||||
|
||||
int size;
|
||||
if (EVP_DecryptUpdate(ctx.get(), plaintext.data(), &size, (const std::uint8_t*)cipher.data(), cipher.size()) != 1) {
|
||||
if(EVP_DecryptUpdate(ctx.get(), plaintext.data(), &size, (const std::uint8_t *)cipher.data(), cipher.size()) != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_SET_TAG, tag.size(), const_cast<char*>(tag.data())) != 1) {
|
||||
if(EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_SET_TAG, tag.size(), const_cast<char *>(tag.data())) != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int len = size;
|
||||
if (EVP_DecryptFinal_ex(ctx.get(), plaintext.data() + size, &len) != 1) {
|
||||
if(EVP_DecryptFinal_ex(ctx.get(), plaintext.data() + size, &len) != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -134,7 +134,7 @@ int cipher_t::encrypt(const std::string_view &plaintext, std::vector<std::uint8_
|
||||
});
|
||||
|
||||
// Gen 7 servers use 128-bit AES ECB
|
||||
if (EVP_EncryptInit_ex(ctx.get(), EVP_aes_128_ecb(), nullptr, key.data(), nullptr) != 1) {
|
||||
if(EVP_EncryptInit_ex(ctx.get(), EVP_aes_128_ecb(), nullptr, key.data(), nullptr) != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -143,11 +143,11 @@ int cipher_t::encrypt(const std::string_view &plaintext, std::vector<std::uint8_
|
||||
cipher.resize((plaintext.size() + 15) / 16 * 16);
|
||||
auto size = (int)cipher.size();
|
||||
// Encrypt into the caller's buffer
|
||||
if (EVP_EncryptUpdate(ctx.get(), cipher.data(), &size, (const std::uint8_t*)plaintext.data(), plaintext.size()) != 1) {
|
||||
if(EVP_EncryptUpdate(ctx.get(), cipher.data(), &size, (const std::uint8_t *)plaintext.data(), plaintext.size()) != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (EVP_EncryptFinal_ex(ctx.get(), cipher.data() + size, &len) != 1) {
|
||||
if(EVP_EncryptFinal_ex(ctx.get(), cipher.data() + size, &len) != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -230,14 +230,14 @@ std::string_view signature(const x509_t &x) {
|
||||
const ASN1_BIT_STRING *asn1 = nullptr;
|
||||
X509_get0_signature(&asn1, nullptr, x.get());
|
||||
|
||||
return { (const char*)asn1->data, (std::size_t)asn1->length };
|
||||
return { (const char *)asn1->data, (std::size_t)asn1->length };
|
||||
}
|
||||
|
||||
std::string rand(std::size_t bytes) {
|
||||
std::string r;
|
||||
r.resize(bytes);
|
||||
|
||||
RAND_bytes((uint8_t*)r.data(), r.size());
|
||||
RAND_bytes((uint8_t *)r.data(), r.size());
|
||||
|
||||
return r;
|
||||
}
|
||||
@ -297,8 +297,8 @@ creds_t gen_creds(const std::string_view &cn, std::uint32_t key_bits) {
|
||||
X509_set_pubkey(x509.get(), pkey.get());
|
||||
|
||||
auto name = X509_get_subject_name(x509.get());
|
||||
X509_NAME_add_entry_by_txt(name,"CN", MBSTRING_ASC,
|
||||
(const std::uint8_t*)cn.data(), cn.size(),
|
||||
X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC,
|
||||
(const std::uint8_t *)cn.data(), cn.size(),
|
||||
-1, 0);
|
||||
|
||||
X509_set_issuer_name(x509.get(), name);
|
||||
@ -324,7 +324,7 @@ bool verify(const x509_t &x509, const std::string_view &data, const std::string_
|
||||
return false;
|
||||
}
|
||||
|
||||
if(EVP_DigestVerifyFinal(ctx.get(), (const uint8_t*)signature.data(), signature.size()) != 1) {
|
||||
if(EVP_DigestVerifyFinal(ctx.get(), (const uint8_t *)signature.data(), signature.size()) != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -338,4 +338,4 @@ bool verify256(const x509_t &x509, const std::string_view &data, const std::stri
|
||||
void md_ctx_destroy(EVP_MD_CTX *ctx) {
|
||||
EVP_MD_CTX_destroy(ctx);
|
||||
}
|
||||
}
|
||||
} // namespace crypto
|
||||
|
||||
@ -5,12 +5,12 @@
|
||||
#ifndef SUNSHINE_CRYPTO_H
|
||||
#define SUNSHINE_CRYPTO_H
|
||||
|
||||
#include <cassert>
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/rand.h>
|
||||
#include <openssl/sha.h>
|
||||
#include <openssl/x509.h>
|
||||
#include <openssl/rand.h>
|
||||
|
||||
#include "utility.h"
|
||||
|
||||
@ -25,14 +25,14 @@ void md_ctx_destroy(EVP_MD_CTX *);
|
||||
|
||||
using sha256_t = std::array<std::uint8_t, SHA256_DIGEST_LENGTH>;
|
||||
|
||||
using aes_t = std::array<std::uint8_t, 16>;
|
||||
using x509_t = util::safe_ptr<X509, X509_free>;
|
||||
using x509_store_t = util::safe_ptr<X509_STORE, X509_STORE_free>;
|
||||
using aes_t = std::array<std::uint8_t, 16>;
|
||||
using x509_t = util::safe_ptr<X509, X509_free>;
|
||||
using x509_store_t = util::safe_ptr<X509_STORE, X509_STORE_free>;
|
||||
using x509_store_ctx_t = util::safe_ptr<X509_STORE_CTX, X509_STORE_CTX_free>;
|
||||
using cipher_ctx_t = util::safe_ptr<EVP_CIPHER_CTX, EVP_CIPHER_CTX_free>;
|
||||
using md_ctx_t = util::safe_ptr<EVP_MD_CTX, md_ctx_destroy>;
|
||||
using bio_t = util::safe_ptr<BIO, BIO_free_all>;
|
||||
using pkey_t = util::safe_ptr<EVP_PKEY, EVP_PKEY_free>;
|
||||
using cipher_ctx_t = util::safe_ptr<EVP_CIPHER_CTX, EVP_CIPHER_CTX_free>;
|
||||
using md_ctx_t = util::safe_ptr<EVP_MD_CTX, md_ctx_destroy>;
|
||||
using bio_t = util::safe_ptr<BIO, BIO_free_all>;
|
||||
using pkey_t = util::safe_ptr<EVP_PKEY, EVP_PKEY_free>;
|
||||
|
||||
sha256_t hash(const std::string_view &plaintext);
|
||||
aes_t gen_aes_key(const std::array<uint8_t, 16> &salt, const std::string_view &pin);
|
||||
@ -58,6 +58,7 @@ public:
|
||||
void add(x509_t &&cert);
|
||||
|
||||
const char *verify(x509_t::element_type *cert);
|
||||
|
||||
private:
|
||||
std::vector<std::pair<x509_t, x509_store_t>> _certs;
|
||||
x509_store_ctx_t _cert_ctx;
|
||||
@ -66,13 +67,14 @@ private:
|
||||
class cipher_t {
|
||||
public:
|
||||
cipher_t(const aes_t &key);
|
||||
cipher_t(cipher_t&&) noexcept = default;
|
||||
cipher_t &operator=(cipher_t&&) noexcept = default;
|
||||
cipher_t(cipher_t &&) noexcept = default;
|
||||
cipher_t &operator=(cipher_t &&) noexcept = default;
|
||||
|
||||
int encrypt(const std::string_view &plaintext, std::vector<std::uint8_t> &cipher);
|
||||
|
||||
int decrypt_gcm(aes_t &iv, const std::string_view &cipher, std::vector<std::uint8_t> &plaintext);
|
||||
int decrypt(const std::string_view &cipher, std::vector<std::uint8_t> &plaintext);
|
||||
|
||||
private:
|
||||
cipher_ctx_t ctx;
|
||||
aes_t key;
|
||||
@ -80,6 +82,6 @@ private:
|
||||
public:
|
||||
bool padding;
|
||||
};
|
||||
}
|
||||
} // namespace crypto
|
||||
|
||||
#endif //SUNSHINE_CRYPTO_H
|
||||
|
||||
@ -10,16 +10,16 @@ extern "C" {
|
||||
|
||||
#include <bitset>
|
||||
|
||||
#include "main.h"
|
||||
#include "config.h"
|
||||
#include "utility.h"
|
||||
#include "thread_pool.h"
|
||||
#include "input.h"
|
||||
#include "main.h"
|
||||
#include "platform/common.h"
|
||||
#include "thread_pool.h"
|
||||
#include "utility.h"
|
||||
|
||||
namespace input {
|
||||
|
||||
constexpr auto MAX_GAMEPADS = std::min((std::size_t)platf::MAX_GAMEPADS, sizeof(std::int16_t)*8);
|
||||
constexpr auto MAX_GAMEPADS = std::min((std::size_t)platf::MAX_GAMEPADS, sizeof(std::int16_t) * 8);
|
||||
#define DISABLE_LEFT_BUTTON_DELAY ((util::ThreadPool::task_id_t)0x01)
|
||||
#define ENABLE_LEFT_BUTTON_DELAY nullptr
|
||||
|
||||
@ -59,7 +59,7 @@ static platf::input_t platf_input;
|
||||
static std::bitset<platf::MAX_GAMEPADS> gamepadMask {};
|
||||
|
||||
void free_gamepad(platf::input_t &platf_input, int id) {
|
||||
platf::gamepad(platf_input, id, platf::gamepad_state_t{});
|
||||
platf::gamepad(platf_input, id, platf::gamepad_state_t {});
|
||||
platf::free_gamepad(platf_input, id);
|
||||
|
||||
free_id(gamepadMask, id);
|
||||
@ -68,7 +68,7 @@ struct gamepad_t {
|
||||
gamepad_t() : gamepad_state {}, back_timeout_id {}, id { -1 }, back_button_state { button_state_e::NONE } {}
|
||||
~gamepad_t() {
|
||||
if(id >= 0) {
|
||||
task_pool.push([id=this->id]() {
|
||||
task_pool.push([id = this->id]() {
|
||||
free_gamepad(platf_input, id);
|
||||
});
|
||||
}
|
||||
@ -89,7 +89,7 @@ struct gamepad_t {
|
||||
};
|
||||
|
||||
struct input_t {
|
||||
input_t() : active_gamepad_state {}, gamepads (MAX_GAMEPADS), mouse_left_button_timeout {} { }
|
||||
input_t() : active_gamepad_state {}, gamepads(MAX_GAMEPADS), mouse_left_button_timeout {} {}
|
||||
|
||||
std::uint16_t active_gamepad_state;
|
||||
std::vector<gamepad_t> gamepads;
|
||||
@ -158,33 +158,32 @@ void print(PNV_MULTI_CONTROLLER_PACKET packet) {
|
||||
|
||||
constexpr int PACKET_TYPE_SCROLL_OR_KEYBOARD = PACKET_TYPE_SCROLL;
|
||||
void print(void *input) {
|
||||
int input_type = util::endian::big(*(int*)input);
|
||||
int input_type = util::endian::big(*(int *)input);
|
||||
|
||||
switch(input_type) {
|
||||
case PACKET_TYPE_REL_MOUSE_MOVE:
|
||||
print((PNV_REL_MOUSE_MOVE_PACKET)input);
|
||||
break;
|
||||
case PACKET_TYPE_ABS_MOUSE_MOVE:
|
||||
print((PNV_ABS_MOUSE_MOVE_PACKET)input);
|
||||
break;
|
||||
case PACKET_TYPE_MOUSE_BUTTON:
|
||||
print((PNV_MOUSE_BUTTON_PACKET)input);
|
||||
break;
|
||||
case PACKET_TYPE_SCROLL_OR_KEYBOARD:
|
||||
{
|
||||
char *tmp_input = (char*)input + 4;
|
||||
if(tmp_input[0] == 0x0A) {
|
||||
print((PNV_SCROLL_PACKET)input);
|
||||
}
|
||||
else {
|
||||
print((PNV_KEYBOARD_PACKET)input);
|
||||
}
|
||||
|
||||
break;
|
||||
case PACKET_TYPE_REL_MOUSE_MOVE:
|
||||
print((PNV_REL_MOUSE_MOVE_PACKET)input);
|
||||
break;
|
||||
case PACKET_TYPE_ABS_MOUSE_MOVE:
|
||||
print((PNV_ABS_MOUSE_MOVE_PACKET)input);
|
||||
break;
|
||||
case PACKET_TYPE_MOUSE_BUTTON:
|
||||
print((PNV_MOUSE_BUTTON_PACKET)input);
|
||||
break;
|
||||
case PACKET_TYPE_SCROLL_OR_KEYBOARD: {
|
||||
char *tmp_input = (char *)input + 4;
|
||||
if(tmp_input[0] == 0x0A) {
|
||||
print((PNV_SCROLL_PACKET)input);
|
||||
}
|
||||
case PACKET_TYPE_MULTI_CONTROLLER:
|
||||
print((PNV_MULTI_CONTROLLER_PACKET)input);
|
||||
break;
|
||||
else {
|
||||
print((PNV_KEYBOARD_PACKET)input);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case PACKET_TYPE_MULTI_CONTROLLER:
|
||||
print((PNV_MULTI_CONTROLLER_PACKET)input);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -245,8 +244,8 @@ void passthrough(std::shared_ptr<input_t> &input, PNV_MOUSE_BUTTON_PACKET packet
|
||||
|
||||
mouse_press[button] = !release;
|
||||
}
|
||||
///////////////////////////////////
|
||||
/*/
|
||||
///////////////////////////////////
|
||||
/*/
|
||||
* When Moonlight sends mouse input through absolute coordinates,
|
||||
* it's possible that BUTTON_RIGHT is pressed down immediately after releasing BUTTON_LEFT.
|
||||
* As a result, Sunshine will left click on hyperlinks in the browser before right clicking
|
||||
@ -261,7 +260,7 @@ void passthrough(std::shared_ptr<input_t> &input, PNV_MOUSE_BUTTON_PACKET packet
|
||||
* when the last mouse coordinates were absolute
|
||||
/*/
|
||||
if(button == BUTTON_LEFT && release && !input->mouse_left_button_timeout) {
|
||||
input->mouse_left_button_timeout = task_pool.pushDelayed([=]() {
|
||||
auto f = [=]() {
|
||||
auto left_released = mouse_press[BUTTON_LEFT];
|
||||
if(left_released) {
|
||||
// Already released left button
|
||||
@ -269,16 +268,17 @@ void passthrough(std::shared_ptr<input_t> &input, PNV_MOUSE_BUTTON_PACKET packet
|
||||
}
|
||||
platf::button_mouse(platf_input, BUTTON_LEFT, release);
|
||||
|
||||
mouse_press[BUTTON_LEFT] = false;
|
||||
mouse_press[BUTTON_LEFT] = false;
|
||||
input->mouse_left_button_timeout = nullptr;
|
||||
}, 10ms).task_id;
|
||||
};
|
||||
|
||||
input->mouse_left_button_timeout = task_pool.pushDelayed(std::move(f), 10ms).task_id;
|
||||
|
||||
return;
|
||||
}
|
||||
if(
|
||||
button == BUTTON_RIGHT && !release &&
|
||||
input->mouse_left_button_timeout > DISABLE_LEFT_BUTTON_DELAY
|
||||
) {
|
||||
input->mouse_left_button_timeout > DISABLE_LEFT_BUTTON_DELAY) {
|
||||
platf::button_mouse(platf_input, BUTTON_RIGHT, false);
|
||||
platf::button_mouse(platf_input, BUTTON_RIGHT, true);
|
||||
|
||||
@ -286,7 +286,7 @@ void passthrough(std::shared_ptr<input_t> &input, PNV_MOUSE_BUTTON_PACKET packet
|
||||
|
||||
return;
|
||||
}
|
||||
///////////////////////////////////
|
||||
///////////////////////////////////
|
||||
|
||||
platf::button_mouse(platf_input, button, release);
|
||||
}
|
||||
@ -341,7 +341,7 @@ void passthrough(PNV_SCROLL_PACKET packet) {
|
||||
|
||||
int updateGamepads(std::vector<gamepad_t> &gamepads, std::int16_t old_state, std::int16_t new_state) {
|
||||
auto xorGamepadMask = old_state ^ new_state;
|
||||
if (!xorGamepadMask) {
|
||||
if(!xorGamepadMask) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -350,7 +350,7 @@ int updateGamepads(std::vector<gamepad_t> &gamepads, std::int16_t old_state, std
|
||||
auto &gamepad = gamepads[x];
|
||||
|
||||
if((old_state >> x) & 1) {
|
||||
if (gamepad.id < 0) {
|
||||
if(gamepad.id < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -410,7 +410,7 @@ void passthrough(std::shared_ptr<input_t> &input, PNV_MULTI_CONTROLLER_PACKET pa
|
||||
display_cursor = false;
|
||||
|
||||
std::uint16_t bf = packet->buttonFlags;
|
||||
platf::gamepad_state_t gamepad_state{
|
||||
platf::gamepad_state_t gamepad_state {
|
||||
bf,
|
||||
packet->leftTrigger,
|
||||
packet->rightTrigger,
|
||||
@ -422,30 +422,30 @@ void passthrough(std::shared_ptr<input_t> &input, PNV_MULTI_CONTROLLER_PACKET pa
|
||||
|
||||
auto bf_new = gamepad_state.buttonFlags;
|
||||
switch(gamepad.back_button_state) {
|
||||
case button_state_e::UP:
|
||||
if(!(platf::BACK & bf_new)) {
|
||||
gamepad.back_button_state = button_state_e::NONE;
|
||||
}
|
||||
gamepad_state.buttonFlags &= ~platf::BACK;
|
||||
break;
|
||||
case button_state_e::DOWN:
|
||||
if(platf::BACK & bf_new) {
|
||||
gamepad.back_button_state = button_state_e::NONE;
|
||||
}
|
||||
gamepad_state.buttonFlags |= platf::BACK;
|
||||
break;
|
||||
case button_state_e::NONE:
|
||||
break;
|
||||
case button_state_e::UP:
|
||||
if(!(platf::BACK & bf_new)) {
|
||||
gamepad.back_button_state = button_state_e::NONE;
|
||||
}
|
||||
gamepad_state.buttonFlags &= ~platf::BACK;
|
||||
break;
|
||||
case button_state_e::DOWN:
|
||||
if(platf::BACK & bf_new) {
|
||||
gamepad.back_button_state = button_state_e::NONE;
|
||||
}
|
||||
gamepad_state.buttonFlags |= platf::BACK;
|
||||
break;
|
||||
case button_state_e::NONE:
|
||||
break;
|
||||
}
|
||||
|
||||
bf = gamepad_state.buttonFlags ^ gamepad.gamepad_state.buttonFlags;
|
||||
bf = gamepad_state.buttonFlags ^ gamepad.gamepad_state.buttonFlags;
|
||||
bf_new = gamepad_state.buttonFlags;
|
||||
|
||||
if (platf::BACK & bf) {
|
||||
if (platf::BACK & bf_new) {
|
||||
if(platf::BACK & bf) {
|
||||
if(platf::BACK & bf_new) {
|
||||
// Don't emulate home button if timeout < 0
|
||||
if(config::input.back_button_timeout >= 0ms) {
|
||||
gamepad.back_timeout_id = task_pool.pushDelayed([input, controller=packet->controllerNumber]() {
|
||||
auto f = [input, controller = packet->controllerNumber]() {
|
||||
auto &gamepad = input->gamepads[controller];
|
||||
|
||||
auto &state = gamepad.gamepad_state;
|
||||
@ -464,10 +464,12 @@ void passthrough(std::shared_ptr<input_t> &input, PNV_MULTI_CONTROLLER_PACKET pa
|
||||
platf::gamepad(platf_input, gamepad.id, state);
|
||||
|
||||
gamepad.back_timeout_id = nullptr;
|
||||
}, config::input.back_button_timeout).task_id;
|
||||
};
|
||||
|
||||
gamepad.back_timeout_id = task_pool.pushDelayed(std::move(f), config::input.back_button_timeout).task_id;
|
||||
}
|
||||
}
|
||||
else if (gamepad.back_timeout_id) {
|
||||
else if(gamepad.back_timeout_id) {
|
||||
task_pool.cancel(gamepad.back_timeout_id);
|
||||
gamepad.back_timeout_id = nullptr;
|
||||
}
|
||||
@ -481,33 +483,32 @@ void passthrough(std::shared_ptr<input_t> &input, PNV_MULTI_CONTROLLER_PACKET pa
|
||||
void passthrough_helper(std::shared_ptr<input_t> input, std::vector<std::uint8_t> &&input_data) {
|
||||
void *payload = input_data.data();
|
||||
|
||||
int input_type = util::endian::big(*(int*)payload);
|
||||
int input_type = util::endian::big(*(int *)payload);
|
||||
|
||||
switch(input_type) {
|
||||
case PACKET_TYPE_REL_MOUSE_MOVE:
|
||||
passthrough(input, (PNV_REL_MOUSE_MOVE_PACKET)payload);
|
||||
break;
|
||||
case PACKET_TYPE_ABS_MOUSE_MOVE:
|
||||
passthrough(input, (PNV_ABS_MOUSE_MOVE_PACKET)payload);
|
||||
break;
|
||||
case PACKET_TYPE_MOUSE_BUTTON:
|
||||
passthrough(input, (PNV_MOUSE_BUTTON_PACKET)payload);
|
||||
break;
|
||||
case PACKET_TYPE_SCROLL_OR_KEYBOARD:
|
||||
{
|
||||
char *tmp_input = (char*)payload + 4;
|
||||
if(tmp_input[0] == 0x0A) {
|
||||
passthrough((PNV_SCROLL_PACKET)payload);
|
||||
}
|
||||
else {
|
||||
passthrough(input, (PNV_KEYBOARD_PACKET)payload);
|
||||
}
|
||||
|
||||
break;
|
||||
case PACKET_TYPE_REL_MOUSE_MOVE:
|
||||
passthrough(input, (PNV_REL_MOUSE_MOVE_PACKET)payload);
|
||||
break;
|
||||
case PACKET_TYPE_ABS_MOUSE_MOVE:
|
||||
passthrough(input, (PNV_ABS_MOUSE_MOVE_PACKET)payload);
|
||||
break;
|
||||
case PACKET_TYPE_MOUSE_BUTTON:
|
||||
passthrough(input, (PNV_MOUSE_BUTTON_PACKET)payload);
|
||||
break;
|
||||
case PACKET_TYPE_SCROLL_OR_KEYBOARD: {
|
||||
char *tmp_input = (char *)payload + 4;
|
||||
if(tmp_input[0] == 0x0A) {
|
||||
passthrough((PNV_SCROLL_PACKET)payload);
|
||||
}
|
||||
case PACKET_TYPE_MULTI_CONTROLLER:
|
||||
passthrough(input, (PNV_MULTI_CONTROLLER_PACKET)payload);
|
||||
break;
|
||||
else {
|
||||
passthrough(input, (PNV_KEYBOARD_PACKET)payload);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case PACKET_TYPE_MULTI_CONTROLLER:
|
||||
passthrough(input, (PNV_MULTI_CONTROLLER_PACKET)payload);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -528,7 +529,7 @@ void reset(std::shared_ptr<input_t> &input) {
|
||||
}
|
||||
}
|
||||
|
||||
for(auto& kp : key_press) {
|
||||
for(auto &kp : key_press) {
|
||||
platf::keyboard(platf_input, kp.first & 0x00FF, true);
|
||||
key_press[kp.first] = false;
|
||||
}
|
||||
@ -537,7 +538,7 @@ void reset(std::shared_ptr<input_t> &input) {
|
||||
|
||||
void init() {
|
||||
touch_port_event = std::make_unique<touch_port_event_t::element_type>();
|
||||
platf_input = platf::input();
|
||||
platf_input = platf::input();
|
||||
}
|
||||
|
||||
std::shared_ptr<input_t> alloc() {
|
||||
@ -547,8 +548,9 @@ std::shared_ptr<input_t> alloc() {
|
||||
task_pool.pushDelayed([]() {
|
||||
platf::move_mouse(platf_input, 1, 1);
|
||||
platf::move_mouse(platf_input, -1, -1);
|
||||
}, 100ms);
|
||||
},
|
||||
100ms);
|
||||
|
||||
return input;
|
||||
}
|
||||
}
|
||||
} // namespace input
|
||||
|
||||
@ -5,8 +5,8 @@
|
||||
#ifndef SUNSHINE_INPUT_H
|
||||
#define SUNSHINE_INPUT_H
|
||||
|
||||
#include "thread_safe.h"
|
||||
#include "platform/common.h"
|
||||
#include "thread_safe.h"
|
||||
namespace input {
|
||||
|
||||
struct input_t;
|
||||
@ -22,6 +22,6 @@ std::shared_ptr<input_t> alloc();
|
||||
|
||||
using touch_port_event_t = std::unique_ptr<safe::event_t<platf::touch_port_t>>;
|
||||
extern touch_port_event_t touch_port_event;
|
||||
}
|
||||
} // namespace input
|
||||
|
||||
#endif //SUNSHINE_INPUT_H
|
||||
|
||||
@ -4,26 +4,26 @@
|
||||
|
||||
#include "process.h"
|
||||
|
||||
#include <thread>
|
||||
#include <iostream>
|
||||
#include <csignal>
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
|
||||
#include <boost/log/common.hpp>
|
||||
#include <boost/log/sinks.hpp>
|
||||
#include <boost/log/expressions.hpp>
|
||||
#include <boost/log/sources/severity_logger.hpp>
|
||||
#include <boost/log/attributes/clock.hpp>
|
||||
#include <boost/log/common.hpp>
|
||||
#include <boost/log/expressions.hpp>
|
||||
#include <boost/log/sinks.hpp>
|
||||
#include <boost/log/sources/severity_logger.hpp>
|
||||
|
||||
#include "video.h"
|
||||
#include "config.h"
|
||||
#include "nvhttp.h"
|
||||
#include "rtsp.h"
|
||||
#include "config.h"
|
||||
#include "thread_pool.h"
|
||||
#include "video.h"
|
||||
|
||||
#include "platform/common.h"
|
||||
extern "C" {
|
||||
#include <rs.h>
|
||||
#include <libavutil/log.h>
|
||||
#include <rs.h>
|
||||
}
|
||||
|
||||
using namespace std::literals;
|
||||
@ -79,31 +79,31 @@ int main(int argc, char *argv[]) {
|
||||
sink->locked_backend()->add_stream(stream);
|
||||
sink->set_filter(severity >= config::sunshine.min_log_level);
|
||||
|
||||
sink->set_formatter([message="Message"s, severity="Severity"s](const bl::record_view &view, bl::formatting_ostream &os) {
|
||||
constexpr int DATE_BUFFER_SIZE = 21 +2 +1; // Full string plus ": \0"
|
||||
sink->set_formatter([message = "Message"s, severity = "Severity"s](const bl::record_view &view, bl::formatting_ostream &os) {
|
||||
constexpr int DATE_BUFFER_SIZE = 21 + 2 + 1; // Full string plus ": \0"
|
||||
|
||||
auto log_level = view.attribute_values()[severity].extract<int>().get();
|
||||
|
||||
std::string_view log_type;
|
||||
switch(log_level) {
|
||||
case 0:
|
||||
log_type = "Verbose: "sv;
|
||||
break;
|
||||
case 1:
|
||||
log_type = "Debug: "sv;
|
||||
break;
|
||||
case 2:
|
||||
log_type = "Info: "sv;
|
||||
break;
|
||||
case 3:
|
||||
log_type = "Warning: "sv;
|
||||
break;
|
||||
case 4:
|
||||
log_type = "Error: "sv;
|
||||
break;
|
||||
case 5:
|
||||
log_type = "Fatal: "sv;
|
||||
break;
|
||||
case 0:
|
||||
log_type = "Verbose: "sv;
|
||||
break;
|
||||
case 1:
|
||||
log_type = "Debug: "sv;
|
||||
break;
|
||||
case 2:
|
||||
log_type = "Info: "sv;
|
||||
break;
|
||||
case 3:
|
||||
log_type = "Warning: "sv;
|
||||
break;
|
||||
case 4:
|
||||
log_type = "Error: "sv;
|
||||
break;
|
||||
case 5:
|
||||
log_type = "Fatal: "sv;
|
||||
break;
|
||||
};
|
||||
|
||||
char _date[DATE_BUFFER_SIZE];
|
||||
|
||||
@ -5,8 +5,8 @@
|
||||
#ifndef SUNSHINE_MAIN_H
|
||||
#define SUNSHINE_MAIN_H
|
||||
|
||||
#include <boost/log/common.hpp>
|
||||
#include "thread_pool.h"
|
||||
#include <boost/log/common.hpp>
|
||||
|
||||
extern util::ThreadPool task_pool;
|
||||
extern bool display_cursor;
|
||||
|
||||
@ -11,23 +11,24 @@ template<class T>
|
||||
class MoveByCopy {
|
||||
public:
|
||||
typedef T move_type;
|
||||
|
||||
private:
|
||||
move_type _to_move;
|
||||
public:
|
||||
|
||||
explicit MoveByCopy(move_type &&to_move) : _to_move(std::move(to_move)) { }
|
||||
public:
|
||||
explicit MoveByCopy(move_type &&to_move) : _to_move(std::move(to_move)) {}
|
||||
|
||||
MoveByCopy(MoveByCopy &&other) = default;
|
||||
|
||||
|
||||
MoveByCopy(const MoveByCopy &other) {
|
||||
*this = other;
|
||||
}
|
||||
|
||||
MoveByCopy& operator=(MoveByCopy &&other) = default;
|
||||
|
||||
MoveByCopy& operator=(const MoveByCopy &other) {
|
||||
this->_to_move = std::move(const_cast<MoveByCopy&>(other)._to_move);
|
||||
|
||||
MoveByCopy &operator=(MoveByCopy &&other) = default;
|
||||
|
||||
MoveByCopy &operator=(const MoveByCopy &other) {
|
||||
this->_to_move = std::move(const_cast<MoveByCopy &>(other)._to_move);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
@ -44,7 +45,7 @@ MoveByCopy<T> cmove(T &movable) {
|
||||
// Do NOT use this unless you are absolutely certain the object to be moved is no longer used by the caller
|
||||
template<class T>
|
||||
MoveByCopy<T> const_cmove(const T &movable) {
|
||||
return MoveByCopy<T>(std::move(const_cast<T&>(movable)));
|
||||
}
|
||||
return MoveByCopy<T>(std::move(const_cast<T &>(movable)));
|
||||
}
|
||||
} // namespace util
|
||||
#endif
|
||||
|
||||
@ -2,9 +2,9 @@
|
||||
// Created by loki on 12/27/19.
|
||||
//
|
||||
|
||||
#include <algorithm>
|
||||
#include "network.h"
|
||||
#include "utility.h"
|
||||
#include <algorithm>
|
||||
|
||||
namespace net {
|
||||
using namespace std::literals;
|
||||
@ -21,17 +21,17 @@ std::vector<std::tuple<std::uint32_t, std::uint32_t>> lan_ips {
|
||||
};
|
||||
|
||||
std::uint32_t ip(const std::string_view &ip_str) {
|
||||
auto begin = std::begin(ip_str);
|
||||
auto end = std::end(ip_str);
|
||||
auto begin = std::begin(ip_str);
|
||||
auto end = std::end(ip_str);
|
||||
auto temp_end = std::find(begin, end, '.');
|
||||
|
||||
std::uint32_t ip = 0;
|
||||
auto shift = 24;
|
||||
auto shift = 24;
|
||||
while(temp_end != end) {
|
||||
ip += (util::from_chars(begin, temp_end) << shift);
|
||||
shift -= 8;
|
||||
|
||||
begin = temp_end + 1;
|
||||
begin = temp_end + 1;
|
||||
temp_end = std::find(begin, end, '.');
|
||||
}
|
||||
|
||||
@ -43,7 +43,7 @@ std::uint32_t ip(const std::string_view &ip_str) {
|
||||
// In the format "xxx.xxx.xxx.xxx/x"
|
||||
std::pair<std::uint32_t, std::uint32_t> ip_block(const std::string_view &ip_str) {
|
||||
auto begin = std::begin(ip_str);
|
||||
auto end = std::find(begin, std::end(ip_str), '/');
|
||||
auto end = std::find(begin, std::end(ip_str), '/');
|
||||
|
||||
auto addr = ip({ begin, (std::size_t)(end - begin) });
|
||||
|
||||
@ -82,12 +82,12 @@ net_e from_address(const std::string_view &view) {
|
||||
|
||||
std::string_view to_enum_string(net_e net) {
|
||||
switch(net) {
|
||||
case PC:
|
||||
return "pc"sv;
|
||||
case LAN:
|
||||
return "lan"sv;
|
||||
case WAN:
|
||||
return "wan"sv;
|
||||
case PC:
|
||||
return "pc"sv;
|
||||
case LAN:
|
||||
return "lan"sv;
|
||||
case WAN:
|
||||
return "wan"sv;
|
||||
}
|
||||
|
||||
// avoid warning
|
||||
@ -112,4 +112,4 @@ void free_host(ENetHost *host) {
|
||||
|
||||
enet_host_destroy(host);
|
||||
}
|
||||
}
|
||||
} // namespace net
|
||||
@ -14,8 +14,8 @@
|
||||
namespace net {
|
||||
void free_host(ENetHost *host);
|
||||
|
||||
using host_t = util::safe_ptr<ENetHost, free_host>;
|
||||
using peer_t = ENetPeer*;
|
||||
using host_t = util::safe_ptr<ENetHost, free_host>;
|
||||
using peer_t = ENetPeer *;
|
||||
using packet_t = util::safe_ptr<ENetPacket, enet_packet_destroy>;
|
||||
|
||||
enum net_e : int {
|
||||
@ -30,6 +30,6 @@ std::string_view to_enum_string(net_e net);
|
||||
net_e from_address(const std::string_view &view);
|
||||
|
||||
host_t host_create(ENetAddress &addr, std::size_t peers, std::uint16_t port);
|
||||
}
|
||||
} // namespace net
|
||||
|
||||
#endif //SUNSHINE_NETWORK_H
|
||||
|
||||
@ -6,9 +6,9 @@
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
#include <boost/property_tree/json_parser.hpp>
|
||||
#include <boost/property_tree/ptree.hpp>
|
||||
#include <boost/property_tree/xml_parser.hpp>
|
||||
#include <boost/property_tree/json_parser.hpp>
|
||||
|
||||
#include <boost/asio/ssl/context.hpp>
|
||||
|
||||
@ -17,14 +17,14 @@
|
||||
#include <boost/asio/ssl/context_base.hpp>
|
||||
|
||||
#include "config.h"
|
||||
#include "utility.h"
|
||||
#include "rtsp.h"
|
||||
#include "crypto.h"
|
||||
#include "main.h"
|
||||
#include "network.h"
|
||||
#include "nvhttp.h"
|
||||
#include "platform/common.h"
|
||||
#include "network.h"
|
||||
#include "rtsp.h"
|
||||
#include "utility.h"
|
||||
#include "uuid.h"
|
||||
#include "main.h"
|
||||
|
||||
|
||||
namespace nvhttp {
|
||||
@ -69,8 +69,8 @@ struct pair_session_t {
|
||||
struct {
|
||||
util::Either<
|
||||
std::shared_ptr<typename SimpleWeb::ServerBase<SimpleWeb::HTTP>::Response>,
|
||||
std::shared_ptr<typename SimpleWeb::ServerBase<SimpleWeb::HTTPS>::Response>
|
||||
> response;
|
||||
std::shared_ptr<typename SimpleWeb::ServerBase<SimpleWeb::HTTPS>::Response>>
|
||||
response;
|
||||
std::string salt;
|
||||
} async_insert_pin;
|
||||
};
|
||||
@ -81,11 +81,11 @@ std::unordered_map<std::string, client_t> map_id_client;
|
||||
std::string unique_id;
|
||||
net::net_e origin_pin_allowed;
|
||||
|
||||
using args_t = SimpleWeb::CaseInsensitiveMultimap;
|
||||
using args_t = SimpleWeb::CaseInsensitiveMultimap;
|
||||
using resp_https_t = std::shared_ptr<typename SimpleWeb::ServerBase<SimpleWeb::HTTPS>::Response>;
|
||||
using req_https_t = std::shared_ptr<typename SimpleWeb::ServerBase<SimpleWeb::HTTPS>::Request>;
|
||||
using resp_http_t = std::shared_ptr<typename SimpleWeb::ServerBase<SimpleWeb::HTTP>::Response>;
|
||||
using req_http_t = std::shared_ptr<typename SimpleWeb::ServerBase<SimpleWeb::HTTP>::Request>;
|
||||
using req_https_t = std::shared_ptr<typename SimpleWeb::ServerBase<SimpleWeb::HTTPS>::Request>;
|
||||
using resp_http_t = std::shared_ptr<typename SimpleWeb::ServerBase<SimpleWeb::HTTP>::Response>;
|
||||
using req_http_t = std::shared_ptr<typename SimpleWeb::ServerBase<SimpleWeb::HTTP>::Request>;
|
||||
|
||||
enum class op_e {
|
||||
ADD,
|
||||
@ -97,7 +97,7 @@ void save_state() {
|
||||
|
||||
root.put("root.uniqueid", unique_id);
|
||||
auto &nodes = root.add_child("root.devices", pt::ptree {});
|
||||
for(auto &[_,client] : map_id_client) {
|
||||
for(auto &[_, client] : map_id_client) {
|
||||
pt::ptree node;
|
||||
|
||||
node.put("uniqueid"s, client.uniqueID);
|
||||
@ -127,17 +127,18 @@ void load_state() {
|
||||
pt::ptree root;
|
||||
try {
|
||||
pt::read_json(config::nvhttp.file_state, root);
|
||||
} catch (std::exception &e) {
|
||||
}
|
||||
catch(std::exception &e) {
|
||||
BOOST_LOG(warning) << e.what();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
unique_id = root.get<std::string>("root.uniqueid");
|
||||
unique_id = root.get<std::string>("root.uniqueid");
|
||||
auto device_nodes = root.get_child("root.devices");
|
||||
|
||||
for(auto &[_,device_node] : device_nodes) {
|
||||
auto uniqID = device_node.get<std::string>("uniqueid");
|
||||
for(auto &[_, device_node] : device_nodes) {
|
||||
auto uniqID = device_node.get<std::string>("uniqueid");
|
||||
auto &client = map_id_client.emplace(uniqID, client_t {}).first->second;
|
||||
|
||||
client.uniqueID = uniqID;
|
||||
@ -150,16 +151,14 @@ void load_state() {
|
||||
|
||||
void update_id_client(const std::string &uniqueID, std::string &&cert, op_e op) {
|
||||
switch(op) {
|
||||
case op_e::ADD:
|
||||
{
|
||||
auto &client = map_id_client[uniqueID];
|
||||
client.certs.emplace_back(std::move(cert));
|
||||
client.uniqueID = uniqueID;
|
||||
}
|
||||
break;
|
||||
case op_e::REMOVE:
|
||||
map_id_client.erase(uniqueID);
|
||||
break;
|
||||
case op_e::ADD: {
|
||||
auto &client = map_id_client[uniqueID];
|
||||
client.certs.emplace_back(std::move(cert));
|
||||
client.uniqueID = uniqueID;
|
||||
} break;
|
||||
case op_e::REMOVE:
|
||||
map_id_client.erase(uniqueID);
|
||||
break;
|
||||
}
|
||||
|
||||
if(!config::sunshine.flags[config::flag::FRESH_STATE]) {
|
||||
@ -175,10 +174,10 @@ void getservercert(pair_session_t &sess, pt::ptree &tree, const std::string &pin
|
||||
}
|
||||
|
||||
std::string_view salt_view { sess.async_insert_pin.salt.data(), 32 };
|
||||
|
||||
|
||||
auto salt = util::from_hex<std::array<uint8_t, 16>>(salt_view, true);
|
||||
|
||||
auto key = crypto::gen_aes_key(*salt, pin);
|
||||
auto key = crypto::gen_aes_key(*salt, pin);
|
||||
sess.cipher_key = std::make_unique<crypto::aes_t>(key);
|
||||
|
||||
tree.put("root.paired", 1);
|
||||
@ -197,7 +196,7 @@ void serverchallengeresp(pair_session_t &sess, pt::ptree &tree, const args_t &ar
|
||||
sess.clienthash = std::move(decrypted);
|
||||
|
||||
auto serversecret = sess.serversecret;
|
||||
auto sign = crypto::sign256(crypto::pkey(conf_intern.pkey), serversecret);
|
||||
auto sign = crypto::sign256(crypto::pkey(conf_intern.pkey), serversecret);
|
||||
|
||||
serversecret.insert(std::end(serversecret), std::begin(sign), std::end(sign));
|
||||
|
||||
@ -215,14 +214,14 @@ void clientchallenge(pair_session_t &sess, pt::ptree &tree, const args_t &args)
|
||||
std::vector<uint8_t> decrypted;
|
||||
cipher.decrypt(challenge, decrypted);
|
||||
|
||||
auto x509 = crypto::x509(conf_intern.servercert);
|
||||
auto sign = crypto::signature(x509);
|
||||
auto x509 = crypto::x509(conf_intern.servercert);
|
||||
auto sign = crypto::signature(x509);
|
||||
auto serversecret = crypto::rand(16);
|
||||
|
||||
decrypted.insert(std::end(decrypted), std::begin(sign), std::end(sign));
|
||||
decrypted.insert(std::end(decrypted), std::begin(serversecret), std::end(serversecret));
|
||||
|
||||
auto hash = crypto::hash({ (char*)decrypted.data(), decrypted.size() });
|
||||
auto hash = crypto::hash({ (char *)decrypted.data(), decrypted.size() });
|
||||
auto serverchallenge = crypto::rand(16);
|
||||
|
||||
std::string plaintext;
|
||||
@ -252,7 +251,7 @@ void clientpairingsecret(std::shared_ptr<safe::queue_t<crypto::x509_t>> &add_cer
|
||||
|
||||
assert((secret.size() + sign.size()) == pairingsecret.size());
|
||||
|
||||
auto x509 = crypto::x509(client.cert);
|
||||
auto x509 = crypto::x509(client.cert);
|
||||
auto x509_sign = crypto::signature(x509);
|
||||
|
||||
std::string data;
|
||||
@ -306,8 +305,8 @@ template<class T>
|
||||
void print_req(std::shared_ptr<typename SimpleWeb::ServerBase<T>::Request> request) {
|
||||
BOOST_LOG(debug) << "TUNNEL :: "sv << tunnel<T>::to_string;
|
||||
|
||||
BOOST_LOG(debug) << "METHOD :: "sv << request->method;
|
||||
BOOST_LOG(debug) << "DESTINATION :: "sv << request->path;
|
||||
BOOST_LOG(debug) << "METHOD :: "sv << request->method;
|
||||
BOOST_LOG(debug) << "DESTINATION :: "sv << request->path;
|
||||
|
||||
for(auto &[name, val] : request->header) {
|
||||
BOOST_LOG(debug) << name << " -- " << val;
|
||||
@ -334,7 +333,8 @@ void not_found(std::shared_ptr<typename SimpleWeb::ServerBase<T>::Response> resp
|
||||
pt::write_xml(data, tree);
|
||||
response->write(data.str());
|
||||
|
||||
*response << "HTTP/1.1 404 NOT FOUND\r\n" << data.str();
|
||||
*response << "HTTP/1.1 404 NOT FOUND\r\n"
|
||||
<< data.str();
|
||||
}
|
||||
|
||||
template<class T>
|
||||
@ -351,9 +351,9 @@ void pair(std::shared_ptr<safe::queue_t<crypto::x509_t>> &add_cert, std::shared_
|
||||
if(it = args.find("phrase"); it != std::end(args)) {
|
||||
if(it->second == "getservercert"sv) {
|
||||
pair_session_t sess;
|
||||
|
||||
|
||||
sess.client.uniqueID = std::move(uniqID);
|
||||
sess.client.cert = util::from_hex_vec(args.at("clientcert"s), true);
|
||||
sess.client.cert = util::from_hex_vec(args.at("clientcert"s), true);
|
||||
|
||||
BOOST_LOG(debug) << sess.client.cert;
|
||||
auto ptr = map_id_sess.emplace(sess.client.uniqueID, std::move(sess)).first;
|
||||
@ -435,7 +435,7 @@ void pin(std::shared_ptr<typename SimpleWeb::ServerBase<T>::Response> response,
|
||||
if(async_response.has_left() && async_response.left()) {
|
||||
async_response.left()->write(data.str());
|
||||
}
|
||||
else if(async_response.has_right() && async_response.right()){
|
||||
else if(async_response.has_right() && async_response.right()) {
|
||||
async_response.right()->write(data.str());
|
||||
}
|
||||
else {
|
||||
@ -455,13 +455,13 @@ void serverinfo(std::shared_ptr<typename SimpleWeb::ServerBase<T>::Response> res
|
||||
print_req<T>(request);
|
||||
|
||||
int pair_status = 0;
|
||||
if constexpr (std::is_same_v<SimpleWeb::HTTPS, T>) {
|
||||
auto args = request->parse_query_string();
|
||||
if constexpr(std::is_same_v<SimpleWeb::HTTPS, T>) {
|
||||
auto args = request->parse_query_string();
|
||||
auto clientID = args.find("uniqueid"s);
|
||||
|
||||
|
||||
if(clientID != std::end(args)) {
|
||||
if (auto it = map_id_client.find(clientID->second); it != std::end(map_id_client)) {
|
||||
if(auto it = map_id_client.find(clientID->second); it != std::end(map_id_client)) {
|
||||
pair_status = 1;
|
||||
}
|
||||
}
|
||||
@ -507,7 +507,7 @@ void serverinfo(std::shared_ptr<typename SimpleWeb::ServerBase<T>::Response> res
|
||||
void applist(resp_https_t response, req_https_t request) {
|
||||
print_req<SimpleWeb::HTTPS>(request);
|
||||
|
||||
auto args = request->parse_query_string();
|
||||
auto args = request->parse_query_string();
|
||||
auto clientID = args.at("uniqueid"s);
|
||||
|
||||
pt::ptree tree;
|
||||
@ -560,8 +560,8 @@ void launch(resp_https_t response, req_https_t request) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto args = request->parse_query_string();
|
||||
auto appid = util::from_view(args.at("appid")) -1;
|
||||
auto args = request->parse_query_string();
|
||||
auto appid = util::from_view(args.at("appid")) - 1;
|
||||
|
||||
auto current_appid = proc::proc.running();
|
||||
if(current_appid != -1) {
|
||||
@ -583,10 +583,10 @@ void launch(resp_https_t response, req_https_t request) {
|
||||
|
||||
stream::launch_session_t launch_session;
|
||||
|
||||
auto clientID = args.at("uniqueid"s);
|
||||
auto clientID = args.at("uniqueid"s);
|
||||
launch_session.gcm_key = *util::from_hex<crypto::aes_t>(args.at("rikey"s), true);
|
||||
uint32_t prepend_iv = util::endian::big<uint32_t>(util::from_view(args.at("rikeyid"s)));
|
||||
auto prepend_iv_p = (uint8_t*)&prepend_iv;
|
||||
uint32_t prepend_iv = util::endian::big<uint32_t>(util::from_view(args.at("rikeyid"s)));
|
||||
auto prepend_iv_p = (uint8_t *)&prepend_iv;
|
||||
|
||||
auto next = std::copy(prepend_iv_p, prepend_iv_p + sizeof(prepend_iv), std::begin(launch_session.iv));
|
||||
std::fill(next, std::end(launch_session.iv), 0);
|
||||
@ -627,11 +627,11 @@ void resume(resp_https_t response, req_https_t request) {
|
||||
|
||||
stream::launch_session_t launch_session;
|
||||
|
||||
auto args = request->parse_query_string();
|
||||
auto clientID = args.at("uniqueid"s);
|
||||
auto args = request->parse_query_string();
|
||||
auto clientID = args.at("uniqueid"s);
|
||||
launch_session.gcm_key = *util::from_hex<crypto::aes_t>(args.at("rikey"s), true);
|
||||
uint32_t prepend_iv = util::endian::big<uint32_t>(util::from_view(args.at("rikeyid"s)));
|
||||
auto prepend_iv_p = (uint8_t*)&prepend_iv;
|
||||
uint32_t prepend_iv = util::endian::big<uint32_t>(util::from_view(args.at("rikeyid"s)));
|
||||
auto prepend_iv_p = (uint8_t *)&prepend_iv;
|
||||
|
||||
auto next = std::copy(prepend_iv_p, prepend_iv_p + sizeof(prepend_iv), std::begin(launch_session.iv));
|
||||
std::fill(next, std::end(launch_session.iv), 0);
|
||||
@ -688,25 +688,25 @@ int create_creds(const std::string &pkey, const std::string &cert) {
|
||||
pkey_dir.remove_filename();
|
||||
cert_dir.remove_filename();
|
||||
|
||||
std::error_code err_code{};
|
||||
std::error_code err_code {};
|
||||
fs::create_directories(pkey_dir, err_code);
|
||||
if (err_code) {
|
||||
if(err_code) {
|
||||
BOOST_LOG(fatal) << "Couldn't create directory ["sv << pkey_dir << "] :"sv << err_code.message();
|
||||
return -1;
|
||||
}
|
||||
|
||||
fs::create_directories(cert_dir, err_code);
|
||||
if (err_code) {
|
||||
if(err_code) {
|
||||
BOOST_LOG(fatal) << "Couldn't create directory ["sv << cert_dir << "] :"sv << err_code.message();
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (write_file(pkey.c_str(), creds.pkey)) {
|
||||
if(write_file(pkey.c_str(), creds.pkey)) {
|
||||
BOOST_LOG(fatal) << "Couldn't open ["sv << config::nvhttp.pkey << ']';
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (write_file(cert.c_str(), creds.x509)) {
|
||||
if(write_file(cert.c_str(), creds.x509)) {
|
||||
BOOST_LOG(fatal) << "Couldn't open ["sv << config::nvhttp.cert << ']';
|
||||
return -1;
|
||||
}
|
||||
@ -715,7 +715,7 @@ int create_creds(const std::string &pkey, const std::string &cert) {
|
||||
fs::perms::owner_read | fs::perms::owner_write,
|
||||
fs::perm_options::replace, err_code);
|
||||
|
||||
if (err_code) {
|
||||
if(err_code) {
|
||||
BOOST_LOG(fatal) << "Couldn't change permissions of ["sv << config::nvhttp.pkey << "] :"sv << err_code.message();
|
||||
return -1;
|
||||
}
|
||||
@ -724,7 +724,7 @@ int create_creds(const std::string &pkey, const std::string &cert) {
|
||||
fs::perms::owner_read | fs::perms::group_read | fs::perms::others_read | fs::perms::owner_write,
|
||||
fs::perm_options::replace, err_code);
|
||||
|
||||
if (err_code) {
|
||||
if(err_code) {
|
||||
BOOST_LOG(fatal) << "Couldn't change permissions of ["sv << config::nvhttp.cert << "] :"sv << err_code.message();
|
||||
return -1;
|
||||
}
|
||||
@ -757,7 +757,7 @@ void start(std::shared_ptr<safe::signal_t> shutdown_event) {
|
||||
load_state();
|
||||
}
|
||||
|
||||
conf_intern.pkey = read_file(config::nvhttp.pkey.c_str());
|
||||
conf_intern.pkey = read_file(config::nvhttp.pkey.c_str());
|
||||
conf_intern.servercert = read_file(config::nvhttp.cert.c_str());
|
||||
|
||||
auto ctx = std::make_shared<boost::asio::ssl::context>(boost::asio::ssl::context::tls);
|
||||
@ -765,7 +765,7 @@ void start(std::shared_ptr<safe::signal_t> shutdown_event) {
|
||||
ctx->use_private_key_file(config::nvhttp.pkey, boost::asio::ssl::context::pem);
|
||||
|
||||
crypto::cert_chain_t cert_chain;
|
||||
for(auto &[_,client] : map_id_client) {
|
||||
for(auto &[_, client] : map_id_client) {
|
||||
for(auto &cert : client.certs) {
|
||||
cert_chain.add(crypto::x509(cert));
|
||||
}
|
||||
@ -813,33 +813,34 @@ void start(std::shared_ptr<safe::signal_t> shutdown_event) {
|
||||
https_server_t https_server { ctx, boost::asio::ssl::verify_peer | boost::asio::ssl::verify_fail_if_no_peer_cert | boost::asio::ssl::verify_client_once };
|
||||
http_server_t http_server;
|
||||
|
||||
https_server.default_resource = not_found<SimpleWeb::HTTPS>;
|
||||
https_server.resource["^/serverinfo$"]["GET"] = serverinfo<SimpleWeb::HTTPS>;
|
||||
https_server.resource["^/pair$"]["GET"] = [&add_cert](auto resp, auto req) { pair<SimpleWeb::HTTPS>(add_cert, resp, req); };
|
||||
https_server.resource["^/applist$"]["GET"] = applist;
|
||||
https_server.resource["^/appasset$"]["GET"] = appasset;
|
||||
https_server.resource["^/launch$"]["GET"] = launch;
|
||||
https_server.default_resource = not_found<SimpleWeb::HTTPS>;
|
||||
https_server.resource["^/serverinfo$"]["GET"] = serverinfo<SimpleWeb::HTTPS>;
|
||||
https_server.resource["^/pair$"]["GET"] = [&add_cert](auto resp, auto req) { pair<SimpleWeb::HTTPS>(add_cert, resp, req); };
|
||||
https_server.resource["^/applist$"]["GET"] = applist;
|
||||
https_server.resource["^/appasset$"]["GET"] = appasset;
|
||||
https_server.resource["^/launch$"]["GET"] = launch;
|
||||
https_server.resource["^/pin/([0-9]+)$"]["GET"] = pin<SimpleWeb::HTTPS>;
|
||||
https_server.resource["^/resume$"]["GET"] = resume;
|
||||
https_server.resource["^/cancel$"]["GET"] = cancel;
|
||||
https_server.resource["^/resume$"]["GET"] = resume;
|
||||
https_server.resource["^/cancel$"]["GET"] = cancel;
|
||||
|
||||
https_server.config.reuse_address = true;
|
||||
https_server.config.address = "0.0.0.0"s;
|
||||
https_server.config.port = PORT_HTTPS;
|
||||
https_server.config.address = "0.0.0.0"s;
|
||||
https_server.config.port = PORT_HTTPS;
|
||||
|
||||
http_server.default_resource = not_found<SimpleWeb::HTTP>;
|
||||
http_server.resource["^/serverinfo$"]["GET"] = serverinfo<SimpleWeb::HTTP>;
|
||||
http_server.resource["^/pair$"]["GET"] = [&add_cert](auto resp, auto req) { pair<SimpleWeb::HTTP>(add_cert, resp, req); };
|
||||
http_server.default_resource = not_found<SimpleWeb::HTTP>;
|
||||
http_server.resource["^/serverinfo$"]["GET"] = serverinfo<SimpleWeb::HTTP>;
|
||||
http_server.resource["^/pair$"]["GET"] = [&add_cert](auto resp, auto req) { pair<SimpleWeb::HTTP>(add_cert, resp, req); };
|
||||
http_server.resource["^/pin/([0-9]+)$"]["GET"] = pin<SimpleWeb::HTTP>;
|
||||
|
||||
http_server.config.reuse_address = true;
|
||||
http_server.config.address = "0.0.0.0"s;
|
||||
http_server.config.port = PORT_HTTP;
|
||||
http_server.config.address = "0.0.0.0"s;
|
||||
http_server.config.port = PORT_HTTP;
|
||||
|
||||
try {
|
||||
https_server.bind();
|
||||
http_server.bind();
|
||||
} catch(boost::system::system_error &err) {
|
||||
}
|
||||
catch(boost::system::system_error &err) {
|
||||
BOOST_LOG(fatal) << "Couldn't bind http server to ports ["sv << PORT_HTTPS << ", "sv << PORT_HTTP << "]: "sv << err.what();
|
||||
|
||||
shutdown_event->raise(true);
|
||||
@ -849,7 +850,8 @@ void start(std::shared_ptr<safe::signal_t> shutdown_event) {
|
||||
auto accept_and_run = [&](auto *http_server) {
|
||||
try {
|
||||
http_server->accept_and_run();
|
||||
} catch(boost::system::system_error &err) {
|
||||
}
|
||||
catch(boost::system::system_error &err) {
|
||||
// It's possible the exception gets thrown after calling http_server->stop() from a different thread
|
||||
if(shutdown_event->peek()) {
|
||||
return;
|
||||
@ -899,4 +901,4 @@ std::string read_file(const char *path) {
|
||||
|
||||
return base64_cert;
|
||||
}
|
||||
}
|
||||
} // namespace nvhttp
|
||||
|
||||
@ -11,8 +11,8 @@
|
||||
#include "thread_safe.h"
|
||||
|
||||
#define CA_DIR SUNSHINE_ASSETS_DIR "/demoCA"
|
||||
#define PRIVATE_KEY_FILE CA_DIR "/cakey.pem"
|
||||
#define CERTIFICATE_FILE CA_DIR "/cacert.pem"
|
||||
#define PRIVATE_KEY_FILE CA_DIR "/cakey.pem"
|
||||
#define CERTIFICATE_FILE CA_DIR "/cacert.pem"
|
||||
|
||||
namespace nvhttp {
|
||||
void start(std::shared_ptr<safe::signal_t> shutdown_event);
|
||||
|
||||
@ -5,9 +5,9 @@
|
||||
#ifndef SUNSHINE_COMMON_H
|
||||
#define SUNSHINE_COMMON_H
|
||||
|
||||
#include <string>
|
||||
#include <mutex>
|
||||
#include "sunshine/utility.h"
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
|
||||
struct sockaddr;
|
||||
namespace platf {
|
||||
@ -44,8 +44,10 @@ enum class pix_fmt_e {
|
||||
};
|
||||
|
||||
inline std::string_view from_pix_fmt(pix_fmt_e pix_fmt) {
|
||||
using namespace std::literals;
|
||||
#define _CONVERT(x) case pix_fmt_e:: x : return #x ## sv
|
||||
using namespace std::literals;
|
||||
#define _CONVERT(x) \
|
||||
case pix_fmt_e::x: \
|
||||
return #x##sv
|
||||
switch(pix_fmt) {
|
||||
_CONVERT(yuv420p);
|
||||
_CONVERT(yuv420p10);
|
||||
@ -65,9 +67,8 @@ struct touch_port_t {
|
||||
|
||||
constexpr touch_port_t(
|
||||
std::uint32_t offset_x, std::uint32_t offset_y,
|
||||
std::uint32_t width, std::uint32_t height) noexcept :
|
||||
offset_x { offset_x }, offset_y { offset_y },
|
||||
width { width }, height { height } {};
|
||||
std::uint32_t width, std::uint32_t height) noexcept : offset_x { offset_x }, offset_y { offset_y },
|
||||
width { width }, height { height } {};
|
||||
};
|
||||
|
||||
struct gamepad_state_t {
|
||||
@ -87,15 +88,15 @@ public:
|
||||
|
||||
struct img_t {
|
||||
public:
|
||||
std::uint8_t *data {};
|
||||
std::int32_t width {};
|
||||
std::uint8_t *data {};
|
||||
std::int32_t width {};
|
||||
std::int32_t height {};
|
||||
std::int32_t pixel_pitch {};
|
||||
std::int32_t row_pitch {};
|
||||
|
||||
img_t() = default;
|
||||
img_t(const img_t&) = delete;
|
||||
img_t(img_t&&) = delete;
|
||||
img_t() = default;
|
||||
img_t(const img_t &) = delete;
|
||||
img_t(img_t &&) = delete;
|
||||
|
||||
virtual ~img_t() = default;
|
||||
};
|
||||
@ -124,7 +125,7 @@ class display_t {
|
||||
public:
|
||||
display_t() noexcept : offset_x { 0 }, offset_y { 0 } {}
|
||||
virtual capture_e snapshot(img_t *img, std::chrono::milliseconds timeout, bool cursor) = 0;
|
||||
virtual std::shared_ptr<img_t> alloc_img() = 0;
|
||||
virtual std::shared_ptr<img_t> alloc_img() = 0;
|
||||
|
||||
virtual int dummy_img(img_t *img) = 0;
|
||||
|
||||
@ -148,7 +149,7 @@ public:
|
||||
};
|
||||
|
||||
|
||||
void freeInput(void*);
|
||||
void freeInput(void *);
|
||||
|
||||
using input_t = util::safe_ptr<void, freeInput>;
|
||||
|
||||
@ -172,6 +173,6 @@ int alloc_gamepad(input_t &input, int nr);
|
||||
void free_gamepad(input_t &input, int nr);
|
||||
|
||||
[[nodiscard]] std::unique_ptr<deinit_t> init();
|
||||
}
|
||||
} // namespace platf
|
||||
|
||||
#endif //SUNSHINE_COMMON_H
|
||||
|
||||
@ -4,8 +4,8 @@
|
||||
|
||||
#include "sunshine/platform/common.h"
|
||||
|
||||
#include <fstream>
|
||||
#include <bitset>
|
||||
#include <fstream>
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <ifaddrs.h>
|
||||
@ -15,36 +15,35 @@
|
||||
#include <X11/Xutil.h>
|
||||
#include <X11/extensions/Xfixes.h>
|
||||
#include <X11/extensions/Xrandr.h>
|
||||
#include <xcb/shm.h>
|
||||
#include <xcb/xfixes.h>
|
||||
#include <sys/ipc.h>
|
||||
#include <sys/shm.h>
|
||||
#include <xcb/shm.h>
|
||||
#include <xcb/xfixes.h>
|
||||
|
||||
#include <pulse/simple.h>
|
||||
#include <pulse/error.h>
|
||||
#include <pulse/simple.h>
|
||||
|
||||
#include "sunshine/task_pool.h"
|
||||
#include "sunshine/config.h"
|
||||
#include "sunshine/main.h"
|
||||
#include "sunshine/task_pool.h"
|
||||
|
||||
namespace platf
|
||||
{
|
||||
namespace platf {
|
||||
using namespace std::literals;
|
||||
|
||||
void freeImage(XImage*);
|
||||
void freeX(XFixesCursorImage*);
|
||||
void freeImage(XImage *);
|
||||
void freeX(XFixesCursorImage *);
|
||||
|
||||
using ifaddr_t = util::safe_ptr<ifaddrs, freeifaddrs>;
|
||||
using ifaddr_t = util::safe_ptr<ifaddrs, freeifaddrs>;
|
||||
using xcb_connect_t = util::safe_ptr<xcb_connection_t, xcb_disconnect>;
|
||||
using xcb_img_t = util::c_ptr<xcb_shm_get_image_reply_t>;
|
||||
using xcb_img_t = util::c_ptr<xcb_shm_get_image_reply_t>;
|
||||
|
||||
using xdisplay_t = util::safe_ptr_v2<Display, int, XCloseDisplay>;
|
||||
using ximg_t = util::safe_ptr<XImage, freeImage>;
|
||||
using xcursor_t = util::safe_ptr<XFixesCursorImage, freeX>;
|
||||
using ximg_t = util::safe_ptr<XImage, freeImage>;
|
||||
using xcursor_t = util::safe_ptr<XFixesCursorImage, freeX>;
|
||||
|
||||
using crtc_info_t = util::safe_ptr<_XRRCrtcInfo, XRRFreeCrtcInfo>;
|
||||
using crtc_info_t = util::safe_ptr<_XRRCrtcInfo, XRRFreeCrtcInfo>;
|
||||
using output_info_t = util::safe_ptr<_XRROutputInfo, XRRFreeOutputInfo>;
|
||||
using screen_res_t = util::safe_ptr<_XRRScreenResources, XRRFreeScreenResources>;
|
||||
using screen_res_t = util::safe_ptr<_XRRScreenResources, XRRFreeScreenResources>;
|
||||
|
||||
class shm_id_t {
|
||||
public:
|
||||
@ -55,7 +54,7 @@ public:
|
||||
}
|
||||
|
||||
~shm_id_t() {
|
||||
if (id != -1) {
|
||||
if(id != -1) {
|
||||
shmctl(id, IPC_RMID, nullptr);
|
||||
id = -1;
|
||||
}
|
||||
@ -65,15 +64,15 @@ public:
|
||||
|
||||
class shm_data_t {
|
||||
public:
|
||||
shm_data_t() : data { (void*) -1 } {}
|
||||
shm_data_t() : data { (void *)-1 } {}
|
||||
shm_data_t(void *data) : data { data } {}
|
||||
|
||||
shm_data_t(shm_data_t &&other) noexcept : data(other.data) {
|
||||
other.data = (void*)-1;
|
||||
other.data = (void *)-1;
|
||||
}
|
||||
|
||||
~shm_data_t() {
|
||||
if((std::uintptr_t) data != -1) {
|
||||
if((std::uintptr_t)data != -1) {
|
||||
shmdt(data);
|
||||
}
|
||||
}
|
||||
@ -81,11 +80,11 @@ public:
|
||||
void *data;
|
||||
};
|
||||
|
||||
struct x11_img_t: public img_t {
|
||||
struct x11_img_t : public img_t {
|
||||
ximg_t img;
|
||||
};
|
||||
|
||||
struct shm_img_t: public img_t {
|
||||
struct shm_img_t : public img_t {
|
||||
~shm_img_t() override {
|
||||
delete[] data;
|
||||
data = nullptr;
|
||||
@ -95,7 +94,7 @@ struct shm_img_t: public img_t {
|
||||
void blend_cursor(Display *display, img_t &img, int offsetX, int offsetY) {
|
||||
xcursor_t overlay { XFixesGetCursorImage(display) };
|
||||
|
||||
if (!overlay) {
|
||||
if(!overlay) {
|
||||
BOOST_LOG(error) << "Couldn't get cursor from XFixesGetCursorImage"sv;
|
||||
return;
|
||||
}
|
||||
@ -109,40 +108,39 @@ void blend_cursor(Display *display, img_t &img, int offsetX, int offsetY) {
|
||||
overlay->x = std::max((short)0, overlay->x);
|
||||
overlay->y = std::max((short)0, overlay->y);
|
||||
|
||||
auto pixels = (int*)img.data;
|
||||
auto pixels = (int *)img.data;
|
||||
|
||||
auto screen_height = img.height;
|
||||
auto screen_width = img.width;
|
||||
auto screen_width = img.width;
|
||||
|
||||
auto delta_height = std::min<uint16_t>(overlay->height,std::max(0, screen_height - overlay->y));
|
||||
auto delta_width = std::min<uint16_t>(overlay->width,std::max(0, screen_width - overlay->x));
|
||||
for (auto y = 0; y < delta_height; ++y) {
|
||||
auto delta_height = std::min<uint16_t>(overlay->height, std::max(0, screen_height - overlay->y));
|
||||
auto delta_width = std::min<uint16_t>(overlay->width, std::max(0, screen_width - overlay->x));
|
||||
for(auto y = 0; y < delta_height; ++y) {
|
||||
auto overlay_begin = &overlay->pixels[y * overlay->width];
|
||||
auto overlay_end = &overlay->pixels[y * overlay->width + delta_width];
|
||||
auto overlay_end = &overlay->pixels[y * overlay->width + delta_width];
|
||||
|
||||
auto pixels_begin = &pixels[(y + overlay->y)* (img.row_pitch / img.pixel_pitch) + overlay->x];
|
||||
auto pixels_begin = &pixels[(y + overlay->y) * (img.row_pitch / img.pixel_pitch) + overlay->x];
|
||||
|
||||
std::for_each(overlay_begin, overlay_end,[&](long pixel) {
|
||||
int *pixel_p = (int*) &pixel;
|
||||
std::for_each(overlay_begin, overlay_end, [&](long pixel) {
|
||||
int *pixel_p = (int *)&pixel;
|
||||
|
||||
auto colors_in = (uint8_t*) pixels_begin;
|
||||
auto colors_in = (uint8_t *)pixels_begin;
|
||||
|
||||
auto alpha = (*(uint*) pixel_p) >> 24u;
|
||||
if (alpha == 255) {
|
||||
auto alpha = (*(uint *)pixel_p) >> 24u;
|
||||
if(alpha == 255) {
|
||||
*pixels_begin = *pixel_p;
|
||||
}
|
||||
else {
|
||||
auto colors_out = (uint8_t*) pixel_p;
|
||||
colors_in[0] = colors_out[0] + (colors_in[0] * (255 - alpha) +255 /2) / 255;
|
||||
colors_in[1] = colors_out[1] + (colors_in[1] * (255 - alpha) + 255 / 2) / 255;
|
||||
colors_in[2] = colors_out[2] + (colors_in[2] * (255 - alpha) + 255 / 2) / 255;
|
||||
auto colors_out = (uint8_t *)pixel_p;
|
||||
colors_in[0] = colors_out[0] + (colors_in[0] * (255 - alpha) + 255 / 2) / 255;
|
||||
colors_in[1] = colors_out[1] + (colors_in[1] * (255 - alpha) + 255 / 2) / 255;
|
||||
colors_in[2] = colors_out[2] + (colors_in[2] * (255 - alpha) + 255 / 2) / 255;
|
||||
}
|
||||
++pixels_begin;
|
||||
});
|
||||
}
|
||||
}
|
||||
struct x11_attr_t: public display_t
|
||||
{
|
||||
struct x11_attr_t : public display_t {
|
||||
xdisplay_t xdisplay;
|
||||
Window xwindow;
|
||||
XWindowAttributes xattr;
|
||||
@ -153,7 +151,7 @@ struct x11_attr_t: public display_t
|
||||
*/
|
||||
int lastWidth, lastHeight;
|
||||
|
||||
x11_attr_t() : xdisplay { XOpenDisplay(nullptr) }, xwindow { }, xattr { } {
|
||||
x11_attr_t() : xdisplay { XOpenDisplay(nullptr) }, xwindow {}, xattr {} {
|
||||
XInitThreads();
|
||||
}
|
||||
|
||||
@ -192,17 +190,17 @@ struct x11_attr_t: public display_t
|
||||
BOOST_LOG(info)
|
||||
<< "Streaming display: "sv << out_info->name << " with res "sv << crt_info->width << 'x' << crt_info->height << " offset by "sv << crt_info->x << 'x' << crt_info->y;
|
||||
|
||||
width = crt_info->width;
|
||||
height = crt_info->height;
|
||||
width = crt_info->width;
|
||||
height = crt_info->height;
|
||||
offset_x = crt_info->x;
|
||||
offset_y = crt_info->y;
|
||||
}
|
||||
else {
|
||||
width = xattr.width;
|
||||
width = xattr.width;
|
||||
height = xattr.height;
|
||||
}
|
||||
|
||||
lastWidth = xattr.width;
|
||||
lastWidth = xattr.width;
|
||||
lastHeight = xattr.height;
|
||||
|
||||
return 0;
|
||||
@ -219,21 +217,21 @@ struct x11_attr_t: public display_t
|
||||
refresh();
|
||||
|
||||
//The whole X server changed, so we gotta reinit everything
|
||||
if (xattr.width != lastWidth || xattr.height != lastHeight) {
|
||||
BOOST_LOG(warning)<< "X dimensions changed in non-SHM mode, request reinit"sv;
|
||||
if(xattr.width != lastWidth || xattr.height != lastHeight) {
|
||||
BOOST_LOG(warning) << "X dimensions changed in non-SHM mode, request reinit"sv;
|
||||
return capture_e::reinit;
|
||||
}
|
||||
XImage *img { XGetImage(xdisplay.get(), xwindow, offset_x, offset_y, width, height, AllPlanes, ZPixmap) };
|
||||
|
||||
auto img_out = (x11_img_t*) img_out_base;
|
||||
img_out->width = img->width;
|
||||
img_out->height = img->height;
|
||||
img_out->data = (uint8_t*) img->data;
|
||||
img_out->row_pitch = img->bytes_per_line;
|
||||
auto img_out = (x11_img_t *)img_out_base;
|
||||
img_out->width = img->width;
|
||||
img_out->height = img->height;
|
||||
img_out->data = (uint8_t *)img->data;
|
||||
img_out->row_pitch = img->bytes_per_line;
|
||||
img_out->pixel_pitch = img->bits_per_pixel / 8;
|
||||
img_out->img.reset(img);
|
||||
|
||||
if (cursor) {
|
||||
if(cursor) {
|
||||
blend_cursor(xdisplay.get(), *img_out_base, offset_x, offset_y);
|
||||
}
|
||||
|
||||
@ -250,7 +248,7 @@ struct x11_attr_t: public display_t
|
||||
}
|
||||
};
|
||||
|
||||
struct shm_attr_t: public x11_attr_t {
|
||||
struct shm_attr_t : public x11_attr_t {
|
||||
xdisplay_t shm_xdisplay; // Prevent race condition with x11_attr_t::xdisplay
|
||||
xcb_connect_t xcb;
|
||||
xcb_screen_t *display;
|
||||
@ -265,7 +263,7 @@ struct shm_attr_t: public x11_attr_t {
|
||||
void delayed_refresh() {
|
||||
refresh();
|
||||
|
||||
refresh_task_id = task_pool.pushDelayed(&shm_attr_t::delayed_refresh,2s, this).task_id;
|
||||
refresh_task_id = task_pool.pushDelayed(&shm_attr_t::delayed_refresh, 2s, this).task_id;
|
||||
}
|
||||
|
||||
shm_attr_t() : x11_attr_t(), shm_xdisplay { XOpenDisplay(nullptr) } {
|
||||
@ -273,25 +271,26 @@ struct shm_attr_t: public x11_attr_t {
|
||||
}
|
||||
|
||||
~shm_attr_t() override {
|
||||
while(!task_pool.cancel(refresh_task_id));
|
||||
while(!task_pool.cancel(refresh_task_id))
|
||||
;
|
||||
}
|
||||
|
||||
capture_e snapshot(img_t *img, std::chrono::milliseconds timeout, bool cursor) override {
|
||||
//The whole X server changed, so we gotta reinit everything
|
||||
if(xattr.width != lastWidth || xattr.height != lastHeight) {
|
||||
BOOST_LOG(warning)<< "X dimensions changed in SHM mode, request reinit"sv;
|
||||
if(xattr.width != lastWidth || xattr.height != lastHeight) {
|
||||
BOOST_LOG(warning) << "X dimensions changed in SHM mode, request reinit"sv;
|
||||
return capture_e::reinit;
|
||||
}
|
||||
else {
|
||||
auto img_cookie = xcb_shm_get_image_unchecked(xcb.get(), display->root, offset_x, offset_y, width, height, ~0, XCB_IMAGE_FORMAT_Z_PIXMAP, seg, 0);
|
||||
|
||||
xcb_img_t img_reply { xcb_shm_get_image_reply(xcb.get(), img_cookie,nullptr) };
|
||||
xcb_img_t img_reply { xcb_shm_get_image_reply(xcb.get(), img_cookie, nullptr) };
|
||||
if(!img_reply) {
|
||||
BOOST_LOG(error) << "Could not get image reply"sv;
|
||||
return capture_e::reinit;
|
||||
}
|
||||
|
||||
std::copy_n((std::uint8_t*)data.data, frame_size(), img->data);
|
||||
std::copy_n((std::uint8_t *)data.data, frame_size(), img->data);
|
||||
|
||||
if(cursor) {
|
||||
blend_cursor(shm_xdisplay.get(), *img, offset_x, offset_y);
|
||||
@ -302,12 +301,12 @@ struct shm_attr_t: public x11_attr_t {
|
||||
}
|
||||
|
||||
std::shared_ptr<img_t> alloc_img() override {
|
||||
auto img = std::make_shared<shm_img_t>();
|
||||
img->width = width;
|
||||
img->height = height;
|
||||
auto img = std::make_shared<shm_img_t>();
|
||||
img->width = width;
|
||||
img->height = height;
|
||||
img->pixel_pitch = 4;
|
||||
img->row_pitch = img->pixel_pitch * width;
|
||||
img->data = new std::uint8_t[height * img->row_pitch];
|
||||
img->row_pitch = img->pixel_pitch * width;
|
||||
img->data = new std::uint8_t[height * img->row_pitch];
|
||||
|
||||
return img;
|
||||
}
|
||||
@ -334,8 +333,8 @@ struct shm_attr_t: public x11_attr_t {
|
||||
}
|
||||
|
||||
auto iter = xcb_setup_roots_iterator(xcb_get_setup(xcb.get()));
|
||||
display = iter.data;
|
||||
seg = xcb_generate_id(xcb.get());
|
||||
display = iter.data;
|
||||
seg = xcb_generate_id(xcb.get());
|
||||
|
||||
shm_id.id = shmget(IPC_PRIVATE, frame_size(), IPC_CREAT | 0777);
|
||||
if(shm_id.id == -1) {
|
||||
@ -360,19 +359,19 @@ struct shm_attr_t: public x11_attr_t {
|
||||
}
|
||||
};
|
||||
|
||||
struct mic_attr_t: public mic_t {
|
||||
struct mic_attr_t : public mic_t {
|
||||
pa_sample_spec ss;
|
||||
util::safe_ptr<pa_simple, pa_simple_free> mic;
|
||||
|
||||
explicit mic_attr_t(pa_sample_format format, std::uint32_t sample_rate,
|
||||
std::uint8_t channels) : ss { format, sample_rate, channels }, mic { } { }
|
||||
std::uint8_t channels) : ss { format, sample_rate, channels }, mic {} {}
|
||||
|
||||
capture_e sample(std::vector<std::int16_t> &sample_buf) override {
|
||||
auto sample_size = sample_buf.size();
|
||||
|
||||
auto buf = sample_buf.data();
|
||||
int status;
|
||||
if(pa_simple_read(mic.get(), buf, sample_size *2, &status)) {
|
||||
if(pa_simple_read(mic.get(), buf, sample_size * 2, &status)) {
|
||||
BOOST_LOG(error) << "pa_simple_read() failed: "sv << pa_strerror(status);
|
||||
|
||||
return capture_e::error;
|
||||
@ -384,7 +383,7 @@ struct mic_attr_t: public mic_t {
|
||||
|
||||
std::shared_ptr<display_t> display(platf::dev_type_e hwdevice_type) {
|
||||
if(hwdevice_type != platf::dev_type_e::none) {
|
||||
BOOST_LOG(error)<< "Could not initialize display with the given hw device type."sv;
|
||||
BOOST_LOG(error) << "Could not initialize display with the given hw device type."sv;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@ -416,16 +415,16 @@ std::unique_ptr<mic_t> microphone(std::uint32_t sample_rate, std::uint32_t) {
|
||||
int status;
|
||||
|
||||
const char *audio_sink = "@DEFAULT_MONITOR@";
|
||||
if (!config::audio.sink.empty()) {
|
||||
if(!config::audio.sink.empty()) {
|
||||
audio_sink = config::audio.sink.c_str();
|
||||
}
|
||||
|
||||
mic->mic.reset(
|
||||
pa_simple_new(nullptr, "sunshine",
|
||||
pa_stream_direction_t::PA_STREAM_RECORD, audio_sink,
|
||||
pa_stream_direction_t::PA_STREAM_RECORD, audio_sink,
|
||||
"sunshine-record", &mic->ss, nullptr, nullptr, &status));
|
||||
|
||||
if (!mic->mic) {
|
||||
if(!mic->mic) {
|
||||
auto err_str = pa_strerror(status);
|
||||
BOOST_LOG(error) << "pa_simple_new() failed: "sv << err_str;
|
||||
|
||||
@ -448,14 +447,14 @@ std::string from_sockaddr(const sockaddr *const ip_addr) {
|
||||
char data[INET6_ADDRSTRLEN];
|
||||
|
||||
auto family = ip_addr->sa_family;
|
||||
if (family == AF_INET6) {
|
||||
inet_ntop(AF_INET6, &((sockaddr_in6*) ip_addr)->sin6_addr, data,
|
||||
INET6_ADDRSTRLEN);
|
||||
if(family == AF_INET6) {
|
||||
inet_ntop(AF_INET6, &((sockaddr_in6 *)ip_addr)->sin6_addr, data,
|
||||
INET6_ADDRSTRLEN);
|
||||
}
|
||||
|
||||
if (family == AF_INET) {
|
||||
inet_ntop(AF_INET, &((sockaddr_in*) ip_addr)->sin_addr, data,
|
||||
INET_ADDRSTRLEN);
|
||||
if(family == AF_INET) {
|
||||
inet_ntop(AF_INET, &((sockaddr_in *)ip_addr)->sin_addr, data,
|
||||
INET_ADDRSTRLEN);
|
||||
}
|
||||
|
||||
return std::string { data };
|
||||
@ -467,26 +466,26 @@ std::pair<std::uint16_t, std::string> from_sockaddr_ex(const sockaddr *const ip_
|
||||
auto family = ip_addr->sa_family;
|
||||
std::uint16_t port;
|
||||
if(family == AF_INET6) {
|
||||
inet_ntop(AF_INET6, &((sockaddr_in6*)ip_addr)->sin6_addr, data,
|
||||
INET6_ADDRSTRLEN);
|
||||
port = ((sockaddr_in6*) ip_addr)->sin6_port;
|
||||
inet_ntop(AF_INET6, &((sockaddr_in6 *)ip_addr)->sin6_addr, data,
|
||||
INET6_ADDRSTRLEN);
|
||||
port = ((sockaddr_in6 *)ip_addr)->sin6_port;
|
||||
}
|
||||
|
||||
if(family == AF_INET) {
|
||||
inet_ntop(AF_INET, &((sockaddr_in*)ip_addr)->sin_addr, data,
|
||||
INET_ADDRSTRLEN);
|
||||
port = ((sockaddr_in*) ip_addr)->sin_port;
|
||||
inet_ntop(AF_INET, &((sockaddr_in *)ip_addr)->sin_addr, data,
|
||||
INET_ADDRSTRLEN);
|
||||
port = ((sockaddr_in *)ip_addr)->sin_port;
|
||||
}
|
||||
|
||||
return { port, std::string {data} };
|
||||
return { port, std::string { data } };
|
||||
}
|
||||
|
||||
std::string get_mac_address(const std::string_view &address) {
|
||||
auto ifaddrs = get_ifaddrs();
|
||||
for (auto pos = ifaddrs.get(); pos != nullptr; pos = pos->ifa_next) {
|
||||
if (pos->ifa_addr && address == from_sockaddr(pos->ifa_addr)) {
|
||||
for(auto pos = ifaddrs.get(); pos != nullptr; pos = pos->ifa_next) {
|
||||
if(pos->ifa_addr && address == from_sockaddr(pos->ifa_addr)) {
|
||||
std::ifstream mac_file("/sys/class/net/"s + pos->ifa_name + "/address");
|
||||
if (mac_file.good()) {
|
||||
if(mac_file.good()) {
|
||||
std::string mac_address;
|
||||
std::getline(mac_file, mac_address);
|
||||
return mac_address;
|
||||
@ -504,4 +503,4 @@ void freeImage(XImage *p) {
|
||||
void freeX(XFixesCursorImage *p) {
|
||||
XFree(p);
|
||||
}
|
||||
}
|
||||
} // namespace platf
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
#include <libevdev/libevdev.h>
|
||||
#include <libevdev/libevdev-uinput.h>
|
||||
#include <libevdev/libevdev.h>
|
||||
|
||||
#include <X11/X.h>
|
||||
#include <X11/Xlib.h>
|
||||
@ -10,8 +10,8 @@
|
||||
#include <cstring>
|
||||
#include <filesystem>
|
||||
|
||||
#include "sunshine/platform/common.h"
|
||||
#include "sunshine/main.h"
|
||||
#include "sunshine/platform/common.h"
|
||||
#include "sunshine/utility.h"
|
||||
|
||||
// Support older versions
|
||||
@ -25,7 +25,7 @@
|
||||
|
||||
namespace platf {
|
||||
using namespace std::literals;
|
||||
using evdev_t = util::safe_ptr<libevdev, libevdev_free>;
|
||||
using evdev_t = util::safe_ptr<libevdev, libevdev_free>;
|
||||
using uinput_t = util::safe_ptr<libevdev_uinput, libevdev_uinput_destroy>;
|
||||
|
||||
using keyboard_t = util::safe_ptr_v2<Display, int, XCloseDisplay>;
|
||||
@ -66,7 +66,7 @@ public:
|
||||
std::filesystem::remove(gamepad_path);
|
||||
}
|
||||
|
||||
gamepads[nr] = std::make_pair(uinput_t{}, gamepad_state_t {});
|
||||
gamepads[nr] = std::make_pair(uinput_t {}, gamepad_state_t {});
|
||||
}
|
||||
|
||||
int create_mouse() {
|
||||
@ -143,7 +143,7 @@ public:
|
||||
};
|
||||
|
||||
void abs_mouse(input_t &input, const touch_port_t &touch_port, float x, float y) {
|
||||
auto touchscreen = ((input_raw_t*)input.get())->touch_input.get();
|
||||
auto touchscreen = ((input_raw_t *)input.get())->touch_input.get();
|
||||
|
||||
auto scaled_x = (int)std::lround((x + touch_port.offset_x) * ((float)target_touch_port.width / (float)touch_port.width));
|
||||
auto scaled_y = (int)std::lround((y + touch_port.offset_y) * ((float)target_touch_port.height / (float)touch_port.height));
|
||||
@ -157,7 +157,7 @@ void abs_mouse(input_t &input, const touch_port_t &touch_port, float x, float y)
|
||||
}
|
||||
|
||||
void move_mouse(input_t &input, int deltaX, int deltaY) {
|
||||
auto mouse = ((input_raw_t*)input.get())->mouse_input.get();
|
||||
auto mouse = ((input_raw_t *)input.get())->mouse_input.get();
|
||||
|
||||
if(deltaX) {
|
||||
libevdev_uinput_write_event(mouse, EV_REL, REL_X, deltaX);
|
||||
@ -176,26 +176,26 @@ void button_mouse(input_t &input, int button, bool release) {
|
||||
|
||||
if(button == 1) {
|
||||
btn_type = BTN_LEFT;
|
||||
scan = 90001;
|
||||
scan = 90001;
|
||||
}
|
||||
else if(button == 2) {
|
||||
btn_type = BTN_MIDDLE;
|
||||
scan = 90003;
|
||||
scan = 90003;
|
||||
}
|
||||
else if(button == 3) {
|
||||
btn_type = BTN_RIGHT;
|
||||
scan = 90002;
|
||||
scan = 90002;
|
||||
}
|
||||
else if(button == 4) {
|
||||
btn_type = BTN_SIDE;
|
||||
scan = 90004;
|
||||
scan = 90004;
|
||||
}
|
||||
else {
|
||||
btn_type = BTN_EXTRA;
|
||||
scan = 90005;
|
||||
scan = 90005;
|
||||
}
|
||||
|
||||
auto mouse = ((input_raw_t*)input.get())->mouse_input.get();
|
||||
auto mouse = ((input_raw_t *)input.get())->mouse_input.get();
|
||||
libevdev_uinput_write_event(mouse, EV_MSC, MSC_SCAN, scan);
|
||||
libevdev_uinput_write_event(mouse, EV_KEY, btn_type, release ? 0 : 1);
|
||||
libevdev_uinput_write_event(mouse, EV_SYN, SYN_REPORT, 0);
|
||||
@ -204,7 +204,7 @@ void button_mouse(input_t &input, int button, bool release) {
|
||||
void scroll(input_t &input, int high_res_distance) {
|
||||
int distance = high_res_distance / 120;
|
||||
|
||||
auto mouse = ((input_raw_t*)input.get())->mouse_input.get();
|
||||
auto mouse = ((input_raw_t *)input.get())->mouse_input.get();
|
||||
libevdev_uinput_write_event(mouse, EV_REL, REL_WHEEL, distance);
|
||||
libevdev_uinput_write_event(mouse, EV_REL, REL_WHEEL_HI_RES, high_res_distance);
|
||||
libevdev_uinput_write_event(mouse, EV_SYN, SYN_REPORT, 0);
|
||||
@ -212,7 +212,7 @@ void scroll(input_t &input, int high_res_distance) {
|
||||
|
||||
uint16_t keysym(uint16_t modcode) {
|
||||
constexpr auto VK_NUMPAD = 0x60;
|
||||
constexpr auto VK_F1 = 0x70;
|
||||
constexpr auto VK_F1 = 0x70;
|
||||
|
||||
if(modcode >= VK_NUMPAD && modcode < VK_NUMPAD + 10) {
|
||||
return XK_KP_0 + (modcode - VK_NUMPAD);
|
||||
@ -224,108 +224,108 @@ uint16_t keysym(uint16_t modcode) {
|
||||
|
||||
|
||||
switch(modcode) {
|
||||
case 0x08:
|
||||
return XK_BackSpace;
|
||||
case 0x09:
|
||||
return XK_Tab;
|
||||
case 0x0D:
|
||||
return XK_Return;
|
||||
case 0x13:
|
||||
return XK_Pause;
|
||||
case 0x14:
|
||||
return XK_Caps_Lock;
|
||||
case 0x1B:
|
||||
return XK_Escape;
|
||||
case 0x21:
|
||||
return XK_Page_Up;
|
||||
case 0x22:
|
||||
return XK_Page_Down;
|
||||
case 0x23:
|
||||
return XK_End;
|
||||
case 0x24:
|
||||
return XK_Home;
|
||||
case 0x25:
|
||||
return XK_Left;
|
||||
case 0x26:
|
||||
return XK_Up;
|
||||
case 0x27:
|
||||
return XK_Right;
|
||||
case 0x28:
|
||||
return XK_Down;
|
||||
case 0x29:
|
||||
return XK_Select;
|
||||
case 0x2B:
|
||||
return XK_Execute;
|
||||
case 0x2C:
|
||||
return XK_Print;
|
||||
case 0x2D:
|
||||
return XK_Insert;
|
||||
case 0x2E:
|
||||
return XK_Delete;
|
||||
case 0x2F:
|
||||
return XK_Help;
|
||||
case 0x6A:
|
||||
return XK_KP_Multiply;
|
||||
case 0x6B:
|
||||
return XK_KP_Add;
|
||||
case 0x6C:
|
||||
return XK_KP_Separator;
|
||||
case 0x6D:
|
||||
return XK_KP_Subtract;
|
||||
case 0x6E:
|
||||
return XK_KP_Decimal;
|
||||
case 0x6F:
|
||||
return XK_KP_Divide;
|
||||
case 0x90:
|
||||
return XK_Num_Lock;
|
||||
case 0x91:
|
||||
return XK_Scroll_Lock;
|
||||
case 0xA0:
|
||||
return XK_Shift_L;
|
||||
case 0xA1:
|
||||
return XK_Shift_R;
|
||||
case 0xA2:
|
||||
return XK_Control_L;
|
||||
case 0xA3:
|
||||
return XK_Control_R;
|
||||
case 0xA4:
|
||||
return XK_Alt_L;
|
||||
case 0xA5: /* return XK_Alt_R; */
|
||||
return XK_Super_L;
|
||||
case 0x5B:
|
||||
return XK_Super_L;
|
||||
case 0x5C:
|
||||
return XK_Super_R;
|
||||
case 0xBA:
|
||||
return XK_semicolon;
|
||||
case 0xBB:
|
||||
return XK_equal;
|
||||
case 0xBC:
|
||||
return XK_comma;
|
||||
case 0xBD:
|
||||
return XK_minus;
|
||||
case 0xBE:
|
||||
return XK_period;
|
||||
case 0xBF:
|
||||
return XK_slash;
|
||||
case 0xC0:
|
||||
return XK_grave;
|
||||
case 0xDB:
|
||||
return XK_bracketleft;
|
||||
case 0xDC:
|
||||
return XK_backslash;
|
||||
case 0xDD:
|
||||
return XK_bracketright;
|
||||
case 0xDE:
|
||||
return XK_apostrophe;
|
||||
case 0x08:
|
||||
return XK_BackSpace;
|
||||
case 0x09:
|
||||
return XK_Tab;
|
||||
case 0x0D:
|
||||
return XK_Return;
|
||||
case 0x13:
|
||||
return XK_Pause;
|
||||
case 0x14:
|
||||
return XK_Caps_Lock;
|
||||
case 0x1B:
|
||||
return XK_Escape;
|
||||
case 0x21:
|
||||
return XK_Page_Up;
|
||||
case 0x22:
|
||||
return XK_Page_Down;
|
||||
case 0x23:
|
||||
return XK_End;
|
||||
case 0x24:
|
||||
return XK_Home;
|
||||
case 0x25:
|
||||
return XK_Left;
|
||||
case 0x26:
|
||||
return XK_Up;
|
||||
case 0x27:
|
||||
return XK_Right;
|
||||
case 0x28:
|
||||
return XK_Down;
|
||||
case 0x29:
|
||||
return XK_Select;
|
||||
case 0x2B:
|
||||
return XK_Execute;
|
||||
case 0x2C:
|
||||
return XK_Print;
|
||||
case 0x2D:
|
||||
return XK_Insert;
|
||||
case 0x2E:
|
||||
return XK_Delete;
|
||||
case 0x2F:
|
||||
return XK_Help;
|
||||
case 0x6A:
|
||||
return XK_KP_Multiply;
|
||||
case 0x6B:
|
||||
return XK_KP_Add;
|
||||
case 0x6C:
|
||||
return XK_KP_Separator;
|
||||
case 0x6D:
|
||||
return XK_KP_Subtract;
|
||||
case 0x6E:
|
||||
return XK_KP_Decimal;
|
||||
case 0x6F:
|
||||
return XK_KP_Divide;
|
||||
case 0x90:
|
||||
return XK_Num_Lock;
|
||||
case 0x91:
|
||||
return XK_Scroll_Lock;
|
||||
case 0xA0:
|
||||
return XK_Shift_L;
|
||||
case 0xA1:
|
||||
return XK_Shift_R;
|
||||
case 0xA2:
|
||||
return XK_Control_L;
|
||||
case 0xA3:
|
||||
return XK_Control_R;
|
||||
case 0xA4:
|
||||
return XK_Alt_L;
|
||||
case 0xA5: /* return XK_Alt_R; */
|
||||
return XK_Super_L;
|
||||
case 0x5B:
|
||||
return XK_Super_L;
|
||||
case 0x5C:
|
||||
return XK_Super_R;
|
||||
case 0xBA:
|
||||
return XK_semicolon;
|
||||
case 0xBB:
|
||||
return XK_equal;
|
||||
case 0xBC:
|
||||
return XK_comma;
|
||||
case 0xBD:
|
||||
return XK_minus;
|
||||
case 0xBE:
|
||||
return XK_period;
|
||||
case 0xBF:
|
||||
return XK_slash;
|
||||
case 0xC0:
|
||||
return XK_grave;
|
||||
case 0xDB:
|
||||
return XK_bracketleft;
|
||||
case 0xDC:
|
||||
return XK_backslash;
|
||||
case 0xDD:
|
||||
return XK_bracketright;
|
||||
case 0xDE:
|
||||
return XK_apostrophe;
|
||||
}
|
||||
|
||||
return modcode;
|
||||
}
|
||||
|
||||
void keyboard(input_t &input, uint16_t modcode, bool release) {
|
||||
auto &keyboard = ((input_raw_t*)input.get())->keyboard;
|
||||
KeyCode kc = XKeysymToKeycode(keyboard.get(), keysym(modcode));
|
||||
auto &keyboard = ((input_raw_t *)input.get())->keyboard;
|
||||
KeyCode kc = XKeysymToKeycode(keyboard.get(), keysym(modcode));
|
||||
|
||||
if(!kc) {
|
||||
return;
|
||||
@ -338,18 +338,18 @@ void keyboard(input_t &input, uint16_t modcode, bool release) {
|
||||
}
|
||||
|
||||
int alloc_gamepad(input_t &input, int nr) {
|
||||
return ((input_raw_t*)input.get())->alloc_gamepad(nr);
|
||||
return ((input_raw_t *)input.get())->alloc_gamepad(nr);
|
||||
}
|
||||
|
||||
void free_gamepad(input_t &input, int nr) {
|
||||
((input_raw_t*)input.get())->clear_gamepad(nr);
|
||||
((input_raw_t *)input.get())->clear_gamepad(nr);
|
||||
}
|
||||
|
||||
void gamepad(input_t &input, int nr, const gamepad_state_t &gamepad_state) {
|
||||
TUPLE_2D_REF(uinput, gamepad_state_old, ((input_raw_t*)input.get())->gamepads[nr]);
|
||||
TUPLE_2D_REF(uinput, gamepad_state_old, ((input_raw_t *)input.get())->gamepads[nr]);
|
||||
|
||||
|
||||
auto bf = gamepad_state.buttonFlags ^ gamepad_state_old.buttonFlags;
|
||||
auto bf = gamepad_state.buttonFlags ^ gamepad_state_old.buttonFlags;
|
||||
auto bf_new = gamepad_state.buttonFlags;
|
||||
|
||||
if(bf) {
|
||||
@ -366,17 +366,17 @@ void gamepad(input_t &input, int nr, const gamepad_state_t &gamepad_state) {
|
||||
libevdev_uinput_write_event(uinput.get(), EV_ABS, ABS_HAT0X, button_state);
|
||||
}
|
||||
|
||||
if(START & bf) libevdev_uinput_write_event(uinput.get(), EV_KEY, BTN_START, bf_new & START ? 1 : 0);
|
||||
if(BACK & bf) libevdev_uinput_write_event(uinput.get(), EV_KEY, BTN_SELECT, bf_new & BACK ? 1 : 0);
|
||||
if(LEFT_STICK & bf) libevdev_uinput_write_event(uinput.get(), EV_KEY, BTN_THUMBL, bf_new & LEFT_STICK ? 1 : 0);
|
||||
if(RIGHT_STICK & bf) libevdev_uinput_write_event(uinput.get(), EV_KEY, BTN_THUMBR, bf_new & RIGHT_STICK ? 1 : 0);
|
||||
if(LEFT_BUTTON & bf) libevdev_uinput_write_event(uinput.get(), EV_KEY, BTN_TL, bf_new & LEFT_BUTTON ? 1 : 0);
|
||||
if(RIGHT_BUTTON & bf) libevdev_uinput_write_event(uinput.get(), EV_KEY, BTN_TR, bf_new & RIGHT_BUTTON ? 1 : 0);
|
||||
if(HOME & bf) libevdev_uinput_write_event(uinput.get(), EV_KEY, BTN_MODE, bf_new & HOME ? 1 : 0);
|
||||
if(A & bf) libevdev_uinput_write_event(uinput.get(), EV_KEY, BTN_SOUTH, bf_new & A ? 1 : 0);
|
||||
if(B & bf) libevdev_uinput_write_event(uinput.get(), EV_KEY, BTN_EAST, bf_new & B ? 1 : 0);
|
||||
if(X & bf) libevdev_uinput_write_event(uinput.get(), EV_KEY, BTN_NORTH, bf_new & X ? 1 : 0);
|
||||
if(Y & bf) libevdev_uinput_write_event(uinput.get(), EV_KEY, BTN_WEST, bf_new & Y ? 1 : 0);
|
||||
if(START & bf) libevdev_uinput_write_event(uinput.get(), EV_KEY, BTN_START, bf_new & START ? 1 : 0);
|
||||
if(BACK & bf) libevdev_uinput_write_event(uinput.get(), EV_KEY, BTN_SELECT, bf_new & BACK ? 1 : 0);
|
||||
if(LEFT_STICK & bf) libevdev_uinput_write_event(uinput.get(), EV_KEY, BTN_THUMBL, bf_new & LEFT_STICK ? 1 : 0);
|
||||
if(RIGHT_STICK & bf) libevdev_uinput_write_event(uinput.get(), EV_KEY, BTN_THUMBR, bf_new & RIGHT_STICK ? 1 : 0);
|
||||
if(LEFT_BUTTON & bf) libevdev_uinput_write_event(uinput.get(), EV_KEY, BTN_TL, bf_new & LEFT_BUTTON ? 1 : 0);
|
||||
if(RIGHT_BUTTON & bf) libevdev_uinput_write_event(uinput.get(), EV_KEY, BTN_TR, bf_new & RIGHT_BUTTON ? 1 : 0);
|
||||
if(HOME & bf) libevdev_uinput_write_event(uinput.get(), EV_KEY, BTN_MODE, bf_new & HOME ? 1 : 0);
|
||||
if(A & bf) libevdev_uinput_write_event(uinput.get(), EV_KEY, BTN_SOUTH, bf_new & A ? 1 : 0);
|
||||
if(B & bf) libevdev_uinput_write_event(uinput.get(), EV_KEY, BTN_EAST, bf_new & B ? 1 : 0);
|
||||
if(X & bf) libevdev_uinput_write_event(uinput.get(), EV_KEY, BTN_NORTH, bf_new & X ? 1 : 0);
|
||||
if(Y & bf) libevdev_uinput_write_event(uinput.get(), EV_KEY, BTN_WEST, bf_new & Y ? 1 : 0);
|
||||
}
|
||||
|
||||
if(gamepad_state_old.lt != gamepad_state.lt) {
|
||||
@ -408,7 +408,7 @@ void gamepad(input_t &input, int nr, const gamepad_state_t &gamepad_state) {
|
||||
}
|
||||
|
||||
evdev_t mouse() {
|
||||
evdev_t dev { libevdev_new() };
|
||||
evdev_t dev { libevdev_new() };
|
||||
|
||||
libevdev_set_uniq(dev.get(), "Sunshine Mouse");
|
||||
libevdev_set_id_product(dev.get(), 0x4038);
|
||||
@ -553,7 +553,7 @@ evdev_t x360() {
|
||||
|
||||
input_t input() {
|
||||
input_t result { new input_raw_t() };
|
||||
auto &gp = *(input_raw_t*)result.get();
|
||||
auto &gp = *(input_raw_t *)result.get();
|
||||
|
||||
gp.keyboard.reset(XOpenDisplay(nullptr));
|
||||
|
||||
@ -568,8 +568,8 @@ input_t input() {
|
||||
|
||||
// Ensure starting from clean slate
|
||||
gp.clear();
|
||||
gp.touch_dev = touchscreen();
|
||||
gp.mouse_dev = mouse();
|
||||
gp.touch_dev = touchscreen();
|
||||
gp.mouse_dev = mouse();
|
||||
gp.gamepad_dev = x360();
|
||||
|
||||
if(gp.create_mouse() || gp.create_touchscreen()) {
|
||||
@ -581,9 +581,9 @@ input_t input() {
|
||||
}
|
||||
|
||||
void freeInput(void *p) {
|
||||
auto *input = (input_raw_t*)p;
|
||||
auto *input = (input_raw_t *)p;
|
||||
delete input;
|
||||
}
|
||||
|
||||
std::unique_ptr<deinit_t> init() { return std::make_unique<deinit_t>(); }
|
||||
}
|
||||
} // namespace platf
|
||||
|
||||
@ -2,9 +2,9 @@
|
||||
// Created by loki on 1/12/20.
|
||||
//
|
||||
|
||||
#include <roapi.h>
|
||||
#include <mmdeviceapi.h>
|
||||
#include <audioclient.h>
|
||||
#include <mmdeviceapi.h>
|
||||
#include <roapi.h>
|
||||
|
||||
#include <codecvt>
|
||||
|
||||
@ -36,7 +36,7 @@ using device_t = util::safe_ptr<IMMDevice, Release<IMMDevice>>;
|
||||
using audio_client_t = util::safe_ptr<IAudioClient, Release<IAudioClient>>;
|
||||
using audio_capture_t = util::safe_ptr<IAudioCaptureClient, Release<IAudioCaptureClient>>;
|
||||
using wave_format_t = util::safe_ptr<WAVEFORMATEX, co_task_free<WAVEFORMATEX>>;
|
||||
using handle_t = util::safe_ptr_v2<void, BOOL, CloseHandle>;
|
||||
using handle_t = util::safe_ptr_v2<void, BOOL, CloseHandle>;
|
||||
|
||||
class co_init_t : public deinit_t {
|
||||
public:
|
||||
@ -53,32 +53,26 @@ struct format_t {
|
||||
std::string_view name;
|
||||
int channels;
|
||||
int channel_mask;
|
||||
} formats [] {
|
||||
{
|
||||
"Stereo"sv,
|
||||
} formats[] {
|
||||
{ "Stereo"sv,
|
||||
2,
|
||||
SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT
|
||||
},
|
||||
{
|
||||
"Mono"sv,
|
||||
SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT },
|
||||
{ "Mono"sv,
|
||||
1,
|
||||
SPEAKER_FRONT_CENTER
|
||||
},
|
||||
{
|
||||
"Surround 5.1"sv,
|
||||
SPEAKER_FRONT_CENTER },
|
||||
{ "Surround 5.1"sv,
|
||||
6,
|
||||
SPEAKER_FRONT_LEFT |
|
||||
SPEAKER_FRONT_RIGHT |
|
||||
SPEAKER_FRONT_CENTER |
|
||||
SPEAKER_LOW_FREQUENCY |
|
||||
SPEAKER_BACK_LEFT |
|
||||
SPEAKER_BACK_RIGHT
|
||||
}
|
||||
SPEAKER_FRONT_LEFT |
|
||||
SPEAKER_FRONT_RIGHT |
|
||||
SPEAKER_FRONT_CENTER |
|
||||
SPEAKER_LOW_FREQUENCY |
|
||||
SPEAKER_BACK_LEFT |
|
||||
SPEAKER_BACK_RIGHT }
|
||||
};
|
||||
|
||||
void set_wave_format(audio::wave_format_t &wave_format, const format_t &format) {
|
||||
wave_format->nChannels = format.channels;
|
||||
wave_format->nBlockAlign = wave_format->nChannels * wave_format->wBitsPerSample / 8;
|
||||
wave_format->nChannels = format.channels;
|
||||
wave_format->nBlockAlign = wave_format->nChannels * wave_format->wBitsPerSample / 8;
|
||||
wave_format->nAvgBytesPerSec = wave_format->nSamplesPerSec * wave_format->nBlockAlign;
|
||||
|
||||
if(wave_format->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
|
||||
@ -97,8 +91,8 @@ void surround51_to_stereo(std::vector<std::int16_t> &sample_in, const util::buff
|
||||
channels51 // number of channels in surround sound
|
||||
};
|
||||
|
||||
auto sample_in_pos = std::begin(sample_in);
|
||||
auto sample_end = std::begin(sample_out) + sample_in.size() / 2 * channels51;
|
||||
auto sample_in_pos = std::begin(sample_in);
|
||||
auto sample_end = std::begin(sample_out) + sample_in.size() / 2 * channels51;
|
||||
|
||||
for(auto sample_out_p = std::begin(sample_out); sample_out_p != sample_end; sample_out_p += channels51) {
|
||||
std::uint32_t left {}, right {};
|
||||
@ -113,7 +107,8 @@ void surround51_to_stereo(std::vector<std::int16_t> &sample_in, const util::buff
|
||||
right += sample_out_p[front_center] * 90 / 100;
|
||||
right += sample_out_p[low_frequency] * 30 / 100;
|
||||
right += sample_out_p[back_left] * 30 / 100;
|
||||
right += sample_out_p[back_right] * 70 / 100;;
|
||||
right += sample_out_p[back_right] * 70 / 100;
|
||||
;
|
||||
|
||||
*sample_in_pos++ = (std::uint16_t)left;
|
||||
*sample_in_pos++ = (std::uint16_t)right;
|
||||
@ -121,8 +116,8 @@ void surround51_to_stereo(std::vector<std::int16_t> &sample_in, const util::buff
|
||||
}
|
||||
|
||||
void mono_to_stereo(std::vector<std::int16_t> &sample_in, const util::buffer_t<std::int16_t> &sample_out) {
|
||||
auto sample_in_pos = std::begin(sample_in);
|
||||
auto sample_end = std::begin(sample_out) + sample_in.size() / 2;
|
||||
auto sample_in_pos = std::begin(sample_in);
|
||||
auto sample_end = std::begin(sample_out) + sample_in.size() / 2;
|
||||
|
||||
for(auto sample_out_p = std::begin(sample_out); sample_out_p != sample_end; ++sample_out_p) {
|
||||
*sample_in_pos++ = *sample_out_p;
|
||||
@ -156,23 +151,23 @@ audio_client_t make_audio_client(device_t &device, const format_t &format, int s
|
||||
wave_format->wBitsPerSample = 16;
|
||||
wave_format->nSamplesPerSec = sample_rate;
|
||||
switch(wave_format->wFormatTag) {
|
||||
case WAVE_FORMAT_PCM:
|
||||
case WAVE_FORMAT_PCM:
|
||||
break;
|
||||
case WAVE_FORMAT_IEEE_FLOAT:
|
||||
break;
|
||||
case WAVE_FORMAT_EXTENSIBLE: {
|
||||
auto wave_ex = (PWAVEFORMATEXTENSIBLE)wave_format.get();
|
||||
if(IsEqualGUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, wave_ex->SubFormat)) {
|
||||
wave_ex->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
|
||||
wave_ex->Samples.wValidBitsPerSample = 16;
|
||||
break;
|
||||
case WAVE_FORMAT_IEEE_FLOAT:
|
||||
break;
|
||||
case WAVE_FORMAT_EXTENSIBLE: {
|
||||
auto wave_ex = (PWAVEFORMATEXTENSIBLE) wave_format.get();
|
||||
if (IsEqualGUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, wave_ex->SubFormat)) {
|
||||
wave_ex->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
|
||||
wave_ex->Samples.wValidBitsPerSample = 16;
|
||||
break;
|
||||
}
|
||||
|
||||
BOOST_LOG(error) << "Unsupported Sub Format for WAVE_FORMAT_EXTENSIBLE: [0x"sv << util::hex(wave_ex->SubFormat).to_string_view() << ']';
|
||||
}
|
||||
default:
|
||||
BOOST_LOG(error) << "Unsupported Wave Format: [0x"sv << util::hex(wave_format->wFormatTag).to_string_view() << ']';
|
||||
return nullptr;
|
||||
|
||||
BOOST_LOG(error) << "Unsupported Sub Format for WAVE_FORMAT_EXTENSIBLE: [0x"sv << util::hex(wave_ex->SubFormat).to_string_view() << ']';
|
||||
}
|
||||
default:
|
||||
BOOST_LOG(error) << "Unsupported Wave Format: [0x"sv << util::hex(wave_format->wFormatTag).to_string_view() << ']';
|
||||
return nullptr;
|
||||
};
|
||||
|
||||
set_wave_format(wave_format, format);
|
||||
@ -195,7 +190,7 @@ audio_client_t make_audio_client(device_t &device, const format_t &format, int s
|
||||
class mic_wasapi_t : public mic_t {
|
||||
public:
|
||||
capture_e sample(std::vector<std::int16_t> &sample_in) override {
|
||||
auto sample_size = sample_in.size() /2 * format->channels;
|
||||
auto sample_size = sample_in.size() / 2 * format->channels;
|
||||
while(sample_buf_pos - std::begin(sample_buf) < sample_size) {
|
||||
//FIXME: Use IAudioClient3 instead of IAudioClient, that would allows for adjusting the latency of the audio samples
|
||||
auto capture_result = _fill_buffer();
|
||||
@ -243,7 +238,7 @@ public:
|
||||
nullptr,
|
||||
CLSCTX_ALL,
|
||||
IID_IMMDeviceEnumerator,
|
||||
(void **) &device_enum);
|
||||
(void **)&device_enum);
|
||||
|
||||
if(FAILED(status)) {
|
||||
BOOST_LOG(error) << "Couldn't create Device Enumerator [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
@ -292,32 +287,32 @@ public:
|
||||
|
||||
std::uint32_t frames;
|
||||
status = audio_client->GetBufferSize(&frames);
|
||||
if (FAILED(status)) {
|
||||
if(FAILED(status)) {
|
||||
BOOST_LOG(error) << "Couldn't acquire the number of audio frames [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
// *2 --> needs to fit double
|
||||
sample_buf = util::buffer_t<std::int16_t> { std::max(frames *2, frame_size * format->channels *2) };
|
||||
sample_buf = util::buffer_t<std::int16_t> { std::max(frames * 2, frame_size * format->channels * 2) };
|
||||
sample_buf_pos = std::begin(sample_buf);
|
||||
|
||||
status = audio_client->GetService(IID_IAudioCaptureClient, (void**)&audio_capture);
|
||||
if (FAILED(status)) {
|
||||
status = audio_client->GetService(IID_IAudioCaptureClient, (void **)&audio_capture);
|
||||
if(FAILED(status)) {
|
||||
BOOST_LOG(error) << "Couldn't initialize audio capture client [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
status = audio_client->SetEventHandle(audio_event.get());
|
||||
if (FAILED(status)) {
|
||||
if(FAILED(status)) {
|
||||
BOOST_LOG(error) << "Couldn't set event handle [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
status = audio_client->Start();
|
||||
if (FAILED(status)) {
|
||||
if(FAILED(status)) {
|
||||
BOOST_LOG(error) << "Couldn't start recording [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
|
||||
return -1;
|
||||
@ -331,6 +326,7 @@ public:
|
||||
audio_client->Stop();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
capture_e _fill_buffer() {
|
||||
HRESULT status;
|
||||
@ -347,45 +343,45 @@ private:
|
||||
} block_aligned;
|
||||
|
||||
status = WaitForSingleObjectEx(audio_event.get(), default_latency_ms, FALSE);
|
||||
switch (status) {
|
||||
case WAIT_OBJECT_0:
|
||||
break;
|
||||
case WAIT_TIMEOUT:
|
||||
return capture_e::timeout;
|
||||
default:
|
||||
BOOST_LOG(error) << "Couldn't wait for audio event: [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return capture_e::error;
|
||||
switch(status) {
|
||||
case WAIT_OBJECT_0:
|
||||
break;
|
||||
case WAIT_TIMEOUT:
|
||||
return capture_e::timeout;
|
||||
default:
|
||||
BOOST_LOG(error) << "Couldn't wait for audio event: [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return capture_e::error;
|
||||
}
|
||||
|
||||
std::uint32_t packet_size{};
|
||||
for (
|
||||
std::uint32_t packet_size {};
|
||||
for(
|
||||
status = audio_capture->GetNextPacketSize(&packet_size);
|
||||
SUCCEEDED(status) && packet_size > 0;
|
||||
status = audio_capture->GetNextPacketSize(&packet_size)
|
||||
) {
|
||||
status = audio_capture->GetNextPacketSize(&packet_size)) {
|
||||
DWORD buffer_flags;
|
||||
status = audio_capture->GetBuffer(
|
||||
(BYTE **) &sample_aligned.samples,
|
||||
(BYTE **)&sample_aligned.samples,
|
||||
&block_aligned.audio_sample_size,
|
||||
&buffer_flags,
|
||||
nullptr, nullptr);
|
||||
|
||||
switch (status) {
|
||||
case S_OK:
|
||||
break;
|
||||
case AUDCLNT_E_DEVICE_INVALIDATED:
|
||||
return capture_e::reinit;
|
||||
default:
|
||||
BOOST_LOG(error) << "Couldn't capture audio [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return capture_e::error;
|
||||
switch(status) {
|
||||
case S_OK:
|
||||
break;
|
||||
case AUDCLNT_E_DEVICE_INVALIDATED:
|
||||
return capture_e::reinit;
|
||||
default:
|
||||
BOOST_LOG(error) << "Couldn't capture audio [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return capture_e::error;
|
||||
}
|
||||
|
||||
sample_aligned.uninitialized = std::end(sample_buf) - sample_buf_pos;
|
||||
auto n = std::min(sample_aligned.uninitialized, block_aligned.audio_sample_size * format->channels);
|
||||
auto n = std::min(sample_aligned.uninitialized, block_aligned.audio_sample_size * format->channels);
|
||||
|
||||
if (buffer_flags & AUDCLNT_BUFFERFLAGS_SILENT) {
|
||||
if(buffer_flags & AUDCLNT_BUFFERFLAGS_SILENT) {
|
||||
std::fill_n(sample_buf_pos, n, 0);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
std::copy_n(sample_aligned.samples, n, sample_buf_pos);
|
||||
}
|
||||
|
||||
@ -394,16 +390,17 @@ private:
|
||||
audio_capture->ReleaseBuffer(block_aligned.audio_sample_size);
|
||||
}
|
||||
|
||||
if (status == AUDCLNT_E_DEVICE_INVALIDATED) {
|
||||
if(status == AUDCLNT_E_DEVICE_INVALIDATED) {
|
||||
return capture_e::reinit;
|
||||
}
|
||||
|
||||
if (FAILED(status)) {
|
||||
if(FAILED(status)) {
|
||||
return capture_e::error;
|
||||
}
|
||||
|
||||
return capture_e::ok;
|
||||
}
|
||||
|
||||
public:
|
||||
handle_t audio_event;
|
||||
|
||||
@ -419,7 +416,7 @@ public:
|
||||
|
||||
format_t *format;
|
||||
};
|
||||
}
|
||||
} // namespace platf::audio
|
||||
|
||||
namespace platf {
|
||||
|
||||
@ -444,4 +441,4 @@ std::unique_ptr<deinit_t> init() {
|
||||
}
|
||||
return std::make_unique<platf::audio::co_init_t>();
|
||||
}
|
||||
}
|
||||
} // namespace platf
|
||||
|
||||
@ -5,14 +5,14 @@
|
||||
#ifndef SUNSHINE_DISPLAY_H
|
||||
#define SUNSHINE_DISPLAY_H
|
||||
|
||||
#include <dxgi.h>
|
||||
#include <d3d11.h>
|
||||
#include <d3d11_4.h>
|
||||
#include <d3dcommon.h>
|
||||
#include <dxgi.h>
|
||||
#include <dxgi1_2.h>
|
||||
|
||||
#include "sunshine/utility.h"
|
||||
#include "sunshine/platform/common.h"
|
||||
#include "sunshine/utility.h"
|
||||
|
||||
namespace platf::dxgi {
|
||||
extern const char *format_str[];
|
||||
@ -43,7 +43,7 @@ using processor_t = util::safe_ptr<ID3D11VideoProcessor, Release<ID3D11Vide
|
||||
using processor_out_t = util::safe_ptr<ID3D11VideoProcessorOutputView, Release<ID3D11VideoProcessorOutputView>>;
|
||||
using processor_in_t = util::safe_ptr<ID3D11VideoProcessorInputView, Release<ID3D11VideoProcessorInputView>>;
|
||||
using processor_enum_t = util::safe_ptr<ID3D11VideoProcessorEnumerator, Release<ID3D11VideoProcessorEnumerator>>;
|
||||
}
|
||||
} // namespace video
|
||||
|
||||
class hwdevice_t;
|
||||
struct cursor_t {
|
||||
@ -86,16 +86,14 @@ public:
|
||||
DXGI_FORMAT format;
|
||||
D3D_FEATURE_LEVEL feature_level;
|
||||
|
||||
typedef enum _D3DKMT_SCHEDULINGPRIORITYCLASS
|
||||
{
|
||||
typedef enum _D3DKMT_SCHEDULINGPRIORITYCLASS {
|
||||
D3DKMT_SCHEDULINGPRIORITYCLASS_IDLE,
|
||||
D3DKMT_SCHEDULINGPRIORITYCLASS_BELOW_NORMAL,
|
||||
D3DKMT_SCHEDULINGPRIORITYCLASS_NORMAL,
|
||||
D3DKMT_SCHEDULINGPRIORITYCLASS_ABOVE_NORMAL,
|
||||
D3DKMT_SCHEDULINGPRIORITYCLASS_HIGH,
|
||||
D3DKMT_SCHEDULINGPRIORITYCLASS_REALTIME
|
||||
}
|
||||
D3DKMT_SCHEDULINGPRIORITYCLASS;
|
||||
} D3DKMT_SCHEDULINGPRIORITYCLASS;
|
||||
|
||||
typedef NTSTATUS WINAPI (*PD3DKMTSetProcessSchedulingPriorityClass)(HANDLE, D3DKMT_SCHEDULINGPRIORITYCLASS);
|
||||
};
|
||||
@ -123,8 +121,8 @@ public:
|
||||
std::shared_ptr<platf::hwdevice_t> make_hwdevice(int width, int height, pix_fmt_e pix_fmt) override;
|
||||
|
||||
gpu_cursor_t cursor;
|
||||
std::vector<hwdevice_t*> hwdevices;
|
||||
std::vector<hwdevice_t *> hwdevices;
|
||||
};
|
||||
}
|
||||
} // namespace platf::dxgi
|
||||
|
||||
#endif
|
||||
@ -23,18 +23,18 @@ capture_e duplication_t::next_frame(DXGI_OUTDUPL_FRAME_INFO &frame_info, std::ch
|
||||
auto status = dup->AcquireNextFrame(timeout.count(), &frame_info, res_p);
|
||||
|
||||
switch(status) {
|
||||
case S_OK:
|
||||
has_frame = true;
|
||||
return capture_e::ok;
|
||||
case DXGI_ERROR_WAIT_TIMEOUT:
|
||||
return capture_e::timeout;
|
||||
case WAIT_ABANDONED:
|
||||
case DXGI_ERROR_ACCESS_LOST:
|
||||
case DXGI_ERROR_ACCESS_DENIED:
|
||||
return capture_e::reinit;
|
||||
default:
|
||||
BOOST_LOG(error) << "Couldn't acquire next frame [0x"sv << util::hex(status).to_string_view();
|
||||
return capture_e::error;
|
||||
case S_OK:
|
||||
has_frame = true;
|
||||
return capture_e::ok;
|
||||
case DXGI_ERROR_WAIT_TIMEOUT:
|
||||
return capture_e::timeout;
|
||||
case WAIT_ABANDONED:
|
||||
case DXGI_ERROR_ACCESS_LOST:
|
||||
case DXGI_ERROR_ACCESS_DENIED:
|
||||
return capture_e::reinit;
|
||||
default:
|
||||
BOOST_LOG(error) << "Couldn't acquire next frame [0x"sv << util::hex(status).to_string_view();
|
||||
return capture_e::error;
|
||||
}
|
||||
}
|
||||
|
||||
@ -52,20 +52,20 @@ capture_e duplication_t::release_frame() {
|
||||
}
|
||||
|
||||
auto status = dup->ReleaseFrame();
|
||||
switch (status) {
|
||||
case S_OK:
|
||||
has_frame = false;
|
||||
return capture_e::ok;
|
||||
case DXGI_ERROR_WAIT_TIMEOUT:
|
||||
return capture_e::timeout;
|
||||
case WAIT_ABANDONED:
|
||||
case DXGI_ERROR_ACCESS_LOST:
|
||||
case DXGI_ERROR_ACCESS_DENIED:
|
||||
has_frame = false;
|
||||
return capture_e::reinit;
|
||||
default:
|
||||
BOOST_LOG(error) << "Couldn't release frame [0x"sv << util::hex(status).to_string_view();
|
||||
return capture_e::error;
|
||||
switch(status) {
|
||||
case S_OK:
|
||||
has_frame = false;
|
||||
return capture_e::ok;
|
||||
case DXGI_ERROR_WAIT_TIMEOUT:
|
||||
return capture_e::timeout;
|
||||
case WAIT_ABANDONED:
|
||||
case DXGI_ERROR_ACCESS_LOST:
|
||||
case DXGI_ERROR_ACCESS_DENIED:
|
||||
has_frame = false;
|
||||
return capture_e::reinit;
|
||||
default:
|
||||
BOOST_LOG(error) << "Couldn't release frame [0x"sv << util::hex(status).to_string_view();
|
||||
return capture_e::error;
|
||||
}
|
||||
}
|
||||
|
||||
@ -74,7 +74,7 @@ duplication_t::~duplication_t() {
|
||||
}
|
||||
|
||||
int display_base_t::init() {
|
||||
/* Uncomment when use of IDXGIOutput5 is implemented
|
||||
/* Uncomment when use of IDXGIOutput5 is implemented
|
||||
std::call_once(windows_cpp_once_flag, []() {
|
||||
DECLARE_HANDLE(DPI_AWARENESS_CONTEXT);
|
||||
const auto DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 = ((DPI_AWARENESS_CONTEXT)-4);
|
||||
@ -93,7 +93,7 @@ int display_base_t::init() {
|
||||
|
||||
HRESULT status;
|
||||
|
||||
status = CreateDXGIFactory1(IID_IDXGIFactory1, (void**)&factory);
|
||||
status = CreateDXGIFactory1(IID_IDXGIFactory1, (void **)&factory);
|
||||
if(FAILED(status)) {
|
||||
BOOST_LOG(error) << "Failed to create DXGIFactory1 [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return -1;
|
||||
@ -102,7 +102,7 @@ int display_base_t::init() {
|
||||
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t> converter;
|
||||
|
||||
auto adapter_name = converter.from_bytes(config::video.adapter_name);
|
||||
auto output_name = converter.from_bytes(config::video.output_name);
|
||||
auto output_name = converter.from_bytes(config::video.output_name);
|
||||
|
||||
adapter_t::pointer adapter_p;
|
||||
for(int x = 0; factory->EnumAdapters1(x, &adapter_p) != DXGI_ERROR_NOT_FOUND; ++x) {
|
||||
@ -131,8 +131,8 @@ int display_base_t::init() {
|
||||
|
||||
offset_x = desc.DesktopCoordinates.left;
|
||||
offset_y = desc.DesktopCoordinates.top;
|
||||
width = desc.DesktopCoordinates.right - offset_x;
|
||||
height = desc.DesktopCoordinates.bottom - offset_y;
|
||||
width = desc.DesktopCoordinates.right - offset_x;
|
||||
height = desc.DesktopCoordinates.bottom - offset_y;
|
||||
}
|
||||
}
|
||||
|
||||
@ -157,7 +157,7 @@ int display_base_t::init() {
|
||||
D3D_FEATURE_LEVEL_9_1
|
||||
};
|
||||
|
||||
status = adapter->QueryInterface(IID_IDXGIAdapter, (void**)&adapter_p);
|
||||
status = adapter->QueryInterface(IID_IDXGIAdapter, (void **)&adapter_p);
|
||||
if(FAILED(status)) {
|
||||
BOOST_LOG(error) << "Failed to query IDXGIAdapter interface"sv;
|
||||
return -1;
|
||||
@ -195,7 +195,7 @@ int display_base_t::init() {
|
||||
<< "Device Sys Mem : "sv << adapter_desc.DedicatedSystemMemory / 1048576 << " MiB"sv << std::endl
|
||||
<< "Share Sys Mem : "sv << adapter_desc.SharedSystemMemory / 1048576 << " MiB"sv << std::endl
|
||||
<< "Feature Level : 0x"sv << util::hex(feature_level).to_string_view() << std::endl
|
||||
<< "Capture size : "sv << width << 'x' << height;
|
||||
<< "Capture size : "sv << width << 'x' << height;
|
||||
|
||||
// Bump up thread priority
|
||||
{
|
||||
@ -204,13 +204,13 @@ int display_base_t::init() {
|
||||
HANDLE token;
|
||||
LUID val;
|
||||
|
||||
if (OpenProcessToken(GetCurrentProcess(), flags, &token) &&
|
||||
if(OpenProcessToken(GetCurrentProcess(), flags, &token) &&
|
||||
!!LookupPrivilegeValue(NULL, SE_INC_BASE_PRIORITY_NAME, &val)) {
|
||||
tp.PrivilegeCount = 1;
|
||||
tp.Privileges[0].Luid = val;
|
||||
tp.PrivilegeCount = 1;
|
||||
tp.Privileges[0].Luid = val;
|
||||
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
|
||||
|
||||
if (!AdjustTokenPrivileges(token, false, &tp, sizeof(tp), NULL, NULL)) {
|
||||
if(!AdjustTokenPrivileges(token, false, &tp, sizeof(tp), NULL, NULL)) {
|
||||
BOOST_LOG(warning) << "Could not set privilege to increase GPU priority";
|
||||
}
|
||||
}
|
||||
@ -218,19 +218,19 @@ int display_base_t::init() {
|
||||
CloseHandle(token);
|
||||
|
||||
HMODULE gdi32 = GetModuleHandleA("GDI32");
|
||||
if (gdi32) {
|
||||
if(gdi32) {
|
||||
PD3DKMTSetProcessSchedulingPriorityClass fn =
|
||||
(PD3DKMTSetProcessSchedulingPriorityClass)GetProcAddress(gdi32, "D3DKMTSetProcessSchedulingPriorityClass");
|
||||
if (fn) {
|
||||
if(fn) {
|
||||
status = fn(GetCurrentProcess(), D3DKMT_SCHEDULINGPRIORITYCLASS_REALTIME);
|
||||
if (FAILED(status)) {
|
||||
if(FAILED(status)) {
|
||||
BOOST_LOG(warning) << "Failed to set realtime GPU priority. Please run application as administrator for optimal performance.";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dxgi::dxgi_t dxgi;
|
||||
status = device->QueryInterface(IID_IDXGIDevice, (void**)&dxgi);
|
||||
status = device->QueryInterface(IID_IDXGIDevice, (void **)&dxgi);
|
||||
if(FAILED(status)) {
|
||||
BOOST_LOG(warning) << "Failed to query DXGI interface from device [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return -1;
|
||||
@ -242,7 +242,7 @@ int display_base_t::init() {
|
||||
// Try to reduce latency
|
||||
{
|
||||
dxgi::dxgi1_t dxgi {};
|
||||
status = device->QueryInterface(IID_IDXGIDevice, (void**)&dxgi);
|
||||
status = device->QueryInterface(IID_IDXGIDevice, (void **)&dxgi);
|
||||
if(FAILED(status)) {
|
||||
BOOST_LOG(error) << "Failed to query DXGI interface from device [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return -1;
|
||||
@ -258,7 +258,7 @@ int display_base_t::init() {
|
||||
//TODO: Use IDXGIOutput5 for improved performance
|
||||
{
|
||||
dxgi::output1_t output1 {};
|
||||
status = output->QueryInterface(IID_IDXGIOutput1, (void**)&output1);
|
||||
status = output->QueryInterface(IID_IDXGIOutput1, (void **)&output1);
|
||||
if(FAILED(status)) {
|
||||
BOOST_LOG(error) << "Failed to query IDXGIOutput1 from the output"sv;
|
||||
return -1;
|
||||
@ -266,7 +266,7 @@ int display_base_t::init() {
|
||||
|
||||
// We try this twice, in case we still get an error on reinitialization
|
||||
for(int x = 0; x < 2; ++x) {
|
||||
status = output1->DuplicateOutput((IUnknown*)device.get(), &dup.dup);
|
||||
status = output1->DuplicateOutput((IUnknown *)device.get(), &dup.dup);
|
||||
if(SUCCEEDED(status)) {
|
||||
break;
|
||||
}
|
||||
@ -414,7 +414,7 @@ const char *format_str[] = {
|
||||
"DXGI_FORMAT_V408"
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace platf::dxgi
|
||||
|
||||
namespace platf {
|
||||
std::shared_ptr<display_t> display(dev_type_e hwdevice_type) {
|
||||
@ -435,4 +435,4 @@ std::shared_ptr<display_t> display(dev_type_e hwdevice_type) {
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
} // namespace platf
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
#include "sunshine/main.h"
|
||||
#include "display.h"
|
||||
#include "sunshine/main.h"
|
||||
|
||||
namespace platf {
|
||||
using namespace std::literals;
|
||||
}
|
||||
|
||||
namespace platf::dxgi {
|
||||
struct img_t : public ::platf::img_t {
|
||||
struct img_t : public ::platf::img_t {
|
||||
~img_t() override {
|
||||
delete[] data;
|
||||
data = nullptr;
|
||||
@ -22,29 +22,29 @@ void blend_cursor_monochrome(const cursor_t &cursor, img_t &img) {
|
||||
auto cursor_skip_y = -std::min(0, cursor.y);
|
||||
auto cursor_skip_x = -std::min(0, cursor.x);
|
||||
|
||||
// img cursor.{x,y} > img.{x,y}, truncate parts of the cursor.img_data
|
||||
// img cursor.{x,y} > img.{x,y}, truncate parts of the cursor.img_data
|
||||
auto cursor_truncate_y = std::max(0, cursor.y - img.height);
|
||||
auto cursor_truncate_x = std::max(0, cursor.x - img.width);
|
||||
|
||||
auto cursor_width = width - cursor_skip_x - cursor_truncate_x;
|
||||
auto cursor_width = width - cursor_skip_x - cursor_truncate_x;
|
||||
auto cursor_height = height - cursor_skip_y - cursor_truncate_y;
|
||||
|
||||
if(cursor_height > height || cursor_width > width) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto img_skip_y = std::max(0, cursor.y);
|
||||
auto img_skip_x = std::max(0, cursor.x);
|
||||
auto img_skip_y = std::max(0, cursor.y);
|
||||
auto img_skip_x = std::max(0, cursor.x);
|
||||
|
||||
auto cursor_img_data = cursor.img_data.data() + cursor_skip_y * pitch;
|
||||
|
||||
int delta_height = std::min(cursor_height - cursor_truncate_y, std::max(0, img.height - img_skip_y));
|
||||
int delta_width = std::min(cursor_width - cursor_truncate_x, std::max(0, img.width - img_skip_x));
|
||||
int delta_width = std::min(cursor_width - cursor_truncate_x, std::max(0, img.width - img_skip_x));
|
||||
|
||||
auto pixels_per_byte = width / pitch;
|
||||
auto bytes_per_row = delta_width / pixels_per_byte;
|
||||
auto bytes_per_row = delta_width / pixels_per_byte;
|
||||
|
||||
auto img_data = (int*)img.data;
|
||||
auto img_data = (int *)img.data;
|
||||
for(int i = 0; i < delta_height; ++i) {
|
||||
auto and_mask = &cursor_img_data[i * pitch];
|
||||
auto xor_mask = &cursor_img_data[(i + height) * pitch];
|
||||
@ -76,8 +76,8 @@ void blend_cursor_monochrome(const cursor_t &cursor, img_t &img) {
|
||||
}
|
||||
|
||||
void apply_color_alpha(int *img_pixel_p, int cursor_pixel) {
|
||||
auto colors_out = (std::uint8_t*)&cursor_pixel;
|
||||
auto colors_in = (std::uint8_t*)img_pixel_p;
|
||||
auto colors_out = (std::uint8_t *)&cursor_pixel;
|
||||
auto colors_in = (std::uint8_t *)img_pixel_p;
|
||||
|
||||
//TODO: When use of IDXGIOutput5 is implemented, support different color formats
|
||||
auto alpha = colors_out[3];
|
||||
@ -85,15 +85,15 @@ void apply_color_alpha(int *img_pixel_p, int cursor_pixel) {
|
||||
*img_pixel_p = cursor_pixel;
|
||||
}
|
||||
else {
|
||||
colors_in[0] = colors_out[0] + (colors_in[0] * (255 - alpha) + 255/2) / 255;
|
||||
colors_in[1] = colors_out[1] + (colors_in[1] * (255 - alpha) + 255/2) / 255;
|
||||
colors_in[2] = colors_out[2] + (colors_in[2] * (255 - alpha) + 255/2) / 255;
|
||||
colors_in[0] = colors_out[0] + (colors_in[0] * (255 - alpha) + 255 / 2) / 255;
|
||||
colors_in[1] = colors_out[1] + (colors_in[1] * (255 - alpha) + 255 / 2) / 255;
|
||||
colors_in[2] = colors_out[2] + (colors_in[2] * (255 - alpha) + 255 / 2) / 255;
|
||||
}
|
||||
}
|
||||
|
||||
void apply_color_masked(int *img_pixel_p, int cursor_pixel) {
|
||||
//TODO: When use of IDXGIOutput5 is implemented, support different color formats
|
||||
auto alpha = ((std::uint8_t*)&cursor_pixel)[3];
|
||||
auto alpha = ((std::uint8_t *)&cursor_pixel)[3];
|
||||
if(alpha == 0xFF) {
|
||||
*img_pixel_p ^= cursor_pixel;
|
||||
}
|
||||
@ -111,30 +111,30 @@ void blend_cursor_color(const cursor_t &cursor, img_t &img, const bool masked) {
|
||||
auto cursor_skip_y = -std::min(0, cursor.y);
|
||||
auto cursor_skip_x = -std::min(0, cursor.x);
|
||||
|
||||
// img cursor.{x,y} > img.{x,y}, truncate parts of the cursor.img_data
|
||||
// img cursor.{x,y} > img.{x,y}, truncate parts of the cursor.img_data
|
||||
auto cursor_truncate_y = std::max(0, cursor.y - img.height);
|
||||
auto cursor_truncate_x = std::max(0, cursor.x - img.width);
|
||||
|
||||
auto img_skip_y = std::max(0, cursor.y);
|
||||
auto img_skip_x = std::max(0, cursor.x);
|
||||
auto img_skip_y = std::max(0, cursor.y);
|
||||
auto img_skip_x = std::max(0, cursor.x);
|
||||
|
||||
auto cursor_width = width - cursor_skip_x - cursor_truncate_x;
|
||||
auto cursor_width = width - cursor_skip_x - cursor_truncate_x;
|
||||
auto cursor_height = height - cursor_skip_y - cursor_truncate_y;
|
||||
|
||||
if(cursor_height > height || cursor_width > width) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto cursor_img_data = (int*)&cursor.img_data[cursor_skip_y * pitch];
|
||||
auto cursor_img_data = (int *)&cursor.img_data[cursor_skip_y * pitch];
|
||||
|
||||
int delta_height = std::min(cursor_height - cursor_truncate_y, std::max(0, img.height - img_skip_y));
|
||||
int delta_width = std::min(cursor_width - cursor_truncate_x, std::max(0, img.width - img_skip_x));
|
||||
int delta_width = std::min(cursor_width - cursor_truncate_x, std::max(0, img.width - img_skip_x));
|
||||
|
||||
auto img_data = (int*)img.data;
|
||||
auto img_data = (int *)img.data;
|
||||
|
||||
for(int i = 0; i < delta_height; ++i) {
|
||||
auto cursor_begin = &cursor_img_data[i * cursor.shape_info.Width + cursor_skip_x];
|
||||
auto cursor_end = &cursor_begin[delta_width];
|
||||
auto cursor_end = &cursor_begin[delta_width];
|
||||
|
||||
auto img_pixel_p = &img_data[(i + img_skip_y) * (img.row_pitch / img.pixel_pitch) + img_skip_x];
|
||||
std::for_each(cursor_begin, cursor_end, [&](int cursor_pixel) {
|
||||
@ -151,22 +151,22 @@ void blend_cursor_color(const cursor_t &cursor, img_t &img, const bool masked) {
|
||||
|
||||
void blend_cursor(const cursor_t &cursor, img_t &img) {
|
||||
switch(cursor.shape_info.Type) {
|
||||
case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_COLOR:
|
||||
blend_cursor_color(cursor, img, false);
|
||||
break;
|
||||
case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MONOCHROME:
|
||||
blend_cursor_monochrome(cursor, img);
|
||||
break;
|
||||
case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR:
|
||||
blend_cursor_color(cursor, img, true);
|
||||
break;
|
||||
default:
|
||||
BOOST_LOG(warning) << "Unsupported cursor format ["sv << cursor.shape_info.Type << ']';
|
||||
case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_COLOR:
|
||||
blend_cursor_color(cursor, img, false);
|
||||
break;
|
||||
case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MONOCHROME:
|
||||
blend_cursor_monochrome(cursor, img);
|
||||
break;
|
||||
case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR:
|
||||
blend_cursor_color(cursor, img, true);
|
||||
break;
|
||||
default:
|
||||
BOOST_LOG(warning) << "Unsupported cursor format ["sv << cursor.shape_info.Type << ']';
|
||||
}
|
||||
}
|
||||
|
||||
capture_e display_ram_t::snapshot(::platf::img_t *img_base, std::chrono::milliseconds timeout, bool cursor_visible) {
|
||||
auto img = (img_t*)img_base;
|
||||
auto img = (img_t *)img_base;
|
||||
|
||||
HRESULT status;
|
||||
|
||||
@ -174,9 +174,9 @@ capture_e display_ram_t::snapshot(::platf::img_t *img_base, std::chrono::millise
|
||||
|
||||
resource_t::pointer res_p {};
|
||||
auto capture_status = dup.next_frame(frame_info, timeout, &res_p);
|
||||
resource_t res{res_p};
|
||||
resource_t res { res_p };
|
||||
|
||||
if (capture_status != capture_e::ok) {
|
||||
if(capture_status != capture_e::ok) {
|
||||
return capture_status;
|
||||
}
|
||||
|
||||
@ -187,7 +187,7 @@ capture_e display_ram_t::snapshot(::platf::img_t *img_base, std::chrono::millise
|
||||
|
||||
UINT dummy;
|
||||
status = dup.dup->GetFramePointerShape(img_data.size(), img_data.data(), &dummy, &cursor.shape_info);
|
||||
if (FAILED(status)) {
|
||||
if(FAILED(status)) {
|
||||
BOOST_LOG(error) << "Failed to get new pointer shape [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
|
||||
return capture_e::error;
|
||||
@ -195,18 +195,18 @@ capture_e display_ram_t::snapshot(::platf::img_t *img_base, std::chrono::millise
|
||||
}
|
||||
|
||||
if(frame_info.LastMouseUpdateTime.QuadPart) {
|
||||
cursor.x = frame_info.PointerPosition.Position.x;
|
||||
cursor.y = frame_info.PointerPosition.Position.y;
|
||||
cursor.x = frame_info.PointerPosition.Position.x;
|
||||
cursor.y = frame_info.PointerPosition.Position.y;
|
||||
cursor.visible = frame_info.PointerPosition.Visible;
|
||||
}
|
||||
|
||||
// If frame has been updated
|
||||
if (frame_info.LastPresentTime.QuadPart != 0) {
|
||||
if(frame_info.LastPresentTime.QuadPart != 0) {
|
||||
{
|
||||
texture2d_t src {};
|
||||
status = res->QueryInterface(IID_ID3D11Texture2D, (void **)&src);
|
||||
|
||||
if (FAILED(status)) {
|
||||
if(FAILED(status)) {
|
||||
BOOST_LOG(error) << "Couldn't query interface [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return capture_e::error;
|
||||
}
|
||||
@ -221,14 +221,14 @@ capture_e display_ram_t::snapshot(::platf::img_t *img_base, std::chrono::millise
|
||||
}
|
||||
|
||||
status = device_ctx->Map(texture.get(), 0, D3D11_MAP_READ, 0, &img_info);
|
||||
if (FAILED(status)) {
|
||||
if(FAILED(status)) {
|
||||
BOOST_LOG(error) << "Failed to map texture [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
|
||||
return capture_e::error;
|
||||
}
|
||||
}
|
||||
|
||||
const bool mouse_update =
|
||||
const bool mouse_update =
|
||||
(frame_info.LastMouseUpdateTime.QuadPart || frame_info.PointerShapeBufferSize > 0) &&
|
||||
(cursor_visible && cursor.visible);
|
||||
|
||||
@ -238,7 +238,7 @@ capture_e display_ram_t::snapshot(::platf::img_t *img_base, std::chrono::millise
|
||||
return capture_e::timeout;
|
||||
}
|
||||
|
||||
std::copy_n((std::uint8_t*)img_info.pData, height * img_info.RowPitch, (std::uint8_t*)img->data);
|
||||
std::copy_n((std::uint8_t *)img_info.pData, height * img_info.RowPitch, (std::uint8_t *)img->data);
|
||||
|
||||
if(cursor_visible && cursor.visible) {
|
||||
blend_cursor(cursor, *img);
|
||||
@ -250,11 +250,11 @@ capture_e display_ram_t::snapshot(::platf::img_t *img_base, std::chrono::millise
|
||||
std::shared_ptr<platf::img_t> display_ram_t::alloc_img() {
|
||||
auto img = std::make_shared<img_t>();
|
||||
|
||||
img->pixel_pitch = 4;
|
||||
img->row_pitch = img_info.RowPitch;
|
||||
img->width = width;
|
||||
img->height = height;
|
||||
img->data = new std::uint8_t[img->row_pitch * height];
|
||||
img->pixel_pitch = 4;
|
||||
img->row_pitch = img_info.RowPitch;
|
||||
img->width = width;
|
||||
img->height = height;
|
||||
img->data = new std::uint8_t[img->row_pitch * height];
|
||||
|
||||
return img;
|
||||
}
|
||||
@ -269,14 +269,14 @@ int display_ram_t::init() {
|
||||
}
|
||||
|
||||
D3D11_TEXTURE2D_DESC t {};
|
||||
t.Width = width;
|
||||
t.Height = height;
|
||||
t.MipLevels = 1;
|
||||
t.ArraySize = 1;
|
||||
t.Width = width;
|
||||
t.Height = height;
|
||||
t.MipLevels = 1;
|
||||
t.ArraySize = 1;
|
||||
t.SampleDesc.Count = 1;
|
||||
t.Usage = D3D11_USAGE_STAGING;
|
||||
t.Format = format;
|
||||
t.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
|
||||
t.Usage = D3D11_USAGE_STAGING;
|
||||
t.Format = format;
|
||||
t.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
|
||||
|
||||
auto status = device->CreateTexture2D(&t, nullptr, &texture);
|
||||
|
||||
@ -294,4 +294,4 @@ int display_ram_t::init() {
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
} // namespace platf::dxgi
|
||||
|
||||
@ -3,8 +3,8 @@
|
||||
#include <d3dcompiler.h>
|
||||
#include <directxmath.h>
|
||||
|
||||
#include "sunshine/main.h"
|
||||
#include "display.h"
|
||||
#include "sunshine/main.h"
|
||||
|
||||
#define SUNSHINE_SHADERS_DIR SUNSHINE_ASSETS_DIR "/shaders"
|
||||
namespace platf {
|
||||
@ -30,7 +30,7 @@ using depth_stencil_view_t = util::safe_ptr<ID3D11DepthStencilView, Release<ID3
|
||||
using float4 = DirectX::XMFLOAT4;
|
||||
using float3 = DirectX::XMFLOAT3;
|
||||
using float2 = DirectX::XMFLOAT2;
|
||||
struct __attribute__ ((__aligned__ (16))) color_t {
|
||||
struct __attribute__((__aligned__(16))) color_t {
|
||||
float4 color_vec_y;
|
||||
float4 color_vec_u;
|
||||
float4 color_vec_v;
|
||||
@ -44,10 +44,10 @@ color_t make_color_matrix(float Cr, float Cb, float U_max, float V_max, float ad
|
||||
float Cr_i = 1.0f - Cr;
|
||||
float Cb_i = 1.0f - Cb;
|
||||
|
||||
float shift_y = range_Y.x / 256.0f;
|
||||
float shift_y = range_Y.x / 256.0f;
|
||||
float shift_uv = range_UV.x / 256.0f;
|
||||
|
||||
float scale_y = (range_Y.y - range_Y.x) / 256.0f;
|
||||
float scale_y = (range_Y.y - range_Y.x) / 256.0f;
|
||||
float scale_uv = (range_UV.y - range_UV.x) / 256.0f;
|
||||
return {
|
||||
{ Cr, Cg, Cb, add_Y },
|
||||
@ -66,7 +66,7 @@ color_t colors[] {
|
||||
};
|
||||
|
||||
template<class T>
|
||||
buf_t make_buffer(device_t::pointer device, const T& t) {
|
||||
buf_t make_buffer(device_t::pointer device, const T &t) {
|
||||
static_assert(sizeof(T) % 16 == 0, "Buffer needs to be aligned on a 16-byte alignment");
|
||||
|
||||
D3D11_BUFFER_DESC buffer_desc {
|
||||
@ -91,18 +91,18 @@ buf_t make_buffer(device_t::pointer device, const T& t) {
|
||||
|
||||
blend_t make_blend(device_t::pointer device, bool enable) {
|
||||
D3D11_BLEND_DESC bdesc {};
|
||||
auto &rt = bdesc.RenderTarget[0];
|
||||
rt.BlendEnable = enable;
|
||||
auto &rt = bdesc.RenderTarget[0];
|
||||
rt.BlendEnable = enable;
|
||||
rt.RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;
|
||||
|
||||
if(enable) {
|
||||
rt.BlendOp = D3D11_BLEND_OP_ADD;
|
||||
rt.BlendOp = D3D11_BLEND_OP_ADD;
|
||||
rt.BlendOpAlpha = D3D11_BLEND_OP_ADD;
|
||||
|
||||
rt.SrcBlend = D3D11_BLEND_SRC_ALPHA;
|
||||
rt.SrcBlend = D3D11_BLEND_SRC_ALPHA;
|
||||
rt.DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
|
||||
|
||||
rt.SrcBlendAlpha = D3D11_BLEND_ZERO;
|
||||
rt.SrcBlendAlpha = D3D11_BLEND_ZERO;
|
||||
rt.DestBlendAlpha = D3D11_BLEND_ZERO;
|
||||
}
|
||||
|
||||
@ -130,79 +130,79 @@ struct img_d3d_t : public platf::img_t {
|
||||
~img_d3d_t() override = default;
|
||||
};
|
||||
|
||||
util::buffer_t<std::uint8_t> make_cursor_image(util::buffer_t<std::uint8_t> &&img_data, DXGI_OUTDUPL_POINTER_SHAPE_INFO shape_info) {
|
||||
constexpr std::uint32_t black = 0xFF000000;
|
||||
constexpr std::uint32_t white = 0xFFFFFFFF;
|
||||
util::buffer_t<std::uint8_t> make_cursor_image(util::buffer_t<std::uint8_t> &&img_data, DXGI_OUTDUPL_POINTER_SHAPE_INFO shape_info) {
|
||||
constexpr std::uint32_t black = 0xFF000000;
|
||||
constexpr std::uint32_t white = 0xFFFFFFFF;
|
||||
constexpr std::uint32_t transparent = 0;
|
||||
|
||||
switch(shape_info.Type) {
|
||||
case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR:
|
||||
std::for_each((std::uint32_t*)std::begin(img_data), (std::uint32_t*)std::end(img_data), [](auto &pixel) {
|
||||
if(pixel & 0xFF000000) {
|
||||
pixel = transparent;
|
||||
}
|
||||
});
|
||||
case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_COLOR:
|
||||
return std::move(img_data);
|
||||
default:
|
||||
break;
|
||||
case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR:
|
||||
std::for_each((std::uint32_t *)std::begin(img_data), (std::uint32_t *)std::end(img_data), [](auto &pixel) {
|
||||
if(pixel & 0xFF000000) {
|
||||
pixel = transparent;
|
||||
}
|
||||
});
|
||||
case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_COLOR:
|
||||
return std::move(img_data);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
shape_info.Height /= 2;
|
||||
|
||||
util::buffer_t<std::uint8_t> cursor_img { shape_info.Width * shape_info.Height * 4 };
|
||||
|
||||
auto bytes = shape_info.Pitch * shape_info.Height;
|
||||
auto pixel_begin = (std::uint32_t*)std::begin(cursor_img);
|
||||
auto pixel_data = pixel_begin;
|
||||
auto and_mask = std::begin(img_data);
|
||||
auto xor_mask = std::begin(img_data) + bytes;
|
||||
auto bytes = shape_info.Pitch * shape_info.Height;
|
||||
auto pixel_begin = (std::uint32_t *)std::begin(cursor_img);
|
||||
auto pixel_data = pixel_begin;
|
||||
auto and_mask = std::begin(img_data);
|
||||
auto xor_mask = std::begin(img_data) + bytes;
|
||||
|
||||
for(auto x = 0; x < bytes; ++x) {
|
||||
for(auto x = 0; x < bytes; ++x) {
|
||||
for(auto c = 7; c >= 0; --c) {
|
||||
auto bit = 1 << c;
|
||||
auto bit = 1 << c;
|
||||
auto color_type = ((*and_mask & bit) ? 1 : 0) + ((*xor_mask & bit) ? 2 : 0);
|
||||
|
||||
switch(color_type) {
|
||||
case 0: //black
|
||||
*pixel_data = black;
|
||||
break;
|
||||
case 2: //white
|
||||
*pixel_data = white;
|
||||
break;
|
||||
case 1: //transparent
|
||||
{
|
||||
*pixel_data = transparent;
|
||||
case 0: //black
|
||||
*pixel_data = black;
|
||||
break;
|
||||
case 2: //white
|
||||
*pixel_data = white;
|
||||
break;
|
||||
case 1: //transparent
|
||||
{
|
||||
*pixel_data = transparent;
|
||||
|
||||
break;
|
||||
break;
|
||||
}
|
||||
case 3: //inverse
|
||||
{
|
||||
auto top_p = pixel_data - shape_info.Width;
|
||||
auto left_p = pixel_data - 1;
|
||||
auto right_p = pixel_data + 1;
|
||||
auto bottom_p = pixel_data + shape_info.Width;
|
||||
|
||||
// Get the x coordinate of the pixel
|
||||
auto column = (pixel_data - pixel_begin) % shape_info.Width != 0;
|
||||
|
||||
if(top_p >= pixel_begin && *top_p == transparent) {
|
||||
*top_p = black;
|
||||
}
|
||||
case 3: //inverse
|
||||
{
|
||||
auto top_p = pixel_data - shape_info.Width;
|
||||
auto left_p = pixel_data - 1;
|
||||
auto right_p = pixel_data + 1;
|
||||
auto bottom_p = pixel_data + shape_info.Width;
|
||||
|
||||
// Get the x coordinate of the pixel
|
||||
auto column = (pixel_data - pixel_begin) % shape_info.Width != 0;
|
||||
|
||||
if(top_p >= pixel_begin && *top_p == transparent) {
|
||||
*top_p = black;
|
||||
}
|
||||
|
||||
if(column != 0 && left_p >= pixel_begin && *left_p == transparent) {
|
||||
*left_p = black;
|
||||
}
|
||||
|
||||
if(bottom_p < (std::uint32_t*)std::end(cursor_img)) {
|
||||
*bottom_p = black;
|
||||
}
|
||||
|
||||
if(column != shape_info.Width -1) {
|
||||
*right_p = black;
|
||||
}
|
||||
*pixel_data = white;
|
||||
if(column != 0 && left_p >= pixel_begin && *left_p == transparent) {
|
||||
*left_p = black;
|
||||
}
|
||||
|
||||
if(bottom_p < (std::uint32_t *)std::end(cursor_img)) {
|
||||
*bottom_p = black;
|
||||
}
|
||||
|
||||
if(column != shape_info.Width - 1) {
|
||||
*right_p = black;
|
||||
}
|
||||
*pixel_data = white;
|
||||
}
|
||||
}
|
||||
|
||||
++pixel_data;
|
||||
@ -225,7 +225,7 @@ blob_t compile_shader(LPCSTR file, LPCSTR entrypoint, LPCSTR shader_model) {
|
||||
#endif
|
||||
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t> converter;
|
||||
|
||||
auto wFile = converter.from_bytes(file);
|
||||
auto wFile = converter.from_bytes(file);
|
||||
auto status = D3DCompileFromFile(wFile.c_str(), nullptr, nullptr, entrypoint, shader_model, flags, 0, &compiled_p, &msg_p);
|
||||
|
||||
if(msg_p) {
|
||||
@ -251,7 +251,7 @@ blob_t compile_vertex_shader(LPCSTR file) {
|
||||
|
||||
class hwdevice_t : public platf::hwdevice_t {
|
||||
public:
|
||||
hwdevice_t(std::vector<hwdevice_t*> *hwdevices_p) : hwdevices_p { hwdevices_p } {}
|
||||
hwdevice_t(std::vector<hwdevice_t *> *hwdevices_p) : hwdevices_p { hwdevices_p } {}
|
||||
hwdevice_t() = delete;
|
||||
|
||||
void set_cursor_pos(LONG rel_x, LONG rel_y, bool visible) {
|
||||
@ -271,7 +271,7 @@ public:
|
||||
int set_cursor_texture(texture2d_t::pointer texture, LONG width, LONG height) {
|
||||
auto device = (device_t::pointer)data;
|
||||
|
||||
cursor_view.Width = width;
|
||||
cursor_view.Width = width;
|
||||
cursor_view.Height = height;
|
||||
|
||||
D3D11_SHADER_RESOURCE_VIEW_DESC desc {
|
||||
@ -290,7 +290,7 @@ public:
|
||||
}
|
||||
|
||||
int convert(platf::img_t &img_base) override {
|
||||
auto &img = (img_d3d_t&)img_base;
|
||||
auto &img = (img_d3d_t &)img_base;
|
||||
|
||||
if(!img.input_res) {
|
||||
auto device = (device_t::pointer)data;
|
||||
@ -347,17 +347,17 @@ public:
|
||||
}
|
||||
|
||||
void set_colorspace(std::uint32_t colorspace, std::uint32_t color_range) override {
|
||||
switch (colorspace) {
|
||||
case 5: // SWS_CS_SMPTE170M
|
||||
color_p = &colors[0];
|
||||
break;
|
||||
case 1: // SWS_CS_ITU709
|
||||
color_p = &colors[2];
|
||||
break;
|
||||
case 9: // SWS_CS_BT2020
|
||||
default:
|
||||
BOOST_LOG(warning) << "Colorspace: ["sv << colorspace << "] not yet supported: switching to default"sv;
|
||||
color_p = &colors[0];
|
||||
switch(colorspace) {
|
||||
case 5: // SWS_CS_SMPTE170M
|
||||
color_p = &colors[0];
|
||||
break;
|
||||
case 1: // SWS_CS_ITU709
|
||||
color_p = &colors[2];
|
||||
break;
|
||||
case 9: // SWS_CS_BT2020
|
||||
default:
|
||||
BOOST_LOG(warning) << "Colorspace: ["sv << colorspace << "] not yet supported: switching to default"sv;
|
||||
color_p = &colors[0];
|
||||
};
|
||||
|
||||
if(color_range > 1) {
|
||||
@ -378,8 +378,7 @@ public:
|
||||
int init(
|
||||
std::shared_ptr<platf::display_t> display, device_t::pointer device_p, device_ctx_t::pointer device_ctx_p,
|
||||
int in_width, int in_height, int out_width, int out_height,
|
||||
pix_fmt_e pix_fmt
|
||||
) {
|
||||
pix_fmt_e pix_fmt) {
|
||||
HRESULT status;
|
||||
|
||||
device_p->AddRef();
|
||||
@ -387,7 +386,7 @@ public:
|
||||
|
||||
this->device_ctx_p = device_ctx_p;
|
||||
|
||||
cursor_visible = false;
|
||||
cursor_visible = false;
|
||||
cursor_view.MinDepth = 0.0f;
|
||||
cursor_view.MaxDepth = 1.0f;
|
||||
|
||||
@ -427,7 +426,7 @@ public:
|
||||
}
|
||||
|
||||
blend_disable = make_blend(device_p, false);
|
||||
blend_enable = make_blend(device_p, true);
|
||||
blend_enable = make_blend(device_p, true);
|
||||
|
||||
if(!blend_disable || !blend_enable) {
|
||||
return -1;
|
||||
@ -460,14 +459,14 @@ public:
|
||||
&input_layout);
|
||||
|
||||
D3D11_TEXTURE2D_DESC t {};
|
||||
t.Width = out_width;
|
||||
t.Height = out_height;
|
||||
t.MipLevels = 1;
|
||||
t.ArraySize = 1;
|
||||
t.Width = out_width;
|
||||
t.Height = out_height;
|
||||
t.MipLevels = 1;
|
||||
t.ArraySize = 1;
|
||||
t.SampleDesc.Count = 1;
|
||||
t.Usage = D3D11_USAGE_DEFAULT;
|
||||
t.Format = pix_fmt == pix_fmt_e::nv12 ? DXGI_FORMAT_NV12 : DXGI_FORMAT_P010;
|
||||
t.BindFlags = D3D11_BIND_RENDER_TARGET;
|
||||
t.Usage = D3D11_USAGE_DEFAULT;
|
||||
t.Format = pix_fmt == pix_fmt_e::nv12 ? DXGI_FORMAT_NV12 : DXGI_FORMAT_P010;
|
||||
t.BindFlags = D3D11_BIND_RENDER_TARGET;
|
||||
|
||||
status = device_p->CreateTexture2D(&t, nullptr, &img.texture);
|
||||
if(FAILED(status)) {
|
||||
@ -475,11 +474,11 @@ public:
|
||||
return -1;
|
||||
}
|
||||
|
||||
img.display = std::move(display);
|
||||
img.width = out_width;
|
||||
img.height = out_height;
|
||||
img.data = (std::uint8_t*)img.texture.get();
|
||||
img.row_pitch = out_width;
|
||||
img.display = std::move(display);
|
||||
img.width = out_width;
|
||||
img.height = out_height;
|
||||
img.data = (std::uint8_t *)img.texture.get();
|
||||
img.row_pitch = out_width;
|
||||
img.pixel_pitch = 1;
|
||||
|
||||
D3D11_RENDER_TARGET_VIEW_DESC nv12_rt_desc {
|
||||
@ -494,20 +493,20 @@ public:
|
||||
}
|
||||
|
||||
nv12_rt_desc.Format = DXGI_FORMAT_R8G8_UNORM;
|
||||
status = device_p->CreateRenderTargetView(img.texture.get(), &nv12_rt_desc, &nv12_UV_rt);
|
||||
status = device_p->CreateRenderTargetView(img.texture.get(), &nv12_rt_desc, &nv12_UV_rt);
|
||||
if(FAILED(status)) {
|
||||
BOOST_LOG(error) << "Failed to create render target view [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return -1;
|
||||
}
|
||||
|
||||
D3D11_SAMPLER_DESC sampler_desc {};
|
||||
sampler_desc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
|
||||
sampler_desc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;
|
||||
sampler_desc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;
|
||||
sampler_desc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
|
||||
sampler_desc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
|
||||
sampler_desc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;
|
||||
sampler_desc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;
|
||||
sampler_desc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
|
||||
sampler_desc.ComparisonFunc = D3D11_COMPARISON_NEVER;
|
||||
sampler_desc.MinLOD = 0;
|
||||
sampler_desc.MaxLOD = D3D11_FLOAT32_MAX;
|
||||
sampler_desc.MinLOD = 0;
|
||||
sampler_desc.MaxLOD = D3D11_FLOAT32_MAX;
|
||||
|
||||
status = device_p->CreateSamplerState(&sampler_desc, &sampler_linear);
|
||||
if(FAILED(status)) {
|
||||
@ -527,7 +526,7 @@ public:
|
||||
|
||||
~hwdevice_t() override {
|
||||
if(data) {
|
||||
((ID3D11Device*)data)->Release();
|
||||
((ID3D11Device *)data)->Release();
|
||||
}
|
||||
|
||||
auto it = std::find(std::begin(*hwdevices_p), std::end(*hwdevices_p), this);
|
||||
@ -535,6 +534,7 @@ public:
|
||||
hwdevices_p->erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void _init_view_port(float x, float y, float width, float height) {
|
||||
D3D11_VIEWPORT view {
|
||||
@ -633,11 +633,11 @@ public:
|
||||
device_ctx_t::pointer device_ctx_p;
|
||||
|
||||
// The destructor will remove itself from the list of hardware devices, this is done synchronously
|
||||
std::vector<hwdevice_t*> *hwdevices_p;
|
||||
std::vector<hwdevice_t *> *hwdevices_p;
|
||||
};
|
||||
|
||||
capture_e display_vram_t::snapshot(platf::img_t *img_base, std::chrono::milliseconds timeout, bool cursor_visible) {
|
||||
auto img = (img_d3d_t*)img_base;
|
||||
auto img = (img_d3d_t *)img_base;
|
||||
|
||||
HRESULT status;
|
||||
|
||||
@ -645,7 +645,7 @@ capture_e display_vram_t::snapshot(platf::img_t *img_base, std::chrono::millisec
|
||||
|
||||
resource_t::pointer res_p {};
|
||||
auto capture_status = dup.next_frame(frame_info, timeout, &res_p);
|
||||
resource_t res{ res_p };
|
||||
resource_t res { res_p };
|
||||
|
||||
if(capture_status != capture_e::ok) {
|
||||
return capture_status;
|
||||
@ -653,7 +653,7 @@ capture_e display_vram_t::snapshot(platf::img_t *img_base, std::chrono::millisec
|
||||
|
||||
const bool mouse_update_flag = frame_info.LastMouseUpdateTime.QuadPart != 0 || frame_info.PointerShapeBufferSize > 0;
|
||||
const bool frame_update_flag = frame_info.AccumulatedFrames != 0 || frame_info.LastPresentTime.QuadPart != 0;
|
||||
const bool update_flag = mouse_update_flag || frame_update_flag;
|
||||
const bool update_flag = mouse_update_flag || frame_update_flag;
|
||||
|
||||
if(!update_flag) {
|
||||
return capture_e::timeout;
|
||||
@ -666,7 +666,7 @@ capture_e display_vram_t::snapshot(platf::img_t *img_base, std::chrono::millisec
|
||||
|
||||
UINT dummy;
|
||||
status = dup.dup->GetFramePointerShape(img_data.size(), std::begin(img_data), &dummy, &shape_info);
|
||||
if (FAILED(status)) {
|
||||
if(FAILED(status)) {
|
||||
BOOST_LOG(error) << "Failed to get new pointer shape [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
|
||||
return capture_e::error;
|
||||
@ -682,14 +682,14 @@ capture_e display_vram_t::snapshot(platf::img_t *img_base, std::chrono::millisec
|
||||
|
||||
// Create texture for cursor
|
||||
D3D11_TEXTURE2D_DESC t {};
|
||||
t.Width = shape_info.Width;
|
||||
t.Height = cursor_img.size() / data.SysMemPitch;
|
||||
t.MipLevels = 1;
|
||||
t.ArraySize = 1;
|
||||
t.Width = shape_info.Width;
|
||||
t.Height = cursor_img.size() / data.SysMemPitch;
|
||||
t.MipLevels = 1;
|
||||
t.ArraySize = 1;
|
||||
t.SampleDesc.Count = 1;
|
||||
t.Usage = D3D11_USAGE_DEFAULT;
|
||||
t.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
|
||||
t.BindFlags = D3D11_BIND_SHADER_RESOURCE;
|
||||
t.Usage = D3D11_USAGE_DEFAULT;
|
||||
t.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
|
||||
t.BindFlags = D3D11_BIND_SHADER_RESOURCE;
|
||||
|
||||
texture2d_t texture;
|
||||
auto status = device->CreateTexture2D(&t, &data, &texture);
|
||||
@ -734,14 +734,14 @@ std::shared_ptr<platf::img_t> display_vram_t::alloc_img() {
|
||||
auto img = std::make_shared<img_d3d_t>();
|
||||
|
||||
D3D11_TEXTURE2D_DESC t {};
|
||||
t.Width = width;
|
||||
t.Height = height;
|
||||
t.MipLevels = 1;
|
||||
t.ArraySize = 1;
|
||||
t.Width = width;
|
||||
t.Height = height;
|
||||
t.MipLevels = 1;
|
||||
t.ArraySize = 1;
|
||||
t.SampleDesc.Count = 1;
|
||||
t.Usage = D3D11_USAGE_DEFAULT;
|
||||
t.Format = format;
|
||||
t.BindFlags = D3D11_BIND_SHADER_RESOURCE;
|
||||
t.Usage = D3D11_USAGE_DEFAULT;
|
||||
t.Format = format;
|
||||
t.BindFlags = D3D11_BIND_SHADER_RESOURCE;
|
||||
|
||||
auto status = device->CreateTexture2D(&t, nullptr, &img->texture);
|
||||
if(FAILED(status)) {
|
||||
@ -749,7 +749,7 @@ std::shared_ptr<platf::img_t> display_vram_t::alloc_img() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
img->data = (std::uint8_t*)img->texture.get();
|
||||
img->data = (std::uint8_t *)img->texture.get();
|
||||
img->row_pitch = 0;
|
||||
img->pixel_pitch = 4;
|
||||
img->width = 0;
|
||||
@ -760,9 +760,9 @@ std::shared_ptr<platf::img_t> display_vram_t::alloc_img() {
|
||||
}
|
||||
|
||||
int display_vram_t::dummy_img(platf::img_t *img_base) {
|
||||
auto img = (img_d3d_t*)img_base;
|
||||
auto img = (img_d3d_t *)img_base;
|
||||
|
||||
img->row_pitch = width * 4;
|
||||
img->row_pitch = width * 4;
|
||||
auto dummy_data = std::make_unique<int[]>(width * height);
|
||||
D3D11_SUBRESOURCE_DATA data {
|
||||
dummy_data.get(),
|
||||
@ -770,14 +770,14 @@ int display_vram_t::dummy_img(platf::img_t *img_base) {
|
||||
};
|
||||
|
||||
D3D11_TEXTURE2D_DESC t {};
|
||||
t.Width = width;
|
||||
t.Height = height;
|
||||
t.MipLevels = 1;
|
||||
t.ArraySize = 1;
|
||||
t.Width = width;
|
||||
t.Height = height;
|
||||
t.MipLevels = 1;
|
||||
t.ArraySize = 1;
|
||||
t.SampleDesc.Count = 1;
|
||||
t.Usage = D3D11_USAGE_DEFAULT;
|
||||
t.Format = format;
|
||||
t.BindFlags = D3D11_BIND_SHADER_RESOURCE;
|
||||
t.Usage = D3D11_USAGE_DEFAULT;
|
||||
t.Format = format;
|
||||
t.BindFlags = D3D11_BIND_SHADER_RESOURCE;
|
||||
|
||||
dxgi::texture2d_t tex;
|
||||
auto status = device->CreateTexture2D(&t, &data, &tex);
|
||||
@ -787,7 +787,7 @@ int display_vram_t::dummy_img(platf::img_t *img_base) {
|
||||
}
|
||||
|
||||
img->texture = std::move(tex);
|
||||
img->data = (std::uint8_t*)img->texture.get();
|
||||
img->data = (std::uint8_t *)img->texture.get();
|
||||
img->height = height;
|
||||
img->width = width;
|
||||
img->pixel_pitch = 4;
|
||||
@ -864,4 +864,4 @@ int init() {
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
} // namespace platf::dxgi
|
||||
@ -1,12 +1,12 @@
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include <cmath>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
|
||||
#include <ws2tcpip.h>
|
||||
#include <winsock2.h>
|
||||
#include <windows.h>
|
||||
#include <winuser.h>
|
||||
#include <iphlpapi.h>
|
||||
#include <windows.h>
|
||||
#include <winsock2.h>
|
||||
#include <winuser.h>
|
||||
#include <ws2tcpip.h>
|
||||
|
||||
#include <ViGEm/Client.h>
|
||||
|
||||
@ -100,11 +100,11 @@ std::string from_sockaddr(const sockaddr *const socket_address) {
|
||||
|
||||
auto family = socket_address->sa_family;
|
||||
if(family == AF_INET6) {
|
||||
inet_ntop(AF_INET6, &((sockaddr_in6*)socket_address)->sin6_addr, data, INET6_ADDRSTRLEN);
|
||||
inet_ntop(AF_INET6, &((sockaddr_in6 *)socket_address)->sin6_addr, data, INET6_ADDRSTRLEN);
|
||||
}
|
||||
|
||||
if(family == AF_INET) {
|
||||
inet_ntop(AF_INET, &((sockaddr_in*)socket_address)->sin_addr, data, INET_ADDRSTRLEN);
|
||||
inet_ntop(AF_INET, &((sockaddr_in *)socket_address)->sin_addr, data, INET_ADDRSTRLEN);
|
||||
}
|
||||
|
||||
return std::string { data };
|
||||
@ -116,13 +116,13 @@ std::pair<std::uint16_t, std::string> from_sockaddr_ex(const sockaddr *const ip_
|
||||
auto family = ip_addr->sa_family;
|
||||
std::uint16_t port;
|
||||
if(family == AF_INET6) {
|
||||
inet_ntop(AF_INET6, &((sockaddr_in6*)ip_addr)->sin6_addr, data, INET6_ADDRSTRLEN);
|
||||
port = ((sockaddr_in6*)ip_addr)->sin6_port;
|
||||
inet_ntop(AF_INET6, &((sockaddr_in6 *)ip_addr)->sin6_addr, data, INET6_ADDRSTRLEN);
|
||||
port = ((sockaddr_in6 *)ip_addr)->sin6_port;
|
||||
}
|
||||
|
||||
if(family == AF_INET) {
|
||||
inet_ntop(AF_INET, &((sockaddr_in*)ip_addr)->sin_addr, data, INET_ADDRSTRLEN);
|
||||
port = ((sockaddr_in*)ip_addr)->sin_port;
|
||||
inet_ntop(AF_INET, &((sockaddr_in *)ip_addr)->sin_addr, data, INET_ADDRSTRLEN);
|
||||
port = ((sockaddr_in *)ip_addr)->sin_port;
|
||||
}
|
||||
|
||||
return { port, std::string { data } };
|
||||
@ -163,7 +163,7 @@ std::string get_mac_address(const std::string_view &address) {
|
||||
input_t input() {
|
||||
input_t result { new vigem_t {} };
|
||||
|
||||
auto vigem = (vigem_t*)result.get();
|
||||
auto vigem = (vigem_t *)result.get();
|
||||
if(vigem->init()) {
|
||||
return nullptr;
|
||||
}
|
||||
@ -176,7 +176,7 @@ retry:
|
||||
auto send = SendInput(1, &i, sizeof(INPUT));
|
||||
if(send != 1) {
|
||||
auto hDesk = pairInputDesktop();
|
||||
if (_lastKnownInputDesktop != hDesk) {
|
||||
if(_lastKnownInputDesktop != hDesk) {
|
||||
_lastKnownInputDesktop = hDesk;
|
||||
goto retry;
|
||||
}
|
||||
@ -186,7 +186,7 @@ retry:
|
||||
void abs_mouse(input_t &input, const touch_port_t &touch_port, float x, float y) {
|
||||
INPUT i {};
|
||||
|
||||
i.type = INPUT_MOUSE;
|
||||
i.type = INPUT_MOUSE;
|
||||
auto &mi = i.mi;
|
||||
|
||||
mi.dwFlags =
|
||||
@ -208,13 +208,13 @@ void abs_mouse(input_t &input, const touch_port_t &touch_port, float x, float y)
|
||||
void move_mouse(input_t &input, int deltaX, int deltaY) {
|
||||
INPUT i {};
|
||||
|
||||
i.type = INPUT_MOUSE;
|
||||
i.type = INPUT_MOUSE;
|
||||
auto &mi = i.mi;
|
||||
|
||||
mi.dwFlags = MOUSEEVENTF_MOVE;
|
||||
mi.dx = deltaX;
|
||||
mi.dy = deltaY;
|
||||
|
||||
mi.dx = deltaX;
|
||||
mi.dy = deltaY;
|
||||
|
||||
send_input(i);
|
||||
}
|
||||
|
||||
@ -223,34 +223,34 @@ void button_mouse(input_t &input, int button, bool release) {
|
||||
|
||||
INPUT i {};
|
||||
|
||||
i.type = INPUT_MOUSE;
|
||||
i.type = INPUT_MOUSE;
|
||||
auto &mi = i.mi;
|
||||
|
||||
int mouse_button;
|
||||
if(button == 1) {
|
||||
mi.dwFlags = release ? MOUSEEVENTF_LEFTUP : MOUSEEVENTF_LEFTDOWN;
|
||||
mi.dwFlags = release ? MOUSEEVENTF_LEFTUP : MOUSEEVENTF_LEFTDOWN;
|
||||
mouse_button = VK_LBUTTON;
|
||||
}
|
||||
else if(button == 2) {
|
||||
mi.dwFlags = release ? MOUSEEVENTF_MIDDLEUP : MOUSEEVENTF_MIDDLEDOWN;
|
||||
mi.dwFlags = release ? MOUSEEVENTF_MIDDLEUP : MOUSEEVENTF_MIDDLEDOWN;
|
||||
mouse_button = VK_MBUTTON;
|
||||
}
|
||||
else if(button == 3) {
|
||||
mi.dwFlags = release ? MOUSEEVENTF_RIGHTUP : MOUSEEVENTF_RIGHTDOWN;
|
||||
mi.dwFlags = release ? MOUSEEVENTF_RIGHTUP : MOUSEEVENTF_RIGHTDOWN;
|
||||
mouse_button = VK_RBUTTON;
|
||||
}
|
||||
else if(button == 4) {
|
||||
mi.dwFlags = release ? MOUSEEVENTF_XUP : MOUSEEVENTF_XDOWN;
|
||||
mi.dwFlags = release ? MOUSEEVENTF_XUP : MOUSEEVENTF_XDOWN;
|
||||
mi.mouseData = XBUTTON1;
|
||||
mouse_button = VK_XBUTTON1;
|
||||
}
|
||||
else {
|
||||
mi.dwFlags = release ? MOUSEEVENTF_XUP : MOUSEEVENTF_XDOWN;
|
||||
mi.dwFlags = release ? MOUSEEVENTF_XUP : MOUSEEVENTF_XDOWN;
|
||||
mi.mouseData = XBUTTON2;
|
||||
mouse_button = VK_XBUTTON2;
|
||||
}
|
||||
|
||||
auto key_state = GetAsyncKeyState(mouse_button);
|
||||
auto key_state = GetAsyncKeyState(mouse_button);
|
||||
bool key_state_down = (key_state & KEY_STATE_DOWN) != 0;
|
||||
if(key_state_down != release) {
|
||||
BOOST_LOG(warning) << "Button state of mouse_button ["sv << button << "] does not match the desired state"sv;
|
||||
@ -264,10 +264,10 @@ void button_mouse(input_t &input, int button, bool release) {
|
||||
void scroll(input_t &input, int distance) {
|
||||
INPUT i {};
|
||||
|
||||
i.type = INPUT_MOUSE;
|
||||
i.type = INPUT_MOUSE;
|
||||
auto &mi = i.mi;
|
||||
|
||||
mi.dwFlags = MOUSEEVENTF_WHEEL;
|
||||
mi.dwFlags = MOUSEEVENTF_WHEEL;
|
||||
mi.mouseData = distance;
|
||||
|
||||
send_input(i);
|
||||
@ -279,12 +279,12 @@ void keyboard(input_t &input, uint16_t modcode, bool release) {
|
||||
}
|
||||
|
||||
INPUT i {};
|
||||
i.type = INPUT_KEYBOARD;
|
||||
i.type = INPUT_KEYBOARD;
|
||||
auto &ki = i.ki;
|
||||
|
||||
// For some reason, MapVirtualKey(VK_LWIN, MAPVK_VK_TO_VSC) doesn't seem to work :/
|
||||
if(modcode != VK_LWIN && modcode != VK_RWIN && modcode != VK_PAUSE) {
|
||||
ki.wScan = MapVirtualKey(modcode, MAPVK_VK_TO_VSC);
|
||||
ki.wScan = MapVirtualKey(modcode, MAPVK_VK_TO_VSC);
|
||||
ki.dwFlags = KEYEVENTF_SCANCODE;
|
||||
}
|
||||
else {
|
||||
@ -293,23 +293,23 @@ void keyboard(input_t &input, uint16_t modcode, bool release) {
|
||||
|
||||
// https://docs.microsoft.com/en-us/windows/win32/inputdev/about-keyboard-input#keystroke-message-flags
|
||||
switch(modcode) {
|
||||
case VK_RMENU:
|
||||
case VK_RCONTROL:
|
||||
case VK_INSERT:
|
||||
case VK_DELETE:
|
||||
case VK_HOME:
|
||||
case VK_END:
|
||||
case VK_PRIOR:
|
||||
case VK_NEXT:
|
||||
case VK_UP:
|
||||
case VK_DOWN:
|
||||
case VK_LEFT:
|
||||
case VK_RIGHT:
|
||||
case VK_DIVIDE:
|
||||
ki.dwFlags |= KEYEVENTF_EXTENDEDKEY;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
case VK_RMENU:
|
||||
case VK_RCONTROL:
|
||||
case VK_INSERT:
|
||||
case VK_DELETE:
|
||||
case VK_HOME:
|
||||
case VK_END:
|
||||
case VK_PRIOR:
|
||||
case VK_NEXT:
|
||||
case VK_UP:
|
||||
case VK_DOWN:
|
||||
case VK_LEFT:
|
||||
case VK_RIGHT:
|
||||
case VK_DIVIDE:
|
||||
ki.dwFlags |= KEYEVENTF_EXTENDEDKEY;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if(release) {
|
||||
@ -324,7 +324,7 @@ int alloc_gamepad(input_t &input, int nr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return ((vigem_t*)input.get())->alloc_x360(nr);
|
||||
return ((vigem_t *)input.get())->alloc_x360(nr);
|
||||
}
|
||||
|
||||
void free_gamepad(input_t &input, int nr) {
|
||||
@ -332,7 +332,7 @@ void free_gamepad(input_t &input, int nr) {
|
||||
return;
|
||||
}
|
||||
|
||||
((vigem_t*)input.get())->free_target(nr);
|
||||
((vigem_t *)input.get())->free_target(nr);
|
||||
}
|
||||
void gamepad(input_t &input, int nr, const gamepad_state_t &gamepad_state) {
|
||||
// If there is no gamepad support
|
||||
@ -340,7 +340,7 @@ void gamepad(input_t &input, int nr, const gamepad_state_t &gamepad_state) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto vigem = (vigem_t*)input.get();
|
||||
auto vigem = (vigem_t *)input.get();
|
||||
|
||||
auto &xusb = *(PXUSB_REPORT)&gamepad_state;
|
||||
auto &x360 = vigem->x360s[nr];
|
||||
@ -354,19 +354,20 @@ void gamepad(input_t &input, int nr, const gamepad_state_t &gamepad_state) {
|
||||
}
|
||||
}
|
||||
|
||||
int thread_priority() {
|
||||
int thread_priority() {
|
||||
return SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST) ? 0 : 1;
|
||||
}
|
||||
|
||||
HDESK pairInputDesktop() {
|
||||
auto hDesk = OpenInputDesktop(DF_ALLOWOTHERACCOUNTHOOK, FALSE, GENERIC_ALL);
|
||||
if (NULL == hDesk) {
|
||||
if(NULL == hDesk) {
|
||||
auto err = GetLastError();
|
||||
BOOST_LOG(error) << "Failed to OpenInputDesktop [0x"sv << util::hex(err).to_string_view() << ']';
|
||||
}
|
||||
else {
|
||||
BOOST_LOG(info) << std::endl << "Opened desktop [0x"sv << util::hex(hDesk).to_string_view() << ']';
|
||||
if (!SetThreadDesktop(hDesk) ) {
|
||||
BOOST_LOG(info) << std::endl
|
||||
<< "Opened desktop [0x"sv << util::hex(hDesk).to_string_view() << ']';
|
||||
if(!SetThreadDesktop(hDesk)) {
|
||||
auto err = GetLastError();
|
||||
BOOST_LOG(error) << "Failed to SetThreadDesktop [0x"sv << util::hex(err).to_string_view() << ']';
|
||||
}
|
||||
@ -377,8 +378,8 @@ HDESK pairInputDesktop() {
|
||||
}
|
||||
|
||||
void freeInput(void *p) {
|
||||
auto vigem = (vigem_t*)p;
|
||||
auto vigem = (vigem_t *)p;
|
||||
|
||||
delete vigem;
|
||||
}
|
||||
}
|
||||
} // namespace platf
|
||||
|
||||
@ -4,14 +4,14 @@
|
||||
|
||||
#include "process.h"
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/property_tree/ptree.hpp>
|
||||
#include <boost/property_tree/json_parser.hpp>
|
||||
#include <boost/property_tree/ptree.hpp>
|
||||
|
||||
#include "utility.h"
|
||||
#include "main.h"
|
||||
#include "utility.h"
|
||||
|
||||
namespace proc {
|
||||
using namespace std::literals;
|
||||
@ -57,11 +57,11 @@ int proc_t::execute(int app_id) {
|
||||
// Ensure starting from a clean slate
|
||||
terminate();
|
||||
|
||||
_app_id = app_id;
|
||||
_app_id = app_id;
|
||||
auto &proc = _apps[app_id];
|
||||
|
||||
_undo_begin = std::begin(proc.prep_cmds);
|
||||
_undo_it = _undo_begin;
|
||||
_undo_it = _undo_begin;
|
||||
|
||||
if(!proc.output.empty() && proc.output != "null"sv) {
|
||||
_pipe.reset(fopen(proc.output.c_str(), "a"));
|
||||
@ -147,7 +147,7 @@ void proc_t::terminate() {
|
||||
std::abort();
|
||||
}
|
||||
|
||||
for(;_undo_it != _undo_begin; --_undo_it) {
|
||||
for(; _undo_it != _undo_begin; --_undo_it) {
|
||||
auto &cmd = (_undo_it - 1)->undo_cmd;
|
||||
|
||||
if(cmd.empty()) {
|
||||
@ -192,9 +192,11 @@ std::string_view::iterator find_match(std::string_view::iterator begin, std::str
|
||||
do {
|
||||
++begin;
|
||||
switch(*begin) {
|
||||
case '(': ++stack;
|
||||
case '(':
|
||||
++stack;
|
||||
break;
|
||||
case ')': --stack;
|
||||
case ')':
|
||||
--stack;
|
||||
}
|
||||
} while(begin != end && stack != 0);
|
||||
|
||||
@ -205,7 +207,7 @@ std::string_view::iterator find_match(std::string_view::iterator begin, std::str
|
||||
}
|
||||
|
||||
std::string parse_env_val(bp::native_environment &env, const std::string_view &val_raw) {
|
||||
auto pos = std::begin(val_raw);
|
||||
auto pos = std::begin(val_raw);
|
||||
auto dollar = std::find(pos, std::end(val_raw), '$');
|
||||
|
||||
std::stringstream ss;
|
||||
@ -214,23 +216,23 @@ std::string parse_env_val(bp::native_environment &env, const std::string_view &v
|
||||
auto next = dollar + 1;
|
||||
if(next != std::end(val_raw)) {
|
||||
switch(*next) {
|
||||
case '(': {
|
||||
ss.write(pos, (dollar - pos));
|
||||
auto var_begin = next + 1;
|
||||
auto var_end = find_match(next, std::end(val_raw));
|
||||
case '(': {
|
||||
ss.write(pos, (dollar - pos));
|
||||
auto var_begin = next + 1;
|
||||
auto var_end = find_match(next, std::end(val_raw));
|
||||
|
||||
ss << env[std::string { var_begin, var_end }].to_string();
|
||||
ss << env[std::string { var_begin, var_end }].to_string();
|
||||
|
||||
pos = var_end + 1;
|
||||
next = var_end;
|
||||
pos = var_end + 1;
|
||||
next = var_end;
|
||||
|
||||
break;
|
||||
}
|
||||
case '$':
|
||||
ss.write(pos, (next - pos));
|
||||
pos = next + 1;
|
||||
++next;
|
||||
break;
|
||||
break;
|
||||
}
|
||||
case '$':
|
||||
ss.write(pos, (next - pos));
|
||||
pos = next + 1;
|
||||
++next;
|
||||
break;
|
||||
}
|
||||
|
||||
dollar = std::find(next, std::end(val_raw), '$');
|
||||
@ -245,7 +247,7 @@ std::string parse_env_val(bp::native_environment &env, const std::string_view &v
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::optional<proc::proc_t> parse(const std::string& file_name) {
|
||||
std::optional<proc::proc_t> parse(const std::string &file_name) {
|
||||
pt::ptree tree;
|
||||
|
||||
try {
|
||||
@ -256,19 +258,19 @@ std::optional<proc::proc_t> parse(const std::string& file_name) {
|
||||
|
||||
auto this_env = boost::this_process::environment();
|
||||
|
||||
for(auto &[name,val] : env_vars) {
|
||||
for(auto &[name, val] : env_vars) {
|
||||
this_env[name] = parse_env_val(this_env, val.get_value<std::string>());
|
||||
}
|
||||
|
||||
std::vector<proc::ctx_t> apps;
|
||||
for(auto &[_,app_node] : apps_node) {
|
||||
for(auto &[_, app_node] : apps_node) {
|
||||
proc::ctx_t ctx;
|
||||
|
||||
auto prep_nodes_opt = app_node.get_child_optional("prep-cmd"s);
|
||||
auto prep_nodes_opt = app_node.get_child_optional("prep-cmd"s);
|
||||
auto detached_nodes_opt = app_node.get_child_optional("detached"s);
|
||||
auto output = app_node.get_optional<std::string>("output"s);
|
||||
auto name = parse_env_val(this_env, app_node.get<std::string>("name"s));
|
||||
auto cmd = app_node.get_optional<std::string>("cmd"s);
|
||||
auto output = app_node.get_optional<std::string>("output"s);
|
||||
auto name = parse_env_val(this_env, app_node.get<std::string>("name"s));
|
||||
auto cmd = app_node.get_optional<std::string>("cmd"s);
|
||||
|
||||
std::vector<proc::cmd_t> prep_cmds;
|
||||
if(prep_nodes_opt) {
|
||||
@ -276,7 +278,7 @@ std::optional<proc::proc_t> parse(const std::string& file_name) {
|
||||
|
||||
prep_cmds.reserve(prep_nodes.size());
|
||||
for(auto &[_, prep_node] : prep_nodes) {
|
||||
auto do_cmd = parse_env_val(this_env, prep_node.get<std::string>("do"s));
|
||||
auto do_cmd = parse_env_val(this_env, prep_node.get<std::string>("do"s));
|
||||
auto undo_cmd = prep_node.get_optional<std::string>("undo"s);
|
||||
|
||||
if(undo_cmd) {
|
||||
@ -306,9 +308,9 @@ std::optional<proc::proc_t> parse(const std::string& file_name) {
|
||||
ctx.cmd = parse_env_val(this_env, *cmd);
|
||||
}
|
||||
|
||||
ctx.name = std::move(name);
|
||||
ctx.name = std::move(name);
|
||||
ctx.prep_cmds = std::move(prep_cmds);
|
||||
ctx.detached = std::move(detached);
|
||||
ctx.detached = std::move(detached);
|
||||
|
||||
apps.emplace_back(std::move(ctx));
|
||||
}
|
||||
@ -316,7 +318,8 @@ std::optional<proc::proc_t> parse(const std::string& file_name) {
|
||||
return proc::proc_t {
|
||||
std::move(this_env), std::move(apps)
|
||||
};
|
||||
} catch (std::exception &e) {
|
||||
}
|
||||
catch(std::exception &e) {
|
||||
BOOST_LOG(error) << e.what();
|
||||
}
|
||||
|
||||
@ -330,4 +333,4 @@ void refresh(const std::string &file_name) {
|
||||
proc = std::move(*proc_opt);
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace proc
|
||||
|
||||
@ -9,8 +9,8 @@
|
||||
#define __kernel_entry
|
||||
#endif
|
||||
|
||||
#include <unordered_map>
|
||||
#include <optional>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <boost/process.hpp>
|
||||
|
||||
@ -61,10 +61,9 @@ public:
|
||||
|
||||
proc_t(
|
||||
boost::process::environment &&env,
|
||||
std::vector<ctx_t> &&apps) :
|
||||
_app_id(-1),
|
||||
_env(std::move(env)),
|
||||
_apps(std::move(apps)) {}
|
||||
std::vector<ctx_t> &&apps) : _app_id(-1),
|
||||
_env(std::move(env)),
|
||||
_apps(std::move(apps)) {}
|
||||
|
||||
int execute(int app_id);
|
||||
|
||||
@ -77,7 +76,7 @@ public:
|
||||
|
||||
const std::vector<ctx_t> &get_apps() const;
|
||||
std::vector<ctx_t> &get_apps();
|
||||
|
||||
|
||||
void terminate();
|
||||
|
||||
private:
|
||||
@ -98,8 +97,8 @@ private:
|
||||
};
|
||||
|
||||
void refresh(const std::string &file_name);
|
||||
std::optional<proc::proc_t> parse(const std::string& file_name);
|
||||
std::optional<proc::proc_t> parse(const std::string &file_name);
|
||||
|
||||
extern proc_t proc;
|
||||
}
|
||||
} // namespace proc
|
||||
#endif //SUNSHINE_PROCESS_H
|
||||
|
||||
@ -10,12 +10,12 @@ public:
|
||||
typedef T iterator;
|
||||
typedef typename std::iterator<std::random_access_iterator_tag, V>::value_type class_t;
|
||||
|
||||
typedef class_t& reference;
|
||||
typedef class_t* pointer;
|
||||
typedef class_t &reference;
|
||||
typedef class_t *pointer;
|
||||
|
||||
typedef std::ptrdiff_t diff_t;
|
||||
|
||||
iterator operator += (diff_t step) {
|
||||
iterator operator+=(diff_t step) {
|
||||
while(step-- > 0) {
|
||||
++_this();
|
||||
}
|
||||
@ -23,7 +23,7 @@ public:
|
||||
return _this();
|
||||
}
|
||||
|
||||
iterator operator -= (diff_t step) {
|
||||
iterator operator-=(diff_t step) {
|
||||
while(step-- > 0) {
|
||||
--_this();
|
||||
}
|
||||
@ -31,19 +31,19 @@ public:
|
||||
return _this();
|
||||
}
|
||||
|
||||
iterator operator +(diff_t step) {
|
||||
iterator operator+(diff_t step) {
|
||||
iterator new_ = _this();
|
||||
|
||||
return new_ += step;
|
||||
}
|
||||
|
||||
iterator operator -(diff_t step) {
|
||||
iterator operator-(diff_t step) {
|
||||
iterator new_ = _this();
|
||||
|
||||
return new_ -= step;
|
||||
}
|
||||
|
||||
diff_t operator -(iterator first) {
|
||||
diff_t operator-(iterator first) {
|
||||
diff_t step = 0;
|
||||
while(first != _this()) {
|
||||
++step;
|
||||
@ -53,8 +53,14 @@ public:
|
||||
return step;
|
||||
}
|
||||
|
||||
iterator operator++() { _this().inc(); return _this(); }
|
||||
iterator operator--() { _this().dec(); return _this(); }
|
||||
iterator operator++() {
|
||||
_this().inc();
|
||||
return _this();
|
||||
}
|
||||
iterator operator--() {
|
||||
_this().dec();
|
||||
return _this();
|
||||
}
|
||||
|
||||
iterator operator++(int) {
|
||||
iterator new_ = _this();
|
||||
@ -78,35 +84,35 @@ public:
|
||||
pointer operator->() { return &*_this(); }
|
||||
const pointer operator->() const { return &*_this(); }
|
||||
|
||||
bool operator != (const iterator &other) const {
|
||||
bool operator!=(const iterator &other) const {
|
||||
return !(_this() == other);
|
||||
}
|
||||
|
||||
bool operator < (const iterator &other) const {
|
||||
bool operator<(const iterator &other) const {
|
||||
return !(_this() >= other);
|
||||
}
|
||||
|
||||
bool operator >= (const iterator &other) const {
|
||||
bool operator>=(const iterator &other) const {
|
||||
return _this() == other || _this() > other;
|
||||
}
|
||||
|
||||
bool operator <= (const iterator &other) const {
|
||||
bool operator<=(const iterator &other) const {
|
||||
return _this() == other || _this() < other;
|
||||
}
|
||||
|
||||
bool operator == (const iterator &other) const { return _this().eq(other); };
|
||||
bool operator > (const iterator &other) const { return _this().gt(other); }
|
||||
private:
|
||||
bool operator==(const iterator &other) const { return _this().eq(other); };
|
||||
bool operator>(const iterator &other) const { return _this().gt(other); }
|
||||
|
||||
iterator &_this() { return *static_cast<iterator*>(this); }
|
||||
const iterator &_this() const { return *static_cast<const iterator*>(this); }
|
||||
private:
|
||||
iterator &_this() { return *static_cast<iterator *>(this); }
|
||||
const iterator &_this() const { return *static_cast<const iterator *>(this); }
|
||||
};
|
||||
|
||||
template<class V, class It>
|
||||
class round_robin_t : public it_wrap_t<V, round_robin_t<V, It>> {
|
||||
public:
|
||||
using iterator = It;
|
||||
using pointer = V*;
|
||||
using pointer = V *;
|
||||
|
||||
round_robin_t(iterator begin, iterator end) : _begin(begin), _end(end), _pos(begin) {}
|
||||
|
||||
@ -119,10 +125,10 @@ public:
|
||||
}
|
||||
|
||||
void dec() {
|
||||
if(_pos == _begin) {
|
||||
if(_pos == _begin) {
|
||||
_pos = _end;
|
||||
}
|
||||
|
||||
|
||||
--_pos;
|
||||
}
|
||||
|
||||
@ -133,6 +139,7 @@ public:
|
||||
pointer get() const {
|
||||
return &*_pos;
|
||||
}
|
||||
|
||||
private:
|
||||
It _begin;
|
||||
It _end;
|
||||
@ -144,6 +151,6 @@ template<class V, class It>
|
||||
round_robin_t<V, It> make_round_robin(It begin, It end) {
|
||||
return round_robin_t<V, It>(begin, end);
|
||||
}
|
||||
}
|
||||
} // namespace util
|
||||
|
||||
#endif
|
||||
|
||||
@ -7,10 +7,10 @@ extern "C" {
|
||||
}
|
||||
|
||||
#include "config.h"
|
||||
#include "input.h"
|
||||
#include "main.h"
|
||||
#include "network.h"
|
||||
#include "rtsp.h"
|
||||
#include "input.h"
|
||||
#include "stream.h"
|
||||
#include "sync.h"
|
||||
|
||||
@ -25,7 +25,7 @@ namespace stream {
|
||||
|
||||
//FIXME: Quick and dirty workaround for bug in MinGW 9.3 causing a linker error when using std::to_string
|
||||
template<class T>
|
||||
std::string to_string(T && t) {
|
||||
std::string to_string(T &&t) {
|
||||
std::stringstream ss;
|
||||
ss << std::forward<T>(t);
|
||||
return ss.str();
|
||||
@ -41,11 +41,11 @@ void free_msg(PRTSP_MESSAGE msg) {
|
||||
|
||||
class rtsp_server_t;
|
||||
|
||||
using msg_t = util::safe_ptr<RTSP_MESSAGE, free_msg>;
|
||||
using cmd_func_t = std::function<void(rtsp_server_t*, net::peer_t, msg_t&&)>;
|
||||
using msg_t = util::safe_ptr<RTSP_MESSAGE, free_msg>;
|
||||
using cmd_func_t = std::function<void(rtsp_server_t *, net::peer_t, msg_t &&)>;
|
||||
|
||||
void print_msg(PRTSP_MESSAGE msg);
|
||||
void cmd_not_found(net::host_t::pointer host, net::peer_t peer, msg_t&& req);
|
||||
void cmd_not_found(net::host_t::pointer host, net::peer_t peer, msg_t &&req);
|
||||
|
||||
class rtsp_server_t {
|
||||
public:
|
||||
@ -82,61 +82,61 @@ public:
|
||||
ENetEvent event;
|
||||
auto res = enet_host_service(_host.get(), &event, std::chrono::floor<std::chrono::milliseconds>(timeout).count());
|
||||
|
||||
if (res > 0) {
|
||||
switch (event.type) {
|
||||
case ENET_EVENT_TYPE_RECEIVE: {
|
||||
net::packet_t packet{event.packet};
|
||||
net::peer_t peer{event.peer};
|
||||
if(res > 0) {
|
||||
switch(event.type) {
|
||||
case ENET_EVENT_TYPE_RECEIVE: {
|
||||
net::packet_t packet { event.packet };
|
||||
net::peer_t peer { event.peer };
|
||||
|
||||
msg_t req { new msg_t::element_type };
|
||||
msg_t req { new msg_t::element_type };
|
||||
|
||||
//TODO: compare addresses of the peers
|
||||
if (_queue_packet.second == nullptr) {
|
||||
parseRtspMessage(req.get(), (char *) packet->data, packet->dataLength);
|
||||
for (auto option = req->options; option != nullptr; option = option->next) {
|
||||
if ("Content-length"sv == option->option) {
|
||||
_queue_packet = std::make_pair(peer, std::move(packet));
|
||||
return;
|
||||
}
|
||||
//TODO: compare addresses of the peers
|
||||
if(_queue_packet.second == nullptr) {
|
||||
parseRtspMessage(req.get(), (char *)packet->data, packet->dataLength);
|
||||
for(auto option = req->options; option != nullptr; option = option->next) {
|
||||
if("Content-length"sv == option->option) {
|
||||
_queue_packet = std::make_pair(peer, std::move(packet));
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
std::vector<char> full_payload;
|
||||
|
||||
auto old_msg = std::move(_queue_packet);
|
||||
auto &old_packet = old_msg.second;
|
||||
|
||||
std::string_view new_payload{(char *) packet->data, packet->dataLength};
|
||||
std::string_view old_payload{(char *) old_packet->data, old_packet->dataLength};
|
||||
full_payload.resize(new_payload.size() + old_payload.size());
|
||||
|
||||
std::copy(std::begin(old_payload), std::end(old_payload), std::begin(full_payload));
|
||||
std::copy(std::begin(new_payload), std::end(new_payload), std::begin(full_payload) + old_payload.size());
|
||||
|
||||
parseRtspMessage(req.get(), full_payload.data(), full_payload.size());
|
||||
}
|
||||
|
||||
print_msg(req.get());
|
||||
|
||||
msg_t resp;
|
||||
auto func = _map_cmd_cb.find(req->message.request.command);
|
||||
if (func != std::end(_map_cmd_cb)) {
|
||||
func->second(this, peer, std::move(req));
|
||||
}
|
||||
else {
|
||||
cmd_not_found(host(), peer, std::move(req));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
case ENET_EVENT_TYPE_CONNECT:
|
||||
BOOST_LOG(info) << "CLIENT CONNECTED TO RTSP"sv;
|
||||
break;
|
||||
case ENET_EVENT_TYPE_DISCONNECT:
|
||||
BOOST_LOG(info) << "CLIENT DISCONNECTED FROM RTSP"sv;
|
||||
break;
|
||||
case ENET_EVENT_TYPE_NONE:
|
||||
break;
|
||||
else {
|
||||
std::vector<char> full_payload;
|
||||
|
||||
auto old_msg = std::move(_queue_packet);
|
||||
auto &old_packet = old_msg.second;
|
||||
|
||||
std::string_view new_payload { (char *)packet->data, packet->dataLength };
|
||||
std::string_view old_payload { (char *)old_packet->data, old_packet->dataLength };
|
||||
full_payload.resize(new_payload.size() + old_payload.size());
|
||||
|
||||
std::copy(std::begin(old_payload), std::end(old_payload), std::begin(full_payload));
|
||||
std::copy(std::begin(new_payload), std::end(new_payload), std::begin(full_payload) + old_payload.size());
|
||||
|
||||
parseRtspMessage(req.get(), full_payload.data(), full_payload.size());
|
||||
}
|
||||
|
||||
print_msg(req.get());
|
||||
|
||||
msg_t resp;
|
||||
auto func = _map_cmd_cb.find(req->message.request.command);
|
||||
if(func != std::end(_map_cmd_cb)) {
|
||||
func->second(this, peer, std::move(req));
|
||||
}
|
||||
else {
|
||||
cmd_not_found(host(), peer, std::move(req));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
case ENET_EVENT_TYPE_CONNECT:
|
||||
BOOST_LOG(info) << "CLIENT CONNECTED TO RTSP"sv;
|
||||
break;
|
||||
case ENET_EVENT_TYPE_DISCONNECT:
|
||||
BOOST_LOG(info) << "CLIENT DISCONNECTED FROM RTSP"sv;
|
||||
break;
|
||||
case ENET_EVENT_TYPE_NONE:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -149,7 +149,7 @@ public:
|
||||
auto lg = _session_slots.lock();
|
||||
|
||||
for(auto &slot : *_session_slots) {
|
||||
if (slot && (all || session::state(*slot) == session::state_e::STOPPING)) {
|
||||
if(slot && (all || session::state(*slot) == session::state_e::STOPPING)) {
|
||||
session::stop(*slot);
|
||||
session::join(*slot);
|
||||
|
||||
@ -194,7 +194,6 @@ public:
|
||||
safe::event_t<launch_session_t> launch_event;
|
||||
|
||||
private:
|
||||
|
||||
// named _queue_packet because I want to make it an actual queue
|
||||
// It's like this for convenience sake
|
||||
std::pair<net::peer_t, net::packet_t> _queue_packet;
|
||||
@ -225,11 +224,11 @@ void respond(net::host_t::pointer host, net::peer_t peer, msg_t &resp) {
|
||||
auto payload = std::make_pair(resp->payload, resp->payloadLength);
|
||||
|
||||
auto lg = util::fail_guard([&]() {
|
||||
resp->payload = payload.first;
|
||||
resp->payload = payload.first;
|
||||
resp->payloadLength = payload.second;
|
||||
});
|
||||
|
||||
resp->payload = nullptr;
|
||||
resp->payload = nullptr;
|
||||
resp->payloadLength = 0;
|
||||
|
||||
int serialized_len;
|
||||
@ -264,35 +263,35 @@ void respond(net::host_t::pointer host, net::peer_t peer, msg_t &resp) {
|
||||
|
||||
void respond(net::host_t::pointer host, net::peer_t peer, POPTION_ITEM options, int statuscode, const char *status_msg, int seqn, const std::string_view &payload) {
|
||||
msg_t resp { new msg_t::element_type };
|
||||
createRtspResponse(resp.get(), nullptr, 0, const_cast<char*>("RTSP/1.0"), statuscode, const_cast<char*>(status_msg), seqn, options, const_cast<char*>(payload.data()), (int)payload.size());
|
||||
createRtspResponse(resp.get(), nullptr, 0, const_cast<char *>("RTSP/1.0"), statuscode, const_cast<char *>(status_msg), seqn, options, const_cast<char *>(payload.data()), (int)payload.size());
|
||||
|
||||
respond(host, peer, resp);
|
||||
}
|
||||
|
||||
void cmd_not_found(net::host_t::pointer host, net::peer_t peer, msg_t&& req) {
|
||||
void cmd_not_found(net::host_t::pointer host, net::peer_t peer, msg_t &&req) {
|
||||
respond(host, peer, nullptr, 404, "NOT FOUND", req->sequenceNumber, {});
|
||||
}
|
||||
|
||||
void cmd_option(rtsp_server_t *server, net::peer_t peer, msg_t&& req) {
|
||||
void cmd_option(rtsp_server_t *server, net::peer_t peer, msg_t &&req) {
|
||||
OPTION_ITEM option {};
|
||||
|
||||
// I know these string literals will not be modified
|
||||
option.option = const_cast<char*>("CSeq");
|
||||
option.option = const_cast<char *>("CSeq");
|
||||
|
||||
auto seqn_str = to_string(req->sequenceNumber);
|
||||
option.content = const_cast<char*>(seqn_str.c_str());
|
||||
auto seqn_str = to_string(req->sequenceNumber);
|
||||
option.content = const_cast<char *>(seqn_str.c_str());
|
||||
|
||||
respond(server->host(), peer, &option, 200, "OK", req->sequenceNumber, {});
|
||||
}
|
||||
|
||||
void cmd_describe(rtsp_server_t *server, net::peer_t peer, msg_t&& req) {
|
||||
void cmd_describe(rtsp_server_t *server, net::peer_t peer, msg_t &&req) {
|
||||
OPTION_ITEM option {};
|
||||
|
||||
// I know these string literals will not be modified
|
||||
option.option = const_cast<char*>("CSeq");
|
||||
option.option = const_cast<char *>("CSeq");
|
||||
|
||||
auto seqn_str = to_string(req->sequenceNumber);
|
||||
option.content = const_cast<char*>(seqn_str.c_str());
|
||||
auto seqn_str = to_string(req->sequenceNumber);
|
||||
option.content = const_cast<char *>(seqn_str.c_str());
|
||||
|
||||
std::string_view payload;
|
||||
if(config::video.hevc_mode == 1) {
|
||||
@ -311,10 +310,10 @@ void cmd_setup(rtsp_server_t *server, net::peer_t peer, msg_t &&req) {
|
||||
auto &seqn = options[0];
|
||||
auto &session_option = options[1];
|
||||
|
||||
seqn.option = const_cast<char*>("CSeq");
|
||||
seqn.option = const_cast<char *>("CSeq");
|
||||
|
||||
auto seqn_str = to_string(req->sequenceNumber);
|
||||
seqn.content = const_cast<char*>(seqn_str.c_str());
|
||||
seqn.content = const_cast<char *>(seqn_str.c_str());
|
||||
|
||||
std::string_view target { req->message.request.target };
|
||||
auto begin = std::find(std::begin(target), std::end(target), '=') + 1;
|
||||
@ -324,8 +323,8 @@ void cmd_setup(rtsp_server_t *server, net::peer_t peer, msg_t &&req) {
|
||||
if(type == "audio"sv) {
|
||||
seqn.next = &session_option;
|
||||
|
||||
session_option.option = const_cast<char*>("Session");
|
||||
session_option.content = const_cast<char*>("DEADBEEFCAFE;timeout = 90");
|
||||
session_option.option = const_cast<char *>("Session");
|
||||
session_option.content = const_cast<char *>("DEADBEEFCAFE;timeout = 90");
|
||||
}
|
||||
else if(type != "video"sv && type != "control"sv) {
|
||||
cmd_not_found(server->host(), peer, std::move(req));
|
||||
@ -340,10 +339,10 @@ void cmd_announce(rtsp_server_t *server, net::peer_t peer, msg_t &&req) {
|
||||
OPTION_ITEM option {};
|
||||
|
||||
// I know these string literals will not be modified
|
||||
option.option = const_cast<char*>("CSeq");
|
||||
option.option = const_cast<char *>("CSeq");
|
||||
|
||||
auto seqn_str = to_string(req->sequenceNumber);
|
||||
option.content = const_cast<char*>(seqn_str.c_str());
|
||||
auto seqn_str = to_string(req->sequenceNumber);
|
||||
option.content = const_cast<char *>(seqn_str.c_str());
|
||||
|
||||
if(!server->launch_event.peek()) {
|
||||
// /launch has not been used
|
||||
@ -362,10 +361,10 @@ void cmd_announce(rtsp_server_t *server, net::peer_t peer, msg_t &&req) {
|
||||
};
|
||||
|
||||
{
|
||||
auto pos = std::begin(payload);
|
||||
auto pos = std::begin(payload);
|
||||
auto begin = pos;
|
||||
while (pos != std::end(payload)) {
|
||||
if (whitespace(*pos++)) {
|
||||
while(pos != std::end(payload)) {
|
||||
if(whitespace(*pos++)) {
|
||||
lines.emplace_back(begin, pos - begin - 1);
|
||||
|
||||
while(pos != std::end(payload) && whitespace(*pos)) { ++pos; }
|
||||
@ -386,10 +385,10 @@ void cmd_announce(rtsp_server_t *server, net::peer_t peer, msg_t &&req) {
|
||||
auto pos = line.find(':');
|
||||
|
||||
auto name = line.substr(2, pos - 2);
|
||||
auto val = line.substr(pos + 1);
|
||||
auto val = line.substr(pos + 1);
|
||||
|
||||
if(val[val.size() -1] == ' ') {
|
||||
val = val.substr(0, val.size() -1);
|
||||
if(val[val.size() - 1] == ' ') {
|
||||
val = val.substr(0, val.size() - 1);
|
||||
}
|
||||
args.emplace(name, val);
|
||||
}
|
||||
@ -418,8 +417,8 @@ void cmd_announce(rtsp_server_t *server, net::peer_t peer, msg_t &&req) {
|
||||
config.monitor.encoderCscMode = util::from_view(args.at("x-nv-video[0].encoderCscMode"sv));
|
||||
config.monitor.videoFormat = util::from_view(args.at("x-nv-vqos[0].bitStreamFormat"sv));
|
||||
config.monitor.dynamicRange = util::from_view(args.at("x-nv-video[0].dynamicRangeMode"sv));
|
||||
|
||||
} catch(std::out_of_range &) {
|
||||
}
|
||||
catch(std::out_of_range &) {
|
||||
|
||||
respond(server->host(), peer, &option, 400, "BAD REQUEST", req->sequenceNumber, {});
|
||||
return;
|
||||
@ -442,7 +441,7 @@ void cmd_announce(rtsp_server_t *server, net::peer_t peer, msg_t &&req) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(session::start(*session, platf::from_sockaddr((sockaddr*)&peer->address.address))) {
|
||||
if(session::start(*session, platf::from_sockaddr((sockaddr *)&peer->address.address))) {
|
||||
BOOST_LOG(error) << "Failed to start a streaming session"sv;
|
||||
|
||||
server->clear(slot);
|
||||
@ -457,10 +456,10 @@ void cmd_play(rtsp_server_t *server, net::peer_t peer, msg_t &&req) {
|
||||
OPTION_ITEM option {};
|
||||
|
||||
// I know these string literals will not be modified
|
||||
option.option = const_cast<char*>("CSeq");
|
||||
option.option = const_cast<char *>("CSeq");
|
||||
|
||||
auto seqn_str = to_string(req->sequenceNumber);
|
||||
option.content = const_cast<char*>(seqn_str.c_str());
|
||||
auto seqn_str = to_string(req->sequenceNumber);
|
||||
option.content = const_cast<char *>(seqn_str.c_str());
|
||||
|
||||
respond(server->host(), peer, &option, 200, "OK", req->sequenceNumber, {});
|
||||
}
|
||||
@ -518,7 +517,7 @@ void print_msg(PRTSP_MESSAGE msg) {
|
||||
BOOST_LOG(debug) << "status :: "sv << status;
|
||||
}
|
||||
else {
|
||||
auto& req = msg->message.request;
|
||||
auto &req = msg->message.request;
|
||||
|
||||
std::string_view command { req.command };
|
||||
std::string_view target { req.target };
|
||||
@ -534,6 +533,8 @@ void print_msg(PRTSP_MESSAGE msg) {
|
||||
BOOST_LOG(debug) << name << " :: "sv << content;
|
||||
}
|
||||
|
||||
BOOST_LOG(debug) << "---Begin MessageBuffer---"sv << std::endl << messageBuffer << std::endl << "---End MessageBuffer---"sv << std::endl;
|
||||
}
|
||||
BOOST_LOG(debug) << "---Begin MessageBuffer---"sv << std::endl
|
||||
<< messageBuffer << std::endl
|
||||
<< "---End MessageBuffer---"sv << std::endl;
|
||||
}
|
||||
} // namespace stream
|
||||
|
||||
@ -21,6 +21,6 @@ int session_count();
|
||||
|
||||
void rtpThread(std::shared_ptr<safe::signal_t> shutdown_event);
|
||||
|
||||
}
|
||||
} // namespace stream
|
||||
|
||||
#endif //SUNSHINE_RTSP_H
|
||||
|
||||
@ -4,8 +4,8 @@
|
||||
|
||||
#include "process.h"
|
||||
|
||||
#include <queue>
|
||||
#include <future>
|
||||
#include <queue>
|
||||
|
||||
#include <fstream>
|
||||
#include <openssl/err.h>
|
||||
@ -15,14 +15,14 @@ extern "C" {
|
||||
#include <rs.h>
|
||||
}
|
||||
|
||||
#include "network.h"
|
||||
#include "config.h"
|
||||
#include "utility.h"
|
||||
#include "stream.h"
|
||||
#include "thread_safe.h"
|
||||
#include "sync.h"
|
||||
#include "input.h"
|
||||
#include "main.h"
|
||||
#include "network.h"
|
||||
#include "stream.h"
|
||||
#include "sync.h"
|
||||
#include "thread_safe.h"
|
||||
#include "utility.h"
|
||||
|
||||
#define IDX_START_A 0
|
||||
#define IDX_REQUEST_IDR_FRAME 0
|
||||
@ -45,7 +45,7 @@ static const short packetTypes[] = {
|
||||
};
|
||||
|
||||
constexpr auto VIDEO_STREAM_PORT = 47998;
|
||||
constexpr auto CONTROL_PORT = 47999;
|
||||
constexpr auto CONTROL_PORT = 47999;
|
||||
constexpr auto AUDIO_STREAM_PORT = 48000;
|
||||
|
||||
namespace asio = boost::asio;
|
||||
@ -89,7 +89,7 @@ using rh_t = util::safe_ptr<reed_solomon, reed_solomon_release>;
|
||||
using video_packet_t = util::c_ptr<video_packet_raw_t>;
|
||||
using audio_packet_t = util::c_ptr<audio_packet_raw_t>;
|
||||
|
||||
using message_queue_t = std::shared_ptr<safe::queue_t<std::pair<std::uint16_t, std::string>>>;
|
||||
using message_queue_t = std::shared_ptr<safe::queue_t<std::pair<std::uint16_t, std::string>>>;
|
||||
using message_queue_queue_t = std::shared_ptr<safe::queue_t<std::tuple<socket_e, asio::ip::address, message_queue_t>>>;
|
||||
|
||||
static inline void while_starting_do_nothing(std::atomic<session::state_e> &state) {
|
||||
@ -124,7 +124,7 @@ public:
|
||||
// Therefore, iterate is implemented further down the source file
|
||||
void iterate(std::chrono::milliseconds timeout);
|
||||
|
||||
void map(uint16_t type, std::function<void(session_t *, const std::string_view&)> cb) {
|
||||
void map(uint16_t type, std::function<void(session_t *, const std::string_view &)> cb) {
|
||||
_map_type_cb.emplace(type, std::move(cb));
|
||||
}
|
||||
|
||||
@ -140,10 +140,10 @@ public:
|
||||
}
|
||||
|
||||
// Callbacks
|
||||
std::unordered_map<std::uint16_t, std::function<void(session_t *, const std::string_view&)>> _map_type_cb;
|
||||
std::unordered_map<std::uint16_t, std::function<void(session_t *, const std::string_view &)>> _map_type_cb;
|
||||
|
||||
// Mapping ip:port to session
|
||||
util::sync_t<std::unordered_multimap<std::string, std::pair<std::uint16_t, session_t*>>> _map_addr_session;
|
||||
util::sync_t<std::unordered_multimap<std::string, std::pair<std::uint16_t, session_t *>>> _map_addr_session;
|
||||
|
||||
ENetAddress _addr;
|
||||
net::host_t _host;
|
||||
@ -210,7 +210,7 @@ static auto broadcast = safe::make_shared<broadcast_ctx_t>(start_broadcast, end_
|
||||
safe::signal_t broadcast_shutdown_event;
|
||||
|
||||
session_t *control_server_t::get_session(const net::peer_t peer) {
|
||||
TUPLE_2D(port, addr_string, platf::from_sockaddr_ex((sockaddr*)&peer->address.address));
|
||||
TUPLE_2D(port, addr_string, platf::from_sockaddr_ex((sockaddr *)&peer->address.address));
|
||||
|
||||
auto lg = _map_addr_session.lock();
|
||||
TUPLE_2D(begin, end, _map_addr_session->equal_range(addr_string));
|
||||
@ -231,7 +231,7 @@ session_t *control_server_t::get_session(const net::peer_t peer) {
|
||||
TUPLE_2D_REF(session_port, session_p, it->second);
|
||||
|
||||
session_p->control.peer = peer;
|
||||
session_port = port;
|
||||
session_port = port;
|
||||
|
||||
return session_p;
|
||||
}
|
||||
@ -246,7 +246,7 @@ void control_server_t::iterate(std::chrono::milliseconds timeout) {
|
||||
if(res > 0) {
|
||||
auto session = get_session(event.peer);
|
||||
if(!session) {
|
||||
BOOST_LOG(warning) << "Rejected connection from ["sv << platf::from_sockaddr((sockaddr*)&event.peer->address.address) << "]: it's not properly set up"sv;
|
||||
BOOST_LOG(warning) << "Rejected connection from ["sv << platf::from_sockaddr((sockaddr *)&event.peer->address.address) << "]: it's not properly set up"sv;
|
||||
enet_peer_disconnect_now(event.peer, 0);
|
||||
|
||||
return;
|
||||
@ -255,37 +255,37 @@ void control_server_t::iterate(std::chrono::milliseconds timeout) {
|
||||
session->pingTimeout = std::chrono::steady_clock::now() + config::stream.ping_timeout;
|
||||
|
||||
switch(event.type) {
|
||||
case ENET_EVENT_TYPE_RECEIVE:
|
||||
{
|
||||
net::packet_t packet { event.packet };
|
||||
case ENET_EVENT_TYPE_RECEIVE: {
|
||||
net::packet_t packet { event.packet };
|
||||
|
||||
auto type = (std::uint16_t *)packet->data;
|
||||
std::string_view payload { (char*)packet->data + sizeof(*type), packet->dataLength - sizeof(*type) };
|
||||
auto type = (std::uint16_t *)packet->data;
|
||||
std::string_view payload { (char *)packet->data + sizeof(*type), packet->dataLength - sizeof(*type) };
|
||||
|
||||
auto cb = _map_type_cb.find(*type);
|
||||
if(cb == std::end(_map_type_cb)) {
|
||||
BOOST_LOG(warning)
|
||||
<< "type [Unknown] { "sv << util::hex(*type).to_string_view() << " }"sv << std::endl
|
||||
<< "---data---"sv << std::endl << util::hex_vec(payload) << std::endl << "---end data---"sv;
|
||||
}
|
||||
|
||||
else {
|
||||
cb->second(session, payload);
|
||||
}
|
||||
auto cb = _map_type_cb.find(*type);
|
||||
if(cb == std::end(_map_type_cb)) {
|
||||
BOOST_LOG(warning)
|
||||
<< "type [Unknown] { "sv << util::hex(*type).to_string_view() << " }"sv << std::endl
|
||||
<< "---data---"sv << std::endl
|
||||
<< util::hex_vec(payload) << std::endl
|
||||
<< "---end data---"sv;
|
||||
}
|
||||
break;
|
||||
case ENET_EVENT_TYPE_CONNECT:
|
||||
BOOST_LOG(info) << "CLIENT CONNECTED"sv;
|
||||
break;
|
||||
case ENET_EVENT_TYPE_DISCONNECT:
|
||||
BOOST_LOG(info) << "CLIENT DISCONNECTED"sv;
|
||||
// No more clients to send video data to ^_^
|
||||
if(session->state == session::state_e::RUNNING) {
|
||||
session::stop(*session);
|
||||
}
|
||||
break;
|
||||
case ENET_EVENT_TYPE_NONE:
|
||||
break;
|
||||
|
||||
else {
|
||||
cb->second(session, payload);
|
||||
}
|
||||
} break;
|
||||
case ENET_EVENT_TYPE_CONNECT:
|
||||
BOOST_LOG(info) << "CLIENT CONNECTED"sv;
|
||||
break;
|
||||
case ENET_EVENT_TYPE_DISCONNECT:
|
||||
BOOST_LOG(info) << "CLIENT DISCONNECTED"sv;
|
||||
// No more clients to send video data to ^_^
|
||||
if(session->state == session::state_e::RUNNING) {
|
||||
session::stop(*session);
|
||||
}
|
||||
break;
|
||||
case ENET_EVENT_TYPE_NONE:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -302,11 +302,11 @@ struct fec_t {
|
||||
util::buffer_t<char> shards;
|
||||
|
||||
char *data(size_t el) {
|
||||
return &shards[el*blocksize];
|
||||
return &shards[el * blocksize];
|
||||
}
|
||||
|
||||
std::string_view operator[](size_t el) const {
|
||||
return { &shards[el*blocksize], blocksize };
|
||||
return { &shards[el * blocksize], blocksize };
|
||||
}
|
||||
|
||||
size_t size() const {
|
||||
@ -321,7 +321,7 @@ fec_t encode(const std::string_view &payload, size_t blocksize, size_t fecpercen
|
||||
|
||||
auto data_shards = payload_size / blocksize + (pad ? 1 : 0);
|
||||
auto parity_shards = (data_shards * fecpercentage + 99) / 100;
|
||||
auto nr_shards = data_shards + parity_shards;
|
||||
auto nr_shards = data_shards + parity_shards;
|
||||
|
||||
if(nr_shards > DATA_SHARDS_MAX) {
|
||||
BOOST_LOG(error)
|
||||
@ -332,14 +332,14 @@ fec_t encode(const std::string_view &payload, size_t blocksize, size_t fecpercen
|
||||
}
|
||||
|
||||
util::buffer_t<char> shards { nr_shards * blocksize };
|
||||
util::buffer_t<uint8_t*> shards_p { nr_shards };
|
||||
util::buffer_t<uint8_t *> shards_p { nr_shards };
|
||||
|
||||
// copy payload + padding
|
||||
auto next = std::copy(std::begin(payload), std::end(payload), std::begin(shards));
|
||||
std::fill(next, std::end(shards), 0); // padding with zero
|
||||
|
||||
for(auto x = 0; x < nr_shards; ++x) {
|
||||
shards_p[x] = (uint8_t*)&shards[x * blocksize];
|
||||
shards_p[x] = (uint8_t *)&shards[x * blocksize];
|
||||
}
|
||||
|
||||
// packets = parity_shards + data_shards
|
||||
@ -355,11 +355,11 @@ fec_t encode(const std::string_view &payload, size_t blocksize, size_t fecpercen
|
||||
std::move(shards)
|
||||
};
|
||||
}
|
||||
}
|
||||
} // namespace fec
|
||||
|
||||
template<class F>
|
||||
std::vector<uint8_t> insert(uint64_t insert_size, uint64_t slice_size, const std::string_view &data, F &&f) {
|
||||
auto pad = data.size() % slice_size != 0;
|
||||
auto pad = data.size() % slice_size != 0;
|
||||
auto elements = data.size() / slice_size + (pad ? 1 : 0);
|
||||
|
||||
std::vector<uint8_t> result;
|
||||
@ -367,20 +367,20 @@ std::vector<uint8_t> insert(uint64_t insert_size, uint64_t slice_size, const std
|
||||
|
||||
auto next = std::begin(data);
|
||||
for(auto x = 0; x < elements - 1; ++x) {
|
||||
void *p = &result[x*(insert_size + slice_size)];
|
||||
void *p = &result[x * (insert_size + slice_size)];
|
||||
|
||||
f(p, x, elements);
|
||||
|
||||
std::copy(next, next + slice_size, (char*)p + insert_size);
|
||||
std::copy(next, next + slice_size, (char *)p + insert_size);
|
||||
next += slice_size;
|
||||
}
|
||||
|
||||
auto x = elements - 1;
|
||||
void *p = &result[x*(insert_size + slice_size)];
|
||||
auto x = elements - 1;
|
||||
void *p = &result[x * (insert_size + slice_size)];
|
||||
|
||||
f(p, x, elements);
|
||||
|
||||
std::copy(next, std::end(data), (char*)p + insert_size);
|
||||
std::copy(next, std::end(data), (char *)p + insert_size);
|
||||
|
||||
return result;
|
||||
}
|
||||
@ -389,7 +389,7 @@ std::vector<uint8_t> replace(const std::string_view &original, const std::string
|
||||
std::vector<uint8_t> replaced;
|
||||
|
||||
auto begin = std::begin(original);
|
||||
auto next = std::search(begin, std::end(original), std::begin(old), std::end(old));
|
||||
auto next = std::search(begin, std::end(original), std::begin(old), std::end(old));
|
||||
|
||||
std::copy(begin, next, std::back_inserter(replaced));
|
||||
std::copy(std::begin(_new), std::end(_new), std::back_inserter(replaced));
|
||||
@ -408,8 +408,8 @@ void controlBroadcastThread(safe::signal_t *shutdown_event, control_server_t *se
|
||||
});
|
||||
|
||||
server->map(packetTypes[IDX_LOSS_STATS], [&](session_t *session, const std::string_view &payload) {
|
||||
int32_t *stats = (int32_t*)payload.data();
|
||||
auto count = stats[0];
|
||||
int32_t *stats = (int32_t *)payload.data();
|
||||
auto count = stats[0];
|
||||
std::chrono::milliseconds t { stats[1] };
|
||||
|
||||
auto lastGoodFrame = stats[3];
|
||||
@ -424,9 +424,9 @@ void controlBroadcastThread(safe::signal_t *shutdown_event, control_server_t *se
|
||||
});
|
||||
|
||||
server->map(packetTypes[IDX_INVALIDATE_REF_FRAMES], [&](session_t *session, const std::string_view &payload) {
|
||||
auto frames = (std::int64_t *)payload.data();
|
||||
auto frames = (std::int64_t *)payload.data();
|
||||
auto firstFrame = frames[0];
|
||||
auto lastFrame = frames[1];
|
||||
auto lastFrame = frames[1];
|
||||
|
||||
BOOST_LOG(debug)
|
||||
<< "type [IDX_INVALIDATE_REF_FRAMES]"sv << std::endl
|
||||
@ -439,7 +439,7 @@ void controlBroadcastThread(safe::signal_t *shutdown_event, control_server_t *se
|
||||
server->map(packetTypes[IDX_INPUT_DATA], [&](session_t *session, const std::string_view &payload) {
|
||||
BOOST_LOG(debug) << "type [IDX_INPUT_DATA]"sv;
|
||||
|
||||
int32_t tagged_cipher_length = util::endian::big(*(int32_t*)payload.data());
|
||||
int32_t tagged_cipher_length = util::endian::big(*(int32_t *)payload.data());
|
||||
std::string_view tagged_cipher { payload.data() + sizeof(tagged_cipher_length), (size_t)tagged_cipher_length };
|
||||
|
||||
crypto::cipher_t cipher { session->gcm_key };
|
||||
@ -498,7 +498,7 @@ void controlBroadcastThread(safe::signal_t *shutdown_event, control_server_t *se
|
||||
payload[0] = packetTypes[IDX_TERMINATION];
|
||||
payload[1] = reason;
|
||||
|
||||
server->send(std::string_view {(char*)payload.data(), payload.size()});
|
||||
server->send(std::string_view { (char *)payload.data(), payload.size() });
|
||||
|
||||
auto lg = server->_map_addr_session.lock();
|
||||
for(auto pos = std::begin(*server->_map_addr_session); pos != std::end(*server->_map_addr_session); ++pos) {
|
||||
@ -519,7 +519,7 @@ void recvThread(broadcast_ctx_t &ctx) {
|
||||
auto &audio_sock = ctx.audio_sock;
|
||||
|
||||
auto &message_queue_queue = ctx.message_queue_queue;
|
||||
auto &io = ctx.io;
|
||||
auto &io = ctx.io;
|
||||
|
||||
udp::endpoint peer;
|
||||
|
||||
@ -532,28 +532,28 @@ void recvThread(broadcast_ctx_t &ctx) {
|
||||
TUPLE_3D_REF(socket_type, addr, message_queue, *message_queue_opt);
|
||||
|
||||
switch(socket_type) {
|
||||
case socket_e::video:
|
||||
if(message_queue) {
|
||||
peer_to_video_session.emplace(addr, message_queue);
|
||||
}
|
||||
else {
|
||||
peer_to_video_session.erase(addr);
|
||||
}
|
||||
break;
|
||||
case socket_e::audio:
|
||||
if(message_queue) {
|
||||
peer_to_audio_session.emplace(addr, message_queue);
|
||||
}
|
||||
else {
|
||||
peer_to_audio_session.erase(addr);
|
||||
}
|
||||
break;
|
||||
case socket_e::video:
|
||||
if(message_queue) {
|
||||
peer_to_video_session.emplace(addr, message_queue);
|
||||
}
|
||||
else {
|
||||
peer_to_video_session.erase(addr);
|
||||
}
|
||||
break;
|
||||
case socket_e::audio:
|
||||
if(message_queue) {
|
||||
peer_to_audio_session.emplace(addr, message_queue);
|
||||
}
|
||||
else {
|
||||
peer_to_audio_session.erase(addr);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
auto recv_func_init = [&](udp::socket &sock, int buf_elem, std::map<asio::ip::address, message_queue_t> &peer_to_session) {
|
||||
recv_func[buf_elem] = [&,buf_elem](const boost::system::error_code &ec, size_t bytes) {
|
||||
recv_func[buf_elem] = [&, buf_elem](const boost::system::error_code &ec, size_t bytes) {
|
||||
auto fg = util::fail_guard([&]() {
|
||||
sock.async_receive_from(asio::buffer(buf[buf_elem]), peer, 0, recv_func[buf_elem]);
|
||||
});
|
||||
@ -601,20 +601,20 @@ void videoBroadcastThread(safe::signal_t *shutdown_event, udp::socket &sock, vid
|
||||
break;
|
||||
}
|
||||
|
||||
auto session = (session_t*)packet->channel_data;
|
||||
auto lowseq = session->video.lowseq;
|
||||
auto session = (session_t *)packet->channel_data;
|
||||
auto lowseq = session->video.lowseq;
|
||||
|
||||
std::string_view payload{(char *) packet->data, (size_t) packet->size};
|
||||
std::string_view payload { (char *)packet->data, (size_t)packet->size };
|
||||
std::vector<uint8_t> payload_new;
|
||||
|
||||
auto nv_packet_header = "\0017charss"sv;
|
||||
std::copy(std::begin(nv_packet_header), std::end(nv_packet_header), std::back_inserter(payload_new));
|
||||
std::copy(std::begin(payload), std::end(payload), std::back_inserter(payload_new));
|
||||
|
||||
payload = {(char *) payload_new.data(), payload_new.size()};
|
||||
payload = { (char *)payload_new.data(), payload_new.size() };
|
||||
|
||||
// make sure moonlight recognizes the nalu code for IDR frames
|
||||
if (packet->flags & AV_PKT_FLAG_KEY) {
|
||||
if(packet->flags & AV_PKT_FLAG_KEY) {
|
||||
// TODO: Not all encoders encode their IDR frames with the 4 byte NALU prefix
|
||||
std::string_view frame_old = "\000\000\001e"sv;
|
||||
std::string_view frame_new = "\000\000\000\001e"sv;
|
||||
@ -624,27 +624,25 @@ void videoBroadcastThread(safe::signal_t *shutdown_event, udp::socket &sock, vid
|
||||
}
|
||||
|
||||
payload_new = replace(payload, frame_old, frame_new);
|
||||
payload = {(char *) payload_new.data(), payload_new.size()};
|
||||
payload = { (char *)payload_new.data(), payload_new.size() };
|
||||
}
|
||||
|
||||
// insert packet headers
|
||||
auto blocksize = session->config.packetsize + MAX_RTP_HEADER_SIZE;
|
||||
auto blocksize = session->config.packetsize + MAX_RTP_HEADER_SIZE;
|
||||
auto payload_blocksize = blocksize - sizeof(video_packet_raw_t);
|
||||
|
||||
auto fecPercentage = config::stream.fec_percentage;
|
||||
|
||||
payload_new = insert(sizeof(video_packet_raw_t), payload_blocksize,
|
||||
payload, [&](void *p, int fecIndex, int end) {
|
||||
payload, [&](void *p, int fecIndex, int end) {
|
||||
video_packet_raw_t *video_packet = (video_packet_raw_t *)p;
|
||||
|
||||
video_packet->packet.flags = FLAG_CONTAINS_PIC_DATA;
|
||||
video_packet->packet.frameIndex = packet->pts;
|
||||
video_packet->packet.flags = FLAG_CONTAINS_PIC_DATA;
|
||||
video_packet->packet.frameIndex = packet->pts;
|
||||
video_packet->packet.streamPacketIndex = ((uint32_t)lowseq + fecIndex) << 8;
|
||||
video_packet->packet.fecInfo = (
|
||||
fecIndex << 12 |
|
||||
end << 22 |
|
||||
fecPercentage << 4
|
||||
);
|
||||
video_packet->packet.fecInfo = (fecIndex << 12 |
|
||||
end << 22 |
|
||||
fecPercentage << 4);
|
||||
|
||||
if(fecIndex == 0) {
|
||||
video_packet->packet.flags |= FLAG_SOF;
|
||||
@ -654,11 +652,11 @@ void videoBroadcastThread(safe::signal_t *shutdown_event, udp::socket &sock, vid
|
||||
video_packet->packet.flags |= FLAG_EOF;
|
||||
}
|
||||
|
||||
video_packet->rtp.header = FLAG_EXTENSION;
|
||||
video_packet->rtp.header = FLAG_EXTENSION;
|
||||
video_packet->rtp.sequenceNumber = util::endian::big<uint16_t>(lowseq + fecIndex);
|
||||
});
|
||||
|
||||
payload = {(char *) payload_new.data(), payload_new.size()};
|
||||
payload = { (char *)payload_new.data(), payload_new.size() };
|
||||
|
||||
auto shards = fec::encode(payload, blocksize, fecPercentage);
|
||||
if(shards.data_shards == 0) {
|
||||
@ -666,17 +664,15 @@ void videoBroadcastThread(safe::signal_t *shutdown_event, udp::socket &sock, vid
|
||||
continue;
|
||||
}
|
||||
|
||||
for (auto x = shards.data_shards; x < shards.size(); ++x) {
|
||||
for(auto x = shards.data_shards; x < shards.size(); ++x) {
|
||||
auto *inspect = (video_packet_raw_t *)shards.data(x);
|
||||
|
||||
inspect->packet.frameIndex = packet->pts;
|
||||
inspect->packet.fecInfo = (
|
||||
x << 12 |
|
||||
shards.data_shards << 22 |
|
||||
fecPercentage << 4
|
||||
);
|
||||
inspect->packet.fecInfo = (x << 12 |
|
||||
shards.data_shards << 22 |
|
||||
fecPercentage << 4);
|
||||
|
||||
inspect->rtp.header = FLAG_EXTENSION;
|
||||
inspect->rtp.header = FLAG_EXTENSION;
|
||||
inspect->rtp.sequenceNumber = util::endian::big<uint16_t>(lowseq + x);
|
||||
}
|
||||
|
||||
@ -698,27 +694,27 @@ void videoBroadcastThread(safe::signal_t *shutdown_event, udp::socket &sock, vid
|
||||
}
|
||||
|
||||
void audioBroadcastThread(safe::signal_t *shutdown_event, udp::socket &sock, audio::packet_queue_t packets) {
|
||||
while (auto packet = packets->pop()) {
|
||||
while(auto packet = packets->pop()) {
|
||||
if(shutdown_event->peek()) {
|
||||
break;
|
||||
}
|
||||
|
||||
TUPLE_2D_REF(channel_data, packet_data, *packet);
|
||||
auto session = (session_t*)channel_data;
|
||||
auto session = (session_t *)channel_data;
|
||||
|
||||
auto frame = session->audio.frame++;
|
||||
|
||||
audio_packet_t audio_packet { (audio_packet_raw_t*)malloc(sizeof(audio_packet_raw_t) + packet_data.size()) };
|
||||
audio_packet_t audio_packet { (audio_packet_raw_t *)malloc(sizeof(audio_packet_raw_t) + packet_data.size()) };
|
||||
|
||||
audio_packet->rtp.header = 0;
|
||||
audio_packet->rtp.packetType = 97;
|
||||
audio_packet->rtp.header = 0;
|
||||
audio_packet->rtp.packetType = 97;
|
||||
audio_packet->rtp.sequenceNumber = util::endian::big(frame);
|
||||
audio_packet->rtp.timestamp = 0;
|
||||
audio_packet->rtp.ssrc = 0;
|
||||
audio_packet->rtp.timestamp = 0;
|
||||
audio_packet->rtp.ssrc = 0;
|
||||
|
||||
std::copy(std::begin(packet_data), std::end(packet_data), audio_packet->payload());
|
||||
|
||||
sock.send_to(asio::buffer((char*)audio_packet.get(), sizeof(audio_packet_raw_t) + packet_data.size()), session->audio.peer);
|
||||
sock.send_to(asio::buffer((char *)audio_packet.get(), sizeof(audio_packet_raw_t) + packet_data.size()), session->audio.peer);
|
||||
BOOST_LOG(verbose) << "Audio ["sv << frame << "] :: send..."sv;
|
||||
}
|
||||
|
||||
@ -761,12 +757,12 @@ int start_broadcast(broadcast_ctx_t &ctx) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ctx.video_packets = std::make_shared<video::packet_queue_t::element_type>(30);
|
||||
ctx.audio_packets = std::make_shared<audio::packet_queue_t::element_type>(30);
|
||||
ctx.video_packets = std::make_shared<video::packet_queue_t::element_type>(30);
|
||||
ctx.audio_packets = std::make_shared<audio::packet_queue_t::element_type>(30);
|
||||
ctx.message_queue_queue = std::make_shared<message_queue_queue_t::element_type>(30);
|
||||
|
||||
ctx.video_thread = std::thread { videoBroadcastThread, &broadcast_shutdown_event, std::ref(ctx.video_sock), ctx.video_packets };
|
||||
ctx.audio_thread = std::thread { audioBroadcastThread, &broadcast_shutdown_event, std::ref(ctx.audio_sock), ctx.audio_packets };
|
||||
ctx.video_thread = std::thread { videoBroadcastThread, &broadcast_shutdown_event, std::ref(ctx.video_sock), ctx.video_packets };
|
||||
ctx.audio_thread = std::thread { audioBroadcastThread, &broadcast_shutdown_event, std::ref(ctx.audio_sock), ctx.audio_packets };
|
||||
ctx.control_thread = std::thread { controlBroadcastThread, &broadcast_shutdown_event, &ctx.control_server };
|
||||
|
||||
ctx.recv_thread = std::thread { recvThread, std::ref(ctx) };
|
||||
@ -842,7 +838,7 @@ void videoThread(session_t *session, std::string addr_str) {
|
||||
while_starting_do_nothing(session->state);
|
||||
|
||||
auto addr = asio::ip::make_address(addr_str);
|
||||
auto ref = broadcast.ref();
|
||||
auto ref = broadcast.ref();
|
||||
auto port = recv_ping(ref, socket_e::video, addr, config::stream.ping_timeout);
|
||||
if(port < 0) {
|
||||
return;
|
||||
@ -864,7 +860,7 @@ void audioThread(session_t *session, std::string addr_str) {
|
||||
|
||||
auto addr = asio::ip::make_address(addr_str);
|
||||
|
||||
auto ref = broadcast.ref();
|
||||
auto ref = broadcast.ref();
|
||||
auto port = recv_ping(ref, socket_e::audio, addr, config::stream.ping_timeout);
|
||||
if(port < 0) {
|
||||
return;
|
||||
@ -884,7 +880,7 @@ state_e state(session_t &session) {
|
||||
|
||||
void stop(session_t &session) {
|
||||
while_starting_do_nothing(session.state);
|
||||
auto expected = state_e::RUNNING;
|
||||
auto expected = state_e::RUNNING;
|
||||
auto already_stopping = !session.state.compare_exchange_strong(expected, state_e::STOPPING);
|
||||
if(already_stopping) {
|
||||
return;
|
||||
@ -918,8 +914,8 @@ int start(session_t &session, const std::string &addr_string) {
|
||||
|
||||
session.pingTimeout = std::chrono::steady_clock::now() + config::stream.ping_timeout;
|
||||
|
||||
session.audioThread = std::thread {audioThread, &session, addr_string};
|
||||
session.videoThread = std::thread {videoThread, &session, addr_string};
|
||||
session.audioThread = std::thread { audioThread, &session, addr_string };
|
||||
session.videoThread = std::thread { videoThread, &session, addr_string };
|
||||
|
||||
session.state.store(state_e::RUNNING, std::memory_order_relaxed);
|
||||
|
||||
@ -929,12 +925,12 @@ int start(session_t &session, const std::string &addr_string) {
|
||||
std::shared_ptr<session_t> alloc(config_t &config, crypto::aes_t &gcm_key, crypto::aes_t &iv) {
|
||||
auto session = std::make_shared<session_t>();
|
||||
|
||||
session->config = config;
|
||||
session->config = config;
|
||||
session->gcm_key = gcm_key;
|
||||
session->iv = iv;
|
||||
session->iv = iv;
|
||||
|
||||
session->video.idr_events = std::make_shared<video::idr_event_t::element_type>();
|
||||
session->video.lowseq = 0;
|
||||
session->video.lowseq = 0;
|
||||
|
||||
session->audio.frame = 1;
|
||||
|
||||
@ -943,5 +939,5 @@ std::shared_ptr<session_t> alloc(config_t &config, crypto::aes_t &gcm_key, crypt
|
||||
|
||||
return session;
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace session
|
||||
} // namespace stream
|
||||
|
||||
@ -7,9 +7,9 @@
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
|
||||
#include "video.h"
|
||||
#include "audio.h"
|
||||
#include "crypto.h"
|
||||
#include "video.h"
|
||||
|
||||
namespace stream {
|
||||
struct session_t;
|
||||
@ -35,9 +35,9 @@ int start(session_t &session, const std::string &addr_string);
|
||||
void stop(session_t &session);
|
||||
void join(session_t &session);
|
||||
state_e state(session_t &session);
|
||||
}
|
||||
} // namespace session
|
||||
|
||||
extern safe::signal_t broadcast_shutdown_event;
|
||||
}
|
||||
} // namespace stream
|
||||
|
||||
#endif //SUNSHINE_STREAM_H
|
||||
|
||||
@ -5,9 +5,9 @@
|
||||
#ifndef SUNSHINE_SYNC_H
|
||||
#define SUNSHINE_SYNC_H
|
||||
|
||||
#include <utility>
|
||||
#include <mutex>
|
||||
#include <array>
|
||||
#include <mutex>
|
||||
#include <utility>
|
||||
|
||||
namespace util {
|
||||
|
||||
@ -21,8 +21,8 @@ public:
|
||||
return std::lock_guard { _lock };
|
||||
}
|
||||
|
||||
template<class ...Args>
|
||||
sync_t(Args&&... args) : raw {std::forward<Args>(args)... } {}
|
||||
template<class... Args>
|
||||
sync_t(Args &&...args) : raw { std::forward<Args>(args)... } {}
|
||||
|
||||
sync_t &operator=(sync_t &&other) noexcept {
|
||||
std::lock(_lock, other._lock);
|
||||
@ -84,11 +84,12 @@ public:
|
||||
}
|
||||
|
||||
value_t raw;
|
||||
|
||||
private:
|
||||
mutex_t _lock;
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace util
|
||||
|
||||
|
||||
#endif //T_MAN_SYNC_H
|
||||
|
||||
@ -1,18 +1,18 @@
|
||||
#ifndef KITTY_TASK_POOL_H
|
||||
#define KITTY_TASK_POOL_H
|
||||
|
||||
#include <deque>
|
||||
#include <vector>
|
||||
#include <future>
|
||||
#include <chrono>
|
||||
#include <utility>
|
||||
#include <deque>
|
||||
#include <functional>
|
||||
#include <future>
|
||||
#include <mutex>
|
||||
#include <type_traits>
|
||||
#include <optional>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "utility.h"
|
||||
#include "move_by_copy.h"
|
||||
#include "utility.h"
|
||||
namespace util {
|
||||
|
||||
class _ImplBase {
|
||||
@ -29,8 +29,7 @@ class _Impl : public _ImplBase {
|
||||
Function _func;
|
||||
|
||||
public:
|
||||
|
||||
_Impl(Function&& f) : _func(std::forward<Function>(f)) { }
|
||||
_Impl(Function &&f) : _func(std::forward<Function>(f)) {}
|
||||
|
||||
void run() override {
|
||||
_func();
|
||||
@ -40,7 +39,7 @@ public:
|
||||
class TaskPool {
|
||||
public:
|
||||
typedef std::unique_ptr<_ImplBase> __task;
|
||||
typedef _ImplBase* task_id_t;
|
||||
typedef _ImplBase *task_id_t;
|
||||
|
||||
|
||||
typedef std::chrono::steady_clock::time_point __time_point;
|
||||
@ -53,9 +52,10 @@ public:
|
||||
|
||||
timer_task_t(task_id_t task_id, std::future<R> &future) : task_id { task_id }, future { std::move(future) } {}
|
||||
};
|
||||
|
||||
protected:
|
||||
std::deque<__task> _tasks;
|
||||
std::vector<std::pair<__time_point, __task>> _timer_tasks;
|
||||
std::vector<std::pair<__time_point, __task>> _timer_tasks;
|
||||
std::mutex _task_mutex;
|
||||
|
||||
public:
|
||||
@ -70,8 +70,8 @@ public:
|
||||
}
|
||||
|
||||
template<class Function, class... Args>
|
||||
auto push(Function && newTask, Args &&... args) {
|
||||
static_assert(std::is_invocable_v<Function, Args&&...>, "arguments don't match the function");
|
||||
auto push(Function &&newTask, Args &&...args) {
|
||||
static_assert(std::is_invocable_v<Function, Args &&...>, "arguments don't match the function");
|
||||
|
||||
using __return = std::invoke_result_t<Function, Args &&...>;
|
||||
using task_t = std::packaged_task<__return()>;
|
||||
@ -81,12 +81,12 @@ public:
|
||||
};
|
||||
|
||||
task_t task(std::move(bind));
|
||||
|
||||
|
||||
auto future = task.get_future();
|
||||
|
||||
|
||||
std::lock_guard<std::mutex> lg(_task_mutex);
|
||||
_tasks.emplace_back(toRunnable(std::move(task)));
|
||||
|
||||
|
||||
return future;
|
||||
}
|
||||
|
||||
@ -107,14 +107,14 @@ public:
|
||||
* @return an id to potentially delay the task
|
||||
*/
|
||||
template<class Function, class X, class Y, class... Args>
|
||||
auto pushDelayed(Function &&newTask, std::chrono::duration<X, Y> duration, Args &&... args) {
|
||||
static_assert(std::is_invocable_v<Function, Args&&...>, "arguments don't match the function");
|
||||
auto pushDelayed(Function &&newTask, std::chrono::duration<X, Y> duration, Args &&...args) {
|
||||
static_assert(std::is_invocable_v<Function, Args &&...>, "arguments don't match the function");
|
||||
|
||||
using __return = std::invoke_result_t<Function, Args &&...>;
|
||||
using task_t = std::packaged_task<__return()>;
|
||||
|
||||
__time_point time_point;
|
||||
if constexpr (std::is_floating_point_v<X>) {
|
||||
if constexpr(std::is_floating_point_v<X>) {
|
||||
time_point = std::chrono::steady_clock::now() + std::chrono::duration_cast<std::chrono::nanoseconds>(duration);
|
||||
}
|
||||
else {
|
||||
@ -127,7 +127,7 @@ public:
|
||||
|
||||
task_t task(std::move(bind));
|
||||
|
||||
auto future = task.get_future();
|
||||
auto future = task.get_future();
|
||||
auto runnable = toRunnable(std::move(task));
|
||||
|
||||
task_id_t task_id = &*runnable;
|
||||
@ -160,13 +160,14 @@ public:
|
||||
}
|
||||
|
||||
// smaller time goes to the back
|
||||
auto prev = it -1;
|
||||
auto prev = it - 1;
|
||||
while(it > _timer_tasks.cbegin()) {
|
||||
if(std::get<0>(*it) > std::get<0>(*prev)) {
|
||||
std::swap(*it, *prev);
|
||||
}
|
||||
|
||||
--prev; --it;
|
||||
--prev;
|
||||
--it;
|
||||
}
|
||||
}
|
||||
|
||||
@ -201,20 +202,20 @@ public:
|
||||
|
||||
std::optional<__task> pop() {
|
||||
std::lock_guard lg(_task_mutex);
|
||||
|
||||
|
||||
if(!_tasks.empty()) {
|
||||
__task task = std::move(_tasks.front());
|
||||
_tasks.pop_front();
|
||||
return std::move(task);
|
||||
}
|
||||
|
||||
|
||||
if(!_timer_tasks.empty() && std::get<0>(_timer_tasks.back()) <= std::chrono::steady_clock::now()) {
|
||||
__task task = std::move(std::get<1>(_timer_tasks.back()));
|
||||
_timer_tasks.pop_back();
|
||||
|
||||
|
||||
return std::move(task);
|
||||
}
|
||||
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
@ -233,12 +234,12 @@ public:
|
||||
|
||||
return std::get<0>(_timer_tasks.back());
|
||||
}
|
||||
private:
|
||||
|
||||
private:
|
||||
template<class Function>
|
||||
std::unique_ptr<_ImplBase> toRunnable(Function &&f) {
|
||||
return std::make_unique<_Impl<Function>>(std::forward<Function&&>(f));
|
||||
return std::make_unique<_Impl<Function>>(std::forward<Function &&>(f));
|
||||
}
|
||||
};
|
||||
}
|
||||
} // namespace util
|
||||
#endif
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
#ifndef KITTY_THREAD_POOL_H
|
||||
#define KITTY_THREAD_POOL_H
|
||||
|
||||
#include <thread>
|
||||
#include "task_pool.h"
|
||||
#include <thread>
|
||||
|
||||
namespace util {
|
||||
/*
|
||||
@ -12,32 +12,33 @@ namespace util {
|
||||
class ThreadPool : public TaskPool {
|
||||
public:
|
||||
typedef TaskPool::__task __task;
|
||||
|
||||
|
||||
private:
|
||||
std::vector<std::thread> _thread;
|
||||
|
||||
std::condition_variable _cv;
|
||||
std::mutex _lock;
|
||||
|
||||
|
||||
bool _continue;
|
||||
|
||||
public:
|
||||
ThreadPool() : _continue { false } {}
|
||||
|
||||
explicit ThreadPool(int threads) : _thread(threads), _continue { true } {
|
||||
for (auto &t : _thread) {
|
||||
for(auto &t : _thread) {
|
||||
t = std::thread(&ThreadPool::_main, this);
|
||||
}
|
||||
}
|
||||
|
||||
~ThreadPool() noexcept {
|
||||
if (!_continue) return;
|
||||
if(!_continue) return;
|
||||
|
||||
stop();
|
||||
join();
|
||||
}
|
||||
|
||||
template<class Function, class... Args>
|
||||
auto push(Function && newTask, Args &&... args) {
|
||||
auto push(Function &&newTask, Args &&...args) {
|
||||
std::lock_guard lg(_lock);
|
||||
auto future = TaskPool::push(std::forward<Function>(newTask), std::forward<Args>(args)...);
|
||||
|
||||
@ -52,7 +53,7 @@ public:
|
||||
}
|
||||
|
||||
template<class Function, class X, class Y, class... Args>
|
||||
auto pushDelayed(Function &&newTask, std::chrono::duration<X, Y> duration, Args &&... args) {
|
||||
auto pushDelayed(Function &&newTask, std::chrono::duration<X, Y> duration, Args &&...args) {
|
||||
std::lock_guard lg(_lock);
|
||||
auto future = TaskPool::pushDelayed(std::forward<Function>(newTask), duration, std::forward<Args>(args)...);
|
||||
|
||||
@ -79,15 +80,14 @@ public:
|
||||
}
|
||||
|
||||
void join() {
|
||||
for (auto & t : _thread) {
|
||||
for(auto &t : _thread) {
|
||||
t.join();
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
void _main() {
|
||||
while (_continue) {
|
||||
while(_continue) {
|
||||
if(auto task = this->pop()) {
|
||||
(*task)->run();
|
||||
}
|
||||
@ -117,5 +117,5 @@ public:
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
} // namespace util
|
||||
#endif
|
||||
|
||||
@ -5,11 +5,11 @@
|
||||
#ifndef SUNSHINE_THREAD_SAFE_H
|
||||
#define SUNSHINE_THREAD_SAFE_H
|
||||
|
||||
#include <vector>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
#include "utility.h"
|
||||
|
||||
@ -19,14 +19,14 @@ class event_t {
|
||||
using status_t = util::optional_t<T>;
|
||||
|
||||
public:
|
||||
template<class...Args>
|
||||
template<class... Args>
|
||||
void raise(Args &&...args) {
|
||||
std::lock_guard lg { _lock };
|
||||
if(!_continue) {
|
||||
return;
|
||||
}
|
||||
|
||||
if constexpr (std::is_same_v<std::optional<T>, status_t>) {
|
||||
if constexpr(std::is_same_v<std::optional<T>, status_t>) {
|
||||
_status = std::make_optional<T>(std::forward<Args>(args)...);
|
||||
}
|
||||
else {
|
||||
@ -38,42 +38,42 @@ public:
|
||||
|
||||
// pop and view shoud not be used interchangebly
|
||||
status_t pop() {
|
||||
std::unique_lock ul{ _lock };
|
||||
std::unique_lock ul { _lock };
|
||||
|
||||
if (!_continue) {
|
||||
if(!_continue) {
|
||||
return util::false_v<status_t>;
|
||||
}
|
||||
|
||||
while (!_status) {
|
||||
while(!_status) {
|
||||
_cv.wait(ul);
|
||||
|
||||
if (!_continue) {
|
||||
if(!_continue) {
|
||||
return util::false_v<status_t>;
|
||||
}
|
||||
}
|
||||
|
||||
auto val = std::move(_status);
|
||||
_status = util::false_v<status_t>;
|
||||
_status = util::false_v<status_t>;
|
||||
return val;
|
||||
}
|
||||
|
||||
// pop and view shoud not be used interchangebly
|
||||
template<class Rep, class Period>
|
||||
status_t pop(std::chrono::duration<Rep, Period> delay) {
|
||||
std::unique_lock ul{ _lock };
|
||||
std::unique_lock ul { _lock };
|
||||
|
||||
if (!_continue) {
|
||||
if(!_continue) {
|
||||
return util::false_v<status_t>;
|
||||
}
|
||||
|
||||
while (!_status) {
|
||||
if (!_continue || _cv.wait_for(ul, delay) == std::cv_status::timeout) {
|
||||
while(!_status) {
|
||||
if(!_continue || _cv.wait_for(ul, delay) == std::cv_status::timeout) {
|
||||
return util::false_v<status_t>;
|
||||
}
|
||||
}
|
||||
|
||||
auto val = std::move(_status);
|
||||
_status = util::false_v<status_t>;
|
||||
_status = util::false_v<status_t>;
|
||||
return val;
|
||||
}
|
||||
|
||||
@ -81,14 +81,14 @@ public:
|
||||
const status_t &view() {
|
||||
std::unique_lock ul { _lock };
|
||||
|
||||
if (!_continue) {
|
||||
if(!_continue) {
|
||||
return util::false_v<status_t>;
|
||||
}
|
||||
|
||||
while (!_status) {
|
||||
while(!_status) {
|
||||
_cv.wait(ul);
|
||||
|
||||
if (!_continue) {
|
||||
if(!_continue) {
|
||||
return util::false_v<status_t>;
|
||||
}
|
||||
}
|
||||
@ -103,7 +103,7 @@ public:
|
||||
}
|
||||
|
||||
void stop() {
|
||||
std::lock_guard lg{ _lock };
|
||||
std::lock_guard lg { _lock };
|
||||
|
||||
_continue = false;
|
||||
|
||||
@ -111,7 +111,7 @@ public:
|
||||
}
|
||||
|
||||
void reset() {
|
||||
std::lock_guard lg{ _lock };
|
||||
std::lock_guard lg { _lock };
|
||||
|
||||
_continue = true;
|
||||
|
||||
@ -121,8 +121,8 @@ public:
|
||||
[[nodiscard]] bool running() const {
|
||||
return _continue;
|
||||
}
|
||||
private:
|
||||
|
||||
private:
|
||||
bool _continue { true };
|
||||
status_t _status { util::false_v<status_t> };
|
||||
|
||||
@ -137,8 +137,8 @@ class queue_t {
|
||||
public:
|
||||
queue_t(std::uint32_t max_elements) : _max_elements { max_elements } {}
|
||||
|
||||
template<class ...Args>
|
||||
void raise(Args &&... args) {
|
||||
template<class... Args>
|
||||
void raise(Args &&...args) {
|
||||
std::lock_guard ul { _lock };
|
||||
|
||||
if(!_continue) {
|
||||
@ -164,12 +164,12 @@ public:
|
||||
status_t pop(std::chrono::duration<Rep, Period> delay) {
|
||||
std::unique_lock ul { _lock };
|
||||
|
||||
if (!_continue) {
|
||||
if(!_continue) {
|
||||
return util::false_v<status_t>;
|
||||
}
|
||||
|
||||
while (_queue.empty()) {
|
||||
if (!_continue || _cv.wait_for(ul, delay) == std::cv_status::timeout) {
|
||||
while(_queue.empty()) {
|
||||
if(!_continue || _cv.wait_for(ul, delay) == std::cv_status::timeout) {
|
||||
return util::false_v<status_t>;
|
||||
}
|
||||
}
|
||||
@ -183,14 +183,14 @@ public:
|
||||
status_t pop() {
|
||||
std::unique_lock ul { _lock };
|
||||
|
||||
if (!_continue) {
|
||||
if(!_continue) {
|
||||
return util::false_v<status_t>;
|
||||
}
|
||||
|
||||
while (_queue.empty()) {
|
||||
while(_queue.empty()) {
|
||||
_cv.wait(ul);
|
||||
|
||||
if (!_continue) {
|
||||
if(!_continue) {
|
||||
return util::false_v<status_t>;
|
||||
}
|
||||
}
|
||||
@ -219,7 +219,6 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
bool _continue { true };
|
||||
std::uint32_t _max_elements;
|
||||
|
||||
@ -235,7 +234,7 @@ public:
|
||||
using element_type = T;
|
||||
|
||||
using construct_f = std::function<int(element_type &)>;
|
||||
using destruct_f = std::function<void(element_type &)>;
|
||||
using destruct_f = std::function<void(element_type &)>;
|
||||
|
||||
struct ptr_t {
|
||||
shared_t *owner;
|
||||
@ -252,7 +251,7 @@ public:
|
||||
return;
|
||||
}
|
||||
|
||||
auto tmp = ptr.owner->ref();
|
||||
auto tmp = ptr.owner->ref();
|
||||
tmp.owner = nullptr;
|
||||
}
|
||||
|
||||
@ -282,7 +281,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
operator bool () const {
|
||||
operator bool() const {
|
||||
return owner != nullptr;
|
||||
}
|
||||
|
||||
@ -298,22 +297,22 @@ public:
|
||||
}
|
||||
|
||||
element_type *get() const {
|
||||
return reinterpret_cast<element_type*>(owner->_object_buf.data());
|
||||
return reinterpret_cast<element_type *>(owner->_object_buf.data());
|
||||
}
|
||||
|
||||
element_type *operator->() {
|
||||
return reinterpret_cast<element_type*>(owner->_object_buf.data());
|
||||
return reinterpret_cast<element_type *>(owner->_object_buf.data());
|
||||
}
|
||||
};
|
||||
|
||||
template<class FC, class FD>
|
||||
shared_t(FC && fc, FD &&fd) : _construct { std::forward<FC>(fc) }, _destruct { std::forward<FD>(fd) } {}
|
||||
shared_t(FC &&fc, FD &&fd) : _construct { std::forward<FC>(fc) }, _destruct { std::forward<FD>(fd) } {}
|
||||
[[nodiscard]] ptr_t ref() {
|
||||
std::lock_guard lg { _lock };
|
||||
|
||||
if(!_count) {
|
||||
new(_object_buf.data()) element_type;
|
||||
if(_construct(*reinterpret_cast<element_type*>(_object_buf.data()))) {
|
||||
if(_construct(*reinterpret_cast<element_type *>(_object_buf.data()))) {
|
||||
return ptr_t { nullptr };
|
||||
}
|
||||
}
|
||||
@ -322,6 +321,7 @@ public:
|
||||
|
||||
return ptr_t { this };
|
||||
}
|
||||
|
||||
private:
|
||||
construct_f _construct;
|
||||
destruct_f _destruct;
|
||||
@ -340,6 +340,6 @@ auto make_shared(F_Construct &&fc, F_Destruct &&fd) {
|
||||
}
|
||||
|
||||
using signal_t = event_t<bool>;
|
||||
}
|
||||
} // namespace safe
|
||||
|
||||
#endif //SUNSHINE_THREAD_SAFE_H
|
||||
|
||||
@ -1,63 +1,67 @@
|
||||
#ifndef UTILITY_H
|
||||
#define UTILITY_H
|
||||
|
||||
#include <algorithm>
|
||||
#include <condition_variable>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <algorithm>
|
||||
#include <optional>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <string_view>
|
||||
|
||||
#define KITTY_WHILE_LOOP(x, y, z) { x;while(y) z }
|
||||
#define KITTY_DECL_CONSTR(x)\
|
||||
x(x&&) noexcept = default;\
|
||||
x&operator=(x&&) noexcept = default;\
|
||||
#define KITTY_WHILE_LOOP(x, y, z) \
|
||||
{ \
|
||||
x; \
|
||||
while(y) z \
|
||||
}
|
||||
#define KITTY_DECL_CONSTR(x) \
|
||||
x(x &&) noexcept = default; \
|
||||
x &operator=(x &&) noexcept = default; \
|
||||
x();
|
||||
|
||||
#define KITTY_DEFAULT_CONSTR(x)\
|
||||
x(x&&) noexcept = default;\
|
||||
x&operator=(x&&) noexcept = default;\
|
||||
x() = default;
|
||||
#define KITTY_DEFAULT_CONSTR(x) \
|
||||
x(x &&) noexcept = default; \
|
||||
x &operator=(x &&) noexcept = default; \
|
||||
x() = default;
|
||||
|
||||
#define KITTY_DEFAULT_CONSTR_THROW(x)\
|
||||
x(x&&) = default;\
|
||||
x&operator=(x&&) = default;\
|
||||
x() = default;
|
||||
#define KITTY_DEFAULT_CONSTR_THROW(x) \
|
||||
x(x &&) = default; \
|
||||
x &operator=(x &&) = default; \
|
||||
x() = default;
|
||||
|
||||
#define TUPLE_2D(a,b, expr)\
|
||||
decltype(expr) a##_##b = expr;\
|
||||
auto &a = std::get<0>(a##_##b);\
|
||||
auto &b = std::get<1>(a##_##b)
|
||||
#define TUPLE_2D(a, b, expr) \
|
||||
decltype(expr) a##_##b = expr; \
|
||||
auto &a = std::get<0>(a##_##b); \
|
||||
auto &b = std::get<1>(a##_##b)
|
||||
|
||||
#define TUPLE_2D_REF(a,b, expr)\
|
||||
auto &a##_##b = expr;\
|
||||
auto &a = std::get<0>(a##_##b);\
|
||||
auto &b = std::get<1>(a##_##b)
|
||||
#define TUPLE_2D_REF(a, b, expr) \
|
||||
auto &a##_##b = expr; \
|
||||
auto &a = std::get<0>(a##_##b); \
|
||||
auto &b = std::get<1>(a##_##b)
|
||||
|
||||
#define TUPLE_3D(a,b,c, expr)\
|
||||
decltype(expr) a##_##b##_##c = expr;\
|
||||
auto &a = std::get<0>(a##_##b##_##c);\
|
||||
auto &b = std::get<1>(a##_##b##_##c);\
|
||||
auto &c = std::get<2>(a##_##b##_##c)
|
||||
#define TUPLE_3D(a, b, c, expr) \
|
||||
decltype(expr) a##_##b##_##c = expr; \
|
||||
auto &a = std::get<0>(a##_##b##_##c); \
|
||||
auto &b = std::get<1>(a##_##b##_##c); \
|
||||
auto &c = std::get<2>(a##_##b##_##c)
|
||||
|
||||
#define TUPLE_3D_REF(a,b,c, expr)\
|
||||
auto &a##_##b##_##c = expr;\
|
||||
auto &a = std::get<0>(a##_##b##_##c);\
|
||||
auto &b = std::get<1>(a##_##b##_##c);\
|
||||
auto &c = std::get<2>(a##_##b##_##c)
|
||||
#define TUPLE_3D_REF(a, b, c, expr) \
|
||||
auto &a##_##b##_##c = expr; \
|
||||
auto &a = std::get<0>(a##_##b##_##c); \
|
||||
auto &b = std::get<1>(a##_##b##_##c); \
|
||||
auto &c = std::get<2>(a##_##b##_##c)
|
||||
|
||||
namespace util {
|
||||
|
||||
template<template<typename...> class X, class...Y>
|
||||
template<template<typename...> class X, class... Y>
|
||||
struct __instantiation_of : public std::false_type {};
|
||||
|
||||
template<template<typename...> class X, class... Y>
|
||||
struct __instantiation_of<X, X<Y...>> : public std::true_type {};
|
||||
|
||||
template<template<typename...> class X, class T, class...Y>
|
||||
template<template<typename...> class X, class T, class... Y>
|
||||
static constexpr auto instantiation_of_v = __instantiation_of<X, T, Y...>::value;
|
||||
|
||||
template<bool V, class X, class Y>
|
||||
@ -76,14 +80,16 @@ struct __either<false, X, Y> {
|
||||
template<bool V, class X, class Y>
|
||||
using either_t = typename __either<V, X, Y>::type;
|
||||
|
||||
template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
|
||||
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
|
||||
template<class... Ts>
|
||||
struct overloaded : Ts... { using Ts::operator()...; };
|
||||
template<class... Ts>
|
||||
overloaded(Ts...) -> overloaded<Ts...>;
|
||||
|
||||
template<class T>
|
||||
class FailGuard {
|
||||
public:
|
||||
FailGuard() = delete;
|
||||
FailGuard(T && f) noexcept : _func { std::forward<T>(f) } {}
|
||||
FailGuard(T &&f) noexcept : _func { std::forward<T>(f) } {}
|
||||
FailGuard(FailGuard &&other) noexcept : _func { std::move(other._func) } {
|
||||
this->failure = other.failure;
|
||||
|
||||
@ -103,12 +109,13 @@ public:
|
||||
|
||||
void disable() { failure = false; }
|
||||
bool failure { true };
|
||||
|
||||
private:
|
||||
T _func;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
[[nodiscard]] auto fail_guard(T && f) {
|
||||
[[nodiscard]] auto fail_guard(T &&f) {
|
||||
return FailGuard<T> { std::forward<T>(f) };
|
||||
}
|
||||
|
||||
@ -118,9 +125,9 @@ void append_struct(std::vector<uint8_t> &buf, const T &_struct) {
|
||||
|
||||
buf.reserve(data_len);
|
||||
|
||||
auto *data = (uint8_t *) & _struct;
|
||||
auto *data = (uint8_t *)&_struct;
|
||||
|
||||
for (size_t x = 0; x < data_len; ++x) {
|
||||
for(size_t x = 0; x < data_len; ++x) {
|
||||
buf.push_back(data[x]);
|
||||
}
|
||||
}
|
||||
@ -129,24 +136,26 @@ template<class T>
|
||||
class Hex {
|
||||
public:
|
||||
typedef T elem_type;
|
||||
|
||||
private:
|
||||
const char _bits[16] {
|
||||
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
|
||||
};
|
||||
|
||||
char _hex[sizeof(elem_type) * 2];
|
||||
|
||||
public:
|
||||
Hex(const elem_type &elem, bool rev) {
|
||||
if(!rev) {
|
||||
const uint8_t *data = reinterpret_cast<const uint8_t *>(&elem) + sizeof(elem_type) - 1;
|
||||
for (auto it = begin(); it < cend();) {
|
||||
for(auto it = begin(); it < cend();) {
|
||||
*it++ = _bits[*data / 16];
|
||||
*it++ = _bits[*data-- % 16];
|
||||
}
|
||||
}
|
||||
else {
|
||||
const uint8_t *data = reinterpret_cast<const uint8_t *>(&elem);
|
||||
for (auto it = begin(); it < cend();) {
|
||||
for(auto it = begin(); it < cend();) {
|
||||
*it++ = _bits[*data / 16];
|
||||
*it++ = _bits[*data++ % 16];
|
||||
}
|
||||
@ -178,7 +187,7 @@ Hex<T> hex(const T &elem, bool rev = false) {
|
||||
|
||||
template<class It>
|
||||
std::string hex_vec(It begin, It end, bool rev = false) {
|
||||
auto str_size = 2*std::distance(begin, end);
|
||||
auto str_size = 2 * std::distance(begin, end);
|
||||
|
||||
|
||||
std::string hex;
|
||||
@ -189,14 +198,14 @@ std::string hex_vec(It begin, It end, bool rev = false) {
|
||||
};
|
||||
|
||||
if(rev) {
|
||||
for (auto it = std::begin(hex); it < std::end(hex);) {
|
||||
for(auto it = std::begin(hex); it < std::end(hex);) {
|
||||
*it++ = _bits[((uint8_t)*begin) / 16];
|
||||
*it++ = _bits[((uint8_t)*begin++) % 16];
|
||||
}
|
||||
}
|
||||
else {
|
||||
--end;
|
||||
for (auto it = std::begin(hex); it < std::end(hex);) {
|
||||
for(auto it = std::begin(hex); it < std::end(hex);) {
|
||||
*it++ = _bits[((uint8_t)*end) / 16];
|
||||
*it++ = _bits[((uint8_t)*end--) % 16];
|
||||
}
|
||||
@ -207,7 +216,7 @@ std::string hex_vec(It begin, It end, bool rev = false) {
|
||||
}
|
||||
|
||||
template<class C>
|
||||
std::string hex_vec(C&& c, bool rev = false) {
|
||||
std::string hex_vec(C &&c, bool rev = false) {
|
||||
return hex_vec(std::begin(c), std::end(c), rev);
|
||||
}
|
||||
|
||||
@ -216,7 +225,7 @@ std::optional<T> from_hex(const std::string_view &hex, bool rev = false) {
|
||||
std::uint8_t buf[sizeof(T)];
|
||||
|
||||
static char constexpr shift_bit = 'a' - 'A';
|
||||
auto is_convertable = [] (char ch) -> bool {
|
||||
auto is_convertable = [](char ch) -> bool {
|
||||
if(isdigit(ch)) {
|
||||
return true;
|
||||
}
|
||||
@ -235,9 +244,9 @@ std::optional<T> from_hex(const std::string_view &hex, bool rev = false) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const char *data = hex.data() + hex.size() -1;
|
||||
const char *data = hex.data() + hex.size() - 1;
|
||||
|
||||
auto convert = [] (char ch) -> std::uint8_t {
|
||||
auto convert = [](char ch) -> std::uint8_t {
|
||||
if(ch >= '0' && ch <= '9') {
|
||||
return (std::uint8_t)ch - '0';
|
||||
}
|
||||
@ -266,7 +275,7 @@ inline std::string from_hex_vec(const std::string &hex, bool rev = false) {
|
||||
std::string buf;
|
||||
|
||||
static char constexpr shift_bit = 'a' - 'A';
|
||||
auto is_convertable = [] (char ch) -> bool {
|
||||
auto is_convertable = [](char ch) -> bool {
|
||||
if(isdigit(ch)) {
|
||||
return true;
|
||||
}
|
||||
@ -283,9 +292,9 @@ inline std::string from_hex_vec(const std::string &hex, bool rev = false) {
|
||||
auto buf_size = std::count_if(std::begin(hex), std::end(hex), is_convertable) / 2;
|
||||
buf.resize(buf_size);
|
||||
|
||||
const char *data = hex.data() + hex.size() -1;
|
||||
const char *data = hex.data() + hex.size() - 1;
|
||||
|
||||
auto convert = [] (char ch) -> std::uint8_t {
|
||||
auto convert = [](char ch) -> std::uint8_t {
|
||||
if(ch >= '0' && ch <= '9') {
|
||||
return (std::uint8_t)ch - '0';
|
||||
}
|
||||
@ -317,18 +326,18 @@ public:
|
||||
std::size_t operator()(const value_type &value) const {
|
||||
const auto *p = reinterpret_cast<const char *>(&value);
|
||||
|
||||
return std::hash<std::string_view>{}(std::string_view { p, sizeof(value_type) });
|
||||
return std::hash<std::string_view> {}(std::string_view { p, sizeof(value_type) });
|
||||
}
|
||||
};
|
||||
|
||||
template<class T>
|
||||
auto enm(const T& val) -> const std::underlying_type_t<T>& {
|
||||
return *reinterpret_cast<const std::underlying_type_t<T>*>(&val);
|
||||
auto enm(const T &val) -> const std::underlying_type_t<T> & {
|
||||
return *reinterpret_cast<const std::underlying_type_t<T> *>(&val);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
auto enm(T& val) -> std::underlying_type_t<T>& {
|
||||
return *reinterpret_cast<std::underlying_type_t<T>*>(&val);
|
||||
auto enm(T &val) -> std::underlying_type_t<T> & {
|
||||
return *reinterpret_cast<std::underlying_type_t<T> *>(&val);
|
||||
}
|
||||
|
||||
inline std::int64_t from_chars(const char *begin, const char *end) {
|
||||
@ -377,11 +386,11 @@ public:
|
||||
};
|
||||
|
||||
// Compared to std::unique_ptr, it adds the ability to get the address of the pointer itself
|
||||
template <typename T, typename D = std::default_delete<T>>
|
||||
template<typename T, typename D = std::default_delete<T>>
|
||||
class uniq_ptr {
|
||||
public:
|
||||
using element_type = T;
|
||||
using pointer = element_type*;
|
||||
using pointer = element_type *;
|
||||
using deleter_type = D;
|
||||
|
||||
constexpr uniq_ptr() noexcept : _p { nullptr } {}
|
||||
@ -436,7 +445,7 @@ public:
|
||||
|
||||
pointer release() {
|
||||
auto tmp = _p;
|
||||
_p = nullptr;
|
||||
_p = nullptr;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
@ -468,69 +477,70 @@ public:
|
||||
return &_p;
|
||||
}
|
||||
|
||||
deleter_type& get_deleter() {
|
||||
deleter_type &get_deleter() {
|
||||
return _deleter;
|
||||
}
|
||||
|
||||
const deleter_type& get_deleter() const {
|
||||
const deleter_type &get_deleter() const {
|
||||
return _deleter;
|
||||
}
|
||||
|
||||
explicit operator bool() const {
|
||||
return _p != nullptr;
|
||||
}
|
||||
|
||||
protected:
|
||||
pointer _p;
|
||||
deleter_type _deleter;
|
||||
};
|
||||
|
||||
template<class T1, class D1, class T2, class D2>
|
||||
bool operator==(const uniq_ptr<T1, D1>& x, const uniq_ptr<T2, D2>& y) {
|
||||
bool operator==(const uniq_ptr<T1, D1> &x, const uniq_ptr<T2, D2> &y) {
|
||||
return x.get() == y.get();
|
||||
}
|
||||
|
||||
template<class T1, class D1, class T2, class D2>
|
||||
bool operator!=(const uniq_ptr<T1, D1>& x, const uniq_ptr<T2, D2>& y) {
|
||||
bool operator!=(const uniq_ptr<T1, D1> &x, const uniq_ptr<T2, D2> &y) {
|
||||
return x.get() != y.get();
|
||||
}
|
||||
|
||||
template<class T1, class D1, class T2, class D2>
|
||||
bool operator==(const std::unique_ptr<T1, D1>& x, const uniq_ptr<T2, D2>& y) {
|
||||
bool operator==(const std::unique_ptr<T1, D1> &x, const uniq_ptr<T2, D2> &y) {
|
||||
return x.get() == y.get();
|
||||
}
|
||||
|
||||
template<class T1, class D1, class T2, class D2>
|
||||
bool operator!=(const std::unique_ptr<T1, D1>& x, const uniq_ptr<T2, D2>& y) {
|
||||
bool operator!=(const std::unique_ptr<T1, D1> &x, const uniq_ptr<T2, D2> &y) {
|
||||
return x.get() != y.get();
|
||||
}
|
||||
|
||||
template<class T1, class D1, class T2, class D2>
|
||||
bool operator==(const uniq_ptr<T1, D1>& x, const std::unique_ptr<T1, D1>& y) {
|
||||
bool operator==(const uniq_ptr<T1, D1> &x, const std::unique_ptr<T1, D1> &y) {
|
||||
return x.get() == y.get();
|
||||
}
|
||||
|
||||
template<class T1, class D1, class T2, class D2>
|
||||
bool operator!=(const uniq_ptr<T1, D1>& x, const std::unique_ptr<T1, D1>& y) {
|
||||
bool operator!=(const uniq_ptr<T1, D1> &x, const std::unique_ptr<T1, D1> &y) {
|
||||
return x.get() != y.get();
|
||||
}
|
||||
|
||||
template<class T, class D>
|
||||
bool operator==(const uniq_ptr<T, D>& x, std::nullptr_t) {
|
||||
bool operator==(const uniq_ptr<T, D> &x, std::nullptr_t) {
|
||||
return !(bool)x;
|
||||
}
|
||||
|
||||
template<class T, class D>
|
||||
bool operator!=(const uniq_ptr<T, D>& x, std::nullptr_t) {
|
||||
bool operator!=(const uniq_ptr<T, D> &x, std::nullptr_t) {
|
||||
return (bool)x;
|
||||
}
|
||||
|
||||
template<class T, class D>
|
||||
bool operator==(std::nullptr_t, const uniq_ptr<T, D>& y) {
|
||||
bool operator==(std::nullptr_t, const uniq_ptr<T, D> &y) {
|
||||
return !(bool)y;
|
||||
}
|
||||
|
||||
template<class T, class D>
|
||||
bool operator!=(std::nullptr_t, const uniq_ptr<T, D>& y) {
|
||||
bool operator!=(std::nullptr_t, const uniq_ptr<T, D> &y) {
|
||||
return (bool)y;
|
||||
}
|
||||
|
||||
@ -538,8 +548,8 @@ template<class T>
|
||||
class wrap_ptr {
|
||||
public:
|
||||
using element_type = T;
|
||||
using pointer = element_type*;
|
||||
using reference = element_type&;
|
||||
using pointer = element_type *;
|
||||
using reference = element_type &;
|
||||
|
||||
wrap_ptr() : _own_ptr { false }, _p { nullptr } {}
|
||||
wrap_ptr(pointer p) : _own_ptr { false }, _p { p } {}
|
||||
@ -555,7 +565,7 @@ public:
|
||||
|
||||
_p = other._p;
|
||||
|
||||
_own_ptr = other._own_ptr;
|
||||
_own_ptr = other._own_ptr;
|
||||
other._own_ptr = false;
|
||||
|
||||
return *this;
|
||||
@ -565,7 +575,7 @@ public:
|
||||
wrap_ptr &operator=(std::unique_ptr<V> &&uniq_ptr) {
|
||||
static_assert(std::is_base_of_v<element_type, V>, "element_type must be base class of V");
|
||||
_own_ptr = true;
|
||||
_p = uniq_ptr.release();
|
||||
_p = uniq_ptr.release();
|
||||
|
||||
return *this;
|
||||
}
|
||||
@ -575,7 +585,7 @@ public:
|
||||
delete _p;
|
||||
}
|
||||
|
||||
_p = p;
|
||||
_p = p;
|
||||
_own_ptr = false;
|
||||
|
||||
return *this;
|
||||
@ -617,13 +627,11 @@ struct __false_v<T, std::enable_if_t<instantiation_of_v<std::optional, T>>> {
|
||||
|
||||
template<class T>
|
||||
struct __false_v<T, std::enable_if_t<
|
||||
(
|
||||
std::is_pointer_v<T> ||
|
||||
instantiation_of_v<std::unique_ptr, T> ||
|
||||
instantiation_of_v<std::shared_ptr, T> ||
|
||||
instantiation_of_v<uniq_ptr, T>
|
||||
)
|
||||
>> {
|
||||
(
|
||||
std::is_pointer_v<T> ||
|
||||
instantiation_of_v<std::unique_ptr, T> ||
|
||||
instantiation_of_v<std::shared_ptr, T> ||
|
||||
instantiation_of_v<uniq_ptr, T>)>> {
|
||||
static constexpr std::nullptr_t value = nullptr;
|
||||
};
|
||||
|
||||
@ -638,18 +646,18 @@ static constexpr auto false_v = __false_v<T>::value;
|
||||
template<class T>
|
||||
using optional_t = either_t<
|
||||
(std::is_same_v<T, bool> ||
|
||||
instantiation_of_v<std::unique_ptr, T> ||
|
||||
instantiation_of_v<std::shared_ptr, T> ||
|
||||
instantiation_of_v<uniq_ptr, T> ||
|
||||
std::is_pointer_v<T>),
|
||||
instantiation_of_v<std::unique_ptr, T> ||
|
||||
instantiation_of_v<std::shared_ptr, T> ||
|
||||
instantiation_of_v<uniq_ptr, T> ||
|
||||
std::is_pointer_v<T>),
|
||||
T, std::optional<T>>;
|
||||
|
||||
template<class T>
|
||||
class buffer_t {
|
||||
public:
|
||||
buffer_t() : _els { 0 } {};
|
||||
buffer_t(buffer_t&&) noexcept = default;
|
||||
buffer_t &operator=(buffer_t&& other) noexcept = default;
|
||||
buffer_t(buffer_t &&) noexcept = default;
|
||||
buffer_t &operator=(buffer_t &&other) noexcept = default;
|
||||
|
||||
explicit buffer_t(size_t elements) : _els { elements }, _buf { std::make_unique<T[]>(elements) } {}
|
||||
explicit buffer_t(size_t elements, const T &t) : _els { elements }, _buf { std::make_unique<T[]>(elements) } {
|
||||
@ -703,7 +711,7 @@ T either(std::optional<T> &&l, T &&r) {
|
||||
return std::forward<T>(r);
|
||||
}
|
||||
|
||||
template<class ReturnType, class ...Args>
|
||||
template<class ReturnType, class... Args>
|
||||
struct Function {
|
||||
typedef ReturnType (*type)(Args...);
|
||||
};
|
||||
@ -711,18 +719,18 @@ struct Function {
|
||||
template<class T, class ReturnType, typename Function<ReturnType, T>::type function>
|
||||
struct Destroy {
|
||||
typedef T pointer;
|
||||
|
||||
|
||||
void operator()(pointer p) {
|
||||
function(p);
|
||||
}
|
||||
};
|
||||
|
||||
template<class T, typename Function<void, T*>::type function>
|
||||
using safe_ptr = uniq_ptr<T, Destroy<T*, void, function>>;
|
||||
template<class T, typename Function<void, T *>::type function>
|
||||
using safe_ptr = uniq_ptr<T, Destroy<T *, void, function>>;
|
||||
|
||||
// You cannot specialize an alias
|
||||
template<class T, class ReturnType, typename Function<ReturnType, T*>::type function>
|
||||
using safe_ptr_v2 = uniq_ptr<T, Destroy<T*, ReturnType, function>>;
|
||||
template<class T, class ReturnType, typename Function<ReturnType, T *>::type function>
|
||||
using safe_ptr_v2 = uniq_ptr<T, Destroy<T *, ReturnType, function>>;
|
||||
|
||||
template<class T>
|
||||
void c_free(T *p) {
|
||||
@ -737,22 +745,22 @@ template<class T = void>
|
||||
struct endianness {
|
||||
enum : bool {
|
||||
#if defined(__BYTE_ORDER) && __BYTE_ORDER == __BIG_ENDIAN || \
|
||||
defined(__BIG_ENDIAN__) || \
|
||||
defined(__ARMEB__) || \
|
||||
defined(__THUMBEB__) || \
|
||||
defined(__AARCH64EB__) || \
|
||||
defined(_MIBSEB) || defined(__MIBSEB) || defined(__MIBSEB__)
|
||||
defined(__BIG_ENDIAN__) || \
|
||||
defined(__ARMEB__) || \
|
||||
defined(__THUMBEB__) || \
|
||||
defined(__AARCH64EB__) || \
|
||||
defined(_MIBSEB) || defined(__MIBSEB) || defined(__MIBSEB__)
|
||||
// It's a big-endian target architecture
|
||||
little = false,
|
||||
#elif defined(__BYTE_ORDER) && __BYTE_ORDER == __LITTLE_ENDIAN || \
|
||||
defined(__LITTLE_ENDIAN__) || \
|
||||
defined(__ARMEL__) || \
|
||||
defined(__THUMBEL__) || \
|
||||
defined(__AARCH64EL__) || \
|
||||
defined(_MIPSEL) || defined(__MIPSEL) || defined(__MIPSEL__) || \
|
||||
defined(_WIN32)
|
||||
defined(__LITTLE_ENDIAN__) || \
|
||||
defined(__ARMEL__) || \
|
||||
defined(__THUMBEL__) || \
|
||||
defined(__AARCH64EL__) || \
|
||||
defined(_MIPSEL) || defined(__MIPSEL) || defined(__MIPSEL__) || \
|
||||
defined(_WIN32)
|
||||
// It's a little-endian target architecture
|
||||
little = true,
|
||||
little = true,
|
||||
#else
|
||||
#error "Unknown Endianness"
|
||||
#endif
|
||||
@ -761,15 +769,14 @@ struct endianness {
|
||||
};
|
||||
|
||||
template<class T, class S = void>
|
||||
struct endian_helper { };
|
||||
struct endian_helper {};
|
||||
|
||||
template<class T>
|
||||
struct endian_helper<T, std::enable_if_t<
|
||||
!(instantiation_of_v<std::optional, T>)
|
||||
>> {
|
||||
!(instantiation_of_v<std::optional, T>)>> {
|
||||
static inline T big(T x) {
|
||||
if constexpr (endianness<T>::little) {
|
||||
uint8_t *data = reinterpret_cast<uint8_t*>(&x);
|
||||
if constexpr(endianness<T>::little) {
|
||||
uint8_t *data = reinterpret_cast<uint8_t *>(&x);
|
||||
|
||||
std::reverse(data, data + sizeof(x));
|
||||
}
|
||||
@ -778,8 +785,8 @@ struct endian_helper<T, std::enable_if_t<
|
||||
}
|
||||
|
||||
static inline T little(T x) {
|
||||
if constexpr (endianness<T>::big) {
|
||||
uint8_t *data = reinterpret_cast<uint8_t*>(&x);
|
||||
if constexpr(endianness<T>::big) {
|
||||
uint8_t *data = reinterpret_cast<uint8_t *>(&x);
|
||||
|
||||
std::reverse(data, data + sizeof(x));
|
||||
}
|
||||
@ -790,32 +797,31 @@ struct endian_helper<T, std::enable_if_t<
|
||||
|
||||
template<class T>
|
||||
struct endian_helper<T, std::enable_if_t<
|
||||
instantiation_of_v<std::optional, T>
|
||||
>> {
|
||||
static inline T little(T x) {
|
||||
if(!x) return x;
|
||||
instantiation_of_v<std::optional, T>>> {
|
||||
static inline T little(T x) {
|
||||
if(!x) return x;
|
||||
|
||||
if constexpr (endianness<T>::big) {
|
||||
auto *data = reinterpret_cast<uint8_t*>(&*x);
|
||||
if constexpr(endianness<T>::big) {
|
||||
auto *data = reinterpret_cast<uint8_t *>(&*x);
|
||||
|
||||
std::reverse(data, data + sizeof(*x));
|
||||
std::reverse(data, data + sizeof(*x));
|
||||
}
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
static inline T big(T x) {
|
||||
if(!x) return x;
|
||||
|
||||
static inline T big(T x) {
|
||||
if(!x) return x;
|
||||
if constexpr(endianness<T>::big) {
|
||||
auto *data = reinterpret_cast<uint8_t *>(&*x);
|
||||
|
||||
if constexpr (endianness<T>::big) {
|
||||
auto *data = reinterpret_cast<uint8_t*>(&*x);
|
||||
std::reverse(data, data + sizeof(*x));
|
||||
}
|
||||
|
||||
std::reverse(data, data + sizeof(*x));
|
||||
return x;
|
||||
}
|
||||
|
||||
return x;
|
||||
}
|
||||
};
|
||||
|
||||
template<class T>
|
||||
@ -823,7 +829,7 @@ inline auto little(T x) { return endian_helper<T>::little(x); }
|
||||
|
||||
template<class T>
|
||||
inline auto big(T x) { return endian_helper<T>::big(x); }
|
||||
} /* endian */
|
||||
} // namespace endian
|
||||
|
||||
} /* util */
|
||||
} // namespace util
|
||||
#endif
|
||||
|
||||
@ -18,12 +18,12 @@ union uuid_t {
|
||||
std::uniform_int_distribution<std::uint8_t> dist(0, std::numeric_limits<std::uint8_t>::max());
|
||||
|
||||
uuid_t buf;
|
||||
for (auto &el : buf.b8) {
|
||||
for(auto &el : buf.b8) {
|
||||
el = dist(engine);
|
||||
}
|
||||
|
||||
buf.b8[7] &= (std::uint8_t) 0b00101111;
|
||||
buf.b8[9] &= (std::uint8_t) 0b10011111;
|
||||
buf.b8[7] &= (std::uint8_t)0b00101111;
|
||||
buf.b8[9] &= (std::uint8_t)0b10011111;
|
||||
|
||||
return buf;
|
||||
}
|
||||
@ -31,7 +31,7 @@ union uuid_t {
|
||||
static uuid_t generate() {
|
||||
std::random_device r;
|
||||
|
||||
std::default_random_engine engine{r()};
|
||||
std::default_random_engine engine { r() };
|
||||
|
||||
return generate(engine);
|
||||
}
|
||||
@ -41,7 +41,7 @@ union uuid_t {
|
||||
|
||||
result.reserve(sizeof(uuid_t) * 2 + 4);
|
||||
|
||||
auto hex = util::hex(*this, true);
|
||||
auto hex = util::hex(*this, true);
|
||||
auto hex_view = hex.to_string_view();
|
||||
|
||||
std::string_view slices[] = {
|
||||
@ -75,5 +75,5 @@ union uuid_t {
|
||||
return (b64[0] > other.b64[0] || (b64[0] == other.b64[0] && b64[1] > other.b64[1]));
|
||||
}
|
||||
};
|
||||
}
|
||||
} // namespace util
|
||||
#endif //T_MAN_UUID_H
|
||||
|
||||
@ -3,19 +3,19 @@
|
||||
//
|
||||
|
||||
#include <atomic>
|
||||
#include <thread>
|
||||
#include <bitset>
|
||||
#include <thread>
|
||||
|
||||
extern "C" {
|
||||
#include <libswscale/swscale.h>
|
||||
}
|
||||
|
||||
#include "config.h"
|
||||
#include "main.h"
|
||||
#include "platform/common.h"
|
||||
#include "round_robin.h"
|
||||
#include "sync.h"
|
||||
#include "config.h"
|
||||
#include "video.h"
|
||||
#include "main.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
extern "C" {
|
||||
@ -52,7 +52,7 @@ enum class profile_hevc_e : int {
|
||||
main_10,
|
||||
rext,
|
||||
};
|
||||
}
|
||||
} // namespace nv
|
||||
|
||||
using ctx_t = util::safe_ptr<AVCodecContext, free_ctx>;
|
||||
using frame_t = util::safe_ptr<AVFrame, free_frame>;
|
||||
@ -81,7 +81,7 @@ public:
|
||||
img.row_pitch, 0
|
||||
};
|
||||
|
||||
int ret = sws_scale(sws.get(), (std::uint8_t*const*)&img.data, linesizes, 0, img.height, frame->data, frame->linesize);
|
||||
int ret = sws_scale(sws.get(), (std::uint8_t *const *)&img.data, linesizes, 0, img.height, frame->data, frame->linesize);
|
||||
if(ret <= 0) {
|
||||
BOOST_LOG(fatal) << "Couldn't convert image to required format and/or size"sv;
|
||||
|
||||
@ -94,9 +94,8 @@ public:
|
||||
virtual void set_colorspace(std::uint32_t colorspace, std::uint32_t color_range) {
|
||||
sws_setColorspaceDetails(sws.get(),
|
||||
sws_getCoefficients(SWS_CS_DEFAULT), 0,
|
||||
sws_getCoefficients(colorspace), color_range -1,
|
||||
0, 1 << 16, 1 << 16
|
||||
);
|
||||
sws_getCoefficients(colorspace), color_range - 1,
|
||||
0, 1 << 16, 1 << 16);
|
||||
}
|
||||
|
||||
int init(int in_width, int in_height, int out_width, int out_height, AVFrame *frame, AVPixelFormat format) {
|
||||
@ -104,8 +103,7 @@ public:
|
||||
in_width, in_height, AV_PIX_FMT_BGR0,
|
||||
out_width, out_height, format,
|
||||
SWS_LANCZOS | SWS_ACCURATE_RND,
|
||||
nullptr, nullptr, nullptr
|
||||
));
|
||||
nullptr, nullptr, nullptr));
|
||||
data = frame;
|
||||
|
||||
return sws ? 0 : -1;
|
||||
@ -119,8 +117,8 @@ public:
|
||||
struct encoder_t {
|
||||
std::string_view name;
|
||||
enum flag_e {
|
||||
PASSED, // Is supported
|
||||
REF_FRAMES_RESTRICT, // Set maximum reference frames
|
||||
PASSED, // Is supported
|
||||
REF_FRAMES_RESTRICT, // Set maximum reference frames
|
||||
REF_FRAMES_AUTOSELECT, // Allow encoder to select maximum reference frames (If !REF_FRAMES_RESTRICT --> REF_FRAMES_AUTOSELECT)
|
||||
DYNAMIC_RANGE,
|
||||
MAX_FLAGS
|
||||
@ -131,9 +129,9 @@ struct encoder_t {
|
||||
option_t(const option_t &) = default;
|
||||
|
||||
std::string name;
|
||||
std::variant<int, int*, std::optional<int>*, std::string, std::string*> value;
|
||||
std::variant<int, int *, std::optional<int> *, std::string, std::string *> value;
|
||||
|
||||
option_t(std::string &&name, decltype(value) &&value) : name { std::move(name) }, value { std::move(value) } {}
|
||||
option_t(std::string &&name, decltype(value) &&value) : name { std::move(name) }, value { std::move(value) } {}
|
||||
};
|
||||
|
||||
struct {
|
||||
@ -167,24 +165,22 @@ struct encoder_t {
|
||||
bool system_memory;
|
||||
bool hevc_mode;
|
||||
|
||||
std::function<void(const platf::img_t&, frame_t&)> img_to_frame;
|
||||
std::function<void(const platf::img_t &, frame_t &)> img_to_frame;
|
||||
std::function<util::Either<buffer_t, int>(platf::hwdevice_t *hwdevice)> make_hwdevice_ctx;
|
||||
};
|
||||
|
||||
class session_t {
|
||||
public:
|
||||
session_t() = default;
|
||||
session_t(ctx_t &&ctx, frame_t &&frame, util::wrap_ptr<platf::hwdevice_t> &&device) :
|
||||
ctx { std::move(ctx) }, frame { std::move(frame) }, device { std::move(device) } {}
|
||||
session_t(ctx_t &&ctx, frame_t &&frame, util::wrap_ptr<platf::hwdevice_t> &&device) : ctx { std::move(ctx) }, frame { std::move(frame) }, device { std::move(device) } {}
|
||||
|
||||
session_t(session_t &&other) noexcept :
|
||||
ctx { std::move(other.ctx) }, frame { std::move(other.frame) }, device { std::move(other.device) } {}
|
||||
session_t(session_t &&other) noexcept : ctx { std::move(other.ctx) }, frame { std::move(other.frame) }, device { std::move(other.device) } {}
|
||||
|
||||
// Ensure objects are destroyed in the correct order
|
||||
session_t &operator=(session_t &&other) {
|
||||
device = std::move(other.device);
|
||||
frame = std::move(other.frame);
|
||||
ctx = std::move(other.ctx);
|
||||
frame = std::move(other.frame);
|
||||
ctx = std::move(other.ctx);
|
||||
|
||||
return *this;
|
||||
}
|
||||
@ -207,7 +203,7 @@ struct sync_session_ctx_t {
|
||||
|
||||
struct sync_session_t {
|
||||
sync_session_ctx_t *ctx;
|
||||
|
||||
|
||||
std::chrono::steady_clock::time_point next_frame;
|
||||
std::chrono::nanoseconds delay;
|
||||
|
||||
@ -217,7 +213,7 @@ struct sync_session_t {
|
||||
};
|
||||
|
||||
using encode_session_ctx_queue_t = safe::queue_t<sync_session_ctx_t>;
|
||||
using encode_e = platf::capture_e;
|
||||
using encode_e = platf::capture_e;
|
||||
|
||||
struct capture_ctx_t {
|
||||
img_event_t images;
|
||||
@ -244,7 +240,7 @@ void end_capture_async(capture_thread_async_ctx_t &ctx);
|
||||
|
||||
// Keep a reference counter to ensure the capture thread only runs when other threads have a reference to the capture thread
|
||||
auto capture_thread_async = safe::make_shared<capture_thread_async_ctx_t>(start_capture_async, end_capture_async);
|
||||
auto capture_thread_sync = safe::make_shared<capture_thread_sync_ctx_t>(start_capture_sync, end_capture_sync);
|
||||
auto capture_thread_sync = safe::make_shared<capture_thread_sync_ctx_t>(start_capture_sync, end_capture_sync);
|
||||
|
||||
#ifdef _WIN32
|
||||
static encoder_t nvenc {
|
||||
@ -254,26 +250,21 @@ static encoder_t nvenc {
|
||||
AV_PIX_FMT_D3D11,
|
||||
AV_PIX_FMT_NV12, AV_PIX_FMT_P010,
|
||||
{
|
||||
{
|
||||
{ "forced-idr"s, 1 },
|
||||
{ { "forced-idr"s, 1 },
|
||||
{ "zerolatency"s, 1 },
|
||||
{ "preset"s, &config::video.nv.preset },
|
||||
{ "rc"s, &config::video.nv.rc }
|
||||
},
|
||||
std::nullopt, std::nullopt,
|
||||
{ "rc"s, &config::video.nv.rc } },
|
||||
std::nullopt,
|
||||
std::nullopt,
|
||||
"hevc_nvenc"s,
|
||||
},
|
||||
{
|
||||
{
|
||||
{ "forced-idr"s, 1 },
|
||||
{ { { "forced-idr"s, 1 },
|
||||
{ "zerolatency"s, 1 },
|
||||
{ "preset"s, &config::video.nv.preset },
|
||||
{ "rc"s, &config::video.nv.rc },
|
||||
{ "coder"s, &config::video.nv.coder }
|
||||
},
|
||||
std::nullopt, std::make_optional<encoder_t::option_t>({"qp"s, &config::video.qp}),
|
||||
"h264_nvenc"s
|
||||
},
|
||||
{ "coder"s, &config::video.nv.coder } },
|
||||
std::nullopt, std::make_optional<encoder_t::option_t>({ "qp"s, &config::video.qp }),
|
||||
"h264_nvenc"s },
|
||||
false,
|
||||
true,
|
||||
|
||||
@ -288,26 +279,23 @@ static encoder_t amdvce {
|
||||
AV_PIX_FMT_D3D11,
|
||||
AV_PIX_FMT_NV12, AV_PIX_FMT_P010,
|
||||
{
|
||||
{
|
||||
{ "header_insertion_mode"s, "idr"s },
|
||||
{ { "header_insertion_mode"s, "idr"s },
|
||||
{ "gops_per_idr"s, 30 },
|
||||
{ "usage"s, "ultralowlatency"s },
|
||||
{ "quality"s, &config::video.amd.quality },
|
||||
{ "rc"s, &config::video.amd.rc }
|
||||
},
|
||||
std::nullopt, std::make_optional<encoder_t::option_t>({"qp"s, &config::video.qp}),
|
||||
{ "rc"s, &config::video.amd.rc } },
|
||||
std::nullopt,
|
||||
std::make_optional<encoder_t::option_t>({ "qp"s, &config::video.qp }),
|
||||
"hevc_amf"s,
|
||||
},
|
||||
{
|
||||
{
|
||||
{ {
|
||||
{ "usage"s, "ultralowlatency"s },
|
||||
{ "quality"s, &config::video.amd.quality },
|
||||
{ "rc"s, &config::video.amd.rc },
|
||||
{"log_to_dbg"s,"1"s},
|
||||
{ "log_to_dbg"s, "1"s },
|
||||
},
|
||||
std::nullopt, std::make_optional<encoder_t::option_t>({"qp"s, &config::video.qp}),
|
||||
"h264_amf"s
|
||||
},
|
||||
std::nullopt, std::make_optional<encoder_t::option_t>({ "qp"s, &config::video.qp }),
|
||||
"h264_amf"s },
|
||||
false,
|
||||
true,
|
||||
|
||||
@ -322,27 +310,20 @@ static encoder_t software {
|
||||
AV_HWDEVICE_TYPE_NONE,
|
||||
AV_PIX_FMT_NONE,
|
||||
AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV420P10,
|
||||
{
|
||||
// x265's Info SEI is so long that it causes the IDR picture data to be
|
||||
{ // x265's Info SEI is so long that it causes the IDR picture data to be
|
||||
// kicked to the 2nd packet in the frame, breaking Moonlight's parsing logic.
|
||||
// It also looks like gop_size isn't passed on to x265, so we have to set
|
||||
// 'keyint=-1' in the parameters ourselves.
|
||||
{
|
||||
{ "x265-params"s, "info=0:keyint=-1"s },
|
||||
{ "preset"s, &config::video.sw.preset },
|
||||
{ "tune"s, &config::video.sw.tune }
|
||||
},
|
||||
{ "tune"s, &config::video.sw.tune } },
|
||||
std::make_optional<encoder_t::option_t>("crf"s, &config::video.crf), std::make_optional<encoder_t::option_t>("qp"s, &config::video.qp),
|
||||
"libx265"s
|
||||
},
|
||||
{
|
||||
{
|
||||
{ "preset"s, &config::video.sw.preset },
|
||||
{ "tune"s, &config::video.sw.tune }
|
||||
},
|
||||
"libx265"s },
|
||||
{ { { "preset"s, &config::video.sw.preset },
|
||||
{ "tune"s, &config::video.sw.tune } },
|
||||
std::make_optional<encoder_t::option_t>("crf"s, &config::video.crf), std::make_optional<encoder_t::option_t>("qp"s, &config::video.qp),
|
||||
"libx264"s
|
||||
},
|
||||
"libx264"s },
|
||||
true,
|
||||
false,
|
||||
|
||||
@ -366,7 +347,7 @@ void reset_display(std::shared_ptr<platf::display_t> &disp, AVHWDeviceType type)
|
||||
if(disp) {
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
std::this_thread::sleep_for(200ms);
|
||||
}
|
||||
}
|
||||
@ -375,8 +356,7 @@ void captureThread(
|
||||
std::shared_ptr<safe::queue_t<capture_ctx_t>> capture_ctx_queue,
|
||||
util::sync_t<std::weak_ptr<platf::display_t>> &display_wp,
|
||||
safe::signal_t &reinit_event,
|
||||
const encoder_t &encoder
|
||||
) {
|
||||
const encoder_t &encoder) {
|
||||
std::vector<capture_ctx_t> capture_ctxs;
|
||||
|
||||
auto fg = util::fail_guard([&]() {
|
||||
@ -410,7 +390,7 @@ void captureThread(
|
||||
}
|
||||
}
|
||||
|
||||
if(auto capture_ctx = capture_ctx_queue->pop()) {
|
||||
if(auto capture_ctx = capture_ctx_queue->pop()) {
|
||||
capture_ctxs.emplace_back(std::move(*capture_ctx));
|
||||
|
||||
delay = capture_ctxs.back().delay;
|
||||
@ -430,64 +410,64 @@ void captureThread(
|
||||
while(img.use_count() > 1) {}
|
||||
|
||||
auto status = disp->snapshot(img.get(), 1000ms, display_cursor);
|
||||
switch (status) {
|
||||
case platf::capture_e::reinit: {
|
||||
reinit_event.raise(true);
|
||||
switch(status) {
|
||||
case platf::capture_e::reinit: {
|
||||
reinit_event.raise(true);
|
||||
|
||||
// Some classes of images contain references to the display --> display won't delete unless img is deleted
|
||||
for(auto &img : imgs) {
|
||||
img.reset();
|
||||
// Some classes of images contain references to the display --> display won't delete unless img is deleted
|
||||
for(auto &img : imgs) {
|
||||
img.reset();
|
||||
}
|
||||
|
||||
// Some classes of display cannot have multiple instances at once
|
||||
disp.reset();
|
||||
|
||||
// display_wp is modified in this thread only
|
||||
while(!display_wp->expired()) {
|
||||
std::this_thread::sleep_for(100ms);
|
||||
}
|
||||
|
||||
while(capture_ctx_queue->running()) {
|
||||
reset_display(disp, encoder.dev_type);
|
||||
|
||||
if(disp) {
|
||||
break;
|
||||
}
|
||||
std::this_thread::sleep_for(200ms);
|
||||
}
|
||||
if(!disp) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Some classes of display cannot have multiple instances at once
|
||||
disp.reset();
|
||||
|
||||
// display_wp is modified in this thread only
|
||||
while(!display_wp->expired()) {
|
||||
std::this_thread::sleep_for(100ms);
|
||||
}
|
||||
|
||||
while(capture_ctx_queue->running()) {
|
||||
reset_display(disp, encoder.dev_type);
|
||||
|
||||
if(disp) {
|
||||
break;
|
||||
}
|
||||
std::this_thread::sleep_for(200ms);
|
||||
}
|
||||
if(!disp) {
|
||||
display_wp = disp;
|
||||
// Re-allocate images
|
||||
for(auto &img : imgs) {
|
||||
img = disp->alloc_img();
|
||||
if(!img) {
|
||||
BOOST_LOG(error) << "Couldn't initialize an image"sv;
|
||||
return;
|
||||
}
|
||||
|
||||
display_wp = disp;
|
||||
// Re-allocate images
|
||||
for(auto &img : imgs) {
|
||||
img = disp->alloc_img();
|
||||
if(!img) {
|
||||
BOOST_LOG(error) << "Couldn't initialize an image"sv;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
reinit_event.reset();
|
||||
continue;
|
||||
}
|
||||
case platf::capture_e::error:
|
||||
return;
|
||||
case platf::capture_e::timeout:
|
||||
std::this_thread::sleep_for(1ms);
|
||||
continue;
|
||||
case platf::capture_e::ok:
|
||||
break;
|
||||
default:
|
||||
BOOST_LOG(error) << "Unrecognized capture status ["sv << (int)status << ']';
|
||||
return;
|
||||
|
||||
reinit_event.reset();
|
||||
continue;
|
||||
}
|
||||
case platf::capture_e::error:
|
||||
return;
|
||||
case platf::capture_e::timeout:
|
||||
std::this_thread::sleep_for(1ms);
|
||||
continue;
|
||||
case platf::capture_e::ok:
|
||||
break;
|
||||
default:
|
||||
BOOST_LOG(error) << "Unrecognized capture status ["sv << (int)status << ']';
|
||||
return;
|
||||
}
|
||||
|
||||
KITTY_WHILE_LOOP(auto capture_ctx = std::begin(capture_ctxs), capture_ctx != std::end(capture_ctxs), {
|
||||
if(!capture_ctx->images->running()) {
|
||||
auto tmp_delay = capture_ctx->delay;
|
||||
capture_ctx = capture_ctxs.erase(capture_ctx);
|
||||
capture_ctx = capture_ctxs.erase(capture_ctx);
|
||||
|
||||
if(tmp_delay == delay) {
|
||||
delay = std::min_element(std::begin(capture_ctxs), std::end(capture_ctxs), [](const auto &l, const auto &r) {
|
||||
@ -513,21 +493,21 @@ int encode(int64_t frame_nr, ctx_t &ctx, frame_t &frame, packet_queue_t &packets
|
||||
|
||||
/* send the frame to the encoder */
|
||||
auto ret = avcodec_send_frame(ctx.get(), frame.get());
|
||||
if (ret < 0) {
|
||||
char err_str[AV_ERROR_MAX_STRING_SIZE] {0};
|
||||
if(ret < 0) {
|
||||
char err_str[AV_ERROR_MAX_STRING_SIZE] { 0 };
|
||||
BOOST_LOG(error) << "Could not send a frame for encoding: "sv << av_make_error_string(err_str, AV_ERROR_MAX_STRING_SIZE, ret);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (ret >= 0) {
|
||||
while(ret >= 0) {
|
||||
auto packet = std::make_unique<packet_t::element_type>(nullptr);
|
||||
|
||||
ret = avcodec_receive_packet(ctx.get(), packet.get());
|
||||
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
|
||||
if(ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
|
||||
return 0;
|
||||
}
|
||||
else if (ret < 0) {
|
||||
else if(ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -543,7 +523,7 @@ std::optional<session_t> make_session(const encoder_t &encoder, const config_t &
|
||||
|
||||
auto &video_format = config.videoFormat == 0 ? encoder.h264 : encoder.hevc;
|
||||
if(!video_format[encoder_t::PASSED]) {
|
||||
BOOST_LOG(error) << encoder.name << ": "sv << video_format.name << " mode not supported"sv;
|
||||
BOOST_LOG(error) << encoder.name << ": "sv << video_format.name << " mode not supported"sv;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
@ -560,10 +540,10 @@ std::optional<session_t> make_session(const encoder_t &encoder, const config_t &
|
||||
}
|
||||
|
||||
ctx_t ctx { avcodec_alloc_context3(codec) };
|
||||
ctx->width = config.width;
|
||||
ctx->height = config.height;
|
||||
ctx->time_base = AVRational{1, config.framerate};
|
||||
ctx->framerate = AVRational{config.framerate, 1};
|
||||
ctx->width = config.width;
|
||||
ctx->height = config.height;
|
||||
ctx->time_base = AVRational { 1, config.framerate };
|
||||
ctx->framerate = AVRational { config.framerate, 1 };
|
||||
|
||||
if(config.videoFormat == 0) {
|
||||
ctx->profile = encoder.profile.h264_high;
|
||||
@ -579,7 +559,7 @@ std::optional<session_t> make_session(const encoder_t &encoder, const config_t &
|
||||
ctx->max_b_frames = 0;
|
||||
|
||||
// Use an infinite GOP length since I-frames are generated on demand
|
||||
ctx->gop_size = std::numeric_limits<int>::max();
|
||||
ctx->gop_size = std::numeric_limits<int>::max();
|
||||
ctx->keyint_min = ctx->gop_size;
|
||||
|
||||
if(config.numRefFrames == 0) {
|
||||
@ -596,34 +576,34 @@ std::optional<session_t> make_session(const encoder_t &encoder, const config_t &
|
||||
ctx->color_range = (config.encoderCscMode & 0x1) ? AVCOL_RANGE_JPEG : AVCOL_RANGE_MPEG;
|
||||
|
||||
int sws_color_space;
|
||||
switch (config.encoderCscMode >> 1) {
|
||||
case 0:
|
||||
default:
|
||||
// Rec. 601
|
||||
BOOST_LOG(info) << "Color coding [Rec. 601]"sv;
|
||||
ctx->color_primaries = AVCOL_PRI_SMPTE170M;
|
||||
ctx->color_trc = AVCOL_TRC_SMPTE170M;
|
||||
ctx->colorspace = AVCOL_SPC_SMPTE170M;
|
||||
sws_color_space = SWS_CS_SMPTE170M;
|
||||
break;
|
||||
switch(config.encoderCscMode >> 1) {
|
||||
case 0:
|
||||
default:
|
||||
// Rec. 601
|
||||
BOOST_LOG(info) << "Color coding [Rec. 601]"sv;
|
||||
ctx->color_primaries = AVCOL_PRI_SMPTE170M;
|
||||
ctx->color_trc = AVCOL_TRC_SMPTE170M;
|
||||
ctx->colorspace = AVCOL_SPC_SMPTE170M;
|
||||
sws_color_space = SWS_CS_SMPTE170M;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
// Rec. 709
|
||||
BOOST_LOG(info) << "Color coding [Rec. 709]"sv;
|
||||
ctx->color_primaries = AVCOL_PRI_BT709;
|
||||
ctx->color_trc = AVCOL_TRC_BT709;
|
||||
ctx->colorspace = AVCOL_SPC_BT709;
|
||||
sws_color_space = SWS_CS_ITU709;
|
||||
break;
|
||||
case 1:
|
||||
// Rec. 709
|
||||
BOOST_LOG(info) << "Color coding [Rec. 709]"sv;
|
||||
ctx->color_primaries = AVCOL_PRI_BT709;
|
||||
ctx->color_trc = AVCOL_TRC_BT709;
|
||||
ctx->colorspace = AVCOL_SPC_BT709;
|
||||
sws_color_space = SWS_CS_ITU709;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
// Rec. 2020
|
||||
BOOST_LOG(info) << "Color coding [Rec. 2020]"sv;
|
||||
ctx->color_primaries = AVCOL_PRI_BT2020;
|
||||
ctx->color_trc = AVCOL_TRC_BT2020_10;
|
||||
ctx->colorspace = AVCOL_SPC_BT2020_NCL;
|
||||
sws_color_space = SWS_CS_BT2020;
|
||||
break;
|
||||
case 2:
|
||||
// Rec. 2020
|
||||
BOOST_LOG(info) << "Color coding [Rec. 2020]"sv;
|
||||
ctx->color_primaries = AVCOL_PRI_BT2020;
|
||||
ctx->color_trc = AVCOL_TRC_BT2020_10;
|
||||
ctx->colorspace = AVCOL_SPC_BT2020_NCL;
|
||||
sws_color_space = SWS_CS_BT2020;
|
||||
break;
|
||||
}
|
||||
BOOST_LOG(info) << "Color range: ["sv << ((config.encoderCscMode & 0x1) ? "JPEG"sv : "MPEG"sv) << ']';
|
||||
|
||||
@ -660,18 +640,19 @@ std::optional<session_t> make_session(const encoder_t &encoder, const config_t &
|
||||
ctx->slices = std::max(config.slicesPerFrame, config::video.min_threads);
|
||||
}
|
||||
|
||||
ctx->thread_type = FF_THREAD_SLICE;
|
||||
ctx->thread_type = FF_THREAD_SLICE;
|
||||
ctx->thread_count = ctx->slices;
|
||||
|
||||
AVDictionary *options {nullptr};
|
||||
AVDictionary *options { nullptr };
|
||||
auto handle_option = [&options](const encoder_t::option_t &option) {
|
||||
std::visit(util::overloaded {
|
||||
[&](int v) { av_dict_set_int(&options, option.name.c_str(), v, 0); },
|
||||
[&](int *v) { av_dict_set_int(&options, option.name.c_str(), *v, 0); },
|
||||
[&](std::optional<int> *v) { if(*v) av_dict_set_int(&options, option.name.c_str(), **v, 0); },
|
||||
[&](const std::string &v) { av_dict_set(&options, option.name.c_str(), v.c_str(), 0); },
|
||||
[&](std::string *v) { if(!v->empty()) av_dict_set(&options, option.name.c_str(), v->c_str(), 0); }
|
||||
}, option.value);
|
||||
std::visit(
|
||||
util::overloaded {
|
||||
[&](int v) { av_dict_set_int(&options, option.name.c_str(), v, 0); },
|
||||
[&](int *v) { av_dict_set_int(&options, option.name.c_str(), *v, 0); },
|
||||
[&](std::optional<int> *v) { if(*v) av_dict_set_int(&options, option.name.c_str(), **v, 0); },
|
||||
[&](const std::string &v) { av_dict_set(&options, option.name.c_str(), v.c_str(), 0); },
|
||||
[&](std::string *v) { if(!v->empty()) av_dict_set(&options, option.name.c_str(), v->c_str(), 0); } },
|
||||
option.value);
|
||||
};
|
||||
|
||||
for(auto &option : video_format.options) {
|
||||
@ -679,11 +660,11 @@ std::optional<session_t> make_session(const encoder_t &encoder, const config_t &
|
||||
}
|
||||
|
||||
if(config.bitrate > 500) {
|
||||
auto bitrate = config.bitrate * 1000;
|
||||
ctx->rc_max_rate = bitrate;
|
||||
auto bitrate = config.bitrate * 1000;
|
||||
ctx->rc_max_rate = bitrate;
|
||||
ctx->rc_buffer_size = bitrate / config.framerate;
|
||||
ctx->bit_rate = bitrate;
|
||||
ctx->rc_min_rate = bitrate;
|
||||
ctx->bit_rate = bitrate;
|
||||
ctx->rc_min_rate = bitrate;
|
||||
}
|
||||
else if(video_format.crf && config::video.crf != 0) {
|
||||
handle_option(*video_format.crf);
|
||||
@ -698,7 +679,7 @@ std::optional<session_t> make_session(const encoder_t &encoder, const config_t &
|
||||
|
||||
avcodec_open2(ctx.get(), codec, &options);
|
||||
|
||||
frame_t frame {av_frame_alloc() };
|
||||
frame_t frame { av_frame_alloc() };
|
||||
frame->format = ctx->pix_fmt;
|
||||
frame->width = ctx->width;
|
||||
frame->height = ctx->height;
|
||||
@ -730,13 +711,12 @@ std::optional<session_t> make_session(const encoder_t &encoder, const config_t &
|
||||
return std::make_optional(session_t {
|
||||
std::move(ctx),
|
||||
std::move(frame),
|
||||
std::move(device)
|
||||
});
|
||||
std::move(device) });
|
||||
}
|
||||
|
||||
void encode_run(
|
||||
int &frame_nr, int &key_frame_nr, // Store progress of the frame number
|
||||
safe::signal_t* shutdown_event, // Signal for shutdown event of the session
|
||||
safe::signal_t *shutdown_event, // Signal for shutdown event of the session
|
||||
packet_queue_t packets,
|
||||
idr_event_t idr_events,
|
||||
img_event_t images,
|
||||
@ -763,13 +743,13 @@ void encode_run(
|
||||
if(idr_events->peek()) {
|
||||
session->frame->pict_type = AV_PICTURE_TYPE_I;
|
||||
session->frame->key_frame = 1;
|
||||
auto event = idr_events->pop();
|
||||
auto event = idr_events->pop();
|
||||
if(!event) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto end = event->second;
|
||||
frame_nr = end;
|
||||
auto end = event->second;
|
||||
frame_nr = end;
|
||||
key_frame_nr = end + config.framerate;
|
||||
}
|
||||
else if(frame_nr == key_frame_nr) {
|
||||
@ -794,7 +774,7 @@ void encode_run(
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if(encode(frame_nr++, session->ctx, session->frame, packets, channel_data)) {
|
||||
BOOST_LOG(error) << "Could not encode video packet"sv;
|
||||
return;
|
||||
@ -808,12 +788,12 @@ void encode_run(
|
||||
std::optional<sync_session_t> make_synced_session(platf::display_t *disp, const encoder_t &encoder, platf::img_t &img, sync_session_ctx_t &ctx) {
|
||||
sync_session_t encode_session;
|
||||
|
||||
encode_session.ctx = &ctx;
|
||||
encode_session.ctx = &ctx;
|
||||
encode_session.next_frame = std::chrono::steady_clock::now();
|
||||
|
||||
encode_session.delay = std::chrono::nanoseconds { 1s } / ctx.config.framerate;
|
||||
|
||||
auto pix_fmt = ctx.config.dynamicRange == 0 ? map_pix_fmt(encoder.static_pix_fmt) : map_pix_fmt(encoder.dynamic_pix_fmt);
|
||||
auto pix_fmt = ctx.config.dynamicRange == 0 ? map_pix_fmt(encoder.static_pix_fmt) : map_pix_fmt(encoder.dynamic_pix_fmt);
|
||||
auto hwdevice = disp->make_hwdevice(ctx.config.width, ctx.config.height, pix_fmt);
|
||||
if(!hwdevice) {
|
||||
return std::nullopt;
|
||||
@ -824,9 +804,9 @@ std::optional<sync_session_t> make_synced_session(platf::display_t *disp, const
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
encode_session.img_tmp = &img;
|
||||
encode_session.img_tmp = &img;
|
||||
encode_session.hwdevice = std::move(hwdevice);
|
||||
encode_session.session = std::move(*session);
|
||||
encode_session.session = std::move(*session);
|
||||
|
||||
return std::move(encode_session);
|
||||
}
|
||||
@ -873,7 +853,7 @@ encode_e encode_run_sync(std::vector<std::unique_ptr<sync_session_ctx_t>> &synce
|
||||
while(encode_session_ctx_queue.running()) {
|
||||
while(encode_session_ctx_queue.peek()) {
|
||||
auto encode_session_ctx = encode_session_ctx_queue.pop();
|
||||
if(!encode_session_ctx) {
|
||||
if(!encode_session_ctx) {
|
||||
return encode_e::ok;
|
||||
}
|
||||
|
||||
@ -892,28 +872,28 @@ encode_e encode_run_sync(std::vector<std::unique_ptr<sync_session_ctx_t>> &synce
|
||||
auto delay = std::max(0ms, std::chrono::duration_cast<std::chrono::milliseconds>(next_frame - std::chrono::steady_clock::now()));
|
||||
|
||||
auto status = disp->snapshot(img.get(), delay, display_cursor);
|
||||
switch(status) {
|
||||
case platf::capture_e::reinit:
|
||||
case platf::capture_e::error:
|
||||
return status;
|
||||
case platf::capture_e::timeout:
|
||||
break;
|
||||
case platf::capture_e::ok:
|
||||
img_tmp = img.get();
|
||||
break;
|
||||
switch(status) {
|
||||
case platf::capture_e::reinit:
|
||||
case platf::capture_e::error:
|
||||
return status;
|
||||
case platf::capture_e::timeout:
|
||||
break;
|
||||
case platf::capture_e::ok:
|
||||
img_tmp = img.get();
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
|
||||
|
||||
next_frame = now + 1s;
|
||||
KITTY_WHILE_LOOP(auto pos = std::begin(synced_sessions), pos != std::end(synced_sessions), {
|
||||
auto ctx = pos->ctx;
|
||||
if(ctx->shutdown_event->peek()) {
|
||||
// Let waiting thread know it can delete shutdown_event
|
||||
ctx->join_event->raise(true);
|
||||
|
||||
|
||||
pos = synced_sessions.erase(pos);
|
||||
synced_session_ctxs.erase(std::find_if(std::begin(synced_session_ctxs), std::end(synced_session_ctxs), [&ctx_p=ctx](auto &ctx) {
|
||||
synced_session_ctxs.erase(std::find_if(std::begin(synced_session_ctxs), std::end(synced_session_ctxs), [&ctx_p = ctx](auto &ctx) {
|
||||
return ctx.get() == ctx_p;
|
||||
}));
|
||||
|
||||
@ -929,9 +909,9 @@ encode_e encode_run_sync(std::vector<std::unique_ptr<sync_session_ctx_t>> &synce
|
||||
pos->session.frame->key_frame = 1;
|
||||
|
||||
auto event = ctx->idr_events->pop();
|
||||
auto end = event->second;
|
||||
auto end = event->second;
|
||||
|
||||
ctx->frame_nr = end;
|
||||
ctx->frame_nr = end;
|
||||
ctx->key_frame_nr = end + ctx->config.framerate;
|
||||
}
|
||||
else if(ctx->frame_nr == ctx->key_frame_nr) {
|
||||
@ -992,7 +972,7 @@ void captureThreadSync() {
|
||||
std::vector<std::unique_ptr<sync_session_ctx_t>> synced_session_ctxs;
|
||||
|
||||
auto &ctx = ref->encode_session_ctx_queue;
|
||||
auto lg = util::fail_guard([&]() {
|
||||
auto lg = util::fail_guard([&]() {
|
||||
ctx.stop();
|
||||
|
||||
for(auto &ctx : synced_session_ctxs) {
|
||||
@ -1006,7 +986,8 @@ void captureThreadSync() {
|
||||
}
|
||||
});
|
||||
|
||||
while(encode_run_sync(synced_session_ctxs, ctx) == encode_e::reinit);
|
||||
while(encode_run_sync(synced_session_ctxs, ctx) == encode_e::reinit)
|
||||
;
|
||||
}
|
||||
|
||||
void capture_async(
|
||||
@ -1017,7 +998,7 @@ void capture_async(
|
||||
void *channel_data) {
|
||||
|
||||
auto images = std::make_shared<img_event_t::element_type>();
|
||||
auto lg = util::fail_guard([&]() {
|
||||
auto lg = util::fail_guard([&]() {
|
||||
images->stop();
|
||||
shutdown_event->raise(true);
|
||||
});
|
||||
@ -1029,14 +1010,13 @@ void capture_async(
|
||||
|
||||
auto delay = std::chrono::floor<std::chrono::nanoseconds>(1s) / config.framerate;
|
||||
ref->capture_ctx_queue->raise(capture_ctx_t {
|
||||
images, delay
|
||||
});
|
||||
images, delay });
|
||||
|
||||
if(!ref->capture_ctx_queue->running()) {
|
||||
return;
|
||||
}
|
||||
|
||||
int frame_nr = 1;
|
||||
int frame_nr = 1;
|
||||
int key_frame_nr = 1;
|
||||
|
||||
while(!shutdown_event->peek() && images->running()) {
|
||||
@ -1056,7 +1036,7 @@ void capture_async(
|
||||
display = ref->display_wp->lock();
|
||||
}
|
||||
|
||||
auto pix_fmt = config.dynamicRange == 0 ? platf::pix_fmt_e::yuv420p : platf::pix_fmt_e::yuv420p10;
|
||||
auto pix_fmt = config.dynamicRange == 0 ? platf::pix_fmt_e::yuv420p : platf::pix_fmt_e::yuv420p10;
|
||||
auto hwdevice = display->make_hwdevice(config.width, config.height, pix_fmt);
|
||||
if(!hwdevice) {
|
||||
return;
|
||||
@ -1098,8 +1078,7 @@ void capture(
|
||||
safe::signal_t join_event;
|
||||
auto ref = capture_thread_sync.ref();
|
||||
ref->encode_session_ctx_queue.raise(sync_session_ctx_t {
|
||||
shutdown_event, &join_event, packets, idr_events, config, 1, 1, channel_data
|
||||
});
|
||||
shutdown_event, &join_event, packets, idr_events, config, 1, 1, channel_data });
|
||||
|
||||
// Wait for join signal
|
||||
join_event.view();
|
||||
@ -1112,7 +1091,7 @@ bool validate_config(std::shared_ptr<platf::display_t> &disp, const encoder_t &e
|
||||
return false;
|
||||
}
|
||||
|
||||
auto pix_fmt = config.dynamicRange == 0 ? map_pix_fmt(encoder.static_pix_fmt) : map_pix_fmt(encoder.dynamic_pix_fmt);
|
||||
auto pix_fmt = config.dynamicRange == 0 ? map_pix_fmt(encoder.static_pix_fmt) : map_pix_fmt(encoder.dynamic_pix_fmt);
|
||||
auto hwdevice = disp->make_hwdevice(config.width, config.height, pix_fmt);
|
||||
if(!hwdevice) {
|
||||
return false;
|
||||
@ -1152,14 +1131,14 @@ bool validate_encoder(encoder_t &encoder) {
|
||||
});
|
||||
|
||||
auto force_hevc = config::video.hevc_mode >= 2;
|
||||
auto test_hevc = force_hevc || (config::video.hevc_mode == 0 && encoder.hevc_mode);
|
||||
auto test_hevc = force_hevc || (config::video.hevc_mode == 0 && encoder.hevc_mode);
|
||||
|
||||
encoder.h264.capabilities.set();
|
||||
encoder.hevc.capabilities.set();
|
||||
|
||||
// First, test encoder viability
|
||||
config_t config_max_ref_frames { 1920, 1080, 60, 1000, 1, 1, 1, 0, 0 };
|
||||
config_t config_autoselect { 1920, 1080, 60, 1000, 1, 0, 1, 0, 0 };
|
||||
config_t config_autoselect { 1920, 1080, 60, 1000, 1, 0, 1, 0, 0 };
|
||||
|
||||
auto max_ref_frames_h264 = validate_config(disp, encoder, config_max_ref_frames);
|
||||
auto autoselect_h264 = validate_config(disp, encoder, config_autoselect);
|
||||
@ -1168,13 +1147,13 @@ bool validate_encoder(encoder_t &encoder) {
|
||||
return false;
|
||||
}
|
||||
|
||||
encoder.h264[encoder_t::REF_FRAMES_RESTRICT] = max_ref_frames_h264;
|
||||
encoder.h264[encoder_t::REF_FRAMES_RESTRICT] = max_ref_frames_h264;
|
||||
encoder.h264[encoder_t::REF_FRAMES_AUTOSELECT] = autoselect_h264;
|
||||
encoder.h264[encoder_t::PASSED] = true;
|
||||
encoder.h264[encoder_t::PASSED] = true;
|
||||
|
||||
if(test_hevc) {
|
||||
config_max_ref_frames.videoFormat = 1;
|
||||
config_autoselect.videoFormat = 1;
|
||||
config_autoselect.videoFormat = 1;
|
||||
|
||||
auto max_ref_frames_hevc = validate_config(disp, encoder, config_max_ref_frames);
|
||||
auto autoselect_hevc = validate_config(disp, encoder, config_autoselect);
|
||||
@ -1184,7 +1163,7 @@ bool validate_encoder(encoder_t &encoder) {
|
||||
return false;
|
||||
}
|
||||
|
||||
encoder.hevc[encoder_t::REF_FRAMES_RESTRICT] = max_ref_frames_hevc;
|
||||
encoder.hevc[encoder_t::REF_FRAMES_RESTRICT] = max_ref_frames_hevc;
|
||||
encoder.hevc[encoder_t::REF_FRAMES_AUTOSELECT] = autoselect_hevc;
|
||||
}
|
||||
encoder.hevc[encoder_t::PASSED] = test_hevc;
|
||||
@ -1222,10 +1201,9 @@ int init() {
|
||||
|
||||
KITTY_WHILE_LOOP(auto pos = std::begin(encoders), pos != std::end(encoders), {
|
||||
if(
|
||||
(!config::video.encoder.empty() && pos->name != config::video.encoder) ||
|
||||
!validate_encoder(*pos) ||
|
||||
(config::video.hevc_mode == 3 && !pos->hevc[encoder_t::DYNAMIC_RANGE])
|
||||
) {
|
||||
(!config::video.encoder.empty() && pos->name != config::video.encoder) ||
|
||||
!validate_encoder(*pos) ||
|
||||
(config::video.hevc_mode == 3 && !pos->hevc[encoder_t::DYNAMIC_RANGE])) {
|
||||
pos = encoders.erase(pos);
|
||||
|
||||
continue;
|
||||
@ -1235,7 +1213,7 @@ int init() {
|
||||
})
|
||||
|
||||
if(encoders.empty()) {
|
||||
if(config::video.encoder.empty()) {
|
||||
if(config::video.encoder.empty()) {
|
||||
BOOST_LOG(fatal) << "Couldn't find any encoder"sv;
|
||||
}
|
||||
else {
|
||||
@ -1258,7 +1236,7 @@ int init() {
|
||||
BOOST_LOG(info) << "Found encoder "sv << encoder.name << ": ["sv << encoder.h264.name << ", "sv << encoder.hevc.name << ']';
|
||||
}
|
||||
else {
|
||||
BOOST_LOG(info) << "Found encoder "sv << encoder.name << ": ["sv << encoder.h264.name << ']';
|
||||
BOOST_LOG(info) << "Found encoder "sv << encoder.name << ": ["sv << encoder.h264.name << ']';
|
||||
}
|
||||
|
||||
if(config::video.hevc_mode == 0) {
|
||||
@ -1274,12 +1252,12 @@ util::Either<buffer_t, int> make_hwdevice_ctx(AVHWDeviceType type, void *hwdevic
|
||||
int err;
|
||||
if(hwdevice) {
|
||||
ctx.reset(av_hwdevice_ctx_alloc(type));
|
||||
((AVHWDeviceContext*)ctx.get())->hwctx = hwdevice;
|
||||
((AVHWDeviceContext *)ctx.get())->hwctx = hwdevice;
|
||||
|
||||
err = av_hwdevice_ctx_init(ctx.get());
|
||||
}
|
||||
else {
|
||||
AVBufferRef *ref {};
|
||||
AVBufferRef *ref {};
|
||||
err = av_hwdevice_ctx_create(&ref, type, nullptr, nullptr, 0);
|
||||
ctx.reset(ref);
|
||||
}
|
||||
@ -1292,13 +1270,13 @@ util::Either<buffer_t, int> make_hwdevice_ctx(AVHWDeviceType type, void *hwdevic
|
||||
}
|
||||
|
||||
int hwframe_ctx(ctx_t &ctx, buffer_t &hwdevice, AVPixelFormat format) {
|
||||
buffer_t frame_ref { av_hwframe_ctx_alloc(hwdevice.get())};
|
||||
buffer_t frame_ref { av_hwframe_ctx_alloc(hwdevice.get()) };
|
||||
|
||||
auto frame_ctx = (AVHWFramesContext*)frame_ref->data;
|
||||
frame_ctx->format = ctx->pix_fmt;
|
||||
frame_ctx->sw_format = format;
|
||||
frame_ctx->height = ctx->height;
|
||||
frame_ctx->width = ctx->width;
|
||||
auto frame_ctx = (AVHWFramesContext *)frame_ref->data;
|
||||
frame_ctx->format = ctx->pix_fmt;
|
||||
frame_ctx->sw_format = format;
|
||||
frame_ctx->height = ctx->height;
|
||||
frame_ctx->width = ctx->width;
|
||||
frame_ctx->initial_pool_size = 0;
|
||||
|
||||
if(auto err = av_hwframe_ctx_init(frame_ref.get()); err < 0) {
|
||||
@ -1319,22 +1297,22 @@ void sw_img_to_frame(const platf::img_t &img, frame_t &frame) {}
|
||||
namespace platf::dxgi {
|
||||
void lock(void *hwdevice);
|
||||
void unlock(void *hwdevice);
|
||||
}
|
||||
void do_nothing(void*) {}
|
||||
} // namespace platf::dxgi
|
||||
void do_nothing(void *) {}
|
||||
namespace video {
|
||||
void dxgi_img_to_frame(const platf::img_t &img, frame_t &frame) {
|
||||
if(img.data == frame->data[0]) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Need to have something refcounted
|
||||
if(!frame->buf[0]) {
|
||||
frame->buf[0] = av_buffer_allocz(sizeof(AVD3D11FrameDescriptor));
|
||||
}
|
||||
|
||||
auto desc = (AVD3D11FrameDescriptor*)frame->buf[0]->data;
|
||||
desc->texture = (ID3D11Texture2D*)img.data;
|
||||
desc->index = 0;
|
||||
auto desc = (AVD3D11FrameDescriptor *)frame->buf[0]->data;
|
||||
desc->texture = (ID3D11Texture2D *)img.data;
|
||||
desc->index = 0;
|
||||
|
||||
frame->data[0] = img.data;
|
||||
frame->data[1] = 0;
|
||||
@ -1342,27 +1320,27 @@ void dxgi_img_to_frame(const platf::img_t &img, frame_t &frame) {
|
||||
frame->linesize[0] = img.row_pitch;
|
||||
|
||||
frame->height = img.height;
|
||||
frame->width = img.width;
|
||||
frame->width = img.width;
|
||||
}
|
||||
|
||||
util::Either<buffer_t, int> dxgi_make_hwdevice_ctx(platf::hwdevice_t *hwdevice_ctx) {
|
||||
buffer_t ctx_buf { av_hwdevice_ctx_alloc(AV_HWDEVICE_TYPE_D3D11VA) };
|
||||
auto ctx = (AVD3D11VADeviceContext*)((AVHWDeviceContext*)ctx_buf->data)->hwctx;
|
||||
|
||||
std::fill_n((std::uint8_t*)ctx, sizeof(AVD3D11VADeviceContext), 0);
|
||||
auto ctx = (AVD3D11VADeviceContext *)((AVHWDeviceContext *)ctx_buf->data)->hwctx;
|
||||
|
||||
auto device = (ID3D11Device*)hwdevice_ctx->data;
|
||||
std::fill_n((std::uint8_t *)ctx, sizeof(AVD3D11VADeviceContext), 0);
|
||||
|
||||
auto device = (ID3D11Device *)hwdevice_ctx->data;
|
||||
|
||||
device->AddRef();
|
||||
ctx->device = device;
|
||||
|
||||
ctx->lock_ctx = (void*)1;
|
||||
ctx->lock = do_nothing;
|
||||
ctx->unlock = do_nothing;
|
||||
ctx->lock_ctx = (void *)1;
|
||||
ctx->lock = do_nothing;
|
||||
ctx->unlock = do_nothing;
|
||||
|
||||
auto err = av_hwdevice_ctx_init(ctx_buf.get());
|
||||
if(err) {
|
||||
char err_str[AV_ERROR_MAX_STRING_SIZE] {0};
|
||||
char err_str[AV_ERROR_MAX_STRING_SIZE] { 0 };
|
||||
BOOST_LOG(error) << "Failed to create FFMpeg hardware device context: "sv << av_make_error_string(err_str, AV_ERROR_MAX_STRING_SIZE, err);
|
||||
|
||||
return err;
|
||||
@ -1402,12 +1380,12 @@ void end_capture_sync(capture_thread_sync_ctx_t &ctx) {}
|
||||
|
||||
platf::dev_type_e map_dev_type(AVHWDeviceType type) {
|
||||
switch(type) {
|
||||
case AV_HWDEVICE_TYPE_D3D11VA:
|
||||
return platf::dev_type_e::dxgi;
|
||||
case AV_PICTURE_TYPE_NONE:
|
||||
return platf::dev_type_e::none;
|
||||
default:
|
||||
return platf::dev_type_e::unknown;
|
||||
case AV_HWDEVICE_TYPE_D3D11VA:
|
||||
return platf::dev_type_e::dxgi;
|
||||
case AV_PICTURE_TYPE_NONE:
|
||||
return platf::dev_type_e::none;
|
||||
default:
|
||||
return platf::dev_type_e::unknown;
|
||||
}
|
||||
|
||||
return platf::dev_type_e::unknown;
|
||||
@ -1415,16 +1393,16 @@ platf::dev_type_e map_dev_type(AVHWDeviceType type) {
|
||||
|
||||
platf::pix_fmt_e map_pix_fmt(AVPixelFormat fmt) {
|
||||
switch(fmt) {
|
||||
case AV_PIX_FMT_YUV420P10:
|
||||
return platf::pix_fmt_e::yuv420p10;
|
||||
case AV_PIX_FMT_YUV420P:
|
||||
return platf::pix_fmt_e::yuv420p;
|
||||
case AV_PIX_FMT_NV12:
|
||||
return platf::pix_fmt_e::nv12;
|
||||
case AV_PIX_FMT_P010:
|
||||
return platf::pix_fmt_e::p010;
|
||||
default:
|
||||
return platf::pix_fmt_e::unknown;
|
||||
case AV_PIX_FMT_YUV420P10:
|
||||
return platf::pix_fmt_e::yuv420p10;
|
||||
case AV_PIX_FMT_YUV420P:
|
||||
return platf::pix_fmt_e::yuv420p;
|
||||
case AV_PIX_FMT_NV12:
|
||||
return platf::pix_fmt_e::nv12;
|
||||
case AV_PIX_FMT_P010:
|
||||
return platf::pix_fmt_e::p010;
|
||||
default:
|
||||
return platf::pix_fmt_e::unknown;
|
||||
}
|
||||
|
||||
return platf::pix_fmt_e::unknown;
|
||||
|
||||
@ -5,9 +5,9 @@
|
||||
#ifndef SUNSHINE_VIDEO_H
|
||||
#define SUNSHINE_VIDEO_H
|
||||
|
||||
#include "thread_safe.h"
|
||||
#include "input.h"
|
||||
#include "platform/common.h"
|
||||
#include "thread_safe.h"
|
||||
|
||||
extern "C" {
|
||||
#include <libavcodec/avcodec.h>
|
||||
@ -18,15 +18,15 @@ namespace video {
|
||||
|
||||
struct packet_raw_t : public AVPacket {
|
||||
void init_packet() {
|
||||
pts = AV_NOPTS_VALUE;
|
||||
dts = AV_NOPTS_VALUE;
|
||||
pos = -1;
|
||||
duration = 0;
|
||||
flags = 0;
|
||||
stream_index = 0;
|
||||
buf = nullptr;
|
||||
side_data = nullptr;
|
||||
side_data_elems = 0;
|
||||
pts = AV_NOPTS_VALUE;
|
||||
dts = AV_NOPTS_VALUE;
|
||||
pos = -1;
|
||||
duration = 0;
|
||||
flags = 0;
|
||||
stream_index = 0;
|
||||
buf = nullptr;
|
||||
side_data = nullptr;
|
||||
side_data_elems = 0;
|
||||
}
|
||||
|
||||
template<class P>
|
||||
@ -70,6 +70,6 @@ void capture(
|
||||
void *channel_data);
|
||||
|
||||
int init();
|
||||
}
|
||||
} // namespace video
|
||||
|
||||
#endif //SUNSHINE_VIDEO_H
|
||||
|
||||
@ -9,7 +9,7 @@ set_target_properties(dxgi-info PROPERTIES CXX_STANDARD 17)
|
||||
target_link_libraries(dxgi-info
|
||||
${CMAKE_THREAD_LIBS_INIT}
|
||||
dxgi
|
||||
${PLATFORM_LIBRARIES})
|
||||
${PLATFORM_LIBRARIES})
|
||||
target_compile_options(dxgi-info PRIVATE ${SUNSHINE_COMPILE_OPTIONS})
|
||||
|
||||
add_executable(audio-info audio.cpp)
|
||||
@ -17,5 +17,5 @@ set_target_properties(audio-info PROPERTIES CXX_STANDARD 17)
|
||||
target_link_libraries(audio-info
|
||||
${CMAKE_THREAD_LIBS_INIT}
|
||||
ksuser
|
||||
${PLATFORM_LIBRARIES})
|
||||
${PLATFORM_LIBRARIES})
|
||||
target_compile_options(audio-info PRIVATE ${SUNSHINE_COMPILE_OPTIONS})
|
||||
|
||||
113
tools/audio.cpp
113
tools/audio.cpp
@ -2,9 +2,9 @@
|
||||
// Created by loki on 1/24/20.
|
||||
//
|
||||
|
||||
#include <roapi.h>
|
||||
#include <mmdeviceapi.h>
|
||||
#include <audioclient.h>
|
||||
#include <mmdeviceapi.h>
|
||||
#include <roapi.h>
|
||||
|
||||
#include <synchapi.h>
|
||||
|
||||
@ -16,8 +16,8 @@
|
||||
|
||||
#include "sunshine/utility.h"
|
||||
|
||||
DEFINE_PROPERTYKEY(PKEY_Device_DeviceDesc, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 2); // DEVPROP_TYPE_STRING
|
||||
DEFINE_PROPERTYKEY(PKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 14); // DEVPROP_TYPE_STRING
|
||||
DEFINE_PROPERTYKEY(PKEY_Device_DeviceDesc, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 2); // DEVPROP_TYPE_STRING
|
||||
DEFINE_PROPERTYKEY(PKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 14); // DEVPROP_TYPE_STRING
|
||||
DEFINE_PROPERTYKEY(PKEY_DeviceInterface_FriendlyName, 0x026e516e, 0xb814, 0x414b, 0x83, 0xcd, 0x85, 0x6d, 0x6f, 0xef, 0x48, 0x22, 2);
|
||||
|
||||
using namespace std::literals;
|
||||
@ -27,7 +27,7 @@ const IID IID_IAudioClient = __uuidof(IAudioClient);
|
||||
const IID IID_IAudioCaptureClient = __uuidof(IAudioCaptureClient);
|
||||
|
||||
constexpr auto SAMPLE_RATE = 48000;
|
||||
int device_state_filter = DEVICE_STATE_ACTIVE;
|
||||
int device_state_filter = DEVICE_STATE_ACTIVE;
|
||||
|
||||
namespace audio {
|
||||
template<class T>
|
||||
@ -73,32 +73,26 @@ struct format_t {
|
||||
std::string_view name;
|
||||
int channels;
|
||||
int channel_mask;
|
||||
} formats [] {
|
||||
{
|
||||
"Mono"sv,
|
||||
} formats[] {
|
||||
{ "Mono"sv,
|
||||
1,
|
||||
SPEAKER_FRONT_CENTER
|
||||
},
|
||||
{
|
||||
"Stereo"sv,
|
||||
SPEAKER_FRONT_CENTER },
|
||||
{ "Stereo"sv,
|
||||
2,
|
||||
SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT
|
||||
},
|
||||
{
|
||||
"Surround 5.1"sv,
|
||||
SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT },
|
||||
{ "Surround 5.1"sv,
|
||||
6,
|
||||
SPEAKER_FRONT_LEFT |
|
||||
SPEAKER_FRONT_RIGHT |
|
||||
SPEAKER_FRONT_CENTER |
|
||||
SPEAKER_LOW_FREQUENCY |
|
||||
SPEAKER_BACK_LEFT |
|
||||
SPEAKER_BACK_RIGHT
|
||||
}
|
||||
SPEAKER_FRONT_LEFT |
|
||||
SPEAKER_FRONT_RIGHT |
|
||||
SPEAKER_FRONT_CENTER |
|
||||
SPEAKER_LOW_FREQUENCY |
|
||||
SPEAKER_BACK_LEFT |
|
||||
SPEAKER_BACK_RIGHT }
|
||||
};
|
||||
|
||||
void set_wave_format(audio::wave_format_t &wave_format, const format_t &format) {
|
||||
wave_format->nChannels = format.channels;
|
||||
wave_format->nBlockAlign = wave_format->nChannels * wave_format->wBitsPerSample / 8;
|
||||
wave_format->nChannels = format.channels;
|
||||
wave_format->nBlockAlign = wave_format->nChannels * wave_format->wBitsPerSample / 8;
|
||||
wave_format->nAvgBytesPerSec = wave_format->nSamplesPerSec * wave_format->nBlockAlign;
|
||||
|
||||
if(wave_format->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
|
||||
@ -112,7 +106,7 @@ audio_client_t make_audio_client(device_t &device, const format_t &format) {
|
||||
IID_IAudioClient,
|
||||
CLSCTX_ALL,
|
||||
nullptr,
|
||||
(void **) &audio_client);
|
||||
(void **)&audio_client);
|
||||
|
||||
if(FAILED(status)) {
|
||||
std::cout << "Couldn't activate Device: [0x"sv << util::hex(status).to_string_view() << ']' << std::endl;
|
||||
@ -123,7 +117,7 @@ audio_client_t make_audio_client(device_t &device, const format_t &format) {
|
||||
wave_format_t wave_format;
|
||||
status = audio_client->GetMixFormat(&wave_format);
|
||||
|
||||
if (FAILED(status)) {
|
||||
if(FAILED(status)) {
|
||||
std::cout << "Couldn't acquire Wave Format [0x"sv << util::hex(status).to_string_view() << ']' << std::endl;
|
||||
|
||||
return nullptr;
|
||||
@ -132,23 +126,23 @@ audio_client_t make_audio_client(device_t &device, const format_t &format) {
|
||||
wave_format->wBitsPerSample = 16;
|
||||
wave_format->nSamplesPerSec = SAMPLE_RATE;
|
||||
switch(wave_format->wFormatTag) {
|
||||
case WAVE_FORMAT_PCM:
|
||||
case WAVE_FORMAT_PCM:
|
||||
break;
|
||||
case WAVE_FORMAT_IEEE_FLOAT:
|
||||
break;
|
||||
case WAVE_FORMAT_EXTENSIBLE: {
|
||||
auto wave_ex = (PWAVEFORMATEXTENSIBLE)wave_format.get();
|
||||
if(IsEqualGUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, wave_ex->SubFormat)) {
|
||||
wave_ex->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
|
||||
wave_ex->Samples.wValidBitsPerSample = 16;
|
||||
break;
|
||||
case WAVE_FORMAT_IEEE_FLOAT:
|
||||
break;
|
||||
case WAVE_FORMAT_EXTENSIBLE: {
|
||||
auto wave_ex = (PWAVEFORMATEXTENSIBLE) wave_format.get();
|
||||
if (IsEqualGUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, wave_ex->SubFormat)) {
|
||||
wave_ex->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
|
||||
wave_ex->Samples.wValidBitsPerSample = 16;
|
||||
break;
|
||||
}
|
||||
|
||||
std::cout << "Unsupported Sub Format for WAVE_FORMAT_EXTENSIBLE: [0x"sv << util::hex(wave_ex->SubFormat).to_string_view() << ']' << std::endl;
|
||||
}
|
||||
default:
|
||||
std::cout << "Unsupported Wave Format: [0x"sv << util::hex(wave_format->wFormatTag).to_string_view() << ']' << std::endl;
|
||||
return nullptr;
|
||||
|
||||
std::cout << "Unsupported Sub Format for WAVE_FORMAT_EXTENSIBLE: [0x"sv << util::hex(wave_ex->SubFormat).to_string_view() << ']' << std::endl;
|
||||
}
|
||||
default:
|
||||
std::cout << "Unsupported Wave Format: [0x"sv << util::hex(wave_format->wFormatTag).to_string_view() << ']' << std::endl;
|
||||
return nullptr;
|
||||
};
|
||||
|
||||
set_wave_format(wave_format, format);
|
||||
@ -191,18 +185,18 @@ void print_device(device_t &device) {
|
||||
|
||||
std::wstring device_state_string = L"Unknown"s;
|
||||
switch(device_state) {
|
||||
case DEVICE_STATE_ACTIVE:
|
||||
device_state_string = L"Active"s;
|
||||
break;
|
||||
case DEVICE_STATE_DISABLED:
|
||||
device_state_string = L"Disabled"s;
|
||||
break;
|
||||
case DEVICE_STATE_UNPLUGGED:
|
||||
device_state_string = L"Unplugged"s;
|
||||
break;
|
||||
case DEVICE_STATE_NOTPRESENT:
|
||||
device_state_string = L"Not present"s;
|
||||
break;
|
||||
case DEVICE_STATE_ACTIVE:
|
||||
device_state_string = L"Active"s;
|
||||
break;
|
||||
case DEVICE_STATE_DISABLED:
|
||||
device_state_string = L"Disabled"s;
|
||||
break;
|
||||
case DEVICE_STATE_UNPLUGGED:
|
||||
device_state_string = L"Unplugged"s;
|
||||
break;
|
||||
case DEVICE_STATE_NOTPRESENT:
|
||||
device_state_string = L"Not present"s;
|
||||
break;
|
||||
}
|
||||
|
||||
std::wcout
|
||||
@ -211,7 +205,8 @@ void print_device(device_t &device) {
|
||||
<< L"Device name : "sv << no_null((LPWSTR)device_friendly_name.prop.pszVal) << std::endl
|
||||
<< L"Adapter name : "sv << no_null((LPWSTR)adapter_friendly_name.prop.pszVal) << std::endl
|
||||
<< L"Device description : "sv << no_null((LPWSTR)device_desc.prop.pszVal) << std::endl
|
||||
<< L"Device state : "sv << device_state_string << std::endl << std::endl;
|
||||
<< L"Device state : "sv << device_state_string << std::endl
|
||||
<< std::endl;
|
||||
|
||||
if(device_state != DEVICE_STATE_ACTIVE) {
|
||||
return;
|
||||
@ -224,7 +219,7 @@ void print_device(device_t &device) {
|
||||
std::cout << format.name << ": "sv << (!audio_client ? "unsupported"sv : "supported"sv) << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace audio
|
||||
|
||||
void print_help() {
|
||||
std::cout
|
||||
@ -281,9 +276,9 @@ int main(int argc, char *argv[]) {
|
||||
nullptr,
|
||||
CLSCTX_ALL,
|
||||
IID_IMMDeviceEnumerator,
|
||||
(void **) &device_enum);
|
||||
(void **)&device_enum);
|
||||
|
||||
if (FAILED(status)) {
|
||||
if(FAILED(status)) {
|
||||
std::cout << "Couldn't create Device Enumerator: [0x"sv << util::hex(status).to_string_view() << ']' << std::endl;
|
||||
|
||||
return -1;
|
||||
@ -292,7 +287,7 @@ int main(int argc, char *argv[]) {
|
||||
audio::collection_t collection;
|
||||
status = device_enum->EnumAudioEndpoints(eRender, DEVICE_STATEMASK_ALL, &collection);
|
||||
|
||||
if (FAILED(status)) {
|
||||
if(FAILED(status)) {
|
||||
std::cout << "Couldn't enumerate: [0x"sv << util::hex(status).to_string_view() << ']' << std::endl;
|
||||
|
||||
return -1;
|
||||
|
||||
@ -2,8 +2,8 @@
|
||||
// Created by loki on 1/23/20.
|
||||
//
|
||||
|
||||
#include <dxgi.h>
|
||||
#include <d3dcommon.h>
|
||||
#include <dxgi.h>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
@ -16,17 +16,17 @@ void Release(T *dxgi) {
|
||||
dxgi->Release();
|
||||
}
|
||||
|
||||
using factory1_t = util::safe_ptr<IDXGIFactory1, Release<IDXGIFactory1>>;
|
||||
using adapter_t = util::safe_ptr<IDXGIAdapter1, Release<IDXGIAdapter1>>;
|
||||
using output_t = util::safe_ptr<IDXGIOutput, Release<IDXGIOutput>>;
|
||||
using factory1_t = util::safe_ptr<IDXGIFactory1, Release<IDXGIFactory1>>;
|
||||
using adapter_t = util::safe_ptr<IDXGIAdapter1, Release<IDXGIAdapter1>>;
|
||||
using output_t = util::safe_ptr<IDXGIOutput, Release<IDXGIOutput>>;
|
||||
|
||||
}
|
||||
} // namespace dxgi
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
HRESULT status;
|
||||
|
||||
dxgi::factory1_t::pointer factory_p {};
|
||||
status = CreateDXGIFactory1(IID_IDXGIFactory1, (void**)&factory_p);
|
||||
status = CreateDXGIFactory1(IID_IDXGIFactory1, (void **)&factory_p);
|
||||
dxgi::factory1_t factory { factory_p };
|
||||
if(FAILED(status)) {
|
||||
std::cout << "Failed to create DXGIFactory1 [0x"sv << util::hex(status).to_string_view() << ']' << std::endl;
|
||||
@ -49,12 +49,13 @@ int main(int argc, char *argv[]) {
|
||||
<< "Device Device ID : 0x"sv << util::hex(adapter_desc.DeviceId).to_string_view() << std::endl
|
||||
<< "Device Video Mem : "sv << adapter_desc.DedicatedVideoMemory / 1048576 << " MiB"sv << std::endl
|
||||
<< "Device Sys Mem : "sv << adapter_desc.DedicatedSystemMemory / 1048576 << " MiB"sv << std::endl
|
||||
<< "Share Sys Mem : "sv << adapter_desc.SharedSystemMemory / 1048576 << " MiB"sv << std::endl << std::endl
|
||||
<< "Share Sys Mem : "sv << adapter_desc.SharedSystemMemory / 1048576 << " MiB"sv << std::endl
|
||||
<< std::endl
|
||||
<< " ====== OUTPUT ======"sv << std::endl;
|
||||
|
||||
dxgi::output_t::pointer output_p {};
|
||||
for(int y = 0; adapter->EnumOutputs(y, &output_p) != DXGI_ERROR_NOT_FOUND; ++y) {
|
||||
dxgi::output_t output {output_p };
|
||||
dxgi::output_t output { output_p };
|
||||
|
||||
DXGI_OUTPUT_DESC desc;
|
||||
output->GetDesc(&desc);
|
||||
@ -66,7 +67,8 @@ int main(int argc, char *argv[]) {
|
||||
<< L" Output Name : "sv << desc.DeviceName << std::endl;
|
||||
std::cout
|
||||
<< " AttachedToDesktop : "sv << (desc.AttachedToDesktop ? "yes"sv : "no"sv) << std::endl
|
||||
<< " Resolution : "sv << width << 'x' << height << std::endl << std::endl;
|
||||
<< " Resolution : "sv << width << 'x' << height << std::endl
|
||||
<< std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user