diff --git a/.clang-format b/.clang-format index e72a1e27..d6da5d94 100644 --- a/.clang-format +++ b/.clang-format @@ -6,27 +6,34 @@ # Generated from CLion C/C++ Code Style settings BasedOnStyle: LLVM AccessModifierOffset: -2 -AlignAfterOpenBracket: DontAlign -AlignConsecutiveAssignments: false +AlignAfterOpenBracket: BlockIndent +AlignConsecutiveAssignments: None +AlignEscapedNewlines: DontAlign AlignOperands: Align AllowAllArgumentsOnNextLine: false AllowAllConstructorInitializersOnNextLine: false AllowAllParametersOfDeclarationOnNextLine: false -AllowShortBlocksOnASingleLine: Always +AllowShortBlocksOnASingleLine: Empty AllowShortCaseLabelsOnASingleLine: false -AllowShortFunctionsOnASingleLine: All -AllowShortIfStatementsOnASingleLine: WithoutElse -AllowShortLambdasOnASingleLine: All +AllowShortEnumsOnASingleLine: false +AllowShortFunctionsOnASingleLine: Empty +AllowShortIfStatementsOnASingleLine: Never +AllowShortLambdasOnASingleLine: None AllowShortLoopsOnASingleLine: true AlignTrailingComments: false -AlwaysBreakAfterReturnType: All +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: true AlwaysBreakTemplateDeclarations: MultiLine -BreakBeforeBraces: Custom +BinPackArguments: false +BinPackParameters: false +BracedInitializerIndentWidth: 2 BraceWrapping: AfterCaseLabel: false AfterClass: false AfterControlStatement: Never AfterEnum: false + AfterExternBlock: true AfterFunction: false AfterNamespace: false AfterObjCDeclaration: false @@ -36,39 +43,75 @@ BraceWrapping: IndentBraces: false SplitEmptyFunction: false SplitEmptyRecord: true +BreakArrays: true BreakBeforeBinaryOperators: None +BreakBeforeBraces: Attach BreakBeforeTernaryOperators: false BreakConstructorInitializers: AfterColon BreakInheritanceList: AfterColon ColumnLimit: 0 CompactNamespaces: false ContinuationIndentWidth: 2 +Cpp11BracedListStyle: true +EmptyLineAfterAccessModifier: Never +EmptyLineBeforeAccessModifier: Always +ExperimentalAutoDetectBinPacking: true +FixNamespaceComments: true +IncludeBlocks: Regroup +IndentAccessModifiers: false +IndentCaseBlocks: true IndentCaseLabels: true +IndentExternBlock: Indent +IndentGotoLabels: true IndentPPDirectives: BeforeHash IndentWidth: 2 +IndentWrappedFunctionNames: true +InsertBraces: true +InsertNewlineAtEOF: true KeepEmptyLinesAtTheStartOfBlocks: false +LineEnding: LF MaxEmptyLinesToKeep: 1 NamespaceIndentation: All +ObjCBinPackProtocolList: Never ObjCSpaceAfterProperty: true ObjCSpaceBeforeProtocolList: true +PackConstructorInitializers: Never +PenaltyBreakBeforeFirstCallParameter: 1 +PenaltyBreakComment: 1 +PenaltyBreakString: 1 +PenaltyBreakFirstLessLess: 0 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 100000000 PointerAlignment: Right +ReferenceAlignment: Pointer ReflowComments: true +RemoveBracesLLVM: false +RemoveSemicolon: false +SeparateDefinitionBlocks: Always +SortIncludes: CaseInsensitive +SortUsingDeclarations: Lexicographic SpaceAfterCStyleCast: true SpaceAfterLogicalNot: false -SpaceAfterTemplateKeyword: true +SpaceAfterTemplateKeyword: false SpaceBeforeAssignmentOperators: true +SpaceBeforeCaseColon: false SpaceBeforeCpp11BracedList: true SpaceBeforeCtorInitializerColon: false SpaceBeforeInheritanceColon: false +SpaceBeforeJsonColon: false SpaceBeforeParens: ControlStatements SpaceBeforeRangeBasedForLoopColon: true +SpaceBeforeSquareBrackets: false +SpaceInEmptyBlock: false SpaceInEmptyParentheses: false SpacesBeforeTrailingComments: 2 SpacesInAngles: Never SpacesInCStyleCastParentheses: false SpacesInContainerLiterals: false +SpacesInLineCommentPrefix: + Maximum: 3 + Minimum: 1 SpacesInParentheses: false SpacesInSquareBrackets: false TabWidth: 2 -Cpp11BracedListStyle: false UseTab: Never diff --git a/scripts/update_clang_format.py b/scripts/update_clang_format.py index 9e0dacda..7ae027fa 100644 --- a/scripts/update_clang_format.py +++ b/scripts/update_clang_format.py @@ -7,12 +7,12 @@ directories = [ 'src', 'tests', 'tools', - os.path.join('third-party', 'glad'), - os.path.join('third-party', 'nvfbc'), ] file_types = [ 'cpp', + 'cu', 'h', + 'hpp', 'm', 'mm' ] diff --git a/src/audio.cpp b/src/audio.cpp index 82b1ec37..f060118d 100644 --- a/src/audio.cpp +++ b/src/audio.cpp @@ -2,16 +2,18 @@ * @file src/audio.cpp * @brief Definitions for audio capture and encoding. */ +// standard includes #include +// lib includes #include -#include "platform/common.h" - +// local includes #include "audio.h" #include "config.h" #include "globals.h" #include "logging.h" +#include "platform/common.h" #include "thread_safe.h" #include "utility.h" @@ -20,15 +22,11 @@ namespace audio { using opus_t = util::safe_ptr; using sample_queue_t = std::shared_ptr>>; - static int - start_audio_control(audio_ctx_t &ctx); - static void - stop_audio_control(audio_ctx_t &); - static void - apply_surround_params(opus_stream_config_t &stream, const stream_params_t ¶ms); + static int start_audio_control(audio_ctx_t &ctx); + static void stop_audio_control(audio_ctx_t &); + static void apply_surround_params(opus_stream_config_t &stream, const stream_params_t ¶ms); - int - map_stream(int channels, bool quality); + int map_stream(int channels, bool quality); constexpr auto SAMPLE_RATE = 48000; @@ -85,8 +83,7 @@ namespace audio { }, }; - void - encodeThread(sample_queue_t samples, config_t config, void *channel_data) { + void encodeThread(sample_queue_t samples, config_t config, void *channel_data) { auto packets = mail::man->queue(mail::audio_packets); auto stream = stream_configs[map_stream(config.channels, config.flags[config_t::HIGH_QUALITY])]; if (config.flags[config_t::CUSTOM_SURROUND_PARAMS]) { @@ -96,14 +93,15 @@ namespace audio { // Encoding takes place on this thread platf::adjust_thread_priority(platf::thread_priority_e::high); - opus_t opus { opus_multistream_encoder_create( + opus_t opus {opus_multistream_encoder_create( stream.sampleRate, stream.channelCount, stream.streams, stream.coupledStreams, stream.mapping, OPUS_APPLICATION_RESTRICTED_LOWDELAY, - nullptr) }; + nullptr + )}; opus_multistream_encoder_ctl(opus.get(), OPUS_SET_BITRATE(stream.bitrate)); opus_multistream_encoder_ctl(opus.get(), OPUS_SET_VBR(0)); @@ -114,7 +112,7 @@ namespace audio { auto frame_size = config.packetDuration * stream.sampleRate / 1000; while (auto sample = samples->pop()) { - buffer_t packet { 1400 }; + buffer_t packet {1400}; int bytes = opus_multistream_encode_float(opus.get(), sample->data(), frame_size, std::begin(packet), packet.size()); if (bytes < 0) { @@ -129,8 +127,7 @@ namespace audio { } } - void - capture(safe::mail_t mail, config_t config, void *channel_data) { + void capture(safe::mail_t mail, config_t config, void *channel_data) { auto shutdown_event = mail->event(mail::shutdown); auto stream = stream_configs[map_stream(config.channels, config.flags[config_t::HIGH_QUALITY])]; if (config.flags[config_t::CUSTOM_SURROUND_PARAMS]) { @@ -204,7 +201,7 @@ namespace audio { platf::adjust_thread_priority(platf::thread_priority_e::critical); auto samples = std::make_shared(30); - std::thread thread { encodeThread, samples, config, channel_data }; + std::thread thread {encodeThread, samples, config, channel_data}; auto fg = util::fail_guard([&]() { samples->stop(); @@ -243,14 +240,12 @@ namespace audio { } } - audio_ctx_ref_t - get_audio_ctx_ref() { - static auto control_shared { safe::make_shared(start_audio_control, stop_audio_control) }; + audio_ctx_ref_t get_audio_ctx_ref() { + static auto control_shared {safe::make_shared(start_audio_control, stop_audio_control)}; return control_shared.ref(); } - bool - is_audio_ctx_sink_available(const audio_ctx_t &ctx) { + bool is_audio_ctx_sink_available(const audio_ctx_t &ctx) { if (!ctx.control) { return false; } @@ -263,8 +258,7 @@ namespace audio { return ctx.control->is_sink_available(sink); } - int - map_stream(int channels, bool quality) { + int map_stream(int channels, bool quality) { int shift = quality ? 1 : 0; switch (channels) { case 2: @@ -277,8 +271,7 @@ namespace audio { return STEREO; } - int - start_audio_control(audio_ctx_t &ctx) { + int start_audio_control(audio_ctx_t &ctx) { auto fg = util::fail_guard([]() { BOOST_LOG(warning) << "There will be no audio"sv; }); @@ -305,8 +298,7 @@ namespace audio { return 0; } - void - stop_audio_control(audio_ctx_t &ctx) { + void stop_audio_control(audio_ctx_t &ctx) { // restore audio-sink if applicable if (!ctx.restore_sink) { return; @@ -320,8 +312,7 @@ namespace audio { } } - void - apply_surround_params(opus_stream_config_t &stream, const stream_params_t ¶ms) { + void apply_surround_params(opus_stream_config_t &stream, const stream_params_t ¶ms) { stream.channelCount = params.channelCount; stream.streams = params.streams; stream.coupledStreams = params.coupledStreams; diff --git a/src/audio.h b/src/audio.h index 927dfdef..2afb42e5 100644 --- a/src/audio.h +++ b/src/audio.h @@ -71,8 +71,7 @@ namespace audio { using packet_t = std::pair; using audio_ctx_ref_t = safe::shared_t::ptr_t; - void - capture(safe::mail_t mail, config_t config, void *channel_data); + void capture(safe::mail_t mail, config_t config, void *channel_data); /** * @brief Get the reference to the audio context. @@ -84,8 +83,7 @@ namespace audio { * audio_ctx_ref_t audio = get_audio_ctx_ref() * @examples_end */ - audio_ctx_ref_t - get_audio_ctx_ref(); + audio_ctx_ref_t get_audio_ctx_ref(); /** * @brief Check if the audio sink held by audio context is available. @@ -101,6 +99,5 @@ namespace audio { * return false; * @examples_end */ - bool - is_audio_ctx_sink_available(const audio_ctx_t &ctx); + bool is_audio_ctx_sink_available(const audio_ctx_t &ctx); } // namespace audio diff --git a/src/cbs.cpp b/src/cbs.cpp index 0b795a55..67cda0c1 100644 --- a/src/cbs.cpp +++ b/src/cbs.cpp @@ -3,6 +3,7 @@ * @brief Definitions for FFmpeg Coded Bitstream API. */ extern "C" { +// lib includes #include #include #include @@ -10,14 +11,15 @@ extern "C" { #include } +// local includes #include "cbs.h" #include "logging.h" #include "utility.h" using namespace std::literals; + namespace cbs { - void - close(CodedBitstreamContext *c) { + void close(CodedBitstreamContext *c) { ff_cbs_close(&c); } @@ -36,8 +38,7 @@ namespace cbs { std::fill_n((std::uint8_t *) this, sizeof(*this), 0); } - frag_t & - operator=(frag_t &&o) { + frag_t &operator=(frag_t &&o) { std::copy((std::uint8_t *) &o, (std::uint8_t *) (&o + 1), (std::uint8_t *) this); o.data = nullptr; @@ -53,12 +54,11 @@ namespace cbs { } }; - util::buffer_t - write(cbs::ctx_t &cbs_ctx, std::uint8_t nal, void *uh, AVCodecID codec_id) { + util::buffer_t write(cbs::ctx_t &cbs_ctx, std::uint8_t nal, void *uh, AVCodecID codec_id) { cbs::frag_t frag; auto err = ff_cbs_insert_unit_content(&frag, -1, nal, uh, nullptr); if (err < 0) { - char err_str[AV_ERROR_MAX_STRING_SIZE] { 0 }; + char err_str[AV_ERROR_MAX_STRING_SIZE] {0}; BOOST_LOG(error) << "Could not insert NAL unit SPS: "sv << av_make_error_string(err_str, AV_ERROR_MAX_STRING_SIZE, err); return {}; @@ -66,29 +66,27 @@ namespace cbs { err = ff_cbs_write_fragment_data(cbs_ctx.get(), &frag); if (err < 0) { - char err_str[AV_ERROR_MAX_STRING_SIZE] { 0 }; + char err_str[AV_ERROR_MAX_STRING_SIZE] {0}; BOOST_LOG(error) << "Could not write fragment data: "sv << av_make_error_string(err_str, AV_ERROR_MAX_STRING_SIZE, err); return {}; } // frag.data_size * 8 - frag.data_bit_padding == bits in fragment - util::buffer_t data { frag.data_size }; + util::buffer_t data {frag.data_size}; std::copy_n(frag.data, frag.data_size, std::begin(data)); return data; } - util::buffer_t - write(std::uint8_t nal, void *uh, AVCodecID codec_id) { + util::buffer_t write(std::uint8_t nal, void *uh, AVCodecID codec_id) { cbs::ctx_t cbs_ctx; ff_cbs_init(&cbs_ctx, codec_id, nullptr); return write(cbs_ctx, nal, uh, codec_id); } - h264_t - make_sps_h264(const AVCodecContext *avctx, const AVPacket *packet) { + h264_t make_sps_h264(const AVCodecContext *avctx, const AVPacket *packet) { cbs::ctx_t ctx; if (ff_cbs_init(&ctx, AV_CODEC_ID_H264, nullptr)) { return {}; @@ -98,7 +96,7 @@ namespace cbs { int err = ff_cbs_read_packet(ctx.get(), &frag, packet); if (err < 0) { - char err_str[AV_ERROR_MAX_STRING_SIZE] { 0 }; + char err_str[AV_ERROR_MAX_STRING_SIZE] {0}; BOOST_LOG(error) << "Couldn't read packet: "sv << av_make_error_string(err_str, AV_ERROR_MAX_STRING_SIZE, err); return {}; @@ -144,8 +142,7 @@ namespace cbs { }; } - hevc_t - make_sps_hevc(const AVCodecContext *avctx, const AVPacket *packet) { + hevc_t make_sps_hevc(const AVCodecContext *avctx, const AVPacket *packet) { cbs::ctx_t ctx; if (ff_cbs_init(&ctx, AV_CODEC_ID_H265, nullptr)) { return {}; @@ -155,7 +152,7 @@ namespace cbs { int err = ff_cbs_read_packet(ctx.get(), &frag, packet); if (err < 0) { - char err_str[AV_ERROR_MAX_STRING_SIZE] { 0 }; + char err_str[AV_ERROR_MAX_STRING_SIZE] {0}; BOOST_LOG(error) << "Couldn't read packet: "sv << av_make_error_string(err_str, AV_ERROR_MAX_STRING_SIZE, err); return {}; @@ -222,8 +219,7 @@ namespace cbs { * It then checks if the SPS->VUI (Video Usability Information) is present in the active SPS of the packet. * This is done for both H264 and H265 codecs. */ - bool - validate_sps(const AVPacket *packet, int codec_id) { + bool validate_sps(const AVPacket *packet, int codec_id) { cbs::ctx_t ctx; if (ff_cbs_init(&ctx, (AVCodecID) codec_id, nullptr)) { return false; @@ -233,7 +229,7 @@ namespace cbs { int err = ff_cbs_read_packet(ctx.get(), &frag, packet); if (err < 0) { - char err_str[AV_ERROR_MAX_STRING_SIZE] { 0 }; + char err_str[AV_ERROR_MAX_STRING_SIZE] {0}; BOOST_LOG(error) << "Couldn't read packet: "sv << av_make_error_string(err_str, AV_ERROR_MAX_STRING_SIZE, err); return false; diff --git a/src/cbs.h b/src/cbs.h index a22a756b..5dfddab5 100644 --- a/src/cbs.h +++ b/src/cbs.h @@ -4,6 +4,7 @@ */ #pragma once +// local includes #include "utility.h" struct AVPacket; @@ -25,10 +26,8 @@ namespace cbs { nal_t sps; }; - hevc_t - make_sps_hevc(const AVCodecContext *ctx, const AVPacket *packet); - h264_t - make_sps_h264(const AVCodecContext *ctx, const AVPacket *packet); + hevc_t make_sps_hevc(const AVCodecContext *ctx, const AVPacket *packet); + h264_t make_sps_h264(const AVCodecContext *ctx, const AVPacket *packet); /** * @brief Validates the Sequence Parameter Set (SPS) of a given packet. @@ -36,6 +35,5 @@ namespace cbs { * @param codec_id The ID of the codec used (either AV_CODEC_ID_H264 or AV_CODEC_ID_H265). * @return True if the SPS->VUI is present in the active SPS of the packet, false otherwise. */ - bool - validate_sps(const AVPacket *packet, int codec_id); + bool validate_sps(const AVPacket *packet, int codec_id); } // namespace cbs diff --git a/src/config.cpp b/src/config.cpp index 40bbde61..bdf719e0 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -2,6 +2,7 @@ * @file src/config.cpp * @brief Definitions for the configuration of Sunshine. */ +// standard includes #include #include #include @@ -11,21 +12,22 @@ #include #include +// lib includes #include #include #include #include +// local includes #include "config.h" #include "entry_handler.h" #include "file_handler.h" #include "logging.h" #include "nvhttp.h" +#include "platform/common.h" #include "rtsp.h" #include "utility.h" -#include "platform/common.h" - #ifdef _WIN32 #include #endif @@ -43,15 +45,21 @@ using namespace std::literals; #define CERTIFICATE_FILE CA_DIR "/cacert.pem" #define APPS_JSON_PATH platf::appdata().string() + "/apps.json" + namespace config { namespace nv { - nvenc::nvenc_two_pass - twopass_from_view(const std::string_view &preset) { - if (preset == "disabled") return nvenc::nvenc_two_pass::disabled; - if (preset == "quarter_res") return nvenc::nvenc_two_pass::quarter_resolution; - if (preset == "full_res") return nvenc::nvenc_two_pass::full_resolution; + nvenc::nvenc_two_pass twopass_from_view(const std::string_view &preset) { + if (preset == "disabled") { + return nvenc::nvenc_two_pass::disabled; + } + if (preset == "quarter_res") { + return nvenc::nvenc_two_pass::quarter_resolution; + } + if (preset == "full_res") { + return nvenc::nvenc_two_pass::full_resolution; + } BOOST_LOG(warning) << "config: unknown nvenc_twopass value: " << preset; return nvenc::nvenc_two_pass::quarter_resolution; } @@ -178,11 +186,11 @@ namespace config { cavlc = AMF_VIDEO_ENCODER_CALV ///< CAVLC }; - template - std::optional - quality_from_view(const std::string_view &quality_type, const std::optional(&original)) { + template + std::optional quality_from_view(const std::string_view &quality_type, const std::optional(&original)) { #define _CONVERT_(x) \ - if (quality_type == #x##sv) return (int) T::x + if (quality_type == #x##sv) \ + return (int) T::x _CONVERT_(balanced); _CONVERT_(quality); _CONVERT_(speed); @@ -190,11 +198,11 @@ namespace config { return original; } - template - std::optional - rc_from_view(const std::string_view &rc, const std::optional(&original)) { + template + std::optional rc_from_view(const std::string_view &rc, const std::optional(&original)) { #define _CONVERT_(x) \ - if (rc == #x##sv) return (int) T::x + if (rc == #x##sv) \ + return (int) T::x _CONVERT_(cbr); _CONVERT_(cqp); _CONVERT_(vbr_latency); @@ -203,11 +211,11 @@ namespace config { return original; } - template - std::optional - usage_from_view(const std::string_view &usage, const std::optional(&original)) { + template + std::optional usage_from_view(const std::string_view &usage, const std::optional(&original)) { #define _CONVERT_(x) \ - if (usage == #x##sv) return (int) T::x + if (usage == #x##sv) \ + return (int) T::x _CONVERT_(lowlatency); _CONVERT_(lowlatency_high_quality); _CONVERT_(transcoding); @@ -217,11 +225,16 @@ namespace config { return original; } - 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; + 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; + } return _auto; } @@ -244,10 +257,10 @@ namespace config { disabled = false ///< Disabled }; - std::optional - preset_from_view(const std::string_view &preset) { + std::optional preset_from_view(const std::string_view &preset) { #define _CONVERT_(x) \ - if (preset == #x##sv) return x + if (preset == #x##sv) \ + return x _CONVERT_(veryslow); _CONVERT_(slower); _CONVERT_(slow); @@ -259,11 +272,16 @@ namespace config { return std::nullopt; } - std::optional - coder_from_view(const std::string_view &coder) { - if (coder == "auto"sv) return _auto; - if (coder == "cabac"sv || coder == "ac"sv) return disabled; - if (coder == "cavlc"sv || coder == "vlc"sv) return enabled; + std::optional coder_from_view(const std::string_view &coder) { + if (coder == "auto"sv) { + return _auto; + } + if (coder == "cabac"sv || coder == "ac"sv) { + return disabled; + } + if (coder == "cavlc"sv || coder == "vlc"sv) { + return enabled; + } return std::nullopt; } @@ -277,32 +295,40 @@ namespace config { cavlc ///< CAVLC }; - 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; + 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; + } return -1; } - int - allow_software_from_view(const std::string_view &software) { - if (software == "allowed"sv || software == "forced") return 1; + int allow_software_from_view(const std::string_view &software) { + if (software == "allowed"sv || software == "forced") { + return 1; + } return 0; } - int - force_software_from_view(const std::string_view &software) { - if (software == "forced") return 1; + int force_software_from_view(const std::string_view &software) { + if (software == "forced") { + return 1; + } return 0; } - int - rt_from_view(const std::string_view &rt) { - if (rt == "disabled" || rt == "off" || rt == "0") return 0; + int rt_from_view(const std::string_view &rt) { + if (rt == "disabled" || rt == "off" || rt == "0") { + return 0; + } return 1; } @@ -310,10 +336,10 @@ namespace config { } // namespace vt namespace sw { - int - svtav1_preset_from_view(const std::string_view &preset) { + int svtav1_preset_from_view(const std::string_view &preset) { #define _CONVERT_(x, y) \ - if (preset == #x##sv) return y + if (preset == #x##sv) \ + return y _CONVERT_(veryslow, 1); _CONVERT_(slower, 2); _CONVERT_(slow, 4); @@ -329,10 +355,10 @@ namespace config { } // namespace sw namespace dd { - video_t::dd_t::config_option_e - config_option_from_view(const std::string_view value) { + video_t::dd_t::config_option_e config_option_from_view(const std::string_view value) { #define _CONVERT_(x) \ - if (value == #x##sv) return video_t::dd_t::config_option_e::x + if (value == #x##sv) \ + return video_t::dd_t::config_option_e::x _CONVERT_(disabled); _CONVERT_(verify_only); _CONVERT_(ensure_active); @@ -342,10 +368,10 @@ namespace config { return video_t::dd_t::config_option_e::disabled; // Default to this if value is invalid } - video_t::dd_t::resolution_option_e - resolution_option_from_view(const std::string_view value) { + video_t::dd_t::resolution_option_e resolution_option_from_view(const std::string_view value) { #define _CONVERT_2_ARG_(str, val) \ - if (value == #str##sv) return video_t::dd_t::resolution_option_e::val + if (value == #str##sv) \ + return video_t::dd_t::resolution_option_e::val #define _CONVERT_(x) _CONVERT_2_ARG_(x, x) _CONVERT_(disabled); _CONVERT_2_ARG_(auto, automatic); @@ -355,10 +381,10 @@ namespace config { return video_t::dd_t::resolution_option_e::disabled; // Default to this if value is invalid } - video_t::dd_t::refresh_rate_option_e - refresh_rate_option_from_view(const std::string_view value) { + video_t::dd_t::refresh_rate_option_e refresh_rate_option_from_view(const std::string_view value) { #define _CONVERT_2_ARG_(str, val) \ - if (value == #str##sv) return video_t::dd_t::refresh_rate_option_e::val + if (value == #str##sv) \ + return video_t::dd_t::refresh_rate_option_e::val #define _CONVERT_(x) _CONVERT_2_ARG_(x, x) _CONVERT_(disabled); _CONVERT_2_ARG_(auto, automatic); @@ -368,10 +394,10 @@ namespace config { return video_t::dd_t::refresh_rate_option_e::disabled; // Default to this if value is invalid } - video_t::dd_t::hdr_option_e - hdr_option_from_view(const std::string_view value) { + video_t::dd_t::hdr_option_e hdr_option_from_view(const std::string_view value) { #define _CONVERT_2_ARG_(str, val) \ - if (value == #str##sv) return video_t::dd_t::hdr_option_e::val + if (value == #str##sv) \ + return video_t::dd_t::hdr_option_e::val #define _CONVERT_(x) _CONVERT_2_ARG_(x, x) _CONVERT_(disabled); _CONVERT_2_ARG_(auto, automatic); @@ -380,9 +406,8 @@ namespace config { return video_t::dd_t::hdr_option_e::disabled; // Default to this if value is invalid } - video_t::dd_t::mode_remapping_t - mode_remapping_from_view(const std::string_view value) { - const auto parse_entry_list { [](const auto &entry_list, auto &output_field) { + video_t::dd_t::mode_remapping_t mode_remapping_from_view(const std::string_view value) { + const auto parse_entry_list {[](const auto &entry_list, auto &output_field) { for (auto &[_, entry] : entry_list) { auto requested_resolution = entry.template get_optional("requested_resolution"s); auto requested_fps = entry.template get_optional("requested_fps"s); @@ -393,9 +418,10 @@ namespace config { requested_resolution.value_or(""), requested_fps.value_or(""), final_resolution.value_or(""), - final_refresh_rate.value_or("") }); + final_refresh_rate.value_or("") + }); } - } }; + }}; // We need to add a wrapping object to make it valid JSON, otherwise ptree cannot parse it. std::stringstream json_stream; @@ -515,13 +541,13 @@ namespace config { input_t input { { - { 0x10, 0xA0 }, - { 0x11, 0xA2 }, - { 0x12, 0xA4 }, + {0x10, 0xA0}, + {0x11, 0xA2}, + {0x12, 0xA4}, }, -1ms, // back_button_timeout 500ms, // key_repeat_delay - std::chrono::duration { 1 / 24.9 }, // key_repeat_period + std::chrono::duration {1 / 24.9}, // key_repeat_period { platf::supported_gamepads(nullptr).front().name.data(), @@ -556,23 +582,19 @@ namespace config { {}, // prep commands }; - bool - endline(char ch) { + bool endline(char ch) { return ch == '\r' || ch == '\n'; } - bool - space_tab(char ch) { + bool space_tab(char ch) { return ch == ' ' || ch == '\t'; } - bool - whitespace(char ch) { + bool whitespace(char ch) { return space_tab(ch) || endline(ch); } - std::string - to_string(const char *begin, const char *end) { + std::string to_string(const char *begin, const char *end) { std::string result; KITTY_WHILE_LOOP(auto pos = begin, pos != end, { @@ -587,9 +609,8 @@ namespace config { return result; } - template - It - skip_list(It skipper, It end) { + template + It skip_list(It skipper, It end) { int stack = 1; while (skipper != end && stack) { if (*skipper == '[') { @@ -608,7 +629,7 @@ namespace config { std::pair< std::string_view::const_iterator, std::optional>> - parse_option(std::string_view::const_iterator begin, std::string_view::const_iterator end) { + parse_option(std::string_view::const_iterator begin, std::string_view::const_iterator end) { begin = std::find_if_not(begin, end, whitespace); auto endl = std::find_if(begin, end, endline); auto endc = std::find(begin, endl, '#'); @@ -638,11 +659,11 @@ namespace config { return std::make_pair( endl, - std::make_pair(to_string(begin, end_name), to_string(begin_val, endl))); + std::make_pair(to_string(begin, end_name), to_string(begin_val, endl)) + ); } - std::unordered_map - parse_config(const std::string_view &file_content) { + std::unordered_map parse_config(const std::string_view &file_content) { std::unordered_map vars; auto pos = std::begin(file_content); @@ -667,8 +688,7 @@ namespace config { return vars; } - void - string_f(std::unordered_map &vars, const std::string &name, std::string &input) { + void string_f(std::unordered_map &vars, const std::string &name, std::string &input) { auto it = vars.find(name); if (it == std::end(vars)) { return; @@ -679,9 +699,8 @@ namespace config { vars.erase(it); } - template - void - generic_f(std::unordered_map &vars, const std::string &name, T &input, F &&f) { + template + void generic_f(std::unordered_map &vars, const std::string &name, T &input, F &&f) { std::string tmp; string_f(vars, name, tmp); if (!tmp.empty()) { @@ -689,8 +708,7 @@ namespace config { } } - void - string_restricted_f(std::unordered_map &vars, const std::string &name, std::string &input, const std::vector &allowed_vals) { + void string_restricted_f(std::unordered_map &vars, const std::string &name, std::string &input, const std::vector &allowed_vals) { std::string temp; string_f(vars, name, temp); @@ -702,8 +720,7 @@ namespace config { } } - void - path_f(std::unordered_map &vars, const std::string &name, fs::path &input) { + void path_f(std::unordered_map &vars, const std::string &name, fs::path &input) { // appdata needs to be retrieved once only static auto appdata = platf::appdata(); @@ -727,8 +744,7 @@ namespace config { } } - void - path_f(std::unordered_map &vars, const std::string &name, std::string &input) { + void path_f(std::unordered_map &vars, const std::string &name, std::string &input) { fs::path temp = input; path_f(vars, name, temp); @@ -736,8 +752,7 @@ namespace config { input = temp.string(); } - void - int_f(std::unordered_map &vars, const std::string &name, int &input) { + void int_f(std::unordered_map &vars, const std::string &name, int &input) { auto it = vars.find(name); if (it == std::end(vars)) { @@ -754,16 +769,14 @@ namespace config { // If that integer is in hexadecimal if (val.size() >= 2 && val.substr(0, 2) == "0x"sv) { input = util::from_hex(val.substr(2)); - } - else { + } else { input = util::from_view(val); } vars.erase(it); } - void - int_f(std::unordered_map &vars, const std::string &name, std::optional &input) { + void int_f(std::unordered_map &vars, const std::string &name, std::optional &input) { auto it = vars.find(name); if (it == std::end(vars)) { @@ -780,17 +793,15 @@ namespace config { // If that integer is in hexadecimal if (val.size() >= 2 && val.substr(0, 2) == "0x"sv) { input = util::from_hex(val.substr(2)); - } - else { + } else { input = util::from_view(val); } vars.erase(it); } - template - void - int_f(std::unordered_map &vars, const std::string &name, int &input, F &&f) { + template + void int_f(std::unordered_map &vars, const std::string &name, int &input, F &&f) { std::string tmp; string_f(vars, name, tmp); if (!tmp.empty()) { @@ -798,9 +809,8 @@ namespace config { } } - template - void - int_f(std::unordered_map &vars, const std::string &name, std::optional &input, F &&f) { + template + void int_f(std::unordered_map &vars, const std::string &name, std::optional &input, F &&f) { std::string tmp; string_f(vars, name, tmp); if (!tmp.empty()) { @@ -808,8 +818,7 @@ namespace config { } } - void - int_between_f(std::unordered_map &vars, const std::string &name, int &input, const std::pair &range) { + void int_between_f(std::unordered_map &vars, const std::string &name, int &input, const std::pair &range) { int temp = input; int_f(vars, name, temp); @@ -820,9 +829,10 @@ namespace config { } } - bool - to_bool(std::string &boolean) { - std::for_each(std::begin(boolean), std::end(boolean), [](char ch) { return (char) std::tolower(ch); }); + bool to_bool(std::string &boolean) { + std::for_each(std::begin(boolean), std::end(boolean), [](char ch) { + return (char) std::tolower(ch); + }); return boolean == "true"sv || boolean == "yes"sv || @@ -832,8 +842,7 @@ namespace config { (std::find(std::begin(boolean), std::end(boolean), '1') != std::end(boolean)); } - void - bool_f(std::unordered_map &vars, const std::string &name, bool &input) { + void bool_f(std::unordered_map &vars, const std::string &name, bool &input) { std::string tmp; string_f(vars, name, tmp); @@ -844,8 +853,7 @@ namespace config { input = to_bool(tmp); } - void - double_f(std::unordered_map &vars, const std::string &name, double &input) { + void double_f(std::unordered_map &vars, const std::string &name, double &input) { std::string tmp; string_f(vars, name, tmp); @@ -863,8 +871,7 @@ namespace config { input = val; } - void - double_between_f(std::unordered_map &vars, const std::string &name, double &input, const std::pair &range) { + void double_between_f(std::unordered_map &vars, const std::string &name, double &input, const std::pair &range) { double temp = input; double_f(vars, name, temp); @@ -875,8 +882,7 @@ namespace config { } } - void - list_string_f(std::unordered_map &vars, const std::string &name, std::vector &input) { + void list_string_f(std::unordered_map &vars, const std::string &name, std::vector &input) { std::string string; string_f(vars, name, string); @@ -900,15 +906,12 @@ namespace config { while (pos < std::cend(string)) { if (*pos == '[') { pos = skip_list(pos + 1, std::cend(string)) + 1; - } - else if (*pos == ']') { + } else if (*pos == ']') { break; - } - else if (*pos == ',') { + } else if (*pos == ',') { input.emplace_back(begin, pos); pos = begin = std::find_if_not(pos + 1, std::cend(string), whitespace); - } - else { + } else { ++pos; } } @@ -918,8 +921,7 @@ namespace config { } } - void - list_prep_cmd_f(std::unordered_map &vars, const std::string &name, std::vector &input) { + void list_prep_cmd_f(std::unordered_map &vars, const std::string &name, std::vector &input) { std::string string; string_f(vars, name, string); @@ -945,8 +947,7 @@ namespace config { } } - void - list_int_f(std::unordered_map &vars, const std::string &name, std::vector &input) { + void list_int_f(std::unordered_map &vars, const std::string &name, std::vector &input) { std::vector list; list_string_f(vars, name, list); @@ -972,16 +973,14 @@ namespace config { // If the integer is a hexadecimal if (val.size() >= 2 && val.substr(0, 2) == "0x"sv) { tmp = util::from_hex(val.substr(2)); - } - else { + } else { tmp = util::from_view(val); } input.emplace_back(tmp); } } - void - map_int_int_f(std::unordered_map &vars, const std::string &name, std::unordered_map &input) { + void map_int_int_f(std::unordered_map &vars, const std::string &name, std::unordered_map &input) { std::vector list; list_int_f(vars, name, list); @@ -1000,8 +999,7 @@ namespace config { } } - int - apply_flags(const char *line) { + int apply_flags(const char *line) { int ret = 0; while (*line != '\0') { switch (*line) { @@ -1028,8 +1026,7 @@ namespace config { return ret; } - std::vector & - get_supported_gamepad_options() { + std::vector &get_supported_gamepad_options() { const auto options = platf::supported_gamepads(nullptr); static std::vector opts {}; opts.reserve(options.size()); @@ -1039,8 +1036,7 @@ namespace config { return opts; } - void - apply_config(std::unordered_map &&vars) { + void apply_config(std::unordered_map &&vars) { if (!fs::exists(stream.file_apps.c_str())) { fs::copy_file(SUNSHINE_ASSETS_DIR "/apps.json", stream.file_apps); } @@ -1050,8 +1046,8 @@ namespace config { } int_f(vars, "qp", video.qp); - int_between_f(vars, "hevc_mode", video.hevc_mode, { 0, 3 }); - int_between_f(vars, "av1_mode", video.av1_mode, { 0, 3 }); + int_between_f(vars, "hevc_mode", video.hevc_mode, {0, 3}); + int_between_f(vars, "av1_mode", video.av1_mode, {0, 3}); int_f(vars, "min_threads", video.min_threads); string_f(vars, "sw_preset", video.sw.sw_preset); if (!video.sw.sw_preset.empty()) { @@ -1059,8 +1055,8 @@ namespace config { } string_f(vars, "sw_tune", video.sw.sw_tune); - int_between_f(vars, "nvenc_preset", video.nv.quality_preset, { 1, 7 }); - int_between_f(vars, "nvenc_vbv_increase", video.nv.vbv_percentage_increase, { 0, 400 }); + int_between_f(vars, "nvenc_preset", video.nv.quality_preset, {1, 7}); + int_between_f(vars, "nvenc_vbv_increase", video.nv.vbv_percentage_increase, {0, 400}); bool_f(vars, "nvenc_spatial_aq", video.nv.adaptive_quantization); generic_f(vars, "nvenc_twopass", video.nv.two_pass, nv::twopass_from_view); bool_f(vars, "nvenc_h264_cavlc", video.nv.h264_cavlc); @@ -1131,15 +1127,15 @@ namespace config { generic_f(vars, "dd_hdr_option", video.dd.hdr_option, dd::hdr_option_from_view); { int value = -1; - int_between_f(vars, "dd_config_revert_delay", value, { 0, std::numeric_limits::max() }); + int_between_f(vars, "dd_config_revert_delay", value, {0, std::numeric_limits::max()}); if (value >= 0) { - video.dd.config_revert_delay = std::chrono::milliseconds { value }; + video.dd.config_revert_delay = std::chrono::milliseconds {value}; } } generic_f(vars, "dd_mode_remapping", video.dd.mode_remapping, dd::mode_remapping_from_view); bool_f(vars, "dd_wa_hdr_toggle", video.dd.wa.hdr_toggle); - int_between_f(vars, "min_fps_factor", video.min_fps_factor, { 1, 3 }); + int_between_f(vars, "min_fps_factor", video.min_fps_factor, {1, 3}); path_f(vars, "pkey", nvhttp.pkey); path_f(vars, "cert", nvhttp.cert); @@ -1158,19 +1154,19 @@ namespace config { string_f(vars, "virtual_sink", audio.virtual_sink); bool_f(vars, "install_steam_audio_drivers", audio.install_steam_drivers); - string_restricted_f(vars, "origin_web_ui_allowed", nvhttp.origin_web_ui_allowed, { "pc"sv, "lan"sv, "wan"sv }); + string_restricted_f(vars, "origin_web_ui_allowed", nvhttp.origin_web_ui_allowed, {"pc"sv, "lan"sv, "wan"sv}); int to = -1; - int_between_f(vars, "ping_timeout", to, { -1, std::numeric_limits::max() }); + int_between_f(vars, "ping_timeout", to, {-1, std::numeric_limits::max()}); if (to != -1) { stream.ping_timeout = std::chrono::milliseconds(to); } - int_between_f(vars, "lan_encryption_mode", stream.lan_encryption_mode, { 0, 2 }); - int_between_f(vars, "wan_encryption_mode", stream.wan_encryption_mode, { 0, 2 }); + int_between_f(vars, "lan_encryption_mode", stream.lan_encryption_mode, {0, 2}); + int_between_f(vars, "wan_encryption_mode", stream.wan_encryption_mode, {0, 2}); path_f(vars, "file_apps", stream.file_apps); - int_between_f(vars, "fec_percentage", stream.fec_percentage, { 1, 255 }); + int_between_f(vars, "fec_percentage", stream.fec_percentage, {1, 255}); map_int_int_f(vars, "keybindings"s, input.keybindings); @@ -1187,20 +1183,20 @@ namespace config { int_f(vars, "back_button_timeout", to); if (to > std::numeric_limits::min()) { - input.back_button_timeout = std::chrono::milliseconds { to }; + input.back_button_timeout = std::chrono::milliseconds {to}; } - double repeat_frequency { 0 }; - double_between_f(vars, "key_repeat_frequency", repeat_frequency, { 0, std::numeric_limits::max() }); + double repeat_frequency {0}; + double_between_f(vars, "key_repeat_frequency", repeat_frequency, {0, std::numeric_limits::max()}); if (repeat_frequency > 0) { - config::input.key_repeat_period = std::chrono::duration { 1 / repeat_frequency }; + config::input.key_repeat_period = std::chrono::duration {1 / repeat_frequency}; } to = -1; int_f(vars, "key_repeat_delay", to); if (to >= 0) { - input.key_repeat_delay = std::chrono::milliseconds { to }; + input.key_repeat_delay = std::chrono::milliseconds {to}; } string_restricted_f(vars, "gamepad"s, input.gamepad, get_supported_gamepad_options()); @@ -1220,10 +1216,10 @@ namespace config { bool_f(vars, "notify_pre_releases", sunshine.notify_pre_releases); int port = sunshine.port; - int_between_f(vars, "port"s, port, { 1024 + nvhttp::PORT_HTTPS, 65535 - rtsp_stream::RTSP_SETUP_PORT }); + int_between_f(vars, "port"s, port, {1024 + nvhttp::PORT_HTTPS, 65535 - rtsp_stream::RTSP_SETUP_PORT}); sunshine.port = (std::uint16_t) port; - string_restricted_f(vars, "address_family", sunshine.address_family, { "ipv4"sv, "both"sv }); + string_restricted_f(vars, "address_family", sunshine.address_family, {"ipv4"sv, "both"sv}); bool upnp = false; bool_f(vars, "upnp"s, upnp); @@ -1259,26 +1255,19 @@ namespace config { if (!log_level_string.empty()) { if (log_level_string == "verbose"sv) { sunshine.min_log_level = 0; - } - else if (log_level_string == "debug"sv) { + } else if (log_level_string == "debug"sv) { sunshine.min_log_level = 1; - } - else if (log_level_string == "info"sv) { + } else if (log_level_string == "info"sv) { sunshine.min_log_level = 2; - } - else if (log_level_string == "warning"sv) { + } else if (log_level_string == "warning"sv) { sunshine.min_log_level = 3; - } - else if (log_level_string == "error"sv) { + } else if (log_level_string == "error"sv) { sunshine.min_log_level = 4; - } - else if (log_level_string == "fatal"sv) { + } else if (log_level_string == "fatal"sv) { sunshine.min_log_level = 5; - } - else if (log_level_string == "none"sv) { + } else if (log_level_string == "none"sv) { sunshine.min_log_level = 6; - } - else { + } else { // accept digit directly auto val = log_level_string[0]; if (val >= '0' && val < '7') { @@ -1301,8 +1290,7 @@ namespace config { } } - int - parse(int argc, char *argv[]) { + int parse(int argc, char *argv[]) { std::unordered_map cmd_vars; #ifdef _WIN32 bool shortcut_launch = false; @@ -1319,8 +1307,7 @@ namespace config { #ifdef _WIN32 else if (line == "--shortcut"sv) { shortcut_launch = true; - } - else if (line == "--shortcut-admin"sv) { + } else if (line == "--shortcut-admin"sv) { service_admin_launch = true; } #endif @@ -1336,15 +1323,13 @@ namespace config { logging::print_help(*argv); return -1; } - } - else { + } else { auto line_end = line + strlen(line); auto pos = std::find(line, line_end, '='); if (pos == line_end) { sunshine.config_file = line; - } - else { + } else { TUPLE_EL(var, 1, parse_option(line, line_end)); if (!var) { logging::print_help(*argv); @@ -1370,7 +1355,7 @@ namespace config { // Create empty config file if it does not exist if (!fs::exists(sunshine.config_file)) { - std::ofstream { sunshine.config_file }; + std::ofstream {sunshine.config_file}; } // Read config file @@ -1385,11 +1370,9 @@ namespace config { // the path is incorrect or inaccessible. apply_config(std::move(vars)); config_loaded = true; - } - catch (const std::filesystem::filesystem_error &err) { + } catch (const std::filesystem::filesystem_error &err) { BOOST_LOG(fatal) << "Failed to apply config: "sv << err.what(); - } - catch (const boost::filesystem::filesystem_error &err) { + } catch (const boost::filesystem::filesystem_error &err) { BOOST_LOG(fatal) << "Failed to apply config: "sv << err.what(); } @@ -1419,7 +1402,7 @@ namespace config { // Always return 1 to ensure Sunshine doesn't start normally return 1; } - else if (shortcut_launch) { + if (shortcut_launch) { if (!service_ctrl::is_service_running()) { // If the service isn't running, relaunch ourselves as admin to start it WCHAR executable[MAX_PATH]; diff --git a/src/config.h b/src/config.h index e429f0a9..34e673d1 100644 --- a/src/config.h +++ b/src/config.h @@ -4,6 +4,7 @@ */ #pragma once +// standard includes #include #include #include @@ -11,6 +12,7 @@ #include #include +// local includes #include "nvenc/nvenc_config.h" namespace config { @@ -22,6 +24,7 @@ namespace config { int av1_mode; int min_threads; // Minimum number of threads/slices for CPU encoding + struct { std::string sw_preset; std::string sw_tune; @@ -204,17 +207,25 @@ namespace config { CONST_PIN, ///< Use "universal" pin FLAG_SIZE ///< Number of flags }; - } + } // namespace flag struct prep_cmd_t { prep_cmd_t(std::string &&do_cmd, std::string &&undo_cmd, bool &&elevated): - do_cmd(std::move(do_cmd)), undo_cmd(std::move(undo_cmd)), elevated(std::move(elevated)) {} + do_cmd(std::move(do_cmd)), + undo_cmd(std::move(undo_cmd)), + elevated(std::move(elevated)) { + } + explicit prep_cmd_t(std::string &&do_cmd, bool &&elevated): - do_cmd(std::move(do_cmd)), elevated(std::move(elevated)) {} + do_cmd(std::move(do_cmd)), + elevated(std::move(elevated)) { + } + std::string do_cmd; std::string undo_cmd; bool elevated; }; + struct sunshine_t { std::string locale; int min_log_level; @@ -248,8 +259,6 @@ namespace config { extern input_t input; extern sunshine_t sunshine; - int - parse(int argc, char *argv[]); - std::unordered_map - parse_config(const std::string_view &file_content); + int parse(int argc, char *argv[]); + std::unordered_map parse_config(const std::string_view &file_content); } // namespace config diff --git a/src/confighttp.cpp b/src/confighttp.cpp index f2a36441..5050a4eb 100644 --- a/src/confighttp.cpp +++ b/src/confighttp.cpp @@ -6,25 +6,22 @@ */ #define BOOST_BIND_GLOBAL_PLACEHOLDERS -#include "process.h" - +// standard includes #include #include +// lib includes +#include +#include +#include +#include #include #include #include - -#include - -#include - -#include - #include #include -#include +// local includes #include "config.h" #include "confighttp.h" #include "crypto.h" @@ -36,6 +33,7 @@ #include "network.h" #include "nvhttp.h" #include "platform/common.h" +#include "process.h" #include "rtsp.h" #include "utility.h" #include "uuid.h" @@ -62,8 +60,7 @@ namespace confighttp { * @brief Log the request details. * @param request The HTTP request object. */ - void - print_req(const req_https_t &request) { + void print_req(const req_https_t &request) { BOOST_LOG(debug) << "METHOD :: "sv << request->method; BOOST_LOG(debug) << "DESTINATION :: "sv << request->path; @@ -85,8 +82,7 @@ namespace confighttp { * @param response The HTTP response object. * @param output_tree The JSON tree to send. */ - void - send_response(resp_https_t response, const pt::ptree &output_tree) { + void send_response(resp_https_t response, const pt::ptree &output_tree) { std::ostringstream data; pt::write_json(data, output_tree); response->write(data.str()); @@ -97,12 +93,11 @@ namespace confighttp { * @param response The HTTP response object. * @param request The HTTP request object. */ - void - send_unauthorized(resp_https_t response, req_https_t request) { + void send_unauthorized(resp_https_t response, req_https_t request) { auto address = net::addr_to_normalized_string(request->remote_endpoint().address()); BOOST_LOG(info) << "Web UI: ["sv << address << "] -- not authorized"sv; const SimpleWeb::CaseInsensitiveMultimap headers { - { "WWW-Authenticate", R"(Basic realm="Sunshine Gamestream Host", charset="UTF-8")" } + {"WWW-Authenticate", R"(Basic realm="Sunshine Gamestream Host", charset="UTF-8")"} }; response->write(SimpleWeb::StatusCode::client_error_unauthorized, headers); } @@ -113,12 +108,11 @@ namespace confighttp { * @param request The HTTP request object. * @param path The path to redirect to. */ - void - send_redirect(resp_https_t response, req_https_t request, const char *path) { + void send_redirect(resp_https_t response, req_https_t request, const char *path) { auto address = net::addr_to_normalized_string(request->remote_endpoint().address()); BOOST_LOG(info) << "Web UI: ["sv << address << "] -- not authorized"sv; const SimpleWeb::CaseInsensitiveMultimap headers { - { "Location", path } + {"Location", path} }; response->write(SimpleWeb::StatusCode::redirection_temporary_redirect, headers); } @@ -129,8 +123,7 @@ namespace confighttp { * @param request The HTTP request object. * @return True if the user is authenticated, false otherwise. */ - bool - authenticate(resp_https_t response, req_https_t request) { + bool authenticate(resp_https_t response, req_https_t request) { auto address = net::addr_to_normalized_string(request->remote_endpoint().address()); auto ip_type = net::from_address(address); @@ -180,8 +173,7 @@ namespace confighttp { * @param response The HTTP response object. * @param request The HTTP request object. */ - void - not_found(resp_https_t response, [[maybe_unused]] req_https_t request) { + void not_found(resp_https_t response, [[maybe_unused]] req_https_t request) { constexpr SimpleWeb::StatusCode code = SimpleWeb::StatusCode::client_error_not_found; pt::ptree tree; @@ -203,8 +195,7 @@ namespace confighttp { * @param request The HTTP request object. * @param error_message The error message to include in the response. */ - void - bad_request(resp_https_t response, [[maybe_unused]] req_https_t request, const std::string &error_message = "Bad Request") { + void bad_request(resp_https_t response, [[maybe_unused]] req_https_t request, const std::string &error_message = "Bad Request") { constexpr SimpleWeb::StatusCode code = SimpleWeb::StatusCode::client_error_bad_request; pt::ptree tree; @@ -227,9 +218,10 @@ namespace confighttp { * @param request The HTTP request object. * @todo combine these functions into a single function that accepts the page, i.e "index", "pin", "apps" */ - void - getIndexPage(resp_https_t response, req_https_t request) { - if (!authenticate(response, request)) return; + void getIndexPage(resp_https_t response, req_https_t request) { + if (!authenticate(response, request)) { + return; + } print_req(request); @@ -244,9 +236,10 @@ namespace confighttp { * @param response The HTTP response object. * @param request The HTTP request object. */ - void - getPinPage(resp_https_t response, req_https_t request) { - if (!authenticate(response, request)) return; + void getPinPage(resp_https_t response, req_https_t request) { + if (!authenticate(response, request)) { + return; + } print_req(request); @@ -261,9 +254,10 @@ namespace confighttp { * @param response The HTTP response object. * @param request The HTTP request object. */ - void - getAppsPage(resp_https_t response, req_https_t request) { - if (!authenticate(response, request)) return; + void getAppsPage(resp_https_t response, req_https_t request) { + if (!authenticate(response, request)) { + return; + } print_req(request); @@ -279,9 +273,10 @@ namespace confighttp { * @param response The HTTP response object. * @param request The HTTP request object. */ - void - getClientsPage(resp_https_t response, req_https_t request) { - if (!authenticate(response, request)) return; + void getClientsPage(resp_https_t response, req_https_t request) { + if (!authenticate(response, request)) { + return; + } print_req(request); @@ -296,9 +291,10 @@ namespace confighttp { * @param response The HTTP response object. * @param request The HTTP request object. */ - void - getConfigPage(resp_https_t response, req_https_t request) { - if (!authenticate(response, request)) return; + void getConfigPage(resp_https_t response, req_https_t request) { + if (!authenticate(response, request)) { + return; + } print_req(request); @@ -313,9 +309,10 @@ namespace confighttp { * @param response The HTTP response object. * @param request The HTTP request object. */ - void - getPasswordPage(resp_https_t response, req_https_t request) { - if (!authenticate(response, request)) return; + void getPasswordPage(resp_https_t response, req_https_t request) { + if (!authenticate(response, request)) { + return; + } print_req(request); @@ -330,8 +327,7 @@ namespace confighttp { * @param response The HTTP response object. * @param request The HTTP request object. */ - void - getWelcomePage(resp_https_t response, req_https_t request) { + void getWelcomePage(resp_https_t response, req_https_t request) { print_req(request); if (!config::sunshine.username.empty()) { send_redirect(response, request, "/"); @@ -348,9 +344,10 @@ namespace confighttp { * @param response The HTTP response object. * @param request The HTTP request object. */ - void - getTroubleshootingPage(resp_https_t response, req_https_t request) { - if (!authenticate(response, request)) return; + void getTroubleshootingPage(resp_https_t response, req_https_t request) { + if (!authenticate(response, request)) { + return; + } print_req(request); @@ -367,8 +364,7 @@ namespace confighttp { * @todo combine function with getSunshineLogoImage and possibly getNodeModules * @todo use mime_types map */ - void - getFaviconImage(resp_https_t response, req_https_t request) { + void getFaviconImage(resp_https_t response, req_https_t request) { print_req(request); std::ifstream in(WEB_DIR "images/sunshine.ico", std::ios::binary); @@ -384,8 +380,7 @@ namespace confighttp { * @todo combine function with getFaviconImage and possibly getNodeModules * @todo use mime_types map */ - void - getSunshineLogoImage(resp_https_t response, req_https_t request) { + void getSunshineLogoImage(resp_https_t response, req_https_t request) { print_req(request); std::ifstream in(WEB_DIR "images/logo-sunshine-45.png", std::ios::binary); @@ -400,8 +395,7 @@ namespace confighttp { * @param query The path to check. * @return True if the path is a child of the base path, false otherwise. */ - bool - isChildPath(fs::path const &base, fs::path const &query) { + bool isChildPath(fs::path const &base, fs::path const &query) { auto relPath = fs::relative(base, query); return *(relPath.begin()) != fs::path(".."); } @@ -411,8 +405,7 @@ namespace confighttp { * @param response The HTTP response object. * @param request The HTTP request object. */ - void - getNodeModules(resp_https_t response, req_https_t request) { + void getNodeModules(resp_https_t response, req_https_t request) { print_req(request); fs::path webDirPath(WEB_DIR); fs::path nodeModulesPath(webDirPath / "assets"); @@ -455,9 +448,10 @@ namespace confighttp { * * @api_examples{/api/apps| GET| null} */ - void - getApps(resp_https_t response, req_https_t request) { - if (!authenticate(response, request)) return; + void getApps(resp_https_t response, req_https_t request) { + if (!authenticate(response, request)) { + return; + } print_req(request); @@ -474,9 +468,10 @@ namespace confighttp { * * @api_examples{/api/logs| GET| null} */ - void - getLogs(resp_https_t response, req_https_t request) { - if (!authenticate(response, request)) return; + void getLogs(resp_https_t response, req_https_t request) { + if (!authenticate(response, request)) { + return; + } print_req(request); @@ -518,9 +513,10 @@ namespace confighttp { * * @api_examples{/api/apps| POST| {"name":"Hello, World!","index":-1}} */ - void - saveApp(resp_https_t response, req_https_t request) { - if (!authenticate(response, request)) return; + void saveApp(resp_https_t response, req_https_t request) { + if (!authenticate(response, request)) { + return; + } print_req(request); @@ -551,16 +547,14 @@ namespace confighttp { if (index == -1) { apps_node.push_back(std::make_pair("", inputTree)); - } - else { + } else { // Unfortunately Boost PT does not allow to directly edit the array, copy should do the trick pt::ptree newApps; int i = 0; for (const auto &[k, v] : apps_node) { if (i == index) { newApps.push_back(std::make_pair("", inputTree)); - } - else { + } else { newApps.push_back(std::make_pair("", v)); } i++; @@ -590,8 +584,7 @@ namespace confighttp { outputTree.put("status", true); send_response(response, outputTree); - } - catch (std::exception &e) { + } catch (std::exception &e) { BOOST_LOG(warning) << "SaveApp: "sv << e.what(); bad_request(response, request, e.what()); } @@ -604,9 +597,10 @@ namespace confighttp { * * @api_examples{/api/apps/9999| DELETE| null} */ - void - deleteApp(resp_https_t response, req_https_t request) { - if (!authenticate(response, request)) return; + void deleteApp(resp_https_t response, req_https_t request) { + if (!authenticate(response, request)) { + return; + } print_req(request); @@ -622,8 +616,7 @@ namespace confighttp { std::string error; if (const int max_index = static_cast(apps_node.size()) - 1; max_index < 0) { error = "No applications to delete"; - } - else { + } else { error = "'index' out of range, max index is "s + std::to_string(max_index); } bad_request(response, request, error); @@ -646,8 +639,7 @@ namespace confighttp { outputTree.put("status", true); outputTree.put("result", "application "s + std::to_string(index) + " deleted"); send_response(response, outputTree); - } - catch (std::exception &e) { + } catch (std::exception &e) { BOOST_LOG(warning) << "DeleteApp: "sv << e.what(); bad_request(response, request, e.what()); } @@ -667,9 +659,10 @@ namespace confighttp { * * @api_examples{/api/covers/upload| POST| {"key":"igdb_1234","url":"https://images.igdb.com/igdb/image/upload/t_cover_big_2x/abc123.png"}} */ - void - uploadCover(resp_https_t response, req_https_t request) { - if (!authenticate(response, request)) return; + void uploadCover(resp_https_t response, req_https_t request) { + if (!authenticate(response, request)) { + return; + } std::stringstream ss; std::stringstream configStream; @@ -678,8 +671,7 @@ namespace confighttp { pt::ptree inputTree; try { pt::read_json(ss, inputTree); - } - catch (std::exception &e) { + } catch (std::exception &e) { BOOST_LOG(warning) << "UploadCover: "sv << e.what(); bad_request(response, request, e.what()); return; @@ -705,8 +697,7 @@ namespace confighttp { bad_request(response, request, "Failed to download cover"); return; } - } - else { + } else { auto data = SimpleWeb::Crypto::Base64::decode(inputTree.get("data")); std::ofstream imgfile(path); @@ -724,9 +715,10 @@ namespace confighttp { * * @api_examples{/api/config| GET| null} */ - void - getConfig(resp_https_t response, req_https_t request) { - if (!authenticate(response, request)) return; + void getConfig(resp_https_t response, req_https_t request) { + if (!authenticate(response, request)) { + return; + } print_req(request); @@ -751,8 +743,7 @@ namespace confighttp { * * @api_examples{/api/configLocale| GET| null} */ - void - getLocale(resp_https_t response, req_https_t request) { + void getLocale(resp_https_t response, req_https_t request) { // we need to return the locale whether authenticated or not print_req(request); @@ -778,9 +769,10 @@ namespace confighttp { * * @api_examples{/api/config| POST| {"key":"value"}} */ - void - saveConfig(resp_https_t response, req_https_t request) { - if (!authenticate(response, request)) return; + void saveConfig(resp_https_t response, req_https_t request) { + if (!authenticate(response, request)) { + return; + } print_req(request); @@ -794,15 +786,16 @@ namespace confighttp { pt::read_json(ss, inputTree); for (const auto &[k, v] : inputTree) { std::string value = inputTree.get(k); - if (value.length() == 0 || value.compare("null") == 0) continue; + if (value.length() == 0 || value.compare("null") == 0) { + continue; + } configStream << k << " = " << value << std::endl; } file_handler::write_file(config::sunshine.config_file.c_str(), configStream.str()); outputTree.put("status", true); send_response(response, outputTree); - } - catch (std::exception &e) { + } catch (std::exception &e) { BOOST_LOG(warning) << "SaveConfig: "sv << e.what(); bad_request(response, request, e.what()); } @@ -815,9 +808,10 @@ namespace confighttp { * * @api_examples{/api/restart| POST| null} */ - void - restart(resp_https_t response, req_https_t request) { - if (!authenticate(response, request)) return; + void restart(resp_https_t response, req_https_t request) { + if (!authenticate(response, request)) { + return; + } print_req(request); @@ -832,9 +826,10 @@ namespace confighttp { * * @api_examples{/api/reset-display-device-persistence| POST| null} */ - void - resetDisplayDevicePersistence(resp_https_t response, req_https_t request) { - if (!authenticate(response, request)) return; + void resetDisplayDevicePersistence(resp_https_t response, req_https_t request) { + if (!authenticate(response, request)) { + return; + } print_req(request); @@ -860,9 +855,10 @@ namespace confighttp { * * @api_examples{/api/password| POST| {"currentUsername":"admin","currentPassword":"admin","newUsername":"admin","newPassword":"admin","confirmNewPassword":"admin"}} */ - void - savePassword(resp_https_t response, req_https_t request) { - if (!config::sunshine.username.empty() && !authenticate(response, request)) return; + void savePassword(resp_https_t response, req_https_t request) { + if (!config::sunshine.username.empty() && !authenticate(response, request)) { + return; + } print_req(request); @@ -881,40 +877,37 @@ namespace confighttp { auto password = inputTree.count("currentPassword") > 0 ? inputTree.get("currentPassword") : ""; auto newPassword = inputTree.count("newPassword") > 0 ? inputTree.get("newPassword") : ""; auto confirmPassword = inputTree.count("confirmNewPassword") > 0 ? inputTree.get("confirmNewPassword") : ""; - if (newUsername.length() == 0) newUsername = username; + if (newUsername.length() == 0) { + newUsername = username; + } if (newUsername.length() == 0) { errors.emplace_back("Invalid Username"); - } - else { + } else { auto hash = util::hex(crypto::hash(password + config::sunshine.salt)).to_string(); if (config::sunshine.username.empty() || (boost::iequals(username, config::sunshine.username) && hash == config::sunshine.password)) { if (newPassword.empty() || newPassword != confirmPassword) { errors.emplace_back("Password Mismatch"); - } - else { + } else { http::save_user_creds(config::sunshine.credentials_file, newUsername, newPassword); http::reload_user_creds(config::sunshine.credentials_file); outputTree.put("status", true); } - } - else { + } else { errors.emplace_back("Invalid Current Credentials"); } } if (!errors.empty()) { // join the errors array - std::string error = std::accumulate(errors.begin(), errors.end(), std::string(), - [](const std::string &a, const std::string &b) { - return a.empty() ? b : a + ", " + b; - }); + std::string error = std::accumulate(errors.begin(), errors.end(), std::string(), [](const std::string &a, const std::string &b) { + return a.empty() ? b : a + ", " + b; + }); bad_request(response, request, error); return; } send_response(response, outputTree); - } - catch (std::exception &e) { + } catch (std::exception &e) { BOOST_LOG(warning) << "SavePassword: "sv << e.what(); bad_request(response, request, e.what()); } @@ -934,9 +927,10 @@ namespace confighttp { * * @api_examples{/api/pin| POST| {"pin":"1234","name":"My PC"}} */ - void - savePin(resp_https_t response, req_https_t request) { - if (!authenticate(response, request)) return; + void savePin(resp_https_t response, req_https_t request) { + if (!authenticate(response, request)) { + return; + } print_req(request); @@ -952,8 +946,7 @@ namespace confighttp { std::string name = inputTree.get("name"); outputTree.put("status", nvhttp::pin(pin, name)); send_response(response, outputTree); - } - catch (std::exception &e) { + } catch (std::exception &e) { BOOST_LOG(warning) << "SavePin: "sv << e.what(); bad_request(response, request, e.what()); } @@ -966,9 +959,10 @@ namespace confighttp { * * @api_examples{/api/clients/unpair-all| POST| null} */ - void - unpairAll(resp_https_t response, req_https_t request) { - if (!authenticate(response, request)) return; + void unpairAll(resp_https_t response, req_https_t request) { + if (!authenticate(response, request)) { + return; + } print_req(request); @@ -993,9 +987,10 @@ namespace confighttp { * * @api_examples{/api/unpair| POST| {"uuid":"1234"}} */ - void - unpair(resp_https_t response, req_https_t request) { - if (!authenticate(response, request)) return; + void unpair(resp_https_t response, req_https_t request) { + if (!authenticate(response, request)) { + return; + } print_req(request); @@ -1010,8 +1005,7 @@ namespace confighttp { std::string uuid = inputTree.get("uuid"); outputTree.put("status", nvhttp::unpair_client(uuid)); send_response(response, outputTree); - } - catch (std::exception &e) { + } catch (std::exception &e) { BOOST_LOG(warning) << "Unpair: "sv << e.what(); bad_request(response, request, e.what()); } @@ -1024,9 +1018,10 @@ namespace confighttp { * * @api_examples{/api/clients/list| GET| null} */ - void - listClients(resp_https_t response, req_https_t request) { - if (!authenticate(response, request)) return; + void listClients(resp_https_t response, req_https_t request) { + if (!authenticate(response, request)) { + return; + } print_req(request); @@ -1046,9 +1041,10 @@ namespace confighttp { * * @api_examples{/api/apps/close| POST| null} */ - void - closeApp(resp_https_t response, req_https_t request) { - if (!authenticate(response, request)) return; + void closeApp(resp_https_t response, req_https_t request) { + if (!authenticate(response, request)) { + return; + } print_req(request); @@ -1059,14 +1055,13 @@ namespace confighttp { send_response(response, outputTree); } - void - start() { + void start() { auto shutdown_event = mail::man->event(mail::shutdown); auto port_https = net::map_port(PORT_HTTPS); auto address_family = net::af_from_enum_string(config::sunshine.address_family); - https_server_t server { config::nvhttp.cert, config::nvhttp.pkey }; + https_server_t server {config::nvhttp.cert, config::nvhttp.pkey}; server.default_resource["DELETE"] = [](resp_https_t response, req_https_t request) { bad_request(response, request); }; @@ -1116,8 +1111,7 @@ namespace confighttp { server->start([](unsigned short port) { BOOST_LOG(info) << "Configuration UI available at [https://localhost:"sv << port << "]"; }); - } - catch (boost::system::system_error &err) { + } catch (boost::system::system_error &err) { // It's possible the exception gets thrown after calling server->stop() from a different thread if (shutdown_event->peek()) { return; @@ -1128,7 +1122,7 @@ namespace confighttp { return; } }; - std::thread tcp { accept_and_run, &server }; + std::thread tcp {accept_and_run, &server}; // Wait for any event shutdown_event->view(); diff --git a/src/confighttp.h b/src/confighttp.h index db202cb6..af5174aa 100644 --- a/src/confighttp.h +++ b/src/confighttp.h @@ -4,34 +4,35 @@ */ #pragma once +// standard includes #include #include +// local includes #include "thread_safe.h" #define WEB_DIR SUNSHINE_ASSETS_DIR "/web/" namespace confighttp { constexpr auto PORT_HTTPS = 1; - void - start(); + void start(); } // namespace confighttp // mime types map const std::map mime_types = { - { "css", "text/css" }, - { "gif", "image/gif" }, - { "htm", "text/html" }, - { "html", "text/html" }, - { "ico", "image/x-icon" }, - { "jpeg", "image/jpeg" }, - { "jpg", "image/jpeg" }, - { "js", "application/javascript" }, - { "json", "application/json" }, - { "png", "image/png" }, - { "svg", "image/svg+xml" }, - { "ttf", "font/ttf" }, - { "txt", "text/plain" }, - { "woff2", "font/woff2" }, - { "xml", "text/xml" }, + {"css", "text/css"}, + {"gif", "image/gif"}, + {"htm", "text/html"}, + {"html", "text/html"}, + {"ico", "image/x-icon"}, + {"jpeg", "image/jpeg"}, + {"jpg", "image/jpeg"}, + {"js", "application/javascript"}, + {"json", "application/json"}, + {"png", "image/png"}, + {"svg", "image/svg+xml"}, + {"ttf", "font/ttf"}, + {"txt", "text/plain"}, + {"woff2", "font/woff2"}, + {"xml", "text/xml"}, }; diff --git a/src/crypto.cpp b/src/crypto.cpp index 2b30a3bc..a6f326e7 100644 --- a/src/crypto.cpp +++ b/src/crypto.cpp @@ -2,29 +2,33 @@ * @file src/crypto.cpp * @brief Definitions for cryptography functions. */ -#include "crypto.h" +// lib includes #include #include +// local includes +#include "crypto.h" + namespace crypto { using asn1_string_t = util::safe_ptr; 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() }; + _certs {}, + _cert_ctx {X509_STORE_CTX_new()} { + } + + void cert_chain_t::add(x509_t &&cert) { + x509_store_t x509_store {X509_STORE_new()}; X509_STORE_add_cert(x509_store.get(), cert.get()); _certs.emplace_back(std::make_pair(std::move(cert), std::move(x509_store))); } - void - cert_chain_t::clear() { + + void cert_chain_t::clear() { _certs.clear(); } - static int - openssl_verify_cb(int ok, X509_STORE_CTX *ctx) { + static int openssl_verify_cb(int ok, X509_STORE_CTX *ctx) { int err_code = X509_STORE_CTX_get_error(ctx); switch (err_code) { @@ -52,8 +56,7 @@ namespace crypto { * @param cert The certificate to verify. * @return nullptr if the certificate is valid, otherwise an error string. */ - const char * - cert_chain_t::verify(x509_t::element_type *cert) { + const char *cert_chain_t::verify(x509_t::element_type *cert) { int err_code = 0; for (auto &[_, x509_store] : _certs) { auto fg = util::fail_guard([this]() { @@ -86,8 +89,7 @@ namespace crypto { namespace cipher { - static int - init_decrypt_gcm(cipher_ctx_t &ctx, aes_t *key, aes_t *iv, bool padding) { + static int init_decrypt_gcm(cipher_ctx_t &ctx, aes_t *key, aes_t *iv, bool padding) { ctx.reset(EVP_CIPHER_CTX_new()); if (!ctx) { @@ -110,8 +112,7 @@ namespace crypto { return 0; } - static int - init_encrypt_gcm(cipher_ctx_t &ctx, aes_t *key, aes_t *iv, bool padding) { + static int init_encrypt_gcm(cipher_ctx_t &ctx, aes_t *key, aes_t *iv, bool padding) { ctx.reset(EVP_CIPHER_CTX_new()); // Gen 7 servers use 128-bit AES ECB @@ -131,8 +132,7 @@ namespace crypto { return 0; } - static int - init_encrypt_cbc(cipher_ctx_t &ctx, aes_t *key, aes_t *iv, bool padding) { + static int init_encrypt_cbc(cipher_ctx_t &ctx, aes_t *key, aes_t *iv, bool padding) { ctx.reset(EVP_CIPHER_CTX_new()); // Gen 7 servers use 128-bit AES ECB @@ -145,8 +145,7 @@ namespace crypto { return 0; } - int - gcm_t::decrypt(const std::string_view &tagged_cipher, std::vector &plaintext, aes_t *iv) { + int gcm_t::decrypt(const std::string_view &tagged_cipher, std::vector &plaintext, aes_t *iv) { if (!decrypt_ctx && init_decrypt_gcm(decrypt_ctx, &key, iv, padding)) { return -1; } @@ -185,8 +184,7 @@ namespace crypto { * The function handles the creation and initialization of the encryption context, and manages the encryption process. * The resulting ciphertext and the GCM tag are written into the tagged_cipher buffer. */ - int - gcm_t::encrypt(const std::string_view &plaintext, std::uint8_t *tag, std::uint8_t *ciphertext, aes_t *iv) { + int gcm_t::encrypt(const std::string_view &plaintext, std::uint8_t *tag, std::uint8_t *ciphertext, aes_t *iv) { if (!encrypt_ctx && init_encrypt_gcm(encrypt_ctx, &key, iv, padding)) { return -1; } @@ -216,14 +214,12 @@ namespace crypto { return update_outlen + final_outlen; } - int - gcm_t::encrypt(const std::string_view &plaintext, std::uint8_t *tagged_cipher, aes_t *iv) { + int gcm_t::encrypt(const std::string_view &plaintext, std::uint8_t *tagged_cipher, aes_t *iv) { // This overload handles the common case of [GCM tag][cipher text] buffer layout return encrypt(plaintext, tagged_cipher, tagged_cipher + tag_size, iv); } - int - ecb_t::decrypt(const std::string_view &cipher, std::vector &plaintext) { + int ecb_t::decrypt(const std::string_view &cipher, std::vector &plaintext) { auto fg = util::fail_guard([this]() { EVP_CIPHER_CTX_reset(decrypt_ctx.get()); }); @@ -250,8 +246,7 @@ namespace crypto { return 0; } - int - ecb_t::encrypt(const std::string_view &plaintext, std::vector &cipher) { + int ecb_t::encrypt(const std::string_view &plaintext, std::vector &cipher) { auto fg = util::fail_guard([this]() { EVP_CIPHER_CTX_reset(encrypt_ctx.get()); }); @@ -284,8 +279,7 @@ namespace crypto { * The function handles the creation and initialization of the encryption context, and manages the encryption process. * The resulting ciphertext is written into the cipher buffer. */ - int - cbc_t::encrypt(const std::string_view &plaintext, std::uint8_t *cipher, aes_t *iv) { + int cbc_t::encrypt(const std::string_view &plaintext, std::uint8_t *cipher, aes_t *iv) { if (!encrypt_ctx && init_encrypt_cbc(encrypt_ctx, &key, iv, padding)) { return -1; } @@ -311,18 +305,20 @@ namespace crypto { } ecb_t::ecb_t(const aes_t &key, bool padding): - cipher_t { EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_new(), key, padding } {} + cipher_t {EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_new(), key, padding} { + } cbc_t::cbc_t(const aes_t &key, bool padding): - cipher_t { nullptr, nullptr, key, padding } {} + cipher_t {nullptr, nullptr, key, padding} { + } gcm_t::gcm_t(const crypto::aes_t &key, bool padding): - cipher_t { nullptr, nullptr, key, padding } {} + cipher_t {nullptr, nullptr, key, padding} { + } } // namespace cipher - aes_t - gen_aes_key(const std::array &salt, const std::string_view &pin) { + aes_t gen_aes_key(const std::array &salt, const std::string_view &pin) { aes_t key(16); std::string salt_pin; @@ -338,16 +334,14 @@ namespace crypto { return key; } - sha256_t - hash(const std::string_view &plaintext) { + sha256_t hash(const std::string_view &plaintext) { sha256_t hsh; EVP_Digest(plaintext.data(), plaintext.size(), hsh.data(), nullptr, EVP_sha256(), nullptr); return hsh; } - x509_t - x509(const std::string_view &x) { - bio_t io { BIO_new(BIO_s_mem()) }; + x509_t x509(const std::string_view &x) { + bio_t io {BIO_new(BIO_s_mem())}; BIO_write(io.get(), x.data(), x.size()); @@ -357,9 +351,8 @@ namespace crypto { return p; } - pkey_t - pkey(const std::string_view &k) { - bio_t io { BIO_new(BIO_s_mem()) }; + pkey_t pkey(const std::string_view &k) { + bio_t io {BIO_new(BIO_s_mem())}; BIO_write(io.get(), k.data(), k.size()); @@ -369,40 +362,36 @@ namespace crypto { return p; } - std::string - pem(x509_t &x509) { - bio_t bio { BIO_new(BIO_s_mem()) }; + std::string pem(x509_t &x509) { + bio_t bio {BIO_new(BIO_s_mem())}; PEM_write_bio_X509(bio.get(), x509.get()); BUF_MEM *mem_ptr; BIO_get_mem_ptr(bio.get(), &mem_ptr); - return { mem_ptr->data, mem_ptr->length }; + return {mem_ptr->data, mem_ptr->length}; } - std::string - pem(pkey_t &pkey) { - bio_t bio { BIO_new(BIO_s_mem()) }; + std::string pem(pkey_t &pkey) { + bio_t bio {BIO_new(BIO_s_mem())}; PEM_write_bio_PrivateKey(bio.get(), pkey.get(), nullptr, nullptr, 0, nullptr, nullptr); BUF_MEM *mem_ptr; BIO_get_mem_ptr(bio.get(), &mem_ptr); - return { mem_ptr->data, mem_ptr->length }; + return {mem_ptr->data, mem_ptr->length}; } - std::string_view - signature(const x509_t &x) { + std::string_view signature(const x509_t &x) { // X509_ALGOR *_ = nullptr; 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 rand(std::size_t bytes) { std::string r; r.resize(bytes); @@ -411,9 +400,8 @@ namespace crypto { return r; } - std::vector - sign(const pkey_t &pkey, const std::string_view &data, const EVP_MD *md) { - md_ctx_t ctx { EVP_MD_CTX_create() }; + std::vector sign(const pkey_t &pkey, const std::string_view &data, const EVP_MD *md) { + md_ctx_t ctx {EVP_MD_CTX_create()}; if (EVP_DigestSignInit(ctx.get(), nullptr, md, nullptr, (EVP_PKEY *) pkey.get()) != 1) { return {}; @@ -436,10 +424,9 @@ namespace crypto { return digest; } - creds_t - gen_creds(const std::string_view &cn, std::uint32_t key_bits) { - x509_t x509 { X509_new() }; - pkey_ctx_t ctx { EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, nullptr) }; + creds_t gen_creds(const std::string_view &cn, std::uint32_t key_bits) { + x509_t x509 {X509_new()}; + pkey_ctx_t ctx {EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, nullptr)}; pkey_t pkey; EVP_PKEY_keygen_init(ctx.get()); @@ -449,7 +436,7 @@ namespace crypto { X509_set_version(x509.get(), 2); // Generate a real serial number to avoid SEC_ERROR_REUSED_ISSUER_AND_SERIAL with Firefox - bignum_t serial { BN_new() }; + bignum_t serial {BN_new()}; BN_rand(serial.get(), 159, BN_RAND_TOP_ANY, BN_RAND_BOTTOM_ANY); // 159 bits to fit in 20 bytes in DER format BN_set_negative(serial.get(), 0); // Serial numbers must be positive BN_to_ASN1_INTEGER(serial.get(), X509_get_serialNumber(x509.get())); @@ -459,8 +446,8 @@ namespace crypto { X509_gmtime_adj(X509_get_notBefore(x509.get()), 0); X509_gmtime_adj(X509_get_notAfter(x509.get()), 20 * year); #else - asn1_string_t not_before { ASN1_STRING_dup(X509_get0_notBefore(x509.get())) }; - asn1_string_t not_after { ASN1_STRING_dup(X509_get0_notAfter(x509.get())) }; + asn1_string_t not_before {ASN1_STRING_dup(X509_get0_notBefore(x509.get()))}; + asn1_string_t not_after {ASN1_STRING_dup(X509_get0_notAfter(x509.get()))}; X509_gmtime_adj(not_before.get(), 0); X509_gmtime_adj(not_after.get(), 20 * year); @@ -472,26 +459,22 @@ namespace crypto { 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(), - -1, 0); + 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); X509_sign(x509.get(), pkey.get(), EVP_sha256()); - return { pem(x509), pem(pkey) }; + return {pem(x509), pem(pkey)}; } - std::vector - sign256(const pkey_t &pkey, const std::string_view &data) { + std::vector sign256(const pkey_t &pkey, const std::string_view &data) { return sign(pkey, data, EVP_sha256()); } - bool - verify(const x509_t &x509, const std::string_view &data, const std::string_view &signature, const EVP_MD *md) { + bool verify(const x509_t &x509, const std::string_view &data, const std::string_view &signature, const EVP_MD *md) { auto pkey = X509_get0_pubkey(x509.get()); - md_ctx_t ctx { EVP_MD_CTX_create() }; + md_ctx_t ctx {EVP_MD_CTX_create()}; if (EVP_DigestVerifyInit(ctx.get(), nullptr, md, nullptr, pkey) != 1) { return false; @@ -508,18 +491,15 @@ namespace crypto { return true; } - bool - verify256(const x509_t &x509, const std::string_view &data, const std::string_view &signature) { + bool verify256(const x509_t &x509, const std::string_view &data, const std::string_view &signature) { return verify(x509, data, signature, EVP_sha256()); } - void - md_ctx_destroy(EVP_MD_CTX *ctx) { + void md_ctx_destroy(EVP_MD_CTX *ctx) { EVP_MD_CTX_destroy(ctx); } - std::string - rand_alphabet(std::size_t bytes, const std::string_view &alphabet) { + std::string rand_alphabet(std::size_t bytes, const std::string_view &alphabet) { auto value = rand(bytes); for (std::size_t i = 0; i != value.size(); ++i) { diff --git a/src/crypto.h b/src/crypto.h index 859c6675..944cd77e 100644 --- a/src/crypto.h +++ b/src/crypto.h @@ -4,12 +4,16 @@ */ #pragma once +// standard includes #include + +// lib includes #include #include #include #include +// local includes #include "utility.h" namespace crypto { @@ -18,8 +22,7 @@ namespace crypto { std::string pkey; }; - void - md_ctx_destroy(EVP_MD_CTX *); + void md_ctx_destroy(EVP_MD_CTX *); using sha256_t = std::array; @@ -39,50 +42,33 @@ namespace crypto { * @param plaintext * @return The SHA-256 hash of the plaintext. */ - sha256_t - hash(const std::string_view &plaintext); + sha256_t hash(const std::string_view &plaintext); - aes_t - gen_aes_key(const std::array &salt, const std::string_view &pin); + aes_t gen_aes_key(const std::array &salt, const std::string_view &pin); + x509_t x509(const std::string_view &x); + pkey_t pkey(const std::string_view &k); + std::string pem(x509_t &x509); + std::string pem(pkey_t &pkey); - x509_t - x509(const std::string_view &x); - pkey_t - pkey(const std::string_view &k); - std::string - pem(x509_t &x509); - std::string - pem(pkey_t &pkey); + std::vector sign256(const pkey_t &pkey, const std::string_view &data); + bool verify256(const x509_t &x509, const std::string_view &data, const std::string_view &signature); - std::vector - sign256(const pkey_t &pkey, const std::string_view &data); - bool - verify256(const x509_t &x509, const std::string_view &data, const std::string_view &signature); + creds_t gen_creds(const std::string_view &cn, std::uint32_t key_bits); - creds_t - gen_creds(const std::string_view &cn, std::uint32_t key_bits); + std::string_view signature(const x509_t &x); - std::string_view - signature(const x509_t &x); - - std::string - rand(std::size_t bytes); - std::string - rand_alphabet(std::size_t bytes, - const std::string_view &alphabet = std::string_view { "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!%&()=-" }); + std::string rand(std::size_t bytes); + std::string rand_alphabet(std::size_t bytes, const std::string_view &alphabet = std::string_view {"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!%&()=-"}); class cert_chain_t { public: KITTY_DECL_CONSTR(cert_chain_t) - void - add(x509_t &&cert); + void add(x509_t &&cert); - void - clear(); + void clear(); - const char * - verify(x509_t::element_type *cert); + const char *verify(x509_t::element_type *cert); private: std::vector> _certs; @@ -91,8 +77,8 @@ namespace crypto { namespace cipher { constexpr std::size_t tag_size = 16; - constexpr std::size_t - round_to_pkcs7_padded(std::size_t size) { + + constexpr std::size_t round_to_pkcs7_padded(std::size_t size) { return ((size + 15) / 16) * 16; } @@ -110,23 +96,19 @@ namespace crypto { public: ecb_t() = default; ecb_t(ecb_t &&) noexcept = default; - ecb_t & - operator=(ecb_t &&) noexcept = default; + ecb_t &operator=(ecb_t &&) noexcept = default; ecb_t(const aes_t &key, bool padding = true); - int - encrypt(const std::string_view &plaintext, std::vector &cipher); - int - decrypt(const std::string_view &cipher, std::vector &plaintext); + int encrypt(const std::string_view &plaintext, std::vector &cipher); + int decrypt(const std::string_view &cipher, std::vector &plaintext); }; class gcm_t: public cipher_t { public: gcm_t() = default; gcm_t(gcm_t &&) noexcept = default; - gcm_t & - operator=(gcm_t &&) noexcept = default; + gcm_t &operator=(gcm_t &&) noexcept = default; gcm_t(const crypto::aes_t &key, bool padding = true); @@ -138,8 +120,7 @@ namespace crypto { * @param iv The initialization vector to be used for the encryption. * @return The total length of the ciphertext and GCM tag. Returns -1 in case of an error. */ - int - encrypt(const std::string_view &plaintext, std::uint8_t *tag, std::uint8_t *ciphertext, aes_t *iv); + int encrypt(const std::string_view &plaintext, std::uint8_t *tag, std::uint8_t *ciphertext, aes_t *iv); /** * @brief Encrypts the plaintext using AES GCM mode. @@ -149,19 +130,16 @@ namespace crypto { * @param iv The initialization vector to be used for the encryption. * @return The total length of the ciphertext and GCM tag written into tagged_cipher. Returns -1 in case of an error. */ - int - encrypt(const std::string_view &plaintext, std::uint8_t *tagged_cipher, aes_t *iv); + int encrypt(const std::string_view &plaintext, std::uint8_t *tagged_cipher, aes_t *iv); - int - decrypt(const std::string_view &cipher, std::vector &plaintext, aes_t *iv); + int decrypt(const std::string_view &cipher, std::vector &plaintext, aes_t *iv); }; class cbc_t: public cipher_t { public: cbc_t() = default; cbc_t(cbc_t &&) noexcept = default; - cbc_t & - operator=(cbc_t &&) noexcept = default; + cbc_t &operator=(cbc_t &&) noexcept = default; cbc_t(const crypto::aes_t &key, bool padding = true); @@ -173,8 +151,7 @@ namespace crypto { * @param iv The initialization vector to be used for the encryption. * @return The total length of the ciphertext written into cipher. Returns -1 in case of an error. */ - int - encrypt(const std::string_view &plaintext, std::uint8_t *cipher, aes_t *iv); + int encrypt(const std::string_view &plaintext, std::uint8_t *cipher, aes_t *iv); }; } // namespace cipher } // namespace crypto diff --git a/src/display_device.cpp b/src/display_device.cpp index fc273fcb..d0288290 100644 --- a/src/display_device.cpp +++ b/src/display_device.cpp @@ -29,15 +29,15 @@ namespace display_device { namespace { - constexpr std::chrono::milliseconds DEFAULT_RETRY_INTERVAL { 5000 }; + constexpr std::chrono::milliseconds DEFAULT_RETRY_INTERVAL {5000}; /** * @brief A global for the settings manager interface and other settings whose lifetime is managed by `display_device::init(...)`. */ struct { std::mutex mutex {}; - std::chrono::milliseconds config_revert_delay { 0 }; - std::unique_ptr> sm_instance { nullptr }; + std::chrono::milliseconds config_revert_delay {0}; + std::unique_ptr> sm_instance {nullptr}; } DD_DATA; /** @@ -49,8 +49,7 @@ namespace display_device { */ class sunshine_audio_context_t: public AudioContextInterface { public: - [[nodiscard]] bool - capture() override { + [[nodiscard]] bool capture() override { return context_scheduler.execute([](auto &audio_context) { // Explicitly releasing the context first in case it was not release yet so that it can be potentially cleaned up. audio_context = boost::none; @@ -61,8 +60,7 @@ namespace display_device { }); } - [[nodiscard]] bool - isCaptured() const override { + [[nodiscard]] bool isCaptured() const override { return context_scheduler.execute([](const auto &audio_context) { if (audio_context) { // In case we still have context we need to check whether it was released or not. @@ -74,8 +72,7 @@ namespace display_device { }); } - void - release() override { + void release() override { context_scheduler.schedule([](auto &audio_context, auto &stop_token) { if (audio_context) { audio_context->released = true; @@ -93,7 +90,7 @@ namespace display_device { audio_context = boost::none; stop_token.requestStop(); }, - SchedulerOptions { .m_sleep_durations = { 2s } }); + SchedulerOptions {.m_sleep_durations = {2s}}); } private: @@ -102,20 +99,20 @@ namespace display_device { * @brief A reference to the audio context that will automatically extend the audio session. * @note It is auto-initialized here for convenience. */ - decltype(audio::get_audio_ctx_ref()) audio_ctx_ref { audio::get_audio_ctx_ref() }; + decltype(audio::get_audio_ctx_ref()) audio_ctx_ref {audio::get_audio_ctx_ref()}; /** * @brief Will be set to true if the capture was released, but we still have to keep the context around, because the device is not available. */ - bool released { false }; + bool released {false}; /** * @brief How many times to check if the audio sink is available before giving up. */ - int retry_counter { 15 }; + int retry_counter {15}; }; - RetryScheduler> context_scheduler { std::make_unique>(boost::none) }; + RetryScheduler> context_scheduler {std::make_unique>(boost::none)}; }; /** @@ -124,9 +121,8 @@ namespace display_device { * @param value String to be converted * @return Parsed unsigned integer. */ - unsigned int - stou(const std::string &value) { - unsigned long result { std::stoul(value) }; + unsigned int stou(const std::string &value) { + unsigned long result {std::stoul(value)}; if (result > std::numeric_limits::max()) { throw std::out_of_range("stou"); } @@ -151,10 +147,9 @@ namespace display_device { * } * @examples_end */ - bool - parse_resolution_string(const std::string &input, std::optional &output) { - const std::string trimmed_input { boost::algorithm::trim_copy(input) }; - const std::regex resolution_regex { R"(^(\d+)x(\d+)$)" }; + bool parse_resolution_string(const std::string &input, std::optional &output) { + const std::string trimmed_input {boost::algorithm::trim_copy(input)}; + const std::regex resolution_regex {R"(^(\d+)x(\d+)$)"}; if (std::smatch match; std::regex_match(trimmed_input, match, resolution_regex)) { try { @@ -163,16 +158,13 @@ namespace display_device { stou(match[2].str()) }; return true; - } - catch (const std::out_of_range &) { + } catch (const std::out_of_range &) { BOOST_LOG(error) << "Failed to parse resolution string " << trimmed_input << " (number out of range)."; - } - catch (const std::exception &err) { + } catch (const std::exception &err) { BOOST_LOG(error) << "Failed to parse resolution string " << trimmed_input << ":\n" << err.what(); } - } - else { + } else { if (trimmed_input.empty()) { output = std::nullopt; return true; @@ -203,16 +195,17 @@ namespace display_device { * } * @examples_end */ - bool - parse_refresh_rate_string(const std::string &input, std::optional &output, const bool allow_decimal_point = true) { - static const auto is_zero { [](const auto &character) { return character == '0'; } }; - const std::string trimmed_input { boost::algorithm::trim_copy(input) }; - const std::regex refresh_rate_regex { allow_decimal_point ? R"(^(\d+)(?:\.(\d+))?$)" : R"(^(\d+)$)" }; + bool parse_refresh_rate_string(const std::string &input, std::optional &output, const bool allow_decimal_point = true) { + static const auto is_zero {[](const auto &character) { + return character == '0'; + }}; + const std::string trimmed_input {boost::algorithm::trim_copy(input)}; + const std::regex refresh_rate_regex {allow_decimal_point ? R"(^(\d+)(?:\.(\d+))?$)" : R"(^(\d+)$)"}; if (std::smatch match; std::regex_match(trimmed_input, match, refresh_rate_regex)) { try { // Here we are trimming zeros from the string to possibly reduce out of bounds case - std::string trimmed_match_1 { boost::algorithm::trim_left_copy_if(match[1].str(), is_zero) }; + std::string trimmed_match_1 {boost::algorithm::trim_left_copy_if(match[1].str(), is_zero)}; if (trimmed_match_1.empty()) { trimmed_match_1 = "0"s; // Just in case ALL the string is full of zeros, we want to leave one } @@ -230,33 +223,29 @@ namespace display_device { // denominator = 1000 // We are essentially removing the decimal point here: 59.995 -> 59995 - const std::string numerator_str { trimmed_match_1 + trimmed_match_2 }; - const auto numerator { stou(numerator_str) }; + const std::string numerator_str {trimmed_match_1 + trimmed_match_2}; + const auto numerator {stou(numerator_str)}; // Here we are counting decimal places and calculating denominator: 10^decimal_places - const auto denominator { static_cast(std::pow(10, trimmed_match_2.size())) }; + const auto denominator {static_cast(std::pow(10, trimmed_match_2.size()))}; - output = Rational { numerator, denominator }; - } - else { + output = Rational {numerator, denominator}; + } else { // We do not have a decimal point, just a valid number. // For example: // 60: // numerator = 60 // denominator = 1 - output = Rational { stou(trimmed_match_1), 1 }; + output = Rational {stou(trimmed_match_1), 1}; } return true; - } - catch (const std::out_of_range &) { + } catch (const std::out_of_range &) { BOOST_LOG(error) << "Failed to parse refresh rate string " << trimmed_input << " (number out of range)."; - } - catch (const std::exception &err) { + } catch (const std::exception &err) { BOOST_LOG(error) << "Failed to parse refresh rate string " << trimmed_input << ":\n" << err.what(); } - } - else { + } else { if (trimmed_input.empty()) { output = std::nullopt; return true; @@ -279,8 +268,7 @@ namespace display_device { * const auto device_prep_option = parse_device_prep_option(video_config); * @examples_end */ - std::optional - parse_device_prep_option(const config::video_t &video_config) { + std::optional parse_device_prep_option(const config::video_t &video_config) { using enum config::video_t::dd_t::config_option_e; using enum SingleDisplayConfiguration::DevicePreparation; @@ -315,44 +303,42 @@ namespace display_device { * const bool success = parse_resolution_option(video_config, *launch_session, config); * @examples_end */ - bool - parse_resolution_option(const config::video_t &video_config, const rtsp_stream::launch_session_t &session, SingleDisplayConfiguration &config) { + bool parse_resolution_option(const config::video_t &video_config, const rtsp_stream::launch_session_t &session, SingleDisplayConfiguration &config) { using resolution_option_e = config::video_t::dd_t::resolution_option_e; switch (video_config.dd.resolution_option) { - case resolution_option_e::automatic: { - if (!session.enable_sops) { - BOOST_LOG(warning) << R"(Sunshine is configured to change resolution automatically, but the "Optimize game settings" is not set in the client! Resolution will not be changed.)"; - } - else if (session.width >= 0 && session.height >= 0) { - config.m_resolution = Resolution { - static_cast(session.width), - static_cast(session.height) - }; - } - else { - BOOST_LOG(error) << "Resolution provided by client session config is invalid: " << session.width << "x" << session.height; - return false; - } - break; - } - case resolution_option_e::manual: { - if (!session.enable_sops) { - BOOST_LOG(warning) << R"(Sunshine is configured to change resolution manually, but the "Optimize game settings" is not set in the client! Resolution will not be changed.)"; - } - else { - if (!parse_resolution_string(video_config.dd.manual_resolution, config.m_resolution)) { - BOOST_LOG(error) << "Failed to parse manual resolution string!"; + case resolution_option_e::automatic: + { + if (!session.enable_sops) { + BOOST_LOG(warning) << R"(Sunshine is configured to change resolution automatically, but the "Optimize game settings" is not set in the client! Resolution will not be changed.)"; + } else if (session.width >= 0 && session.height >= 0) { + config.m_resolution = Resolution { + static_cast(session.width), + static_cast(session.height) + }; + } else { + BOOST_LOG(error) << "Resolution provided by client session config is invalid: " << session.width << "x" << session.height; return false; } + break; + } + case resolution_option_e::manual: + { + if (!session.enable_sops) { + BOOST_LOG(warning) << R"(Sunshine is configured to change resolution manually, but the "Optimize game settings" is not set in the client! Resolution will not be changed.)"; + } else { + if (!parse_resolution_string(video_config.dd.manual_resolution, config.m_resolution)) { + BOOST_LOG(error) << "Failed to parse manual resolution string!"; + return false; + } - if (!config.m_resolution) { - BOOST_LOG(error) << "Manual resolution must be specified!"; - return false; + if (!config.m_resolution) { + BOOST_LOG(error) << "Manual resolution must be specified!"; + return false; + } } + break; } - break; - } case resolution_option_e::disabled: break; } @@ -375,33 +361,33 @@ namespace display_device { * const bool success = parse_refresh_rate_option(video_config, *launch_session, config); * @examples_end */ - bool - parse_refresh_rate_option(const config::video_t &video_config, const rtsp_stream::launch_session_t &session, SingleDisplayConfiguration &config) { + bool parse_refresh_rate_option(const config::video_t &video_config, const rtsp_stream::launch_session_t &session, SingleDisplayConfiguration &config) { using refresh_rate_option_e = config::video_t::dd_t::refresh_rate_option_e; switch (video_config.dd.refresh_rate_option) { - case refresh_rate_option_e::automatic: { - if (session.fps >= 0) { - config.m_refresh_rate = Rational { static_cast(session.fps), 1 }; - } - else { - BOOST_LOG(error) << "FPS value provided by client session config is invalid: " << session.fps; - return false; - } - break; - } - case refresh_rate_option_e::manual: { - if (!parse_refresh_rate_string(video_config.dd.manual_refresh_rate, config.m_refresh_rate)) { - BOOST_LOG(error) << "Failed to parse manual refresh rate string!"; - return false; + case refresh_rate_option_e::automatic: + { + if (session.fps >= 0) { + config.m_refresh_rate = Rational {static_cast(session.fps), 1}; + } else { + BOOST_LOG(error) << "FPS value provided by client session config is invalid: " << session.fps; + return false; + } + break; } + case refresh_rate_option_e::manual: + { + if (!parse_refresh_rate_string(video_config.dd.manual_refresh_rate, config.m_refresh_rate)) { + BOOST_LOG(error) << "Failed to parse manual refresh rate string!"; + return false; + } - if (!config.m_refresh_rate) { - BOOST_LOG(error) << "Manual refresh rate must be specified!"; - return false; + if (!config.m_refresh_rate) { + BOOST_LOG(error) << "Manual refresh rate must be specified!"; + return false; + } + break; } - break; - } case refresh_rate_option_e::disabled: break; } @@ -422,8 +408,7 @@ namespace display_device { * const auto hdr_option = parse_hdr_option(video_config, *launch_session); * @examples_end */ - std::optional - parse_hdr_option(const config::video_t &video_config, const rtsp_stream::launch_session_t &session) { + std::optional parse_hdr_option(const config::video_t &video_config, const rtsp_stream::launch_session_t &session) { using hdr_option_e = config::video_t::dd_t::hdr_option_e; switch (video_config.dd.hdr_option) { @@ -450,11 +435,10 @@ namespace display_device { * @param video_config User's video related configuration. * @returns Enum value if remapping can be performed, null optional if remapping shall be skipped. */ - std::optional - determine_remapping_type(const config::video_t &video_config) { + std::optional determine_remapping_type(const config::video_t &video_config) { using dd_t = config::video_t::dd_t; - const bool auto_resolution { video_config.dd.resolution_option == dd_t::resolution_option_e::automatic }; - const bool auto_refresh_rate { video_config.dd.refresh_rate_option == dd_t::refresh_rate_option_e::automatic }; + const bool auto_resolution {video_config.dd.resolution_option == dd_t::resolution_option_e::automatic}; + const bool auto_refresh_rate {video_config.dd.refresh_rate_option == dd_t::refresh_rate_option_e::automatic}; if (auto_resolution && auto_refresh_rate) { return remapping_type_e::mixed; @@ -486,8 +470,7 @@ namespace display_device { * @param type Remapping type to check. * @returns True if resolution is to be mapped, false otherwise. */ - bool - is_resolution_mapped(const remapping_type_e type) { + bool is_resolution_mapped(const remapping_type_e type) { return type == remapping_type_e::resolution_only || type == remapping_type_e::mixed; } @@ -496,8 +479,7 @@ namespace display_device { * @param type Remapping type to check. * @returns True if FPS is to be mapped, false otherwise. */ - bool - is_fps_mapped(const remapping_type_e type) { + bool is_fps_mapped(const remapping_type_e type) { return type == remapping_type_e::refresh_rate_only || type == remapping_type_e::mixed; } @@ -507,17 +489,16 @@ namespace display_device { * @param type Specify which entry fields should be parsed. * @returns Parsed structure or null optional if a necessary field could not be parsed. */ - std::optional - parse_remapping_entry(const config::video_t::dd_t::mode_remapping_entry_t &entry, const remapping_type_e type) { + std::optional parse_remapping_entry(const config::video_t::dd_t::mode_remapping_entry_t &entry, const remapping_type_e type) { parsed_remapping_entry_t result {}; if (is_resolution_mapped(type) && (!parse_resolution_string(entry.requested_resolution, result.requested_resolution) || - !parse_resolution_string(entry.final_resolution, result.final_resolution))) { + !parse_resolution_string(entry.final_resolution, result.final_resolution))) { return std::nullopt; } if (is_fps_mapped(type) && (!parse_refresh_rate_string(entry.requested_fps, result.requested_fps, false) || - !parse_refresh_rate_string(entry.final_refresh_rate, result.final_refresh_rate))) { + !parse_refresh_rate_string(entry.final_refresh_rate, result.final_refresh_rate))) { return std::nullopt; } @@ -539,14 +520,13 @@ namespace display_device { * const bool success = remap_display_mode_if_needed(video_config, *launch_session, config); * @examples_end */ - bool - remap_display_mode_if_needed(const config::video_t &video_config, const rtsp_stream::launch_session_t &session, SingleDisplayConfiguration &config) { - const auto remapping_type { determine_remapping_type(video_config) }; + bool remap_display_mode_if_needed(const config::video_t &video_config, const rtsp_stream::launch_session_t &session, SingleDisplayConfiguration &config) { + const auto remapping_type {determine_remapping_type(video_config)}; if (!remapping_type) { return true; } - const auto &remapping_list { [&]() { + const auto &remapping_list {[&]() { using enum remapping_type_e; switch (*remapping_type) { @@ -558,7 +538,7 @@ namespace display_device { default: return video_config.dd.mode_remapping.mixed; } - }() }; + }()}; if (remapping_list.empty()) { BOOST_LOG(debug) << "No values are available for display mode remapping."; @@ -566,9 +546,9 @@ namespace display_device { } BOOST_LOG(debug) << "Trying to remap display modes..."; - const auto entry_to_string { [type = *remapping_type](const config::video_t::dd_t::mode_remapping_entry_t &entry) { - const bool mapping_resolution { is_resolution_mapped(type) }; - const bool mapping_fps { is_fps_mapped(type) }; + const auto entry_to_string {[type = *remapping_type](const config::video_t::dd_t::mode_remapping_entry_t &entry) { + const bool mapping_resolution {is_resolution_mapped(type)}; + const bool mapping_fps {is_fps_mapped(type)}; // clang-format off return (mapping_resolution ? " - requested resolution: "s + entry.requested_resolution + "\n" : "") + @@ -576,10 +556,10 @@ namespace display_device { (mapping_resolution ? " - final resolution: "s + entry.final_resolution + "\n" : "") + (mapping_fps ? " - final refresh rate: "s + entry.final_refresh_rate : ""); // clang-format on - } }; + }}; for (const auto &entry : remapping_list) { - const auto parsed_entry { parse_remapping_entry(entry, *remapping_type) }; + const auto parsed_entry {parse_remapping_entry(entry, *remapping_type)}; if (!parsed_entry) { BOOST_LOG(error) << "Failed to parse remapping entry from:\n" << entry_to_string(entry); @@ -632,16 +612,18 @@ namespace display_device { * @param video_config User's video related configuration. * @return An interface or nullptr if the OS does not support the interface. */ - std::unique_ptr - make_settings_manager([[maybe_unused]] const std::filesystem::path &persistence_filepath, [[maybe_unused]] const config::video_t &video_config) { + std::unique_ptr make_settings_manager([[maybe_unused]] const std::filesystem::path &persistence_filepath, [[maybe_unused]] const config::video_t &video_config) { #ifdef _WIN32 return std::make_unique( std::make_shared(std::make_shared()), std::make_shared(), std::make_unique( - std::make_shared(persistence_filepath)), + std::make_shared(persistence_filepath) + ), WinWorkarounds { - .m_hdr_blank_delay = video_config.dd.wa.hdr_toggle ? std::make_optional(500ms) : std::nullopt }); + .m_hdr_blank_delay = video_config.dd.wa.hdr_toggle ? std::make_optional(500ms) : std::nullopt + } + ); #else return nullptr; #endif @@ -660,17 +642,16 @@ namespace display_device { * @brief Reverts the configuration based on the provided option. * @note This is function does not lock mutex. */ - void - revert_configuration_unlocked(const revert_option_e option) { + void revert_configuration_unlocked(const revert_option_e option) { if (!DD_DATA.sm_instance) { // Platform is not supported, nothing to do. return; } // Note: by default the executor function is immediately executed in the calling thread. With delay, we want to avoid that. - SchedulerOptions scheduler_option { .m_sleep_durations = { DEFAULT_RETRY_INTERVAL } }; + SchedulerOptions scheduler_option {.m_sleep_durations = {DEFAULT_RETRY_INTERVAL}}; if (option == revert_option_e::try_indefinitely_with_delay && DD_DATA.config_revert_delay > std::chrono::milliseconds::zero()) { - scheduler_option.m_sleep_durations = { DD_DATA.config_revert_delay, DEFAULT_RETRY_INTERVAL }; + scheduler_option.m_sleep_durations = {DD_DATA.config_revert_delay, DEFAULT_RETRY_INTERVAL}; scheduler_option.m_execution = SchedulerOptions::Execution::ScheduledOnly; } @@ -681,17 +662,21 @@ namespace display_device { return; } - auto available_devices { [&settings_iface]() { - const auto devices { settings_iface.enumAvailableDevices() }; + auto available_devices {[&settings_iface]() { + const auto devices {settings_iface.enumAvailableDevices()}; std::set parsed_devices; std::transform( - std::begin(devices), std::end(devices), + std::begin(devices), + std::end(devices), std::inserter(parsed_devices, std::end(parsed_devices)), - [](const auto &device) { return device.m_device_id + " - " + device.m_friendly_name; }); + [](const auto &device) { + return device.m_device_id + " - " + device.m_friendly_name; + } + ); return parsed_devices; - }() }; + }()}; if (available_devices == tried_out_devices) { BOOST_LOG(debug) << "Skipping reverting configuration, because no newly added/removed devices were detected since last check. Currently available devices:\n" << toJson(available_devices); @@ -699,11 +684,10 @@ namespace display_device { } using enum SettingsManagerInterface::RevertResult; - if (const auto result { settings_iface.revertSettings() }; result == Ok) { + if (const auto result {settings_iface.revertSettings()}; result == Ok) { stop_token.requestStop(); return; - } - else if (result == ApiTemporarilyUnavailable) { + } else if (result == ApiTemporarilyUnavailable) { // Do nothing and retry next time return; } @@ -713,13 +697,12 @@ namespace display_device { << toJson(available_devices); tried_out_devices.swap(available_devices); }, - scheduler_option); + scheduler_option); } } // namespace - std::unique_ptr - init(const std::filesystem::path &persistence_filepath, const config::video_t &video_config) { - std::lock_guard lock { DD_DATA.mutex }; + std::unique_ptr init(const std::filesystem::path &persistence_filepath, const config::video_t &video_config) { + std::lock_guard lock {DD_DATA.mutex}; // We can support re-init without any issues, however we should make sure to clean up first! revert_configuration_unlocked(revert_option_e::try_once); DD_DATA.config_revert_delay = video_config.dd.config_revert_delay; @@ -727,10 +710,12 @@ namespace display_device { // If we fail to create settings manager, this means platform is not supported, and // we will need to provided error-free pass-trough in other methods - if (auto settings_manager { make_settings_manager(persistence_filepath, video_config) }) { + if (auto settings_manager {make_settings_manager(persistence_filepath, video_config)}) { DD_DATA.sm_instance = std::make_unique>(std::move(settings_manager)); - const auto available_devices { DD_DATA.sm_instance->execute([](auto &settings_iface) { return settings_iface.enumAvailableDevices(); }) }; + const auto available_devices {DD_DATA.sm_instance->execute([](auto &settings_iface) { + return settings_iface.enumAvailableDevices(); + })}; BOOST_LOG(info) << "Currently available display devices:\n" << toJson(available_devices); @@ -742,44 +727,44 @@ namespace display_device { class deinit_t: public platf::deinit_t { public: ~deinit_t() override { - std::lock_guard lock { DD_DATA.mutex }; + std::lock_guard lock {DD_DATA.mutex}; try { // This may throw if used incorrectly. At the moment this will not happen, however // in case some unforeseen changes are made that could raise an exception, // we definitely don't want this to happen in destructor. Especially in the // deinit_t where the outcome does not really matter. revert_configuration_unlocked(revert_option_e::try_once); - } - catch (std::exception &err) { + } catch (std::exception &err) { BOOST_LOG(fatal) << err.what(); } DD_DATA.sm_instance = nullptr; } }; + return std::make_unique(); } - std::string - map_output_name(const std::string &output_name) { - std::lock_guard lock { DD_DATA.mutex }; + std::string map_output_name(const std::string &output_name) { + std::lock_guard lock {DD_DATA.mutex}; if (!DD_DATA.sm_instance) { // Fallback to giving back the output name if the platform is not supported. return output_name; } - return DD_DATA.sm_instance->execute([&output_name](auto &settings_iface) { return settings_iface.getDisplayName(output_name); }); + return DD_DATA.sm_instance->execute([&output_name](auto &settings_iface) { + return settings_iface.getDisplayName(output_name); + }); } - void - configure_display(const config::video_t &video_config, const rtsp_stream::launch_session_t &session) { - const auto result { parse_configuration(video_config, session) }; - if (const auto *parsed_config { std::get_if(&result) }; parsed_config) { + void configure_display(const config::video_t &video_config, const rtsp_stream::launch_session_t &session) { + const auto result {parse_configuration(video_config, session)}; + if (const auto *parsed_config {std::get_if(&result)}; parsed_config) { configure_display(*parsed_config); return; } - if (const auto *disabled { std::get_if(&result) }; disabled) { + if (const auto *disabled {std::get_if(&result)}; disabled) { revert_configuration(); return; } @@ -788,9 +773,8 @@ namespace display_device { // want to revert active configuration in case we have any } - void - configure_display(const SingleDisplayConfiguration &config) { - std::lock_guard lock { DD_DATA.mutex }; + void configure_display(const SingleDisplayConfiguration &config) { + std::lock_guard lock {DD_DATA.mutex}; if (!DD_DATA.sm_instance) { // Platform is not supported, nothing to do. return; @@ -803,18 +787,16 @@ namespace display_device { stop_token.requestStop(); } }, - { .m_sleep_durations = { DEFAULT_RETRY_INTERVAL } }); + {.m_sleep_durations = {DEFAULT_RETRY_INTERVAL}}); } - void - revert_configuration() { - std::lock_guard lock { DD_DATA.mutex }; + void revert_configuration() { + std::lock_guard lock {DD_DATA.mutex}; revert_configuration_unlocked(revert_option_e::try_indefinitely_with_delay); } - bool - reset_persistence() { - std::lock_guard lock { DD_DATA.mutex }; + bool reset_persistence() { + std::lock_guard lock {DD_DATA.mutex}; if (!DD_DATA.sm_instance) { // Platform is not supported, assume success. return true; @@ -828,9 +810,8 @@ namespace display_device { }); } - std::variant - parse_configuration(const config::video_t &video_config, const rtsp_stream::launch_session_t &session) { - const auto device_prep { parse_device_prep_option(video_config) }; + std::variant parse_configuration(const config::video_t &video_config, const rtsp_stream::launch_session_t &session) { + const auto device_prep {parse_device_prep_option(video_config)}; if (!device_prep) { return configuration_disabled_tag_t {}; } diff --git a/src/display_device.h b/src/display_device.h index e17c408f..3ba5c41b 100644 --- a/src/display_device.h +++ b/src/display_device.h @@ -4,18 +4,22 @@ */ #pragma once -// lib includes -#include +// standard includes #include #include +// lib includes +#include + // forward declarations namespace platf { class deinit_t; } + namespace config { struct video_t; } + namespace rtsp_stream { struct launch_session_t; } @@ -32,8 +36,7 @@ namespace display_device { * const auto init_guard { init("/my/persitence/file.state", video_config) }; * @examples_end */ - [[nodiscard]] std::unique_ptr - init(const std::filesystem::path &persistence_filepath, const config::video_t &video_config); + [[nodiscard]] std::unique_ptr init(const std::filesystem::path &persistence_filepath, const config::video_t &video_config); /** * @brief Map the output name to a specific display. @@ -45,8 +48,7 @@ namespace display_device { * const auto mapped_name_custom { map_output_name("{some-device-id}") }; * @examples_end */ - [[nodiscard]] std::string - map_output_name(const std::string &output_name); + [[nodiscard]] std::string map_output_name(const std::string &output_name); /** * @brief Configure the display device based on the user configuration and the session information. @@ -62,8 +64,7 @@ namespace display_device { * configure_display(video_config, *launch_session); * @examples_end */ - void - configure_display(const config::video_t &video_config, const rtsp_stream::launch_session_t &session); + void configure_display(const config::video_t &video_config, const rtsp_stream::launch_session_t &session); /** * @brief Configure the display device using the provided configuration. @@ -83,8 +84,7 @@ namespace display_device { * configure_display(valid_config); * @examples_end */ - void - configure_display(const SingleDisplayConfiguration &config); + void configure_display(const SingleDisplayConfiguration &config); /** * @brief Revert the display configuration and restore the previous state. @@ -96,8 +96,7 @@ namespace display_device { * revert_configuration(); * @examples_end */ - void - revert_configuration(); + void revert_configuration(); /** * @brief Reset the persistence and currently held initial display state. @@ -119,8 +118,7 @@ namespace display_device { * const auto result = reset_persistence(); * @examples_end */ - [[nodiscard]] bool - reset_persistence(); + [[nodiscard]] bool reset_persistence(); /** * @brief A tag structure indicating that configuration parsing has failed. @@ -150,6 +148,5 @@ namespace display_device { * } * @examples_end */ - [[nodiscard]] std::variant - parse_configuration(const config::video_t &video_config, const rtsp_stream::launch_session_t &session); + [[nodiscard]] std::variant parse_configuration(const config::video_t &video_config, const rtsp_stream::launch_session_t &session); } // namespace display_device diff --git a/src/entry_handler.cpp b/src/entry_handler.cpp index 2f755e9a..ed978dcd 100644 --- a/src/entry_handler.cpp +++ b/src/entry_handler.cpp @@ -26,21 +26,18 @@ extern "C" { using namespace std::literals; -void -launch_ui() { +void launch_ui() { std::string url = "https://localhost:" + std::to_string(net::map_port(confighttp::PORT_HTTPS)); platf::open_url(url); } -void -launch_ui_with_path(std::string path) { +void launch_ui_with_path(std::string path) { std::string url = "https://localhost:" + std::to_string(net::map_port(confighttp::PORT_HTTPS)) + path; platf::open_url(url); } namespace args { - int - creds(const char *name, int argc, char *argv[]) { + int creds(const char *name, int argc, char *argv[]) { if (argc < 2 || argv[0] == "help"sv || argv[1] == "help"sv) { help(name); } @@ -50,21 +47,18 @@ namespace args { return 0; } - int - help(const char *name) { + int help(const char *name) { logging::print_help(name); return 0; } - int - version() { + int version() { // version was already logged at startup return 0; } #ifdef _WIN32 - int - restore_nvprefs_undo() { + int restore_nvprefs_undo() { if (nvprefs_instance.load()) { nvprefs_instance.restore_from_and_delete_undo_file_if_exists(); nvprefs_instance.unload(); @@ -78,8 +72,7 @@ namespace lifetime { char **argv; std::atomic_int desired_exit_code; - void - exit_sunshine(int exit_code, bool async) { + void exit_sunshine(int exit_code, bool async) { // Store the exit code of the first exit_sunshine() call int zero = 0; desired_exit_code.compare_exchange_strong(zero, exit_code); @@ -94,8 +87,7 @@ namespace lifetime { } } - void - debug_trap() { + void debug_trap() { #ifdef _WIN32 DebugBreak(); #else @@ -103,22 +95,19 @@ namespace lifetime { #endif } - char ** - get_argv() { + char **get_argv() { return argv; } } // namespace lifetime -void -log_publisher_data() { +void log_publisher_data() { BOOST_LOG(info) << "Package Publisher: "sv << SUNSHINE_PUBLISHER_NAME; BOOST_LOG(info) << "Publisher Website: "sv << SUNSHINE_PUBLISHER_WEBSITE; BOOST_LOG(info) << "Get support: "sv << SUNSHINE_PUBLISHER_ISSUE_URL; } #ifdef _WIN32 -bool -is_gamestream_enabled() { +bool is_gamestream_enabled() { DWORD enabled; DWORD size = sizeof(enabled); return RegGetValueW( @@ -128,7 +117,8 @@ is_gamestream_enabled() { RRF_RT_REG_DWORD, nullptr, &enabled, - &size) == ERROR_SUCCESS && + &size + ) == ERROR_SUCCESS && enabled != 0; } @@ -168,8 +158,7 @@ namespace service_ctrl { /** * @brief Asynchronously starts the Sunshine service. */ - bool - start_service() { + bool start_service() { if (!service_handle) { return false; } @@ -189,8 +178,7 @@ namespace service_ctrl { * @brief Query the service status. * @param status The SERVICE_STATUS struct to populate. */ - bool - query_service_status(SERVICE_STATUS &status) { + bool query_service_status(SERVICE_STATUS &status) { if (!service_handle) { return false; } @@ -209,9 +197,8 @@ namespace service_ctrl { SC_HANDLE service_handle = NULL; }; - bool - is_service_running() { - service_controller sc { SERVICE_QUERY_STATUS }; + bool is_service_running() { + service_controller sc {SERVICE_QUERY_STATUS}; SERVICE_STATUS status; if (!sc.query_service_status(status)) { @@ -221,9 +208,8 @@ namespace service_ctrl { return status.dwCurrentState == SERVICE_RUNNING; } - bool - start_service() { - service_controller sc { SERVICE_QUERY_STATUS | SERVICE_START }; + bool start_service() { + service_controller sc {SERVICE_QUERY_STATUS | SERVICE_START}; std::cout << "Starting Sunshine..."sv; @@ -247,8 +233,7 @@ namespace service_ctrl { return true; } - bool - wait_for_ui_ready() { + bool wait_for_ui_ready() { std::cout << "Waiting for Web UI to be ready..."; // Wait up to 30 seconds for the web UI to start diff --git a/src/entry_handler.h b/src/entry_handler.h index a2c9735a..a83fed29 100644 --- a/src/entry_handler.h +++ b/src/entry_handler.h @@ -18,8 +18,7 @@ * launch_ui(); * @examples_end */ -void -launch_ui(); +void launch_ui(); /** * @brief Launch the Web UI at a specific endpoint. @@ -27,8 +26,7 @@ launch_ui(); * launch_ui_with_path("/pin"); * @examples_end */ -void -launch_ui_with_path(std::string path); +void launch_ui_with_path(std::string path); /** * @brief Functions for handling command line arguments. @@ -43,8 +41,7 @@ namespace args { * creds("sunshine", 2, {"new_username", "new_password"}); * @examples_end */ - int - creds(const char *name, int argc, char *argv[]); + int creds(const char *name, int argc, char *argv[]); /** * @brief Print help to stdout, then exit. @@ -53,8 +50,7 @@ namespace args { * help("sunshine"); * @examples_end */ - int - help(const char *name); + int help(const char *name); /** * @brief Print the version to stdout, then exit. @@ -62,8 +58,7 @@ namespace args { * version(); * @examples_end */ - int - version(); + int version(); #ifdef _WIN32 /** @@ -75,8 +70,7 @@ namespace args { * restore_nvprefs_undo(); * @examples_end */ - int - restore_nvprefs_undo(); + int restore_nvprefs_undo(); #endif } // namespace args @@ -92,35 +86,30 @@ namespace lifetime { * @param exit_code The exit code to return from main(). * @param async Specifies whether our termination will be non-blocking. */ - void - exit_sunshine(int exit_code, bool async); + void exit_sunshine(int exit_code, bool async); /** * @brief Breaks into the debugger or terminates Sunshine if no debugger is attached. */ - void - debug_trap(); + void debug_trap(); /** * @brief Get the argv array passed to main(). */ - char ** - get_argv(); + char **get_argv(); } // namespace lifetime /** * @brief Log the publisher metadata provided from CMake. */ -void -log_publisher_data(); +void log_publisher_data(); #ifdef _WIN32 /** * @brief Check if NVIDIA's GameStream software is running. * @return `true` if GameStream is enabled, `false` otherwise. */ -bool -is_gamestream_enabled(); +bool is_gamestream_enabled(); /** * @brief Namespace for controlling the Sunshine service model on Windows. @@ -132,8 +121,7 @@ namespace service_ctrl { * is_service_running(); * @examples_end */ - bool - is_service_running(); + bool is_service_running(); /** * @brief Start the service and wait for startup to complete. @@ -141,8 +129,7 @@ namespace service_ctrl { * start_service(); * @examples_end */ - bool - start_service(); + bool start_service(); /** * @brief Wait for the UI to be ready after Sunshine startup. @@ -150,7 +137,6 @@ namespace service_ctrl { * wait_for_ui_ready(); * @examples_end */ - bool - wait_for_ui_ready(); + bool wait_for_ui_ready(); } // namespace service_ctrl #endif diff --git a/src/file_handler.cpp b/src/file_handler.cpp index b5c9638a..f7b4ee72 100644 --- a/src/file_handler.cpp +++ b/src/file_handler.cpp @@ -12,8 +12,7 @@ #include "logging.h" namespace file_handler { - std::string - get_parent_directory(const std::string &path) { + std::string get_parent_directory(const std::string &path) { // remove any trailing path separators std::string trimmed_path = path; while (!trimmed_path.empty() && trimmed_path.back() == '/') { @@ -24,8 +23,7 @@ namespace file_handler { return p.parent_path().string(); } - bool - make_directory(const std::string &path) { + bool make_directory(const std::string &path) { // first, check if the directory already exists if (std::filesystem::exists(path)) { return true; @@ -34,19 +32,17 @@ namespace file_handler { return std::filesystem::create_directories(path); } - std::string - read_file(const char *path) { + std::string read_file(const char *path) { if (!std::filesystem::exists(path)) { BOOST_LOG(debug) << "Missing file: " << path; return {}; } std::ifstream in(path); - return std::string { (std::istreambuf_iterator(in)), std::istreambuf_iterator() }; + return std::string {(std::istreambuf_iterator(in)), std::istreambuf_iterator()}; } - int - write_file(const char *path, const std::string_view &contents) { + int write_file(const char *path, const std::string_view &contents) { std::ofstream out(path); if (!out.is_open()) { diff --git a/src/file_handler.h b/src/file_handler.h index d669f17b..5029fb08 100644 --- a/src/file_handler.h +++ b/src/file_handler.h @@ -4,6 +4,7 @@ */ #pragma once +// standard includes #include /** @@ -18,8 +19,7 @@ namespace file_handler { * std::string parent_dir = get_parent_directory("path/to/file"); * @examples_end */ - std::string - get_parent_directory(const std::string &path); + std::string get_parent_directory(const std::string &path); /** * @brief Make a directory. @@ -29,8 +29,7 @@ namespace file_handler { * bool dir_created = make_directory("path/to/directory"); * @examples_end */ - bool - make_directory(const std::string &path); + bool make_directory(const std::string &path); /** * @brief Read a file to string. @@ -40,8 +39,7 @@ namespace file_handler { * std::string contents = read_file("path/to/file"); * @examples_end */ - std::string - read_file(const char *path); + std::string read_file(const char *path); /** * @brief Writes a file. @@ -52,6 +50,5 @@ namespace file_handler { * int write_status = write_file("path/to/file", "file contents"); * @examples_end */ - int - write_file(const char *path, const std::string_view &contents); + int write_file(const char *path, const std::string_view &contents); } // namespace file_handler diff --git a/src/globals.cpp b/src/globals.cpp index fa9aa1d7..60daf398 100644 --- a/src/globals.cpp +++ b/src/globals.cpp @@ -2,6 +2,7 @@ * @file globals.cpp * @brief Definitions for globally accessible variables and functions. */ +// local includes #include "globals.h" safe::mail_t mail::man; diff --git a/src/globals.h b/src/globals.h index 79c49eec..1617b7c6 100644 --- a/src/globals.h +++ b/src/globals.h @@ -4,6 +4,7 @@ */ #pragma once +// local includes #include "entry_handler.h" #include "thread_pool.h" @@ -31,9 +32,9 @@ extern nvprefs::nvprefs_interface nvprefs_instance; * @brief Handles process-wide communication. */ namespace mail { -#define MAIL(x) \ +#define MAIL(x) \ constexpr auto x = std::string_view { \ - #x \ + #x \ } /** diff --git a/src/httpcommon.cpp b/src/httpcommon.cpp index 419ca6dd..b4214ed8 100644 --- a/src/httpcommon.cpp +++ b/src/httpcommon.cpp @@ -4,22 +4,21 @@ */ #define BOOST_BIND_GLOBAL_PLACEHOLDERS -#include "process.h" - +// standard includes #include #include +// lib includes +#include +#include #include #include #include - -#include - +#include #include #include -#include -#include +// local includes #include "config.h" #include "crypto.h" #include "file_handler.h" @@ -28,6 +27,7 @@ #include "network.h" #include "nvhttp.h" #include "platform/common.h" +#include "process.h" #include "rtsp.h" #include "utility.h" #include "uuid.h" @@ -37,16 +37,13 @@ namespace http { namespace fs = std::filesystem; namespace pt = boost::property_tree; - int - reload_user_creds(const std::string &file); - bool - user_creds_exist(const std::string &file); + int reload_user_creds(const std::string &file); + bool user_creds_exist(const std::string &file); std::string unique_id; net::net_e origin_web_ui_allowed; - int - init() { + int init() { bool clean_slate = config::sunshine.flags[config::flag::FRESH_STATE]; origin_web_ui_allowed = net::from_enum_string(config::nvhttp.origin_web_ui_allowed); @@ -63,23 +60,22 @@ namespace http { } } if (user_creds_exist(config::sunshine.credentials_file)) { - if (reload_user_creds(config::sunshine.credentials_file)) return -1; - } - else { + if (reload_user_creds(config::sunshine.credentials_file)) { + return -1; + } + } else { BOOST_LOG(info) << "Open the Web UI to set your new username and password and getting started"; } return 0; } - int - save_user_creds(const std::string &file, const std::string &username, const std::string &password, bool run_our_mouth) { + int save_user_creds(const std::string &file, const std::string &username, const std::string &password, bool run_our_mouth) { pt::ptree outputTree; if (fs::exists(file)) { try { pt::read_json(file, outputTree); - } - catch (std::exception &e) { + } catch (std::exception &e) { BOOST_LOG(error) << "Couldn't read user credentials: "sv << e.what(); return -1; } @@ -91,8 +87,7 @@ namespace http { outputTree.put("password", util::hex(crypto::hash(password + salt)).to_string()); try { pt::write_json(file, outputTree); - } - catch (std::exception &e) { + } catch (std::exception &e) { BOOST_LOG(error) << "error writing to the credentials file, perhaps try this again as an administrator? Details: "sv << e.what(); return -1; } @@ -101,8 +96,7 @@ namespace http { return 0; } - bool - user_creds_exist(const std::string &file) { + bool user_creds_exist(const std::string &file) { if (!fs::exists(file)) { return false; } @@ -113,32 +107,28 @@ namespace http { return inputTree.find("username") != inputTree.not_found() && inputTree.find("password") != inputTree.not_found() && inputTree.find("salt") != inputTree.not_found(); - } - catch (std::exception &e) { + } catch (std::exception &e) { BOOST_LOG(error) << "validating user credentials: "sv << e.what(); } return false; } - int - reload_user_creds(const std::string &file) { + int reload_user_creds(const std::string &file) { pt::ptree inputTree; try { pt::read_json(file, inputTree); config::sunshine.username = inputTree.get("username"); config::sunshine.password = inputTree.get("password"); config::sunshine.salt = inputTree.get("salt"); - } - catch (std::exception &e) { + } catch (std::exception &e) { BOOST_LOG(error) << "loading user credentials: "sv << e.what(); return -1; } return 0; } - int - create_creds(const std::string &pkey, const std::string &cert) { + int create_creds(const std::string &pkey, const std::string &cert) { fs::path pkey_path = pkey; fs::path cert_path = cert; @@ -172,18 +162,14 @@ namespace http { return -1; } - fs::permissions(pkey_path, - fs::perms::owner_read | fs::perms::owner_write, - fs::perm_options::replace, err_code); + fs::permissions(pkey_path, fs::perms::owner_read | fs::perms::owner_write, fs::perm_options::replace, err_code); if (err_code) { BOOST_LOG(error) << "Couldn't change permissions of ["sv << config::nvhttp.pkey << "] :"sv << err_code.message(); return -1; } - fs::permissions(cert_path, - fs::perms::owner_read | fs::perms::group_read | fs::perms::others_read | fs::perms::owner_write, - fs::perm_options::replace, err_code); + fs::permissions(cert_path, 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) { BOOST_LOG(error) << "Couldn't change permissions of ["sv << config::nvhttp.cert << "] :"sv << err_code.message(); @@ -193,15 +179,13 @@ namespace http { return 0; } - bool - download_file(const std::string &url, const std::string &file) { + bool download_file(const std::string &url, const std::string &file) { CURL *curl = curl_easy_init(); if (curl) { // sonar complains about weak ssl and tls versions // ideally, the setopts should go after the early returns; however sonar cannot detect the fix curl_easy_setopt(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2); - } - else { + } else { BOOST_LOG(error) << "Couldn't create CURL instance"; return false; } @@ -234,8 +218,7 @@ namespace http { return result == CURLE_OK; } - std::string - url_escape(const std::string &url) { + std::string url_escape(const std::string &url) { CURL *curl = curl_easy_init(); char *string = curl_easy_escape(curl, url.c_str(), url.length()); std::string result(string); @@ -244,8 +227,7 @@ namespace http { return result; } - std::string - url_get_host(const std::string &url) { + std::string url_get_host(const std::string &url) { CURLU *curlu = curl_url(); curl_url_set(curlu, CURLUPART_URL, url.c_str(), url.length()); char *host; diff --git a/src/httpcommon.h b/src/httpcommon.h index 324ada03..71fe8072 100644 --- a/src/httpcommon.h +++ b/src/httpcommon.h @@ -4,30 +4,25 @@ */ #pragma once +// local includes #include "network.h" #include "thread_safe.h" namespace http { - int - init(); - int - create_creds(const std::string &pkey, const std::string &cert); - int - save_user_creds( + int init(); + int create_creds(const std::string &pkey, const std::string &cert); + int save_user_creds( const std::string &file, const std::string &username, const std::string &password, - bool run_our_mouth = false); + bool run_our_mouth = false + ); - int - reload_user_creds(const std::string &file); - bool - download_file(const std::string &url, const std::string &file); - std::string - url_escape(const std::string &url); - std::string - url_get_host(const std::string &url); + int reload_user_creds(const std::string &file); + bool download_file(const std::string &url, const std::string &file); + std::string url_escape(const std::string &url); + std::string url_get_host(const std::string &url); extern std::string unique_id; extern net::net_e origin_web_ui_allowed; diff --git a/src/input.cpp b/src/input.cpp index bf275467..768ddece 100644 --- a/src/input.cpp +++ b/src/input.cpp @@ -2,13 +2,13 @@ * @file src/input.cpp * @brief Definitions for gamepad, keyboard, and mouse input handling. */ -// define uint32_t for #include extern "C" { #include #include } +// standard includes #include #include #include @@ -16,6 +16,10 @@ extern "C" { #include #include +// lib includes +#include + +// local includes #include "config.h" #include "globals.h" #include "input.h" @@ -24,14 +28,13 @@ extern "C" { #include "thread_pool.h" #include "utility.h" -#include - // Win32 WHEEL_DELTA constant #ifndef WHEEL_DELTA #define WHEEL_DELTA 120 #endif using namespace std::literals; + namespace input { constexpr auto MAX_GAMEPADS = std::min((std::size_t) platf::MAX_GAMEPADS, sizeof(std::int16_t) * 8); @@ -54,9 +57,8 @@ namespace input { UP ///< Button is up }; - template - int - alloc_id(std::bitset &gamepad_mask) { + template + int alloc_id(std::bitset &gamepad_mask) { for (int x = 0; x < gamepad_mask.size(); ++x) { if (!gamepad_mask[x]) { gamepad_mask[x] = true; @@ -67,23 +69,22 @@ namespace input { return -1; } - template - void - free_id(std::bitset &gamepad_mask, int id) { + template + void free_id(std::bitset &gamepad_mask, int id) { gamepad_mask[id] = false; } typedef uint32_t key_press_id_t; - key_press_id_t - make_kpid(uint16_t vk, uint8_t flags) { + + key_press_id_t make_kpid(uint16_t vk, uint8_t flags) { return (key_press_id_t) vk << 8 | flags; } - uint16_t - vk_from_kpid(key_press_id_t kpid) { + + uint16_t vk_from_kpid(key_press_id_t kpid) { return kpid >> 8; } - uint8_t - flags_from_kpid(key_press_id_t kpid) { + + uint8_t flags_from_kpid(key_press_id_t kpid) { return kpid & 0xFF; } @@ -92,8 +93,7 @@ namespace input { * @param f Netfloat value. * @return The native endianness float value. */ - float - from_netfloat(netfloat f) { + float from_netfloat(netfloat f) { return boost::endian::endian_load(f); } @@ -104,8 +104,7 @@ namespace input { * @param max The maximum value for clamping. * @return Clamped native endianess float value. */ - float - from_clamped_netfloat(netfloat f, float min, float max) { + float from_clamped_netfloat(netfloat f, float min, float max) { return std::clamp(from_netfloat(f), min, max); } @@ -116,16 +115,21 @@ namespace input { static platf::input_t platf_input; static std::bitset gamepadMask {}; - void - free_gamepad(platf::input_t &platf_input, int id) { + void free_gamepad(platf::input_t &platf_input, int id) { platf::gamepad_update(platf_input, id, platf::gamepad_state_t {}); platf::free_gamepad(platf_input, id); free_id(gamepadMask, id); } + struct gamepad_t { gamepad_t(): - gamepad_state {}, back_timeout_id {}, id { -1 }, back_button_state { button_state_e::NONE } {} + 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]() { @@ -158,16 +162,18 @@ namespace input { input_t( safe::mail_raw_t::event_t touch_port_event, - platf::feedback_queue_t feedback_queue): + platf::feedback_queue_t feedback_queue + ): shortcutFlags {}, gamepads(MAX_GAMEPADS), - client_context { platf::allocate_client_input_context(platf_input) }, - touch_port_event { std::move(touch_port_event) }, - feedback_queue { std::move(feedback_queue) }, + client_context {platf::allocate_client_input_context(platf_input)}, + touch_port_event {std::move(touch_port_event)}, + feedback_queue {std::move(feedback_queue)}, mouse_left_button_timeout {}, - touch_port { { 0, 0, 0, 0 }, 0, 0, 1.0f }, + touch_port {{0, 0, 0, 0}, 0, 0, 1.0f}, accumulated_vscroll_delta {}, - accumulated_hscroll_delta {} {} + accumulated_hscroll_delta {} { + } // Keep track of alt+ctrl+shift key combo int shortcutFlags; @@ -194,8 +200,7 @@ namespace input { * @param keyCode The VKEY code * @return 0 if no shortcut applied, > 0 if shortcut applied. */ - inline int - apply_shortcut(short keyCode) { + inline int apply_shortcut(short keyCode) { constexpr auto VK_F1 = 0x70; constexpr auto VK_F13 = 0x7C; @@ -215,8 +220,7 @@ namespace input { return 0; } - void - print(PNV_REL_MOUSE_MOVE_PACKET packet) { + void print(PNV_REL_MOUSE_MOVE_PACKET packet) { BOOST_LOG(debug) << "--begin relative mouse move packet--"sv << std::endl << "deltaX ["sv << util::endian::big(packet->deltaX) << ']' << std::endl @@ -224,8 +228,7 @@ namespace input { << "--end relative mouse move packet--"sv; } - void - print(PNV_ABS_MOUSE_MOVE_PACKET packet) { + void print(PNV_ABS_MOUSE_MOVE_PACKET packet) { BOOST_LOG(debug) << "--begin absolute mouse move packet--"sv << std::endl << "x ["sv << util::endian::big(packet->x) << ']' << std::endl @@ -235,8 +238,7 @@ namespace input { << "--end absolute mouse move packet--"sv; } - void - print(PNV_MOUSE_BUTTON_PACKET packet) { + void print(PNV_MOUSE_BUTTON_PACKET packet) { BOOST_LOG(debug) << "--begin mouse button packet--"sv << std::endl << "action ["sv << util::hex(packet->header.magic).to_string_view() << ']' << std::endl @@ -244,24 +246,21 @@ namespace input { << "--end mouse button packet--"sv; } - void - print(PNV_SCROLL_PACKET packet) { + void print(PNV_SCROLL_PACKET packet) { BOOST_LOG(debug) << "--begin mouse scroll packet--"sv << std::endl << "scrollAmt1 ["sv << util::endian::big(packet->scrollAmt1) << ']' << std::endl << "--end mouse scroll packet--"sv; } - void - print(PSS_HSCROLL_PACKET packet) { + void print(PSS_HSCROLL_PACKET packet) { BOOST_LOG(debug) << "--begin mouse hscroll packet--"sv << std::endl << "scrollAmount ["sv << util::endian::big(packet->scrollAmount) << ']' << std::endl << "--end mouse hscroll packet--"sv; } - void - print(PNV_KEYBOARD_PACKET packet) { + void print(PNV_KEYBOARD_PACKET packet) { BOOST_LOG(debug) << "--begin keyboard packet--"sv << std::endl << "keyAction ["sv << util::hex(packet->header.magic).to_string_view() << ']' << std::endl @@ -271,8 +270,7 @@ namespace input { << "--end keyboard packet--"sv; } - void - print(PNV_UNICODE_PACKET packet) { + void print(PNV_UNICODE_PACKET packet) { std::string text(packet->text, util::endian::big(packet->header.size) - sizeof(packet->header.magic)); BOOST_LOG(debug) << "--begin unicode packet--"sv << std::endl @@ -280,8 +278,7 @@ namespace input { << "--end unicode packet--"sv; } - void - print(PNV_MULTI_CONTROLLER_PACKET packet) { + void print(PNV_MULTI_CONTROLLER_PACKET packet) { // Moonlight spams controller packet even when not necessary BOOST_LOG(verbose) << "--begin controller packet--"sv << std::endl @@ -301,8 +298,7 @@ namespace input { * @brief Prints a touch packet. * @param packet The touch packet. */ - void - print(PSS_TOUCH_PACKET packet) { + void print(PSS_TOUCH_PACKET packet) { BOOST_LOG(debug) << "--begin touch packet--"sv << std::endl << "eventType ["sv << util::hex(packet->eventType).to_string_view() << ']' << std::endl @@ -320,8 +316,7 @@ namespace input { * @brief Prints a pen packet. * @param packet The pen packet. */ - void - print(PSS_PEN_PACKET packet) { + void print(PSS_PEN_PACKET packet) { BOOST_LOG(debug) << "--begin pen packet--"sv << std::endl << "eventType ["sv << util::hex(packet->eventType).to_string_view() << ']' << std::endl @@ -341,8 +336,7 @@ namespace input { * @brief Prints a controller arrival packet. * @param packet The controller arrival packet. */ - void - print(PSS_CONTROLLER_ARRIVAL_PACKET packet) { + void print(PSS_CONTROLLER_ARRIVAL_PACKET packet) { BOOST_LOG(debug) << "--begin controller arrival packet--"sv << std::endl << "controllerNumber ["sv << (uint32_t) packet->controllerNumber << ']' << std::endl @@ -356,8 +350,7 @@ namespace input { * @brief Prints a controller touch packet. * @param packet The controller touch packet. */ - void - print(PSS_CONTROLLER_TOUCH_PACKET packet) { + void print(PSS_CONTROLLER_TOUCH_PACKET packet) { BOOST_LOG(debug) << "--begin controller touch packet--"sv << std::endl << "controllerNumber ["sv << (uint32_t) packet->controllerNumber << ']' << std::endl @@ -373,8 +366,7 @@ namespace input { * @brief Prints a controller motion packet. * @param packet The controller motion packet. */ - void - print(PSS_CONTROLLER_MOTION_PACKET packet) { + void print(PSS_CONTROLLER_MOTION_PACKET packet) { BOOST_LOG(verbose) << "--begin controller motion packet--"sv << std::endl << "controllerNumber ["sv << util::hex(packet->controllerNumber).to_string_view() << ']' << std::endl @@ -389,8 +381,7 @@ namespace input { * @brief Prints a controller battery packet. * @param packet The controller battery packet. */ - void - print(PSS_CONTROLLER_BATTERY_PACKET packet) { + void print(PSS_CONTROLLER_BATTERY_PACKET packet) { BOOST_LOG(verbose) << "--begin controller battery packet--"sv << std::endl << "controllerNumber ["sv << util::hex(packet->controllerNumber).to_string_view() << ']' << std::endl @@ -399,8 +390,7 @@ namespace input { << "--end controller battery packet--"sv; } - void - print(void *payload) { + void print(void *payload) { auto header = (PNV_INPUT_HEADER) payload; switch (util::endian::little(header->magic)) { @@ -451,8 +441,7 @@ namespace input { } } - void - passthrough(std::shared_ptr &input, PNV_REL_MOUSE_MOVE_PACKET packet) { + void passthrough(std::shared_ptr &input, PNV_REL_MOUSE_MOVE_PACKET packet) { if (!config::input.mouse) { return; } @@ -468,8 +457,7 @@ namespace input { * @param size The size of the client's surface containing the value. * @return The host-relative coordinate pair if a touchport is available. */ - std::optional> - client_to_touchport(std::shared_ptr &input, const std::pair &val, const std::pair &size) { + std::optional> client_to_touchport(std::shared_ptr &input, const std::pair &val, const std::pair &size) { auto &touch_port_event = input->touch_port_event; auto &touch_port = input->touch_port; if (touch_port_event->peek()) { @@ -492,7 +480,7 @@ namespace input { x = std::clamp(x, offsetX, (size.first * scalarX) - offsetX); y = std::clamp(y, offsetY, (size.second * scalarY) - offsetY); - return std::pair { (x - offsetX) * touch_port.scalar_inv, (y - offsetY) * touch_port.scalar_inv }; + return std::pair {(x - offsetX) * touch_port.scalar_inv, (y - offsetY) * touch_port.scalar_inv}; } /** @@ -502,8 +490,7 @@ namespace input { * @param scalar The scalar cartesian coordinate pair. * @return The scaled radial coordinate. */ - float - multiply_polar_by_cartesian_scalar(float r, float angle, const std::pair &scalar) { + float multiply_polar_by_cartesian_scalar(float r, float angle, const std::pair &scalar) { // Convert polar to cartesian coordinates float x = r * std::cos(angle); float y = r * std::sin(angle); @@ -516,8 +503,7 @@ namespace input { return std::sqrt(std::pow(x, 2) + std::pow(y, 2)); } - std::pair - scale_client_contact_area(const std::pair &val, uint16_t rotation, const std::pair &scalar) { + std::pair scale_client_contact_area(const std::pair &val, uint16_t rotation, const std::pair &scalar) { // If the rotation is unknown, we'll just scale both axes equally by using // a 45-degree angle for our scaling calculations float angle = rotation == LI_ROT_UNKNOWN ? (M_PI / 4) : (rotation * (M_PI / 180)); @@ -527,11 +513,10 @@ namespace input { float minor = val.second != 0.0f ? val.second : val.first; // The minor axis is perpendicular to major axis so the angle must be rotated by 90 degrees - return { multiply_polar_by_cartesian_scalar(major, angle, scalar), multiply_polar_by_cartesian_scalar(minor, angle + (M_PI / 2), scalar) }; + return {multiply_polar_by_cartesian_scalar(major, angle, scalar), multiply_polar_by_cartesian_scalar(minor, angle + (M_PI / 2), scalar)}; } - void - passthrough(std::shared_ptr &input, PNV_ABS_MOUSE_MOVE_PACKET packet) { + void passthrough(std::shared_ptr &input, PNV_ABS_MOUSE_MOVE_PACKET packet) { if (!config::input.mouse) { return; } @@ -554,22 +539,23 @@ namespace input { auto width = (float) util::endian::big(packet->width); auto height = (float) util::endian::big(packet->height); - auto tpcoords = client_to_touchport(input, { x, y }, { width, height }); + auto tpcoords = client_to_touchport(input, {x, y}, {width, height}); if (!tpcoords) { return; } auto &touch_port = input->touch_port; platf::touch_port_t abs_port { - touch_port.offset_x, touch_port.offset_y, - touch_port.env_width, touch_port.env_height + touch_port.offset_x, + touch_port.offset_y, + touch_port.env_width, + touch_port.env_height }; platf::abs_mouse(platf_input, abs_port, tpcoords->first, tpcoords->second); } - void - passthrough(std::shared_ptr &input, PNV_MOUSE_BUTTON_PACKET packet) { + void passthrough(std::shared_ptr &input, PNV_MOUSE_BUTTON_PACKET packet) { if (!config::input.mouse) { return; } @@ -617,7 +603,8 @@ namespace input { } 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); @@ -629,8 +616,7 @@ namespace input { platf::button_mouse(platf_input, button, release); } - short - map_keycode(short keycode) { + short map_keycode(short keycode) { auto it = config::input.keybindings.find(keycode); if (it != std::end(config::input.keybindings)) { return it->second; @@ -642,16 +628,14 @@ namespace input { /** * @brief Update flags for keyboard shortcut combo's */ - inline void - update_shortcutFlags(int *flags, short keyCode, bool release) { + inline void update_shortcutFlags(int *flags, short keyCode, bool release) { switch (keyCode) { case VKEY_SHIFT: case VKEY_LSHIFT: case VKEY_RSHIFT: if (release) { *flags &= ~input_t::SHIFT; - } - else { + } else { *flags |= input_t::SHIFT; } break; @@ -660,8 +644,7 @@ namespace input { case VKEY_RCONTROL: if (release) { *flags &= ~input_t::CTRL; - } - else { + } else { *flags |= input_t::CTRL; } break; @@ -670,16 +653,14 @@ namespace input { case VKEY_RMENU: if (release) { *flags &= ~input_t::ALT; - } - else { + } else { *flags |= input_t::ALT; } break; } } - bool - is_modifier(uint16_t keyCode) { + bool is_modifier(uint16_t keyCode) { switch (keyCode) { case VKEY_SHIFT: case VKEY_LSHIFT: @@ -696,8 +677,7 @@ namespace input { } } - void - send_key_and_modifiers(uint16_t key_code, bool release, uint8_t flags, uint8_t synthetic_modifiers) { + void send_key_and_modifiers(uint16_t key_code, bool release, uint8_t flags, uint8_t synthetic_modifiers) { if (!release) { // Press any synthetic modifiers required for this key if (synthetic_modifiers & MODIFIER_SHIFT) { @@ -727,8 +707,7 @@ namespace input { } } - void - repeat_key(uint16_t key_code, uint8_t flags, uint8_t synthetic_modifiers) { + void repeat_key(uint16_t key_code, uint8_t flags, uint8_t synthetic_modifiers) { // If key no longer pressed, stop repeating if (!key_press[make_kpid(key_code, flags)]) { key_press_repeat_id = nullptr; @@ -740,8 +719,7 @@ namespace input { key_press_repeat_id = task_pool.pushDelayed(repeat_key, config::input.key_repeat_period, key_code, flags, synthetic_modifiers).task_id; } - void - passthrough(std::shared_ptr &input, PNV_KEYBOARD_PACKET packet) { + void passthrough(std::shared_ptr &input, PNV_KEYBOARD_PACKET packet) { if (!config::input.keyboard) { return; } @@ -780,13 +758,11 @@ namespace input { if (config::input.key_repeat_delay.count() > 0) { key_press_repeat_id = task_pool.pushDelayed(repeat_key, config::input.key_repeat_delay, keyCode, packet->flags, synthetic_modifiers).task_id; } - } - else { + } else { // Already released return; } - } - else if (!release) { + } else if (!release) { // Already pressed down key return; } @@ -803,16 +779,14 @@ namespace input { * @param input The input context pointer. * @param packet The scroll packet. */ - void - passthrough(std::shared_ptr &input, PNV_SCROLL_PACKET packet) { + void passthrough(std::shared_ptr &input, PNV_SCROLL_PACKET packet) { if (!config::input.mouse) { return; } if (config::input.high_resolution_scrolling) { platf::scroll(platf_input, util::endian::big(packet->scrollAmt1)); - } - else { + } else { input->accumulated_vscroll_delta += util::endian::big(packet->scrollAmt1); auto full_ticks = input->accumulated_vscroll_delta / WHEEL_DELTA; if (full_ticks) { @@ -828,16 +802,14 @@ namespace input { * @param input The input context pointer. * @param packet The scroll packet. */ - void - passthrough(std::shared_ptr &input, PSS_HSCROLL_PACKET packet) { + void passthrough(std::shared_ptr &input, PSS_HSCROLL_PACKET packet) { if (!config::input.mouse) { return; } if (config::input.high_resolution_scrolling) { platf::hscroll(platf_input, util::endian::big(packet->scrollAmount)); - } - else { + } else { input->accumulated_hscroll_delta += util::endian::big(packet->scrollAmount); auto full_ticks = input->accumulated_hscroll_delta / WHEEL_DELTA; if (full_ticks) { @@ -848,8 +820,7 @@ namespace input { } } - void - passthrough(PNV_UNICODE_PACKET packet) { + void passthrough(PNV_UNICODE_PACKET packet) { if (!config::input.keyboard) { return; } @@ -863,8 +834,7 @@ namespace input { * @param input The input context pointer. * @param packet The controller arrival packet. */ - void - passthrough(std::shared_ptr &input, PSS_CONTROLLER_ARRIVAL_PACKET packet) { + void passthrough(std::shared_ptr &input, PSS_CONTROLLER_ARRIVAL_PACKET packet) { if (!config::input.controller) { return; } @@ -891,7 +861,7 @@ namespace input { } // Allocate a new gamepad - if (platf::alloc_gamepad(platf_input, { id, packet->controllerNumber }, arrival, input->feedback_queue)) { + if (platf::alloc_gamepad(platf_input, {id, packet->controllerNumber}, arrival, input->feedback_queue)) { free_id(gamepadMask, id); return; } @@ -904,25 +874,23 @@ namespace input { * @param input The input context pointer. * @param packet The touch packet. */ - void - passthrough(std::shared_ptr &input, PSS_TOUCH_PACKET packet) { + void passthrough(std::shared_ptr &input, PSS_TOUCH_PACKET packet) { if (!config::input.mouse) { return; } // Convert the client normalized coordinates to touchport coordinates - auto coords = client_to_touchport(input, - { from_clamped_netfloat(packet->x, 0.0f, 1.0f) * 65535.f, - from_clamped_netfloat(packet->y, 0.0f, 1.0f) * 65535.f }, - { 65535.f, 65535.f }); + auto coords = client_to_touchport(input, {from_clamped_netfloat(packet->x, 0.0f, 1.0f) * 65535.f, from_clamped_netfloat(packet->y, 0.0f, 1.0f) * 65535.f}, {65535.f, 65535.f}); if (!coords) { return; } auto &touch_port = input->touch_port; platf::touch_port_t abs_port { - touch_port.offset_x, touch_port.offset_y, - touch_port.env_width, touch_port.env_height + touch_port.offset_x, + touch_port.offset_y, + touch_port.env_width, + touch_port.env_height }; // Renormalize the coordinates @@ -937,10 +905,11 @@ namespace input { // Normalize the contact area based on the touchport auto contact_area = scale_client_contact_area( - { from_clamped_netfloat(packet->contactAreaMajor, 0.0f, 1.0f) * 65535.f, - from_clamped_netfloat(packet->contactAreaMinor, 0.0f, 1.0f) * 65535.f }, + {from_clamped_netfloat(packet->contactAreaMajor, 0.0f, 1.0f) * 65535.f, + from_clamped_netfloat(packet->contactAreaMinor, 0.0f, 1.0f) * 65535.f}, rotation, - { abs_port.width / 65535.f, abs_port.height / 65535.f }); + {abs_port.width / 65535.f, abs_port.height / 65535.f} + ); platf::touch_input_t touch { packet->eventType, @@ -961,25 +930,23 @@ namespace input { * @param input The input context pointer. * @param packet The pen packet. */ - void - passthrough(std::shared_ptr &input, PSS_PEN_PACKET packet) { + void passthrough(std::shared_ptr &input, PSS_PEN_PACKET packet) { if (!config::input.mouse) { return; } // Convert the client normalized coordinates to touchport coordinates - auto coords = client_to_touchport(input, - { from_clamped_netfloat(packet->x, 0.0f, 1.0f) * 65535.f, - from_clamped_netfloat(packet->y, 0.0f, 1.0f) * 65535.f }, - { 65535.f, 65535.f }); + auto coords = client_to_touchport(input, {from_clamped_netfloat(packet->x, 0.0f, 1.0f) * 65535.f, from_clamped_netfloat(packet->y, 0.0f, 1.0f) * 65535.f}, {65535.f, 65535.f}); if (!coords) { return; } auto &touch_port = input->touch_port; platf::touch_port_t abs_port { - touch_port.offset_x, touch_port.offset_y, - touch_port.env_width, touch_port.env_height + touch_port.offset_x, + touch_port.offset_y, + touch_port.env_width, + touch_port.env_height }; // Renormalize the coordinates @@ -994,10 +961,11 @@ namespace input { // Normalize the contact area based on the touchport auto contact_area = scale_client_contact_area( - { from_clamped_netfloat(packet->contactAreaMajor, 0.0f, 1.0f) * 65535.f, - from_clamped_netfloat(packet->contactAreaMinor, 0.0f, 1.0f) * 65535.f }, + {from_clamped_netfloat(packet->contactAreaMajor, 0.0f, 1.0f) * 65535.f, + from_clamped_netfloat(packet->contactAreaMinor, 0.0f, 1.0f) * 65535.f}, rotation, - { abs_port.width / 65535.f, abs_port.height / 65535.f }); + {abs_port.width / 65535.f, abs_port.height / 65535.f} + ); platf::pen_input_t pen { packet->eventType, @@ -1020,8 +988,7 @@ namespace input { * @param input The input context pointer. * @param packet The controller touch packet. */ - void - passthrough(std::shared_ptr &input, PSS_CONTROLLER_TOUCH_PACKET packet) { + void passthrough(std::shared_ptr &input, PSS_CONTROLLER_TOUCH_PACKET packet) { if (!config::input.controller) { return; } @@ -1038,7 +1005,7 @@ namespace input { } platf::gamepad_touch_t touch { - { gamepad.id, packet->controllerNumber }, + {gamepad.id, packet->controllerNumber}, packet->eventType, util::endian::little(packet->pointerId), from_clamped_netfloat(packet->x, 0.0f, 1.0f), @@ -1054,8 +1021,7 @@ namespace input { * @param input The input context pointer. * @param packet The controller motion packet. */ - void - passthrough(std::shared_ptr &input, PSS_CONTROLLER_MOTION_PACKET packet) { + void passthrough(std::shared_ptr &input, PSS_CONTROLLER_MOTION_PACKET packet) { if (!config::input.controller) { return; } @@ -1072,7 +1038,7 @@ namespace input { } platf::gamepad_motion_t motion { - { gamepad.id, packet->controllerNumber }, + {gamepad.id, packet->controllerNumber}, packet->motionType, from_netfloat(packet->x), from_netfloat(packet->y), @@ -1087,8 +1053,7 @@ namespace input { * @param input The input context pointer. * @param packet The controller battery packet. */ - void - passthrough(std::shared_ptr &input, PSS_CONTROLLER_BATTERY_PACKET packet) { + void passthrough(std::shared_ptr &input, PSS_CONTROLLER_BATTERY_PACKET packet) { if (!config::input.controller) { return; } @@ -1105,7 +1070,7 @@ namespace input { } platf::gamepad_battery_t battery { - { gamepad.id, packet->controllerNumber }, + {gamepad.id, packet->controllerNumber}, packet->batteryState, packet->batteryPercentage }; @@ -1113,8 +1078,7 @@ namespace input { platf::gamepad_battery(platf_input, battery); } - void - passthrough(std::shared_ptr &input, PNV_MULTI_CONTROLLER_PACKET packet) { + void passthrough(std::shared_ptr &input, PNV_MULTI_CONTROLLER_PACKET packet) { if (!config::input.controller) { return; } @@ -1135,14 +1099,13 @@ namespace input { return; } - if (platf::alloc_gamepad(platf_input, { id, (uint8_t) packet->controllerNumber }, {}, input->feedback_queue)) { + if (platf::alloc_gamepad(platf_input, {id, (uint8_t) packet->controllerNumber}, {}, input->feedback_queue)) { free_id(gamepadMask, id); return; } gamepad.id = id; - } - else if (!(packet->activeGamepadMask & (1 << packet->controllerNumber)) && gamepad.id >= 0) { + } else if (!(packet->activeGamepadMask & (1 << packet->controllerNumber)) && gamepad.id >= 0) { // If this is the final event for a gamepad being removed, free the gamepad and return. free_gamepad(platf_input, gamepad.id); gamepad.id = -1; @@ -1219,8 +1182,7 @@ namespace input { 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; } @@ -1243,8 +1205,7 @@ namespace input { * @param src A later packet to attempt to batch. * @return The status of the batching operation. */ - batch_result_e - batch(PNV_REL_MOUSE_MOVE_PACKET dest, PNV_REL_MOUSE_MOVE_PACKET src) { + batch_result_e batch(PNV_REL_MOUSE_MOVE_PACKET dest, PNV_REL_MOUSE_MOVE_PACKET src) { short deltaX, deltaY; // Batching is safe as long as the result doesn't overflow a 16-bit integer @@ -1267,8 +1228,7 @@ namespace input { * @param src A later packet to attempt to batch. * @return The status of the batching operation. */ - batch_result_e - batch(PNV_ABS_MOUSE_MOVE_PACKET dest, PNV_ABS_MOUSE_MOVE_PACKET src) { + batch_result_e batch(PNV_ABS_MOUSE_MOVE_PACKET dest, PNV_ABS_MOUSE_MOVE_PACKET src) { // Batching must only happen if the reference width and height don't change if (dest->width != src->width || dest->height != src->height) { return batch_result_e::terminate_batch; @@ -1285,8 +1245,7 @@ namespace input { * @param src A later packet to attempt to batch. * @return The status of the batching operation. */ - batch_result_e - batch(PNV_SCROLL_PACKET dest, PNV_SCROLL_PACKET src) { + batch_result_e batch(PNV_SCROLL_PACKET dest, PNV_SCROLL_PACKET src) { short scrollAmt; // Batching is safe as long as the result doesn't overflow a 16-bit integer @@ -1306,8 +1265,7 @@ namespace input { * @param src A later packet to attempt to batch. * @return The status of the batching operation. */ - batch_result_e - batch(PSS_HSCROLL_PACKET dest, PSS_HSCROLL_PACKET src) { + batch_result_e batch(PSS_HSCROLL_PACKET dest, PSS_HSCROLL_PACKET src) { short scrollAmt; // Batching is safe as long as the result doesn't overflow a 16-bit integer @@ -1326,8 +1284,7 @@ namespace input { * @param src A later packet to attempt to batch. * @return The status of the batching operation. */ - batch_result_e - batch(PNV_MULTI_CONTROLLER_PACKET dest, PNV_MULTI_CONTROLLER_PACKET src) { + batch_result_e batch(PNV_MULTI_CONTROLLER_PACKET dest, PNV_MULTI_CONTROLLER_PACKET src) { // Do not allow batching if the active controllers change if (dest->activeGamepadMask != src->activeGamepadMask) { return batch_result_e::terminate_batch; @@ -1355,8 +1312,7 @@ namespace input { * @param src A later packet to attempt to batch. * @return The status of the batching operation. */ - batch_result_e - batch(PSS_TOUCH_PACKET dest, PSS_TOUCH_PACKET src) { + batch_result_e batch(PSS_TOUCH_PACKET dest, PSS_TOUCH_PACKET src) { // Only batch hover or move events if (dest->eventType != LI_TOUCH_EVENT_MOVE && dest->eventType != LI_TOUCH_EVENT_HOVER) { @@ -1390,8 +1346,7 @@ namespace input { * @param src A later packet to attempt to batch. * @return The status of the batching operation. */ - batch_result_e - batch(PSS_PEN_PACKET dest, PSS_PEN_PACKET src) { + batch_result_e batch(PSS_PEN_PACKET dest, PSS_PEN_PACKET src) { // Only batch hover or move events if (dest->eventType != LI_TOUCH_EVENT_MOVE && dest->eventType != LI_TOUCH_EVENT_HOVER) { @@ -1424,8 +1379,7 @@ namespace input { * @param src A later packet to attempt to batch. * @return The status of the batching operation. */ - batch_result_e - batch(PSS_CONTROLLER_TOUCH_PACKET dest, PSS_CONTROLLER_TOUCH_PACKET src) { + batch_result_e batch(PSS_CONTROLLER_TOUCH_PACKET dest, PSS_CONTROLLER_TOUCH_PACKET src) { // Only batch hover or move events if (dest->eventType != LI_TOUCH_EVENT_MOVE && dest->eventType != LI_TOUCH_EVENT_HOVER) { @@ -1465,8 +1419,7 @@ namespace input { * @param src A later packet to attempt to batch. * @return The status of the batching operation. */ - batch_result_e - batch(PSS_CONTROLLER_MOTION_PACKET dest, PSS_CONTROLLER_MOTION_PACKET src) { + batch_result_e batch(PSS_CONTROLLER_MOTION_PACKET dest, PSS_CONTROLLER_MOTION_PACKET src) { // We can only batch entries for the same controller, but allow batching attempts to continue // in case we have more packets for this controller later in the queue. if (dest->controllerNumber != src->controllerNumber) { @@ -1489,8 +1442,7 @@ namespace input { * @param src A later packet to attempt to batch. * @return The status of the batching operation. */ - batch_result_e - batch(PNV_INPUT_HEADER dest, PNV_INPUT_HEADER src) { + batch_result_e batch(PNV_INPUT_HEADER dest, PNV_INPUT_HEADER src) { // We can only batch if the packet types are the same if (dest->magic != src->magic) { return batch_result_e::terminate_batch; @@ -1526,8 +1478,7 @@ namespace input { * @brief Called on a thread pool thread to process an input message. * @param input The input context pointer. */ - void - passthrough_next_message(std::shared_ptr input) { + void passthrough_next_message(std::shared_ptr input) { // 'entry' backs the 'payload' pointer, so they must remain in scope together std::vector entry; PNV_INPUT_HEADER payload; @@ -1558,12 +1509,10 @@ namespace input { if (batch_result == batch_result_e::terminate_batch) { // Stop batching break; - } - else if (batch_result == batch_result_e::batched) { + } else if (batch_result == batch_result_e::batched) { // Erase this entry since it was batched i = input->input_queue.erase(i); - } - else { + } else { // We couldn't batch this entry, but try to batch later entries. i++; } @@ -1627,8 +1576,7 @@ namespace input { * @param input The input context pointer. * @param input_data The input message. */ - void - passthrough(std::shared_ptr &input, std::vector &&input_data) { + void passthrough(std::shared_ptr &input, std::vector &&input_data) { { std::lock_guard lg(input->input_queue_lock); input->input_queue.push_back(std::move(input_data)); @@ -1636,8 +1584,7 @@ namespace input { task_pool.push(passthrough_next_message, input); } - void - reset(std::shared_ptr &input) { + void reset(std::shared_ptr &input) { task_pool.cancel(key_press_repeat_id); task_pool.cancel(input->mouse_left_button_timeout); @@ -1668,15 +1615,13 @@ namespace input { } }; - [[nodiscard]] std::unique_ptr - init() { + [[nodiscard]] std::unique_ptr init() { platf_input = platf::input(); return std::make_unique(); } - bool - probe_gamepads() { + bool probe_gamepads() { auto input = static_cast(platf_input.get()); const auto gamepads = platf::supported_gamepads(input); for (auto &gamepad : gamepads) { @@ -1687,18 +1632,18 @@ namespace input { return true; } - std::shared_ptr - alloc(safe::mail_t mail) { + std::shared_ptr alloc(safe::mail_t mail) { auto input = std::make_shared( mail->event(mail::touch_port), - mail->queue(mail::gamepad_feedback)); + mail->queue(mail::gamepad_feedback) + ); // Workaround to ensure new frames will be captured when a client connects task_pool.pushDelayed([]() { platf::move_mouse(platf_input, 1, 1); platf::move_mouse(platf_input, -1, -1); }, - 100ms); + 100ms); return input; } diff --git a/src/input.h b/src/input.h index a20c9c30..96b2f457 100644 --- a/src/input.h +++ b/src/input.h @@ -4,29 +4,25 @@ */ #pragma once +// standard includes #include +// local includes #include "platform/common.h" #include "thread_safe.h" namespace input { struct input_t; - void - print(void *input); - void - reset(std::shared_ptr &input); - void - passthrough(std::shared_ptr &input, std::vector &&input_data); + void print(void *input); + void reset(std::shared_ptr &input); + void passthrough(std::shared_ptr &input, std::vector &&input_data); - [[nodiscard]] std::unique_ptr - init(); + [[nodiscard]] std::unique_ptr init(); - bool - probe_gamepads(); + bool probe_gamepads(); - std::shared_ptr - alloc(safe::mail_t mail); + std::shared_ptr alloc(safe::mail_t mail); struct touch_port_t: public platf::touch_port_t { int env_width, env_height; @@ -36,8 +32,7 @@ namespace input { float scalar_inv; - explicit - operator bool() const { + explicit operator bool() const { return width != 0 && height != 0 && env_width != 0 && env_height != 0; } }; @@ -49,6 +44,5 @@ namespace input { * @param scalar The scalar cartesian coordinate pair. * @return The major and minor axis pair. */ - std::pair - scale_client_contact_area(const std::pair &val, uint16_t rotation, const std::pair &scalar); + std::pair scale_client_contact_area(const std::pair &val, uint16_t rotation, const std::pair &scalar); } // namespace input diff --git a/src/logging.cpp b/src/logging.cpp index e4057d9e..1913eef1 100644 --- a/src/logging.cpp +++ b/src/logging.cpp @@ -47,15 +47,13 @@ namespace logging { deinit(); } - void - deinit() { + void deinit() { log_flush(); bl::core::get()->remove_sink(sink); sink.reset(); } - void - formatter(const boost::log::record_view &view, boost::log::formatting_ostream &os) { + void formatter(const boost::log::record_view &view, boost::log::formatting_ostream &os) { constexpr const char *message = "Message"; constexpr const char *severity = "Severity"; @@ -90,7 +88,8 @@ namespace logging { auto now = std::chrono::system_clock::now(); auto ms = std::chrono::duration_cast( - now - std::chrono::time_point_cast(now)); + now - std::chrono::time_point_cast(now) + ); auto t = std::chrono::system_clock::to_time_t(now); auto lt = *std::localtime(&t); @@ -99,8 +98,7 @@ namespace logging { << log_type << view.attribute_values()[message].extract(); } - [[nodiscard]] std::unique_ptr - init(int min_log_level, const std::string &log_file) { + [[nodiscard]] std::unique_ptr init(int min_log_level, const std::string &log_file) { if (sink) { // Deinitialize the logging system before reinitializing it. This can probably only ever be hit in tests. deinit(); @@ -112,7 +110,7 @@ namespace logging { sink = boost::make_shared(); #ifndef SUNSHINE_TESTS - boost::shared_ptr stream { &std::cout, boost::null_deleter() }; + boost::shared_ptr stream {&std::cout, boost::null_deleter()}; sink->locked_backend()->add_stream(stream); #endif sink->locked_backend()->add_stream(boost::make_shared(log_file)); @@ -127,12 +125,10 @@ namespace logging { return std::make_unique(); } - void - setup_av_logging(int min_log_level) { + void setup_av_logging(int min_log_level) { if (min_log_level >= 1) { av_log_set_level(AV_LOG_QUIET); - } - else { + } else { av_log_set_level(AV_LOG_DEBUG); } av_log_set_callback([](void *ptr, int level, const char *fmt, va_list vl) { @@ -144,28 +140,23 @@ namespace logging { // We print AV_LOG_FATAL at the error level. FFmpeg prints things as fatal that // are expected in some cases, such as lack of codec support or similar things. BOOST_LOG(error) << buffer; - } - else if (level <= AV_LOG_WARNING) { + } else if (level <= AV_LOG_WARNING) { BOOST_LOG(warning) << buffer; - } - else if (level <= AV_LOG_INFO) { + } else if (level <= AV_LOG_INFO) { BOOST_LOG(info) << buffer; - } - else if (level <= AV_LOG_VERBOSE) { + } else if (level <= AV_LOG_VERBOSE) { // AV_LOG_VERBOSE is less verbose than AV_LOG_DEBUG BOOST_LOG(debug) << buffer; - } - else { + } else { BOOST_LOG(verbose) << buffer; } }); } - void - setup_libdisplaydevice_logging(int min_log_level) { - constexpr int min_level { static_cast(display_device::Logger::LogLevel::verbose) }; - constexpr int max_level { static_cast(display_device::Logger::LogLevel::fatal) }; - const auto log_level { static_cast(std::min(std::max(min_level, min_log_level), max_level)) }; + void setup_libdisplaydevice_logging(int min_log_level) { + constexpr int min_level {static_cast(display_device::Logger::LogLevel::verbose)}; + constexpr int max_level {static_cast(display_device::Logger::LogLevel::fatal)}; + const auto log_level {static_cast(std::min(std::max(min_level, min_log_level), max_level))}; display_device::Logger::get().setLogLevel(log_level); display_device::Logger::get().setCustomCallback([](const display_device::Logger::LogLevel level, const std::string &message) { @@ -192,15 +183,13 @@ namespace logging { }); } - void - log_flush() { + void log_flush() { if (sink) { sink->flush(); } } - void - print_help(const char *name) { + void print_help(const char *name) { std::cout << "Usage: "sv << name << " [options] [/path/to/configuration_file] [--cmd]"sv << std::endl << " Any configurable option can be overwritten with: \"name=value\""sv << std::endl @@ -220,13 +209,11 @@ namespace logging { << std::endl; } - std::string - bracket(const std::string &input) { + std::string bracket(const std::string &input) { return "["s + input + "]"s; } - std::wstring - bracket(const std::wstring &input) { + std::wstring bracket(const std::wstring &input) { return L"["s + input + L"]"s; } diff --git a/src/logging.h b/src/logging.h index 88f26274..02e01467 100644 --- a/src/logging.h +++ b/src/logging.h @@ -41,11 +41,9 @@ namespace logging { * deinit(); * @examples_end */ - void - deinit(); + void deinit(); - void - formatter(const boost::log::record_view &view, boost::log::formatting_ostream &os); + void formatter(const boost::log::record_view &view, boost::log::formatting_ostream &os); /** * @brief Initialize the logging system. @@ -56,22 +54,19 @@ namespace logging { * log_init(2, "sunshine.log"); * @examples_end */ - [[nodiscard]] std::unique_ptr - init(int min_log_level, const std::string &log_file); + [[nodiscard]] std::unique_ptr init(int min_log_level, const std::string &log_file); /** * @brief Setup AV logging. * @param min_log_level The log level. */ - void - setup_av_logging(int min_log_level); + void setup_av_logging(int min_log_level); /** * @brief Setup logging for libdisplaydevice. * @param min_log_level The log level. */ - void - setup_libdisplaydevice_logging(int min_log_level); + void setup_libdisplaydevice_logging(int min_log_level); /** * @brief Flush the log. @@ -79,8 +74,7 @@ namespace logging { * log_flush(); * @examples_end */ - void - log_flush(); + void log_flush(); /** * @brief Print help to stdout. @@ -89,8 +83,7 @@ namespace logging { * print_help("sunshine"); * @examples_end */ - void - print_help(const char *name); + void print_help(const char *name); /** * @brief A helper class for tracking and logging numerical values across a period of time @@ -105,28 +98,24 @@ namespace logging { * // [2024:01:01:12:00:00]: Debug: Test time value (min/max/avg): 1ms/3ms/2.00ms * @examples_end */ - template + template class min_max_avg_periodic_logger { public: - min_max_avg_periodic_logger(boost::log::sources::severity_logger &severity, - std::string_view message, - std::string_view units, - std::chrono::seconds interval_in_seconds = std::chrono::seconds(20)): + min_max_avg_periodic_logger(boost::log::sources::severity_logger &severity, std::string_view message, std::string_view units, std::chrono::seconds interval_in_seconds = std::chrono::seconds(20)): severity(severity), message(message), units(units), interval(interval_in_seconds), - enabled(config::sunshine.min_log_level <= severity.default_severity()) {} + enabled(config::sunshine.min_log_level <= severity.default_severity()) { + } - void - collect_and_log(const T &value) { + void collect_and_log(const T &value) { if (enabled) { auto print_info = [&](const T &min_value, const T &max_value, double avg_value) { auto f = stat_trackers::two_digits_after_decimal(); if constexpr (std::is_floating_point_v) { BOOST_LOG(severity.get()) << message << " (min/max/avg): " << f % min_value << units << "/" << f % max_value << units << "/" << f % avg_value << units; - } - else { + } else { BOOST_LOG(severity.get()) << message << " (min/max/avg): " << min_value << units << "/" << max_value << units << "/" << f % avg_value << units; } }; @@ -134,18 +123,19 @@ namespace logging { } } - void - collect_and_log(std::function func) { - if (enabled) collect_and_log(func()); + void collect_and_log(std::function func) { + if (enabled) { + collect_and_log(func()); + } } - void - reset() { - if (enabled) tracker.reset(); + void reset() { + if (enabled) { + tracker.reset(); + } } - bool - is_enabled() const { + bool is_enabled() const { return enabled; } @@ -175,40 +165,41 @@ namespace logging { */ class time_delta_periodic_logger { public: - time_delta_periodic_logger(boost::log::sources::severity_logger &severity, - std::string_view message, - std::chrono::seconds interval_in_seconds = std::chrono::seconds(20)): - logger(severity, message, "ms", interval_in_seconds) {} - - void - first_point(const std::chrono::steady_clock::time_point &point) { - if (logger.is_enabled()) point1 = point; + time_delta_periodic_logger(boost::log::sources::severity_logger &severity, std::string_view message, std::chrono::seconds interval_in_seconds = std::chrono::seconds(20)): + logger(severity, message, "ms", interval_in_seconds) { } - void - first_point_now() { - if (logger.is_enabled()) first_point(std::chrono::steady_clock::now()); + void first_point(const std::chrono::steady_clock::time_point &point) { + if (logger.is_enabled()) { + point1 = point; + } } - void - second_point_and_log(const std::chrono::steady_clock::time_point &point) { + void first_point_now() { + if (logger.is_enabled()) { + first_point(std::chrono::steady_clock::now()); + } + } + + void second_point_and_log(const std::chrono::steady_clock::time_point &point) { if (logger.is_enabled()) { logger.collect_and_log(std::chrono::duration(point - point1).count()); } } - void - second_point_now_and_log() { - if (logger.is_enabled()) second_point_and_log(std::chrono::steady_clock::now()); + void second_point_now_and_log() { + if (logger.is_enabled()) { + second_point_and_log(std::chrono::steady_clock::now()); + } } - void - reset() { - if (logger.is_enabled()) logger.reset(); + void reset() { + if (logger.is_enabled()) { + logger.reset(); + } } - bool - is_enabled() const { + bool is_enabled() const { return logger.is_enabled(); } @@ -222,15 +213,13 @@ namespace logging { * @param input Input string. * @return Enclosed string. */ - std::string - bracket(const std::string &input); + std::string bracket(const std::string &input); /** * @brief Enclose string in square brackets. * @param input Input string. * @return Enclosed string. */ - std::wstring - bracket(const std::wstring &input); + std::wstring bracket(const std::wstring &input); } // namespace logging diff --git a/src/main.cpp b/src/main.cpp index 04ab7d13..04b080c5 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -30,31 +30,37 @@ extern "C" { using namespace std::literals; std::map> signal_handlers; -void -on_signal_forwarder(int sig) { + +void on_signal_forwarder(int sig) { signal_handlers.at(sig)(); } -template -void -on_signal(int sig, FN &&fn) { +template +void on_signal(int sig, FN &&fn) { signal_handlers.emplace(sig, std::forward(fn)); std::signal(sig, on_signal_forwarder); } std::map> cmd_to_func { - { "creds"sv, [](const char *name, int argc, char **argv) { return args::creds(name, argc, argv); } }, - { "help"sv, [](const char *name, int argc, char **argv) { return args::help(name); } }, - { "version"sv, [](const char *name, int argc, char **argv) { return args::version(); } }, + {"creds"sv, [](const char *name, int argc, char **argv) { + return args::creds(name, argc, argv); + }}, + {"help"sv, [](const char *name, int argc, char **argv) { + return args::help(name); + }}, + {"version"sv, [](const char *name, int argc, char **argv) { + return args::version(); + }}, #ifdef _WIN32 - { "restore-nvprefs-undo"sv, [](const char *name, int argc, char **argv) { return args::restore_nvprefs_undo(); } }, + {"restore-nvprefs-undo"sv, [](const char *name, int argc, char **argv) { + return args::restore_nvprefs_undo(); + }}, #endif }; #ifdef _WIN32 -LRESULT CALLBACK -SessionMonitorWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { +LRESULT CALLBACK SessionMonitorWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_CLOSE: DestroyWindow(hwnd); @@ -62,19 +68,19 @@ SessionMonitorWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { case WM_DESTROY: PostQuitMessage(0); return 0; - case WM_ENDSESSION: { - // Terminate ourselves with a blocking exit call - std::cout << "Received WM_ENDSESSION"sv << std::endl; - lifetime::exit_sunshine(0, false); - return 0; - } + case WM_ENDSESSION: + { + // Terminate ourselves with a blocking exit call + std::cout << "Received WM_ENDSESSION"sv << std::endl; + lifetime::exit_sunshine(0, false); + return 0; + } default: return DefWindowProc(hwnd, uMsg, wParam, lParam); } } -WINAPI BOOL -ConsoleCtrlHandler(DWORD type) { +WINAPI BOOL ConsoleCtrlHandler(DWORD type) { if (type == CTRL_CLOSE_EVENT) { BOOST_LOG(info) << "Console closed handler called"; lifetime::exit_sunshine(0, false); @@ -83,8 +89,7 @@ ConsoleCtrlHandler(DWORD type) { } #endif -int -main(int argc, char *argv[]) { +int main(int argc, char *argv[]) { lifetime::argv = argv; task_pool_util::TaskPool::task_id_t force_shutdown = nullptr; @@ -188,7 +193,8 @@ main(int argc, char *argv[]) { nullptr, nullptr, nullptr, - nullptr); + nullptr + ); session_monitor_hwnd_promise.set_value(wnd); @@ -216,12 +222,10 @@ main(int argc, char *argv[]) { if (session_monitor_join_thread_future.wait_for(1s) == std::future_status::ready) { session_monitor_thread.join(); return; - } - else { + } else { BOOST_LOG(warning) << "session_monitor_join_thread_future reached timeout"; } - } - else { + } else { BOOST_LOG(warning) << "session_monitor_hwnd_future reached timeout"; } @@ -324,8 +328,8 @@ main(int argc, char *argv[]) { return lifetime::desired_exit_code; } - std::thread httpThread { nvhttp::start }; - std::thread configThread { confighttp::start }; + std::thread httpThread {nvhttp::start}; + std::thread configThread {confighttp::start}; #ifdef _WIN32 // If we're using the default port and GameStream is enabled, warn the user diff --git a/src/main.h b/src/main.h index d867a656..f1a6e15c 100644 --- a/src/main.h +++ b/src/main.h @@ -12,5 +12,4 @@ * main(1, const char* args[] = {"sunshine", nullptr}); * @examples_end */ -int -main(int argc, char *argv[]); +int main(int argc, char *argv[]); diff --git a/src/move_by_copy.h b/src/move_by_copy.h index 964dcfb0..d1c0181d 100644 --- a/src/move_by_copy.h +++ b/src/move_by_copy.h @@ -4,6 +4,7 @@ */ #pragma once +// standard includes #include /** @@ -14,7 +15,7 @@ namespace move_by_copy_util { * When a copy is made, it moves the object * This allows you to move an object when a move can't be done. */ - template + template class MoveByCopy { public: typedef T move_type; @@ -24,7 +25,8 @@ namespace move_by_copy_util { public: explicit MoveByCopy(move_type &&to_move): - _to_move(std::move(to_move)) {} + _to_move(std::move(to_move)) { + } MoveByCopy(MoveByCopy &&other) = default; @@ -32,11 +34,9 @@ namespace move_by_copy_util { *this = other; } - MoveByCopy & - operator=(MoveByCopy &&other) = default; + MoveByCopy &operator=(MoveByCopy &&other) = default; - MoveByCopy & - operator=(const MoveByCopy &other) { + MoveByCopy &operator=(const MoveByCopy &other) { this->_to_move = std::move(const_cast(other)._to_move); return *this; @@ -47,16 +47,14 @@ namespace move_by_copy_util { } }; - template - MoveByCopy - cmove(T &movable) { + template + MoveByCopy cmove(T &movable) { return MoveByCopy(std::move(movable)); } // Do NOT use this unless you are absolutely certain the object to be moved is no longer used by the caller - template - MoveByCopy - const_cmove(const T &movable) { + template + MoveByCopy const_cmove(const T &movable) { return MoveByCopy(std::move(const_cast(movable))); } } // namespace move_by_copy_util diff --git a/src/network.cpp b/src/network.cpp index 5b34df46..338afca5 100644 --- a/src/network.cpp +++ b/src/network.cpp @@ -2,13 +2,16 @@ * @file src/network.cpp * @brief Definitions for networking related functions. */ -#include "network.h" -#include "config.h" -#include "logging.h" -#include "utility.h" +// standard includes #include #include +// local includes +#include "config.h" +#include "logging.h" +#include "network.h" +#include "utility.h" + using namespace std::literals; namespace ip = boost::asio::ip; @@ -33,8 +36,7 @@ namespace net { ip::make_network_v6("fe80::/64"sv), }; - net_e - from_enum_string(const std::string_view &view) { + net_e from_enum_string(const std::string_view &view) { if (view == "wan") { return WAN; } @@ -45,8 +47,7 @@ namespace net { return PC; } - net_e - from_address(const std::string_view &view) { + net_e from_address(const std::string_view &view) { auto addr = normalize_address(ip::make_address(view)); if (addr.is_v6()) { @@ -61,8 +62,7 @@ namespace net { return LAN; } } - } - else { + } else { for (auto &range : pc_ips_v4) { if (range.hosts().find(addr.to_v4()) != range.hosts().end()) { return PC; @@ -79,8 +79,7 @@ namespace net { return WAN; } - std::string_view - to_enum_string(net_e net) { + std::string_view to_enum_string(net_e net) { switch (net) { case PC: return "pc"sv; @@ -94,8 +93,7 @@ namespace net { return "wan"sv; } - af_e - af_from_enum_string(const std::string_view &view) { + af_e af_from_enum_string(const std::string_view &view) { if (view == "ipv4") { return IPV4; } @@ -107,8 +105,7 @@ namespace net { return BOTH; } - std::string_view - af_to_any_address_string(af_e af) { + std::string_view af_to_any_address_string(af_e af) { switch (af) { case IPV4: return "0.0.0.0"sv; @@ -120,8 +117,7 @@ namespace net { return "::"sv; } - boost::asio::ip::address - normalize_address(boost::asio::ip::address address) { + boost::asio::ip::address normalize_address(boost::asio::ip::address address) { // Convert IPv6-mapped IPv4 addresses into regular IPv4 addresses if (address.is_v6()) { auto v6 = address.to_v6(); @@ -133,37 +129,31 @@ namespace net { return address; } - std::string - addr_to_normalized_string(boost::asio::ip::address address) { + std::string addr_to_normalized_string(boost::asio::ip::address address) { return normalize_address(address).to_string(); } - std::string - addr_to_url_escaped_string(boost::asio::ip::address address) { + std::string addr_to_url_escaped_string(boost::asio::ip::address address) { address = normalize_address(address); if (address.is_v6()) { std::stringstream ss; ss << '[' << address.to_string() << ']'; return ss.str(); - } - else { + } else { return address.to_string(); } } - int - encryption_mode_for_address(boost::asio::ip::address address) { + int encryption_mode_for_address(boost::asio::ip::address address) { auto nettype = net::from_address(address.to_string()); if (nettype == net::net_e::PC || nettype == net::net_e::LAN) { return config::stream.lan_encryption_mode; - } - else { + } else { return config::stream.wan_encryption_mode; } } - host_t - host_create(af_e af, ENetAddress &addr, std::uint16_t port) { + host_t host_create(af_e af, ENetAddress &addr, std::uint16_t port) { static std::once_flag enet_init_flag; std::call_once(enet_init_flag, []() { enet_initialize(); @@ -174,7 +164,7 @@ namespace net { enet_address_set_port(&addr, port); // Maximum of 128 clients, which should be enough for anyone - auto host = host_t { enet_host_create(af == IPV4 ? AF_INET : AF_INET6, &addr, 128, 0, 0, 0) }; + auto host = host_t {enet_host_create(af == IPV4 ? AF_INET : AF_INET6, &addr, 128, 0, 0, 0)}; // Enable opportunistic QoS tagging (automatically disables if the network appears to drop tagged packets) enet_socket_set_option(host->socket, ENET_SOCKOPT_QOS, 1); @@ -182,8 +172,7 @@ namespace net { return host; } - void - free_host(ENetHost *host) { + void free_host(ENetHost *host) { std::for_each(host->peers, host->peers + host->peerCount, [](ENetPeer &peer_ref) { ENetPeer *peer = &peer_ref; @@ -195,8 +184,7 @@ namespace net { enet_host_destroy(host); } - std::uint16_t - map_port(int port) { + std::uint16_t map_port(int port) { // calculate the port from the config port auto mapped_port = (std::uint16_t)((int) config::sunshine.port + port); @@ -213,10 +201,9 @@ namespace net { * @param hostname The hostname to use for instance name generation. * @return Hostname-based instance name or "Sunshine" if hostname is invalid. */ - std::string - mdns_instance_name(const std::string_view &hostname) { + std::string mdns_instance_name(const std::string_view &hostname) { // Start with the unmodified hostname - std::string instancename { hostname.data(), hostname.size() }; + std::string instancename {hostname.data(), hostname.size()}; // Truncate to 63 characters per RFC 6763 section 7.2. if (instancename.size() > 63) { @@ -227,8 +214,7 @@ namespace net { // Replace any spaces with dashes if (instancename[i] == ' ') { instancename[i] = '-'; - } - else if (!std::isalnum(instancename[i]) && instancename[i] != '-') { + } else if (!std::isalnum(instancename[i]) && instancename[i] != '-') { // Stop at the first invalid character instancename.resize(i); break; diff --git a/src/network.h b/src/network.h index 98de905b..99aa0189 100644 --- a/src/network.h +++ b/src/network.h @@ -4,18 +4,19 @@ */ #pragma once +// standard includes #include #include +// lib includes #include - #include +// local includes #include "utility.h" namespace net { - void - free_host(ENetHost *host); + void free_host(ENetHost *host); /** * @brief Map a specified port based on the base port. @@ -26,8 +27,7 @@ namespace net { * @examples_end * @todo Ensure port is not already in use by another application. */ - std::uint16_t - map_port(int port); + std::uint16_t map_port(int port); using host_t = util::safe_ptr; using peer_t = ENetPeer *; @@ -44,32 +44,26 @@ namespace net { BOTH ///< IPv4 and IPv6 }; - net_e - from_enum_string(const std::string_view &view); - std::string_view - to_enum_string(net_e net); + net_e from_enum_string(const std::string_view &view); + std::string_view to_enum_string(net_e net); - net_e - from_address(const std::string_view &view); + net_e from_address(const std::string_view &view); - host_t - host_create(af_e af, ENetAddress &addr, std::uint16_t port); + host_t host_create(af_e af, ENetAddress &addr, std::uint16_t port); /** * @brief Get the address family enum value from a string. * @param view The config option value. * @return The address family enum value. */ - af_e - af_from_enum_string(const std::string_view &view); + af_e af_from_enum_string(const std::string_view &view); /** * @brief Get the wildcard binding address for a given address family. * @param af Address family. * @return Normalized address. */ - std::string_view - af_to_any_address_string(af_e af); + std::string_view af_to_any_address_string(af_e af); /** * @brief Convert an address to a normalized form. @@ -77,8 +71,7 @@ namespace net { * @param address The address to normalize. * @return Normalized address. */ - boost::asio::ip::address - normalize_address(boost::asio::ip::address address); + boost::asio::ip::address normalize_address(boost::asio::ip::address address); /** * @brief Get the given address in normalized string form. @@ -86,8 +79,7 @@ namespace net { * @param address The address to normalize. * @return Normalized address in string form. */ - std::string - addr_to_normalized_string(boost::asio::ip::address address); + std::string addr_to_normalized_string(boost::asio::ip::address address); /** * @brief Get the given address in a normalized form for the host portion of a URL. @@ -95,22 +87,19 @@ namespace net { * @param address The address to normalize and escape. * @return Normalized address in URL-escaped string. */ - std::string - addr_to_url_escaped_string(boost::asio::ip::address address); + std::string addr_to_url_escaped_string(boost::asio::ip::address address); /** * @brief Get the encryption mode for the given remote endpoint address. * @param address The address used to look up the desired encryption mode. * @return The WAN or LAN encryption mode, based on the provided address. */ - int - encryption_mode_for_address(boost::asio::ip::address address); + int encryption_mode_for_address(boost::asio::ip::address address); /** * @brief Returns a string for use as the instance name for mDNS. * @param hostname The hostname to use for instance name generation. * @return Hostname-based instance name or "Sunshine" if hostname is invalid. */ - std::string - mdns_instance_name(const std::string_view &hostname); + std::string mdns_instance_name(const std::string_view &hostname); } // namespace net diff --git a/src/nvenc/nvenc_base.cpp b/src/nvenc/nvenc_base.cpp index 6dd72755..b897f51d 100644 --- a/src/nvenc/nvenc_base.cpp +++ b/src/nvenc/nvenc_base.cpp @@ -1,656 +1,678 @@ -/** - * @file src/nvenc/nvenc_base.cpp - * @brief Definitions for abstract platform-agnostic base of standalone NVENC encoder. - */ -#include "nvenc_base.h" - -#include "src/config.h" -#include "src/logging.h" -#include "src/utility.h" - -#define MAKE_NVENC_VER(major, minor) ((major) | ((minor) << 24)) - -// Make sure we check backwards compatibility when bumping the Video Codec SDK version -// Things to look out for: -// - NV_ENC_*_VER definitions where the value inside NVENCAPI_STRUCT_VERSION() was increased -// - Incompatible struct changes in nvEncodeAPI.h (fields removed, semantics changed, etc.) -// - Test both old and new drivers with all supported codecs -#if NVENCAPI_VERSION != MAKE_NVENC_VER(12U, 0U) - #error Check and update NVENC code for backwards compatibility! -#endif - -namespace { - - GUID - quality_preset_guid_from_number(unsigned number) { - if (number > 7) number = 7; - - switch (number) { - case 1: - default: - return NV_ENC_PRESET_P1_GUID; - - case 2: - return NV_ENC_PRESET_P2_GUID; - - case 3: - return NV_ENC_PRESET_P3_GUID; - - case 4: - return NV_ENC_PRESET_P4_GUID; - - case 5: - return NV_ENC_PRESET_P5_GUID; - - case 6: - return NV_ENC_PRESET_P6_GUID; - - case 7: - return NV_ENC_PRESET_P7_GUID; - } - }; - - bool - equal_guids(const GUID &guid1, const GUID &guid2) { - return std::memcmp(&guid1, &guid2, sizeof(GUID)) == 0; - } - - auto - quality_preset_string_from_guid(const GUID &guid) { - if (equal_guids(guid, NV_ENC_PRESET_P1_GUID)) { - return "P1"; - } - if (equal_guids(guid, NV_ENC_PRESET_P2_GUID)) { - return "P2"; - } - if (equal_guids(guid, NV_ENC_PRESET_P3_GUID)) { - return "P3"; - } - if (equal_guids(guid, NV_ENC_PRESET_P4_GUID)) { - return "P4"; - } - if (equal_guids(guid, NV_ENC_PRESET_P5_GUID)) { - return "P5"; - } - if (equal_guids(guid, NV_ENC_PRESET_P6_GUID)) { - return "P6"; - } - if (equal_guids(guid, NV_ENC_PRESET_P7_GUID)) { - return "P7"; - } - return "Unknown"; - } - -} // namespace - -namespace nvenc { - - nvenc_base::nvenc_base(NV_ENC_DEVICE_TYPE device_type): - device_type(device_type) { - } - - nvenc_base::~nvenc_base() { - // Use destroy_encoder() instead - } - - bool - nvenc_base::create_encoder(const nvenc_config &config, const video::config_t &client_config, const nvenc_colorspace_t &colorspace, NV_ENC_BUFFER_FORMAT buffer_format) { - // Pick the minimum NvEncode API version required to support the specified codec - // to maximize driver compatibility. AV1 was introduced in SDK v12.0. - minimum_api_version = (client_config.videoFormat <= 1) ? MAKE_NVENC_VER(11U, 0U) : MAKE_NVENC_VER(12U, 0U); - - if (!nvenc && !init_library()) return false; - - if (encoder) destroy_encoder(); - auto fail_guard = util::fail_guard([this] { destroy_encoder(); }); - - encoder_params.width = client_config.width; - encoder_params.height = client_config.height; - encoder_params.buffer_format = buffer_format; - encoder_params.rfi = true; - - NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS session_params = { min_struct_version(NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS_VER) }; - session_params.device = device; - session_params.deviceType = device_type; - session_params.apiVersion = minimum_api_version; - if (nvenc_failed(nvenc->nvEncOpenEncodeSessionEx(&session_params, &encoder))) { - BOOST_LOG(error) << "NvEnc: NvEncOpenEncodeSessionEx() failed: " << last_nvenc_error_string; - return false; - } - - uint32_t encode_guid_count = 0; - if (nvenc_failed(nvenc->nvEncGetEncodeGUIDCount(encoder, &encode_guid_count))) { - BOOST_LOG(error) << "NvEnc: NvEncGetEncodeGUIDCount() failed: " << last_nvenc_error_string; - return false; - }; - - std::vector encode_guids(encode_guid_count); - if (nvenc_failed(nvenc->nvEncGetEncodeGUIDs(encoder, encode_guids.data(), encode_guids.size(), &encode_guid_count))) { - BOOST_LOG(error) << "NvEnc: NvEncGetEncodeGUIDs() failed: " << last_nvenc_error_string; - return false; - } - - NV_ENC_INITIALIZE_PARAMS init_params = { min_struct_version(NV_ENC_INITIALIZE_PARAMS_VER) }; - - switch (client_config.videoFormat) { - case 0: - // H.264 - init_params.encodeGUID = NV_ENC_CODEC_H264_GUID; - break; - - case 1: - // HEVC - init_params.encodeGUID = NV_ENC_CODEC_HEVC_GUID; - break; - - case 2: - // AV1 - init_params.encodeGUID = NV_ENC_CODEC_AV1_GUID; - break; - - default: - BOOST_LOG(error) << "NvEnc: unknown video format " << client_config.videoFormat; - return false; - } - - { - auto search_predicate = [&](const GUID &guid) { - return equal_guids(init_params.encodeGUID, guid); - }; - if (std::find_if(encode_guids.begin(), encode_guids.end(), search_predicate) == encode_guids.end()) { - BOOST_LOG(error) << "NvEnc: encoding format is not supported by the gpu"; - return false; - } - } - - auto get_encoder_cap = [&](NV_ENC_CAPS cap) { - NV_ENC_CAPS_PARAM param = { min_struct_version(NV_ENC_CAPS_PARAM_VER), cap }; - int value = 0; - nvenc->nvEncGetEncodeCaps(encoder, init_params.encodeGUID, ¶m, &value); - return value; - }; - - auto buffer_is_10bit = [&]() { - return buffer_format == NV_ENC_BUFFER_FORMAT_YUV420_10BIT || buffer_format == NV_ENC_BUFFER_FORMAT_YUV444_10BIT; - }; - - auto buffer_is_yuv444 = [&]() { - return buffer_format == NV_ENC_BUFFER_FORMAT_AYUV || buffer_format == NV_ENC_BUFFER_FORMAT_YUV444_10BIT; - }; - - { - auto supported_width = get_encoder_cap(NV_ENC_CAPS_WIDTH_MAX); - auto supported_height = get_encoder_cap(NV_ENC_CAPS_HEIGHT_MAX); - if (encoder_params.width > supported_width || encoder_params.height > supported_height) { - BOOST_LOG(error) << "NvEnc: gpu max encode resolution " << supported_width << "x" << supported_height << ", requested " << encoder_params.width << "x" << encoder_params.height; - return false; - } - } - - if (buffer_is_10bit() && !get_encoder_cap(NV_ENC_CAPS_SUPPORT_10BIT_ENCODE)) { - BOOST_LOG(error) << "NvEnc: gpu doesn't support 10-bit encode"; - return false; - } - - if (buffer_is_yuv444() && !get_encoder_cap(NV_ENC_CAPS_SUPPORT_YUV444_ENCODE)) { - BOOST_LOG(error) << "NvEnc: gpu doesn't support YUV444 encode"; - return false; - } - - if (async_event_handle && !get_encoder_cap(NV_ENC_CAPS_ASYNC_ENCODE_SUPPORT)) { - BOOST_LOG(warning) << "NvEnc: gpu doesn't support async encode"; - async_event_handle = nullptr; - } - - encoder_params.rfi = get_encoder_cap(NV_ENC_CAPS_SUPPORT_REF_PIC_INVALIDATION); - - init_params.presetGUID = quality_preset_guid_from_number(config.quality_preset); - init_params.tuningInfo = NV_ENC_TUNING_INFO_ULTRA_LOW_LATENCY; - init_params.enablePTD = 1; - init_params.enableEncodeAsync = async_event_handle ? 1 : 0; - init_params.enableWeightedPrediction = config.weighted_prediction && get_encoder_cap(NV_ENC_CAPS_SUPPORT_WEIGHTED_PREDICTION); - - init_params.encodeWidth = encoder_params.width; - init_params.darWidth = encoder_params.width; - init_params.encodeHeight = encoder_params.height; - init_params.darHeight = encoder_params.height; - init_params.frameRateNum = client_config.framerate; - init_params.frameRateDen = 1; - - NV_ENC_PRESET_CONFIG preset_config = { min_struct_version(NV_ENC_PRESET_CONFIG_VER), { min_struct_version(NV_ENC_CONFIG_VER, 7, 8) } }; - if (nvenc_failed(nvenc->nvEncGetEncodePresetConfigEx(encoder, init_params.encodeGUID, init_params.presetGUID, init_params.tuningInfo, &preset_config))) { - BOOST_LOG(error) << "NvEnc: NvEncGetEncodePresetConfigEx() failed: " << last_nvenc_error_string; - return false; - } - - NV_ENC_CONFIG enc_config = preset_config.presetCfg; - enc_config.profileGUID = NV_ENC_CODEC_PROFILE_AUTOSELECT_GUID; - enc_config.gopLength = NVENC_INFINITE_GOPLENGTH; - enc_config.frameIntervalP = 1; - enc_config.rcParams.rateControlMode = NV_ENC_PARAMS_RC_CBR; - enc_config.rcParams.zeroReorderDelay = 1; - enc_config.rcParams.enableLookahead = 0; - enc_config.rcParams.lowDelayKeyFrameScale = 1; - enc_config.rcParams.multiPass = config.two_pass == nvenc_two_pass::quarter_resolution ? NV_ENC_TWO_PASS_QUARTER_RESOLUTION : - config.two_pass == nvenc_two_pass::full_resolution ? NV_ENC_TWO_PASS_FULL_RESOLUTION : - NV_ENC_MULTI_PASS_DISABLED; - - enc_config.rcParams.enableAQ = config.adaptive_quantization; - enc_config.rcParams.averageBitRate = client_config.bitrate * 1000; - - if (get_encoder_cap(NV_ENC_CAPS_SUPPORT_CUSTOM_VBV_BUF_SIZE)) { - enc_config.rcParams.vbvBufferSize = client_config.bitrate * 1000 / client_config.framerate; - if (config.vbv_percentage_increase > 0) { - enc_config.rcParams.vbvBufferSize += enc_config.rcParams.vbvBufferSize * config.vbv_percentage_increase / 100; - } - } - - auto set_h264_hevc_common_format_config = [&](auto &format_config) { - format_config.repeatSPSPPS = 1; - format_config.idrPeriod = NVENC_INFINITE_GOPLENGTH; - format_config.sliceMode = 3; - format_config.sliceModeData = client_config.slicesPerFrame; - if (buffer_is_yuv444()) { - format_config.chromaFormatIDC = 3; - } - format_config.enableFillerDataInsertion = config.insert_filler_data; - }; - - auto set_ref_frames = [&](uint32_t &ref_frames_option, NV_ENC_NUM_REF_FRAMES &L0_option, uint32_t ref_frames_default) { - if (client_config.numRefFrames > 0) { - ref_frames_option = client_config.numRefFrames; - } - else { - ref_frames_option = ref_frames_default; - } - if (ref_frames_option > 0 && !get_encoder_cap(NV_ENC_CAPS_SUPPORT_MULTIPLE_REF_FRAMES)) { - ref_frames_option = 1; - encoder_params.rfi = false; - } - encoder_params.ref_frames_in_dpb = ref_frames_option; - // This limits ref frames any frame can use to 1, but allows larger buffer size for fallback if some frames are invalidated through rfi - L0_option = NV_ENC_NUM_REF_FRAMES_1; - }; - - auto set_minqp_if_enabled = [&](int value) { - if (config.enable_min_qp) { - enc_config.rcParams.enableMinQP = 1; - enc_config.rcParams.minQP.qpInterP = value; - enc_config.rcParams.minQP.qpIntra = value; - } - }; - - auto fill_h264_hevc_vui = [&](auto &vui_config) { - vui_config.videoSignalTypePresentFlag = 1; - vui_config.videoFormat = NV_ENC_VUI_VIDEO_FORMAT_UNSPECIFIED; - vui_config.videoFullRangeFlag = colorspace.full_range; - vui_config.colourDescriptionPresentFlag = 1; - vui_config.colourPrimaries = colorspace.primaries; - vui_config.transferCharacteristics = colorspace.tranfer_function; - vui_config.colourMatrix = colorspace.matrix; - vui_config.chromaSampleLocationFlag = buffer_is_yuv444() ? 0 : 1; - vui_config.chromaSampleLocationTop = 0; - vui_config.chromaSampleLocationBot = 0; - }; - - switch (client_config.videoFormat) { - case 0: { - // H.264 - enc_config.profileGUID = buffer_is_yuv444() ? NV_ENC_H264_PROFILE_HIGH_444_GUID : NV_ENC_H264_PROFILE_HIGH_GUID; - auto &format_config = enc_config.encodeCodecConfig.h264Config; - set_h264_hevc_common_format_config(format_config); - if (config.h264_cavlc || !get_encoder_cap(NV_ENC_CAPS_SUPPORT_CABAC)) { - format_config.entropyCodingMode = NV_ENC_H264_ENTROPY_CODING_MODE_CAVLC; - } - else { - format_config.entropyCodingMode = NV_ENC_H264_ENTROPY_CODING_MODE_CABAC; - } - set_ref_frames(format_config.maxNumRefFrames, format_config.numRefL0, 5); - set_minqp_if_enabled(config.min_qp_h264); - fill_h264_hevc_vui(format_config.h264VUIParameters); - break; - } - - case 1: { - // HEVC - auto &format_config = enc_config.encodeCodecConfig.hevcConfig; - set_h264_hevc_common_format_config(format_config); - if (buffer_is_10bit()) { - format_config.pixelBitDepthMinus8 = 2; - } - set_ref_frames(format_config.maxNumRefFramesInDPB, format_config.numRefL0, 5); - set_minqp_if_enabled(config.min_qp_hevc); - fill_h264_hevc_vui(format_config.hevcVUIParameters); - if (client_config.enableIntraRefresh == 1) { - if (get_encoder_cap(NV_ENC_CAPS_SUPPORT_INTRA_REFRESH)) { - format_config.enableIntraRefresh = 1; - format_config.intraRefreshPeriod = 300; - format_config.intraRefreshCnt = 299; - if (get_encoder_cap(NV_ENC_CAPS_SINGLE_SLICE_INTRA_REFRESH)) { - format_config.singleSliceIntraRefresh = 1; - } - else { - BOOST_LOG(warning) << "NvEnc: Single Slice Intra Refresh not supported"; - } - } - else { - BOOST_LOG(error) << "NvEnc: Client asked for intra-refresh but the encoder does not support intra-refresh"; - } - } - break; - } - - case 2: { - // AV1 - auto &format_config = enc_config.encodeCodecConfig.av1Config; - format_config.repeatSeqHdr = 1; - format_config.idrPeriod = NVENC_INFINITE_GOPLENGTH; - if (buffer_is_yuv444()) { - format_config.chromaFormatIDC = 3; - } - format_config.enableBitstreamPadding = config.insert_filler_data; - if (buffer_is_10bit()) { - format_config.inputPixelBitDepthMinus8 = 2; - format_config.pixelBitDepthMinus8 = 2; - } - format_config.colorPrimaries = colorspace.primaries; - format_config.transferCharacteristics = colorspace.tranfer_function; - format_config.matrixCoefficients = colorspace.matrix; - format_config.colorRange = colorspace.full_range; - format_config.chromaSamplePosition = buffer_is_yuv444() ? 0 : 1; - set_ref_frames(format_config.maxNumRefFramesInDPB, format_config.numFwdRefs, 8); - set_minqp_if_enabled(config.min_qp_av1); - - if (client_config.slicesPerFrame > 1) { - // NVENC only supports slice counts that are powers of two, so we'll pick powers of two - // with bias to rows due to hopefully more similar macroblocks with a row vs a column. - format_config.numTileRows = std::pow(2, std::ceil(std::log2(client_config.slicesPerFrame) / 2)); - format_config.numTileColumns = std::pow(2, std::floor(std::log2(client_config.slicesPerFrame) / 2)); - } - break; - } - } - - init_params.encodeConfig = &enc_config; - - if (nvenc_failed(nvenc->nvEncInitializeEncoder(encoder, &init_params))) { - BOOST_LOG(error) << "NvEnc: NvEncInitializeEncoder() failed: " << last_nvenc_error_string; - return false; - } - - if (async_event_handle) { - NV_ENC_EVENT_PARAMS event_params = { min_struct_version(NV_ENC_EVENT_PARAMS_VER) }; - event_params.completionEvent = async_event_handle; - if (nvenc_failed(nvenc->nvEncRegisterAsyncEvent(encoder, &event_params))) { - BOOST_LOG(error) << "NvEnc: NvEncRegisterAsyncEvent() failed: " << last_nvenc_error_string; - return false; - } - } - - NV_ENC_CREATE_BITSTREAM_BUFFER create_bitstream_buffer = { min_struct_version(NV_ENC_CREATE_BITSTREAM_BUFFER_VER) }; - if (nvenc_failed(nvenc->nvEncCreateBitstreamBuffer(encoder, &create_bitstream_buffer))) { - BOOST_LOG(error) << "NvEnc: NvEncCreateBitstreamBuffer() failed: " << last_nvenc_error_string; - return false; - } - output_bitstream = create_bitstream_buffer.bitstreamBuffer; - - if (!create_and_register_input_buffer()) { - return false; - } - - { - auto f = stat_trackers::two_digits_after_decimal(); - BOOST_LOG(debug) << "NvEnc: requested encoded frame size " << f % (client_config.bitrate / 8. / client_config.framerate) << " kB"; - } - - { - auto video_format_string = client_config.videoFormat == 0 ? "H.264 " : - client_config.videoFormat == 1 ? "HEVC " : - client_config.videoFormat == 2 ? "AV1 " : - " "; - std::string extra; - if (init_params.enableEncodeAsync) extra += " async"; - if (buffer_is_yuv444()) extra += " yuv444"; - if (buffer_is_10bit()) extra += " 10-bit"; - if (enc_config.rcParams.multiPass != NV_ENC_MULTI_PASS_DISABLED) extra += " two-pass"; - if (config.vbv_percentage_increase > 0 && get_encoder_cap(NV_ENC_CAPS_SUPPORT_CUSTOM_VBV_BUF_SIZE)) extra += " vbv+" + std::to_string(config.vbv_percentage_increase); - if (encoder_params.rfi) extra += " rfi"; - if (init_params.enableWeightedPrediction) extra += " weighted-prediction"; - if (enc_config.rcParams.enableAQ) extra += " spatial-aq"; - if (enc_config.rcParams.enableMinQP) extra += " qpmin=" + std::to_string(enc_config.rcParams.minQP.qpInterP); - if (config.insert_filler_data) extra += " filler-data"; - - BOOST_LOG(info) << "NvEnc: created encoder " << video_format_string << quality_preset_string_from_guid(init_params.presetGUID) << extra; - } - - encoder_state = {}; - fail_guard.disable(); - return true; - } - - void - nvenc_base::destroy_encoder() { - if (output_bitstream) { - if (nvenc_failed(nvenc->nvEncDestroyBitstreamBuffer(encoder, output_bitstream))) { - BOOST_LOG(error) << "NvEnc: NvEncDestroyBitstreamBuffer() failed: " << last_nvenc_error_string; - } - output_bitstream = nullptr; - } - if (encoder && async_event_handle) { - NV_ENC_EVENT_PARAMS event_params = { min_struct_version(NV_ENC_EVENT_PARAMS_VER) }; - event_params.completionEvent = async_event_handle; - if (nvenc_failed(nvenc->nvEncUnregisterAsyncEvent(encoder, &event_params))) { - BOOST_LOG(error) << "NvEnc: NvEncUnregisterAsyncEvent() failed: " << last_nvenc_error_string; - } - } - if (registered_input_buffer) { - if (nvenc_failed(nvenc->nvEncUnregisterResource(encoder, registered_input_buffer))) { - BOOST_LOG(error) << "NvEnc: NvEncUnregisterResource() failed: " << last_nvenc_error_string; - } - registered_input_buffer = nullptr; - } - if (encoder) { - if (nvenc_failed(nvenc->nvEncDestroyEncoder(encoder))) { - BOOST_LOG(error) << "NvEnc: NvEncDestroyEncoder() failed: " << last_nvenc_error_string; - } - encoder = nullptr; - } - - encoder_state = {}; - encoder_params = {}; - } - - nvenc_encoded_frame - nvenc_base::encode_frame(uint64_t frame_index, bool force_idr) { - if (!encoder) { - return {}; - } - - assert(registered_input_buffer); - assert(output_bitstream); - - if (!synchronize_input_buffer()) { - BOOST_LOG(error) << "NvEnc: failed to synchronize input buffer"; - return {}; - } - - NV_ENC_MAP_INPUT_RESOURCE mapped_input_buffer = { min_struct_version(NV_ENC_MAP_INPUT_RESOURCE_VER) }; - mapped_input_buffer.registeredResource = registered_input_buffer; - - if (nvenc_failed(nvenc->nvEncMapInputResource(encoder, &mapped_input_buffer))) { - BOOST_LOG(error) << "NvEnc: NvEncMapInputResource() failed: " << last_nvenc_error_string; - return {}; - } - auto unmap_guard = util::fail_guard([&] { - if (nvenc_failed(nvenc->nvEncUnmapInputResource(encoder, mapped_input_buffer.mappedResource))) { - BOOST_LOG(error) << "NvEnc: NvEncUnmapInputResource() failed: " << last_nvenc_error_string; - } - }); - - NV_ENC_PIC_PARAMS pic_params = { min_struct_version(NV_ENC_PIC_PARAMS_VER, 4, 6) }; - pic_params.inputWidth = encoder_params.width; - pic_params.inputHeight = encoder_params.height; - pic_params.encodePicFlags = force_idr ? NV_ENC_PIC_FLAG_FORCEIDR : 0; - pic_params.inputTimeStamp = frame_index; - pic_params.pictureStruct = NV_ENC_PIC_STRUCT_FRAME; - pic_params.inputBuffer = mapped_input_buffer.mappedResource; - pic_params.bufferFmt = mapped_input_buffer.mappedBufferFmt; - pic_params.outputBitstream = output_bitstream; - pic_params.completionEvent = async_event_handle; - - if (nvenc_failed(nvenc->nvEncEncodePicture(encoder, &pic_params))) { - BOOST_LOG(error) << "NvEnc: NvEncEncodePicture() failed: " << last_nvenc_error_string; - return {}; - } - - NV_ENC_LOCK_BITSTREAM lock_bitstream = { min_struct_version(NV_ENC_LOCK_BITSTREAM_VER, 1, 2) }; - lock_bitstream.outputBitstream = output_bitstream; - lock_bitstream.doNotWait = 0; - - if (async_event_handle && !wait_for_async_event(100)) { - BOOST_LOG(error) << "NvEnc: frame " << frame_index << " encode wait timeout"; - return {}; - } - - if (nvenc_failed(nvenc->nvEncLockBitstream(encoder, &lock_bitstream))) { - BOOST_LOG(error) << "NvEnc: NvEncLockBitstream() failed: " << last_nvenc_error_string; - return {}; - } - - auto data_pointer = (uint8_t *) lock_bitstream.bitstreamBufferPtr; - nvenc_encoded_frame encoded_frame { - { data_pointer, data_pointer + lock_bitstream.bitstreamSizeInBytes }, - lock_bitstream.outputTimeStamp, - lock_bitstream.pictureType == NV_ENC_PIC_TYPE_IDR, - encoder_state.rfi_needs_confirmation, - }; - - if (encoder_state.rfi_needs_confirmation) { - // Invalidation request has been fulfilled, and video network packet will be marked as such - encoder_state.rfi_needs_confirmation = false; - } - - encoder_state.last_encoded_frame_index = frame_index; - - if (encoded_frame.idr) { - BOOST_LOG(debug) << "NvEnc: idr frame " << encoded_frame.frame_index; - } - - if (nvenc_failed(nvenc->nvEncUnlockBitstream(encoder, lock_bitstream.outputBitstream))) { - BOOST_LOG(error) << "NvEnc: NvEncUnlockBitstream() failed: " << last_nvenc_error_string; - } - - encoder_state.frame_size_logger.collect_and_log(encoded_frame.data.size() / 1000.); - - return encoded_frame; - } - - bool - nvenc_base::invalidate_ref_frames(uint64_t first_frame, uint64_t last_frame) { - if (!encoder || !encoder_params.rfi) return false; - - if (first_frame >= encoder_state.last_rfi_range.first && - last_frame <= encoder_state.last_rfi_range.second) { - BOOST_LOG(debug) << "NvEnc: rfi request " << first_frame << "-" << last_frame << " already done"; - return true; - } - - encoder_state.rfi_needs_confirmation = true; - - if (last_frame < first_frame) { - BOOST_LOG(error) << "NvEnc: invaid rfi request " << first_frame << "-" << last_frame << ", generating IDR"; - return false; - } - - BOOST_LOG(debug) << "NvEnc: rfi request " << first_frame << "-" << last_frame << " expanding to last encoded frame " << encoder_state.last_encoded_frame_index; - last_frame = encoder_state.last_encoded_frame_index; - - encoder_state.last_rfi_range = { first_frame, last_frame }; - - if (last_frame - first_frame + 1 >= encoder_params.ref_frames_in_dpb) { - BOOST_LOG(debug) << "NvEnc: rfi request too large, generating IDR"; - return false; - } - - for (auto i = first_frame; i <= last_frame; i++) { - if (nvenc_failed(nvenc->nvEncInvalidateRefFrames(encoder, i))) { - BOOST_LOG(error) << "NvEnc: NvEncInvalidateRefFrames() " << i << " failed: " << last_nvenc_error_string; - return false; - } - } - - return true; - } - - bool - nvenc_base::nvenc_failed(NVENCSTATUS status) { - auto status_string = [](NVENCSTATUS status) -> std::string { - switch (status) { -#define nvenc_status_case(x) \ - case x: \ - return #x; - nvenc_status_case(NV_ENC_SUCCESS); - nvenc_status_case(NV_ENC_ERR_NO_ENCODE_DEVICE); - nvenc_status_case(NV_ENC_ERR_UNSUPPORTED_DEVICE); - nvenc_status_case(NV_ENC_ERR_INVALID_ENCODERDEVICE); - nvenc_status_case(NV_ENC_ERR_INVALID_DEVICE); - nvenc_status_case(NV_ENC_ERR_DEVICE_NOT_EXIST); - nvenc_status_case(NV_ENC_ERR_INVALID_PTR); - nvenc_status_case(NV_ENC_ERR_INVALID_EVENT); - nvenc_status_case(NV_ENC_ERR_INVALID_PARAM); - nvenc_status_case(NV_ENC_ERR_INVALID_CALL); - nvenc_status_case(NV_ENC_ERR_OUT_OF_MEMORY); - nvenc_status_case(NV_ENC_ERR_ENCODER_NOT_INITIALIZED); - nvenc_status_case(NV_ENC_ERR_UNSUPPORTED_PARAM); - nvenc_status_case(NV_ENC_ERR_LOCK_BUSY); - nvenc_status_case(NV_ENC_ERR_NOT_ENOUGH_BUFFER); - nvenc_status_case(NV_ENC_ERR_INVALID_VERSION); - nvenc_status_case(NV_ENC_ERR_MAP_FAILED); - nvenc_status_case(NV_ENC_ERR_NEED_MORE_INPUT); - nvenc_status_case(NV_ENC_ERR_ENCODER_BUSY); - nvenc_status_case(NV_ENC_ERR_EVENT_NOT_REGISTERD); - nvenc_status_case(NV_ENC_ERR_GENERIC); - nvenc_status_case(NV_ENC_ERR_INCOMPATIBLE_CLIENT_KEY); - nvenc_status_case(NV_ENC_ERR_UNIMPLEMENTED); - nvenc_status_case(NV_ENC_ERR_RESOURCE_REGISTER_FAILED); - nvenc_status_case(NV_ENC_ERR_RESOURCE_NOT_REGISTERED); - nvenc_status_case(NV_ENC_ERR_RESOURCE_NOT_MAPPED); - // Newer versions of sdk may add more constants, look for them at the end of NVENCSTATUS enum -#undef nvenc_status_case - default: - return std::to_string(status); - } - }; - - last_nvenc_error_string.clear(); - if (status != NV_ENC_SUCCESS) { - /* This API function gives broken strings more often than not - if (nvenc && encoder) { - last_nvenc_error_string = nvenc->nvEncGetLastErrorString(encoder); - if (!last_nvenc_error_string.empty()) last_nvenc_error_string += " "; - } - */ - last_nvenc_error_string += status_string(status); - return true; - } - - return false; - } - - uint32_t - nvenc_base::min_struct_version(uint32_t version, uint32_t v11_struct_version, uint32_t v12_struct_version) { - assert(minimum_api_version); - - // Mask off and replace the original NVENCAPI_VERSION - version &= ~NVENCAPI_VERSION; - version |= minimum_api_version; - - // If there's a struct version override, apply that too - if (v11_struct_version || v12_struct_version) { - version &= ~(0xFFu << 16); - version |= (((minimum_api_version & 0xFF) >= 12) ? v12_struct_version : v11_struct_version) << 16; - } - - return version; - } -} // namespace nvenc +/** + * @file src/nvenc/nvenc_base.cpp + * @brief Definitions for abstract platform-agnostic base of standalone NVENC encoder. + */ +// this include +#include "nvenc_base.h" + +// local includes +#include "src/config.h" +#include "src/logging.h" +#include "src/utility.h" + +#define MAKE_NVENC_VER(major, minor) ((major) | ((minor) << 24)) + +// Make sure we check backwards compatibility when bumping the Video Codec SDK version +// Things to look out for: +// - NV_ENC_*_VER definitions where the value inside NVENCAPI_STRUCT_VERSION() was increased +// - Incompatible struct changes in nvEncodeAPI.h (fields removed, semantics changed, etc.) +// - Test both old and new drivers with all supported codecs +#if NVENCAPI_VERSION != MAKE_NVENC_VER(12U, 0U) + #error Check and update NVENC code for backwards compatibility! +#endif + +namespace { + + GUID quality_preset_guid_from_number(unsigned number) { + if (number > 7) { + number = 7; + } + + switch (number) { + case 1: + default: + return NV_ENC_PRESET_P1_GUID; + + case 2: + return NV_ENC_PRESET_P2_GUID; + + case 3: + return NV_ENC_PRESET_P3_GUID; + + case 4: + return NV_ENC_PRESET_P4_GUID; + + case 5: + return NV_ENC_PRESET_P5_GUID; + + case 6: + return NV_ENC_PRESET_P6_GUID; + + case 7: + return NV_ENC_PRESET_P7_GUID; + } + }; + + bool equal_guids(const GUID &guid1, const GUID &guid2) { + return std::memcmp(&guid1, &guid2, sizeof(GUID)) == 0; + } + + auto quality_preset_string_from_guid(const GUID &guid) { + if (equal_guids(guid, NV_ENC_PRESET_P1_GUID)) { + return "P1"; + } + if (equal_guids(guid, NV_ENC_PRESET_P2_GUID)) { + return "P2"; + } + if (equal_guids(guid, NV_ENC_PRESET_P3_GUID)) { + return "P3"; + } + if (equal_guids(guid, NV_ENC_PRESET_P4_GUID)) { + return "P4"; + } + if (equal_guids(guid, NV_ENC_PRESET_P5_GUID)) { + return "P5"; + } + if (equal_guids(guid, NV_ENC_PRESET_P6_GUID)) { + return "P6"; + } + if (equal_guids(guid, NV_ENC_PRESET_P7_GUID)) { + return "P7"; + } + return "Unknown"; + } + +} // namespace + +namespace nvenc { + + nvenc_base::nvenc_base(NV_ENC_DEVICE_TYPE device_type): + device_type(device_type) { + } + + nvenc_base::~nvenc_base() { + // Use destroy_encoder() instead + } + + bool nvenc_base::create_encoder(const nvenc_config &config, const video::config_t &client_config, const nvenc_colorspace_t &colorspace, NV_ENC_BUFFER_FORMAT buffer_format) { + // Pick the minimum NvEncode API version required to support the specified codec + // to maximize driver compatibility. AV1 was introduced in SDK v12.0. + minimum_api_version = (client_config.videoFormat <= 1) ? MAKE_NVENC_VER(11U, 0U) : MAKE_NVENC_VER(12U, 0U); + + if (!nvenc && !init_library()) { + return false; + } + + if (encoder) { + destroy_encoder(); + } + auto fail_guard = util::fail_guard([this] { + destroy_encoder(); + }); + + encoder_params.width = client_config.width; + encoder_params.height = client_config.height; + encoder_params.buffer_format = buffer_format; + encoder_params.rfi = true; + + NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS session_params = {min_struct_version(NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS_VER)}; + session_params.device = device; + session_params.deviceType = device_type; + session_params.apiVersion = minimum_api_version; + if (nvenc_failed(nvenc->nvEncOpenEncodeSessionEx(&session_params, &encoder))) { + BOOST_LOG(error) << "NvEnc: NvEncOpenEncodeSessionEx() failed: " << last_nvenc_error_string; + return false; + } + + uint32_t encode_guid_count = 0; + if (nvenc_failed(nvenc->nvEncGetEncodeGUIDCount(encoder, &encode_guid_count))) { + BOOST_LOG(error) << "NvEnc: NvEncGetEncodeGUIDCount() failed: " << last_nvenc_error_string; + return false; + }; + + std::vector encode_guids(encode_guid_count); + if (nvenc_failed(nvenc->nvEncGetEncodeGUIDs(encoder, encode_guids.data(), encode_guids.size(), &encode_guid_count))) { + BOOST_LOG(error) << "NvEnc: NvEncGetEncodeGUIDs() failed: " << last_nvenc_error_string; + return false; + } + + NV_ENC_INITIALIZE_PARAMS init_params = {min_struct_version(NV_ENC_INITIALIZE_PARAMS_VER)}; + + switch (client_config.videoFormat) { + case 0: + // H.264 + init_params.encodeGUID = NV_ENC_CODEC_H264_GUID; + break; + + case 1: + // HEVC + init_params.encodeGUID = NV_ENC_CODEC_HEVC_GUID; + break; + + case 2: + // AV1 + init_params.encodeGUID = NV_ENC_CODEC_AV1_GUID; + break; + + default: + BOOST_LOG(error) << "NvEnc: unknown video format " << client_config.videoFormat; + return false; + } + + { + auto search_predicate = [&](const GUID &guid) { + return equal_guids(init_params.encodeGUID, guid); + }; + if (std::find_if(encode_guids.begin(), encode_guids.end(), search_predicate) == encode_guids.end()) { + BOOST_LOG(error) << "NvEnc: encoding format is not supported by the gpu"; + return false; + } + } + + auto get_encoder_cap = [&](NV_ENC_CAPS cap) { + NV_ENC_CAPS_PARAM param = {min_struct_version(NV_ENC_CAPS_PARAM_VER), cap}; + int value = 0; + nvenc->nvEncGetEncodeCaps(encoder, init_params.encodeGUID, ¶m, &value); + return value; + }; + + auto buffer_is_10bit = [&]() { + return buffer_format == NV_ENC_BUFFER_FORMAT_YUV420_10BIT || buffer_format == NV_ENC_BUFFER_FORMAT_YUV444_10BIT; + }; + + auto buffer_is_yuv444 = [&]() { + return buffer_format == NV_ENC_BUFFER_FORMAT_AYUV || buffer_format == NV_ENC_BUFFER_FORMAT_YUV444_10BIT; + }; + + { + auto supported_width = get_encoder_cap(NV_ENC_CAPS_WIDTH_MAX); + auto supported_height = get_encoder_cap(NV_ENC_CAPS_HEIGHT_MAX); + if (encoder_params.width > supported_width || encoder_params.height > supported_height) { + BOOST_LOG(error) << "NvEnc: gpu max encode resolution " << supported_width << "x" << supported_height << ", requested " << encoder_params.width << "x" << encoder_params.height; + return false; + } + } + + if (buffer_is_10bit() && !get_encoder_cap(NV_ENC_CAPS_SUPPORT_10BIT_ENCODE)) { + BOOST_LOG(error) << "NvEnc: gpu doesn't support 10-bit encode"; + return false; + } + + if (buffer_is_yuv444() && !get_encoder_cap(NV_ENC_CAPS_SUPPORT_YUV444_ENCODE)) { + BOOST_LOG(error) << "NvEnc: gpu doesn't support YUV444 encode"; + return false; + } + + if (async_event_handle && !get_encoder_cap(NV_ENC_CAPS_ASYNC_ENCODE_SUPPORT)) { + BOOST_LOG(warning) << "NvEnc: gpu doesn't support async encode"; + async_event_handle = nullptr; + } + + encoder_params.rfi = get_encoder_cap(NV_ENC_CAPS_SUPPORT_REF_PIC_INVALIDATION); + + init_params.presetGUID = quality_preset_guid_from_number(config.quality_preset); + init_params.tuningInfo = NV_ENC_TUNING_INFO_ULTRA_LOW_LATENCY; + init_params.enablePTD = 1; + init_params.enableEncodeAsync = async_event_handle ? 1 : 0; + init_params.enableWeightedPrediction = config.weighted_prediction && get_encoder_cap(NV_ENC_CAPS_SUPPORT_WEIGHTED_PREDICTION); + + init_params.encodeWidth = encoder_params.width; + init_params.darWidth = encoder_params.width; + init_params.encodeHeight = encoder_params.height; + init_params.darHeight = encoder_params.height; + init_params.frameRateNum = client_config.framerate; + init_params.frameRateDen = 1; + + NV_ENC_PRESET_CONFIG preset_config = {min_struct_version(NV_ENC_PRESET_CONFIG_VER), {min_struct_version(NV_ENC_CONFIG_VER, 7, 8)}}; + if (nvenc_failed(nvenc->nvEncGetEncodePresetConfigEx(encoder, init_params.encodeGUID, init_params.presetGUID, init_params.tuningInfo, &preset_config))) { + BOOST_LOG(error) << "NvEnc: NvEncGetEncodePresetConfigEx() failed: " << last_nvenc_error_string; + return false; + } + + NV_ENC_CONFIG enc_config = preset_config.presetCfg; + enc_config.profileGUID = NV_ENC_CODEC_PROFILE_AUTOSELECT_GUID; + enc_config.gopLength = NVENC_INFINITE_GOPLENGTH; + enc_config.frameIntervalP = 1; + enc_config.rcParams.rateControlMode = NV_ENC_PARAMS_RC_CBR; + enc_config.rcParams.zeroReorderDelay = 1; + enc_config.rcParams.enableLookahead = 0; + enc_config.rcParams.lowDelayKeyFrameScale = 1; + enc_config.rcParams.multiPass = config.two_pass == nvenc_two_pass::quarter_resolution ? NV_ENC_TWO_PASS_QUARTER_RESOLUTION : + config.two_pass == nvenc_two_pass::full_resolution ? NV_ENC_TWO_PASS_FULL_RESOLUTION : + NV_ENC_MULTI_PASS_DISABLED; + + enc_config.rcParams.enableAQ = config.adaptive_quantization; + enc_config.rcParams.averageBitRate = client_config.bitrate * 1000; + + if (get_encoder_cap(NV_ENC_CAPS_SUPPORT_CUSTOM_VBV_BUF_SIZE)) { + enc_config.rcParams.vbvBufferSize = client_config.bitrate * 1000 / client_config.framerate; + if (config.vbv_percentage_increase > 0) { + enc_config.rcParams.vbvBufferSize += enc_config.rcParams.vbvBufferSize * config.vbv_percentage_increase / 100; + } + } + + auto set_h264_hevc_common_format_config = [&](auto &format_config) { + format_config.repeatSPSPPS = 1; + format_config.idrPeriod = NVENC_INFINITE_GOPLENGTH; + format_config.sliceMode = 3; + format_config.sliceModeData = client_config.slicesPerFrame; + if (buffer_is_yuv444()) { + format_config.chromaFormatIDC = 3; + } + format_config.enableFillerDataInsertion = config.insert_filler_data; + }; + + auto set_ref_frames = [&](uint32_t &ref_frames_option, NV_ENC_NUM_REF_FRAMES &L0_option, uint32_t ref_frames_default) { + if (client_config.numRefFrames > 0) { + ref_frames_option = client_config.numRefFrames; + } else { + ref_frames_option = ref_frames_default; + } + if (ref_frames_option > 0 && !get_encoder_cap(NV_ENC_CAPS_SUPPORT_MULTIPLE_REF_FRAMES)) { + ref_frames_option = 1; + encoder_params.rfi = false; + } + encoder_params.ref_frames_in_dpb = ref_frames_option; + // This limits ref frames any frame can use to 1, but allows larger buffer size for fallback if some frames are invalidated through rfi + L0_option = NV_ENC_NUM_REF_FRAMES_1; + }; + + auto set_minqp_if_enabled = [&](int value) { + if (config.enable_min_qp) { + enc_config.rcParams.enableMinQP = 1; + enc_config.rcParams.minQP.qpInterP = value; + enc_config.rcParams.minQP.qpIntra = value; + } + }; + + auto fill_h264_hevc_vui = [&](auto &vui_config) { + vui_config.videoSignalTypePresentFlag = 1; + vui_config.videoFormat = NV_ENC_VUI_VIDEO_FORMAT_UNSPECIFIED; + vui_config.videoFullRangeFlag = colorspace.full_range; + vui_config.colourDescriptionPresentFlag = 1; + vui_config.colourPrimaries = colorspace.primaries; + vui_config.transferCharacteristics = colorspace.tranfer_function; + vui_config.colourMatrix = colorspace.matrix; + vui_config.chromaSampleLocationFlag = buffer_is_yuv444() ? 0 : 1; + vui_config.chromaSampleLocationTop = 0; + vui_config.chromaSampleLocationBot = 0; + }; + + switch (client_config.videoFormat) { + case 0: + { + // H.264 + enc_config.profileGUID = buffer_is_yuv444() ? NV_ENC_H264_PROFILE_HIGH_444_GUID : NV_ENC_H264_PROFILE_HIGH_GUID; + auto &format_config = enc_config.encodeCodecConfig.h264Config; + set_h264_hevc_common_format_config(format_config); + if (config.h264_cavlc || !get_encoder_cap(NV_ENC_CAPS_SUPPORT_CABAC)) { + format_config.entropyCodingMode = NV_ENC_H264_ENTROPY_CODING_MODE_CAVLC; + } else { + format_config.entropyCodingMode = NV_ENC_H264_ENTROPY_CODING_MODE_CABAC; + } + set_ref_frames(format_config.maxNumRefFrames, format_config.numRefL0, 5); + set_minqp_if_enabled(config.min_qp_h264); + fill_h264_hevc_vui(format_config.h264VUIParameters); + break; + } + + case 1: + { + // HEVC + auto &format_config = enc_config.encodeCodecConfig.hevcConfig; + set_h264_hevc_common_format_config(format_config); + if (buffer_is_10bit()) { + format_config.pixelBitDepthMinus8 = 2; + } + set_ref_frames(format_config.maxNumRefFramesInDPB, format_config.numRefL0, 5); + set_minqp_if_enabled(config.min_qp_hevc); + fill_h264_hevc_vui(format_config.hevcVUIParameters); + if (client_config.enableIntraRefresh == 1) { + if (get_encoder_cap(NV_ENC_CAPS_SUPPORT_INTRA_REFRESH)) { + format_config.enableIntraRefresh = 1; + format_config.intraRefreshPeriod = 300; + format_config.intraRefreshCnt = 299; + if (get_encoder_cap(NV_ENC_CAPS_SINGLE_SLICE_INTRA_REFRESH)) { + format_config.singleSliceIntraRefresh = 1; + } else { + BOOST_LOG(warning) << "NvEnc: Single Slice Intra Refresh not supported"; + } + } else { + BOOST_LOG(error) << "NvEnc: Client asked for intra-refresh but the encoder does not support intra-refresh"; + } + } + break; + } + + case 2: + { + // AV1 + auto &format_config = enc_config.encodeCodecConfig.av1Config; + format_config.repeatSeqHdr = 1; + format_config.idrPeriod = NVENC_INFINITE_GOPLENGTH; + if (buffer_is_yuv444()) { + format_config.chromaFormatIDC = 3; + } + format_config.enableBitstreamPadding = config.insert_filler_data; + if (buffer_is_10bit()) { + format_config.inputPixelBitDepthMinus8 = 2; + format_config.pixelBitDepthMinus8 = 2; + } + format_config.colorPrimaries = colorspace.primaries; + format_config.transferCharacteristics = colorspace.tranfer_function; + format_config.matrixCoefficients = colorspace.matrix; + format_config.colorRange = colorspace.full_range; + format_config.chromaSamplePosition = buffer_is_yuv444() ? 0 : 1; + set_ref_frames(format_config.maxNumRefFramesInDPB, format_config.numFwdRefs, 8); + set_minqp_if_enabled(config.min_qp_av1); + + if (client_config.slicesPerFrame > 1) { + // NVENC only supports slice counts that are powers of two, so we'll pick powers of two + // with bias to rows due to hopefully more similar macroblocks with a row vs a column. + format_config.numTileRows = std::pow(2, std::ceil(std::log2(client_config.slicesPerFrame) / 2)); + format_config.numTileColumns = std::pow(2, std::floor(std::log2(client_config.slicesPerFrame) / 2)); + } + break; + } + } + + init_params.encodeConfig = &enc_config; + + if (nvenc_failed(nvenc->nvEncInitializeEncoder(encoder, &init_params))) { + BOOST_LOG(error) << "NvEnc: NvEncInitializeEncoder() failed: " << last_nvenc_error_string; + return false; + } + + if (async_event_handle) { + NV_ENC_EVENT_PARAMS event_params = {min_struct_version(NV_ENC_EVENT_PARAMS_VER)}; + event_params.completionEvent = async_event_handle; + if (nvenc_failed(nvenc->nvEncRegisterAsyncEvent(encoder, &event_params))) { + BOOST_LOG(error) << "NvEnc: NvEncRegisterAsyncEvent() failed: " << last_nvenc_error_string; + return false; + } + } + + NV_ENC_CREATE_BITSTREAM_BUFFER create_bitstream_buffer = {min_struct_version(NV_ENC_CREATE_BITSTREAM_BUFFER_VER)}; + if (nvenc_failed(nvenc->nvEncCreateBitstreamBuffer(encoder, &create_bitstream_buffer))) { + BOOST_LOG(error) << "NvEnc: NvEncCreateBitstreamBuffer() failed: " << last_nvenc_error_string; + return false; + } + output_bitstream = create_bitstream_buffer.bitstreamBuffer; + + if (!create_and_register_input_buffer()) { + return false; + } + + { + auto f = stat_trackers::two_digits_after_decimal(); + BOOST_LOG(debug) << "NvEnc: requested encoded frame size " << f % (client_config.bitrate / 8. / client_config.framerate) << " kB"; + } + + { + auto video_format_string = client_config.videoFormat == 0 ? "H.264 " : + client_config.videoFormat == 1 ? "HEVC " : + client_config.videoFormat == 2 ? "AV1 " : + " "; + std::string extra; + if (init_params.enableEncodeAsync) { + extra += " async"; + } + if (buffer_is_yuv444()) { + extra += " yuv444"; + } + if (buffer_is_10bit()) { + extra += " 10-bit"; + } + if (enc_config.rcParams.multiPass != NV_ENC_MULTI_PASS_DISABLED) { + extra += " two-pass"; + } + if (config.vbv_percentage_increase > 0 && get_encoder_cap(NV_ENC_CAPS_SUPPORT_CUSTOM_VBV_BUF_SIZE)) { + extra += " vbv+" + std::to_string(config.vbv_percentage_increase); + } + if (encoder_params.rfi) { + extra += " rfi"; + } + if (init_params.enableWeightedPrediction) { + extra += " weighted-prediction"; + } + if (enc_config.rcParams.enableAQ) { + extra += " spatial-aq"; + } + if (enc_config.rcParams.enableMinQP) { + extra += " qpmin=" + std::to_string(enc_config.rcParams.minQP.qpInterP); + } + if (config.insert_filler_data) { + extra += " filler-data"; + } + + BOOST_LOG(info) << "NvEnc: created encoder " << video_format_string << quality_preset_string_from_guid(init_params.presetGUID) << extra; + } + + encoder_state = {}; + fail_guard.disable(); + return true; + } + + void nvenc_base::destroy_encoder() { + if (output_bitstream) { + if (nvenc_failed(nvenc->nvEncDestroyBitstreamBuffer(encoder, output_bitstream))) { + BOOST_LOG(error) << "NvEnc: NvEncDestroyBitstreamBuffer() failed: " << last_nvenc_error_string; + } + output_bitstream = nullptr; + } + if (encoder && async_event_handle) { + NV_ENC_EVENT_PARAMS event_params = {min_struct_version(NV_ENC_EVENT_PARAMS_VER)}; + event_params.completionEvent = async_event_handle; + if (nvenc_failed(nvenc->nvEncUnregisterAsyncEvent(encoder, &event_params))) { + BOOST_LOG(error) << "NvEnc: NvEncUnregisterAsyncEvent() failed: " << last_nvenc_error_string; + } + } + if (registered_input_buffer) { + if (nvenc_failed(nvenc->nvEncUnregisterResource(encoder, registered_input_buffer))) { + BOOST_LOG(error) << "NvEnc: NvEncUnregisterResource() failed: " << last_nvenc_error_string; + } + registered_input_buffer = nullptr; + } + if (encoder) { + if (nvenc_failed(nvenc->nvEncDestroyEncoder(encoder))) { + BOOST_LOG(error) << "NvEnc: NvEncDestroyEncoder() failed: " << last_nvenc_error_string; + } + encoder = nullptr; + } + + encoder_state = {}; + encoder_params = {}; + } + + nvenc_encoded_frame nvenc_base::encode_frame(uint64_t frame_index, bool force_idr) { + if (!encoder) { + return {}; + } + + assert(registered_input_buffer); + assert(output_bitstream); + + if (!synchronize_input_buffer()) { + BOOST_LOG(error) << "NvEnc: failed to synchronize input buffer"; + return {}; + } + + NV_ENC_MAP_INPUT_RESOURCE mapped_input_buffer = {min_struct_version(NV_ENC_MAP_INPUT_RESOURCE_VER)}; + mapped_input_buffer.registeredResource = registered_input_buffer; + + if (nvenc_failed(nvenc->nvEncMapInputResource(encoder, &mapped_input_buffer))) { + BOOST_LOG(error) << "NvEnc: NvEncMapInputResource() failed: " << last_nvenc_error_string; + return {}; + } + auto unmap_guard = util::fail_guard([&] { + if (nvenc_failed(nvenc->nvEncUnmapInputResource(encoder, mapped_input_buffer.mappedResource))) { + BOOST_LOG(error) << "NvEnc: NvEncUnmapInputResource() failed: " << last_nvenc_error_string; + } + }); + + NV_ENC_PIC_PARAMS pic_params = {min_struct_version(NV_ENC_PIC_PARAMS_VER, 4, 6)}; + pic_params.inputWidth = encoder_params.width; + pic_params.inputHeight = encoder_params.height; + pic_params.encodePicFlags = force_idr ? NV_ENC_PIC_FLAG_FORCEIDR : 0; + pic_params.inputTimeStamp = frame_index; + pic_params.pictureStruct = NV_ENC_PIC_STRUCT_FRAME; + pic_params.inputBuffer = mapped_input_buffer.mappedResource; + pic_params.bufferFmt = mapped_input_buffer.mappedBufferFmt; + pic_params.outputBitstream = output_bitstream; + pic_params.completionEvent = async_event_handle; + + if (nvenc_failed(nvenc->nvEncEncodePicture(encoder, &pic_params))) { + BOOST_LOG(error) << "NvEnc: NvEncEncodePicture() failed: " << last_nvenc_error_string; + return {}; + } + + NV_ENC_LOCK_BITSTREAM lock_bitstream = {min_struct_version(NV_ENC_LOCK_BITSTREAM_VER, 1, 2)}; + lock_bitstream.outputBitstream = output_bitstream; + lock_bitstream.doNotWait = 0; + + if (async_event_handle && !wait_for_async_event(100)) { + BOOST_LOG(error) << "NvEnc: frame " << frame_index << " encode wait timeout"; + return {}; + } + + if (nvenc_failed(nvenc->nvEncLockBitstream(encoder, &lock_bitstream))) { + BOOST_LOG(error) << "NvEnc: NvEncLockBitstream() failed: " << last_nvenc_error_string; + return {}; + } + + auto data_pointer = (uint8_t *) lock_bitstream.bitstreamBufferPtr; + nvenc_encoded_frame encoded_frame { + {data_pointer, data_pointer + lock_bitstream.bitstreamSizeInBytes}, + lock_bitstream.outputTimeStamp, + lock_bitstream.pictureType == NV_ENC_PIC_TYPE_IDR, + encoder_state.rfi_needs_confirmation, + }; + + if (encoder_state.rfi_needs_confirmation) { + // Invalidation request has been fulfilled, and video network packet will be marked as such + encoder_state.rfi_needs_confirmation = false; + } + + encoder_state.last_encoded_frame_index = frame_index; + + if (encoded_frame.idr) { + BOOST_LOG(debug) << "NvEnc: idr frame " << encoded_frame.frame_index; + } + + if (nvenc_failed(nvenc->nvEncUnlockBitstream(encoder, lock_bitstream.outputBitstream))) { + BOOST_LOG(error) << "NvEnc: NvEncUnlockBitstream() failed: " << last_nvenc_error_string; + } + + encoder_state.frame_size_logger.collect_and_log(encoded_frame.data.size() / 1000.); + + return encoded_frame; + } + + bool nvenc_base::invalidate_ref_frames(uint64_t first_frame, uint64_t last_frame) { + if (!encoder || !encoder_params.rfi) { + return false; + } + + if (first_frame >= encoder_state.last_rfi_range.first && + last_frame <= encoder_state.last_rfi_range.second) { + BOOST_LOG(debug) << "NvEnc: rfi request " << first_frame << "-" << last_frame << " already done"; + return true; + } + + encoder_state.rfi_needs_confirmation = true; + + if (last_frame < first_frame) { + BOOST_LOG(error) << "NvEnc: invaid rfi request " << first_frame << "-" << last_frame << ", generating IDR"; + return false; + } + + BOOST_LOG(debug) << "NvEnc: rfi request " << first_frame << "-" << last_frame << " expanding to last encoded frame " << encoder_state.last_encoded_frame_index; + last_frame = encoder_state.last_encoded_frame_index; + + encoder_state.last_rfi_range = {first_frame, last_frame}; + + if (last_frame - first_frame + 1 >= encoder_params.ref_frames_in_dpb) { + BOOST_LOG(debug) << "NvEnc: rfi request too large, generating IDR"; + return false; + } + + for (auto i = first_frame; i <= last_frame; i++) { + if (nvenc_failed(nvenc->nvEncInvalidateRefFrames(encoder, i))) { + BOOST_LOG(error) << "NvEnc: NvEncInvalidateRefFrames() " << i << " failed: " << last_nvenc_error_string; + return false; + } + } + + return true; + } + + bool nvenc_base::nvenc_failed(NVENCSTATUS status) { + auto status_string = [](NVENCSTATUS status) -> std::string { + switch (status) { +#define nvenc_status_case(x) \ + case x: \ + return #x; + nvenc_status_case(NV_ENC_SUCCESS); + nvenc_status_case(NV_ENC_ERR_NO_ENCODE_DEVICE); + nvenc_status_case(NV_ENC_ERR_UNSUPPORTED_DEVICE); + nvenc_status_case(NV_ENC_ERR_INVALID_ENCODERDEVICE); + nvenc_status_case(NV_ENC_ERR_INVALID_DEVICE); + nvenc_status_case(NV_ENC_ERR_DEVICE_NOT_EXIST); + nvenc_status_case(NV_ENC_ERR_INVALID_PTR); + nvenc_status_case(NV_ENC_ERR_INVALID_EVENT); + nvenc_status_case(NV_ENC_ERR_INVALID_PARAM); + nvenc_status_case(NV_ENC_ERR_INVALID_CALL); + nvenc_status_case(NV_ENC_ERR_OUT_OF_MEMORY); + nvenc_status_case(NV_ENC_ERR_ENCODER_NOT_INITIALIZED); + nvenc_status_case(NV_ENC_ERR_UNSUPPORTED_PARAM); + nvenc_status_case(NV_ENC_ERR_LOCK_BUSY); + nvenc_status_case(NV_ENC_ERR_NOT_ENOUGH_BUFFER); + nvenc_status_case(NV_ENC_ERR_INVALID_VERSION); + nvenc_status_case(NV_ENC_ERR_MAP_FAILED); + nvenc_status_case(NV_ENC_ERR_NEED_MORE_INPUT); + nvenc_status_case(NV_ENC_ERR_ENCODER_BUSY); + nvenc_status_case(NV_ENC_ERR_EVENT_NOT_REGISTERD); + nvenc_status_case(NV_ENC_ERR_GENERIC); + nvenc_status_case(NV_ENC_ERR_INCOMPATIBLE_CLIENT_KEY); + nvenc_status_case(NV_ENC_ERR_UNIMPLEMENTED); + nvenc_status_case(NV_ENC_ERR_RESOURCE_REGISTER_FAILED); + nvenc_status_case(NV_ENC_ERR_RESOURCE_NOT_REGISTERED); + nvenc_status_case(NV_ENC_ERR_RESOURCE_NOT_MAPPED); + // Newer versions of sdk may add more constants, look for them at the end of NVENCSTATUS enum +#undef nvenc_status_case + default: + return std::to_string(status); + } + }; + + last_nvenc_error_string.clear(); + if (status != NV_ENC_SUCCESS) { + /* This API function gives broken strings more often than not + if (nvenc && encoder) { + last_nvenc_error_string = nvenc->nvEncGetLastErrorString(encoder); + if (!last_nvenc_error_string.empty()) last_nvenc_error_string += " "; + } + */ + last_nvenc_error_string += status_string(status); + return true; + } + + return false; + } + + uint32_t nvenc_base::min_struct_version(uint32_t version, uint32_t v11_struct_version, uint32_t v12_struct_version) { + assert(minimum_api_version); + + // Mask off and replace the original NVENCAPI_VERSION + version &= ~NVENCAPI_VERSION; + version |= minimum_api_version; + + // If there's a struct version override, apply that too + if (v11_struct_version || v12_struct_version) { + version &= ~(0xFFu << 16); + version |= (((minimum_api_version & 0xFF) >= 12) ? v12_struct_version : v11_struct_version) << 16; + } + + return version; + } +} // namespace nvenc diff --git a/src/nvenc/nvenc_base.h b/src/nvenc/nvenc_base.h index c49aa401..a4615a84 100644 --- a/src/nvenc/nvenc_base.h +++ b/src/nvenc/nvenc_base.h @@ -1,161 +1,155 @@ -/** - * @file src/nvenc/nvenc_base.h - * @brief Declarations for abstract platform-agnostic base of standalone NVENC encoder. - */ -#pragma once - -#include "nvenc_colorspace.h" -#include "nvenc_config.h" -#include "nvenc_encoded_frame.h" - -#include "src/logging.h" -#include "src/video.h" - -#include - -/** - * @brief Standalone NVENC encoder - */ -namespace nvenc { - - /** - * @brief Abstract platform-agnostic base of standalone NVENC encoder. - * Derived classes perform platform-specific operations. - */ - class nvenc_base { - public: - /** - * @param device_type Underlying device type used by derived class. - */ - explicit nvenc_base(NV_ENC_DEVICE_TYPE device_type); - virtual ~nvenc_base(); - - nvenc_base(const nvenc_base &) = delete; - nvenc_base & - operator=(const nvenc_base &) = delete; - - /** - * @brief Create the encoder. - * @param config NVENC encoder configuration. - * @param client_config Stream configuration requested by the client. - * @param colorspace YUV colorspace. - * @param buffer_format Platform-agnostic input surface format. - * @return `true` on success, `false` on error - */ - bool - create_encoder(const nvenc_config &config, const video::config_t &client_config, const nvenc_colorspace_t &colorspace, NV_ENC_BUFFER_FORMAT buffer_format); - - /** - * @brief Destroy the encoder. - * Derived classes classes call it in the destructor. - */ - void - destroy_encoder(); - - /** - * @brief Encode the next frame using platform-specific input surface. - * @param frame_index Frame index that uniquely identifies the frame. - * Afterwards serves as parameter for `invalidate_ref_frames()`. - * No restrictions on the first frame index, but later frame indexes must be subsequent. - * @param force_idr Whether to encode frame as forced IDR. - * @return Encoded frame. - */ - nvenc_encoded_frame - encode_frame(uint64_t frame_index, bool force_idr); - - /** - * @brief Perform reference frame invalidation (RFI) procedure. - * @param first_frame First frame index of the invalidation range. - * @param last_frame Last frame index of the invalidation range. - * @return `true` on success, `false` on error. - * After error next frame must be encoded with `force_idr = true`. - */ - bool - invalidate_ref_frames(uint64_t first_frame, uint64_t last_frame); - - protected: - /** - * @brief Required. Used for loading NvEnc library and setting `nvenc` variable with `NvEncodeAPICreateInstance()`. - * Called during `create_encoder()` if `nvenc` variable is not initialized. - * @return `true` on success, `false` on error - */ - virtual bool - init_library() = 0; - - /** - * @brief Required. Used for creating outside-facing input surface, - * registering this surface with `nvenc->nvEncRegisterResource()` and setting `registered_input_buffer` variable. - * Called during `create_encoder()`. - * @return `true` on success, `false` on error - */ - virtual bool - create_and_register_input_buffer() = 0; - - /** - * @brief Optional. Override if you must perform additional operations on the registered input surface in the beginning of `encode_frame()`. - * Typically used for interop copy. - * @return `true` on success, `false` on error - */ - virtual bool - synchronize_input_buffer() { return true; } - - /** - * @brief Optional. Override if you want to create encoder in async mode. - * In this case must also set `async_event_handle` variable. - * @param timeout_ms Wait timeout in milliseconds - * @return `true` on success, `false` on timeout or error - */ - virtual bool - wait_for_async_event(uint32_t timeout_ms) { return false; } - - bool - nvenc_failed(NVENCSTATUS status); - - /** - * @brief This function returns the corresponding struct version for the minimum API required by the codec. - * @details Reducing the struct versions maximizes driver compatibility by avoiding needless API breaks. - * @param version The raw structure version from `NVENCAPI_STRUCT_VERSION()`. - * @param v11_struct_version Optionally specifies the struct version to use with v11 SDK major versions. - * @param v12_struct_version Optionally specifies the struct version to use with v12 SDK major versions. - * @return A suitable struct version for the active codec. - */ - uint32_t - min_struct_version(uint32_t version, uint32_t v11_struct_version = 0, uint32_t v12_struct_version = 0); - - const NV_ENC_DEVICE_TYPE device_type; - - void *encoder = nullptr; - - struct { - uint32_t width = 0; - uint32_t height = 0; - NV_ENC_BUFFER_FORMAT buffer_format = NV_ENC_BUFFER_FORMAT_UNDEFINED; - uint32_t ref_frames_in_dpb = 0; - bool rfi = false; - } encoder_params; - - std::string last_nvenc_error_string; - - // Derived classes set these variables - void *device = nullptr; ///< Platform-specific handle of encoding device. - ///< Should be set in constructor or `init_library()`. - std::shared_ptr nvenc; ///< Function pointers list produced by `NvEncodeAPICreateInstance()`. - ///< Should be set in `init_library()`. - NV_ENC_REGISTERED_PTR registered_input_buffer = nullptr; ///< Platform-specific input surface registered with `NvEncRegisterResource()`. - ///< Should be set in `create_and_register_input_buffer()`. - void *async_event_handle = nullptr; ///< (optional) Platform-specific handle of event object event. - ///< Can be set in constructor or `init_library()`, must override `wait_for_async_event()`. - - private: - NV_ENC_OUTPUT_PTR output_bitstream = nullptr; - uint32_t minimum_api_version = 0; - - struct { - uint64_t last_encoded_frame_index = 0; - bool rfi_needs_confirmation = false; - std::pair last_rfi_range; - logging::min_max_avg_periodic_logger frame_size_logger = { debug, "NvEnc: encoded frame sizes in kB", "" }; - } encoder_state; - }; - -} // namespace nvenc +/** + * @file src/nvenc/nvenc_base.h + * @brief Declarations for abstract platform-agnostic base of standalone NVENC encoder. + */ +#pragma once + +// lib includes +#include + +// local includes +#include "nvenc_colorspace.h" +#include "nvenc_config.h" +#include "nvenc_encoded_frame.h" +#include "src/logging.h" +#include "src/video.h" + +/** + * @brief Standalone NVENC encoder + */ +namespace nvenc { + + /** + * @brief Abstract platform-agnostic base of standalone NVENC encoder. + * Derived classes perform platform-specific operations. + */ + class nvenc_base { + public: + /** + * @param device_type Underlying device type used by derived class. + */ + explicit nvenc_base(NV_ENC_DEVICE_TYPE device_type); + virtual ~nvenc_base(); + + nvenc_base(const nvenc_base &) = delete; + nvenc_base &operator=(const nvenc_base &) = delete; + + /** + * @brief Create the encoder. + * @param config NVENC encoder configuration. + * @param client_config Stream configuration requested by the client. + * @param colorspace YUV colorspace. + * @param buffer_format Platform-agnostic input surface format. + * @return `true` on success, `false` on error + */ + bool create_encoder(const nvenc_config &config, const video::config_t &client_config, const nvenc_colorspace_t &colorspace, NV_ENC_BUFFER_FORMAT buffer_format); + + /** + * @brief Destroy the encoder. + * Derived classes classes call it in the destructor. + */ + void destroy_encoder(); + + /** + * @brief Encode the next frame using platform-specific input surface. + * @param frame_index Frame index that uniquely identifies the frame. + * Afterwards serves as parameter for `invalidate_ref_frames()`. + * No restrictions on the first frame index, but later frame indexes must be subsequent. + * @param force_idr Whether to encode frame as forced IDR. + * @return Encoded frame. + */ + nvenc_encoded_frame encode_frame(uint64_t frame_index, bool force_idr); + + /** + * @brief Perform reference frame invalidation (RFI) procedure. + * @param first_frame First frame index of the invalidation range. + * @param last_frame Last frame index of the invalidation range. + * @return `true` on success, `false` on error. + * After error next frame must be encoded with `force_idr = true`. + */ + bool invalidate_ref_frames(uint64_t first_frame, uint64_t last_frame); + + protected: + /** + * @brief Required. Used for loading NvEnc library and setting `nvenc` variable with `NvEncodeAPICreateInstance()`. + * Called during `create_encoder()` if `nvenc` variable is not initialized. + * @return `true` on success, `false` on error + */ + virtual bool init_library() = 0; + + /** + * @brief Required. Used for creating outside-facing input surface, + * registering this surface with `nvenc->nvEncRegisterResource()` and setting `registered_input_buffer` variable. + * Called during `create_encoder()`. + * @return `true` on success, `false` on error + */ + virtual bool create_and_register_input_buffer() = 0; + + /** + * @brief Optional. Override if you must perform additional operations on the registered input surface in the beginning of `encode_frame()`. + * Typically used for interop copy. + * @return `true` on success, `false` on error + */ + virtual bool synchronize_input_buffer() { + return true; + } + + /** + * @brief Optional. Override if you want to create encoder in async mode. + * In this case must also set `async_event_handle` variable. + * @param timeout_ms Wait timeout in milliseconds + * @return `true` on success, `false` on timeout or error + */ + virtual bool wait_for_async_event(uint32_t timeout_ms) { + return false; + } + + bool nvenc_failed(NVENCSTATUS status); + + /** + * @brief This function returns the corresponding struct version for the minimum API required by the codec. + * @details Reducing the struct versions maximizes driver compatibility by avoiding needless API breaks. + * @param version The raw structure version from `NVENCAPI_STRUCT_VERSION()`. + * @param v11_struct_version Optionally specifies the struct version to use with v11 SDK major versions. + * @param v12_struct_version Optionally specifies the struct version to use with v12 SDK major versions. + * @return A suitable struct version for the active codec. + */ + uint32_t min_struct_version(uint32_t version, uint32_t v11_struct_version = 0, uint32_t v12_struct_version = 0); + + const NV_ENC_DEVICE_TYPE device_type; + + void *encoder = nullptr; + + struct { + uint32_t width = 0; + uint32_t height = 0; + NV_ENC_BUFFER_FORMAT buffer_format = NV_ENC_BUFFER_FORMAT_UNDEFINED; + uint32_t ref_frames_in_dpb = 0; + bool rfi = false; + } encoder_params; + + std::string last_nvenc_error_string; + + // Derived classes set these variables + void *device = nullptr; ///< Platform-specific handle of encoding device. + ///< Should be set in constructor or `init_library()`. + std::shared_ptr nvenc; ///< Function pointers list produced by `NvEncodeAPICreateInstance()`. + ///< Should be set in `init_library()`. + NV_ENC_REGISTERED_PTR registered_input_buffer = nullptr; ///< Platform-specific input surface registered with `NvEncRegisterResource()`. + ///< Should be set in `create_and_register_input_buffer()`. + void *async_event_handle = nullptr; ///< (optional) Platform-specific handle of event object event. + ///< Can be set in constructor or `init_library()`, must override `wait_for_async_event()`. + + private: + NV_ENC_OUTPUT_PTR output_bitstream = nullptr; + uint32_t minimum_api_version = 0; + + struct { + uint64_t last_encoded_frame_index = 0; + bool rfi_needs_confirmation = false; + std::pair last_rfi_range; + logging::min_max_avg_periodic_logger frame_size_logger = {debug, "NvEnc: encoded frame sizes in kB", ""}; + } encoder_state; + }; + +} // namespace nvenc diff --git a/src/nvenc/nvenc_colorspace.h b/src/nvenc/nvenc_colorspace.h index c9ed5193..a31ca426 100644 --- a/src/nvenc/nvenc_colorspace.h +++ b/src/nvenc/nvenc_colorspace.h @@ -1,21 +1,22 @@ -/** - * @file src/nvenc/nvenc_colorspace.h - * @brief Declarations for NVENC YUV colorspace. - */ -#pragma once - -#include - -namespace nvenc { - - /** - * @brief YUV colorspace and color range. - */ - struct nvenc_colorspace_t { - NV_ENC_VUI_COLOR_PRIMARIES primaries; - NV_ENC_VUI_TRANSFER_CHARACTERISTIC tranfer_function; - NV_ENC_VUI_MATRIX_COEFFS matrix; - bool full_range; - }; - -} // namespace nvenc +/** + * @file src/nvenc/nvenc_colorspace.h + * @brief Declarations for NVENC YUV colorspace. + */ +#pragma once + +// lib includes +#include + +namespace nvenc { + + /** + * @brief YUV colorspace and color range. + */ + struct nvenc_colorspace_t { + NV_ENC_VUI_COLOR_PRIMARIES primaries; + NV_ENC_VUI_TRANSFER_CHARACTERISTIC tranfer_function; + NV_ENC_VUI_MATRIX_COEFFS matrix; + bool full_range; + }; + +} // namespace nvenc diff --git a/src/nvenc/nvenc_config.h b/src/nvenc/nvenc_config.h index 213a0d28..824397e8 100644 --- a/src/nvenc/nvenc_config.h +++ b/src/nvenc/nvenc_config.h @@ -1,53 +1,53 @@ -/** - * @file src/nvenc/nvenc_config.h - * @brief Declarations for NVENC encoder configuration. - */ -#pragma once - -namespace nvenc { - - enum class nvenc_two_pass { - disabled, ///< Single pass, the fastest and no extra vram - quarter_resolution, ///< Larger motion vectors being caught, faster and uses less extra vram - full_resolution, ///< Better overall statistics, slower and uses more extra vram - }; - - /** - * @brief NVENC encoder configuration. - */ - struct nvenc_config { - // Quality preset from 1 to 7, higher is slower - int quality_preset = 1; - - // Use optional preliminary pass for better motion vectors, bitrate distribution and stricter VBV(HRD), uses CUDA cores - nvenc_two_pass two_pass = nvenc_two_pass::quarter_resolution; - - // Percentage increase of VBV/HRD from the default single frame, allows low-latency variable bitrate - int vbv_percentage_increase = 0; - - // Improves fades compression, uses CUDA cores - bool weighted_prediction = false; - - // Allocate more bitrate to flat regions since they're visually more perceptible, uses CUDA cores - bool adaptive_quantization = false; - - // Don't use QP below certain value, limits peak image quality to save bitrate - bool enable_min_qp = false; - - // Min QP value for H.264 when enable_min_qp is selected - unsigned min_qp_h264 = 19; - - // Min QP value for HEVC when enable_min_qp is selected - unsigned min_qp_hevc = 23; - - // Min QP value for AV1 when enable_min_qp is selected - unsigned min_qp_av1 = 23; - - // Use CAVLC entropy coding in H.264 instead of CABAC, not relevant and here for historical reasons - bool h264_cavlc = false; - - // Add filler data to encoded frames to stay at target bitrate, mainly for testing - bool insert_filler_data = false; - }; - -} // namespace nvenc +/** + * @file src/nvenc/nvenc_config.h + * @brief Declarations for NVENC encoder configuration. + */ +#pragma once + +namespace nvenc { + + enum class nvenc_two_pass { + disabled, ///< Single pass, the fastest and no extra vram + quarter_resolution, ///< Larger motion vectors being caught, faster and uses less extra vram + full_resolution, ///< Better overall statistics, slower and uses more extra vram + }; + + /** + * @brief NVENC encoder configuration. + */ + struct nvenc_config { + // Quality preset from 1 to 7, higher is slower + int quality_preset = 1; + + // Use optional preliminary pass for better motion vectors, bitrate distribution and stricter VBV(HRD), uses CUDA cores + nvenc_two_pass two_pass = nvenc_two_pass::quarter_resolution; + + // Percentage increase of VBV/HRD from the default single frame, allows low-latency variable bitrate + int vbv_percentage_increase = 0; + + // Improves fades compression, uses CUDA cores + bool weighted_prediction = false; + + // Allocate more bitrate to flat regions since they're visually more perceptible, uses CUDA cores + bool adaptive_quantization = false; + + // Don't use QP below certain value, limits peak image quality to save bitrate + bool enable_min_qp = false; + + // Min QP value for H.264 when enable_min_qp is selected + unsigned min_qp_h264 = 19; + + // Min QP value for HEVC when enable_min_qp is selected + unsigned min_qp_hevc = 23; + + // Min QP value for AV1 when enable_min_qp is selected + unsigned min_qp_av1 = 23; + + // Use CAVLC entropy coding in H.264 instead of CABAC, not relevant and here for historical reasons + bool h264_cavlc = false; + + // Add filler data to encoded frames to stay at target bitrate, mainly for testing + bool insert_filler_data = false; + }; + +} // namespace nvenc diff --git a/src/nvenc/nvenc_d3d11.cpp b/src/nvenc/nvenc_d3d11.cpp index 7dd545b4..28d31371 100644 --- a/src/nvenc/nvenc_d3d11.cpp +++ b/src/nvenc/nvenc_d3d11.cpp @@ -1,58 +1,57 @@ -/** - * @file src/nvenc/nvenc_d3d11.cpp - * @brief Definitions for abstract Direct3D11 NVENC encoder. - */ -#include "src/logging.h" - -#ifdef _WIN32 - #include "nvenc_d3d11.h" - -namespace nvenc { - - nvenc_d3d11::~nvenc_d3d11() { - if (dll) { - FreeLibrary(dll); - dll = NULL; - } - } - - bool - nvenc_d3d11::init_library() { - if (dll) return true; - - #ifdef _WIN64 - constexpr auto dll_name = "nvEncodeAPI64.dll"; - #else - constexpr auto dll_name = "nvEncodeAPI.dll"; - #endif - - if ((dll = LoadLibraryEx(dll_name, NULL, LOAD_LIBRARY_SEARCH_SYSTEM32))) { - if (auto create_instance = (decltype(NvEncodeAPICreateInstance) *) GetProcAddress(dll, "NvEncodeAPICreateInstance")) { - auto new_nvenc = std::make_unique(); - new_nvenc->version = min_struct_version(NV_ENCODE_API_FUNCTION_LIST_VER); - if (nvenc_failed(create_instance(new_nvenc.get()))) { - BOOST_LOG(error) << "NvEnc: NvEncodeAPICreateInstance() failed: " << last_nvenc_error_string; - } - else { - nvenc = std::move(new_nvenc); - return true; - } - } - else { - BOOST_LOG(error) << "NvEnc: No NvEncodeAPICreateInstance() in " << dll_name; - } - } - else { - BOOST_LOG(debug) << "NvEnc: Couldn't load NvEnc library " << dll_name; - } - - if (dll) { - FreeLibrary(dll); - dll = NULL; - } - - return false; - } - -} // namespace nvenc -#endif +/** + * @file src/nvenc/nvenc_d3d11.cpp + * @brief Definitions for abstract Direct3D11 NVENC encoder. + */ +// local includes +#include "src/logging.h" + +#ifdef _WIN32 + #include "nvenc_d3d11.h" + +namespace nvenc { + + nvenc_d3d11::~nvenc_d3d11() { + if (dll) { + FreeLibrary(dll); + dll = NULL; + } + } + + bool nvenc_d3d11::init_library() { + if (dll) { + return true; + } + + #ifdef _WIN64 + constexpr auto dll_name = "nvEncodeAPI64.dll"; + #else + constexpr auto dll_name = "nvEncodeAPI.dll"; + #endif + + if ((dll = LoadLibraryEx(dll_name, NULL, LOAD_LIBRARY_SEARCH_SYSTEM32))) { + if (auto create_instance = (decltype(NvEncodeAPICreateInstance) *) GetProcAddress(dll, "NvEncodeAPICreateInstance")) { + auto new_nvenc = std::make_unique(); + new_nvenc->version = min_struct_version(NV_ENCODE_API_FUNCTION_LIST_VER); + if (nvenc_failed(create_instance(new_nvenc.get()))) { + BOOST_LOG(error) << "NvEnc: NvEncodeAPICreateInstance() failed: " << last_nvenc_error_string; + } else { + nvenc = std::move(new_nvenc); + return true; + } + } else { + BOOST_LOG(error) << "NvEnc: No NvEncodeAPICreateInstance() in " << dll_name; + } + } else { + BOOST_LOG(debug) << "NvEnc: Couldn't load NvEnc library " << dll_name; + } + + if (dll) { + FreeLibrary(dll); + dll = NULL; + } + + return false; + } + +} // namespace nvenc +#endif diff --git a/src/nvenc/nvenc_d3d11.h b/src/nvenc/nvenc_d3d11.h index 2d4d4fe7..29af1818 100644 --- a/src/nvenc/nvenc_d3d11.h +++ b/src/nvenc/nvenc_d3d11.h @@ -1,47 +1,48 @@ -/** - * @file src/nvenc/nvenc_d3d11.h - * @brief Declarations for abstract Direct3D11 NVENC encoder. - */ -#pragma once -#ifdef _WIN32 - - #include - #include - - #include "nvenc_base.h" - -namespace nvenc { - - _COM_SMARTPTR_TYPEDEF(ID3D11Device, IID_ID3D11Device); - _COM_SMARTPTR_TYPEDEF(ID3D11Texture2D, IID_ID3D11Texture2D); - _COM_SMARTPTR_TYPEDEF(IDXGIDevice, IID_IDXGIDevice); - _COM_SMARTPTR_TYPEDEF(IDXGIAdapter, IID_IDXGIAdapter); - - /** - * @brief Abstract Direct3D11 NVENC encoder. - * Encapsulates common code used by native and interop implementations. - */ - class nvenc_d3d11: public nvenc_base { - public: - explicit nvenc_d3d11(NV_ENC_DEVICE_TYPE device_type): - nvenc_base(device_type) {} - - ~nvenc_d3d11(); - - /** - * @brief Get input surface texture. - * @return Input surface texture. - */ - virtual ID3D11Texture2D * - get_input_texture() = 0; - - protected: - bool - init_library() override; - - private: - HMODULE dll = NULL; - }; - -} // namespace nvenc -#endif +/** + * @file src/nvenc/nvenc_d3d11.h + * @brief Declarations for abstract Direct3D11 NVENC encoder. + */ +#pragma once +#ifdef _WIN32 + + // standard includes + #include + #include + + // local includes + #include "nvenc_base.h" + +namespace nvenc { + + _COM_SMARTPTR_TYPEDEF(ID3D11Device, IID_ID3D11Device); + _COM_SMARTPTR_TYPEDEF(ID3D11Texture2D, IID_ID3D11Texture2D); + _COM_SMARTPTR_TYPEDEF(IDXGIDevice, IID_IDXGIDevice); + _COM_SMARTPTR_TYPEDEF(IDXGIAdapter, IID_IDXGIAdapter); + + /** + * @brief Abstract Direct3D11 NVENC encoder. + * Encapsulates common code used by native and interop implementations. + */ + class nvenc_d3d11: public nvenc_base { + public: + explicit nvenc_d3d11(NV_ENC_DEVICE_TYPE device_type): + nvenc_base(device_type) { + } + + ~nvenc_d3d11(); + + /** + * @brief Get input surface texture. + * @return Input surface texture. + */ + virtual ID3D11Texture2D *get_input_texture() = 0; + + protected: + bool init_library() override; + + private: + HMODULE dll = NULL; + }; + +} // namespace nvenc +#endif diff --git a/src/nvenc/nvenc_d3d11_native.cpp b/src/nvenc/nvenc_d3d11_native.cpp index a563b33d..afc66578 100644 --- a/src/nvenc/nvenc_d3d11_native.cpp +++ b/src/nvenc/nvenc_d3d11_native.cpp @@ -1,71 +1,74 @@ -/** - * @file src/nvenc/nvenc_d3d11_native.cpp - * @brief Definitions for native Direct3D11 NVENC encoder. - */ -#ifdef _WIN32 - #include "nvenc_d3d11_native.h" - - #include "nvenc_utils.h" - -namespace nvenc { - - nvenc_d3d11_native::nvenc_d3d11_native(ID3D11Device *d3d_device): - nvenc_d3d11(NV_ENC_DEVICE_TYPE_DIRECTX), - d3d_device(d3d_device) { - device = d3d_device; - } - - nvenc_d3d11_native::~nvenc_d3d11_native() { - if (encoder) destroy_encoder(); - } - - ID3D11Texture2D * - nvenc_d3d11_native::get_input_texture() { - return d3d_input_texture.GetInterfacePtr(); - } - - bool - nvenc_d3d11_native::create_and_register_input_buffer() { - if (encoder_params.buffer_format == NV_ENC_BUFFER_FORMAT_YUV444_10BIT) { - BOOST_LOG(error) << "NvEnc: 10-bit 4:4:4 encoding is incompatible with D3D11 surface formats, use CUDA interop"; - return false; - } - - if (!d3d_input_texture) { - D3D11_TEXTURE2D_DESC desc = {}; - desc.Width = encoder_params.width; - desc.Height = encoder_params.height; - desc.MipLevels = 1; - desc.ArraySize = 1; - desc.Format = dxgi_format_from_nvenc_format(encoder_params.buffer_format); - desc.SampleDesc.Count = 1; - desc.Usage = D3D11_USAGE_DEFAULT; - desc.BindFlags = D3D11_BIND_RENDER_TARGET; - if (d3d_device->CreateTexture2D(&desc, nullptr, &d3d_input_texture) != S_OK) { - BOOST_LOG(error) << "NvEnc: couldn't create input texture"; - return false; - } - } - - if (!registered_input_buffer) { - NV_ENC_REGISTER_RESOURCE register_resource = { min_struct_version(NV_ENC_REGISTER_RESOURCE_VER, 3, 4) }; - register_resource.resourceType = NV_ENC_INPUT_RESOURCE_TYPE_DIRECTX; - register_resource.width = encoder_params.width; - register_resource.height = encoder_params.height; - register_resource.resourceToRegister = d3d_input_texture.GetInterfacePtr(); - register_resource.bufferFormat = encoder_params.buffer_format; - register_resource.bufferUsage = NV_ENC_INPUT_IMAGE; - - if (nvenc_failed(nvenc->nvEncRegisterResource(encoder, ®ister_resource))) { - BOOST_LOG(error) << "NvEnc: NvEncRegisterResource() failed: " << last_nvenc_error_string; - return false; - } - - registered_input_buffer = register_resource.registeredResource; - } - - return true; - } - -} // namespace nvenc -#endif +/** + * @file src/nvenc/nvenc_d3d11_native.cpp + * @brief Definitions for native Direct3D11 NVENC encoder. + */ +#ifdef _WIN32 + // this include + #include "nvenc_d3d11_native.h" + + // local includes + #include "nvenc_utils.h" + +namespace nvenc { + + nvenc_d3d11_native::nvenc_d3d11_native(ID3D11Device *d3d_device): + nvenc_d3d11(NV_ENC_DEVICE_TYPE_DIRECTX), + d3d_device(d3d_device) { + device = d3d_device; + } + + nvenc_d3d11_native::~nvenc_d3d11_native() { + if (encoder) { + destroy_encoder(); + } + } + + ID3D11Texture2D * + nvenc_d3d11_native::get_input_texture() { + return d3d_input_texture.GetInterfacePtr(); + } + + bool nvenc_d3d11_native::create_and_register_input_buffer() { + if (encoder_params.buffer_format == NV_ENC_BUFFER_FORMAT_YUV444_10BIT) { + BOOST_LOG(error) << "NvEnc: 10-bit 4:4:4 encoding is incompatible with D3D11 surface formats, use CUDA interop"; + return false; + } + + if (!d3d_input_texture) { + D3D11_TEXTURE2D_DESC desc = {}; + desc.Width = encoder_params.width; + desc.Height = encoder_params.height; + desc.MipLevels = 1; + desc.ArraySize = 1; + desc.Format = dxgi_format_from_nvenc_format(encoder_params.buffer_format); + desc.SampleDesc.Count = 1; + desc.Usage = D3D11_USAGE_DEFAULT; + desc.BindFlags = D3D11_BIND_RENDER_TARGET; + if (d3d_device->CreateTexture2D(&desc, nullptr, &d3d_input_texture) != S_OK) { + BOOST_LOG(error) << "NvEnc: couldn't create input texture"; + return false; + } + } + + if (!registered_input_buffer) { + NV_ENC_REGISTER_RESOURCE register_resource = {min_struct_version(NV_ENC_REGISTER_RESOURCE_VER, 3, 4)}; + register_resource.resourceType = NV_ENC_INPUT_RESOURCE_TYPE_DIRECTX; + register_resource.width = encoder_params.width; + register_resource.height = encoder_params.height; + register_resource.resourceToRegister = d3d_input_texture.GetInterfacePtr(); + register_resource.bufferFormat = encoder_params.buffer_format; + register_resource.bufferUsage = NV_ENC_INPUT_IMAGE; + + if (nvenc_failed(nvenc->nvEncRegisterResource(encoder, ®ister_resource))) { + BOOST_LOG(error) << "NvEnc: NvEncRegisterResource() failed: " << last_nvenc_error_string; + return false; + } + + registered_input_buffer = register_resource.registeredResource; + } + + return true; + } + +} // namespace nvenc +#endif diff --git a/src/nvenc/nvenc_d3d11_native.h b/src/nvenc/nvenc_d3d11_native.h index f9d49b18..0e6f039e 100644 --- a/src/nvenc/nvenc_d3d11_native.h +++ b/src/nvenc/nvenc_d3d11_native.h @@ -1,38 +1,37 @@ -/** - * @file src/nvenc/nvenc_d3d11_native.h - * @brief Declarations for native Direct3D11 NVENC encoder. - */ -#pragma once -#ifdef _WIN32 - - #include - #include - - #include "nvenc_d3d11.h" - -namespace nvenc { - - /** - * @brief Native Direct3D11 NVENC encoder. - */ - class nvenc_d3d11_native final: public nvenc_d3d11 { - public: - /** - * @param d3d_device Direct3D11 device used for encoding. - */ - explicit nvenc_d3d11_native(ID3D11Device *d3d_device); - ~nvenc_d3d11_native(); - - ID3D11Texture2D * - get_input_texture() override; - - private: - bool - create_and_register_input_buffer() override; - - const ID3D11DevicePtr d3d_device; - ID3D11Texture2DPtr d3d_input_texture; - }; - -} // namespace nvenc -#endif +/** + * @file src/nvenc/nvenc_d3d11_native.h + * @brief Declarations for native Direct3D11 NVENC encoder. + */ +#pragma once +#ifdef _WIN32 + // standard includes + #include + #include + + // local includes + #include "nvenc_d3d11.h" + +namespace nvenc { + + /** + * @brief Native Direct3D11 NVENC encoder. + */ + class nvenc_d3d11_native final: public nvenc_d3d11 { + public: + /** + * @param d3d_device Direct3D11 device used for encoding. + */ + explicit nvenc_d3d11_native(ID3D11Device *d3d_device); + ~nvenc_d3d11_native(); + + ID3D11Texture2D *get_input_texture() override; + + private: + bool create_and_register_input_buffer() override; + + const ID3D11DevicePtr d3d_device; + ID3D11Texture2DPtr d3d_input_texture; + }; + +} // namespace nvenc +#endif diff --git a/src/nvenc/nvenc_d3d11_on_cuda.cpp b/src/nvenc/nvenc_d3d11_on_cuda.cpp index 37fe8963..02436e69 100644 --- a/src/nvenc/nvenc_d3d11_on_cuda.cpp +++ b/src/nvenc/nvenc_d3d11_on_cuda.cpp @@ -1,267 +1,269 @@ -/** - * @file src/nvenc/nvenc_d3d11_on_cuda.cpp - * @brief Definitions for CUDA NVENC encoder with Direct3D11 input surfaces. - */ -#ifdef _WIN32 - #include "nvenc_d3d11_on_cuda.h" - - #include "nvenc_utils.h" - -namespace nvenc { - - nvenc_d3d11_on_cuda::nvenc_d3d11_on_cuda(ID3D11Device *d3d_device): - nvenc_d3d11(NV_ENC_DEVICE_TYPE_CUDA), - d3d_device(d3d_device) { - } - - nvenc_d3d11_on_cuda::~nvenc_d3d11_on_cuda() { - if (encoder) destroy_encoder(); - - if (cuda_context) { - { - auto autopop_context = push_context(); - - if (cuda_d3d_input_texture) { - if (cuda_failed(cuda_functions.cuGraphicsUnregisterResource(cuda_d3d_input_texture))) { - BOOST_LOG(error) << "NvEnc: cuGraphicsUnregisterResource() failed: error " << last_cuda_error; - } - cuda_d3d_input_texture = nullptr; - } - - if (cuda_surface) { - if (cuda_failed(cuda_functions.cuMemFree(cuda_surface))) { - BOOST_LOG(error) << "NvEnc: cuMemFree() failed: error " << last_cuda_error; - } - cuda_surface = 0; - } - } - - if (cuda_failed(cuda_functions.cuCtxDestroy(cuda_context))) { - BOOST_LOG(error) << "NvEnc: cuCtxDestroy() failed: error " << last_cuda_error; - } - cuda_context = nullptr; - } - - if (cuda_functions.dll) { - FreeLibrary(cuda_functions.dll); - cuda_functions = {}; - } - } - - ID3D11Texture2D * - nvenc_d3d11_on_cuda::get_input_texture() { - return d3d_input_texture.GetInterfacePtr(); - } - - bool - nvenc_d3d11_on_cuda::init_library() { - if (!nvenc_d3d11::init_library()) return false; - - constexpr auto dll_name = "nvcuda.dll"; - - if ((cuda_functions.dll = LoadLibraryEx(dll_name, NULL, LOAD_LIBRARY_SEARCH_SYSTEM32))) { - auto load_function = [&](T &location, auto symbol) -> bool { - location = (T) GetProcAddress(cuda_functions.dll, symbol); - return location != nullptr; - }; - if (!load_function(cuda_functions.cuInit, "cuInit") || - !load_function(cuda_functions.cuD3D11GetDevice, "cuD3D11GetDevice") || - !load_function(cuda_functions.cuCtxCreate, "cuCtxCreate_v2") || - !load_function(cuda_functions.cuCtxDestroy, "cuCtxDestroy_v2") || - !load_function(cuda_functions.cuCtxPushCurrent, "cuCtxPushCurrent_v2") || - !load_function(cuda_functions.cuCtxPopCurrent, "cuCtxPopCurrent_v2") || - !load_function(cuda_functions.cuMemAllocPitch, "cuMemAllocPitch_v2") || - !load_function(cuda_functions.cuMemFree, "cuMemFree_v2") || - !load_function(cuda_functions.cuGraphicsD3D11RegisterResource, "cuGraphicsD3D11RegisterResource") || - !load_function(cuda_functions.cuGraphicsUnregisterResource, "cuGraphicsUnregisterResource") || - !load_function(cuda_functions.cuGraphicsMapResources, "cuGraphicsMapResources") || - !load_function(cuda_functions.cuGraphicsUnmapResources, "cuGraphicsUnmapResources") || - !load_function(cuda_functions.cuGraphicsSubResourceGetMappedArray, "cuGraphicsSubResourceGetMappedArray") || - !load_function(cuda_functions.cuMemcpy2D, "cuMemcpy2D_v2")) { - BOOST_LOG(error) << "NvEnc: missing CUDA functions in " << dll_name; - FreeLibrary(cuda_functions.dll); - cuda_functions = {}; - } - } - else { - BOOST_LOG(debug) << "NvEnc: couldn't load CUDA dynamic library " << dll_name; - } - - if (cuda_functions.dll) { - IDXGIDevicePtr dxgi_device; - IDXGIAdapterPtr dxgi_adapter; - if (d3d_device && - SUCCEEDED(d3d_device->QueryInterface(IID_PPV_ARGS(&dxgi_device))) && - SUCCEEDED(dxgi_device->GetAdapter(&dxgi_adapter))) { - CUdevice cuda_device; - if (cuda_succeeded(cuda_functions.cuInit(0)) && - cuda_succeeded(cuda_functions.cuD3D11GetDevice(&cuda_device, dxgi_adapter)) && - cuda_succeeded(cuda_functions.cuCtxCreate(&cuda_context, CU_CTX_SCHED_BLOCKING_SYNC, cuda_device)) && - cuda_succeeded(cuda_functions.cuCtxPopCurrent(&cuda_context))) { - device = cuda_context; - } - else { - BOOST_LOG(error) << "NvEnc: couldn't create CUDA interop context: error " << last_cuda_error; - } - } - else { - BOOST_LOG(error) << "NvEnc: couldn't get DXGI adapter for CUDA interop"; - } - } - - return device != nullptr; - } - - bool - nvenc_d3d11_on_cuda::create_and_register_input_buffer() { - if (encoder_params.buffer_format != NV_ENC_BUFFER_FORMAT_YUV444_10BIT) { - BOOST_LOG(error) << "NvEnc: CUDA interop is expected to be used only for 10-bit 4:4:4 encoding"; - return false; - } - - if (!d3d_input_texture) { - D3D11_TEXTURE2D_DESC desc = {}; - desc.Width = encoder_params.width; - desc.Height = encoder_params.height * 3; // Planar YUV - desc.MipLevels = 1; - desc.ArraySize = 1; - desc.Format = dxgi_format_from_nvenc_format(encoder_params.buffer_format); - desc.SampleDesc.Count = 1; - desc.Usage = D3D11_USAGE_DEFAULT; - desc.BindFlags = D3D11_BIND_RENDER_TARGET; - - if (d3d_device->CreateTexture2D(&desc, nullptr, &d3d_input_texture) != S_OK) { - BOOST_LOG(error) << "NvEnc: couldn't create input texture"; - return false; - } - } - - { - auto autopop_context = push_context(); - if (!autopop_context) return false; - - if (!cuda_d3d_input_texture) { - if (cuda_failed(cuda_functions.cuGraphicsD3D11RegisterResource( - &cuda_d3d_input_texture, - d3d_input_texture, - CU_GRAPHICS_REGISTER_FLAGS_NONE))) { - BOOST_LOG(error) << "NvEnc: cuGraphicsD3D11RegisterResource() failed: error " << last_cuda_error; - return false; - } - } - - if (!cuda_surface) { - if (cuda_failed(cuda_functions.cuMemAllocPitch( - &cuda_surface, - &cuda_surface_pitch, - // Planar 16-bit YUV - encoder_params.width * 2, - encoder_params.height * 3, 16))) { - BOOST_LOG(error) << "NvEnc: cuMemAllocPitch() failed: error " << last_cuda_error; - return false; - } - } - } - - if (!registered_input_buffer) { - NV_ENC_REGISTER_RESOURCE register_resource = { min_struct_version(NV_ENC_REGISTER_RESOURCE_VER, 3, 4) }; - register_resource.resourceType = NV_ENC_INPUT_RESOURCE_TYPE_CUDADEVICEPTR; - register_resource.width = encoder_params.width; - register_resource.height = encoder_params.height; - register_resource.pitch = cuda_surface_pitch; - register_resource.resourceToRegister = (void *) cuda_surface; - register_resource.bufferFormat = encoder_params.buffer_format; - register_resource.bufferUsage = NV_ENC_INPUT_IMAGE; - - if (nvenc_failed(nvenc->nvEncRegisterResource(encoder, ®ister_resource))) { - BOOST_LOG(error) << "NvEnc: NvEncRegisterResource() failed: " << last_nvenc_error_string; - return false; - } - - registered_input_buffer = register_resource.registeredResource; - } - - return true; - } - - bool - nvenc_d3d11_on_cuda::synchronize_input_buffer() { - auto autopop_context = push_context(); - if (!autopop_context) return false; - - if (cuda_failed(cuda_functions.cuGraphicsMapResources(1, &cuda_d3d_input_texture, 0))) { - BOOST_LOG(error) << "NvEnc: cuGraphicsMapResources() failed: error " << last_cuda_error; - return false; - } - - auto unmap = [&]() -> bool { - if (cuda_failed(cuda_functions.cuGraphicsUnmapResources(1, &cuda_d3d_input_texture, 0))) { - BOOST_LOG(error) << "NvEnc: cuGraphicsUnmapResources() failed: error " << last_cuda_error; - return false; - } - return true; - }; - auto unmap_guard = util::fail_guard(unmap); - - CUarray input_texture_array; - if (cuda_failed(cuda_functions.cuGraphicsSubResourceGetMappedArray(&input_texture_array, cuda_d3d_input_texture, 0, 0))) { - BOOST_LOG(error) << "NvEnc: cuGraphicsSubResourceGetMappedArray() failed: error " << last_cuda_error; - return false; - } - - { - CUDA_MEMCPY2D copy_params = {}; - copy_params.srcMemoryType = CU_MEMORYTYPE_ARRAY; - copy_params.srcArray = input_texture_array; - copy_params.dstMemoryType = CU_MEMORYTYPE_DEVICE; - copy_params.dstDevice = cuda_surface; - copy_params.dstPitch = cuda_surface_pitch; - // Planar 16-bit YUV - copy_params.WidthInBytes = encoder_params.width * 2; - copy_params.Height = encoder_params.height * 3; - - if (cuda_failed(cuda_functions.cuMemcpy2D(©_params))) { - BOOST_LOG(error) << "NvEnc: cuMemcpy2D() failed: error " << last_cuda_error; - return false; - } - } - - unmap_guard.disable(); - return unmap(); - } - - bool - nvenc_d3d11_on_cuda::cuda_succeeded(CUresult result) { - last_cuda_error = result; - return result == CUDA_SUCCESS; - } - - bool - nvenc_d3d11_on_cuda::cuda_failed(CUresult result) { - last_cuda_error = result; - return result != CUDA_SUCCESS; - } - - nvenc_d3d11_on_cuda::autopop_context::~autopop_context() { - if (pushed_context) { - CUcontext popped_context; - if (parent.cuda_failed(parent.cuda_functions.cuCtxPopCurrent(&popped_context))) { - BOOST_LOG(error) << "NvEnc: cuCtxPopCurrent() failed: error " << parent.last_cuda_error; - } - } - } - - nvenc_d3d11_on_cuda::autopop_context - nvenc_d3d11_on_cuda::push_context() { - if (cuda_context && - cuda_succeeded(cuda_functions.cuCtxPushCurrent(cuda_context))) { - return { *this, cuda_context }; - } - else { - BOOST_LOG(error) << "NvEnc: cuCtxPushCurrent() failed: error " << last_cuda_error; - return { *this, nullptr }; - } - } - -} // namespace nvenc -#endif +/** + * @file src/nvenc/nvenc_d3d11_on_cuda.cpp + * @brief Definitions for CUDA NVENC encoder with Direct3D11 input surfaces. + */ +#ifdef _WIN32 + // this include + #include "nvenc_d3d11_on_cuda.h" + + // local includes + #include "nvenc_utils.h" + +namespace nvenc { + + nvenc_d3d11_on_cuda::nvenc_d3d11_on_cuda(ID3D11Device *d3d_device): + nvenc_d3d11(NV_ENC_DEVICE_TYPE_CUDA), + d3d_device(d3d_device) { + } + + nvenc_d3d11_on_cuda::~nvenc_d3d11_on_cuda() { + if (encoder) { + destroy_encoder(); + } + + if (cuda_context) { + { + auto autopop_context = push_context(); + + if (cuda_d3d_input_texture) { + if (cuda_failed(cuda_functions.cuGraphicsUnregisterResource(cuda_d3d_input_texture))) { + BOOST_LOG(error) << "NvEnc: cuGraphicsUnregisterResource() failed: error " << last_cuda_error; + } + cuda_d3d_input_texture = nullptr; + } + + if (cuda_surface) { + if (cuda_failed(cuda_functions.cuMemFree(cuda_surface))) { + BOOST_LOG(error) << "NvEnc: cuMemFree() failed: error " << last_cuda_error; + } + cuda_surface = 0; + } + } + + if (cuda_failed(cuda_functions.cuCtxDestroy(cuda_context))) { + BOOST_LOG(error) << "NvEnc: cuCtxDestroy() failed: error " << last_cuda_error; + } + cuda_context = nullptr; + } + + if (cuda_functions.dll) { + FreeLibrary(cuda_functions.dll); + cuda_functions = {}; + } + } + + ID3D11Texture2D *nvenc_d3d11_on_cuda::get_input_texture() { + return d3d_input_texture.GetInterfacePtr(); + } + + bool nvenc_d3d11_on_cuda::init_library() { + if (!nvenc_d3d11::init_library()) { + return false; + } + + constexpr auto dll_name = "nvcuda.dll"; + + if ((cuda_functions.dll = LoadLibraryEx(dll_name, NULL, LOAD_LIBRARY_SEARCH_SYSTEM32))) { + auto load_function = [&](T &location, auto symbol) -> bool { + location = (T) GetProcAddress(cuda_functions.dll, symbol); + return location != nullptr; + }; + if (!load_function(cuda_functions.cuInit, "cuInit") || + !load_function(cuda_functions.cuD3D11GetDevice, "cuD3D11GetDevice") || + !load_function(cuda_functions.cuCtxCreate, "cuCtxCreate_v2") || + !load_function(cuda_functions.cuCtxDestroy, "cuCtxDestroy_v2") || + !load_function(cuda_functions.cuCtxPushCurrent, "cuCtxPushCurrent_v2") || + !load_function(cuda_functions.cuCtxPopCurrent, "cuCtxPopCurrent_v2") || + !load_function(cuda_functions.cuMemAllocPitch, "cuMemAllocPitch_v2") || + !load_function(cuda_functions.cuMemFree, "cuMemFree_v2") || + !load_function(cuda_functions.cuGraphicsD3D11RegisterResource, "cuGraphicsD3D11RegisterResource") || + !load_function(cuda_functions.cuGraphicsUnregisterResource, "cuGraphicsUnregisterResource") || + !load_function(cuda_functions.cuGraphicsMapResources, "cuGraphicsMapResources") || + !load_function(cuda_functions.cuGraphicsUnmapResources, "cuGraphicsUnmapResources") || + !load_function(cuda_functions.cuGraphicsSubResourceGetMappedArray, "cuGraphicsSubResourceGetMappedArray") || + !load_function(cuda_functions.cuMemcpy2D, "cuMemcpy2D_v2")) { + BOOST_LOG(error) << "NvEnc: missing CUDA functions in " << dll_name; + FreeLibrary(cuda_functions.dll); + cuda_functions = {}; + } + } else { + BOOST_LOG(debug) << "NvEnc: couldn't load CUDA dynamic library " << dll_name; + } + + if (cuda_functions.dll) { + IDXGIDevicePtr dxgi_device; + IDXGIAdapterPtr dxgi_adapter; + if (d3d_device && + SUCCEEDED(d3d_device->QueryInterface(IID_PPV_ARGS(&dxgi_device))) && + SUCCEEDED(dxgi_device->GetAdapter(&dxgi_adapter))) { + CUdevice cuda_device; + if (cuda_succeeded(cuda_functions.cuInit(0)) && + cuda_succeeded(cuda_functions.cuD3D11GetDevice(&cuda_device, dxgi_adapter)) && + cuda_succeeded(cuda_functions.cuCtxCreate(&cuda_context, CU_CTX_SCHED_BLOCKING_SYNC, cuda_device)) && + cuda_succeeded(cuda_functions.cuCtxPopCurrent(&cuda_context))) { + device = cuda_context; + } else { + BOOST_LOG(error) << "NvEnc: couldn't create CUDA interop context: error " << last_cuda_error; + } + } else { + BOOST_LOG(error) << "NvEnc: couldn't get DXGI adapter for CUDA interop"; + } + } + + return device != nullptr; + } + + bool nvenc_d3d11_on_cuda::create_and_register_input_buffer() { + if (encoder_params.buffer_format != NV_ENC_BUFFER_FORMAT_YUV444_10BIT) { + BOOST_LOG(error) << "NvEnc: CUDA interop is expected to be used only for 10-bit 4:4:4 encoding"; + return false; + } + + if (!d3d_input_texture) { + D3D11_TEXTURE2D_DESC desc = {}; + desc.Width = encoder_params.width; + desc.Height = encoder_params.height * 3; // Planar YUV + desc.MipLevels = 1; + desc.ArraySize = 1; + desc.Format = dxgi_format_from_nvenc_format(encoder_params.buffer_format); + desc.SampleDesc.Count = 1; + desc.Usage = D3D11_USAGE_DEFAULT; + desc.BindFlags = D3D11_BIND_RENDER_TARGET; + + if (d3d_device->CreateTexture2D(&desc, nullptr, &d3d_input_texture) != S_OK) { + BOOST_LOG(error) << "NvEnc: couldn't create input texture"; + return false; + } + } + + { + auto autopop_context = push_context(); + if (!autopop_context) { + return false; + } + + if (!cuda_d3d_input_texture) { + if (cuda_failed(cuda_functions.cuGraphicsD3D11RegisterResource( + &cuda_d3d_input_texture, + d3d_input_texture, + CU_GRAPHICS_REGISTER_FLAGS_NONE + ))) { + BOOST_LOG(error) << "NvEnc: cuGraphicsD3D11RegisterResource() failed: error " << last_cuda_error; + return false; + } + } + + if (!cuda_surface) { + if (cuda_failed(cuda_functions.cuMemAllocPitch( + &cuda_surface, + &cuda_surface_pitch, + // Planar 16-bit YUV + encoder_params.width * 2, + encoder_params.height * 3, + 16 + ))) { + BOOST_LOG(error) << "NvEnc: cuMemAllocPitch() failed: error " << last_cuda_error; + return false; + } + } + } + + if (!registered_input_buffer) { + NV_ENC_REGISTER_RESOURCE register_resource = {min_struct_version(NV_ENC_REGISTER_RESOURCE_VER, 3, 4)}; + register_resource.resourceType = NV_ENC_INPUT_RESOURCE_TYPE_CUDADEVICEPTR; + register_resource.width = encoder_params.width; + register_resource.height = encoder_params.height; + register_resource.pitch = cuda_surface_pitch; + register_resource.resourceToRegister = (void *) cuda_surface; + register_resource.bufferFormat = encoder_params.buffer_format; + register_resource.bufferUsage = NV_ENC_INPUT_IMAGE; + + if (nvenc_failed(nvenc->nvEncRegisterResource(encoder, ®ister_resource))) { + BOOST_LOG(error) << "NvEnc: NvEncRegisterResource() failed: " << last_nvenc_error_string; + return false; + } + + registered_input_buffer = register_resource.registeredResource; + } + + return true; + } + + bool nvenc_d3d11_on_cuda::synchronize_input_buffer() { + auto autopop_context = push_context(); + if (!autopop_context) { + return false; + } + + if (cuda_failed(cuda_functions.cuGraphicsMapResources(1, &cuda_d3d_input_texture, 0))) { + BOOST_LOG(error) << "NvEnc: cuGraphicsMapResources() failed: error " << last_cuda_error; + return false; + } + + auto unmap = [&]() -> bool { + if (cuda_failed(cuda_functions.cuGraphicsUnmapResources(1, &cuda_d3d_input_texture, 0))) { + BOOST_LOG(error) << "NvEnc: cuGraphicsUnmapResources() failed: error " << last_cuda_error; + return false; + } + return true; + }; + auto unmap_guard = util::fail_guard(unmap); + + CUarray input_texture_array; + if (cuda_failed(cuda_functions.cuGraphicsSubResourceGetMappedArray(&input_texture_array, cuda_d3d_input_texture, 0, 0))) { + BOOST_LOG(error) << "NvEnc: cuGraphicsSubResourceGetMappedArray() failed: error " << last_cuda_error; + return false; + } + + { + CUDA_MEMCPY2D copy_params = {}; + copy_params.srcMemoryType = CU_MEMORYTYPE_ARRAY; + copy_params.srcArray = input_texture_array; + copy_params.dstMemoryType = CU_MEMORYTYPE_DEVICE; + copy_params.dstDevice = cuda_surface; + copy_params.dstPitch = cuda_surface_pitch; + // Planar 16-bit YUV + copy_params.WidthInBytes = encoder_params.width * 2; + copy_params.Height = encoder_params.height * 3; + + if (cuda_failed(cuda_functions.cuMemcpy2D(©_params))) { + BOOST_LOG(error) << "NvEnc: cuMemcpy2D() failed: error " << last_cuda_error; + return false; + } + } + + unmap_guard.disable(); + return unmap(); + } + + bool nvenc_d3d11_on_cuda::cuda_succeeded(CUresult result) { + last_cuda_error = result; + return result == CUDA_SUCCESS; + } + + bool nvenc_d3d11_on_cuda::cuda_failed(CUresult result) { + last_cuda_error = result; + return result != CUDA_SUCCESS; + } + + nvenc_d3d11_on_cuda::autopop_context::~autopop_context() { + if (pushed_context) { + CUcontext popped_context; + if (parent.cuda_failed(parent.cuda_functions.cuCtxPopCurrent(&popped_context))) { + BOOST_LOG(error) << "NvEnc: cuCtxPopCurrent() failed: error " << parent.last_cuda_error; + } + } + } + + nvenc_d3d11_on_cuda::autopop_context nvenc_d3d11_on_cuda::push_context() { + if (cuda_context && + cuda_succeeded(cuda_functions.cuCtxPushCurrent(cuda_context))) { + return {*this, cuda_context}; + } else { + BOOST_LOG(error) << "NvEnc: cuCtxPushCurrent() failed: error " << last_cuda_error; + return {*this, nullptr}; + } + } + +} // namespace nvenc +#endif diff --git a/src/nvenc/nvenc_d3d11_on_cuda.h b/src/nvenc/nvenc_d3d11_on_cuda.h index 81114321..1c912f50 100644 --- a/src/nvenc/nvenc_d3d11_on_cuda.h +++ b/src/nvenc/nvenc_d3d11_on_cuda.h @@ -1,96 +1,89 @@ -/** - * @file src/nvenc/nvenc_d3d11_on_cuda.h - * @brief Declarations for CUDA NVENC encoder with Direct3D11 input surfaces. - */ -#pragma once -#ifdef _WIN32 - - #include "nvenc_d3d11.h" - - #include - -namespace nvenc { - - /** - * @brief Interop Direct3D11 on CUDA NVENC encoder. - * Input surface is Direct3D11, encoding is performed by CUDA. - */ - class nvenc_d3d11_on_cuda final: public nvenc_d3d11 { - public: - /** - * @param d3d_device Direct3D11 device that will create input surface texture. - * CUDA encoding device will be derived from it. - */ - explicit nvenc_d3d11_on_cuda(ID3D11Device *d3d_device); - ~nvenc_d3d11_on_cuda(); - - ID3D11Texture2D * - get_input_texture() override; - - private: - bool - init_library() override; - - bool - create_and_register_input_buffer() override; - - bool - synchronize_input_buffer() override; - - bool - cuda_succeeded(CUresult result); - - bool - cuda_failed(CUresult result); - - struct autopop_context { - autopop_context(nvenc_d3d11_on_cuda &parent, CUcontext pushed_context): - parent(parent), - pushed_context(pushed_context) { - } - - ~autopop_context(); - - explicit - operator bool() const { - return pushed_context != nullptr; - } - - nvenc_d3d11_on_cuda &parent; - CUcontext pushed_context = nullptr; - }; - - autopop_context - push_context(); - - HMODULE dll = NULL; - const ID3D11DevicePtr d3d_device; - ID3D11Texture2DPtr d3d_input_texture; - - struct { - tcuInit *cuInit; - tcuD3D11GetDevice *cuD3D11GetDevice; - tcuCtxCreate_v2 *cuCtxCreate; - tcuCtxDestroy_v2 *cuCtxDestroy; - tcuCtxPushCurrent_v2 *cuCtxPushCurrent; - tcuCtxPopCurrent_v2 *cuCtxPopCurrent; - tcuMemAllocPitch_v2 *cuMemAllocPitch; - tcuMemFree_v2 *cuMemFree; - tcuGraphicsD3D11RegisterResource *cuGraphicsD3D11RegisterResource; - tcuGraphicsUnregisterResource *cuGraphicsUnregisterResource; - tcuGraphicsMapResources *cuGraphicsMapResources; - tcuGraphicsUnmapResources *cuGraphicsUnmapResources; - tcuGraphicsSubResourceGetMappedArray *cuGraphicsSubResourceGetMappedArray; - tcuMemcpy2D_v2 *cuMemcpy2D; - HMODULE dll; - } cuda_functions = {}; - - CUresult last_cuda_error = CUDA_SUCCESS; - CUcontext cuda_context = nullptr; - CUgraphicsResource cuda_d3d_input_texture = nullptr; - CUdeviceptr cuda_surface = 0; - size_t cuda_surface_pitch = 0; - }; - -} // namespace nvenc -#endif +/** + * @file src/nvenc/nvenc_d3d11_on_cuda.h + * @brief Declarations for CUDA NVENC encoder with Direct3D11 input surfaces. + */ +#pragma once +#ifdef _WIN32 + // lib includes + #include + + // local includes + #include "nvenc_d3d11.h" + +namespace nvenc { + + /** + * @brief Interop Direct3D11 on CUDA NVENC encoder. + * Input surface is Direct3D11, encoding is performed by CUDA. + */ + class nvenc_d3d11_on_cuda final: public nvenc_d3d11 { + public: + /** + * @param d3d_device Direct3D11 device that will create input surface texture. + * CUDA encoding device will be derived from it. + */ + explicit nvenc_d3d11_on_cuda(ID3D11Device *d3d_device); + ~nvenc_d3d11_on_cuda(); + + ID3D11Texture2D *get_input_texture() override; + + private: + bool init_library() override; + + bool create_and_register_input_buffer() override; + + bool synchronize_input_buffer() override; + + bool cuda_succeeded(CUresult result); + + bool cuda_failed(CUresult result); + + struct autopop_context { + autopop_context(nvenc_d3d11_on_cuda &parent, CUcontext pushed_context): + parent(parent), + pushed_context(pushed_context) { + } + + ~autopop_context(); + + explicit operator bool() const { + return pushed_context != nullptr; + } + + nvenc_d3d11_on_cuda &parent; + CUcontext pushed_context = nullptr; + }; + + autopop_context push_context(); + + HMODULE dll = NULL; + const ID3D11DevicePtr d3d_device; + ID3D11Texture2DPtr d3d_input_texture; + + struct { + tcuInit *cuInit; + tcuD3D11GetDevice *cuD3D11GetDevice; + tcuCtxCreate_v2 *cuCtxCreate; + tcuCtxDestroy_v2 *cuCtxDestroy; + tcuCtxPushCurrent_v2 *cuCtxPushCurrent; + tcuCtxPopCurrent_v2 *cuCtxPopCurrent; + tcuMemAllocPitch_v2 *cuMemAllocPitch; + tcuMemFree_v2 *cuMemFree; + tcuGraphicsD3D11RegisterResource *cuGraphicsD3D11RegisterResource; + tcuGraphicsUnregisterResource *cuGraphicsUnregisterResource; + tcuGraphicsMapResources *cuGraphicsMapResources; + tcuGraphicsUnmapResources *cuGraphicsUnmapResources; + tcuGraphicsSubResourceGetMappedArray *cuGraphicsSubResourceGetMappedArray; + tcuMemcpy2D_v2 *cuMemcpy2D; + HMODULE dll; + } cuda_functions = {}; + + CUresult last_cuda_error = CUDA_SUCCESS; + CUcontext cuda_context = nullptr; + CUgraphicsResource cuda_d3d_input_texture = nullptr; + CUdeviceptr cuda_surface = 0; + size_t cuda_surface_pitch = 0; + }; + +} // namespace nvenc +#endif diff --git a/src/nvenc/nvenc_encoded_frame.h b/src/nvenc/nvenc_encoded_frame.h index 46a8e46d..ff94de46 100644 --- a/src/nvenc/nvenc_encoded_frame.h +++ b/src/nvenc/nvenc_encoded_frame.h @@ -1,22 +1,23 @@ -/** - * @file src/nvenc/nvenc_encoded_frame.h - * @brief Declarations for NVENC encoded frame. - */ -#pragma once - -#include -#include - -namespace nvenc { - - /** - * @brief Encoded frame. - */ - struct nvenc_encoded_frame { - std::vector data; - uint64_t frame_index = 0; - bool idr = false; - bool after_ref_frame_invalidation = false; - }; - -} // namespace nvenc +/** + * @file src/nvenc/nvenc_encoded_frame.h + * @brief Declarations for NVENC encoded frame. + */ +#pragma once + +// standard includes +#include +#include + +namespace nvenc { + + /** + * @brief Encoded frame. + */ + struct nvenc_encoded_frame { + std::vector data; + uint64_t frame_index = 0; + bool idr = false; + bool after_ref_frame_invalidation = false; + }; + +} // namespace nvenc diff --git a/src/nvenc/nvenc_utils.cpp b/src/nvenc/nvenc_utils.cpp index 26e2dc30..2d19bd46 100644 --- a/src/nvenc/nvenc_utils.cpp +++ b/src/nvenc/nvenc_utils.cpp @@ -1,94 +1,93 @@ -/** - * @file src/nvenc/nvenc_utils.cpp - * @brief Definitions for NVENC utilities. - */ -#include - -#include "nvenc_utils.h" - -namespace nvenc { - -#ifdef _WIN32 - DXGI_FORMAT - dxgi_format_from_nvenc_format(NV_ENC_BUFFER_FORMAT format) { - switch (format) { - case NV_ENC_BUFFER_FORMAT_YUV420_10BIT: - return DXGI_FORMAT_P010; - - case NV_ENC_BUFFER_FORMAT_NV12: - return DXGI_FORMAT_NV12; - - case NV_ENC_BUFFER_FORMAT_AYUV: - return DXGI_FORMAT_AYUV; - - case NV_ENC_BUFFER_FORMAT_YUV444_10BIT: - return DXGI_FORMAT_R16_UINT; - - default: - return DXGI_FORMAT_UNKNOWN; - } - } -#endif - - NV_ENC_BUFFER_FORMAT - nvenc_format_from_sunshine_format(platf::pix_fmt_e format) { - switch (format) { - case platf::pix_fmt_e::nv12: - return NV_ENC_BUFFER_FORMAT_NV12; - - case platf::pix_fmt_e::p010: - return NV_ENC_BUFFER_FORMAT_YUV420_10BIT; - - case platf::pix_fmt_e::ayuv: - return NV_ENC_BUFFER_FORMAT_AYUV; - - case platf::pix_fmt_e::yuv444p16: - return NV_ENC_BUFFER_FORMAT_YUV444_10BIT; - - default: - return NV_ENC_BUFFER_FORMAT_UNDEFINED; - } - } - - nvenc_colorspace_t - nvenc_colorspace_from_sunshine_colorspace(const video::sunshine_colorspace_t &sunshine_colorspace) { - nvenc_colorspace_t colorspace; - - switch (sunshine_colorspace.colorspace) { - case video::colorspace_e::rec601: - // Rec. 601 - colorspace.primaries = NV_ENC_VUI_COLOR_PRIMARIES_SMPTE170M; - colorspace.tranfer_function = NV_ENC_VUI_TRANSFER_CHARACTERISTIC_SMPTE170M; - colorspace.matrix = NV_ENC_VUI_MATRIX_COEFFS_SMPTE170M; - break; - - case video::colorspace_e::rec709: - // Rec. 709 - colorspace.primaries = NV_ENC_VUI_COLOR_PRIMARIES_BT709; - colorspace.tranfer_function = NV_ENC_VUI_TRANSFER_CHARACTERISTIC_BT709; - colorspace.matrix = NV_ENC_VUI_MATRIX_COEFFS_BT709; - break; - - case video::colorspace_e::bt2020sdr: - // Rec. 2020 - colorspace.primaries = NV_ENC_VUI_COLOR_PRIMARIES_BT2020; - assert(sunshine_colorspace.bit_depth == 10); - colorspace.tranfer_function = NV_ENC_VUI_TRANSFER_CHARACTERISTIC_BT2020_10; - colorspace.matrix = NV_ENC_VUI_MATRIX_COEFFS_BT2020_NCL; - break; - - case video::colorspace_e::bt2020: - // Rec. 2020 with ST 2084 perceptual quantizer - colorspace.primaries = NV_ENC_VUI_COLOR_PRIMARIES_BT2020; - assert(sunshine_colorspace.bit_depth == 10); - colorspace.tranfer_function = NV_ENC_VUI_TRANSFER_CHARACTERISTIC_SMPTE2084; - colorspace.matrix = NV_ENC_VUI_MATRIX_COEFFS_BT2020_NCL; - break; - } - - colorspace.full_range = sunshine_colorspace.full_range; - - return colorspace; - } - -} // namespace nvenc +/** + * @file src/nvenc/nvenc_utils.cpp + * @brief Definitions for NVENC utilities. + */ +// standard includes +#include + +// local includes +#include "nvenc_utils.h" + +namespace nvenc { + +#ifdef _WIN32 + DXGI_FORMAT dxgi_format_from_nvenc_format(NV_ENC_BUFFER_FORMAT format) { + switch (format) { + case NV_ENC_BUFFER_FORMAT_YUV420_10BIT: + return DXGI_FORMAT_P010; + + case NV_ENC_BUFFER_FORMAT_NV12: + return DXGI_FORMAT_NV12; + + case NV_ENC_BUFFER_FORMAT_AYUV: + return DXGI_FORMAT_AYUV; + + case NV_ENC_BUFFER_FORMAT_YUV444_10BIT: + return DXGI_FORMAT_R16_UINT; + + default: + return DXGI_FORMAT_UNKNOWN; + } + } +#endif + + NV_ENC_BUFFER_FORMAT nvenc_format_from_sunshine_format(platf::pix_fmt_e format) { + switch (format) { + case platf::pix_fmt_e::nv12: + return NV_ENC_BUFFER_FORMAT_NV12; + + case platf::pix_fmt_e::p010: + return NV_ENC_BUFFER_FORMAT_YUV420_10BIT; + + case platf::pix_fmt_e::ayuv: + return NV_ENC_BUFFER_FORMAT_AYUV; + + case platf::pix_fmt_e::yuv444p16: + return NV_ENC_BUFFER_FORMAT_YUV444_10BIT; + + default: + return NV_ENC_BUFFER_FORMAT_UNDEFINED; + } + } + + nvenc_colorspace_t nvenc_colorspace_from_sunshine_colorspace(const video::sunshine_colorspace_t &sunshine_colorspace) { + nvenc_colorspace_t colorspace; + + switch (sunshine_colorspace.colorspace) { + case video::colorspace_e::rec601: + // Rec. 601 + colorspace.primaries = NV_ENC_VUI_COLOR_PRIMARIES_SMPTE170M; + colorspace.tranfer_function = NV_ENC_VUI_TRANSFER_CHARACTERISTIC_SMPTE170M; + colorspace.matrix = NV_ENC_VUI_MATRIX_COEFFS_SMPTE170M; + break; + + case video::colorspace_e::rec709: + // Rec. 709 + colorspace.primaries = NV_ENC_VUI_COLOR_PRIMARIES_BT709; + colorspace.tranfer_function = NV_ENC_VUI_TRANSFER_CHARACTERISTIC_BT709; + colorspace.matrix = NV_ENC_VUI_MATRIX_COEFFS_BT709; + break; + + case video::colorspace_e::bt2020sdr: + // Rec. 2020 + colorspace.primaries = NV_ENC_VUI_COLOR_PRIMARIES_BT2020; + assert(sunshine_colorspace.bit_depth == 10); + colorspace.tranfer_function = NV_ENC_VUI_TRANSFER_CHARACTERISTIC_BT2020_10; + colorspace.matrix = NV_ENC_VUI_MATRIX_COEFFS_BT2020_NCL; + break; + + case video::colorspace_e::bt2020: + // Rec. 2020 with ST 2084 perceptual quantizer + colorspace.primaries = NV_ENC_VUI_COLOR_PRIMARIES_BT2020; + assert(sunshine_colorspace.bit_depth == 10); + colorspace.tranfer_function = NV_ENC_VUI_TRANSFER_CHARACTERISTIC_SMPTE2084; + colorspace.matrix = NV_ENC_VUI_MATRIX_COEFFS_BT2020_NCL; + break; + } + + colorspace.full_range = sunshine_colorspace.full_range; + + return colorspace; + } + +} // namespace nvenc diff --git a/src/nvenc/nvenc_utils.h b/src/nvenc/nvenc_utils.h index db428676..e8b48eee 100644 --- a/src/nvenc/nvenc_utils.h +++ b/src/nvenc/nvenc_utils.h @@ -1,31 +1,30 @@ -/** - * @file src/nvenc/nvenc_utils.h - * @brief Declarations for NVENC utilities. - */ -#pragma once - -#ifdef _WIN32 - #include -#endif - -#include "nvenc_colorspace.h" - -#include "src/platform/common.h" -#include "src/video_colorspace.h" - -#include - -namespace nvenc { - -#ifdef _WIN32 - DXGI_FORMAT - dxgi_format_from_nvenc_format(NV_ENC_BUFFER_FORMAT format); -#endif - - NV_ENC_BUFFER_FORMAT - nvenc_format_from_sunshine_format(platf::pix_fmt_e format); - - nvenc_colorspace_t - nvenc_colorspace_from_sunshine_colorspace(const video::sunshine_colorspace_t &sunshine_colorspace); - -} // namespace nvenc +/** + * @file src/nvenc/nvenc_utils.h + * @brief Declarations for NVENC utilities. + */ +#pragma once + +// plafform includes +#ifdef _WIN32 + #include +#endif + +// lib includes +#include + +// local includes +#include "nvenc_colorspace.h" +#include "src/platform/common.h" +#include "src/video_colorspace.h" + +namespace nvenc { + +#ifdef _WIN32 + DXGI_FORMAT dxgi_format_from_nvenc_format(NV_ENC_BUFFER_FORMAT format); +#endif + + NV_ENC_BUFFER_FORMAT nvenc_format_from_sunshine_format(platf::pix_fmt_e format); + + nvenc_colorspace_t nvenc_colorspace_from_sunshine_colorspace(const video::sunshine_colorspace_t &sunshine_colorspace); + +} // namespace nvenc diff --git a/src/nvhttp.cpp b/src/nvhttp.cpp index 83505310..52d15014 100644 --- a/src/nvhttp.cpp +++ b/src/nvhttp.cpp @@ -7,16 +7,16 @@ // standard includes #include +#include #include // lib includes -#include #include #include #include #include #include -#include +#include // local includes #include "config.h" @@ -36,6 +36,7 @@ #include "video.h" using namespace std::literals; + namespace nvhttp { namespace fs = std::filesystem; @@ -61,8 +62,7 @@ namespace nvhttp { protected: boost::asio::ssl::context context; - void - after_bind() override { + void after_bind() override { if (verify) { context.set_verify_mode(boost::asio::ssl::verify_peer | boost::asio::ssl::verify_fail_if_no_peer_cert | boost::asio::ssl::verify_client_once); context.set_verify_callback([](int verified, boost::asio::ssl::verify_context &ctx) { @@ -73,17 +73,18 @@ namespace nvhttp { } // This is Server::accept() with SSL validation support added - void - accept() override { + void accept() override { auto connection = create_connection(*io_service, context); acceptor->async_accept(connection->socket->lowest_layer(), [this, connection](const SimpleWeb::error_code &ec) { auto lock = connection->handler_runner->continue_lock(); - if (!lock) + if (!lock) { return; + } - if (ec != SimpleWeb::error::operation_aborted) + if (ec != SimpleWeb::error::operation_aborted) { this->accept(); + } auto session = std::make_shared(config.max_request_streambuf_size, connection); @@ -96,20 +97,22 @@ namespace nvhttp { session->connection->socket->async_handshake(boost::asio::ssl::stream_base::server, [this, session](const SimpleWeb::error_code &ec) { session->connection->cancel_timeout(); auto lock = session->connection->handler_runner->continue_lock(); - if (!lock) + if (!lock) { return; - if (!ec) { - if (verify && !verify(session->connection->socket->native_handle())) - this->write(session, on_verify_failed); - else - this->read(session); } - else if (this->on_error) + if (!ec) { + if (verify && !verify(session->connection->socket->native_handle())) { + this->write(session, on_verify_failed); + } else { + this->read(session); + } + } else if (this->on_error) { this->on_error(session->request, ec); + } }); - } - else if (this->on_error) + } else if (this->on_error) { this->on_error(session->request, ec); + } }); } }; @@ -148,8 +151,7 @@ namespace nvhttp { REMOVE ///< Remove certificate }; - std::string - get_arg(const args_t &args, const char *name, const char *default_value = nullptr) { + std::string get_arg(const args_t &args, const char *name, const char *default_value = nullptr) { auto it = args.find(name); if (it == std::end(args)) { if (default_value != NULL) { @@ -161,15 +163,13 @@ namespace nvhttp { return it->second; } - void - save_state() { + void save_state() { pt::ptree root; if (fs::exists(config::nvhttp.file_state)) { try { pt::read_json(config::nvhttp.file_state, root); - } - catch (std::exception &e) { + } catch (std::exception &e) { BOOST_LOG(error) << "Couldn't read "sv << config::nvhttp.file_state << ": "sv << e.what(); return; } @@ -193,15 +193,13 @@ namespace nvhttp { try { pt::write_json(config::nvhttp.file_state, root); - } - catch (std::exception &e) { + } catch (std::exception &e) { BOOST_LOG(error) << "Couldn't write "sv << config::nvhttp.file_state << ": "sv << e.what(); return; } } - void - load_state() { + void load_state() { if (!fs::exists(config::nvhttp.file_state)) { BOOST_LOG(info) << "File "sv << config::nvhttp.file_state << " doesn't exist"sv; http::unique_id = uuid_util::uuid_t::generate().string(); @@ -211,8 +209,7 @@ namespace nvhttp { pt::ptree tree; try { pt::read_json(config::nvhttp.file_state, tree); - } - catch (std::exception &e) { + } catch (std::exception &e) { BOOST_LOG(error) << "Couldn't read "sv << config::nvhttp.file_state << ": "sv << e.what(); return; @@ -266,8 +263,7 @@ namespace nvhttp { client_root = client; } - void - add_authorized_client(const std::string &name, std::string &&cert) { + void add_authorized_client(const std::string &name, std::string &&cert) { client_t &client = client_root; named_cert_t named_cert; named_cert.name = name; @@ -280,8 +276,7 @@ namespace nvhttp { } } - std::shared_ptr - make_launch_session(bool host_audio, const args_t &args) { + std::shared_ptr make_launch_session(bool host_audio, const args_t &args) { auto launch_session = std::make_shared(); launch_session->id = ++session_id_counter; @@ -295,9 +290,15 @@ namespace nvhttp { int x = 0; std::string segment; while (std::getline(mode, segment, 'x')) { - if (x == 0) launch_session->width = atoi(segment.c_str()); - if (x == 1) launch_session->height = atoi(segment.c_str()); - if (x == 2) launch_session->fps = atoi(segment.c_str()); + if (x == 0) { + launch_session->width = atoi(segment.c_str()); + } + if (x == 1) { + launch_session->height = atoi(segment.c_str()); + } + if (x == 2) { + launch_session->fps = atoi(segment.c_str()); + } x++; } launch_session->unique_id = (get_arg(args, "uniqueid", "unknown")); @@ -312,7 +313,8 @@ namespace nvhttp { auto corever = util::from_view(get_arg(args, "corever", "0")); if (corever >= 1) { launch_session->rtsp_cipher = crypto::cipher::gcm_t { - launch_session->gcm_key, false + launch_session->gcm_key, + false }; launch_session->rtsp_iv_counter = 0; } @@ -331,21 +333,18 @@ namespace nvhttp { return launch_session; } - void - remove_session(const pair_session_t &sess) { + void remove_session(const pair_session_t &sess) { map_id_sess.erase(sess.client.uniqueID); } - void - fail_pair(pair_session_t &sess, pt::ptree &tree, const std::string status_msg) { + void fail_pair(pair_session_t &sess, pt::ptree &tree, const std::string status_msg) { tree.put("root.paired", 0); tree.put("root..status_code", 400); tree.put("root..status_message", status_msg); remove_session(sess); // Security measure, delete the session when something went wrong and force a re-pair } - void - getservercert(pair_session_t &sess, pt::ptree &tree, const std::string &pin) { + void getservercert(pair_session_t &sess, pt::ptree &tree, const std::string &pin) { if (sess.last_phase != PAIR_PHASE::NONE) { fail_pair(sess, tree, "Out of order call to getservercert"); return; @@ -357,7 +356,7 @@ namespace nvhttp { return; } - std::string_view salt_view { sess.async_insert_pin.salt.data(), 32 }; + std::string_view salt_view {sess.async_insert_pin.salt.data(), 32}; auto salt = util::from_hex>(salt_view, true); @@ -369,8 +368,7 @@ namespace nvhttp { tree.put("root..status_code", 200); } - void - clientchallenge(pair_session_t &sess, pt::ptree &tree, const std::string &challenge) { + void clientchallenge(pair_session_t &sess, pt::ptree &tree, const std::string &challenge) { if (sess.last_phase != PAIR_PHASE::GETSERVERCERT) { fail_pair(sess, tree, "Out of order call to clientchallenge"); return; @@ -393,7 +391,7 @@ namespace nvhttp { 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; @@ -413,8 +411,7 @@ namespace nvhttp { tree.put("root..status_code", 200); } - void - serverchallengeresp(pair_session_t &sess, pt::ptree &tree, const std::string &encrypted_response) { + void serverchallengeresp(pair_session_t &sess, pt::ptree &tree, const std::string &encrypted_response) { if (sess.last_phase != PAIR_PHASE::CLIENTCHALLENGE) { fail_pair(sess, tree, "Out of order call to serverchallengeresp"); return; @@ -443,8 +440,7 @@ namespace nvhttp { tree.put("root..status_code", 200); } - void - clientpairingsecret(pair_session_t &sess, std::shared_ptr> &add_cert, pt::ptree &tree, const std::string &client_pairing_secret) { + void clientpairingsecret(pair_session_t &sess, std::shared_ptr> &add_cert, pt::ptree &tree, const std::string &client_pairing_secret) { if (sess.last_phase != PAIR_PHASE::SERVERCHALLENGERESP) { fail_pair(sess, tree, "Out of order call to clientpairingsecret"); return; @@ -458,8 +454,8 @@ namespace nvhttp { return; } - std::string_view secret { client_pairing_secret.data(), 16 }; - std::string_view sign { client_pairing_secret.data() + secret.size(), client_pairing_secret.size() - secret.size() }; + std::string_view secret {client_pairing_secret.data(), 16}; + std::string_view sign {client_pairing_secret.data() + secret.size(), client_pairing_secret.size() - secret.size()}; auto x509 = crypto::x509(client.cert); if (!x509) { @@ -486,8 +482,7 @@ namespace nvhttp { // The client is now successfully paired and will be authorized to connect add_authorized_client(client.name, std::move(client.cert)); - } - else { + } else { tree.put("root.paired", 0); } @@ -495,22 +490,21 @@ namespace nvhttp { tree.put("root..status_code", 200); } - template + template struct tunnel; - template <> + template<> struct tunnel { static auto constexpr to_string = "HTTPS"sv; }; - template <> + template<> struct tunnel { static auto constexpr to_string = "NONE"sv; }; - template - void - print_req(std::shared_ptr::Request> request) { + template + void print_req(std::shared_ptr::Request> request) { BOOST_LOG(debug) << "TUNNEL :: "sv << tunnel::to_string; BOOST_LOG(debug) << "METHOD :: "sv << request->method; @@ -529,9 +523,8 @@ namespace nvhttp { BOOST_LOG(debug) << " [--] "sv; } - template - void - not_found(std::shared_ptr::Response> response, std::shared_ptr::Request> request) { + template + void not_found(std::shared_ptr::Response> response, std::shared_ptr::Request> request) { print_req(request); pt::ptree tree; @@ -549,9 +542,8 @@ namespace nvhttp { response->close_connection_after_response = true; } - template - void - pair(std::shared_ptr> &add_cert, std::shared_ptr::Response> response, std::shared_ptr::Request> request) { + template + void pair(std::shared_ptr> &add_cert, std::shared_ptr::Response> response, std::shared_ptr::Request> request) { print_req(request); pt::ptree tree; @@ -572,7 +564,7 @@ namespace nvhttp { return; } - auto uniqID { get_arg(args, "uniqueid") }; + auto uniqID {get_arg(args, "uniqueid")}; args_t::const_iterator it; if (it = args.find("phrase"); it != std::end(args)) { @@ -593,8 +585,7 @@ namespace nvhttp { std::getline(std::cin, pin); getservercert(ptr->second, tree, pin); - } - else { + } else { #if defined SUNSHINE_TRAY && SUNSHINE_TRAY >= 1 system_tray::update_tray_require_pin(); #endif @@ -603,8 +594,7 @@ namespace nvhttp { fg.disable(); return; } - } - else if (it->second == "pairchallenge"sv) { + } else if (it->second == "pairchallenge"sv) { tree.put("root.paired", 1); tree.put("root..status_code", 200); return; @@ -622,23 +612,19 @@ namespace nvhttp { if (it = args.find("clientchallenge"); it != std::end(args)) { auto challenge = util::from_hex_vec(it->second, true); clientchallenge(sess_it->second, tree, challenge); - } - else if (it = args.find("serverchallengeresp"); it != std::end(args)) { + } else if (it = args.find("serverchallengeresp"); it != std::end(args)) { auto encrypted_response = util::from_hex_vec(it->second, true); serverchallengeresp(sess_it->second, tree, encrypted_response); - } - else if (it = args.find("clientpairingsecret"); it != std::end(args)) { + } else if (it = args.find("clientpairingsecret"); it != std::end(args)) { auto pairingsecret = util::from_hex_vec(it->second, true); clientpairingsecret(sess_it->second, add_cert, tree, pairingsecret); - } - else { + } else { tree.put("root..status_code", 404); tree.put("root..status_message", "Invalid pairing request"); } } - bool - pin(std::string pin, std::string name) { + bool pin(std::string pin, std::string name) { pt::ptree tree; if (map_id_sess.empty()) { return false; @@ -649,7 +635,9 @@ namespace nvhttp { tree.put("root.paired", 0); tree.put("root..status_code", 400); tree.put( - "root..status_message", "Pin must be 4 digits, " + std::to_string(pin.size()) + " provided"); + "root..status_message", + "Pin must be 4 digits, " + std::to_string(pin.size()) + " provided" + ); return false; } @@ -672,11 +660,9 @@ namespace nvhttp { auto &async_response = sess.async_insert_pin.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 { + } else { return false; } @@ -686,9 +672,8 @@ namespace nvhttp { return true; } - template - void - serverinfo(std::shared_ptr::Response> response, std::shared_ptr::Request> request) { + template + void serverinfo(std::shared_ptr::Response> response, std::shared_ptr::Request> request) { print_req(request); int pair_status = 0; @@ -719,8 +704,7 @@ namespace nvhttp { // For HTTP requests, use a placeholder MAC address that Moonlight knows to ignore. if constexpr (std::is_same_v) { tree.put("root.mac", platf::get_mac_address(net::addr_to_normalized_string(local_endpoint.address()))); - } - else { + } else { tree.put("root.mac", "00:00:00:00:00:00"); } @@ -735,8 +719,7 @@ namespace nvhttp { // support know to ignore this bogus address. if (local_endpoint.address().is_v6() && !local_endpoint.address().to_v6().is_v4_mapped()) { tree.put("root.LocalIP", "127.0.0.1"); - } - else { + } else { tree.put("root.LocalIP", net::addr_to_normalized_string(local_endpoint.address())); } @@ -782,8 +765,7 @@ namespace nvhttp { response->close_connection_after_response = true; } - pt::ptree - get_all_clients() { + pt::ptree get_all_clients() { pt::ptree named_cert_nodes; client_t &client = client_root; for (auto &named_cert : client.named_devices) { @@ -796,8 +778,7 @@ namespace nvhttp { return named_cert_nodes; } - void - applist(resp_https_t response, req_https_t request) { + void applist(resp_https_t response, req_https_t request) { print_req(request); pt::ptree tree; @@ -825,12 +806,11 @@ namespace nvhttp { } } - void - launch(bool &host_audio, resp_https_t response, req_https_t request) { + void launch(bool &host_audio, resp_https_t response, req_https_t request) { print_req(request); pt::ptree tree; - bool revert_display_configuration { false }; + bool revert_display_configuration {false}; auto g = util::fail_guard([&]() { std::ostringstream data; @@ -848,7 +828,8 @@ namespace nvhttp { args.find("rikey"s) == std::end(args) || args.find("rikeyid"s) == std::end(args) || args.find("localAudioPlayMode"s) == std::end(args) || - args.find("appid"s) == std::end(args)) { + args.find("appid"s) == std::end(args) + ) { tree.put("root.resume", 0); tree.put("root..status_code", 400); tree.put("root..status_message", "Missing a required launch parameter"); @@ -915,9 +896,7 @@ namespace nvhttp { } tree.put("root..status_code", 200); - tree.put("root.sessionUrl0", launch_session->rtsp_url_scheme + - net::addr_to_url_escaped_string(request->local_endpoint().address()) + ':' + - std::to_string(net::map_port(rtsp_stream::RTSP_SETUP_PORT))); + tree.put("root.sessionUrl0", launch_session->rtsp_url_scheme + net::addr_to_url_escaped_string(request->local_endpoint().address()) + ':' + std::to_string(net::map_port(rtsp_stream::RTSP_SETUP_PORT))); tree.put("root.gamesession", 1); rtsp_stream::launch_session_raise(launch_session); @@ -926,8 +905,7 @@ namespace nvhttp { revert_display_configuration = false; } - void - resume(bool &host_audio, resp_https_t response, req_https_t request) { + void resume(bool &host_audio, resp_https_t response, req_https_t request) { print_req(request); pt::ptree tree; @@ -951,7 +929,8 @@ namespace nvhttp { auto args = request->parse_query_string(); if ( args.find("rikey"s) == std::end(args) || - args.find("rikeyid"s) == std::end(args)) { + args.find("rikeyid"s) == std::end(args) + ) { tree.put("root.resume", 0); tree.put("root..status_code", 400); tree.put("root..status_message", "Missing a required resume parameter"); @@ -962,7 +941,7 @@ namespace nvhttp { // Newer Moonlight clients send localAudioPlayMode on /resume too, // so we should use it if it's present in the args and there are // no active sessions we could be interfering with. - const bool no_active_sessions { rtsp_stream::session_count() == 0 }; + const bool no_active_sessions {rtsp_stream::session_count() == 0}; if (no_active_sessions && args.find("localAudioPlayMode"s) != std::end(args)) { host_audio = util::from_view(get_arg(args, "localAudioPlayMode")); } @@ -999,16 +978,13 @@ namespace nvhttp { } tree.put("root..status_code", 200); - tree.put("root.sessionUrl0", launch_session->rtsp_url_scheme + - net::addr_to_url_escaped_string(request->local_endpoint().address()) + ':' + - std::to_string(net::map_port(rtsp_stream::RTSP_SETUP_PORT))); + tree.put("root.sessionUrl0", launch_session->rtsp_url_scheme + net::addr_to_url_escaped_string(request->local_endpoint().address()) + ':' + std::to_string(net::map_port(rtsp_stream::RTSP_SETUP_PORT))); tree.put("root.resume", 1); rtsp_stream::launch_session_raise(launch_session); } - void - cancel(resp_https_t response, req_https_t request) { + void cancel(resp_https_t response, req_https_t request) { print_req(request); pt::ptree tree; @@ -1033,8 +1009,7 @@ namespace nvhttp { display_device::revert_configuration(); } - void - appasset(resp_https_t response, req_https_t request) { + void appasset(resp_https_t response, req_https_t request) { print_req(request); auto args = request->parse_query_string(); @@ -1047,14 +1022,12 @@ namespace nvhttp { response->close_connection_after_response = true; } - void - setup(const std::string &pkey, const std::string &cert) { + void setup(const std::string &pkey, const std::string &cert) { conf_intern.pkey = pkey; conf_intern.servercert = cert; } - void - start() { + void start() { auto shutdown_event = mail::man->event(mail::shutdown); auto port_http = net::map_port(PORT_HTTP); @@ -1077,7 +1050,7 @@ namespace nvhttp { // launch will store it in host_audio bool host_audio {}; - https_server_t https_server { config::nvhttp.cert, config::nvhttp.pkey }; + https_server_t https_server {config::nvhttp.cert, config::nvhttp.pkey}; http_server_t http_server; // Verify certificates after establishing connection @@ -1143,11 +1116,17 @@ namespace nvhttp { https_server.default_resource["GET"] = not_found; https_server.resource["^/serverinfo$"]["GET"] = serverinfo; - https_server.resource["^/pair$"]["GET"] = [&add_cert](auto resp, auto req) { pair(add_cert, resp, req); }; + https_server.resource["^/pair$"]["GET"] = [&add_cert](auto resp, auto req) { + pair(add_cert, resp, req); + }; https_server.resource["^/applist$"]["GET"] = applist; https_server.resource["^/appasset$"]["GET"] = appasset; - https_server.resource["^/launch$"]["GET"] = [&host_audio](auto resp, auto req) { launch(host_audio, resp, req); }; - https_server.resource["^/resume$"]["GET"] = [&host_audio](auto resp, auto req) { resume(host_audio, resp, req); }; + https_server.resource["^/launch$"]["GET"] = [&host_audio](auto resp, auto req) { + launch(host_audio, resp, req); + }; + https_server.resource["^/resume$"]["GET"] = [&host_audio](auto resp, auto req) { + resume(host_audio, resp, req); + }; https_server.resource["^/cancel$"]["GET"] = cancel; https_server.config.reuse_address = true; @@ -1156,7 +1135,9 @@ namespace nvhttp { http_server.default_resource["GET"] = not_found; http_server.resource["^/serverinfo$"]["GET"] = serverinfo; - http_server.resource["^/pair$"]["GET"] = [&add_cert](auto resp, auto req) { pair(add_cert, resp, req); }; + http_server.resource["^/pair$"]["GET"] = [&add_cert](auto resp, auto req) { + pair(add_cert, resp, req); + }; http_server.config.reuse_address = true; http_server.config.address = net::af_to_any_address_string(address_family); @@ -1165,8 +1146,7 @@ namespace nvhttp { auto accept_and_run = [&](auto *http_server) { try { http_server->start(); - } - 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; @@ -1177,8 +1157,8 @@ namespace nvhttp { return; } }; - std::thread ssl { accept_and_run, &https_server }; - std::thread tcp { accept_and_run, &http_server }; + std::thread ssl {accept_and_run, &https_server}; + std::thread tcp {accept_and_run, &http_server}; // Wait for any event shutdown_event->view(); @@ -1190,24 +1170,21 @@ namespace nvhttp { tcp.join(); } - void - erase_all_clients() { + void erase_all_clients() { client_t client; client_root = client; cert_chain.clear(); save_state(); } - int - unpair_client(std::string uuid) { + int unpair_client(std::string uuid) { int removed = 0; client_t &client = client_root; for (auto it = client.named_devices.begin(); it != client.named_devices.end();) { if ((*it).uuid == uuid) { it = client.named_devices.erase(it); removed++; - } - else { + } else { ++it; } } diff --git a/src/nvhttp.h b/src/nvhttp.h index e3af8a26..3ffbcafc 100644 --- a/src/nvhttp.h +++ b/src/nvhttp.h @@ -9,8 +9,8 @@ #include // lib includes -#include #include +#include // local includes #include "crypto.h" @@ -49,21 +49,20 @@ namespace nvhttp { * nvhttp::start(); * @examples_end */ - void - start(); + void start(); /** * @brief Setup the nvhttp server. * @param pkey * @param cert */ - void - setup(const std::string &pkey, const std::string &cert); + void setup(const std::string &pkey, const std::string &cert); class SunshineHTTPS: public SimpleWeb::HTTPS { public: SunshineHTTPS(boost::asio::io_context &io_context, boost::asio::ssl::context &ctx): - SimpleWeb::HTTPS(io_context, ctx) {} + SimpleWeb::HTTPS(io_context, ctx) { + } virtual ~SunshineHTTPS() { // Gracefully shutdown the TLS connection @@ -111,8 +110,7 @@ namespace nvhttp { * @brief removes the temporary pairing session * @param sess */ - void - remove_session(const pair_session_t &sess); + void remove_session(const pair_session_t &sess); /** * @brief Pair, phase 1 @@ -124,8 +122,7 @@ namespace nvhttp { * * At this stage we only have to send back our public certificate. */ - void - getservercert(pair_session_t &sess, boost::property_tree::ptree &tree, const std::string &pin); + void getservercert(pair_session_t &sess, boost::property_tree::ptree &tree, const std::string &pin); /** * @brief Pair, phase 2 @@ -139,8 +136,7 @@ namespace nvhttp { * * The hash + server_challenge will then be AES encrypted and sent as the `challengeresponse` in the returned XML */ - void - clientchallenge(pair_session_t &sess, boost::property_tree::ptree &tree, const std::string &challenge); + void clientchallenge(pair_session_t &sess, boost::property_tree::ptree &tree, const std::string &challenge); /** * @brief Pair, phase 3 @@ -149,8 +145,7 @@ namespace nvhttp { * we have to send back the `pairingsecret`: * using our private key we have to sign the certificate_signature + server_secret (generated in phase 2) */ - void - serverchallengeresp(pair_session_t &sess, boost::property_tree::ptree &tree, const std::string &encrypted_response); + void serverchallengeresp(pair_session_t &sess, boost::property_tree::ptree &tree, const std::string &encrypted_response); /** * @brief Pair, phase 4 (final) @@ -166,8 +161,7 @@ namespace nvhttp { * Then using the client certificate public key we should be able to verify that * the client secret has been signed by Moonlight */ - void - clientpairingsecret(pair_session_t &sess, std::shared_ptr> &add_cert, boost::property_tree::ptree &tree, const std::string &client_pairing_secret); + void clientpairingsecret(pair_session_t &sess, std::shared_ptr> &add_cert, boost::property_tree::ptree &tree, const std::string &client_pairing_secret); /** * @brief Compare the user supplied pin to the Moonlight pin. @@ -178,8 +172,7 @@ namespace nvhttp { * bool pin_status = nvhttp::pin("1234", "laptop"); * @examples_end */ - bool - pin(std::string pin, std::string name); + bool pin(std::string pin, std::string name); /** * @brief Remove single client. @@ -187,8 +180,7 @@ namespace nvhttp { * nvhttp::unpair_client("4D7BB2DD-5704-A405-B41C-891A022932E1"); * @examples_end */ - int - unpair_client(std::string uniqueid); + int unpair_client(std::string uniqueid); /** * @brief Get all paired clients. @@ -197,8 +189,7 @@ namespace nvhttp { * boost::property_tree::ptree clients = nvhttp::get_all_clients(); * @examples_end */ - boost::property_tree::ptree - get_all_clients(); + boost::property_tree::ptree get_all_clients(); /** * @brief Remove all paired clients. @@ -206,6 +197,5 @@ namespace nvhttp { * nvhttp::erase_all_clients(); * @examples_end */ - void - erase_all_clients(); + void erase_all_clients(); } // namespace nvhttp diff --git a/src/platform/common.h b/src/platform/common.h index abcbefc8..49d1fc7c 100644 --- a/src/platform/common.h +++ b/src/platform/common.h @@ -4,18 +4,21 @@ */ #pragma once +// standard includes #include #include #include #include #include +// lib includes #include #ifndef _WIN32 #include #include #endif +// local includes #include "src/config.h" #include "src/logging.h" #include "src/thread_safe.h" @@ -44,13 +47,15 @@ namespace boost { class address; } // namespace ip } // namespace asio + namespace filesystem { class path; } + namespace process::inline v1 { class child; class group; - template + template class basic_environment; typedef basic_environment environment; } // namespace process::inline v1 @@ -59,6 +64,7 @@ namespace boost { namespace video { struct config_t; } // namespace video + namespace nvenc { class nvenc_base; } @@ -103,26 +109,23 @@ namespace platf { }; struct gamepad_feedback_msg_t { - static gamepad_feedback_msg_t - make_rumble(std::uint16_t id, std::uint16_t lowfreq, std::uint16_t highfreq) { + static gamepad_feedback_msg_t make_rumble(std::uint16_t id, std::uint16_t lowfreq, std::uint16_t highfreq) { gamepad_feedback_msg_t msg; msg.type = gamepad_feedback_e::rumble; msg.id = id; - msg.data.rumble = { lowfreq, highfreq }; + msg.data.rumble = {lowfreq, highfreq}; return msg; } - static gamepad_feedback_msg_t - make_rumble_triggers(std::uint16_t id, std::uint16_t left, std::uint16_t right) { + static gamepad_feedback_msg_t make_rumble_triggers(std::uint16_t id, std::uint16_t left, std::uint16_t right) { gamepad_feedback_msg_t msg; msg.type = gamepad_feedback_e::rumble_triggers; msg.id = id; - msg.data.rumble_triggers = { left, right }; + msg.data.rumble_triggers = {left, right}; return msg; } - static gamepad_feedback_msg_t - make_motion_event_state(std::uint16_t id, std::uint8_t motion_type, std::uint16_t report_rate) { + static gamepad_feedback_msg_t make_motion_event_state(std::uint16_t id, std::uint8_t motion_type, std::uint16_t report_rate) { gamepad_feedback_msg_t msg; msg.type = gamepad_feedback_e::set_motion_event_state; msg.id = id; @@ -131,30 +134,33 @@ namespace platf { return msg; } - static gamepad_feedback_msg_t - make_rgb_led(std::uint16_t id, std::uint8_t r, std::uint8_t g, std::uint8_t b) { + static gamepad_feedback_msg_t make_rgb_led(std::uint16_t id, std::uint8_t r, std::uint8_t g, std::uint8_t b) { gamepad_feedback_msg_t msg; msg.type = gamepad_feedback_e::set_rgb_led; msg.id = id; - msg.data.rgb_led = { r, g, b }; + msg.data.rgb_led = {r, g, b}; return msg; } gamepad_feedback_e type; std::uint16_t id; + union { struct { std::uint16_t lowfreq; std::uint16_t highfreq; } rumble; + struct { std::uint16_t left_trigger; std::uint16_t right_trigger; } rumble_triggers; + struct { std::uint16_t report_rate; std::uint8_t motion_type; } motion_event_state; + struct { std::uint8_t r; std::uint8_t g; @@ -179,7 +185,8 @@ namespace platf { }; constexpr std::uint8_t map_stereo[] { - FRONT_LEFT, FRONT_RIGHT + FRONT_LEFT, + FRONT_RIGHT }; constexpr std::uint8_t map_surround51[] { FRONT_LEFT, @@ -221,10 +228,9 @@ namespace platf { unknown ///< Unknown }; - inline std::string_view - from_pix_fmt(pix_fmt_e pix_fmt) { + inline std::string_view from_pix_fmt(pix_fmt_e pix_fmt) { using namespace std::literals; -#define _CONVERT(x) \ +#define _CONVERT(x) \ case pix_fmt_e::x: \ return #x##sv switch (pix_fmt) { @@ -344,10 +350,8 @@ namespace platf { img_t(img_t &&) = delete; img_t(const img_t &) = delete; - img_t & - operator=(img_t &&) = delete; - img_t & - operator=(const img_t &) = delete; + img_t &operator=(img_t &&) = delete; + img_t &operator=(const img_t &) = delete; std::uint8_t *data {}; std::int32_t width {}; @@ -371,14 +375,14 @@ namespace platf { std::string surround51; std::string surround71; }; + std::optional null; }; struct encode_device_t { virtual ~encode_device_t() = default; - virtual int - convert(platf::img_t &img) = 0; + virtual int convert(platf::img_t &img) = 0; video::sunshine_colorspace_t colorspace; }; @@ -387,21 +391,18 @@ namespace platf { void *data {}; AVFrame *frame {}; - int - convert(platf::img_t &img) override { + int convert(platf::img_t &img) override { return -1; } - virtual void - apply_colorspace() { + virtual void apply_colorspace() { } /** * @brief Set the frame to be encoded. * @note Implementations must take ownership of 'frame'. */ - virtual int - set_frame(AVFrame *frame, AVBufferRef *hw_frames_ctx) { + virtual int set_frame(AVFrame *frame, AVBufferRef *hw_frames_ctx) { BOOST_LOG(error) << "Illegal call to hwdevice_t::set_frame(). Did you forget to override it?"; return -1; }; @@ -410,29 +411,25 @@ namespace platf { * @brief Initialize the hwframes context. * @note Implementations may set parameters during initialization of the hwframes context. */ - virtual void - init_hwframes(AVHWFramesContext *frames) {}; + virtual void init_hwframes(AVHWFramesContext *frames) {}; /** * @brief Provides a hook for allow platform-specific code to adjust codec options. * @note Implementations may set or modify codec options prior to codec initialization. */ - virtual void - init_codec_options(AVCodecContext *ctx, AVDictionary **options) {}; + virtual void init_codec_options(AVCodecContext *ctx, AVDictionary **options) {}; /** * @brief Prepare to derive a context. * @note Implementations may make modifications required before context derivation */ - virtual int - prepare_to_derive_context(int hw_device_type) { + virtual int prepare_to_derive_context(int hw_device_type) { return 0; }; }; struct nvenc_encode_device_t: encode_device_t { - virtual bool - init_encoder(const video::config_t &client_config, const video::sunshine_colorspace_t &colorspace) = 0; + virtual bool init_encoder(const video::config_t &client_config, const video::sunshine_colorspace_t &colorspace) = 0; nvenc::nvenc_base *nvenc = nullptr; }; @@ -466,7 +463,9 @@ namespace platf { using pull_free_image_cb_t = std::function &img_out)>; display_t() noexcept: - offset_x { 0 }, offset_y { 0 } {} + offset_x {0}, + offset_y {0} { + } /** * @brief Capture a frame. @@ -480,32 +479,25 @@ namespace platf { * @retval capture_e::error On error * @retval capture_e::reinit When need of reinitialization */ - virtual capture_e - capture(const push_captured_image_cb_t &push_captured_image_cb, const pull_free_image_cb_t &pull_free_image_cb, bool *cursor) = 0; + virtual capture_e capture(const push_captured_image_cb_t &push_captured_image_cb, const pull_free_image_cb_t &pull_free_image_cb, bool *cursor) = 0; - virtual std::shared_ptr - alloc_img() = 0; + virtual std::shared_ptr alloc_img() = 0; - virtual int - dummy_img(img_t *img) = 0; + virtual int dummy_img(img_t *img) = 0; - virtual std::unique_ptr - make_avcodec_encode_device(pix_fmt_e pix_fmt) { + virtual std::unique_ptr make_avcodec_encode_device(pix_fmt_e pix_fmt) { return nullptr; } - virtual std::unique_ptr - make_nvenc_encode_device(pix_fmt_e pix_fmt) { + virtual std::unique_ptr make_nvenc_encode_device(pix_fmt_e pix_fmt) { return nullptr; } - virtual bool - is_hdr() { + virtual bool is_hdr() { return false; } - virtual bool - get_hdr_metadata(SS_HDR_METADATA &metadata) { + virtual bool get_hdr_metadata(SS_HDR_METADATA &metadata) { std::memset(&metadata, 0, sizeof(metadata)); return false; } @@ -516,8 +508,7 @@ namespace platf { * @param config The codec configuration. * @return `true` if supported, `false` otherwise. */ - virtual bool - is_codec_supported(std::string_view name, const ::video::config_t &config) { + virtual bool is_codec_supported(std::string_view name, const ::video::config_t &config) { return true; } @@ -531,57 +522,46 @@ namespace platf { protected: // collect capture timing data (at loglevel debug) - logging::time_delta_periodic_logger sleep_overshoot_logger = { debug, "Frame capture sleep overshoot" }; + logging::time_delta_periodic_logger sleep_overshoot_logger = {debug, "Frame capture sleep overshoot"}; }; class mic_t { public: - virtual capture_e - sample(std::vector &frame_buffer) = 0; + virtual capture_e sample(std::vector &frame_buffer) = 0; virtual ~mic_t() = default; }; class audio_control_t { public: - virtual int - set_sink(const std::string &sink) = 0; + virtual int set_sink(const std::string &sink) = 0; - virtual std::unique_ptr - microphone(const std::uint8_t *mapping, int channels, std::uint32_t sample_rate, std::uint32_t frame_size) = 0; + virtual std::unique_ptr microphone(const std::uint8_t *mapping, int channels, std::uint32_t sample_rate, std::uint32_t frame_size) = 0; /** * @brief Check if the audio sink is available in the system. * @param sink Sink to be checked. * @returns True if available, false otherwise. */ - virtual bool - is_sink_available(const std::string &sink) = 0; + virtual bool is_sink_available(const std::string &sink) = 0; - virtual std::optional - sink_info() = 0; + virtual std::optional sink_info() = 0; virtual ~audio_control_t() = default; }; - void - freeInput(void *); + void freeInput(void *); using input_t = util::safe_ptr; - std::filesystem::path - appdata(); + std::filesystem::path appdata(); - std::string - get_mac_address(const std::string_view &address); + std::string get_mac_address(const std::string_view &address); - std::string - from_sockaddr(const sockaddr *const); - std::pair - from_sockaddr_ex(const sockaddr *const); + std::string from_sockaddr(const sockaddr *const); + std::pair from_sockaddr_ex(const sockaddr *const); - std::unique_ptr - audio_control(); + std::unique_ptr audio_control(); /** * @brief Get the display_t instance for the given hwdevice_type. @@ -591,22 +571,18 @@ namespace platf { * @param config Stream configuration * @return The display_t instance based on hwdevice_type. */ - std::shared_ptr - display(mem_type_e hwdevice_type, const std::string &display_name, const video::config_t &config); + std::shared_ptr display(mem_type_e hwdevice_type, const std::string &display_name, const video::config_t &config); // A list of names of displays accepted as display_name with the mem_type_e - std::vector - display_names(mem_type_e hwdevice_type); + std::vector display_names(mem_type_e hwdevice_type); /** * @brief Check if GPUs/drivers have changed since the last call to this function. * @return `true` if a change has occurred or if it is unknown whether a change occurred. */ - bool - needs_encoder_reenumeration(); + bool needs_encoder_reenumeration(); - boost::process::v1::child - run_command(bool elevated, bool interactive, const std::string &cmd, boost::filesystem::path &working_dir, const boost::process::v1::environment &env, FILE *file, std::error_code &ec, boost::process::v1::group *group); + boost::process::v1::child run_command(bool elevated, bool interactive, const std::string &cmd, boost::filesystem::path &working_dir, const boost::process::v1::environment &env, FILE *file, std::error_code &ec, boost::process::v1::group *group); enum class thread_priority_e : int { low, ///< Low priority @@ -614,17 +590,13 @@ namespace platf { high, ///< High priority critical ///< Critical priority }; - void - adjust_thread_priority(thread_priority_e priority); + void adjust_thread_priority(thread_priority_e priority); // Allow OS-specific actions to be taken to prepare for streaming - void - streaming_will_start(); - void - streaming_will_stop(); + void streaming_will_start(); + void streaming_will_stop(); - void - restart(); + void restart(); /** * @brief Set an environment variable. @@ -632,16 +604,14 @@ namespace platf { * @param value The value to set the environment variable to. * @return 0 on success, non-zero on failure. */ - int - set_env(const std::string &name, const std::string &value); + int set_env(const std::string &name, const std::string &value); /** * @brief Unset an environment variable. * @param name The name of the environment variable. * @return 0 on success, non-zero on failure. */ - int - unset_env(const std::string &name); + int unset_env(const std::string &name); struct buffer_descriptor_t { const char *buffer; @@ -649,9 +619,14 @@ namespace platf { // Constructors required for emplace_back() prior to C++20 buffer_descriptor_t(const char *buffer, size_t size): - buffer(buffer), size(size) {} + buffer(buffer), + size(size) { + } + buffer_descriptor_t(): - buffer(nullptr), size(0) {} + buffer(nullptr), + size(0) { + } }; struct batched_send_info_t { @@ -682,24 +657,22 @@ namespace platf { * @param offset The offset in the total payload data (bytes). * @return Buffer descriptor describing the region at the given offset. */ - buffer_descriptor_t - buffer_for_payload_offset(ptrdiff_t offset) { + buffer_descriptor_t buffer_for_payload_offset(ptrdiff_t offset) { for (const auto &desc : payload_buffers) { if (offset < desc.size) { return { desc.buffer + offset, desc.size - offset, }; - } - else { + } else { offset -= desc.size; } } return {}; } }; - bool - send_batch(batched_send_info_t &send_info); + + bool send_batch(batched_send_info_t &send_info); struct send_info_t { const char *header; @@ -712,8 +685,8 @@ namespace platf { uint16_t target_port; boost::asio::ip::address &source_address; }; - bool - send(send_info_t &send_info); + + bool send(send_info_t &send_info); enum class qos_data_type_e : int { audio, ///< Audio @@ -728,34 +701,29 @@ namespace platf { * @param data_type The type of traffic sent on this socket. * @param dscp_tagging Specifies whether to enable DSCP tagging on outgoing traffic. */ - std::unique_ptr - enable_socket_qos(uintptr_t native_socket, boost::asio::ip::address &address, uint16_t port, qos_data_type_e data_type, bool dscp_tagging); + std::unique_ptr enable_socket_qos(uintptr_t native_socket, boost::asio::ip::address &address, uint16_t port, qos_data_type_e data_type, bool dscp_tagging); /** * @brief Open a url in the default web browser. * @param url The url to open. */ - void - open_url(const std::string &url); + void open_url(const std::string &url); /** * @brief Attempt to gracefully terminate a process group. * @param native_handle The native handle of the process group. * @return `true` if termination was successfully requested. */ - bool - request_process_group_exit(std::uintptr_t native_handle); + bool request_process_group_exit(std::uintptr_t native_handle); /** * @brief Check if a process group still has running children. * @param native_handle The native handle of the process group. * @return `true` if processes are still running. */ - bool - process_group_running(std::uintptr_t native_handle); + bool process_group_running(std::uintptr_t native_handle); - input_t - input(); + input_t input(); /** * @brief Get the current mouse position on screen * @param input The input_t instance to use. @@ -764,24 +732,15 @@ namespace platf { * auto [x, y] = get_mouse_loc(input); * @examples_end */ - util::point_t - get_mouse_loc(input_t &input); - void - move_mouse(input_t &input, int deltaX, int deltaY); - void - abs_mouse(input_t &input, const touch_port_t &touch_port, float x, float y); - void - button_mouse(input_t &input, int button, bool release); - void - scroll(input_t &input, int distance); - void - hscroll(input_t &input, int distance); - void - keyboard_update(input_t &input, uint16_t modcode, bool release, uint8_t flags); - void - gamepad_update(input_t &input, int nr, const gamepad_state_t &gamepad_state); - void - unicode(input_t &input, char *utf8, int size); + util::point_t get_mouse_loc(input_t &input); + void move_mouse(input_t &input, int deltaX, int deltaY); + void abs_mouse(input_t &input, const touch_port_t &touch_port, float x, float y); + void button_mouse(input_t &input, int button, bool release); + void scroll(input_t &input, int distance); + void hscroll(input_t &input, int distance); + void keyboard_update(input_t &input, uint16_t modcode, bool release, uint8_t flags); + void gamepad_update(input_t &input, int nr, const gamepad_state_t &gamepad_state); + void unicode(input_t &input, char *utf8, int size); typedef deinit_t client_input_t; @@ -790,8 +749,7 @@ namespace platf { * @param input The global input context. * @return A unique pointer to a per-client input data context. */ - std::unique_ptr - allocate_client_input_context(input_t &input); + std::unique_ptr allocate_client_input_context(input_t &input); /** * @brief Send a touch event to the OS. @@ -799,8 +757,7 @@ namespace platf { * @param touch_port The current viewport for translating to screen coordinates. * @param touch The touch event. */ - void - touch_update(client_input_t *input, const touch_port_t &touch_port, const touch_input_t &touch); + void touch_update(client_input_t *input, const touch_port_t &touch_port, const touch_input_t &touch); /** * @brief Send a pen event to the OS. @@ -808,32 +765,28 @@ namespace platf { * @param touch_port The current viewport for translating to screen coordinates. * @param pen The pen event. */ - void - pen_update(client_input_t *input, const touch_port_t &touch_port, const pen_input_t &pen); + void pen_update(client_input_t *input, const touch_port_t &touch_port, const pen_input_t &pen); /** * @brief Send a gamepad touch event to the OS. * @param input The global input context. * @param touch The touch event. */ - void - gamepad_touch(input_t &input, const gamepad_touch_t &touch); + void gamepad_touch(input_t &input, const gamepad_touch_t &touch); /** * @brief Send a gamepad motion event to the OS. * @param input The global input context. * @param motion The motion event. */ - void - gamepad_motion(input_t &input, const gamepad_motion_t &motion); + void gamepad_motion(input_t &input, const gamepad_motion_t &motion); /** * @brief Send a gamepad battery event to the OS. * @param input The global input context. * @param battery The battery event. */ - void - gamepad_battery(input_t &input, const gamepad_battery_t &battery); + void gamepad_battery(input_t &input, const gamepad_battery_t &battery); /** * @brief Create a new virtual gamepad. @@ -843,35 +796,29 @@ namespace platf { * @param feedback_queue The queue for posting messages back to the client. * @return 0 on success. */ - int - alloc_gamepad(input_t &input, const gamepad_id_t &id, const gamepad_arrival_t &metadata, feedback_queue_t feedback_queue); - void - free_gamepad(input_t &input, int nr); + int alloc_gamepad(input_t &input, const gamepad_id_t &id, const gamepad_arrival_t &metadata, feedback_queue_t feedback_queue); + void free_gamepad(input_t &input, int nr); /** * @brief Get the supported platform capabilities to advertise to the client. * @return Capability flags. */ - platform_caps::caps_t - get_capabilities(); + platform_caps::caps_t get_capabilities(); #define SERVICE_NAME "Sunshine" #define SERVICE_TYPE "_nvstream._tcp" namespace publish { - [[nodiscard]] std::unique_ptr - start(); + [[nodiscard]] std::unique_ptr start(); } - [[nodiscard]] std::unique_ptr - init(); + [[nodiscard]] std::unique_ptr init(); /** * @brief Returns the current computer name in UTF-8. * @return Computer name or a placeholder upon failure. */ - std::string - get_host_name(); + std::string get_host_name(); /** * @brief Gets the supported gamepads for this platform backend. @@ -879,8 +826,7 @@ namespace platf { * @param input Pointer to the platform's `input_t` or `nullptr`. * @return Vector of gamepad options and status. */ - std::vector & - supported_gamepads(input_t *input); + std::vector &supported_gamepads(input_t *input); struct high_precision_timer: private boost::noncopyable { virtual ~high_precision_timer() = default; @@ -889,22 +835,19 @@ namespace platf { * @brief Sleep for the duration * @param duration Sleep duration */ - virtual void - sleep_for(const std::chrono::nanoseconds &duration) = 0; + virtual void sleep_for(const std::chrono::nanoseconds &duration) = 0; /** * @brief Check if platform-specific timer backend has been initialized successfully * @return `true` on success, `false` on error */ - virtual - operator bool() = 0; + virtual operator bool() = 0; }; /** * @brief Create platform-specific timer capable of high-precision sleep * @return A unique pointer to timer */ - std::unique_ptr - create_high_precision_timer(); + std::unique_ptr create_high_precision_timer(); } // namespace platf diff --git a/src/platform/linux/audio.cpp b/src/platform/linux/audio.cpp index a48ee2f0..0e53e939 100644 --- a/src/platform/linux/audio.cpp +++ b/src/platform/linux/audio.cpp @@ -2,20 +2,21 @@ * @file src/platform/linux/audio.cpp * @brief Definitions for audio control on Linux. */ +// standard includes #include #include #include +// lib includes #include - #include #include #include -#include "src/platform/common.h" - +// local includes #include "src/config.h" #include "src/logging.h" +#include "src/platform/common.h" #include "src/thread_safe.h" namespace platf { @@ -32,8 +33,7 @@ namespace platf { PA_CHANNEL_POSITION_SIDE_RIGHT, }; - std::string - to_string(const char *name, const std::uint8_t *mapping, int channels) { + std::string to_string(const char *name, const std::uint8_t *mapping, int channels) { std::stringstream ss; ss << "rate=48000 sink_name="sv << name << " format=float channels="sv << channels << " channel_map="sv; @@ -53,8 +53,7 @@ namespace platf { struct mic_attr_t: public mic_t { util::safe_ptr mic; - capture_e - sample(std::vector &sample_buf) override { + capture_e sample(std::vector &sample_buf) override { auto sample_size = sample_buf.size(); auto buf = sample_buf.data(); @@ -69,11 +68,10 @@ namespace platf { } }; - std::unique_ptr - microphone(const std::uint8_t *mapping, int channels, std::uint32_t sample_rate, std::uint32_t frame_size, std::string source_name) { + std::unique_ptr microphone(const std::uint8_t *mapping, int channels, std::uint32_t sample_rate, std::uint32_t frame_size, std::string source_name) { auto mic = std::make_unique(); - pa_sample_spec ss { PA_SAMPLE_FLOAT32, sample_rate, (std::uint8_t) channels }; + pa_sample_spec ss {PA_SAMPLE_FLOAT32, sample_rate, (std::uint8_t) channels}; pa_channel_map pa_map; pa_map.channels = channels; @@ -92,9 +90,8 @@ namespace platf { int status; mic->mic.reset( - pa_simple_new(nullptr, "sunshine", - pa_stream_direction_t::PA_STREAM_RECORD, source_name.c_str(), - "sunshine-record", &ss, &pa_map, &pa_attr, &status)); + pa_simple_new(nullptr, "sunshine", pa_stream_direction_t::PA_STREAM_RECORD, source_name.c_str(), "sunshine-record", &ss, &pa_map, &pa_attr, &status) + ); if (!mic->mic) { auto err_str = pa_strerror(status); @@ -106,38 +103,37 @@ namespace platf { } namespace pa { - template + template struct add_const_helper; - template + template struct add_const_helper { using type = const std::remove_pointer_t *; }; - template + template struct add_const_helper { using type = const T *; }; - template + template using add_const_t = typename add_const_helper, T>::type; - template - void - pa_free(T *p) { + template + void pa_free(T *p) { pa_xfree(p); } + using ctx_t = util::safe_ptr; using loop_t = util::safe_ptr; using op_t = util::safe_ptr; using string_t = util::safe_ptr>; - template + template using cb_simple_t = std::function i)>; - template - void - cb(ctx_t::pointer ctx, add_const_t i, void *userdata) { + template + void cb(ctx_t::pointer ctx, add_const_t i, void *userdata) { auto &f = *(cb_simple_t *) userdata; // Cannot similarly filter on eol here. Unless reported otherwise assume @@ -145,12 +141,11 @@ namespace platf { f(ctx, i); } - template + template using cb_t = std::function i, int eol)>; - template - void - cb(ctx_t::pointer ctx, add_const_t i, int eol, void *userdata) { + template + void cb(ctx_t::pointer ctx, add_const_t i, int eol, void *userdata) { auto &f = *(cb_t *) userdata; // For some reason, pulseaudio calls this callback after disconnecting @@ -161,22 +156,19 @@ namespace platf { f(ctx, i, eol); } - void - cb_i(ctx_t::pointer ctx, std::uint32_t i, void *userdata) { + void cb_i(ctx_t::pointer ctx, std::uint32_t i, void *userdata) { auto alarm = (safe::alarm_raw_t *) userdata; alarm->ring(i); } - void - ctx_state_cb(ctx_t::pointer ctx, void *userdata) { + void ctx_state_cb(ctx_t::pointer ctx, void *userdata) { auto &f = *(std::function *) userdata; f(ctx); } - void - success_cb(ctx_t::pointer ctx, int status, void *userdata) { + void success_cb(ctx_t::pointer ctx, int status, void *userdata) { assert(userdata != nullptr); auto alarm = (safe::alarm_raw_t *) userdata; @@ -205,8 +197,8 @@ namespace platf { std::unique_ptr> events_cb; std::thread worker; - int - init() { + + int init() { events = std::make_unique>(); loop.reset(pa_mainloop_new()); ctx.reset(pa_context_new(pa_mainloop_get_api(loop.get()), "sunshine")); @@ -262,8 +254,7 @@ namespace platf { return 0; } - int - load_null(const char *name, const std::uint8_t *channel_mapping, int channels) { + int load_null(const char *name, const std::uint8_t *channel_mapping, int channels) { auto alarm = safe::make_alarm(); op_t op { @@ -272,15 +263,15 @@ namespace platf { "module-null-sink", to_string(name, channel_mapping, channels).c_str(), cb_i, - alarm.get()), + alarm.get() + ), }; alarm->wait(); return *alarm->status(); } - int - unload_null(std::uint32_t i) { + int unload_null(std::uint32_t i) { if (i == PA_INVALID_INDEX) { return 0; } @@ -301,8 +292,7 @@ namespace platf { return 0; } - std::optional - sink_info() override { + std::optional sink_info() override { constexpr auto stereo = "sink-sunshine-stereo"; constexpr auto surround51 = "sink-sunshine-surround51"; constexpr auto surround71 = "sink-sunshine-surround71"; @@ -331,20 +321,18 @@ namespace platf { index.stereo = sink_info->owner_module; ++nullcount; - } - else if (!std::strcmp(sink_info->name, surround51)) { + } else if (!std::strcmp(sink_info->name, surround51)) { index.surround51 = sink_info->owner_module; ++nullcount; - } - else if (!std::strcmp(sink_info->name, surround71)) { + } else if (!std::strcmp(sink_info->name, surround71)) { index.surround71 = sink_info->owner_module; ++nullcount; } }; - op_t op { pa_context_get_sink_info_list(ctx.get(), cb, &f) }; + op_t op {pa_context_get_sink_info_list(ctx.get(), cb, &f)}; if (!op) { BOOST_LOG(error) << "Couldn't create card info operation: "sv << pa_strerror(pa_context_errno(ctx.get())); @@ -365,8 +353,7 @@ namespace platf { index.stereo = load_null(stereo, speaker::map_stereo, sizeof(speaker::map_stereo)); if (index.stereo == PA_INVALID_INDEX) { BOOST_LOG(warning) << "Couldn't create virtual sink for stereo: "sv << pa_strerror(pa_context_errno(ctx.get())); - } - else { + } else { ++nullcount; } } @@ -375,8 +362,7 @@ namespace platf { index.surround51 = load_null(surround51, speaker::map_surround51, sizeof(speaker::map_surround51)); if (index.surround51 == PA_INVALID_INDEX) { BOOST_LOG(warning) << "Couldn't create virtual sink for surround-51: "sv << pa_strerror(pa_context_errno(ctx.get())); - } - else { + } else { ++nullcount; } } @@ -385,8 +371,7 @@ namespace platf { index.surround71 = load_null(surround71, speaker::map_surround71, sizeof(speaker::map_surround71)); if (index.surround71 == PA_INVALID_INDEX) { BOOST_LOG(warning) << "Couldn't create virtual sink for surround-71: "sv << pa_strerror(pa_context_errno(ctx.get())); - } - else { + } else { ++nullcount; } } @@ -396,14 +381,13 @@ namespace platf { } if (nullcount == 3) { - sink.null = std::make_optional(sink_t::null_t { stereo, surround51, surround71 }); + sink.null = std::make_optional(sink_t::null_t {stereo, surround51, surround71}); } return std::make_optional(std::move(sink)); } - std::string - get_default_sink_name() { + std::string get_default_sink_name() { std::string sink_name; auto alarm = safe::make_alarm(); @@ -419,14 +403,13 @@ namespace platf { alarm->ring(0); }; - op_t server_op { pa_context_get_server_info(ctx.get(), cb, &server_f) }; + op_t server_op {pa_context_get_server_info(ctx.get(), cb, &server_f)}; alarm->wait(); // No need to check status. If it failed just return default name. return sink_name; } - std::string - get_monitor_name(const std::string &sink_name) { + std::string get_monitor_name(const std::string &sink_name) { std::string monitor_name; auto alarm = safe::make_alarm(); @@ -449,7 +432,7 @@ namespace platf { monitor_name = sink_info->monitor_source_name; }; - op_t sink_op { pa_context_get_sink_info_by_name(ctx.get(), sink_name.c_str(), cb, &sink_f) }; + op_t sink_op {pa_context_get_sink_info_by_name(ctx.get(), sink_name.c_str(), cb, &sink_f)}; alarm->wait(); // No need to check status. If it failed just return default name. @@ -457,8 +440,7 @@ namespace platf { return monitor_name; } - std::unique_ptr - microphone(const std::uint8_t *mapping, int channels, std::uint32_t sample_rate, std::uint32_t frame_size) override { + std::unique_ptr microphone(const std::uint8_t *mapping, int channels, std::uint32_t sample_rate, std::uint32_t frame_size) override { // Sink choice priority: // 1. Config sink // 2. Last sink swapped to (Usually virtual in this case) @@ -467,26 +449,32 @@ namespace platf { // but this happens right after the swap so the default returned by PA was not // the new one just set! auto sink_name = config::audio.sink; - if (sink_name.empty()) sink_name = requested_sink; - if (sink_name.empty()) sink_name = get_default_sink_name(); + if (sink_name.empty()) { + sink_name = requested_sink; + } + if (sink_name.empty()) { + sink_name = get_default_sink_name(); + } return ::platf::microphone(mapping, channels, sample_rate, frame_size, get_monitor_name(sink_name)); } - bool - is_sink_available(const std::string &sink) override { + bool is_sink_available(const std::string &sink) override { BOOST_LOG(warning) << "audio_control_t::is_sink_available() unimplemented: "sv << sink; return true; } - int - set_sink(const std::string &sink) override { + int set_sink(const std::string &sink) override { auto alarm = safe::make_alarm(); BOOST_LOG(info) << "Setting default sink to: ["sv << sink << "]"sv; op_t op { pa_context_set_default_sink( - ctx.get(), sink.c_str(), success_cb, alarm.get()), + ctx.get(), + sink.c_str(), + success_cb, + alarm.get() + ), }; if (!op) { @@ -525,8 +513,7 @@ namespace platf { }; } // namespace pa - std::unique_ptr - audio_control() { + std::unique_ptr audio_control() { auto audio = std::make_unique(); if (audio->init()) { @@ -535,4 +522,4 @@ namespace platf { return audio; } -} // namespace platf \ No newline at end of file +} // namespace platf diff --git a/src/platform/linux/cuda.cpp b/src/platform/linux/cuda.cpp index 5498d9a8..3a17b9cf 100644 --- a/src/platform/linux/cuda.cpp +++ b/src/platform/linux/cuda.cpp @@ -2,13 +2,15 @@ * @file src/platform/linux/cuda.cpp * @brief Definitions for CUDA encoding. */ +// standard includes #include #include #include #include -#include +// lib includes #include +#include extern "C" { #include @@ -16,6 +18,7 @@ extern "C" { #include } +// local includes #include "cuda.h" #include "graphics.h" #include "src/logging.h" @@ -27,7 +30,8 @@ extern "C" { #define SUNSHINE_STRINGVIEW(x) SUNSHINE_STRINGVIEW_HELPER(x) #define CU_CHECK(x, y) \ - if (check((x), SUNSHINE_STRINGVIEW(y ": "))) return -1 + if (check((x), SUNSHINE_STRINGVIEW(y ": "))) \ + return -1 #define CU_CHECK_IGNORE(x, y) \ check((x), SUNSHINE_STRINGVIEW(y ": ")) @@ -35,17 +39,16 @@ extern "C" { namespace fs = std::filesystem; using namespace std::literals; + namespace cuda { constexpr auto cudaDevAttrMaxThreadsPerBlock = (CUdevice_attribute) 1; constexpr auto cudaDevAttrMaxThreadsPerMultiProcessor = (CUdevice_attribute) 39; - void - pass_error(const std::string_view &sv, const char *name, const char *description) { + void pass_error(const std::string_view &sv, const char *name, const char *description) { BOOST_LOG(error) << sv << name << ':' << description; } - void - cff(CudaFunctions *cf) { + void cff(CudaFunctions *cf) { cuda_free_functions(&cf); } @@ -53,8 +56,7 @@ namespace cuda { static cdf_t cdf; - inline static int - check(CUresult result, const std::string_view &sv) { + inline static int check(CUresult result, const std::string_view &sv) { if (result != CUDA_SUCCESS) { const char *name; const char *description; @@ -69,13 +71,11 @@ namespace cuda { return 0; } - void - freeStream(CUstream stream) { + void freeStream(CUstream stream) { CU_CHECK_IGNORE(cdf->cuStreamDestroy(stream), "Couldn't destroy cuda stream"); } - void - unregisterResource(CUgraphicsResource resource) { + void unregisterResource(CUgraphicsResource resource) { CU_CHECK_IGNORE(cdf->cuGraphicsUnregisterResource(resource), "Couldn't unregister resource"); } @@ -86,8 +86,7 @@ namespace cuda { tex_t tex; }; - int - init() { + int init() { auto status = cuda_load_functions(&cdf, nullptr); if (status) { BOOST_LOG(error) << "Couldn't load cuda: "sv << status; @@ -102,8 +101,7 @@ namespace cuda { class cuda_t: public platf::avcodec_encode_device_t { public: - int - init(int in_width, int in_height) { + int init(int in_width, int in_height) { if (!cdf) { BOOST_LOG(warning) << "cuda not initialized"sv; return -1; @@ -117,8 +115,7 @@ namespace cuda { return 0; } - int - set_frame(AVFrame *frame, AVBufferRef *hw_frames_ctx) override { + int set_frame(AVFrame *frame, AVBufferRef *hw_frames_ctx) override { this->hwframe.reset(frame); this->frame = frame; @@ -156,8 +153,7 @@ namespace cuda { return 0; } - void - apply_colorspace() override { + void apply_colorspace() override { sws.apply_colorspace(colorspace); auto tex = tex_t::make(height, width * 4); @@ -182,11 +178,10 @@ namespace cuda { return; } - sws.convert(frame->data[0], frame->data[1], frame->linesize[0], frame->linesize[1], tex->texture.linear, stream.get(), { frame->width, frame->height, 0, 0 }); + sws.convert(frame->data[0], frame->data[1], frame->linesize[0], frame->linesize[1], tex->texture.linear, stream.get(), {frame->width, frame->height, 0, 0}); } - cudaTextureObject_t - tex_obj(const tex_t &tex) const { + cudaTextureObject_t tex_obj(const tex_t &tex) const { return linear_interpolation ? tex.texture.linear : tex.texture.point; } @@ -203,13 +198,11 @@ namespace cuda { class cuda_ram_t: public cuda_t { public: - int - convert(platf::img_t &img) override { + int convert(platf::img_t &img) override { return sws.load_ram(img, tex.array) || sws.convert(frame->data[0], frame->data[1], frame->linesize[0], frame->linesize[1], tex_obj(tex), stream.get()); } - int - set_frame(AVFrame *frame, AVBufferRef *hw_frames_ctx) { + int set_frame(AVFrame *frame, AVBufferRef *hw_frames_ctx) { if (cuda_t::set_frame(frame, hw_frames_ctx)) { return -1; } @@ -229,8 +222,7 @@ namespace cuda { class cuda_vram_t: public cuda_t { public: - int - convert(platf::img_t &img) override { + int convert(platf::img_t &img) override { return sws.convert(frame->data[0], frame->data[1], frame->linesize[0], frame->linesize[1], tex_obj(((img_t *) &img)->tex), stream.get()); } }; @@ -240,8 +232,7 @@ namespace cuda { * @param index CUDA device index to open. * @return File descriptor or -1 on failure. */ - file_t - open_drm_fd_for_cuda_device(int index) { + file_t open_drm_fd_for_cuda_device(int index) { CUdevice device; CU_CHECK(cdf->cuDeviceGet(&device, index), "Couldn't get CUDA device"); @@ -252,29 +243,29 @@ namespace cuda { BOOST_LOG(debug) << "Found CUDA device with PCI bus ID: "sv << pci_bus_id.data(); // Linux uses lowercase hexadecimal while CUDA uses uppercase - std::transform(pci_bus_id.begin(), pci_bus_id.end(), pci_bus_id.begin(), - [](char c) { return std::tolower(c); }); + std::transform(pci_bus_id.begin(), pci_bus_id.end(), pci_bus_id.begin(), [](char c) { + return std::tolower(c); + }); // Look for the name of the primary node in sysfs try { char sysfs_path[PATH_MAX]; std::snprintf(sysfs_path, sizeof(sysfs_path), "/sys/bus/pci/devices/%s/drm", pci_bus_id.data()); - fs::path sysfs_dir { sysfs_path }; - for (auto &entry : fs::directory_iterator { sysfs_dir }) { + fs::path sysfs_dir {sysfs_path}; + for (auto &entry : fs::directory_iterator {sysfs_dir}) { auto file = entry.path().filename(); auto filestring = file.generic_string(); - if (std::string_view { filestring }.substr(0, 4) != "card"sv) { + if (std::string_view {filestring}.substr(0, 4) != "card"sv) { continue; } BOOST_LOG(debug) << "Found DRM primary node: "sv << filestring; - fs::path dri_path { "/dev/dri"sv }; + fs::path dri_path {"/dev/dri"sv}; auto device_path = dri_path / file; return open(device_path.c_str(), O_RDWR); } - } - catch (const std::filesystem::filesystem_error &err) { + } catch (const std::filesystem::filesystem_error &err) { BOOST_LOG(error) << "Failed to read sysfs: "sv << err.what(); } @@ -292,8 +283,7 @@ namespace cuda { * @param offset_y Offset of content in captured frame. * @return 0 on success or -1 on failure. */ - int - init(int in_width, int in_height, int offset_x, int offset_y) { + int init(int in_width, int in_height, int offset_x, int offset_y) { // This must be non-zero to tell the video core that it's a hardware encoding device. data = (void *) 0x1; @@ -340,8 +330,7 @@ namespace cuda { * @param hw_frames_ctx_buf FFmpeg hardware frame context. * @return 0 on success or -1 on failure. */ - int - set_frame(AVFrame *frame, AVBufferRef *hw_frames_ctx_buf) override { + int set_frame(AVFrame *frame, AVBufferRef *hw_frames_ctx_buf) override { this->hwframe.reset(frame); this->frame = frame; @@ -377,10 +366,8 @@ namespace cuda { cuda_ctx->stream = stream.get(); - CU_CHECK(cdf->cuGraphicsGLRegisterImage(&y_res, nv12->tex[0], GL_TEXTURE_2D, CU_GRAPHICS_REGISTER_FLAGS_READ_ONLY), - "Couldn't register Y plane texture"); - CU_CHECK(cdf->cuGraphicsGLRegisterImage(&uv_res, nv12->tex[1], GL_TEXTURE_2D, CU_GRAPHICS_REGISTER_FLAGS_READ_ONLY), - "Couldn't register UV plane texture"); + CU_CHECK(cdf->cuGraphicsGLRegisterImage(&y_res, nv12->tex[0], GL_TEXTURE_2D, CU_GRAPHICS_REGISTER_FLAGS_READ_ONLY), "Couldn't register Y plane texture"); + CU_CHECK(cdf->cuGraphicsGLRegisterImage(&uv_res, nv12->tex[1], GL_TEXTURE_2D, CU_GRAPHICS_REGISTER_FLAGS_READ_ONLY), "Couldn't register UV plane texture"); return 0; } @@ -390,15 +377,13 @@ namespace cuda { * @param img Captured screen image. * @return 0 on success or -1 on failure. */ - int - convert(platf::img_t &img) override { + int convert(platf::img_t &img) override { auto &descriptor = (egl::img_descriptor_t &) img; if (descriptor.sequence == 0) { // For dummy images, use a blank RGB texture instead of importing a DMA-BUF rgb = egl::create_blank(img); - } - else if (descriptor.sequence > sequence) { + } else if (descriptor.sequence > sequence) { sequence = descriptor.sequence; rgb = egl::rgb_t {}; @@ -419,7 +404,7 @@ namespace cuda { auto fmt_desc = av_pix_fmt_desc_get(sw_format); // Map the GL textures to read for CUDA - CUgraphicsResource resources[2] = { y_res.get(), uv_res.get() }; + CUgraphicsResource resources[2] = {y_res.get(), uv_res.get()}; CU_CHECK(cdf->cuGraphicsMapResources(2, resources, stream.get()), "Couldn't map GL textures in CUDA"); // Copy from the GL textures to the target CUDA frame @@ -445,8 +430,7 @@ namespace cuda { /** * @brief Configures shader parameters for the specified colorspace. */ - void - apply_colorspace() override { + void apply_colorspace() override { sws.apply_colorspace(colorspace); } @@ -474,8 +458,7 @@ namespace cuda { int offset_x, offset_y; }; - std::unique_ptr - make_avcodec_encode_device(int width, int height, bool vram) { + std::unique_ptr make_avcodec_encode_device(int width, int height, bool vram) { if (init()) { return nullptr; } @@ -484,8 +467,7 @@ namespace cuda { if (vram) { cuda = std::make_unique(); - } - else { + } else { cuda = std::make_unique(); } @@ -504,8 +486,7 @@ namespace cuda { * @param offset_y Offset of content in captured frame. * @return FFmpeg encoding device context. */ - std::unique_ptr - make_avcodec_gl_encode_device(int width, int height, int offset_x, int offset_y) { + std::unique_ptr make_avcodec_gl_encode_device(int width, int height, int offset_x, int offset_y) { if (init()) { return nullptr; } @@ -521,29 +502,30 @@ namespace cuda { namespace nvfbc { static PNVFBCCREATEINSTANCE createInstance {}; - static NVFBC_API_FUNCTION_LIST func { NVFBC_VERSION }; + static NVFBC_API_FUNCTION_LIST func {NVFBC_VERSION}; - static constexpr inline NVFBC_BOOL - nv_bool(bool b) { + static constexpr inline NVFBC_BOOL nv_bool(bool b) { return b ? NVFBC_TRUE : NVFBC_FALSE; } - static void *handle { nullptr }; - int - init() { + static void *handle {nullptr}; + + int init() { static bool funcs_loaded = false; - if (funcs_loaded) return 0; + if (funcs_loaded) { + return 0; + } if (!handle) { - handle = dyn::handle({ "libnvidia-fbc.so.1", "libnvidia-fbc.so" }); + handle = dyn::handle({"libnvidia-fbc.so.1", "libnvidia-fbc.so"}); if (!handle) { return -1; } } std::vector> funcs { - { (dyn::apiproc *) &createInstance, "NvFBCCreateInstance" }, + {(dyn::apiproc *) &createInstance, "NvFBCCreateInstance"}, }; if (dyn::load(handle, funcs)) { @@ -569,7 +551,7 @@ namespace cuda { class ctx_t { public: ctx_t(NVFBC_SESSION_HANDLE handle) { - NVFBC_BIND_CONTEXT_PARAMS params { NVFBC_BIND_CONTEXT_PARAMS_VER }; + NVFBC_BIND_CONTEXT_PARAMS params {NVFBC_BIND_CONTEXT_PARAMS_VER}; if (func.nvFBCBindContext(handle, ¶ms)) { BOOST_LOG(error) << "Couldn't bind NvFBC context to current thread: " << func.nvFBCGetLastErrorStr(handle); @@ -579,7 +561,7 @@ namespace cuda { } ~ctx_t() { - NVFBC_RELEASE_CONTEXT_PARAMS params { NVFBC_RELEASE_CONTEXT_PARAMS_VER }; + NVFBC_RELEASE_CONTEXT_PARAMS params {NVFBC_RELEASE_CONTEXT_PARAMS_VER}; if (func.nvFBCReleaseContext(handle, ¶ms)) { BOOST_LOG(error) << "Couldn't release NvFBC context from current thread: " << func.nvFBCGetLastErrorStr(handle); } @@ -597,26 +579,26 @@ namespace cuda { public: handle_t() = default; + handle_t(handle_t &&other): - handle_flags { other.handle_flags }, handle { other.handle } { + handle_flags {other.handle_flags}, + handle {other.handle} { other.handle_flags.reset(); } - handle_t & - operator=(handle_t &&other) { + handle_t &operator=(handle_t &&other) { std::swap(handle_flags, other.handle_flags); std::swap(handle, other.handle); return *this; } - static std::optional - make() { - NVFBC_CREATE_HANDLE_PARAMS params { NVFBC_CREATE_HANDLE_PARAMS_VER }; + static std::optional make() { + NVFBC_CREATE_HANDLE_PARAMS params {NVFBC_CREATE_HANDLE_PARAMS_VER}; // Set privateData to allow NvFBC on consumer NVIDIA GPUs. // Based on https://github.com/keylase/nvidia-patch/blob/3193b4b1cea91527bf09ea9b8db5aade6a3f3c0a/win/nvfbcwrp/nvfbcwrp_main.cpp#L23-L25 . - const unsigned int MAGIC_PRIVATE_DATA[4] = { 0xAEF57AC5, 0x401D1A39, 0x1B856BBE, 0x9ED0CEBA }; + const unsigned int MAGIC_PRIVATE_DATA[4] = {0xAEF57AC5, 0x401D1A39, 0x1B856BBE, 0x9ED0CEBA}; params.privateData = MAGIC_PRIVATE_DATA; params.privateDataSize = sizeof(MAGIC_PRIVATE_DATA); @@ -633,14 +615,12 @@ namespace cuda { return handle; } - const char * - last_error() { + const char *last_error() { return func.nvFBCGetLastErrorStr(handle); } - std::optional - status() { - NVFBC_GET_STATUS_PARAMS params { NVFBC_GET_STATUS_PARAMS_VER }; + std::optional status() { + NVFBC_GET_STATUS_PARAMS params {NVFBC_GET_STATUS_PARAMS_VER}; auto status = func.nvFBCGetStatus(handle, ¶ms); if (status) { @@ -652,8 +632,7 @@ namespace cuda { return params; } - int - capture(NVFBC_CREATE_CAPTURE_SESSION_PARAMS &capture_params) { + int capture(NVFBC_CREATE_CAPTURE_SESSION_PARAMS &capture_params) { if (func.nvFBCCreateCaptureSession(handle, &capture_params)) { BOOST_LOG(error) << "Failed to start capture session: "sv << last_error(); return -1; @@ -673,13 +652,12 @@ namespace cuda { return 0; } - int - stop() { + int stop() { if (!handle_flags[SESSION_CAPTURE]) { return 0; } - NVFBC_DESTROY_CAPTURE_SESSION_PARAMS params { NVFBC_DESTROY_CAPTURE_SESSION_PARAMS_VER }; + NVFBC_DESTROY_CAPTURE_SESSION_PARAMS params {NVFBC_DESTROY_CAPTURE_SESSION_PARAMS_VER}; if (func.nvFBCDestroyCaptureSession(handle, ¶ms)) { BOOST_LOG(error) << "Couldn't destroy capture session: "sv << last_error(); @@ -692,17 +670,16 @@ namespace cuda { return 0; } - int - reset() { + int reset() { if (!handle_flags[SESSION_HANDLE]) { return 0; } stop(); - NVFBC_DESTROY_HANDLE_PARAMS params { NVFBC_DESTROY_HANDLE_PARAMS_VER }; + NVFBC_DESTROY_HANDLE_PARAMS params {NVFBC_DESTROY_HANDLE_PARAMS_VER}; - ctx_t ctx { handle }; + ctx_t ctx {handle}; if (func.nvFBCDestroyHandle(handle, ¶ms)) { BOOST_LOG(error) << "Couldn't destroy session handle: "sv << func.nvFBCGetLastErrorStr(handle); } @@ -723,14 +700,13 @@ namespace cuda { class display_t: public platf::display_t { public: - int - init(const std::string_view &display_name, const ::video::config_t &config) { + int init(const std::string_view &display_name, const ::video::config_t &config) { auto handle = handle_t::make(); if (!handle) { return -1; } - ctx_t ctx { handle->handle }; + ctx_t ctx {handle->handle}; auto status_params = handle->status(); if (!status_params) { @@ -744,19 +720,17 @@ namespace cuda { if (monitor_nr < 0 || monitor_nr >= status_params->dwOutputNum) { BOOST_LOG(warning) << "Can't stream monitor ["sv << monitor_nr << "], it needs to be between [0] and ["sv << status_params->dwOutputNum - 1 << "], defaulting to virtual desktop"sv; - } - else { + } else { streamedMonitor = monitor_nr; } - } - else { + } else { BOOST_LOG(warning) << "XrandR not available, streaming entire virtual desktop"sv; } } - delay = std::chrono::nanoseconds { 1s } / config.framerate; + delay = std::chrono::nanoseconds {1s} / config.framerate; - capture_params = NVFBC_CREATE_CAPTURE_SESSION_PARAMS { NVFBC_CREATE_CAPTURE_SESSION_PARAMS_VER }; + capture_params = NVFBC_CREATE_CAPTURE_SESSION_PARAMS {NVFBC_CREATE_CAPTURE_SESSION_PARAMS_VER}; capture_params.eCaptureType = NVFBC_CAPTURE_SHARED_CUDA; capture_params.bDisableAutoModesetRecovery = nv_bool(true); @@ -773,8 +747,7 @@ namespace cuda { capture_params.eTrackingType = NVFBC_TRACKING_OUTPUT; capture_params.dwOutputId = output.dwId; - } - else { + } else { capture_params.eTrackingType = NVFBC_TRACKING_SCREEN; width = status_params->screenSize.w; @@ -788,8 +761,7 @@ namespace cuda { return 0; } - platf::capture_e - capture(const push_captured_image_cb_t &push_captured_image_cb, const pull_free_image_cb_t &pull_free_image_cb, bool *cursor) override { + platf::capture_e capture(const push_captured_image_cb_t &push_captured_image_cb, const pull_free_image_cb_t &pull_free_image_cb, bool *cursor) override { auto next_frame = std::chrono::steady_clock::now(); { @@ -802,7 +774,7 @@ namespace cuda { // Force display_t::capture to initialize handle_t::capture cursor_visible = !*cursor; - ctx_t ctx { handle.handle }; + ctx_t ctx {handle.handle}; auto fg = util::fail_guard([&]() { handle.reset(); }); @@ -849,8 +821,7 @@ namespace cuda { } // Reinitialize the capture session. - platf::capture_e - reinit(bool cursor) { + platf::capture_e reinit(bool cursor) { if (handle.stop()) { return platf::capture_e::error; } @@ -860,8 +831,7 @@ namespace cuda { capture_params.bPushModel = nv_bool(false); capture_params.bWithCursor = nv_bool(true); capture_params.bAllowDirectCapture = nv_bool(false); - } - else { + } else { capture_params.bPushModel = nv_bool(true); capture_params.bWithCursor = nv_bool(false); capture_params.bAllowDirectCapture = nv_bool(true); @@ -919,8 +889,7 @@ namespace cuda { return platf::capture_e::ok; } - platf::capture_e - snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr &img_out, std::chrono::milliseconds timeout, bool cursor) { + platf::capture_e snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr &img_out, std::chrono::milliseconds timeout, bool cursor) { if (cursor != cursor_visible) { auto status = reinit(cursor); if (status != platf::capture_e::ok) { @@ -960,13 +929,11 @@ namespace cuda { return platf::capture_e::ok; } - std::unique_ptr - make_avcodec_encode_device(platf::pix_fmt_e pix_fmt) { + std::unique_ptr make_avcodec_encode_device(platf::pix_fmt_e pix_fmt) { return ::cuda::make_avcodec_encode_device(width, height, true); } - std::shared_ptr - alloc_img() override { + std::shared_ptr alloc_img() override { auto img = std::make_shared(); img->data = nullptr; @@ -985,8 +952,7 @@ namespace cuda { return img; }; - int - dummy_img(platf::img_t *) override { + int dummy_img(platf::img_t *) override { return 0; } @@ -1001,8 +967,7 @@ namespace cuda { } // namespace cuda namespace platf { - std::shared_ptr - nvfbc_display(mem_type_e hwdevice_type, const std::string &display_name, const video::config_t &config) { + std::shared_ptr nvfbc_display(mem_type_e hwdevice_type, const std::string &display_name, const video::config_t &config) { if (hwdevice_type != mem_type_e::cuda) { BOOST_LOG(error) << "Could not initialize nvfbc display with the given hw device type"sv; return nullptr; @@ -1017,8 +982,7 @@ namespace platf { return display; } - std::vector - nvfbc_display_names() { + std::vector nvfbc_display_names() { if (cuda::init() || cuda::nvfbc::init()) { return {}; } diff --git a/src/platform/linux/cuda.cu b/src/platform/linux/cuda.cu index 7456dd4d..6d288ef8 100644 --- a/src/platform/linux/cuda.cu +++ b/src/platform/linux/cuda.cu @@ -2,14 +2,17 @@ * @file src/platform/linux/cuda.cu * @brief CUDA implementation for Linux. */ -// #include -#include +// standard includes #include #include #include #include #include +// platform includes +#include + +// local includes #include "cuda.h" using namespace std::literals; @@ -18,16 +21,20 @@ using namespace std::literals; #define SUNSHINE_STRINGVIEW(x) SUNSHINE_STRINGVIEW_HELPER(x) #define CU_CHECK(x, y) \ - if(check((x), SUNSHINE_STRINGVIEW(y ": "))) return -1 + if (check((x), SUNSHINE_STRINGVIEW(y ": "))) \ + return -1 #define CU_CHECK_VOID(x, y) \ - if(check((x), SUNSHINE_STRINGVIEW(y ": "))) return; + if (check((x), SUNSHINE_STRINGVIEW(y ": "))) \ + return; #define CU_CHECK_PTR(x, y) \ - if(check((x), SUNSHINE_STRINGVIEW(y ": "))) return nullptr; + if (check((x), SUNSHINE_STRINGVIEW(y ": "))) \ + return nullptr; #define CU_CHECK_OPT(x, y) \ - if(check((x), SUNSHINE_STRINGVIEW(y ": "))) return std::nullopt; + if (check((x), SUNSHINE_STRINGVIEW(y ": "))) \ + return std::nullopt; #define CU_CHECK_IGNORE(x, y) \ check((x), SUNSHINE_STRINGVIEW(y ": ")) @@ -42,277 +49,293 @@ using namespace std::literals; * Not pretty and extremely error-prone, fix at earliest convenience. */ namespace platf { -struct img_t: std::enable_shared_from_this { -public: - std::uint8_t *data {}; - std::int32_t width {}; - std::int32_t height {}; - std::int32_t pixel_pitch {}; - std::int32_t row_pitch {}; + struct img_t: std::enable_shared_from_this { + public: + std::uint8_t *data {}; + std::int32_t width {}; + std::int32_t height {}; + std::int32_t pixel_pitch {}; + std::int32_t row_pitch {}; - std::optional frame_timestamp; + std::optional frame_timestamp; - virtual ~img_t() = default; -}; -} // namespace platf + virtual ~img_t() = default; + }; +} // namespace platf // End special declarations namespace cuda { -struct alignas(16) cuda_color_t { - float4 color_vec_y; - float4 color_vec_u; - float4 color_vec_v; - float2 range_y; - float2 range_uv; -}; + struct alignas(16) cuda_color_t { + float4 color_vec_y; + float4 color_vec_u; + float4 color_vec_v; + float2 range_y; + float2 range_uv; + }; -static_assert(sizeof(video::color_t) == sizeof(cuda::cuda_color_t), "color matrix struct mismatch"); + static_assert(sizeof(video::color_t) == sizeof(cuda::cuda_color_t), "color matrix struct mismatch"); -auto constexpr INVALID_TEXTURE = std::numeric_limits::max(); + auto constexpr INVALID_TEXTURE = std::numeric_limits::max(); -template -inline T div_align(T l, T r) { - return (l + r - 1) / r; -} - -void pass_error(const std::string_view &sv, const char *name, const char *description); -inline static int check(cudaError_t result, const std::string_view &sv) { - if(result) { - auto name = cudaGetErrorName(result); - auto description = cudaGetErrorString(result); - - pass_error(sv, name, description); - return -1; + template + inline T div_align(T l, T r) { + return (l + r - 1) / r; } - return 0; -} + void pass_error(const std::string_view &sv, const char *name, const char *description); -template -ptr_t make_ptr() { - void *p; - CU_CHECK_PTR(cudaMalloc(&p, sizeof(T)), "Couldn't allocate color matrix"); + inline static int check(cudaError_t result, const std::string_view &sv) { + if (result) { + auto name = cudaGetErrorName(result); + auto description = cudaGetErrorString(result); - ptr_t ptr { p }; + pass_error(sv, name, description); + return -1; + } - return ptr; -} - -void freeCudaPtr_t::operator()(void *ptr) { - CU_CHECK_IGNORE(cudaFree(ptr), "Couldn't free cuda device pointer"); -} - -void freeCudaStream_t::operator()(cudaStream_t ptr) { - CU_CHECK_IGNORE(cudaStreamDestroy(ptr), "Couldn't free cuda stream"); -} - -stream_t make_stream(int flags) { - cudaStream_t stream; - - if(!flags) { - CU_CHECK_PTR(cudaStreamCreate(&stream), "Couldn't create cuda stream"); - } - else { - CU_CHECK_PTR(cudaStreamCreateWithFlags(&stream, flags), "Couldn't create cuda stream with flags"); + return 0; } - return stream_t { stream }; -} + template + ptr_t make_ptr() { + void *p; + CU_CHECK_PTR(cudaMalloc(&p, sizeof(T)), "Couldn't allocate color matrix"); -inline __device__ float3 bgra_to_rgb(uchar4 vec) { - return make_float3((float)vec.z, (float)vec.y, (float)vec.x); -} + ptr_t ptr {p}; -inline __device__ float3 bgra_to_rgb(float4 vec) { - return make_float3(vec.z, vec.y, vec.x); -} - -inline __device__ float2 calcUV(float3 pixel, const cuda_color_t *const color_matrix) { - float4 vec_u = color_matrix->color_vec_u; - float4 vec_v = color_matrix->color_vec_v; - - float u = dot(pixel, make_float3(vec_u)) + vec_u.w; - float v = dot(pixel, make_float3(vec_v)) + vec_v.w; - - u = u * color_matrix->range_uv.x + color_matrix->range_uv.y; - v = v * color_matrix->range_uv.x + color_matrix->range_uv.y; - - return make_float2(u, v); -} - -inline __device__ float calcY(float3 pixel, const cuda_color_t *const color_matrix) { - float4 vec_y = color_matrix->color_vec_y; - - return (dot(pixel, make_float3(vec_y)) + vec_y.w) * color_matrix->range_y.x + color_matrix->range_y.y; -} - -__global__ void RGBA_to_NV12( - cudaTextureObject_t srcImage, std::uint8_t *dstY, std::uint8_t *dstUV, - std::uint32_t dstPitchY, std::uint32_t dstPitchUV, - float scale, const viewport_t viewport, const cuda_color_t *const color_matrix) { - - int idX = (threadIdx.x + blockDim.x * blockIdx.x) * 2; - int idY = (threadIdx.y + blockDim.y * blockIdx.y) * 2; - - if(idX >= viewport.width) return; - if(idY >= viewport.height) return; - - float x = idX * scale; - float y = idY * scale; - - idX += viewport.offsetX; - idY += viewport.offsetY; - - uint8_t *dstY0 = dstY + idX + idY * dstPitchY; - uint8_t *dstY1 = dstY + idX + (idY + 1) * dstPitchY; - dstUV = dstUV + idX + (idY / 2 * dstPitchUV); - - float3 rgb_lt = bgra_to_rgb(tex2D(srcImage, x, y)); - float3 rgb_rt = bgra_to_rgb(tex2D(srcImage, x + scale, y)); - float3 rgb_lb = bgra_to_rgb(tex2D(srcImage, x, y + scale)); - float3 rgb_rb = bgra_to_rgb(tex2D(srcImage, x + scale, y + scale)); - - float2 uv_lt = calcUV(rgb_lt, color_matrix) * 256.0f; - float2 uv_rt = calcUV(rgb_rt, color_matrix) * 256.0f; - float2 uv_lb = calcUV(rgb_lb, color_matrix) * 256.0f; - float2 uv_rb = calcUV(rgb_rb, color_matrix) * 256.0f; - - float2 uv = (uv_lt + uv_lb + uv_rt + uv_rb) * 0.25f; - - dstUV[0] = uv.x; - dstUV[1] = uv.y; - dstY0[0] = calcY(rgb_lt, color_matrix) * 245.0f; // 245.0f is a magic number to ensure slight changes in luminosity are more visible - dstY0[1] = calcY(rgb_rt, color_matrix) * 245.0f; // 245.0f is a magic number to ensure slight changes in luminosity are more visible - dstY1[0] = calcY(rgb_lb, color_matrix) * 245.0f; // 245.0f is a magic number to ensure slight changes in luminosity are more visible - dstY1[1] = calcY(rgb_rb, color_matrix) * 245.0f; // 245.0f is a magic number to ensure slight changes in luminosity are more visible -} - -int tex_t::copy(std::uint8_t *src, int height, int pitch) { - CU_CHECK(cudaMemcpy2DToArray(array, 0, 0, src, pitch, pitch, height, cudaMemcpyDeviceToDevice), "Couldn't copy to cuda array from deviceptr"); - - return 0; -} - -std::optional tex_t::make(int height, int pitch) { - tex_t tex; - - auto format = cudaCreateChannelDesc(); - CU_CHECK_OPT(cudaMallocArray(&tex.array, &format, pitch, height, cudaArrayDefault), "Couldn't allocate cuda array"); - - cudaResourceDesc res {}; - res.resType = cudaResourceTypeArray; - res.res.array.array = tex.array; - - cudaTextureDesc desc {}; - - desc.readMode = cudaReadModeNormalizedFloat; - desc.filterMode = cudaFilterModePoint; - desc.normalizedCoords = false; - - std::fill_n(std::begin(desc.addressMode), 2, cudaAddressModeClamp); - - CU_CHECK_OPT(cudaCreateTextureObject(&tex.texture.point, &res, &desc, nullptr), "Couldn't create cuda texture that uses point interpolation"); - - desc.filterMode = cudaFilterModeLinear; - - CU_CHECK_OPT(cudaCreateTextureObject(&tex.texture.linear, &res, &desc, nullptr), "Couldn't create cuda texture that uses linear interpolation"); - - return tex; -} - -tex_t::tex_t() : array {}, texture { INVALID_TEXTURE, INVALID_TEXTURE } {} -tex_t::tex_t(tex_t &&other) : array { other.array }, texture { other.texture } { - other.array = 0; - other.texture.point = INVALID_TEXTURE; - other.texture.linear = INVALID_TEXTURE; -} - -tex_t &tex_t::operator=(tex_t &&other) { - std::swap(array, other.array); - std::swap(texture, other.texture); - - return *this; -} - -tex_t::~tex_t() { - if(texture.point != INVALID_TEXTURE) { - CU_CHECK_IGNORE(cudaDestroyTextureObject(texture.point), "Couldn't deallocate cuda texture that uses point interpolation"); - - texture.point = INVALID_TEXTURE; + return ptr; } - if(texture.linear != INVALID_TEXTURE) { - CU_CHECK_IGNORE(cudaDestroyTextureObject(texture.linear), "Couldn't deallocate cuda texture that uses linear interpolation"); - - texture.linear = INVALID_TEXTURE; + void freeCudaPtr_t::operator()(void *ptr) { + CU_CHECK_IGNORE(cudaFree(ptr), "Couldn't free cuda device pointer"); } - if(array) { - CU_CHECK_IGNORE(cudaFreeArray(array), "Couldn't deallocate cuda array"); - - array = cudaArray_t {}; - } -} - -sws_t::sws_t(int in_width, int in_height, int out_width, int out_height, int pitch, int threadsPerBlock, ptr_t &&color_matrix) - : threadsPerBlock { threadsPerBlock }, color_matrix { std::move(color_matrix) } { - // Ensure aspect ratio is maintained - auto scalar = std::fminf(out_width / (float)in_width, out_height / (float)in_height); - auto out_width_f = in_width * scalar; - auto out_height_f = in_height * scalar; - - // result is always positive - auto offsetX_f = (out_width - out_width_f) / 2; - auto offsetY_f = (out_height - out_height_f) / 2; - - viewport.width = out_width_f; - viewport.height = out_height_f; - - viewport.offsetX = offsetX_f; - viewport.offsetY = offsetY_f; - - scale = 1.0f / scalar; -} - -std::optional sws_t::make(int in_width, int in_height, int out_width, int out_height, int pitch) { - cudaDeviceProp props; - int device; - CU_CHECK_OPT(cudaGetDevice(&device), "Couldn't get cuda device"); - CU_CHECK_OPT(cudaGetDeviceProperties(&props, device), "Couldn't get cuda device properties"); - - auto ptr = make_ptr(); - if(!ptr) { - return std::nullopt; + void freeCudaStream_t::operator()(cudaStream_t ptr) { + CU_CHECK_IGNORE(cudaStreamDestroy(ptr), "Couldn't free cuda stream"); } - return std::make_optional(in_width, in_height, out_width, out_height, pitch, props.maxThreadsPerMultiProcessor / props.maxBlocksPerMultiProcessor, std::move(ptr)); -} + stream_t make_stream(int flags) { + cudaStream_t stream; -int sws_t::convert(std::uint8_t *Y, std::uint8_t *UV, std::uint32_t pitchY, std::uint32_t pitchUV, cudaTextureObject_t texture, stream_t::pointer stream) { - return convert(Y, UV, pitchY, pitchUV, texture, stream, viewport); -} + if (!flags) { + CU_CHECK_PTR(cudaStreamCreate(&stream), "Couldn't create cuda stream"); + } else { + CU_CHECK_PTR(cudaStreamCreateWithFlags(&stream, flags), "Couldn't create cuda stream with flags"); + } -int sws_t::convert(std::uint8_t *Y, std::uint8_t *UV, std::uint32_t pitchY, std::uint32_t pitchUV, cudaTextureObject_t texture, stream_t::pointer stream, const viewport_t &viewport) { - int threadsX = viewport.width / 2; - int threadsY = viewport.height / 2; + return stream_t {stream}; + } - dim3 block(threadsPerBlock); - dim3 grid(div_align(threadsX, threadsPerBlock), threadsY); + inline __device__ float3 bgra_to_rgb(uchar4 vec) { + return make_float3((float) vec.z, (float) vec.y, (float) vec.x); + } - RGBA_to_NV12<<>>(texture, Y, UV, pitchY, pitchUV, scale, viewport, (cuda_color_t *)color_matrix.get()); + inline __device__ float3 bgra_to_rgb(float4 vec) { + return make_float3(vec.z, vec.y, vec.x); + } - return CU_CHECK_IGNORE(cudaGetLastError(), "RGBA_to_NV12 failed"); -} + inline __device__ float2 calcUV(float3 pixel, const cuda_color_t *const color_matrix) { + float4 vec_u = color_matrix->color_vec_u; + float4 vec_v = color_matrix->color_vec_v; -void sws_t::apply_colorspace(const video::sunshine_colorspace_t& colorspace) { - auto color_p = video::color_vectors_from_colorspace(colorspace); - CU_CHECK_IGNORE(cudaMemcpy(color_matrix.get(), color_p, sizeof(video::color_t), cudaMemcpyHostToDevice), "Couldn't copy color matrix to cuda"); -} + float u = dot(pixel, make_float3(vec_u)) + vec_u.w; + float v = dot(pixel, make_float3(vec_v)) + vec_v.w; -int sws_t::load_ram(platf::img_t &img, cudaArray_t array) { - return CU_CHECK_IGNORE(cudaMemcpy2DToArray(array, 0, 0, img.data, img.row_pitch, img.width * img.pixel_pitch, img.height, cudaMemcpyHostToDevice), "Couldn't copy to cuda array"); -} + u = u * color_matrix->range_uv.x + color_matrix->range_uv.y; + v = v * color_matrix->range_uv.x + color_matrix->range_uv.y; -} // namespace cuda + return make_float2(u, v); + } + + inline __device__ float calcY(float3 pixel, const cuda_color_t *const color_matrix) { + float4 vec_y = color_matrix->color_vec_y; + + return (dot(pixel, make_float3(vec_y)) + vec_y.w) * color_matrix->range_y.x + color_matrix->range_y.y; + } + + __global__ void RGBA_to_NV12( + cudaTextureObject_t srcImage, + std::uint8_t *dstY, + std::uint8_t *dstUV, + std::uint32_t dstPitchY, + std::uint32_t dstPitchUV, + float scale, + const viewport_t viewport, + const cuda_color_t *const color_matrix + ) { + int idX = (threadIdx.x + blockDim.x * blockIdx.x) * 2; + int idY = (threadIdx.y + blockDim.y * blockIdx.y) * 2; + + if (idX >= viewport.width) { + return; + } + if (idY >= viewport.height) { + return; + } + + float x = idX * scale; + float y = idY * scale; + + idX += viewport.offsetX; + idY += viewport.offsetY; + + uint8_t *dstY0 = dstY + idX + idY * dstPitchY; + uint8_t *dstY1 = dstY + idX + (idY + 1) * dstPitchY; + dstUV = dstUV + idX + (idY / 2 * dstPitchUV); + + float3 rgb_lt = bgra_to_rgb(tex2D(srcImage, x, y)); + float3 rgb_rt = bgra_to_rgb(tex2D(srcImage, x + scale, y)); + float3 rgb_lb = bgra_to_rgb(tex2D(srcImage, x, y + scale)); + float3 rgb_rb = bgra_to_rgb(tex2D(srcImage, x + scale, y + scale)); + + float2 uv_lt = calcUV(rgb_lt, color_matrix) * 256.0f; + float2 uv_rt = calcUV(rgb_rt, color_matrix) * 256.0f; + float2 uv_lb = calcUV(rgb_lb, color_matrix) * 256.0f; + float2 uv_rb = calcUV(rgb_rb, color_matrix) * 256.0f; + + float2 uv = (uv_lt + uv_lb + uv_rt + uv_rb) * 0.25f; + + dstUV[0] = uv.x; + dstUV[1] = uv.y; + dstY0[0] = calcY(rgb_lt, color_matrix) * 245.0f; // 245.0f is a magic number to ensure slight changes in luminosity are more visible + dstY0[1] = calcY(rgb_rt, color_matrix) * 245.0f; // 245.0f is a magic number to ensure slight changes in luminosity are more visible + dstY1[0] = calcY(rgb_lb, color_matrix) * 245.0f; // 245.0f is a magic number to ensure slight changes in luminosity are more visible + dstY1[1] = calcY(rgb_rb, color_matrix) * 245.0f; // 245.0f is a magic number to ensure slight changes in luminosity are more visible + } + + int tex_t::copy(std::uint8_t *src, int height, int pitch) { + CU_CHECK(cudaMemcpy2DToArray(array, 0, 0, src, pitch, pitch, height, cudaMemcpyDeviceToDevice), "Couldn't copy to cuda array from deviceptr"); + + return 0; + } + + std::optional tex_t::make(int height, int pitch) { + tex_t tex; + + auto format = cudaCreateChannelDesc(); + CU_CHECK_OPT(cudaMallocArray(&tex.array, &format, pitch, height, cudaArrayDefault), "Couldn't allocate cuda array"); + + cudaResourceDesc res {}; + res.resType = cudaResourceTypeArray; + res.res.array.array = tex.array; + + cudaTextureDesc desc {}; + + desc.readMode = cudaReadModeNormalizedFloat; + desc.filterMode = cudaFilterModePoint; + desc.normalizedCoords = false; + + std::fill_n(std::begin(desc.addressMode), 2, cudaAddressModeClamp); + + CU_CHECK_OPT(cudaCreateTextureObject(&tex.texture.point, &res, &desc, nullptr), "Couldn't create cuda texture that uses point interpolation"); + + desc.filterMode = cudaFilterModeLinear; + + CU_CHECK_OPT(cudaCreateTextureObject(&tex.texture.linear, &res, &desc, nullptr), "Couldn't create cuda texture that uses linear interpolation"); + + return tex; + } + + tex_t::tex_t(): + array {}, + texture {INVALID_TEXTURE, INVALID_TEXTURE} { + } + + tex_t::tex_t(tex_t &&other): + array {other.array}, + texture {other.texture} { + other.array = 0; + other.texture.point = INVALID_TEXTURE; + other.texture.linear = INVALID_TEXTURE; + } + + tex_t &tex_t::operator=(tex_t &&other) { + std::swap(array, other.array); + std::swap(texture, other.texture); + + return *this; + } + + tex_t::~tex_t() { + if (texture.point != INVALID_TEXTURE) { + CU_CHECK_IGNORE(cudaDestroyTextureObject(texture.point), "Couldn't deallocate cuda texture that uses point interpolation"); + + texture.point = INVALID_TEXTURE; + } + + if (texture.linear != INVALID_TEXTURE) { + CU_CHECK_IGNORE(cudaDestroyTextureObject(texture.linear), "Couldn't deallocate cuda texture that uses linear interpolation"); + + texture.linear = INVALID_TEXTURE; + } + + if (array) { + CU_CHECK_IGNORE(cudaFreeArray(array), "Couldn't deallocate cuda array"); + + array = cudaArray_t {}; + } + } + + sws_t::sws_t(int in_width, int in_height, int out_width, int out_height, int pitch, int threadsPerBlock, ptr_t &&color_matrix): + threadsPerBlock {threadsPerBlock}, + color_matrix {std::move(color_matrix)} { + // Ensure aspect ratio is maintained + auto scalar = std::fminf(out_width / (float) in_width, out_height / (float) in_height); + auto out_width_f = in_width * scalar; + auto out_height_f = in_height * scalar; + + // result is always positive + auto offsetX_f = (out_width - out_width_f) / 2; + auto offsetY_f = (out_height - out_height_f) / 2; + + viewport.width = out_width_f; + viewport.height = out_height_f; + + viewport.offsetX = offsetX_f; + viewport.offsetY = offsetY_f; + + scale = 1.0f / scalar; + } + + std::optional sws_t::make(int in_width, int in_height, int out_width, int out_height, int pitch) { + cudaDeviceProp props; + int device; + CU_CHECK_OPT(cudaGetDevice(&device), "Couldn't get cuda device"); + CU_CHECK_OPT(cudaGetDeviceProperties(&props, device), "Couldn't get cuda device properties"); + + auto ptr = make_ptr(); + if (!ptr) { + return std::nullopt; + } + + return std::make_optional(in_width, in_height, out_width, out_height, pitch, props.maxThreadsPerMultiProcessor / props.maxBlocksPerMultiProcessor, std::move(ptr)); + } + + int sws_t::convert(std::uint8_t *Y, std::uint8_t *UV, std::uint32_t pitchY, std::uint32_t pitchUV, cudaTextureObject_t texture, stream_t::pointer stream) { + return convert(Y, UV, pitchY, pitchUV, texture, stream, viewport); + } + + int sws_t::convert(std::uint8_t *Y, std::uint8_t *UV, std::uint32_t pitchY, std::uint32_t pitchUV, cudaTextureObject_t texture, stream_t::pointer stream, const viewport_t &viewport) { + int threadsX = viewport.width / 2; + int threadsY = viewport.height / 2; + + dim3 block(threadsPerBlock); + dim3 grid(div_align(threadsX, threadsPerBlock), threadsY); + + RGBA_to_NV12<<>>(texture, Y, UV, pitchY, pitchUV, scale, viewport, (cuda_color_t *) color_matrix.get()); + + return CU_CHECK_IGNORE(cudaGetLastError(), "RGBA_to_NV12 failed"); + } + + void sws_t::apply_colorspace(const video::sunshine_colorspace_t &colorspace) { + auto color_p = video::color_vectors_from_colorspace(colorspace); + CU_CHECK_IGNORE(cudaMemcpy(color_matrix.get(), color_p, sizeof(video::color_t), cudaMemcpyHostToDevice), "Couldn't copy color matrix to cuda"); + } + + int sws_t::load_ram(platf::img_t &img, cudaArray_t array) { + return CU_CHECK_IGNORE(cudaMemcpy2DToArray(array, 0, 0, img.data, img.row_pitch, img.width * img.pixel_pitch, img.height, cudaMemcpyHostToDevice), "Couldn't copy to cuda array"); + } + +} // namespace cuda diff --git a/src/platform/linux/cuda.h b/src/platform/linux/cuda.h index 88b684b9..c7f5daac 100644 --- a/src/platform/linux/cuda.h +++ b/src/platform/linux/cuda.h @@ -5,15 +5,16 @@ #pragma once #if defined(SUNSHINE_BUILD_CUDA) - - #include "src/video_colorspace.h" - + // standard includes #include #include #include #include #include + // local includes + #include "src/video_colorspace.h" + namespace platf { class avcodec_encode_device_t; class img_t; @@ -22,11 +23,10 @@ namespace platf { namespace cuda { namespace nvfbc { - std::vector - display_names(); + std::vector display_names(); } - std::unique_ptr - make_avcodec_encode_device(int width, int height, bool vram); + + std::unique_ptr make_avcodec_encode_device(int width, int height, bool vram); /** * @brief Create a GL->CUDA encoding device for consuming captured dmabufs. @@ -36,11 +36,9 @@ namespace cuda { * @param offset_y Offset of content in captured frame. * @return FFmpeg encoding device context. */ - std::unique_ptr - make_avcodec_gl_encode_device(int width, int height, int offset_x, int offset_y); + std::unique_ptr make_avcodec_gl_encode_device(int width, int height, int offset_x, int offset_y); - int - init(); + int init(); } // namespace cuda typedef struct cudaArray *cudaArray_t; @@ -57,21 +55,18 @@ namespace cuda { class freeCudaPtr_t { public: - void - operator()(void *ptr); + void operator()(void *ptr); }; class freeCudaStream_t { public: - void - operator()(cudaStream_t ptr); + void operator()(cudaStream_t ptr); }; using ptr_t = std::unique_ptr; using stream_t = std::unique_ptr; - stream_t - make_stream(int flags = 0); + stream_t make_stream(int flags = 0); struct viewport_t { int width, height; @@ -80,19 +75,16 @@ namespace cuda { class tex_t { public: - static std::optional - make(int height, int pitch); + static std::optional make(int height, int pitch); tex_t(); tex_t(tex_t &&); - tex_t & - operator=(tex_t &&other); + tex_t &operator=(tex_t &&other); ~tex_t(); - int - copy(std::uint8_t *src, int height, int pitch); + int copy(std::uint8_t *src, int height, int pitch); cudaArray_t array; @@ -113,20 +105,15 @@ namespace cuda { * * pitch -- The size of a single row of pixels in bytes */ - static std::optional - make(int in_width, int in_height, int out_width, int out_height, int pitch); + static std::optional make(int in_width, int in_height, int out_width, int out_height, int pitch); // Converts loaded image into a CUDevicePtr - int - convert(std::uint8_t *Y, std::uint8_t *UV, std::uint32_t pitchY, std::uint32_t pitchUV, cudaTextureObject_t texture, stream_t::pointer stream); - int - convert(std::uint8_t *Y, std::uint8_t *UV, std::uint32_t pitchY, std::uint32_t pitchUV, cudaTextureObject_t texture, stream_t::pointer stream, const viewport_t &viewport); + int convert(std::uint8_t *Y, std::uint8_t *UV, std::uint32_t pitchY, std::uint32_t pitchUV, cudaTextureObject_t texture, stream_t::pointer stream); + int convert(std::uint8_t *Y, std::uint8_t *UV, std::uint32_t pitchY, std::uint32_t pitchUV, cudaTextureObject_t texture, stream_t::pointer stream, const viewport_t &viewport); - void - apply_colorspace(const video::sunshine_colorspace_t &colorspace); + void apply_colorspace(const video::sunshine_colorspace_t &colorspace); - int - load_ram(platf::img_t &img, cudaArray_t array); + int load_ram(platf::img_t &img, cudaArray_t array); ptr_t color_matrix; @@ -138,4 +125,4 @@ namespace cuda { }; } // namespace cuda -#endif \ No newline at end of file +#endif diff --git a/src/platform/linux/graphics.cpp b/src/platform/linux/graphics.cpp index 8ffba667..568067f3 100644 --- a/src/platform/linux/graphics.cpp +++ b/src/platform/linux/graphics.cpp @@ -2,13 +2,15 @@ * @file src/platform/linux/graphics.cpp * @brief Definitions for graphics related functions. */ +// standard includes +#include + +// local includes #include "graphics.h" #include "src/file_handler.h" #include "src/logging.h" #include "src/video.h" -#include - extern "C" { #include } @@ -17,8 +19,7 @@ extern "C" { // There aren't that many DRM_FORMAT I need to use, so define them here // // They aren't likely to change any time soon. -#define fourcc_code(a, b, c, d) ((std::uint32_t)(a) | ((std::uint32_t)(b) << 8) | \ - ((std::uint32_t)(c) << 16) | ((std::uint32_t)(d) << 24)) +#define fourcc_code(a, b, c, d) ((std::uint32_t)(a) | ((std::uint32_t)(b) << 8) | ((std::uint32_t)(c) << 16) | ((std::uint32_t)(d) << 24)) #define fourcc_mod_code(vendor, val) ((((uint64_t) vendor) << 56) | ((val) & 0x00ffffffffffffffULL)) #define DRM_FORMAT_MOD_INVALID fourcc_mod_code(0, ((1ULL << 56) - 1)) @@ -27,11 +28,11 @@ extern "C" { #endif using namespace std::literals; + namespace gl { GladGLContext ctx; - void - drain_errors(const std::string_view &prefix) { + void drain_errors(const std::string_view &prefix) { GLenum err; while ((err = ctx.GetError()) != GL_NO_ERROR) { BOOST_LOG(error) << "GL: "sv << prefix << ": ["sv << util::hex(err).to_string_view() << ']'; @@ -44,13 +45,12 @@ namespace gl { } } - tex_t - tex_t::make(std::size_t count) { - tex_t textures { count }; + tex_t tex_t::make(std::size_t count) { + tex_t textures {count}; ctx.GenTextures(textures.size(), textures.begin()); - float color[] = { 0.0f, 0.0f, 0.0f, 1.0f }; + float color[] = {0.0f, 0.0f, 0.0f, 1.0f}; for (auto tex : textures) { gl::ctx.BindTexture(GL_TEXTURE_2D, tex); @@ -70,25 +70,22 @@ namespace gl { } } - frame_buf_t - frame_buf_t::make(std::size_t count) { - frame_buf_t frame_buf { count }; + frame_buf_t frame_buf_t::make(std::size_t count) { + frame_buf_t frame_buf {count}; ctx.GenFramebuffers(frame_buf.size(), frame_buf.begin()); return frame_buf; } - void - frame_buf_t::copy(int id, int texture, int offset_x, int offset_y, int width, int height) { + void frame_buf_t::copy(int id, int texture, int offset_x, int offset_y, int width, int height) { gl::ctx.BindFramebuffer(GL_FRAMEBUFFER, (*this)[id]); gl::ctx.ReadBuffer(GL_COLOR_ATTACHMENT0 + id); gl::ctx.BindTexture(GL_TEXTURE_2D, texture); gl::ctx.CopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, offset_x, offset_y, width, height); } - std::string - shader_t::err_str() { + std::string shader_t::err_str() { int length; ctx.GetShaderiv(handle(), GL_INFO_LOG_LENGTH, &length); @@ -102,8 +99,7 @@ namespace gl { return string; } - util::Either - shader_t::compile(const std::string_view &source, GLenum type) { + util::Either shader_t::compile(const std::string_view &source, GLenum type) { shader_t shader; auto data = source.data(); @@ -123,13 +119,11 @@ namespace gl { return shader; } - GLuint - shader_t::handle() const { + GLuint shader_t::handle() const { return _shader.el; } - buffer_t - buffer_t::make(util::buffer_t &&offsets, const char *block, const std::string_view &data) { + buffer_t buffer_t::make(util::buffer_t &&offsets, const char *block, const std::string_view &data) { buffer_t buffer; buffer._block = block; buffer._size = data.size(); @@ -142,25 +136,21 @@ namespace gl { return buffer; } - GLuint - buffer_t::handle() const { + GLuint buffer_t::handle() const { return _buffer.el; } - const char * - buffer_t::block() const { + const char *buffer_t::block() const { return _block; } - void - buffer_t::update(const std::string_view &view, std::size_t offset) { + void buffer_t::update(const std::string_view &view, std::size_t offset) { ctx.BindBuffer(GL_UNIFORM_BUFFER, handle()); ctx.BufferSubData(GL_UNIFORM_BUFFER, offset, view.size(), (const void *) view.data()); } - void - buffer_t::update(std::string_view *members, std::size_t count, std::size_t offset) { - util::buffer_t buffer { _size }; + void buffer_t::update(std::string_view *members, std::size_t count, std::size_t offset) { + util::buffer_t buffer {_size}; for (int x = 0; x < count; ++x) { auto val = members[x]; @@ -171,8 +161,7 @@ namespace gl { update(util::view(buffer.begin(), buffer.end()), offset); } - std::string - program_t::err_str() { + std::string program_t::err_str() { int length; ctx.GetProgramiv(handle(), GL_INFO_LOG_LENGTH, &length); @@ -186,8 +175,7 @@ namespace gl { return string; } - util::Either - program_t::link(const shader_t &vert, const shader_t &frag) { + util::Either program_t::link(const shader_t &vert, const shader_t &frag) { program_t program; program._program.el = ctx.CreateProgram(); @@ -214,16 +202,14 @@ namespace gl { return program; } - void - program_t::bind(const buffer_t &buffer) { + void program_t::bind(const buffer_t &buffer) { ctx.UseProgram(handle()); auto i = ctx.GetUniformBlockIndex(handle(), buffer.block()); ctx.BindBufferBase(GL_UNIFORM_BUFFER, i, buffer.handle()); } - std::optional - program_t::uniform(const char *block, std::pair *members, std::size_t count) { + std::optional program_t::uniform(const char *block, std::pair *members, std::size_t count) { auto i = ctx.GetUniformBlockIndex(handle(), block); if (i == GL_INVALID_INDEX) { BOOST_LOG(error) << "Couldn't find index of ["sv << block << ']'; @@ -235,7 +221,7 @@ namespace gl { bool error_flag = false; - util::buffer_t offsets { count }; + util::buffer_t offsets {count}; auto indices = (std::uint32_t *) alloca(count * sizeof(std::uint32_t)); auto names = (const char **) alloca(count * sizeof(const char *)); auto names_p = names; @@ -260,7 +246,7 @@ namespace gl { } ctx.GetActiveUniformsiv(handle(), count, indices, GL_UNIFORM_OFFSET, offsets.begin()); - util::buffer_t buffer { (std::size_t) size }; + util::buffer_t buffer {(std::size_t) size}; for (int x = 0; x < count; ++x) { auto val = std::get<1>(members[x]); @@ -268,11 +254,10 @@ namespace gl { std::copy_n((const std::uint8_t *) val.data(), val.size(), &buffer[offsets[x]]); } - return buffer_t::make(std::move(offsets), block, std::string_view { (char *) buffer.begin(), buffer.size() }); + return buffer_t::make(std::move(offsets), block, std::string_view {(char *) buffer.begin(), buffer.size()}); } - GLuint - program_t::handle() const { + GLuint program_t::handle() const { return _program.el; } @@ -282,23 +267,24 @@ namespace gbm { device_destroy_fn device_destroy; create_device_fn create_device; - int - init() { - static void *handle { nullptr }; + int init() { + static void *handle {nullptr}; static bool funcs_loaded = false; - if (funcs_loaded) return 0; + if (funcs_loaded) { + return 0; + } if (!handle) { - handle = dyn::handle({ "libgbm.so.1", "libgbm.so" }); + handle = dyn::handle({"libgbm.so.1", "libgbm.so"}); if (!handle) { return -1; } } std::vector> funcs { - { (GLADapiproc *) &device_destroy, "gbm_device_destroy" }, - { (GLADapiproc *) &create_device, "gbm_create_device" }, + {(GLADapiproc *) &device_destroy, "gbm_device_destroy"}, + {(GLADapiproc *) &create_device, "gbm_create_device"}, }; if (dyn::load(handle, funcs)) { @@ -334,16 +320,14 @@ namespace egl { constexpr auto EGL_DMA_BUF_PLANE3_MODIFIER_LO_EXT = 0x3449; constexpr auto EGL_DMA_BUF_PLANE3_MODIFIER_HI_EXT = 0x344A; - bool - fail() { + bool fail() { return eglGetError() != EGL_SUCCESS; } /** * @memberof egl::display_t */ - display_t - make_display(std::variant native_display) { + display_t make_display(std::variant native_display) { constexpr auto EGL_PLATFORM_GBM_MESA = 0x31D7; constexpr auto EGL_PLATFORM_WAYLAND_KHR = 0x31D8; constexpr auto EGL_PLATFORM_X11_KHR = 0x31D5; @@ -408,10 +392,11 @@ namespace egl { return display; } - std::optional - make_ctx(display_t::pointer display) { + std::optional make_ctx(display_t::pointer display) { constexpr int conf_attr[] { - EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, EGL_NONE + EGL_RENDERABLE_TYPE, + EGL_OPENGL_BIT, + EGL_NONE }; int count; @@ -427,10 +412,12 @@ namespace egl { } constexpr int attr[] { - EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE + EGL_CONTEXT_CLIENT_VERSION, + 3, + EGL_NONE }; - ctx_t ctx { display, eglCreateContext(display, conf, EGL_NO_CONTEXT, attr) }; + ctx_t ctx {display, eglCreateContext(display, conf, EGL_NO_CONTEXT, attr)}; if (fail()) { BOOST_LOG(error) << "Couldn't create EGL context: ["sv << util::hex(eglGetError()).to_string_view() << ']'; return std::nullopt; @@ -465,8 +452,7 @@ namespace egl { EGLAttrib hi; }; - inline plane_attr_t - get_plane(std::uint32_t plane_indice) { + inline plane_attr_t get_plane(std::uint32_t plane_indice) { switch (plane_indice) { case 0: return { @@ -511,8 +497,7 @@ namespace egl { * @param surface The surface descriptor. * @return Vector of EGL attributes. */ - std::vector - surface_descriptor_to_egl_attribs(const surface_descriptor_t &surface) { + std::vector surface_descriptor_to_egl_attribs(const surface_descriptor_t &surface) { std::vector attribs; attribs.emplace_back(EGL_WIDTH); @@ -549,8 +534,7 @@ namespace egl { return attribs; } - std::optional - import_source(display_t::pointer egl_display, const surface_descriptor_t &xrgb) { + std::optional import_source(display_t::pointer egl_display, const surface_descriptor_t &xrgb) { auto attribs = surface_descriptor_to_egl_attribs(xrgb); rgb_t rgb { @@ -580,8 +564,7 @@ namespace egl { * @param img The image to use for texture sizing. * @return The new RGB texture. */ - rgb_t - create_blank(platf::img_t &img) { + rgb_t create_blank(platf::img_t &img) { rgb_t rgb { EGL_NO_DISPLAY, EGL_NO_IMAGE, @@ -597,7 +580,7 @@ namespace egl { GLenum attachment = GL_COLOR_ATTACHMENT0; gl::ctx.DrawBuffers(1, &attachment); - const GLuint rgb_black[] = { 0, 0, 0, 0 }; + const GLuint rgb_black[] = {0, 0, 0, 0}; gl::ctx.ClearBufferuiv(GL_COLOR, 0, rgb_black); gl_drain_errors; @@ -605,8 +588,7 @@ namespace egl { return rgb; } - std::optional - import_target(display_t::pointer egl_display, std::array &&fds, const surface_descriptor_t &y, const surface_descriptor_t &uv) { + std::optional import_target(display_t::pointer egl_display, std::array &&fds, const surface_descriptor_t &y, const surface_descriptor_t &uv) { auto y_attribs = surface_descriptor_to_egl_attribs(y); auto uv_attribs = surface_descriptor_to_egl_attribs(uv); @@ -642,8 +624,8 @@ namespace egl { gl::ctx.BindFramebuffer(GL_FRAMEBUFFER, nv12->buf[x]); gl::ctx.DrawBuffers(1, &attachments[x]); - const float y_black[] = { 0.0f, 0.0f, 0.0f, 0.0f }; - const float uv_black[] = { 0.5f, 0.5f, 0.5f, 0.5f }; + const float y_black[] = {0.0f, 0.0f, 0.0f, 0.0f}; + const float uv_black[] = {0.5f, 0.5f, 0.5f, 0.5f}; gl::ctx.ClearBufferfv(GL_COLOR, 0, x == 0 ? y_black : uv_black); } @@ -661,8 +643,7 @@ namespace egl { * @param format Format of the target frame. * @return The new RGB texture. */ - std::optional - create_target(int width, int height, AVPixelFormat format) { + std::optional create_target(int width, int height, AVPixelFormat format) { nv12_t nv12 { EGL_NO_DISPLAY, EGL_NO_IMAGE, @@ -679,12 +660,10 @@ namespace egl { if (fmt_desc->comp[0].depth <= 8) { y_format = GL_R8; uv_format = GL_RG8; - } - else if (fmt_desc->comp[0].depth <= 16) { + } else if (fmt_desc->comp[0].depth <= 16) { y_format = GL_R16; uv_format = GL_RG16; - } - else { + } else { BOOST_LOG(error) << "Unsupported target pixel format: "sv << format; return std::nullopt; } @@ -693,8 +672,7 @@ namespace egl { gl::ctx.TexStorage2D(GL_TEXTURE_2D, 1, y_format, width, height); gl::ctx.BindTexture(GL_TEXTURE_2D, nv12->tex[1]); - gl::ctx.TexStorage2D(GL_TEXTURE_2D, 1, uv_format, - width >> fmt_desc->log2_chroma_w, height >> fmt_desc->log2_chroma_h); + gl::ctx.TexStorage2D(GL_TEXTURE_2D, 1, uv_format, width >> fmt_desc->log2_chroma_w, height >> fmt_desc->log2_chroma_h); nv12->buf.bind(std::begin(nv12->tex), std::end(nv12->tex)); @@ -707,8 +685,8 @@ namespace egl { gl::ctx.BindFramebuffer(GL_FRAMEBUFFER, nv12->buf[x]); gl::ctx.DrawBuffers(1, &attachments[x]); - const float y_black[] = { 0.0f, 0.0f, 0.0f, 0.0f }; - const float uv_black[] = { 0.5f, 0.5f, 0.5f, 0.5f }; + const float y_black[] = {0.0f, 0.0f, 0.0f, 0.0f}; + const float uv_black[] = {0.5f, 0.5f, 0.5f, 0.5f}; gl::ctx.ClearBufferfv(GL_COLOR, 0, x == 0 ? y_black : uv_black); } @@ -719,8 +697,7 @@ namespace egl { return nv12; } - void - sws_t::apply_colorspace(const video::sunshine_colorspace_t &colorspace) { + void sws_t::apply_colorspace(const video::sunshine_colorspace_t &colorspace) { auto color_p = video::color_vectors_from_colorspace(colorspace); std::string_view members[] { @@ -737,8 +714,7 @@ namespace egl { program[1].bind(color_matrix); } - std::optional - sws_t::make(int in_width, int in_height, int out_width, int out_height, gl::tex_t &&tex) { + std::optional sws_t::make(int in_width, int in_height, int out_width, int out_height, gl::tex_t &&tex) { sws_t sws; sws.serial = std::numeric_limits::max(); @@ -866,8 +842,7 @@ namespace egl { return sws; } - int - sws_t::blank(gl::frame_buf_t &fb, int offsetX, int offsetY, int width, int height) { + int sws_t::blank(gl::frame_buf_t &fb, int offsetX, int offsetY, int width, int height) { auto f = [&]() { std::swap(offsetX, this->offsetX); std::swap(offsetY, this->offsetY); @@ -881,8 +856,7 @@ namespace egl { return convert(fb); } - std::optional - sws_t::make(int in_width, int in_height, int out_width, int out_height, AVPixelFormat format) { + std::optional sws_t::make(int in_width, int in_height, int out_width, int out_height, AVPixelFormat format) { GLint gl_format; // Decide the bit depth format of the backing texture based the target frame format @@ -916,16 +890,14 @@ namespace egl { return make(in_width, in_height, out_width, out_height, std::move(tex)); } - void - sws_t::load_ram(platf::img_t &img) { + void sws_t::load_ram(platf::img_t &img) { loaded_texture = tex[0]; gl::ctx.BindTexture(GL_TEXTURE_2D, loaded_texture); gl::ctx.TexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, img.width, img.height, GL_BGRA, GL_UNSIGNED_BYTE, img.data); } - void - sws_t::load_vram(img_descriptor_t &img, int offset_x, int offset_y, int texture) { + void sws_t::load_vram(img_descriptor_t &img, int offset_x, int offset_y, int texture) { // When only a sub-part of the image must be encoded... const bool copy = offset_x || offset_y || img.sd.width != in_width || img.sd.height != in_height; if (copy) { @@ -934,8 +906,7 @@ namespace egl { loaded_texture = tex[0]; framebuf.copy(0, loaded_texture, offset_x, offset_y, in_width, in_height); - } - else { + } else { loaded_texture = texture; } @@ -985,8 +956,7 @@ namespace egl { } } - int - sws_t::convert(gl::frame_buf_t &fb) { + int sws_t::convert(gl::frame_buf_t &fb) { gl::ctx.BindTexture(GL_TEXTURE_2D, loaded_texture); GLenum attachments[] { @@ -1019,7 +989,6 @@ namespace egl { } } // namespace egl -void -free_frame(AVFrame *frame) { +void free_frame(AVFrame *frame) { av_frame_free(&frame); } diff --git a/src/platform/linux/graphics.h b/src/platform/linux/graphics.h index d7dbcf1b..00e38943 100644 --- a/src/platform/linux/graphics.h +++ b/src/platform/linux/graphics.h @@ -4,12 +4,15 @@ */ #pragma once +// standard includes #include #include +// lib includes #include #include +// local includes #include "misc.h" #include "src/logging.h" #include "src/platform/common.h" @@ -21,35 +24,30 @@ #define gl_drain_errors_helper(x) gl::drain_errors(x) #define gl_drain_errors gl_drain_errors_helper(__FILE__ ":" SUNSHINE_STRINGIFY(__LINE__)) -extern "C" int -close(int __fd); +extern "C" int close(int __fd); // X11 Display extern "C" struct _XDisplay; struct AVFrame; -void -free_frame(AVFrame *frame); +void free_frame(AVFrame *frame); using frame_t = util::safe_ptr; namespace gl { extern GladGLContext ctx; - void - drain_errors(const std::string_view &prefix); + void drain_errors(const std::string_view &prefix); class tex_t: public util::buffer_t { using util::buffer_t::buffer_t; public: tex_t(tex_t &&) = default; - tex_t & - operator=(tex_t &&) = default; + tex_t &operator=(tex_t &&) = default; ~tex_t(); - static tex_t - make(std::size_t count); + static tex_t make(std::size_t count); }; class frame_buf_t: public util::buffer_t { @@ -57,16 +55,13 @@ namespace gl { public: frame_buf_t(frame_buf_t &&) = default; - frame_buf_t & - operator=(frame_buf_t &&) = default; + frame_buf_t &operator=(frame_buf_t &&) = default; ~frame_buf_t(); - static frame_buf_t - make(std::size_t count); + static frame_buf_t make(std::size_t count); - inline void - bind(std::nullptr_t, std::nullptr_t) { + inline void bind(std::nullptr_t, std::nullptr_t) { int x = 0; for (auto fb : (*this)) { ctx.BindFramebuffer(GL_FRAMEBUFFER, fb); @@ -77,9 +72,8 @@ namespace gl { return; } - template - void - bind(It it_begin, It it_end) { + template + void bind(It it_begin, It it_end) { using namespace std::literals; if (std::distance(it_begin, it_end) > size()) { BOOST_LOG(warning) << "To many elements to bind"sv; @@ -100,8 +94,7 @@ namespace gl { /** * Copies a part of the framebuffer to texture */ - void - copy(int id, int texture, int offset_x, int offset_y, int width, int height); + void copy(int id, int texture, int offset_x, int offset_y, int width, int height); }; class shader_t { @@ -112,14 +105,11 @@ namespace gl { }); public: - std::string - err_str(); + std::string err_str(); - static util::Either - compile(const std::string_view &source, GLenum type); + static util::Either compile(const std::string_view &source, GLenum type); - GLuint - handle() const; + GLuint handle() const; private: shader_internal_t _shader; @@ -133,19 +123,14 @@ namespace gl { }); public: - static buffer_t - make(util::buffer_t &&offsets, const char *block, const std::string_view &data); + static buffer_t make(util::buffer_t &&offsets, const char *block, const std::string_view &data); - GLuint - handle() const; + GLuint handle() const; - const char * - block() const; + const char *block() const; - void - update(const std::string_view &view, std::size_t offset = 0); - void - update(std::string_view *members, std::size_t count, std::size_t offset = 0); + void update(const std::string_view &view, std::size_t offset = 0); + void update(std::string_view *members, std::size_t count, std::size_t offset = 0); private: const char *_block; @@ -165,20 +150,15 @@ namespace gl { }); public: - std::string - err_str(); + std::string err_str(); - static util::Either - link(const shader_t &vert, const shader_t &frag); + static util::Either link(const shader_t &vert, const shader_t &frag); - void - bind(const buffer_t &buffer); + void bind(const buffer_t &buffer); - std::optional - uniform(const char *block, std::pair *members, std::size_t count); + std::optional uniform(const char *block, std::pair *members, std::size_t count); - GLuint - handle() const; + GLuint handle() const; private: program_internal_t _program; @@ -195,8 +175,7 @@ namespace gbm { using gbm_t = util::dyn_safe_ptr; - int - init(); + int init(); } // namespace gbm @@ -258,24 +237,23 @@ namespace egl { std::uint32_t offsets[4]; }; - display_t - make_display(std::variant native_display); - std::optional - make_ctx(display_t::pointer display); + display_t make_display(std::variant native_display); + std::optional make_ctx(display_t::pointer display); std::optional - import_source( - display_t::pointer egl_display, - const surface_descriptor_t &xrgb); + import_source( + display_t::pointer egl_display, + const surface_descriptor_t &xrgb + ); - rgb_t - create_blank(platf::img_t &img); + rgb_t create_blank(platf::img_t &img); - std::optional - import_target( + std::optional import_target( display_t::pointer egl_display, std::array &&fds, - const surface_descriptor_t &y, const surface_descriptor_t &uv); + const surface_descriptor_t &y, + const surface_descriptor_t &uv + ); /** * @brief Creates biplanar YUV textures to render into. @@ -284,8 +262,7 @@ namespace egl { * @param format Format of the target frame. * @return The new RGB texture. */ - std::optional - create_target(int width, int height, AVPixelFormat format); + std::optional create_target(int width, int height, AVPixelFormat format); class cursor_t: public platf::img_t { public: @@ -304,8 +281,7 @@ namespace egl { reset(); } - void - reset() { + void reset() { for (auto x = 0; x < 4; ++x) { if (sd.fds[x] >= 0) { close(sd.fds[x]); @@ -323,26 +299,19 @@ namespace egl { class sws_t { public: - static std::optional - make(int in_width, int in_height, int out_width, int out_height, gl::tex_t &&tex); - static std::optional - make(int in_width, int in_height, int out_width, int out_height, AVPixelFormat format); + static std::optional make(int in_width, int in_height, int out_width, int out_height, gl::tex_t &&tex); + static std::optional make(int in_width, int in_height, int out_width, int out_height, AVPixelFormat format); // Convert the loaded image into the first two framebuffers - int - convert(gl::frame_buf_t &fb); + int convert(gl::frame_buf_t &fb); // Make an area of the image black - int - blank(gl::frame_buf_t &fb, int offsetX, int offsetY, int width, int height); + int blank(gl::frame_buf_t &fb, int offsetX, int offsetY, int width, int height); - void - load_ram(platf::img_t &img); - void - load_vram(img_descriptor_t &img, int offset_x, int offset_y, int texture); + void load_ram(platf::img_t &img); + void load_vram(img_descriptor_t &img, int offset_x, int offset_y, int texture); - void - apply_colorspace(const video::sunshine_colorspace_t &colorspace); + void apply_colorspace(const video::sunshine_colorspace_t &colorspace); // The first texture is the monitor image. // The second texture is the cursor image @@ -367,6 +336,5 @@ namespace egl { std::uint64_t serial; }; - bool - fail(); + bool fail(); } // namespace egl diff --git a/src/platform/linux/input/inputtino.cpp b/src/platform/linux/input/inputtino.cpp index 5a3278db..3102fdd9 100644 --- a/src/platform/linux/input/inputtino.cpp +++ b/src/platform/linux/input/inputtino.cpp @@ -2,132 +2,114 @@ * @file src/platform/linux/input/inputtino.cpp * @brief Definitions for the inputtino Linux input handling. */ +// lib includes #include #include -#include "src/config.h" -#include "src/platform/common.h" -#include "src/utility.h" - +// local includes #include "inputtino_common.h" #include "inputtino_gamepad.h" #include "inputtino_keyboard.h" #include "inputtino_mouse.h" #include "inputtino_pen.h" #include "inputtino_touch.h" +#include "src/config.h" +#include "src/platform/common.h" +#include "src/utility.h" using namespace std::literals; namespace platf { - input_t - input() { - return { new input_raw_t() }; + input_t input() { + return {new input_raw_t()}; } - std::unique_ptr - allocate_client_input_context(input_t &input) { + std::unique_ptr allocate_client_input_context(input_t &input) { return std::make_unique(input); } - void - freeInput(void *p) { + void freeInput(void *p) { auto *input = (input_raw_t *) p; delete input; } - void - move_mouse(input_t &input, int deltaX, int deltaY) { + void move_mouse(input_t &input, int deltaX, int deltaY) { auto raw = (input_raw_t *) input.get(); platf::mouse::move(raw, deltaX, deltaY); } - void - abs_mouse(input_t &input, const touch_port_t &touch_port, float x, float y) { + void abs_mouse(input_t &input, const touch_port_t &touch_port, float x, float y) { auto raw = (input_raw_t *) input.get(); platf::mouse::move_abs(raw, touch_port, x, y); } - void - button_mouse(input_t &input, int button, bool release) { + void button_mouse(input_t &input, int button, bool release) { auto raw = (input_raw_t *) input.get(); platf::mouse::button(raw, button, release); } - void - scroll(input_t &input, int high_res_distance) { + void scroll(input_t &input, int high_res_distance) { auto raw = (input_raw_t *) input.get(); platf::mouse::scroll(raw, high_res_distance); } - void - hscroll(input_t &input, int high_res_distance) { + void hscroll(input_t &input, int high_res_distance) { auto raw = (input_raw_t *) input.get(); platf::mouse::hscroll(raw, high_res_distance); } - void - keyboard_update(input_t &input, uint16_t modcode, bool release, uint8_t flags) { + void keyboard_update(input_t &input, uint16_t modcode, bool release, uint8_t flags) { auto raw = (input_raw_t *) input.get(); platf::keyboard::update(raw, modcode, release, flags); } - void - unicode(input_t &input, char *utf8, int size) { + void unicode(input_t &input, char *utf8, int size) { auto raw = (input_raw_t *) input.get(); platf::keyboard::unicode(raw, utf8, size); } - void - touch_update(client_input_t *input, const touch_port_t &touch_port, const touch_input_t &touch) { + void touch_update(client_input_t *input, const touch_port_t &touch_port, const touch_input_t &touch) { auto raw = (client_input_raw_t *) input; platf::touch::update(raw, touch_port, touch); } - void - pen_update(client_input_t *input, const touch_port_t &touch_port, const pen_input_t &pen) { + void pen_update(client_input_t *input, const touch_port_t &touch_port, const pen_input_t &pen) { auto raw = (client_input_raw_t *) input; platf::pen::update(raw, touch_port, pen); } - int - alloc_gamepad(input_t &input, const gamepad_id_t &id, const gamepad_arrival_t &metadata, feedback_queue_t feedback_queue) { + int alloc_gamepad(input_t &input, const gamepad_id_t &id, const gamepad_arrival_t &metadata, feedback_queue_t feedback_queue) { auto raw = (input_raw_t *) input.get(); return platf::gamepad::alloc(raw, id, metadata, feedback_queue); } - void - free_gamepad(input_t &input, int nr) { + void free_gamepad(input_t &input, int nr) { auto raw = (input_raw_t *) input.get(); platf::gamepad::free(raw, nr); } - void - gamepad_update(input_t &input, int nr, const gamepad_state_t &gamepad_state) { + void gamepad_update(input_t &input, int nr, const gamepad_state_t &gamepad_state) { auto raw = (input_raw_t *) input.get(); platf::gamepad::update(raw, nr, gamepad_state); } - void - gamepad_touch(input_t &input, const gamepad_touch_t &touch) { + void gamepad_touch(input_t &input, const gamepad_touch_t &touch) { auto raw = (input_raw_t *) input.get(); platf::gamepad::touch(raw, touch); } - void - gamepad_motion(input_t &input, const gamepad_motion_t &motion) { + void gamepad_motion(input_t &input, const gamepad_motion_t &motion) { auto raw = (input_raw_t *) input.get(); platf::gamepad::motion(raw, motion); } - void - gamepad_battery(input_t &input, const gamepad_battery_t &battery) { + void gamepad_battery(input_t &input, const gamepad_battery_t &battery) { auto raw = (input_raw_t *) input.get(); platf::gamepad::battery(raw, battery); } - platform_caps::caps_t - get_capabilities() { + platform_caps::caps_t get_capabilities() { platform_caps::caps_t caps = 0; // TODO: if has_uinput caps |= platform_caps::pen_touch; @@ -140,14 +122,12 @@ namespace platf { return caps; } - util::point_t - get_mouse_loc(input_t &input) { + util::point_t get_mouse_loc(input_t &input) { auto raw = (input_raw_t *) input.get(); return platf::mouse::get_location(raw); } - std::vector & - supported_gamepads(input_t *input) { + std::vector &supported_gamepads(input_t *input) { return platf::gamepad::supported_gamepads(input); } } // namespace platf diff --git a/src/platform/linux/input/inputtino_common.h b/src/platform/linux/input/inputtino_common.h index 67d19328..855f88a4 100644 --- a/src/platform/linux/input/inputtino_common.h +++ b/src/platform/linux/input/inputtino_common.h @@ -4,10 +4,12 @@ */ #pragma once +// lib includes #include #include #include +// local includes #include "src/config.h" #include "src/logging.h" #include "src/platform/common.h" @@ -94,8 +96,7 @@ namespace platf { inputtino::Result pen; }; - inline float - deg2rad(float degree) { + inline float deg2rad(float degree) { return degree * (M_PI / 180.f); } } // namespace platf diff --git a/src/platform/linux/input/inputtino_gamepad.cpp b/src/platform/linux/input/inputtino_gamepad.cpp index 15fbfcaf..b5ef01dd 100644 --- a/src/platform/linux/input/inputtino_gamepad.cpp +++ b/src/platform/linux/input/inputtino_gamepad.cpp @@ -2,18 +2,19 @@ * @file src/platform/linux/input/inputtino_gamepad.cpp * @brief Definitions for inputtino gamepad input handling. */ +// lib includes #include #include #include +// local includes +#include "inputtino_common.h" +#include "inputtino_gamepad.h" #include "src/config.h" #include "src/logging.h" #include "src/platform/common.h" #include "src/utility.h" -#include "inputtino_common.h" -#include "inputtino_gamepad.h" - using namespace std::literals; namespace platf::gamepad { @@ -25,69 +26,54 @@ namespace platf::gamepad { GAMEPAD_STATUS ///< Helper to indicate the number of status }; - auto - create_xbox_one() { - return inputtino::XboxOneJoypad::create({ .name = "Sunshine X-Box One (virtual) pad", - // https://github.com/torvalds/linux/blob/master/drivers/input/joystick/xpad.c#L147 - .vendor_id = 0x045E, - .product_id = 0x02EA, - .version = 0x0408 }); + auto create_xbox_one() { + return inputtino::XboxOneJoypad::create({.name = "Sunshine X-Box One (virtual) pad", + // https://github.com/torvalds/linux/blob/master/drivers/input/joystick/xpad.c#L147 + .vendor_id = 0x045E, + .product_id = 0x02EA, + .version = 0x0408}); } - auto - create_switch() { - return inputtino::SwitchJoypad::create({ .name = "Sunshine Nintendo (virtual) pad", - // https://github.com/torvalds/linux/blob/master/drivers/hid/hid-ids.h#L981 - .vendor_id = 0x057e, - .product_id = 0x2009, - .version = 0x8111 }); + auto create_switch() { + return inputtino::SwitchJoypad::create({.name = "Sunshine Nintendo (virtual) pad", + // https://github.com/torvalds/linux/blob/master/drivers/hid/hid-ids.h#L981 + .vendor_id = 0x057e, + .product_id = 0x2009, + .version = 0x8111}); } - auto - create_ds5() { - return inputtino::PS5Joypad::create({ .name = "Sunshine DualSense (virtual) pad", - .vendor_id = 0x054C, - .product_id = 0x0CE6, - .version = 0x8111 }); + auto create_ds5() { + return inputtino::PS5Joypad::create({.name = "Sunshine DualSense (virtual) pad", .vendor_id = 0x054C, .product_id = 0x0CE6, .version = 0x8111}); } - int - alloc(input_raw_t *raw, const gamepad_id_t &id, const gamepad_arrival_t &metadata, feedback_queue_t feedback_queue) { + int alloc(input_raw_t *raw, const gamepad_id_t &id, const gamepad_arrival_t &metadata, feedback_queue_t feedback_queue) { ControllerType selectedGamepadType; if (config::input.gamepad == "xone"sv) { BOOST_LOG(info) << "Gamepad " << id.globalIndex << " will be Xbox One controller (manual selection)"sv; selectedGamepadType = XboxOneWired; - } - else if (config::input.gamepad == "ds5"sv) { + } else if (config::input.gamepad == "ds5"sv) { BOOST_LOG(info) << "Gamepad " << id.globalIndex << " will be DualSense 5 controller (manual selection)"sv; selectedGamepadType = DualSenseWired; - } - else if (config::input.gamepad == "switch"sv) { + } else if (config::input.gamepad == "switch"sv) { BOOST_LOG(info) << "Gamepad " << id.globalIndex << " will be Nintendo Pro controller (manual selection)"sv; selectedGamepadType = SwitchProWired; - } - else if (metadata.type == LI_CTYPE_XBOX) { + } else if (metadata.type == LI_CTYPE_XBOX) { BOOST_LOG(info) << "Gamepad " << id.globalIndex << " will be Xbox One controller (auto-selected by client-reported type)"sv; selectedGamepadType = XboxOneWired; - } - else if (metadata.type == LI_CTYPE_PS) { + } else if (metadata.type == LI_CTYPE_PS) { BOOST_LOG(info) << "Gamepad " << id.globalIndex << " will be DualShock 5 controller (auto-selected by client-reported type)"sv; selectedGamepadType = DualSenseWired; - } - else if (metadata.type == LI_CTYPE_NINTENDO) { + } else if (metadata.type == LI_CTYPE_NINTENDO) { BOOST_LOG(info) << "Gamepad " << id.globalIndex << " will be Nintendo Pro controller (auto-selected by client-reported type)"sv; selectedGamepadType = SwitchProWired; - } - else if (config::input.motion_as_ds4 && (metadata.capabilities & (LI_CCAP_ACCEL | LI_CCAP_GYRO))) { + } else if (config::input.motion_as_ds4 && (metadata.capabilities & (LI_CCAP_ACCEL | LI_CCAP_GYRO))) { BOOST_LOG(info) << "Gamepad " << id.globalIndex << " will be DualShock 5 controller (auto-selected by motion sensor presence)"sv; selectedGamepadType = DualSenseWired; - } - else if (config::input.touchpad_as_ds4 && (metadata.capabilities & LI_CCAP_TOUCHPAD)) { + } else if (config::input.touchpad_as_ds4 && (metadata.capabilities & LI_CCAP_TOUCHPAD)) { BOOST_LOG(info) << "Gamepad " << id.globalIndex << " will be DualShock 5 controller (auto-selected by touchpad presence)"sv; selectedGamepadType = DualSenseWired; - } - else { + } else { BOOST_LOG(info) << "Gamepad " << id.globalIndex << " will be Xbox One controller (default)"sv; selectedGamepadType = XboxOneWired; } @@ -102,8 +88,7 @@ namespace platf::gamepad { if (metadata.capabilities & LI_CCAP_RGB_LED) { BOOST_LOG(warning) << "Gamepad " << id.globalIndex << " has an RGB LED, but it is not usable when emulating a joypad different from DS5"sv; } - } - else if (selectedGamepadType == DualSenseWired) { + } else if (selectedGamepadType == DualSenseWired) { if (!(metadata.capabilities & (LI_CCAP_ACCEL | LI_CCAP_GYRO))) { BOOST_LOG(warning) << "Gamepad " << id.globalIndex << " is emulating a DualShock 5 controller, but the client gamepad doesn't have motion sensors active"sv; } @@ -125,73 +110,71 @@ namespace platf::gamepad { }; switch (selectedGamepadType) { - case XboxOneWired: { - auto xOne = create_xbox_one(); - if (xOne) { - (*xOne).set_on_rumble(on_rumble_fn); - gamepad->joypad = std::make_unique(std::move(*xOne)); - raw->gamepads[id.globalIndex] = std::move(gamepad); - return 0; + case XboxOneWired: + { + auto xOne = create_xbox_one(); + if (xOne) { + (*xOne).set_on_rumble(on_rumble_fn); + gamepad->joypad = std::make_unique(std::move(*xOne)); + raw->gamepads[id.globalIndex] = std::move(gamepad); + return 0; + } else { + BOOST_LOG(warning) << "Unable to create virtual Xbox One controller: " << xOne.getErrorMessage(); + return -1; + } } - else { - BOOST_LOG(warning) << "Unable to create virtual Xbox One controller: " << xOne.getErrorMessage(); - return -1; + case SwitchProWired: + { + auto switchPro = create_switch(); + if (switchPro) { + (*switchPro).set_on_rumble(on_rumble_fn); + gamepad->joypad = std::make_unique(std::move(*switchPro)); + raw->gamepads[id.globalIndex] = std::move(gamepad); + return 0; + } else { + BOOST_LOG(warning) << "Unable to create virtual Switch Pro controller: " << switchPro.getErrorMessage(); + return -1; + } } - } - case SwitchProWired: { - auto switchPro = create_switch(); - if (switchPro) { - (*switchPro).set_on_rumble(on_rumble_fn); - gamepad->joypad = std::make_unique(std::move(*switchPro)); - raw->gamepads[id.globalIndex] = std::move(gamepad); - return 0; - } - else { - BOOST_LOG(warning) << "Unable to create virtual Switch Pro controller: " << switchPro.getErrorMessage(); - return -1; - } - } - case DualSenseWired: { - auto ds5 = create_ds5(); - if (ds5) { - (*ds5).set_on_rumble(on_rumble_fn); - (*ds5).set_on_led([feedback_queue, idx = id.clientRelativeIndex, gamepad](int r, int g, int b) { - // Don't resend duplicate LED data - if (gamepad->last_rgb_led.type == platf::gamepad_feedback_e::set_rgb_led && gamepad->last_rgb_led.data.rgb_led.r == r && gamepad->last_rgb_led.data.rgb_led.g == g && gamepad->last_rgb_led.data.rgb_led.b == b) { - return; - } + case DualSenseWired: + { + auto ds5 = create_ds5(); + if (ds5) { + (*ds5).set_on_rumble(on_rumble_fn); + (*ds5).set_on_led([feedback_queue, idx = id.clientRelativeIndex, gamepad](int r, int g, int b) { + // Don't resend duplicate LED data + if (gamepad->last_rgb_led.type == platf::gamepad_feedback_e::set_rgb_led && gamepad->last_rgb_led.data.rgb_led.r == r && gamepad->last_rgb_led.data.rgb_led.g == g && gamepad->last_rgb_led.data.rgb_led.b == b) { + return; + } - auto msg = gamepad_feedback_msg_t::make_rgb_led(idx, r, g, b); - feedback_queue->raise(msg); - gamepad->last_rgb_led = msg; - }); + auto msg = gamepad_feedback_msg_t::make_rgb_led(idx, r, g, b); + feedback_queue->raise(msg); + gamepad->last_rgb_led = msg; + }); - // Activate the motion sensors - feedback_queue->raise(gamepad_feedback_msg_t::make_motion_event_state(id.clientRelativeIndex, LI_MOTION_TYPE_ACCEL, 100)); - feedback_queue->raise(gamepad_feedback_msg_t::make_motion_event_state(id.clientRelativeIndex, LI_MOTION_TYPE_GYRO, 100)); + // Activate the motion sensors + feedback_queue->raise(gamepad_feedback_msg_t::make_motion_event_state(id.clientRelativeIndex, LI_MOTION_TYPE_ACCEL, 100)); + feedback_queue->raise(gamepad_feedback_msg_t::make_motion_event_state(id.clientRelativeIndex, LI_MOTION_TYPE_GYRO, 100)); - gamepad->joypad = std::make_unique(std::move(*ds5)); - raw->gamepads[id.globalIndex] = std::move(gamepad); - return 0; + gamepad->joypad = std::make_unique(std::move(*ds5)); + raw->gamepads[id.globalIndex] = std::move(gamepad); + return 0; + } else { + BOOST_LOG(warning) << "Unable to create virtual DualShock 5 controller: " << ds5.getErrorMessage(); + return -1; + } } - else { - BOOST_LOG(warning) << "Unable to create virtual DualShock 5 controller: " << ds5.getErrorMessage(); - return -1; - } - } } return -1; } - void - free(input_raw_t *raw, int nr) { + void free(input_raw_t *raw, int nr) { // This will call the destructor which in turn will stop the background threads for rumble and LED (and ultimately remove the joypad device) raw->gamepads[nr]->joypad.reset(); raw->gamepads[nr].reset(); } - void - update(input_raw_t *raw, int nr, const gamepad_state_t &gamepad_state) { + void update(input_raw_t *raw, int nr, const gamepad_state_t &gamepad_state) { auto gamepad = raw->gamepads[nr]; if (!gamepad) { return; @@ -203,11 +186,10 @@ namespace platf::gamepad { gc.set_stick(inputtino::Joypad::RS, gamepad_state.rsX, gamepad_state.rsY); gc.set_triggers(gamepad_state.lt, gamepad_state.rt); }, - *gamepad->joypad); + *gamepad->joypad); } - void - touch(input_raw_t *raw, const gamepad_touch_t &touch) { + void touch(input_raw_t *raw, const gamepad_touch_t &touch) { auto gamepad = raw->gamepads[touch.id.globalIndex]; if (!gamepad) { return; @@ -216,15 +198,13 @@ namespace platf::gamepad { if (std::holds_alternative(*gamepad->joypad)) { if (touch.pressure > 0.5) { std::get(*gamepad->joypad).place_finger(touch.pointerId, touch.x * inputtino::PS5Joypad::touchpad_width, touch.y * inputtino::PS5Joypad::touchpad_height); - } - else { + } else { std::get(*gamepad->joypad).release_finger(touch.pointerId); } } } - void - motion(input_raw_t *raw, const gamepad_motion_t &motion) { + void motion(input_raw_t *raw, const gamepad_motion_t &motion) { auto gamepad = raw->gamepads[motion.id.globalIndex]; if (!gamepad) { return; @@ -242,8 +222,7 @@ namespace platf::gamepad { } } - void - battery(input_raw_t *raw, const gamepad_battery_t &battery) { + void battery(input_raw_t *raw, const gamepad_battery_t &battery) { auto gamepad = raw->gamepads[battery.id.globalIndex]; if (!gamepad) { return; @@ -272,14 +251,13 @@ namespace platf::gamepad { } } - std::vector & - supported_gamepads(input_t *input) { + std::vector &supported_gamepads(input_t *input) { if (!input) { static std::vector gps { - supported_gamepad_t { "auto", true, "" }, - supported_gamepad_t { "xone", false, "" }, - supported_gamepad_t { "ds5", false, "" }, - supported_gamepad_t { "switch", false, "" }, + supported_gamepad_t {"auto", true, ""}, + supported_gamepad_t {"xone", false, ""}, + supported_gamepad_t {"ds5", false, ""}, + supported_gamepad_t {"switch", false, ""}, }; return gps; @@ -290,10 +268,10 @@ namespace platf::gamepad { auto xOne = create_xbox_one(); static std::vector gps { - supported_gamepad_t { "auto", true, "" }, - supported_gamepad_t { "xone", static_cast(xOne), !xOne ? xOne.getErrorMessage() : "" }, - supported_gamepad_t { "ds5", static_cast(ds5), !ds5 ? ds5.getErrorMessage() : "" }, - supported_gamepad_t { "switch", static_cast(switchPro), !switchPro ? switchPro.getErrorMessage() : "" }, + supported_gamepad_t {"auto", true, ""}, + supported_gamepad_t {"xone", static_cast(xOne), !xOne ? xOne.getErrorMessage() : ""}, + supported_gamepad_t {"ds5", static_cast(ds5), !ds5 ? ds5.getErrorMessage() : ""}, + supported_gamepad_t {"switch", static_cast(switchPro), !switchPro ? switchPro.getErrorMessage() : ""}, }; for (auto &[name, is_enabled, reason_disabled] : gps) { diff --git a/src/platform/linux/input/inputtino_gamepad.h b/src/platform/linux/input/inputtino_gamepad.h index 481b8091..8d26c9e1 100644 --- a/src/platform/linux/input/inputtino_gamepad.h +++ b/src/platform/linux/input/inputtino_gamepad.h @@ -4,13 +4,14 @@ */ #pragma once +// lib includes #include #include #include -#include "src/platform/common.h" - +// local includes #include "inputtino_common.h" +#include "src/platform/common.h" using namespace std::literals; @@ -22,24 +23,17 @@ namespace platf::gamepad { SwitchProWired ///< Switch Pro Wired Controller }; - int - alloc(input_raw_t *raw, const gamepad_id_t &id, const gamepad_arrival_t &metadata, feedback_queue_t feedback_queue); + int alloc(input_raw_t *raw, const gamepad_id_t &id, const gamepad_arrival_t &metadata, feedback_queue_t feedback_queue); - void - free(input_raw_t *raw, int nr); + void free(input_raw_t *raw, int nr); - void - update(input_raw_t *raw, int nr, const gamepad_state_t &gamepad_state); + void update(input_raw_t *raw, int nr, const gamepad_state_t &gamepad_state); - void - touch(input_raw_t *raw, const gamepad_touch_t &touch); + void touch(input_raw_t *raw, const gamepad_touch_t &touch); - void - motion(input_raw_t *raw, const gamepad_motion_t &motion); + void motion(input_raw_t *raw, const gamepad_motion_t &motion); - void - battery(input_raw_t *raw, const gamepad_battery_t &battery); + void battery(input_raw_t *raw, const gamepad_battery_t &battery); - std::vector & - supported_gamepads(input_t *input); + std::vector &supported_gamepads(input_t *input); } // namespace platf::gamepad diff --git a/src/platform/linux/input/inputtino_keyboard.cpp b/src/platform/linux/input/inputtino_keyboard.cpp index ef5c330b..e384b312 100644 --- a/src/platform/linux/input/inputtino_keyboard.cpp +++ b/src/platform/linux/input/inputtino_keyboard.cpp @@ -2,18 +2,19 @@ * @file src/platform/linux/input/inputtino_keyboard.cpp * @brief Definitions for inputtino keyboard input handling. */ +// lib includes #include #include #include +// local includes +#include "inputtino_common.h" +#include "inputtino_keyboard.h" #include "src/config.h" #include "src/logging.h" #include "src/platform/common.h" #include "src/utility.h" -#include "inputtino_common.h" -#include "inputtino_keyboard.h" - using namespace std::literals; namespace platf::keyboard { @@ -25,8 +26,7 @@ namespace platf::keyboard { * * adapted from: https://stackoverflow.com/a/7639754 */ - std::string - to_hex(const std::basic_string &str) { + std::string to_hex(const std::basic_string &str) { std::stringstream ss; ss << std::hex << std::setfill('0'); for (const auto &ch : str) { @@ -42,48 +42,123 @@ namespace platf::keyboard { * A map of linux scan code -> Moonlight keyboard code */ static const std::map key_mappings = { - { KEY_BACKSPACE, 0x08 }, { KEY_TAB, 0x09 }, { KEY_ENTER, 0x0D }, { KEY_LEFTSHIFT, 0x10 }, - { KEY_LEFTCTRL, 0x11 }, { KEY_CAPSLOCK, 0x14 }, { KEY_ESC, 0x1B }, { KEY_SPACE, 0x20 }, - { KEY_PAGEUP, 0x21 }, { KEY_PAGEDOWN, 0x22 }, { KEY_END, 0x23 }, { KEY_HOME, 0x24 }, - { KEY_LEFT, 0x25 }, { KEY_UP, 0x26 }, { KEY_RIGHT, 0x27 }, { KEY_DOWN, 0x28 }, - { KEY_SYSRQ, 0x2C }, { KEY_INSERT, 0x2D }, { KEY_DELETE, 0x2E }, { KEY_0, 0x30 }, - { KEY_1, 0x31 }, { KEY_2, 0x32 }, { KEY_3, 0x33 }, { KEY_4, 0x34 }, - { KEY_5, 0x35 }, { KEY_6, 0x36 }, { KEY_7, 0x37 }, { KEY_8, 0x38 }, - { KEY_9, 0x39 }, { KEY_A, 0x41 }, { KEY_B, 0x42 }, { KEY_C, 0x43 }, - { KEY_D, 0x44 }, { KEY_E, 0x45 }, { KEY_F, 0x46 }, { KEY_G, 0x47 }, - { KEY_H, 0x48 }, { KEY_I, 0x49 }, { KEY_J, 0x4A }, { KEY_K, 0x4B }, - { KEY_L, 0x4C }, { KEY_M, 0x4D }, { KEY_N, 0x4E }, { KEY_O, 0x4F }, - { KEY_P, 0x50 }, { KEY_Q, 0x51 }, { KEY_R, 0x52 }, { KEY_S, 0x53 }, - { KEY_T, 0x54 }, { KEY_U, 0x55 }, { KEY_V, 0x56 }, { KEY_W, 0x57 }, - { KEY_X, 0x58 }, { KEY_Y, 0x59 }, { KEY_Z, 0x5A }, { KEY_LEFTMETA, 0x5B }, - { KEY_RIGHTMETA, 0x5C }, { KEY_KP0, 0x60 }, { KEY_KP1, 0x61 }, { KEY_KP2, 0x62 }, - { KEY_KP3, 0x63 }, { KEY_KP4, 0x64 }, { KEY_KP5, 0x65 }, { KEY_KP6, 0x66 }, - { KEY_KP7, 0x67 }, { KEY_KP8, 0x68 }, { KEY_KP9, 0x69 }, { KEY_KPASTERISK, 0x6A }, - { KEY_KPPLUS, 0x6B }, { KEY_KPMINUS, 0x6D }, { KEY_KPDOT, 0x6E }, { KEY_KPSLASH, 0x6F }, - { KEY_F1, 0x70 }, { KEY_F2, 0x71 }, { KEY_F3, 0x72 }, { KEY_F4, 0x73 }, - { KEY_F5, 0x74 }, { KEY_F6, 0x75 }, { KEY_F7, 0x76 }, { KEY_F8, 0x77 }, - { KEY_F9, 0x78 }, { KEY_F10, 0x79 }, { KEY_F11, 0x7A }, { KEY_F12, 0x7B }, - { KEY_NUMLOCK, 0x90 }, { KEY_SCROLLLOCK, 0x91 }, { KEY_LEFTSHIFT, 0xA0 }, { KEY_RIGHTSHIFT, 0xA1 }, - { KEY_LEFTCTRL, 0xA2 }, { KEY_RIGHTCTRL, 0xA3 }, { KEY_LEFTALT, 0xA4 }, { KEY_RIGHTALT, 0xA5 }, - { KEY_SEMICOLON, 0xBA }, { KEY_EQUAL, 0xBB }, { KEY_COMMA, 0xBC }, { KEY_MINUS, 0xBD }, - { KEY_DOT, 0xBE }, { KEY_SLASH, 0xBF }, { KEY_GRAVE, 0xC0 }, { KEY_LEFTBRACE, 0xDB }, - { KEY_BACKSLASH, 0xDC }, { KEY_RIGHTBRACE, 0xDD }, { KEY_APOSTROPHE, 0xDE }, { KEY_102ND, 0xE2 } + {KEY_BACKSPACE, 0x08}, + {KEY_TAB, 0x09}, + {KEY_ENTER, 0x0D}, + {KEY_LEFTSHIFT, 0x10}, + {KEY_LEFTCTRL, 0x11}, + {KEY_CAPSLOCK, 0x14}, + {KEY_ESC, 0x1B}, + {KEY_SPACE, 0x20}, + {KEY_PAGEUP, 0x21}, + {KEY_PAGEDOWN, 0x22}, + {KEY_END, 0x23}, + {KEY_HOME, 0x24}, + {KEY_LEFT, 0x25}, + {KEY_UP, 0x26}, + {KEY_RIGHT, 0x27}, + {KEY_DOWN, 0x28}, + {KEY_SYSRQ, 0x2C}, + {KEY_INSERT, 0x2D}, + {KEY_DELETE, 0x2E}, + {KEY_0, 0x30}, + {KEY_1, 0x31}, + {KEY_2, 0x32}, + {KEY_3, 0x33}, + {KEY_4, 0x34}, + {KEY_5, 0x35}, + {KEY_6, 0x36}, + {KEY_7, 0x37}, + {KEY_8, 0x38}, + {KEY_9, 0x39}, + {KEY_A, 0x41}, + {KEY_B, 0x42}, + {KEY_C, 0x43}, + {KEY_D, 0x44}, + {KEY_E, 0x45}, + {KEY_F, 0x46}, + {KEY_G, 0x47}, + {KEY_H, 0x48}, + {KEY_I, 0x49}, + {KEY_J, 0x4A}, + {KEY_K, 0x4B}, + {KEY_L, 0x4C}, + {KEY_M, 0x4D}, + {KEY_N, 0x4E}, + {KEY_O, 0x4F}, + {KEY_P, 0x50}, + {KEY_Q, 0x51}, + {KEY_R, 0x52}, + {KEY_S, 0x53}, + {KEY_T, 0x54}, + {KEY_U, 0x55}, + {KEY_V, 0x56}, + {KEY_W, 0x57}, + {KEY_X, 0x58}, + {KEY_Y, 0x59}, + {KEY_Z, 0x5A}, + {KEY_LEFTMETA, 0x5B}, + {KEY_RIGHTMETA, 0x5C}, + {KEY_KP0, 0x60}, + {KEY_KP1, 0x61}, + {KEY_KP2, 0x62}, + {KEY_KP3, 0x63}, + {KEY_KP4, 0x64}, + {KEY_KP5, 0x65}, + {KEY_KP6, 0x66}, + {KEY_KP7, 0x67}, + {KEY_KP8, 0x68}, + {KEY_KP9, 0x69}, + {KEY_KPASTERISK, 0x6A}, + {KEY_KPPLUS, 0x6B}, + {KEY_KPMINUS, 0x6D}, + {KEY_KPDOT, 0x6E}, + {KEY_KPSLASH, 0x6F}, + {KEY_F1, 0x70}, + {KEY_F2, 0x71}, + {KEY_F3, 0x72}, + {KEY_F4, 0x73}, + {KEY_F5, 0x74}, + {KEY_F6, 0x75}, + {KEY_F7, 0x76}, + {KEY_F8, 0x77}, + {KEY_F9, 0x78}, + {KEY_F10, 0x79}, + {KEY_F11, 0x7A}, + {KEY_F12, 0x7B}, + {KEY_NUMLOCK, 0x90}, + {KEY_SCROLLLOCK, 0x91}, + {KEY_LEFTSHIFT, 0xA0}, + {KEY_RIGHTSHIFT, 0xA1}, + {KEY_LEFTCTRL, 0xA2}, + {KEY_RIGHTCTRL, 0xA3}, + {KEY_LEFTALT, 0xA4}, + {KEY_RIGHTALT, 0xA5}, + {KEY_SEMICOLON, 0xBA}, + {KEY_EQUAL, 0xBB}, + {KEY_COMMA, 0xBC}, + {KEY_MINUS, 0xBD}, + {KEY_DOT, 0xBE}, + {KEY_SLASH, 0xBF}, + {KEY_GRAVE, 0xC0}, + {KEY_LEFTBRACE, 0xDB}, + {KEY_BACKSLASH, 0xDC}, + {KEY_RIGHTBRACE, 0xDD}, + {KEY_APOSTROPHE, 0xDE}, + {KEY_102ND, 0xE2} }; - void - update(input_raw_t *raw, uint16_t modcode, bool release, uint8_t flags) { + void update(input_raw_t *raw, uint16_t modcode, bool release, uint8_t flags) { if (raw->keyboard) { if (release) { (*raw->keyboard).release(modcode); - } - else { + } else { (*raw->keyboard).press(modcode); } } } - void - unicode(input_raw_t *raw, char *utf8, int size) { + void unicode(input_raw_t *raw, char *utf8, int size) { if (raw->keyboard) { /* Reading input text as UTF-8 */ auto utf8_str = boost::locale::conv::to_utf(utf8, utf8 + size, "UTF-8"); @@ -106,8 +181,7 @@ namespace platf::keyboard { auto wincode = key_mappings.find(keycode); if (keycode == -1 || wincode == key_mappings.end()) { BOOST_LOG(warning) << "Unicode, unable to find keycode for: "sv << ch; - } - else { + } else { (*raw->keyboard).press(wincode->second); (*raw->keyboard).release(wincode->second); } diff --git a/src/platform/linux/input/inputtino_keyboard.h b/src/platform/linux/input/inputtino_keyboard.h index 22be4666..6405b625 100644 --- a/src/platform/linux/input/inputtino_keyboard.h +++ b/src/platform/linux/input/inputtino_keyboard.h @@ -4,18 +4,18 @@ */ #pragma once +// lib includes #include #include #include +// local includes #include "inputtino_common.h" using namespace std::literals; namespace platf::keyboard { - void - update(input_raw_t *raw, uint16_t modcode, bool release, uint8_t flags); + void update(input_raw_t *raw, uint16_t modcode, bool release, uint8_t flags); - void - unicode(input_raw_t *raw, char *utf8, int size); + void unicode(input_raw_t *raw, char *utf8, int size); } // namespace platf::keyboard diff --git a/src/platform/linux/input/inputtino_mouse.cpp b/src/platform/linux/input/inputtino_mouse.cpp index 5e26e126..f8d822de 100644 --- a/src/platform/linux/input/inputtino_mouse.cpp +++ b/src/platform/linux/input/inputtino_mouse.cpp @@ -2,38 +2,36 @@ * @file src/platform/linux/input/inputtino_mouse.cpp * @brief Definitions for inputtino mouse input handling. */ +// lib includes #include #include #include +// local includes +#include "inputtino_common.h" +#include "inputtino_mouse.h" #include "src/config.h" #include "src/logging.h" #include "src/platform/common.h" #include "src/utility.h" -#include "inputtino_common.h" -#include "inputtino_mouse.h" - using namespace std::literals; namespace platf::mouse { - void - move(input_raw_t *raw, int deltaX, int deltaY) { + void move(input_raw_t *raw, int deltaX, int deltaY) { if (raw->mouse) { (*raw->mouse).move(deltaX, deltaY); } } - void - move_abs(input_raw_t *raw, const touch_port_t &touch_port, float x, float y) { + void move_abs(input_raw_t *raw, const touch_port_t &touch_port, float x, float y) { if (raw->mouse) { (*raw->mouse).move_abs(x, y, touch_port.width, touch_port.height); } } - void - button(input_raw_t *raw, int button, bool release) { + void button(input_raw_t *raw, int button, bool release) { if (raw->mouse) { inputtino::Mouse::MOUSE_BUTTON btn_type; switch (button) { @@ -58,35 +56,31 @@ namespace platf::mouse { } if (release) { (*raw->mouse).release(btn_type); - } - else { + } else { (*raw->mouse).press(btn_type); } } } - void - scroll(input_raw_t *raw, int high_res_distance) { + void scroll(input_raw_t *raw, int high_res_distance) { if (raw->mouse) { (*raw->mouse).vertical_scroll(high_res_distance); } } - void - hscroll(input_raw_t *raw, int high_res_distance) { + void hscroll(input_raw_t *raw, int high_res_distance) { if (raw->mouse) { (*raw->mouse).horizontal_scroll(high_res_distance); } } - util::point_t - get_location(input_raw_t *raw) { + util::point_t get_location(input_raw_t *raw) { if (raw->mouse) { // TODO: decide what to do after https://github.com/games-on-whales/inputtino/issues/6 is resolved. // TODO: auto x = (*raw->mouse).get_absolute_x(); // TODO: auto y = (*raw->mouse).get_absolute_y(); - return { 0, 0 }; + return {0, 0}; } - return { 0, 0 }; + return {0, 0}; } } // namespace platf::mouse diff --git a/src/platform/linux/input/inputtino_mouse.h b/src/platform/linux/input/inputtino_mouse.h index 06c55e87..67eaf97a 100644 --- a/src/platform/linux/input/inputtino_mouse.h +++ b/src/platform/linux/input/inputtino_mouse.h @@ -3,33 +3,27 @@ * @brief Declarations for inputtino mouse input handling. */ #pragma once - +// lib includes #include #include #include -#include "src/platform/common.h" - +// local includes #include "inputtino_common.h" +#include "src/platform/common.h" using namespace std::literals; namespace platf::mouse { - void - move(input_raw_t *raw, int deltaX, int deltaY); + void move(input_raw_t *raw, int deltaX, int deltaY); - void - move_abs(input_raw_t *raw, const touch_port_t &touch_port, float x, float y); + void move_abs(input_raw_t *raw, const touch_port_t &touch_port, float x, float y); - void - button(input_raw_t *raw, int button, bool release); + void button(input_raw_t *raw, int button, bool release); - void - scroll(input_raw_t *raw, int high_res_distance); + void scroll(input_raw_t *raw, int high_res_distance); - void - hscroll(input_raw_t *raw, int high_res_distance); + void hscroll(input_raw_t *raw, int high_res_distance); - util::point_t - get_location(input_raw_t *raw); + util::point_t get_location(input_raw_t *raw); } // namespace platf::mouse diff --git a/src/platform/linux/input/inputtino_pen.cpp b/src/platform/linux/input/inputtino_pen.cpp index 1e171382..ed10f407 100644 --- a/src/platform/linux/input/inputtino_pen.cpp +++ b/src/platform/linux/input/inputtino_pen.cpp @@ -2,23 +2,23 @@ * @file src/platform/linux/input/inputtino_pen.cpp * @brief Definitions for inputtino pen input handling. */ +// lib includes #include #include #include +// local includes +#include "inputtino_common.h" +#include "inputtino_pen.h" #include "src/config.h" #include "src/logging.h" #include "src/platform/common.h" #include "src/utility.h" -#include "inputtino_common.h" -#include "inputtino_pen.h" - using namespace std::literals; namespace platf::pen { - void - update(client_input_raw_t *raw, const touch_port_t &touch_port, const pen_input_t &pen) { + void update(client_input_raw_t *raw, const touch_port_t &touch_port, const pen_input_t &pen) { if (raw->pen) { // First set the buttons (*raw->pen).set_btn(inputtino::PenTablet::PRIMARY, pen.penButtons & LI_PEN_BUTTON_PRIMARY); @@ -63,13 +63,7 @@ namespace platf::pen { bool is_touching = pen.eventType == LI_TOUCH_EVENT_DOWN || pen.eventType == LI_TOUCH_EVENT_MOVE; - (*raw->pen).place_tool(tool, - pen.x, - pen.y, - is_touching ? pen.pressureOrDistance : -1, - is_touching ? -1 : pen.pressureOrDistance, - tilt_x, - tilt_y); + (*raw->pen).place_tool(tool, pen.x, pen.y, is_touching ? pen.pressureOrDistance : -1, is_touching ? -1 : pen.pressureOrDistance, tilt_x, tilt_y); } } } // namespace platf::pen diff --git a/src/platform/linux/input/inputtino_pen.h b/src/platform/linux/input/inputtino_pen.h index 69bb1773..667f4ad8 100644 --- a/src/platform/linux/input/inputtino_pen.h +++ b/src/platform/linux/input/inputtino_pen.h @@ -4,17 +4,17 @@ */ #pragma once +// lib includes #include #include #include -#include "src/platform/common.h" - +// local includes #include "inputtino_common.h" +#include "src/platform/common.h" using namespace std::literals; namespace platf::pen { - void - update(client_input_raw_t *raw, const touch_port_t &touch_port, const pen_input_t &pen); + void update(client_input_raw_t *raw, const touch_port_t &touch_port, const pen_input_t &pen); } diff --git a/src/platform/linux/input/inputtino_touch.cpp b/src/platform/linux/input/inputtino_touch.cpp index 5a1f4c5d..c7dce604 100644 --- a/src/platform/linux/input/inputtino_touch.cpp +++ b/src/platform/linux/input/inputtino_touch.cpp @@ -2,52 +2,53 @@ * @file src/platform/linux/input/inputtino_touch.cpp * @brief Definitions for inputtino touch input handling. */ +// lib includes #include #include #include +// local includes +#include "inputtino_common.h" +#include "inputtino_touch.h" #include "src/config.h" #include "src/logging.h" #include "src/platform/common.h" #include "src/utility.h" -#include "inputtino_common.h" -#include "inputtino_touch.h" - using namespace std::literals; namespace platf::touch { - void - update(client_input_raw_t *raw, const touch_port_t &touch_port, const touch_input_t &touch) { + void update(client_input_raw_t *raw, const touch_port_t &touch_port, const touch_input_t &touch) { if (raw->touch) { switch (touch.eventType) { case LI_TOUCH_EVENT_HOVER: case LI_TOUCH_EVENT_DOWN: - case LI_TOUCH_EVENT_MOVE: { - // Convert our 0..360 range to -90..90 relative to Y axis - int adjusted_angle = touch.rotation; + case LI_TOUCH_EVENT_MOVE: + { + // Convert our 0..360 range to -90..90 relative to Y axis + int adjusted_angle = touch.rotation; - if (adjusted_angle > 90 && adjusted_angle < 270) { - // Lower hemisphere - adjusted_angle = 180 - adjusted_angle; - } + if (adjusted_angle > 90 && adjusted_angle < 270) { + // Lower hemisphere + adjusted_angle = 180 - adjusted_angle; + } - // Wrap the value if it's out of range - if (adjusted_angle > 90) { - adjusted_angle -= 360; + // Wrap the value if it's out of range + if (adjusted_angle > 90) { + adjusted_angle -= 360; + } else if (adjusted_angle < -90) { + adjusted_angle += 360; + } + (*raw->touch).place_finger(touch.pointerId, touch.x, touch.y, touch.pressureOrDistance, adjusted_angle); + break; } - else if (adjusted_angle < -90) { - adjusted_angle += 360; - } - (*raw->touch).place_finger(touch.pointerId, touch.x, touch.y, touch.pressureOrDistance, adjusted_angle); - break; - } case LI_TOUCH_EVENT_CANCEL: case LI_TOUCH_EVENT_UP: - case LI_TOUCH_EVENT_HOVER_LEAVE: { - (*raw->touch).release_finger(touch.pointerId); - break; - } + case LI_TOUCH_EVENT_HOVER_LEAVE: + { + (*raw->touch).release_finger(touch.pointerId); + break; + } // TODO: LI_TOUCH_EVENT_CANCEL_ALL } } diff --git a/src/platform/linux/input/inputtino_touch.h b/src/platform/linux/input/inputtino_touch.h index 250ba1ec..5e1d6581 100644 --- a/src/platform/linux/input/inputtino_touch.h +++ b/src/platform/linux/input/inputtino_touch.h @@ -4,17 +4,17 @@ */ #pragma once +// lib includes #include #include #include -#include "src/platform/common.h" - +// local includes #include "inputtino_common.h" +#include "src/platform/common.h" using namespace std::literals; namespace platf::touch { - void - update(client_input_raw_t *raw, const touch_port_t &touch_port, const touch_input_t &touch); + void update(client_input_raw_t *raw, const touch_port_t &touch_port, const touch_input_t &touch); } diff --git a/src/platform/linux/kmsgrab.cpp b/src/platform/linux/kmsgrab.cpp index be5f8a0c..3db74899 100644 --- a/src/platform/linux/kmsgrab.cpp +++ b/src/platform/linux/kmsgrab.cpp @@ -2,28 +2,30 @@ * @file src/platform/linux/kmsgrab.cpp * @brief Definitions for KMS screen capture. */ -#include +// standard includes #include #include +#include +#include +#include + +// platform includes +#include #include #include #include -#include #include #include -#include -#include - +// local includes +#include "cuda.h" +#include "graphics.h" #include "src/config.h" #include "src/logging.h" #include "src/platform/common.h" #include "src/round_robin.h" #include "src/utility.h" #include "src/video.h" - -#include "cuda.h" -#include "graphics.h" #include "vaapi.h" #include "wayland.h" @@ -59,7 +61,10 @@ namespace platf { class wrapper_fb { public: wrapper_fb(drmModeFB *fb): - fb { fb }, fb_id { fb->fb_id }, width { fb->width }, height { fb->height } { + fb {fb}, + fb_id {fb->fb_id}, + width {fb->width}, + height {fb->height} { pixel_format = DRM_FORMAT_XRGB8888; modifier = DRM_FORMAT_MOD_INVALID; std::fill_n(handles, 4, 0); @@ -70,7 +75,10 @@ namespace platf { } wrapper_fb(drmModeFB2 *fb2): - fb2 { fb2 }, fb_id { fb2->fb_id }, width { fb2->width }, height { fb2->height } { + fb2 {fb2}, + fb_id {fb2->fb_id}, + width {fb2->width}, + height {fb2->height} { pixel_format = fb2->pixel_format; modifier = (fb2->flags & DRM_MODE_FB_MODIFIERS) ? fb2->modifier : DRM_FORMAT_MOD_INVALID; @@ -82,8 +90,7 @@ namespace platf { ~wrapper_fb() { if (fb) { drmModeFreeFB(fb); - } - else if (fb2) { + } else if (fb2) { drmModeFreeFB2(fb2); } } @@ -116,8 +123,7 @@ namespace platf { static int env_width; static int env_height; - std::string_view - plane_type(std::uint64_t val) { + std::string_view plane_type(std::uint64_t val) { switch (val) { case DRM_PLANE_TYPE_OVERLAY: return "DRM_PLANE_TYPE_OVERLAY"sv; @@ -165,10 +171,10 @@ namespace platf { static std::vector card_descriptors; - static std::uint32_t - from_view(const std::string_view &string) { + static std::uint32_t from_view(const std::string_view &string) { #define _CONVERT(x, y) \ - if (string == x) return DRM_MODE_CONNECTOR_##y + if (string == x) \ + return DRM_MODE_CONNECTOR_##y // This list was created from the following sources: // https://gitlab.freedesktop.org/mesa/drm/-/blob/main/xf86drmMode.c (drmModeGetConnectorTypeName) @@ -212,7 +218,7 @@ namespace platf { // value appended to the string. Let's try to read it. if (string.find("Unknown"sv) == 0) { std::uint32_t type; - std::string null_terminated_string { string }; + std::string null_terminated_string {string}; if (std::sscanf(null_terminated_string.c_str(), "Unknown%u", &type) == 1) { return type; } @@ -225,15 +231,19 @@ namespace platf { class plane_it_t: public round_robin_util::it_wrap_t { public: plane_it_t(int fd, std::uint32_t *plane_p, std::uint32_t *end): - fd { fd }, plane_p { plane_p }, end { end } { + fd {fd}, + plane_p {plane_p}, + end {end} { load_next_valid_plane(); } plane_it_t(int fd, std::uint32_t *end): - fd { fd }, plane_p { end }, end { end } {} + fd {fd}, + plane_p {end}, + end {end} { + } - void - load_next_valid_plane() { + void load_next_valid_plane() { this->plane.reset(); for (; plane_p != end; ++plane_p) { @@ -248,19 +258,16 @@ namespace platf { } } - void - inc() { + void inc() { ++plane_p; load_next_valid_plane(); } - bool - eq(const plane_it_t &other) const { + bool eq(const plane_it_t &other) const { return plane_p == other.plane_p; } - plane_t::pointer - get() { + plane_t::pointer get() { return plane.get(); } @@ -289,8 +296,7 @@ namespace platf { public: using connector_interal_t = util::safe_ptr; - int - init(const char *path) { + int init(const char *path) { cap_sys_admin admin; fd.el = open(path, O_RDWR); @@ -299,7 +305,7 @@ namespace platf { return -1; } - version_t ver { drmGetVersion(fd.el) }; + version_t ver {drmGetVersion(fd.el)}; BOOST_LOG(info) << path << " -> "sv << ((ver && ver->name) ? ver->name : "UNKNOWN"); // Open the render node for this card to share with libva. @@ -313,8 +319,7 @@ namespace platf { render_fd.el = dup(fd.el); } free(rendernode_path); - } - else { + } else { BOOST_LOG(warning) << "No render device name for: "sv << path; render_fd.el = dup(fd.el); } @@ -346,8 +351,7 @@ namespace platf { return 0; } - fb_t - fb(plane_t::pointer plane) { + fb_t fb(plane_t::pointer plane) { cap_sys_admin admin; auto fb2 = drmModeGetFB2(fd.el, plane->fb_id); @@ -363,36 +367,30 @@ namespace platf { return nullptr; } - crtc_t - crtc(std::uint32_t id) { + crtc_t crtc(std::uint32_t id) { return drmModeGetCrtc(fd.el, id); } - encoder_t - encoder(std::uint32_t id) { + encoder_t encoder(std::uint32_t id) { return drmModeGetEncoder(fd.el, id); } - res_t - res() { + res_t res() { return drmModeGetResources(fd.el); } - bool - is_nvidia() { - version_t ver { drmGetVersion(fd.el) }; + bool is_nvidia() { + version_t ver {drmGetVersion(fd.el)}; return ver && ver->name && strncmp(ver->name, "nvidia-drm", 10) == 0; } - bool - is_cursor(std::uint32_t plane_id) { + bool is_cursor(std::uint32_t plane_id) { auto props = plane_props(plane_id); for (auto &[prop, val] : props) { if (prop->name == "type"sv) { if (val == DRM_PLANE_TYPE_CURSOR) { return true; - } - else { + } else { return false; } } @@ -401,8 +399,7 @@ namespace platf { return false; } - std::optional - prop_value_by_name(const std::vector> &props, std::string_view name) { + std::optional prop_value_by_name(const std::vector> &props, std::string_view name) { for (auto &[prop, val] : props) { if (prop->name == name) { return val; @@ -411,8 +408,7 @@ namespace platf { return std::nullopt; } - std::uint32_t - get_panel_orientation(std::uint32_t plane_id) { + std::uint32_t get_panel_orientation(std::uint32_t plane_id) { auto props = plane_props(plane_id); auto value = prop_value_by_name(props, "rotation"sv); if (value) { @@ -423,8 +419,7 @@ namespace platf { return DRM_MODE_ROTATE_0; } - int - get_crtc_index_by_id(std::uint32_t crtc_id) { + int get_crtc_index_by_id(std::uint32_t crtc_id) { auto resources = res(); for (int i = 0; i < resources->count_crtcs; i++) { if (resources->crtcs[i] == crtc_id) { @@ -434,13 +429,11 @@ namespace platf { return -1; } - connector_interal_t - connector(std::uint32_t id) { + connector_interal_t connector(std::uint32_t id) { return drmModeGetConnector(fd.el, id); } - std::vector - monitors(conn_type_count_t &conn_type_count) { + std::vector monitors(conn_type_count_t &conn_type_count) { auto resources = res(); if (!resources) { BOOST_LOG(error) << "Couldn't get connector resources"sv; @@ -474,8 +467,7 @@ namespace platf { return monitors; } - file_t - handleFD(std::uint32_t handle) { + file_t handleFD(std::uint32_t handle) { file_t fb_fd; auto status = drmPrimeHandleToFD(fd.el, handle, 0 /* flags */, &fb_fd.el); @@ -486,8 +478,7 @@ namespace platf { return fb_fd; } - std::vector> - props(std::uint32_t id, std::uint32_t type) { + std::vector> props(std::uint32_t id, std::uint32_t type) { obj_prop_t obj_prop = drmModeObjectGetProperties(fd.el, id, type); if (!obj_prop) { return {}; @@ -503,39 +494,32 @@ namespace platf { return props; } - std::vector> - plane_props(std::uint32_t id) { + std::vector> plane_props(std::uint32_t id) { return props(id, DRM_MODE_OBJECT_PLANE); } - std::vector> - crtc_props(std::uint32_t id) { + std::vector> crtc_props(std::uint32_t id) { return props(id, DRM_MODE_OBJECT_CRTC); } - std::vector> - connector_props(std::uint32_t id) { + std::vector> connector_props(std::uint32_t id) { return props(id, DRM_MODE_OBJECT_CONNECTOR); } - plane_t - operator[](std::uint32_t index) { + plane_t operator[](std::uint32_t index) { return drmModeGetPlane(fd.el, plane_res->planes[index]); } - std::uint32_t - count() { + std::uint32_t count() { return plane_res->count_planes; } - plane_it_t - begin() const { - return plane_it_t { fd.el, plane_res->planes, plane_res->planes + plane_res->count_planes }; + plane_it_t begin() const { + return plane_it_t {fd.el, plane_res->planes, plane_res->planes + plane_res->count_planes}; } - plane_it_t - end() const { - return plane_it_t { fd.el, plane_res->planes + plane_res->count_planes }; + plane_it_t end() const { + return plane_it_t {fd.el, plane_res->planes + plane_res->count_planes}; } file_t fd; @@ -543,16 +527,14 @@ namespace platf { plane_res_t plane_res; }; - std::map - map_crtc_to_monitor(const std::vector &connectors) { + std::map map_crtc_to_monitor(const std::vector &connectors) { std::map result; for (auto &connector : connectors) { - result.emplace(connector.crtc_id, - monitor_t { - connector.type, - connector.index, - }); + result.emplace(connector.crtc_id, monitor_t { + connector.type, + connector.index, + }); } return result; @@ -565,8 +547,7 @@ namespace platf { } }; - void - print(plane_t::pointer plane, fb_t::pointer fb, crtc_t::pointer crtc) { + void print(plane_t::pointer plane, fb_t::pointer fb, crtc_t::pointer crtc) { if (crtc) { BOOST_LOG(debug) << "crtc("sv << crtc->x << ", "sv << crtc->y << ')'; BOOST_LOG(debug) << "crtc("sv << crtc->width << ", "sv << crtc->height << ')'; @@ -601,21 +582,22 @@ namespace platf { class display_t: public platf::display_t { public: display_t(mem_type_e mem_type): - platf::display_t(), mem_type { mem_type } {} + platf::display_t(), + mem_type {mem_type} { + } - int - init(const std::string &display_name, const ::video::config_t &config) { - delay = std::chrono::nanoseconds { 1s } / config.framerate; + int init(const std::string &display_name, const ::video::config_t &config) { + delay = std::chrono::nanoseconds {1s} / config.framerate; int monitor_index = util::from_view(display_name); int monitor = 0; - fs::path card_dir { "/dev/dri"sv }; - for (auto &entry : fs::directory_iterator { card_dir }) { + fs::path card_dir {"/dev/dri"sv}; + for (auto &entry : fs::directory_iterator {card_dir}) { auto file = entry.path().filename(); auto filestring = file.generic_string(); - if (filestring.size() < 4 || std::string_view { filestring }.substr(0, 4) != "card"sv) { + if (filestring.size() < 4 || std::string_view {filestring}.substr(0, 4) != "card"sv) { continue; } @@ -779,8 +761,7 @@ namespace platf { if (!(plane->possible_crtcs & (1 << crtc_index))) { // Skip cursor planes for other CRTCs continue; - } - else if (plane->possible_crtcs != (1 << crtc_index)) { + } else if (plane->possible_crtcs != (1 << crtc_index)) { // We assume a 1:1 mapping between cursor planes and CRTCs, which seems to // match the behavior of drivers in the real world. If it's violated, we'll // proceed anyway but print a warning in the log. @@ -799,8 +780,7 @@ namespace platf { return 0; } - bool - is_hdr() { + bool is_hdr() { if (!hdr_metadata_blob_id || *hdr_metadata_blob_id == 0) { return false; } @@ -846,8 +826,7 @@ namespace platf { } } - bool - get_hdr_metadata(SS_HDR_METADATA &metadata) { + bool get_hdr_metadata(SS_HDR_METADATA &metadata) { // This performs all the metadata validation if (!is_hdr()) { return false; @@ -876,8 +855,7 @@ namespace platf { return true; } - void - update_cursor() { + void update_cursor() { if (cursor_plane_id < 0) { return; } @@ -898,26 +876,19 @@ namespace platf { for (auto &[prop, val] : props) { if (prop->name == "CRTC_X"sv) { prop_crtc_x = val; - } - else if (prop->name == "CRTC_Y"sv) { + } else if (prop->name == "CRTC_Y"sv) { prop_crtc_y = val; - } - else if (prop->name == "CRTC_W"sv) { + } else if (prop->name == "CRTC_W"sv) { prop_crtc_w = val; - } - else if (prop->name == "CRTC_H"sv) { + } else if (prop->name == "CRTC_H"sv) { prop_crtc_h = val; - } - else if (prop->name == "SRC_X"sv) { + } else if (prop->name == "SRC_X"sv) { prop_src_x = val; - } - else if (prop->name == "SRC_Y"sv) { + } else if (prop->name == "SRC_Y"sv) { prop_src_y = val; - } - else if (prop->name == "SRC_W"sv) { + } else if (prop->name == "SRC_W"sv) { prop_src_w = val; - } - else if (prop->name == "SRC_H"sv) { + } else if (prop->name == "SRC_H"sv) { prop_src_h = val; } } @@ -951,15 +922,13 @@ namespace platf { if (!plane->fb_id) { captured_cursor.visible = false; captured_cursor.fb_id = 0; - } - else if (plane->fb_id != captured_cursor.fb_id) { + } else if (plane->fb_id != captured_cursor.fb_id) { BOOST_LOG(debug) << "Refreshing cursor image after FB changed"sv; cursor_dirty = true; - } - else if (*prop_src_x != captured_cursor.prop_src_x || - *prop_src_y != captured_cursor.prop_src_y || - *prop_src_w != captured_cursor.prop_src_w || - *prop_src_h != captured_cursor.prop_src_h) { + } else if (*prop_src_x != captured_cursor.prop_src_x || + *prop_src_y != captured_cursor.prop_src_y || + *prop_src_w != captured_cursor.prop_src_w || + *prop_src_h != captured_cursor.prop_src_h) { BOOST_LOG(debug) << "Refreshing cursor image after source dimensions changed"sv; cursor_dirty = true; } @@ -1041,8 +1010,7 @@ namespace platf { // If the image is tightly packed, copy it in one shot if (fb->pitches[0] == src_w * 4 && src_x == 0) { memcpy(captured_cursor.pixels.data(), &((std::uint8_t *) mapped_data)[src_y * fb->pitches[0]], src_h * fb->pitches[0]); - } - else { + } else { // Copy row by row to deal with mismatched pitch or an X offset auto pixel_dst = captured_cursor.pixels.data(); for (int y = 0; y < src_h; y++) { @@ -1068,8 +1036,7 @@ namespace platf { } } - inline capture_e - refresh(file_t *file, egl::surface_descriptor_t *sd, std::optional &frame_timestamp) { + inline capture_e refresh(file_t *file, egl::surface_descriptor_t *sd, std::optional &frame_timestamp) { // Check for a change in HDR metadata if (connector_id) { auto connector_props = card.connector_props(*connector_id); @@ -1123,7 +1090,8 @@ namespace platf { if ( fb->width != img_width || - fb->height != img_height) { + fb->height != img_height + ) { return capture_e::reinit; } @@ -1155,10 +1123,10 @@ namespace platf { class display_ram_t: public display_t { public: display_ram_t(mem_type_e mem_type): - display_t(mem_type) {} + display_t(mem_type) { + } - int - init(const std::string &display_name, const ::video::config_t &config) { + int init(const std::string &display_name, const ::video::config_t &config) { if (!gbm::create_device) { BOOST_LOG(warning) << "libgbm not initialized"sv; return -1; @@ -1189,8 +1157,7 @@ namespace platf { return 0; } - capture_e - capture(const push_captured_image_cb_t &push_captured_image_cb, const pull_free_image_cb_t &pull_free_image_cb, bool *cursor) override { + capture_e capture(const push_captured_image_cb_t &push_captured_image_cb, const pull_free_image_cb_t &pull_free_image_cb, bool *cursor) override { auto next_frame = std::chrono::steady_clock::now(); sleep_overshoot_logger.reset(); @@ -1235,8 +1202,7 @@ namespace platf { return capture_e::ok; } - std::unique_ptr - make_avcodec_encode_device(pix_fmt_e pix_fmt) override { + std::unique_ptr make_avcodec_encode_device(pix_fmt_e pix_fmt) override { #ifdef SUNSHINE_BUILD_VAAPI if (mem_type == mem_type_e::vaapi) { return va::make_avcodec_encode_device(width, height, false); @@ -1252,8 +1218,7 @@ namespace platf { return std::make_unique(); } - void - blend_cursor(img_t &img) { + void blend_cursor(img_t &img) { // TODO: Cursor scaling is not supported in this codepath. // We always draw the cursor at the source size. auto pixels = (int *) img.data; @@ -1290,8 +1255,7 @@ namespace platf { auto alpha = (*(uint *) &cursor_pixel) >> 24u; if (alpha == 255) { *pixels_begin = cursor_pixel; - } - else { + } else { auto colors_out = (uint8_t *) &cursor_pixel; 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; @@ -1302,8 +1266,7 @@ namespace platf { } } - capture_e - snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr &img_out, std::chrono::milliseconds timeout, bool cursor) { + capture_e snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr &img_out, std::chrono::milliseconds timeout, bool cursor) { file_t fb_fd[4]; egl::surface_descriptor_t sd; @@ -1345,8 +1308,7 @@ namespace platf { return capture_e::ok; } - std::shared_ptr - alloc_img() override { + std::shared_ptr alloc_img() override { auto img = std::make_shared(); img->width = width; img->height = height; @@ -1357,8 +1319,7 @@ namespace platf { return img; } - int - dummy_img(platf::img_t *img) override { + int dummy_img(platf::img_t *img) override { return 0; } @@ -1370,10 +1331,10 @@ namespace platf { class display_vram_t: public display_t { public: display_vram_t(mem_type_e mem_type): - display_t(mem_type) {} + display_t(mem_type) { + } - std::unique_ptr - make_avcodec_encode_device(pix_fmt_e pix_fmt) override { + std::unique_ptr make_avcodec_encode_device(pix_fmt_e pix_fmt) override { #ifdef SUNSHINE_BUILD_VAAPI if (mem_type == mem_type_e::vaapi) { return va::make_avcodec_encode_device(width, height, dup(card.render_fd.el), img_offset_x, img_offset_y, true); @@ -1390,8 +1351,7 @@ namespace platf { return nullptr; } - std::shared_ptr - alloc_img() override { + std::shared_ptr alloc_img() override { auto img = std::make_shared(); img->width = width; @@ -1406,14 +1366,12 @@ namespace platf { return img; } - int - dummy_img(platf::img_t *img) override { + int dummy_img(platf::img_t *img) override { // Empty images are recognized as dummies by the zero sequence number return 0; } - capture_e - capture(const push_captured_image_cb_t &push_captured_image_cb, const pull_free_image_cb_t &pull_free_image_cb, bool *cursor) { + capture_e capture(const push_captured_image_cb_t &push_captured_image_cb, const pull_free_image_cb_t &pull_free_image_cb, bool *cursor) { auto next_frame = std::chrono::steady_clock::now(); sleep_overshoot_logger.reset(); @@ -1458,8 +1416,7 @@ namespace platf { return capture_e::ok; } - capture_e - snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr &img_out, std::chrono::milliseconds /* timeout */, bool cursor) { + capture_e snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr &img_out, std::chrono::milliseconds /* timeout */, bool cursor) { file_t fb_fd[4]; if (!pull_free_image_cb(img_out)) { @@ -1491,8 +1448,7 @@ namespace platf { img->pixel_pitch = 4; img->row_pitch = img->pixel_pitch * img->width; img->data = img->buffer.data(); - } - else { + } else { img->data = nullptr; } @@ -1502,8 +1458,7 @@ namespace platf { return capture_e::ok; } - int - init(const std::string &display_name, const ::video::config_t &config) { + int init(const std::string &display_name, const ::video::config_t &config) { if (display_t::init(display_name, config)) { return -1; } @@ -1530,8 +1485,7 @@ namespace platf { } // namespace kms - std::shared_ptr - kms_display(mem_type_e hwdevice_type, const std::string &display_name, const ::video::config_t &config) { + std::shared_ptr kms_display(mem_type_e hwdevice_type, const std::string &display_name, const ::video::config_t &config) { if (hwdevice_type == mem_type_e::vaapi || hwdevice_type == mem_type_e::cuda) { auto disp = std::make_shared(hwdevice_type); @@ -1561,8 +1515,7 @@ namespace platf { * * This is an ugly hack :( */ - void - correlate_to_wayland(std::vector &cds) { + void correlate_to_wayland(std::vector &cds) { auto monitors = wl::monitors(); BOOST_LOG(info) << "-------- Start of KMS monitor list --------"sv; @@ -1578,8 +1531,7 @@ namespace platf { std::uint32_t index; if (index_begin == std::string_view::npos) { index = 1; - } - else { + } else { index = std::max(1, util::from_view(name.substr(index_begin + 1))); } @@ -1594,7 +1546,8 @@ namespace platf { // A sanity check, it's guesswork after all. if ( monitor_descriptor.viewport.width != monitor->viewport.width || - monitor_descriptor.viewport.height != monitor->viewport.height) { + monitor_descriptor.viewport.height != monitor->viewport.height + ) { BOOST_LOG(warning) << "Mismatch on expected Resolution compared to actual resolution: "sv << monitor_descriptor.viewport.width << 'x' << monitor_descriptor.viewport.height @@ -1616,8 +1569,7 @@ namespace platf { } // A list of names of displays accepted as display_name - std::vector - kms_display_names(mem_type_e hwdevice_type) { + std::vector kms_display_names(mem_type_e hwdevice_type) { int count = 0; if (!fs::exists("/dev/dri")) { @@ -1635,12 +1587,12 @@ namespace platf { std::vector cds; std::vector display_names; - fs::path card_dir { "/dev/dri"sv }; - for (auto &entry : fs::directory_iterator { card_dir }) { + fs::path card_dir {"/dev/dri"sv}; + for (auto &entry : fs::directory_iterator {card_dir}) { auto file = entry.path().filename(); auto filestring = file.generic_string(); - if (std::string_view { filestring }.substr(0, 4) != "card"sv) { + if (std::string_view {filestring}.substr(0, 4) != "card"sv) { continue; } @@ -1655,8 +1607,7 @@ namespace platf { BOOST_LOG(debug) << file << " is not a CUDA device"sv; if (config::video.encoder == "nvenc") { BOOST_LOG(warning) << "Using NVENC with your display connected to a different GPU may not work properly!"sv; - } - else { + } else { continue; } } diff --git a/src/platform/linux/misc.cpp b/src/platform/linux/misc.cpp index 05c5a264..81c08ed4 100644 --- a/src/platform/linux/misc.cpp +++ b/src/platform/linux/misc.cpp @@ -12,16 +12,18 @@ #include #include -// lib includes +// platform includes #include -#include -#include -#include #include -#include #include #include #include + +// lib includes +#include +#include +#include +#include #include // local includes @@ -46,8 +48,7 @@ namespace bp = boost::process; window_system_e window_system; namespace dyn { - void * - handle(const std::vector &libs) { + void *handle(const std::vector &libs) { void *handle; for (auto lib : libs) { @@ -70,8 +71,7 @@ namespace dyn { return nullptr; } - int - load(void *handle, const std::vector> &funcs, bool strict) { + int load(void *handle, const std::vector> &funcs, bool strict) { int err = 0; for (auto &func : funcs) { TUPLE_2D_REF(fn, name, func); @@ -88,16 +88,16 @@ namespace dyn { return err; } } // namespace dyn + namespace platf { using ifaddr_t = util::safe_ptr; - ifaddr_t - get_ifaddrs() { - ifaddrs *p { nullptr }; + ifaddr_t get_ifaddrs() { + ifaddrs *p {nullptr}; getifaddrs(&p); - return ifaddr_t { p }; + return ifaddr_t {p}; } /** @@ -105,8 +105,7 @@ namespace platf { * @details This is used for the log directory, so it cannot invoke Boost logging! * @return The path of the appdata directory that should be used. */ - fs::path - appdata() { + fs::path appdata() { static std::once_flag migration_flag; static fs::path config_path; @@ -172,8 +171,7 @@ namespace platf { std::cerr << "Migration failed: " << ec.message() << std::endl; config_path = old_config_path; } - } - else { + } else { // We cannot use Boost logging because it hasn't been initialized yet! std::cerr << "Config exists in both "sv << old_config_path << " and "sv << config_path << ". Using "sv << config_path << " for config" << std::endl; std::cerr << "It is recommended to remove "sv << old_config_path << std::endl; @@ -185,45 +183,36 @@ namespace platf { return config_path; } - std::string - from_sockaddr(const sockaddr *const ip_addr) { + 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); - } - else if (family == AF_INET) { - inet_ntop(AF_INET, &((sockaddr_in *) ip_addr)->sin_addr, data, - INET_ADDRSTRLEN); + inet_ntop(AF_INET6, &((sockaddr_in6 *) ip_addr)->sin6_addr, data, INET6_ADDRSTRLEN); + } else if (family == AF_INET) { + inet_ntop(AF_INET, &((sockaddr_in *) ip_addr)->sin_addr, data, INET_ADDRSTRLEN); } - return std::string { data }; + return std::string {data}; } - std::pair - from_sockaddr_ex(const sockaddr *const ip_addr) { + std::pair from_sockaddr_ex(const sockaddr *const ip_addr) { char data[INET6_ADDRSTRLEN] = {}; auto family = ip_addr->sa_family; std::uint16_t port = 0; if (family == AF_INET6) { - inet_ntop(AF_INET6, &((sockaddr_in6 *) ip_addr)->sin6_addr, data, - INET6_ADDRSTRLEN); + inet_ntop(AF_INET6, &((sockaddr_in6 *) ip_addr)->sin6_addr, data, INET6_ADDRSTRLEN); port = ((sockaddr_in6 *) ip_addr)->sin6_port; - } - else if (family == AF_INET) { - inet_ntop(AF_INET, &((sockaddr_in *) ip_addr)->sin_addr, data, - INET_ADDRSTRLEN); + } else if (family == AF_INET) { + 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) { + 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)) { @@ -240,8 +229,7 @@ namespace platf { return "00:00:00:00:00:00"s; } - bp::child - run_command(bool elevated, bool interactive, const std::string &cmd, boost::filesystem::path &working_dir, const bp::environment &env, FILE *file, std::error_code &ec, bp::group *group) { + bp::child run_command(bool elevated, bool interactive, const std::string &cmd, boost::filesystem::path &working_dir, const bp::environment &env, FILE *file, std::error_code &ec, bp::group *group) { // clang-format off if (!group) { if (!file) { @@ -266,8 +254,7 @@ namespace platf { * @brief Open a url in the default web browser. * @param url The url to open. */ - void - open_url(const std::string &url) { + void open_url(const std::string &url) { // set working dir to user home directory auto working_dir = boost::filesystem::path(std::getenv("HOME")); std::string cmd = R"(xdg-open ")" + url + R"(")"; @@ -277,30 +264,25 @@ namespace platf { auto child = run_command(false, false, cmd, working_dir, _env, nullptr, ec, nullptr); if (ec) { BOOST_LOG(warning) << "Couldn't open url ["sv << url << "]: System: "sv << ec.message(); - } - else { + } else { BOOST_LOG(info) << "Opened url ["sv << url << "]"sv; child.detach(); } } - void - adjust_thread_priority(thread_priority_e priority) { + void adjust_thread_priority(thread_priority_e priority) { // Unimplemented } - void - streaming_will_start() { + void streaming_will_start() { // Nothing to do } - void - streaming_will_stop() { + void streaming_will_stop() { // Nothing to do } - void - restart_on_exit() { + void restart_on_exit() { char executable[PATH_MAX]; ssize_t len = readlink("/proc/self/exe", executable, PATH_MAX - 1); if (len == -1) { @@ -322,42 +304,35 @@ namespace platf { } } - void - restart() { + void restart() { // Gracefully clean up and restart ourselves instead of exiting atexit(restart_on_exit); lifetime::exit_sunshine(0, true); } - int - set_env(const std::string &name, const std::string &value) { + int set_env(const std::string &name, const std::string &value) { return setenv(name.c_str(), value.c_str(), 1); } - int - unset_env(const std::string &name) { + int unset_env(const std::string &name) { return unsetenv(name.c_str()); } - bool - request_process_group_exit(std::uintptr_t native_handle) { + bool request_process_group_exit(std::uintptr_t native_handle) { if (kill(-((pid_t) native_handle), SIGTERM) == 0 || errno == ESRCH) { BOOST_LOG(debug) << "Successfully sent SIGTERM to process group: "sv << native_handle; return true; - } - else { + } else { BOOST_LOG(warning) << "Unable to send SIGTERM to process group ["sv << native_handle << "]: "sv << errno; return false; } } - bool - process_group_running(std::uintptr_t native_handle) { + bool process_group_running(std::uintptr_t native_handle) { return waitpid(-((pid_t) native_handle), nullptr, WNOHANG) >= 0; } - struct sockaddr_in - to_sockaddr(boost::asio::ip::address_v4 address, uint16_t port) { + struct sockaddr_in to_sockaddr(boost::asio::ip::address_v4 address, uint16_t port) { struct sockaddr_in saddr_v4 = {}; saddr_v4.sin_family = AF_INET; @@ -369,8 +344,7 @@ namespace platf { return saddr_v4; } - struct sockaddr_in6 - to_sockaddr(boost::asio::ip::address_v6 address, uint16_t port) { + struct sockaddr_in6 to_sockaddr(boost::asio::ip::address_v6 address, uint16_t port) { struct sockaddr_in6 saddr_v6 = {}; saddr_v6.sin6_family = AF_INET6; @@ -383,8 +357,7 @@ namespace platf { return saddr_v6; } - bool - send_batch(batched_send_info_t &send_info) { + bool send_batch(batched_send_info_t &send_info) { auto sockfd = (int) send_info.native_socket; struct msghdr msg = {}; @@ -396,8 +369,7 @@ namespace platf { msg.msg_name = (struct sockaddr *) &taddr_v6; msg.msg_namelen = sizeof(taddr_v6); - } - else { + } else { taddr_v4 = to_sockaddr(send_info.target_address.to_v4(), send_info.target_port); msg.msg_name = (struct sockaddr *) &taddr_v4; @@ -405,10 +377,10 @@ namespace platf { } union { - char buf[CMSG_SPACE(sizeof(uint16_t)) + - std::max(CMSG_SPACE(sizeof(struct in_pktinfo)), CMSG_SPACE(sizeof(struct in6_pktinfo)))]; + char buf[CMSG_SPACE(sizeof(uint16_t)) + std::max(CMSG_SPACE(sizeof(struct in_pktinfo)), CMSG_SPACE(sizeof(struct in6_pktinfo)))]; struct cmsghdr alignment; } cmbuf = {}; // Must be zeroed for CMSG_NXTHDR() + socklen_t cmbuflen = 0; msg.msg_control = cmbuf.buf; @@ -430,8 +402,7 @@ namespace platf { pktinfo_cm->cmsg_type = IPV6_PKTINFO; pktinfo_cm->cmsg_len = CMSG_LEN(sizeof(pktInfo)); memcpy(CMSG_DATA(pktinfo_cm), &pktInfo, sizeof(pktInfo)); - } - else { + } else { struct in_pktinfo pktInfo; struct sockaddr_in saddr_v4 = to_sockaddr(send_info.source_address.to_v4(), 0); @@ -469,8 +440,7 @@ namespace platf { iovs[iovlen].iov_len = send_info.payload_size; iovlen++; } - } - else { + } else { // Translate buffer descriptors into iovs auto payload_offset = (send_info.block_offset + seg_index) * send_info.payload_size; auto payload_length = payload_offset + (segs_in_batch * send_info.payload_size); @@ -496,8 +466,7 @@ namespace platf { cm->cmsg_type = UDP_SEGMENT; cm->cmsg_len = CMSG_LEN(sizeof(uint16_t)); *((uint16_t *) CMSG_DATA(cm)) = msg_size; - } - else { + } else { msg.msg_controllen = cmbuflen; } @@ -593,8 +562,7 @@ namespace platf { } } - bool - send(send_info_t &send_info) { + bool send(send_info_t &send_info) { auto sockfd = (int) send_info.native_socket; struct msghdr msg = {}; @@ -606,8 +574,7 @@ namespace platf { msg.msg_name = (struct sockaddr *) &taddr_v6; msg.msg_namelen = sizeof(taddr_v6); - } - else { + } else { taddr_v4 = to_sockaddr(send_info.target_address.to_v4(), send_info.target_port); msg.msg_name = (struct sockaddr *) &taddr_v4; @@ -618,6 +585,7 @@ namespace platf { char buf[std::max(CMSG_SPACE(sizeof(struct in_pktinfo)), CMSG_SPACE(sizeof(struct in6_pktinfo)))]; struct cmsghdr alignment; } cmbuf; + socklen_t cmbuflen = 0; msg.msg_control = cmbuf.buf; @@ -637,8 +605,7 @@ namespace platf { pktinfo_cm->cmsg_type = IPV6_PKTINFO; pktinfo_cm->cmsg_len = CMSG_LEN(sizeof(pktInfo)); memcpy(CMSG_DATA(pktinfo_cm), &pktInfo, sizeof(pktInfo)); - } - else { + } else { struct in_pktinfo pktInfo; struct sockaddr_in saddr_v4 = to_sockaddr(send_info.source_address.to_v4(), 0); @@ -703,7 +670,8 @@ namespace platf { class qos_t: public deinit_t { public: qos_t(int sockfd, std::vector> options): - sockfd(sockfd), options(options) { + sockfd(sockfd), + options(options) { qos_ref_count++; } @@ -731,8 +699,7 @@ namespace platf { * @param data_type The type of traffic sent on this socket. * @param dscp_tagging Specifies whether to enable DSCP tagging on outgoing traffic. */ - std::unique_ptr - enable_socket_qos(uintptr_t native_socket, boost::asio::ip::address &address, uint16_t port, qos_data_type_e data_type, bool dscp_tagging) { + std::unique_ptr enable_socket_qos(uintptr_t native_socket, boost::asio::ip::address &address, uint16_t port, qos_data_type_e data_type, bool dscp_tagging) { int sockfd = (int) native_socket; std::vector> reset_options; @@ -745,8 +712,7 @@ namespace platf { if (address.is_v6() && !address.to_v6().is_v4_mapped()) { level = SOL_IPV6; option = IPV6_TCLASS; - } - else { + } else { level = SOL_IP; option = IP_TOS; } @@ -773,8 +739,7 @@ namespace platf { if (setsockopt(sockfd, level, option, &dscp, sizeof(dscp)) == 0) { // Reset TOS to -1 when QoS is disabled reset_options.emplace_back(std::make_tuple(level, option, -1)); - } - else { + } else { BOOST_LOG(error) << "Failed to set TOS/TCLASS: "sv << errno; } } @@ -790,20 +755,17 @@ namespace platf { if (setsockopt(sockfd, SOL_SOCKET, SO_PRIORITY, &priority, sizeof(priority)) == 0) { // Reset SO_PRIORITY to 0 when QoS is disabled reset_options.emplace_back(std::make_tuple(SOL_SOCKET, SO_PRIORITY, 0)); - } - else { + } else { BOOST_LOG(error) << "Failed to set SO_PRIORITY: "sv << errno; } return std::make_unique(sockfd, reset_options); } - std::string - get_host_name() { + std::string get_host_name() { try { return boost::asio::ip::host_name(); - } - catch (boost::system::system_error &err) { + } catch (boost::system::system_error &err) { BOOST_LOG(error) << "Failed to get hostname: "sv << err.what(); return "Sunshine"s; } @@ -830,67 +792,62 @@ namespace platf { static std::bitset sources; #ifdef SUNSHINE_BUILD_CUDA - std::vector - nvfbc_display_names(); - std::shared_ptr - nvfbc_display(mem_type_e hwdevice_type, const std::string &display_name, const video::config_t &config); + std::vector nvfbc_display_names(); + std::shared_ptr nvfbc_display(mem_type_e hwdevice_type, const std::string &display_name, const video::config_t &config); - bool - verify_nvfbc() { + bool verify_nvfbc() { return !nvfbc_display_names().empty(); } #endif #ifdef SUNSHINE_BUILD_WAYLAND - std::vector - wl_display_names(); - std::shared_ptr - wl_display(mem_type_e hwdevice_type, const std::string &display_name, const video::config_t &config); + std::vector wl_display_names(); + std::shared_ptr wl_display(mem_type_e hwdevice_type, const std::string &display_name, const video::config_t &config); - bool - verify_wl() { + bool verify_wl() { return window_system == window_system_e::WAYLAND && !wl_display_names().empty(); } #endif #ifdef SUNSHINE_BUILD_DRM - std::vector - kms_display_names(mem_type_e hwdevice_type); - std::shared_ptr - kms_display(mem_type_e hwdevice_type, const std::string &display_name, const video::config_t &config); + std::vector kms_display_names(mem_type_e hwdevice_type); + std::shared_ptr kms_display(mem_type_e hwdevice_type, const std::string &display_name, const video::config_t &config); - bool - verify_kms() { + bool verify_kms() { return !kms_display_names(mem_type_e::unknown).empty(); } #endif #ifdef SUNSHINE_BUILD_X11 - std::vector - x11_display_names(); - std::shared_ptr - x11_display(mem_type_e hwdevice_type, const std::string &display_name, const video::config_t &config); + std::vector x11_display_names(); + std::shared_ptr x11_display(mem_type_e hwdevice_type, const std::string &display_name, const video::config_t &config); - bool - verify_x11() { + bool verify_x11() { return window_system == window_system_e::X11 && !x11_display_names().empty(); } #endif - std::vector - display_names(mem_type_e hwdevice_type) { + std::vector display_names(mem_type_e hwdevice_type) { #ifdef SUNSHINE_BUILD_CUDA // display using NvFBC only supports mem_type_e::cuda - if (sources[source::NVFBC] && hwdevice_type == mem_type_e::cuda) return nvfbc_display_names(); + if (sources[source::NVFBC] && hwdevice_type == mem_type_e::cuda) { + return nvfbc_display_names(); + } #endif #ifdef SUNSHINE_BUILD_WAYLAND - if (sources[source::WAYLAND]) return wl_display_names(); + if (sources[source::WAYLAND]) { + return wl_display_names(); + } #endif #ifdef SUNSHINE_BUILD_DRM - if (sources[source::KMS]) return kms_display_names(hwdevice_type); + if (sources[source::KMS]) { + return kms_display_names(hwdevice_type); + } #endif #ifdef SUNSHINE_BUILD_X11 - if (sources[source::X11]) return x11_display_names(); + if (sources[source::X11]) { + return x11_display_names(); + } #endif return {}; } @@ -899,14 +856,12 @@ namespace platf { * @brief Returns if GPUs/drivers have changed since the last call to this function. * @return `true` if a change has occurred or if it is unknown whether a change occurred. */ - bool - needs_encoder_reenumeration() { + bool needs_encoder_reenumeration() { // We don't track GPU state, so we will always reenumerate. Fortunately, it is fast on Linux. return true; } - std::shared_ptr - display(mem_type_e hwdevice_type, const std::string &display_name, const video::config_t &config) { + std::shared_ptr display(mem_type_e hwdevice_type, const std::string &display_name, const video::config_t &config) { #ifdef SUNSHINE_BUILD_CUDA if (sources[source::NVFBC] && hwdevice_type == mem_type_e::cuda) { BOOST_LOG(info) << "Screencasting with NvFBC"sv; @@ -935,8 +890,7 @@ namespace platf { return nullptr; } - std::unique_ptr - init() { + std::unique_ptr init() { // enable low latency mode for AMD // https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/30039 set_env("AMD_DEBUG", "lowlatencyenc"); @@ -1005,8 +959,7 @@ namespace platf { class linux_high_precision_timer: public high_precision_timer { public: - void - sleep_for(const std::chrono::nanoseconds &duration) override { + void sleep_for(const std::chrono::nanoseconds &duration) override { std::this_thread::sleep_for(duration); } @@ -1015,8 +968,7 @@ namespace platf { } }; - std::unique_ptr - create_high_precision_timer() { + std::unique_ptr create_high_precision_timer() { return std::make_unique(); } } // namespace platf diff --git a/src/platform/linux/misc.h b/src/platform/linux/misc.h index b084c985..c9f98f44 100644 --- a/src/platform/linux/misc.h +++ b/src/platform/linux/misc.h @@ -4,9 +4,11 @@ */ #pragma once +// standard includes #include #include +// local includes #include "src/utility.h" KITTY_USING_MOVE_T(file_t, int, -1, { @@ -26,9 +28,7 @@ extern window_system_e window_system; namespace dyn { typedef void (*apiproc)(void); - int - load(void *handle, const std::vector> &funcs, bool strict = true); - void * - handle(const std::vector &libs); + int load(void *handle, const std::vector> &funcs, bool strict = true); + void *handle(const std::vector &libs); } // namespace dyn diff --git a/src/platform/linux/publish.cpp b/src/platform/linux/publish.cpp index 9b7dbbc5..a2bac72f 100644 --- a/src/platform/linux/publish.cpp +++ b/src/platform/linux/publish.cpp @@ -3,8 +3,10 @@ * @brief Definitions for publishing services on Linux. * @note Adapted from https://www.avahi.org/doxygen/html/client-publish-service_8c-example.html */ +// standard includes #include +// local includes #include "misc.h" #include "src/logging.h" #include "src/network.h" @@ -84,6 +86,7 @@ namespace avahi { }; constexpr auto IF_UNSPEC = -1; + enum proto { PROTO_INET = 0, ///< IPv4 PROTO_INET6 = 1, ///< IPv6 @@ -164,7 +167,8 @@ namespace avahi { const char *domain, const char *host, uint16_t port, - ...); + ... + ); typedef int (*entry_group_is_empty_fn)(EntryGroup *); typedef int (*entry_group_reset_fn)(EntryGroup *); @@ -199,30 +203,31 @@ namespace avahi { simple_poll_new_fn simple_poll_new; simple_poll_free_fn simple_poll_free; - int - init_common() { - static void *handle { nullptr }; + int init_common() { + static void *handle {nullptr}; static bool funcs_loaded = false; - if (funcs_loaded) return 0; + if (funcs_loaded) { + return 0; + } if (!handle) { - handle = dyn::handle({ "libavahi-common.so.3", "libavahi-common.so" }); + handle = dyn::handle({"libavahi-common.so.3", "libavahi-common.so"}); if (!handle) { return -1; } } std::vector> funcs { - { (dyn::apiproc *) &alternative_service_name, "avahi_alternative_service_name" }, - { (dyn::apiproc *) &free, "avahi_free" }, - { (dyn::apiproc *) &strdup, "avahi_strdup" }, - { (dyn::apiproc *) &strerror, "avahi_strerror" }, - { (dyn::apiproc *) &simple_poll_get, "avahi_simple_poll_get" }, - { (dyn::apiproc *) &simple_poll_loop, "avahi_simple_poll_loop" }, - { (dyn::apiproc *) &simple_poll_quit, "avahi_simple_poll_quit" }, - { (dyn::apiproc *) &simple_poll_new, "avahi_simple_poll_new" }, - { (dyn::apiproc *) &simple_poll_free, "avahi_simple_poll_free" }, + {(dyn::apiproc *) &alternative_service_name, "avahi_alternative_service_name"}, + {(dyn::apiproc *) &free, "avahi_free"}, + {(dyn::apiproc *) &strdup, "avahi_strdup"}, + {(dyn::apiproc *) &strerror, "avahi_strerror"}, + {(dyn::apiproc *) &simple_poll_get, "avahi_simple_poll_get"}, + {(dyn::apiproc *) &simple_poll_loop, "avahi_simple_poll_loop"}, + {(dyn::apiproc *) &simple_poll_quit, "avahi_simple_poll_quit"}, + {(dyn::apiproc *) &simple_poll_new, "avahi_simple_poll_new"}, + {(dyn::apiproc *) &simple_poll_free, "avahi_simple_poll_free"}, }; if (dyn::load(handle, funcs)) { @@ -233,34 +238,35 @@ namespace avahi { return 0; } - int - init_client() { + int init_client() { if (init_common()) { return -1; } - static void *handle { nullptr }; + static void *handle {nullptr}; static bool funcs_loaded = false; - if (funcs_loaded) return 0; + if (funcs_loaded) { + return 0; + } if (!handle) { - handle = dyn::handle({ "libavahi-client.so.3", "libavahi-client.so" }); + handle = dyn::handle({"libavahi-client.so.3", "libavahi-client.so"}); if (!handle) { return -1; } } std::vector> funcs { - { (dyn::apiproc *) &client_new, "avahi_client_new" }, - { (dyn::apiproc *) &client_free, "avahi_client_free" }, - { (dyn::apiproc *) &entry_group_get_client, "avahi_entry_group_get_client" }, - { (dyn::apiproc *) &entry_group_new, "avahi_entry_group_new" }, - { (dyn::apiproc *) &entry_group_add_service, "avahi_entry_group_add_service" }, - { (dyn::apiproc *) &entry_group_is_empty, "avahi_entry_group_is_empty" }, - { (dyn::apiproc *) &entry_group_reset, "avahi_entry_group_reset" }, - { (dyn::apiproc *) &entry_group_commit, "avahi_entry_group_commit" }, - { (dyn::apiproc *) &client_errno, "avahi_client_errno" }, + {(dyn::apiproc *) &client_new, "avahi_client_new"}, + {(dyn::apiproc *) &client_free, "avahi_client_free"}, + {(dyn::apiproc *) &entry_group_get_client, "avahi_entry_group_get_client"}, + {(dyn::apiproc *) &entry_group_new, "avahi_entry_group_new"}, + {(dyn::apiproc *) &entry_group_add_service, "avahi_entry_group_add_service"}, + {(dyn::apiproc *) &entry_group_is_empty, "avahi_entry_group_is_empty"}, + {(dyn::apiproc *) &entry_group_reset, "avahi_entry_group_reset"}, + {(dyn::apiproc *) &entry_group_commit, "avahi_entry_group_commit"}, + {(dyn::apiproc *) &client_errno, "avahi_client_errno"}, }; if (dyn::load(handle, funcs)) { @@ -274,13 +280,12 @@ namespace avahi { namespace platf::publish { - template - void - free(T *p) { + template + void free(T *p) { avahi::free(p); } - template + template using ptr_t = util::safe_ptr>; using client_t = util::dyn_safe_ptr; using poll_t = util::dyn_safe_ptr; @@ -292,11 +297,9 @@ namespace platf::publish { ptr_t name; - void - create_services(avahi::Client *c); + void create_services(avahi::Client *c); - void - entry_group_callback(avahi::EntryGroup *g, avahi::EntryGroupState state, void *) { + void entry_group_callback(avahi::EntryGroup *g, avahi::EntryGroupState state, void *) { group = g; switch (state) { @@ -319,8 +322,7 @@ namespace platf::publish { } } - void - create_services(avahi::Client *c) { + void create_services(avahi::Client *c) { int ret; auto fg = util::fail_guard([]() { @@ -339,13 +341,16 @@ namespace platf::publish { ret = avahi::entry_group_add_service( group, - avahi::IF_UNSPEC, avahi::PROTO_UNSPEC, + avahi::IF_UNSPEC, + avahi::PROTO_UNSPEC, avahi::PublishFlags(0), name.get(), SERVICE_TYPE, - nullptr, nullptr, + nullptr, + nullptr, net::map_port(nvhttp::PORT_HTTP), - nullptr); + nullptr + ); if (ret < 0) { if (ret == avahi::ERR_COLLISION) { @@ -375,8 +380,7 @@ namespace platf::publish { fg.disable(); } - void - client_callback(avahi::Client *c, avahi::ClientState state, void *) { + void client_callback(avahi::Client *c, avahi::ClientState state, void *) { switch (state) { case avahi::CLIENT_S_RUNNING: create_services(c); @@ -387,8 +391,9 @@ namespace platf::publish { break; case avahi::CLIENT_S_COLLISION: case avahi::CLIENT_S_REGISTERING: - if (group) + if (group) { avahi::entry_group_reset(group); + } break; case avahi::CLIENT_CONNECTING:; } @@ -399,7 +404,8 @@ namespace platf::publish { std::thread poll_thread; deinit_t(std::thread poll_thread): - poll_thread { std::move(poll_thread) } {} + poll_thread {std::move(poll_thread)} { + } ~deinit_t() override { if (avahi::simple_poll_quit && poll) { @@ -412,8 +418,7 @@ namespace platf::publish { } }; - [[nodiscard]] std::unique_ptr<::platf::deinit_t> - start() { + [[nodiscard]] std::unique_ptr<::platf::deinit_t> start() { if (avahi::init_client()) { return nullptr; } @@ -430,13 +435,14 @@ namespace platf::publish { name.reset(avahi::strdup(instance_name.c_str())); client.reset( - avahi::client_new(avahi::simple_poll_get(poll.get()), avahi::ClientFlags(0), client_callback, nullptr, &avhi_error)); + avahi::client_new(avahi::simple_poll_get(poll.get()), avahi::ClientFlags(0), client_callback, nullptr, &avhi_error) + ); if (!client) { BOOST_LOG(error) << "Failed to create client: "sv << avahi::strerror(avhi_error); return nullptr; } - return std::make_unique(std::thread { avahi::simple_poll_loop, poll.get() }); + return std::make_unique(std::thread {avahi::simple_poll_loop, poll.get()}); } } // namespace platf::publish diff --git a/src/platform/linux/vaapi.cpp b/src/platform/linux/vaapi.cpp index 909ade10..88444d10 100644 --- a/src/platform/linux/vaapi.cpp +++ b/src/platform/linux/vaapi.cpp @@ -2,28 +2,30 @@ * @file src/platform/linux/vaapi.cpp * @brief Definitions for VA-API hardware accelerated capture. */ +// standard includes +#include #include #include -#include - extern "C" { #include #include #include #include #if !VA_CHECK_VERSION(1, 9, 0) -// vaSyncBuffer stub allows Sunshine built against libva <2.9.0 to link against ffmpeg on libva 2.9.0 or later -VAStatus -vaSyncBuffer( - VADisplay dpy, - VABufferID buf_id, - uint64_t timeout_ns) { - return VA_STATUS_ERROR_UNIMPLEMENTED; -} + // vaSyncBuffer stub allows Sunshine built against libva <2.9.0 to link against ffmpeg on libva 2.9.0 or later + VAStatus + vaSyncBuffer( + VADisplay dpy, + VABufferID buf_id, + uint64_t timeout_ns + ) { + return VA_STATUS_ERROR_UNIMPLEMENTED; + } #endif } +// local includes #include "graphics.h" #include "misc.h" #include "src/config.h" @@ -69,6 +71,7 @@ namespace va { // Number of layers making up the surface. uint32_t num_layers; + struct { // DRM format fourcc of this layer (DRM_FOURCC_*). uint32_t drm_format; @@ -89,13 +92,11 @@ namespace va { using display_t = util::safe_ptr_v2; - int - vaapi_init_avcodec_hardware_input_buffer(platf::avcodec_encode_device_t *encode_device, AVBufferRef **hw_device_buf); + int vaapi_init_avcodec_hardware_input_buffer(platf::avcodec_encode_device_t *encode_device, AVBufferRef **hw_device_buf); class va_t: public platf::avcodec_encode_device_t { public: - int - init(int in_width, int in_height, file_t &&render_device) { + int init(int in_width, int in_height, file_t &&render_device) { file = std::move(render_device); if (!gbm::create_device) { @@ -135,8 +136,7 @@ namespace va { * @param profile The profile to match. * @return A valid encoding entrypoint or 0 on failure. */ - VAEntrypoint - select_va_entrypoint(VAProfile profile) { + VAEntrypoint select_va_entrypoint(VAProfile profile) { std::vector entrypoints(vaMaxNumEntrypoints(va_display)); int num_eps; auto status = vaQueryConfigEntrypoints(va_display, profile, entrypoints.data(), &num_eps); @@ -166,8 +166,7 @@ namespace va { * @param profile The profile to match. * @return Boolean value indicating if the profile is supported. */ - bool - is_va_profile_supported(VAProfile profile) { + bool is_va_profile_supported(VAProfile profile) { std::vector profiles(vaMaxNumProfiles(va_display)); int num_profs; auto status = vaQueryConfigProfiles(va_display, profiles.data(), &num_profs); @@ -185,13 +184,11 @@ namespace va { * @param ctx The FFmpeg codec context. * @return The matching VA profile or `VAProfileNone` on failure. */ - VAProfile - get_va_profile(AVCodecContext *ctx) { + VAProfile get_va_profile(AVCodecContext *ctx) { if (ctx->codec_id == AV_CODEC_ID_H264) { // There's no VAAPI profile for H.264 4:4:4 return VAProfileH264High; - } - else if (ctx->codec_id == AV_CODEC_ID_HEVC) { + } else if (ctx->codec_id == AV_CODEC_ID_HEVC) { switch (ctx->profile) { case FF_PROFILE_HEVC_REXT: switch (av_pix_fmt_desc_get(ctx->sw_pix_fmt)->comp[0].depth) { @@ -206,8 +203,7 @@ namespace va { case FF_PROFILE_HEVC_MAIN: return VAProfileHEVCMain; } - } - else if (ctx->codec_id == AV_CODEC_ID_AV1) { + } else if (ctx->codec_id == AV_CODEC_ID_AV1) { switch (ctx->profile) { case FF_PROFILE_AV1_HIGH: return VAProfileAV1Profile1; @@ -220,8 +216,7 @@ namespace va { return VAProfileNone; } - void - init_codec_options(AVCodecContext *ctx, AVDictionary **options) override { + void init_codec_options(AVCodecContext *ctx, AVDictionary **options) override { auto va_profile = get_va_profile(ctx); if (va_profile == VAProfileNone || !is_va_profile_supported(va_profile)) { // Don't bother doing anything if the profile isn't supported @@ -239,19 +234,18 @@ namespace va { if (va_entrypoint == VAEntrypointEncSliceLP) { BOOST_LOG(info) << "Using LP encoding mode"sv; av_dict_set_int(options, "low_power", 1, 0); - } - else { + } else { BOOST_LOG(info) << "Using normal encoding mode"sv; } - VAConfigAttrib rc_attr = { VAConfigAttribRateControl }; + VAConfigAttrib rc_attr = {VAConfigAttribRateControl}; auto status = vaGetConfigAttributes(va_display, va_profile, va_entrypoint, &rc_attr, 1); if (status != VA_STATUS_SUCCESS) { // Stick to the default rate control (CQP) rc_attr.value = 0; } - VAConfigAttrib slice_attr = { VAConfigAttribEncMaxSlices }; + VAConfigAttrib slice_attr = {VAConfigAttribEncMaxSlices}; status = vaGetConfigAttributes(va_display, va_profile, va_entrypoint, &slice_attr, 1); if (status != VA_STATUS_SUCCESS) { // Assume only a single slice is supported @@ -281,27 +275,22 @@ namespace va { if (rc_attr.value & VA_RC_VBR) { BOOST_LOG(info) << "Using VBR with single frame VBV size"sv; av_dict_set(options, "rc_mode", "VBR", 0); - } - else if (rc_attr.value & VA_RC_CBR) { + } else if (rc_attr.value & VA_RC_CBR) { BOOST_LOG(info) << "Using CBR with single frame VBV size"sv; av_dict_set(options, "rc_mode", "CBR", 0); - } - else { + } else { BOOST_LOG(warning) << "Using CQP with single frame VBV size"sv; av_dict_set_int(options, "qp", config::video.qp, 0); } - } - else if (!(rc_attr.value & (VA_RC_CBR | VA_RC_VBR))) { + } else if (!(rc_attr.value & (VA_RC_CBR | VA_RC_VBR))) { BOOST_LOG(warning) << "Using CQP rate control"sv; av_dict_set_int(options, "qp", config::video.qp, 0); - } - else { + } else { BOOST_LOG(info) << "Using default rate control"sv; } } - int - set_frame(AVFrame *frame, AVBufferRef *hw_frames_ctx_buf) override { + int set_frame(AVFrame *frame, AVBufferRef *hw_frames_ctx_buf) override { this->hwframe.reset(frame); this->frame = frame; @@ -321,7 +310,8 @@ namespace va { surface, va::SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2, va::EXPORT_SURFACE_WRITE_ONLY | va::EXPORT_SURFACE_SEPARATE_LAYERS, - &prime); + &prime + ); if (status) { BOOST_LOG(error) << "Couldn't export va surface handle: ["sv << (int) surface << "]: "sv << vaErrorStr(status); @@ -377,8 +367,7 @@ namespace va { return 0; } - void - apply_colorspace() override { + void apply_colorspace() override { sws.apply_colorspace(colorspace); } @@ -401,8 +390,7 @@ namespace va { class va_ram_t: public va_t { public: - int - convert(platf::img_t &img) override { + int convert(platf::img_t &img) override { sws.load_ram(img); sws.convert(nv12->buf); @@ -412,15 +400,13 @@ namespace va { class va_vram_t: public va_t { public: - int - convert(platf::img_t &img) override { + int convert(platf::img_t &img) override { auto &descriptor = (egl::img_descriptor_t &) img; if (descriptor.sequence == 0) { // For dummy images, use a blank RGB texture instead of importing a DMA-BUF rgb = egl::create_blank(img); - } - else if (descriptor.sequence > sequence) { + } else if (descriptor.sequence > sequence) { sequence = descriptor.sequence; rgb = egl::rgb_t {}; @@ -440,8 +426,7 @@ namespace va { return 0; } - int - init(int in_width, int in_height, file_t &&render_device, int offset_x, int offset_y) { + int init(int in_width, int in_height, file_t &&render_device, int offset_x, int offset_y) { if (va_t::init(in_width, in_height, std::move(render_device))) { return -1; } @@ -471,6 +456,7 @@ namespace va { void *xdisplay; int fd; } drm; + int drm_fd; } VAAPIDevicePriv; @@ -494,13 +480,11 @@ namespace va { unsigned int driver_quirks; } AVVAAPIDeviceContext; - static void - __log(void *level, const char *msg) { + static void __log(void *level, const char *msg) { BOOST_LOG(*(boost::log::sources::severity_logger *) level) << msg; } - static void - vaapi_hwdevice_ctx_free(AVHWDeviceContext *ctx) { + static void vaapi_hwdevice_ctx_free(AVHWDeviceContext *ctx) { auto hwctx = (AVVAAPIDeviceContext *) ctx->hwctx; auto priv = (VAAPIDevicePriv *) ctx->user_opaque; @@ -509,8 +493,7 @@ namespace va { av_freep(&priv); } - int - vaapi_init_avcodec_hardware_input_buffer(platf::avcodec_encode_device_t *base, AVBufferRef **hw_device_buf) { + int vaapi_init_avcodec_hardware_input_buffer(platf::avcodec_encode_device_t *base, AVBufferRef **hw_device_buf) { auto va = (va::va_t *) base; auto fd = dup(va->file.el); @@ -522,7 +505,7 @@ namespace va { av_free(priv); }); - va::display_t display { vaGetDisplayDRM(fd) }; + va::display_t display {vaGetDisplayDRM(fd)}; if (!display) { auto render_device = config::video.adapter_name.empty() ? "/dev/dri/renderD128" : config::video.adapter_name.c_str(); @@ -556,7 +539,7 @@ namespace va { auto err = av_hwdevice_ctx_init(*hw_device_buf); 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; @@ -565,8 +548,7 @@ namespace va { return 0; } - static bool - query(display_t::pointer display, VAProfile profile) { + static bool query(display_t::pointer display, VAProfile profile) { std::vector entrypoints; entrypoints.resize(vaMaxNumEntrypoints(display)); @@ -587,15 +569,14 @@ namespace va { return false; } - bool - validate(int fd) { - va::display_t display { vaGetDisplayDRM(fd) }; + bool validate(int fd) { + va::display_t display {vaGetDisplayDRM(fd)}; if (!display) { char string[1024]; auto bytes = readlink(("/proc/self/fd/" + std::to_string(fd)).c_str(), string, sizeof(string)); - std::string_view render_device { string, (std::size_t) bytes }; + std::string_view render_device {string, (std::size_t) bytes}; BOOST_LOG(error) << "Couldn't open a va display from DRM with device: "sv << render_device; return false; @@ -623,8 +604,7 @@ namespace va { return true; } - std::unique_ptr - make_avcodec_encode_device(int width, int height, file_t &&card, int offset_x, int offset_y, bool vram) { + std::unique_ptr make_avcodec_encode_device(int width, int height, file_t &&card, int offset_x, int offset_y, bool vram) { if (vram) { auto egl = std::make_unique(); if (egl->init(width, height, std::move(card), offset_x, offset_y)) { @@ -644,8 +624,7 @@ namespace va { } } - std::unique_ptr - make_avcodec_encode_device(int width, int height, int offset_x, int offset_y, bool vram) { + std::unique_ptr make_avcodec_encode_device(int width, int height, int offset_x, int offset_y, bool vram) { auto render_device = config::video.adapter_name.empty() ? "/dev/dri/renderD128" : config::video.adapter_name.c_str(); file_t file = open(render_device, O_RDWR); @@ -659,8 +638,7 @@ namespace va { return make_avcodec_encode_device(width, height, std::move(file), offset_x, offset_y, vram); } - std::unique_ptr - make_avcodec_encode_device(int width, int height, bool vram) { + std::unique_ptr make_avcodec_encode_device(int width, int height, bool vram) { return make_avcodec_encode_device(width, height, 0, 0, vram); } } // namespace va diff --git a/src/platform/linux/vaapi.h b/src/platform/linux/vaapi.h index de1ea702..490df1bc 100644 --- a/src/platform/linux/vaapi.h +++ b/src/platform/linux/vaapi.h @@ -4,12 +4,14 @@ */ #pragma once +// local includes #include "misc.h" #include "src/platform/common.h" namespace egl { struct surface_descriptor_t; } + namespace va { /** * Width --> Width of the image @@ -18,14 +20,10 @@ namespace va { * offset_y --> Vertical offset of the image in the texture * file_t card --> The file descriptor of the render device used for encoding */ - std::unique_ptr - make_avcodec_encode_device(int width, int height, bool vram); - std::unique_ptr - make_avcodec_encode_device(int width, int height, int offset_x, int offset_y, bool vram); - std::unique_ptr - make_avcodec_encode_device(int width, int height, file_t &&card, int offset_x, int offset_y, bool vram); + std::unique_ptr make_avcodec_encode_device(int width, int height, bool vram); + std::unique_ptr make_avcodec_encode_device(int width, int height, int offset_x, int offset_y, bool vram); + std::unique_ptr make_avcodec_encode_device(int width, int height, file_t &&card, int offset_x, int offset_y, bool vram); // Ensure the render device pointed to by fd is capable of encoding h264 with the hevc_mode configured - bool - validate(int fd); + bool validate(int fd); } // namespace va diff --git a/src/platform/linux/wayland.cpp b/src/platform/linux/wayland.cpp index a6dba44e..c6e02d72 100644 --- a/src/platform/linux/wayland.cpp +++ b/src/platform/linux/wayland.cpp @@ -2,12 +2,15 @@ * @file src/platform/linux/wayland.cpp * @brief Definitions for Wayland capture. */ +// standard includes +#include + +// platform includes #include #include #include -#include - +// local includes #include "graphics.h" #include "src/logging.h" #include "src/platform/common.h" @@ -27,16 +30,14 @@ using namespace std::literals; namespace wl { // Helper to call C++ method from wayland C callback - template - static auto - classCall(void *data, Params... params) -> decltype(((*reinterpret_cast(data)).*m)(params...)) { + template + static auto classCall(void *data, Params... params) -> decltype(((*reinterpret_cast(data)).*m)(params...)) { return ((*reinterpret_cast(data)).*m)(params...); } #define CLASS_CALL(c, m) classCall - int - display_t::init(const char *display_name) { + int display_t::init(const char *display_name) { if (!display_name) { display_name = std::getenv("WAYLAND_DISPLAY"); } @@ -57,8 +58,7 @@ namespace wl { return 0; } - void - display_t::roundtrip() { + void display_t::roundtrip() { wl_display_roundtrip(display_internal.get()); } @@ -67,8 +67,7 @@ namespace wl { * @param timeout The timeout in milliseconds. * @return `true` if new events were dispatched or `false` if the timeout expired. */ - bool - display_t::dispatch(std::chrono::milliseconds timeout) { + bool display_t::dispatch(std::chrono::milliseconds timeout) { // Check if any events are queued already. If not, flush // outgoing events, and prepare to wait for readability. if (wl_display_prepare_read(display_internal.get()) == 0) { @@ -81,8 +80,7 @@ namespace wl { if (poll(&pfd, 1, timeout.count()) == 1 && (pfd.revents & POLLIN)) { // Read the new event(s) wl_display_read_events(display_internal.get()); - } - else { + } else { // We timed out, so unlock the queue now wl_display_cancel_read(display_internal.get()); return false; @@ -94,13 +92,12 @@ namespace wl { return true; } - wl_registry * - display_t::registry() { + wl_registry *display_t::registry() { return wl_display_get_registry(display_internal.get()); } inline monitor_t::monitor_t(wl_output *output): - output { output }, + output {output}, wl_listener { &CLASS_CALL(monitor_t, wl_geometry), &CLASS_CALL(monitor_t, wl_mode), @@ -113,46 +110,40 @@ namespace wl { &CLASS_CALL(monitor_t, xdg_done), &CLASS_CALL(monitor_t, xdg_name), &CLASS_CALL(monitor_t, xdg_description) - } {} + } { + } - inline void - monitor_t::xdg_name(zxdg_output_v1 *, const char *name) { + inline void monitor_t::xdg_name(zxdg_output_v1 *, const char *name) { this->name = name; BOOST_LOG(info) << "Name: "sv << this->name; } - void - monitor_t::xdg_description(zxdg_output_v1 *, const char *description) { + void monitor_t::xdg_description(zxdg_output_v1 *, const char *description) { this->description = description; BOOST_LOG(info) << "Found monitor: "sv << this->description; } - void - monitor_t::xdg_position(zxdg_output_v1 *, std::int32_t x, std::int32_t y) { + void monitor_t::xdg_position(zxdg_output_v1 *, std::int32_t x, std::int32_t y) { viewport.offset_x = x; viewport.offset_y = y; BOOST_LOG(info) << "Offset: "sv << x << 'x' << y; } - void - monitor_t::xdg_size(zxdg_output_v1 *, std::int32_t width, std::int32_t height) { + void monitor_t::xdg_size(zxdg_output_v1 *, std::int32_t width, std::int32_t height) { BOOST_LOG(info) << "Logical size: "sv << width << 'x' << height; } - void - monitor_t::wl_mode(wl_output *wl_output, std::uint32_t flags, - std::int32_t width, std::int32_t height, std::int32_t refresh) { + void monitor_t::wl_mode(wl_output *wl_output, std::uint32_t flags, std::int32_t width, std::int32_t height, std::int32_t refresh) { viewport.width = width; viewport.height = height; BOOST_LOG(info) << "Resolution: "sv << width << 'x' << height; } - void - monitor_t::listen(zxdg_output_manager_v1 *output_manager) { + void monitor_t::listen(zxdg_output_manager_v1 *output_manager) { auto xdg_output = zxdg_output_manager_v1_get_xdg_output(output_manager, output); zxdg_output_v1_add_listener(xdg_output, &xdg_listener, this); wl_output_add_listener(output, &wl_listener, this); @@ -160,34 +151,33 @@ namespace wl { interface_t::interface_t() noexcept : - output_manager { nullptr }, + output_manager {nullptr}, listener { &CLASS_CALL(interface_t, add_interface), &CLASS_CALL(interface_t, del_interface) - } {} + } { + } - void - interface_t::listen(wl_registry *registry) { + void interface_t::listen(wl_registry *registry) { wl_registry_add_listener(registry, &listener, this); } - void - interface_t::add_interface(wl_registry *registry, std::uint32_t id, const char *interface, std::uint32_t version) { + void interface_t::add_interface(wl_registry *registry, std::uint32_t id, const char *interface, std::uint32_t version) { BOOST_LOG(debug) << "Available interface: "sv << interface << '(' << id << ") version "sv << version; if (!std::strcmp(interface, wl_output_interface.name)) { BOOST_LOG(info) << "Found interface: "sv << interface << '(' << id << ") version "sv << version; monitors.emplace_back( std::make_unique( - (wl_output *) wl_registry_bind(registry, id, &wl_output_interface, 2))); - } - else if (!std::strcmp(interface, zxdg_output_manager_v1_interface.name)) { + (wl_output *) wl_registry_bind(registry, id, &wl_output_interface, 2) + ) + ); + } else if (!std::strcmp(interface, zxdg_output_manager_v1_interface.name)) { BOOST_LOG(info) << "Found interface: "sv << interface << '(' << id << ") version "sv << version; output_manager = (zxdg_output_manager_v1 *) wl_registry_bind(registry, id, &zxdg_output_manager_v1_interface, version); this->interface[XDG_OUTPUT] = true; - } - else if (!std::strcmp(interface, zwlr_export_dmabuf_manager_v1_interface.name)) { + } else if (!std::strcmp(interface, zwlr_export_dmabuf_manager_v1_interface.name)) { BOOST_LOG(info) << "Found interface: "sv << interface << '(' << id << ") version "sv << version; dmabuf_manager = (zwlr_export_dmabuf_manager_v1 *) wl_registry_bind(registry, id, &zwlr_export_dmabuf_manager_v1_interface, version); @@ -195,13 +185,15 @@ namespace wl { } } - void - interface_t::del_interface(wl_registry *registry, uint32_t id) { + void interface_t::del_interface(wl_registry *registry, uint32_t id) { BOOST_LOG(info) << "Delete: "sv << id; } dmabuf_t::dmabuf_t(): - status { READY }, frames {}, current_frame { &frames[0] }, listener { + status {READY}, + frames {}, + current_frame {&frames[0]}, + listener { &CLASS_CALL(dmabuf_t, frame), &CLASS_CALL(dmabuf_t, object), &CLASS_CALL(dmabuf_t, ready), @@ -209,8 +201,7 @@ namespace wl { } { } - void - dmabuf_t::listen(zwlr_export_dmabuf_manager_v1 *dmabuf_manager, wl_output *output, bool blend_cursor) { + void dmabuf_t::listen(zwlr_export_dmabuf_manager_v1 *dmabuf_manager, wl_output *output, bool blend_cursor) { auto frame = zwlr_export_dmabuf_manager_v1_capture_output(dmabuf_manager, blend_cursor, output); zwlr_export_dmabuf_frame_v1_add_listener(frame, &listener, this); @@ -223,15 +214,19 @@ namespace wl { } } - void - dmabuf_t::frame( + void dmabuf_t::frame( zwlr_export_dmabuf_frame_v1 *frame, - std::uint32_t width, std::uint32_t height, - std::uint32_t x, std::uint32_t y, - std::uint32_t buffer_flags, std::uint32_t flags, + std::uint32_t width, + std::uint32_t height, + std::uint32_t x, + std::uint32_t y, + std::uint32_t buffer_flags, + std::uint32_t flags, std::uint32_t format, - std::uint32_t high, std::uint32_t low, - std::uint32_t obj_count) { + std::uint32_t high, + std::uint32_t low, + std::uint32_t obj_count + ) { auto next_frame = get_next_frame(); next_frame->sd.fourcc = format; @@ -240,15 +235,15 @@ namespace wl { next_frame->sd.modifier = (((std::uint64_t) high) << 32) | low; } - void - dmabuf_t::object( + void dmabuf_t::object( zwlr_export_dmabuf_frame_v1 *frame, std::uint32_t index, std::int32_t fd, std::uint32_t size, std::uint32_t offset, std::uint32_t stride, - std::uint32_t plane_index) { + std::uint32_t plane_index + ) { auto next_frame = get_next_frame(); next_frame->sd.fds[plane_index] = fd; @@ -256,10 +251,12 @@ namespace wl { next_frame->sd.offsets[plane_index] = offset; } - void - dmabuf_t::ready( + void dmabuf_t::ready( zwlr_export_dmabuf_frame_v1 *frame, - std::uint32_t tv_sec_hi, std::uint32_t tv_sec_lo, std::uint32_t tv_nsec) { + std::uint32_t tv_sec_hi, + std::uint32_t tv_sec_lo, + std::uint32_t tv_nsec + ) { zwlr_export_dmabuf_frame_v1_destroy(frame); current_frame->destroy(); @@ -268,10 +265,10 @@ namespace wl { status = READY; } - void - dmabuf_t::cancel( + void dmabuf_t::cancel( zwlr_export_dmabuf_frame_v1 *frame, - std::uint32_t reason) { + std::uint32_t reason + ) { zwlr_export_dmabuf_frame_v1_destroy(frame); auto next_frame = get_next_frame(); @@ -280,8 +277,7 @@ namespace wl { status = REINIT; } - void - frame_t::destroy() { + void frame_t::destroy() { for (auto x = 0; x < 4; ++x) { if (sd.fds[x] >= 0) { close(sd.fds[x]); @@ -296,8 +292,7 @@ namespace wl { std::fill_n(sd.fds, 4, -1); }; - std::vector> - monitors(const char *display_name) { + std::vector> monitors(const char *display_name) { display_t display; if (display.init(display_name)) { @@ -323,15 +318,13 @@ namespace wl { return std::move(interface.monitors); } - static bool - validate() { + static bool validate() { display_t display; return display.init() == 0; } - int - init() { + int init() { static bool validated = validate(); return !validated; @@ -339,4 +332,4 @@ namespace wl { } // namespace wl -#pragma GCC diagnostic pop \ No newline at end of file +#pragma GCC diagnostic pop diff --git a/src/platform/linux/wayland.h b/src/platform/linux/wayland.h index ece9d3bd..6a6986f6 100644 --- a/src/platform/linux/wayland.h +++ b/src/platform/linux/wayland.h @@ -4,6 +4,7 @@ */ #pragma once +// standard includes #include #ifdef SUNSHINE_BUILD_WAYLAND @@ -11,6 +12,7 @@ #include #endif +// local includes #include "graphics.h" /** @@ -27,8 +29,7 @@ namespace wl { frame_t(); egl::surface_descriptor_t sd; - void - destroy(); + void destroy(); }; class dmabuf_t { @@ -42,50 +43,52 @@ namespace wl { dmabuf_t(dmabuf_t &&) = delete; dmabuf_t(const dmabuf_t &) = delete; - dmabuf_t & - operator=(const dmabuf_t &) = delete; - dmabuf_t & - operator=(dmabuf_t &&) = delete; + dmabuf_t &operator=(const dmabuf_t &) = delete; + dmabuf_t &operator=(dmabuf_t &&) = delete; dmabuf_t(); - void - listen(zwlr_export_dmabuf_manager_v1 *dmabuf_manager, wl_output *output, bool blend_cursor = false); + void listen(zwlr_export_dmabuf_manager_v1 *dmabuf_manager, wl_output *output, bool blend_cursor = false); ~dmabuf_t(); - void - frame( + void frame( zwlr_export_dmabuf_frame_v1 *frame, - std::uint32_t width, std::uint32_t height, - std::uint32_t x, std::uint32_t y, - std::uint32_t buffer_flags, std::uint32_t flags, + std::uint32_t width, + std::uint32_t height, + std::uint32_t x, + std::uint32_t y, + std::uint32_t buffer_flags, + std::uint32_t flags, std::uint32_t format, - std::uint32_t high, std::uint32_t low, - std::uint32_t obj_count); + std::uint32_t high, + std::uint32_t low, + std::uint32_t obj_count + ); - void - object( + void object( zwlr_export_dmabuf_frame_v1 *frame, std::uint32_t index, std::int32_t fd, std::uint32_t size, std::uint32_t offset, std::uint32_t stride, - std::uint32_t plane_index); + std::uint32_t plane_index + ); - void - ready( + void ready( zwlr_export_dmabuf_frame_v1 *frame, - std::uint32_t tv_sec_hi, std::uint32_t tv_sec_lo, std::uint32_t tv_nsec); + std::uint32_t tv_sec_hi, + std::uint32_t tv_sec_lo, + std::uint32_t tv_nsec + ); - void - cancel( + void cancel( zwlr_export_dmabuf_frame_v1 *frame, - std::uint32_t reason); + std::uint32_t reason + ); - inline frame_t * - get_next_frame() { + inline frame_t *get_next_frame() { return current_frame == &frames[0] ? &frames[1] : &frames[0]; } @@ -102,38 +105,31 @@ namespace wl { monitor_t(monitor_t &&) = delete; monitor_t(const monitor_t &) = delete; - monitor_t & - operator=(const monitor_t &) = delete; - monitor_t & - operator=(monitor_t &&) = delete; + monitor_t &operator=(const monitor_t &) = delete; + monitor_t &operator=(monitor_t &&) = delete; monitor_t(wl_output *output); - void - xdg_name(zxdg_output_v1 *, const char *name); - void - xdg_description(zxdg_output_v1 *, const char *description); - void - xdg_position(zxdg_output_v1 *, std::int32_t x, std::int32_t y); - void - xdg_size(zxdg_output_v1 *, std::int32_t width, std::int32_t height); - void - xdg_done(zxdg_output_v1 *) {} + void xdg_name(zxdg_output_v1 *, const char *name); + void xdg_description(zxdg_output_v1 *, const char *description); + void xdg_position(zxdg_output_v1 *, std::int32_t x, std::int32_t y); + void xdg_size(zxdg_output_v1 *, std::int32_t width, std::int32_t height); - void - wl_geometry(wl_output *wl_output, std::int32_t x, std::int32_t y, - std::int32_t physical_width, std::int32_t physical_height, std::int32_t subpixel, - const char *make, const char *model, std::int32_t transform) {} - void - wl_mode(wl_output *wl_output, std::uint32_t flags, - std::int32_t width, std::int32_t height, std::int32_t refresh); - void - wl_done(wl_output *wl_output) {} - void - wl_scale(wl_output *wl_output, std::int32_t factor) {} + void xdg_done(zxdg_output_v1 *) { + } - void - listen(zxdg_output_manager_v1 *output_manager); + void wl_geometry(wl_output *wl_output, std::int32_t x, std::int32_t y, std::int32_t physical_width, std::int32_t physical_height, std::int32_t subpixel, const char *make, const char *model, std::int32_t transform) { + } + + void wl_mode(wl_output *wl_output, std::uint32_t flags, std::int32_t width, std::int32_t height, std::int32_t refresh); + + void wl_done(wl_output *wl_output) { + } + + void wl_scale(wl_output *wl_output, std::int32_t factor) { + } + + void listen(zxdg_output_manager_v1 *output_manager); wl_output *output; @@ -162,31 +158,25 @@ namespace wl { interface_t(interface_t &&) = delete; interface_t(const interface_t &) = delete; - interface_t & - operator=(const interface_t &) = delete; - interface_t & - operator=(interface_t &&) = delete; + interface_t &operator=(const interface_t &) = delete; + interface_t &operator=(interface_t &&) = delete; interface_t() noexcept; - void - listen(wl_registry *registry); + void listen(wl_registry *registry); std::vector> monitors; zwlr_export_dmabuf_manager_v1 *dmabuf_manager; zxdg_output_manager_v1 *output_manager; - bool - operator[](interface_e bit) const { + bool operator[](interface_e bit) const { return interface[bit]; } private: - void - add_interface(wl_registry *registry, std::uint32_t id, const char *interface, std::uint32_t version); - void - del_interface(wl_registry *registry, uint32_t id); + void add_interface(wl_registry *registry, std::uint32_t id, const char *interface, std::uint32_t version); + void del_interface(wl_registry *registry, uint32_t id); std::bitset interface; @@ -201,24 +191,19 @@ namespace wl { * @param display_name The name of the display. * @return 0 on success, -1 on failure. */ - int - init(const char *display_name = nullptr); + int init(const char *display_name = nullptr); // Roundtrip with Wayland connection - void - roundtrip(); + void roundtrip(); // Wait up to the timeout to read and dispatch new events - bool - dispatch(std::chrono::milliseconds timeout); + bool dispatch(std::chrono::milliseconds timeout); // Get the registry associated with the display // No need to manually free the registry - wl_registry * - registry(); + wl_registry *registry(); - inline display_internal_t::pointer - get() { + inline display_internal_t::pointer get() { return display_internal.get(); } @@ -226,11 +211,9 @@ namespace wl { display_internal_t display_internal; }; - std::vector> - monitors(const char *display_name = nullptr); + std::vector> monitors(const char *display_name = nullptr); - int - init(); + int init(); } // namespace wl #else @@ -243,15 +226,12 @@ namespace wl { monitor_t(monitor_t &&) = delete; monitor_t(const monitor_t &) = delete; - monitor_t & - operator=(const monitor_t &) = delete; - monitor_t & - operator=(monitor_t &&) = delete; + monitor_t &operator=(const monitor_t &) = delete; + monitor_t &operator=(monitor_t &&) = delete; monitor_t(wl_output *output); - void - listen(zxdg_output_manager_v1 *output_manager); + void listen(zxdg_output_manager_v1 *output_manager); wl_output *output; @@ -261,10 +241,12 @@ namespace wl { platf::touch_port_t viewport; }; - inline std::vector> - monitors(const char *display_name = nullptr) { return {}; } + inline std::vector> monitors(const char *display_name = nullptr) { + return {}; + } - inline int - init() { return -1; } + inline int init() { + return -1; + } } // namespace wl #endif diff --git a/src/platform/linux/wlgrab.cpp b/src/platform/linux/wlgrab.cpp index 4c5946c8..b867b908 100644 --- a/src/platform/linux/wlgrab.cpp +++ b/src/platform/linux/wlgrab.cpp @@ -2,18 +2,19 @@ * @file src/platform/linux/wlgrab.cpp * @brief Definitions for wlgrab capture. */ +// standard includes #include -#include "src/platform/common.h" - -#include "src/logging.h" -#include "src/video.h" - +// local includes #include "cuda.h" +#include "src/logging.h" +#include "src/platform/common.h" +#include "src/video.h" #include "vaapi.h" #include "wayland.h" using namespace std::literals; + namespace wl { static int env_width; static int env_height; @@ -27,9 +28,8 @@ namespace wl { class wlr_t: public platf::display_t { public: - int - init(platf::mem_type_e hwdevice_type, const std::string &display_name, const ::video::config_t &config) { - delay = std::chrono::nanoseconds { 1s } / config.framerate; + int init(platf::mem_type_e hwdevice_type, const std::string &display_name, const ::video::config_t &config) { + delay = std::chrono::nanoseconds {1s} / config.framerate; mem_type = hwdevice_type; if (display.init()) { @@ -82,13 +82,11 @@ namespace wl { return 0; } - int - dummy_img(platf::img_t *img) override { + int dummy_img(platf::img_t *img) override { return 0; } - inline platf::capture_e - snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr &img_out, std::chrono::milliseconds timeout, bool cursor) { + inline platf::capture_e snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr &img_out, std::chrono::milliseconds timeout, bool cursor) { auto to = std::chrono::steady_clock::now() + timeout; // Dispatch events until we get a new frame or the timeout expires @@ -105,7 +103,8 @@ namespace wl { if ( dmabuf.status == dmabuf_t::REINIT || current_frame->sd.width != width || - current_frame->sd.height != height) { + current_frame->sd.height != height + ) { return platf::capture_e::reinit; } @@ -125,8 +124,7 @@ namespace wl { class wlr_ram_t: public wlr_t { public: - platf::capture_e - capture(const push_captured_image_cb_t &push_captured_image_cb, const pull_free_image_cb_t &pull_free_image_cb, bool *cursor) override { + platf::capture_e capture(const push_captured_image_cb_t &push_captured_image_cb, const pull_free_image_cb_t &pull_free_image_cb, bool *cursor) override { auto next_frame = std::chrono::steady_clock::now(); sleep_overshoot_logger.reset(); @@ -171,8 +169,7 @@ namespace wl { return platf::capture_e::ok; } - platf::capture_e - snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr &img_out, std::chrono::milliseconds timeout, bool cursor) { + platf::capture_e snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr &img_out, std::chrono::milliseconds timeout, bool cursor) { auto status = wlr_t::snapshot(pull_free_image_cb, img_out, timeout, cursor); if (status != platf::capture_e::ok) { return status; @@ -204,8 +201,7 @@ namespace wl { return platf::capture_e::ok; } - int - init(platf::mem_type_e hwdevice_type, const std::string &display_name, const ::video::config_t &config) { + int init(platf::mem_type_e hwdevice_type, const std::string &display_name, const ::video::config_t &config) { if (wlr_t::init(hwdevice_type, display_name, config)) { return -1; } @@ -225,8 +221,7 @@ namespace wl { return 0; } - std::unique_ptr - make_avcodec_encode_device(platf::pix_fmt_e pix_fmt) override { + std::unique_ptr make_avcodec_encode_device(platf::pix_fmt_e pix_fmt) override { #ifdef SUNSHINE_BUILD_VAAPI if (mem_type == platf::mem_type_e::vaapi) { return va::make_avcodec_encode_device(width, height, false); @@ -242,8 +237,7 @@ namespace wl { return std::make_unique(); } - std::shared_ptr - alloc_img() override { + std::shared_ptr alloc_img() override { auto img = std::make_shared(); img->width = width; img->height = height; @@ -260,8 +254,7 @@ namespace wl { class wlr_vram_t: public wlr_t { public: - platf::capture_e - capture(const push_captured_image_cb_t &push_captured_image_cb, const pull_free_image_cb_t &pull_free_image_cb, bool *cursor) override { + platf::capture_e capture(const push_captured_image_cb_t &push_captured_image_cb, const pull_free_image_cb_t &pull_free_image_cb, bool *cursor) override { auto next_frame = std::chrono::steady_clock::now(); sleep_overshoot_logger.reset(); @@ -306,8 +299,7 @@ namespace wl { return platf::capture_e::ok; } - platf::capture_e - snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr &img_out, std::chrono::milliseconds timeout, bool cursor) { + platf::capture_e snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr &img_out, std::chrono::milliseconds timeout, bool cursor) { auto status = wlr_t::snapshot(pull_free_image_cb, img_out, timeout, cursor); if (status != platf::capture_e::ok) { return status; @@ -332,8 +324,7 @@ namespace wl { return platf::capture_e::ok; } - std::shared_ptr - alloc_img() override { + std::shared_ptr alloc_img() override { auto img = std::make_shared(); img->width = width; @@ -348,8 +339,7 @@ namespace wl { return img; } - std::unique_ptr - make_avcodec_encode_device(platf::pix_fmt_e pix_fmt) override { + std::unique_ptr make_avcodec_encode_device(platf::pix_fmt_e pix_fmt) override { #ifdef SUNSHINE_BUILD_VAAPI if (mem_type == platf::mem_type_e::vaapi) { return va::make_avcodec_encode_device(width, height, 0, 0, true); @@ -365,8 +355,7 @@ namespace wl { return std::make_unique(); } - int - dummy_img(platf::img_t *img) override { + int dummy_img(platf::img_t *img) override { // Empty images are recognized as dummies by the zero sequence number return 0; } @@ -377,8 +366,7 @@ namespace wl { } // namespace wl namespace platf { - std::shared_ptr - wl_display(mem_type_e hwdevice_type, const std::string &display_name, const video::config_t &config) { + std::shared_ptr wl_display(mem_type_e hwdevice_type, const std::string &display_name, const video::config_t &config) { if (hwdevice_type != platf::mem_type_e::system && hwdevice_type != platf::mem_type_e::vaapi && hwdevice_type != platf::mem_type_e::cuda) { BOOST_LOG(error) << "Could not initialize display with the given hw device type."sv; return nullptr; @@ -401,8 +389,7 @@ namespace platf { return wlr; } - std::vector - wl_display_names() { + std::vector wl_display_names() { std::vector display_names; wl::display_t display; diff --git a/src/platform/linux/x11grab.cpp b/src/platform/linux/x11grab.cpp index 44c37609..6eef5a81 100644 --- a/src/platform/linux/x11grab.cpp +++ b/src/platform/linux/x11grab.cpp @@ -2,61 +2,49 @@ * @file src/platform/linux/x11grab.cpp * @brief Definitions for x11 capture. */ -#include "src/platform/common.h" - +// standard includes #include #include +// plaform includes +#include +#include +#include +#include #include #include #include -#include -#include -#include -#include #include #include -#include "src/config.h" -#include "src/globals.h" -#include "src/logging.h" -#include "src/task_pool.h" -#include "src/video.h" - +// local includes #include "cuda.h" #include "graphics.h" #include "misc.h" +#include "src/config.h" +#include "src/globals.h" +#include "src/logging.h" +#include "src/platform/common.h" +#include "src/task_pool.h" +#include "src/video.h" #include "vaapi.h" #include "x11grab.h" using namespace std::literals; namespace platf { - int - load_xcb(); - int - load_x11(); + int load_xcb(); + int load_x11(); namespace x11 { -#define _FN(x, ret, args) \ +#define _FN(x, ret, args) \ typedef ret(*x##_fn) args; \ static x##_fn x - _FN(GetImage, XImage *, - ( - Display * display, - Drawable d, - int x, int y, - unsigned int width, unsigned int height, - unsigned long plane_mask, - int format)); + _FN(GetImage, XImage *, (Display * display, Drawable d, int x, int y, unsigned int width, unsigned int height, unsigned long plane_mask, int format)); _FN(OpenDisplay, Display *, (_Xconst char *display_name)); - _FN(GetWindowAttributes, Status, - ( - Display * display, - Window w, - XWindowAttributes *window_attributes_return)); + _FN(GetWindowAttributes, Status, (Display * display, Window w, XWindowAttributes *window_attributes_return)); _FN(CloseDisplay, int, (Display * display)); _FN(Free, int, (void *data)); @@ -70,27 +58,28 @@ namespace platf { _FN(FreeOutputInfo, void, (XRROutputInfo * outputInfo)); _FN(FreeCrtcInfo, void, (XRRCrtcInfo * crtcInfo)); - static int - init() { - static void *handle { nullptr }; + static int init() { + static void *handle {nullptr}; static bool funcs_loaded = false; - if (funcs_loaded) return 0; + if (funcs_loaded) { + return 0; + } if (!handle) { - handle = dyn::handle({ "libXrandr.so.2", "libXrandr.so" }); + handle = dyn::handle({"libXrandr.so.2", "libXrandr.so"}); if (!handle) { return -1; } } std::vector> funcs { - { (dyn::apiproc *) &GetScreenResources, "XRRGetScreenResources" }, - { (dyn::apiproc *) &GetOutputInfo, "XRRGetOutputInfo" }, - { (dyn::apiproc *) &GetCrtcInfo, "XRRGetCrtcInfo" }, - { (dyn::apiproc *) &FreeScreenResources, "XRRFreeScreenResources" }, - { (dyn::apiproc *) &FreeOutputInfo, "XRRFreeOutputInfo" }, - { (dyn::apiproc *) &FreeCrtcInfo, "XRRFreeCrtcInfo" }, + {(dyn::apiproc *) &GetScreenResources, "XRRGetScreenResources"}, + {(dyn::apiproc *) &GetOutputInfo, "XRRGetOutputInfo"}, + {(dyn::apiproc *) &GetCrtcInfo, "XRRGetCrtcInfo"}, + {(dyn::apiproc *) &FreeScreenResources, "XRRFreeScreenResources"}, + {(dyn::apiproc *) &FreeOutputInfo, "XRRFreeOutputInfo"}, + {(dyn::apiproc *) &FreeCrtcInfo, "XRRFreeCrtcInfo"}, }; if (dyn::load(handle, funcs)) { @@ -102,25 +91,27 @@ namespace platf { } } // namespace rr + namespace fix { _FN(GetCursorImage, XFixesCursorImage *, (Display * dpy)); - static int - init() { - static void *handle { nullptr }; + static int init() { + static void *handle {nullptr}; static bool funcs_loaded = false; - if (funcs_loaded) return 0; + if (funcs_loaded) { + return 0; + } if (!handle) { - handle = dyn::handle({ "libXfixes.so.3", "libXfixes.so" }); + handle = dyn::handle({"libXfixes.so.3", "libXfixes.so"}); if (!handle) { return -1; } } std::vector> funcs { - { (dyn::apiproc *) &GetCursorImage, "XFixesGetCursorImage" }, + {(dyn::apiproc *) &GetCursorImage, "XFixesGetCursorImage"}, }; if (dyn::load(handle, funcs)) { @@ -132,27 +123,28 @@ namespace platf { } } // namespace fix - static int - init() { - static void *handle { nullptr }; + static int init() { + static void *handle {nullptr}; static bool funcs_loaded = false; - if (funcs_loaded) return 0; + if (funcs_loaded) { + return 0; + } if (!handle) { - handle = dyn::handle({ "libX11.so.6", "libX11.so" }); + handle = dyn::handle({"libX11.so.6", "libX11.so"}); if (!handle) { return -1; } } std::vector> funcs { - { (dyn::apiproc *) &GetImage, "XGetImage" }, - { (dyn::apiproc *) &OpenDisplay, "XOpenDisplay" }, - { (dyn::apiproc *) &GetWindowAttributes, "XGetWindowAttributes" }, - { (dyn::apiproc *) &Free, "XFree" }, - { (dyn::apiproc *) &CloseDisplay, "XCloseDisplay" }, - { (dyn::apiproc *) &InitThreads, "XInitThreads" }, + {(dyn::apiproc *) &GetImage, "XGetImage"}, + {(dyn::apiproc *) &OpenDisplay, "XOpenDisplay"}, + {(dyn::apiproc *) &GetWindowAttributes, "XGetWindowAttributes"}, + {(dyn::apiproc *) &Free, "XFree"}, + {(dyn::apiproc *) &CloseDisplay, "XCloseDisplay"}, + {(dyn::apiproc *) &InitThreads, "XInitThreads"}, }; if (dyn::load(handle, funcs)) { @@ -167,31 +159,13 @@ namespace platf { namespace xcb { static xcb_extension_t *shm_id; - _FN(shm_get_image_reply, xcb_shm_get_image_reply_t *, - ( - xcb_connection_t * c, - xcb_shm_get_image_cookie_t cookie, - xcb_generic_error_t **e)); + _FN(shm_get_image_reply, xcb_shm_get_image_reply_t *, (xcb_connection_t * c, xcb_shm_get_image_cookie_t cookie, xcb_generic_error_t **e)); - _FN(shm_get_image_unchecked, xcb_shm_get_image_cookie_t, - ( - xcb_connection_t * c, - xcb_drawable_t drawable, - int16_t x, int16_t y, - uint16_t width, uint16_t height, - uint32_t plane_mask, - uint8_t format, - xcb_shm_seg_t shmseg, - uint32_t offset)); + _FN(shm_get_image_unchecked, xcb_shm_get_image_cookie_t, (xcb_connection_t * c, xcb_drawable_t drawable, int16_t x, int16_t y, uint16_t width, uint16_t height, uint32_t plane_mask, uint8_t format, xcb_shm_seg_t shmseg, uint32_t offset)); - _FN(shm_attach, xcb_void_cookie_t, - (xcb_connection_t * c, - xcb_shm_seg_t shmseg, - uint32_t shmid, - uint8_t read_only)); + _FN(shm_attach, xcb_void_cookie_t, (xcb_connection_t * c, xcb_shm_seg_t shmseg, uint32_t shmid, uint8_t read_only)); - _FN(get_extension_data, xcb_query_extension_reply_t *, - (xcb_connection_t * c, xcb_extension_t *ext)); + _FN(get_extension_data, xcb_query_extension_reply_t *, (xcb_connection_t * c, xcb_extension_t *ext)); _FN(get_setup, xcb_setup_t *, (xcb_connection_t * c)); _FN(disconnect, void, (xcb_connection_t * c)); @@ -200,25 +174,26 @@ namespace platf { _FN(setup_roots_iterator, xcb_screen_iterator_t, (const xcb_setup_t *R)); _FN(generate_id, std::uint32_t, (xcb_connection_t * c)); - int - init_shm() { - static void *handle { nullptr }; + int init_shm() { + static void *handle {nullptr}; static bool funcs_loaded = false; - if (funcs_loaded) return 0; + if (funcs_loaded) { + return 0; + } if (!handle) { - handle = dyn::handle({ "libxcb-shm.so.0", "libxcb-shm.so" }); + handle = dyn::handle({"libxcb-shm.so.0", "libxcb-shm.so"}); if (!handle) { return -1; } } std::vector> funcs { - { (dyn::apiproc *) &shm_id, "xcb_shm_id" }, - { (dyn::apiproc *) &shm_get_image_reply, "xcb_shm_get_image_reply" }, - { (dyn::apiproc *) &shm_get_image_unchecked, "xcb_shm_get_image_unchecked" }, - { (dyn::apiproc *) &shm_attach, "xcb_shm_attach" }, + {(dyn::apiproc *) &shm_id, "xcb_shm_id"}, + {(dyn::apiproc *) &shm_get_image_reply, "xcb_shm_get_image_reply"}, + {(dyn::apiproc *) &shm_get_image_unchecked, "xcb_shm_get_image_unchecked"}, + {(dyn::apiproc *) &shm_attach, "xcb_shm_attach"}, }; if (dyn::load(handle, funcs)) { @@ -229,28 +204,29 @@ namespace platf { return 0; } - int - init() { - static void *handle { nullptr }; + int init() { + static void *handle {nullptr}; static bool funcs_loaded = false; - if (funcs_loaded) return 0; + if (funcs_loaded) { + return 0; + } if (!handle) { - handle = dyn::handle({ "libxcb.so.1", "libxcb.so" }); + handle = dyn::handle({"libxcb.so.1", "libxcb.so"}); if (!handle) { return -1; } } std::vector> funcs { - { (dyn::apiproc *) &get_extension_data, "xcb_get_extension_data" }, - { (dyn::apiproc *) &get_setup, "xcb_get_setup" }, - { (dyn::apiproc *) &disconnect, "xcb_disconnect" }, - { (dyn::apiproc *) &connection_has_error, "xcb_connection_has_error" }, - { (dyn::apiproc *) &connect, "xcb_connect" }, - { (dyn::apiproc *) &setup_roots_iterator, "xcb_setup_roots_iterator" }, - { (dyn::apiproc *) &generate_id, "xcb_generate_id" }, + {(dyn::apiproc *) &get_extension_data, "xcb_get_extension_data"}, + {(dyn::apiproc *) &get_setup, "xcb_get_setup"}, + {(dyn::apiproc *) &disconnect, "xcb_disconnect"}, + {(dyn::apiproc *) &connection_has_error, "xcb_connection_has_error"}, + {(dyn::apiproc *) &connect, "xcb_connect"}, + {(dyn::apiproc *) &setup_roots_iterator, "xcb_setup_roots_iterator"}, + {(dyn::apiproc *) &generate_id, "xcb_generate_id"}, }; if (dyn::load(handle, funcs)) { @@ -264,10 +240,8 @@ namespace platf { #undef _FN } // namespace xcb - void - freeImage(XImage *); - void - freeX(XFixesCursorImage *); + void freeImage(XImage *); + void freeX(XFixesCursorImage *); using xcb_connect_t = util::dyn_safe_ptr; using xcb_img_t = util::c_ptr; @@ -282,9 +256,13 @@ namespace platf { class shm_id_t { public: shm_id_t(): - id { -1 } {} + id {-1} { + } + shm_id_t(int id): - id { id } {} + id {id} { + } + shm_id_t(shm_id_t &&other) noexcept: id(other.id) { other.id = -1; @@ -296,15 +274,19 @@ namespace platf { id = -1; } } + int id; }; class shm_data_t { public: shm_data_t(): - data { (void *) -1 } {} + data {(void *) -1} { + } + shm_data_t(void *data): - data { data } {} + data {data} { + } shm_data_t(shm_data_t &&other) noexcept: data(other.data) { @@ -331,9 +313,8 @@ namespace platf { } }; - static void - blend_cursor(Display *display, img_t &img, int offsetX, int offsetY) { - xcursor_t overlay { x11::fix::GetCursorImage(display) }; + static void blend_cursor(Display *display, img_t &img, int offsetX, int offsetY) { + xcursor_t overlay {x11::fix::GetCursorImage(display)}; if (!overlay) { BOOST_LOG(error) << "Couldn't get cursor from XFixesGetCursorImage"sv; @@ -370,8 +351,7 @@ namespace platf { auto alpha = (*(uint *) pixel_p) >> 24u; if (alpha == 255) { *pixels_begin = *pixel_p; - } - else { + } 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; @@ -398,18 +378,20 @@ namespace platf { // int env_width, env_height; x11_attr_t(mem_type_e mem_type): - xdisplay { x11::OpenDisplay(nullptr) }, xwindow {}, xattr {}, mem_type { mem_type } { + xdisplay {x11::OpenDisplay(nullptr)}, + xwindow {}, + xattr {}, + mem_type {mem_type} { x11::InitThreads(); } - int - init(const std::string &display_name, const ::video::config_t &config) { + int init(const std::string &display_name, const ::video::config_t &config) { if (!xdisplay) { BOOST_LOG(error) << "Could not open X11 display"sv; return -1; } - delay = std::chrono::nanoseconds { 1s } / config.framerate; + delay = std::chrono::nanoseconds {1s} / config.framerate; xwindow = DefaultRootWindow(xdisplay.get()); @@ -422,13 +404,13 @@ namespace platf { if (streamedMonitor != -1) { BOOST_LOG(info) << "Configuring selected display ("sv << streamedMonitor << ") to stream"sv; - screen_res_t screenr { x11::rr::GetScreenResources(xdisplay.get(), xwindow) }; + screen_res_t screenr {x11::rr::GetScreenResources(xdisplay.get(), xwindow)}; int output = screenr->noutput; output_info_t result; int monitor = 0; for (int x = 0; x < output; ++x) { - output_info_t out_info { x11::rr::GetOutputInfo(xdisplay.get(), screenr.get(), screenr->outputs[x]) }; + output_info_t out_info {x11::rr::GetOutputInfo(xdisplay.get(), screenr.get(), screenr->outputs[x])}; if (out_info) { if (monitor++ == streamedMonitor) { result = std::move(out_info); @@ -443,7 +425,7 @@ namespace platf { } if (result->crtc) { - crtc_info_t crt_info { x11::rr::GetCrtcInfo(xdisplay.get(), screenr.get(), result->crtc) }; + crtc_info_t crt_info {x11::rr::GetCrtcInfo(xdisplay.get(), screenr.get(), result->crtc)}; BOOST_LOG(info) << "Streaming display: "sv << result->name << " with res "sv << crt_info->width << 'x' << crt_info->height << " offset by "sv << crt_info->x << 'x' << crt_info->y; @@ -451,14 +433,12 @@ namespace platf { height = crt_info->height; offset_x = crt_info->x; offset_y = crt_info->y; - } - else { + } else { BOOST_LOG(warning) << "Couldn't get requested display info, defaulting to recording entire virtual desktop"sv; width = xattr.width; height = xattr.height; } - } - else { + } else { width = xattr.width; height = xattr.height; } @@ -472,13 +452,11 @@ namespace platf { /** * Called when the display attributes should change. */ - void - refresh() { + void refresh() { x11::GetWindowAttributes(xdisplay.get(), xwindow, &xattr); // Update xattr's } - capture_e - capture(const push_captured_image_cb_t &push_captured_image_cb, const pull_free_image_cb_t &pull_free_image_cb, bool *cursor) override { + capture_e capture(const push_captured_image_cb_t &push_captured_image_cb, const pull_free_image_cb_t &pull_free_image_cb, bool *cursor) override { auto next_frame = std::chrono::steady_clock::now(); sleep_overshoot_logger.reset(); @@ -523,8 +501,7 @@ namespace platf { return capture_e::ok; } - capture_e - snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr &img_out, std::chrono::milliseconds timeout, bool cursor) { + capture_e snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr &img_out, std::chrono::milliseconds timeout, bool cursor) { refresh(); // The whole X server changed, so we must reinit everything @@ -538,7 +515,7 @@ namespace platf { } auto img = (x11_img_t *) img_out.get(); - XImage *x_img { x11::GetImage(xdisplay.get(), xwindow, offset_x, offset_y, width, height, AllPlanes, ZPixmap) }; + XImage *x_img {x11::GetImage(xdisplay.get(), xwindow, offset_x, offset_y, width, height, AllPlanes, ZPixmap)}; img->frame_timestamp = std::chrono::steady_clock::now(); img->width = x_img->width; @@ -555,13 +532,11 @@ namespace platf { return capture_e::ok; } - std::shared_ptr - alloc_img() override { + std::shared_ptr alloc_img() override { return std::make_shared(); } - std::unique_ptr - make_avcodec_encode_device(pix_fmt_e pix_fmt) override { + std::unique_ptr make_avcodec_encode_device(pix_fmt_e pix_fmt) override { #ifdef SUNSHINE_BUILD_VAAPI if (mem_type == mem_type_e::vaapi) { return va::make_avcodec_encode_device(width, height, false); @@ -577,8 +552,7 @@ namespace platf { return std::make_unique(); } - int - dummy_img(img_t *img) override { + int dummy_img(img_t *img) override { // TODO: stop cheating and give black image if (!img) { return -1; @@ -605,15 +579,15 @@ namespace platf { task_pool_util::TaskPool::task_id_t refresh_task_id; - void - delayed_refresh() { + void delayed_refresh() { refresh(); refresh_task_id = task_pool.pushDelayed(&shm_attr_t::delayed_refresh, 2s, this).task_id; } shm_attr_t(mem_type_e mem_type): - x11_attr_t(mem_type), shm_xdisplay { x11::OpenDisplay(nullptr) } { + x11_attr_t(mem_type), + shm_xdisplay {x11::OpenDisplay(nullptr)} { refresh_task_id = task_pool.pushDelayed(&shm_attr_t::delayed_refresh, 2s, this).task_id; } @@ -621,8 +595,7 @@ namespace platf { while (!task_pool.cancel(refresh_task_id)); } - capture_e - capture(const push_captured_image_cb_t &push_captured_image_cb, const pull_free_image_cb_t &pull_free_image_cb, bool *cursor) override { + capture_e capture(const push_captured_image_cb_t &push_captured_image_cb, const pull_free_image_cb_t &pull_free_image_cb, bool *cursor) override { auto next_frame = std::chrono::steady_clock::now(); sleep_overshoot_logger.reset(); @@ -667,18 +640,16 @@ namespace platf { return capture_e::ok; } - capture_e - snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr &img_out, std::chrono::milliseconds timeout, bool cursor) { + capture_e snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr &img_out, std::chrono::milliseconds timeout, bool cursor) { // The whole X server changed, so we must reinit everything if (xattr.width != env_width || xattr.height != env_height) { BOOST_LOG(warning) << "X dimensions changed in SHM mode, request reinit"sv; return capture_e::reinit; - } - else { + } 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); auto frame_timestamp = std::chrono::steady_clock::now(); - 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; @@ -699,8 +670,7 @@ namespace platf { } } - std::shared_ptr - alloc_img() override { + std::shared_ptr alloc_img() override { auto img = std::make_shared(); img->width = width; img->height = height; @@ -711,13 +681,11 @@ namespace platf { return img; } - int - dummy_img(platf::img_t *img) override { + int dummy_img(platf::img_t *img) override { return 0; } - int - init(const std::string &display_name, const ::video::config_t &config) { + int init(const std::string &display_name, const ::video::config_t &config) { if (x11_attr_t::init(display_name, config)) { return 1; } @@ -756,14 +724,12 @@ namespace platf { return 0; } - std::uint32_t - frame_size() { + std::uint32_t frame_size() { return width * height * 4; } }; - std::shared_ptr - x11_display(platf::mem_type_e hwdevice_type, const std::string &display_name, const ::video::config_t &config) { + std::shared_ptr x11_display(platf::mem_type_e hwdevice_type, const std::string &display_name, const ::video::config_t &config) { if (hwdevice_type != platf::mem_type_e::system && hwdevice_type != platf::mem_type_e::vaapi && hwdevice_type != platf::mem_type_e::cuda) { BOOST_LOG(error) << "Could not initialize x11 display with the given hw device type"sv; return nullptr; @@ -797,8 +763,7 @@ namespace platf { return x11_disp; } - std::vector - x11_display_names() { + std::vector x11_display_names() { if (load_x11() || load_xcb()) { BOOST_LOG(error) << "Couldn't init x11 libraries"sv; @@ -807,18 +772,18 @@ namespace platf { BOOST_LOG(info) << "Detecting displays"sv; - x11::xdisplay_t xdisplay { x11::OpenDisplay(nullptr) }; + x11::xdisplay_t xdisplay {x11::OpenDisplay(nullptr)}; if (!xdisplay) { return {}; } auto xwindow = DefaultRootWindow(xdisplay.get()); - screen_res_t screenr { x11::rr::GetScreenResources(xdisplay.get(), xwindow) }; + screen_res_t screenr {x11::rr::GetScreenResources(xdisplay.get(), xwindow)}; int output = screenr->noutput; int monitor = 0; for (int x = 0; x < output; ++x) { - output_info_t out_info { x11::rr::GetOutputInfo(xdisplay.get(), screenr.get(), screenr->outputs[x]) }; + output_info_t out_info {x11::rr::GetOutputInfo(xdisplay.get(), screenr.get(), screenr->outputs[x])}; if (out_info) { BOOST_LOG(info) << "Detected display: "sv << out_info->name << " (id: "sv << monitor << ")"sv << out_info->name << " connected: "sv << (out_info->connection == RR_Connected); ++monitor; @@ -835,25 +800,22 @@ namespace platf { return names; } - void - freeImage(XImage *p) { + void freeImage(XImage *p) { XDestroyImage(p); } - void - freeX(XFixesCursorImage *p) { + + void freeX(XFixesCursorImage *p) { x11::Free(p); } - int - load_xcb() { + int load_xcb() { // This will be called once only static int xcb_status = xcb::init_shm() || xcb::init(); return xcb_status; } - int - load_x11() { + int load_x11() { // This will be called once only static int x11_status = window_system == window_system_e::NONE || @@ -863,8 +825,7 @@ namespace platf { } namespace x11 { - std::optional - cursor_t::make() { + std::optional cursor_t::make() { if (load_x11()) { return std::nullopt; } @@ -876,8 +837,7 @@ namespace platf { return cursor; } - void - cursor_t::capture(egl::cursor_t &img) { + void cursor_t::capture(egl::cursor_t &img) { auto display = (xdisplay_t::pointer) ctx.get(); xcursor_t xcursor = fix::GetCursorImage(display); @@ -904,23 +864,19 @@ namespace platf { img.serial = xcursor->cursor_serial; } - void - cursor_t::blend(img_t &img, int offsetX, int offsetY) { + void cursor_t::blend(img_t &img, int offsetX, int offsetY) { blend_cursor((xdisplay_t::pointer) ctx.get(), img, offsetX, offsetY); } - xdisplay_t - make_display() { + xdisplay_t make_display() { return OpenDisplay(nullptr); } - void - freeDisplay(_XDisplay *xdisplay) { + void freeDisplay(_XDisplay *xdisplay) { CloseDisplay(xdisplay); } - void - freeCursorCtx(cursor_ctx_t::pointer ctx) { + void freeCursorCtx(cursor_ctx_t::pointer ctx) { CloseDisplay((xdisplay_t::pointer) ctx); } } // namespace x11 diff --git a/src/platform/linux/x11grab.h b/src/platform/linux/x11grab.h index 13229490..9d0a1864 100644 --- a/src/platform/linux/x11grab.h +++ b/src/platform/linux/x11grab.h @@ -4,8 +4,10 @@ */ #pragma once +// standard includes #include +// local includes #include "src/platform/common.h" #include "src/utility.h" @@ -18,21 +20,17 @@ namespace egl { namespace platf::x11 { struct cursor_ctx_raw_t; - void - freeCursorCtx(cursor_ctx_raw_t *ctx); - void - freeDisplay(_XDisplay *xdisplay); + void freeCursorCtx(cursor_ctx_raw_t *ctx); + void freeDisplay(_XDisplay *xdisplay); using cursor_ctx_t = util::safe_ptr; using xdisplay_t = util::safe_ptr<_XDisplay, freeDisplay>; class cursor_t { public: - static std::optional - make(); + static std::optional make(); - void - capture(egl::cursor_t &img); + void capture(egl::cursor_t &img); /** * Capture and blend the cursor into the image @@ -40,12 +38,10 @@ namespace platf::x11 { * img <-- destination image * offsetX, offsetY <--- Top left corner of the virtual screen */ - void - blend(img_t &img, int offsetX, int offsetY); + void blend(img_t &img, int offsetX, int offsetY); cursor_ctx_t ctx; }; - xdisplay_t - make_display(); + xdisplay_t make_display(); } // namespace platf::x11 diff --git a/src/platform/macos/av_audio.h b/src/platform/macos/av_audio.h index bf5b13b3..9ef1cca2 100644 --- a/src/platform/macos/av_audio.h +++ b/src/platform/macos/av_audio.h @@ -4,8 +4,10 @@ */ #pragma once +// platform includes #import +// lib includes #include "third-party/TPCircularBuffer/TPCircularBuffer.h" #define kBufferLength 4096 diff --git a/src/platform/macos/av_audio.m b/src/platform/macos/av_audio.m index f0e631ef..a274cd6c 100644 --- a/src/platform/macos/av_audio.m +++ b/src/platform/macos/av_audio.m @@ -2,12 +2,13 @@ * @file src/platform/macos/av_audio.m * @brief Definitions for audio capture on macOS. */ +// local includes #import "av_audio.h" @implementation AVAudio + (NSArray *)microphones { - if ([[NSProcessInfo processInfo] isOperatingSystemAtLeastVersion:((NSOperatingSystemVersion) { 10, 15, 0 })]) { + if ([[NSProcessInfo processInfo] isOperatingSystemAtLeastVersion:((NSOperatingSystemVersion) {10, 15, 0})]) { // This will generate a warning about AVCaptureDeviceDiscoverySession being // unavailable before macOS 10.15, but we have a guard to prevent it from // being called on those earlier systems. @@ -16,14 +17,12 @@ // a different method. #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunguarded-availability-new" - AVCaptureDeviceDiscoverySession *discoverySession = [AVCaptureDeviceDiscoverySession discoverySessionWithDeviceTypes:@[AVCaptureDeviceTypeBuiltInMicrophone, - AVCaptureDeviceTypeExternalUnknown] + AVCaptureDeviceDiscoverySession *discoverySession = [AVCaptureDeviceDiscoverySession discoverySessionWithDeviceTypes:@[AVCaptureDeviceTypeBuiltInMicrophone, AVCaptureDeviceTypeExternalUnknown] mediaType:AVMediaTypeAudio position:AVCaptureDevicePositionUnspecified]; return discoverySession.devices; #pragma clang diagnostic pop - } - else { + } else { // We're intentionally using a deprecated API here specifically for versions // of macOS where it's not deprecated, so we can ignore any deprecation // warnings: @@ -75,8 +74,7 @@ if ([self.audioCaptureSession canAddInput:audioInput]) { [self.audioCaptureSession addInput:audioInput]; - } - else { + } else { [audioInput dealloc]; return -1; } @@ -92,17 +90,14 @@ (NSString *) AVLinearPCMIsNonInterleaved: @NO }]; - dispatch_queue_attr_t qos = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_CONCURRENT, - QOS_CLASS_USER_INITIATED, - DISPATCH_QUEUE_PRIORITY_HIGH); + dispatch_queue_attr_t qos = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_CONCURRENT, QOS_CLASS_USER_INITIATED, DISPATCH_QUEUE_PRIORITY_HIGH); dispatch_queue_t recordingQueue = dispatch_queue_create("audioSamplingQueue", qos); [audioOutput setSampleBufferDelegate:self queue:recordingQueue]; if ([self.audioCaptureSession canAddOutput:audioOutput]) { [self.audioCaptureSession addOutput:audioOutput]; - } - else { + } else { [audioInput release]; [audioOutput release]; return -1; diff --git a/src/platform/macos/av_img_t.h b/src/platform/macos/av_img_t.h index 202aa5f8..50896c4c 100644 --- a/src/platform/macos/av_img_t.h +++ b/src/platform/macos/av_img_t.h @@ -4,17 +4,20 @@ */ #pragma once -#include "src/platform/common.h" - +// platform includes #include #include +// local includes +#include "src/platform/common.h" + namespace platf { struct av_sample_buf_t { CMSampleBufferRef buf; explicit av_sample_buf_t(CMSampleBufferRef buf): - buf((CMSampleBufferRef) CFRetain(buf)) {} + buf((CMSampleBufferRef) CFRetain(buf)) { + } ~av_sample_buf_t() { if (buf != nullptr) { @@ -29,12 +32,12 @@ namespace platf { // Constructor explicit av_pixel_buf_t(CMSampleBufferRef sb): buf( - CMSampleBufferGetImageBuffer(sb)) { + CMSampleBufferGetImageBuffer(sb) + ) { CVPixelBufferLockBaseAddress(buf, kCVPixelBufferLock_ReadOnly); } - [[nodiscard]] uint8_t * - data() const { + [[nodiscard]] uint8_t *data() const { return static_cast(CVPixelBufferGetBaseAddress(buf)); } @@ -59,8 +62,11 @@ namespace platf { temp_retain_av_img_t( std::shared_ptr sb, std::shared_ptr pb, - uint8_t *dt): + uint8_t *dt + ): sample_buffer(std::move(sb)), - pixel_buffer(std::move(pb)), data(dt) {} + pixel_buffer(std::move(pb)), + data(dt) { + } }; } // namespace platf diff --git a/src/platform/macos/av_video.h b/src/platform/macos/av_video.h index 1525372d..4ff24609 100644 --- a/src/platform/macos/av_video.h +++ b/src/platform/macos/av_video.h @@ -4,8 +4,9 @@ */ #pragma once -#import +// platform includes #import +#import struct CaptureSession { AVCaptureVideoDataOutput *output; diff --git a/src/platform/macos/av_video.m b/src/platform/macos/av_video.m index bb7723bb..34d70a0c 100644 --- a/src/platform/macos/av_video.m +++ b/src/platform/macos/av_video.m @@ -2,6 +2,7 @@ * @file src/platform/macos/av_video.m * @brief Definitions for video capture on macOS. */ +// local includes #import "av_video.h" @implementation AVVideo @@ -62,8 +63,7 @@ if ([self.session canAddInput:screenInput]) { [self.session addInput:screenInput]; - } - else { + } else { [screenInput release]; return nil; } @@ -97,9 +97,7 @@ (NSString *) AVVideoScalingModeKey: AVVideoScalingModeResizeAspect, }]; - dispatch_queue_attr_t qos = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, - QOS_CLASS_USER_INITIATED, - DISPATCH_QUEUE_PRIORITY_HIGH); + dispatch_queue_attr_t qos = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_USER_INITIATED, DISPATCH_QUEUE_PRIORITY_HIGH); dispatch_queue_t recordingQueue = dispatch_queue_create("videoCaptureQueue", qos); [videoOutput setSampleBufferDelegate:self queue:recordingQueue]; @@ -107,8 +105,7 @@ if ([self.session canAddOutput:videoOutput]) { [self.session addOutput:videoOutput]; - } - else { + } else { [videoOutput release]; return nil; } diff --git a/src/platform/macos/display.mm b/src/platform/macos/display.mm index 5220c68d..be124b2d 100644 --- a/src/platform/macos/display.mm +++ b/src/platform/macos/display.mm @@ -2,15 +2,15 @@ * @file src/platform/macos/display.mm * @brief Definitions for display capture on macOS. */ +// local includes +#include "src/config.h" +#include "src/logging.h" #include "src/platform/common.h" #include "src/platform/macos/av_img_t.h" #include "src/platform/macos/av_video.h" #include "src/platform/macos/misc.h" #include "src/platform/macos/nv12_zero_device.h" -#include "src/config.h" -#include "src/logging.h" - // Avoid conflict between AVFoundation and libavutil both defining AVMediaType #define AVMediaType AVMediaType_FFmpeg #include "src/video.h" @@ -29,8 +29,7 @@ namespace platf { [av_capture release]; } - capture_e - capture(const push_captured_image_cb_t &push_captured_image_cb, const pull_free_image_cb_t &pull_free_image_cb, bool *cursor) override { + capture_e capture(const push_captured_image_cb_t &push_captured_image_cb, const pull_free_image_cb_t &pull_free_image_cb, bool *cursor) override { auto signal = [av_capture capture:^(CMSampleBufferRef sampleBuffer) { auto new_sample_buffer = std::make_shared(sampleBuffer); auto new_pixel_buffer = std::make_shared(new_sample_buffer->buf); @@ -46,7 +45,8 @@ namespace platf { auto old_data_retainer = std::make_shared( av_img->sample_buffer, av_img->pixel_buffer, - img_out->data); + img_out->data + ); av_img->sample_buffer = new_sample_buffer; av_img->pixel_buffer = new_pixel_buffer; @@ -74,33 +74,28 @@ namespace platf { return capture_e::ok; } - std::shared_ptr - alloc_img() override { + std::shared_ptr alloc_img() override { return std::make_shared(); } - std::unique_ptr - make_avcodec_encode_device(pix_fmt_e pix_fmt) override { + std::unique_ptr make_avcodec_encode_device(pix_fmt_e pix_fmt) override { if (pix_fmt == pix_fmt_e::yuv420p) { av_capture.pixelFormat = kCVPixelFormatType_32BGRA; return std::make_unique(); - } - else if (pix_fmt == pix_fmt_e::nv12 || pix_fmt == pix_fmt_e::p010) { + } else if (pix_fmt == pix_fmt_e::nv12 || pix_fmt == pix_fmt_e::p010) { auto device = std::make_unique(); device->init(static_cast(av_capture), pix_fmt, setResolution, setPixelFormat); return device; - } - else { + } else { BOOST_LOG(error) << "Unsupported Pixel Format."sv; return nullptr; } } - int - dummy_img(img_t *img) override { + int dummy_img(img_t *img) override { if (!platf::is_screen_capture_allowed()) { // If we don't have the screen capture permission, this function will hang // indefinitely without doing anything useful. Exit instead to avoid this. @@ -117,7 +112,8 @@ namespace platf { auto old_data_retainer = std::make_shared( av_img->sample_buffer, av_img->pixel_buffer, - img->data); + img->data + ); av_img->sample_buffer = new_sample_buffer; av_img->pixel_buffer = new_pixel_buffer; @@ -146,19 +142,16 @@ namespace platf { * width --> the intended capture width * height --> the intended capture height */ - static void - setResolution(void *display, int width, int height) { + static void setResolution(void *display, int width, int height) { [static_cast(display) setFrameWidth:width frameHeight:height]; } - static void - setPixelFormat(void *display, OSType pixelFormat) { + static void setPixelFormat(void *display, OSType pixelFormat) { static_cast(display).pixelFormat = pixelFormat; } }; - std::shared_ptr - display(platf::mem_type_e hwdevice_type, const std::string &display_name, const video::config_t &config) { + std::shared_ptr display(platf::mem_type_e hwdevice_type, const std::string &display_name, const video::config_t &config) { if (hwdevice_type != platf::mem_type_e::system && hwdevice_type != platf::mem_type_e::videotoolbox) { BOOST_LOG(error) << "Could not initialize display with the given hw device type."sv; return nullptr; @@ -200,8 +193,7 @@ namespace platf { return display; } - std::vector - display_names(mem_type_e hwdevice_type) { + std::vector display_names(mem_type_e hwdevice_type) { __block std::vector display_names; auto display_array = [AVVideo displayNames]; @@ -219,8 +211,7 @@ namespace platf { * @brief Returns if GPUs/drivers have changed since the last call to this function. * @return `true` if a change has occurred or if it is unknown whether a change occurred. */ - bool - needs_encoder_reenumeration() { + bool needs_encoder_reenumeration() { // We don't track GPU state, so we will always reenumerate. Fortunately, it is fast on macOS. return true; } diff --git a/src/platform/macos/input.cpp b/src/platform/macos/input.cpp index bc6cf394..7e61ab4b 100644 --- a/src/platform/macos/input.cpp +++ b/src/platform/macos/input.cpp @@ -2,22 +2,24 @@ * @file src/platform/macos/input.cpp * @brief Definitions for macOS input handling. */ -#include "src/input.h" - -#import +// standard includes #include +#include +#include + +// platform includes +#include +#import +#include #include +// local includes #include "src/display_device.h" +#include "src/input.h" #include "src/logging.h" #include "src/platform/common.h" #include "src/utility.h" -#include -#include -#include -#include - /** * @brief Delay for a double click, in milliseconds. * @todo Make this configurable. @@ -50,8 +52,7 @@ namespace platf { }; // Customized less operator for using std::lower_bound() on a KeyCodeMap array. - bool - operator<(const KeyCodeMap &a, const KeyCodeMap &b) { + bool operator<(const KeyCodeMap &a, const KeyCodeMap &b) { return a.win_keycode < b.win_keycode; } @@ -227,13 +228,15 @@ const KeyCodeMap kKeyCodesMap[] = { }; // clang-format on - int - keysym(int keycode) { + int keysym(int keycode) { KeyCodeMap key_map {}; key_map.win_keycode = keycode; const KeyCodeMap *temp_map = std::lower_bound( - kKeyCodesMap, kKeyCodesMap + sizeof(kKeyCodesMap) / sizeof(kKeyCodesMap[0]), key_map); + kKeyCodesMap, + kKeyCodesMap + sizeof(kKeyCodesMap) / sizeof(kKeyCodesMap[0]), + key_map + ); if (temp_map >= kKeyCodesMap + sizeof(kKeyCodesMap) / sizeof(kKeyCodesMap[0]) || temp_map->win_keycode != keycode || temp_map->mac_keycode == -1) { @@ -243,8 +246,7 @@ const KeyCodeMap kKeyCodesMap[] = { return temp_map->mac_keycode; } - void - keyboard_update(input_t &input, uint16_t modcode, bool release, uint8_t flags) { + void keyboard_update(input_t &input, uint16_t modcode, bool release, uint8_t flags) { auto key = keysym(modcode); BOOST_LOG(debug) << "got keycode: 0x"sv << std::hex << modcode << ", translated to: 0x" << std::hex << key << ", release:" << release; @@ -284,8 +286,7 @@ const KeyCodeMap kKeyCodesMap[] = { macos_input->kb_flags = release ? macos_input->kb_flags & ~mask : macos_input->kb_flags | mask; CGEventSetType(event, kCGEventFlagsChanged); CGEventSetFlags(event, macos_input->kb_flags); - } - else { + } else { CGEventSetIntegerValueField(event, kCGKeyboardEventKeycode, key); CGEventSetType(event, release ? kCGEventKeyUp : kCGEventKeyDown); } @@ -293,30 +294,25 @@ const KeyCodeMap kKeyCodesMap[] = { CGEventPost(kCGHIDEventTap, event); } - void - unicode(input_t &input, char *utf8, int size) { + void unicode(input_t &input, char *utf8, int size) { BOOST_LOG(info) << "unicode: Unicode input not yet implemented for MacOS."sv; } - int - alloc_gamepad(input_t &input, const gamepad_id_t &id, const gamepad_arrival_t &metadata, feedback_queue_t feedback_queue) { + int alloc_gamepad(input_t &input, const gamepad_id_t &id, const gamepad_arrival_t &metadata, feedback_queue_t feedback_queue) { BOOST_LOG(info) << "alloc_gamepad: Gamepad not yet implemented for MacOS."sv; return -1; } - void - free_gamepad(input_t &input, int nr) { + void free_gamepad(input_t &input, int nr) { BOOST_LOG(info) << "free_gamepad: Gamepad not yet implemented for MacOS."sv; } - void - gamepad_update(input_t &input, int nr, const gamepad_state_t &gamepad_state) { + void gamepad_update(input_t &input, int nr, const gamepad_state_t &gamepad_state) { BOOST_LOG(info) << "gamepad: Gamepad not yet implemented for MacOS."sv; } // returns current mouse location: - util::point_t - get_mouse_loc(input_t &input) { + util::point_t get_mouse_loc(input_t &input) { // Creating a new event every time to avoid any reuse risk const auto macos_input = static_cast(input.get()); const auto snapshot_event = CGEventCreate(macos_input->source); @@ -328,14 +324,14 @@ const KeyCodeMap kKeyCodesMap[] = { }; } - void - post_mouse( + void post_mouse( input_t &input, const CGMouseButton button, const CGEventType type, const util::point_t raw_location, const util::point_t previous_location, - const int click_count) { + const int click_count + ) { BOOST_LOG(debug) << "mouse_event: "sv << button << ", type: "sv << type << ", location:"sv << raw_location.x << ":"sv << raw_location.y << " click_count: "sv << click_count; const auto macos_input = static_cast(input.get()); @@ -368,8 +364,7 @@ const KeyCodeMap kKeyCodesMap[] = { CGWarpMouseCursorPosition(location); } - inline CGEventType - event_type_mouse(input_t &input) { + inline CGEventType event_type_mouse(input_t &input) { const auto macos_input = static_cast(input.get()); if (macos_input->mouse_down[0]) { @@ -384,28 +379,28 @@ const KeyCodeMap kKeyCodesMap[] = { return kCGEventMouseMoved; } - void - move_mouse( + void move_mouse( input_t &input, const int deltaX, - const int deltaY) { + const int deltaY + ) { const auto current = get_mouse_loc(input); - const auto location = util::point_t { current.x + deltaX, current.y + deltaY }; + const auto location = util::point_t {current.x + deltaX, current.y + deltaY}; post_mouse(input, kCGMouseButtonLeft, event_type_mouse(input), location, current, 0); } - void - abs_mouse( + void abs_mouse( input_t &input, const touch_port_t &touch_port, const float x, - const float y) { + const float y + ) { const auto macos_input = static_cast(input.get()); const auto scaling = macos_input->displayScaling; const auto display = macos_input->display; - auto location = util::point_t { x * scaling, y * scaling }; + auto location = util::point_t {x * scaling, y * scaling}; CGRect display_bounds = CGDisplayBounds(display); // in order to get the correct mouse location for capturing display , we need to add the display bounds to the location location.x += display_bounds.origin.x; @@ -414,8 +409,7 @@ const KeyCodeMap kKeyCodesMap[] = { post_mouse(input, kCGMouseButtonLeft, event_type_mouse(input), location, get_mouse_loc(input), 0); } - void - button_mouse(input_t &input, const int button, const bool release) { + void button_mouse(input_t &input, const int button, const bool release) { CGMouseButton mac_button; CGEventType event; @@ -447,26 +441,26 @@ const KeyCodeMap kKeyCodesMap[] = { if (now < macos_input->last_mouse_event[mac_button][release] + MULTICLICK_DELAY_MS) { post_mouse(input, mac_button, event, mouse_position, mouse_position, 2); - } - else { + } else { post_mouse(input, mac_button, event, mouse_position, mouse_position, 1); } macos_input->last_mouse_event[mac_button][release] = now; } - void - scroll(input_t &input, const int high_res_distance) { + void scroll(input_t &input, const int high_res_distance) { CGEventRef upEvent = CGEventCreateScrollWheelEvent( nullptr, kCGScrollEventUnitLine, - 2, high_res_distance > 0 ? 1 : -1, high_res_distance); + 2, + high_res_distance > 0 ? 1 : -1, + high_res_distance + ); CGEventPost(kCGHIDEventTap, upEvent); CFRelease(upEvent); } - void - hscroll(input_t &input, int high_res_distance) { + void hscroll(input_t &input, int high_res_distance) { // Unimplemented } @@ -475,8 +469,7 @@ const KeyCodeMap kKeyCodesMap[] = { * @param input The global input context. * @return A unique pointer to a per-client input data context. */ - std::unique_ptr - allocate_client_input_context(input_t &input) { + std::unique_ptr allocate_client_input_context(input_t &input) { // Unused return nullptr; } @@ -487,8 +480,7 @@ const KeyCodeMap kKeyCodesMap[] = { * @param touch_port The current viewport for translating to screen coordinates. * @param touch The touch event. */ - void - touch_update(client_input_t *input, const touch_port_t &touch_port, const touch_input_t &touch) { + void touch_update(client_input_t *input, const touch_port_t &touch_port, const touch_input_t &touch) { // Unimplemented feature - platform_caps::pen_touch } @@ -498,8 +490,7 @@ const KeyCodeMap kKeyCodesMap[] = { * @param touch_port The current viewport for translating to screen coordinates. * @param pen The pen event. */ - void - pen_update(client_input_t *input, const touch_port_t &touch_port, const pen_input_t &pen) { + void pen_update(client_input_t *input, const touch_port_t &touch_port, const pen_input_t &pen) { // Unimplemented feature - platform_caps::pen_touch } @@ -508,8 +499,7 @@ const KeyCodeMap kKeyCodesMap[] = { * @param input The global input context. * @param touch The touch event. */ - void - gamepad_touch(input_t &input, const gamepad_touch_t &touch) { + void gamepad_touch(input_t &input, const gamepad_touch_t &touch) { // Unimplemented feature - platform_caps::controller_touch } @@ -518,8 +508,7 @@ const KeyCodeMap kKeyCodesMap[] = { * @param input The global input context. * @param motion The motion event. */ - void - gamepad_motion(input_t &input, const gamepad_motion_t &motion) { + void gamepad_motion(input_t &input, const gamepad_motion_t &motion) { // Unimplemented } @@ -528,14 +517,12 @@ const KeyCodeMap kKeyCodesMap[] = { * @param input The global input context. * @param battery The battery event. */ - void - gamepad_battery(input_t &input, const gamepad_battery_t &battery) { + void gamepad_battery(input_t &input, const gamepad_battery_t &battery) { // Unimplemented } - input_t - input() { - input_t result { new macos_input_t() }; + input_t input() { + input_t result {new macos_input_t()}; const auto macos_input = static_cast(result.get()); @@ -550,8 +537,7 @@ const KeyCodeMap kKeyCodesMap[] = { CGDirectDisplayID displays[max_display]; if (CGGetActiveDisplayList(max_display, displays, &display_count) != kCGErrorSuccess) { BOOST_LOG(error) << "Unable to get active display list , error: "sv << std::endl; - } - else { + } else { for (int i = 0; i < display_count; i++) { CGDirectDisplayID display_id = displays[i]; if (display_id == std::atoi(output_name.c_str())) { @@ -581,8 +567,7 @@ const KeyCodeMap kKeyCodesMap[] = { return result; } - void - freeInput(void *p) { + void freeInput(void *p) { const auto *input = static_cast(p); CFRelease(input->source); @@ -592,10 +577,9 @@ const KeyCodeMap kKeyCodesMap[] = { delete input; } - std::vector & - supported_gamepads(input_t *input) { + std::vector &supported_gamepads(input_t *input) { static std::vector gamepads { - supported_gamepad_t { "", false, "gamepads.macos_not_implemented" } + supported_gamepad_t {"", false, "gamepads.macos_not_implemented"} }; return gamepads; @@ -605,8 +589,7 @@ const KeyCodeMap kKeyCodesMap[] = { * @brief Returns the supported platform capabilities to advertise to the client. * @return Capability flags. */ - platform_caps::caps_t - get_capabilities() { + platform_caps::caps_t get_capabilities() { return 0; } } // namespace platf diff --git a/src/platform/macos/microphone.mm b/src/platform/macos/microphone.mm index 8d2129f2..06b9c19a 100644 --- a/src/platform/macos/microphone.mm +++ b/src/platform/macos/microphone.mm @@ -2,11 +2,11 @@ * @file src/platform/macos/microphone.mm * @brief Definitions for microphone capture on macOS. */ -#include "src/platform/common.h" -#include "src/platform/macos/av_audio.h" - +// local includes #include "src/config.h" #include "src/logging.h" +#include "src/platform/common.h" +#include "src/platform/macos/av_audio.h" namespace platf { using namespace std::literals; @@ -18,8 +18,7 @@ namespace platf { [av_audio_capture release]; } - capture_e - sample(std::vector &sample_in) override { + capture_e sample(std::vector &sample_in) override { auto sample_size = sample_in.size(); uint32_t length = 0; @@ -45,14 +44,12 @@ namespace platf { AVCaptureDevice *audio_capture_device {}; public: - int - set_sink(const std::string &sink) override { + int set_sink(const std::string &sink) override { BOOST_LOG(warning) << "audio_control_t::set_sink() unimplemented: "sv << sink; return 0; } - std::unique_ptr - microphone(const std::uint8_t *mapping, int channels, std::uint32_t sample_rate, std::uint32_t frame_size) override { + std::unique_ptr microphone(const std::uint8_t *mapping, int channels, std::uint32_t sample_rate, std::uint32_t frame_size) override { auto mic = std::make_unique(); const char *audio_sink = ""; @@ -81,22 +78,19 @@ namespace platf { return mic; } - bool - is_sink_available(const std::string &sink) override { + bool is_sink_available(const std::string &sink) override { BOOST_LOG(warning) << "audio_control_t::is_sink_available() unimplemented: "sv << sink; return true; } - std::optional - sink_info() override { + std::optional sink_info() override { sink_t sink; return sink; } }; - std::unique_ptr - audio_control() { + std::unique_ptr audio_control() { return std::make_unique(); } } // namespace platf diff --git a/src/platform/macos/misc.h b/src/platform/macos/misc.h index 47d22ed4..e791661d 100644 --- a/src/platform/macos/misc.h +++ b/src/platform/macos/misc.h @@ -4,21 +4,20 @@ */ #pragma once +// standard includes #include +// platform includes #include namespace platf { - bool - is_screen_capture_allowed(); + bool is_screen_capture_allowed(); } namespace dyn { typedef void (*apiproc)(); - int - load(void *handle, const std::vector> &funcs, bool strict = true); - void * - handle(const std::vector &libs); + int load(void *handle, const std::vector> &funcs, bool strict = true); + void *handle(const std::vector &libs); } // namespace dyn diff --git a/src/platform/macos/misc.mm b/src/platform/macos/misc.mm index 9f11669f..540dd74f 100644 --- a/src/platform/macos/misc.mm +++ b/src/platform/macos/misc.mm @@ -8,24 +8,29 @@ #define __APPLE_USE_RFC_3542 1 #endif -#include -#include -#include +// standard includes #include #include + +// platform includes +#include +#include +#include #include #include #include +// lib includes +#include +#include +#include + +// local includes #include "misc.h" #include "src/entry_handler.h" #include "src/logging.h" #include "src/platform/common.h" -#include -#include -#include - using namespace std::literals; namespace fs = std::filesystem; namespace bp = boost::process; @@ -37,24 +42,20 @@ namespace platf { #if __MAC_OS_X_VERSION_MAX_ALLOWED < 110000 // __MAC_11_0 // If they're not in the SDK then we can use our own function definitions. // Need to use weak import so that this will link in macOS 10.14 and earlier - extern "C" bool - CGPreflightScreenCaptureAccess(void) __attribute__((weak_import)); - extern "C" bool - CGRequestScreenCaptureAccess(void) __attribute__((weak_import)); + extern "C" bool CGPreflightScreenCaptureAccess(void) __attribute__((weak_import)); + extern "C" bool CGRequestScreenCaptureAccess(void) __attribute__((weak_import)); #endif namespace { - auto screen_capture_allowed = std::atomic { false }; + auto screen_capture_allowed = std::atomic {false}; } // namespace // Return whether screen capture is allowed for this process. - bool - is_screen_capture_allowed() { + bool is_screen_capture_allowed() { return screen_capture_allowed; } - std::unique_ptr - init() { + std::unique_ptr init() { // This will generate a warning about CGPreflightScreenCaptureAccess and // CGRequestScreenCaptureAccess being unavailable before macOS 10.15, but // we have a guard to prevent it from being called on those earlier systems. @@ -69,7 +70,7 @@ namespace platf { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunguarded-availability-new" #pragma clang diagnostic ignored "-Wtautological-pointer-compare" - if ([[NSProcessInfo processInfo] isOperatingSystemAtLeastVersion:((NSOperatingSystemVersion) { 10, 15, 0 })] && + if ([[NSProcessInfo processInfo] isOperatingSystemAtLeastVersion:((NSOperatingSystemVersion) {10, 15, 0})] && // Double check that these weakly-linked symbols have been loaded: CGPreflightScreenCaptureAccess != nullptr && CGRequestScreenCaptureAccess != nullptr && !CGPreflightScreenCaptureAccess()) { @@ -84,66 +85,55 @@ namespace platf { return std::make_unique(); } - fs::path - appdata() { + fs::path appdata() { const char *homedir; if ((homedir = getenv("HOME")) == nullptr) { homedir = getpwuid(geteuid())->pw_dir; } - return fs::path { homedir } / ".config/sunshine"sv; + return fs::path {homedir} / ".config/sunshine"sv; } using ifaddr_t = util::safe_ptr; - ifaddr_t - get_ifaddrs() { - ifaddrs *p { nullptr }; + ifaddr_t get_ifaddrs() { + ifaddrs *p {nullptr}; getifaddrs(&p); - return ifaddr_t { p }; + return ifaddr_t {p}; } - std::string - from_sockaddr(const sockaddr *const ip_addr) { + 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); - } - else if (family == AF_INET) { - inet_ntop(AF_INET, &((sockaddr_in *) ip_addr)->sin_addr, data, - INET_ADDRSTRLEN); + inet_ntop(AF_INET6, &((sockaddr_in6 *) ip_addr)->sin6_addr, data, INET6_ADDRSTRLEN); + } else if (family == AF_INET) { + inet_ntop(AF_INET, &((sockaddr_in *) ip_addr)->sin_addr, data, INET_ADDRSTRLEN); } - return std::string { data }; + return std::string {data}; } - std::pair - from_sockaddr_ex(const sockaddr *const ip_addr) { + std::pair from_sockaddr_ex(const sockaddr *const ip_addr) { char data[INET6_ADDRSTRLEN] = {}; auto family = ip_addr->sa_family; std::uint16_t port = 0; if (family == AF_INET6) { - inet_ntop(AF_INET6, &((sockaddr_in6 *) ip_addr)->sin6_addr, data, - INET6_ADDRSTRLEN); + inet_ntop(AF_INET6, &((sockaddr_in6 *) ip_addr)->sin6_addr, data, INET6_ADDRSTRLEN); port = ((sockaddr_in6 *) ip_addr)->sin6_port; - } - else if (family == AF_INET) { - inet_ntop(AF_INET, &((sockaddr_in *) ip_addr)->sin_addr, data, - INET_ADDRSTRLEN); + } else if (family == AF_INET) { + 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) { + 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) { @@ -160,8 +150,7 @@ namespace platf { ptr = (unsigned char *) LLADDR((struct sockaddr_dl *) (ifaptr)->ifa_addr); char buff[100]; - snprintf(buff, sizeof(buff), "%02x:%02x:%02x:%02x:%02x:%02x", - *ptr, *(ptr + 1), *(ptr + 2), *(ptr + 3), *(ptr + 4), *(ptr + 5)); + snprintf(buff, sizeof(buff), "%02x:%02x:%02x:%02x:%02x:%02x", *ptr, *(ptr + 1), *(ptr + 2), *(ptr + 3), *(ptr + 4), *(ptr + 5)); mac_address = buff; break; } @@ -181,8 +170,7 @@ namespace platf { return "00:00:00:00:00:00"s; } - bp::child - run_command(bool elevated, bool interactive, const std::string &cmd, boost::filesystem::path &working_dir, const bp::environment &env, FILE *file, std::error_code &ec, bp::group *group) { + bp::child run_command(bool elevated, bool interactive, const std::string &cmd, boost::filesystem::path &working_dir, const bp::environment &env, FILE *file, std::error_code &ec, bp::group *group) { // clang-format off if (!group) { if (!file) { @@ -207,8 +195,7 @@ namespace platf { * @brief Open a url in the default web browser. * @param url The url to open. */ - void - open_url(const std::string &url) { + void open_url(const std::string &url) { boost::filesystem::path working_dir; std::string cmd = R"(open ")" + url + R"(")"; @@ -217,30 +204,25 @@ namespace platf { auto child = run_command(false, false, cmd, working_dir, _env, nullptr, ec, nullptr); if (ec) { BOOST_LOG(warning) << "Couldn't open url ["sv << url << "]: System: "sv << ec.message(); - } - else { + } else { BOOST_LOG(info) << "Opened url ["sv << url << "]"sv; child.detach(); } } - void - adjust_thread_priority(thread_priority_e priority) { + void adjust_thread_priority(thread_priority_e priority) { // Unimplemented } - void - streaming_will_start() { + void streaming_will_start() { // Nothing to do } - void - streaming_will_stop() { + void streaming_will_stop() { // Nothing to do } - void - restart_on_exit() { + void restart_on_exit() { char executable[2048]; uint32_t size = sizeof(executable); if (_NSGetExecutablePath(executable, &size) < 0) { @@ -261,42 +243,35 @@ namespace platf { } } - void - restart() { + void restart() { // Gracefully clean up and restart ourselves instead of exiting atexit(restart_on_exit); lifetime::exit_sunshine(0, true); } - int - set_env(const std::string &name, const std::string &value) { + int set_env(const std::string &name, const std::string &value) { return setenv(name.c_str(), value.c_str(), 1); } - int - unset_env(const std::string &name) { + int unset_env(const std::string &name) { return unsetenv(name.c_str()); } - bool - request_process_group_exit(std::uintptr_t native_handle) { + bool request_process_group_exit(std::uintptr_t native_handle) { if (killpg((pid_t) native_handle, SIGTERM) == 0 || errno == ESRCH) { BOOST_LOG(debug) << "Successfully sent SIGTERM to process group: "sv << native_handle; return true; - } - else { + } else { BOOST_LOG(warning) << "Unable to send SIGTERM to process group ["sv << native_handle << "]: "sv << errno; return false; } } - bool - process_group_running(std::uintptr_t native_handle) { + bool process_group_running(std::uintptr_t native_handle) { return waitpid(-((pid_t) native_handle), nullptr, WNOHANG) >= 0; } - struct sockaddr_in - to_sockaddr(boost::asio::ip::address_v4 address, uint16_t port) { + struct sockaddr_in to_sockaddr(boost::asio::ip::address_v4 address, uint16_t port) { struct sockaddr_in saddr_v4 = {}; saddr_v4.sin_family = AF_INET; @@ -308,8 +283,7 @@ namespace platf { return saddr_v4; } - struct sockaddr_in6 - to_sockaddr(boost::asio::ip::address_v6 address, uint16_t port) { + struct sockaddr_in6 to_sockaddr(boost::asio::ip::address_v6 address, uint16_t port) { struct sockaddr_in6 saddr_v6 = {}; saddr_v6.sin6_family = AF_INET6; @@ -322,14 +296,12 @@ namespace platf { return saddr_v6; } - bool - send_batch(batched_send_info_t &send_info) { + bool send_batch(batched_send_info_t &send_info) { // Fall back to unbatched send calls return false; } - bool - send(send_info_t &send_info) { + bool send(send_info_t &send_info) { auto sockfd = (int) send_info.native_socket; struct msghdr msg = {}; @@ -341,8 +313,7 @@ namespace platf { msg.msg_name = (struct sockaddr *) &taddr_v6; msg.msg_namelen = sizeof(taddr_v6); - } - else { + } else { taddr_v4 = to_sockaddr(send_info.target_address.to_v4(), send_info.target_port); msg.msg_name = (struct sockaddr *) &taddr_v4; @@ -353,6 +324,7 @@ namespace platf { char buf[std::max(CMSG_SPACE(sizeof(struct in_pktinfo)), CMSG_SPACE(sizeof(struct in6_pktinfo)))]; struct cmsghdr alignment; } cmbuf {}; + socklen_t cmbuflen = 0; msg.msg_control = cmbuf.buf; @@ -372,8 +344,7 @@ namespace platf { pktinfo_cm->cmsg_type = IPV6_PKTINFO; pktinfo_cm->cmsg_len = CMSG_LEN(sizeof(pktInfo)); memcpy(CMSG_DATA(pktinfo_cm), &pktInfo, sizeof(pktInfo)); - } - else { + } else { struct in_pktinfo pktInfo {}; struct sockaddr_in saddr_v4 = to_sockaddr(send_info.source_address.to_v4(), 0); @@ -438,7 +409,8 @@ namespace platf { class qos_t: public deinit_t { public: qos_t(int sockfd, std::vector> options): - sockfd(sockfd), options(options) { + sockfd(sockfd), + options(options) { qos_ref_count++; } @@ -466,8 +438,7 @@ namespace platf { * @param data_type The type of traffic sent on this socket. * @param dscp_tagging Specifies whether to enable DSCP tagging on outgoing traffic. */ - std::unique_ptr - enable_socket_qos(uintptr_t native_socket, boost::asio::ip::address &address, uint16_t port, qos_data_type_e data_type, bool dscp_tagging) { + std::unique_ptr enable_socket_qos(uintptr_t native_socket, boost::asio::ip::address &address, uint16_t port, qos_data_type_e data_type, bool dscp_tagging) { int sockfd = (int) native_socket; std::vector> reset_options; @@ -489,8 +460,7 @@ namespace platf { if (setsockopt(sockfd, SOL_SOCKET, SO_NET_SERVICE_TYPE, &service_type, sizeof(service_type)) == 0) { // Reset SO_NET_SERVICE_TYPE to best-effort when QoS is disabled reset_options.emplace_back(std::make_tuple(SOL_SOCKET, SO_NET_SERVICE_TYPE, NET_SERVICE_TYPE_BE)); - } - else { + } else { BOOST_LOG(error) << "Failed to set SO_NET_SERVICE_TYPE: "sv << errno; } } @@ -501,8 +471,7 @@ namespace platf { if (address.is_v6()) { level = IPPROTO_IPV6; option = IPV6_TCLASS; - } - else { + } else { level = IPPROTO_IP; option = IP_TOS; } @@ -529,8 +498,7 @@ namespace platf { if (setsockopt(sockfd, level, option, &dscp, sizeof(dscp)) == 0) { // Reset TOS to -1 when QoS is disabled reset_options.emplace_back(std::make_tuple(level, option, -1)); - } - else { + } else { BOOST_LOG(error) << "Failed to set TOS/TCLASS: "sv << errno; } } @@ -539,12 +507,10 @@ namespace platf { return std::make_unique(sockfd, reset_options); } - std::string - get_host_name() { + std::string get_host_name() { try { return boost::asio::ip::host_name(); - } - catch (boost::system::system_error &err) { + } catch (boost::system::system_error &err) { BOOST_LOG(error) << "Failed to get hostname: "sv << err.what(); return "Sunshine"s; } @@ -552,8 +518,7 @@ namespace platf { class macos_high_precision_timer: public high_precision_timer { public: - void - sleep_for(const std::chrono::nanoseconds &duration) override { + void sleep_for(const std::chrono::nanoseconds &duration) override { std::this_thread::sleep_for(duration); } @@ -562,15 +527,13 @@ namespace platf { } }; - std::unique_ptr - create_high_precision_timer() { + std::unique_ptr create_high_precision_timer() { return std::make_unique(); } } // namespace platf namespace dyn { - void * - handle(const std::vector &libs) { + void *handle(const std::vector &libs) { void *handle; for (auto lib : libs) { @@ -593,8 +556,7 @@ namespace dyn { return nullptr; } - int - load(void *handle, const std::vector> &funcs, bool strict) { + int load(void *handle, const std::vector> &funcs, bool strict) { int err = 0; for (auto &func : funcs) { TUPLE_2D_REF(fn, name, func); diff --git a/src/platform/macos/nv12_zero_device.cpp b/src/platform/macos/nv12_zero_device.cpp index e5002890..b4fb28cb 100644 --- a/src/platform/macos/nv12_zero_device.cpp +++ b/src/platform/macos/nv12_zero_device.cpp @@ -2,11 +2,12 @@ * @file src/platform/macos/nv12_zero_device.cpp * @brief Definitions for NV12 zero copy device on macOS. */ +// standard includes #include +// local includes #include "src/platform/macos/av_img_t.h" #include "src/platform/macos/nv12_zero_device.h" - #include "src/video.h" extern "C" { @@ -15,20 +16,17 @@ extern "C" { namespace platf { - void - free_frame(AVFrame *frame) { + void free_frame(AVFrame *frame) { av_frame_free(&frame); } - void - free_buffer(void *opaque, uint8_t *data) { + void free_buffer(void *opaque, uint8_t *data) { CVPixelBufferRelease((CVPixelBufferRef) data); } util::safe_ptr av_frame; - int - nv12_zero_device::convert(platf::img_t &img) { + int nv12_zero_device::convert(platf::img_t &img) { auto *av_img = (av_img_t *) &img; // Release any existing CVPixelBuffer previously retained for encoding @@ -47,8 +45,7 @@ namespace platf { return 0; } - int - nv12_zero_device::set_frame(AVFrame *frame, AVBufferRef *hw_frames_ctx) { + int nv12_zero_device::set_frame(AVFrame *frame, AVBufferRef *hw_frames_ctx) { this->frame = frame; av_frame.reset(frame); @@ -58,11 +55,8 @@ namespace platf { return 0; } - int - nv12_zero_device::init(void *display, pix_fmt_e pix_fmt, resolution_fn_t resolution_fn, const pixel_format_fn_t &pixel_format_fn) { - pixel_format_fn(display, pix_fmt == pix_fmt_e::nv12 ? - kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange : - kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange); + int nv12_zero_device::init(void *display, pix_fmt_e pix_fmt, resolution_fn_t resolution_fn, const pixel_format_fn_t &pixel_format_fn) { + pixel_format_fn(display, pix_fmt == pix_fmt_e::nv12 ? kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange : kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange); this->display = display; this->resolution_fn = std::move(resolution_fn); diff --git a/src/platform/macos/nv12_zero_device.h b/src/platform/macos/nv12_zero_device.h index fd9f80f8..3aa7540c 100644 --- a/src/platform/macos/nv12_zero_device.h +++ b/src/platform/macos/nv12_zero_device.h @@ -4,13 +4,13 @@ */ #pragma once +// local includes #include "src/platform/common.h" struct AVFrame; namespace platf { - void - free_frame(AVFrame *frame); + void free_frame(AVFrame *frame); class nv12_zero_device: public avcodec_encode_device_t { // display holds a pointer to an av_video object. Since the namespaces of AVFoundation @@ -24,13 +24,10 @@ namespace platf { resolution_fn_t resolution_fn; using pixel_format_fn_t = std::function; - int - init(void *display, pix_fmt_e pix_fmt, resolution_fn_t resolution_fn, const pixel_format_fn_t &pixel_format_fn); + int init(void *display, pix_fmt_e pix_fmt, resolution_fn_t resolution_fn, const pixel_format_fn_t &pixel_format_fn); - int - convert(img_t &img) override; - int - set_frame(AVFrame *frame, AVBufferRef *hw_frames_ctx) override; + int convert(img_t &img) override; + int set_frame(AVFrame *frame, AVBufferRef *hw_frames_ctx) override; private: util::safe_ptr av_frame; diff --git a/src/platform/macos/publish.cpp b/src/platform/macos/publish.cpp index b8c977c0..0718b784 100644 --- a/src/platform/macos/publish.cpp +++ b/src/platform/macos/publish.cpp @@ -2,9 +2,13 @@ * @file src/platform/macos/publish.cpp * @brief Definitions for publishing services on macOS. */ -#include +// standard includes #include +// platform includes +#include + +// local includes #include "src/logging.h" #include "src/network.h" #include "src/nvhttp.h" @@ -17,12 +21,13 @@ namespace platf::publish { /** @brief Custom deleter intended to be used for `std::unique_ptr`. */ struct ServiceRefDeleter { typedef DNSServiceRef pointer; ///< Type of object to be deleted. - void - operator()(pointer serviceRef) { + + void operator()(pointer serviceRef) { DNSServiceRefDeallocate(serviceRef); BOOST_LOG(info) << "Deregistered DNS service."sv; } }; + /** @brief This class encapsulates the polling and deinitialization of our connection with * the mDNS service. Implements the `::platf::deinit_t` interface. */ @@ -37,25 +42,25 @@ namespace platf::publish { */ deinit_t(DNSServiceRef serviceRef): unique_ptr(serviceRef) { - _thread = std::thread { [serviceRef, &_stopRequested = std::as_const(_stopRequested)]() { + _thread = std::thread {[serviceRef, &_stopRequested = std::as_const(_stopRequested)]() { const auto socket = DNSServiceRefSockFD(serviceRef); while (!_stopRequested) { auto fdset = fd_set {}; FD_ZERO(&fdset); FD_SET(socket, &fdset); - auto timeout = timeval { .tv_sec = 3, .tv_usec = 0 }; // 3 second timeout + auto timeout = timeval {.tv_sec = 3, .tv_usec = 0}; // 3 second timeout const auto ready = select(socket + 1, &fdset, nullptr, nullptr, &timeout); if (ready == -1) { BOOST_LOG(error) << "Failed to obtain response from DNS service."sv; break; - } - else if (ready != 0) { + } else if (ready != 0) { DNSServiceProcessResult(serviceRef); break; } } - } }; + }}; } + /** @brief Ensure that we gracefully finish polling the mDNS service before freeing our * connection to it. */ @@ -63,9 +68,9 @@ namespace platf::publish { _stopRequested = true; _thread.join(); } + deinit_t(const deinit_t &) = delete; - deinit_t & - operator=(const deinit_t &) = delete; + deinit_t &operator=(const deinit_t &) = delete; private: std::thread _thread; ///< Thread for polling the mDNS service for a response. @@ -75,10 +80,7 @@ namespace platf::publish { /** @brief Callback that will be invoked when the mDNS service finishes registering our service. * @param errorCode Describes whether the registration was successful. */ - void - registrationCallback(DNSServiceRef /*serviceRef*/, DNSServiceFlags /*flags*/, - DNSServiceErrorType errorCode, const char * /*name*/, - const char * /*regtype*/, const char * /*domain*/, void * /*context*/) { + void registrationCallback(DNSServiceRef /*serviceRef*/, DNSServiceFlags /*flags*/, DNSServiceErrorType errorCode, const char * /*name*/, const char * /*regtype*/, const char * /*domain*/, void * /*context*/) { if (errorCode != kDNSServiceErr_NoError) { BOOST_LOG(error) << "Failed to register DNS service: Error "sv << errorCode; return; @@ -98,8 +100,7 @@ namespace platf::publish { * which will manage polling for a response from the mDNS service, and then, when * deconstructed, will deregister the service. */ - [[nodiscard]] std::unique_ptr<::platf::deinit_t> - start() { + [[nodiscard]] std::unique_ptr<::platf::deinit_t> start() { auto serviceRef = DNSServiceRef {}; const auto status = DNSServiceRegister( &serviceRef, diff --git a/src/platform/windows/PolicyConfig.h b/src/platform/windows/PolicyConfig.h index 087f85fa..a61d2873 100644 --- a/src/platform/windows/PolicyConfig.h +++ b/src/platform/windows/PolicyConfig.h @@ -8,14 +8,15 @@ #pragma once +// platform includes #include #ifdef __MINGW32__ #undef DEFINE_GUID #ifdef __cplusplus - #define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) EXTERN_C const GUID DECLSPEC_SELECTANY name = { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } } + #define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) EXTERN_C const GUID DECLSPEC_SELECTANY name = {l, w1, w2, {b1, b2, b3, b4, b5, b6, b7, b8}} #else - #define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) const GUID DECLSPEC_SELECTANY name = { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } } + #define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) const GUID DECLSPEC_SELECTANY name = {l, w1, w2, {b1, b2, b3, b4, b5, b6, b7, b8}} #endif DEFINE_GUID(IID_IPolicyConfig, 0xf8679f50, 0x850a, 0x41cf, 0x9c, 0x72, 0x43, 0x0f, 0x29, 0x02, 0x90, 0xc8); @@ -40,64 +41,69 @@ class DECLSPEC_UUID("870af99c-171d-4f9e-af0d-e63df40c2bc9") CPolicyConfigClient; // ---------------------------------------------------------------------------- interface IPolicyConfig: public IUnknown { public: - virtual HRESULT - GetMixFormat( + virtual HRESULT GetMixFormat( PCWSTR, - WAVEFORMATEX **); + WAVEFORMATEX ** + ); - virtual HRESULT STDMETHODCALLTYPE - GetDeviceFormat( + virtual HRESULT STDMETHODCALLTYPE GetDeviceFormat( PCWSTR, INT, - WAVEFORMATEX **); + WAVEFORMATEX ** + ); virtual HRESULT STDMETHODCALLTYPE ResetDeviceFormat( - PCWSTR); + PCWSTR + ); virtual HRESULT STDMETHODCALLTYPE - SetDeviceFormat( - PCWSTR, - WAVEFORMATEX *, - WAVEFORMATEX *); + SetDeviceFormat( + PCWSTR, + WAVEFORMATEX *, + WAVEFORMATEX * + ); virtual HRESULT STDMETHODCALLTYPE GetProcessingPeriod( PCWSTR, INT, PINT64, - PINT64); + PINT64 + ); virtual HRESULT STDMETHODCALLTYPE SetProcessingPeriod( PCWSTR, - PINT64); + PINT64 + ); - virtual HRESULT STDMETHODCALLTYPE - GetShareMode( + virtual HRESULT STDMETHODCALLTYPE GetShareMode( PCWSTR, - struct DeviceShareMode *); + struct DeviceShareMode * + ); - virtual HRESULT STDMETHODCALLTYPE - SetShareMode( + virtual HRESULT STDMETHODCALLTYPE SetShareMode( PCWSTR, - struct DeviceShareMode *); + struct DeviceShareMode * + ); - virtual HRESULT STDMETHODCALLTYPE - GetPropertyValue( + virtual HRESULT STDMETHODCALLTYPE GetPropertyValue( PCWSTR, const PROPERTYKEY &, - PROPVARIANT *); + PROPVARIANT * + ); - virtual HRESULT STDMETHODCALLTYPE - SetPropertyValue( + virtual HRESULT STDMETHODCALLTYPE SetPropertyValue( PCWSTR, const PROPERTYKEY &, - PROPVARIANT *); + PROPVARIANT * + ); - virtual HRESULT STDMETHODCALLTYPE - SetDefaultEndpoint( + virtual HRESULT STDMETHODCALLTYPE SetDefaultEndpoint( PCWSTR wszDeviceId, - ERole eRole); + ERole eRole + ); virtual HRESULT STDMETHODCALLTYPE SetEndpointVisibility( PCWSTR, - INT); + INT + ); }; diff --git a/src/platform/windows/audio.cpp b/src/platform/windows/audio.cpp index 3c401976..6726a470 100644 --- a/src/platform/windows/audio.cpp +++ b/src/platform/windows/audio.cpp @@ -3,22 +3,21 @@ * @brief Definitions for Windows audio capture. */ #define INITGUID -#include -#include -#include +// platform includes +#include +#include +#include +#include +#include #include -#include - -#include - +// local includes +#include "misc.h" #include "src/config.h" #include "src/logging.h" #include "src/platform/common.h" -#include "misc.h" - // Must be the last included file // clang-format off #include "PolicyConfig.h" @@ -65,8 +64,7 @@ namespace { _size, }; - constexpr WAVEFORMATEXTENSIBLE - create_waveformat(sample_format_e sample_format, WORD channel_count, DWORD channel_mask) { + constexpr WAVEFORMATEXTENSIBLE create_waveformat(sample_format_e sample_format, WORD channel_count, DWORD channel_mask) { WAVEFORMATEXTENSIBLE waveformat = {}; switch (sample_format) { @@ -119,9 +117,8 @@ namespace { using virtual_sink_waveformats_t = std::vector; - template - virtual_sink_waveformats_t - create_virtual_sink_waveformats() { + template + virtual_sink_waveformats_t create_virtual_sink_waveformats() { if constexpr (channel_count == 2) { auto channel_mask = waveformat_mask_stereo; // only choose 24 or 16-bit formats to avoid clobbering existing Dolby/DTS spatial audio settings @@ -130,8 +127,7 @@ namespace { create_waveformat(sample_format_e::s24, channel_count, channel_mask), create_waveformat(sample_format_e::s16, channel_count, channel_mask), }; - } - else if (channel_count == 6) { + } else if (channel_count == 6) { auto channel_mask1 = waveformat_mask_surround51_with_backspeakers; auto channel_mask2 = waveformat_mask_surround51_with_sidespeakers; return { @@ -146,8 +142,7 @@ namespace { create_waveformat(sample_format_e::s16, channel_count, channel_mask1), create_waveformat(sample_format_e::s16, channel_count, channel_mask2), }; - } - else if (channel_count == 8) { + } else if (channel_count == 8) { auto channel_mask = waveformat_mask_surround71; return { create_waveformat(sample_format_e::f32, channel_count, channel_mask), @@ -159,8 +154,7 @@ namespace { } } - std::string - waveformat_to_pretty_string(const WAVEFORMATEXTENSIBLE &waveformat) { + std::string waveformat_to_pretty_string(const WAVEFORMATEXTENSIBLE &waveformat) { std::string result = waveformat.SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT ? "F" : waveformat.SubFormat == KSDATAFORMAT_SUBTYPE_PCM ? "S" : "UNKNOWN"; @@ -196,16 +190,15 @@ namespace { } // namespace using namespace std::literals; + namespace platf::audio { - template - void - Release(T *p) { + template + void Release(T *p) { p->Release(); } - template - void - co_task_free(T *p) { + template + void co_task_free(T *p) { CoTaskMemFree((LPVOID) p); } @@ -272,14 +265,14 @@ namespace platf::audio { }, }; - audio_client_t - make_audio_client(device_t &device, const format_t &format) { + audio_client_t make_audio_client(device_t &device, const format_t &format) { audio_client_t audio_client; auto status = device->Activate( IID_IAudioClient, CLSCTX_ALL, nullptr, - (void **) &audio_client); + (void **) &audio_client + ); if (FAILED(status)) { BOOST_LOG(error) << "Couldn't activate Device: [0x"sv << util::hex(status).to_string_view() << ']'; @@ -311,9 +304,11 @@ namespace platf::audio { AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_LOOPBACK | AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM | AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY, // Enable automatic resampling to 48 KHz - 0, 0, + 0, + 0, (LPWAVEFORMATEX) &capture_waveformat, - nullptr); + nullptr + ); if (status) { BOOST_LOG(error) << "Couldn't initialize audio client for ["sv << format.name << "]: [0x"sv << util::hex(status).to_string_view() << ']'; @@ -325,14 +320,14 @@ namespace platf::audio { return audio_client; } - device_t - default_device(device_enum_t &device_enum) { + device_t default_device(device_enum_t &device_enum) { device_t device; HRESULT status; status = device_enum->GetDefaultAudioEndpoint( eRender, eConsole, - &device); + &device + ); if (FAILED(status)) { BOOST_LOG(error) << "Couldn't get default audio endpoint [0x"sv << util::hex(status).to_string_view() << ']'; @@ -345,68 +340,68 @@ namespace platf::audio { class audio_notification_t: public ::IMMNotificationClient { public: - audio_notification_t() {} + audio_notification_t() { + } // IUnknown implementation (unused by IMMDeviceEnumerator) - ULONG STDMETHODCALLTYPE - AddRef() { + ULONG STDMETHODCALLTYPE AddRef() { return 1; } - ULONG STDMETHODCALLTYPE - Release() { + ULONG STDMETHODCALLTYPE Release() { return 1; } - HRESULT STDMETHODCALLTYPE - QueryInterface(REFIID riid, VOID **ppvInterface) { + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, VOID **ppvInterface) { if (IID_IUnknown == riid) { AddRef(); *ppvInterface = (IUnknown *) this; return S_OK; - } - else if (__uuidof(IMMNotificationClient) == riid) { + } else if (__uuidof(IMMNotificationClient) == riid) { AddRef(); *ppvInterface = (IMMNotificationClient *) this; return S_OK; - } - else { + } else { *ppvInterface = NULL; return E_NOINTERFACE; } } // IMMNotificationClient - HRESULT STDMETHODCALLTYPE - OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR pwstrDeviceId) { + HRESULT STDMETHODCALLTYPE OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR pwstrDeviceId) { if (flow == eRender) { default_render_device_changed_flag.store(true); } return S_OK; } - HRESULT STDMETHODCALLTYPE - OnDeviceAdded(LPCWSTR pwstrDeviceId) { return S_OK; } + HRESULT STDMETHODCALLTYPE OnDeviceAdded(LPCWSTR pwstrDeviceId) { + return S_OK; + } - HRESULT STDMETHODCALLTYPE - OnDeviceRemoved(LPCWSTR pwstrDeviceId) { return S_OK; } + HRESULT STDMETHODCALLTYPE OnDeviceRemoved(LPCWSTR pwstrDeviceId) { + return S_OK; + } - HRESULT STDMETHODCALLTYPE - OnDeviceStateChanged( + HRESULT STDMETHODCALLTYPE OnDeviceStateChanged( LPCWSTR pwstrDeviceId, - DWORD dwNewState) { return S_OK; } + DWORD dwNewState + ) { + return S_OK; + } - HRESULT STDMETHODCALLTYPE - OnPropertyValueChanged( + HRESULT STDMETHODCALLTYPE OnPropertyValueChanged( LPCWSTR pwstrDeviceId, - const PROPERTYKEY key) { return S_OK; } + const PROPERTYKEY key + ) { + return S_OK; + } /** * @brief Checks if the default rendering device changed and resets the change flag * @return `true` if the device changed since last call */ - bool - check_default_render_device_changed() { + bool check_default_render_device_changed() { return default_render_device_changed_flag.exchange(false); } @@ -416,8 +411,7 @@ namespace platf::audio { class mic_wasapi_t: public mic_t { public: - capture_e - sample(std::vector &sample_out) override { + capture_e sample(std::vector &sample_out) override { auto sample_size = sample_out.size(); // Refill the sample buffer if needed @@ -438,8 +432,7 @@ namespace platf::audio { return capture_e::ok; } - int - init(std::uint32_t sample_rate, std::uint32_t frame_size, std::uint32_t channels_out) { + int init(std::uint32_t sample_rate, std::uint32_t frame_size, std::uint32_t channels_out) { audio_event.reset(CreateEventA(nullptr, FALSE, FALSE, nullptr)); if (!audio_event) { BOOST_LOG(error) << "Couldn't create Event handle"sv; @@ -454,7 +447,8 @@ namespace platf::audio { 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() << ']'; @@ -509,7 +503,7 @@ namespace platf::audio { } // *2 --> needs to fit double - sample_buf = util::buffer_t { std::max(frames, frame_size) * 2 * channels_out }; + sample_buf = util::buffer_t {std::max(frames, frame_size) * 2 * channels_out}; sample_buf_pos = std::begin(sample_buf); status = audio_client->GetService(IID_IAudioCaptureClient, (void **) &audio_capture); @@ -559,8 +553,7 @@ namespace platf::audio { } private: - capture_e - _fill_buffer() { + capture_e _fill_buffer() { HRESULT status; // Total number of samples @@ -600,13 +593,16 @@ namespace platf::audio { 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, &block_aligned.audio_sample_size, &buffer_flags, - nullptr, nullptr); + nullptr, + nullptr + ); switch (status) { case S_OK: @@ -631,8 +627,7 @@ namespace platf::audio { 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); } @@ -674,8 +669,7 @@ namespace platf::audio { class audio_control_t: public ::platf::audio_control_t { public: - std::optional - sink_info() override { + std::optional sink_info() override { sink_t sink; // Fill host sink name with the device_id of the current default audio device. @@ -697,8 +691,7 @@ namespace platf::audio { match_fields_list_t match_list; if (config::audio.virtual_sink.empty()) { match_list = match_steam_speakers(); - } - else { + } else { match_list = match_all_fields(from_utf8(config::audio.virtual_sink)); } @@ -714,16 +707,14 @@ namespace platf::audio { "virtual-"s + formats[1].name + device_id, "virtual-"s + formats[2].name + device_id, }); - } - else if (!config::audio.virtual_sink.empty()) { + } else if (!config::audio.virtual_sink.empty()) { BOOST_LOG(warning) << "Couldn't find the specified virtual audio sink " << config::audio.virtual_sink; } return sink; } - bool - is_sink_available(const std::string &sink) override { + bool is_sink_available(const std::string &sink) override { const auto match_list = match_all_fields(from_utf8(sink)); const auto matched = find_device_id(match_list); return static_cast(matched); @@ -735,8 +726,7 @@ namespace platf::audio { * @return A pair of device_id and format reference if the sink name matches * our naming scheme for virtual audio sinks, `std::nullopt` otherwise. */ - std::optional>> - extract_virtual_sink_info(const std::string &sink) { + std::optional>> extract_virtual_sink_info(const std::string &sink) { // Encoding format: // [virtual-(format name)]device_id std::string current = sink; @@ -756,8 +746,7 @@ namespace platf::audio { return std::nullopt; } - std::unique_ptr - microphone(const std::uint8_t *mapping, int channels, std::uint32_t sample_rate, std::uint32_t frame_size) override { + std::unique_ptr microphone(const std::uint8_t *mapping, int channels, std::uint32_t sample_rate, std::uint32_t frame_size) override { auto mic = std::make_unique(); if (mic->init(sample_rate, frame_size, channels)) { @@ -784,8 +773,7 @@ namespace platf::audio { * virtual-(format name) * If it doesn't contain that prefix, then the format will not be changed */ - std::optional - set_format(const std::string &sink) { + std::optional set_format(const std::string &sink) { if (sink.empty()) { return std::nullopt; } @@ -799,8 +787,7 @@ namespace platf::audio { auto matched = find_device_id(match_all_fields(from_utf8(sink))); if (matched) { return matched->second; - } - else { + } else { BOOST_LOG(error) << "Couldn't find audio sink " << sink; return std::nullopt; } @@ -826,8 +813,7 @@ namespace platf::audio { return std::nullopt; } - int - set_sink(const std::string &sink) override { + int set_sink(const std::string &sink) override { auto device_id = set_format(sink); if (!device_id) { return -1; @@ -840,8 +826,7 @@ namespace platf::audio { // Depending on the format of the string, we could get either of these errors if (status == HRESULT_FROM_WIN32(ERROR_NOT_FOUND) || status == E_INVALIDARG) { BOOST_LOG(warning) << "Audio sink not found: "sv << sink; - } - else { + } else { BOOST_LOG(warning) << "Couldn't set ["sv << sink << "] to role ["sv << x << "]: 0x"sv << util::hex(status).to_string_view(); } @@ -868,20 +853,18 @@ namespace platf::audio { using match_fields_list_t = std::vector>; using matched_field_t = std::pair; - audio_control_t::match_fields_list_t - match_steam_speakers() { + audio_control_t::match_fields_list_t match_steam_speakers() { return { - { match_field_e::adapter_friendly_name, L"Steam Streaming Speakers" } + {match_field_e::adapter_friendly_name, L"Steam Streaming Speakers"} }; } - audio_control_t::match_fields_list_t - match_all_fields(const std::wstring &name) { + audio_control_t::match_fields_list_t match_all_fields(const std::wstring &name) { return { - { match_field_e::device_id, name }, // {0.0.0.00000000}.{29dd7668-45b2-4846-882d-950f55bf7eb8} - { match_field_e::device_friendly_name, name }, // Digital Audio (S/PDIF) (High Definition Audio Device) - { match_field_e::device_description, name }, // Digital Audio (S/PDIF) - { match_field_e::adapter_friendly_name, name }, // High Definition Audio Device + {match_field_e::device_id, name}, // {0.0.0.00000000}.{29dd7668-45b2-4846-882d-950f55bf7eb8} + {match_field_e::device_friendly_name, name}, // Digital Audio (S/PDIF) (High Definition Audio Device) + {match_field_e::device_description, name}, // Digital Audio (S/PDIF) + {match_field_e::adapter_friendly_name, name}, // High Definition Audio Device }; } @@ -890,8 +873,7 @@ namespace platf::audio { * @param match_list Pairs of match fields and values * @return Optional pair of matched field and device_id */ - std::optional - find_device_id(const match_fields_list_t &match_list) { + std::optional find_device_id(const match_fields_list_t &match_list) { if (match_list.empty()) { return std::nullopt; } @@ -965,8 +947,7 @@ namespace platf::audio { /** * @brief Resets the default audio device from Steam Streaming Speakers. */ - void - reset_default_device() { + void reset_default_device() { auto matched_steam = find_device_id(match_steam_speakers()); if (!matched_steam) { return; @@ -1027,8 +1008,7 @@ namespace platf::audio { * @brief Installs the Steam Streaming Speakers driver, if present. * @return `true` if installation was successful. */ - bool - install_steam_audio_drivers() { + bool install_steam_audio_drivers() { #ifdef STEAM_DRIVER_SUBDIR // MinGW's libnewdev.a is missing DiInstallDriverW() even though the headers have it, // so we have to load it at runtime. It's Vista or later, so it will always be available. @@ -1072,8 +1052,7 @@ namespace platf::audio { } return true; - } - else { + } else { auto err = GetLastError(); switch (err) { case ERROR_ACCESS_DENIED: @@ -1096,14 +1075,14 @@ namespace platf::audio { #endif } - int - init() { + int init() { auto status = CoCreateInstance( CLSID_CPolicyConfigClient, nullptr, CLSCTX_ALL, IID_IPolicyConfig, - (void **) &policy); + (void **) &policy + ); if (FAILED(status)) { BOOST_LOG(error) << "Couldn't create audio policy config: [0x"sv << util::hex(status).to_string_view() << ']'; @@ -1116,7 +1095,8 @@ namespace platf::audio { 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() << ']'; @@ -1126,7 +1106,8 @@ namespace platf::audio { return 0; } - ~audio_control_t() override {} + ~audio_control_t() override { + } policy_t policy; audio::device_enum_t device_enum; @@ -1138,12 +1119,10 @@ namespace platf { // It's not big enough to justify it's own source file :/ namespace dxgi { - int - init(); + int init(); } - std::unique_ptr - audio_control() { + std::unique_ptr audio_control() { auto control = std::make_unique(); if (control->init()) { @@ -1160,8 +1139,7 @@ namespace platf { return control; } - std::unique_ptr - init() { + std::unique_ptr init() { if (dxgi::init()) { return nullptr; } diff --git a/src/platform/windows/display.h b/src/platform/windows/display.h index f37dd3d7..74c4be7c 100644 --- a/src/platform/windows/display.h +++ b/src/platform/windows/display.h @@ -4,16 +4,17 @@ */ #pragma once +// platform includes #include #include #include #include #include #include - #include #include +// local includes #include "src/platform/common.h" #include "src/utility.h" #include "src/video.h" @@ -25,9 +26,8 @@ namespace platf::dxgi { // You should have a debugger like WinDbg attached to receive debug messages. auto constexpr D3D11_CREATE_DEVICE_FLAGS = 0; - template - void - Release(T *dxgi) { + template + void Release(T *dxgi) { dxgi->Release(); } @@ -72,6 +72,7 @@ namespace platf::dxgi { } // namespace video class hwdevice_t; + struct cursor_t { std::vector img_data; @@ -83,10 +84,9 @@ namespace platf::dxgi { class gpu_cursor_t { public: gpu_cursor_t(): - cursor_view { 0, 0, 0, 0, 0.0f, 1.0f } {}; + cursor_view {0, 0, 0, 0, 0.0f, 1.0f} {}; - void - set_pos(LONG topleft_x, LONG topleft_y, LONG display_width, LONG display_height, DXGI_MODE_ROTATION display_rotation, bool visible) { + void set_pos(LONG topleft_x, LONG topleft_y, LONG display_width, LONG display_height, DXGI_MODE_ROTATION display_rotation, bool visible) { this->topleft_x = topleft_x; this->topleft_y = topleft_y; this->display_width = display_width; @@ -96,16 +96,14 @@ namespace platf::dxgi { update_viewport(); } - void - set_texture(LONG texture_width, LONG texture_height, texture2d_t &&texture) { + void set_texture(LONG texture_width, LONG texture_height, texture2d_t &&texture) { this->texture = std::move(texture); this->texture_width = texture_width; this->texture_height = texture_height; update_viewport(); } - void - update_viewport() { + void update_viewport() { switch (display_rotation) { case DXGI_MODE_ROTATION_UNSPECIFIED: case DXGI_MODE_ROTATION_IDENTITY: @@ -158,11 +156,9 @@ namespace platf::dxgi { class display_base_t: public display_t { public: - int - init(const ::video::config_t &config, const std::string &display_name); + int init(const ::video::config_t &config, const std::string &display_name); - capture_e - capture(const push_captured_image_cb_t &push_captured_image_cb, const pull_free_image_cb_t &pull_free_image_cb, bool *cursor) override; + capture_e capture(const push_captured_image_cb_t &push_captured_image_cb, const pull_free_image_cb_t &pull_free_image_cb, bool *cursor) override; factory1_t factory; adapter_t adapter; @@ -209,6 +205,7 @@ namespace platf::dxgi { UINT IndependentVidPnVSyncControl : 1; UINT Reserved : 28; }; + UINT Value; }; } D3DKMT_WDDM_2_7_CAPS; @@ -231,30 +228,21 @@ namespace platf::dxgi { typedef NTSTATUS(WINAPI *PD3DKMTQueryAdapterInfo)(D3DKMT_QUERYADAPTERINFO *); typedef NTSTATUS(WINAPI *PD3DKMTCloseAdapter)(D3DKMT_CLOSEADAPTER *); - virtual bool - is_hdr() override; - virtual bool - get_hdr_metadata(SS_HDR_METADATA &metadata) override; + virtual bool is_hdr() override; + virtual bool get_hdr_metadata(SS_HDR_METADATA &metadata) override; - const char * - dxgi_format_to_string(DXGI_FORMAT format); - const char * - colorspace_to_string(DXGI_COLOR_SPACE_TYPE type); - virtual std::vector - get_supported_capture_formats() = 0; + const char *dxgi_format_to_string(DXGI_FORMAT format); + const char *colorspace_to_string(DXGI_COLOR_SPACE_TYPE type); + virtual std::vector get_supported_capture_formats() = 0; protected: - int - get_pixel_pitch() { + int get_pixel_pitch() { return (capture_format == DXGI_FORMAT_R16G16B16A16_FLOAT) ? 8 : 4; } - virtual capture_e - snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr &img_out, std::chrono::milliseconds timeout, bool cursor_visible) = 0; - virtual capture_e - release_snapshot() = 0; - virtual int - complete_img(img_t *img, bool dummy) = 0; + virtual capture_e snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr &img_out, std::chrono::milliseconds timeout, bool cursor_visible) = 0; + virtual capture_e release_snapshot() = 0; + virtual int complete_img(img_t *img, bool dummy) = 0; }; /** @@ -262,17 +250,12 @@ namespace platf::dxgi { */ class display_ram_t: public display_base_t { public: - std::shared_ptr - alloc_img() override; - int - dummy_img(img_t *img) override; - int - complete_img(img_t *img, bool dummy) override; - std::vector - get_supported_capture_formats() override; + std::shared_ptr alloc_img() override; + int dummy_img(img_t *img) override; + int complete_img(img_t *img, bool dummy) override; + std::vector get_supported_capture_formats() override; - std::unique_ptr - make_avcodec_encode_device(pix_fmt_e pix_fmt) override; + std::unique_ptr make_avcodec_encode_device(pix_fmt_e pix_fmt) override; D3D11_MAPPED_SUBRESOURCE img_info; texture2d_t texture; @@ -283,23 +266,16 @@ namespace platf::dxgi { */ class display_vram_t: public display_base_t, public std::enable_shared_from_this { public: - std::shared_ptr - alloc_img() override; - int - dummy_img(img_t *img_base) override; - int - complete_img(img_t *img_base, bool dummy) override; - std::vector - get_supported_capture_formats() override; + std::shared_ptr alloc_img() override; + int dummy_img(img_t *img_base) override; + int complete_img(img_t *img_base, bool dummy) override; + std::vector get_supported_capture_formats() override; - bool - is_codec_supported(std::string_view name, const ::video::config_t &config) override; + bool is_codec_supported(std::string_view name, const ::video::config_t &config) override; - std::unique_ptr - make_avcodec_encode_device(pix_fmt_e pix_fmt) override; + std::unique_ptr make_avcodec_encode_device(pix_fmt_e pix_fmt) override; - std::unique_ptr - make_nvenc_encode_device(pix_fmt_e pix_fmt) override; + std::unique_ptr make_nvenc_encode_device(pix_fmt_e pix_fmt) override; std::atomic next_image_id; }; @@ -313,14 +289,10 @@ namespace platf::dxgi { bool has_frame {}; std::chrono::steady_clock::time_point last_protected_content_warning_time {}; - int - init(display_base_t *display, const ::video::config_t &config); - capture_e - next_frame(DXGI_OUTDUPL_FRAME_INFO &frame_info, std::chrono::milliseconds timeout, resource_t::pointer *res_p); - capture_e - reset(dup_t::pointer dup_p = dup_t::pointer()); - capture_e - release_frame(); + int init(display_base_t *display, const ::video::config_t &config); + capture_e next_frame(DXGI_OUTDUPL_FRAME_INFO &frame_info, std::chrono::milliseconds timeout, resource_t::pointer *res_p); + capture_e reset(dup_t::pointer dup_p = dup_t::pointer()); + capture_e release_frame(); ~duplication_t(); }; @@ -330,12 +302,9 @@ namespace platf::dxgi { */ class display_ddup_ram_t: public display_ram_t { public: - int - init(const ::video::config_t &config, const std::string &display_name); - capture_e - snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr &img_out, std::chrono::milliseconds timeout, bool cursor_visible) override; - capture_e - release_snapshot() override; + int init(const ::video::config_t &config, const std::string &display_name); + capture_e snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr &img_out, std::chrono::milliseconds timeout, bool cursor_visible) override; + capture_e release_snapshot() override; duplication_t dup; cursor_t cursor; @@ -346,12 +315,9 @@ namespace platf::dxgi { */ class display_ddup_vram_t: public display_vram_t { public: - int - init(const ::video::config_t &config, const std::string &display_name); - capture_e - snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr &img_out, std::chrono::milliseconds timeout, bool cursor_visible) override; - capture_e - release_snapshot() override; + int init(const ::video::config_t &config, const std::string &display_name); + capture_e snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr &img_out, std::chrono::milliseconds timeout, bool cursor_visible) override; + capture_e release_snapshot() override; duplication_t dup; sampler_state_t sampler_linear; @@ -375,29 +341,24 @@ namespace platf::dxgi { * Display duplicator that uses the Windows.Graphics.Capture API. */ class wgc_capture_t { - winrt::Windows::Graphics::DirectX::Direct3D11::IDirect3DDevice uwp_device { nullptr }; - winrt::Windows::Graphics::Capture::GraphicsCaptureItem item { nullptr }; - winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool frame_pool { nullptr }; - winrt::Windows::Graphics::Capture::GraphicsCaptureSession capture_session { nullptr }; - winrt::Windows::Graphics::Capture::Direct3D11CaptureFrame produced_frame { nullptr }, consumed_frame { nullptr }; + winrt::Windows::Graphics::DirectX::Direct3D11::IDirect3DDevice uwp_device {nullptr}; + winrt::Windows::Graphics::Capture::GraphicsCaptureItem item {nullptr}; + winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool frame_pool {nullptr}; + winrt::Windows::Graphics::Capture::GraphicsCaptureSession capture_session {nullptr}; + winrt::Windows::Graphics::Capture::Direct3D11CaptureFrame produced_frame {nullptr}, consumed_frame {nullptr}; SRWLOCK frame_lock = SRWLOCK_INIT; CONDITION_VARIABLE frame_present_cv; - void - on_frame_arrived(winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool const &sender, winrt::Windows::Foundation::IInspectable const &); + void on_frame_arrived(winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool const &sender, winrt::Windows::Foundation::IInspectable const &); public: wgc_capture_t(); ~wgc_capture_t(); - int - init(display_base_t *display, const ::video::config_t &config); - capture_e - next_frame(std::chrono::milliseconds timeout, ID3D11Texture2D **out, uint64_t &out_time); - capture_e - release_frame(); - int - set_cursor_visible(bool); + int init(display_base_t *display, const ::video::config_t &config); + capture_e next_frame(std::chrono::milliseconds timeout, ID3D11Texture2D **out, uint64_t &out_time); + capture_e release_frame(); + int set_cursor_visible(bool); }; /** @@ -407,12 +368,9 @@ namespace platf::dxgi { wgc_capture_t dup; public: - int - init(const ::video::config_t &config, const std::string &display_name); - capture_e - snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr &img_out, std::chrono::milliseconds timeout, bool cursor_visible) override; - capture_e - release_snapshot() override; + int init(const ::video::config_t &config, const std::string &display_name); + capture_e snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr &img_out, std::chrono::milliseconds timeout, bool cursor_visible) override; + capture_e release_snapshot() override; }; /** @@ -422,11 +380,8 @@ namespace platf::dxgi { wgc_capture_t dup; public: - int - init(const ::video::config_t &config, const std::string &display_name); - capture_e - snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr &img_out, std::chrono::milliseconds timeout, bool cursor_visible) override; - capture_e - release_snapshot() override; + int init(const ::video::config_t &config, const std::string &display_name); + capture_e snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr &img_out, std::chrono::milliseconds timeout, bool cursor_visible) override; + capture_e release_snapshot() override; }; } // namespace platf::dxgi diff --git a/src/platform/windows/display_base.cpp b/src/platform/windows/display_base.cpp index cdce7962..66985662 100644 --- a/src/platform/windows/display_base.cpp +++ b/src/platform/windows/display_base.cpp @@ -2,13 +2,16 @@ * @file src/platform/windows/display_base.cpp * @brief Definitions for the Windows display base code. */ +// standard includes #include -#include #include +// platform includes +#include + +// lib includes #include #include - #include // We have to include boost/process/v1.hpp before display.h due to WinSock.h, @@ -36,14 +39,14 @@ typedef enum _D3DKMT_GPU_PREFERENCE_QUERY_STATE: DWORD { namespace platf { using namespace std::literals; } + namespace platf::dxgi { namespace bp = boost::process; /** * DDAPI-specific initialization goes here. */ - int - duplication_t::init(display_base_t *display, const ::video::config_t &config) { + int duplication_t::init(display_base_t *display, const ::video::config_t &config) { HRESULT status; // Capture format will be determined from the first call to AcquireNextFrame() @@ -81,8 +84,7 @@ namespace platf::dxgi { BOOST_LOG(warning) << "DuplicateOutput1 Failed [0x"sv << util::hex(status).to_string_view() << ']'; return -1; } - } - else { + } else { BOOST_LOG(warning) << "IDXGIOutput5 is not supported by your OS. Capture performance may be reduced."sv; dxgi::output1_t output1 {}; @@ -124,8 +126,7 @@ namespace platf::dxgi { return 0; } - capture_e - duplication_t::next_frame(DXGI_OUTDUPL_FRAME_INFO &frame_info, std::chrono::milliseconds timeout, resource_t::pointer *res_p) { + capture_e duplication_t::next_frame(DXGI_OUTDUPL_FRAME_INFO &frame_info, std::chrono::milliseconds timeout, resource_t::pointer *res_p) { auto capture_status = release_frame(); if (capture_status != capture_e::ok) { return capture_status; @@ -157,8 +158,7 @@ namespace platf::dxgi { } } - capture_e - duplication_t::reset(dup_t::pointer dup_p) { + capture_e duplication_t::reset(dup_t::pointer dup_p) { auto capture_status = release_frame(); dup.reset(dup_p); @@ -166,8 +166,7 @@ namespace platf::dxgi { return capture_status; } - capture_e - duplication_t::release_frame() { + capture_e duplication_t::release_frame() { if (!has_frame) { return capture_e::ok; } @@ -195,16 +194,14 @@ namespace platf::dxgi { release_frame(); } - capture_e - display_base_t::capture(const push_captured_image_cb_t &push_captured_image_cb, const pull_free_image_cb_t &pull_free_image_cb, bool *cursor) { + capture_e display_base_t::capture(const push_captured_image_cb_t &push_captured_image_cb, const pull_free_image_cb_t &pull_free_image_cb, bool *cursor) { auto adjust_client_frame_rate = [&]() -> DXGI_RATIONAL { // Adjust capture frame interval when display refresh rate is not integral but very close to requested fps. if (display_refresh_rate.Denominator > 1) { DXGI_RATIONAL candidate = display_refresh_rate; if (client_frame_rate % display_refresh_rate_rounded == 0) { candidate.Numerator *= client_frame_rate / display_refresh_rate_rounded; - } - else if (display_refresh_rate_rounded % client_frame_rate == 0) { + } else if (display_refresh_rate_rounded % client_frame_rate == 0) { candidate.Denominator *= display_refresh_rate_rounded / client_frame_rate; } double candidate_rate = (double) candidate.Numerator / candidate.Denominator; @@ -215,7 +212,7 @@ namespace platf::dxgi { } } - return { (uint32_t) client_frame_rate, 1 }; + return {(uint32_t) client_frame_rate, 1}; }; DXGI_RATIONAL client_frame_rate_adjusted = adjust_client_frame_rate(); @@ -258,8 +255,7 @@ namespace platf::dxgi { frame_pacing_group_start = std::nullopt; frame_pacing_group_frames = 0; status = capture_e::timeout; - } - else { + } else { timer->sleep_for(sleep_period); sleep_overshoot_logger.first_point(sleep_target); sleep_overshoot_logger.second_point_now_and_log(); @@ -268,8 +264,7 @@ namespace platf::dxgi { if (status == capture_e::ok && img_out) { frame_pacing_group_frames += 1; - } - else { + } else { frame_pacing_group_start = std::nullopt; frame_pacing_group_frames = 0; } @@ -289,8 +284,7 @@ namespace platf::dxgi { } frame_pacing_group_frames = 1; - } - else if (status == platf::capture_e::timeout) { + } else if (status == platf::capture_e::timeout) { // The D3D11 device is protected by an unfair lock that is held the entire time that // IDXGIOutputDuplication::AcquireNextFrame() is running. This is normally harmless, // however sometimes the encoding thread needs to interact with our ID3D11Device to @@ -348,8 +342,7 @@ namespace platf::dxgi { * @param output The DXGI output to capture. * @param enumeration_only Specifies whether this test is occurring for display enumeration. */ - bool - test_dxgi_duplication(adapter_t &adapter, output_t &output, bool enumeration_only) { + bool test_dxgi_duplication(adapter_t &adapter, output_t &output, bool enumeration_only) { D3D_FEATURE_LEVEL featureLevels[] { D3D_FEATURE_LEVEL_11_1, D3D_FEATURE_LEVEL_11_0, @@ -366,11 +359,13 @@ namespace platf::dxgi { D3D_DRIVER_TYPE_UNKNOWN, nullptr, D3D11_CREATE_DEVICE_FLAGS, - featureLevels, sizeof(featureLevels) / sizeof(D3D_FEATURE_LEVEL), + featureLevels, + sizeof(featureLevels) / sizeof(D3D_FEATURE_LEVEL), D3D11_SDK_VERSION, &device, nullptr, - nullptr); + nullptr + ); if (FAILED(status)) { BOOST_LOG(error) << "Failed to create D3D11 device for DD test [0x"sv << util::hex(status).to_string_view() << ']'; return false; @@ -403,8 +398,7 @@ namespace platf::dxgi { // capture the current desktop, just bail immediately. Retrying won't help. if (enumeration_only && status == E_ACCESSDENIED) { break; - } - else { + } else { std::this_thread::sleep_for(200ms); } } @@ -418,8 +412,7 @@ namespace platf::dxgi { * @param gpuPreference A pointer to the location where the preference will be written. * @return Always STATUS_SUCCESS if valid arguments are provided. */ - NTSTATUS - __stdcall NtGdiDdDDIGetCachedHybridQueryValueHook(D3DKMT_GPU_PREFERENCE_QUERY_STATE *gpuPreference) { + NTSTATUS __stdcall NtGdiDdDDIGetCachedHybridQueryValueHook(D3DKMT_GPU_PREFERENCE_QUERY_STATE *gpuPreference) { // By faking a cached GPU preference state of D3DKMT_GPU_PREFERENCE_STATE_UNSPECIFIED, this will // prevent DXGI from performing the normal GPU preference resolution that looks at the registry, // power settings, and the hybrid adapter DDI interface to pick a GPU. Instead, we will not be @@ -428,14 +421,12 @@ namespace platf::dxgi { if (gpuPreference) { *gpuPreference = D3DKMT_GPU_PREFERENCE_STATE_UNSPECIFIED; return 0; // STATUS_SUCCESS - } - else { + } else { return STATUS_INVALID_PARAMETER; } } - int - display_base_t::init(const ::video::config_t &config, const std::string &display_name) { + int display_base_t::init(const ::video::config_t &config, const std::string &display_name) { std::once_flag windows_cpp_once_flag; std::call_once(windows_cpp_once_flag, []() { @@ -479,7 +470,7 @@ namespace platf::dxgi { adapter_t::pointer adapter_p; for (int tries = 0; tries < 2; ++tries) { for (int x = 0; factory->EnumAdapters1(x, &adapter_p) != DXGI_ERROR_NOT_FOUND; ++x) { - dxgi::adapter_t adapter_tmp { adapter_p }; + dxgi::adapter_t adapter_tmp {adapter_p}; DXGI_ADAPTER_DESC1 adapter_desc; adapter_tmp->GetDesc1(&adapter_desc); @@ -490,7 +481,7 @@ namespace platf::dxgi { dxgi::output_t::pointer output_p; for (int y = 0; adapter_tmp->EnumOutputs(y, &output_p) != DXGI_ERROR_NOT_FOUND; ++y) { - dxgi::output_t output_tmp { output_p }; + dxgi::output_t output_tmp {output_p}; DXGI_OUTPUT_DESC desc; output_tmp->GetDesc(&desc); @@ -512,8 +503,7 @@ namespace platf::dxgi { display_rotation == DXGI_MODE_ROTATION_ROTATE270) { width_before_rotation = height; height_before_rotation = width; - } - else { + } else { width_before_rotation = width; height_before_rotation = height; } @@ -570,11 +560,13 @@ namespace platf::dxgi { D3D_DRIVER_TYPE_UNKNOWN, nullptr, D3D11_CREATE_DEVICE_FLAGS, - featureLevels, sizeof(featureLevels) / sizeof(D3D_FEATURE_LEVEL), + featureLevels, + sizeof(featureLevels) / sizeof(D3D_FEATURE_LEVEL), D3D11_SDK_VERSION, &device, &feature_level, - &device_ctx); + &device_ctx + ); adapter_p->Release(); @@ -632,7 +624,7 @@ namespace platf::dxgi { return false; } - D3DKMT_OPENADAPTERFROMLUID d3dkmt_adapter = { adapter }; + D3DKMT_OPENADAPTERFROMLUID d3dkmt_adapter = {adapter}; if (FAILED(d3dkmt_open_adapter(&d3dkmt_adapter))) { BOOST_LOG(error) << "D3DKMTOpenAdapterFromLuid() failed while trying to determine GPU HAGS status"; return false; @@ -649,13 +641,12 @@ namespace platf::dxgi { if (SUCCEEDED(d3dkmt_query_adapter_info(&d3dkmt_adapter_info))) { result = d3dkmt_adapter_caps.HwSchEnabled; - } - else { + } else { BOOST_LOG(warning) << "D3DKMTQueryAdapterInfo() failed while trying to determine GPU HAGS status"; result = false; } - D3DKMT_CLOSEADAPTER d3dkmt_close_adapter_wrap = { d3dkmt_adapter.hAdapter }; + D3DKMT_CLOSEADAPTER d3dkmt_close_adapter_wrap = {d3dkmt_adapter.hAdapter}; if (FAILED(d3dkmt_close_adapter(&d3dkmt_close_adapter_wrap))) { BOOST_LOG(error) << "D3DKMTCloseAdapter() failed while trying to determine GPU HAGS status"; } @@ -671,15 +662,16 @@ namespace platf::dxgi { // As of 2023.07, NVIDIA driver has unfixed bug(s) where "realtime" can cause unrecoverable encoding freeze or outright driver crash // This issue happens more frequently with HAGS, in DX12 games or when VRAM is filled close to max capacity // Track OBS to see if they find better workaround or NVIDIA fixes it on their end, they seem to be in communication - if (hags_enabled && !config::video.nv_realtime_hags) priority = D3DKMT_SCHEDULINGPRIORITYCLASS_HIGH; + if (hags_enabled && !config::video.nv_realtime_hags) { + priority = D3DKMT_SCHEDULINGPRIORITYCLASS_HIGH; + } } BOOST_LOG(info) << "Active GPU has HAGS " << (hags_enabled ? "enabled" : "disabled"); BOOST_LOG(info) << "Using " << (priority == D3DKMT_SCHEDULINGPRIORITYCLASS_HIGH ? "high" : "realtime") << " GPU priority"; if (FAILED(d3dkmt_set_process_priority(GetCurrentProcess(), priority))) { BOOST_LOG(warning) << "Failed to adjust GPU priority. Please run application as administrator for optimal performance."; } - } - else { + } else { BOOST_LOG(error) << "Couldn't load D3DKMTSetProcessSchedulingPriorityClass function from gdi32.dll to adjust GPU priority"; } } @@ -740,8 +732,7 @@ namespace platf::dxgi { return 0; } - bool - display_base_t::is_hdr() { + bool display_base_t::is_hdr() { dxgi::output6_t output6 {}; auto status = output->QueryInterface(IID_IDXGIOutput6, (void **) &output6); @@ -756,8 +747,7 @@ namespace platf::dxgi { return desc1.ColorSpace == DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020; } - bool - display_base_t::get_hdr_metadata(SS_HDR_METADATA &metadata) { + bool display_base_t::get_hdr_metadata(SS_HDR_METADATA &metadata) { dxgi::output6_t output6 {}; std::memset(&metadata, 0, sizeof(metadata)); @@ -928,20 +918,31 @@ namespace platf::dxgi { "DXGI_FORMAT_A8P8", "DXGI_FORMAT_B4G4R4A4_UNORM", - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, "DXGI_FORMAT_P208", "DXGI_FORMAT_V208", "DXGI_FORMAT_V408" }; - const char * - display_base_t::dxgi_format_to_string(DXGI_FORMAT format) { + const char *display_base_t::dxgi_format_to_string(DXGI_FORMAT format) { return format_str[format]; } - const char * - display_base_t::colorspace_to_string(DXGI_COLOR_SPACE_TYPE type) { + const char *display_base_t::colorspace_to_string(DXGI_COLOR_SPACE_TYPE type) { const char *type_str[] = { "DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709", "DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709", @@ -972,8 +973,7 @@ namespace platf::dxgi { if (type < ARRAYSIZE(type_str)) { return type_str[type]; - } - else { + } else { return "UNKNOWN"; } } @@ -985,8 +985,7 @@ namespace platf { * Pick a display adapter and capture method. * @param hwdevice_type enables possible use of hardware encoder */ - std::shared_ptr - display(mem_type_e hwdevice_type, const std::string &display_name, const video::config_t &config) { + std::shared_ptr display(mem_type_e hwdevice_type, const std::string &display_name, const video::config_t &config) { if (config::video.capture == "ddx" || config::video.capture.empty()) { if (hwdevice_type == mem_type_e::dxgi) { auto disp = std::make_shared(); @@ -994,8 +993,7 @@ namespace platf { if (!disp->init(config, display_name)) { return disp; } - } - else if (hwdevice_type == mem_type_e::system) { + } else if (hwdevice_type == mem_type_e::system) { auto disp = std::make_shared(); if (!disp->init(config, display_name)) { @@ -1011,8 +1009,7 @@ namespace platf { if (!disp->init(config, display_name)) { return disp; } - } - else if (hwdevice_type == mem_type_e::system) { + } else if (hwdevice_type == mem_type_e::system) { auto disp = std::make_shared(); if (!disp->init(config, display_name)) { @@ -1025,8 +1022,7 @@ namespace platf { return nullptr; } - std::vector - display_names(mem_type_e) { + std::vector display_names(mem_type_e) { std::vector display_names; HRESULT status; @@ -1066,7 +1062,7 @@ namespace platf { 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); @@ -1096,8 +1092,7 @@ namespace platf { * @brief Returns if GPUs/drivers have changed since the last call to this function. * @return `true` if a change has occurred or if it is unknown whether a change occurred. */ - bool - needs_encoder_reenumeration() { + bool needs_encoder_reenumeration() { // Serialize access to the static DXGI factory static std::mutex reenumeration_state_lock; auto lg = std::lock_guard(reenumeration_state_lock); @@ -1117,8 +1112,7 @@ namespace platf { // can deal with any initialization races that may occur when the system is booting. BOOST_LOG(info) << "Encoder reenumeration is required"sv; return true; - } - else { + } else { // The DXGI factory from last time is still current, so no encoder changes have occurred. return false; } diff --git a/src/platform/windows/display_ram.cpp b/src/platform/windows/display_ram.cpp index 1105c674..e8ec540e 100644 --- a/src/platform/windows/display_ram.cpp +++ b/src/platform/windows/display_ram.cpp @@ -2,8 +2,8 @@ * @file src/platform/windows/display_ram.cpp * @brief Definitions for handling ram. */ +// local includes #include "display.h" - #include "misc.h" #include "src/logging.h" @@ -19,8 +19,7 @@ namespace platf::dxgi { } }; - void - blend_cursor_monochrome(const cursor_t &cursor, img_t &img) { + void blend_cursor_monochrome(const cursor_t &cursor, img_t &img) { int height = cursor.shape_info.Height / 2; int width = cursor.shape_info.Width; int pitch = cursor.shape_info.Pitch; @@ -82,8 +81,7 @@ namespace platf::dxgi { } } - void - apply_color_alpha(int *img_pixel_p, int cursor_pixel) { + 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; @@ -91,28 +89,24 @@ namespace platf::dxgi { auto alpha = colors_out[3]; if (alpha == 255) { *img_pixel_p = cursor_pixel; - } - else { + } 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; } } - void - apply_color_masked(int *img_pixel_p, int cursor_pixel) { + 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]; if (alpha == 0xFF) { *img_pixel_p ^= cursor_pixel; - } - else { + } else { *img_pixel_p = cursor_pixel; } } - void - blend_cursor_color(const cursor_t &cursor, img_t &img, const bool masked) { + void blend_cursor_color(const cursor_t &cursor, img_t &img, const bool masked) { int height = cursor.shape_info.Height; int width = cursor.shape_info.Width; int pitch = cursor.shape_info.Pitch; @@ -150,8 +144,7 @@ namespace platf::dxgi { std::for_each(cursor_begin, cursor_end, [&](int cursor_pixel) { if (masked) { apply_color_masked(img_pixel_p, cursor_pixel); - } - else { + } else { apply_color_alpha(img_pixel_p, cursor_pixel); } ++img_pixel_p; @@ -159,8 +152,7 @@ namespace platf::dxgi { } } - void - blend_cursor(const cursor_t &cursor, img_t &img) { + 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); @@ -176,14 +168,13 @@ namespace platf::dxgi { } } - capture_e - display_ddup_ram_t::snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr &img_out, std::chrono::milliseconds timeout, bool cursor_visible) { + capture_e display_ddup_ram_t::snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr &img_out, std::chrono::milliseconds timeout, bool cursor_visible) { HRESULT status; DXGI_OUTDUPL_FRAME_INFO frame_info; 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; @@ -290,8 +281,7 @@ namespace platf::dxgi { if (dummy_img(img)) { return capture_e::error; } - } - else { + } else { // Map the staging texture for CPU access (making it inaccessible for the GPU) status = device_ctx->Map(texture.get(), 0, D3D11_MAP_READ, 0, &img_info); if (FAILED(status)) { @@ -325,13 +315,11 @@ namespace platf::dxgi { return capture_e::ok; } - capture_e - display_ddup_ram_t::release_snapshot() { + capture_e display_ddup_ram_t::release_snapshot() { return dup.release_frame(); } - std::shared_ptr - display_ram_t::alloc_img() { + std::shared_ptr display_ram_t::alloc_img() { auto img = std::make_shared(); // Initialize fields that are format-independent @@ -341,8 +329,7 @@ namespace platf::dxgi { return img; } - int - display_ram_t::complete_img(platf::img_t *img, bool dummy) { + int display_ram_t::complete_img(platf::img_t *img, bool dummy) { // If this is not a dummy image, we must know the format by now if (!dummy && capture_format == DXGI_FORMAT_UNKNOWN) { BOOST_LOG(error) << "display_ram_t::complete_img() called with unknown capture format!"; @@ -373,8 +360,7 @@ namespace platf::dxgi { /** * @memberof platf::dxgi::display_ram_t */ - int - display_ram_t::dummy_img(platf::img_t *img) { + int display_ram_t::dummy_img(platf::img_t *img) { if (complete_img(img, true)) { return -1; } @@ -383,13 +369,11 @@ namespace platf::dxgi { return 0; } - std::vector - display_ram_t::get_supported_capture_formats() { - return { DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT_B8G8R8X8_UNORM }; + std::vector display_ram_t::get_supported_capture_formats() { + return {DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT_B8G8R8X8_UNORM}; } - int - display_ddup_ram_t::init(const ::video::config_t &config, const std::string &display_name) { + int display_ddup_ram_t::init(const ::video::config_t &config, const std::string &display_name) { if (display_base_t::init(config, display_name) || dup.init(this, config)) { return -1; } @@ -397,8 +381,7 @@ namespace platf::dxgi { return 0; } - std::unique_ptr - display_ram_t::make_avcodec_encode_device(pix_fmt_e pix_fmt) { + std::unique_ptr display_ram_t::make_avcodec_encode_device(pix_fmt_e pix_fmt) { return std::make_unique(); } diff --git a/src/platform/windows/display_vram.cpp b/src/platform/windows/display_vram.cpp index bb7ad2bd..bf85af4f 100644 --- a/src/platform/windows/display_vram.cpp +++ b/src/platform/windows/display_vram.cpp @@ -2,8 +2,10 @@ * @file src/platform/windows/display_vram.cpp * @brief Definitions for handling video ram. */ +// standard includes #include +// platform includes #include #include @@ -12,6 +14,11 @@ extern "C" { #include } +// lib includes +#include +#include + +// local includes #include "display.h" #include "misc.h" #include "src/config.h" @@ -22,10 +29,6 @@ extern "C" { #include "src/nvenc/nvenc_utils.h" #include "src/video.h" -#include - -#include - #if !defined(SUNSHINE_SHADERS_DIR) // for testing this needs to be defined in cmake as we don't do an install #define SUNSHINE_SHADERS_DIR SUNSHINE_ASSETS_DIR "/shaders/directx" #endif @@ -33,8 +36,7 @@ namespace platf { using namespace std::literals; } -static void -free_frame(AVFrame *frame) { +static void free_frame(AVFrame *frame) { av_frame_free(&frame); } @@ -42,9 +44,8 @@ using frame_t = util::safe_ptr; namespace platf::dxgi { - template - buf_t - make_buffer(device_t::pointer device, const T &t) { + template + 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 { @@ -64,11 +65,10 @@ namespace platf::dxgi { return nullptr; } - return buf_t { buf_p }; + return buf_t {buf_p}; } - blend_t - make_blend(device_t::pointer device, bool enable, bool invert) { + blend_t make_blend(device_t::pointer device, bool enable, bool invert) { D3D11_BLEND_DESC bdesc {}; auto &rt = bdesc.RenderTarget[0]; rt.BlendEnable = enable; @@ -82,8 +82,7 @@ namespace platf::dxgi { // Invert colors rt.SrcBlend = D3D11_BLEND_INV_DEST_COLOR; rt.DestBlend = D3D11_BLEND_INV_SRC_COLOR; - } - else { + } else { // Regular alpha blending rt.SrcBlend = D3D11_BLEND_SRC_ALPHA; rt.DestBlend = D3D11_BLEND_INV_SRC_ALPHA; @@ -163,8 +162,7 @@ namespace platf::dxgi { bool _locked = false; texture_lock_helper(const texture_lock_helper &) = delete; - texture_lock_helper & - operator=(const texture_lock_helper &) = delete; + texture_lock_helper &operator=(const texture_lock_helper &) = delete; texture_lock_helper(texture_lock_helper &&other) { _mutex.reset(other._mutex.release()); @@ -172,9 +170,10 @@ namespace platf::dxgi { other._locked = false; } - texture_lock_helper & - operator=(texture_lock_helper &&other) { - if (_locked) _mutex->ReleaseSync(0); + texture_lock_helper &operator=(texture_lock_helper &&other) { + if (_locked) { + _mutex->ReleaseSync(0); + } _mutex.reset(other._mutex.release()); _locked = other._locked; other._locked = false; @@ -183,29 +182,32 @@ namespace platf::dxgi { texture_lock_helper(IDXGIKeyedMutex *mutex): _mutex(mutex) { - if (_mutex) _mutex->AddRef(); + if (_mutex) { + _mutex->AddRef(); + } } ~texture_lock_helper() { - if (_locked) _mutex->ReleaseSync(0); + if (_locked) { + _mutex->ReleaseSync(0); + } } - bool - lock() { - if (_locked) return true; + bool lock() { + if (_locked) { + return true; + } HRESULT status = _mutex->AcquireSync(0, INFINITE); if (status == S_OK) { _locked = true; - } - else { + } else { BOOST_LOG(error) << "Failed to acquire texture mutex [0x"sv << util::hex(status).to_string_view() << ']'; } return _locked; } }; - util::buffer_t - make_cursor_xor_image(const util::buffer_t &img_data, DXGI_OUTDUPL_POINTER_SHAPE_INFO shape_info) { + util::buffer_t make_cursor_xor_image(const util::buffer_t &img_data, DXGI_OUTDUPL_POINTER_SHAPE_INFO shape_info) { constexpr std::uint32_t inverted = 0xFFFFFFFF; constexpr std::uint32_t transparent = 0; @@ -213,25 +215,24 @@ namespace platf::dxgi { case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_COLOR: // This type doesn't require any XOR-blending return {}; - case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR: { - util::buffer_t cursor_img = img_data; - std::for_each((std::uint32_t *) std::begin(cursor_img), (std::uint32_t *) std::end(cursor_img), [](auto &pixel) { - auto alpha = (std::uint8_t)((pixel >> 24) & 0xFF); - if (alpha == 0xFF) { - // Pixels with 0xFF alpha will be XOR-blended as is. - } - else if (alpha == 0x00) { - // Pixels with 0x00 alpha will be blended by make_cursor_alpha_image(). - // We make them transparent for the XOR-blended cursor image. - pixel = transparent; - } - else { - // Other alpha values are illegal in masked color cursors - BOOST_LOG(warning) << "Illegal alpha value in masked color cursor: " << alpha; - } - }); - return cursor_img; - } + case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR: + { + util::buffer_t cursor_img = img_data; + std::for_each((std::uint32_t *) std::begin(cursor_img), (std::uint32_t *) std::end(cursor_img), [](auto &pixel) { + auto alpha = (std::uint8_t)((pixel >> 24) & 0xFF); + if (alpha == 0xFF) { + // Pixels with 0xFF alpha will be XOR-blended as is. + } else if (alpha == 0x00) { + // Pixels with 0x00 alpha will be blended by make_cursor_alpha_image(). + // We make them transparent for the XOR-blended cursor image. + pixel = transparent; + } else { + // Other alpha values are illegal in masked color cursors + BOOST_LOG(warning) << "Illegal alpha value in masked color cursor: " << alpha; + } + }); + return cursor_img; + } case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MONOCHROME: // Monochrome is handled below break; @@ -242,7 +243,7 @@ namespace platf::dxgi { shape_info.Height /= 2; - util::buffer_t cursor_img { shape_info.Width * shape_info.Height * 4 }; + util::buffer_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); @@ -275,33 +276,31 @@ namespace platf::dxgi { return cursor_img; } - util::buffer_t - make_cursor_alpha_image(const util::buffer_t &img_data, DXGI_OUTDUPL_POINTER_SHAPE_INFO shape_info) { + util::buffer_t make_cursor_alpha_image(const util::buffer_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: { - util::buffer_t cursor_img = img_data; - std::for_each((std::uint32_t *) std::begin(cursor_img), (std::uint32_t *) std::end(cursor_img), [](auto &pixel) { - auto alpha = (std::uint8_t)((pixel >> 24) & 0xFF); - if (alpha == 0xFF) { - // Pixels with 0xFF alpha will be XOR-blended by make_cursor_xor_image(). - // We make them transparent for the alpha-blended cursor image. - pixel = transparent; - } - else if (alpha == 0x00) { - // Pixels with 0x00 alpha will be blended as opaque with the alpha-blended image. - pixel |= 0xFF000000; - } - else { - // Other alpha values are illegal in masked color cursors - BOOST_LOG(warning) << "Illegal alpha value in masked color cursor: " << alpha; - } - }); - return cursor_img; - } + case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR: + { + util::buffer_t cursor_img = img_data; + std::for_each((std::uint32_t *) std::begin(cursor_img), (std::uint32_t *) std::end(cursor_img), [](auto &pixel) { + auto alpha = (std::uint8_t)((pixel >> 24) & 0xFF); + if (alpha == 0xFF) { + // Pixels with 0xFF alpha will be XOR-blended by make_cursor_xor_image(). + // We make them transparent for the alpha-blended cursor image. + pixel = transparent; + } else if (alpha == 0x00) { + // Pixels with 0x00 alpha will be blended as opaque with the alpha-blended image. + pixel |= 0xFF000000; + } else { + // Other alpha values are illegal in masked color cursors + BOOST_LOG(warning) << "Illegal alpha value in masked color cursor: " << alpha; + } + }); + return cursor_img; + } case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_COLOR: // Color cursors are just an ARGB bitmap which requires no processing. return img_data; @@ -315,7 +314,7 @@ namespace platf::dxgi { shape_info.Height /= 2; - util::buffer_t cursor_img { shape_info.Width * shape_info.Height * 4 }; + util::buffer_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); @@ -350,8 +349,7 @@ namespace platf::dxgi { return cursor_img; } - blob_t - compile_shader(LPCSTR file, LPCSTR entrypoint, LPCSTR shader_model) { + blob_t compile_shader(LPCSTR file, LPCSTR entrypoint, LPCSTR shader_model) { blob_t::pointer msg_p = nullptr; blob_t::pointer compiled_p; @@ -365,7 +363,7 @@ namespace platf::dxgi { auto status = D3DCompileFromFile(wFile.c_str(), nullptr, D3D_COMPILE_STANDARD_FILE_INCLUDE, entrypoint, shader_model, flags, 0, &compiled_p, &msg_p); if (msg_p) { - BOOST_LOG(warning) << std::string_view { (const char *) msg_p->GetBufferPointer(), msg_p->GetBufferSize() - 1 }; + BOOST_LOG(warning) << std::string_view {(const char *) msg_p->GetBufferPointer(), msg_p->GetBufferSize() - 1}; msg_p->Release(); } @@ -374,29 +372,25 @@ namespace platf::dxgi { return nullptr; } - return blob_t { compiled_p }; + return blob_t {compiled_p}; } - blob_t - compile_pixel_shader(LPCSTR file) { + blob_t compile_pixel_shader(LPCSTR file) { return compile_shader(file, "main_ps", "ps_5_0"); } - blob_t - compile_vertex_shader(LPCSTR file) { + blob_t compile_vertex_shader(LPCSTR file) { return compile_shader(file, "main_vs", "vs_5_0"); } class d3d_base_encode_device final { public: - int - convert(platf::img_t &img_base) { + int convert(platf::img_t &img_base) { // Garbage collect mapped capture images whose weak references have expired for (auto it = img_ctx_map.begin(); it != img_ctx_map.end();) { if (it->second.img_weak.expired()) { it = img_ctx_map.erase(it); - } - else { + } else { it++; } } @@ -443,7 +437,9 @@ namespace platf::dxgi { // Clear render target view(s) once so that the aspect ratio mismatch "bars" appear black if (!rtvs_cleared) { auto black = create_black_texture_for_rtv_clear(); - if (black) draw(black, out_Y_or_YUV_viewports_for_clear, out_UV_viewport_for_clear); + if (black) { + draw(black, out_Y_or_YUV_viewports_for_clear, out_UV_viewport_for_clear); + } rtvs_cleared = true; } @@ -460,8 +456,7 @@ namespace platf::dxgi { return 0; } - void - apply_colorspace(const ::video::sunshine_colorspace_t &colorspace) { + void apply_colorspace(const ::video::sunshine_colorspace_t &colorspace) { auto color_vectors = ::video::color_vectors_from_colorspace(colorspace); if (format == DXGI_FORMAT_AYUV || @@ -486,23 +481,22 @@ namespace platf::dxgi { this->color_matrix = std::move(color_matrix); } - int - init_output(ID3D11Texture2D *frame_texture, int width, int height) { + int init_output(ID3D11Texture2D *frame_texture, int width, int height) { // The underlying frame pool owns the texture, so we must reference it for ourselves frame_texture->AddRef(); output_texture.reset(frame_texture); HRESULT status = S_OK; -#define create_vertex_shader_helper(x, y) \ +#define create_vertex_shader_helper(x, y) \ if (FAILED(status = device->CreateVertexShader(x->GetBufferPointer(), x->GetBufferSize(), nullptr, &y))) { \ - BOOST_LOG(error) << "Failed to create vertex shader " << #x << ": " << util::log_hex(status); \ - return -1; \ + BOOST_LOG(error) << "Failed to create vertex shader " << #x << ": " << util::log_hex(status); \ + return -1; \ } -#define create_pixel_shader_helper(x, y) \ +#define create_pixel_shader_helper(x, y) \ if (FAILED(status = device->CreatePixelShader(x->GetBufferPointer(), x->GetBufferSize(), nullptr, &y))) { \ - BOOST_LOG(error) << "Failed to create pixel shader " << #x << ": " << util::log_hex(status); \ - return -1; \ + BOOST_LOG(error) << "Failed to create pixel shader " << #x << ": " << util::log_hex(status); \ + return -1; \ } const bool downscaling = display->width > width || display->height > height; @@ -517,8 +511,7 @@ namespace platf::dxgi { create_vertex_shader_helper(convert_yuv420_packed_uv_type0s_vs_hlsl, convert_UV_vs); create_pixel_shader_helper(convert_yuv420_packed_uv_type0s_ps_hlsl, convert_UV_ps); create_pixel_shader_helper(convert_yuv420_packed_uv_type0s_ps_linear_hlsl, convert_UV_fp16_ps); - } - else { + } else { create_vertex_shader_helper(convert_yuv420_packed_uv_type0_vs_hlsl, convert_UV_vs); create_pixel_shader_helper(convert_yuv420_packed_uv_type0_ps_hlsl, convert_UV_ps); create_pixel_shader_helper(convert_yuv420_packed_uv_type0_ps_linear_hlsl, convert_UV_fp16_ps); @@ -531,8 +524,7 @@ namespace platf::dxgi { create_pixel_shader_helper(convert_yuv420_planar_y_ps_hlsl, convert_Y_or_YUV_ps); if (display->is_hdr()) { create_pixel_shader_helper(convert_yuv420_planar_y_ps_perceptual_quantizer_hlsl, convert_Y_or_YUV_fp16_ps); - } - else { + } else { create_pixel_shader_helper(convert_yuv420_planar_y_ps_linear_hlsl, convert_Y_or_YUV_fp16_ps); } if (downscaling) { @@ -540,18 +532,15 @@ namespace platf::dxgi { create_pixel_shader_helper(convert_yuv420_packed_uv_type0s_ps_hlsl, convert_UV_ps); if (display->is_hdr()) { create_pixel_shader_helper(convert_yuv420_packed_uv_type0s_ps_perceptual_quantizer_hlsl, convert_UV_fp16_ps); - } - else { + } else { create_pixel_shader_helper(convert_yuv420_packed_uv_type0s_ps_linear_hlsl, convert_UV_fp16_ps); } - } - else { + } else { create_vertex_shader_helper(convert_yuv420_packed_uv_type0_vs_hlsl, convert_UV_vs); create_pixel_shader_helper(convert_yuv420_packed_uv_type0_ps_hlsl, convert_UV_ps); if (display->is_hdr()) { create_pixel_shader_helper(convert_yuv420_packed_uv_type0_ps_perceptual_quantizer_hlsl, convert_UV_fp16_ps); - } - else { + } else { create_pixel_shader_helper(convert_yuv420_packed_uv_type0_ps_linear_hlsl, convert_UV_fp16_ps); } } @@ -563,8 +552,7 @@ namespace platf::dxgi { create_pixel_shader_helper(convert_yuv444_planar_ps_hlsl, convert_Y_or_YUV_ps); if (display->is_hdr()) { create_pixel_shader_helper(convert_yuv444_planar_ps_perceptual_quantizer_hlsl, convert_Y_or_YUV_fp16_ps); - } - else { + } else { create_pixel_shader_helper(convert_yuv444_planar_ps_linear_hlsl, convert_Y_or_YUV_fp16_ps); } break; @@ -582,8 +570,7 @@ namespace platf::dxgi { create_pixel_shader_helper(convert_yuv444_packed_y410_ps_hlsl, convert_Y_or_YUV_ps); if (display->is_hdr()) { create_pixel_shader_helper(convert_yuv444_packed_y410_ps_perceptual_quantizer_hlsl, convert_Y_or_YUV_fp16_ps); - } - else { + } else { create_pixel_shader_helper(convert_yuv444_packed_y410_ps_linear_hlsl, convert_Y_or_YUV_fp16_ps); } break; @@ -611,22 +598,22 @@ namespace platf::dxgi { auto offsetX = (out_width - out_width_f) / 2; auto offsetY = (out_height - out_height_f) / 2; - out_Y_or_YUV_viewports[0] = { offsetX, offsetY, out_width_f, out_height_f, 0.0f, 1.0f }; // Y plane + out_Y_or_YUV_viewports[0] = {offsetX, offsetY, out_width_f, out_height_f, 0.0f, 1.0f}; // Y plane out_Y_or_YUV_viewports[1] = out_Y_or_YUV_viewports[0]; // U plane out_Y_or_YUV_viewports[1].TopLeftY += out_height; out_Y_or_YUV_viewports[2] = out_Y_or_YUV_viewports[1]; // V plane out_Y_or_YUV_viewports[2].TopLeftY += out_height; - out_Y_or_YUV_viewports_for_clear[0] = { 0, 0, (float) out_width, (float) out_height, 0.0f, 1.0f }; // Y plane + out_Y_or_YUV_viewports_for_clear[0] = {0, 0, (float) out_width, (float) out_height, 0.0f, 1.0f}; // Y plane out_Y_or_YUV_viewports_for_clear[1] = out_Y_or_YUV_viewports_for_clear[0]; // U plane out_Y_or_YUV_viewports_for_clear[1].TopLeftY += out_height; out_Y_or_YUV_viewports_for_clear[2] = out_Y_or_YUV_viewports_for_clear[1]; // V plane out_Y_or_YUV_viewports_for_clear[2].TopLeftY += out_height; - out_UV_viewport = { offsetX / 2, offsetY / 2, out_width_f / 2, out_height_f / 2, 0.0f, 1.0f }; - out_UV_viewport_for_clear = { 0, 0, (float) out_width / 2, (float) out_height / 2, 0.0f, 1.0f }; + out_UV_viewport = {offsetX / 2, offsetY / 2, out_width_f / 2, out_height_f / 2, 0.0f, 1.0f}; + out_UV_viewport_for_clear = {0, 0, (float) out_width / 2, (float) out_height / 2, 0.0f, 1.0f}; - float subsample_offset_in[16 / sizeof(float)] { 1.0f / (float) out_width_f, 1.0f / (float) out_height_f }; // aligned to 16-byte + float subsample_offset_in[16 / sizeof(float)] {1.0f / (float) out_width_f, 1.0f / (float) out_height_f}; // aligned to 16-byte subsample_offset = make_buffer(device.get(), subsample_offset_in); if (!subsample_offset) { @@ -637,7 +624,7 @@ namespace platf::dxgi { { int32_t rotation_modifier = display->display_rotation == DXGI_MODE_ROTATION_UNSPECIFIED ? 0 : display->display_rotation - 1; - int32_t rotation_data[16 / sizeof(int32_t)] { -rotation_modifier }; // aligned to 16-byte + int32_t rotation_data[16 / sizeof(int32_t)] {-rotation_modifier}; // aligned to 16-byte auto rotation = make_buffer(device.get(), rotation_data); if (!rotation) { BOOST_LOG(error) << "Failed to create display rotation vertex constant buffer"; @@ -695,22 +682,25 @@ namespace platf::dxgi { }; // Create Y/YUV render target view - if (!create_rtv(out_Y_or_YUV_rtv, rtv_Y_or_YUV_format)) return -1; + if (!create_rtv(out_Y_or_YUV_rtv, rtv_Y_or_YUV_format)) { + return -1; + } // Create UV render target view if needed - if (rtv_UV_format != DXGI_FORMAT_UNKNOWN && !create_rtv(out_UV_rtv, rtv_UV_format)) return -1; + if (rtv_UV_format != DXGI_FORMAT_UNKNOWN && !create_rtv(out_UV_rtv, rtv_UV_format)) { + return -1; + } if (rtv_simple_clear) { // Clear the RTVs to ensure the aspect ratio padding is black - const float y_black[] = { 0.0f, 0.0f, 0.0f, 0.0f }; + const float y_black[] = {0.0f, 0.0f, 0.0f, 0.0f}; device_ctx->ClearRenderTargetView(out_Y_or_YUV_rtv.get(), y_black); if (out_UV_rtv) { - const float uv_black[] = { 0.5f, 0.5f, 0.5f, 0.5f }; + const float uv_black[] = {0.5f, 0.5f, 0.5f, 0.5f}; device_ctx->ClearRenderTargetView(out_UV_rtv.get(), uv_black); } rtvs_cleared = true; - } - else { + } else { // Can't use ClearRenderTargetView(), will clear on first convert() rtvs_cleared = false; } @@ -718,8 +708,7 @@ namespace platf::dxgi { return 0; } - int - init(std::shared_ptr display, adapter_t::pointer adapter_p, pix_fmt_e pix_fmt) { + int init(std::shared_ptr display, adapter_t::pointer adapter_p, pix_fmt_e pix_fmt) { switch (pix_fmt) { case pix_fmt_e::nv12: format = DXGI_FORMAT_NV12; @@ -761,11 +750,13 @@ namespace platf::dxgi { D3D_DRIVER_TYPE_UNKNOWN, nullptr, D3D11_CREATE_DEVICE_FLAGS | D3D11_CREATE_DEVICE_VIDEO_SUPPORT, - featureLevels, sizeof(featureLevels) / sizeof(D3D_FEATURE_LEVEL), + featureLevels, + sizeof(featureLevels) / sizeof(D3D_FEATURE_LEVEL), D3D11_SDK_VERSION, &device, nullptr, - &device_ctx); + &device_ctx + ); if (FAILED(status)) { BOOST_LOG(error) << "Failed to create encoder D3D11 device [0x"sv << util::hex(status).to_string_view() << ']'; @@ -842,8 +833,7 @@ namespace platf::dxgi { std::weak_ptr img_weak; - void - reset() { + void reset() { capture_texture_p = nullptr; encoder_texture.reset(); encoder_input_res.reset(); @@ -852,8 +842,7 @@ namespace platf::dxgi { } }; - int - initialize_image_context(const img_d3d_t &img, encoder_img_ctx_t &img_ctx) { + int initialize_image_context(const img_d3d_t &img, encoder_img_ctx_t &img_ctx) { // If we've already opened the shared texture, we're done if (img_ctx.encoder_texture && img.capture_texture.get() == img_ctx.capture_texture_p) { return 0; @@ -898,8 +887,7 @@ namespace platf::dxgi { return 0; } - shader_res_t - create_black_texture_for_rtv_clear() { + shader_res_t create_black_texture_for_rtv_clear() { constexpr auto width = 32; constexpr auto height = 32; @@ -914,7 +902,7 @@ namespace platf::dxgi { texture_desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; std::vector mem(4 * width * height, 0); - D3D11_SUBRESOURCE_DATA texture_data = { mem.data(), 4 * width, 0 }; + D3D11_SUBRESOURCE_DATA texture_data = {mem.data(), 4 * width, 0}; texture2d_t texture; auto status = device->CreateTexture2D(&texture_desc, &texture_data, &texture); @@ -974,25 +962,21 @@ namespace platf::dxgi { class d3d_avcodec_encode_device_t: public avcodec_encode_device_t { public: - int - init(std::shared_ptr display, adapter_t::pointer adapter_p, pix_fmt_e pix_fmt) { + int init(std::shared_ptr display, adapter_t::pointer adapter_p, pix_fmt_e pix_fmt) { int result = base.init(display, adapter_p, pix_fmt); data = base.device.get(); return result; } - int - convert(platf::img_t &img_base) override { + int convert(platf::img_t &img_base) override { return base.convert(img_base); } - void - apply_colorspace() override { + void apply_colorspace() override { base.apply_colorspace(colorspace); } - void - init_hwframes(AVHWFramesContext *frames) override { + void init_hwframes(AVHWFramesContext *frames) override { // We may be called with a QSV or D3D11VA context if (frames->device_ctx->type == AV_HWDEVICE_TYPE_D3D11VA) { auto d3d11_frames = (AVD3D11VAFramesContext *) frames->hwctx; @@ -1006,8 +990,7 @@ namespace platf::dxgi { frames->initial_pool_size = 1; } - int - prepare_to_derive_context(int hw_device_type) override { + int prepare_to_derive_context(int hw_device_type) override { // QuickSync requires our device to be multithread-protected if (hw_device_type == AV_HWDEVICE_TYPE_QSV) { multithread_t mt; @@ -1024,8 +1007,7 @@ namespace platf::dxgi { return 0; } - int - set_frame(AVFrame *frame, AVBufferRef *hw_frames_ctx) override { + int set_frame(AVFrame *frame, AVBufferRef *hw_frames_ctx) override { this->hwframe.reset(frame); this->frame = frame; @@ -1033,7 +1015,7 @@ namespace platf::dxgi { if (!frame->buf[0]) { auto err = av_hwframe_get_buffer(hw_frames_ctx, frame, 0); 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 get hwframe buffer: "sv << av_make_error_string(err_str, AV_ERROR_MAX_STRING_SIZE, err); return -1; } @@ -1042,21 +1024,20 @@ namespace platf::dxgi { // If this is a frame from a derived context, we'll need to map it to D3D11 ID3D11Texture2D *frame_texture; if (frame->format != AV_PIX_FMT_D3D11) { - frame_t d3d11_frame { av_frame_alloc() }; + frame_t d3d11_frame {av_frame_alloc()}; d3d11_frame->format = AV_PIX_FMT_D3D11; auto err = av_hwframe_map(d3d11_frame.get(), frame, AV_HWFRAME_MAP_WRITE | AV_HWFRAME_MAP_OVERWRITE); 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 map D3D11 frame: "sv << av_make_error_string(err_str, AV_ERROR_MAX_STRING_SIZE, err); return -1; } // Get the texture from the mapped frame frame_texture = (ID3D11Texture2D *) d3d11_frame->data[0]; - } - else { + } else { // Otherwise, we can just use the texture inside the original frame frame_texture = (ID3D11Texture2D *) frame->data[0]; } @@ -1071,20 +1052,20 @@ namespace platf::dxgi { class d3d_nvenc_encode_device_t: public nvenc_encode_device_t { public: - bool - init_device(std::shared_ptr display, adapter_t::pointer adapter_p, pix_fmt_e pix_fmt) { + bool init_device(std::shared_ptr display, adapter_t::pointer adapter_p, pix_fmt_e pix_fmt) { buffer_format = nvenc::nvenc_format_from_sunshine_format(pix_fmt); if (buffer_format == NV_ENC_BUFFER_FORMAT_UNDEFINED) { BOOST_LOG(error) << "Unexpected pixel format for NvENC ["sv << from_pix_fmt(pix_fmt) << ']'; return false; } - if (base.init(display, adapter_p, pix_fmt)) return false; + if (base.init(display, adapter_p, pix_fmt)) { + return false; + } if (pix_fmt == pix_fmt_e::yuv444p16) { nvenc_d3d = std::make_unique(base.device.get()); - } - else { + } else { nvenc_d3d = std::make_unique(base.device.get()); } nvenc = nvenc_d3d.get(); @@ -1092,19 +1073,21 @@ namespace platf::dxgi { return true; } - bool - init_encoder(const ::video::config_t &client_config, const ::video::sunshine_colorspace_t &colorspace) override { - if (!nvenc_d3d) return false; + bool init_encoder(const ::video::config_t &client_config, const ::video::sunshine_colorspace_t &colorspace) override { + if (!nvenc_d3d) { + return false; + } auto nvenc_colorspace = nvenc::nvenc_colorspace_from_sunshine_colorspace(colorspace); - if (!nvenc_d3d->create_encoder(config::video.nv, client_config, nvenc_colorspace, buffer_format)) return false; + if (!nvenc_d3d->create_encoder(config::video.nv, client_config, nvenc_colorspace, buffer_format)) { + return false; + } base.apply_colorspace(colorspace); return base.init_output(nvenc_d3d->get_input_texture(), client_config.width, client_config.height) == 0; } - int - convert(platf::img_t &img_base) override { + int convert(platf::img_t &img_base) override { return base.convert(img_base); } @@ -1114,8 +1097,7 @@ namespace platf::dxgi { NV_ENC_BUFFER_FORMAT buffer_format = NV_ENC_BUFFER_FORMAT_UNDEFINED; }; - bool - set_cursor_texture(device_t::pointer device, gpu_cursor_t &cursor, util::buffer_t &&cursor_img, DXGI_OUTDUPL_POINTER_SHAPE_INFO &shape_info) { + bool set_cursor_texture(device_t::pointer device, gpu_cursor_t &cursor, util::buffer_t &&cursor_img, DXGI_OUTDUPL_POINTER_SHAPE_INFO &shape_info) { // This cursor image may not be used if (cursor_img.size() == 0) { cursor.input_res.reset(); @@ -1159,14 +1141,13 @@ namespace platf::dxgi { return true; } - capture_e - display_ddup_vram_t::snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr &img_out, std::chrono::milliseconds timeout, bool cursor_visible) { + capture_e display_ddup_vram_t::snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr &img_out, std::chrono::milliseconds timeout, bool cursor_visible) { HRESULT status; DXGI_OUTDUPL_FRAME_INFO frame_info; 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; @@ -1189,7 +1170,7 @@ namespace platf::dxgi { if (frame_info.PointerShapeBufferSize > 0) { DXGI_OUTDUPL_POINTER_SHAPE_INFO shape_info {}; - util::buffer_t img_data { frame_info.PointerShapeBufferSize }; + util::buffer_t img_data {frame_info.PointerShapeBufferSize}; UINT dummy; status = dup.dup->GetFramePointerShape(img_data.size(), std::begin(img_data), &dummy, &shape_info); @@ -1209,11 +1190,9 @@ namespace platf::dxgi { } if (frame_info.LastMouseUpdateTime.QuadPart) { - cursor_alpha.set_pos(frame_info.PointerPosition.Position.x, frame_info.PointerPosition.Position.y, - width, height, display_rotation, frame_info.PointerPosition.Visible); + cursor_alpha.set_pos(frame_info.PointerPosition.Position.x, frame_info.PointerPosition.Position.y, width, height, display_rotation, frame_info.PointerPosition.Visible); - cursor_xor.set_pos(frame_info.PointerPosition.Position.x, frame_info.PointerPosition.Position.y, - width, height, display_rotation, frame_info.PointerPosition.Visible); + cursor_xor.set_pos(frame_info.PointerPosition.Position.x, frame_info.PointerPosition.Position.y, width, height, display_rotation, frame_info.PointerPosition.Visible); } const bool blend_mouse_cursor_flag = (cursor_alpha.visible || cursor_xor.visible) && cursor_visible; @@ -1272,8 +1251,7 @@ namespace platf::dxgi { // We don't know the final capture format yet, so we will encode a black dummy image last_frame_action = lfa::nothing; out_frame_action = ofa::dummy_fallback; - } - else { + } else { if (src) { // We got a new frame from DesktopDuplication... if (blend_mouse_cursor_flag) { @@ -1285,8 +1263,7 @@ namespace platf::dxgi { last_frame_action = lfa::copy_src_to_surface; // Copy the intermediate surface to a new image from pull_free_image_cb and blend the mouse cursor onto it. out_frame_action = ofa::copy_last_surface_and_blend_cursor; - } - else { + } else { // ...and we don't need to blend the mouse cursor. // Copy the frame to a new image from pull_free_image_cb and save the shared pointer to the image // in case the mouse cursor appears without a new frame from DesktopDuplication. @@ -1294,8 +1271,7 @@ namespace platf::dxgi { // Use saved last image shared pointer as output image evading copy. out_frame_action = ofa::forward_last_img; } - } - else if (!std::holds_alternative(last_frame_variant)) { + } else if (!std::holds_alternative(last_frame_variant)) { // We didn't get a new frame from DesktopDuplication... if (blend_mouse_cursor_flag) { // ...but we need to blend the mouse cursor. @@ -1307,8 +1283,7 @@ namespace platf::dxgi { // Copy the intermediate surface which contains last DesktopDuplication frame // to a new image from pull_free_image_cb and blend the mouse cursor onto it. out_frame_action = ofa::copy_last_surface_and_blend_cursor; - } - else { + } else { // ...and we don't need to blend the mouse cursor. // This happens when the mouse cursor disappears from screen, // or there's mouse cursor on screen, but its drawing is disabled in sunshine. @@ -1355,92 +1330,113 @@ namespace platf::dxgi { // Finish creating the image (if it hasn't happened already), // also creates synchronization primitives for shared access from multiple direct3d devices. - if (complete_img(d3d_img.get(), dummy)) return { nullptr, nullptr }; + if (complete_img(d3d_img.get(), dummy)) { + return {nullptr, nullptr}; + } // This image is shared between capture direct3d device and encoders direct3d devices, // we must acquire lock before doing anything to it. texture_lock_helper lock_helper(d3d_img->capture_mutex.get()); if (!lock_helper.lock()) { BOOST_LOG(error) << "Failed to lock capture texture"; - return { nullptr, nullptr }; + return {nullptr, nullptr}; } // Clear the blank flag now that we're ready to capture into the image d3d_img->blank = false; - return { std::move(d3d_img), std::move(lock_helper) }; + return {std::move(d3d_img), std::move(lock_helper)}; }; switch (last_frame_action) { - case lfa::nothing: { - break; - } - - case lfa::replace_surface_with_img: { - auto p_surface = std::get_if(&last_frame_variant); - if (!p_surface) { - BOOST_LOG(error) << "Logical error at " << __FILE__ << ":" << __LINE__; - return capture_e::error; + case lfa::nothing: + { + break; } - std::shared_ptr img; - if (!pull_free_image_cb(img)) return capture_e::interrupted; + case lfa::replace_surface_with_img: + { + auto p_surface = std::get_if(&last_frame_variant); + if (!p_surface) { + BOOST_LOG(error) << "Logical error at " << __FILE__ << ":" << __LINE__; + return capture_e::error; + } - auto [d3d_img, lock] = get_locked_d3d_img(img); - if (!d3d_img) return capture_e::error; + std::shared_ptr img; + if (!pull_free_image_cb(img)) { + return capture_e::interrupted; + } - device_ctx->CopyResource(d3d_img->capture_texture.get(), p_surface->get()); + auto [d3d_img, lock] = get_locked_d3d_img(img); + if (!d3d_img) { + return capture_e::error; + } - // We delay the destruction of intermediate surface in case the mouse cursor reappears shortly. - old_surface_delayed_destruction.reset(p_surface->release()); - old_surface_timestamp = std::chrono::steady_clock::now(); + device_ctx->CopyResource(d3d_img->capture_texture.get(), p_surface->get()); - last_frame_variant = img; - break; - } + // We delay the destruction of intermediate surface in case the mouse cursor reappears shortly. + old_surface_delayed_destruction.reset(p_surface->release()); + old_surface_timestamp = std::chrono::steady_clock::now(); - case lfa::replace_img_with_surface: { - auto p_img = std::get_if>(&last_frame_variant); - if (!p_img) { - BOOST_LOG(error) << "Logical error at " << __FILE__ << ":" << __LINE__; - return capture_e::error; + last_frame_variant = img; + break; } - auto [d3d_img, lock] = get_locked_d3d_img(*p_img); - if (!d3d_img) return capture_e::error; - p_img = nullptr; - last_frame_variant = texture2d_t {}; - auto &surface = std::get(last_frame_variant); - if (!create_surface(surface)) return capture_e::error; + case lfa::replace_img_with_surface: + { + auto p_img = std::get_if>(&last_frame_variant); + if (!p_img) { + BOOST_LOG(error) << "Logical error at " << __FILE__ << ":" << __LINE__; + return capture_e::error; + } + auto [d3d_img, lock] = get_locked_d3d_img(*p_img); + if (!d3d_img) { + return capture_e::error; + } - device_ctx->CopyResource(surface.get(), d3d_img->capture_texture.get()); - break; - } - - case lfa::copy_src_to_img: { - last_frame_variant = {}; - - std::shared_ptr img; - if (!pull_free_image_cb(img)) return capture_e::interrupted; - - auto [d3d_img, lock] = get_locked_d3d_img(img); - if (!d3d_img) return capture_e::error; - - device_ctx->CopyResource(d3d_img->capture_texture.get(), src.get()); - last_frame_variant = img; - break; - } - - case lfa::copy_src_to_surface: { - auto p_surface = std::get_if(&last_frame_variant); - if (!p_surface) { + p_img = nullptr; last_frame_variant = texture2d_t {}; - p_surface = std::get_if(&last_frame_variant); - if (!create_surface(*p_surface)) return capture_e::error; + auto &surface = std::get(last_frame_variant); + if (!create_surface(surface)) { + return capture_e::error; + } + + device_ctx->CopyResource(surface.get(), d3d_img->capture_texture.get()); + break; + } + + case lfa::copy_src_to_img: + { + last_frame_variant = {}; + + std::shared_ptr img; + if (!pull_free_image_cb(img)) { + return capture_e::interrupted; + } + + auto [d3d_img, lock] = get_locked_d3d_img(img); + if (!d3d_img) { + return capture_e::error; + } + + device_ctx->CopyResource(d3d_img->capture_texture.get(), src.get()); + last_frame_variant = img; + break; + } + + case lfa::copy_src_to_surface: + { + auto p_surface = std::get_if(&last_frame_variant); + if (!p_surface) { + last_frame_variant = texture2d_t {}; + p_surface = std::get_if(&last_frame_variant); + if (!create_surface(*p_surface)) { + return capture_e::error; + } + } + device_ctx->CopyResource(p_surface->get(), src.get()); + break; } - device_ctx->CopyResource(p_surface->get(), src.get()); - break; - } } auto blend_cursor = [&](img_d3d_t &d3d_img) { @@ -1476,59 +1472,70 @@ namespace platf::dxgi { }; switch (out_frame_action) { - case ofa::forward_last_img: { - auto p_img = std::get_if>(&last_frame_variant); - if (!p_img) { - BOOST_LOG(error) << "Logical error at " << __FILE__ << ":" << __LINE__; - return capture_e::error; - } - img_out = *p_img; - break; - } - - case ofa::copy_last_surface_and_blend_cursor: { - auto p_surface = std::get_if(&last_frame_variant); - if (!p_surface) { - BOOST_LOG(error) << "Logical error at " << __FILE__ << ":" << __LINE__; - return capture_e::error; - } - if (!blend_mouse_cursor_flag) { - BOOST_LOG(error) << "Logical error at " << __FILE__ << ":" << __LINE__; - return capture_e::error; + case ofa::forward_last_img: + { + auto p_img = std::get_if>(&last_frame_variant); + if (!p_img) { + BOOST_LOG(error) << "Logical error at " << __FILE__ << ":" << __LINE__; + return capture_e::error; + } + img_out = *p_img; + break; } - if (!pull_free_image_cb(img_out)) return capture_e::interrupted; + case ofa::copy_last_surface_and_blend_cursor: + { + auto p_surface = std::get_if(&last_frame_variant); + if (!p_surface) { + BOOST_LOG(error) << "Logical error at " << __FILE__ << ":" << __LINE__; + return capture_e::error; + } + if (!blend_mouse_cursor_flag) { + BOOST_LOG(error) << "Logical error at " << __FILE__ << ":" << __LINE__; + return capture_e::error; + } - auto [d3d_img, lock] = get_locked_d3d_img(img_out); - if (!d3d_img) return capture_e::error; + if (!pull_free_image_cb(img_out)) { + return capture_e::interrupted; + } - device_ctx->CopyResource(d3d_img->capture_texture.get(), p_surface->get()); - blend_cursor(*d3d_img); - break; - } + auto [d3d_img, lock] = get_locked_d3d_img(img_out); + if (!d3d_img) { + return capture_e::error; + } - case ofa::dummy_fallback: { - if (!pull_free_image_cb(img_out)) return capture_e::interrupted; - - // Clear the image if it has been used as a dummy. - // It can have the mouse cursor blended onto it. - auto old_d3d_img = (img_d3d_t *) img_out.get(); - bool reclear_dummy = !old_d3d_img->blank && old_d3d_img->capture_texture; - - auto [d3d_img, lock] = get_locked_d3d_img(img_out, true); - if (!d3d_img) return capture_e::error; - - if (reclear_dummy) { - const float rgb_black[] = { 0.0f, 0.0f, 0.0f, 0.0f }; - device_ctx->ClearRenderTargetView(d3d_img->capture_rt.get(), rgb_black); - } - - if (blend_mouse_cursor_flag) { + device_ctx->CopyResource(d3d_img->capture_texture.get(), p_surface->get()); blend_cursor(*d3d_img); + break; } - break; - } + case ofa::dummy_fallback: + { + if (!pull_free_image_cb(img_out)) { + return capture_e::interrupted; + } + + // Clear the image if it has been used as a dummy. + // It can have the mouse cursor blended onto it. + auto old_d3d_img = (img_d3d_t *) img_out.get(); + bool reclear_dummy = !old_d3d_img->blank && old_d3d_img->capture_texture; + + auto [d3d_img, lock] = get_locked_d3d_img(img_out, true); + if (!d3d_img) { + return capture_e::error; + } + + if (reclear_dummy) { + const float rgb_black[] = {0.0f, 0.0f, 0.0f, 0.0f}; + device_ctx->ClearRenderTargetView(d3d_img->capture_rt.get(), rgb_black); + } + + if (blend_mouse_cursor_flag) { + blend_cursor(*d3d_img); + } + + break; + } } // Perform delayed destruction of the unused surface if the time is due. @@ -1543,13 +1550,11 @@ namespace platf::dxgi { return capture_e::ok; } - capture_e - display_ddup_vram_t::release_snapshot() { + capture_e display_ddup_vram_t::release_snapshot() { return dup.release_frame(); } - int - display_ddup_vram_t::init(const ::video::config_t &config, const std::string &display_name) { + int display_ddup_vram_t::init(const ::video::config_t &config, const std::string &display_name) { if (display_base_t::init(config, display_name) || dup.init(this, config)) { return -1; } @@ -1577,7 +1582,7 @@ namespace platf::dxgi { { int32_t rotation_modifier = display_rotation == DXGI_MODE_ROTATION_UNSPECIFIED ? 0 : display_rotation - 1; - int32_t rotation_data[16 / sizeof(int32_t)] { rotation_modifier }; // aligned to 16-byte + int32_t rotation_data[16 / sizeof(int32_t)] {rotation_modifier}; // aligned to 16-byte auto rotation = make_buffer(device.get(), rotation_data); if (!rotation) { BOOST_LOG(error) << "Failed to create display rotation vertex constant buffer"; @@ -1597,7 +1602,7 @@ namespace platf::dxgi { // Use a 300 nit target for the mouse cursor. We should really get // the user's SDR white level in nits, but there is no API that // provides that information to Win32 apps. - float white_multiplier_data[16 / sizeof(float)] { 300.0f / 80.f }; // aligned to 16-byte + float white_multiplier_data[16 / sizeof(float)] {300.0f / 80.f}; // aligned to 16-byte auto white_multiplier = make_buffer(device.get(), white_multiplier_data); if (!white_multiplier) { BOOST_LOG(warning) << "Failed to create cursor blending (normalized white) white multiplier constant buffer"; @@ -1605,8 +1610,7 @@ namespace platf::dxgi { } device_ctx->PSSetConstantBuffers(1, 1, &white_multiplier); - } - else { + } else { status = device->CreatePixelShader(cursor_ps_hlsl->GetBufferPointer(), cursor_ps_hlsl->GetBufferSize(), nullptr, &cursor_ps); if (status) { BOOST_LOG(error) << "Failed to create cursor blending pixel shader [0x"sv << util::hex(status).to_string_view() << ']'; @@ -1636,14 +1640,14 @@ namespace platf::dxgi { * @param timeout how long to wait for the next frame * @param cursor_visible */ - capture_e - display_wgc_vram_t::snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr &img_out, std::chrono::milliseconds timeout, bool cursor_visible) { + capture_e display_wgc_vram_t::snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr &img_out, std::chrono::milliseconds timeout, bool cursor_visible) { texture2d_t src; uint64_t frame_qpc; dup.set_cursor_visible(cursor_visible); auto capture_status = dup.next_frame(timeout, &src, frame_qpc); - if (capture_status != capture_e::ok) + if (capture_status != capture_e::ok) { return capture_status; + } auto frame_timestamp = std::chrono::steady_clock::now() - qpc_time_difference(qpc_counter(), frame_qpc); D3D11_TEXTURE2D_DESC desc; @@ -1664,8 +1668,9 @@ namespace platf::dxgi { } std::shared_ptr img; - if (!pull_free_image_cb(img)) + if (!pull_free_image_cb(img)) { return capture_e::interrupted; + } auto d3d_img = std::static_pointer_cast(img); d3d_img->blank = false; // image is always ready for capture @@ -1673,13 +1678,11 @@ namespace platf::dxgi { texture_lock_helper lock_helper(d3d_img->capture_mutex.get()); if (lock_helper.lock()) { device_ctx->CopyResource(d3d_img->capture_texture.get(), src.get()); - } - else { + } else { BOOST_LOG(error) << "Failed to lock capture texture"; return capture_e::error; } - } - else { + } else { return capture_e::error; } img_out = img; @@ -1690,21 +1693,19 @@ namespace platf::dxgi { return capture_e::ok; } - capture_e - display_wgc_vram_t::release_snapshot() { + capture_e display_wgc_vram_t::release_snapshot() { return dup.release_frame(); } - int - display_wgc_vram_t::init(const ::video::config_t &config, const std::string &display_name) { - if (display_base_t::init(config, display_name) || dup.init(this, config)) + int display_wgc_vram_t::init(const ::video::config_t &config, const std::string &display_name) { + if (display_base_t::init(config, display_name) || dup.init(this, config)) { return -1; + } return 0; } - std::shared_ptr - display_vram_t::alloc_img() { + std::shared_ptr display_vram_t::alloc_img() { auto img = std::make_shared(); // Initialize format-independent fields @@ -1717,8 +1718,7 @@ namespace platf::dxgi { } // This cannot use ID3D11DeviceContext because it can be called concurrently by the encoding thread - int - display_vram_t::complete_img(platf::img_t *img_base, bool dummy) { + int display_vram_t::complete_img(platf::img_t *img_base, bool dummy) { auto img = (img_d3d_t *) img_base; // If this already has a capture texture and it's not switching dummy state, nothing to do @@ -1801,13 +1801,11 @@ namespace platf::dxgi { /** * @memberof platf::dxgi::display_vram_t */ - int - display_vram_t::dummy_img(platf::img_t *img_base) { + int display_vram_t::dummy_img(platf::img_t *img_base) { return complete_img(img_base, true); } - std::vector - display_vram_t::get_supported_capture_formats() { + std::vector display_vram_t::get_supported_capture_formats() { return { // scRGB FP16 is the ideal format for Wide Color Gamut and Advanced Color // displays (both SDR and HDR). This format uses linear gamma, so we will @@ -1837,8 +1835,7 @@ namespace platf::dxgi { * @param config The codec configuration. * @return `true` if supported, `false` otherwise. */ - bool - display_vram_t::is_codec_supported(std::string_view name, const ::video::config_t &config) { + bool display_vram_t::is_codec_supported(std::string_view name, const ::video::config_t &config) { DXGI_ADAPTER_DESC adapter_desc; adapter->GetDesc(&adapter_desc); @@ -1871,8 +1868,7 @@ namespace platf::dxgi { << AMF_GET_BUILD_VERSION(version); BOOST_LOG(warning) << "If your AMD GPU supports AV1 encoding, update your graphics drivers!"sv; return false; - } - else if (config.dynamicRange && version < AMF_MAKE_FULL_VERSION(1, 4, 23, 0)) { + } else if (config.dynamicRange && version < AMF_MAKE_FULL_VERSION(1, 4, 23, 0)) { // Older versions of the AMD AMF runtime can crash when fed P010 surfaces. // Fail if AMF version is below 1.4.23 where HEVC Main10 encoding was introduced. // AMF 1.4.23 corresponds to driver version 21.12.1 (21.40.11.03) or newer. @@ -1884,20 +1880,16 @@ namespace platf::dxgi { BOOST_LOG(warning) << "If your AMD GPU supports HEVC Main10 encoding, update your graphics drivers!"sv; return false; } - } - else { + } else { BOOST_LOG(warning) << "AMFQueryVersion() failed: "sv << result; } - } - else { + } else { BOOST_LOG(warning) << "AMF DLL missing export: "sv << AMF_QUERY_VERSION_FUNCTION_NAME; } - } - else { + } else { BOOST_LOG(warning) << "Detected AMD GPU but AMF failed to load"sv; } - } - else if (adapter_desc.VendorId == 0x8086) { // Intel + } else if (adapter_desc.VendorId == 0x8086) { // Intel // If it's not a QSV encoder, it's not compatible with an Intel GPU if (!boost::algorithm::ends_with(name, "_qsv")) { return false; @@ -1909,22 +1901,19 @@ namespace platf::dxgi { } // TODO: Blacklist HEVC 4:4:4 based on adapter model } - } - else if (adapter_desc.VendorId == 0x10de) { // Nvidia + } else if (adapter_desc.VendorId == 0x10de) { // Nvidia // If it's not an NVENC encoder, it's not compatible with an Nvidia GPU if (!boost::algorithm::ends_with(name, "_nvenc")) { return false; } - } - else { + } else { BOOST_LOG(warning) << "Unknown GPU vendor ID: " << util::hex(adapter_desc.VendorId).to_string_view(); } return true; } - std::unique_ptr - display_vram_t::make_avcodec_encode_device(pix_fmt_e pix_fmt) { + std::unique_ptr display_vram_t::make_avcodec_encode_device(pix_fmt_e pix_fmt) { auto device = std::make_unique(); if (device->init(shared_from_this(), adapter.get(), pix_fmt) != 0) { return nullptr; @@ -1932,8 +1921,7 @@ namespace platf::dxgi { return device; } - std::unique_ptr - display_vram_t::make_nvenc_encode_device(pix_fmt_e pix_fmt) { + std::unique_ptr display_vram_t::make_nvenc_encode_device(pix_fmt_e pix_fmt) { auto device = std::make_unique(); if (!device->init_device(shared_from_this(), adapter.get(), pix_fmt)) { return nullptr; @@ -1941,14 +1929,15 @@ namespace platf::dxgi { return device; } - int - init() { + int init() { BOOST_LOG(info) << "Compiling shaders..."sv; #define compile_vertex_shader_helper(x) \ - if (!(x##_hlsl = compile_vertex_shader(SUNSHINE_SHADERS_DIR "/" #x ".hlsl"))) return -1; + if (!(x##_hlsl = compile_vertex_shader(SUNSHINE_SHADERS_DIR "/" #x ".hlsl"))) \ + return -1; #define compile_pixel_shader_helper(x) \ - if (!(x##_hlsl = compile_pixel_shader(SUNSHINE_SHADERS_DIR "/" #x ".hlsl"))) return -1; + if (!(x##_hlsl = compile_pixel_shader(SUNSHINE_SHADERS_DIR "/" #x ".hlsl"))) \ + return -1; compile_pixel_shader_helper(convert_yuv420_packed_uv_type0_ps); compile_pixel_shader_helper(convert_yuv420_packed_uv_type0_ps_linear); diff --git a/src/platform/windows/display_wgc.cpp b/src/platform/windows/display_wgc.cpp index df5567ee..5b31dcae 100644 --- a/src/platform/windows/display_wgc.cpp +++ b/src/platform/windows/display_wgc.cpp @@ -1,336 +1,335 @@ -/** - * @file src/platform/windows/display_wgc.cpp - * @brief Definitions for WinRT Windows.Graphics.Capture API - */ -#include - -#include "display.h" - -#include "misc.h" -#include "src/logging.h" - -// Gross hack to work around MINGW-packages#22160 -#define ____FIReference_1_boolean_INTERFACE_DEFINED__ - -#include -#include -#include -#include - -namespace platf { - using namespace std::literals; -} - -namespace winrt { - using namespace Windows::Foundation; - using namespace Windows::Foundation::Metadata; - using namespace Windows::Graphics::Capture; - using namespace Windows::Graphics::DirectX::Direct3D11; - - extern "C" { - HRESULT __stdcall CreateDirect3D11DeviceFromDXGIDevice(::IDXGIDevice *dxgiDevice, ::IInspectable **graphicsDevice); - } - - /** - * Windows structures sometimes have compile-time GUIDs. GCC supports this, but in a roundabout way. - * If WINRT_IMPL_HAS_DECLSPEC_UUID is true, then the compiler supports adding this attribute to a struct. For example, Visual Studio. - * If not, then MinGW GCC has a workaround to assign a GUID to a structure. - */ - struct -#if WINRT_IMPL_HAS_DECLSPEC_UUID - __declspec(uuid("A9B3D012-3DF2-4EE3-B8D1-8695F457D3C1")) -#endif - IDirect3DDxgiInterfaceAccess: ::IUnknown { - virtual HRESULT __stdcall GetInterface(REFIID id, void **object) = 0; - }; -} // namespace winrt -#if !WINRT_IMPL_HAS_DECLSPEC_UUID -static constexpr GUID GUID__IDirect3DDxgiInterfaceAccess = { - 0xA9B3D012, 0x3DF2, 0x4EE3, { 0xB8, 0xD1, 0x86, 0x95, 0xF4, 0x57, 0xD3, 0xC1 } - // compare with __declspec(uuid(...)) for the struct above. -}; -template <> -constexpr auto -__mingw_uuidof() -> GUID const & { - return GUID__IDirect3DDxgiInterfaceAccess; -} -#endif - -namespace platf::dxgi { - wgc_capture_t::wgc_capture_t() { - InitializeConditionVariable(&frame_present_cv); - } - - wgc_capture_t::~wgc_capture_t() { - if (capture_session) - capture_session.Close(); - if (frame_pool) - frame_pool.Close(); - item = nullptr; - capture_session = nullptr; - frame_pool = nullptr; - } - - /** - * @brief Initialize the Windows.Graphics.Capture backend. - * @return 0 on success, -1 on failure. - */ - int - wgc_capture_t::init(display_base_t *display, const ::video::config_t &config) { - HRESULT status; - dxgi::dxgi_t dxgi; - winrt::com_ptr<::IInspectable> d3d_comhandle; - try { - if (!winrt::GraphicsCaptureSession::IsSupported()) { - BOOST_LOG(error) << "Screen capture is not supported on this device for this release of Windows!"sv; - return -1; - } - if (FAILED(status = display->device->QueryInterface(IID_IDXGIDevice, (void **) &dxgi))) { - BOOST_LOG(error) << "Failed to query DXGI interface from device [0x"sv << util::hex(status).to_string_view() << ']'; - return -1; - } - if (FAILED(status = winrt::CreateDirect3D11DeviceFromDXGIDevice(*&dxgi, d3d_comhandle.put()))) { - BOOST_LOG(error) << "Failed to query WinRT DirectX interface from device [0x"sv << util::hex(status).to_string_view() << ']'; - return -1; - } - } - catch (winrt::hresult_error &e) { - BOOST_LOG(error) << "Screen capture is not supported on this device for this release of Windows: failed to acquire device: [0x"sv << util::hex(e.code()).to_string_view() << ']'; - return -1; - } - - DXGI_OUTPUT_DESC output_desc; - uwp_device = d3d_comhandle.as(); - display->output->GetDesc(&output_desc); - - auto monitor_factory = winrt::get_activation_factory(); - if (monitor_factory == nullptr || - FAILED(status = monitor_factory->CreateForMonitor(output_desc.Monitor, winrt::guid_of(), winrt::put_abi(item)))) { - BOOST_LOG(error) << "Screen capture is not supported on this device for this release of Windows: failed to acquire display: [0x"sv << util::hex(status).to_string_view() << ']'; - return -1; - } - - if (config.dynamicRange) - display->capture_format = DXGI_FORMAT_R16G16B16A16_FLOAT; - else - display->capture_format = DXGI_FORMAT_B8G8R8A8_UNORM; - - try { - frame_pool = winrt::Direct3D11CaptureFramePool::CreateFreeThreaded(uwp_device, static_cast(display->capture_format), 2, item.Size()); - capture_session = frame_pool.CreateCaptureSession(item); - frame_pool.FrameArrived({ this, &wgc_capture_t::on_frame_arrived }); - } - catch (winrt::hresult_error &e) { - BOOST_LOG(error) << "Screen capture is not supported on this device for this release of Windows: failed to create capture session: [0x"sv << util::hex(e.code()).to_string_view() << ']'; - return -1; - } - try { - if (winrt::ApiInformation::IsPropertyPresent(L"Windows.Graphics.Capture.GraphicsCaptureSession", L"IsBorderRequired")) { - capture_session.IsBorderRequired(false); - } - else { - BOOST_LOG(warning) << "Can't disable colored border around capture area on this version of Windows"; - } - } - catch (winrt::hresult_error &e) { - BOOST_LOG(warning) << "Screen capture may not be fully supported on this device for this release of Windows: failed to disable border around capture area: [0x"sv << util::hex(e.code()).to_string_view() << ']'; - } - try { - capture_session.StartCapture(); - } - catch (winrt::hresult_error &e) { - BOOST_LOG(error) << "Screen capture is not supported on this device for this release of Windows: failed to start capture: [0x"sv << util::hex(e.code()).to_string_view() << ']'; - return -1; - } - return 0; - } - - /** - * This function runs in a separate thread spawned by the frame pool and is a producer of frames. - * To maintain parity with the original display interface, this frame will be consumed by the capture thread. - * Acquire a read-write lock, make the produced frame available to the capture thread, then wake the capture thread. - */ - void - wgc_capture_t::on_frame_arrived(winrt::Direct3D11CaptureFramePool const &sender, winrt::IInspectable const &) { - winrt::Windows::Graphics::Capture::Direct3D11CaptureFrame frame { nullptr }; - try { - frame = sender.TryGetNextFrame(); - } - catch (winrt::hresult_error &e) { - BOOST_LOG(warning) << "Failed to capture frame: "sv << e.code(); - return; - } - if (frame != nullptr) { - AcquireSRWLockExclusive(&frame_lock); - if (produced_frame) - produced_frame.Close(); - - produced_frame = frame; - ReleaseSRWLockExclusive(&frame_lock); - WakeConditionVariable(&frame_present_cv); - } - } - - /** - * @brief Get the next frame from the producer thread. - * If not available, the capture thread blocks until one is, or the wait times out. - * @param timeout how long to wait for the next frame - * @param out a texture containing the frame just captured - * @param out_time the timestamp of the frame just captured - */ - capture_e - wgc_capture_t::next_frame(std::chrono::milliseconds timeout, ID3D11Texture2D **out, uint64_t &out_time) { - // this CONSUMER runs in the capture thread - release_frame(); - - AcquireSRWLockExclusive(&frame_lock); - if (produced_frame == nullptr && SleepConditionVariableSRW(&frame_present_cv, &frame_lock, timeout.count(), 0) == 0) { - ReleaseSRWLockExclusive(&frame_lock); - if (GetLastError() == ERROR_TIMEOUT) - return capture_e::timeout; - else - return capture_e::error; - } - if (produced_frame) { - consumed_frame = produced_frame; - produced_frame = nullptr; - } - ReleaseSRWLockExclusive(&frame_lock); - if (consumed_frame == nullptr) // spurious wakeup - return capture_e::timeout; - - auto capture_access = consumed_frame.Surface().as(); - if (capture_access == nullptr) - return capture_e::error; - capture_access->GetInterface(IID_ID3D11Texture2D, (void **) out); - out_time = consumed_frame.SystemRelativeTime().count(); // raw ticks from query performance counter - return capture_e::ok; - } - - capture_e - wgc_capture_t::release_frame() { - if (consumed_frame != nullptr) { - consumed_frame.Close(); - consumed_frame = nullptr; - } - return capture_e::ok; - } - - int - wgc_capture_t::set_cursor_visible(bool x) { - try { - if (capture_session.IsCursorCaptureEnabled() != x) - capture_session.IsCursorCaptureEnabled(x); - return 0; - } - catch (winrt::hresult_error &) { - return -1; - } - } - - int - display_wgc_ram_t::init(const ::video::config_t &config, const std::string &display_name) { - if (display_base_t::init(config, display_name) || dup.init(this, config)) - return -1; - - texture.reset(); - return 0; - } - - /** - * @brief Get the next frame from the Windows.Graphics.Capture API and copy it into a new snapshot texture. - * @param pull_free_image_cb call this to get a new free image from the video subsystem. - * @param img_out the captured frame is returned here - * @param timeout how long to wait for the next frame - * @param cursor_visible whether to capture the cursor - */ - capture_e - display_wgc_ram_t::snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr &img_out, std::chrono::milliseconds timeout, bool cursor_visible) { - HRESULT status; - texture2d_t src; - uint64_t frame_qpc; - dup.set_cursor_visible(cursor_visible); - auto capture_status = dup.next_frame(timeout, &src, frame_qpc); - if (capture_status != capture_e::ok) - return capture_status; - - auto frame_timestamp = std::chrono::steady_clock::now() - qpc_time_difference(qpc_counter(), frame_qpc); - D3D11_TEXTURE2D_DESC desc; - src->GetDesc(&desc); - - // Create the staging texture if it doesn't exist. It should match the source in size and format. - if (texture == nullptr) { - capture_format = desc.Format; - BOOST_LOG(info) << "Capture format ["sv << dxgi_format_to_string(capture_format) << ']'; - - D3D11_TEXTURE2D_DESC t {}; - t.Width = width; - t.Height = height; - t.MipLevels = 1; - t.ArraySize = 1; - t.SampleDesc.Count = 1; - t.Usage = D3D11_USAGE_STAGING; - t.Format = capture_format; - t.CPUAccessFlags = D3D11_CPU_ACCESS_READ; - - auto status = device->CreateTexture2D(&t, nullptr, &texture); - - if (FAILED(status)) { - BOOST_LOG(error) << "Failed to create staging texture [0x"sv << util::hex(status).to_string_view() << ']'; - return capture_e::error; - } - } - - // It's possible for our display enumeration to race with mode changes and result in - // mismatched image pool and desktop texture sizes. If this happens, just reinit again. - if (desc.Width != width || desc.Height != height) { - BOOST_LOG(info) << "Capture size changed ["sv << width << 'x' << height << " -> "sv << desc.Width << 'x' << desc.Height << ']'; - return capture_e::reinit; - } - // It's also possible for the capture format to change on the fly. If that happens, - // reinitialize capture to try format detection again and create new images. - if (capture_format != desc.Format) { - BOOST_LOG(info) << "Capture format changed ["sv << dxgi_format_to_string(capture_format) << " -> "sv << dxgi_format_to_string(desc.Format) << ']'; - return capture_e::reinit; - } - - // Copy from GPU to CPU - device_ctx->CopyResource(texture.get(), src.get()); - - if (!pull_free_image_cb(img_out)) { - return capture_e::interrupted; - } - auto img = (img_t *) img_out.get(); - - // Map the staging texture for CPU access (making it inaccessible for the GPU) - if (FAILED(status = device_ctx->Map(texture.get(), 0, D3D11_MAP_READ, 0, &img_info))) { - BOOST_LOG(error) << "Failed to map texture [0x"sv << util::hex(status).to_string_view() << ']'; - - return capture_e::error; - } - - // Now that we know the capture format, we can finish creating the image - if (complete_img(img, false)) { - device_ctx->Unmap(texture.get(), 0); - img_info.pData = nullptr; - return capture_e::error; - } - - std::copy_n((std::uint8_t *) img_info.pData, height * img_info.RowPitch, (std::uint8_t *) img->data); - - // Unmap the staging texture to allow GPU access again - device_ctx->Unmap(texture.get(), 0); - img_info.pData = nullptr; - - if (img) { - img->frame_timestamp = frame_timestamp; - } - - return capture_e::ok; - } - - capture_e - display_wgc_ram_t::release_snapshot() { - return dup.release_frame(); - } -} // namespace platf::dxgi +/** + * @file src/platform/windows/display_wgc.cpp + * @brief Definitions for WinRT Windows.Graphics.Capture API + */ +// platform includes +#include + +// local includes +#include "display.h" +#include "misc.h" +#include "src/logging.h" + +// Gross hack to work around MINGW-packages#22160 +#define ____FIReference_1_boolean_INTERFACE_DEFINED__ + +#include +#include +#include +#include + +namespace platf { + using namespace std::literals; +} + +namespace winrt { + using namespace Windows::Foundation; + using namespace Windows::Foundation::Metadata; + using namespace Windows::Graphics::Capture; + using namespace Windows::Graphics::DirectX::Direct3D11; + + extern "C" { + HRESULT __stdcall CreateDirect3D11DeviceFromDXGIDevice(::IDXGIDevice *dxgiDevice, ::IInspectable **graphicsDevice); + } + + /** + * Windows structures sometimes have compile-time GUIDs. GCC supports this, but in a roundabout way. + * If WINRT_IMPL_HAS_DECLSPEC_UUID is true, then the compiler supports adding this attribute to a struct. For example, Visual Studio. + * If not, then MinGW GCC has a workaround to assign a GUID to a structure. + */ + struct +#if WINRT_IMPL_HAS_DECLSPEC_UUID + __declspec(uuid("A9B3D012-3DF2-4EE3-B8D1-8695F457D3C1")) +#endif + IDirect3DDxgiInterfaceAccess: ::IUnknown { + virtual HRESULT __stdcall GetInterface(REFIID id, void **object) = 0; + }; +} // namespace winrt +#if !WINRT_IMPL_HAS_DECLSPEC_UUID +static constexpr GUID GUID__IDirect3DDxgiInterfaceAccess = { + 0xA9B3D012, + 0x3DF2, + 0x4EE3, + {0xB8, 0xD1, 0x86, 0x95, 0xF4, 0x57, 0xD3, 0xC1} + // compare with __declspec(uuid(...)) for the struct above. +}; + +template<> +constexpr auto __mingw_uuidof() -> GUID const & { + return GUID__IDirect3DDxgiInterfaceAccess; +} +#endif + +namespace platf::dxgi { + wgc_capture_t::wgc_capture_t() { + InitializeConditionVariable(&frame_present_cv); + } + + wgc_capture_t::~wgc_capture_t() { + if (capture_session) { + capture_session.Close(); + } + if (frame_pool) { + frame_pool.Close(); + } + item = nullptr; + capture_session = nullptr; + frame_pool = nullptr; + } + + /** + * @brief Initialize the Windows.Graphics.Capture backend. + * @return 0 on success, -1 on failure. + */ + int wgc_capture_t::init(display_base_t *display, const ::video::config_t &config) { + HRESULT status; + dxgi::dxgi_t dxgi; + winrt::com_ptr<::IInspectable> d3d_comhandle; + try { + if (!winrt::GraphicsCaptureSession::IsSupported()) { + BOOST_LOG(error) << "Screen capture is not supported on this device for this release of Windows!"sv; + return -1; + } + if (FAILED(status = display->device->QueryInterface(IID_IDXGIDevice, (void **) &dxgi))) { + BOOST_LOG(error) << "Failed to query DXGI interface from device [0x"sv << util::hex(status).to_string_view() << ']'; + return -1; + } + if (FAILED(status = winrt::CreateDirect3D11DeviceFromDXGIDevice(*&dxgi, d3d_comhandle.put()))) { + BOOST_LOG(error) << "Failed to query WinRT DirectX interface from device [0x"sv << util::hex(status).to_string_view() << ']'; + return -1; + } + } catch (winrt::hresult_error &e) { + BOOST_LOG(error) << "Screen capture is not supported on this device for this release of Windows: failed to acquire device: [0x"sv << util::hex(e.code()).to_string_view() << ']'; + return -1; + } + + DXGI_OUTPUT_DESC output_desc; + uwp_device = d3d_comhandle.as(); + display->output->GetDesc(&output_desc); + + auto monitor_factory = winrt::get_activation_factory(); + if (monitor_factory == nullptr || + FAILED(status = monitor_factory->CreateForMonitor(output_desc.Monitor, winrt::guid_of(), winrt::put_abi(item)))) { + BOOST_LOG(error) << "Screen capture is not supported on this device for this release of Windows: failed to acquire display: [0x"sv << util::hex(status).to_string_view() << ']'; + return -1; + } + + if (config.dynamicRange) { + display->capture_format = DXGI_FORMAT_R16G16B16A16_FLOAT; + } else { + display->capture_format = DXGI_FORMAT_B8G8R8A8_UNORM; + } + + try { + frame_pool = winrt::Direct3D11CaptureFramePool::CreateFreeThreaded(uwp_device, static_cast(display->capture_format), 2, item.Size()); + capture_session = frame_pool.CreateCaptureSession(item); + frame_pool.FrameArrived({this, &wgc_capture_t::on_frame_arrived}); + } catch (winrt::hresult_error &e) { + BOOST_LOG(error) << "Screen capture is not supported on this device for this release of Windows: failed to create capture session: [0x"sv << util::hex(e.code()).to_string_view() << ']'; + return -1; + } + try { + if (winrt::ApiInformation::IsPropertyPresent(L"Windows.Graphics.Capture.GraphicsCaptureSession", L"IsBorderRequired")) { + capture_session.IsBorderRequired(false); + } else { + BOOST_LOG(warning) << "Can't disable colored border around capture area on this version of Windows"; + } + } catch (winrt::hresult_error &e) { + BOOST_LOG(warning) << "Screen capture may not be fully supported on this device for this release of Windows: failed to disable border around capture area: [0x"sv << util::hex(e.code()).to_string_view() << ']'; + } + try { + capture_session.StartCapture(); + } catch (winrt::hresult_error &e) { + BOOST_LOG(error) << "Screen capture is not supported on this device for this release of Windows: failed to start capture: [0x"sv << util::hex(e.code()).to_string_view() << ']'; + return -1; + } + return 0; + } + + /** + * This function runs in a separate thread spawned by the frame pool and is a producer of frames. + * To maintain parity with the original display interface, this frame will be consumed by the capture thread. + * Acquire a read-write lock, make the produced frame available to the capture thread, then wake the capture thread. + */ + void wgc_capture_t::on_frame_arrived(winrt::Direct3D11CaptureFramePool const &sender, winrt::IInspectable const &) { + winrt::Windows::Graphics::Capture::Direct3D11CaptureFrame frame {nullptr}; + try { + frame = sender.TryGetNextFrame(); + } catch (winrt::hresult_error &e) { + BOOST_LOG(warning) << "Failed to capture frame: "sv << e.code(); + return; + } + if (frame != nullptr) { + AcquireSRWLockExclusive(&frame_lock); + if (produced_frame) { + produced_frame.Close(); + } + + produced_frame = frame; + ReleaseSRWLockExclusive(&frame_lock); + WakeConditionVariable(&frame_present_cv); + } + } + + /** + * @brief Get the next frame from the producer thread. + * If not available, the capture thread blocks until one is, or the wait times out. + * @param timeout how long to wait for the next frame + * @param out a texture containing the frame just captured + * @param out_time the timestamp of the frame just captured + */ + capture_e wgc_capture_t::next_frame(std::chrono::milliseconds timeout, ID3D11Texture2D **out, uint64_t &out_time) { + // this CONSUMER runs in the capture thread + release_frame(); + + AcquireSRWLockExclusive(&frame_lock); + if (produced_frame == nullptr && SleepConditionVariableSRW(&frame_present_cv, &frame_lock, timeout.count(), 0) == 0) { + ReleaseSRWLockExclusive(&frame_lock); + if (GetLastError() == ERROR_TIMEOUT) { + return capture_e::timeout; + } else { + return capture_e::error; + } + } + if (produced_frame) { + consumed_frame = produced_frame; + produced_frame = nullptr; + } + ReleaseSRWLockExclusive(&frame_lock); + if (consumed_frame == nullptr) { // spurious wakeup + return capture_e::timeout; + } + + auto capture_access = consumed_frame.Surface().as(); + if (capture_access == nullptr) { + return capture_e::error; + } + capture_access->GetInterface(IID_ID3D11Texture2D, (void **) out); + out_time = consumed_frame.SystemRelativeTime().count(); // raw ticks from query performance counter + return capture_e::ok; + } + + capture_e wgc_capture_t::release_frame() { + if (consumed_frame != nullptr) { + consumed_frame.Close(); + consumed_frame = nullptr; + } + return capture_e::ok; + } + + int wgc_capture_t::set_cursor_visible(bool x) { + try { + if (capture_session.IsCursorCaptureEnabled() != x) { + capture_session.IsCursorCaptureEnabled(x); + } + return 0; + } catch (winrt::hresult_error &) { + return -1; + } + } + + int display_wgc_ram_t::init(const ::video::config_t &config, const std::string &display_name) { + if (display_base_t::init(config, display_name) || dup.init(this, config)) { + return -1; + } + + texture.reset(); + return 0; + } + + /** + * @brief Get the next frame from the Windows.Graphics.Capture API and copy it into a new snapshot texture. + * @param pull_free_image_cb call this to get a new free image from the video subsystem. + * @param img_out the captured frame is returned here + * @param timeout how long to wait for the next frame + * @param cursor_visible whether to capture the cursor + */ + capture_e display_wgc_ram_t::snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr &img_out, std::chrono::milliseconds timeout, bool cursor_visible) { + HRESULT status; + texture2d_t src; + uint64_t frame_qpc; + dup.set_cursor_visible(cursor_visible); + auto capture_status = dup.next_frame(timeout, &src, frame_qpc); + if (capture_status != capture_e::ok) { + return capture_status; + } + + auto frame_timestamp = std::chrono::steady_clock::now() - qpc_time_difference(qpc_counter(), frame_qpc); + D3D11_TEXTURE2D_DESC desc; + src->GetDesc(&desc); + + // Create the staging texture if it doesn't exist. It should match the source in size and format. + if (texture == nullptr) { + capture_format = desc.Format; + BOOST_LOG(info) << "Capture format ["sv << dxgi_format_to_string(capture_format) << ']'; + + D3D11_TEXTURE2D_DESC t {}; + t.Width = width; + t.Height = height; + t.MipLevels = 1; + t.ArraySize = 1; + t.SampleDesc.Count = 1; + t.Usage = D3D11_USAGE_STAGING; + t.Format = capture_format; + t.CPUAccessFlags = D3D11_CPU_ACCESS_READ; + + auto status = device->CreateTexture2D(&t, nullptr, &texture); + + if (FAILED(status)) { + BOOST_LOG(error) << "Failed to create staging texture [0x"sv << util::hex(status).to_string_view() << ']'; + return capture_e::error; + } + } + + // It's possible for our display enumeration to race with mode changes and result in + // mismatched image pool and desktop texture sizes. If this happens, just reinit again. + if (desc.Width != width || desc.Height != height) { + BOOST_LOG(info) << "Capture size changed ["sv << width << 'x' << height << " -> "sv << desc.Width << 'x' << desc.Height << ']'; + return capture_e::reinit; + } + // It's also possible for the capture format to change on the fly. If that happens, + // reinitialize capture to try format detection again and create new images. + if (capture_format != desc.Format) { + BOOST_LOG(info) << "Capture format changed ["sv << dxgi_format_to_string(capture_format) << " -> "sv << dxgi_format_to_string(desc.Format) << ']'; + return capture_e::reinit; + } + + // Copy from GPU to CPU + device_ctx->CopyResource(texture.get(), src.get()); + + if (!pull_free_image_cb(img_out)) { + return capture_e::interrupted; + } + auto img = (img_t *) img_out.get(); + + // Map the staging texture for CPU access (making it inaccessible for the GPU) + if (FAILED(status = device_ctx->Map(texture.get(), 0, D3D11_MAP_READ, 0, &img_info))) { + BOOST_LOG(error) << "Failed to map texture [0x"sv << util::hex(status).to_string_view() << ']'; + + return capture_e::error; + } + + // Now that we know the capture format, we can finish creating the image + if (complete_img(img, false)) { + device_ctx->Unmap(texture.get(), 0); + img_info.pData = nullptr; + return capture_e::error; + } + + std::copy_n((std::uint8_t *) img_info.pData, height * img_info.RowPitch, (std::uint8_t *) img->data); + + // Unmap the staging texture to allow GPU access again + device_ctx->Unmap(texture.get(), 0); + img_info.pData = nullptr; + + if (img) { + img->frame_timestamp = frame_timestamp; + } + + return capture_e::ok; + } + + capture_e display_wgc_ram_t::release_snapshot() { + return dup.release_frame(); + } +} // namespace platf::dxgi diff --git a/src/platform/windows/input.cpp b/src/platform/windows/input.cpp index ea055174..53591bd7 100644 --- a/src/platform/windows/input.cpp +++ b/src/platform/windows/input.cpp @@ -3,13 +3,18 @@ * @brief Definitions for input handling on Windows. */ #define WINVER 0x0A00 + +// platform includes #include +// standard includes #include #include +// lib includes #include +// local includes #include "keylayout.h" #include "misc.h" #include "src/config.h" @@ -19,12 +24,9 @@ #ifdef __MINGW32__ DECLARE_HANDLE(HSYNTHETICPOINTERDEVICE); -WINUSERAPI HSYNTHETICPOINTERDEVICE WINAPI -CreateSyntheticPointerDevice(POINTER_INPUT_TYPE pointerType, ULONG maxCount, POINTER_FEEDBACK_MODE mode); -WINUSERAPI BOOL WINAPI -InjectSyntheticPointerInput(HSYNTHETICPOINTERDEVICE device, CONST POINTER_TYPE_INFO *pointerInfo, UINT32 count); -WINUSERAPI VOID WINAPI -DestroySyntheticPointerDevice(HSYNTHETICPOINTERDEVICE device); +WINUSERAPI HSYNTHETICPOINTERDEVICE WINAPI CreateSyntheticPointerDevice(POINTER_INPUT_TYPE pointerType, ULONG maxCount, POINTER_FEEDBACK_MODE mode); +WINUSERAPI BOOL WINAPI InjectSyntheticPointerInput(HSYNTHETICPOINTERDEVICE device, CONST POINTER_TYPE_INFO *pointerInfo, UINT32 count); +WINUSERAPI VOID WINAPI DestroySyntheticPointerDevice(HSYNTHETICPOINTERDEVICE device); #endif namespace platf { @@ -33,28 +35,32 @@ namespace platf { thread_local HDESK _lastKnownInputDesktop = nullptr; constexpr touch_port_t target_touch_port { - 0, 0, - 65535, 65535 + 0, + 0, + 65535, + 65535 }; using client_t = util::safe_ptr<_VIGEM_CLIENT_T, vigem_free>; using target_t = util::safe_ptr<_VIGEM_TARGET_T, vigem_target_free>; - void CALLBACK - x360_notify( + void CALLBACK x360_notify( client_t::pointer client, target_t::pointer target, - std::uint8_t largeMotor, std::uint8_t smallMotor, + std::uint8_t largeMotor, + std::uint8_t smallMotor, std::uint8_t /* led_number */, - void *userdata); + void *userdata + ); - void CALLBACK - ds4_notify( + void CALLBACK ds4_notify( client_t::pointer client, target_t::pointer target, - std::uint8_t largeMotor, std::uint8_t smallMotor, + std::uint8_t largeMotor, + std::uint8_t smallMotor, DS4_LIGHTBAR_COLOR /* led_color */, - void *userdata); + void *userdata + ); struct gp_touch_context_t { uint8_t pointerIndex; @@ -94,14 +100,14 @@ namespace platf { constexpr DS4_TOUCH ds4_touch_unused = { .bPacketCounter = 0, .bIsUpTrackingNum1 = 0x80, - .bTouchData1 = { 0x00, 0x00, 0x00 }, + .bTouchData1 = {0x00, 0x00, 0x00}, .bIsUpTrackingNum2 = 0x80, - .bTouchData2 = { 0x00, 0x00, 0x00 }, + .bTouchData2 = {0x00, 0x00, 0x00}, }; // See https://github.com/ViGEm/ViGEmBus/blob/22835473d17fbf0c4d4bb2f2d42fd692b6e44df4/sys/Ds4Pdo.cpp#L153-L164 constexpr DS4_REPORT_EX ds4_report_init_ex = { - { { .bThumbLX = 0x80, + {{.bThumbLX = 0x80, .bThumbLY = 0x80, .bThumbRX = 0x80, .bThumbRY = 0x80, @@ -117,12 +123,12 @@ namespace platf { .wAccelX = 0, .wAccelY = 0, .wAccelZ = 0, - ._bUnknown1 = { 0x00, 0x00, 0x00, 0x00, 0x00 }, + ._bUnknown1 = {0x00, 0x00, 0x00, 0x00, 0x00}, .bBatteryLvlSpecial = 0x1A, // Wired - Full battery - ._bUnknown2 = { 0x00, 0x00 }, + ._bUnknown2 = {0x00, 0x00}, .bTouchPacketsN = 1, .sCurrentTouch = ds4_touch_unused, - .sPreviousTouch = { ds4_touch_unused, ds4_touch_unused } } } + .sPreviousTouch = {ds4_touch_unused, ds4_touch_unused}}} }; /** @@ -134,8 +140,7 @@ namespace platf { * @param y Y component of motion. * @param z Z component of motion. */ - static void - ds4_update_motion(gamepad_context_t &gamepad, uint8_t motion_type, float x, float y, float z) { + static void ds4_update_motion(gamepad_context_t &gamepad, uint8_t motion_type, float x, float y, float z) { auto &report = gamepad.report.ds4.Report; // Use int32 to process this data, so we can clamp if needed. @@ -192,17 +197,15 @@ namespace platf { class vigem_t { public: - int - init() { + int init() { // Probe ViGEm during startup to see if we can successfully attach gamepads. This will allow us to // immediately display the error message in the web UI even before the user tries to stream. - client_t client { vigem_alloc() }; + client_t client {vigem_alloc()}; VIGEM_ERROR status = vigem_connect(client.get()); if (!VIGEM_SUCCESS(status)) { // Log a special fatal message for this case to show the error in the web UI BOOST_LOG(fatal) << "ViGEmBus is not installed or running. You must install ViGEmBus for gamepad support!"sv; - } - else { + } else { vigem_disconnect(client.get()); } @@ -218,8 +221,7 @@ namespace platf { * @param gp_type The type of gamepad. * @return 0 on success. */ - int - alloc_gamepad_internal(const gamepad_id_t &id, feedback_queue_t &feedback_queue, VIGEM_TARGET_TYPE gp_type) { + int alloc_gamepad_internal(const gamepad_id_t &id, feedback_queue_t &feedback_queue, VIGEM_TARGET_TYPE gp_type) { auto &gamepad = gamepads[id.globalIndex]; assert(!gamepad.gp); @@ -242,8 +244,7 @@ namespace platf { if (gp_type == Xbox360Wired) { gamepad.gp.reset(vigem_target_x360_alloc()); XUSB_REPORT_INIT(&gamepad.report.x360); - } - else { + } else { gamepad.gp.reset(vigem_target_ds4_alloc()); // There is no equivalent DS4_REPORT_EX_INIT() @@ -272,8 +273,7 @@ namespace platf { if (gp_type == Xbox360Wired) { status = vigem_target_x360_register_notification(client.get(), gamepad.gp.get(), x360_notify, this); - } - else { + } else { status = vigem_target_ds4_register_notification(client.get(), gamepad.gp.get(), ds4_notify, this); } @@ -288,8 +288,7 @@ namespace platf { * @brief Detaches the specified gamepad * @param nr The gamepad. */ - void - free_target(int nr) { + void free_target(int nr) { auto &gamepad = gamepads[nr]; if (gamepad.repeat_task) { @@ -327,8 +326,7 @@ namespace platf { * @param largeMotor The large motor. * @param smallMotor The small motor. */ - void - rumble(target_t::pointer target, std::uint8_t largeMotor, std::uint8_t smallMotor) { + void rumble(target_t::pointer target, std::uint8_t largeMotor, std::uint8_t smallMotor) { for (int x = 0; x < gamepads.size(); ++x) { auto &gamepad = gamepads[x]; @@ -342,7 +340,10 @@ namespace platf { normalizedLargeMotor != gamepad.last_rumble.data.rumble.lowfreq) { // We have to use the client-relative index when communicating back to the client gamepad_feedback_msg_t msg = gamepad_feedback_msg_t::make_rumble( - gamepad.client_relative_index, normalizedLargeMotor, normalizedSmallMotor); + gamepad.client_relative_index, + normalizedLargeMotor, + normalizedSmallMotor + ); gamepad.feedback_queue->raise(msg); gamepad.last_rumble = msg; } @@ -358,8 +359,7 @@ namespace platf { * @param g The red channel. * @param b The red channel. */ - void - set_rgb_led(target_t::pointer target, std::uint8_t r, std::uint8_t g, std::uint8_t b) { + void set_rgb_led(target_t::pointer target, std::uint8_t r, std::uint8_t g, std::uint8_t b) { for (int x = 0; x < gamepads.size(); ++x) { auto &gamepad = gamepads[x]; @@ -401,13 +401,14 @@ namespace platf { client_t client; }; - void CALLBACK - x360_notify( + void CALLBACK x360_notify( client_t::pointer client, target_t::pointer target, - std::uint8_t largeMotor, std::uint8_t smallMotor, + std::uint8_t largeMotor, + std::uint8_t smallMotor, std::uint8_t /* led_number */, - void *userdata) { + void *userdata + ) { BOOST_LOG(debug) << "largeMotor: "sv << (int) largeMotor << std::endl << "smallMotor: "sv << (int) smallMotor; @@ -415,13 +416,14 @@ namespace platf { task_pool.push(&vigem_t::rumble, (vigem_t *) userdata, target, largeMotor, smallMotor); } - void CALLBACK - ds4_notify( + void CALLBACK ds4_notify( client_t::pointer client, target_t::pointer target, - std::uint8_t largeMotor, std::uint8_t smallMotor, + std::uint8_t largeMotor, + std::uint8_t smallMotor, DS4_LIGHTBAR_COLOR led_color, - void *userdata) { + void *userdata + ) { BOOST_LOG(debug) << "largeMotor: "sv << (int) largeMotor << std::endl << "smallMotor: "sv << (int) smallMotor << std::endl @@ -445,9 +447,8 @@ namespace platf { decltype(DestroySyntheticPointerDevice) *fnDestroySyntheticPointerDevice; }; - input_t - input() { - input_t result { new input_raw_t {} }; + input_t input() { + input_t result {new input_raw_t {}}; auto &raw = *(input_raw_t *) result.get(); raw.vigem = new vigem_t {}; @@ -468,8 +469,7 @@ namespace platf { * @brief Calls SendInput() and switches input desktops if required. * @param i The `INPUT` struct to send. */ - void - send_input(INPUT &i) { + void send_input(INPUT &i) { retry: auto send = SendInput(1, &i, sizeof(INPUT)); if (send != 1) { @@ -491,8 +491,7 @@ namespace platf { * @param count The number of elements in `pointerInfo`. * @return true if input was successfully injected. */ - bool - inject_synthetic_pointer_input(input_raw_t *input, HSYNTHETICPOINTERDEVICE device, const POINTER_TYPE_INFO *pointerInfo, UINT32 count) { + bool inject_synthetic_pointer_input(input_raw_t *input, HSYNTHETICPOINTERDEVICE device, const POINTER_TYPE_INFO *pointerInfo, UINT32 count) { retry: if (!input->fnInjectSyntheticPointerInput(device, pointerInfo, count)) { auto hDesk = syncThreadDesktop(); @@ -505,8 +504,7 @@ namespace platf { return true; } - void - abs_mouse(input_t &input, const touch_port_t &touch_port, float x, float y) { + void abs_mouse(input_t &input, const touch_port_t &touch_port, float x, float y) { INPUT i {}; i.type = INPUT_MOUSE; @@ -528,8 +526,7 @@ namespace platf { send_input(i); } - void - move_mouse(input_t &input, int deltaX, int deltaY) { + void move_mouse(input_t &input, int deltaX, int deltaY) { INPUT i {}; i.type = INPUT_MOUSE; @@ -542,13 +539,12 @@ namespace platf { send_input(i); } - util::point_t - get_mouse_loc(input_t &input) { + util::point_t get_mouse_loc(input_t &input) { throw std::runtime_error("not implemented yet, has to pass tests"); // TODO: Tests are failing, something wrong here? POINT p; if (!GetCursorPos(&p)) { - return util::point_t { 0.0, 0.0 }; + return util::point_t {0.0, 0.0}; } return util::point_t { @@ -557,8 +553,7 @@ namespace platf { }; } - void - button_mouse(input_t &input, int button, bool release) { + void button_mouse(input_t &input, int button, bool release) { INPUT i {}; i.type = INPUT_MOUSE; @@ -566,18 +561,14 @@ namespace platf { if (button == 1) { mi.dwFlags = release ? MOUSEEVENTF_LEFTUP : MOUSEEVENTF_LEFTDOWN; - } - else if (button == 2) { + } else if (button == 2) { mi.dwFlags = release ? MOUSEEVENTF_MIDDLEUP : MOUSEEVENTF_MIDDLEDOWN; - } - else if (button == 3) { + } else if (button == 3) { mi.dwFlags = release ? MOUSEEVENTF_RIGHTUP : MOUSEEVENTF_RIGHTDOWN; - } - else if (button == 4) { + } else if (button == 4) { mi.dwFlags = release ? MOUSEEVENTF_XUP : MOUSEEVENTF_XDOWN; mi.mouseData = XBUTTON1; - } - else { + } else { mi.dwFlags = release ? MOUSEEVENTF_XUP : MOUSEEVENTF_XDOWN; mi.mouseData = XBUTTON2; } @@ -585,8 +576,7 @@ namespace platf { send_input(i); } - void - scroll(input_t &input, int distance) { + void scroll(input_t &input, int distance) { INPUT i {}; i.type = INPUT_MOUSE; @@ -598,8 +588,7 @@ namespace platf { send_input(i); } - void - hscroll(input_t &input, int distance) { + void hscroll(input_t &input, int distance) { INPUT i {}; i.type = INPUT_MOUSE; @@ -611,8 +600,7 @@ namespace platf { send_input(i); } - void - keyboard_update(input_t &input, uint16_t modcode, bool release, uint8_t flags) { + void keyboard_update(input_t &input, uint16_t modcode, bool release, uint8_t flags) { INPUT i {}; i.type = INPUT_KEYBOARD; auto &ki = i.ki; @@ -623,8 +611,7 @@ namespace platf { if (!(flags & SS_KBE_FLAG_NON_NORMALIZED)) { // Mask off the extended key byte ki.wScan = VK_TO_SCANCODE_MAP[modcode & 0xFF]; - } - else if (config::input.always_send_scancodes && modcode != VK_LWIN && modcode != VK_RWIN && modcode != VK_PAUSE) { + } else if (config::input.always_send_scancodes && modcode != VK_LWIN && modcode != VK_RWIN && modcode != VK_PAUSE) { // For some reason, MapVirtualKey(VK_LWIN, MAPVK_VK_TO_VSC) doesn't seem to work :/ ki.wScan = MapVirtualKey(modcode, MAPVK_VK_TO_VSC); } @@ -632,8 +619,7 @@ namespace platf { // If we can map this to a scancode, send it as a scancode for maximum game compatibility. if (ki.wScan) { ki.dwFlags = KEYEVENTF_SCANCODE; - } - else { + } else { // If there is no scancode mapping or it's non-normalized, send it as a regular VK event. ki.wVk = modcode; } @@ -712,8 +698,7 @@ namespace platf { * @param input The global input context. * @return A unique pointer to a per-client input data context. */ - std::unique_ptr - allocate_client_input_context(input_t &input) { + std::unique_ptr allocate_client_input_context(input_t &input) { return std::make_unique(input); } @@ -722,8 +707,7 @@ namespace platf { * @details Since this swaps entries around, all slot pointers/references are invalid after compaction. * @param raw The client-specific input context. */ - void - perform_touch_compaction(client_input_raw_t *raw) { + void perform_touch_compaction(client_input_raw_t *raw) { // Windows requires all active touches be contiguous when fed into InjectSyntheticPointerInput(). UINT32 i; for (i = 0; i < ARRAYSIZE(raw->touchInfo); i++) { @@ -754,8 +738,7 @@ namespace platf { * @param eventType The LI_TOUCH_EVENT value from the client. * @return A pointer to the slot entry. */ - POINTER_TYPE_INFO * - pointer_by_id(client_input_raw_t *raw, uint32_t pointerId, uint8_t eventType) { + POINTER_TYPE_INFO *pointer_by_id(client_input_raw_t *raw, uint32_t pointerId, uint8_t eventType) { // Compact active touches into a single contiguous block perform_touch_compaction(raw); @@ -795,8 +778,7 @@ namespace platf { * @param x The normalized 0.0-1.0 X coordinate. * @param y The normalized 0.0-1.0 Y coordinate. */ - void - populate_common_pointer_info(POINTER_INFO &pointerInfo, const touch_port_t &touchPort, uint8_t eventType, float x, float y) { + void populate_common_pointer_info(POINTER_INFO &pointerInfo, const touch_port_t &touchPort, uint8_t eventType, float x, float y) { switch (eventType) { case LI_TOUCH_EVENT_HOVER: pointerInfo.pointerFlags &= ~POINTER_FLAG_INCONTACT; @@ -825,8 +807,7 @@ namespace platf { // we'll set POINTER_FLAG_UP, otherwise set POINTER_FLAG_UPDATE. if (pointerInfo.pointerFlags & POINTER_FLAG_INCONTACT) { pointerInfo.pointerFlags |= POINTER_FLAG_UP; - } - else { + } else { pointerInfo.pointerFlags |= POINTER_FLAG_UPDATE; } pointerInfo.pointerFlags &= ~(POINTER_FLAG_INCONTACT | POINTER_FLAG_INRANGE); @@ -857,8 +838,7 @@ namespace platf { * @brief Repeats the current touch state to avoid the interactions timing out. * @param raw The raw client-specific input context. */ - void - repeat_touch(client_input_raw_t *raw) { + void repeat_touch(client_input_raw_t *raw) { if (!inject_synthetic_pointer_input(raw->global, raw->touch, raw->touchInfo, raw->activeTouchSlots)) { auto err = GetLastError(); BOOST_LOG(warning) << "Failed to refresh virtual touch input: "sv << err; @@ -871,8 +851,7 @@ namespace platf { * @brief Repeats the current pen state to avoid the interactions timing out. * @param raw The raw client-specific input context. */ - void - repeat_pen(client_input_raw_t *raw) { + void repeat_pen(client_input_raw_t *raw) { if (!inject_synthetic_pointer_input(raw->global, raw->pen, &raw->penInfo, 1)) { auto err = GetLastError(); BOOST_LOG(warning) << "Failed to refresh virtual pen input: "sv << err; @@ -885,8 +864,7 @@ namespace platf { * @brief Cancels all active touches. * @param raw The raw client-specific input context. */ - void - cancel_all_active_touches(client_input_raw_t *raw) { + void cancel_all_active_touches(client_input_raw_t *raw) { // Cancel touch repeat callbacks if (raw->touchRepeatTask) { task_pool.cancel(raw->touchRepeatTask); @@ -922,8 +900,7 @@ namespace platf { * @param touch_port The current viewport for translating to screen coordinates. * @param touch The touch event. */ - void - touch_update(client_input_t *input, const touch_port_t &touch_port, const touch_input_t &touch) { + void touch_update(client_input_t *input, const touch_port_t &touch_port, const touch_input_t &touch) { auto raw = (client_input_raw_t *) input; // Bail if we're not running on an OS that supports virtual touch input @@ -944,8 +921,7 @@ namespace platf { BOOST_LOG(warning) << "Failed to create virtual touch device: "sv << err; return; } - } - else { + } else { // No need to cancel anything if we had no touch input device return; } @@ -991,8 +967,7 @@ namespace platf { // Convert the 0.0f..1.0f float to the 0..1024 range that Windows uses touchInfo.pressure = (UINT32) (touch.pressureOrDistance * 1024); - } - else { + } else { // The default touch pressure is 512 touchInfo.pressure = 512; } @@ -1018,8 +993,7 @@ namespace platf { touchInfo.touchMask |= TOUCH_MASK_CONTACTAREA; } - } - else { + } else { touchInfo.pressure = 0; touchInfo.rcContact = {}; } @@ -1027,8 +1001,7 @@ namespace platf { if (touch.rotation != LI_ROT_UNKNOWN) { touchInfo.touchMask |= TOUCH_MASK_ORIENTATION; touchInfo.orientation = touch.rotation; - } - else { + } else { touchInfo.orientation = 0; } @@ -1053,8 +1026,7 @@ namespace platf { * @param touch_port The current viewport for translating to screen coordinates. * @param pen The pen event. */ - void - pen_update(client_input_t *input, const touch_port_t &touch_port, const pen_input_t &pen) { + void pen_update(client_input_t *input, const touch_port_t &touch_port, const pen_input_t &pen) { auto raw = (client_input_raw_t *) input; // Bail if we're not running on an OS that supports virtual pen input @@ -1075,8 +1047,7 @@ namespace platf { BOOST_LOG(warning) << "Failed to create virtual pen device: "sv << err; return; } - } - else { + } else { // No need to cancel anything if we had no pen input device return; } @@ -1100,8 +1071,7 @@ namespace platf { // Windows only supports a single pen button, so send all buttons as the barrel button if (pen.penButtons) { penInfo.penFlags |= PEN_FLAG_BARREL; - } - else { + } else { penInfo.penFlags &= ~PEN_FLAG_BARREL; } @@ -1126,8 +1096,7 @@ namespace platf { // Convert the 0.0f..1.0f float to the 0..1024 range that Windows uses penInfo.pressure = (UINT32) (pen.pressureOrDistance * 1024); - } - else { + } else { // The default pen pressure is 0 penInfo.pressure = 0; } @@ -1135,8 +1104,7 @@ namespace platf { if (pen.rotation != LI_ROT_UNKNOWN) { penInfo.penMask |= PEN_MASK_ROTATION; penInfo.rotation = pen.rotation; - } - else { + } else { penInfo.rotation = 0; } @@ -1151,8 +1119,7 @@ namespace platf { penInfo.penMask |= PEN_MASK_TILT_X | PEN_MASK_TILT_Y; penInfo.tiltX = (INT32) (std::atan2(std::sin(-rotationRads) * r, z) * 180.f / M_PI); penInfo.tiltY = (INT32) (std::atan2(std::cos(-rotationRads) * r, z) * 180.f / M_PI); - } - else { + } else { penInfo.tiltX = 0; penInfo.tiltY = 0; } @@ -1172,8 +1139,7 @@ namespace platf { } } - void - unicode(input_t &input, char *utf8, int size) { + void unicode(input_t &input, char *utf8, int size) { // We can do no worse than one UTF-16 character per byte of UTF-8 WCHAR wide[size]; @@ -1201,8 +1167,7 @@ namespace platf { } } - int - alloc_gamepad(input_t &input, const gamepad_id_t &id, const gamepad_arrival_t &metadata, feedback_queue_t feedback_queue) { + int alloc_gamepad(input_t &input, const gamepad_id_t &id, const gamepad_arrival_t &metadata, feedback_queue_t feedback_queue) { auto raw = (input_raw_t *) input.get(); if (!raw->vigem) { @@ -1214,28 +1179,22 @@ namespace platf { if (config::input.gamepad == "x360"sv) { BOOST_LOG(info) << "Gamepad " << id.globalIndex << " will be Xbox 360 controller (manual selection)"sv; selectedGamepadType = Xbox360Wired; - } - else if (config::input.gamepad == "ds4"sv) { + } else if (config::input.gamepad == "ds4"sv) { BOOST_LOG(info) << "Gamepad " << id.globalIndex << " will be DualShock 4 controller (manual selection)"sv; selectedGamepadType = DualShock4Wired; - } - else if (metadata.type == LI_CTYPE_PS) { + } else if (metadata.type == LI_CTYPE_PS) { BOOST_LOG(info) << "Gamepad " << id.globalIndex << " will be DualShock 4 controller (auto-selected by client-reported type)"sv; selectedGamepadType = DualShock4Wired; - } - else if (metadata.type == LI_CTYPE_XBOX) { + } else if (metadata.type == LI_CTYPE_XBOX) { BOOST_LOG(info) << "Gamepad " << id.globalIndex << " will be Xbox 360 controller (auto-selected by client-reported type)"sv; selectedGamepadType = Xbox360Wired; - } - else if (config::input.motion_as_ds4 && (metadata.capabilities & (LI_CCAP_ACCEL | LI_CCAP_GYRO))) { + } else if (config::input.motion_as_ds4 && (metadata.capabilities & (LI_CCAP_ACCEL | LI_CCAP_GYRO))) { BOOST_LOG(info) << "Gamepad " << id.globalIndex << " will be DualShock 4 controller (auto-selected by motion sensor presence)"sv; selectedGamepadType = DualShock4Wired; - } - else if (config::input.touchpad_as_ds4 && (metadata.capabilities & LI_CCAP_TOUCHPAD)) { + } else if (config::input.touchpad_as_ds4 && (metadata.capabilities & LI_CCAP_TOUCHPAD)) { BOOST_LOG(info) << "Gamepad " << id.globalIndex << " will be DualShock 4 controller (auto-selected by touchpad presence)"sv; selectedGamepadType = DualShock4Wired; - } - else { + } else { BOOST_LOG(info) << "Gamepad " << id.globalIndex << " will be Xbox 360 controller (default)"sv; selectedGamepadType = Xbox360Wired; } @@ -1250,8 +1209,7 @@ namespace platf { if (metadata.capabilities & LI_CCAP_RGB_LED) { BOOST_LOG(warning) << "Gamepad " << id.globalIndex << " has an RGB LED, but it is not usable when emulating an Xbox 360 controller"sv; } - } - else if (selectedGamepadType == DualShock4Wired) { + } else if (selectedGamepadType == DualShock4Wired) { if (!(metadata.capabilities & (LI_CCAP_ACCEL | LI_CCAP_GYRO))) { BOOST_LOG(warning) << "Gamepad " << id.globalIndex << " is emulating a DualShock 4 controller, but the client gamepad doesn't have motion sensors active"sv; } @@ -1263,8 +1221,7 @@ namespace platf { return raw->vigem->alloc_gamepad_internal(id, feedback_queue, selectedGamepadType); } - void - free_gamepad(input_t &input, int nr) { + void free_gamepad(input_t &input, int nr) { auto raw = (input_raw_t *) input.get(); if (!raw->vigem) { @@ -1279,26 +1236,55 @@ namespace platf { * @param gamepad_state The gamepad button/axis state sent from the client. * @return XUSB_BUTTON flags. */ - static XUSB_BUTTON - x360_buttons(const gamepad_state_t &gamepad_state) { + static XUSB_BUTTON x360_buttons(const gamepad_state_t &gamepad_state) { int buttons {}; auto flags = gamepad_state.buttonFlags; - if (flags & DPAD_UP) buttons |= XUSB_GAMEPAD_DPAD_UP; - if (flags & DPAD_DOWN) buttons |= XUSB_GAMEPAD_DPAD_DOWN; - if (flags & DPAD_LEFT) buttons |= XUSB_GAMEPAD_DPAD_LEFT; - if (flags & DPAD_RIGHT) buttons |= XUSB_GAMEPAD_DPAD_RIGHT; - if (flags & START) buttons |= XUSB_GAMEPAD_START; - if (flags & BACK) buttons |= XUSB_GAMEPAD_BACK; - if (flags & LEFT_STICK) buttons |= XUSB_GAMEPAD_LEFT_THUMB; - if (flags & RIGHT_STICK) buttons |= XUSB_GAMEPAD_RIGHT_THUMB; - if (flags & LEFT_BUTTON) buttons |= XUSB_GAMEPAD_LEFT_SHOULDER; - if (flags & RIGHT_BUTTON) buttons |= XUSB_GAMEPAD_RIGHT_SHOULDER; - if (flags & (HOME | MISC_BUTTON)) buttons |= XUSB_GAMEPAD_GUIDE; - if (flags & A) buttons |= XUSB_GAMEPAD_A; - if (flags & B) buttons |= XUSB_GAMEPAD_B; - if (flags & X) buttons |= XUSB_GAMEPAD_X; - if (flags & Y) buttons |= XUSB_GAMEPAD_Y; + if (flags & DPAD_UP) { + buttons |= XUSB_GAMEPAD_DPAD_UP; + } + if (flags & DPAD_DOWN) { + buttons |= XUSB_GAMEPAD_DPAD_DOWN; + } + if (flags & DPAD_LEFT) { + buttons |= XUSB_GAMEPAD_DPAD_LEFT; + } + if (flags & DPAD_RIGHT) { + buttons |= XUSB_GAMEPAD_DPAD_RIGHT; + } + if (flags & START) { + buttons |= XUSB_GAMEPAD_START; + } + if (flags & BACK) { + buttons |= XUSB_GAMEPAD_BACK; + } + if (flags & LEFT_STICK) { + buttons |= XUSB_GAMEPAD_LEFT_THUMB; + } + if (flags & RIGHT_STICK) { + buttons |= XUSB_GAMEPAD_RIGHT_THUMB; + } + if (flags & LEFT_BUTTON) { + buttons |= XUSB_GAMEPAD_LEFT_SHOULDER; + } + if (flags & RIGHT_BUTTON) { + buttons |= XUSB_GAMEPAD_RIGHT_SHOULDER; + } + if (flags & (HOME | MISC_BUTTON)) { + buttons |= XUSB_GAMEPAD_GUIDE; + } + if (flags & A) { + buttons |= XUSB_GAMEPAD_A; + } + if (flags & B) { + buttons |= XUSB_GAMEPAD_B; + } + if (flags & X) { + buttons |= XUSB_GAMEPAD_X; + } + if (flags & Y) { + buttons |= XUSB_GAMEPAD_Y; + } return (XUSB_BUTTON) buttons; } @@ -1308,8 +1294,7 @@ namespace platf { * @param gamepad The gamepad to update. * @param gamepad_state The gamepad button/axis state sent from the client. */ - static void - x360_update_state(gamepad_context_t &gamepad, const gamepad_state_t &gamepad_state) { + static void x360_update_state(gamepad_context_t &gamepad, const gamepad_state_t &gamepad_state) { auto &report = gamepad.report.x360; report.wButtons = x360_buttons(gamepad_state); @@ -1321,17 +1306,14 @@ namespace platf { report.sThumbRY = gamepad_state.rsY; } - static DS4_DPAD_DIRECTIONS - ds4_dpad(const gamepad_state_t &gamepad_state) { + static DS4_DPAD_DIRECTIONS ds4_dpad(const gamepad_state_t &gamepad_state) { auto flags = gamepad_state.buttonFlags; if (flags & DPAD_UP) { if (flags & DPAD_RIGHT) { return DS4_BUTTON_DPAD_NORTHEAST; - } - else if (flags & DPAD_LEFT) { + } else if (flags & DPAD_LEFT) { return DS4_BUTTON_DPAD_NORTHWEST; - } - else { + } else { return DS4_BUTTON_DPAD_NORTH; } } @@ -1339,11 +1321,9 @@ namespace platf { else if (flags & DPAD_DOWN) { if (flags & DPAD_RIGHT) { return DS4_BUTTON_DPAD_SOUTHEAST; - } - else if (flags & DPAD_LEFT) { + } else if (flags & DPAD_LEFT) { return DS4_BUTTON_DPAD_SOUTHWEST; - } - else { + } else { return DS4_BUTTON_DPAD_SOUTH; } } @@ -1364,50 +1344,76 @@ namespace platf { * @param gamepad_state The gamepad button/axis state sent from the client. * @return DS4_BUTTONS flags. */ - static DS4_BUTTONS - ds4_buttons(const gamepad_state_t &gamepad_state) { + static DS4_BUTTONS ds4_buttons(const gamepad_state_t &gamepad_state) { int buttons {}; auto flags = gamepad_state.buttonFlags; - if (flags & LEFT_STICK) buttons |= DS4_BUTTON_THUMB_LEFT; - if (flags & RIGHT_STICK) buttons |= DS4_BUTTON_THUMB_RIGHT; - if (flags & LEFT_BUTTON) buttons |= DS4_BUTTON_SHOULDER_LEFT; - if (flags & RIGHT_BUTTON) buttons |= DS4_BUTTON_SHOULDER_RIGHT; - if (flags & START) buttons |= DS4_BUTTON_OPTIONS; - if (flags & BACK) buttons |= DS4_BUTTON_SHARE; - if (flags & A) buttons |= DS4_BUTTON_CROSS; - if (flags & B) buttons |= DS4_BUTTON_CIRCLE; - if (flags & X) buttons |= DS4_BUTTON_SQUARE; - if (flags & Y) buttons |= DS4_BUTTON_TRIANGLE; + if (flags & LEFT_STICK) { + buttons |= DS4_BUTTON_THUMB_LEFT; + } + if (flags & RIGHT_STICK) { + buttons |= DS4_BUTTON_THUMB_RIGHT; + } + if (flags & LEFT_BUTTON) { + buttons |= DS4_BUTTON_SHOULDER_LEFT; + } + if (flags & RIGHT_BUTTON) { + buttons |= DS4_BUTTON_SHOULDER_RIGHT; + } + if (flags & START) { + buttons |= DS4_BUTTON_OPTIONS; + } + if (flags & BACK) { + buttons |= DS4_BUTTON_SHARE; + } + if (flags & A) { + buttons |= DS4_BUTTON_CROSS; + } + if (flags & B) { + buttons |= DS4_BUTTON_CIRCLE; + } + if (flags & X) { + buttons |= DS4_BUTTON_SQUARE; + } + if (flags & Y) { + buttons |= DS4_BUTTON_TRIANGLE; + } - if (gamepad_state.lt > 0) buttons |= DS4_BUTTON_TRIGGER_LEFT; - if (gamepad_state.rt > 0) buttons |= DS4_BUTTON_TRIGGER_RIGHT; + if (gamepad_state.lt > 0) { + buttons |= DS4_BUTTON_TRIGGER_LEFT; + } + if (gamepad_state.rt > 0) { + buttons |= DS4_BUTTON_TRIGGER_RIGHT; + } return (DS4_BUTTONS) buttons; } - static DS4_SPECIAL_BUTTONS - ds4_special_buttons(const gamepad_state_t &gamepad_state) { + static DS4_SPECIAL_BUTTONS ds4_special_buttons(const gamepad_state_t &gamepad_state) { int buttons {}; - if (gamepad_state.buttonFlags & HOME) buttons |= DS4_SPECIAL_BUTTON_PS; + if (gamepad_state.buttonFlags & HOME) { + buttons |= DS4_SPECIAL_BUTTON_PS; + } // Allow either PS4/PS5 clickpad button or Xbox Series X share button to activate DS4 clickpad - if (gamepad_state.buttonFlags & (TOUCHPAD_BUTTON | MISC_BUTTON)) buttons |= DS4_SPECIAL_BUTTON_TOUCHPAD; + if (gamepad_state.buttonFlags & (TOUCHPAD_BUTTON | MISC_BUTTON)) { + buttons |= DS4_SPECIAL_BUTTON_TOUCHPAD; + } // Manual DS4 emulation: check if BACK button should also trigger DS4 touchpad click - if (config::input.gamepad == "ds4"sv && config::input.ds4_back_as_touchpad_click && (gamepad_state.buttonFlags & BACK)) buttons |= DS4_SPECIAL_BUTTON_TOUCHPAD; + if (config::input.gamepad == "ds4"sv && config::input.ds4_back_as_touchpad_click && (gamepad_state.buttonFlags & BACK)) { + buttons |= DS4_SPECIAL_BUTTON_TOUCHPAD; + } return (DS4_SPECIAL_BUTTONS) buttons; } - static std::uint8_t - to_ds4_triggerX(std::int16_t v) { + static std::uint8_t to_ds4_triggerX(std::int16_t v) { return (v + std::numeric_limits::max() / 2 + 1) / 257; } - static std::uint8_t - to_ds4_triggerY(std::int16_t v) { + static std::uint8_t to_ds4_triggerY(std::int16_t v) { auto new_v = -((std::numeric_limits::max() / 2 + v - 1)) / 257; return new_v == 0 ? 0xFF : (std::uint8_t) new_v; @@ -1418,8 +1424,7 @@ namespace platf { * @param gamepad The gamepad to update. * @param gamepad_state The gamepad button/axis state sent from the client. */ - static void - ds4_update_state(gamepad_context_t &gamepad, const gamepad_state_t &gamepad_state) { + static void ds4_update_state(gamepad_context_t &gamepad, const gamepad_state_t &gamepad_state) { auto &report = gamepad.report.ds4.Report; report.wButtons = static_cast(ds4_buttons(gamepad_state)) | static_cast(ds4_dpad(gamepad_state)); @@ -1441,8 +1446,7 @@ namespace platf { * @param vigem The global ViGEm context object. * @param nr The global gamepad index. */ - void - ds4_update_ts_and_send(vigem_t *vigem, int nr) { + void ds4_update_ts_and_send(vigem_t *vigem, int nr) { auto &gamepad = vigem->gamepads[nr]; // Cancel any pending updates. We will requeue one here when we're finished. @@ -1477,8 +1481,7 @@ namespace platf { * @param nr The gamepad index to update. * @param gamepad_state The gamepad button/axis state sent from the client. */ - void - gamepad_update(input_t &input, int nr, const gamepad_state_t &gamepad_state) { + void gamepad_update(input_t &input, int nr, const gamepad_state_t &gamepad_state) { auto vigem = ((input_raw_t *) input.get())->vigem; // If there is no gamepad support @@ -1499,8 +1502,7 @@ namespace platf { if (!VIGEM_SUCCESS(status)) { BOOST_LOG(warning) << "Couldn't send gamepad input to ViGEm ["sv << util::hex(status).to_string_view() << ']'; } - } - else { + } else { ds4_update_state(gamepad, gamepad_state); ds4_update_ts_and_send(vigem, nr); } @@ -1511,8 +1513,7 @@ namespace platf { * @param input The global input context. * @param touch The touch event. */ - void - gamepad_touch(input_t &input, const gamepad_touch_t &touch) { + void gamepad_touch(input_t &input, const gamepad_touch_t &touch) { auto vigem = ((input_raw_t *) input.get())->vigem; // If there is no gamepad support @@ -1542,8 +1543,7 @@ namespace platf { // Set pointer 0 down report.sCurrentTouch.bIsUpTrackingNum1 &= ~0x80; report.sCurrentTouch.bIsUpTrackingNum1++; - } - else if (gamepad.available_pointers & 0x2) { + } else if (gamepad.available_pointers & 0x2) { // Reserve pointer index 1 for this touch gamepad.pointer_id_map[touch.pointerId] = pointerIndex = 1; gamepad.available_pointers &= ~(1 << pointerIndex); @@ -1551,13 +1551,11 @@ namespace platf { // Set pointer 1 down report.sCurrentTouch.bIsUpTrackingNum2 &= ~0x80; report.sCurrentTouch.bIsUpTrackingNum2++; - } - else { + } else { BOOST_LOG(warning) << "No more free pointer indices! Did the client miss an touch up event?"sv; return; } - } - else if (touch.eventType == LI_TOUCH_EVENT_CANCEL_ALL) { + } else if (touch.eventType == LI_TOUCH_EVENT_CANCEL_ALL) { // Raise both pointers report.sCurrentTouch.bIsUpTrackingNum1 |= 0x80; report.sCurrentTouch.bIsUpTrackingNum2 |= 0x80; @@ -1567,8 +1565,7 @@ namespace platf { // All pointers are now available gamepad.available_pointers = 0x3; - } - else { + } else { auto i = gamepad.pointer_id_map.find(touch.pointerId); if (i == gamepad.pointer_id_map.end()) { BOOST_LOG(warning) << "Pointer ID not found! Did the client miss a touch down event?"sv; @@ -1584,15 +1581,13 @@ namespace platf { // Set pointer up if (pointerIndex == 0) { report.sCurrentTouch.bIsUpTrackingNum1 |= 0x80; - } - else { + } else { report.sCurrentTouch.bIsUpTrackingNum2 |= 0x80; } // Free the pointer index gamepad.available_pointers |= (1 << pointerIndex); - } - else if (touch.eventType != LI_TOUCH_EVENT_MOVE) { + } else if (touch.eventType != LI_TOUCH_EVENT_MOVE) { BOOST_LOG(warning) << "Unsupported touch event for gamepad: "sv << (uint32_t) touch.eventType; return; } @@ -1611,8 +1606,7 @@ namespace platf { if (touch.eventType != LI_TOUCH_EVENT_CANCEL_ALL) { if (pointerIndex == 0) { memcpy(report.sCurrentTouch.bTouchData1, touchData, sizeof(touchData)); - } - else { + } else { memcpy(report.sCurrentTouch.bTouchData2, touchData, sizeof(touchData)); } } @@ -1625,8 +1619,7 @@ namespace platf { * @param input The global input context. * @param motion The motion event. */ - void - gamepad_motion(input_t &input, const gamepad_motion_t &motion) { + void gamepad_motion(input_t &input, const gamepad_motion_t &motion) { auto vigem = ((input_raw_t *) input.get())->vigem; // If there is no gamepad support @@ -1653,8 +1646,7 @@ namespace platf { * @param input The global input context. * @param battery The battery event. */ - void - gamepad_battery(input_t &input, const gamepad_battery_t &battery) { + void gamepad_battery(input_t &input, const gamepad_battery_t &battery) { auto vigem = ((input_raw_t *) input.get())->vigem; // If there is no gamepad support @@ -1683,8 +1675,7 @@ namespace platf { case LI_BATTERY_STATE_DISCHARGING: if (battery.state == LI_BATTERY_STATE_CHARGING) { report.bBatteryLvlSpecial |= 0x10; // Connected via USB - } - else { + } else { report.bBatteryLvlSpecial &= ~0x10; // Not connected via USB } @@ -1723,20 +1714,18 @@ namespace platf { ds4_update_ts_and_send(vigem, battery.id.globalIndex); } - void - freeInput(void *p) { + void freeInput(void *p) { auto input = (input_raw_t *) p; delete input; } - std::vector & - supported_gamepads(input_t *input) { + std::vector &supported_gamepads(input_t *input) { if (!input) { static std::vector gps { - supported_gamepad_t { "auto", true, "" }, - supported_gamepad_t { "x360", false, "" }, - supported_gamepad_t { "ds4", false, "" }, + supported_gamepad_t {"auto", true, ""}, + supported_gamepad_t {"x360", false, ""}, + supported_gamepad_t {"ds4", false, ""}, }; return gps; @@ -1748,9 +1737,9 @@ namespace platf { // ds4 == ps4 static std::vector gps { - supported_gamepad_t { "auto", true, reason }, - supported_gamepad_t { "x360", enabled, reason }, - supported_gamepad_t { "ds4", enabled, reason } + supported_gamepad_t {"auto", true, reason}, + supported_gamepad_t {"x360", enabled, reason}, + supported_gamepad_t {"ds4", enabled, reason} }; for (auto &[name, is_enabled, reason_disabled] : gps) { @@ -1766,8 +1755,7 @@ namespace platf { * @brief Returns the supported platform capabilities to advertise to the client. * @return Capability flags. */ - platform_caps::caps_t - get_capabilities() { + platform_caps::caps_t get_capabilities() { platform_caps::caps_t caps = 0; // We support controller touchpad input as long as we're not emulating X360 @@ -1780,8 +1768,7 @@ namespace platf { if (config::input.native_pen_touch) { caps |= platform_caps::pen_touch; } - } - else { + } else { BOOST_LOG(warning) << "Touch input requires Windows 10 1809 or later"sv; } diff --git a/src/platform/windows/keylayout.h b/src/platform/windows/keylayout.h index 55dfa284..2d362ef2 100644 --- a/src/platform/windows/keylayout.h +++ b/src/platform/windows/keylayout.h @@ -4,6 +4,7 @@ */ #pragma once +// standard includes #include #include diff --git a/src/platform/windows/misc.cpp b/src/platform/windows/misc.cpp index 419ae749..c931b0ad 100644 --- a/src/platform/windows/misc.cpp +++ b/src/platform/windows/misc.cpp @@ -2,12 +2,15 @@ * @file src/platform/windows/misc.cpp * @brief Miscellaneous definitions for Windows. */ +// standard includes #include #include #include +#include #include #include +// lib includes #include #include #include @@ -34,16 +37,14 @@ #define NTDDI_VERSION NTDDI_WIN10 #include +// local includes #include "misc.h" - +#include "nvprefs/nvprefs_interface.h" #include "src/entry_handler.h" #include "src/globals.h" #include "src/logging.h" #include "src/platform/common.h" #include "src/utility.h" -#include - -#include "nvprefs/nvprefs_interface.h" // UDP_SEND_MSG_SIZE was added in the Windows 10 20H1 SDK #ifndef UDP_SEND_MSG_SIZE @@ -63,16 +64,14 @@ #include extern "C" { -NTSTATUS NTAPI -NtSetTimerResolution(ULONG DesiredResolution, BOOLEAN SetResolution, PULONG CurrentResolution); + NTSTATUS NTAPI NtSetTimerResolution(ULONG DesiredResolution, BOOLEAN SetResolution, PULONG CurrentResolution); } namespace { std::atomic used_nt_set_timer_resolution = false; - bool - nt_set_timer_resolution_max() { + bool nt_set_timer_resolution_max() { ULONG minimum, maximum, current; if (!NT_SUCCESS(NtQueryTimerResolution(&minimum, &maximum, ¤t)) || !NT_SUCCESS(NtSetTimerResolution(maximum, TRUE, ¤t))) { @@ -81,8 +80,7 @@ namespace { return true; } - bool - nt_set_timer_resolution_min() { + bool nt_set_timer_resolution_min() { ULONG minimum, maximum, current; if (!NT_SUCCESS(NtQueryTimerResolution(&minimum, &maximum, ¤t)) || !NT_SUCCESS(NtSetTimerResolution(minimum, TRUE, ¤t))) { @@ -96,6 +94,7 @@ namespace { namespace bp = boost::process; using namespace std::literals; + namespace platf { using adapteraddrs_t = util::c_ptr; @@ -116,30 +115,26 @@ namespace platf { decltype(WlanEnumInterfaces) *fn_WlanEnumInterfaces = nullptr; decltype(WlanSetInterface) *fn_WlanSetInterface = nullptr; - std::filesystem::path - appdata() { + std::filesystem::path appdata() { WCHAR sunshine_path[MAX_PATH]; GetModuleFileNameW(NULL, sunshine_path, _countof(sunshine_path)); - return std::filesystem::path { sunshine_path }.remove_filename() / L"config"sv; + return std::filesystem::path {sunshine_path}.remove_filename() / L"config"sv; } - std::string - from_sockaddr(const sockaddr *const socket_address) { + std::string from_sockaddr(const sockaddr *const socket_address) { char data[INET6_ADDRSTRLEN] = {}; auto family = socket_address->sa_family; if (family == AF_INET6) { inet_ntop(AF_INET6, &((sockaddr_in6 *) socket_address)->sin6_addr, data, INET6_ADDRSTRLEN); - } - else if (family == AF_INET) { + } else if (family == AF_INET) { inet_ntop(AF_INET, &((sockaddr_in *) socket_address)->sin_addr, data, INET_ADDRSTRLEN); } - return std::string { data }; + return std::string {data}; } - std::pair - from_sockaddr_ex(const sockaddr *const ip_addr) { + std::pair from_sockaddr_ex(const sockaddr *const ip_addr) { char data[INET6_ADDRSTRLEN] = {}; auto family = ip_addr->sa_family; @@ -147,18 +142,16 @@ namespace platf { if (family == AF_INET6) { inet_ntop(AF_INET6, &((sockaddr_in6 *) ip_addr)->sin6_addr, data, INET6_ADDRSTRLEN); port = ((sockaddr_in6 *) ip_addr)->sin6_port; - } - else if (family == AF_INET) { + } else if (family == AF_INET) { 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}}; } - adapteraddrs_t - get_adapteraddrs() { - adapteraddrs_t info { nullptr }; + adapteraddrs_t get_adapteraddrs() { + adapteraddrs_t info {nullptr}; ULONG size = 0; while (GetAdaptersAddresses(AF_UNSPEC, 0, nullptr, info.get(), &size) == ERROR_BUFFER_OVERFLOW) { @@ -168,8 +161,7 @@ namespace platf { return info; } - std::string - get_mac_address(const std::string_view &address) { + std::string get_mac_address(const std::string_view &address) { adapteraddrs_t info = get_adapteraddrs(); for (auto adapter_pos = info.get(); adapter_pos != nullptr; adapter_pos = adapter_pos->Next) { for (auto addr_pos = adapter_pos->FirstUnicastAddress; addr_pos != nullptr; addr_pos = addr_pos->Next) { @@ -190,8 +182,7 @@ namespace platf { return "00:00:00:00:00:00"s; } - HDESK - syncThreadDesktop() { + HDESK syncThreadDesktop() { auto hDesk = OpenInputDesktop(DF_ALLOWOTHERACCOUNTHOOK, FALSE, GENERIC_ALL); if (!hDesk) { auto err = GetLastError(); @@ -210,23 +201,15 @@ namespace platf { return hDesk; } - void - print_status(const std::string_view &prefix, HRESULT status) { + void print_status(const std::string_view &prefix, HRESULT status) { char err_string[1024]; - DWORD bytes = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - nullptr, - status, - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - err_string, - sizeof(err_string), - nullptr); + DWORD bytes = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, status, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), err_string, sizeof(err_string), nullptr); - BOOST_LOG(error) << prefix << ": "sv << std::string_view { err_string, bytes }; + BOOST_LOG(error) << prefix << ": "sv << std::string_view {err_string, bytes}; } - bool - IsUserAdmin(HANDLE user_token) { + bool IsUserAdmin(HANDLE user_token) { WINBOOL ret; SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY; PSID AdministratorsGroup; @@ -235,16 +218,21 @@ namespace platf { 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, - 0, 0, 0, 0, 0, 0, - &AdministratorsGroup); + 0, + 0, + 0, + 0, + 0, + 0, + &AdministratorsGroup + ); if (ret) { if (!CheckTokenMembership(user_token, AdministratorsGroup, &ret)) { ret = false; BOOST_LOG(error) << "Failed to verify token membership for administrative access: " << GetLastError(); } FreeSid(AdministratorsGroup); - } - else { + } else { BOOST_LOG(error) << "Unable to allocate SID to check administrative access: " << GetLastError(); } @@ -255,8 +243,7 @@ namespace platf { * @brief Obtain the current sessions user's primary token with elevated privileges. * @return The user's token. If user has admin capability it will be elevated, otherwise it will be a limited token. On error, `nullptr`. */ - HANDLE - retrieve_users_token(bool elevated) { + HANDLE retrieve_users_token(bool elevated) { DWORD consoleSessionId; HANDLE userToken; TOKEN_ELEVATION_TYPE elevationType; @@ -317,8 +304,7 @@ namespace platf { return userToken; } - bool - merge_user_environment_block(bp::environment &env, HANDLE shell_token) { + bool merge_user_environment_block(bp::environment &env, HANDLE shell_token) { // Get the target user's environment block PVOID env_block; if (!CreateEnvironmentBlock(&env_block, shell_token, FALSE)) { @@ -328,13 +314,14 @@ namespace platf { // Parse the environment block and populate env for (auto c = (PWCHAR) env_block; *c != UNICODE_NULL; c += wcslen(c) + 1) { // Environment variable entries end with a null-terminator, so std::wstring() will get an entire entry. - std::string env_tuple = to_utf8(std::wstring { c }); + std::string env_tuple = to_utf8(std::wstring {c}); std::string env_name = env_tuple.substr(0, env_tuple.find('=')); std::string env_val = env_tuple.substr(env_tuple.find('=') + 1); // Perform a case-insensitive search to see if this variable name already exists - auto itr = std::find_if(env.cbegin(), env.cend(), - [&](const auto &e) { return boost::iequals(e.get_name(), env_name); }); + auto itr = std::find_if(env.cbegin(), env.cend(), [&](const auto &e) { + return boost::iequals(e.get_name(), env_name); + }); if (itr != env.cend()) { // Use this existing name if it is already present to ensure we merge properly env_name = itr->get_name(); @@ -343,8 +330,7 @@ namespace platf { // For the PATH variable, we will merge the values together if (boost::iequals(env_name, "PATH")) { env[env_name] = env_val + ";" + env[env_name].to_string(); - } - else { + } else { // Other variables will be superseded by those in the user's environment block env[env_name] = env_val; } @@ -358,8 +344,7 @@ namespace platf { * @brief Check if the current process is running with system-level privileges. * @return `true` if the current process has system-level privileges, `false` otherwise. */ - bool - is_running_as_system() { + bool is_running_as_system() { BOOL ret; PSID SystemSid; DWORD dwSize = SECURITY_MAX_SID_SIZE; @@ -379,8 +364,7 @@ namespace platf { BOOST_LOG(error) << "Failed to check token membership: " << GetLastError(); ret = false; } - } - else { + } else { BOOST_LOG(error) << "Failed to create a SID for the local system account. This may happen if the system is out of memory or if the SID buffer is too small: " << GetLastError(); } @@ -390,14 +374,12 @@ namespace platf { } // Note: This does NOT append a null terminator - void - append_string_to_environment_block(wchar_t *env_block, int &offset, const std::wstring &wstr) { + void append_string_to_environment_block(wchar_t *env_block, int &offset, const std::wstring &wstr) { std::memcpy(&env_block[offset], wstr.data(), wstr.length() * sizeof(wchar_t)); offset += wstr.length(); } - std::wstring - create_environment_block(bp::environment &env) { + std::wstring create_environment_block(bp::environment &env) { int size = 0; for (const auto &entry : env) { auto name = entry.get_name(); @@ -426,8 +408,7 @@ namespace platf { return std::wstring(env_block, offset); } - LPPROC_THREAD_ATTRIBUTE_LIST - allocate_proc_thread_attr_list(DWORD attribute_count) { + LPPROC_THREAD_ATTRIBUTE_LIST allocate_proc_thread_attr_list(DWORD attribute_count) { SIZE_T size; InitializeProcThreadAttributeList(NULL, attribute_count, 0, &size); @@ -444,8 +425,7 @@ namespace platf { return list; } - void - free_proc_thread_attr_list(LPPROC_THREAD_ATTRIBUTE_LIST list) { + void free_proc_thread_attr_list(LPPROC_THREAD_ATTRIBUTE_LIST list) { DeleteProcThreadAttributeList(list); HeapFree(GetProcessHeap(), 0, list); } @@ -458,8 +438,7 @@ namespace platf { * @param process_info A reference to a `PROCESS_INFORMATION` structure that contains information about the new process. * @return A `bp::child` object representing the new process, or an empty `bp::child` object if the launch failed. */ - bp::child - create_boost_child_from_results(bool process_launched, const std::string &cmd, std::error_code &ec, PROCESS_INFORMATION &process_info) { + bp::child create_boost_child_from_results(bool process_launched, const std::string &cmd, std::error_code &ec, PROCESS_INFORMATION &process_info) { // Use RAII to ensure the process is closed when we're done with it, even if there was an error. auto close_process_handles = util::fail_guard([process_launched, process_info]() { if (process_launched) { @@ -478,8 +457,7 @@ namespace platf { auto child = bp::child((bp::pid_t) process_info.dwProcessId); BOOST_LOG(info) << cmd << " running with PID "sv << child.id(); return child; - } - else { + } else { auto winerror = GetLastError(); BOOST_LOG(error) << "Failed to launch process: "sv << winerror; ec = std::make_error_code(std::errc::invalid_argument); @@ -496,8 +474,7 @@ namespace platf { * @param callback A function that will be executed while impersonating the user. * @return Object that will store any error that occurred during the impersonation */ - std::error_code - impersonate_current_user(HANDLE user_token, std::function callback) { + std::error_code impersonate_current_user(HANDLE user_token, std::function callback) { std::error_code ec; // Impersonate the user when launching the process. This will ensure that appropriate access // checks are done against the user token, not our SYSTEM token. It will also allow network @@ -534,8 +511,7 @@ namespace platf { * @param ec A reference to a `std::error_code` object that will store any error that occurred during the creation of the structure. * @return A structure that contains information about how to launch the new process. */ - STARTUPINFOEXW - create_startup_info(FILE *file, HANDLE *job, std::error_code &ec) { + STARTUPINFOEXW create_startup_info(FILE *file, HANDLE *job, std::error_code &ec) { // Initialize a zeroed-out STARTUPINFOEXW structure and set its size STARTUPINFOEXW startup_info = {}; startup_info.StartupInfo.cb = sizeof(startup_info); @@ -563,13 +539,7 @@ namespace platf { // // Note: The value we point to here must be valid for the lifetime of the attribute list, // so we need to point into the STARTUPINFO instead of our log_file_variable on the stack. - UpdateProcThreadAttribute(startup_info.lpAttributeList, - 0, - PROC_THREAD_ATTRIBUTE_HANDLE_LIST, - &startup_info.StartupInfo.hStdOutput, - sizeof(startup_info.StartupInfo.hStdOutput), - NULL, - NULL); + UpdateProcThreadAttribute(startup_info.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_HANDLE_LIST, &startup_info.StartupInfo.hStdOutput, sizeof(startup_info.StartupInfo.hStdOutput), NULL, NULL); } if (job) { @@ -577,13 +547,7 @@ namespace platf { // // Note: The value we point to here must be valid for the lifetime of the attribute list, // so we take a HANDLE* instead of just a HANDLE to use the caller's stack storage. - UpdateProcThreadAttribute(startup_info.lpAttributeList, - 0, - PROC_THREAD_ATTRIBUTE_JOB_LIST, - job, - sizeof(*job), - NULL, - NULL); + UpdateProcThreadAttribute(startup_info.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_JOB_LIST, job, sizeof(*job), NULL, NULL); } return startup_info; @@ -594,8 +558,7 @@ namespace platf { * @param token The primary token identifying the user to use, or `NULL` to restore original keys. * @return `true` if the override or restore operation was successful. */ - bool - override_per_user_predefined_keys(HANDLE token) { + bool override_per_user_predefined_keys(HANDLE token) { HKEY user_classes_root = NULL; if (token) { auto err = RegOpenUserClassesRoot(token, 0, GENERIC_ALL, &user_classes_root); @@ -651,8 +614,7 @@ namespace platf { * @param argument The raw argument to process. * @return An argument string suitable for use by CreateProcess(). */ - std::wstring - escape_argument(const std::wstring &argument) { + std::wstring escape_argument(const std::wstring &argument) { // If there are no characters requiring quoting/escaping, we're done if (argument.find_first_of(L" \t\n\v\"") == argument.npos) { return argument; @@ -672,11 +634,9 @@ namespace platf { if (it == argument.end()) { escaped_arg.append(backslash_count * 2, L'\\'); break; - } - else if (*it == L'"') { + } else if (*it == L'"') { escaped_arg.append(backslash_count * 2 + 1, L'\\'); - } - else { + } else { escaped_arg.append(backslash_count, L'\\'); } @@ -691,8 +651,7 @@ namespace platf { * @param argument An argument already escaped by `escape_argument()`. * @return An argument string suitable for use by cmd.exe. */ - std::wstring - escape_argument_for_cmd(const std::wstring &argument) { + std::wstring escape_argument_for_cmd(const std::wstring &argument) { // Start with the original string and modify from there std::wstring escaped_arg = argument; @@ -716,8 +675,7 @@ namespace platf { * @param creation_flags The creation flags for CreateProcess(), which may be modified by this function. * @return A command string suitable for use by CreateProcess(). */ - std::wstring - resolve_command_string(const std::string &raw_cmd, const std::wstring &working_dir, HANDLE token, DWORD &creation_flags) { + std::wstring resolve_command_string(const std::string &raw_cmd, const std::wstring &working_dir, HANDLE token, DWORD &creation_flags) { std::wstring raw_cmd_w = from_utf8(raw_cmd); // First, convert the given command into parts so we can get the executable/file/URL without parameters @@ -744,16 +702,14 @@ namespace platf { // If the target is a URL, the class is found using the URL scheme (prior to and not including the ':') lookup_string = scheme.data(); - } - else { + } else { // If the target is not a URL, assume it's a regular file path auto extension = PathFindExtensionW(raw_target.c_str()); if (extension == nullptr || *extension == 0) { // If the file has no extension, assume it's a command and allow CreateProcess() // to try to find it via PATH return from_utf8(raw_cmd); - } - else if (boost::iequals(extension, L".exe")) { + } else if (boost::iequals(extension, L".exe")) { // If the file has an .exe extension, we will bypass the resolution here and // directly pass the unmodified command string to CreateProcess(). The argument // escaping rules are subtly different between CreateProcess() and ShellExecute(), @@ -814,7 +770,7 @@ namespace platf { // uncommon ones that are unsupported here. // // https://web.archive.org/web/20111002101214/http://msdn.microsoft.com/en-us/library/windows/desktop/cc144101(v=vs.85).aspx - std::wstring cmd_string { shell_command_string.data() }; + std::wstring cmd_string {shell_command_string.data()}; size_t match_pos = 0; while ((match_pos = cmd_string.find_first_of(L'%', match_pos)) != std::wstring::npos) { std::wstring match_replacement; @@ -843,19 +799,20 @@ namespace platf { case L'6': case L'7': case L'8': - case L'9': { - // Arguments numbers are 1-based, except for %0 which is equivalent to %1 - int index = next_char - L'0'; - if (next_char != L'0') { - index--; - } + case L'9': + { + // Arguments numbers are 1-based, except for %0 which is equivalent to %1 + int index = next_char - L'0'; + if (next_char != L'0') { + index--; + } - // Replace with the matching argument, or nothing if the index is invalid - if (index < raw_cmd_parts.size()) { - match_replacement = raw_cmd_parts.at(index); + // Replace with the matching argument, or nothing if the index is invalid + if (index < raw_cmd_parts.size()) { + match_replacement = raw_cmd_parts.at(index); + } + break; } - break; - } // All arguments following the target case L'*': @@ -878,29 +835,29 @@ namespace platf { // Long file path of target case L'l': case L'd': - case L'v': { - std::array path; - std::array other_dirs { working_dir.c_str(), nullptr }; + case L'v': + { + std::array path; + std::array other_dirs {working_dir.c_str(), nullptr}; - // PathFindOnPath() is a little gross because it uses the same - // buffer for input and output, so we need to copy our input - // into the path array. - std::wcsncpy(path.data(), raw_target.c_str(), path.size()); - if (path[path.size() - 1] != 0) { - // The path was so long it was truncated by this copy. We'll - // assume it was an absolute path (likely) and use it unmodified. - match_replacement = raw_target; + // PathFindOnPath() is a little gross because it uses the same + // buffer for input and output, so we need to copy our input + // into the path array. + std::wcsncpy(path.data(), raw_target.c_str(), path.size()); + if (path[path.size() - 1] != 0) { + // The path was so long it was truncated by this copy. We'll + // assume it was an absolute path (likely) and use it unmodified. + match_replacement = raw_target; + } + // See if we can find the path on our search path or working directory + else if (PathFindOnPathW(path.data(), other_dirs.data())) { + match_replacement = std::wstring {path.data()}; + } else { + // We couldn't find the target, so we'll just hope for the best + match_replacement = raw_target; + } + break; } - // See if we can find the path on our search path or working directory - else if (PathFindOnPathW(path.data(), other_dirs.data())) { - match_replacement = std::wstring { path.data() }; - } - else { - // We couldn't find the target, so we'll just hope for the best - match_replacement = raw_target; - } - break; - } // Working directory case L'w': @@ -938,8 +895,7 @@ namespace platf { * @param group A pointer to a `bp::group` object to which the new process should belong (may be `nullptr`). * @return A `bp::child` object representing the new process, or an empty `bp::child` object if the launch fails. */ - bp::child - run_command(bool elevated, bool interactive, const std::string &cmd, boost::filesystem::path &working_dir, const bp::environment &env, FILE *file, std::error_code &ec, bp::group *group) { + bp::child run_command(bool elevated, bool interactive, const std::string &cmd, boost::filesystem::path &working_dir, const bp::environment &env, FILE *file, std::error_code &ec, bp::group *group) { std::wstring start_dir = from_utf8(working_dir.string()); HANDLE job = group ? group->native_handle() : nullptr; STARTUPINFOEXW startup_info = create_startup_info(file, job ? &job : nullptr, ec); @@ -967,9 +923,11 @@ namespace platf { // Find the PATH variable in our environment block using a case-insensitive search auto sunshine_wenv = boost::this_process::wenvironment(); - std::wstring path_var_name { L"PATH" }; + std::wstring path_var_name {L"PATH"}; std::wstring old_path_val; - auto itr = std::find_if(sunshine_wenv.cbegin(), sunshine_wenv.cend(), [&](const auto &e) { return boost::iequals(e.get_name(), path_var_name); }); + auto itr = std::find_if(sunshine_wenv.cbegin(), sunshine_wenv.cend(), [&](const auto &e) { + return boost::iequals(e.get_name(), path_var_name); + }); if (itr != sunshine_wenv.cend()) { // Use the existing variable if it exists, since Boost treats these as case-sensitive. path_var_name = itr->get_name(); @@ -984,8 +942,7 @@ namespace platf { auto restore_path = util::fail_guard([&]() { if (old_path_val.empty()) { sunshine_wenv[path_var_name].clear(); - } - else { + } else { sunshine_wenv[path_var_name].assign(old_path_val); } }); @@ -1015,17 +972,7 @@ namespace platf { ec = impersonate_current_user(user_token, [&]() { std::wstring env_block = create_environment_block(cloned_env); std::wstring wcmd = resolve_command_string(cmd, start_dir, user_token, creation_flags); - ret = CreateProcessAsUserW(user_token, - NULL, - (LPWSTR) wcmd.c_str(), - NULL, - NULL, - !!(startup_info.StartupInfo.dwFlags & STARTF_USESTDHANDLES), - creation_flags, - env_block.data(), - start_dir.empty() ? NULL : start_dir.c_str(), - (LPSTARTUPINFOW) &startup_info, - &process_info); + ret = CreateProcessAsUserW(user_token, NULL, (LPWSTR) wcmd.c_str(), NULL, NULL, !!(startup_info.StartupInfo.dwFlags & STARTF_USESTDHANDLES), creation_flags, env_block.data(), start_dir.empty() ? NULL : start_dir.c_str(), (LPSTARTUPINFOW) &startup_info, &process_info); }); } // Otherwise, launch the process using CreateProcessW() @@ -1049,16 +996,7 @@ namespace platf { std::wstring env_block = create_environment_block(cloned_env); std::wstring wcmd = resolve_command_string(cmd, start_dir, NULL, creation_flags); - ret = CreateProcessW(NULL, - (LPWSTR) wcmd.c_str(), - NULL, - NULL, - !!(startup_info.StartupInfo.dwFlags & STARTF_USESTDHANDLES), - creation_flags, - env_block.data(), - start_dir.empty() ? NULL : start_dir.c_str(), - (LPSTARTUPINFOW) &startup_info, - &process_info); + ret = CreateProcessW(NULL, (LPWSTR) wcmd.c_str(), NULL, NULL, !!(startup_info.StartupInfo.dwFlags & STARTF_USESTDHANDLES), creation_flags, env_block.data(), start_dir.empty() ? NULL : start_dir.c_str(), (LPSTARTUPINFOW) &startup_info, &process_info); } // Use the results of the launch to create a bp::child object @@ -1069,8 +1007,7 @@ namespace platf { * @brief Open a url in the default web browser. * @param url The url to open. */ - void - open_url(const std::string &url) { + void open_url(const std::string &url) { boost::process::v1::environment _env = boost::this_process::environment(); auto working_dir = boost::filesystem::path(); std::error_code ec; @@ -1078,15 +1015,13 @@ namespace platf { auto child = run_command(false, false, url, working_dir, _env, nullptr, ec, nullptr); if (ec) { BOOST_LOG(warning) << "Couldn't open url ["sv << url << "]: System: "sv << ec.message(); - } - else { + } else { BOOST_LOG(info) << "Opened url ["sv << url << "]"sv; child.detach(); } } - void - adjust_thread_priority(thread_priority_e priority) { + void adjust_thread_priority(thread_priority_e priority) { int win32_priority; switch (priority) { @@ -1113,8 +1048,7 @@ namespace platf { } } - void - streaming_will_start() { + void streaming_will_start() { static std::once_flag load_wlanapi_once_flag; std::call_once(load_wlanapi_once_flag, []() { // wlanapi.dll is not installed by default on Windows Server, so we load it dynamically @@ -1150,8 +1084,7 @@ namespace platf { // Reduce timer period to 0.5ms if (nt_set_timer_resolution_max()) { used_nt_set_timer_resolution = true; - } - else { + } else { BOOST_LOG(error) << "NtSetTimerResolution() failed, falling back to timeBeginPeriod()"; timeBeginPeriod(1); used_nt_set_timer_resolution = false; @@ -1186,8 +1119,7 @@ namespace platf { // https://docs.microsoft.com/en-us/windows-hardware/drivers/network/oid-wdi-set-connection-quality // https://docs.microsoft.com/en-us/previous-versions/windows/hardware/wireless/native-802-11-media-streaming BOOL value = TRUE; - auto error = fn_WlanSetInterface(wlan_handle, &wlan_interface_list->InterfaceInfo[i].InterfaceGuid, - wlan_intf_opcode_media_streaming_mode, sizeof(value), &value, nullptr); + auto error = fn_WlanSetInterface(wlan_handle, &wlan_interface_list->InterfaceInfo[i].InterfaceGuid, wlan_intf_opcode_media_streaming_mode, sizeof(value), &value, nullptr); if (error == ERROR_SUCCESS) { BOOST_LOG(info) << "WLAN interface "sv << i << " is now in low latency mode"sv; } @@ -1195,8 +1127,7 @@ namespace platf { } fn_WlanFreeMemory(wlan_interface_list); - } - else { + } else { fn_WlanCloseHandle(wlan_handle, nullptr); wlan_handle = NULL; } @@ -1220,21 +1151,18 @@ namespace platf { if (SystemParametersInfoW(SPI_SETMOUSEKEYS, 0, &new_mouse_keys_state, 0)) { // Remember to restore the previous settings when we stop streaming enabled_mouse_keys = true; - } - else { + } else { auto winerr = GetLastError(); BOOST_LOG(warning) << "Unable to enable Mouse Keys: "sv << winerr; } - } - else { + } else { auto winerr = GetLastError(); BOOST_LOG(warning) << "Unable to get current state of Mouse Keys: "sv << winerr; } } } - void - streaming_will_stop() { + void streaming_will_stop() { // Demote ourselves back to normal priority class SetPriorityClass(GetCurrentProcess(), NORMAL_PRIORITY_CLASS); @@ -1244,8 +1172,7 @@ namespace platf { if (!nt_set_timer_resolution_min()) { BOOST_LOG(error) << "nt_set_timer_resolution_min() failed even though nt_set_timer_resolution_max() succeeded"; } - } - else { + } else { timeEndPeriod(1); } @@ -1268,8 +1195,7 @@ namespace platf { } } - void - restart_on_exit() { + void restart_on_exit() { STARTUPINFOEXW startup_info {}; startup_info.StartupInfo.cb = sizeof(startup_info); @@ -1281,16 +1207,7 @@ namespace platf { } PROCESS_INFORMATION process_info; - if (!CreateProcessW(executable, - GetCommandLineW(), - nullptr, - nullptr, - false, - CREATE_UNICODE_ENVIRONMENT | EXTENDED_STARTUPINFO_PRESENT, - nullptr, - nullptr, - (LPSTARTUPINFOW) &startup_info, - &process_info)) { + if (!CreateProcessW(executable, GetCommandLineW(), nullptr, nullptr, false, CREATE_UNICODE_ENVIRONMENT | EXTENDED_STARTUPINFO_PRESENT, nullptr, nullptr, (LPSTARTUPINFOW) &startup_info, &process_info)) { auto winerr = GetLastError(); BOOST_LOG(fatal) << "Unable to restart Sunshine: "sv << winerr; return; @@ -1300,8 +1217,7 @@ namespace platf { CloseHandle(process_info.hThread); } - void - restart() { + void restart() { // If we're running standalone, we have to respawn ourselves via CreateProcess(). // If we're running from the service, we should just exit and let it respawn us. if (GetConsoleWindow() != NULL) { @@ -1313,13 +1229,11 @@ namespace platf { lifetime::exit_sunshine(0, true); } - int - set_env(const std::string &name, const std::string &value) { + int set_env(const std::string &name, const std::string &value) { return _putenv_s(name.c_str(), value.c_str()); } - int - unset_env(const std::string &name) { + int unset_env(const std::string &name) { return _putenv_s(name.c_str(), ""); } @@ -1328,8 +1242,7 @@ namespace platf { bool requested_exit; }; - static BOOL CALLBACK - prgrp_enum_windows(HWND hwnd, LPARAM lParam) { + static BOOL CALLBACK prgrp_enum_windows(HWND hwnd, LPARAM lParam) { auto enum_ctx = (enum_wnd_context_t *) lParam; // Find the owner PID of this window @@ -1345,8 +1258,7 @@ namespace platf { if (SendNotifyMessageW(hwnd, WM_CLOSE, 0, 0)) { BOOST_LOG(debug) << "Sent WM_CLOSE to PID: "sv << wnd_process_id; enum_ctx->requested_exit = true; - } - else { + } else { auto error = GetLastError(); BOOST_LOG(warning) << "Failed to send WM_CLOSE to PID ["sv << wnd_process_id << "]: " << error; } @@ -1356,8 +1268,7 @@ namespace platf { return TRUE; } - bool - request_process_group_exit(std::uintptr_t native_handle) { + bool request_process_group_exit(std::uintptr_t native_handle) { auto job_handle = (HANDLE) native_handle; // Get list of all processes in our job object @@ -1367,8 +1278,7 @@ namespace platf { auto fg = util::fail_guard([&process_id_list]() { free(process_id_list); }); - while (!(success = QueryInformationJobObject(job_handle, JobObjectBasicProcessIdList, - process_id_list, required_length, &required_length)) && + while (!(success = QueryInformationJobObject(job_handle, JobObjectBasicProcessIdList, process_id_list, required_length, &required_length)) && GetLastError() == ERROR_MORE_DATA) { free(process_id_list); process_id_list = (PJOBOBJECT_BASIC_PROCESS_ID_LIST) calloc(1, required_length); @@ -1381,8 +1291,7 @@ namespace platf { auto err = GetLastError(); BOOST_LOG(warning) << "Failed to enumerate processes in group: "sv << err; return false; - } - else if (process_id_list->NumberOfProcessIdsInList == 0) { + } else if (process_id_list->NumberOfProcessIdsInList == 0) { // If all processes are already dead, treat it as a success return true; } @@ -1400,8 +1309,7 @@ namespace platf { return enum_ctx.requested_exit; } - bool - process_group_running(std::uintptr_t native_handle) { + bool process_group_running(std::uintptr_t native_handle) { JOBOBJECT_BASIC_ACCOUNTING_INFORMATION accounting_info; if (!QueryInformationJobObject((HANDLE) native_handle, JobObjectBasicAccountingInformation, &accounting_info, sizeof(accounting_info), nullptr)) { @@ -1413,8 +1321,7 @@ namespace platf { return accounting_info.ActiveProcesses != 0; } - SOCKADDR_IN - to_sockaddr(boost::asio::ip::address_v4 address, uint16_t port) { + SOCKADDR_IN to_sockaddr(boost::asio::ip::address_v4 address, uint16_t port) { SOCKADDR_IN saddr_v4 = {}; saddr_v4.sin_family = AF_INET; @@ -1426,8 +1333,7 @@ namespace platf { return saddr_v4; } - SOCKADDR_IN6 - to_sockaddr(boost::asio::ip::address_v6 address, uint16_t port) { + SOCKADDR_IN6 to_sockaddr(boost::asio::ip::address_v6 address, uint16_t port) { SOCKADDR_IN6 saddr_v6 = {}; saddr_v6.sin6_family = AF_INET6; @@ -1442,8 +1348,7 @@ namespace platf { // Use UDP segmentation offload if it is supported by the OS. If the NIC is capable, this will use // hardware acceleration to reduce CPU usage. Support for USO was introduced in Windows 10 20H1. - bool - send_batch(batched_send_info_t &send_info) { + bool send_batch(batched_send_info_t &send_info) { WSAMSG msg; // Convert the target address into a SOCKADDR @@ -1454,8 +1359,7 @@ namespace platf { msg.name = (PSOCKADDR) &taddr_v6; msg.namelen = sizeof(taddr_v6); - } - else { + } else { taddr_v4 = to_sockaddr(send_info.target_address.to_v4(), send_info.target_port); msg.name = (PSOCKADDR) &taddr_v4; @@ -1477,8 +1381,7 @@ namespace platf { bufs[bufcount].len = send_info.payload_size; bufcount++; } - } - else { + } else { // Translate buffer descriptors into WSABUFs auto payload_offset = send_info.block_offset * send_info.payload_size; auto payload_length = payload_offset + (send_info.block_count * send_info.payload_size); @@ -1496,8 +1399,7 @@ namespace platf { msg.dwFlags = 0; // At most, one DWORD option and one PKTINFO option - char cmbuf[WSA_CMSG_SPACE(sizeof(DWORD)) + - std::max(WSA_CMSG_SPACE(sizeof(IN6_PKTINFO)), WSA_CMSG_SPACE(sizeof(IN_PKTINFO)))] = {}; + char cmbuf[WSA_CMSG_SPACE(sizeof(DWORD)) + std::max(WSA_CMSG_SPACE(sizeof(IN6_PKTINFO)), WSA_CMSG_SPACE(sizeof(IN_PKTINFO)))] = {}; ULONG cmbuflen = 0; msg.Control.buf = cmbuf; @@ -1517,8 +1419,7 @@ namespace platf { cm->cmsg_type = IPV6_PKTINFO; cm->cmsg_len = WSA_CMSG_LEN(sizeof(pktInfo)); memcpy(WSA_CMSG_DATA(cm), &pktInfo, sizeof(pktInfo)); - } - else { + } else { IN_PKTINFO pktInfo; SOCKADDR_IN saddr_v4 = to_sockaddr(send_info.source_address.to_v4(), 0); @@ -1550,8 +1451,7 @@ namespace platf { return WSASendMsg((SOCKET) send_info.native_socket, &msg, 0, &bytes_sent, nullptr, nullptr) != SOCKET_ERROR; } - bool - send(send_info_t &send_info) { + bool send(send_info_t &send_info) { WSAMSG msg; // Convert the target address into a SOCKADDR @@ -1562,8 +1462,7 @@ namespace platf { msg.name = (PSOCKADDR) &taddr_v6; msg.namelen = sizeof(taddr_v6); - } - else { + } else { taddr_v4 = to_sockaddr(send_info.target_address.to_v4(), send_info.target_port); msg.name = (PSOCKADDR) &taddr_v4; @@ -1605,8 +1504,7 @@ namespace platf { cm->cmsg_type = IPV6_PKTINFO; cm->cmsg_len = WSA_CMSG_LEN(sizeof(pktInfo)); memcpy(WSA_CMSG_DATA(cm), &pktInfo, sizeof(pktInfo)); - } - else { + } else { IN_PKTINFO pktInfo; SOCKADDR_IN saddr_v4 = to_sockaddr(send_info.source_address.to_v4(), 0); @@ -1636,7 +1534,8 @@ namespace platf { class qos_t: public deinit_t { public: qos_t(QOS_FLOWID flow_id): - flow_id(flow_id) {} + flow_id(flow_id) { + } virtual ~qos_t() { if (!fn_QOSRemoveSocketFromFlow(qos_handle, (SOCKET) NULL, flow_id, 0)) { @@ -1657,8 +1556,7 @@ namespace platf { * @param data_type The type of traffic sent on this socket. * @param dscp_tagging Specifies whether to enable DSCP tagging on outgoing traffic. */ - std::unique_ptr - enable_socket_qos(uintptr_t native_socket, boost::asio::ip::address &address, uint16_t port, qos_data_type_e data_type, bool dscp_tagging) { + std::unique_ptr enable_socket_qos(uintptr_t native_socket, boost::asio::ip::address &address, uint16_t port, qos_data_type_e data_type, bool dscp_tagging) { SOCKADDR_IN saddr_v4; SOCKADDR_IN6 saddr_v6; PSOCKADDR dest_addr; @@ -1693,7 +1591,7 @@ namespace platf { return; } - QOS_VERSION qos_version { 1, 0 }; + QOS_VERSION qos_version {1, 0}; if (!fn_QOSCreateHandle(&qos_version, &qos_handle)) { auto winerr = GetLastError(); BOOST_LOG(warning) << "QOSCreateHandle() failed: "sv << winerr; @@ -1733,15 +1631,13 @@ namespace platf { if (connect((SOCKET) native_socket, (PSOCKADDR) &saddr_v6, sizeof(saddr_v6)) < 0) { auto wsaerr = WSAGetLastError(); BOOST_LOG(error) << "qWAVE dual-stack workaround failed: "sv << wsaerr; - } - else { + } else { BOOST_LOG(debug) << "Using qWAVE connect() workaround for QoS tagging"sv; using_connect_hack = true; dest_addr = nullptr; } } - } - else { + } else { saddr_v4 = to_sockaddr(address.to_v4(), port); dest_addr = (PSOCKADDR) &saddr_v4; } @@ -1768,15 +1664,16 @@ namespace platf { return std::make_unique(flow_id); } - int64_t - qpc_counter() { + + int64_t qpc_counter() { LARGE_INTEGER performance_counter; - if (QueryPerformanceCounter(&performance_counter)) return performance_counter.QuadPart; + if (QueryPerformanceCounter(&performance_counter)) { + return performance_counter.QuadPart; + } return 0; } - std::chrono::nanoseconds - qpc_time_difference(int64_t performance_counter1, int64_t performance_counter2) { + std::chrono::nanoseconds qpc_time_difference(int64_t performance_counter1, int64_t performance_counter2) { auto get_frequency = []() { LARGE_INTEGER frequency; frequency.QuadPart = 0; @@ -1790,8 +1687,7 @@ namespace platf { return {}; } - std::wstring - from_utf8(const std::string &string) { + std::wstring from_utf8(const std::string &string) { // No conversion needed if the string is empty if (string.empty()) { return {}; @@ -1817,16 +1713,14 @@ namespace platf { return output; } - std::string - to_utf8(const std::wstring &string) { + std::string to_utf8(const std::wstring &string) { // No conversion needed if the string is empty if (string.empty()) { return {}; } // Get the output size required to store the string - auto output_size = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, string.data(), string.size(), - nullptr, 0, nullptr, nullptr); + auto output_size = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, string.data(), string.size(), nullptr, 0, nullptr, nullptr); if (output_size == 0) { auto winerr = GetLastError(); BOOST_LOG(error) << "Failed to get UTF-8 buffer size: "sv << winerr; @@ -1835,8 +1729,7 @@ namespace platf { // Perform the conversion std::string output(output_size, '\0'); - output_size = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, string.data(), string.size(), - output.data(), output.size(), nullptr, nullptr); + output_size = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, string.data(), string.size(), output.data(), output.size(), nullptr, nullptr); if (output_size == 0) { auto winerr = GetLastError(); BOOST_LOG(error) << "Failed to convert string to UTF-8: "sv << winerr; @@ -1846,8 +1739,7 @@ namespace platf { return output; } - std::string - get_host_name() { + std::string get_host_name() { WCHAR hostname[256]; if (GetHostNameW(hostname, ARRAYSIZE(hostname)) == SOCKET_ERROR) { BOOST_LOG(error) << "GetHostNameW() failed: "sv << WSAGetLastError(); @@ -1870,11 +1762,12 @@ namespace platf { } ~win32_high_precision_timer() { - if (timer) CloseHandle(timer); + if (timer) { + CloseHandle(timer); + } } - void - sleep_for(const std::chrono::nanoseconds &duration) override { + void sleep_for(const std::chrono::nanoseconds &duration) override { if (!timer) { BOOST_LOG(error) << "Attempting high_precision_timer::sleep_for() with uninitialized timer"; return; @@ -1902,8 +1795,7 @@ namespace platf { HANDLE timer = NULL; }; - std::unique_ptr - create_high_precision_timer() { + std::unique_ptr create_high_precision_timer() { return std::make_unique(); } } // namespace platf diff --git a/src/platform/windows/misc.h b/src/platform/windows/misc.h index b045104f..30d85376 100644 --- a/src/platform/windows/misc.h +++ b/src/platform/windows/misc.h @@ -4,36 +4,33 @@ */ #pragma once +// standard includes #include #include + +// platform includes #include #include namespace platf { - void - print_status(const std::string_view &prefix, HRESULT status); - HDESK - syncThreadDesktop(); + void print_status(const std::string_view &prefix, HRESULT status); + HDESK syncThreadDesktop(); - int64_t - qpc_counter(); + int64_t qpc_counter(); - std::chrono::nanoseconds - qpc_time_difference(int64_t performance_counter1, int64_t performance_counter2); + std::chrono::nanoseconds qpc_time_difference(int64_t performance_counter1, int64_t performance_counter2); /** * @brief Convert a UTF-8 string into a UTF-16 wide string. * @param string The UTF-8 string. * @return The converted UTF-16 wide string. */ - std::wstring - from_utf8(const std::string &string); + std::wstring from_utf8(const std::string &string); /** * @brief Convert a UTF-16 wide string into a UTF-8 string. * @param string The UTF-16 wide string. * @return The converted UTF-8 string. */ - std::string - to_utf8(const std::wstring &string); + std::string to_utf8(const std::wstring &string); } // namespace platf diff --git a/src/platform/windows/nvprefs/driver_settings.cpp b/src/platform/windows/nvprefs/driver_settings.cpp index 2cea9fa4..57aa809f 100644 --- a/src/platform/windows/nvprefs/driver_settings.cpp +++ b/src/platform/windows/nvprefs/driver_settings.cpp @@ -2,8 +2,10 @@ * @file src/platform/windows/nvprefs/driver_settings.cpp * @brief Definitions for nvidia driver settings. */ -// local includes +// this include #include "driver_settings.h" + +// local includes #include "nvprefs_common.h" namespace { @@ -11,15 +13,13 @@ namespace { const auto sunshine_application_profile_name = L"SunshineStream"; const auto sunshine_application_path = L"sunshine.exe"; - void - nvapi_error_message(NvAPI_Status status) { + void nvapi_error_message(NvAPI_Status status) { NvAPI_ShortString message = {}; NvAPI_GetErrorMessage(status, message); nvprefs::error_message(std::string("NvAPI error: ") + message); } - void - fill_nvapi_string(NvAPI_UnicodeString &dest, const wchar_t *src) { + void fill_nvapi_string(NvAPI_UnicodeString &dest, const wchar_t *src) { static_assert(sizeof(NvU16) == sizeof(wchar_t)); memcpy_s(dest, NVAPI_UNICODE_STRING_MAX * sizeof(NvU16), src, (wcslen(src) + 1) * sizeof(wchar_t)); } @@ -34,9 +34,10 @@ namespace nvprefs { } } - bool - driver_settings_t::init() { - if (session_handle) return true; + bool driver_settings_t::init() { + if (session_handle) { + return true; + } NvAPI_Status status; @@ -56,8 +57,7 @@ namespace nvprefs { return load_settings(); } - void - driver_settings_t::destroy() { + void driver_settings_t::destroy() { if (session_handle) { NvAPI_DRS_DestroySession(session_handle); session_handle = 0; @@ -65,9 +65,10 @@ namespace nvprefs { NvAPI_Unload(); } - bool - driver_settings_t::load_settings() { - if (!session_handle) return false; + bool driver_settings_t::load_settings() { + if (!session_handle) { + return false; + } NvAPI_Status status = NvAPI_DRS_LoadSettings(session_handle); if (status != NVAPI_OK) { @@ -80,9 +81,10 @@ namespace nvprefs { return true; } - bool - driver_settings_t::save_settings() { - if (!session_handle) return false; + bool driver_settings_t::save_settings() { + if (!session_handle) { + return false; + } NvAPI_Status status = NvAPI_DRS_SaveSettings(session_handle); if (status != NVAPI_OK) { @@ -94,9 +96,10 @@ namespace nvprefs { return true; } - bool - driver_settings_t::restore_global_profile_to_undo(const undo_data_t &undo_data) { - if (!session_handle) return false; + bool driver_settings_t::restore_global_profile_to_undo(const undo_data_t &undo_data) { + if (!session_handle) { + return false; + } const auto &swapchain_data = undo_data.get_opengl_swapchain(); if (swapchain_data) { @@ -130,8 +133,7 @@ namespace nvprefs { error_message("NvAPI_DRS_SetSetting() OGL_CPL_PREFER_DXPRESENT failed"); return false; } - } - else { + } else { status = NvAPI_DRS_DeleteProfileSetting(session_handle, profile_handle, OGL_CPL_PREFER_DXPRESENT_ID); if (status != NVAPI_OK && status != NVAPI_SETTING_NOT_FOUND) { @@ -142,11 +144,9 @@ namespace nvprefs { } info_message("Restored OGL_CPL_PREFER_DXPRESENT for base profile"); - } - else if (status == NVAPI_OK || status == NVAPI_SETTING_NOT_FOUND) { + } else if (status == NVAPI_OK || status == NVAPI_SETTING_NOT_FOUND) { info_message("OGL_CPL_PREFER_DXPRESENT has been changed from our value in base profile, not restoring"); - } - else { + } else { error_message("NvAPI_DRS_GetSetting() OGL_CPL_PREFER_DXPRESENT failed"); return false; } @@ -155,9 +155,10 @@ namespace nvprefs { return true; } - bool - driver_settings_t::check_and_modify_global_profile(std::optional &undo_data) { - if (!session_handle) return false; + bool driver_settings_t::check_and_modify_global_profile(std::optional &undo_data) { + if (!session_handle) { + return false; + } undo_data.reset(); NvAPI_Status status; @@ -184,8 +185,7 @@ namespace nvprefs { undo_data = undo_data_t(); if (status == NVAPI_OK) { undo_data->set_opengl_swapchain(OGL_CPL_PREFER_DXPRESENT_PREFER_ENABLED, setting.u32CurrentValue); - } - else { + } else { undo_data->set_opengl_swapchain(OGL_CPL_PREFER_DXPRESENT_PREFER_ENABLED, std::nullopt); } @@ -204,8 +204,7 @@ namespace nvprefs { } info_message("Changed OGL_CPL_PREFER_DXPRESENT to OGL_CPL_PREFER_DXPRESENT_PREFER_ENABLED for base profile"); - } - else if (status != NVAPI_OK) { + } else if (status != NVAPI_OK) { nvapi_error_message(status); error_message("NvAPI_DRS_GetSetting() OGL_CPL_PREFER_DXPRESENT failed"); return false; @@ -214,9 +213,10 @@ namespace nvprefs { return true; } - bool - driver_settings_t::check_and_modify_application_profile(bool &modified) { - if (!session_handle) return false; + bool driver_settings_t::check_and_modify_application_profile(bool &modified) { + if (!session_handle) { + return false; + } modified = false; NvAPI_Status status; @@ -285,10 +285,9 @@ namespace nvprefs { info_message(std::wstring(L"Removed PREFERRED_PSTATE for ") + sunshine_application_path); } - } - else if (status != NVAPI_OK || - setting.settingLocation != NVDRS_CURRENT_PROFILE_LOCATION || - setting.u32CurrentValue != PREFERRED_PSTATE_PREFER_MAX) { + } else if (status != NVAPI_OK || + setting.settingLocation != NVDRS_CURRENT_PROFILE_LOCATION || + setting.u32CurrentValue != PREFERRED_PSTATE_PREFER_MAX) { // Set power setting if needed setting = {}; setting.version = NVDRS_SETTING_VER1; diff --git a/src/platform/windows/nvprefs/driver_settings.h b/src/platform/windows/nvprefs/driver_settings.h index c2ef8bec..e4649d1d 100644 --- a/src/platform/windows/nvprefs/driver_settings.h +++ b/src/platform/windows/nvprefs/driver_settings.h @@ -21,26 +21,19 @@ namespace nvprefs { public: ~driver_settings_t(); - bool - init(); + bool init(); - void - destroy(); + void destroy(); - bool - load_settings(); + bool load_settings(); - bool - save_settings(); + bool save_settings(); - bool - restore_global_profile_to_undo(const undo_data_t &undo_data); + bool restore_global_profile_to_undo(const undo_data_t &undo_data); - bool - check_and_modify_global_profile(std::optional &undo_data); + bool check_and_modify_global_profile(std::optional &undo_data); - bool - check_and_modify_application_profile(bool &modified); + bool check_and_modify_application_profile(bool &modified); private: NvDRSSessionHandle session_handle = 0; diff --git a/src/platform/windows/nvprefs/nvapi_opensource_wrapper.cpp b/src/platform/windows/nvprefs/nvapi_opensource_wrapper.cpp index 4227882c..b4c04f6a 100644 --- a/src/platform/windows/nvprefs/nvapi_opensource_wrapper.cpp +++ b/src/platform/windows/nvprefs/nvapi_opensource_wrapper.cpp @@ -2,7 +2,7 @@ * @file src/platform/windows/nvprefs/nvapi_opensource_wrapper.cpp * @brief Definitions for the NVAPI wrapper. */ -// standard library headers +// standard includes #include // local includes @@ -17,9 +17,8 @@ namespace { std::map interfaces; HMODULE dll = NULL; - template - NvAPI_Status - call_interface(const char *name, Args... args) { + template + NvAPI_Status call_interface(const char *name, Args... args) { auto func = (Func *) interfaces[name]; if (!func) { @@ -38,7 +37,9 @@ extern void *__cdecl nvapi_QueryInterface(NvU32 id); NVAPI_INTERFACE NvAPI_Initialize() { - if (dll) return NVAPI_OK; + if (dll) { + return NVAPI_OK; + } #ifdef _WIN64 auto dll_name = "nvapi64.dll"; @@ -59,8 +60,7 @@ NvAPI_Initialize() { return NVAPI_LIBRARY_NOT_FOUND; } -NVAPI_INTERFACE -NvAPI_Unload() { +NVAPI_INTERFACE NvAPI_Unload() { if (dll) { interfaces.clear(); FreeLibrary(dll); @@ -69,69 +69,56 @@ NvAPI_Unload() { return NVAPI_OK; } -NVAPI_INTERFACE -NvAPI_GetErrorMessage(NvAPI_Status nr, NvAPI_ShortString szDesc) { +NVAPI_INTERFACE NvAPI_GetErrorMessage(NvAPI_Status nr, NvAPI_ShortString szDesc) { return call_interface("NvAPI_GetErrorMessage", nr, szDesc); } // This is only a subset of NvAPI_DRS_* functions, more can be added if needed -NVAPI_INTERFACE -NvAPI_DRS_CreateSession(NvDRSSessionHandle *phSession) { +NVAPI_INTERFACE NvAPI_DRS_CreateSession(NvDRSSessionHandle *phSession) { return call_interface("NvAPI_DRS_CreateSession", phSession); } -NVAPI_INTERFACE -NvAPI_DRS_DestroySession(NvDRSSessionHandle hSession) { +NVAPI_INTERFACE NvAPI_DRS_DestroySession(NvDRSSessionHandle hSession) { return call_interface("NvAPI_DRS_DestroySession", hSession); } -NVAPI_INTERFACE -NvAPI_DRS_LoadSettings(NvDRSSessionHandle hSession) { +NVAPI_INTERFACE NvAPI_DRS_LoadSettings(NvDRSSessionHandle hSession) { return call_interface("NvAPI_DRS_LoadSettings", hSession); } -NVAPI_INTERFACE -NvAPI_DRS_SaveSettings(NvDRSSessionHandle hSession) { +NVAPI_INTERFACE NvAPI_DRS_SaveSettings(NvDRSSessionHandle hSession) { return call_interface("NvAPI_DRS_SaveSettings", hSession); } -NVAPI_INTERFACE -NvAPI_DRS_CreateProfile(NvDRSSessionHandle hSession, NVDRS_PROFILE *pProfileInfo, NvDRSProfileHandle *phProfile) { +NVAPI_INTERFACE NvAPI_DRS_CreateProfile(NvDRSSessionHandle hSession, NVDRS_PROFILE *pProfileInfo, NvDRSProfileHandle *phProfile) { return call_interface("NvAPI_DRS_CreateProfile", hSession, pProfileInfo, phProfile); } -NVAPI_INTERFACE -NvAPI_DRS_FindProfileByName(NvDRSSessionHandle hSession, NvAPI_UnicodeString profileName, NvDRSProfileHandle *phProfile) { +NVAPI_INTERFACE NvAPI_DRS_FindProfileByName(NvDRSSessionHandle hSession, NvAPI_UnicodeString profileName, NvDRSProfileHandle *phProfile) { return call_interface("NvAPI_DRS_FindProfileByName", hSession, profileName, phProfile); } -NVAPI_INTERFACE -NvAPI_DRS_CreateApplication(NvDRSSessionHandle hSession, NvDRSProfileHandle hProfile, NVDRS_APPLICATION *pApplication) { +NVAPI_INTERFACE NvAPI_DRS_CreateApplication(NvDRSSessionHandle hSession, NvDRSProfileHandle hProfile, NVDRS_APPLICATION *pApplication) { return call_interface("NvAPI_DRS_CreateApplication", hSession, hProfile, pApplication); } -NVAPI_INTERFACE -NvAPI_DRS_GetApplicationInfo(NvDRSSessionHandle hSession, NvDRSProfileHandle hProfile, NvAPI_UnicodeString appName, NVDRS_APPLICATION *pApplication) { +NVAPI_INTERFACE NvAPI_DRS_GetApplicationInfo(NvDRSSessionHandle hSession, NvDRSProfileHandle hProfile, NvAPI_UnicodeString appName, NVDRS_APPLICATION *pApplication) { return call_interface("NvAPI_DRS_GetApplicationInfo", hSession, hProfile, appName, pApplication); } -NVAPI_INTERFACE -NvAPI_DRS_SetSetting(NvDRSSessionHandle hSession, NvDRSProfileHandle hProfile, NVDRS_SETTING *pSetting) { +NVAPI_INTERFACE NvAPI_DRS_SetSetting(NvDRSSessionHandle hSession, NvDRSProfileHandle hProfile, NVDRS_SETTING *pSetting) { return call_interface("NvAPI_DRS_SetSetting", hSession, hProfile, pSetting); } -NVAPI_INTERFACE -NvAPI_DRS_GetSetting(NvDRSSessionHandle hSession, NvDRSProfileHandle hProfile, NvU32 settingId, NVDRS_SETTING *pSetting) { +NVAPI_INTERFACE NvAPI_DRS_GetSetting(NvDRSSessionHandle hSession, NvDRSProfileHandle hProfile, NvU32 settingId, NVDRS_SETTING *pSetting) { return call_interface("NvAPI_DRS_GetSetting", hSession, hProfile, settingId, pSetting); } -NVAPI_INTERFACE -NvAPI_DRS_DeleteProfileSetting(NvDRSSessionHandle hSession, NvDRSProfileHandle hProfile, NvU32 settingId) { +NVAPI_INTERFACE NvAPI_DRS_DeleteProfileSetting(NvDRSSessionHandle hSession, NvDRSProfileHandle hProfile, NvU32 settingId) { return call_interface("NvAPI_DRS_DeleteProfileSetting", hSession, hProfile, settingId); } -NVAPI_INTERFACE -NvAPI_DRS_GetBaseProfile(NvDRSSessionHandle hSession, NvDRSProfileHandle *phProfile) { +NVAPI_INTERFACE NvAPI_DRS_GetBaseProfile(NvDRSSessionHandle hSession, NvDRSProfileHandle *phProfile) { return call_interface("NvAPI_DRS_GetBaseProfile", hSession, phProfile); } diff --git a/src/platform/windows/nvprefs/nvprefs_common.cpp b/src/platform/windows/nvprefs/nvprefs_common.cpp index 902ff81a..649ef60c 100644 --- a/src/platform/windows/nvprefs/nvprefs_common.cpp +++ b/src/platform/windows/nvprefs/nvprefs_common.cpp @@ -2,37 +2,32 @@ * @file src/platform/windows/nvprefs/nvprefs_common.cpp * @brief Definitions for common nvidia preferences. */ -// local includes +// this include #include "nvprefs_common.h" -#include "src/logging.h" -// read user override preferences from global sunshine config +// local includes #include "src/config.h" +#include "src/logging.h" namespace nvprefs { - void - info_message(const std::wstring &message) { + void info_message(const std::wstring &message) { BOOST_LOG(info) << "nvprefs: " << message; } - void - info_message(const std::string &message) { + void info_message(const std::string &message) { BOOST_LOG(info) << "nvprefs: " << message; } - void - error_message(const std::wstring &message) { + void error_message(const std::wstring &message) { BOOST_LOG(error) << "nvprefs: " << message; } - void - error_message(const std::string &message) { + void error_message(const std::string &message) { BOOST_LOG(error) << "nvprefs: " << message; } - nvprefs_options - get_nvprefs_options() { + nvprefs_options get_nvprefs_options() { nvprefs_options options; options.opengl_vulkan_on_dxgi = config::video.nv_opengl_vulkan_on_dxgi; options.sunshine_high_power_mode = config::video.nv_sunshine_high_power_mode; diff --git a/src/platform/windows/nvprefs/nvprefs_common.h b/src/platform/windows/nvprefs/nvprefs_common.h index aa6c00fa..baf4c0fb 100644 --- a/src/platform/windows/nvprefs/nvprefs_common.h +++ b/src/platform/windows/nvprefs/nvprefs_common.h @@ -4,57 +4,51 @@ */ #pragma once -// sunshine utility header for generic smart pointers -#include "src/utility.h" - -// winapi headers +// platform includes // disable clang-format header reordering // clang-format off #include #include // clang-format on +// local includes +#include "src/utility.h" + namespace nvprefs { struct safe_handle: public util::safe_ptr_v2 { using util::safe_ptr_v2::safe_ptr_v2; - explicit - operator bool() const { + + explicit operator bool() const { auto handle = get(); return handle != NULL && handle != INVALID_HANDLE_VALUE; } }; struct safe_hlocal_deleter { - void - operator()(void *p) { + void operator()(void *p) { LocalFree(p); } }; - template + template using safe_hlocal = util::uniq_ptr, safe_hlocal_deleter>; using safe_sid = util::safe_ptr_v2; - void - info_message(const std::wstring &message); + void info_message(const std::wstring &message); - void - info_message(const std::string &message); + void info_message(const std::string &message); - void - error_message(const std::wstring &message); + void error_message(const std::wstring &message); - void - error_message(const std::string &message); + void error_message(const std::string &message); struct nvprefs_options { bool opengl_vulkan_on_dxgi = true; bool sunshine_high_power_mode = true; }; - nvprefs_options - get_nvprefs_options(); + nvprefs_options get_nvprefs_options(); } // namespace nvprefs diff --git a/src/platform/windows/nvprefs/nvprefs_interface.cpp b/src/platform/windows/nvprefs/nvprefs_interface.cpp index ad366cd5..632ffa45 100644 --- a/src/platform/windows/nvprefs/nvprefs_interface.cpp +++ b/src/platform/windows/nvprefs/nvprefs_interface.cpp @@ -39,8 +39,7 @@ namespace nvprefs { unload(); } - bool - nvprefs_interface::load() { + bool nvprefs_interface::load() { if (!pimpl->loaded) { // Check %ProgramData% variable, need it for storing undo file wchar_t program_data_env[MAX_PATH]; @@ -61,8 +60,7 @@ namespace nvprefs { return pimpl->loaded; } - void - nvprefs_interface::unload() { + void nvprefs_interface::unload() { if (pimpl->loaded) { // Unload dynamically loaded nvapi library pimpl->driver_settings.destroy(); @@ -70,9 +68,10 @@ namespace nvprefs { } } - bool - nvprefs_interface::restore_from_and_delete_undo_file_if_exists() { - if (!pimpl->loaded) return false; + bool nvprefs_interface::restore_from_and_delete_undo_file_if_exists() { + if (!pimpl->loaded) { + return false; + } // Check for undo file from previous improper termination bool access_denied = false; @@ -82,12 +81,10 @@ namespace nvprefs { if (auto undo_data = undo_file->read_undo_data()) { if (pimpl->driver_settings.restore_global_profile_to_undo(*undo_data) && pimpl->driver_settings.save_settings()) { info_message("Restored global profile settings from undo file - deleting the file"); - } - else { + } else { error_message("Failed to restore global profile settings from undo file, deleting the file anyway"); } - } - else { + } else { error_message("Coulnd't read undo file, deleting the file anyway"); } @@ -95,8 +92,7 @@ namespace nvprefs { error_message("Couldn't delete undo file"); return false; } - } - else if (access_denied) { + } else if (access_denied) { error_message("Couldn't open undo file from previous improper termination, or confirm that there's no such file"); return false; } @@ -104,43 +100,41 @@ namespace nvprefs { return true; } - bool - nvprefs_interface::modify_application_profile() { - if (!pimpl->loaded) return false; + bool nvprefs_interface::modify_application_profile() { + if (!pimpl->loaded) { + return false; + } // Modify and save sunshine.exe application profile settings, if needed bool modified = false; if (!pimpl->driver_settings.check_and_modify_application_profile(modified)) { error_message("Failed to modify application profile settings"); return false; - } - else if (modified) { + } else if (modified) { if (pimpl->driver_settings.save_settings()) { info_message("Modified application profile settings"); - } - else { + } else { error_message("Couldn't save application profile settings"); return false; } - } - else { + } else { info_message("No need to modify application profile settings"); } return true; } - bool - nvprefs_interface::modify_global_profile() { - if (!pimpl->loaded) return false; + bool nvprefs_interface::modify_global_profile() { + if (!pimpl->loaded) { + return false; + } // Modify but not save global profile settings, if needed std::optional undo_data; if (!pimpl->driver_settings.check_and_modify_global_profile(undo_data)) { error_message("Couldn't modify global profile settings"); return false; - } - else if (!undo_data) { + } else if (!undo_data) { info_message("No need to modify global profile settings"); return true; } @@ -166,8 +160,7 @@ namespace nvprefs { if (pimpl->undo_data) { // Merge undo data if settings has been modified externally since our last modification pimpl->undo_data->merge(*undo_data); - } - else { + } else { pimpl->undo_data = undo_data; } @@ -198,14 +191,14 @@ namespace nvprefs { return true; } - bool - nvprefs_interface::owning_undo_file() { + bool nvprefs_interface::owning_undo_file() { return pimpl->undo_file.has_value(); } - bool - nvprefs_interface::restore_global_profile() { - if (!pimpl->loaded || !pimpl->undo_data || !pimpl->undo_file) return false; + bool nvprefs_interface::restore_global_profile() { + if (!pimpl->loaded || !pimpl->undo_data || !pimpl->undo_file) { + return false; + } // Restore global profile settings with undo data if (pimpl->driver_settings.restore_global_profile_to_undo(*pimpl->undo_data) && @@ -217,8 +210,7 @@ namespace nvprefs { } pimpl->undo_data = std::nullopt; pimpl->undo_file = std::nullopt; - } - else { + } else { error_message("Couldn't restore global profile settings"); return false; } diff --git a/src/platform/windows/nvprefs/nvprefs_interface.h b/src/platform/windows/nvprefs/nvprefs_interface.h index 73235877..655f114f 100644 --- a/src/platform/windows/nvprefs/nvprefs_interface.h +++ b/src/platform/windows/nvprefs/nvprefs_interface.h @@ -4,7 +4,7 @@ */ #pragma once -// standard library headers +// standard includes #include namespace nvprefs { @@ -14,26 +14,19 @@ namespace nvprefs { nvprefs_interface(); ~nvprefs_interface(); - bool - load(); + bool load(); - void - unload(); + void unload(); - bool - restore_from_and_delete_undo_file_if_exists(); + bool restore_from_and_delete_undo_file_if_exists(); - bool - modify_application_profile(); + bool modify_application_profile(); - bool - modify_global_profile(); + bool modify_global_profile(); - bool - owning_undo_file(); + bool owning_undo_file(); - bool - restore_global_profile(); + bool restore_global_profile(); private: struct impl; diff --git a/src/platform/windows/nvprefs/undo_data.cpp b/src/platform/windows/nvprefs/undo_data.cpp index e75b92b8..5a092815 100644 --- a/src/platform/windows/nvprefs/undo_data.cpp +++ b/src/platform/windows/nvprefs/undo_data.cpp @@ -2,7 +2,7 @@ * @file src/platform/windows/nvprefs/undo_data.cpp * @brief Definitions for undoing changes to nvidia preferences. */ -// external includes +// lib includes #include // local includes @@ -17,54 +17,46 @@ namespace nlohmann { using data_t = nvprefs::undo_data_t::data_t; using opengl_swapchain_t = data_t::opengl_swapchain_t; - template + template struct adl_serializer> { - static void - to_json(json &j, const std::optional &opt) { + static void to_json(json &j, const std::optional &opt) { if (opt == std::nullopt) { j = nullptr; - } - else { + } else { j = *opt; } } - static void - from_json(const json &j, std::optional &opt) { + static void from_json(const json &j, std::optional &opt) { if (j.is_null()) { opt = std::nullopt; - } - else { + } else { opt = j.template get(); } } }; - template <> + template<> struct adl_serializer { - static void - to_json(json &j, const data_t &data) { - j = json { { "opengl_swapchain", data.opengl_swapchain } }; + static void to_json(json &j, const data_t &data) { + j = json {{"opengl_swapchain", data.opengl_swapchain}}; } - static void - from_json(const json &j, data_t &data) { + static void from_json(const json &j, data_t &data) { j.at("opengl_swapchain").get_to(data.opengl_swapchain); } }; - template <> + template<> struct adl_serializer { - static void - to_json(json &j, const opengl_swapchain_t &opengl_swapchain) { + static void to_json(json &j, const opengl_swapchain_t &opengl_swapchain) { j = json { - { "our_value", opengl_swapchain.our_value }, - { "undo_value", opengl_swapchain.undo_value } + {"our_value", opengl_swapchain.our_value}, + {"undo_value", opengl_swapchain.undo_value} }; } - static void - from_json(const json &j, opengl_swapchain_t &opengl_swapchain) { + static void from_json(const json &j, opengl_swapchain_t &opengl_swapchain) { j.at("our_value").get_to(opengl_swapchain.our_value); j.at("undo_value").get_to(opengl_swapchain.undo_value); } @@ -73,46 +65,39 @@ namespace nlohmann { namespace nvprefs { - void - undo_data_t::set_opengl_swapchain(uint32_t our_value, std::optional undo_value) { + void undo_data_t::set_opengl_swapchain(uint32_t our_value, std::optional undo_value) { data.opengl_swapchain = data_t::opengl_swapchain_t { our_value, undo_value }; } - std::optional - undo_data_t::get_opengl_swapchain() const { + std::optional undo_data_t::get_opengl_swapchain() const { return data.opengl_swapchain; } - std::string - undo_data_t::write() const { + std::string undo_data_t::write() const { try { // Keep this assignment otherwise data will be treated as an array due to // initializer list shenanigangs. const json json_data = data; return json_data.dump(); - } - catch (const std::exception &err) { - error_message(std::string { "failed to serialize json data" }); + } catch (const std::exception &err) { + error_message(std::string {"failed to serialize json data"}); return {}; } } - void - undo_data_t::read(const std::vector &buffer) { + void undo_data_t::read(const std::vector &buffer) { try { data = json::parse(std::begin(buffer), std::end(buffer)); - } - catch (const std::exception &err) { - error_message(std::string { "failed to parse json data: " } + err.what()); + } catch (const std::exception &err) { + error_message(std::string {"failed to parse json data: "} + err.what()); data = {}; } } - void - undo_data_t::merge(const undo_data_t &newer_data) { + void undo_data_t::merge(const undo_data_t &newer_data) { const auto &swapchain_data = newer_data.get_opengl_swapchain(); if (swapchain_data) { set_opengl_swapchain(swapchain_data->our_value, swapchain_data->undo_value); diff --git a/src/platform/windows/nvprefs/undo_data.h b/src/platform/windows/nvprefs/undo_data.h index 5bd10ad9..aa359273 100644 --- a/src/platform/windows/nvprefs/undo_data.h +++ b/src/platform/windows/nvprefs/undo_data.h @@ -4,7 +4,7 @@ */ #pragma once -// standard library headers +// standard includes #include #include #include @@ -23,20 +23,15 @@ namespace nvprefs { std::optional opengl_swapchain; }; - void - set_opengl_swapchain(uint32_t our_value, std::optional undo_value); + void set_opengl_swapchain(uint32_t our_value, std::optional undo_value); - std::optional - get_opengl_swapchain() const; + std::optional get_opengl_swapchain() const; - std::string - write() const; + std::string write() const; - void - read(const std::vector &buffer); + void read(const std::vector &buffer); - void - merge(const undo_data_t &newer_data); + void merge(const undo_data_t &newer_data); private: data_t data; diff --git a/src/platform/windows/nvprefs/undo_file.cpp b/src/platform/windows/nvprefs/undo_file.cpp index 5897834f..ada737c8 100644 --- a/src/platform/windows/nvprefs/undo_file.cpp +++ b/src/platform/windows/nvprefs/undo_file.cpp @@ -9,13 +9,14 @@ namespace { using namespace nvprefs; - DWORD - relax_permissions(HANDLE file_handle) { + DWORD relax_permissions(HANDLE file_handle) { PACL old_dacl = nullptr; safe_hlocal sd; DWORD status = GetSecurityInfo(file_handle, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, nullptr, nullptr, &old_dacl, nullptr, &sd); - if (status != ERROR_SUCCESS) return status; + if (status != ERROR_SUCCESS) { + return status; + } safe_sid users_sid; SID_IDENTIFIER_AUTHORITY nt_authorithy = SECURITY_NT_AUTHORITY; @@ -32,10 +33,14 @@ namespace { safe_hlocal new_dacl; status = SetEntriesInAcl(1, &ea, old_dacl, &new_dacl); - if (status != ERROR_SUCCESS) return status; + if (status != ERROR_SUCCESS) { + return status; + } status = SetSecurityInfo(file_handle, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, nullptr, nullptr, new_dacl.get(), nullptr); - if (status != ERROR_SUCCESS) return status; + if (status != ERROR_SUCCESS) { + return status; + } return 0; } @@ -44,23 +49,20 @@ namespace { namespace nvprefs { - std::optional - undo_file_t::open_existing_file(std::filesystem::path file_path, bool &access_denied) { + std::optional undo_file_t::open_existing_file(std::filesystem::path file_path, bool &access_denied) { undo_file_t file; file.file_handle.reset(CreateFileW(file_path.c_str(), GENERIC_READ | DELETE, 0, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)); if (file.file_handle) { access_denied = false; return file; - } - else { + } else { auto last_error = GetLastError(); access_denied = (last_error != ERROR_FILE_NOT_FOUND && last_error != ERROR_PATH_NOT_FOUND); return std::nullopt; } } - std::optional - undo_file_t::create_new_file(std::filesystem::path file_path) { + std::optional undo_file_t::create_new_file(std::filesystem::path file_path) { undo_file_t file; file.file_handle.reset(CreateFileW(file_path.c_str(), GENERIC_WRITE | STANDARD_RIGHTS_ALL, 0, nullptr, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL)); @@ -70,35 +72,34 @@ namespace nvprefs { error_message("Failed to relax permissions on undo file"); } return file; - } - else { + } else { return std::nullopt; } } - bool - undo_file_t::delete_file() { - if (!file_handle) return false; + bool undo_file_t::delete_file() { + if (!file_handle) { + return false; + } - FILE_DISPOSITION_INFO delete_file_info = { TRUE }; + FILE_DISPOSITION_INFO delete_file_info = {TRUE}; if (SetFileInformationByHandle(file_handle.get(), FileDispositionInfo, &delete_file_info, sizeof(delete_file_info))) { file_handle.reset(); return true; - } - else { + } else { return false; } } - bool - undo_file_t::write_undo_data(const undo_data_t &undo_data) { - if (!file_handle) return false; + bool undo_file_t::write_undo_data(const undo_data_t &undo_data) { + if (!file_handle) { + return false; + } std::string buffer; try { buffer = undo_data.write(); - } - catch (...) { + } catch (...) { error_message("Couldn't serialize undo data"); return false; } @@ -121,9 +122,10 @@ namespace nvprefs { return true; } - std::optional - undo_file_t::read_undo_data() { - if (!file_handle) return std::nullopt; + std::optional undo_file_t::read_undo_data() { + if (!file_handle) { + return std::nullopt; + } LARGE_INTEGER file_size; if (!GetFileSizeEx(file_handle.get(), &file_size)) { @@ -146,8 +148,7 @@ namespace nvprefs { undo_data_t undo_data; try { undo_data.read(buffer); - } - catch (...) { + } catch (...) { error_message("Couldn't parse undo file"); return std::nullopt; } diff --git a/src/platform/windows/nvprefs/undo_file.h b/src/platform/windows/nvprefs/undo_file.h index 2c20a9ab..39689953 100644 --- a/src/platform/windows/nvprefs/undo_file.h +++ b/src/platform/windows/nvprefs/undo_file.h @@ -4,7 +4,7 @@ */ #pragma once -// standard library headers +// standard includes #include // local includes @@ -15,20 +15,15 @@ namespace nvprefs { class undo_file_t { public: - static std::optional - open_existing_file(std::filesystem::path file_path, bool &access_denied); + static std::optional open_existing_file(std::filesystem::path file_path, bool &access_denied); - static std::optional - create_new_file(std::filesystem::path file_path); + static std::optional create_new_file(std::filesystem::path file_path); - bool - delete_file(); + bool delete_file(); - bool - write_undo_data(const undo_data_t &undo_data); + bool write_undo_data(const undo_data_t &undo_data); - std::optional - read_undo_data(); + std::optional read_undo_data(); private: undo_file_t() = default; diff --git a/src/platform/windows/publish.cpp b/src/platform/windows/publish.cpp index 780d31eb..4c3c6f24 100644 --- a/src/platform/windows/publish.cpp +++ b/src/platform/windows/publish.cpp @@ -2,13 +2,16 @@ * @file src/platform/windows/publish.cpp * @brief Definitions for Windows mDNS service registration. */ +// platform includes +// winsock2.h must be included before windows.h +// clang-format off #include - #include - +// clang-format on #include #include +// local includes #include "misc.h" #include "src/config.h" #include "src/logging.h" @@ -17,7 +20,7 @@ #include "src/platform/common.h" #include "src/thread_safe.h" -#define _FN(x, ret, args) \ +#define _FN(x, ret, args) \ typedef ret(*x##_fn) args; \ static x##_fn x @@ -28,69 +31,69 @@ using namespace std::literals; extern "C" { #ifndef __MINGW32__ -constexpr auto DNS_REQUEST_PENDING = 9506L; -constexpr auto DNS_QUERY_REQUEST_VERSION1 = 0x1; -constexpr auto DNS_QUERY_RESULTS_VERSION1 = 0x1; + constexpr auto DNS_REQUEST_PENDING = 9506L; + constexpr auto DNS_QUERY_REQUEST_VERSION1 = 0x1; + constexpr auto DNS_QUERY_RESULTS_VERSION1 = 0x1; #endif #define SERVICE_DOMAIN "local" -constexpr auto SERVICE_TYPE_DOMAIN = SV(SERVICE_TYPE "." SERVICE_DOMAIN); + constexpr auto SERVICE_TYPE_DOMAIN = SV(SERVICE_TYPE "." SERVICE_DOMAIN); #ifndef __MINGW32__ -typedef struct _DNS_SERVICE_INSTANCE { - LPWSTR pszInstanceName; - LPWSTR pszHostName; + typedef struct _DNS_SERVICE_INSTANCE { + LPWSTR pszInstanceName; + LPWSTR pszHostName; - IP4_ADDRESS *ip4Address; - IP6_ADDRESS *ip6Address; + IP4_ADDRESS *ip4Address; + IP6_ADDRESS *ip6Address; - WORD wPort; - WORD wPriority; - WORD wWeight; + WORD wPort; + WORD wPriority; + WORD wWeight; - // Property list - DWORD dwPropertyCount; + // Property list + DWORD dwPropertyCount; - PWSTR *keys; - PWSTR *values; + PWSTR *keys; + PWSTR *values; - DWORD dwInterfaceIndex; -} DNS_SERVICE_INSTANCE, *PDNS_SERVICE_INSTANCE; + DWORD dwInterfaceIndex; + } DNS_SERVICE_INSTANCE, *PDNS_SERVICE_INSTANCE; #endif -typedef VOID WINAPI -DNS_SERVICE_REGISTER_COMPLETE( - _In_ DWORD Status, - _In_ PVOID pQueryContext, - _In_ PDNS_SERVICE_INSTANCE pInstance); + typedef VOID WINAPI + DNS_SERVICE_REGISTER_COMPLETE( + _In_ DWORD Status, + _In_ PVOID pQueryContext, + _In_ PDNS_SERVICE_INSTANCE pInstance + ); -typedef DNS_SERVICE_REGISTER_COMPLETE *PDNS_SERVICE_REGISTER_COMPLETE; + typedef DNS_SERVICE_REGISTER_COMPLETE *PDNS_SERVICE_REGISTER_COMPLETE; #ifndef __MINGW32__ -typedef struct _DNS_SERVICE_CANCEL { - PVOID reserved; -} DNS_SERVICE_CANCEL, *PDNS_SERVICE_CANCEL; + typedef struct _DNS_SERVICE_CANCEL { + PVOID reserved; + } DNS_SERVICE_CANCEL, *PDNS_SERVICE_CANCEL; -typedef struct _DNS_SERVICE_REGISTER_REQUEST { - ULONG Version; - ULONG InterfaceIndex; - PDNS_SERVICE_INSTANCE pServiceInstance; - PDNS_SERVICE_REGISTER_COMPLETE pRegisterCompletionCallback; - PVOID pQueryContext; - HANDLE hCredentials; - BOOL unicastEnabled; -} DNS_SERVICE_REGISTER_REQUEST, *PDNS_SERVICE_REGISTER_REQUEST; + typedef struct _DNS_SERVICE_REGISTER_REQUEST { + ULONG Version; + ULONG InterfaceIndex; + PDNS_SERVICE_INSTANCE pServiceInstance; + PDNS_SERVICE_REGISTER_COMPLETE pRegisterCompletionCallback; + PVOID pQueryContext; + HANDLE hCredentials; + BOOL unicastEnabled; + } DNS_SERVICE_REGISTER_REQUEST, *PDNS_SERVICE_REGISTER_REQUEST; #endif -_FN(_DnsServiceFreeInstance, VOID, (_In_ PDNS_SERVICE_INSTANCE pInstance)); -_FN(_DnsServiceDeRegister, DWORD, (_In_ PDNS_SERVICE_REGISTER_REQUEST pRequest, _Inout_opt_ PDNS_SERVICE_CANCEL pCancel)); -_FN(_DnsServiceRegister, DWORD, (_In_ PDNS_SERVICE_REGISTER_REQUEST pRequest, _Inout_opt_ PDNS_SERVICE_CANCEL pCancel)); + _FN(_DnsServiceFreeInstance, VOID, (_In_ PDNS_SERVICE_INSTANCE pInstance)); + _FN(_DnsServiceDeRegister, DWORD, (_In_ PDNS_SERVICE_REGISTER_REQUEST pRequest, _Inout_opt_ PDNS_SERVICE_CANCEL pCancel)); + _FN(_DnsServiceRegister, DWORD, (_In_ PDNS_SERVICE_REGISTER_REQUEST pRequest, _Inout_opt_ PDNS_SERVICE_CANCEL pCancel)); } /* extern "C" */ namespace platf::publish { - VOID WINAPI - register_cb(DWORD status, PVOID pQueryContext, PDNS_SERVICE_INSTANCE pInstance) { + VOID WINAPI register_cb(DWORD status, PVOID pQueryContext, PDNS_SERVICE_INSTANCE pInstance) { auto alarm = (safe::alarm_t::element_type *) pQueryContext; if (status) { @@ -100,11 +103,10 @@ namespace platf::publish { alarm->ring(pInstance); } - static int - service(bool enable, PDNS_SERVICE_INSTANCE &existing_instance) { + static int service(bool enable, PDNS_SERVICE_INSTANCE &existing_instance) { auto alarm = safe::make_alarm(); - std::wstring domain { SERVICE_TYPE_DOMAIN.data(), SERVICE_TYPE_DOMAIN.size() }; + std::wstring domain {SERVICE_TYPE_DOMAIN.data(), SERVICE_TYPE_DOMAIN.size()}; auto hostname = platf::get_host_name(); auto name = from_utf8(net::mdns_instance_name(hostname) + '.') + domain; @@ -124,8 +126,8 @@ namespace platf::publish { // Most clients aren't strictly checking TXT record compliance with RFC 1035, // but Apple's mDNS resolver does and rejects the entire answer if an invalid // TXT record is present. - PWCHAR keys[] = { nullptr }; - PWCHAR values[] = { nullptr }; + PWCHAR keys[] = {nullptr}; + PWCHAR values[] = {nullptr}; instance.dwPropertyCount = 1; instance.keys = keys; instance.values = values; @@ -144,8 +146,7 @@ namespace platf::publish { print_status("DnsServiceRegister()"sv, status); return -1; } - } - else { + } else { status = _DnsServiceDeRegister(&req, nullptr); if (status != DNS_REQUEST_PENDING) { print_status("DnsServiceDeRegister()"sv, status); @@ -159,8 +160,7 @@ namespace platf::publish { if (enable) { // Store this instance for later deregistration existing_instance = registered_instance; - } - else if (registered_instance) { + } else if (registered_instance) { // Deregistration was successful _DnsServiceFreeInstance(registered_instance); existing_instance = nullptr; @@ -196,8 +196,7 @@ namespace platf::publish { PDNS_SERVICE_INSTANCE existing_instance; }; - int - load_funcs(HMODULE handle) { + int load_funcs(HMODULE handle) { auto fg = util::fail_guard([handle]() { FreeLibrary(handle); }); @@ -215,8 +214,7 @@ namespace platf::publish { return 0; } - std::unique_ptr<::platf::deinit_t> - start() { + std::unique_ptr<::platf::deinit_t> start() { HMODULE handle = LoadLibrary("dnsapi.dll"); if (!handle || load_funcs(handle)) { diff --git a/src/process.cpp b/src/process.cpp index 3ee9d6b9..c009fda3 100644 --- a/src/process.cpp +++ b/src/process.cpp @@ -4,28 +4,29 @@ */ #define BOOST_BIND_GLOBAL_PLACEHOLDERS -#include "process.h" - +// standard includes #include #include #include #include +// lib includes #include #include #include #include #include #include - #include #include +// local includes #include "config.h" #include "crypto.h" #include "display_device.h" #include "logging.h" #include "platform/common.h" +#include "process.h" #include "system_tray.h" #include "utility.h" @@ -52,13 +53,11 @@ namespace proc { } }; - std::unique_ptr - init() { + std::unique_ptr init() { return std::make_unique(); } - void - terminate_process_group(boost::process::v1::child &proc, boost::process::v1::group &group, std::chrono::seconds exit_timeout) { + void terminate_process_group(boost::process::v1::child &proc, boost::process::v1::group &group, std::chrono::seconds exit_timeout) { if (group.valid() && platf::process_group_running((std::uintptr_t) group.native_handle())) { if (exit_timeout.count() > 0) { // Request processes in the group to exit gracefully @@ -73,16 +72,13 @@ namespace proc { if (exit_timeout.count() < 0) { BOOST_LOG(warning) << "App did not fully exit within the timeout. Terminating the app's remaining processes."sv; - } - else { + } else { BOOST_LOG(info) << "All app processes have successfully exited."sv; } - } - else { + } else { BOOST_LOG(info) << "App did not respond to a graceful termination request. Forcefully terminating the app's processes."sv; } - } - else { + } else { BOOST_LOG(info) << "No graceful exit timeout was specified for this app. Forcefully terminating the app's processes."sv; } @@ -99,8 +95,7 @@ namespace proc { } } - boost::filesystem::path - find_working_directory(const std::string &cmd, boost::process::v1::environment &env) { + boost::filesystem::path find_working_directory(const std::string &cmd, boost::process::v1::environment &env) { // Parse the raw command string into parts to get the actual command portion #ifdef _WIN32 auto parts = boost::program_options::split_winmain(cmd); @@ -135,8 +130,7 @@ namespace proc { return cmd_path.parent_path(); } - int - proc_t::execute(int app_id, std::shared_ptr launch_session) { + int proc_t::execute(int app_id, std::shared_ptr launch_session) { // Ensure starting from a clean slate terminate(); @@ -239,8 +233,7 @@ namespace proc { auto child = platf::run_command(_app.elevated, true, cmd, working_dir, _env, _pipe.get(), ec, nullptr); if (ec) { BOOST_LOG(warning) << "Couldn't spawn ["sv << cmd << "]: System: "sv << ec.message(); - } - else { + } else { child.detach(); } } @@ -248,8 +241,7 @@ namespace proc { if (_app.cmd.empty()) { BOOST_LOG(info) << "Executing [Desktop]"sv; placebo = true; - } - else { + } else { boost::filesystem::path working_dir = _app.working_dir.empty() ? find_working_directory(_app.cmd, _env) : boost::filesystem::path(_app.working_dir); @@ -268,8 +260,7 @@ namespace proc { return 0; } - int - proc_t::running() { + int proc_t::running() { #ifndef _WIN32 // On POSIX OSes, we must periodically wait for our children to avoid // them becoming zombies. This must be synchronized carefully with @@ -282,17 +273,14 @@ namespace proc { if (placebo) { return _app_id; - } - else if (_app.wait_all && _process_group && platf::process_group_running((std::uintptr_t) _process_group.native_handle())) { + } else if (_app.wait_all && _process_group && platf::process_group_running((std::uintptr_t) _process_group.native_handle())) { // The app is still running if any process in the group is still running return _app_id; - } - else if (_process.running()) { + } else if (_process.running()) { // The app is still running only if the initial process launched is still running return _app_id; - } - else if (_app.auto_detach && _process.native_exit_code() == 0 && - std::chrono::steady_clock::now() - _app_launch_time < 5s) { + } else if (_app.auto_detach && _process.native_exit_code() == 0 && + std::chrono::steady_clock::now() - _app_launch_time < 5s) { BOOST_LOG(info) << "App exited gracefully within 5 seconds of launch. Treating the app as a detached command."sv; BOOST_LOG(info) << "Adjust this behavior in the Applications tab or apps.json if this is not what you want."sv; placebo = true; @@ -308,8 +296,7 @@ namespace proc { return 0; } - void - proc_t::terminate() { + void proc_t::terminate() { std::error_code ec; placebo = false; terminate_process_group(_process, _process_group, _app.exit_timeout); @@ -358,12 +345,11 @@ namespace proc { _app_id = -1; } - const std::vector & - proc_t::get_apps() const { + const std::vector &proc_t::get_apps() const { return _apps; } - std::vector & - proc_t::get_apps() { + + std::vector &proc_t::get_apps() { return _apps; } @@ -371,8 +357,7 @@ namespace proc { // Returns image from assets directory if found there. // Returns default image if image configuration is not set. // Returns http content-type header compatible image type. - std::string - proc_t::get_app_image(int app_id) { + std::string proc_t::get_app_image(int app_id) { auto iter = std::find_if(_apps.begin(), _apps.end(), [&app_id](const auto app) { return app.id == std::to_string(app_id); }); @@ -381,8 +366,7 @@ namespace proc { return validate_app_image_path(app_image_path); } - std::string - proc_t::get_last_run_app_name() { + std::string proc_t::get_last_run_app_name() { return _app.name; } @@ -395,8 +379,7 @@ namespace proc { assert(!_process.running()); } - std::string_view::iterator - find_match(std::string_view::iterator begin, std::string_view::iterator end) { + std::string_view::iterator find_match(std::string_view::iterator begin, std::string_view::iterator end) { int stack = 0; --begin; @@ -417,8 +400,7 @@ namespace proc { return begin; } - std::string - parse_env_val(boost::process::v1::native_environment &env, const std::string_view &val_raw) { + std::string parse_env_val(boost::process::v1::native_environment &env, const std::string_view &val_raw) { auto pos = std::begin(val_raw); auto dollar = std::find(pos, std::end(val_raw), '$'); @@ -428,31 +410,33 @@ namespace proc { 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)); - auto var_name = std::string { var_begin, var_end }; + case '(': + { + ss.write(pos, (dollar - pos)); + auto var_begin = next + 1; + auto var_end = find_match(next, std::end(val_raw)); + auto var_name = std::string {var_begin, var_end}; #ifdef _WIN32 - // Windows treats environment variable names in a case-insensitive manner, - // so we look for a case-insensitive match here. This is critical for - // correctly appending to PATH on Windows. - auto itr = std::find_if(env.cbegin(), env.cend(), - [&](const auto &e) { return boost::iequals(e.get_name(), var_name); }); - if (itr != env.cend()) { - // Use an existing case-insensitive match - var_name = itr->get_name(); - } + // Windows treats environment variable names in a case-insensitive manner, + // so we look for a case-insensitive match here. This is critical for + // correctly appending to PATH on Windows. + auto itr = std::find_if(env.cbegin(), env.cend(), [&](const auto &e) { + return boost::iequals(e.get_name(), var_name); + }); + if (itr != env.cend()) { + // Use an existing case-insensitive match + var_name = itr->get_name(); + } #endif - ss << env[var_name].to_string(); + ss << env[var_name].to_string(); - pos = var_end + 1; - next = var_end; + pos = var_end + 1; + next = var_end; - break; - } + break; + } case '$': ss.write(pos, (next - pos)); pos = next + 1; @@ -461,8 +445,7 @@ namespace proc { } dollar = std::find(next, std::end(val_raw), '$'); - } - else { + } else { dollar = next; } } @@ -472,8 +455,7 @@ namespace proc { return ss.str(); } - std::string - validate_app_image_path(std::string app_image_path) { + std::string validate_app_image_path(std::string app_image_path) { if (app_image_path.empty()) { return DEFAULT_APP_IMAGE_PATH; } @@ -491,8 +473,7 @@ namespace proc { auto full_image_path = std::filesystem::path(SUNSHINE_ASSETS_DIR) / app_image_path; if (std::filesystem::exists(full_image_path)) { return full_image_path.string(); - } - else if (app_image_path == "./assets/steam.png") { + } else if (app_image_path == "./assets/steam.png") { // handle old default steam image definition return SUNSHINE_ASSETS_DIR "/steam.png"; } @@ -510,9 +491,8 @@ namespace proc { return app_image_path; } - std::optional - calculate_sha256(const std::string &filename) { - crypto::md_ctx_t ctx { EVP_MD_CTX_create() }; + std::optional calculate_sha256(const std::string &filename) { + crypto::md_ctx_t ctx {EVP_MD_CTX_create()}; if (!ctx) { return std::nullopt; } @@ -546,15 +526,13 @@ namespace proc { return ss.str(); } - uint32_t - calculate_crc32(const std::string &input) { + uint32_t calculate_crc32(const std::string &input) { boost::crc_32_type result; result.process_bytes(input.data(), input.length()); return result.checksum(); } - std::tuple - calculate_app_id(const std::string &app_name, std::string app_image_path, int index) { + std::tuple calculate_app_id(const std::string &app_name, std::string app_image_path, int index) { // Generate id by hashing name with image data if present std::vector to_hash; to_hash.push_back(app_name); @@ -563,8 +541,7 @@ namespace proc { auto file_hash = calculate_sha256(file_path); if (file_hash) { to_hash.push_back(file_hash.value()); - } - else { + } else { // Fallback to just hashing image path to_hash.push_back(file_path); } @@ -572,7 +549,9 @@ namespace proc { // Create combined strings for hash std::stringstream ss; - for_each(to_hash.begin(), to_hash.end(), [&ss](const std::string &s) { ss << s; }); + for_each(to_hash.begin(), to_hash.end(), [&ss](const std::string &s) { + ss << s; + }); auto input_no_index = ss.str(); ss << index; auto input_with_index = ss.str(); @@ -584,8 +563,7 @@ namespace proc { return std::make_tuple(id_no_index, id_with_index); } - std::optional - parse(const std::string &file_name) { + std::optional parse(const std::string &file_name) { pt::ptree tree; try { @@ -629,7 +607,8 @@ namespace proc { prep_cmds.emplace_back( std::move(do_cmd), std::move(undo_cmd), - std::move(prep_cmd.elevated)); + std::move(prep_cmd.elevated) + ); } } @@ -645,7 +624,8 @@ namespace proc { prep_cmds.emplace_back( parse_env_val(this_env, do_cmd.value_or("")), parse_env_val(this_env, undo_cmd.value_or("")), - std::move(elevated.value_or(false))); + std::move(elevated.value_or(false)) + ); } } @@ -684,14 +664,13 @@ namespace proc { ctx.elevated = elevated.value_or(false); ctx.auto_detach = auto_detach.value_or(true); ctx.wait_all = wait_all.value_or(true); - ctx.exit_timeout = std::chrono::seconds { exit_timeout.value_or(5) }; + ctx.exit_timeout = std::chrono::seconds {exit_timeout.value_or(5)}; auto possible_ids = calculate_app_id(name, ctx.image_path, i++); if (ids.count(std::get<0>(possible_ids)) == 0) { // Avoid using index to generate id if possible ctx.id = std::get<0>(possible_ids); - } - else { + } else { // Fallback to include index on collision ctx.id = std::get<1>(possible_ids); } @@ -705,18 +684,17 @@ namespace proc { } return proc::proc_t { - std::move(this_env), std::move(apps) + std::move(this_env), + std::move(apps) }; - } - catch (std::exception &e) { + } catch (std::exception &e) { BOOST_LOG(error) << e.what(); } return std::nullopt; } - void - refresh(const std::string &file_name) { + void refresh(const std::string &file_name) { auto proc_opt = proc::parse(file_name); if (proc_opt) { diff --git a/src/process.h b/src/process.h index 2b0ba8b9..f5a81e90 100644 --- a/src/process.h +++ b/src/process.h @@ -8,11 +8,14 @@ #define __kernel_entry #endif +// standard includes #include #include +// lib includes #include +// local includes #include "config.h" #include "platform/common.h" #include "rtsp.h" @@ -22,6 +25,7 @@ namespace proc { using file_t = util::safe_ptr_v2; typedef config::prep_cmd_t cmd_t; + /** * pre_cmds -- guaranteed to be executed unless any of the commands fail. * detached -- commands detached from Sunshine @@ -69,32 +73,27 @@ namespace proc { proc_t( boost::process::v1::environment &&env, - std::vector &&apps): + std::vector &&apps + ): _app_id(0), _env(std::move(env)), - _apps(std::move(apps)) {} + _apps(std::move(apps)) { + } - int - execute(int app_id, std::shared_ptr launch_session); + int execute(int app_id, std::shared_ptr launch_session); /** * @return `_app_id` if a process is running, otherwise returns `0` */ - int - running(); + int running(); ~proc_t(); - const std::vector & - get_apps() const; - std::vector & - get_apps(); - std::string - get_app_image(int app_id); - std::string - get_last_run_app_name(); - void - terminate(); + const std::vector &get_apps() const; + std::vector &get_apps(); + std::string get_app_image(int app_id); + std::string get_last_run_app_name(); + void terminate(); private: int _app_id; @@ -119,22 +118,17 @@ namespace proc { * @brief Calculate a stable id based on name and image data * @return Tuple of id calculated without index (for use if no collision) and one with. */ - std::tuple - calculate_app_id(const std::string &app_name, std::string app_image_path, int index); + std::tuple calculate_app_id(const std::string &app_name, std::string app_image_path, int index); - std::string - validate_app_image_path(std::string app_image_path); - void - refresh(const std::string &file_name); - std::optional - parse(const std::string &file_name); + std::string validate_app_image_path(std::string app_image_path); + void refresh(const std::string &file_name); + std::optional parse(const std::string &file_name); /** * @brief Initialize proc functions * @return Unique pointer to `deinit_t` to manage cleanup */ - std::unique_ptr - init(); + std::unique_ptr init(); /** * @brief Terminates all child processes in a process group. @@ -142,8 +136,7 @@ namespace proc { * @param group The group of all children in the process tree. * @param exit_timeout The timeout to wait for the process group to gracefully exit. */ - void - terminate_process_group(boost::process::v1::child &proc, boost::process::v1::group &group, std::chrono::seconds exit_timeout); + void terminate_process_group(boost::process::v1::child &proc, boost::process::v1::group &group, std::chrono::seconds exit_timeout); extern proc_t proc; } // namespace proc diff --git a/src/round_robin.h b/src/round_robin.h index d7be2593..20536955 100644 --- a/src/round_robin.h +++ b/src/round_robin.h @@ -4,6 +4,7 @@ */ #pragma once +// standard includes #include /** @@ -12,7 +13,7 @@ * @tparam T The iterator type. */ namespace round_robin_util { - template + template class it_wrap_t { public: using iterator_category = std::random_access_iterator_tag; @@ -26,8 +27,7 @@ namespace round_robin_util { typedef T iterator; typedef std::ptrdiff_t diff_t; - iterator - operator+=(diff_t step) { + iterator operator+=(diff_t step) { while (step-- > 0) { ++_this(); } @@ -35,8 +35,7 @@ namespace round_robin_util { return _this(); } - iterator - operator-=(diff_t step) { + iterator operator-=(diff_t step) { while (step-- > 0) { --_this(); } @@ -44,22 +43,19 @@ namespace round_robin_util { 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; @@ -69,19 +65,17 @@ namespace round_robin_util { return step; } - iterator - operator++() { + iterator operator++() { _this().inc(); return _this(); } - iterator - operator--() { + + iterator operator--() { _this().dec(); return _this(); } - iterator - operator++(int) { + iterator operator++(int) { iterator new_ = _this(); ++_this(); @@ -89,8 +83,7 @@ namespace round_robin_util { return new_; } - iterator - operator--(int) { + iterator operator--(int) { iterator new_ = _this(); --_this(); @@ -98,59 +91,69 @@ namespace round_robin_util { return new_; } - reference - operator*() { return *_this().get(); } - const_reference - operator*() const { return *_this().get(); } + reference operator*() { + return *_this().get(); + } - pointer - operator->() { return &*_this(); } - const_pointer - operator->() const { return &*_this(); } + const_reference operator*() const { + return *_this().get(); + } - bool - operator!=(const iterator &other) const { + pointer operator->() { + return &*_this(); + } + + const_pointer operator->() const { + return &*_this(); + } + + 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); } + bool operator==(const iterator &other) const { + return _this().eq(other); + }; + + bool operator>(const iterator &other) const { + return _this().gt(other); + } private: - iterator & - _this() { return *static_cast(this); } - const iterator & - _this() const { return *static_cast(this); } + iterator &_this() { + return *static_cast(this); + } + + const iterator &_this() const { + return *static_cast(this); + } }; - template + template class round_robin_t: public it_wrap_t> { public: using iterator = It; using pointer = V *; round_robin_t(iterator begin, iterator end): - _begin(begin), _end(end), _pos(begin) {} + _begin(begin), + _end(end), + _pos(begin) { + } - void - inc() { + void inc() { ++_pos; if (_pos == _end) { @@ -158,8 +161,7 @@ namespace round_robin_util { } } - void - dec() { + void dec() { if (_pos == _begin) { _pos = _end; } @@ -167,13 +169,11 @@ namespace round_robin_util { --_pos; } - bool - eq(const round_robin_t &other) const { + bool eq(const round_robin_t &other) const { return *_pos == *other._pos; } - pointer - get() const { + pointer get() const { return &*_pos; } @@ -184,9 +184,8 @@ namespace round_robin_util { It _pos; }; - template - round_robin_t - make_round_robin(It begin, It end) { + template + round_robin_t make_round_robin(It begin, It end) { return round_robin_t(begin, end); } } // namespace round_robin_util diff --git a/src/rswrapper.c b/src/rswrapper.c index b554bc29..953ba477 100644 --- a/src/rswrapper.c +++ b/src/rswrapper.c @@ -121,8 +121,7 @@ reed_solomon_decode_t reed_solomon_decode_fn; * @brief This initializes the RS function pointers to the best vectorized version available. * @details The streaming code will directly invoke these function pointers during encoding. */ -void -reed_solomon_init(void) { +void reed_solomon_init(void) { #if defined(__x86_64__) || defined(__i386__) if (__builtin_cpu_supports("avx512f") && __builtin_cpu_supports("avx512bw")) { reed_solomon_new_fn = reed_solomon_new_avx512; @@ -130,22 +129,19 @@ reed_solomon_init(void) { reed_solomon_encode_fn = reed_solomon_encode_avx512; reed_solomon_decode_fn = reed_solomon_decode_avx512; reed_solomon_init_avx512(); - } - else if (__builtin_cpu_supports("avx2")) { + } else if (__builtin_cpu_supports("avx2")) { reed_solomon_new_fn = reed_solomon_new_avx2; reed_solomon_release_fn = reed_solomon_release_avx2; reed_solomon_encode_fn = reed_solomon_encode_avx2; reed_solomon_decode_fn = reed_solomon_decode_avx2; reed_solomon_init_avx2(); - } - else if (__builtin_cpu_supports("ssse3")) { + } else if (__builtin_cpu_supports("ssse3")) { reed_solomon_new_fn = reed_solomon_new_ssse3; reed_solomon_release_fn = reed_solomon_release_ssse3; reed_solomon_encode_fn = reed_solomon_encode_ssse3; reed_solomon_decode_fn = reed_solomon_decode_ssse3; reed_solomon_init_ssse3(); - } - else + } else #endif { reed_solomon_new_fn = reed_solomon_new_def; diff --git a/src/rswrapper.h b/src/rswrapper.h index d9a4c01d..6a3e3878 100644 --- a/src/rswrapper.h +++ b/src/rswrapper.h @@ -5,6 +5,7 @@ */ #pragma once +// standard includes #include typedef struct _reed_solomon reed_solomon; @@ -28,5 +29,4 @@ extern reed_solomon_decode_t reed_solomon_decode_fn; * @brief This initializes the RS function pointers to the best vectorized version available. * @details The streaming code will directly invoke these function pointers during encoding. */ -void -reed_solomon_init(void); +void reed_solomon_init(void); diff --git a/src/rtsp.cpp b/src/rtsp.cpp index f6b42f78..81627d9c 100644 --- a/src/rtsp.cpp +++ b/src/rtsp.cpp @@ -9,13 +9,18 @@ extern "C" { #include } +// standard includes #include #include +#include +#include #include +// lib includes #include #include +// local includes #include "config.h" #include "globals.h" #include "input.h" @@ -26,9 +31,6 @@ extern "C" { #include "sync.h" #include "video.h" -#include -#include - namespace asio = boost::asio; using asio::ip::tcp; @@ -37,8 +39,7 @@ using asio::ip::udp; using namespace std::literals; namespace rtsp_stream { - void - free_msg(PRTSP_MESSAGE msg) { + void free_msg(PRTSP_MESSAGE msg) { freeMessage(msg); delete msg; @@ -51,18 +52,15 @@ namespace rtsp_stream { // parsing code to be able to tell encrypted from plaintext messages. static constexpr std::uint32_t ENCRYPTED_MESSAGE_TYPE_BIT = 0x80000000; - uint8_t * - payload() { + uint8_t *payload() { return (uint8_t *) (this + 1); } - std::uint32_t - payload_length() { + std::uint32_t payload_length() { return util::endian::big(typeAndLength) & ~ENCRYPTED_MESSAGE_TYPE_BIT; } - bool - is_encrypted() { + bool is_encrypted() { return !!(util::endian::big(typeAndLength) & ENCRYPTED_MESSAGE_TYPE_BIT); } @@ -83,23 +81,21 @@ namespace rtsp_stream { using msg_t = util::safe_ptr; using cmd_func_t = std::function; - void - print_msg(PRTSP_MESSAGE msg); - void - cmd_not_found(tcp::socket &sock, launch_session_t &, msg_t &&req); - void - respond(tcp::socket &sock, launch_session_t &session, POPTION_ITEM options, int statuscode, const char *status_msg, int seqn, const std::string_view &payload); + void print_msg(PRTSP_MESSAGE msg); + void cmd_not_found(tcp::socket &sock, launch_session_t &, msg_t &&req); + void respond(tcp::socket &sock, launch_session_t &session, POPTION_ITEM options, int statuscode, const char *status_msg, int seqn, const std::string_view &payload); class socket_t: public std::enable_shared_from_this { public: socket_t(boost::asio::io_context &io_context, std::function &&handle_data_fn): - handle_data_fn { std::move(handle_data_fn) }, sock { io_context } {} + handle_data_fn {std::move(handle_data_fn)}, + sock {io_context} { + } /** * @brief Queue an asynchronous read to begin the next message. */ - void - read() { + void read() { if (begin == std::end(msg_buf) || (session->rtsp_cipher && begin + sizeof(encrypted_rtsp_header_t) >= std::end(msg_buf))) { BOOST_LOG(error) << "RTSP: read(): Exceeded maximum rtsp packet size: "sv << msg_buf.size(); @@ -113,20 +109,17 @@ namespace rtsp_stream { if (session->rtsp_cipher) { // For encrypted RTSP, we will read the the entire header first - boost::asio::async_read(sock, - boost::asio::buffer(begin, sizeof(encrypted_rtsp_header_t)), - boost::bind( - &socket_t::handle_read_encrypted_header, shared_from_this(), - boost::asio::placeholders::error, - boost::asio::placeholders::bytes_transferred)); - } - else { + boost::asio::async_read(sock, boost::asio::buffer(begin, sizeof(encrypted_rtsp_header_t)), boost::bind(&socket_t::handle_read_encrypted_header, shared_from_this(), boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)); + } else { sock.async_read_some( boost::asio::buffer(begin, (std::size_t)(std::end(msg_buf) - begin)), boost::bind( - &socket_t::handle_read_plaintext, shared_from_this(), + &socket_t::handle_read_plaintext, + shared_from_this(), boost::asio::placeholders::error, - boost::asio::placeholders::bytes_transferred)); + boost::asio::placeholders::bytes_transferred + ) + ); } } @@ -136,8 +129,7 @@ namespace rtsp_stream { * @param ec The error code of the read operation. * @param bytes The number of bytes read. */ - static void - handle_read_encrypted_header(std::shared_ptr &socket, const boost::system::error_code &ec, std::size_t bytes) { + static void handle_read_encrypted_header(std::shared_ptr &socket, const boost::system::error_code &ec, std::size_t bytes) { BOOST_LOG(debug) << "handle_read_encrypted_header(): Handle read of size: "sv << bytes << " bytes"sv; auto sock_close = util::fail_guard([&socket]() { @@ -177,12 +169,7 @@ namespace rtsp_stream { sock_close.disable(); // Read the remainder of the header and full encrypted payload - boost::asio::async_read(socket->sock, - boost::asio::buffer(socket->begin + bytes, payload_length), - boost::bind( - &socket_t::handle_read_encrypted_message, socket->shared_from_this(), - boost::asio::placeholders::error, - boost::asio::placeholders::bytes_transferred)); + boost::asio::async_read(socket->sock, boost::asio::buffer(socket->begin + bytes, payload_length), boost::bind(&socket_t::handle_read_encrypted_message, socket->shared_from_this(), boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)); } /** @@ -191,8 +178,7 @@ namespace rtsp_stream { * @param ec The error code of the read operation. * @param bytes The number of bytes read. */ - static void - handle_read_encrypted_message(std::shared_ptr &socket, const boost::system::error_code &ec, std::size_t bytes) { + static void handle_read_encrypted_message(std::shared_ptr &socket, const boost::system::error_code &ec, std::size_t bytes) { BOOST_LOG(debug) << "handle_read_encrypted(): Handle read of size: "sv << bytes << " bytes"sv; auto sock_close = util::fail_guard([&socket]() { @@ -229,14 +215,14 @@ namespace rtsp_stream { iv[11] = 'R'; // RTSP std::vector plaintext; - if (socket->session->rtsp_cipher->decrypt(std::string_view { (const char *) header->tag, sizeof(header->tag) + bytes }, plaintext, &iv)) { + if (socket->session->rtsp_cipher->decrypt(std::string_view {(const char *) header->tag, sizeof(header->tag) + bytes}, plaintext, &iv)) { BOOST_LOG(error) << "Failed to verify RTSP message tag"sv; respond(socket->sock, *socket->session, nullptr, 400, "BAD REQUEST", 0, {}); return; } - msg_t req { new msg_t::element_type {} }; + msg_t req {new msg_t::element_type {}}; if (auto status = parseRtspMessage(req.get(), (char *) plaintext.data(), plaintext.size())) { BOOST_LOG(error) << "Malformed RTSP message: ["sv << status << ']'; @@ -254,8 +240,7 @@ namespace rtsp_stream { /** * @brief Queue an asynchronous read of the payload portion of a plaintext message. */ - void - read_plaintext_payload() { + void read_plaintext_payload() { if (begin == std::end(msg_buf)) { BOOST_LOG(error) << "RTSP: read_plaintext_payload(): Exceeded maximum rtsp packet size: "sv << msg_buf.size(); @@ -270,9 +255,12 @@ namespace rtsp_stream { sock.async_read_some( boost::asio::buffer(begin, (std::size_t)(std::end(msg_buf) - begin)), boost::bind( - &socket_t::handle_plaintext_payload, shared_from_this(), + &socket_t::handle_plaintext_payload, + shared_from_this(), boost::asio::placeholders::error, - boost::asio::placeholders::bytes_transferred)); + boost::asio::placeholders::bytes_transferred + ) + ); } /** @@ -281,8 +269,7 @@ namespace rtsp_stream { * @param ec The error code of the read operation. * @param bytes The number of bytes read. */ - static void - handle_plaintext_payload(std::shared_ptr &socket, const boost::system::error_code &ec, std::size_t bytes) { + static void handle_plaintext_payload(std::shared_ptr &socket, const boost::system::error_code &ec, std::size_t bytes) { BOOST_LOG(debug) << "handle_plaintext_payload(): Handle read of size: "sv << bytes << " bytes"sv; auto sock_close = util::fail_guard([&socket]() { @@ -301,7 +288,7 @@ namespace rtsp_stream { } auto end = socket->begin + bytes; - msg_t req { new msg_t::element_type {} }; + msg_t req {new msg_t::element_type {}}; if (auto status = parseRtspMessage(req.get(), socket->msg_buf.data(), (std::size_t)(end - socket->msg_buf.data()))) { BOOST_LOG(error) << "Malformed RTSP message: ["sv << status << ']'; @@ -322,8 +309,10 @@ namespace rtsp_stream { // If content_length > bytes read, then we need to store current data read, // to be appended by the next read. - std::string_view content { option->content }; - auto begin = std::find_if(std::begin(content), std::end(content), [](auto ch) { return (bool) std::isdigit(ch); }); + std::string_view content {option->content}; + auto begin = std::find_if(std::begin(content), std::end(content), [](auto ch) { + return (bool) std::isdigit(ch); + }); content_length = util::from_chars(begin, std::end(content)); break; @@ -350,8 +339,7 @@ namespace rtsp_stream { * @param ec The error code of the read operation. * @param bytes The number of bytes read. */ - static void - handle_read_plaintext(std::shared_ptr &socket, const boost::system::error_code &ec, std::size_t bytes) { + static void handle_read_plaintext(std::shared_ptr &socket, const boost::system::error_code &ec, std::size_t bytes) { BOOST_LOG(debug) << "handle_read_plaintext(): Handle read of size: "sv << bytes << " bytes"sv; if (ec) { @@ -393,8 +381,7 @@ namespace rtsp_stream { handle_plaintext_payload(socket, ec, buf_size); } - void - handle_data(msg_t &&req) { + void handle_data(msg_t &&req) { handle_data_fn(sock, *session, std::move(req)); } @@ -416,14 +403,13 @@ namespace rtsp_stream { clear(); } - int - bind(net::af_e af, std::uint16_t port, boost::system::error_code &ec) { + int bind(net::af_e af, std::uint16_t port, boost::system::error_code &ec) { acceptor.open(af == net::IPV4 ? tcp::v4() : tcp::v6(), ec); if (ec) { return -1; } - acceptor.set_option(boost::asio::socket_base::reuse_address { true }); + acceptor.set_option(boost::asio::socket_base::reuse_address {true}); acceptor.bind(tcp::endpoint(af == net::IPV4 ? tcp::v4() : tcp::v6(), port), ec); if (ec) { @@ -446,19 +432,16 @@ namespace rtsp_stream { return 0; } - template - void - iterate(std::chrono::duration timeout) { + template + void iterate(std::chrono::duration timeout) { io_context.run_one_for(timeout); } - void - handle_msg(tcp::socket &sock, launch_session_t &session, msg_t &&req) { + void handle_msg(tcp::socket &sock, launch_session_t &session, msg_t &&req) { auto func = _map_cmd_cb.find(req->message.request.command); if (func != std::end(_map_cmd_cb)) { func->second(this, sock, session, std::move(req)); - } - else { + } else { cmd_not_found(sock, session, std::move(req)); } @@ -466,8 +449,7 @@ namespace rtsp_stream { sock.shutdown(boost::asio::socket_base::shutdown_type::shutdown_both, ec); } - void - handle_accept(const boost::system::error_code &ec) { + void handle_accept(const boost::system::error_code &ec) { if (ec) { BOOST_LOG(error) << "Couldn't accept incoming connections: "sv << ec.message(); @@ -478,13 +460,12 @@ namespace rtsp_stream { auto socket = std::move(next_socket); - auto launch_session { launch_event.view(0s) }; + auto launch_session {launch_event.view(0s)}; if (launch_session) { // Associate the current RTSP session with this socket and start reading socket->session = launch_session; socket->read(); - } - else { + } else { // This can happen due to normal things like port scanning, so let's not make these visible by default BOOST_LOG(debug) << "No pending session for incoming RTSP connection"sv; @@ -502,8 +483,7 @@ namespace rtsp_stream { }); } - void - map(const std::string_view &type, cmd_func_t cb) { + void map(const std::string_view &type, cmd_func_t cb) { _map_cmd_cb.emplace(type, std::move(cb)); } @@ -513,8 +493,7 @@ namespace rtsp_stream { * the session will be discarded. * @param launch_session Streaming session information. */ - void - session_raise(std::shared_ptr launch_session) { + void session_raise(std::shared_ptr launch_session) { auto now = std::chrono::steady_clock::now(); // If a launch event is still pending, don't overwrite it. @@ -530,16 +509,14 @@ namespace rtsp_stream { * @brief Clear state for the oldest launch session. * @param launch_session_id The ID of the session to clear. */ - void - session_clear(uint32_t launch_session_id) { + void session_clear(uint32_t launch_session_id) { // We currently only support a single pending RTSP session, // so the ID should always match the one for that session. auto launch_session = launch_event.view(0s); if (launch_session) { if (launch_session->id != launch_session_id) { BOOST_LOG(error) << "Attempted to clear unexpected session: "sv << launch_session_id << " vs "sv << launch_session->id; - } - else { + } else { launch_event.pop(); } } @@ -549,8 +526,7 @@ namespace rtsp_stream { * @brief Get the number of active sessions. * @return Count of active sessions. */ - int - session_count() { + int session_count() { auto lg = _session_slots.lock(); return _session_slots->size(); } @@ -564,8 +540,7 @@ namespace rtsp_stream { * clear(false); * @examples_end */ - void - clear(bool all = true) { + void clear(bool all = true) { // if a launch event timed out --> Remove it. if (raised_timeout < std::chrono::steady_clock::now()) { auto discarded = launch_event.pop(0s); @@ -583,8 +558,7 @@ namespace rtsp_stream { stream::session::join(slot); i = _session_slots->erase(i); - } - else { + } else { i++; } } @@ -594,8 +568,7 @@ namespace rtsp_stream { * @brief Removes the provided session from the set of sessions. * @param session The session to remove. */ - void - remove(const std::shared_ptr &session) { + void remove(const std::shared_ptr &session) { auto lg = _session_slots.lock(); _session_slots->erase(session); } @@ -604,8 +577,7 @@ namespace rtsp_stream { * @brief Inserts the provided session into the set of sessions. * @param session The session to insert. */ - void - insert(const std::shared_ptr &session) { + void insert(const std::shared_ptr &session) { auto lg = _session_slots.lock(); _session_slots->emplace(session); BOOST_LOG(info) << "New streaming session started [active sessions: "sv << _session_slots->size() << ']'; @@ -619,38 +591,33 @@ namespace rtsp_stream { std::chrono::steady_clock::time_point raised_timeout; boost::asio::io_context io_context; - tcp::acceptor acceptor { io_context }; + tcp::acceptor acceptor {io_context}; std::shared_ptr next_socket; }; rtsp_server_t server {}; - void - launch_session_raise(std::shared_ptr launch_session) { + void launch_session_raise(std::shared_ptr launch_session) { server.session_raise(std::move(launch_session)); } - void - launch_session_clear(uint32_t launch_session_id) { + void launch_session_clear(uint32_t launch_session_id) { server.session_clear(launch_session_id); } - int - session_count() { + int session_count() { // Ensure session_count is up-to-date server.clear(false); return server.session_count(); } - void - terminate_sessions() { + void terminate_sessions() { server.clear(true); } - int - send(tcp::socket &sock, const std::string_view &sv) { + int send(tcp::socket &sock, const std::string_view &sv) { std::size_t bytes_send = 0; while (bytes_send != sv.size()) { @@ -666,8 +633,7 @@ namespace rtsp_stream { return 0; } - void - respond(tcp::socket &sock, launch_session_t &session, msg_t &resp) { + void respond(tcp::socket &sock, launch_session_t &session, msg_t &resp) { auto payload = std::make_pair(resp->payload, resp->payloadLength); // Restore response message for proper destruction @@ -680,11 +646,11 @@ namespace rtsp_stream { resp->payloadLength = 0; int serialized_len; - util::c_ptr raw_resp { serializeRtspMessage(resp.get(), &serialized_len) }; + util::c_ptr raw_resp {serializeRtspMessage(resp.get(), &serialized_len)}; BOOST_LOG(debug) << "---Begin Response---"sv << std::endl - << std::string_view { raw_resp.get(), (std::size_t) serialized_len } << std::endl - << std::string_view { payload.first, (std::size_t) payload.second } << std::endl + << std::string_view {raw_resp.get(), (std::size_t) serialized_len} << std::endl + << std::string_view {payload.first, (std::size_t) payload.second} << std::endl << "---End Response---"sv << std::endl; // Encrypt the RTSP message if encryption is enabled @@ -718,13 +684,12 @@ namespace rtsp_stream { header->sequenceNumber = util::endian::big(session.rtsp_iv_counter); // Encrypt the RTSP message in place - session.rtsp_cipher->encrypt(std::string_view { (const char *) header->payload(), (std::size_t) payload_length }, header->tag, &iv); + session.rtsp_cipher->encrypt(std::string_view {(const char *) header->payload(), (std::size_t) payload_length}, header->tag, &iv); // Send the full encrypted message - send(sock, std::string_view { (char *) message.data(), message.size() }); - } - else { - std::string_view tmp_resp { raw_resp.get(), (size_t) serialized_len }; + send(sock, std::string_view {(char *) message.data(), message.size()}); + } else { + std::string_view tmp_resp {raw_resp.get(), (size_t) serialized_len}; // Send the plaintext RTSP message header if (send(sock, tmp_resp)) { @@ -732,25 +697,22 @@ namespace rtsp_stream { } // Send the plaintext RTSP message payload (if present) - send(sock, std::string_view { payload.first, (std::size_t) payload.second }); + send(sock, std::string_view {payload.first, (std::size_t) payload.second}); } } - void - respond(tcp::socket &sock, launch_session_t &session, POPTION_ITEM options, int statuscode, const char *status_msg, int seqn, const std::string_view &payload) { - msg_t resp { new msg_t::element_type }; + void respond(tcp::socket &sock, launch_session_t &session, 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("RTSP/1.0"), statuscode, const_cast(status_msg), seqn, options, const_cast(payload.data()), (int) payload.size()); respond(sock, session, resp); } - void - cmd_not_found(tcp::socket &sock, launch_session_t &session, msg_t &&req) { + void cmd_not_found(tcp::socket &sock, launch_session_t &session, msg_t &&req) { respond(sock, session, nullptr, 404, "NOT FOUND", req->sequenceNumber, {}); } - void - cmd_option(rtsp_server_t *server, tcp::socket &sock, launch_session_t &session, msg_t &&req) { + void cmd_option(rtsp_server_t *server, tcp::socket &sock, launch_session_t &session, msg_t &&req) { OPTION_ITEM option {}; // I know these string literals will not be modified @@ -762,8 +724,7 @@ namespace rtsp_stream { respond(sock, session, &option, 200, "OK", req->sequenceNumber, {}); } - void - cmd_describe(rtsp_server_t *server, tcp::socket &sock, launch_session_t &session, msg_t &&req) { + void cmd_describe(rtsp_server_t *server, tcp::socket &sock, launch_session_t &session, msg_t &&req) { OPTION_ITEM option {}; // I know these string literals will not be modified @@ -846,8 +807,7 @@ namespace rtsp_stream { respond(sock, session, &option, 200, "OK", req->sequenceNumber, ss.str()); } - void - cmd_setup(rtsp_server_t *server, tcp::socket &sock, launch_session_t &session, msg_t &&req) { + void cmd_setup(rtsp_server_t *server, tcp::socket &sock, launch_session_t &session, msg_t &&req) { OPTION_ITEM options[4] {}; auto &seqn = options[0]; @@ -860,22 +820,19 @@ namespace rtsp_stream { auto seqn_str = std::to_string(req->sequenceNumber); seqn.content = const_cast(seqn_str.c_str()); - std::string_view target { req->message.request.target }; + std::string_view target {req->message.request.target}; auto begin = std::find(std::begin(target), std::end(target), '=') + 1; auto end = std::find(begin, std::end(target), '/'); - std::string_view type { begin, (size_t) std::distance(begin, end) }; + std::string_view type {begin, (size_t) std::distance(begin, end)}; std::uint16_t port; if (type == "audio"sv) { port = net::map_port(stream::AUDIO_STREAM_PORT); - } - else if (type == "video"sv) { + } else if (type == "video"sv) { port = net::map_port(stream::VIDEO_STREAM_PORT); - } - else if (type == "control"sv) { + } else if (type == "control"sv) { port = net::map_port(stream::CONTROL_PORT); - } - else { + } else { cmd_not_found(sock, session, std::move(req)); return; @@ -899,8 +856,7 @@ namespace rtsp_stream { if (type == "control"sv) { payload_option.option = const_cast("X-SS-Connect-Data"); payload_option.content = connect_data.data(); - } - else { + } else { payload_option.option = const_cast("X-SS-Ping-Payload"); payload_option.content = session.av_ping_payload.data(); } @@ -910,8 +866,7 @@ namespace rtsp_stream { respond(sock, session, &seqn, 200, "OK", req->sequenceNumber, {}); } - void - cmd_announce(rtsp_server_t *server, tcp::socket &sock, launch_session_t &session, msg_t &&req) { + void cmd_announce(rtsp_server_t *server, tcp::socket &sock, launch_session_t &session, msg_t &&req) { OPTION_ITEM option {}; // I know these string literals will not be modified @@ -920,7 +875,7 @@ namespace rtsp_stream { auto seqn_str = std::to_string(req->sequenceNumber); option.content = const_cast(seqn_str.c_str()); - std::string_view payload { req->payload, (size_t) req->payloadLength }; + std::string_view payload {req->payload, (size_t) req->payloadLength}; std::vector lines; @@ -935,7 +890,9 @@ namespace rtsp_stream { if (whitespace(*pos++)) { lines.emplace_back(begin, pos - begin - 1); - while (pos != std::end(payload) && whitespace(*pos)) { ++pos; } + while (pos != std::end(payload) && whitespace(*pos)) { + ++pos; + } begin = pos; } } @@ -948,8 +905,7 @@ namespace rtsp_stream { auto type = line.substr(0, 2); if (type == "s="sv) { client = line.substr(2); - } - else if (type == "a=") { + } else if (type == "a=") { auto pos = line.find(':'); auto name = line.substr(2, pos - 2); @@ -1016,8 +972,7 @@ namespace rtsp_stream { config.monitor.enableIntraRefresh = util::from_view(args.at("x-ss-video[0].intraRefresh"sv)); configuredBitrateKbps = util::from_view(args.at("x-ml-video.configuredBitrateKbps"sv)); - } - catch (std::out_of_range &) { + } catch (std::out_of_range &) { respond(sock, session, &option, 400, "BAD REQUEST", req->sequenceNumber, {}); return; } @@ -1028,13 +983,12 @@ namespace rtsp_stream { if (config.audio.channels == 2) { for (auto option = req->options; option != nullptr; option = option->next) { if ("Host"sv == option->option) { - std::string_view content { option->content }; + std::string_view content {option->content}; BOOST_LOG(debug) << "Found Host: "sv << content; config.audio.flags[audio::config_t::HIGH_QUALITY] = (content.find("0.0.0.0"sv) == std::string::npos); } } - } - else if (session.surround_params.length() > 3) { + } else if (session.surround_params.length() > 3) { // Channels std::uint8_t c = session.surround_params[0] - '0'; // Streams @@ -1122,8 +1076,7 @@ namespace rtsp_stream { respond(sock, session, &option, 200, "OK", req->sequenceNumber, {}); } - void - cmd_play(rtsp_server_t *server, tcp::socket &sock, launch_session_t &session, msg_t &&req) { + void cmd_play(rtsp_server_t *server, tcp::socket &sock, launch_session_t &session, msg_t &&req) { OPTION_ITEM option {}; // I know these string literals will not be modified @@ -1135,8 +1088,7 @@ namespace rtsp_stream { respond(sock, session, &option, 200, "OK", req->sequenceNumber, {}); } - void - rtpThread() { + void rtpThread() { auto shutdown_event = mail::man->event(mail::shutdown); auto broadcast_shutdown_event = mail::man->event(mail::broadcast_shutdown); @@ -1159,8 +1111,7 @@ namespace rtsp_stream { if (broadcast_shutdown_event->peek()) { server.clear(); - } - else { + } else { // cleanup all stopped sessions server.clear(false); } @@ -1169,14 +1120,13 @@ namespace rtsp_stream { server.clear(); } - void - print_msg(PRTSP_MESSAGE msg) { + void print_msg(PRTSP_MESSAGE msg) { std::string_view type = msg->type == TYPE_RESPONSE ? "RESPONSE"sv : "REQUEST"sv; - std::string_view payload { msg->payload, (size_t) msg->payloadLength }; - std::string_view protocol { msg->protocol }; + std::string_view payload {msg->payload, (size_t) msg->payloadLength}; + std::string_view protocol {msg->protocol}; auto seqnm = msg->sequenceNumber; - std::string_view messageBuffer { msg->messageBuffer }; + std::string_view messageBuffer {msg->messageBuffer}; BOOST_LOG(debug) << "type ["sv << type << ']'; BOOST_LOG(debug) << "sequence number ["sv << seqnm << ']'; @@ -1187,24 +1137,23 @@ namespace rtsp_stream { auto &resp = msg->message.response; auto statuscode = resp.statusCode; - std::string_view status { resp.statusString }; + std::string_view status {resp.statusString}; BOOST_LOG(debug) << "statuscode :: "sv << statuscode; BOOST_LOG(debug) << "status :: "sv << status; - } - else { + } else { auto &req = msg->message.request; - std::string_view command { req.command }; - std::string_view target { req.target }; + std::string_view command {req.command}; + std::string_view target {req.target}; BOOST_LOG(debug) << "command :: "sv << command; BOOST_LOG(debug) << "target :: "sv << target; } for (auto option = msg->options; option != nullptr; option = option->next) { - std::string_view content { option->content }; - std::string_view name { option->option }; + std::string_view content {option->content}; + std::string_view name {option->option}; BOOST_LOG(debug) << name << " :: "sv << content; } diff --git a/src/rtsp.h b/src/rtsp.h index 0ea78a9d..7e715677 100644 --- a/src/rtsp.h +++ b/src/rtsp.h @@ -4,8 +4,10 @@ */ #pragma once +// standard includes #include +// local includes #include "crypto.h" #include "thread_safe.h" @@ -38,30 +40,25 @@ namespace rtsp_stream { uint32_t rtsp_iv_counter; }; - void - launch_session_raise(std::shared_ptr launch_session); + void launch_session_raise(std::shared_ptr launch_session); /** * @brief Clear state for the specified launch session. * @param launch_session_id The ID of the session to clear. */ - void - launch_session_clear(uint32_t launch_session_id); + void launch_session_clear(uint32_t launch_session_id); /** * @brief Get the number of active sessions. * @return Count of active sessions. */ - int - session_count(); + int session_count(); /** * @brief Terminates all running streaming sessions. */ - void - terminate_sessions(); + void terminate_sessions(); - void - rtpThread(); + void rtpThread(); } // namespace rtsp_stream diff --git a/src/stat_trackers.cpp b/src/stat_trackers.cpp index a5daf6fe..13cc4170 100644 --- a/src/stat_trackers.cpp +++ b/src/stat_trackers.cpp @@ -1,19 +1,18 @@ -/** - * @file src/stat_trackers.cpp - * @brief Definitions for streaming statistic tracking. - */ -#include "stat_trackers.h" - -namespace stat_trackers { - - boost::format - one_digit_after_decimal() { - return boost::format("%1$.1f"); - } - - boost::format - two_digits_after_decimal() { - return boost::format("%1$.2f"); - } - -} // namespace stat_trackers +/** + * @file src/stat_trackers.cpp + * @brief Definitions for streaming statistic tracking. + */ +// local includes +#include "stat_trackers.h" + +namespace stat_trackers { + + boost::format one_digit_after_decimal() { + return boost::format("%1$.1f"); + } + + boost::format two_digits_after_decimal() { + return boost::format("%1$.2f"); + } + +} // namespace stat_trackers diff --git a/src/stat_trackers.h b/src/stat_trackers.h index 47bdca37..2e3873d4 100644 --- a/src/stat_trackers.h +++ b/src/stat_trackers.h @@ -1,56 +1,53 @@ -/** - * @file src/stat_trackers.h - * @brief Declarations for streaming statistic tracking. - */ -#pragma once - -#include -#include -#include - -#include - -namespace stat_trackers { - - boost::format - one_digit_after_decimal(); - - boost::format - two_digits_after_decimal(); - - template - class min_max_avg_tracker { - public: - using callback_function = std::function; - - void - collect_and_callback_on_interval(T stat, const callback_function &callback, std::chrono::seconds interval_in_seconds) { - if (data.calls == 0) { - data.last_callback_time = std::chrono::steady_clock::now(); - } - else if (std::chrono::steady_clock::now() > data.last_callback_time + interval_in_seconds) { - callback(data.stat_min, data.stat_max, data.stat_total / data.calls); - data = {}; - } - data.stat_min = std::min(data.stat_min, stat); - data.stat_max = std::max(data.stat_max, stat); - data.stat_total += stat; - data.calls += 1; - } - - void - reset() { - data = {}; - } - - private: - struct { - std::chrono::steady_clock::time_point last_callback_time = std::chrono::steady_clock::now(); - T stat_min = std::numeric_limits::max(); - T stat_max = std::numeric_limits::min(); - double stat_total = 0; - uint32_t calls = 0; - } data; - }; - -} // namespace stat_trackers +/** + * @file src/stat_trackers.h + * @brief Declarations for streaming statistic tracking. + */ +#pragma once + +// standard includes +#include +#include +#include + +// lib includes +#include + +namespace stat_trackers { + + boost::format one_digit_after_decimal(); + + boost::format two_digits_after_decimal(); + + template + class min_max_avg_tracker { + public: + using callback_function = std::function; + + void collect_and_callback_on_interval(T stat, const callback_function &callback, std::chrono::seconds interval_in_seconds) { + if (data.calls == 0) { + data.last_callback_time = std::chrono::steady_clock::now(); + } else if (std::chrono::steady_clock::now() > data.last_callback_time + interval_in_seconds) { + callback(data.stat_min, data.stat_max, data.stat_total / data.calls); + data = {}; + } + data.stat_min = std::min(data.stat_min, stat); + data.stat_max = std::max(data.stat_max, stat); + data.stat_total += stat; + data.calls += 1; + } + + void reset() { + data = {}; + } + + private: + struct { + std::chrono::steady_clock::time_point last_callback_time = std::chrono::steady_clock::now(); + T stat_min = std::numeric_limits::max(); + T stat_max = std::numeric_limits::min(); + double stat_total = 0; + uint32_t calls = 0; + } data; + }; + +} // namespace stat_trackers diff --git a/src/stream.cpp b/src/stream.cpp index 040a69e9..5d84c484 100644 --- a/src/stream.cpp +++ b/src/stream.cpp @@ -2,37 +2,38 @@ * @file src/stream.cpp * @brief Definitions for the streaming protocols. */ -#include "process.h" +// standard includes +#include #include #include -#include +// lib includes +#include #include -#include - extern "C" { -// clang-format off + // clang-format off #include #include "rswrapper.h" -// clang-format on + // clang-format on } +// local includes #include "config.h" #include "display_device.h" #include "globals.h" #include "input.h" #include "logging.h" #include "network.h" +#include "platform/common.h" +#include "process.h" #include "stream.h" #include "sync.h" #include "system_tray.h" #include "thread_safe.h" #include "utility.h" -#include "platform/common.h" - #define IDX_START_A 0 #define IDX_START_B 1 #define IDX_INVALIDATE_REF_FRAMES 2 @@ -84,8 +85,7 @@ namespace stream { #pragma pack(push, 1) struct video_short_frame_header_t { - uint8_t * - payload() { + uint8_t *payload() { return (uint8_t *) (this + 1); } @@ -112,11 +112,11 @@ namespace stream { static_assert( sizeof(video_short_frame_header_t) == 8, - "Short frame header must be 8 bytes"); + "Short frame header must be 8 bytes" + ); struct video_packet_raw_t { - uint8_t * - payload() { + uint8_t *payload() { return (uint8_t *) (this + 1); } @@ -140,8 +140,7 @@ namespace stream { std::uint16_t type; std::uint16_t payloadLength; - uint8_t * - payload() { + uint8_t *payload() { return (uint8_t *) (this + 1); } }; @@ -203,10 +202,10 @@ namespace stream { // seq is accepted as an arbitrary value in Moonlight std::uint32_t seq; // Monotonically increasing sequence number (used as IV for AES-GCM) - uint8_t * - payload() { + uint8_t *payload() { return (uint8_t *) (this + 1); } + // encrypted control_header_v2 and payload data follow } *control_encrypted_p; @@ -217,10 +216,10 @@ namespace stream { #pragma pack(pop) - constexpr std::size_t - round_to_pkcs7_padded(std::size_t size) { + constexpr std::size_t round_to_pkcs7_padded(std::size_t size) { return ((size + 15) / 16) * 16; } + constexpr std::size_t MAX_AUDIO_PACKET_SIZE = 1400; using audio_aes_t = std::array; @@ -231,19 +230,17 @@ namespace stream { // return bytes written on success // return -1 on error - static inline int - encode_audio(bool encrypted, const audio::buffer_t &plaintext, uint8_t *destination, crypto::aes_t &iv, crypto::cipher::cbc_t &cbc) { + static inline int encode_audio(bool encrypted, const audio::buffer_t &plaintext, uint8_t *destination, crypto::aes_t &iv, crypto::cipher::cbc_t &cbc) { // If encryption isn't enabled if (!encrypted) { std::copy(std::begin(plaintext), std::end(plaintext), destination); return plaintext.size(); } - return cbc.encrypt(std::string_view { (char *) std::begin(plaintext), plaintext.size() }, destination, &iv); + return cbc.encrypt(std::string_view {(char *) std::begin(plaintext), plaintext.size()}, destination, &iv); } - static inline void - while_starting_do_nothing(std::atomic &state) { + static inline void while_starting_do_nothing(std::atomic &state) { while (state.load(std::memory_order_acquire) == session::state_e::STARTING) { std::this_thread::sleep_for(1ms); } @@ -251,8 +248,7 @@ namespace stream { class control_server_t { public: - int - bind(net::af_e address_family, std::uint16_t port) { + int bind(net::af_e address_family, std::uint16_t port) { _host = net::host_create(address_family, _addr, port); return !(bool) _host; @@ -261,16 +257,14 @@ namespace stream { // Get session associated with address. // If none are found, try to find a session not yet claimed. (It will be marked by a port of value 0 // If none of those are found, return nullptr - session_t * - get_session(const net::peer_t peer, uint32_t connect_data); + session_t *get_session(const net::peer_t peer, uint32_t connect_data); // Circular dependency: // iterate refers to session // session refers to broadcast_ctx_t // broadcast_ctx_t refers to control_server_t // Therefore, iterate is implemented further down the source file - void - iterate(std::chrono::milliseconds timeout); + void iterate(std::chrono::milliseconds timeout); /** * @brief Call the handler for a given control stream message. @@ -279,16 +273,13 @@ namespace stream { * @param payload The payload of the message. * @param reinjected `true` if this message is being reprocessed after decryption. */ - void - call(std::uint16_t type, session_t *session, const std::string_view &payload, bool reinjected); + void call(std::uint16_t type, session_t *session, const std::string_view &payload, bool reinjected); - void - map(uint16_t type, std::function cb) { + void map(uint16_t type, std::function cb) { _map_type_cb.emplace(type, std::move(cb)); } - int - send(const std::string_view &payload, net::peer_t peer) { + int send(const std::string_view &payload, net::peer_t peer) { auto packet = enet_packet_create(payload.data(), payload.size(), ENET_PACKET_FLAG_RELIABLE); if (enet_peer_send(peer, 0, packet)) { enet_packet_destroy(packet); @@ -299,8 +290,7 @@ namespace stream { return 0; } - void - flush() { + void flush() { enet_host_flush(_host.get()); } @@ -327,8 +317,8 @@ namespace stream { asio::io_context io_context; - udp::socket video_sock { io_context }; - udp::socket audio_sock { io_context }; + udp::socket video_sock {io_context}; + udp::socket audio_sock {io_context}; control_server_t control_server; }; @@ -411,12 +401,12 @@ namespace stream { * returns empty string_view on failure * returns string_view pointing to payload data */ - template - static inline std::string_view - encode_control(session_t *session, const std::string_view &plaintext, std::array &tagged_cipher) { + template + static inline std::string_view encode_control(session_t *session, const std::string_view &plaintext, std::array &tagged_cipher) { static_assert( max_payload_size >= sizeof(control_encrypted_t) + sizeof(crypto::cipher::tag_size), - "max_payload_size >= sizeof(control_encrypted_t) + sizeof(crypto::cipher::tag_size)"); + "max_payload_size >= sizeof(control_encrypted_t) + sizeof(crypto::cipher::tag_size)" + ); if (session->config.controlProtocolType != 13) { return plaintext; @@ -438,8 +428,7 @@ namespace stream { std::copy_n((uint8_t *) &seq, sizeof(seq), std::begin(iv)); iv[10] = 'H'; // Host originated iv[11] = 'C'; // Control stream - } - else { + } else { // Nvidia's old style encryption uses a 16-byte IV iv.resize(16); @@ -460,18 +449,15 @@ namespace stream { packet->length = util::endian::little(packet_length); packet->seq = util::endian::little(seq); - return std::string_view { (char *) tagged_cipher.data(), packet_length + sizeof(control_encrypted_t) - sizeof(control_encrypted_t::seq) }; + return std::string_view {(char *) tagged_cipher.data(), packet_length + sizeof(control_encrypted_t) - sizeof(control_encrypted_t::seq)}; } - int - start_broadcast(broadcast_ctx_t &ctx); - void - end_broadcast(broadcast_ctx_t &ctx); + int start_broadcast(broadcast_ctx_t &ctx); + void end_broadcast(broadcast_ctx_t &ctx); static auto broadcast = safe::make_shared(start_broadcast, end_broadcast); - session_t * - control_server_t::get_session(const net::peer_t peer, uint32_t connect_data) { + session_t *control_server_t::get_session(const net::peer_t peer, uint32_t connect_data) { { // Fast path - look up existing session by peer auto lg = _peer_to_session.lock(); @@ -497,16 +483,13 @@ namespace stream { if (session_p->config.mlFeatureFlags & ML_FF_SESSION_ID_V1) { if (session_p->control.connect_data != connect_data) { continue; - } - else { + } else { BOOST_LOG(debug) << "Initialized new control stream session by connect data match [v2]"sv; } - } - else { + } else { if (session_p->control.expected_peer_address != peer_addr) { continue; - } - else { + } else { BOOST_LOG(debug) << "Initialized new control stream session by IP address match [v1]"sv; } } @@ -541,8 +524,7 @@ namespace stream { * @param payload The payload of the message. * @param reinjected `true` if this message is being reprocessed after decryption. */ - void - control_server_t::call(std::uint16_t type, session_t *session, const std::string_view &payload, bool reinjected) { + void control_server_t::call(std::uint16_t type, session_t *session, const std::string_view &payload, bool reinjected) { // If we are using the encrypted control stream protocol, drop any messages that come off the wire unencrypted if (session->config.controlProtocolType == 13 && !reinjected && type != packetTypes[IDX_ENCRYPTED]) { BOOST_LOG(error) << "Dropping unencrypted message on encrypted control stream: "sv << util::hex(type).to_string_view(); @@ -556,14 +538,12 @@ namespace stream { << "---data---"sv << std::endl << util::hex_vec(payload) << std::endl << "---end data---"sv; - } - else { + } else { cb->second(session, payload); } } - void - control_server_t::iterate(std::chrono::milliseconds timeout) { + void control_server_t::iterate(std::chrono::milliseconds timeout) { ENetEvent event; auto res = enet_host_service(_host.get(), &event, timeout.count()); @@ -579,14 +559,16 @@ namespace stream { 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)}; - call(type, session, payload, false); - } break; + call(type, session, payload, false); + } + break; case ENET_EVENT_TYPE_CONNECT: BOOST_LOG(info) << "CLIENT CONNECTED"sv; break; @@ -604,7 +586,9 @@ namespace stream { } namespace fec { - using rs_t = util::safe_ptr; + using rs_t = util::safe_ptr; struct fec_t { size_t data_shards; @@ -619,24 +603,20 @@ namespace stream { std::vector payload_buffers; - char * - data(size_t el) { + char *data(size_t el) { return (char *) shards_p[el]; } - char * - prefix(size_t el) { + char *prefix(size_t el) { return prefixsize ? &headers[el * prefixsize] : nullptr; } - size_t - size() const { + size_t size() const { return nr_shards; } }; - static fec_t - encode(const std::string_view &payload, size_t blocksize, size_t fecpercentage, size_t minparityshards, size_t prefixsize) { + static fec_t encode(const std::string_view &payload, size_t blocksize, size_t fecpercentage, size_t minparityshards, size_t prefixsize) { auto payload_size = payload.size(); auto pad = payload_size % blocksize != 0; @@ -658,8 +638,8 @@ namespace stream { // If we need to store a zero-padded data shard, allocate that first to // to keep the shards in order and reduce buffer fragmentation auto parity_shard_offset = pad ? 1 : 0; - util::buffer_t shards { (parity_shard_offset + parity_shards) * blocksize }; - util::buffer_t shards_p { nr_shards }; + util::buffer_t shards {(parity_shard_offset + parity_shards) * blocksize}; + util::buffer_t shards_p {nr_shards}; std::vector payload_buffers; payload_buffers.reserve(2); @@ -696,7 +676,7 @@ namespace stream { } // packets = parity_shards + data_shards - rs_t rs { reed_solomon_new(data_shards, parity_shards) }; + rs_t rs {reed_solomon_new(data_shards, parity_shards)}; reed_solomon_encode(rs.get(), shards_p.begin(), nr_shards, blocksize); } @@ -708,7 +688,7 @@ namespace stream { blocksize, prefixsize, std::move(shards), - util::buffer_t { nr_shards * prefixsize }, + util::buffer_t {nr_shards * prefixsize}, std::move(shards_p), std::move(payload_buffers), }; @@ -722,8 +702,7 @@ namespace stream { * @param data1 The first data buffer. * @param data2 The second data buffer. */ - std::vector - concat_and_insert(uint64_t insert_size, uint64_t slice_size, const std::string_view &data1, const std::string_view &data2) { + std::vector concat_and_insert(uint64_t insert_size, uint64_t slice_size, const std::string_view &data1, const std::string_view &data2) { auto data_size = data1.size() + data2.size(); auto pad = data_size % slice_size != 0; auto elements = data_size / slice_size + (pad ? 1 : 0); @@ -752,8 +731,7 @@ namespace stream { end = std::end(data2); std::copy(next, next + (slice_size - copy_len), (char *) p + copy_len + insert_size); next += slice_size - copy_len; - } - else { + } else { std::copy(next, next + slice_size, (char *) p + insert_size); next += slice_size; } @@ -762,8 +740,7 @@ namespace stream { return result; } - std::vector - replace(const std::string_view &original, const std::string_view &old, const std::string_view &_new) { + std::vector replace(const std::string_view &original, const std::string_view &old, const std::string_view &_new) { std::vector replaced; replaced.reserve(original.size() + _new.size() - old.size()); @@ -786,8 +763,7 @@ namespace stream { * @param msg The message to pass. * @return 0 on success. */ - int - send_feedback_msg(session_t *session, platf::gamepad_feedback_msg_t &msg) { + int send_feedback_msg(session_t *session, platf::gamepad_feedback_msg_t &msg) { if (!session->control.peer) { BOOST_LOG(warning) << "Couldn't send gamepad feedback data, still waiting for PING from Moonlight"sv; // Still waiting for PING from Moonlight @@ -808,13 +784,11 @@ namespace stream { plaintext.highfreq = util::endian::little(data.highfreq); BOOST_LOG(verbose) << "Rumble: "sv << msg.id << " :: "sv << util::hex(data.lowfreq).to_string_view() << " :: "sv << util::hex(data.highfreq).to_string_view(); - std::array + std::array encrypted_payload; payload = encode_control(session, util::view(plaintext), encrypted_payload); - } - else if (msg.type == platf::gamepad_feedback_e::rumble_triggers) { + } else if (msg.type == platf::gamepad_feedback_e::rumble_triggers) { control_rumble_triggers_t plaintext; plaintext.header.type = packetTypes[IDX_RUMBLE_TRIGGER_DATA]; plaintext.header.payloadLength = sizeof(plaintext) - sizeof(control_header_v2); @@ -826,13 +800,11 @@ namespace stream { plaintext.right = util::endian::little(data.right_trigger); BOOST_LOG(verbose) << "Rumble triggers: "sv << msg.id << " :: "sv << util::hex(data.left_trigger).to_string_view() << " :: "sv << util::hex(data.right_trigger).to_string_view(); - std::array + std::array encrypted_payload; payload = encode_control(session, util::view(plaintext), encrypted_payload); - } - else if (msg.type == platf::gamepad_feedback_e::set_motion_event_state) { + } else if (msg.type == platf::gamepad_feedback_e::set_motion_event_state) { control_set_motion_event_t plaintext; plaintext.header.type = packetTypes[IDX_SET_MOTION_EVENT]; plaintext.header.payloadLength = sizeof(plaintext) - sizeof(control_header_v2); @@ -844,13 +816,11 @@ namespace stream { plaintext.type = data.motion_type; BOOST_LOG(verbose) << "Motion event state: "sv << msg.id << " :: "sv << util::hex(data.report_rate).to_string_view() << " :: "sv << util::hex(data.motion_type).to_string_view(); - std::array + std::array encrypted_payload; payload = encode_control(session, util::view(plaintext), encrypted_payload); - } - else if (msg.type == platf::gamepad_feedback_e::set_rgb_led) { + } else if (msg.type == platf::gamepad_feedback_e::set_rgb_led) { control_set_rgb_led_t plaintext; plaintext.header.type = packetTypes[IDX_SET_RGB_LED]; plaintext.header.payloadLength = sizeof(plaintext) - sizeof(control_header_v2); @@ -863,13 +833,11 @@ namespace stream { plaintext.b = data.b; BOOST_LOG(verbose) << "RGB: "sv << msg.id << " :: "sv << util::hex(data.r).to_string_view() << util::hex(data.g).to_string_view() << util::hex(data.b).to_string_view(); - std::array + std::array encrypted_payload; payload = encode_control(session, util::view(plaintext), encrypted_payload); - } - else { + } else { BOOST_LOG(error) << "Unknown gamepad feedback message type"sv; return -1; } @@ -884,8 +852,7 @@ namespace stream { return 0; } - int - send_hdr_mode(session_t *session, video::hdr_info_t hdr_info) { + int send_hdr_mode(session_t *session, video::hdr_info_t hdr_info) { if (!session->control.peer) { BOOST_LOG(warning) << "Couldn't send HDR mode, still waiting for PING from Moonlight"sv; // Still waiting for PING from Moonlight @@ -899,8 +866,7 @@ namespace stream { plaintext.enabled = hdr_info->enabled; plaintext.metadata = hdr_info->metadata; - std::array + std::array encrypted_payload; auto payload = encode_control(session, util::view(plaintext), encrypted_payload); @@ -915,8 +881,7 @@ namespace stream { return 0; } - void - controlBroadcastThread(control_server_t *server) { + void controlBroadcastThread(control_server_t *server) { server->map(packetTypes[IDX_PERIODIC_PING], [](session_t *session, const std::string_view &payload) { BOOST_LOG(verbose) << "type [IDX_PERIODIC_PING]"sv; }); @@ -932,7 +897,7 @@ namespace stream { 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]; - std::chrono::milliseconds t { stats[1] }; + std::chrono::milliseconds t {stats[1]}; auto lastGoodFrame = stats[3]; @@ -968,7 +933,7 @@ namespace stream { BOOST_LOG(debug) << "type [IDX_INPUT_DATA]"sv; auto 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 }; + std::string_view tagged_cipher {payload.data() + sizeof(tagged_cipher_length), (size_t) tagged_cipher_length}; std::vector plaintext; @@ -1004,7 +969,7 @@ namespace stream { } auto tagged_cipher_length = length - 4; - std::string_view tagged_cipher { (char *) header->payload(), (size_t) tagged_cipher_length }; + std::string_view tagged_cipher {(char *) header->payload(), (size_t) tagged_cipher_length}; auto &cipher = session->control.cipher; auto &iv = session->control.incoming_iv; @@ -1021,8 +986,7 @@ namespace stream { std::copy_n((uint8_t *) &seq, sizeof(seq), std::begin(iv)); iv[10] = 'C'; // Client originated iv[11] = 'C'; // Control stream - } - else { + } else { // Nvidia's old style encryption uses a 16-byte IV iv.resize(16); @@ -1040,7 +1004,7 @@ namespace stream { } auto type = *(std::uint16_t *) plaintext.data(); - std::string_view next_payload { (char *) plaintext.data() + 4, plaintext.size() - 4 }; + std::string_view next_payload {(char *) plaintext.data() + 4, plaintext.size() - 4}; if (type == packetTypes[IDX_ENCRYPTED]) { BOOST_LOG(error) << "Bad packet type [IDX_ENCRYPTED] found"sv; @@ -1052,8 +1016,7 @@ namespace stream { if (type == packetTypes[IDX_INPUT_DATA]) { plaintext.erase(std::begin(plaintext), std::begin(plaintext) + 4); input::passthrough(session->input, std::move(plaintext)); - } - else { + } else { server->call(type, session, next_payload, true); } }); @@ -1109,8 +1072,7 @@ namespace stream { // the app terminates before they finish connecting. if (!session->control.peer) { has_session_awaiting_peer = true; - } - else { + } else { auto &feedback_queue = session->control.feedback_queue; while (feedback_queue->peek()) { auto feedback_msg = feedback_queue->pop(); @@ -1148,8 +1110,7 @@ namespace stream { plaintext.header.payloadLength = sizeof(plaintext.ec); plaintext.ec = util::endian::big(reason); - std::array + std::array encrypted_payload; auto lg = server->_sessions.lock(); @@ -1173,8 +1134,7 @@ namespace stream { server->flush(); } - void - recvThread(broadcast_ctx_t &ctx) { + void recvThread(broadcast_ctx_t &ctx) { std::map peer_to_video_session; std::map peer_to_audio_session; @@ -1200,16 +1160,14 @@ namespace stream { case socket_e::video: if (message_queue) { peer_to_video_session.emplace(session_id, message_queue); - } - else { + } else { peer_to_video_session.erase(session_id); } break; case socket_e::audio: if (message_queue) { peer_to_audio_session.emplace(session_id, message_queue); - } - else { + } else { peer_to_audio_session.erase(session_id); } break; @@ -1243,17 +1201,16 @@ namespace stream { auto it = peer_to_session.find(peer.address()); if (it != std::end(peer_to_session)) { BOOST_LOG(debug) << "RAISE: "sv << peer.address().to_string() << ':' << peer.port() << " :: " << type_str; - it->second->raise(peer, std::string { buf[buf_elem].data(), bytes }); + it->second->raise(peer, std::string {buf[buf_elem].data(), bytes}); } - } - else if (bytes >= sizeof(SS_PING)) { + } else if (bytes >= sizeof(SS_PING)) { auto ping = (PSS_PING) buf[buf_elem].data(); // For new PING packets that include a client identifier, search by payload. - auto it = peer_to_session.find(std::string { ping->payload, sizeof(ping->payload) }); + auto it = peer_to_session.find(std::string {ping->payload, sizeof(ping->payload)}); if (it != std::end(peer_to_session)) { BOOST_LOG(debug) << "RAISE: "sv << peer.address().to_string() << ':' << peer.port() << " :: " << type_str; - it->second->raise(peer, std::string { buf[buf_elem].data(), bytes }); + it->second->raise(peer, std::string {buf[buf_elem].data(), bytes}); } } }; @@ -1270,8 +1227,7 @@ namespace stream { } } - void - videoBroadcastThread(udp::socket &sock) { + void videoBroadcastThread(udp::socket &sock) { auto shutdown_event = mail::man->event(mail::broadcast_shutdown); auto packets = mail::man->queue(mail::video_packets); auto timebase = boost::posix_time::microsec_clock::universal_time(); @@ -1305,7 +1261,7 @@ namespace stream { auto session = (session_t *) packet->channel_data; auto lowseq = session->video.lowseq; - std::string_view payload { (char *) packet->data(), packet->data_size() }; + std::string_view payload {(char *) packet->data(), packet->data_size()}; std::vector payload_with_replacements; // Apply replacements on the packet payload before performing any other operations. @@ -1318,7 +1274,7 @@ namespace stream { auto frame_new = replacement._new; payload_with_replacements = replace(payload, frame_old, frame_new); - payload = { (char *) payload_with_replacements.data(), payload_with_replacements.size() }; + payload = {(char *) payload_with_replacements.data(), payload_with_replacements.size()}; } } @@ -1341,8 +1297,7 @@ namespace stream { uint16_t latency = duration_to_latency(std::chrono::steady_clock::now() - *packet->frame_timestamp); frame_header.frame_processing_latency = latency; frame_processing_latency_logger.collect_and_log(latency / 10.); - } - else { + } else { frame_header.frame_processing_latency = 0; } @@ -1351,10 +1306,9 @@ namespace stream { // Insert space for packet headers auto blocksize = session->config.packetsize + MAX_RTP_HEADER_SIZE; auto payload_blocksize = blocksize - sizeof(video_packet_raw_t); - auto payload_new = concat_and_insert(sizeof(video_packet_raw_t), payload_blocksize, - std::string_view { (char *) &frame_header, sizeof(frame_header) }, payload); + auto payload_new = concat_and_insert(sizeof(video_packet_raw_t), payload_blocksize, std::string_view {(char *) &frame_header, sizeof(frame_header)}, payload); - payload = std::string_view { (char *) payload_new.data(), payload_new.size() }; + payload = std::string_view {(char *) payload_new.data(), payload_new.size()}; // There are 2 bits for FEC block count for a maximum of 4 FEC blocks constexpr auto MAX_FEC_BLOCKS = 4; @@ -1402,8 +1356,7 @@ namespace stream { if (x == fec_blocks_needed - 1) { // The last block must extend to the end of the payload fec_blocks[x] = payload.substr(x * aligned_size); - } - else { + } else { // Earlier blocks just extend to the next block offset fec_blocks[x] = payload.substr(x * aligned_size, aligned_size); } @@ -1454,8 +1407,7 @@ namespace stream { frame_fec_latency_logger.first_point_now(); // If video encryption is enabled, we allocate space for the encryption header before each shard - auto shards = fec::encode(current_payload, blocksize, fecPercentage, session->config.minRequiredFecPackets, - session->video.cipher ? sizeof(video_packet_enc_prefix_t) : 0); + auto shards = fec::encode(current_payload, blocksize, fecPercentage, session->config.minRequiredFecPackets, session->video.cipher ? sizeof(video_packet_enc_prefix_t) : 0); frame_fec_latency_logger.second_point_now_and_log(); auto peer_address = session->video.peer.address(); @@ -1484,8 +1436,8 @@ namespace stream { inspect->packet.fecInfo = (x << 12 | - shards.data_shards << 22 | - shards.percentage << 4); + shards.data_shards << 22 | + shards.percentage << 4); inspect->rtp.header = 0x80 | FLAG_EXTENSION; inspect->rtp.sequenceNumber = util::endian::big(lowseq + x); @@ -1512,8 +1464,7 @@ namespace stream { auto *prefix = (video_packet_enc_prefix_t *) shards.prefix(x); prefix->frameNumber = packet->frame_index(); std::copy(std::begin(iv), std::end(iv), prefix->iv); - session->video.cipher->encrypt(std::string_view { (char *) inspect, (size_t) blocksize }, - prefix->tag, (uint8_t *) inspect, &iv); + session->video.cipher->encrypt(std::string_view {(char *) inspect, (size_t) blocksize}, prefix->tag, (uint8_t *) inspect, &iv); } if (x - next_shard_to_send + 1 >= send_batch_size || @@ -1576,8 +1527,7 @@ namespace stream { if (packet->is_idr()) { BOOST_LOG(verbose) << "Key Frame ["sv << packet->frame_index() << "] :: send ["sv << shards.size() << "] shards..."sv; - } - else { + } else { BOOST_LOG(verbose) << "Frame ["sv << packet->frame_index() << "] :: send ["sv << shards.size() << "] shards..."sv << std::endl; } @@ -1586,8 +1536,7 @@ namespace stream { }); session->video.lowseq = lowseq; - } - catch (const std::exception &e) { + } catch (const std::exception &e) { BOOST_LOG(error) << "Broadcast video failed "sv << e.what(); std::this_thread::sleep_for(100ms); } @@ -1596,13 +1545,12 @@ namespace stream { shutdown_event->raise(true); } - void - audioBroadcastThread(udp::socket &sock) { + void audioBroadcastThread(udp::socket &sock) { auto shutdown_event = mail::man->event(mail::broadcast_shutdown); auto packets = mail::man->queue(mail::audio_packets); audio_packet_t audio_packet; - fec::rs_t rs { reed_solomon_new(RTPA_DATA_SHARDS, RTPA_FEC_SHARDS) }; + fec::rs_t rs {reed_solomon_new(RTPA_DATA_SHARDS, RTPA_FEC_SHARDS)}; crypto::aes_t iv(16); // For unknown reasons, the RS parity matrix computed by our RS implementation @@ -1610,7 +1558,7 @@ namespace stream { // but we can simply replace it with the matrix generated by OpenFEC which // works correctly. This is possible because the data and FEC shard count is // constant and known in advance. - const unsigned char parity[] = { 0x77, 0x40, 0x38, 0x0e, 0xc7, 0xa7, 0x0d, 0x6c }; + const unsigned char parity[] = {0x77, 0x40, 0x38, 0x0e, 0xc7, 0xa7, 0x0d, 0x6c}; memcpy(rs.get()->p, parity, sizeof(parity)); audio_packet.rtp.header = 0x80; @@ -1635,8 +1583,7 @@ namespace stream { auto &shards_p = session->audio.shards_p; - auto bytes = encode_audio(session->config.encryptionFlagsEnabled & SS_ENC_AUDIO, packet_data, - shards_p[sequenceNumber % RTPA_DATA_SHARDS], iv, session->audio.cipher); + auto bytes = encode_audio(session->config.encryptionFlagsEnabled & SS_ENC_AUDIO, packet_data, shards_p[sequenceNumber % RTPA_DATA_SHARDS], iv, session->audio.cipher); if (bytes < 0) { BOOST_LOG(error) << "Couldn't encode audio packet"sv; break; @@ -1692,8 +1639,7 @@ namespace stream { BOOST_LOG(verbose) << "Audio FEC ["sv << (sequenceNumber & ~(RTPA_DATA_SHARDS - 1)) << ' ' << x << "] :: send..."sv; } } - } - catch (const std::exception &e) { + } catch (const std::exception &e) { BOOST_LOG(error) << "Broadcast audio failed "sv << e.what(); std::this_thread::sleep_for(100ms); } @@ -1702,8 +1648,7 @@ namespace stream { shutdown_event->raise(true); } - int - start_broadcast(broadcast_ctx_t &ctx) { + int start_broadcast(broadcast_ctx_t &ctx) { auto address_family = net::af_from_enum_string(config::sunshine.address_family); auto protocol = address_family == net::IPV4 ? udp::v4() : udp::v6(); auto control_port = net::map_port(CONTROL_PORT); @@ -1727,8 +1672,7 @@ namespace stream { // Set video socket send buffer size (SO_SENDBUF) to 1MB try { ctx.video_sock.set_option(boost::asio::socket_base::send_buffer_size(1024 * 1024)); - } - catch (...) { + } catch (...) { BOOST_LOG(error) << "Failed to set video socket send buffer size (SO_SENDBUF)"; } @@ -1755,17 +1699,16 @@ namespace stream { ctx.message_queue_queue = std::make_shared(30); - ctx.video_thread = std::thread { videoBroadcastThread, std::ref(ctx.video_sock) }; - ctx.audio_thread = std::thread { audioBroadcastThread, std::ref(ctx.audio_sock) }; - ctx.control_thread = std::thread { controlBroadcastThread, &ctx.control_server }; + ctx.video_thread = std::thread {videoBroadcastThread, std::ref(ctx.video_sock)}; + ctx.audio_thread = std::thread {audioBroadcastThread, std::ref(ctx.audio_sock)}; + ctx.control_thread = std::thread {controlBroadcastThread, &ctx.control_server}; - ctx.recv_thread = std::thread { recvThread, std::ref(ctx) }; + ctx.recv_thread = std::thread {recvThread, std::ref(ctx)}; return 0; } - void - end_broadcast(broadcast_ctx_t &ctx) { + void end_broadcast(broadcast_ctx_t &ctx) { auto broadcast_shutdown_event = mail::man->event(mail::broadcast_shutdown); broadcast_shutdown_event->raise(true); @@ -1799,10 +1742,9 @@ namespace stream { broadcast_shutdown_event->reset(); } - int - recv_ping(session_t *session, decltype(broadcast)::ptr_t ref, socket_e type, std::string_view expected_payload, udp::endpoint &peer, std::chrono::milliseconds timeout) { + int recv_ping(session_t *session, decltype(broadcast)::ptr_t ref, socket_e type, std::string_view expected_payload, udp::endpoint &peer, std::chrono::milliseconds timeout) { auto messages = std::make_shared(30); - av_session_id_t session_id = std::string { expected_payload }; + av_session_id_t session_id = std::string {expected_payload}; // Only allow matches on the peer address for legacy clients if (!(session->config.mlFeatureFlags & ML_FF_SESSION_ID_V1)) { @@ -1835,12 +1777,10 @@ namespace stream { if (msg.find(expected_payload) != std::string::npos) { // Match the new PING payload format BOOST_LOG(debug) << "Received ping [v2] from "sv << recv_peer.address() << ':' << recv_peer.port() << " ["sv << util::hex_vec(msg) << ']'; - } - else if (!(session->config.mlFeatureFlags & ML_FF_SESSION_ID_V1) && msg == "PING"sv) { + } else if (!(session->config.mlFeatureFlags & ML_FF_SESSION_ID_V1) && msg == "PING"sv) { // Match the legacy fixed PING payload only if the new type is not supported BOOST_LOG(debug) << "Received ping [v1] from "sv << recv_peer.address() << ':' << recv_peer.port() << " ["sv << util::hex_vec(msg) << ']'; - } - else { + } else { BOOST_LOG(debug) << "Received non-ping from "sv << recv_peer.address() << ':' << recv_peer.port() << " ["sv << util::hex_vec(msg) << ']'; current_time = std::chrono::steady_clock::now(); continue; @@ -1855,8 +1795,7 @@ namespace stream { return -1; } - void - videoThread(session_t *session) { + void videoThread(session_t *session) { auto fg = util::fail_guard([&]() { session::stop(*session); }); @@ -1871,15 +1810,13 @@ namespace stream { // Enable local prioritization and QoS tagging on video traffic if requested by the client auto address = session->video.peer.address(); - session->video.qos = platf::enable_socket_qos(ref->video_sock.native_handle(), address, - session->video.peer.port(), platf::qos_data_type_e::video, session->config.videoQosType != 0); + session->video.qos = platf::enable_socket_qos(ref->video_sock.native_handle(), address, session->video.peer.port(), platf::qos_data_type_e::video, session->config.videoQosType != 0); BOOST_LOG(debug) << "Start capturing Video"sv; video::capture(session->mail, session->config.monitor, session); } - void - audioThread(session_t *session) { + void audioThread(session_t *session) { auto fg = util::fail_guard([&]() { session::stop(*session); }); @@ -1894,8 +1831,7 @@ namespace stream { // Enable local prioritization and QoS tagging on audio traffic if requested by the client auto address = session->audio.peer.address(); - session->audio.qos = platf::enable_socket_qos(ref->audio_sock.native_handle(), address, - session->audio.peer.port(), platf::qos_data_type_e::audio, session->config.audioQosType != 0); + session->audio.qos = platf::enable_socket_qos(ref->audio_sock.native_handle(), address, session->audio.peer.port(), platf::qos_data_type_e::audio, session->config.audioQosType != 0); BOOST_LOG(debug) << "Start capturing Audio"sv; audio::capture(session->mail, session->config.audio, session); @@ -1904,13 +1840,11 @@ namespace stream { namespace session { std::atomic_uint running_sessions; - state_e - state(session_t &session) { + state_e state(session_t &session) { return session.state.load(std::memory_order_relaxed); } - void - stop(session_t &session) { + void stop(session_t &session) { while_starting_do_nothing(session.state); auto expected = state_e::RUNNING; auto already_stopping = !session.state.compare_exchange_strong(expected, state_e::STOPPING); @@ -1921,8 +1855,7 @@ namespace stream { session.shutdown_event->raise(true); } - void - join(session_t &session) { + void join(session_t &session) { // Current Nvidia drivers have a bug where NVENC can deadlock the encoder thread with hardware-accelerated // GPU scheduling enabled. If this happens, we will terminate ourselves and the service can restart. // The alternative is that Sunshine can never start another session until it's manually restarted. @@ -1953,8 +1886,7 @@ namespace stream { #if defined SUNSHINE_TRAY && SUNSHINE_TRAY >= 1 system_tray::update_tray_pausing(proc::proc.get_last_run_app_name()); #endif - } - else { + } else { display_device::revert_configuration(); } @@ -1964,8 +1896,7 @@ namespace stream { BOOST_LOG(debug) << "Session ended"sv; } - int - start(session_t &session, const std::string &addr_string) { + int start(session_t &session, const std::string &addr_string) { session.input = input::alloc(session.mail); session.broadcast_ref = broadcast.ref(); @@ -1991,8 +1922,8 @@ namespace stream { session.pingTimeout = std::chrono::steady_clock::now() + config::stream.ping_timeout; - session.audioThread = std::thread { audioThread, &session }; - session.videoThread = std::thread { videoThread, &session }; + session.audioThread = std::thread {audioThread, &session}; + session.videoThread = std::thread {videoThread, &session}; session.state.store(state_e::RUNNING, std::memory_order_relaxed); @@ -2007,8 +1938,7 @@ namespace stream { return 0; } - std::shared_ptr - alloc(config_t &config, rtsp_stream::launch_session_t &launch_session) { + std::shared_ptr alloc(config_t &config, rtsp_stream::launch_session_t &launch_session) { auto session = std::make_shared(); auto mail = std::make_shared(); @@ -2023,7 +1953,8 @@ namespace stream { session->control.hdr_queue = mail->event(mail::hdr); session->control.legacy_input_enc_iv = launch_session.iv; session->control.cipher = crypto::cipher::gcm_t { - launch_session.gcm_key, false + launch_session.gcm_key, + false }; session->video.idr_events = mail->event(mail::idr); @@ -2033,15 +1964,16 @@ namespace stream { if (config.encryptionFlagsEnabled & SS_ENC_VIDEO) { BOOST_LOG(info) << "Video encryption enabled"sv; session->video.cipher = crypto::cipher::gcm_t { - launch_session.gcm_key, false + launch_session.gcm_key, + false }; session->video.gcm_iv_counter = 0; } constexpr auto max_block_size = crypto::cipher::round_to_pkcs7_padded(2048); - util::buffer_t shards { RTPA_TOTAL_SHARDS * max_block_size }; - util::buffer_t shards_p { RTPA_TOTAL_SHARDS }; + util::buffer_t shards {RTPA_TOTAL_SHARDS * max_block_size}; + util::buffer_t shards_p {RTPA_TOTAL_SHARDS}; for (auto x = 0; x < RTPA_TOTAL_SHARDS; ++x) { shards_p[x] = (uint8_t *) &shards[x * max_block_size]; @@ -2061,7 +1993,8 @@ namespace stream { session->audio.fec_packet.fecHeader.ssrc = 0; session->audio.cipher = crypto::cipher::cbc_t { - launch_session.gcm_key, true + launch_session.gcm_key, + true }; session->audio.ping_payload = launch_session.av_ping_payload; diff --git a/src/stream.h b/src/stream.h index 95d1e2d4..53afff4f 100644 --- a/src/stream.h +++ b/src/stream.h @@ -3,10 +3,14 @@ * @brief Declarations for the streaming protocols. */ #pragma once + +// standard includes #include +// lib includes #include +// local includes #include "audio.h" #include "crypto.h" #include "video.h" @@ -17,6 +21,7 @@ namespace stream { constexpr auto AUDIO_STREAM_PORT = 11; struct session_t; + struct config_t { audio::config_t audio; video::config_t monitor; @@ -41,15 +46,10 @@ namespace stream { RUNNING, ///< The session is running }; - std::shared_ptr - alloc(config_t &config, rtsp_stream::launch_session_t &launch_session); - 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); + std::shared_ptr alloc(config_t &config, rtsp_stream::launch_session_t &launch_session); + 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 } // namespace stream diff --git a/src/sync.h b/src/sync.h index 5dbeaeb9..a9472a72 100644 --- a/src/sync.h +++ b/src/sync.h @@ -4,29 +4,29 @@ */ #pragma once +// standard includes #include #include #include namespace sync_util { - template + template class sync_t { public: using value_t = T; using mutex_t = M; - std::lock_guard - lock() { - return std::lock_guard { _lock }; + std::lock_guard lock() { + return std::lock_guard {_lock}; } - template + template sync_t(Args &&...args): - raw { std::forward(args)... } {} + raw {std::forward(args)...} { + } - sync_t & - operator=(sync_t &&other) noexcept { + sync_t &operator=(sync_t &&other) noexcept { std::lock(_lock, other._lock); raw = std::move(other.raw); @@ -37,8 +37,7 @@ namespace sync_util { return *this; } - sync_t & - operator=(sync_t &other) noexcept { + sync_t &operator=(sync_t &other) noexcept { std::lock(_lock, other._lock); raw = other.raw; @@ -49,9 +48,8 @@ namespace sync_util { return *this; } - template - sync_t & - operator=(V &&val) { + template + sync_t &operator=(V &&val) { auto lg = lock(); raw = val; @@ -59,8 +57,7 @@ namespace sync_util { return *this; } - sync_t & - operator=(const value_t &val) noexcept { + sync_t &operator=(const value_t &val) noexcept { auto lg = lock(); raw = val; @@ -68,8 +65,7 @@ namespace sync_util { return *this; } - sync_t & - operator=(value_t &&val) noexcept { + sync_t &operator=(value_t &&val) noexcept { auto lg = lock(); raw = std::move(val); @@ -77,18 +73,15 @@ namespace sync_util { return *this; } - value_t * - operator->() { + value_t *operator->() { return &raw; } - value_t & - operator*() { + value_t &operator*() { return raw; } - const value_t & - operator*() const { + const value_t &operator*() const { return raw; } diff --git a/src/system_tray.cpp b/src/system_tray.cpp index 1709d8c9..981ddfc5 100644 --- a/src/system_tray.cpp +++ b/src/system_tray.cpp @@ -31,9 +31,9 @@ #include // lib includes - #include "tray/src/tray.h" #include #include + #include // local includes #include "confighttp.h" @@ -50,43 +50,36 @@ using namespace std::literals; namespace system_tray { static std::atomic tray_initialized = false; - void - tray_open_ui_cb(struct tray_menu *item) { + void tray_open_ui_cb(struct tray_menu *item) { BOOST_LOG(info) << "Opening UI from system tray"sv; launch_ui(); } - void - tray_donate_github_cb(struct tray_menu *item) { + void tray_donate_github_cb(struct tray_menu *item) { platf::open_url("https://github.com/sponsors/LizardByte"); } - void - tray_donate_patreon_cb(struct tray_menu *item) { + void tray_donate_patreon_cb(struct tray_menu *item) { platf::open_url("https://www.patreon.com/LizardByte"); } - void - tray_donate_paypal_cb(struct tray_menu *item) { + void tray_donate_paypal_cb(struct tray_menu *item) { platf::open_url("https://www.paypal.com/paypalme/ReenigneArcher"); } - void - tray_reset_display_device_config_cb(struct tray_menu *item) { + void tray_reset_display_device_config_cb(struct tray_menu *item) { BOOST_LOG(info) << "Resetting display device config from system tray"sv; std::ignore = display_device::reset_persistence(); } - void - tray_restart_cb(struct tray_menu *item) { + void tray_restart_cb(struct tray_menu *item) { BOOST_LOG(info) << "Restarting from system tray"sv; platf::restart(); } - void - tray_quit_cb(struct tray_menu *item) { + void tray_quit_cb(struct tray_menu *item) { BOOST_LOG(info) << "Quitting from system tray"sv; #ifdef _WIN32 @@ -108,29 +101,30 @@ namespace system_tray { .menu = (struct tray_menu[]) { // todo - use boost/locale to translate menu strings - { .text = "Open Sunshine", .cb = tray_open_ui_cb }, - { .text = "-" }, - { .text = "Donate", - .submenu = - (struct tray_menu[]) { - { .text = "GitHub Sponsors", .cb = tray_donate_github_cb }, - { .text = "Patreon", .cb = tray_donate_patreon_cb }, - { .text = "PayPal", .cb = tray_donate_paypal_cb }, - { .text = nullptr } } }, - { .text = "-" }, + {.text = "Open Sunshine", .cb = tray_open_ui_cb}, + {.text = "-"}, + {.text = "Donate", + .submenu = + (struct tray_menu[]) { + {.text = "GitHub Sponsors", .cb = tray_donate_github_cb}, + {.text = "Patreon", .cb = tray_donate_patreon_cb}, + {.text = "PayPal", .cb = tray_donate_paypal_cb}, + {.text = nullptr} + }}, + {.text = "-"}, // Currently display device settings are only supported on Windows #ifdef _WIN32 - { .text = "Reset Display Device Config", .cb = tray_reset_display_device_config_cb }, + {.text = "Reset Display Device Config", .cb = tray_reset_display_device_config_cb}, #endif - { .text = "Restart", .cb = tray_restart_cb }, - { .text = "Quit", .cb = tray_quit_cb }, - { .text = nullptr } }, + {.text = "Restart", .cb = tray_restart_cb}, + {.text = "Quit", .cb = tray_quit_cb}, + {.text = nullptr} + }, .iconPathCount = 4, - .allIconPaths = { TRAY_ICON, TRAY_ICON_LOCKED, TRAY_ICON_PLAYING, TRAY_ICON_PAUSING }, + .allIconPaths = {TRAY_ICON, TRAY_ICON_LOCKED, TRAY_ICON_PLAYING, TRAY_ICON_PAUSING}, }; - int - system_tray() { + int system_tray() { #ifdef _WIN32 // If we're running as SYSTEM, Explorer.exe will not have permission to open our thread handle // to monitor for thread termination. If Explorer fails to open our thread, our tray icon @@ -139,14 +133,7 @@ namespace system_tray { { PACL old_dacl; PSECURITY_DESCRIPTOR sd; - auto error = GetSecurityInfo(GetCurrentThread(), - SE_KERNEL_OBJECT, - DACL_SECURITY_INFORMATION, - nullptr, - nullptr, - &old_dacl, - nullptr, - &sd); + auto error = GetSecurityInfo(GetCurrentThread(), SE_KERNEL_OBJECT, DACL_SECURITY_INFORMATION, nullptr, nullptr, &old_dacl, nullptr, &sd); if (error != ERROR_SUCCESS) { BOOST_LOG(warning) << "GetSecurityInfo() failed: "sv << error; return 1; @@ -186,13 +173,7 @@ namespace system_tray { LocalFree(new_dacl); }); - error = SetSecurityInfo(GetCurrentThread(), - SE_KERNEL_OBJECT, - DACL_SECURITY_INFORMATION, - nullptr, - nullptr, - new_dacl, - nullptr); + error = SetSecurityInfo(GetCurrentThread(), SE_KERNEL_OBJECT, DACL_SECURITY_INFORMATION, nullptr, nullptr, new_dacl, nullptr); if (error != ERROR_SUCCESS) { BOOST_LOG(warning) << "SetSecurityInfo() failed: "sv << error; return 1; @@ -209,8 +190,7 @@ namespace system_tray { if (tray_init(&tray) < 0) { BOOST_LOG(warning) << "Failed to create system tray"sv; return 1; - } - else { + } else { BOOST_LOG(info) << "System tray created"sv; } @@ -222,8 +202,7 @@ namespace system_tray { return 0; } - void - run_tray() { + void run_tray() { // create the system tray #if defined(__APPLE__) || defined(__MACH__) // macOS requires that UI elements be created on the main thread @@ -241,15 +220,13 @@ namespace system_tray { #endif } - int - end_tray() { + int end_tray() { tray_initialized = false; tray_exit(); return 0; } - void - update_tray_playing(std::string app_name) { + void update_tray_playing(std::string app_name) { if (!tray_initialized) { return; } @@ -270,8 +247,7 @@ namespace system_tray { tray_update(&tray); } - void - update_tray_pausing(std::string app_name) { + void update_tray_pausing(std::string app_name) { if (!tray_initialized) { return; } @@ -292,8 +268,7 @@ namespace system_tray { tray_update(&tray); } - void - update_tray_stopped(std::string app_name) { + void update_tray_stopped(std::string app_name) { if (!tray_initialized) { return; } @@ -314,8 +289,7 @@ namespace system_tray { tray_update(&tray); } - void - update_tray_require_pin() { + void update_tray_require_pin() { if (!tray_initialized) { return; } diff --git a/src/system_tray.h b/src/system_tray.h index 8d716c65..00a17f92 100644 --- a/src/system_tray.h +++ b/src/system_tray.h @@ -12,97 +12,83 @@ namespace system_tray { * @brief Callback for opening the UI from the system tray. * @param item The tray menu item. */ - void - tray_open_ui_cb(struct tray_menu *item); + void tray_open_ui_cb(struct tray_menu *item); /** * @brief Callback for opening GitHub Sponsors from the system tray. * @param item The tray menu item. */ - void - tray_donate_github_cb(struct tray_menu *item); + void tray_donate_github_cb(struct tray_menu *item); /** * @brief Callback for opening Patreon from the system tray. * @param item The tray menu item. */ - void - tray_donate_patreon_cb(struct tray_menu *item); + void tray_donate_patreon_cb(struct tray_menu *item); /** * @brief Callback for opening PayPal donation from the system tray. * @param item The tray menu item. */ - void - tray_donate_paypal_cb(struct tray_menu *item); + void tray_donate_paypal_cb(struct tray_menu *item); /** * @brief Callback for resetting display device configuration. * @param item The tray menu item. */ - void - tray_reset_display_device_config_cb(struct tray_menu *item); + void tray_reset_display_device_config_cb(struct tray_menu *item); /** * @brief Callback for restarting Sunshine from the system tray. * @param item The tray menu item. */ - void - tray_restart_cb(struct tray_menu *item); + void tray_restart_cb(struct tray_menu *item); /** * @brief Callback for exiting Sunshine from the system tray. * @param item The tray menu item. */ - void - tray_quit_cb(struct tray_menu *item); + void tray_quit_cb(struct tray_menu *item); /** * @brief Create the system tray. * @details This function has an endless loop, so it should be run in a separate thread. * @return 1 if the system tray failed to create, otherwise 0 once the tray has been terminated. */ - int - system_tray(); + int system_tray(); /** * @brief Run the system tray with platform specific options. * @todo macOS requires that UI elements be created on the main thread, so the system tray is not currently implemented for macOS. */ - int - run_tray(); + int run_tray(); /** * @brief Exit the system tray. * @return 0 after exiting the system tray. */ - int - end_tray(); + int end_tray(); /** * @brief Sets the tray icon in playing mode and spawns the appropriate notification * @param app_name The started application name */ - void - update_tray_playing(std::string app_name); + void update_tray_playing(std::string app_name); /** * @brief Sets the tray icon in pausing mode (stream stopped but app running) and spawns the appropriate notification * @param app_name The paused application name */ - void - update_tray_pausing(std::string app_name); + void update_tray_pausing(std::string app_name); /** * @brief Sets the tray icon in stopped mode (app and stream stopped) and spawns the appropriate notification * @param app_name The started application name */ - void - update_tray_stopped(std::string app_name); + void update_tray_stopped(std::string app_name); /** * @brief Spawns a notification for PIN Pairing. Clicking it opens the PIN Web UI Page */ - void - update_tray_require_pin(); + void update_tray_require_pin(); } // namespace system_tray diff --git a/src/task_pool.h b/src/task_pool.h index 3d6b1b5d..081be24d 100644 --- a/src/task_pool.h +++ b/src/task_pool.h @@ -4,6 +4,7 @@ */ #pragma once +// standard includes #include #include #include @@ -14,8 +15,10 @@ #include #include +// local includes #include "move_by_copy.h" #include "utility.h" + namespace task_pool_util { class _ImplBase { @@ -24,20 +27,19 @@ namespace task_pool_util { inline virtual ~_ImplBase() = default; - virtual void - run() = 0; + virtual void run() = 0; }; - template + template class _Impl: public _ImplBase { Function _func; public: _Impl(Function &&f): - _func(std::forward(f)) {} + _func(std::forward(f)) { + } - void - run() override { + void run() override { _func(); } }; @@ -49,14 +51,16 @@ namespace task_pool_util { typedef std::chrono::steady_clock::time_point __time_point; - template + template class timer_task_t { public: task_id_t task_id; std::future future; timer_task_t(task_id_t task_id, std::future &future): - task_id { task_id }, future { std::move(future) } {} + task_id {task_id}, + future {std::move(future)} { + } }; protected: @@ -66,20 +70,21 @@ namespace task_pool_util { public: TaskPool() = default; - TaskPool(TaskPool &&other) noexcept: - _tasks { std::move(other._tasks) }, _timer_tasks { std::move(other._timer_tasks) } {} - TaskPool & - operator=(TaskPool &&other) noexcept { + TaskPool(TaskPool &&other) noexcept: + _tasks {std::move(other._tasks)}, + _timer_tasks {std::move(other._timer_tasks)} { + } + + TaskPool &operator=(TaskPool &&other) noexcept { std::swap(_tasks, other._tasks); std::swap(_timer_tasks, other._timer_tasks); return *this; } - template - auto - push(Function &&newTask, Args &&...args) { + template + auto push(Function &&newTask, Args &&...args) { static_assert(std::is_invocable_v, "arguments don't match the function"); using __return = std::invoke_result_t; @@ -99,8 +104,7 @@ namespace task_pool_util { return future; } - void - pushDelayed(std::pair<__time_point, __task> &&task) { + void pushDelayed(std::pair<__time_point, __task> &&task) { std::lock_guard lg(_task_mutex); auto it = _timer_tasks.cbegin(); @@ -116,9 +120,8 @@ namespace task_pool_util { /** * @return An id to potentially delay the task. */ - template - auto - pushDelayed(Function &&newTask, std::chrono::duration duration, Args &&...args) { + template + auto pushDelayed(Function &&newTask, std::chrono::duration duration, Args &&...args) { static_assert(std::is_invocable_v, "arguments don't match the function"); using __return = std::invoke_result_t; @@ -127,8 +130,7 @@ namespace task_pool_util { __time_point time_point; if constexpr (std::is_floating_point_v) { time_point = std::chrono::steady_clock::now() + std::chrono::duration_cast(duration); - } - else { + } else { time_point = std::chrono::steady_clock::now() + duration; } @@ -143,18 +145,17 @@ namespace task_pool_util { task_id_t task_id = &*runnable; - pushDelayed(std::pair { time_point, std::move(runnable) }); + pushDelayed(std::pair {time_point, std::move(runnable)}); - return timer_task_t<__return> { task_id, future }; + return timer_task_t<__return> {task_id, future}; } /** * @param task_id The id of the task to delay. * @param duration The delay before executing the task. */ - template - void - delay(task_id_t task_id, std::chrono::duration duration) { + template + void delay(task_id_t task_id, std::chrono::duration duration) { std::lock_guard lg(_task_mutex); auto it = _timer_tasks.begin(); @@ -184,8 +185,7 @@ namespace task_pool_util { } } - bool - cancel(task_id_t task_id) { + bool cancel(task_id_t task_id) { std::lock_guard lg(_task_mutex); auto it = _timer_tasks.begin(); @@ -202,11 +202,12 @@ namespace task_pool_util { return false; } - std::optional> - pop(task_id_t task_id) { + std::optional> pop(task_id_t task_id) { std::lock_guard lg(_task_mutex); - auto pos = std::find_if(std::begin(_timer_tasks), std::end(_timer_tasks), [&task_id](const auto &t) { return t.second.get() == task_id; }); + auto pos = std::find_if(std::begin(_timer_tasks), std::end(_timer_tasks), [&task_id](const auto &t) { + return t.second.get() == task_id; + }); if (pos == std::end(_timer_tasks)) { return std::nullopt; @@ -215,8 +216,7 @@ namespace task_pool_util { return std::move(*pos); } - std::optional<__task> - pop() { + std::optional<__task> pop() { std::lock_guard lg(_task_mutex); if (!_tasks.empty()) { @@ -234,15 +234,13 @@ namespace task_pool_util { return std::nullopt; } - bool - ready() { + bool ready() { std::lock_guard lg(_task_mutex); return !_tasks.empty() || (!_timer_tasks.empty() && std::get<0>(_timer_tasks.back()) <= std::chrono::steady_clock::now()); } - std::optional<__time_point> - next() { + std::optional<__time_point> next() { std::lock_guard lg(_task_mutex); if (_timer_tasks.empty()) { @@ -253,9 +251,8 @@ namespace task_pool_util { } private: - template - std::unique_ptr<_ImplBase> - toRunnable(Function &&f) { + template + std::unique_ptr<_ImplBase> toRunnable(Function &&f) { return std::make_unique<_Impl>(std::forward(f)); } }; diff --git a/src/thread_pool.h b/src/thread_pool.h index 25b3d267..e6377b2a 100644 --- a/src/thread_pool.h +++ b/src/thread_pool.h @@ -4,9 +4,12 @@ */ #pragma once -#include "task_pool.h" +// standard includes #include +// local includes +#include "task_pool.h" + namespace thread_pool_util { /** * Allow threads to execute unhindered while keeping full control over the threads. @@ -25,25 +28,28 @@ namespace thread_pool_util { public: ThreadPool(): - _continue { false } {} + _continue {false} { + } explicit ThreadPool(int threads): - _thread(threads), _continue { true } { + _thread(threads), + _continue {true} { for (auto &t : _thread) { t = std::thread(&ThreadPool::_main, this); } } ~ThreadPool() noexcept { - if (!_continue) return; + if (!_continue) { + return; + } stop(); join(); } - template - auto - push(Function &&newTask, Args &&...args) { + template + auto push(Function &&newTask, Args &&...args) { std::lock_guard lg(_lock); auto future = TaskPool::push(std::forward(newTask), std::forward(args)...); @@ -51,16 +57,14 @@ namespace thread_pool_util { return future; } - void - pushDelayed(std::pair<__time_point, __task> &&task) { + void pushDelayed(std::pair<__time_point, __task> &&task) { std::lock_guard lg(_lock); TaskPool::pushDelayed(std::move(task)); } - template - auto - pushDelayed(Function &&newTask, std::chrono::duration duration, Args &&...args) { + template + auto pushDelayed(Function &&newTask, std::chrono::duration duration, Args &&...args) { std::lock_guard lg(_lock); auto future = TaskPool::pushDelayed(std::forward(newTask), duration, std::forward(args)...); @@ -69,8 +73,7 @@ namespace thread_pool_util { return future; } - void - start(int threads) { + void start(int threads) { _continue = true; _thread.resize(threads); @@ -80,29 +83,25 @@ namespace thread_pool_util { } } - void - stop() { + void stop() { std::lock_guard lg(_lock); _continue = false; _cv.notify_all(); } - void - join() { + void join() { for (auto &t : _thread) { t.join(); } } public: - void - _main() { + void _main() { while (_continue) { if (auto task = this->pop()) { (*task)->run(); - } - else { + } else { std::unique_lock uniq_lock(_lock); if (ready()) { @@ -115,8 +114,7 @@ namespace thread_pool_util { if (auto tp = next()) { _cv.wait_until(uniq_lock, *tp); - } - else { + } else { _cv.wait(uniq_lock); } } diff --git a/src/thread_safe.h b/src/thread_safe.h index 9d8ce8a2..9657de06 100644 --- a/src/thread_safe.h +++ b/src/thread_safe.h @@ -4,6 +4,7 @@ */ #pragma once +// standard includes #include #include #include @@ -12,36 +13,34 @@ #include #include +// local includes #include "utility.h" namespace safe { - template + template class event_t { public: using status_t = util::optional_t; - template - void - raise(Args &&...args) { - std::lock_guard lg { _lock }; + template + void raise(Args &&...args) { + std::lock_guard lg {_lock}; if (!_continue) { return; } if constexpr (std::is_same_v, status_t>) { _status = std::make_optional(std::forward(args)...); - } - else { - _status = status_t { std::forward(args)... }; + } else { + _status = status_t {std::forward(args)...}; } _cv.notify_all(); } // pop and view should not be used interchangeably - status_t - pop() { - std::unique_lock ul { _lock }; + status_t pop() { + std::unique_lock ul {_lock}; if (!_continue) { return util::false_v; @@ -61,10 +60,9 @@ namespace safe { } // pop and view should not be used interchangeably - template - status_t - pop(std::chrono::duration delay) { - std::unique_lock ul { _lock }; + template + status_t pop(std::chrono::duration delay) { + std::unique_lock ul {_lock}; if (!_continue) { return util::false_v; @@ -82,9 +80,8 @@ namespace safe { } // pop and view should not be used interchangeably - status_t - view() { - std::unique_lock ul { _lock }; + status_t view() { + std::unique_lock ul {_lock}; if (!_continue) { return util::false_v; @@ -102,10 +99,9 @@ namespace safe { } // pop and view should not be used interchangeably - template - status_t - view(std::chrono::duration delay) { - std::unique_lock ul { _lock }; + template + status_t view(std::chrono::duration delay) { + std::unique_lock ul {_lock}; if (!_continue) { return util::false_v; @@ -120,49 +116,44 @@ namespace safe { return _status; } - bool - peek() { + bool peek() { return _continue && (bool) _status; } - void - stop() { - std::lock_guard lg { _lock }; + void stop() { + std::lock_guard lg {_lock}; _continue = false; _cv.notify_all(); } - void - reset() { - std::lock_guard lg { _lock }; + void reset() { + std::lock_guard lg {_lock}; _continue = true; _status = util::false_v; } - [[nodiscard]] bool - running() const { + [[nodiscard]] bool running() const { return _continue; } private: - bool _continue { true }; - status_t _status { util::false_v }; + bool _continue {true}; + status_t _status {util::false_v}; std::condition_variable _cv; std::mutex _lock; }; - template + template class alarm_raw_t { public: using status_t = util::optional_t; - void - ring(const status_t &status) { + void ring(const status_t &status) { std::lock_guard lg(_lock); _status = status; @@ -170,8 +161,7 @@ namespace safe { _cv.notify_one(); } - void - ring(status_t &&status) { + void ring(status_t &&status) { std::lock_guard lg(_lock); _status = std::move(status); @@ -179,63 +169,66 @@ namespace safe { _cv.notify_one(); } - template - auto - wait_for(const std::chrono::duration &rel_time) { + template + auto wait_for(const std::chrono::duration &rel_time) { std::unique_lock ul(_lock); - return _cv.wait_for(ul, rel_time, [this]() { return _rang; }); + return _cv.wait_for(ul, rel_time, [this]() { + return _rang; + }); } - template - auto - wait_for(const std::chrono::duration &rel_time, Pred &&pred) { + template + auto wait_for(const std::chrono::duration &rel_time, Pred &&pred) { std::unique_lock ul(_lock); - return _cv.wait_for(ul, rel_time, [this, &pred]() { return _rang || pred(); }); + return _cv.wait_for(ul, rel_time, [this, &pred]() { + return _rang || pred(); + }); } - template - auto - wait_until(const std::chrono::duration &rel_time) { + template + auto wait_until(const std::chrono::duration &rel_time) { std::unique_lock ul(_lock); - return _cv.wait_until(ul, rel_time, [this]() { return _rang; }); + return _cv.wait_until(ul, rel_time, [this]() { + return _rang; + }); } - template - auto - wait_until(const std::chrono::duration &rel_time, Pred &&pred) { + template + auto wait_until(const std::chrono::duration &rel_time, Pred &&pred) { std::unique_lock ul(_lock); - return _cv.wait_until(ul, rel_time, [this, &pred]() { return _rang || pred(); }); + return _cv.wait_until(ul, rel_time, [this, &pred]() { + return _rang || pred(); + }); } - auto - wait() { + auto wait() { std::unique_lock ul(_lock); - _cv.wait(ul, [this]() { return _rang; }); + _cv.wait(ul, [this]() { + return _rang; + }); } - template - auto - wait(Pred &&pred) { + template + auto wait(Pred &&pred) { std::unique_lock ul(_lock); - _cv.wait(ul, [this, &pred]() { return _rang || pred(); }); + _cv.wait(ul, [this, &pred]() { + return _rang || pred(); + }); } - const status_t & - status() const { + const status_t &status() const { return _status; } - status_t & - status() { + status_t &status() { return _status; } - void - reset() { + void reset() { _status = status_t {}; _rang = false; } @@ -244,31 +237,30 @@ namespace safe { std::mutex _lock; std::condition_variable _cv; - status_t _status { util::false_v }; - bool _rang { false }; + status_t _status {util::false_v}; + bool _rang {false}; }; - template + template using alarm_t = std::shared_ptr>; - template - alarm_t - make_alarm() { + template + alarm_t make_alarm() { return std::make_shared>(); } - template + template class queue_t { public: using status_t = util::optional_t; queue_t(std::uint32_t max_elements = 32): - _max_elements { max_elements } {} + _max_elements {max_elements} { + } - template - void - raise(Args &&...args) { - std::lock_guard ul { _lock }; + template + void raise(Args &&...args) { + std::lock_guard ul {_lock}; if (!_continue) { return; @@ -283,15 +275,13 @@ namespace safe { _cv.notify_all(); } - bool - peek() { + bool peek() { return _continue && !_queue.empty(); } - template - status_t - pop(std::chrono::duration delay) { - std::unique_lock ul { _lock }; + template + status_t pop(std::chrono::duration delay) { + std::unique_lock ul {_lock}; if (!_continue) { return util::false_v; @@ -309,9 +299,8 @@ namespace safe { return val; } - status_t - pop() { - std::unique_lock ul { _lock }; + status_t pop() { + std::unique_lock ul {_lock}; if (!_continue) { return util::false_v; @@ -331,27 +320,24 @@ namespace safe { return val; } - std::vector & - unsafe() { + std::vector &unsafe() { return _queue; } - void - stop() { - std::lock_guard lg { _lock }; + void stop() { + std::lock_guard lg {_lock}; _continue = false; _cv.notify_all(); } - [[nodiscard]] bool - running() const { + [[nodiscard]] bool running() const { return _continue; } private: - bool _continue { true }; + bool _continue {true}; std::uint32_t _max_elements; std::mutex _lock; @@ -360,7 +346,7 @@ namespace safe { std::vector _queue; }; - template + template class shared_t { public: using element_type = T; @@ -372,17 +358,20 @@ namespace safe { shared_t *owner; ptr_t(): - owner { nullptr } {} + owner {nullptr} { + } + explicit ptr_t(shared_t *owner): - owner { owner } {} + owner {owner} { + } ptr_t(ptr_t &&ptr) noexcept: - owner { ptr.owner } { + owner {ptr.owner} { ptr.owner = nullptr; } ptr_t(const ptr_t &ptr) noexcept: - owner { ptr.owner } { + owner {ptr.owner} { if (!owner) { return; } @@ -391,8 +380,7 @@ namespace safe { tmp.owner = nullptr; } - ptr_t & - operator=(const ptr_t &ptr) noexcept { + ptr_t &operator=(const ptr_t &ptr) noexcept { if (!ptr.owner) { release(); @@ -402,8 +390,7 @@ namespace safe { return *this = std::move(*ptr.owner->ref()); } - ptr_t & - operator=(ptr_t &&ptr) noexcept { + ptr_t &operator=(ptr_t &&ptr) noexcept { if (owner) { release(); } @@ -423,9 +410,8 @@ namespace safe { return owner != nullptr; } - void - release() { - std::lock_guard lg { owner->_lock }; + void release() { + std::lock_guard lg {owner->_lock}; if (!--owner->_count) { owner->_destruct(*get()); @@ -435,34 +421,34 @@ namespace safe { owner = nullptr; } - element_type * - get() const { + element_type *get() const { return reinterpret_cast(owner->_object_buf.data()); } - element_type * - operator->() { + element_type *operator->() { return reinterpret_cast(owner->_object_buf.data()); } }; - template + template shared_t(FC &&fc, FD &&fd): - _construct { std::forward(fc) }, _destruct { std::forward(fd) } {} - [[nodiscard]] ptr_t - ref() { - std::lock_guard lg { _lock }; + _construct {std::forward(fc)}, + _destruct {std::forward(fd)} { + } + + [[nodiscard]] ptr_t ref() { + std::lock_guard lg {_lock}; if (!_count) { new (_object_buf.data()) element_type; if (_construct(*reinterpret_cast(_object_buf.data()))) { - return ptr_t { nullptr }; + return ptr_t {nullptr}; } } ++_count; - return ptr_t { this }; + return ptr_t {this}; } private: @@ -475,11 +461,11 @@ namespace safe { std::mutex _lock; }; - template - auto - make_shared(F_Construct &&fc, F_Destruct &&fd) { + template + auto make_shared(F_Construct &&fc, F_Destruct &&fd) { return shared_t { - std::forward(fc), std::forward(fd) + std::forward(fc), + std::forward(fd) }; } @@ -488,14 +474,16 @@ namespace safe { class mail_raw_t; using mail_t = std::shared_ptr; - void - cleanup(mail_raw_t *); - template + void cleanup(mail_raw_t *); + + template class post_t: public T { public: - template + template post_t(mail_t mail, Args &&...args): - T(std::forward(args)...), mail { std::move(mail) } {} + T(std::forward(args)...), + mail {std::move(mail)} { + } mail_t mail; @@ -504,24 +492,22 @@ namespace safe { } }; - template - inline auto - lock(const std::weak_ptr &wp) { + template + inline auto lock(const std::weak_ptr &wp) { return std::reinterpret_pointer_cast(wp.lock()); } class mail_raw_t: public std::enable_shared_from_this { public: - template + template using event_t = std::shared_ptr>>; - template + template using queue_t = std::shared_ptr>>; - template - event_t - event(const std::string_view &id) { - std::lock_guard lg { mutex }; + template + event_t event(const std::string_view &id) { + std::lock_guard lg {mutex}; auto it = id_to_post.find(id); if (it != std::end(id_to_post)) { @@ -529,15 +515,14 @@ namespace safe { } auto post = std::make_shared::element_type>(shared_from_this()); - id_to_post.emplace(std::pair> { std::string { id }, post }); + id_to_post.emplace(std::pair> {std::string {id}, post}); return post; } - template - queue_t - queue(const std::string_view &id) { - std::lock_guard lg { mutex }; + template + queue_t queue(const std::string_view &id) { + std::lock_guard lg {mutex}; auto it = id_to_post.find(id); if (it != std::end(id_to_post)) { @@ -545,14 +530,13 @@ namespace safe { } auto post = std::make_shared::element_type>(shared_from_this(), 32); - id_to_post.emplace(std::pair> { std::string { id }, post }); + id_to_post.emplace(std::pair> {std::string {id}, post}); return post; } - void - cleanup() { - std::lock_guard lg { mutex }; + void cleanup() { + std::lock_guard lg {mutex}; for (auto it = std::begin(id_to_post); it != std::end(id_to_post); ++it) { auto &weak = it->second; @@ -570,8 +554,7 @@ namespace safe { std::map, std::less<>> id_to_post; }; - inline void - cleanup(mail_raw_t *mail) { + inline void cleanup(mail_raw_t *mail) { mail->cleanup(); } } // namespace safe diff --git a/src/upnp.cpp b/src/upnp.cpp index 747e604a..14103ad5 100644 --- a/src/upnp.cpp +++ b/src/upnp.cpp @@ -2,9 +2,11 @@ * @file src/upnp.cpp * @brief Definitions for UPnP port mapping. */ +// lib includes #include #include +// local includes #include "config.h" #include "confighttp.h" #include "globals.h" @@ -30,8 +32,7 @@ namespace upnp { std::string description; }; - static std::string_view - status_string(int status) { + static std::string_view status_string(int status) { switch (status) { case 0: return "No IGD device found"sv; @@ -50,8 +51,7 @@ namespace upnp { * This function is a wrapper around UPNP_GetValidIGD() that returns the status code. There is a pre-processor * check to determine which version of the function to call based on the version of the MiniUPnPc library. */ - int - UPNP_GetValidIGDStatus(device_t &device, urls_t *urls, IGDdatas *data, std::array &lan_addr) { + int UPNP_GetValidIGDStatus(device_t &device, urls_t *urls, IGDdatas *data, std::array &lan_addr) { #if (MINIUPNPC_API_VERSION >= 18) return UPNP_GetValidIGD(device.get(), &urls->el, data, lan_addr.data(), lan_addr.size(), nullptr, 0); #else @@ -71,21 +71,21 @@ namespace upnp { auto wm_http = std::to_string(net::map_port(confighttp::PORT_HTTPS)); mappings.assign({ - { { rtsp, rtsp, "TCP"s }, "Sunshine - RTSP"s }, - { { video, video, "UDP"s }, "Sunshine - Video"s }, - { { audio, audio, "UDP"s }, "Sunshine - Audio"s }, - { { control, control, "UDP"s }, "Sunshine - Control"s }, - { { gs_http, gs_http, "TCP"s }, "Sunshine - Client HTTP"s }, - { { gs_https, gs_https, "TCP"s }, "Sunshine - Client HTTPS"s }, + {{rtsp, rtsp, "TCP"s}, "Sunshine - RTSP"s}, + {{video, video, "UDP"s}, "Sunshine - Video"s}, + {{audio, audio, "UDP"s}, "Sunshine - Audio"s}, + {{control, control, "UDP"s}, "Sunshine - Control"s}, + {{gs_http, gs_http, "TCP"s}, "Sunshine - Client HTTP"s}, + {{gs_https, gs_https, "TCP"s}, "Sunshine - Client HTTPS"s}, }); // Only map port for the Web Manager if it is configured to accept connection from WAN if (net::from_enum_string(config::nvhttp.origin_web_ui_allowed) > net::LAN) { - mappings.emplace_back(mapping_t { { wm_http, wm_http, "TCP"s }, "Sunshine - Web UI"s }); + mappings.emplace_back(mapping_t {{wm_http, wm_http, "TCP"s}, "Sunshine - Web UI"s}); } // Start the mapping thread - upnp_thread = std::thread { &deinit_t::upnp_thread_proc, this }; + upnp_thread = std::thread {&deinit_t::upnp_thread_proc, this}; } ~deinit_t() { @@ -97,10 +97,9 @@ namespace upnp { * @details Not many IGDs support this feature, so we perform error logging with debug level. * @return `true` if the pinholes were opened successfully. */ - bool - create_ipv6_pinholes() { + bool create_ipv6_pinholes() { int err; - device_t device { upnpDiscover(2000, nullptr, nullptr, 0, IPv6, 2, &err) }; + device_t device {upnpDiscover(2000, nullptr, nullptr, 0, IPv6, 2, &err)}; if (!device || err) { BOOST_LOG(debug) << "Couldn't discover any IPv6 UPNP devices"sv; return false; @@ -136,35 +135,24 @@ namespace upnp { char uniqueId[8]; // Open a pinhole for the LAN port, since there will be no WAN->LAN port mapping on IPv6 - err = UPNP_AddPinhole(urls->controlURL_6FC, - data.IPv6FC.servicetype, - "", "0", - lan_addr.data(), - mapping.port.lan.c_str(), - mapping.port.proto.c_str(), - mapping_period.c_str(), - uniqueId); + err = UPNP_AddPinhole(urls->controlURL_6FC, data.IPv6FC.servicetype, "", "0", lan_addr.data(), mapping.port.lan.c_str(), mapping.port.proto.c_str(), mapping_period.c_str(), uniqueId); if (err == UPNPCOMMAND_SUCCESS) { BOOST_LOG(debug) << "Successfully created pinhole for "sv << mapping.port.proto << ' ' << mapping.port.lan; - } - else { + } else { BOOST_LOG(debug) << "Failed to create pinhole for "sv << mapping.port.proto << ' ' << mapping.port.lan << ": "sv << err; } } return err == 0; - } - else { + } else { BOOST_LOG(debug) << "IPv6 pinholes are not allowed by the IGD"sv; return false; } - } - else { + } else { BOOST_LOG(debug) << "Failed to get IPv6 firewall status: "sv << err; return false; } - } - else { + } else { BOOST_LOG(debug) << "IPv6 Firewall Control is not supported by the IGD"sv; return false; } @@ -178,8 +166,7 @@ namespace upnp { * @param mapping Information about port to map * @return `true` on success. */ - bool - map_upnp_port(const IGDdatas &data, const urls_t &urls, const std::string &lan_addr, const mapping_t &mapping) { + bool map_upnp_port(const IGDdatas &data, const urls_t &urls, const std::string &lan_addr, const mapping_t &mapping) { char intClient[16]; char intPort[6]; char desc[80]; @@ -197,11 +184,15 @@ namespace upnp { mapping.port.proto.c_str(), nullptr, // Out params - intClient, intPort, desc, enabled, leaseDuration); + intClient, + intPort, + desc, + enabled, + leaseDuration + ); if (err == 714) { // NoSuchEntryInArray BOOST_LOG(debug) << "Mapping entry not found for "sv << mapping.port.wan; - } - else if (err == UPNPCOMMAND_SUCCESS) { + } else if (err == UPNPCOMMAND_SUCCESS) { // Some routers change the description, so we can't check that here if (!std::strcmp(intClient, lan_addr.c_str())) { if (std::atoi(leaseDuration) == 0) { @@ -209,12 +200,10 @@ namespace upnp { // It's a static mapping, so we're done here return true; - } - else { + } else { BOOST_LOG(debug) << "Mapping entry found for "sv << mapping.port.wan << " ("sv << leaseDuration << " seconds remaining)"sv; } - } - else { + } else { BOOST_LOG(warning) << "UPnP conflict detected with: "sv << intClient; // Some UPnP IGDs won't let unauthenticated clients delete other conflicting port mappings @@ -224,14 +213,14 @@ namespace upnp { data.first.servicetype, mapping.port.wan.c_str(), mapping.port.proto.c_str(), - nullptr); + nullptr + ); if (err) { BOOST_LOG(error) << "Unable to delete conflicting UPnP port mapping: "sv << err; return false; } } - } - else { + } else { BOOST_LOG(error) << "UPNP_GetSpecificPortMappingEntry() failed: "sv << err; // If we get a strange error from the router, we'll assume it's some old broken IGDv1 @@ -252,7 +241,8 @@ namespace upnp { mapping.description.c_str(), mapping.port.proto.c_str(), nullptr, - mapping_period.c_str()); + mapping_period.c_str() + ); if (err != UPNPCOMMAND_SUCCESS && !indefinite) { // This may be an old/broken IGD that doesn't like non-static mappings. @@ -266,7 +256,8 @@ namespace upnp { mapping.description.c_str(), mapping.port.proto.c_str(), nullptr, - "0"); + "0" + ); } if (err) { @@ -283,20 +274,19 @@ namespace upnp { * @param urls urls_t from UPNP_GetValidIGD() * @param data IGDdatas from UPNP_GetValidIGD() */ - void - unmap_all_upnp_ports(const urls_t &urls, const IGDdatas &data) { + void unmap_all_upnp_ports(const urls_t &urls, const IGDdatas &data) { for (auto it = std::begin(mappings); it != std::end(mappings); ++it) { auto status = UPNP_DeletePortMapping( urls->controlURL, data.first.servicetype, it->port.wan.c_str(), it->port.proto.c_str(), - nullptr); + nullptr + ); if (status && status != 714) { // NoSuchEntryInArray BOOST_LOG(warning) << "Failed to unmap "sv << it->port.proto << ' ' << it->port.lan << ": "sv << status; - } - else { + } else { BOOST_LOG(debug) << "Successfully unmapped "sv << it->port.proto << ' ' << it->port.lan; } } @@ -305,8 +295,7 @@ namespace upnp { /** * @brief Maintains UPnP port forwarding rules */ - void - upnp_thread_proc() { + void upnp_thread_proc() { auto shutdown_event = mail::man->event(mail::shutdown); bool mapped = false; IGDdatas data; @@ -317,7 +306,7 @@ namespace upnp { // WAN IP address changes, or various other conditions. do { int err = 0; - device_t device { upnpDiscover(2000, nullptr, nullptr, 0, IPv4, 2, &err) }; + device_t device {upnpDiscover(2000, nullptr, nullptr, 0, IPv4, 2, &err)}; if (!device || err) { BOOST_LOG(warning) << "Couldn't discover any IPv4 UPNP devices"sv; mapped = false; @@ -338,7 +327,7 @@ namespace upnp { continue; } - std::string lan_addr_str { lan_addr.data() }; + std::string lan_addr_str {lan_addr.data()}; BOOST_LOG(debug) << "Found valid IGD device: "sv << urls->rootdescURL; @@ -373,8 +362,7 @@ namespace upnp { std::thread upnp_thread; }; - std::unique_ptr - start() { + std::unique_ptr start() { if (!config::sunshine.flags[config::flag::UPNP]) { return nullptr; } diff --git a/src/upnp.h b/src/upnp.h index 6f32f2e9..cc7eb32a 100644 --- a/src/upnp.h +++ b/src/upnp.h @@ -4,8 +4,10 @@ */ #pragma once +// lib includes #include +// local includes #include "platform/common.h" /** @@ -36,9 +38,7 @@ namespace upnp { * @retval 2 A valid IGD has been found but it reported as not connected. * @retval 3 An UPnP device has been found but was not recognized as an IGD. */ - int - UPNP_GetValidIGDStatus(device_t &device, urls_t *urls, IGDdatas *data, std::array &lan_addr); + int UPNP_GetValidIGDStatus(device_t &device, urls_t *urls, IGDdatas *data, std::array &lan_addr); - [[nodiscard]] std::unique_ptr - start(); + [[nodiscard]] std::unique_ptr start(); } // namespace upnp diff --git a/src/utility.h b/src/utility.h index e9adefd8..9721a1a2 100644 --- a/src/utility.h +++ b/src/utility.h @@ -4,6 +4,7 @@ */ #pragma once +// standard includes #include #include #include @@ -17,100 +18,104 @@ #include #define KITTY_WHILE_LOOP(x, y, z) \ - { \ - x; \ - while (y) z \ + { \ + x; \ + while (y) z \ } -template +template struct argument_type; -template +template struct argument_type { typedef U type; }; -#define KITTY_USING_MOVE_T(move_t, t, init_val, z) \ - class move_t { \ - public: \ - using element_type = typename argument_type::type; \ - \ - move_t(): el { init_val } {} \ - template \ - move_t(Args &&...args): el { std::forward(args)... } {} \ - move_t(const move_t &) = delete; \ - \ - move_t(move_t &&other) noexcept: el { std::move(other.el) } { \ - other.el = element_type { init_val }; \ - } \ - \ - move_t & \ - operator=(const move_t &) = delete; \ - \ - move_t & \ - operator=(move_t &&other) { \ - std::swap(el, other.el); \ - return *this; \ - } \ - element_type * \ - operator->() { return ⪙ } \ - const element_type * \ - operator->() const { return ⪙ } \ - \ - inline element_type \ - release() { \ - element_type val = std::move(el); \ - el = element_type { init_val }; \ - return val; \ - } \ - \ - ~move_t() z \ - \ - element_type el; \ +#define KITTY_USING_MOVE_T(move_t, t, init_val, z) \ + class move_t { \ + public: \ + using element_type = typename argument_type::type; \ +\ + move_t(): \ + el {init_val} { \ + } \ + template \ + move_t(Args &&...args): \ + el {std::forward(args)...} { \ + } \ + move_t(const move_t &) = delete; \ +\ + move_t(move_t &&other) noexcept: \ + el {std::move(other.el)} { \ + other.el = element_type {init_val}; \ + } \ +\ + move_t &operator=(const move_t &) = delete; \ +\ + move_t &operator=(move_t &&other) { \ + std::swap(el, other.el); \ + return *this; \ + } \ + element_type *operator->() { \ + return ⪙ \ + } \ + const element_type *operator->() const { \ + return ⪙ \ + } \ +\ + inline element_type release() { \ + element_type val = std::move(el); \ + el = element_type {init_val}; \ + return val; \ + } \ +\ + ~move_t() z \ +\ + element_type el; \ } -#define KITTY_DECL_CONSTR(x) \ - x(x &&) noexcept = default; \ +#define KITTY_DECL_CONSTR(x) \ + x(x &&) noexcept = default; \ x &operator=(x &&) noexcept = default; \ x(); #define KITTY_DEFAULT_CONSTR_MOVE(x) \ - x(x &&) noexcept = default; \ + x(x &&) noexcept = default; \ x &operator=(x &&) noexcept = default; #define KITTY_DEFAULT_CONSTR_MOVE_THROW(x) \ - x(x &&) = default; \ - x &operator=(x &&) = default; \ + x(x &&) = default; \ + x &operator=(x &&) = default; \ x() = default; -#define KITTY_DEFAULT_CONSTR(x) \ - KITTY_DEFAULT_CONSTR_MOVE(x) \ +#define KITTY_DEFAULT_CONSTR(x) \ + KITTY_DEFAULT_CONSTR_MOVE(x) \ x(const x &) noexcept = default; \ x &operator=(const x &) = default; -#define TUPLE_2D(a, b, expr) \ - decltype(expr) a##_##b = expr; \ +#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; \ +#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; \ +#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; \ +#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_EL(a, b, expr) \ +#define TUPLE_EL(a, b, expr) \ decltype(expr) a##_ = expr; \ auto &a = std::get(a##_) @@ -119,46 +124,49 @@ struct argument_type { namespace util { - template