From 30496c79ab0ff22fd600824825a3a60244e5c476 Mon Sep 17 00:00:00 2001 From: Jacek Szafarkiewicz Date: Fri, 4 Sep 2020 15:51:20 +0200 Subject: [PATCH 01/35] Make systemd script cleaner --- sunshine.service.in | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/sunshine.service.in b/sunshine.service.in index 802442df..be1459ca 100644 --- a/sunshine.service.in +++ b/sunshine.service.in @@ -2,12 +2,8 @@ Description=Sunshine Gamestream Server for Moonlight [Service] -WorkingDirectory=/home/%u -Environment="DISPLAY=:0" -Type=simple -# wait for Xorg -ExecStartPre=/bin/sh -c 'while ! pgrep Xorg; do sleep 2; done' -ExecStart=@SUNSHINE_EXECUTABLE_PATH@ +ExecStartPre=/bin/sh -c "test -d %E/sunshine || cp -r '@SUNSHINE_ASSETS_DIR@' '%E/sunshine'" +ExecStart=@SUNSHINE_EXECUTABLE_PATH@ %E/sunshine/sunshine.conf [Install] -WantedBy=default.target +WantedBy=graphical-session.target From 7b39b93bb2cf84f82d37a93e2ac595541c19f759 Mon Sep 17 00:00:00 2001 From: Doomsdayrs <38189170+Doomsdayrs@users.noreply.github.com> Date: Wed, 18 Nov 2020 15:31:13 -0500 Subject: [PATCH 02/35] Update README.md - Cleaned things up a bit more - Simplified instructions - make instruction has `-j ${nproc}` to ease up the word count - Use $USER instead of {username} --- README.md | 55 ++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 34 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index fc414370..b71d37ec 100644 --- a/README.md +++ b/README.md @@ -12,39 +12,52 @@ Sunshine is a Gamestream host for Moonlight ### Requirements: Ubuntu 20.04: - - sudo apt install cmake libssl-dev libavdevice-dev libboost-thread-dev libboost-filesystem-dev libboost-log-dev libpulse-dev libopus-dev libxtst-dev libx11-dev libxfixes-dev libevdev-dev libxcb1-dev libxcb-shm0-dev libxcb-xfixes0-dev +Install the following +``` +sudo apt install cmake libssl-dev libavdevice-dev libboost-thread-dev libboost-filesystem-dev libboost-log-dev libpulse-dev libopus-dev libxtst-dev libx11-dev libxfixes-dev libevdev-dev libxcb1-dev libxcb-shm0-dev libxcb-xfixes0-dev +``` ### Compilation: - `git clone https://github.com/loki-47-6F-64/sunshine.git --recurse-submodules` - `cd sunshine && mkdir build && cd build` - `cmake ..` -- `make`: It is suggested to use the `-j C#` flags with this command, `C#` being the number of cores your PC has +- `make -j ${nproc}` ### Setup: sunshine needs access to uinput to create mouse and gamepad events: -- Add user to group 'input': "usermod -a -G input username -- Create a file: "/etc/udev/rules.d/85-sunshine-input.rules" -- The contents of the file is as follows: - KERNEL=="uinput", GROUP="input", mode="0660" -- assets/sunshine.conf is an example configuration file. Modify it as you see fit and use it by running: "sunshine path/to/sunshine.conf" -- path/to/build/dir/sunshine.service is used to start sunshine in the background: - - `cp sunshine.service $HOME/.config/systemd/user/` - - Modify $HOME/.config/systemd/user/sunshine.conf to point to the sunshine executable - - `systemctl --user start sunshine` +- Add user to group 'input': + `usermod -a -G input $USER` +- Create udev rules: + - Run the following command: + `nano /etc/udev/rules.d/85-sunshine-input.rules` + - Input the following contents: + `KERNEL=="uinput", GROUP="input", mode="0660"` + - Save the file and exit + 1. `CTRL+X` to start exit + 2. `Y` to save modifications +- `assets/sunshine.conf` is an example configuration file. Modify it as you see fit then use it by running: + `sunshine path/to/sunshine.conf` +- Configure autostart service + `path/to/build/dir/sunshine.service` is used to start sunshine in the background. To add it, do the following: + 1. Modify `sunshine.conf` to point to the sunshine executable if nessecary + 2. Copy it to the users systemd, `cp sunshine.service ~/.config/systemd/user/` + 3. Starting + - Onetime: + `systemctl --user start sunshine` + - Always on boot: + `systemctl --user enable sunshine` -- assets/apps.json is an [example](README.md#application-list) of a list of applications that are started just before running a stream +- `assets/apps.json` is an [example](README.md#application-list) of a list of applications that are started just before running a stream ### Trouleshooting: - * If you get "Could not create Sunshine Gamepad: Permission Denied", ensure you are part of the group "input": - * groups - * If Sunshine sends audio from the microphone instead of the speaker, try the following steps: - * pacmd list-sources | grep "name:" - * Copy the name to the configuration option "audio_sink" - * restart sunshine - - +- If you get "Could not create Sunshine Gamepad: Permission Denied", ensure you are part of the group "input": + - `groups $USER` + +- If Sunshine sends audio from the microphone instead of the speaker, try the following steps: + 1. pacmd list-sources | grep "name:" + 2. Copy the name to the configuration option "audio_sink" + 3. restart sunshine ## Windows 10 From 9c41972b65c4d36b10091b325227182118500f3c Mon Sep 17 00:00:00 2001 From: loki Date: Mon, 10 May 2021 01:02:24 +0200 Subject: [PATCH 03/35] minor modification for sunshine.service --- pre-compiled | 2 +- sunshine.service.in | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/pre-compiled b/pre-compiled index afd9a9bb..bbf56474 160000 --- a/pre-compiled +++ b/pre-compiled @@ -1 +1 @@ -Subproject commit afd9a9bbfc6ee1a064b0c1f9210bc20b2170c416 +Subproject commit bbf5647402859945dee541a300b22702a2ee9271 diff --git a/sunshine.service.in b/sunshine.service.in index be1459ca..c0c38289 100644 --- a/sunshine.service.in +++ b/sunshine.service.in @@ -2,8 +2,7 @@ Description=Sunshine Gamestream Server for Moonlight [Service] -ExecStartPre=/bin/sh -c "test -d %E/sunshine || cp -r '@SUNSHINE_ASSETS_DIR@' '%E/sunshine'" -ExecStart=@SUNSHINE_EXECUTABLE_PATH@ %E/sunshine/sunshine.conf +ExecStart=@SUNSHINE_EXECUTABLE_PATH@ [Install] WantedBy=graphical-session.target From 1ba8da978012f67bdad5468a9cfdbf47562f5969 Mon Sep 17 00:00:00 2001 From: loki Date: Mon, 10 May 2021 01:07:24 +0200 Subject: [PATCH 04/35] Fix error in README file --- README.md | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index fb3c15ce..102a1207 100644 --- a/README.md +++ b/README.md @@ -36,13 +36,12 @@ sunshine needs access to uinput to create mouse and gamepad events: - Save the file and exit 1. `CTRL+X` to start exit 2. `Y` to save modifications -- `assets/sunshine.conf` is an example configuration file. Modify it as you see fit then use it by running: +- `assets/sunshine.conf` is an example configuration file. Modify it as you see fit, then use it by running: `sunshine path/to/sunshine.conf` - Configure autostart service - `path/to/build/dir/sunshine.service` is used to start sunshine in the background. To add it, do the following: - 1. Modify `sunshine.conf` to point to the sunshine executable if nessecary - 2. Copy it to the users systemd, `cp sunshine.service ~/.config/systemd/user/` - 3. Starting + `path/to/build/dir/sunshine.service` is used to start sunshine in the background. To use it, do the following: + 1. Copy it to the users systemd, `cp sunshine.service ~/.config/systemd/user/` + 2. Starting - Onetime: `systemctl --user start sunshine` - Always on boot: @@ -118,8 +117,8 @@ sunshine needs access to uinput to create mouse and gamepad events: "cmd":"command to open app", "prep-cmd":[ { - "do":"somecommand", - "undo":"undothatcommand" + "do":"somecommand", + "undo":"undothatcommand" } ] } From 25309f21ee48824139fd8679a739bf5fb2d34904 Mon Sep 17 00:00:00 2001 From: loki Date: Mon, 10 May 2021 01:31:08 +0200 Subject: [PATCH 05/35] Fix appveyor build --- appveyor.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index e0115dab..d737446a 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -9,7 +9,7 @@ environment: install: - sh: sudo apt update --ignore-missing - - sh: sudo apt install -y build-essential cmake libssl-dev libavdevice-dev libboost-thread-dev libboost-filesystem-dev libboost-log-dev libpulse-dev libopus-dev libxtst-dev libx11-dev libxrandr-dev libxfixes-dev libevdev-dev libxcb1-dev libxcb-shm0-dev libxcb-xfixes0-dev + - sh: sudo apt install -y build-essential fakeroot gcc-10 g++-10 cmake libssl-dev libavdevice-dev libboost-thread-dev libboost-filesystem-dev libboost-log-dev libpulse-dev libopus-dev libxtst-dev libx11-dev libxrandr-dev libxfixes-dev libevdev-dev libxcb1-dev libxcb-shm0-dev libxcb-xfixes0-dev - cmd: C:\msys64\usr\bin\bash -lc "pacman --needed --noconfirm -S mingw-w64-x86_64-openssl mingw-w64-x86_64-cmake mingw-w64-x86_64-toolchain mingw-w64-x86_64-opus mingw-w64-x86_64-x265 mingw-w64-x86_64-boost git yasm nasm diffutils make" before_build: @@ -20,7 +20,7 @@ before_build: build_script: - cmd: set OLDPATH=%PATH% - cmd: set PATH=C:\msys64\mingw64\bin - - sh: cmake -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DSUNSHINE_EXECUTABLE_PATH=sunshine -DSUNSHINE_ASSETS_DIR=/etc/sunshine .. + - sh: cmake -DCMAKE_C_COMPILER=gcc-10 -DCMAKE_CXX_COMPILER=g++-10 -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DSUNSHINE_EXECUTABLE_PATH=sunshine -DSUNSHINE_ASSETS_DIR=/etc/sunshine .. - cmd: cmake -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DSUNSHINE_ASSETS_DIR=assets -G "MinGW Makefiles" .. - sh: make -j$(nproc) - cmd: mingw32-make -j2 From b97c902d104479c11d1a92b5a70be54b5d657090 Mon Sep 17 00:00:00 2001 From: Loki Date: Mon, 10 May 2021 15:04:41 +0200 Subject: [PATCH 06/35] print absolute mouse movement packets --- CMakeLists.txt | 9 +++++++++ moonlight-common-c | 2 +- sunshine/input.cpp | 31 +++++++++++++++++++++++-------- 3 files changed, 33 insertions(+), 9 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 55d04342..0dcbe718 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,6 +5,14 @@ project(Sunshine) set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}) add_subdirectory(Simple-Web-Server) + +if(WIN32) + # Ugly hack to compile with #include + add_compile_definitions( + QOS_FLOWID=UINT32 + PQOS_FLOWID=UINT32* + QOS_NON_ADAPTIVE_FLOW=2) +endif() add_subdirectory(moonlight-common-c/enet) find_package(Threads REQUIRED) @@ -70,6 +78,7 @@ if(WIN32) libstdc++.a libwinpthread.a libssp.a + Qwave winmm ksuser wsock32 diff --git a/moonlight-common-c b/moonlight-common-c index cfeb0ffd..5d09d43b 160000 --- a/moonlight-common-c +++ b/moonlight-common-c @@ -1 +1 @@ -Subproject commit cfeb0ffd90992ee0fa4b6b4a179652e3e2786873 +Subproject commit 5d09d43b0866d9fa30cc1741f9e10de5420deaad diff --git a/sunshine/input.cpp b/sunshine/input.cpp index 9a41dd03..a9aee2c4 100644 --- a/sunshine/input.cpp +++ b/sunshine/input.cpp @@ -2,6 +2,8 @@ // Created by loki on 6/20/19. // +// define uint32_t for +#include extern "C" { #include } @@ -86,12 +88,22 @@ struct input_t { using namespace std::literals; -void print(PNV_MOUSE_MOVE_PACKET packet) { +void print(PNV_REL_MOUSE_MOVE_PACKET packet) { BOOST_LOG(debug) - << "--begin mouse move packet--"sv << std::endl + << "--begin relative mouse move packet--"sv << std::endl << "deltaX ["sv << util::endian::big(packet->deltaX) << ']' << std::endl << "deltaY ["sv << util::endian::big(packet->deltaY) << ']' << std::endl - << "--end mouse move packet--"sv; + << "--end relative mouse move packet--"sv; +} + +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 + << "y ["sv << util::endian::big(packet->y) << ']' << std::endl + << "width ["sv << util::endian::big(packet->width) << ']' << std::endl + << "height ["sv << util::endian::big(packet->height) << ']' << std::endl + << "--end absolute mouse move packet--"sv; } void print(PNV_MOUSE_BUTTON_PACKET packet) { @@ -138,8 +150,11 @@ void print(void *input) { int input_type = util::endian::big(*(int*)input); switch(input_type) { - case PACKET_TYPE_MOUSE_MOVE: - print((PNV_MOUSE_MOVE_PACKET)input); + case PACKET_TYPE_REL_MOUSE_MOVE: + print((PNV_REL_MOUSE_MOVE_PACKET)input); + break; + case PACKET_TYPE_ABS_MOUSE_MOVE: + print((PNV_ABS_MOUSE_MOVE_PACKET)input); break; case PACKET_TYPE_MOUSE_BUTTON: print((PNV_MOUSE_BUTTON_PACKET)input); @@ -162,7 +177,7 @@ void print(void *input) { } } -void passthrough(platf::input_t &input, PNV_MOUSE_MOVE_PACKET packet) { +void passthrough(platf::input_t &input, PNV_REL_MOUSE_MOVE_PACKET packet) { display_cursor = true; platf::move_mouse(input, util::endian::big(packet->deltaX), util::endian::big(packet->deltaY)); @@ -374,8 +389,8 @@ void passthrough_helper(std::shared_ptr input, std::vector Date: Tue, 11 May 2021 18:01:56 +0200 Subject: [PATCH 07/35] absolute mouse coordinate support for single monitor on Linux --- sunshine/input.cpp | 77 ++++++++++++++++++----- sunshine/input.h | 2 +- sunshine/platform/common.h | 1 + sunshine/platform/linux/input.cpp | 96 +++++++++++++++++++++++++---- sunshine/platform/windows/input.cpp | 1 + sunshine/stream.cpp | 2 +- 6 files changed, 152 insertions(+), 27 deletions(-) diff --git a/sunshine/input.cpp b/sunshine/input.cpp index a9aee2c4..81a4920a 100644 --- a/sunshine/input.cpp +++ b/sunshine/input.cpp @@ -19,6 +19,9 @@ extern "C" { namespace input { constexpr auto MAX_GAMEPADS = std::min((std::size_t)platf::MAX_GAMEPADS, sizeof(std::int16_t)*8); +#define DISABLE_LEFT_BUTTON_DELAY ((util::ThreadPool::task_id_t)0x01) +#define ENABLE_LEFT_BUTTON_DELAY nullptr + enum class button_state_e { NONE, DOWN, @@ -80,10 +83,12 @@ struct gamepad_t { }; struct input_t { - input_t() : active_gamepad_state {}, gamepads (MAX_GAMEPADS) { } + input_t() : active_gamepad_state {}, gamepads (MAX_GAMEPADS), mouse_left_button_timeout {} { } std::uint16_t active_gamepad_state; std::vector gamepads; + + util::ThreadPool::task_id_t mouse_left_button_timeout; }; using namespace std::literals; @@ -177,23 +182,61 @@ void print(void *input) { } } -void passthrough(platf::input_t &input, PNV_REL_MOUSE_MOVE_PACKET packet) { +void passthrough(std::shared_ptr &input, PNV_REL_MOUSE_MOVE_PACKET packet) { display_cursor = true; - platf::move_mouse(input, util::endian::big(packet->deltaX), util::endian::big(packet->deltaY)); + input->mouse_left_button_timeout = DISABLE_LEFT_BUTTON_DELAY; + platf::move_mouse(platf_input, util::endian::big(packet->deltaX), util::endian::big(packet->deltaY)); +} + +void passthrough(std::shared_ptr &input, PNV_ABS_MOUSE_MOVE_PACKET packet) { + display_cursor = true; + + if(input->mouse_left_button_timeout == DISABLE_LEFT_BUTTON_DELAY) { + input->mouse_left_button_timeout = ENABLE_LEFT_BUTTON_DELAY; + } + + platf::abs_mouse(platf_input, util::endian::big(packet->x), util::endian::big(packet->y)); } void passthrough(std::shared_ptr &input, PNV_MOUSE_BUTTON_PACKET packet) { auto constexpr BUTTON_RELEASED = 0x09; + auto constexpr BUTTON_LEFT = 0x01; display_cursor = true; + auto release = packet->action == BUTTON_RELEASED; + auto button = util::endian::big(packet->button); if(button > 0 && button < mouse_press.size()) { - mouse_press[button] = packet->action != BUTTON_RELEASED; + mouse_press[button] = !release; } - platf::button_mouse(platf_input, button, packet->action == BUTTON_RELEASED); + /*/ + * When Moonlight sends mouse input through absolute coordinates, + * it's possible that BUTTON_RIGHT is pressed down immediately after releasing BUTTON_LEFT. + * As a result, Sunshine will left click on hyperlinks in the browser before right clicking + * + * This can be solved by delaying BUTTON_LEFT, however, any delay on input is undesirable during gaming + * As a compromise, Sunshine will only put delays on BUTTON_LEFT when + * absolute mouse coordinates have been send. + * + * Try to make sure BUTTON_RIGHT gets called before BUTTON_LEFT is released. + * + * input->mouse_left_button_timeout can only be nullptr + * when the last mouse coordinates were absolute + /*/ + if(button == BUTTON_LEFT && release && !input->mouse_left_button_timeout) { + input->mouse_left_button_timeout = task_pool.pushDelayed([=]() { + platf::button_mouse(platf_input, button, release); + + input->mouse_left_button_timeout = nullptr; + }, 10ms).task_id; + + return; + } + + platf::button_mouse(platf_input, button, release); } void repeat_key(short key_code) { @@ -238,10 +281,10 @@ void passthrough(std::shared_ptr &input, PNV_KEYBOARD_PACKET packet) { platf::keyboard(platf_input, packet->keyCode & 0x00FF, release); } -void passthrough(platf::input_t &input, PNV_SCROLL_PACKET packet) { +void passthrough(PNV_SCROLL_PACKET packet) { display_cursor = true; - platf::scroll(input, util::endian::big(packet->scrollAmt1)); + platf::scroll(platf_input, util::endian::big(packet->scrollAmt1)); } int updateGamepads(std::vector &gamepads, std::int16_t old_state, std::int16_t new_state) { @@ -390,7 +433,10 @@ void passthrough_helper(std::shared_ptr input, std::vector input, std::vector &input, std::vector &&in task_pool.push(passthrough_helper, input, util::cmove(input_data)); } -void reset() { - if(task_id) { - task_pool.cancel(task_id); - } +void reset(std::shared_ptr &input) { + task_pool.cancel(task_id); + task_pool.cancel(input->mouse_left_button_timeout); - // Ensure input is synchronous + // Ensure input is synchronous, by using the task_pool task_pool.push([]() { + for(int x = 0; x < mouse_press.size(); ++x) { + platf::button_mouse(platf_input, x, true); + } + for(auto& kp : key_press) { platf::keyboard(platf_input, kp.first & 0x00FF, true); key_press[kp.first] = false; diff --git a/sunshine/input.h b/sunshine/input.h index e1d4c82c..71e1805c 100644 --- a/sunshine/input.h +++ b/sunshine/input.h @@ -11,7 +11,7 @@ namespace input { struct input_t; void print(void *input); -void reset(); +void reset(std::shared_ptr &input); void passthrough(std::shared_ptr &input, std::vector &&input_data); void init(); diff --git a/sunshine/platform/common.h b/sunshine/platform/common.h index fa6b3be7..c8675bfc 100644 --- a/sunshine/platform/common.h +++ b/sunshine/platform/common.h @@ -146,6 +146,7 @@ std::shared_ptr display(dev_type_e hwdevice_type); input_t input(); void move_mouse(input_t &input, int deltaX, int deltaY); +void abs_mouse(input_t &input, int x, int y); void button_mouse(input_t &input, int button, bool release); void scroll(input_t &input, int distance); void keyboard(input_t &input, uint16_t modcode, bool release); diff --git a/sunshine/platform/linux/input.cpp b/sunshine/platform/linux/input.cpp index 6bb5d495..df34e6e8 100644 --- a/sunshine/platform/linux/input.cpp +++ b/sunshine/platform/linux/input.cpp @@ -31,6 +31,15 @@ using keyboard_t = util::safe_ptr_v2; struct input_raw_t { public: + void clear_touchscreen() { + std::filesystem::path touch_path { "sunshine_touchscreen"sv }; + + if(std::filesystem::is_symlink(touch_path)) { + std::filesystem::remove(touch_path); + } + + touch_input.reset(); + } void clear_mouse() { std::filesystem::path mouse_path { "sunshine_mouse"sv }; @@ -55,9 +64,7 @@ public: } int create_mouse() { - libevdev_uinput *buf {}; - int err = libevdev_uinput_create_from_device(mouse_dev.get(), LIBEVDEV_UINPUT_OPEN_MANAGED, &buf); - mouse_input.reset(buf); + int err = libevdev_uinput_create_from_device(mouse_dev.get(), LIBEVDEV_UINPUT_OPEN_MANAGED, &mouse_input); if(err) { BOOST_LOG(error) << "Could not create Sunshine Mouse: "sv << strerror(-err); @@ -69,13 +76,24 @@ public: return 0; } + int create_touchscreen() { + int err = libevdev_uinput_create_from_device(touch_dev.get(), LIBEVDEV_UINPUT_OPEN_MANAGED, &touch_input); + + if(err) { + BOOST_LOG(error) << "Could not create Sunshine Touchscreen: "sv << strerror(-err); + return -1; + } + + std::filesystem::create_symlink(libevdev_uinput_get_devnode(touch_input.get()), "sunshine_touchscreen"sv); + + return 0; + } + int alloc_gamepad(int nr) { TUPLE_2D_REF(input, gamepad_state, gamepads[nr]); - libevdev_uinput *buf; - int err = libevdev_uinput_create_from_device(gamepad_dev.get(), LIBEVDEV_UINPUT_OPEN_MANAGED, &buf); + int err = libevdev_uinput_create_from_device(gamepad_dev.get(), LIBEVDEV_UINPUT_OPEN_MANAGED, &input); - input.reset(buf); gamepad_state = gamepad_state_t {}; if(err) { @@ -96,6 +114,7 @@ public: } void clear() { + clear_touchscreen(); clear_mouse(); for(int x = 0; x < gamepads.size(); ++x) { clear_gamepad(x); @@ -106,16 +125,28 @@ public: clear(); } - evdev_t gamepad_dev; - std::vector> gamepads; - - evdev_t mouse_dev; uinput_t mouse_input; + uinput_t touch_input; + + evdev_t gamepad_dev; + evdev_t touch_dev; + evdev_t mouse_dev; keyboard_t keyboard; }; +void abs_mouse(input_t &input, int x, int y) { + auto touchscreen = ((input_raw_t*)input.get())->touch_input.get(); + + libevdev_uinput_write_event(touchscreen, EV_ABS, ABS_X, x); + libevdev_uinput_write_event(touchscreen, EV_ABS, ABS_Y, y); + libevdev_uinput_write_event(touchscreen, EV_KEY, BTN_TOOL_FINGER, 1); + libevdev_uinput_write_event(touchscreen, EV_KEY, BTN_TOOL_FINGER, 0); + + libevdev_uinput_write_event(touchscreen, EV_SYN, SYN_REPORT, 0); +} + void move_mouse(input_t &input, int deltaX, int deltaY) { auto mouse = ((input_raw_t*)input.get())->mouse_input.get(); @@ -409,6 +440,48 @@ evdev_t mouse() { return dev; } +evdev_t touchscreen() { + evdev_t dev { libevdev_new() }; + + libevdev_set_uniq(dev.get(), "Sunshine Touch"); + libevdev_set_id_product(dev.get(), 0xDEAD); + libevdev_set_id_vendor(dev.get(), 0xBEEF); + libevdev_set_id_bustype(dev.get(), 0x3); + libevdev_set_id_version(dev.get(), 0x111); + libevdev_set_name(dev.get(), "Touchscreen passthrough"); + + libevdev_enable_property(dev.get(), INPUT_PROP_DIRECT); + + + libevdev_enable_event_type(dev.get(), EV_KEY); + libevdev_enable_event_code(dev.get(), EV_KEY, BTN_TOUCH, nullptr); + libevdev_enable_event_code(dev.get(), EV_KEY, BTN_TOOL_PEN, nullptr); // Needed to be enabled for BTN_TOOL_FINGER to work. + libevdev_enable_event_code(dev.get(), EV_KEY, BTN_TOOL_FINGER, nullptr); + + input_absinfo absx { + 0, + 0, + 1919, + 1, + 0, + 28 + }; + + input_absinfo absy { + 0, + 0, + 1199, + 1, + 0, + 28 + }; + libevdev_enable_event_type(dev.get(), EV_ABS); + libevdev_enable_event_code(dev.get(), EV_ABS, ABS_X, &absx); + libevdev_enable_event_code(dev.get(), EV_ABS, ABS_Y, &absy); + + return dev; +} + evdev_t x360() { evdev_t dev { libevdev_new() }; @@ -486,10 +559,11 @@ input_t input() { // Ensure starting from clean slate gp.clear(); + gp.touch_dev = touchscreen(); gp.mouse_dev = mouse(); gp.gamepad_dev = x360(); - if(gp.create_mouse()) { + if(gp.create_mouse() || gp.create_touchscreen()) { log_flush(); std::abort(); } diff --git a/sunshine/platform/windows/input.cpp b/sunshine/platform/windows/input.cpp index 284f3c83..d0af736e 100755 --- a/sunshine/platform/windows/input.cpp +++ b/sunshine/platform/windows/input.cpp @@ -165,6 +165,7 @@ input_t input() { return result; } +void abs_mouse(input_t &input, int x, int y) {} void move_mouse(input_t &input, int deltaX, int deltaY) { INPUT i {}; diff --git a/sunshine/stream.cpp b/sunshine/stream.cpp index a86f4cca..c505fc0a 100644 --- a/sunshine/stream.cpp +++ b/sunshine/stream.cpp @@ -902,7 +902,7 @@ void join(session_t &session) { session.controlEnd.view(); //Reset input on session stop to avoid stuck repeated keys BOOST_LOG(debug) << "Resetting Input..."sv; - input::reset(); + input::reset(session.input); BOOST_LOG(debug) << "Session ended"sv; } From 92cd8648fae1e1f1dacaf4dfe8e7330b339ac4fe Mon Sep 17 00:00:00 2001 From: loki Date: Tue, 11 May 2021 20:10:33 +0200 Subject: [PATCH 08/35] Some refactoring for linux/display.cpp --- sunshine/platform/linux/display.cpp | 211 +++++++++++++--------------- 1 file changed, 94 insertions(+), 117 deletions(-) diff --git a/sunshine/platform/linux/display.cpp b/sunshine/platform/linux/display.cpp index cde96cf7..39fbd616 100644 --- a/sunshine/platform/linux/display.cpp +++ b/sunshine/platform/linux/display.cpp @@ -37,12 +37,15 @@ void freeX(XFixesCursorImage*); using ifaddr_t = util::safe_ptr; using xcb_connect_t = util::safe_ptr; using xcb_img_t = util::c_ptr; -using xcb_cursor_img = util::c_ptr; using xdisplay_t = util::safe_ptr_v2; using ximg_t = util::safe_ptr; using xcursor_t = util::safe_ptr; +using crtc_info_t = util::safe_ptr<_XRRCrtcInfo, XRRFreeCrtcInfo>; +using output_info_t = util::safe_ptr<_XRROutputInfo, XRRFreeOutputInfo>; +using screen_res_t = util::safe_ptr<_XRRScreenResources, XRRFreeScreenResources>; + class shm_id_t { public: shm_id_t() : id { -1 } {} @@ -62,24 +65,16 @@ public: class shm_data_t { public: - shm_data_t() : data { (void*) -1 } - { - } - shm_data_t(void *data) : data { data } - { + shm_data_t() : data { (void*) -1 } {} + shm_data_t(void *data) : data { data } {} + + shm_data_t(shm_data_t &&other) noexcept : data(other.data) { + other.data = (void*)-1; } - shm_data_t(shm_data_t &&other) noexcept : data(other.data) - { - other.data = (void*) -1; - } - - ~shm_data_t() - { - if ((std::uintptr_t) data != -1) - { + ~shm_data_t() { + if((std::uintptr_t) data != -1) { shmdt(data); - data = (void*) -1; } } @@ -91,8 +86,7 @@ struct x11_img_t: public img_t { }; struct shm_img_t: public img_t { - ~shm_img_t() override - { + ~shm_img_t() override { delete[] data; data = nullptr; } @@ -102,8 +96,7 @@ void blend_cursor(Display *display, img_t &img, int offsetX, int offsetY) { xcursor_t overlay { XFixesGetCursorImage(display) }; if (!overlay) { - BOOST_LOG(error) - << "Couldn't get cursor from XFixesGetCursorImage"sv; + BOOST_LOG(error) << "Couldn't get cursor from XFixesGetCursorImage"sv; return; } @@ -113,10 +106,10 @@ void blend_cursor(Display *display, img_t &img, int offsetX, int offsetY) { overlay->x -= offsetX; overlay->y -= offsetY; - overlay->x = std::max((short) 0, overlay->x); - overlay->y = std::max((short) 0, overlay->y); + overlay->x = std::max((short)0, overlay->x); + overlay->y = std::max((short)0, overlay->y); - auto pixels = (int*) img.data; + auto pixels = (int*)img.data; auto screen_height = img.height; auto screen_width = img.width; @@ -154,25 +147,25 @@ struct x11_attr_t: public display_t Window xwindow; XWindowAttributes xattr; - Display* displayDisplay; - /* - * Remember last X (NOT the streamed monitor!) size. This way we can trigger reinitialization if the dimensions changed while streaming + * Last X (NOT the streamed monitor!) size. + * This way we can trigger reinitialization if the dimensions changed while streaming */ - int lastWidth,lastHeight; + int lastWidth, lastHeight; /* * Offsets for when streaming a specific monitor. By default, they are 0. */ - int displayOffsetX,displayOffsetY; + int displayOffsetX, displayOffsetY; - x11_attr_t() : xdisplay { displayDisplay = XOpenDisplay(nullptr) }, xwindow { }, xattr { } - { + x11_attr_t() : xdisplay { XOpenDisplay(nullptr) }, xwindow { }, xattr { }, displayOffsetX { 0 }, displayOffsetY { 0 } { XInitThreads(); - if (!xdisplay) { - BOOST_LOG(fatal) << "Could not open x11 display"sv; - log_flush(); - std::abort(); + } + + int init() { + if(!xdisplay) { + BOOST_LOG(error) << "Could not open X11 display"sv; + return -1; } xwindow = DefaultRootWindow(xdisplay.get()); @@ -184,38 +177,30 @@ struct x11_attr_t: public display_t streamedMonitor = (int)util::from_view(config::video.output_name); } - //If the value has been set at all - if (streamedMonitor != -1) { - - BOOST_LOG(info) << "Configuring selected monitor ("<< streamedMonitor<<") to stream. If it fails here, you may need Xrandr >= 1.5"sv; - XRRScreenResources *screenr = XRRGetScreenResources(displayDisplay, xwindow); - // This is the key right here. Use XRRScreenResources::noutput + if(streamedMonitor != -1) { + BOOST_LOG(info) << "Configuring selected monitor ("sv << streamedMonitor << ") to stream"sv; + screen_res_t screenr { XRRGetScreenResources(xdisplay.get(), xwindow) }; int output = screenr->noutput; - if (streamedMonitor >= output) { - BOOST_LOG(error)<< "Could not stream selected display number because there aren't so many."sv; + if(streamedMonitor >= output) { + BOOST_LOG(error) << "Could not stream display number ["sv << streamedMonitor << "], there are only ["sv << output << "] displays."sv; + return -1; } - else { - XRROutputInfo* out_info = XRRGetOutputInfo(displayDisplay, screenr, screenr->outputs[streamedMonitor]); - if (NULL == out_info || out_info->connection != RR_Connected) - { - BOOST_LOG(error)<< "Could not stream selected display because it doesn't seem to be connected"sv; - } - else - { - XRRCrtcInfo* crt_info = XRRGetCrtcInfo(displayDisplay, screenr, out_info->crtc); - BOOST_LOG(info)<<"Streaming display: "<name<<" with res "<width<<" x "<height<<+" offset by "<x<<" x "<y<<"."sv; - width = crt_info -> width; - height = crt_info -> height; - displayOffsetX = crt_info -> x; - displayOffsetY = crt_info -> y; - - XRRFreeCrtcInfo(crt_info); - } - XRRFreeOutputInfo(out_info); + output_info_t out_info { XRRGetOutputInfo(xdisplay.get(), screenr.get(), screenr->outputs[streamedMonitor]) }; + if(!out_info || out_info->connection != RR_Connected) { + BOOST_LOG(error) << "Could not stream selected display because it doesn't seem to be connected"sv; + return -1; } - XRRFreeScreenResources(screenr); + + crtc_info_t crt_info { XRRGetCrtcInfo(xdisplay.get(), screenr.get(), out_info->crtc) }; + BOOST_LOG(info) + << "Streaming display: "sv << out_info->name << " with res "sv << crt_info->width << 'x' << crt_info->height << " offset by "sv << crt_info->x << 'x' << crt_info->y; + + width = crt_info->width; + height = crt_info->height; + displayOffsetX = crt_info->x; + displayOffsetY = crt_info->y; } else { width = xattr.width; @@ -224,13 +209,14 @@ struct x11_attr_t: public display_t lastWidth = xattr.width; lastHeight = xattr.height; + + return 0; } /** * Called when the display attributes should change. */ - void refresh() - { + void refresh() { XGetWindowAttributes(xdisplay.get(), xwindow, &xattr); //Update xattr's } @@ -242,7 +228,7 @@ struct x11_attr_t: public display_t BOOST_LOG(warning)<< "X dimensions changed in non-SHM mode, request reinit"sv; return capture_e::reinit; } - XImage *img { XGetImage(xdisplay.get(), xwindow, displayOffsetX, displayOffsetY, width,height, AllPlanes, ZPixmap) }; + XImage *img { XGetImage(xdisplay.get(), xwindow, displayOffsetX, displayOffsetY, width, height, AllPlanes, ZPixmap) }; auto img_out = (x11_img_t*) img_out_base; img_out->width = img->width; @@ -253,7 +239,7 @@ struct x11_attr_t: public display_t img_out->img.reset(img); if (cursor) { - blend_cursor(xdisplay.get(), *img_out_base,displayOffsetX,displayOffsetY); + blend_cursor(xdisplay.get(), *img_out_base, displayOffsetX, displayOffsetY); } return capture_e::ok; @@ -288,21 +274,21 @@ struct shm_attr_t: public x11_attr_t { } shm_attr_t() : x11_attr_t(), shm_xdisplay { XOpenDisplay(nullptr) } { - refresh_task_id = task_pool.pushDelayed(&shm_attr_t::delayed_refresh,2s, this).task_id; + refresh_task_id = task_pool.pushDelayed(&shm_attr_t::delayed_refresh, 2s, this).task_id; } ~shm_attr_t() override { - while (!task_pool.cancel(refresh_task_id)); + while(!task_pool.cancel(refresh_task_id)); } capture_e snapshot(img_t *img, std::chrono::milliseconds timeout, bool cursor) override { //The whole X server changed, so we gotta reinit everything - if (xattr.width != lastWidth || xattr.height != lastHeight) { + if(xattr.width != lastWidth || xattr.height != lastHeight) { BOOST_LOG(warning)<< "X dimensions changed in SHM mode, request reinit"sv; return capture_e::reinit; } else { - auto img_cookie = xcb_shm_get_image_unchecked(xcb.get(), display->root,displayOffsetX, displayOffsetY, width, height, ~0, XCB_IMAGE_FORMAT_Z_PIXMAP, seg, 0); + auto img_cookie = xcb_shm_get_image_unchecked(xcb.get(), display->root, displayOffsetX, displayOffsetY, width, height, ~0, XCB_IMAGE_FORMAT_Z_PIXMAP, seg, 0); xcb_img_t img_reply { xcb_shm_get_image_reply(xcb.get(), img_cookie,nullptr) }; if (!img_reply) { @@ -314,7 +300,7 @@ struct shm_attr_t: public x11_attr_t { std::copy_n((std::uint8_t*) data.data, frame_size(), img->data); if (cursor) { - blend_cursor(shm_xdisplay.get(), *img,displayOffsetX,displayOffsetY); + blend_cursor(shm_xdisplay.get(), *img, displayOffsetX, displayOffsetY); } return capture_e::ok; @@ -337,17 +323,18 @@ struct shm_attr_t: public x11_attr_t { } int init() { + if(x11_attr_t::init()) { + return 1; + } + shm_xdisplay.reset(XOpenDisplay(nullptr)); xcb.reset(xcb_connect(nullptr, nullptr)); - if (xcb_connection_has_error(xcb.get())) - { + if(xcb_connection_has_error(xcb.get())) { return -1; } - if (!xcb_get_extension_data(xcb.get(), &xcb_shm_id)->present) - { - BOOST_LOG(error) - << "Missing SHM extension"sv; + if(!xcb_get_extension_data(xcb.get(), &xcb_shm_id)->present) { + BOOST_LOG(error) << "Missing SHM extension"sv; return -1; } @@ -357,30 +344,20 @@ struct shm_attr_t: public x11_attr_t { seg = xcb_generate_id(xcb.get()); shm_id.id = shmget(IPC_PRIVATE, frame_size(), IPC_CREAT | 0777); - if (shm_id.id == -1) - { - BOOST_LOG(error) - << "shmget failed"sv; + if(shm_id.id == -1) { + BOOST_LOG(error) << "shmget failed"sv; return -1; } xcb_shm_attach(xcb.get(), seg, shm_id.id, false); data.data = shmat(shm_id.id, nullptr, 0); - if ((uintptr_t) data.data == -1) - { - BOOST_LOG(error) - << "shmat failed"sv; + if((uintptr_t)data.data == -1) { + BOOST_LOG(error) << "shmat failed"sv; return -1; } - /* - * Commented out resetting of the sizes when intializing X in SHM mode. It might be wrong. Expect issues. This is the default mode, so poisoning those variables is not desired - */ -// width = display->width_in_pixels; -// height = display->height_in_pixels; - return 0; } @@ -401,10 +378,8 @@ struct mic_attr_t: public mic_t { auto buf = sample_buf.data(); int status; - if (pa_simple_read(mic.get(), buf, sample_size * 2, &status)) - { - BOOST_LOG(error) - << "pa_simple_read() failed: "sv << pa_strerror(status); + if(pa_simple_read(mic.get(), buf, sample_size *2, &status)) { + BOOST_LOG(error) << "pa_simple_read() failed: "sv << pa_strerror(status); return capture_e::error; } @@ -413,29 +388,32 @@ struct mic_attr_t: public mic_t { } }; -std::shared_ptr shm_display() { - auto shm = std::make_shared(); - - if (shm->init()) { - return nullptr; - } - - return shm; -} - std::shared_ptr display(platf::dev_type_e hwdevice_type) { - if (hwdevice_type != platf::dev_type_e::none) { + if(hwdevice_type != platf::dev_type_e::none) { BOOST_LOG(error)<< "Could not initialize display with the given hw device type."sv; return nullptr; } - auto shm_disp = shm_display(); //Attempt to use shared memory X11 to avoid copying the frame + // Attempt to use shared memory X11 to avoid copying the frame + auto shm_disp = std::make_shared(); - if (!shm_disp) { - return std::make_shared(); //Fallback to standard way if else + auto status = shm_disp->init(); + if(status > 0) { + // x11_attr_t::init() failed, don't bother trying again. + return nullptr; } - return shm_disp; + if(status == 0) { + return shm_disp; + } + + // Fallback + auto x11_disp = std::make_shared(); + if(x11_disp->init()) { + return nullptr; + } + + return x11_disp; } std::unique_ptr microphone(std::uint32_t sample_rate) { @@ -494,20 +472,19 @@ std::pair from_sockaddr_ex(const sockaddr *const ip_ auto family = ip_addr->sa_family; std::uint16_t port; - if (family == AF_INET6) { - inet_ntop(AF_INET6, &((sockaddr_in6*) ip_addr)->sin6_addr, data, + if(family == AF_INET6) { + inet_ntop(AF_INET6, &((sockaddr_in6*)ip_addr)->sin6_addr, data, INET6_ADDRSTRLEN); port = ((sockaddr_in6*) ip_addr)->sin6_port; } - if (family == AF_INET) { - inet_ntop(AF_INET, &((sockaddr_in*) ip_addr)->sin_addr, data, + 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) { @@ -522,8 +499,8 @@ std::string get_mac_address(const std::string_view &address) { } } } - BOOST_LOG(warning) - << "Unable to find MAC address for "sv << address; + + BOOST_LOG(warning) << "Unable to find MAC address for "sv << address; return "00:00:00:00:00:00"s; } From 1d84c8f9ce57ab6ce6be52d0449c4814d68733dd Mon Sep 17 00:00:00 2001 From: loki Date: Tue, 11 May 2021 23:30:56 +0200 Subject: [PATCH 09/35] Correct dimensions for touchscreen when single monitor attached --- sunshine/input.cpp | 24 ++++++++++++++++++++++-- sunshine/input.h | 7 ++++++- sunshine/platform/common.h | 14 +++++++++++++- sunshine/platform/linux/input.cpp | 19 ++++++++++++++----- sunshine/video.cpp | 10 +++------- sunshine/video.h | 24 ++++++++++++++++++------ 6 files changed, 76 insertions(+), 22 deletions(-) diff --git a/sunshine/input.cpp b/sunshine/input.cpp index 81a4920a..2ad4b7cf 100644 --- a/sunshine/input.cpp +++ b/sunshine/input.cpp @@ -13,8 +13,9 @@ extern "C" { #include "main.h" #include "config.h" #include "utility.h" -#include "platform/common.h" #include "thread_pool.h" +#include "input.h" +#include "platform/common.h" namespace input { @@ -45,6 +46,11 @@ void free_id(std::bitset &gamepad_mask, int id) { gamepad_mask[id] = false; } +touch_port_event_t touch_port_event; +platf::touch_port_t touch_port { + 0, 0, 0, 0 +}; + static util::TaskPool::task_id_t task_id {}; static std::unordered_map key_press {}; static std::array mouse_press {}; @@ -196,7 +202,20 @@ void passthrough(std::shared_ptr &input, PNV_ABS_MOUSE_MOVE_PACKET pack input->mouse_left_button_timeout = ENABLE_LEFT_BUTTON_DELAY; } - platf::abs_mouse(platf_input, util::endian::big(packet->x), util::endian::big(packet->y)); + if(touch_port_event->peek()) { + touch_port = *touch_port_event->pop(); + } + + float x = util::endian::big(packet->x); + float y = util::endian::big(packet->y); + + float width = util::endian::big(packet->width); + float height = util::endian::big(packet->height); + + auto scale_x = (float)touch_port.width / width; + auto scale_y = (float)touch_port.height / height; + + platf::abs_mouse(platf_input, touch_port, x * scale_x, y * scale_y); } void passthrough(std::shared_ptr &input, PNV_MOUSE_BUTTON_PACKET packet) { @@ -481,6 +500,7 @@ void reset(std::shared_ptr &input) { } void init() { + touch_port_event = std::make_unique(); platf_input = platf::input(); } diff --git a/sunshine/input.h b/sunshine/input.h index 71e1805c..16267619 100644 --- a/sunshine/input.h +++ b/sunshine/input.h @@ -5,7 +5,8 @@ #ifndef SUNSHINE_INPUT_H #define SUNSHINE_INPUT_H - +#include "thread_safe.h" +#include "platform/common.h" namespace input { struct input_t; @@ -14,9 +15,13 @@ void print(void *input); void reset(std::shared_ptr &input); void passthrough(std::shared_ptr &input, std::vector &&input_data); + void init(); std::shared_ptr alloc(); + +using touch_port_event_t = std::unique_ptr>; +extern touch_port_event_t touch_port_event; } #endif //SUNSHINE_INPUT_H diff --git a/sunshine/platform/common.h b/sunshine/platform/common.h index c8675bfc..69e41922 100644 --- a/sunshine/platform/common.h +++ b/sunshine/platform/common.h @@ -58,6 +58,18 @@ using namespace std::literals; return "unknown"sv; } +// Dimensions for touchscreen input +struct touch_port_t { + std::uint32_t offset_x, offset_y; + std::uint32_t width, height; + + constexpr touch_port_t( + std::uint32_t offset_x, std::uint32_t offset_y, + std::uint32_t width, std::uint32_t height) noexcept : + offset_x { offset_x }, offset_y { offset_y }, + width { width }, height { height } {}; +}; + struct gamepad_state_t { std::uint16_t buttonFlags; std::uint8_t lt; @@ -146,7 +158,7 @@ std::shared_ptr display(dev_type_e hwdevice_type); input_t input(); void move_mouse(input_t &input, int deltaX, int deltaY); -void abs_mouse(input_t &input, int x, int y); +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 keyboard(input_t &input, uint16_t modcode, bool release); diff --git a/sunshine/platform/linux/input.cpp b/sunshine/platform/linux/input.cpp index df34e6e8..8b4578bc 100644 --- a/sunshine/platform/linux/input.cpp +++ b/sunshine/platform/linux/input.cpp @@ -6,6 +6,7 @@ #include #include +#include #include #include @@ -29,6 +30,11 @@ using uinput_t = util::safe_ptr; using keyboard_t = util::safe_ptr_v2; +constexpr touch_port_t target_touch_port { + 0, 0, + 19200, 12000 +}; + struct input_raw_t { public: void clear_touchscreen() { @@ -136,11 +142,14 @@ public: keyboard_t keyboard; }; -void abs_mouse(input_t &input, int x, int y) { +void abs_mouse(input_t &input, const touch_port_t &touch_port, float x, float y) { auto touchscreen = ((input_raw_t*)input.get())->touch_input.get(); - libevdev_uinput_write_event(touchscreen, EV_ABS, ABS_X, x); - libevdev_uinput_write_event(touchscreen, EV_ABS, ABS_Y, y); + auto scaled_x = (int)std::lround(x * ((float)target_touch_port.width / (float)touch_port.width)); + auto scaled_y = (int)std::lround(y * ((float)target_touch_port.height / (float)touch_port.height)); + + libevdev_uinput_write_event(touchscreen, EV_ABS, ABS_X, scaled_x + touch_port.offset_x); + libevdev_uinput_write_event(touchscreen, EV_ABS, ABS_Y, scaled_y + touch_port.offset_y); libevdev_uinput_write_event(touchscreen, EV_KEY, BTN_TOOL_FINGER, 1); libevdev_uinput_write_event(touchscreen, EV_KEY, BTN_TOOL_FINGER, 0); @@ -461,7 +470,7 @@ evdev_t touchscreen() { input_absinfo absx { 0, 0, - 1919, + target_touch_port.width, 1, 0, 28 @@ -470,7 +479,7 @@ evdev_t touchscreen() { input_absinfo absy { 0, 0, - 1199, + target_touch_port.height, 1, 0, 28 diff --git a/sunshine/video.cpp b/sunshine/video.cpp index c6befe8d..88dfd01b 100644 --- a/sunshine/video.cpp +++ b/sunshine/video.cpp @@ -38,10 +38,6 @@ void free_buffer(AVBufferRef *ref) { av_buffer_unref(&ref); } -void free_packet(AVPacket *packet) { - av_packet_free(&packet); -} - namespace nv { enum class profile_h264_e : int { @@ -70,8 +66,6 @@ platf::pix_fmt_e map_pix_fmt(AVPixelFormat fmt); void sw_img_to_frame(const platf::img_t &img, frame_t &frame); void dxgi_img_to_frame(const platf::img_t &img, frame_t &frame); util::Either dxgi_make_hwdevice_ctx(platf::hwdevice_t *hwdevice_ctx); -void dxgi_img_to_frame(const platf::img_t &img, frame_t &frame); -util::Either dxgi_make_hwdevice_ctx(platf::hwdevice_t *hwdevice_ctx); util::Either make_hwdevice_ctx(AVHWDeviceType type, void *hwdevice_ctx); int hwframe_ctx(ctx_t &ctx, buffer_t &hwdevice, AVPixelFormat format); @@ -183,7 +177,7 @@ public: session_t(ctx_t &&ctx, frame_t &&frame, util::wrap_ptr &&device) : ctx { std::move(ctx) }, frame { std::move(frame) }, device { std::move(device) } {} - session_t(session_t &&other) : + session_t(session_t &&other) noexcept : ctx { std::move(other.ctx) }, frame { std::move(other.frame) }, device { std::move(other.device) } {} // Ensure objects are destroyed in the correct order @@ -862,6 +856,7 @@ encode_e encode_run_sync(std::vector> &synce return encode_e::error; } + input::touch_port_event->raise(0, 0, img->width, img->height); std::vector synced_sessions; for(auto &ctx : synced_session_ctxs) { auto synced_session = make_synced_session(disp.get(), encoder, *img, *ctx); @@ -1069,6 +1064,7 @@ void capture_async( if(display->dummy_img(dummy_img.get())) { return; } + input::touch_port_event->raise(0, 0, dummy_img->width, dummy_img->height); images->raise(std::move(dummy_img)); encode_run( diff --git a/sunshine/video.h b/sunshine/video.h index e4560e05..b628f303 100644 --- a/sunshine/video.h +++ b/sunshine/video.h @@ -6,6 +6,7 @@ #define SUNSHINE_VIDEO_H #include "thread_safe.h" +#include "input.h" #include "platform/common.h" extern "C" { @@ -14,16 +15,27 @@ extern "C" { struct AVPacket; namespace video { -void free_packet(AVPacket *packet); struct packet_raw_t : public AVPacket { - template - explicit packet_raw_t(P *user_data) : channel_data { user_data } { - av_init_packet(this); + void init_packet() { + pts = AV_NOPTS_VALUE; + dts = AV_NOPTS_VALUE; + pos = -1; + duration = 0; + flags = 0; + stream_index = 0; + buf = nullptr; + side_data = nullptr; + side_data_elems = 0; } - explicit packet_raw_t(std::nullptr_t null) : channel_data { nullptr } { - av_init_packet(this); + template + explicit packet_raw_t(P *user_data) : channel_data { user_data } { + init_packet(); + } + + explicit packet_raw_t(std::nullptr_t) : channel_data { nullptr } { + init_packet(); } ~packet_raw_t() { From 2e9a1cfbbaacbe1ea63808214801aeb306fdfb11 Mon Sep 17 00:00:00 2001 From: loki Date: Tue, 11 May 2021 23:51:45 +0200 Subject: [PATCH 10/35] absolute mouse coordinates regardless of the number of monitors attached on Linux --- sunshine/platform/common.h | 4 ++++ sunshine/platform/linux/display.cpp | 28 +++++++++++----------------- sunshine/platform/linux/input.cpp | 8 ++++---- sunshine/platform/windows/input.cpp | 2 +- sunshine/video.cpp | 9 +++++++-- 5 files changed, 27 insertions(+), 24 deletions(-) diff --git a/sunshine/platform/common.h b/sunshine/platform/common.h index 69e41922..96c3d84e 100644 --- a/sunshine/platform/common.h +++ b/sunshine/platform/common.h @@ -122,6 +122,7 @@ enum class capture_e : int { class display_t { public: + display_t() noexcept : offset_x { 0 }, offset_y { 0 } {} virtual capture_e snapshot(img_t *img, std::chrono::milliseconds timeout, bool cursor) = 0; virtual std::shared_ptr alloc_img() = 0; @@ -133,6 +134,9 @@ public: virtual ~display_t() = default; + // Offsets for when streaming a specific monitor. By default, they are 0. + int offset_x, offset_y; + int width, height; }; diff --git a/sunshine/platform/linux/display.cpp b/sunshine/platform/linux/display.cpp index 39fbd616..4ca09f59 100644 --- a/sunshine/platform/linux/display.cpp +++ b/sunshine/platform/linux/display.cpp @@ -153,12 +153,7 @@ struct x11_attr_t: public display_t */ int lastWidth, lastHeight; - /* - * Offsets for when streaming a specific monitor. By default, they are 0. - */ - int displayOffsetX, displayOffsetY; - - x11_attr_t() : xdisplay { XOpenDisplay(nullptr) }, xwindow { }, xattr { }, displayOffsetX { 0 }, displayOffsetY { 0 } { + x11_attr_t() : xdisplay { XOpenDisplay(nullptr) }, xwindow { }, xattr { } { XInitThreads(); } @@ -199,8 +194,8 @@ struct x11_attr_t: public display_t width = crt_info->width; height = crt_info->height; - displayOffsetX = crt_info->x; - displayOffsetY = crt_info->y; + offset_x = crt_info->x; + offset_y = crt_info->y; } else { width = xattr.width; @@ -228,7 +223,7 @@ struct x11_attr_t: public display_t BOOST_LOG(warning)<< "X dimensions changed in non-SHM mode, request reinit"sv; return capture_e::reinit; } - XImage *img { XGetImage(xdisplay.get(), xwindow, displayOffsetX, displayOffsetY, width, height, AllPlanes, ZPixmap) }; + XImage *img { XGetImage(xdisplay.get(), xwindow, offset_x, offset_y, width, height, AllPlanes, ZPixmap) }; auto img_out = (x11_img_t*) img_out_base; img_out->width = img->width; @@ -239,7 +234,7 @@ struct x11_attr_t: public display_t img_out->img.reset(img); if (cursor) { - blend_cursor(xdisplay.get(), *img_out_base, displayOffsetX, displayOffsetY); + blend_cursor(xdisplay.get(), *img_out_base, offset_x, offset_y); } return capture_e::ok; @@ -288,19 +283,18 @@ struct shm_attr_t: public x11_attr_t { return capture_e::reinit; } else { - auto img_cookie = xcb_shm_get_image_unchecked(xcb.get(), display->root, displayOffsetX, displayOffsetY, width, height, ~0, XCB_IMAGE_FORMAT_Z_PIXMAP, seg, 0); + auto img_cookie = xcb_shm_get_image_unchecked(xcb.get(), display->root, offset_x, offset_y, width, height, ~0, XCB_IMAGE_FORMAT_Z_PIXMAP, seg, 0); xcb_img_t img_reply { xcb_shm_get_image_reply(xcb.get(), img_cookie,nullptr) }; - if (!img_reply) { - BOOST_LOG(error) - << "Could not get image reply"sv; + if(!img_reply) { + BOOST_LOG(error) << "Could not get image reply"sv; return capture_e::reinit; } - std::copy_n((std::uint8_t*) data.data, frame_size(), img->data); + std::copy_n((std::uint8_t*)data.data, frame_size(), img->data); - if (cursor) { - blend_cursor(shm_xdisplay.get(), *img, displayOffsetX, displayOffsetY); + if(cursor) { + blend_cursor(shm_xdisplay.get(), *img, offset_x, offset_y); } return capture_e::ok; diff --git a/sunshine/platform/linux/input.cpp b/sunshine/platform/linux/input.cpp index 8b4578bc..b0a5cec1 100644 --- a/sunshine/platform/linux/input.cpp +++ b/sunshine/platform/linux/input.cpp @@ -145,11 +145,11 @@ public: void abs_mouse(input_t &input, const touch_port_t &touch_port, float x, float y) { auto touchscreen = ((input_raw_t*)input.get())->touch_input.get(); - auto scaled_x = (int)std::lround(x * ((float)target_touch_port.width / (float)touch_port.width)); - auto scaled_y = (int)std::lround(y * ((float)target_touch_port.height / (float)touch_port.height)); + auto scaled_x = (int)std::lround((x + touch_port.offset_x) * ((float)target_touch_port.width / (float)touch_port.width)); + auto scaled_y = (int)std::lround((y + touch_port.offset_y) * ((float)target_touch_port.height / (float)touch_port.height)); - libevdev_uinput_write_event(touchscreen, EV_ABS, ABS_X, scaled_x + touch_port.offset_x); - libevdev_uinput_write_event(touchscreen, EV_ABS, ABS_Y, scaled_y + touch_port.offset_y); + libevdev_uinput_write_event(touchscreen, EV_ABS, ABS_X, scaled_x); + libevdev_uinput_write_event(touchscreen, EV_ABS, ABS_Y, scaled_y); libevdev_uinput_write_event(touchscreen, EV_KEY, BTN_TOOL_FINGER, 1); libevdev_uinput_write_event(touchscreen, EV_KEY, BTN_TOOL_FINGER, 0); diff --git a/sunshine/platform/windows/input.cpp b/sunshine/platform/windows/input.cpp index d0af736e..4f40576c 100755 --- a/sunshine/platform/windows/input.cpp +++ b/sunshine/platform/windows/input.cpp @@ -165,7 +165,7 @@ input_t input() { return result; } -void abs_mouse(input_t &input, int x, int y) {} +void abs_mouse(input_t &input, const touch_port_t &touch_port, float x, float y) {} void move_mouse(input_t &input, int deltaX, int deltaY) { INPUT i {}; diff --git a/sunshine/video.cpp b/sunshine/video.cpp index 88dfd01b..54252375 100644 --- a/sunshine/video.cpp +++ b/sunshine/video.cpp @@ -856,7 +856,9 @@ encode_e encode_run_sync(std::vector> &synce return encode_e::error; } - input::touch_port_event->raise(0, 0, img->width, img->height); + // absolute mouse coordinates require that the dimensions of the screen are known + input::touch_port_event->raise(disp->offset_x, disp->offset_y, disp->width, disp->height); + std::vector synced_sessions; for(auto &ctx : synced_session_ctxs) { auto synced_session = make_synced_session(disp.get(), encoder, *img, *ctx); @@ -1064,9 +1066,12 @@ void capture_async( if(display->dummy_img(dummy_img.get())) { return; } - input::touch_port_event->raise(0, 0, dummy_img->width, dummy_img->height); + images->raise(std::move(dummy_img)); + // absolute mouse coordinates require that the dimensions of the screen are known + input::touch_port_event->raise(display->offset_x, display->offset_y, display->width, display->height); + encode_run( frame_nr, key_frame_nr, shutdown_event, From 022b2202f67ef42fc9ba3d120fae3f0a0940e15b Mon Sep 17 00:00:00 2001 From: loki Date: Wed, 12 May 2021 14:42:10 +0200 Subject: [PATCH 11/35] Absolute mouse coordinates on Windows --- sunshine/input.cpp | 38 +++++++++- sunshine/platform/windows/display_base.cpp | 6 +- sunshine/platform/windows/input.cpp | 87 +++++++++++----------- 3 files changed, 84 insertions(+), 47 deletions(-) diff --git a/sunshine/input.cpp b/sunshine/input.cpp index 2ad4b7cf..ce2a81e3 100644 --- a/sunshine/input.cpp +++ b/sunshine/input.cpp @@ -209,6 +209,14 @@ void passthrough(std::shared_ptr &input, PNV_ABS_MOUSE_MOVE_PACKET pack float x = util::endian::big(packet->x); float y = util::endian::big(packet->y); + // Prevent divide by zero + // Don't expect it to happen, but just in case + if(!packet->width || !packet->height) { + BOOST_LOG(warning) << "Moonlight passed invalid dimensions"sv; + + return; + } + float width = util::endian::big(packet->width); float height = util::endian::big(packet->height); @@ -220,7 +228,9 @@ void passthrough(std::shared_ptr &input, PNV_ABS_MOUSE_MOVE_PACKET pack void passthrough(std::shared_ptr &input, PNV_MOUSE_BUTTON_PACKET packet) { auto constexpr BUTTON_RELEASED = 0x09; - auto constexpr BUTTON_LEFT = 0x01; + + auto constexpr BUTTON_LEFT = 0x01; + auto constexpr BUTTON_RIGHT = 0x03; display_cursor = true; @@ -228,9 +238,14 @@ void passthrough(std::shared_ptr &input, PNV_MOUSE_BUTTON_PACKET packet auto button = util::endian::big(packet->button); if(button > 0 && button < mouse_press.size()) { + if(mouse_press[button] != release) { + // button state is already what we want + return; + } + mouse_press[button] = !release; } - +/////////////////////////////////// /*/ * When Moonlight sends mouse input through absolute coordinates, * it's possible that BUTTON_RIGHT is pressed down immediately after releasing BUTTON_LEFT. @@ -247,13 +262,30 @@ void passthrough(std::shared_ptr &input, PNV_MOUSE_BUTTON_PACKET packet /*/ if(button == BUTTON_LEFT && release && !input->mouse_left_button_timeout) { input->mouse_left_button_timeout = task_pool.pushDelayed([=]() { - platf::button_mouse(platf_input, button, release); + auto left_released = mouse_press[BUTTON_LEFT]; + if(left_released) { + // Already released left button + return; + } + platf::button_mouse(platf_input, BUTTON_LEFT, release); input->mouse_left_button_timeout = nullptr; }, 10ms).task_id; return; } + if( + button == BUTTON_RIGHT && !release && + 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); + + mouse_press[BUTTON_RIGHT] = false; + + return; + } +/////////////////////////////////// platf::button_mouse(platf_input, button, release); } diff --git a/sunshine/platform/windows/display_base.cpp b/sunshine/platform/windows/display_base.cpp index 8644ed88..46405397 100644 --- a/sunshine/platform/windows/display_base.cpp +++ b/sunshine/platform/windows/display_base.cpp @@ -129,8 +129,10 @@ int display_base_t::init() { if(desc.AttachedToDesktop) { output = std::move(output_tmp); - width = desc.DesktopCoordinates.right - desc.DesktopCoordinates.left; - height = desc.DesktopCoordinates.bottom - desc.DesktopCoordinates.top; + offset_x = desc.DesktopCoordinates.left; + offset_y = desc.DesktopCoordinates.top; + width = desc.DesktopCoordinates.right - offset_x; + height = desc.DesktopCoordinates.bottom - offset_y; } } diff --git a/sunshine/platform/windows/input.cpp b/sunshine/platform/windows/input.cpp index 4f40576c..b372afd2 100755 --- a/sunshine/platform/windows/input.cpp +++ b/sunshine/platform/windows/input.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include @@ -17,7 +18,12 @@ using namespace std::literals; using adapteraddrs_t = util::c_ptr; -volatile HDESK _lastKnownInputDesktop = NULL; +volatile HDESK _lastKnownInputDesktop = NULL; +constexpr touch_port_t target_touch_port { + 0, 0, + 65535, 65535 +}; + HDESK pairInputDesktop(); class vigem_t { @@ -165,7 +171,40 @@ input_t input() { return result; } -void abs_mouse(input_t &input, const touch_port_t &touch_port, float x, float y) {} +void send_input(INPUT &i) { +retry: + auto send = SendInput(1, &i, sizeof(INPUT)); + if(send != 1) { + auto hDesk = pairInputDesktop(); + if (_lastKnownInputDesktop != hDesk) { + _lastKnownInputDesktop = hDesk; + goto retry; + } + BOOST_LOG(warning) << "Couldn't send input"sv; + } +} +void abs_mouse(input_t &input, const touch_port_t &touch_port, float x, float y) { + INPUT i {}; + + i.type = INPUT_MOUSE; + auto &mi = i.mi; + + mi.dwFlags = + MOUSEEVENTF_MOVE | + MOUSEEVENTF_ABSOLUTE | + + // MOUSEEVENTF_VIRTUALDESK maps to the entirety of the desktop rather than the primary desktop + MOUSEEVENTF_VIRTUALDESK; + + auto scaled_x = std::lround((x + touch_port.offset_x) * ((float)target_touch_port.width / (float)touch_port.width)); + auto scaled_y = std::lround((y + touch_port.offset_y) * ((float)target_touch_port.height / (float)touch_port.height)); + + mi.dx = scaled_x; + mi.dy = scaled_y; + + send_input(i); +} + void move_mouse(input_t &input, int deltaX, int deltaY) { INPUT i {}; @@ -176,16 +215,7 @@ void move_mouse(input_t &input, int deltaX, int deltaY) { mi.dx = deltaX; mi.dy = deltaY; -retry: - auto send = SendInput(1, &i, sizeof(INPUT)); - if(send != 1) { - auto hDesk = pairInputDesktop(); - if (_lastKnownInputDesktop != hDesk) { - _lastKnownInputDesktop = hDesk; - goto retry; - } - BOOST_LOG(warning) << "Couldn't send mouse movement input"sv; - } + send_input(i); } void button_mouse(input_t &input, int button, bool release) { @@ -228,16 +258,7 @@ void button_mouse(input_t &input, int button, bool release) { return; } -retry: - auto send = SendInput(1, &i, sizeof(INPUT)); - if(send != 1) { - auto hDesk = pairInputDesktop(); - if (_lastKnownInputDesktop != hDesk) { - _lastKnownInputDesktop = hDesk; - goto retry; - } - BOOST_LOG(warning) << "Couldn't send mouse button input"sv; - } + send_input(i); } void scroll(input_t &input, int distance) { @@ -249,16 +270,7 @@ void scroll(input_t &input, int distance) { mi.dwFlags = MOUSEEVENTF_WHEEL; mi.mouseData = distance; -retry: - auto send = SendInput(1, &i, sizeof(INPUT)); - if(send != 1) { - auto hDesk = pairInputDesktop(); - if (_lastKnownInputDesktop != hDesk) { - _lastKnownInputDesktop = hDesk; - goto retry; - } - BOOST_LOG(warning) << "Couldn't send mouse scroll input"sv; - } + send_input(i); } void keyboard(input_t &input, uint16_t modcode, bool release) { @@ -304,16 +316,7 @@ void keyboard(input_t &input, uint16_t modcode, bool release) { ki.dwFlags |= KEYEVENTF_KEYUP; } -retry: - auto send = SendInput(1, &i, sizeof(INPUT)); - if(send != 1) { - auto hDesk = pairInputDesktop(); - if (_lastKnownInputDesktop != hDesk) { - _lastKnownInputDesktop = hDesk; - goto retry; - } - BOOST_LOG(warning) << "Couldn't send keyboard input"sv; - } + send_input(i); } int alloc_gamepad(input_t &input, int nr) { From 5d313b509ed6a4fa8e09a34b42a7dac66600f94d Mon Sep 17 00:00:00 2001 From: loki Date: Wed, 12 May 2021 15:48:39 +0200 Subject: [PATCH 12/35] fix resetting mouse buttons on end stream --- sunshine/input.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/sunshine/input.cpp b/sunshine/input.cpp index ce2a81e3..6a585106 100644 --- a/sunshine/input.cpp +++ b/sunshine/input.cpp @@ -269,6 +269,7 @@ void passthrough(std::shared_ptr &input, PNV_MOUSE_BUTTON_PACKET packet } platf::button_mouse(platf_input, BUTTON_LEFT, release); + mouse_press[BUTTON_LEFT] = false; input->mouse_left_button_timeout = nullptr; }, 10ms).task_id; @@ -521,7 +522,10 @@ void reset(std::shared_ptr &input) { // Ensure input is synchronous, by using the task_pool task_pool.push([]() { for(int x = 0; x < mouse_press.size(); ++x) { - platf::button_mouse(platf_input, x, true); + if(mouse_press[x]) { + platf::button_mouse(platf_input, x, true); + mouse_press[x] = false; + } } for(auto& kp : key_press) { From 9e48e582210496491ee3d5d6786dc0e03b235ac2 Mon Sep 17 00:00:00 2001 From: loki Date: Wed, 12 May 2021 23:01:30 +0200 Subject: [PATCH 13/35] Allow applications started by sunshine to be detached --- README.md | 9 +++++++-- assets/apps_linux.json | 2 +- assets/apps_windows.json | 4 +--- sunshine/process.cpp | 35 ++++++++++++++++++++++++++++++----- sunshine/process.h | 9 +++++++++ 5 files changed, 48 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 102a1207..12490bde 100644 --- a/README.md +++ b/README.md @@ -117,15 +117,20 @@ sunshine needs access to uinput to create mouse and gamepad events: "cmd":"command to open app", "prep-cmd":[ { - "do":"somecommand", - "undo":"undothatcommand" + "do":"some-command", + "undo":"undo-that-command" } + ], + "detached":[ + "some-command", + "another-command" ] } ``` - name: Self explanatory - output : The file where the output of the command is stored - If it is not specified, the output is ignored + - detached: A list of commands to be run and forgotten about - prep-cmd: A list of commands to be run before/after the application - If any of the prep-commands fail, starting the application is aborted - do: Run before the application diff --git a/assets/apps_linux.json b/assets/apps_linux.json index 13950778..1cfeb3d4 100644 --- a/assets/apps_linux.json +++ b/assets/apps_linux.json @@ -13,7 +13,7 @@ "name":"Steam BigPicture", "output":"steam.txt", - "cmd":"steam -bigpicture" + "detached":["setsid steam steam://open/bigpicture"] } ] } diff --git a/assets/apps_windows.json b/assets/apps_windows.json index 512fd497..f2a2a7ec 100644 --- a/assets/apps_windows.json +++ b/assets/apps_windows.json @@ -7,9 +7,7 @@ "name":"Steam BigPicture", "output":"steam.txt", - "prep-cmd":[ - {"do":"steam \"steam://open/bigpicture\""} - ] + "detached":["steam steam://open/bigpicture"] } ] } diff --git a/sunshine/process.cpp b/sunshine/process.cpp index ec2aa2fe..85767eac 100644 --- a/sunshine/process.cpp +++ b/sunshine/process.cpp @@ -80,17 +80,31 @@ int proc_t::execute(int app_id) { auto ret = exe(cmd, _env, _pipe, ec); if(ec) { - BOOST_LOG(error) << "System: "sv << ec.message(); + BOOST_LOG(error) << "Couldn't run ["sv << cmd << "]: System: "sv << ec.message(); return -1; } if(ret != 0) { - BOOST_LOG(error) << "Return code ["sv << ret << ']'; + BOOST_LOG(error) << '[' << cmd << "] failed with code ["sv << ret << ']'; return -1; } } - BOOST_LOG(debug) << "Starting ["sv << proc.cmd << ']'; + for(auto &cmd : proc.detached) { + BOOST_LOG(info) << "Spawning ["sv << cmd << ']'; + if(proc.output.empty()) { + bp::spawn(cmd, _env, bp::std_out > bp::null, bp::std_err > bp::null, ec); + } + else { + bp::spawn(cmd, _env, bp::std_out > _pipe.get(), bp::std_err > _pipe.get(), ec); + } + + if(ec) { + BOOST_LOG(warning) << "Couldn't spawn ["sv << cmd << "]: System: "sv << ec.message(); + } + } + + BOOST_LOG(info) << "Executing: ["sv << proc.cmd << ']'; if(proc.cmd.empty()) { placebo = true; } @@ -102,7 +116,7 @@ int proc_t::execute(int app_id) { } if(ec) { - BOOST_LOG(info) << "System: "sv << ec.message(); + BOOST_LOG(warning) << "Couldn't run ["sv << proc.cmd << "]: System: "sv << ec.message(); return -1; } @@ -251,12 +265,12 @@ std::optional parse(const std::string& file_name) { proc::ctx_t ctx; auto prep_nodes_opt = app_node.get_child_optional("prep-cmd"s); + auto detached_nodes_opt = app_node.get_child_optional("detached"s); auto output = app_node.get_optional("output"s); auto name = parse_env_val(this_env, app_node.get("name"s)); auto cmd = app_node.get_optional("cmd"s); std::vector prep_cmds; - if(prep_nodes_opt) { auto &prep_nodes = *prep_nodes_opt; @@ -274,6 +288,16 @@ std::optional parse(const std::string& file_name) { } } + std::vector detached; + if(detached_nodes_opt) { + auto &detached_nodes = *detached_nodes_opt; + + detached.reserve(detached_nodes.size()); + for(auto &[_, detached_val] : detached_nodes) { + detached.emplace_back(parse_env_val(this_env, detached_val.get_value())); + } + } + if(output) { ctx.output = parse_env_val(this_env, *output); } @@ -284,6 +308,7 @@ std::optional parse(const std::string& file_name) { ctx.name = std::move(name); ctx.prep_cmds = std::move(prep_cmds); + ctx.detached = std::move(detached); apps.emplace_back(std::move(ctx)); } diff --git a/sunshine/process.h b/sunshine/process.h index 37c17acd..e267fa44 100644 --- a/sunshine/process.h +++ b/sunshine/process.h @@ -30,6 +30,7 @@ struct cmd_t { }; /* * pre_cmds -- guaranteed to be executed unless any of the commands fail. + * detached -- commands detached from Sunshine * cmd -- Runs indefinitely until: * No session is running and a different set of commands it to be executed * Command exits @@ -41,6 +42,14 @@ struct cmd_t { struct ctx_t { std::vector prep_cmds; + /** + * Some applications, such as Steam, + * either exit quickly, or keep running indefinitely. + * Steam.exe is one such application. + * That is why some applications need be run and forgotten about + */ + std::vector detached; + std::string name; std::string cmd; std::string output; From 1050978246ce203b6f352b75499454e6a2773638 Mon Sep 17 00:00:00 2001 From: loki Date: Wed, 12 May 2021 23:22:13 +0200 Subject: [PATCH 14/35] Add warning to ignore errors during encoder validation --- sunshine/main.cpp | 2 -- sunshine/video.cpp | 24 ++++++++++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/sunshine/main.cpp b/sunshine/main.cpp index 801493aa..9dc686a9 100644 --- a/sunshine/main.cpp +++ b/sunshine/main.cpp @@ -15,7 +15,6 @@ #include #include "video.h" -#include "input.h" #include "nvhttp.h" #include "rtsp.h" #include "config.h" @@ -142,7 +141,6 @@ int main(int argc, char *argv[]) { return 4; } - input::init(); reed_solomon_init(); if(video::init()) { return 2; diff --git a/sunshine/video.cpp b/sunshine/video.cpp index 54252375..3127ccbc 100644 --- a/sunshine/video.cpp +++ b/sunshine/video.cpp @@ -1146,6 +1146,11 @@ bool validate_config(std::shared_ptr &disp, const encoder_t &e bool validate_encoder(encoder_t &encoder) { std::shared_ptr disp; + BOOST_LOG(info) << "Trying encoder ["sv << encoder.name << ']'; + auto fg = util::fail_guard([&]() { + BOOST_LOG(info) << "Encoder ["sv << encoder.name << "] failed"sv; + }); + auto force_hevc = config::video.hevc_mode >= 2; auto test_hevc = force_hevc || (config::video.hevc_mode == 0 && encoder.hevc_mode); @@ -1200,10 +1205,21 @@ bool validate_encoder(encoder_t &encoder) { } } + fg.disable(); return true; } int init() { + // video depends on input for input::touch_port_event + input::init(); + + BOOST_LOG(info) << "//////////////////////////////////////////////////////////////////"sv; + BOOST_LOG(info) << "// //"sv; + BOOST_LOG(info) << "// Testing for available encoders, this may generate errors. //"sv; + BOOST_LOG(info) << "// You can safely ignore those errors. //"sv; + BOOST_LOG(info) << "// //"sv; + BOOST_LOG(info) << "//////////////////////////////////////////////////////////////////"sv; + KITTY_WHILE_LOOP(auto pos = std::begin(encoders), pos != std::end(encoders), { if( (!config::video.encoder.empty() && pos->name != config::video.encoder) || @@ -1229,6 +1245,14 @@ int init() { return -1; } + BOOST_LOG(info); + BOOST_LOG(info) << "//////////////////////////////////////////////////////////////"sv; + BOOST_LOG(info) << "// //"sv; + BOOST_LOG(info) << "// Ignore any errors mentioned above, they are not relevant //"sv; + BOOST_LOG(info) << "// //"sv; + BOOST_LOG(info) << "//////////////////////////////////////////////////////////////"sv; + BOOST_LOG(info); + auto &encoder = encoders.front(); if(encoder.hevc[encoder_t::PASSED]) { BOOST_LOG(info) << "Found encoder "sv << encoder.name << ": ["sv << encoder.h264.name << ", "sv << encoder.hevc.name << ']'; From 381e8bcfaafe550070b09cc8361f831ffe4c98fd Mon Sep 17 00:00:00 2001 From: loki Date: Thu, 13 May 2021 15:26:08 +0200 Subject: [PATCH 15/35] Prepare for release --- gen-deb.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gen-deb.in b/gen-deb.in index cc7c6a0a..ea4e671e 100755 --- a/gen-deb.in +++ b/gen-deb.in @@ -35,7 +35,7 @@ Package: sunshine Architecture: amd64 Maintainer: @loki Priority: optional -Version: 0.2.1 +Version: 0.3.1 Depends: libssl1.1, libavdevice58, libboost-thread1.71.0, libboost-filesystem1.71.0, libboost-log1.71.0, libpulse0, libopus0, libxcb-shm0, libxcb-xfixes0 Description: Gamestream host for Moonlight EOF From 3901e404a97e2502397be27d90e7f2a0a9a65982 Mon Sep 17 00:00:00 2001 From: loki Date: Fri, 14 May 2021 21:32:46 +0200 Subject: [PATCH 16/35] Add forgotten requirement for compilation on ubuntu --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 12490bde..627d51c6 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ Sunshine is a Gamestream host for Moonlight Ubuntu 20.04: Install the following ``` -sudo apt install cmake libssl-dev libavdevice-dev libboost-thread-dev libboost-filesystem-dev libboost-log-dev libpulse-dev libopus-dev libxtst-dev libx11-dev libxfixes-dev libevdev-dev libxcb1-dev libxcb-shm0-dev libxcb-xfixes0-dev +sudo apt install cmake libssl-dev libavdevice-dev libboost-thread-dev libboost-filesystem-dev libboost-log-dev libpulse-dev libopus-dev libxtst-dev libx11-dev libxrandr-dev libxfixes-dev libevdev-dev libxcb1-dev libxcb-shm0-dev libxcb-xfixes0-dev ``` ### Compilation: From 33a330fd6c198fdb14f3da77233c69f5d21c4c92 Mon Sep 17 00:00:00 2001 From: loki Date: Fri, 14 May 2021 21:44:20 +0200 Subject: [PATCH 17/35] Downmix surround 5.1 to stereo --- sunshine/audio.cpp | 10 +- sunshine/platform/common.h | 2 +- sunshine/platform/linux/display.cpp | 2 +- sunshine/platform/windows/audio.cpp | 249 ++++++++++++++++++++-------- tools/audio.cpp | 171 ++++++++++++------- 5 files changed, 305 insertions(+), 129 deletions(-) diff --git a/sunshine/audio.cpp b/sunshine/audio.cpp index 4e13cc8f..7ab716e4 100644 --- a/sunshine/audio.cpp +++ b/sunshine/audio.cpp @@ -94,16 +94,16 @@ void capture(safe::signal_t *shutdown_event, packet_queue_t packets, config_t co //FIXME: Pick correct opus_stream_config_t based on config.channels auto stream = &stereo; - auto mic = platf::microphone(stream->sampleRate); + auto frame_size = config.packetDuration * stream->sampleRate / 1000; + int samples_per_frame = frame_size * stream->channelCount; + + auto mic = platf::microphone(stream->sampleRate, frame_size); if(!mic) { BOOST_LOG(error) << "Couldn't create audio input"sv ; return; } - auto frame_size = config.packetDuration * stream->sampleRate / 1000; - int samples_per_frame = frame_size * stream->channelCount; - while(!shutdown_event->peek()) { std::vector sample_buffer; sample_buffer.resize(samples_per_frame); @@ -116,7 +116,7 @@ void capture(safe::signal_t *shutdown_event, packet_queue_t packets, config_t co continue; case platf::capture_e::reinit: mic.reset(); - mic = platf::microphone(stream->sampleRate); + mic = platf::microphone(stream->sampleRate, frame_size); if(!mic) { BOOST_LOG(error) << "Couldn't re-initialize audio input"sv ; diff --git a/sunshine/platform/common.h b/sunshine/platform/common.h index 96c3d84e..2f414509 100644 --- a/sunshine/platform/common.h +++ b/sunshine/platform/common.h @@ -157,7 +157,7 @@ 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::unique_ptr microphone(std::uint32_t sample_rate); +std::unique_ptr microphone(std::uint32_t sample_rate, std::uint32_t frame_size); std::shared_ptr display(dev_type_e hwdevice_type); input_t input(); diff --git a/sunshine/platform/linux/display.cpp b/sunshine/platform/linux/display.cpp index 4ca09f59..79bd437b 100644 --- a/sunshine/platform/linux/display.cpp +++ b/sunshine/platform/linux/display.cpp @@ -410,7 +410,7 @@ std::shared_ptr display(platf::dev_type_e hwdevice_type) { return x11_disp; } -std::unique_ptr microphone(std::uint32_t sample_rate) { +std::unique_ptr microphone(std::uint32_t sample_rate, std::uint32_t) { auto mic = std::make_unique(PA_SAMPLE_S16LE, sample_rate, 2); int status; diff --git a/sunshine/platform/windows/audio.cpp b/sunshine/platform/windows/audio.cpp index 63369ccb..dea262f5 100644 --- a/sunshine/platform/windows/audio.cpp +++ b/sunshine/platform/windows/audio.cpp @@ -49,10 +49,154 @@ public: } }; +struct format_t { + std::string_view name; + int channels; + int channel_mask; +} formats [] { + { + "Stereo"sv, + 2, + SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT + }, + { + "Mono"sv, + 1, + SPEAKER_FRONT_CENTER + }, + { + "Surround 5.1"sv, + 6, + SPEAKER_FRONT_LEFT | + SPEAKER_FRONT_RIGHT | + SPEAKER_FRONT_CENTER | + SPEAKER_LOW_FREQUENCY | + SPEAKER_BACK_LEFT | + SPEAKER_BACK_RIGHT + } +}; + +void set_wave_format(audio::wave_format_t &wave_format, const format_t &format) { + wave_format->nChannels = format.channels; + wave_format->nBlockAlign = wave_format->nChannels * wave_format->wBitsPerSample / 8; + wave_format->nAvgBytesPerSec = wave_format->nSamplesPerSec * wave_format->nBlockAlign; + + if(wave_format->wFormatTag == WAVE_FORMAT_EXTENSIBLE) { + ((PWAVEFORMATEXTENSIBLE)wave_format.get())->dwChannelMask = format.channel_mask; + } +} + +void surround51_to_stereo(std::vector &sample_in, const util::buffer_t &sample_out) { + enum surround51_e : int { + front_left, + front_right, + front_center, + low_frequency, // subwoofer + back_left, + back_right, + channels51 // number of channels in surround sound + }; + + auto sample_in_pos = std::begin(sample_in); + auto sample_end = std::begin(sample_out) + sample_in.size() / 2 * channels51; + + for(auto sample_out_p = std::begin(sample_out); sample_out_p != sample_end; sample_out_p += channels51) { + std::uint32_t left {}, right {}; + + left += sample_out_p[front_left]; + left += sample_out_p[front_center] * 90 / 100; + left += sample_out_p[low_frequency] * 30 / 100; + left += sample_out_p[back_left] * 70 / 100; + left += sample_out_p[back_right] * 30 / 100; + + right += sample_out_p[front_right]; + right += sample_out_p[front_center] * 90 / 100; + right += sample_out_p[low_frequency] * 30 / 100; + right += sample_out_p[back_left] * 30 / 100; + right += sample_out_p[back_right] * 70 / 100;; + + *sample_in_pos++ = (std::uint16_t)left; + *sample_in_pos++ = (std::uint16_t)right; + } +} + +void mono_to_stereo(std::vector &sample_in, const util::buffer_t &sample_out) { + auto sample_in_pos = std::begin(sample_in); + auto sample_end = std::begin(sample_out) + sample_in.size() / 2; + + for(auto sample_out_p = std::begin(sample_out); sample_out_p != sample_end; ++sample_out_p) { + *sample_in_pos++ = *sample_out_p; + *sample_in_pos++ = *sample_out_p; + } +} + +audio_client_t make_audio_client(device_t &device, const format_t &format, int sample_rate) { + audio_client_t audio_client; + auto status = device->Activate( + IID_IAudioClient, + CLSCTX_ALL, + nullptr, + (void **)&audio_client); + + if(FAILED(status)) { + BOOST_LOG(error) << "Couldn't activate Device: [0x"sv << util::hex(status).to_string_view() << ']'; + + return nullptr; + } + + wave_format_t wave_format; + status = audio_client->GetMixFormat(&wave_format); + + if(FAILED(status)) { + BOOST_LOG(error) << "Couldn't acquire Wave Format [0x"sv << util::hex(status).to_string_view() << ']'; + + return nullptr; + } + + wave_format->wBitsPerSample = 16; + wave_format->nSamplesPerSec = sample_rate; + switch(wave_format->wFormatTag) { + case WAVE_FORMAT_PCM: + break; + case WAVE_FORMAT_IEEE_FLOAT: + break; + case WAVE_FORMAT_EXTENSIBLE: { + auto wave_ex = (PWAVEFORMATEXTENSIBLE) wave_format.get(); + if (IsEqualGUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, wave_ex->SubFormat)) { + wave_ex->SubFormat = KSDATAFORMAT_SUBTYPE_PCM; + wave_ex->Samples.wValidBitsPerSample = 16; + break; + } + + BOOST_LOG(error) << "Unsupported Sub Format for WAVE_FORMAT_EXTENSIBLE: [0x"sv << util::hex(wave_ex->SubFormat).to_string_view() << ']'; + } + default: + BOOST_LOG(error) << "Unsupported Wave Format: [0x"sv << util::hex(wave_format->wFormatTag).to_string_view() << ']'; + return nullptr; + }; + + set_wave_format(wave_format, format); + + status = audio_client->Initialize( + AUDCLNT_SHAREMODE_SHARED, + AUDCLNT_STREAMFLAGS_LOOPBACK | AUDCLNT_STREAMFLAGS_EVENTCALLBACK, + 0, 0, + wave_format.get(), + nullptr); + + if(status) { + BOOST_LOG(debug) << "Couldn't initialize audio client for ["sv << format.name << "]: [0x"sv << util::hex(status).to_string_view() << ']'; + return nullptr; + } + + return audio_client; +} + class mic_wasapi_t : public mic_t { public: capture_e sample(std::vector &sample_in) override { - while(sample_buf_pos - std::begin(sample_buf) < sample_in.size()) { + auto sample_size = sample_in.size() /2 * format->channels; + while(sample_buf_pos - std::begin(sample_buf) < sample_size) { //FIXME: Use IAudioClient3 instead of IAudioClient, that would allows for adjusting the latency of the audio samples auto capture_result = _fill_buffer(); @@ -61,17 +205,32 @@ public: } } - std::copy_n(std::begin(sample_buf), sample_in.size(), std::begin(sample_in)); + switch(format->channels) { + case 1: + mono_to_stereo(sample_in, sample_buf); + break; + case 2: + std::copy_n(std::begin(sample_buf), sample_size, std::begin(sample_in)); + break; + case 6: + if(format->name == "Surround 5.1"sv) { + surround51_to_stereo(sample_in, sample_buf); + break; + } + + BOOST_LOG(error) << '[' << format->name << "] not yet supported"sv; + return capture_e::error; + } // The excess samples should be in front of the queue - std::move(&sample_buf[sample_in.size()], sample_buf_pos, std::begin(sample_buf)); - sample_buf_pos -= sample_in.size(); + std::move(&sample_buf[sample_size], sample_buf_pos, std::begin(sample_buf)); + sample_buf_pos -= sample_size; return capture_e::ok; } - int init(std::uint32_t sample_rate) { + int init(std::uint32_t sample_rate, std::uint32_t frame_size) { audio_event.reset(CreateEventA(nullptr, FALSE, FALSE, nullptr)); if(!audio_event) { BOOST_LOG(error) << "Couldn't create Event handle"sv; @@ -113,70 +272,26 @@ public: return -1; } - status = device->Activate( - IID_IAudioClient, - CLSCTX_ALL, - nullptr, - (void **) &audio_client); + for(auto &format : formats) { + BOOST_LOG(debug) << "Trying audio format ["sv << format.name << ']'; + audio_client = make_audio_client(device, format, sample_rate); - if (FAILED(status)) { - BOOST_LOG(error) << "Couldn't activate audio Device [0x"sv << util::hex(status).to_string_view() << ']'; - - return -1; - } - - status = audio_client->GetMixFormat(&wave_format); - if(FAILED(status)) { - BOOST_LOG(error) << "Couldn't acquire Wave Format [0x"sv << util::hex(status).to_string_view() << ']'; - - return -1; - } - - wave_format->nChannels = 2; - wave_format->wBitsPerSample = 16; - wave_format->nSamplesPerSec = sample_rate; - wave_format->nBlockAlign = wave_format->nChannels * wave_format->wBitsPerSample / 8; - wave_format->nAvgBytesPerSec = wave_format->nSamplesPerSec * wave_format->nBlockAlign; - - switch(wave_format->wFormatTag) { - case WAVE_FORMAT_PCM: + if(audio_client) { + BOOST_LOG(debug) << "Found audio format ["sv << format.name << ']'; + this->format = &format; break; - case WAVE_FORMAT_IEEE_FLOAT: - wave_format->wFormatTag = WAVE_FORMAT_PCM; - break; - case WAVE_FORMAT_EXTENSIBLE: { - auto wave_ex = (PWAVEFORMATEXTENSIBLE) wave_format.get(); - if (IsEqualGUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, wave_ex->SubFormat)) { - wave_ex->SubFormat = KSDATAFORMAT_SUBTYPE_PCM; - wave_ex->Samples.wValidBitsPerSample = 16; - break; - } - - BOOST_LOG(error) << "Unsupported Sub Format for WAVE_FORMAT_EXTENSIBLE: [0x"sv << util::hex(wave_ex->SubFormat).to_string_view() << ']'; - return -1; } - default: - BOOST_LOG(error) << "Unsupported Wave Format: [0x"sv << util::hex(wave_format->wFormatTag).to_string_view() << ']'; - return -1; - }; + } + + if(!audio_client) { + BOOST_LOG(error) << "Couldn't find supported format for audio"sv; + return -1; + } REFERENCE_TIME default_latency; audio_client->GetDevicePeriod(&default_latency, nullptr); default_latency_ms = default_latency / 1000; - status = audio_client->Initialize( - AUDCLNT_SHAREMODE_SHARED, - AUDCLNT_STREAMFLAGS_LOOPBACK | AUDCLNT_STREAMFLAGS_EVENTCALLBACK, - 0, 0, - wave_format.get(), - nullptr); - - if (FAILED(status)) { - BOOST_LOG(error) << "Couldn't initialize audio client [0x"sv << util::hex(status).to_string_view() << ']'; - - return -1; - } - std::uint32_t frames; status = audio_client->GetBufferSize(&frames); if (FAILED(status)) { @@ -185,7 +300,8 @@ public: return -1; } - sample_buf = util::buffer_t { frames }; + // *2 --> needs to fit double + sample_buf = util::buffer_t { std::max(frames *2, frame_size * format->channels *2) }; sample_buf_pos = std::begin(sample_buf); status = audio_client->GetService(IID_IAudioCaptureClient, (void**)&audio_capture); @@ -267,7 +383,7 @@ private: } sample_aligned.uninitialized = std::end(sample_buf) - sample_buf_pos; - auto n = std::min(sample_aligned.uninitialized, block_aligned.audio_sample_size * wave_format->nChannels); + auto n = std::min(sample_aligned.uninitialized, block_aligned.audio_sample_size * format->channels); if (buffer_flags & AUDCLNT_BUFFERFLAGS_SILENT) { std::fill_n(sample_buf_pos, n, 0); @@ -296,13 +412,14 @@ public: device_enum_t device_enum; device_t device; audio_client_t audio_client; - wave_format_t wave_format; audio_capture_t audio_capture; REFERENCE_TIME default_latency_ms; util::buffer_t sample_buf; std::int16_t *sample_buf_pos; + + format_t *format; }; } @@ -313,10 +430,10 @@ namespace dxgi { int init(); } -std::unique_ptr microphone(std::uint32_t sample_rate) { +std::unique_ptr microphone(std::uint32_t sample_rate, std::uint32_t frame_size) { auto mic = std::make_unique(); - if(mic->init(sample_rate)) { + if(mic->init(sample_rate, frame_size)) { return nullptr; } diff --git a/tools/audio.cpp b/tools/audio.cpp index 05717b23..18556c50 100644 --- a/tools/audio.cpp +++ b/tools/audio.cpp @@ -26,7 +26,9 @@ const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator); const IID IID_IAudioClient = __uuidof(IAudioClient); const IID IID_IAudioCaptureClient = __uuidof(IAudioCaptureClient); +constexpr auto SAMPLE_RATE = 48000; int device_state_filter = DEVICE_STATE_ACTIVE; + namespace audio { template void Release(T *p) { @@ -66,19 +68,114 @@ public: const wchar_t *no_null(const wchar_t *str) { return str ? str : L"Unknown"; } -void print_device(device_t &device) { - HRESULT status; - audio::wstring_t::pointer wstring_p {}; +struct format_t { + std::string_view name; + int channels; + int channel_mask; +} formats [] { + { + "Mono"sv, + 1, + SPEAKER_FRONT_CENTER + }, + { + "Stereo"sv, + 2, + SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT + }, + { + "Surround 5.1"sv, + 6, + SPEAKER_FRONT_LEFT | + SPEAKER_FRONT_RIGHT | + SPEAKER_FRONT_CENTER | + SPEAKER_LOW_FREQUENCY | + SPEAKER_BACK_LEFT | + SPEAKER_BACK_RIGHT + } +}; + +void set_wave_format(audio::wave_format_t &wave_format, const format_t &format) { + wave_format->nChannels = format.channels; + wave_format->nBlockAlign = wave_format->nChannels * wave_format->wBitsPerSample / 8; + wave_format->nAvgBytesPerSec = wave_format->nSamplesPerSec * wave_format->nBlockAlign; + + if(wave_format->wFormatTag == WAVE_FORMAT_EXTENSIBLE) { + ((PWAVEFORMATEXTENSIBLE)wave_format.get())->dwChannelMask = format.channel_mask; + } +} + +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); + + if(FAILED(status)) { + std::cout << "Couldn't activate Device: [0x"sv << util::hex(status).to_string_view() << ']' << std::endl; + + return nullptr; + } + + wave_format_t wave_format; + status = audio_client->GetMixFormat(&wave_format); + + if (FAILED(status)) { + std::cout << "Couldn't acquire Wave Format [0x"sv << util::hex(status).to_string_view() << ']' << std::endl; + + return nullptr; + } + + wave_format->wBitsPerSample = 16; + wave_format->nSamplesPerSec = SAMPLE_RATE; + switch(wave_format->wFormatTag) { + case WAVE_FORMAT_PCM: + break; + case WAVE_FORMAT_IEEE_FLOAT: + break; + case WAVE_FORMAT_EXTENSIBLE: { + auto wave_ex = (PWAVEFORMATEXTENSIBLE) wave_format.get(); + if (IsEqualGUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, wave_ex->SubFormat)) { + wave_ex->SubFormat = KSDATAFORMAT_SUBTYPE_PCM; + wave_ex->Samples.wValidBitsPerSample = 16; + break; + } + + std::cout << "Unsupported Sub Format for WAVE_FORMAT_EXTENSIBLE: [0x"sv << util::hex(wave_ex->SubFormat).to_string_view() << ']' << std::endl; + } + default: + std::cout << "Unsupported Wave Format: [0x"sv << util::hex(wave_format->wFormatTag).to_string_view() << ']' << std::endl; + return nullptr; + }; + + set_wave_format(wave_format, format); + + status = audio_client->Initialize( + AUDCLNT_SHAREMODE_SHARED, + AUDCLNT_STREAMFLAGS_LOOPBACK | AUDCLNT_STREAMFLAGS_EVENTCALLBACK, + 0, 0, + wave_format.get(), + nullptr); + + if(status) { + return nullptr; + } + + return audio_client; +} + +void print_device(device_t &device) { + audio::wstring_t wstring; DWORD device_state; device->GetState(&device_state); - device->GetId(&wstring_p); - audio::wstring_t wstring { wstring_p }; + device->GetId(&wstring); - audio::prop_t::pointer prop_p {}; - device->OpenPropertyStore(STGM_READ, &prop_p); - audio::prop_t prop { prop_p }; + audio::prop_t prop; + device->OpenPropertyStore(STGM_READ, &prop); prop_var_t adapter_friendly_name; prop_var_t device_friendly_name; @@ -120,47 +217,12 @@ void print_device(device_t &device) { return; } - // Ensure WaveFromat is compatible - audio_client_t::pointer audio_client_p{}; - status = device->Activate( - IID_IAudioClient, - CLSCTX_ALL, - nullptr, - (void **) &audio_client_p); - audio_client_t audio_client { audio_client_p }; + for(const auto &format : formats) { + // Ensure WaveFromat is compatible + auto audio_client = make_audio_client(device, format); - if (FAILED(status)) { - std::cout << "Couldn't activate Device: [0x"sv << util::hex(status).to_string_view() << ']' << std::endl; - - return; + std::cout << format.name << ": "sv << (!audio_client ? "unsupported"sv : "supported"sv) << std::endl; } - - wave_format_t::pointer wave_format_p{}; - status = audio_client->GetMixFormat(&wave_format_p); - wave_format_t wave_format { wave_format_p }; - - if (FAILED(status)) { - std::cout << "Couldn't acquire Wave Format [0x"sv << util::hex(status).to_string_view() << ']' << std::endl; - - return; - } - - switch(wave_format->wFormatTag) { - case WAVE_FORMAT_PCM: - break; - case WAVE_FORMAT_IEEE_FLOAT: - break; - case WAVE_FORMAT_EXTENSIBLE: { - auto wave_ex = (PWAVEFORMATEXTENSIBLE) wave_format.get(); - if (IsEqualGUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, wave_ex->SubFormat)) { - break; - } - - std::cout << "Unsupported Sub Format for WAVE_FORMAT_EXTENSIBLE: [0x"sv << util::hex(wave_ex->SubFormat).to_string_view() << ']' << std::endl; - } - default: - std::cout << "Unsupported Wave Format: [0x"sv << util::hex(wave_format->wFormatTag).to_string_view() << ']' << std::endl; - }; } } @@ -213,14 +275,13 @@ int main(int argc, char *argv[]) { HRESULT status; - audio::device_enum_t::pointer device_enum_p{}; + audio::device_enum_t device_enum; status = CoCreateInstance( CLSID_MMDeviceEnumerator, nullptr, CLSCTX_ALL, IID_IMMDeviceEnumerator, - (void **) &device_enum_p); - audio::device_enum_t device_enum { device_enum_p }; + (void **) &device_enum); if (FAILED(status)) { std::cout << "Couldn't create Device Enumerator: [0x"sv << util::hex(status).to_string_view() << ']' << std::endl; @@ -228,9 +289,8 @@ int main(int argc, char *argv[]) { return -1; } - audio::collection_t::pointer collection_p {}; - status = device_enum->EnumAudioEndpoints(eRender, DEVICE_STATEMASK_ALL, &collection_p); - audio::collection_t collection { collection_p }; + audio::collection_t collection; + status = device_enum->EnumAudioEndpoints(eRender, DEVICE_STATEMASK_ALL, &collection); if (FAILED(status)) { std::cout << "Couldn't enumerate: [0x"sv << util::hex(status).to_string_view() << ']' << std::endl; @@ -243,9 +303,8 @@ int main(int argc, char *argv[]) { std::cout << "====== Found "sv << count << " potential audio devices ======"sv << std::endl; for(auto x = 0; x < count; ++x) { - audio::device_t::pointer device_p {}; - collection->Item(x, &device_p); - audio::device_t device { device_p }; + audio::device_t device; + collection->Item(x, &device); audio::print_device(device); } From a6c164949369b6528ac466a78e26f759e2737435 Mon Sep 17 00:00:00 2001 From: loki Date: Sat, 15 May 2021 17:01:03 +0200 Subject: [PATCH 18/35] Remove redundent code --- sunshine/platform/windows/audio.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/sunshine/platform/windows/audio.cpp b/sunshine/platform/windows/audio.cpp index dea262f5..4f453ac0 100644 --- a/sunshine/platform/windows/audio.cpp +++ b/sunshine/platform/windows/audio.cpp @@ -213,11 +213,9 @@ public: std::copy_n(std::begin(sample_buf), sample_size, std::begin(sample_in)); break; case 6: - if(format->name == "Surround 5.1"sv) { - surround51_to_stereo(sample_in, sample_buf); - break; - } - + surround51_to_stereo(sample_in, sample_buf); + break; + default: BOOST_LOG(error) << '[' << format->name << "] not yet supported"sv; return capture_e::error; } From 3d8a99f541ea4c8dbf45404508caf6a5d03b50c9 Mon Sep 17 00:00:00 2001 From: loki Date: Mon, 17 May 2021 21:21:57 +0200 Subject: [PATCH 19/35] clang-format --- .clang-format | 67 +++ sunshine/audio.cpp | 79 ++-- sunshine/audio.h | 8 +- sunshine/config.cpp | 171 ++++---- sunshine/config.h | 12 +- sunshine/crypto.cpp | 50 +-- sunshine/crypto.h | 26 +- sunshine/input.cpp | 182 ++++---- sunshine/input.h | 4 +- sunshine/main.cpp | 58 +-- sunshine/main.h | 2 +- sunshine/move_by_copy.h | 21 +- sunshine/network.cpp | 26 +- sunshine/network.h | 6 +- sunshine/nvhttp.cpp | 160 +++---- sunshine/nvhttp.h | 4 +- sunshine/platform/common.h | 31 +- sunshine/platform/linux/display.cpp | 181 ++++---- sunshine/platform/linux/input.cpp | 262 +++++------ sunshine/platform/windows/audio.cpp | 155 ++++--- sunshine/platform/windows/display.h | 16 +- sunshine/platform/windows/display_base.cpp | 92 ++-- sunshine/platform/windows/display_ram.cpp | 116 ++--- sunshine/platform/windows/display_vram.cpp | 270 ++++++------ sunshine/platform/windows/input.cpp | 113 ++--- sunshine/process.cpp | 73 ++-- sunshine/process.h | 15 +- sunshine/round_robin.h | 51 ++- sunshine/rtsp.cpp | 183 ++++---- sunshine/rtsp.h | 2 +- sunshine/stream.cpp | 246 ++++++----- sunshine/stream.h | 6 +- sunshine/sync.h | 11 +- sunshine/task_pool.h | 57 +-- sunshine/thread_pool.h | 22 +- sunshine/thread_safe.h | 76 ++-- sunshine/utility.h | 284 ++++++------ sunshine/uuid.h | 12 +- sunshine/video.cpp | 480 ++++++++++----------- sunshine/video.h | 22 +- tools/CMakeLists.txt | 4 +- tools/audio.cpp | 113 +++-- tools/dxgi.cpp | 20 +- 43 files changed, 1917 insertions(+), 1872 deletions(-) create mode 100644 .clang-format diff --git a/.clang-format b/.clang-format new file mode 100644 index 00000000..a069b562 --- /dev/null +++ b/.clang-format @@ -0,0 +1,67 @@ +# Generated from CLion C/C++ Code Style settings +BasedOnStyle: LLVM +AccessModifierOffset: -2 +AlignAfterOpenBracket: DontAlign +AlignConsecutiveAssignments: AcrossComments +AlignOperands: Align +AllowAllArgumentsOnNextLine: false +AllowAllConstructorInitializersOnNextLine: false +AllowAllParametersOfDeclarationOnNextLine: false +AllowShortBlocksOnASingleLine: Always +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: All +AllowShortIfStatementsOnASingleLine: Always +AllowShortLambdasOnASingleLine: All +AllowShortLoopsOnASingleLine: true +AlwaysBreakAfterReturnType: None +AlwaysBreakTemplateDeclarations: Yes +BreakBeforeBraces: Custom +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: Never + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterUnion: false + BeforeCatch: true + BeforeElse: true + IndentBraces: false + SplitEmptyFunction: false + SplitEmptyRecord: true +BreakBeforeBinaryOperators: None +BreakBeforeTernaryOperators: false +BreakConstructorInitializers: BeforeColon +BreakInheritanceList: BeforeColon +ColumnLimit: 0 +CompactNamespaces: false +ContinuationIndentWidth: 2 +IndentCaseLabels: false +IndentPPDirectives: None +IndentWidth: 2 +KeepEmptyLinesAtTheStartOfBlocks: true +MaxEmptyLinesToKeep: 2 +NamespaceIndentation: None +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PointerAlignment: Right +ReflowComments: false +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: false +SpaceBeforeAssignmentOperators: true +SpaceBeforeCpp11BracedList: true +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: Never +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInCStyleCastParentheses: false +SpacesInContainerLiterals: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +TabWidth: 2 +Cpp11BracedListStyle: false +UseTab: Never diff --git a/sunshine/audio.cpp b/sunshine/audio.cpp index 7ab716e4..c9ddab76 100644 --- a/sunshine/audio.cpp +++ b/sunshine/audio.cpp @@ -4,14 +4,14 @@ #include "platform/common.h" -#include "utility.h" -#include "thread_safe.h" #include "audio.h" #include "main.h" +#include "thread_safe.h" +#include "utility.h" namespace audio { using namespace std::literals; -using opus_t = util::safe_ptr; +using opus_t = util::safe_ptr; using sample_queue_t = std::shared_ptr>>; struct opus_stream_config_t { @@ -23,31 +23,31 @@ struct opus_stream_config_t { }; constexpr std::uint8_t map_stereo[] { 0, 1 }; -constexpr std::uint8_t map_surround51[] {0, 4, 1, 5, 2, 3}; -constexpr std::uint8_t map_high_surround51[] {0, 1, 2, 3, 4, 5}; -constexpr auto SAMPLE_RATE = 48000; +constexpr std::uint8_t map_surround51[] { 0, 4, 1, 5, 2, 3 }; +constexpr std::uint8_t map_high_surround51[] { 0, 1, 2, 3, 4, 5 }; +constexpr auto SAMPLE_RATE = 48000; static opus_stream_config_t stereo = { - SAMPLE_RATE, - 2, - 1, - 1, - map_stereo + SAMPLE_RATE, + 2, + 1, + 1, + map_stereo }; static opus_stream_config_t Surround51 = { - SAMPLE_RATE, - 6, - 4, - 2, - map_surround51 + SAMPLE_RATE, + 6, + 4, + 2, + map_surround51 }; static opus_stream_config_t HighSurround51 = { - SAMPLE_RATE, - 6, - 6, - 0, - map_high_surround51 + SAMPLE_RATE, + 6, + 6, + 0, + map_high_surround51 }; void encodeThread(packet_queue_t packets, sample_queue_t samples, config_t config, void *channel_data) { @@ -60,12 +60,11 @@ void encodeThread(packet_queue_t packets, sample_queue_t samples, config_t confi stream->coupledStreams, stream->mapping, OPUS_APPLICATION_AUDIO, - nullptr) - }; + nullptr) }; auto frame_size = config.packetDuration * stream->sampleRate / 1000; while(auto sample = samples->pop()) { - packet_t packet { 16*1024 }; // 16KB + packet_t packet { 16 * 1024 }; // 16KB int bytes = opus_multistream_encode(opus.get(), sample->data(), frame_size, std::begin(packet), packet.size()); if(bytes < 0) { @@ -94,12 +93,12 @@ void capture(safe::signal_t *shutdown_event, packet_queue_t packets, config_t co //FIXME: Pick correct opus_stream_config_t based on config.channels auto stream = &stereo; - auto frame_size = config.packetDuration * stream->sampleRate / 1000; + auto frame_size = config.packetDuration * stream->sampleRate / 1000; int samples_per_frame = frame_size * stream->channelCount; auto mic = platf::microphone(stream->sampleRate, frame_size); if(!mic) { - BOOST_LOG(error) << "Couldn't create audio input"sv ; + BOOST_LOG(error) << "Couldn't create audio input"sv; return; } @@ -110,24 +109,24 @@ void capture(safe::signal_t *shutdown_event, packet_queue_t packets, config_t co auto status = mic->sample(sample_buffer); switch(status) { - case platf::capture_e::ok: - break; - case platf::capture_e::timeout: - continue; - case platf::capture_e::reinit: - mic.reset(); - mic = platf::microphone(stream->sampleRate, frame_size); - if(!mic) { - BOOST_LOG(error) << "Couldn't re-initialize audio input"sv ; + case platf::capture_e::ok: + break; + case platf::capture_e::timeout: + continue; + case platf::capture_e::reinit: + mic.reset(); + mic = platf::microphone(stream->sampleRate, frame_size); + if(!mic) { + BOOST_LOG(error) << "Couldn't re-initialize audio input"sv; - return; - } - return; - default: return; + } + return; + default: + return; } samples->raise(std::move(sample_buffer)); } } -} +} // namespace audio diff --git a/sunshine/audio.h b/sunshine/audio.h index 4e27c8c0..f170fc36 100644 --- a/sunshine/audio.h +++ b/sunshine/audio.h @@ -1,8 +1,8 @@ #ifndef SUNSHINE_AUDIO_H #define SUNSHINE_AUDIO_H -#include "utility.h" #include "thread_safe.h" +#include "utility.h" namespace audio { struct config_t { int packetDuration; @@ -10,9 +10,9 @@ struct config_t { int mask; }; -using packet_t = util::buffer_t; -using packet_queue_t = std::shared_ptr>>; +using packet_t = util::buffer_t; +using packet_queue_t = std::shared_ptr>>; void capture(safe::signal_t *shutdown_event, packet_queue_t packets, config_t config, void *channel_data); -} +} // namespace audio #endif diff --git a/sunshine/config.cpp b/sunshine/config.cpp index 9408578c..6cc158bd 100644 --- a/sunshine/config.cpp +++ b/sunshine/config.cpp @@ -1,12 +1,12 @@ #include -#include #include +#include #include #include -#include "utility.h" #include "config.h" +#include "utility.h" #define CA_DIR "credentials" #define PRIVATE_KEY_FILE CA_DIR "/cakey.pem" @@ -33,12 +33,12 @@ enum preset_e : int { }; enum rc_e : int { - constqp = 0x0, /**< Constant QP mode */ - vbr = 0x1, /**< Variable bitrate mode */ - cbr = 0x2, /**< Constant bitrate mode */ - cbr_ld_hq = 0x8, /**< low-delay CBR, high quality */ - cbr_hq = 0x10, /**< CBR, high quality (slower) */ - vbr_hq = 0x20 /**< VBR, high quality (slower) */ + constqp = 0x0, /**< Constant QP mode */ + vbr = 0x1, /**< Variable bitrate mode */ + cbr = 0x2, /**< Constant bitrate mode */ + cbr_ld_hq = 0x8, /**< low-delay CBR, high quality */ + cbr_hq = 0x10, /**< CBR, high quality (slower) */ + vbr_hq = 0x20 /**< VBR, high quality (slower) */ }; enum coder_e : int { @@ -48,7 +48,8 @@ enum coder_e : int { }; std::optional preset_from_view(const std::string_view &preset) { -#define _CONVERT_(x) if(preset == #x##sv) return x +#define _CONVERT_(x) \ + if(preset == #x##sv) return x _CONVERT_(slow); _CONVERT_(medium); _CONVERT_(fast); @@ -65,7 +66,8 @@ std::optional preset_from_view(const std::string_view &preset) { } std::optional rc_from_view(const std::string_view &rc) { -#define _CONVERT_(x) if(rc == #x##sv) return x +#define _CONVERT_(x) \ + if(rc == #x##sv) return x _CONVERT_(constqp); _CONVERT_(vbr); _CONVERT_(cbr); @@ -78,12 +80,12 @@ std::optional rc_from_view(const std::string_view &rc) { int coder_from_view(const std::string_view &coder) { if(coder == "auto"sv) return _auto; - if(coder == "cabac"sv || coder == "ac"sv) return cabac; - if(coder == "cavlc"sv || coder == "vlc"sv) return cavlc; + if(coder == "cabac"sv || coder == "ac"sv) return cabac; + if(coder == "cavlc"sv || coder == "vlc"sv) return cavlc; return -1; } -} +} // namespace nv namespace amd { enum quality_e : int { @@ -107,7 +109,8 @@ enum coder_e : int { }; std::optional quality_from_view(const std::string_view &quality) { -#define _CONVERT_(x) if(quality == #x##sv) return x +#define _CONVERT_(x) \ + if(quality == #x##sv) return x _CONVERT_(speed); _CONVERT_(balanced); //_CONVERT_(quality2); @@ -117,7 +120,8 @@ std::optional quality_from_view(const std::string_view &quality) { } std::optional rc_from_view(const std::string_view &rc) { -#define _CONVERT_(x) if(rc == #x##sv) return x +#define _CONVERT_(x) \ + if(rc == #x##sv) return x _CONVERT_(constqp); _CONVERT_(vbr_latency); _CONVERT_(vbr_peak); @@ -128,40 +132,38 @@ std::optional rc_from_view(const std::string_view &rc) { int coder_from_view(const std::string_view &coder) { if(coder == "auto"sv) return _auto; - if(coder == "cabac"sv || coder == "ac"sv) return cabac; - if(coder == "cavlc"sv || coder == "vlc"sv) return cavlc; + if(coder == "cabac"sv || coder == "ac"sv) return cabac; + if(coder == "cavlc"sv || coder == "vlc"sv) return cavlc; return -1; } -} +} // namespace amd video_t video { - 0, // crf + 0, // crf 28, // qp 0, // hevc_mode 1, // min_threads { - "superfast"s, // preset + "superfast"s, // preset "zerolatency"s, // tune - }, // software + }, // software { nv::llhq, std::nullopt, - -1 - }, // nv + -1 }, // nv { amd::balanced, std::nullopt, - -1 - }, // amd + -1 }, // amd {}, // encoder {}, // adapter_name - {}, // output_name + {}, // output_name }; audio_t audio {}; @@ -172,7 +174,7 @@ stream_t stream { APPS_JSON_PATH, 10, // fecPercentage - 1 // channels + 1 // channels }; nvhttp_t nvhttp { @@ -181,18 +183,18 @@ nvhttp_t nvhttp { CERTIFICATE_FILE, boost::asio::ip::host_name(), // sunshine_name, - "sunshine_state.json"s // file_state + "sunshine_state.json"s // file_state }; input_t input { - 2s, // back_button_timeout - 500ms, // key_repeat_delay - std::chrono::duration { 1 / 24.9 } // key_repeat_period + 2s, // back_button_timeout + 500ms, // key_repeat_delay + std::chrono::duration { 1 / 24.9 } // key_repeat_period }; sunshine_t sunshine { 2, // min_log_level - 0 // flags + 0 // flags }; bool whitespace(char ch) { @@ -213,7 +215,7 @@ std::optional> parse_line(std::string_view:: return std::nullopt; } - auto end_name = std::find_if(std::make_reverse_iterator(eq), std::make_reverse_iterator(begin), std::not_fn(whitespace)).base(); + auto end_name = std::find_if(std::make_reverse_iterator(eq), std::make_reverse_iterator(begin), std::not_fn(whitespace)).base(); auto begin_val = std::find_if(eq + 1, end, std::not_fn(whitespace)); return std::pair { to_string(begin, end_name), to_string(begin_val, end) }; @@ -227,7 +229,7 @@ std::unordered_map parse_config(std::string_view file_ while(pos < end) { auto newline = std::find_if(pos, end, [](auto ch) { return ch == '\n' || ch == '\r'; }); - auto var = parse_line(pos, newline); + auto var = parse_line(pos, newline); pos = (*newline == '\r') ? newline + 2 : newline + 1; if(!var) { @@ -271,7 +273,7 @@ void int_f(std::unordered_map &vars, const std::string } auto &val = it->second; - input = util::from_chars(&val[0], &val[0] + val.size()); + input = util::from_chars(&val[0], &val[0] + val.size()); vars.erase(it); } @@ -284,7 +286,7 @@ void int_f(std::unordered_map &vars, const std::string } auto &val = it->second; - input = util::from_chars(&val[0], &val[0] + val.size()); + input = util::from_chars(&val[0], &val[0] + val.size()); vars.erase(it); } @@ -319,15 +321,14 @@ void int_between_f(std::unordered_map &vars, const std } bool to_bool(std::string &boolean) { - std::for_each(std::begin(boolean), std::end(boolean), [](char ch) { return (char)std::tolower(ch); }); + std::for_each(std::begin(boolean), std::end(boolean), [](char ch) { return (char)std::tolower(ch); }); - return - boolean == "true"sv || - boolean == "yes"sv || - boolean == "enable"sv || - (std::find(std::begin(boolean), std::end(boolean), '1') != std::end(boolean)); + return boolean == "true"sv || + boolean == "yes"sv || + boolean == "enable"sv || + (std::find(std::begin(boolean), std::end(boolean), '1') != std::end(boolean)); } -void bool_f(std::unordered_map &vars, const std::string &name, int &input) { +void bool_f(std::unordered_map &vars, const std::string &name, int &input) { std::string tmp; string_f(vars, name, tmp); @@ -338,7 +339,7 @@ void bool_f(std::unordered_map &vars, const std::strin input = to_bool(tmp) ? 1 : 0; } -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); @@ -368,32 +369,33 @@ void double_between_f(std::unordered_map &vars, const } void print_help(const char *name) { - std::cout << - "Usage: "sv << name << " [options] [/path/to/configuration_file]"sv << std::endl << - " Any configurable option can be overwritten with: \"name=value\""sv << std::endl << std::endl << - " --help | print help"sv << std::endl << std::endl << - " flags"sv << std::endl << - " -0 | Read PIN from stdin"sv << std::endl << - " -1 | Do not load previously saved state and do retain any state after shutdown"sv << std::endl << - " | Effectively starting as if for the first time without overwriting any pairings with your devices"sv; + std::cout << "Usage: "sv << name << " [options] [/path/to/configuration_file]"sv << std::endl + << " Any configurable option can be overwritten with: \"name=value\""sv << std::endl + << std::endl + << " --help | print help"sv << std::endl + << std::endl + << " flags"sv << std::endl + << " -0 | Read PIN from stdin"sv << std::endl + << " -1 | Do not load previously saved state and do retain any state after shutdown"sv << std::endl + << " | Effectively starting as if for the first time without overwriting any pairings with your devices"sv; } int apply_flags(const char *line) { int ret = 0; while(*line != '\0') { switch(*line) { - case '0': - config::sunshine.flags[config::flag::PIN_STDIN].flip(); - break; - case '1': - config::sunshine.flags[config::flag::FRESH_STATE].flip(); - break; - case 'p': - config::sunshine.flags[config::flag::CONST_PIN].flip(); - break; - default: - std::cout << "Warning: Unrecognized flag: ["sv << *line << ']' << std::endl; - ret = -1; + case '0': + config::sunshine.flags[config::flag::PIN_STDIN].flip(); + break; + case '1': + config::sunshine.flags[config::flag::FRESH_STATE].flip(); + break; + case 'p': + config::sunshine.flags[config::flag::CONST_PIN].flip(); + break; + default: + std::cout << "Warning: Unrecognized flag: ["sv << *line << ']' << std::endl; + ret = -1; } ++line; @@ -410,9 +412,7 @@ void apply_config(std::unordered_map &&vars) { int_f(vars, "crf", video.crf); int_f(vars, "qp", video.qp); int_f(vars, "min_threads", video.min_threads); - int_between_f(vars, "hevc_mode", video.hevc_mode, { - 0, 3 - }); + int_between_f(vars, "hevc_mode", video.hevc_mode, { 0, 3 }); string_f(vars, "sw_preset", video.sw.preset); string_f(vars, "sw_tune", video.sw.tune); int_f(vars, "nv_preset", video.nv.preset, nv::preset_from_view); @@ -435,26 +435,18 @@ void apply_config(std::unordered_map &&vars) { string_f(vars, "audio_sink", audio.sink); - string_restricted_f(vars, "origin_pin_allowed", nvhttp.origin_pin_allowed, { - "pc"sv, "lan"sv, "wan"sv - }); + string_restricted_f(vars, "origin_pin_allowed", nvhttp.origin_pin_allowed, { "pc"sv, "lan"sv, "wan"sv }); int to = -1; - int_between_f(vars, "ping_timeout", to, { - -1, std::numeric_limits::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, "channels", stream.channels, { - 1, std::numeric_limits::max() - }); + int_between_f(vars, "channels", stream.channels, { 1, std::numeric_limits::max() }); string_f(vars, "file_apps", stream.file_apps); - int_between_f(vars, "fec_percentage", stream.fec_percentage, { - 1, 100 - }); + int_between_f(vars, "fec_percentage", stream.fec_percentage, { 1, 100 }); to = std::numeric_limits::min(); int_f(vars, "back_button_timeout", to); @@ -464,12 +456,10 @@ void apply_config(std::unordered_map &&vars) { } double repeat_frequency { 0 }; - double_between_f(vars, "key_repeat_frequency", repeat_frequency, { - 0, std::numeric_limits::max() - }); + 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; @@ -479,9 +469,7 @@ void apply_config(std::unordered_map &&vars) { } std::string log_level_string; - string_restricted_f(vars, "min_log_level", log_level_string, { - "verbose"sv, "debug"sv, "info"sv, "warning"sv, "error"sv, "fatal"sv, "none"sv - }); + string_restricted_f(vars, "min_log_level", log_level_string, { "verbose"sv, "debug"sv, "info"sv, "warning"sv, "error"sv, "fatal"sv, "none"sv }); if(!log_level_string.empty()) { if(log_level_string == "verbose"sv) { @@ -515,7 +503,7 @@ void apply_config(std::unordered_map &&vars) { } if(sunshine.min_log_level <= 3) { - for(auto &[var,_] : vars) { + for(auto &[var, _] : vars) { std::cout << "Warning: Unrecognized configurable option ["sv << var << ']' << std::endl; } } @@ -526,7 +514,7 @@ int parse(int argc, char *argv[]) { std::unordered_map cmd_vars; - for(auto x = argc -1; x > 0; --x) { + for(auto x = argc - 1; x > 0; --x) { auto line = argv[x]; if(line == "--help"sv) { @@ -569,10 +557,9 @@ int parse(int argc, char *argv[]) { auto vars = parse_config(std::string { // Quick and dirty std::istreambuf_iterator(in), - std::istreambuf_iterator() - }); + std::istreambuf_iterator() }); - for(auto &[name,value] : cmd_vars) { + for(auto &[name, value] : cmd_vars) { vars.insert_or_assign(std::move(name), std::move(value)); } @@ -580,4 +567,4 @@ int parse(int argc, char *argv[]) { return 0; } -} +} // namespace config diff --git a/sunshine/config.h b/sunshine/config.h index f67ac12b..be75f3d2 100644 --- a/sunshine/config.h +++ b/sunshine/config.h @@ -1,16 +1,16 @@ #ifndef SUNSHINE_CONFIG_H #define SUNSHINE_CONFIG_H -#include -#include #include +#include #include +#include namespace config { struct video_t { // ffmpeg params int crf; // higher == more compression and less quality - int qp; // higher == more compression and less quality, ignored if crf != 0 + int qp; // higher == more compression and less quality, ignored if crf != 0 int hevc_mode; @@ -76,9 +76,9 @@ struct input_t { namespace flag { enum flag_e : std::size_t { PIN_STDIN = 0, // Read PIN from stdin instead of http - FRESH_STATE, // Do not load or save state + FRESH_STATE, // Do not load or save state FLAG_SIZE, - CONST_PIN= 4 // Use "universal" pin + CONST_PIN = 4 // Use "universal" pin }; } @@ -96,6 +96,6 @@ extern input_t input; extern sunshine_t sunshine; int parse(int argc, char *argv[]); -} +} // namespace config #endif diff --git a/sunshine/crypto.cpp b/sunshine/crypto.cpp index 398c8e09..c5069a71 100644 --- a/sunshine/crypto.cpp +++ b/sunshine/crypto.cpp @@ -2,14 +2,14 @@ // Created by loki on 5/31/19. // -#include #include "crypto.h" +#include namespace crypto { -using big_num_t = util::safe_ptr; +using big_num_t = util::safe_ptr; //using rsa_t = util::safe_ptr; using asn1_string_t = util::safe_ptr; -cert_chain_t::cert_chain_t() : _certs {}, _cert_ctx {X509_STORE_CTX_new() } {} +cert_chain_t::cert_chain_t() : _certs {}, _cert_ctx { X509_STORE_CTX_new() } {} void cert_chain_t::add(x509_t &&cert) { x509_store_t x509_store { X509_STORE_new() }; @@ -26,7 +26,7 @@ void cert_chain_t::add(x509_t &&cert) { */ const char *cert_chain_t::verify(x509_t::element_type *cert) { int err_code = 0; - for(auto &[_,x509_store] : _certs) { + for(auto &[_, x509_store] : _certs) { auto fg = util::fail_guard([this]() { X509_STORE_CTX_cleanup(_cert_ctx.get()); }); @@ -36,7 +36,7 @@ const char *cert_chain_t::verify(x509_t::element_type *cert) { auto err = X509_verify_cert(_cert_ctx.get()); - if (err == 1) { + if(err == 1) { return nullptr; } @@ -46,7 +46,7 @@ const char *cert_chain_t::verify(x509_t::element_type *cert) { if(err_code == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY) { return nullptr; } - if (err_code != X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT && err_code != X509_V_ERR_INVALID_CA) { + if(err_code != X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT && err_code != X509_V_ERR_INVALID_CA) { return X509_verify_cert_error_string(err_code); } } @@ -63,7 +63,7 @@ int cipher_t::decrypt(const std::string_view &cipher, std::vector }); // Gen 7 servers use 128-bit AES ECB - if (EVP_DecryptInit_ex(ctx.get(), EVP_aes_128_ecb(), nullptr, key.data(), nullptr) != 1) { + if(EVP_DecryptInit_ex(ctx.get(), EVP_aes_128_ecb(), nullptr, key.data(), nullptr) != 1) { return -1; } @@ -72,11 +72,11 @@ int cipher_t::decrypt(const std::string_view &cipher, std::vector plaintext.resize((cipher.size() + 15) / 16 * 16); auto size = (int)plaintext.size(); // Encrypt into the caller's buffer, leaving room for the auth tag to be prepended - if (EVP_DecryptUpdate(ctx.get(), plaintext.data(), &size, (const std::uint8_t*)cipher.data(), cipher.size()) != 1) { + if(EVP_DecryptUpdate(ctx.get(), plaintext.data(), &size, (const std::uint8_t *)cipher.data(), cipher.size()) != 1) { return -1; } - if (EVP_DecryptFinal_ex(ctx.get(), plaintext.data(), &len) != 1) { + if(EVP_DecryptFinal_ex(ctx.get(), plaintext.data(), &len) != 1) { return -1; } @@ -85,7 +85,7 @@ int cipher_t::decrypt(const std::string_view &cipher, std::vector } int cipher_t::decrypt_gcm(aes_t &iv, const std::string_view &tagged_cipher, - std::vector &plaintext) { + std::vector &plaintext) { auto cipher = tagged_cipher.substr(16); auto tag = tagged_cipher.substr(0, 16); @@ -93,15 +93,15 @@ int cipher_t::decrypt_gcm(aes_t &iv, const std::string_view &tagged_cipher, EVP_CIPHER_CTX_reset(ctx.get()); }); - if (EVP_DecryptInit_ex(ctx.get(), EVP_aes_128_gcm(), nullptr, nullptr, nullptr) != 1) { + if(EVP_DecryptInit_ex(ctx.get(), EVP_aes_128_gcm(), nullptr, nullptr, nullptr) != 1) { return -1; } - if (EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_SET_IVLEN, iv.size(), nullptr) != 1) { + if(EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_SET_IVLEN, iv.size(), nullptr) != 1) { return -1; } - if (EVP_DecryptInit_ex(ctx.get(), nullptr, nullptr, key.data(), iv.data()) != 1) { + if(EVP_DecryptInit_ex(ctx.get(), nullptr, nullptr, key.data(), iv.data()) != 1) { return -1; } @@ -109,16 +109,16 @@ int cipher_t::decrypt_gcm(aes_t &iv, const std::string_view &tagged_cipher, plaintext.resize((cipher.size() + 15) / 16 * 16); int size; - if (EVP_DecryptUpdate(ctx.get(), plaintext.data(), &size, (const std::uint8_t*)cipher.data(), cipher.size()) != 1) { + if(EVP_DecryptUpdate(ctx.get(), plaintext.data(), &size, (const std::uint8_t *)cipher.data(), cipher.size()) != 1) { return -1; } - if (EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_SET_TAG, tag.size(), const_cast(tag.data())) != 1) { + if(EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_SET_TAG, tag.size(), const_cast(tag.data())) != 1) { return -1; } int len = size; - if (EVP_DecryptFinal_ex(ctx.get(), plaintext.data() + size, &len) != 1) { + if(EVP_DecryptFinal_ex(ctx.get(), plaintext.data() + size, &len) != 1) { return -1; } @@ -134,7 +134,7 @@ int cipher_t::encrypt(const std::string_view &plaintext, std::vectordata, (std::size_t)asn1->length }; + return { (const char *)asn1->data, (std::size_t)asn1->length }; } std::string rand(std::size_t bytes) { std::string r; r.resize(bytes); - RAND_bytes((uint8_t*)r.data(), r.size()); + RAND_bytes((uint8_t *)r.data(), r.size()); return r; } @@ -297,8 +297,8 @@ creds_t gen_creds(const std::string_view &cn, std::uint32_t key_bits) { X509_set_pubkey(x509.get(), pkey.get()); auto name = X509_get_subject_name(x509.get()); - X509_NAME_add_entry_by_txt(name,"CN", MBSTRING_ASC, - (const std::uint8_t*)cn.data(), cn.size(), + X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, + (const std::uint8_t *)cn.data(), cn.size(), -1, 0); X509_set_issuer_name(x509.get(), name); @@ -324,7 +324,7 @@ bool verify(const x509_t &x509, const std::string_view &data, const std::string_ return false; } - if(EVP_DigestVerifyFinal(ctx.get(), (const uint8_t*)signature.data(), signature.size()) != 1) { + if(EVP_DigestVerifyFinal(ctx.get(), (const uint8_t *)signature.data(), signature.size()) != 1) { return false; } @@ -338,4 +338,4 @@ bool verify256(const x509_t &x509, const std::string_view &data, const std::stri void md_ctx_destroy(EVP_MD_CTX *ctx) { EVP_MD_CTX_destroy(ctx); } -} +} // namespace crypto diff --git a/sunshine/crypto.h b/sunshine/crypto.h index faf1fc1f..8a1b70c8 100644 --- a/sunshine/crypto.h +++ b/sunshine/crypto.h @@ -5,12 +5,12 @@ #ifndef SUNSHINE_CRYPTO_H #define SUNSHINE_CRYPTO_H -#include #include +#include #include +#include #include #include -#include #include "utility.h" @@ -25,14 +25,14 @@ void md_ctx_destroy(EVP_MD_CTX *); using sha256_t = std::array; -using aes_t = std::array; -using x509_t = util::safe_ptr; -using x509_store_t = util::safe_ptr; +using aes_t = std::array; +using x509_t = util::safe_ptr; +using x509_store_t = util::safe_ptr; using x509_store_ctx_t = util::safe_ptr; -using cipher_ctx_t = util::safe_ptr; -using md_ctx_t = util::safe_ptr; -using bio_t = util::safe_ptr; -using pkey_t = util::safe_ptr; +using cipher_ctx_t = util::safe_ptr; +using md_ctx_t = util::safe_ptr; +using bio_t = util::safe_ptr; +using pkey_t = util::safe_ptr; sha256_t hash(const std::string_view &plaintext); aes_t gen_aes_key(const std::array &salt, const std::string_view &pin); @@ -58,6 +58,7 @@ public: void add(x509_t &&cert); const char *verify(x509_t::element_type *cert); + private: std::vector> _certs; x509_store_ctx_t _cert_ctx; @@ -66,13 +67,14 @@ private: class cipher_t { public: cipher_t(const aes_t &key); - cipher_t(cipher_t&&) noexcept = default; - cipher_t &operator=(cipher_t&&) noexcept = default; + cipher_t(cipher_t &&) noexcept = default; + cipher_t &operator=(cipher_t &&) noexcept = default; int encrypt(const std::string_view &plaintext, std::vector &cipher); int decrypt_gcm(aes_t &iv, const std::string_view &cipher, std::vector &plaintext); int decrypt(const std::string_view &cipher, std::vector &plaintext); + private: cipher_ctx_t ctx; aes_t key; @@ -80,6 +82,6 @@ private: public: bool padding; }; -} +} // namespace crypto #endif //SUNSHINE_CRYPTO_H diff --git a/sunshine/input.cpp b/sunshine/input.cpp index 6a585106..6be53163 100644 --- a/sunshine/input.cpp +++ b/sunshine/input.cpp @@ -10,16 +10,16 @@ extern "C" { #include -#include "main.h" #include "config.h" -#include "utility.h" -#include "thread_pool.h" #include "input.h" +#include "main.h" #include "platform/common.h" +#include "thread_pool.h" +#include "utility.h" namespace input { -constexpr auto MAX_GAMEPADS = std::min((std::size_t)platf::MAX_GAMEPADS, sizeof(std::int16_t)*8); +constexpr auto MAX_GAMEPADS = std::min((std::size_t)platf::MAX_GAMEPADS, sizeof(std::int16_t) * 8); #define DISABLE_LEFT_BUTTON_DELAY ((util::ThreadPool::task_id_t)0x01) #define ENABLE_LEFT_BUTTON_DELAY nullptr @@ -59,7 +59,7 @@ static platf::input_t platf_input; static std::bitset gamepadMask {}; void free_gamepad(platf::input_t &platf_input, int id) { - platf::gamepad(platf_input, id, platf::gamepad_state_t{}); + platf::gamepad(platf_input, id, platf::gamepad_state_t {}); platf::free_gamepad(platf_input, id); free_id(gamepadMask, id); @@ -68,7 +68,7 @@ struct gamepad_t { gamepad_t() : gamepad_state {}, back_timeout_id {}, id { -1 }, back_button_state { button_state_e::NONE } {} ~gamepad_t() { if(id >= 0) { - task_pool.push([id=this->id]() { + task_pool.push([id = this->id]() { free_gamepad(platf_input, id); }); } @@ -89,7 +89,7 @@ struct gamepad_t { }; struct input_t { - input_t() : active_gamepad_state {}, gamepads (MAX_GAMEPADS), mouse_left_button_timeout {} { } + input_t() : active_gamepad_state {}, gamepads(MAX_GAMEPADS), mouse_left_button_timeout {} {} std::uint16_t active_gamepad_state; std::vector gamepads; @@ -158,33 +158,32 @@ void print(PNV_MULTI_CONTROLLER_PACKET packet) { constexpr int PACKET_TYPE_SCROLL_OR_KEYBOARD = PACKET_TYPE_SCROLL; void print(void *input) { - int input_type = util::endian::big(*(int*)input); + int input_type = util::endian::big(*(int *)input); switch(input_type) { - case PACKET_TYPE_REL_MOUSE_MOVE: - print((PNV_REL_MOUSE_MOVE_PACKET)input); - break; - case PACKET_TYPE_ABS_MOUSE_MOVE: - print((PNV_ABS_MOUSE_MOVE_PACKET)input); - break; - case PACKET_TYPE_MOUSE_BUTTON: - print((PNV_MOUSE_BUTTON_PACKET)input); - break; - case PACKET_TYPE_SCROLL_OR_KEYBOARD: - { - char *tmp_input = (char*)input + 4; - if(tmp_input[0] == 0x0A) { - print((PNV_SCROLL_PACKET)input); - } - else { - print((PNV_KEYBOARD_PACKET)input); - } - - break; + case PACKET_TYPE_REL_MOUSE_MOVE: + print((PNV_REL_MOUSE_MOVE_PACKET)input); + break; + case PACKET_TYPE_ABS_MOUSE_MOVE: + print((PNV_ABS_MOUSE_MOVE_PACKET)input); + break; + case PACKET_TYPE_MOUSE_BUTTON: + print((PNV_MOUSE_BUTTON_PACKET)input); + break; + case PACKET_TYPE_SCROLL_OR_KEYBOARD: { + char *tmp_input = (char *)input + 4; + if(tmp_input[0] == 0x0A) { + print((PNV_SCROLL_PACKET)input); } - case PACKET_TYPE_MULTI_CONTROLLER: - print((PNV_MULTI_CONTROLLER_PACKET)input); - break; + else { + print((PNV_KEYBOARD_PACKET)input); + } + + break; + } + case PACKET_TYPE_MULTI_CONTROLLER: + print((PNV_MULTI_CONTROLLER_PACKET)input); + break; } } @@ -245,8 +244,8 @@ void passthrough(std::shared_ptr &input, PNV_MOUSE_BUTTON_PACKET packet mouse_press[button] = !release; } -/////////////////////////////////// - /*/ + /////////////////////////////////// + /*/ * When Moonlight sends mouse input through absolute coordinates, * it's possible that BUTTON_RIGHT is pressed down immediately after releasing BUTTON_LEFT. * As a result, Sunshine will left click on hyperlinks in the browser before right clicking @@ -261,7 +260,7 @@ void passthrough(std::shared_ptr &input, PNV_MOUSE_BUTTON_PACKET packet * when the last mouse coordinates were absolute /*/ if(button == BUTTON_LEFT && release && !input->mouse_left_button_timeout) { - input->mouse_left_button_timeout = task_pool.pushDelayed([=]() { + auto f = [=]() { auto left_released = mouse_press[BUTTON_LEFT]; if(left_released) { // Already released left button @@ -269,16 +268,17 @@ void passthrough(std::shared_ptr &input, PNV_MOUSE_BUTTON_PACKET packet } platf::button_mouse(platf_input, BUTTON_LEFT, release); - mouse_press[BUTTON_LEFT] = false; + mouse_press[BUTTON_LEFT] = false; input->mouse_left_button_timeout = nullptr; - }, 10ms).task_id; + }; + + input->mouse_left_button_timeout = task_pool.pushDelayed(std::move(f), 10ms).task_id; return; } if( button == BUTTON_RIGHT && !release && - input->mouse_left_button_timeout > DISABLE_LEFT_BUTTON_DELAY - ) { + input->mouse_left_button_timeout > DISABLE_LEFT_BUTTON_DELAY) { platf::button_mouse(platf_input, BUTTON_RIGHT, false); platf::button_mouse(platf_input, BUTTON_RIGHT, true); @@ -286,7 +286,7 @@ void passthrough(std::shared_ptr &input, PNV_MOUSE_BUTTON_PACKET packet return; } -/////////////////////////////////// + /////////////////////////////////// platf::button_mouse(platf_input, button, release); } @@ -341,7 +341,7 @@ void passthrough(PNV_SCROLL_PACKET packet) { int updateGamepads(std::vector &gamepads, std::int16_t old_state, std::int16_t new_state) { auto xorGamepadMask = old_state ^ new_state; - if (!xorGamepadMask) { + if(!xorGamepadMask) { return 0; } @@ -350,7 +350,7 @@ int updateGamepads(std::vector &gamepads, std::int16_t old_state, std auto &gamepad = gamepads[x]; if((old_state >> x) & 1) { - if (gamepad.id < 0) { + if(gamepad.id < 0) { return -1; } @@ -410,7 +410,7 @@ void passthrough(std::shared_ptr &input, PNV_MULTI_CONTROLLER_PACKET pa display_cursor = false; std::uint16_t bf = packet->buttonFlags; - platf::gamepad_state_t gamepad_state{ + platf::gamepad_state_t gamepad_state { bf, packet->leftTrigger, packet->rightTrigger, @@ -422,30 +422,30 @@ void passthrough(std::shared_ptr &input, PNV_MULTI_CONTROLLER_PACKET pa auto bf_new = gamepad_state.buttonFlags; switch(gamepad.back_button_state) { - case button_state_e::UP: - if(!(platf::BACK & bf_new)) { - gamepad.back_button_state = button_state_e::NONE; - } - gamepad_state.buttonFlags &= ~platf::BACK; - break; - case button_state_e::DOWN: - if(platf::BACK & bf_new) { - gamepad.back_button_state = button_state_e::NONE; - } - gamepad_state.buttonFlags |= platf::BACK; - break; - case button_state_e::NONE: - break; + case button_state_e::UP: + if(!(platf::BACK & bf_new)) { + gamepad.back_button_state = button_state_e::NONE; + } + gamepad_state.buttonFlags &= ~platf::BACK; + break; + case button_state_e::DOWN: + if(platf::BACK & bf_new) { + gamepad.back_button_state = button_state_e::NONE; + } + gamepad_state.buttonFlags |= platf::BACK; + break; + case button_state_e::NONE: + break; } - bf = gamepad_state.buttonFlags ^ gamepad.gamepad_state.buttonFlags; + bf = gamepad_state.buttonFlags ^ gamepad.gamepad_state.buttonFlags; bf_new = gamepad_state.buttonFlags; - if (platf::BACK & bf) { - if (platf::BACK & bf_new) { + if(platf::BACK & bf) { + if(platf::BACK & bf_new) { // Don't emulate home button if timeout < 0 if(config::input.back_button_timeout >= 0ms) { - gamepad.back_timeout_id = task_pool.pushDelayed([input, controller=packet->controllerNumber]() { + auto f = [input, controller = packet->controllerNumber]() { auto &gamepad = input->gamepads[controller]; auto &state = gamepad.gamepad_state; @@ -464,10 +464,12 @@ void passthrough(std::shared_ptr &input, PNV_MULTI_CONTROLLER_PACKET pa platf::gamepad(platf_input, gamepad.id, state); gamepad.back_timeout_id = nullptr; - }, config::input.back_button_timeout).task_id; + }; + + gamepad.back_timeout_id = task_pool.pushDelayed(std::move(f), config::input.back_button_timeout).task_id; } } - else if (gamepad.back_timeout_id) { + else if(gamepad.back_timeout_id) { task_pool.cancel(gamepad.back_timeout_id); gamepad.back_timeout_id = nullptr; } @@ -481,33 +483,32 @@ void passthrough(std::shared_ptr &input, PNV_MULTI_CONTROLLER_PACKET pa void passthrough_helper(std::shared_ptr input, std::vector &&input_data) { void *payload = input_data.data(); - int input_type = util::endian::big(*(int*)payload); + int input_type = util::endian::big(*(int *)payload); switch(input_type) { - case PACKET_TYPE_REL_MOUSE_MOVE: - passthrough(input, (PNV_REL_MOUSE_MOVE_PACKET)payload); - break; - case PACKET_TYPE_ABS_MOUSE_MOVE: - passthrough(input, (PNV_ABS_MOUSE_MOVE_PACKET)payload); - break; - case PACKET_TYPE_MOUSE_BUTTON: - passthrough(input, (PNV_MOUSE_BUTTON_PACKET)payload); - break; - case PACKET_TYPE_SCROLL_OR_KEYBOARD: - { - char *tmp_input = (char*)payload + 4; - if(tmp_input[0] == 0x0A) { - passthrough((PNV_SCROLL_PACKET)payload); - } - else { - passthrough(input, (PNV_KEYBOARD_PACKET)payload); - } - - break; + case PACKET_TYPE_REL_MOUSE_MOVE: + passthrough(input, (PNV_REL_MOUSE_MOVE_PACKET)payload); + break; + case PACKET_TYPE_ABS_MOUSE_MOVE: + passthrough(input, (PNV_ABS_MOUSE_MOVE_PACKET)payload); + break; + case PACKET_TYPE_MOUSE_BUTTON: + passthrough(input, (PNV_MOUSE_BUTTON_PACKET)payload); + break; + case PACKET_TYPE_SCROLL_OR_KEYBOARD: { + char *tmp_input = (char *)payload + 4; + if(tmp_input[0] == 0x0A) { + passthrough((PNV_SCROLL_PACKET)payload); } - case PACKET_TYPE_MULTI_CONTROLLER: - passthrough(input, (PNV_MULTI_CONTROLLER_PACKET)payload); - break; + else { + passthrough(input, (PNV_KEYBOARD_PACKET)payload); + } + + break; + } + case PACKET_TYPE_MULTI_CONTROLLER: + passthrough(input, (PNV_MULTI_CONTROLLER_PACKET)payload); + break; } } @@ -528,7 +529,7 @@ void reset(std::shared_ptr &input) { } } - for(auto& kp : key_press) { + for(auto &kp : key_press) { platf::keyboard(platf_input, kp.first & 0x00FF, true); key_press[kp.first] = false; } @@ -537,7 +538,7 @@ void reset(std::shared_ptr &input) { void init() { touch_port_event = std::make_unique(); - platf_input = platf::input(); + platf_input = platf::input(); } std::shared_ptr alloc() { @@ -547,8 +548,9 @@ std::shared_ptr alloc() { task_pool.pushDelayed([]() { platf::move_mouse(platf_input, 1, 1); platf::move_mouse(platf_input, -1, -1); - }, 100ms); + }, + 100ms); return input; } -} +} // namespace input diff --git a/sunshine/input.h b/sunshine/input.h index 16267619..4c94bb4a 100644 --- a/sunshine/input.h +++ b/sunshine/input.h @@ -5,8 +5,8 @@ #ifndef SUNSHINE_INPUT_H #define SUNSHINE_INPUT_H -#include "thread_safe.h" #include "platform/common.h" +#include "thread_safe.h" namespace input { struct input_t; @@ -22,6 +22,6 @@ std::shared_ptr alloc(); using touch_port_event_t = std::unique_ptr>; extern touch_port_event_t touch_port_event; -} +} // namespace input #endif //SUNSHINE_INPUT_H diff --git a/sunshine/main.cpp b/sunshine/main.cpp index 9dc686a9..0da0a6f6 100644 --- a/sunshine/main.cpp +++ b/sunshine/main.cpp @@ -4,26 +4,26 @@ #include "process.h" -#include -#include #include +#include +#include -#include -#include -#include -#include #include +#include +#include +#include +#include -#include "video.h" +#include "config.h" #include "nvhttp.h" #include "rtsp.h" -#include "config.h" #include "thread_pool.h" +#include "video.h" #include "platform/common.h" extern "C" { -#include #include +#include } using namespace std::literals; @@ -79,31 +79,31 @@ int main(int argc, char *argv[]) { sink->locked_backend()->add_stream(stream); sink->set_filter(severity >= config::sunshine.min_log_level); - sink->set_formatter([message="Message"s, severity="Severity"s](const bl::record_view &view, bl::formatting_ostream &os) { - constexpr int DATE_BUFFER_SIZE = 21 +2 +1; // Full string plus ": \0" + sink->set_formatter([message = "Message"s, severity = "Severity"s](const bl::record_view &view, bl::formatting_ostream &os) { + constexpr int DATE_BUFFER_SIZE = 21 + 2 + 1; // Full string plus ": \0" auto log_level = view.attribute_values()[severity].extract().get(); std::string_view log_type; switch(log_level) { - case 0: - log_type = "Verbose: "sv; - break; - case 1: - log_type = "Debug: "sv; - break; - case 2: - log_type = "Info: "sv; - break; - case 3: - log_type = "Warning: "sv; - break; - case 4: - log_type = "Error: "sv; - break; - case 5: - log_type = "Fatal: "sv; - break; + case 0: + log_type = "Verbose: "sv; + break; + case 1: + log_type = "Debug: "sv; + break; + case 2: + log_type = "Info: "sv; + break; + case 3: + log_type = "Warning: "sv; + break; + case 4: + log_type = "Error: "sv; + break; + case 5: + log_type = "Fatal: "sv; + break; }; char _date[DATE_BUFFER_SIZE]; diff --git a/sunshine/main.h b/sunshine/main.h index b8a85e5b..1cb6f1da 100644 --- a/sunshine/main.h +++ b/sunshine/main.h @@ -5,8 +5,8 @@ #ifndef SUNSHINE_MAIN_H #define SUNSHINE_MAIN_H -#include #include "thread_pool.h" +#include extern util::ThreadPool task_pool; extern bool display_cursor; diff --git a/sunshine/move_by_copy.h b/sunshine/move_by_copy.h index 61e205f5..804a72ba 100644 --- a/sunshine/move_by_copy.h +++ b/sunshine/move_by_copy.h @@ -11,23 +11,24 @@ template class MoveByCopy { public: typedef T move_type; + private: move_type _to_move; -public: - explicit MoveByCopy(move_type &&to_move) : _to_move(std::move(to_move)) { } +public: + explicit MoveByCopy(move_type &&to_move) : _to_move(std::move(to_move)) {} MoveByCopy(MoveByCopy &&other) = default; - + MoveByCopy(const MoveByCopy &other) { *this = other; } - MoveByCopy& operator=(MoveByCopy &&other) = default; - - MoveByCopy& operator=(const MoveByCopy &other) { - this->_to_move = std::move(const_cast(other)._to_move); - + MoveByCopy &operator=(MoveByCopy &&other) = default; + + MoveByCopy &operator=(const MoveByCopy &other) { + this->_to_move = std::move(const_cast(other)._to_move); + return *this; } @@ -44,7 +45,7 @@ MoveByCopy cmove(T &movable) { // Do NOT use this unless you are absolutely certain the object to be moved is no longer used by the caller template MoveByCopy const_cmove(const T &movable) { - return MoveByCopy(std::move(const_cast(movable))); -} + return MoveByCopy(std::move(const_cast(movable))); } +} // namespace util #endif diff --git a/sunshine/network.cpp b/sunshine/network.cpp index 1efb5364..28e031ef 100644 --- a/sunshine/network.cpp +++ b/sunshine/network.cpp @@ -2,9 +2,9 @@ // Created by loki on 12/27/19. // -#include #include "network.h" #include "utility.h" +#include namespace net { using namespace std::literals; @@ -21,17 +21,17 @@ std::vector> lan_ips { }; std::uint32_t ip(const std::string_view &ip_str) { - auto begin = std::begin(ip_str); - auto end = std::end(ip_str); + auto begin = std::begin(ip_str); + auto end = std::end(ip_str); auto temp_end = std::find(begin, end, '.'); std::uint32_t ip = 0; - auto shift = 24; + auto shift = 24; while(temp_end != end) { ip += (util::from_chars(begin, temp_end) << shift); shift -= 8; - begin = temp_end + 1; + begin = temp_end + 1; temp_end = std::find(begin, end, '.'); } @@ -43,7 +43,7 @@ std::uint32_t ip(const std::string_view &ip_str) { // In the format "xxx.xxx.xxx.xxx/x" std::pair ip_block(const std::string_view &ip_str) { auto begin = std::begin(ip_str); - auto end = std::find(begin, std::end(ip_str), '/'); + auto end = std::find(begin, std::end(ip_str), '/'); auto addr = ip({ begin, (std::size_t)(end - begin) }); @@ -82,12 +82,12 @@ net_e from_address(const std::string_view &view) { std::string_view to_enum_string(net_e net) { switch(net) { - case PC: - return "pc"sv; - case LAN: - return "lan"sv; - case WAN: - return "wan"sv; + case PC: + return "pc"sv; + case LAN: + return "lan"sv; + case WAN: + return "wan"sv; } // avoid warning @@ -112,4 +112,4 @@ void free_host(ENetHost *host) { enet_host_destroy(host); } -} \ No newline at end of file +} // namespace net \ No newline at end of file diff --git a/sunshine/network.h b/sunshine/network.h index b3e65a97..88f18a40 100644 --- a/sunshine/network.h +++ b/sunshine/network.h @@ -14,8 +14,8 @@ namespace net { void free_host(ENetHost *host); -using host_t = util::safe_ptr; -using peer_t = ENetPeer*; +using host_t = util::safe_ptr; +using peer_t = ENetPeer *; using packet_t = util::safe_ptr; enum net_e : int { @@ -30,6 +30,6 @@ std::string_view to_enum_string(net_e net); net_e from_address(const std::string_view &view); host_t host_create(ENetAddress &addr, std::size_t peers, std::uint16_t port); -} +} // namespace net #endif //SUNSHINE_NETWORK_H diff --git a/sunshine/nvhttp.cpp b/sunshine/nvhttp.cpp index 98f5a79d..ae413c39 100644 --- a/sunshine/nvhttp.cpp +++ b/sunshine/nvhttp.cpp @@ -6,9 +6,9 @@ #include +#include #include #include -#include #include @@ -17,14 +17,14 @@ #include #include "config.h" -#include "utility.h" -#include "rtsp.h" #include "crypto.h" +#include "main.h" +#include "network.h" #include "nvhttp.h" #include "platform/common.h" -#include "network.h" +#include "rtsp.h" +#include "utility.h" #include "uuid.h" -#include "main.h" namespace nvhttp { @@ -69,8 +69,8 @@ struct pair_session_t { struct { util::Either< std::shared_ptr::Response>, - std::shared_ptr::Response> - > response; + std::shared_ptr::Response>> + response; std::string salt; } async_insert_pin; }; @@ -81,11 +81,11 @@ std::unordered_map map_id_client; std::string unique_id; net::net_e origin_pin_allowed; -using args_t = SimpleWeb::CaseInsensitiveMultimap; +using args_t = SimpleWeb::CaseInsensitiveMultimap; using resp_https_t = std::shared_ptr::Response>; -using req_https_t = std::shared_ptr::Request>; -using resp_http_t = std::shared_ptr::Response>; -using req_http_t = std::shared_ptr::Request>; +using req_https_t = std::shared_ptr::Request>; +using resp_http_t = std::shared_ptr::Response>; +using req_http_t = std::shared_ptr::Request>; enum class op_e { ADD, @@ -97,7 +97,7 @@ void save_state() { root.put("root.uniqueid", unique_id); auto &nodes = root.add_child("root.devices", pt::ptree {}); - for(auto &[_,client] : map_id_client) { + for(auto &[_, client] : map_id_client) { pt::ptree node; node.put("uniqueid"s, client.uniqueID); @@ -127,17 +127,18 @@ void load_state() { pt::ptree root; try { pt::read_json(config::nvhttp.file_state, root); - } catch (std::exception &e) { + } + catch(std::exception &e) { BOOST_LOG(warning) << e.what(); return; } - unique_id = root.get("root.uniqueid"); + unique_id = root.get("root.uniqueid"); auto device_nodes = root.get_child("root.devices"); - for(auto &[_,device_node] : device_nodes) { - auto uniqID = device_node.get("uniqueid"); + for(auto &[_, device_node] : device_nodes) { + auto uniqID = device_node.get("uniqueid"); auto &client = map_id_client.emplace(uniqID, client_t {}).first->second; client.uniqueID = uniqID; @@ -150,16 +151,14 @@ void load_state() { void update_id_client(const std::string &uniqueID, std::string &&cert, op_e op) { switch(op) { - case op_e::ADD: - { - auto &client = map_id_client[uniqueID]; - client.certs.emplace_back(std::move(cert)); - client.uniqueID = uniqueID; - } - break; - case op_e::REMOVE: - map_id_client.erase(uniqueID); - break; + case op_e::ADD: { + auto &client = map_id_client[uniqueID]; + client.certs.emplace_back(std::move(cert)); + client.uniqueID = uniqueID; + } break; + case op_e::REMOVE: + map_id_client.erase(uniqueID); + break; } if(!config::sunshine.flags[config::flag::FRESH_STATE]) { @@ -175,10 +174,10 @@ void getservercert(pair_session_t &sess, pt::ptree &tree, const std::string &pin } std::string_view salt_view { sess.async_insert_pin.salt.data(), 32 }; - + auto salt = util::from_hex>(salt_view, true); - auto key = crypto::gen_aes_key(*salt, pin); + auto key = crypto::gen_aes_key(*salt, pin); sess.cipher_key = std::make_unique(key); tree.put("root.paired", 1); @@ -197,7 +196,7 @@ void serverchallengeresp(pair_session_t &sess, pt::ptree &tree, const args_t &ar sess.clienthash = std::move(decrypted); auto serversecret = sess.serversecret; - auto sign = crypto::sign256(crypto::pkey(conf_intern.pkey), serversecret); + auto sign = crypto::sign256(crypto::pkey(conf_intern.pkey), serversecret); serversecret.insert(std::end(serversecret), std::begin(sign), std::end(sign)); @@ -215,14 +214,14 @@ void clientchallenge(pair_session_t &sess, pt::ptree &tree, const args_t &args) std::vector decrypted; cipher.decrypt(challenge, decrypted); - auto x509 = crypto::x509(conf_intern.servercert); - auto sign = crypto::signature(x509); + auto x509 = crypto::x509(conf_intern.servercert); + auto sign = crypto::signature(x509); auto serversecret = crypto::rand(16); decrypted.insert(std::end(decrypted), std::begin(sign), std::end(sign)); decrypted.insert(std::end(decrypted), std::begin(serversecret), std::end(serversecret)); - auto hash = crypto::hash({ (char*)decrypted.data(), decrypted.size() }); + auto hash = crypto::hash({ (char *)decrypted.data(), decrypted.size() }); auto serverchallenge = crypto::rand(16); std::string plaintext; @@ -252,7 +251,7 @@ void clientpairingsecret(std::shared_ptr> &add_cer assert((secret.size() + sign.size()) == pairingsecret.size()); - auto x509 = crypto::x509(client.cert); + auto x509 = crypto::x509(client.cert); auto x509_sign = crypto::signature(x509); std::string data; @@ -306,8 +305,8 @@ template void print_req(std::shared_ptr::Request> request) { BOOST_LOG(debug) << "TUNNEL :: "sv << tunnel::to_string; - BOOST_LOG(debug) << "METHOD :: "sv << request->method; - BOOST_LOG(debug) << "DESTINATION :: "sv << request->path; + BOOST_LOG(debug) << "METHOD :: "sv << request->method; + BOOST_LOG(debug) << "DESTINATION :: "sv << request->path; for(auto &[name, val] : request->header) { BOOST_LOG(debug) << name << " -- " << val; @@ -334,7 +333,8 @@ void not_found(std::shared_ptr::Response> resp pt::write_xml(data, tree); response->write(data.str()); - *response << "HTTP/1.1 404 NOT FOUND\r\n" << data.str(); + *response << "HTTP/1.1 404 NOT FOUND\r\n" + << data.str(); } template @@ -351,9 +351,9 @@ void pair(std::shared_ptr> &add_cert, std::shared_ if(it = args.find("phrase"); it != std::end(args)) { if(it->second == "getservercert"sv) { pair_session_t sess; - + sess.client.uniqueID = std::move(uniqID); - sess.client.cert = util::from_hex_vec(args.at("clientcert"s), true); + sess.client.cert = util::from_hex_vec(args.at("clientcert"s), true); BOOST_LOG(debug) << sess.client.cert; auto ptr = map_id_sess.emplace(sess.client.uniqueID, std::move(sess)).first; @@ -435,7 +435,7 @@ void pin(std::shared_ptr::Response> response, if(async_response.has_left() && async_response.left()) { async_response.left()->write(data.str()); } - else if(async_response.has_right() && async_response.right()){ + else if(async_response.has_right() && async_response.right()) { async_response.right()->write(data.str()); } else { @@ -455,13 +455,13 @@ void serverinfo(std::shared_ptr::Response> res print_req(request); int pair_status = 0; - if constexpr (std::is_same_v) { - auto args = request->parse_query_string(); + if constexpr(std::is_same_v) { + auto args = request->parse_query_string(); auto clientID = args.find("uniqueid"s); if(clientID != std::end(args)) { - if (auto it = map_id_client.find(clientID->second); it != std::end(map_id_client)) { + if(auto it = map_id_client.find(clientID->second); it != std::end(map_id_client)) { pair_status = 1; } } @@ -507,7 +507,7 @@ void serverinfo(std::shared_ptr::Response> res void applist(resp_https_t response, req_https_t request) { print_req(request); - auto args = request->parse_query_string(); + auto args = request->parse_query_string(); auto clientID = args.at("uniqueid"s); pt::ptree tree; @@ -560,8 +560,8 @@ void launch(resp_https_t response, req_https_t request) { return; } - auto args = request->parse_query_string(); - auto appid = util::from_view(args.at("appid")) -1; + auto args = request->parse_query_string(); + auto appid = util::from_view(args.at("appid")) - 1; auto current_appid = proc::proc.running(); if(current_appid != -1) { @@ -583,10 +583,10 @@ void launch(resp_https_t response, req_https_t request) { stream::launch_session_t launch_session; - auto clientID = args.at("uniqueid"s); + auto clientID = args.at("uniqueid"s); launch_session.gcm_key = *util::from_hex(args.at("rikey"s), true); - uint32_t prepend_iv = util::endian::big(util::from_view(args.at("rikeyid"s))); - auto prepend_iv_p = (uint8_t*)&prepend_iv; + uint32_t prepend_iv = util::endian::big(util::from_view(args.at("rikeyid"s))); + auto prepend_iv_p = (uint8_t *)&prepend_iv; auto next = std::copy(prepend_iv_p, prepend_iv_p + sizeof(prepend_iv), std::begin(launch_session.iv)); std::fill(next, std::end(launch_session.iv), 0); @@ -627,11 +627,11 @@ void resume(resp_https_t response, req_https_t request) { stream::launch_session_t launch_session; - auto args = request->parse_query_string(); - auto clientID = args.at("uniqueid"s); + auto args = request->parse_query_string(); + auto clientID = args.at("uniqueid"s); launch_session.gcm_key = *util::from_hex(args.at("rikey"s), true); - uint32_t prepend_iv = util::endian::big(util::from_view(args.at("rikeyid"s))); - auto prepend_iv_p = (uint8_t*)&prepend_iv; + uint32_t prepend_iv = util::endian::big(util::from_view(args.at("rikeyid"s))); + auto prepend_iv_p = (uint8_t *)&prepend_iv; auto next = std::copy(prepend_iv_p, prepend_iv_p + sizeof(prepend_iv), std::begin(launch_session.iv)); std::fill(next, std::end(launch_session.iv), 0); @@ -688,25 +688,25 @@ int create_creds(const std::string &pkey, const std::string &cert) { pkey_dir.remove_filename(); cert_dir.remove_filename(); - std::error_code err_code{}; + std::error_code err_code {}; fs::create_directories(pkey_dir, err_code); - if (err_code) { + if(err_code) { BOOST_LOG(fatal) << "Couldn't create directory ["sv << pkey_dir << "] :"sv << err_code.message(); return -1; } fs::create_directories(cert_dir, err_code); - if (err_code) { + if(err_code) { BOOST_LOG(fatal) << "Couldn't create directory ["sv << cert_dir << "] :"sv << err_code.message(); return -1; } - if (write_file(pkey.c_str(), creds.pkey)) { + if(write_file(pkey.c_str(), creds.pkey)) { BOOST_LOG(fatal) << "Couldn't open ["sv << config::nvhttp.pkey << ']'; return -1; } - if (write_file(cert.c_str(), creds.x509)) { + if(write_file(cert.c_str(), creds.x509)) { BOOST_LOG(fatal) << "Couldn't open ["sv << config::nvhttp.cert << ']'; return -1; } @@ -715,7 +715,7 @@ int create_creds(const std::string &pkey, const std::string &cert) { fs::perms::owner_read | fs::perms::owner_write, fs::perm_options::replace, err_code); - if (err_code) { + if(err_code) { BOOST_LOG(fatal) << "Couldn't change permissions of ["sv << config::nvhttp.pkey << "] :"sv << err_code.message(); return -1; } @@ -724,7 +724,7 @@ int create_creds(const std::string &pkey, const std::string &cert) { fs::perms::owner_read | fs::perms::group_read | fs::perms::others_read | fs::perms::owner_write, fs::perm_options::replace, err_code); - if (err_code) { + if(err_code) { BOOST_LOG(fatal) << "Couldn't change permissions of ["sv << config::nvhttp.cert << "] :"sv << err_code.message(); return -1; } @@ -757,7 +757,7 @@ void start(std::shared_ptr shutdown_event) { load_state(); } - conf_intern.pkey = read_file(config::nvhttp.pkey.c_str()); + conf_intern.pkey = read_file(config::nvhttp.pkey.c_str()); conf_intern.servercert = read_file(config::nvhttp.cert.c_str()); auto ctx = std::make_shared(boost::asio::ssl::context::tls); @@ -765,7 +765,7 @@ void start(std::shared_ptr shutdown_event) { ctx->use_private_key_file(config::nvhttp.pkey, boost::asio::ssl::context::pem); crypto::cert_chain_t cert_chain; - for(auto &[_,client] : map_id_client) { + for(auto &[_, client] : map_id_client) { for(auto &cert : client.certs) { cert_chain.add(crypto::x509(cert)); } @@ -813,33 +813,34 @@ void start(std::shared_ptr shutdown_event) { https_server_t https_server { ctx, boost::asio::ssl::verify_peer | boost::asio::ssl::verify_fail_if_no_peer_cert | boost::asio::ssl::verify_client_once }; http_server_t http_server; - https_server.default_resource = not_found; - 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["^/applist$"]["GET"] = applist; - https_server.resource["^/appasset$"]["GET"] = appasset; - https_server.resource["^/launch$"]["GET"] = launch; + https_server.default_resource = 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["^/applist$"]["GET"] = applist; + https_server.resource["^/appasset$"]["GET"] = appasset; + https_server.resource["^/launch$"]["GET"] = launch; https_server.resource["^/pin/([0-9]+)$"]["GET"] = pin; - https_server.resource["^/resume$"]["GET"] = resume; - https_server.resource["^/cancel$"]["GET"] = cancel; + https_server.resource["^/resume$"]["GET"] = resume; + https_server.resource["^/cancel$"]["GET"] = cancel; https_server.config.reuse_address = true; - https_server.config.address = "0.0.0.0"s; - https_server.config.port = PORT_HTTPS; + https_server.config.address = "0.0.0.0"s; + https_server.config.port = PORT_HTTPS; - http_server.default_resource = not_found; - http_server.resource["^/serverinfo$"]["GET"] = serverinfo; - http_server.resource["^/pair$"]["GET"] = [&add_cert](auto resp, auto req) { pair(add_cert, resp, req); }; + http_server.default_resource = 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["^/pin/([0-9]+)$"]["GET"] = pin; http_server.config.reuse_address = true; - http_server.config.address = "0.0.0.0"s; - http_server.config.port = PORT_HTTP; + http_server.config.address = "0.0.0.0"s; + http_server.config.port = PORT_HTTP; try { https_server.bind(); http_server.bind(); - } catch(boost::system::system_error &err) { + } + catch(boost::system::system_error &err) { BOOST_LOG(fatal) << "Couldn't bind http server to ports ["sv << PORT_HTTPS << ", "sv << PORT_HTTP << "]: "sv << err.what(); shutdown_event->raise(true); @@ -849,7 +850,8 @@ void start(std::shared_ptr shutdown_event) { auto accept_and_run = [&](auto *http_server) { try { http_server->accept_and_run(); - } catch(boost::system::system_error &err) { + } + catch(boost::system::system_error &err) { // It's possible the exception gets thrown after calling http_server->stop() from a different thread if(shutdown_event->peek()) { return; @@ -899,4 +901,4 @@ std::string read_file(const char *path) { return base64_cert; } -} +} // namespace nvhttp diff --git a/sunshine/nvhttp.h b/sunshine/nvhttp.h index eb90540a..8b3d50a4 100644 --- a/sunshine/nvhttp.h +++ b/sunshine/nvhttp.h @@ -11,8 +11,8 @@ #include "thread_safe.h" #define CA_DIR SUNSHINE_ASSETS_DIR "/demoCA" -#define PRIVATE_KEY_FILE CA_DIR "/cakey.pem" -#define CERTIFICATE_FILE CA_DIR "/cacert.pem" +#define PRIVATE_KEY_FILE CA_DIR "/cakey.pem" +#define CERTIFICATE_FILE CA_DIR "/cacert.pem" namespace nvhttp { void start(std::shared_ptr shutdown_event); diff --git a/sunshine/platform/common.h b/sunshine/platform/common.h index 2f414509..c80fc750 100644 --- a/sunshine/platform/common.h +++ b/sunshine/platform/common.h @@ -5,9 +5,9 @@ #ifndef SUNSHINE_COMMON_H #define SUNSHINE_COMMON_H -#include -#include #include "sunshine/utility.h" +#include +#include struct sockaddr; namespace platf { @@ -44,8 +44,10 @@ enum class pix_fmt_e { }; inline std::string_view from_pix_fmt(pix_fmt_e pix_fmt) { -using namespace std::literals; -#define _CONVERT(x) case pix_fmt_e:: x : return #x ## sv + using namespace std::literals; +#define _CONVERT(x) \ + case pix_fmt_e::x: \ + return #x##sv switch(pix_fmt) { _CONVERT(yuv420p); _CONVERT(yuv420p10); @@ -65,9 +67,8 @@ struct touch_port_t { constexpr touch_port_t( std::uint32_t offset_x, std::uint32_t offset_y, - std::uint32_t width, std::uint32_t height) noexcept : - offset_x { offset_x }, offset_y { offset_y }, - width { width }, height { height } {}; + std::uint32_t width, std::uint32_t height) noexcept : offset_x { offset_x }, offset_y { offset_y }, + width { width }, height { height } {}; }; struct gamepad_state_t { @@ -87,15 +88,15 @@ public: struct img_t { public: - std::uint8_t *data {}; - std::int32_t width {}; + std::uint8_t *data {}; + std::int32_t width {}; std::int32_t height {}; std::int32_t pixel_pitch {}; std::int32_t row_pitch {}; - img_t() = default; - img_t(const img_t&) = delete; - img_t(img_t&&) = delete; + img_t() = default; + img_t(const img_t &) = delete; + img_t(img_t &&) = delete; virtual ~img_t() = default; }; @@ -124,7 +125,7 @@ class display_t { public: display_t() noexcept : offset_x { 0 }, offset_y { 0 } {} virtual capture_e snapshot(img_t *img, std::chrono::milliseconds timeout, bool cursor) = 0; - virtual std::shared_ptr alloc_img() = 0; + virtual std::shared_ptr alloc_img() = 0; virtual int dummy_img(img_t *img) = 0; @@ -148,7 +149,7 @@ public: }; -void freeInput(void*); +void freeInput(void *); using input_t = util::safe_ptr; @@ -172,6 +173,6 @@ int alloc_gamepad(input_t &input, int nr); void free_gamepad(input_t &input, int nr); [[nodiscard]] std::unique_ptr init(); -} +} // namespace platf #endif //SUNSHINE_COMMON_H diff --git a/sunshine/platform/linux/display.cpp b/sunshine/platform/linux/display.cpp index 79bd437b..0c45c749 100644 --- a/sunshine/platform/linux/display.cpp +++ b/sunshine/platform/linux/display.cpp @@ -4,8 +4,8 @@ #include "sunshine/platform/common.h" -#include #include +#include #include #include @@ -15,36 +15,35 @@ #include #include #include -#include -#include #include #include +#include +#include -#include #include +#include -#include "sunshine/task_pool.h" #include "sunshine/config.h" #include "sunshine/main.h" +#include "sunshine/task_pool.h" -namespace platf -{ +namespace platf { using namespace std::literals; -void freeImage(XImage*); -void freeX(XFixesCursorImage*); +void freeImage(XImage *); +void freeX(XFixesCursorImage *); -using ifaddr_t = util::safe_ptr; +using ifaddr_t = util::safe_ptr; using xcb_connect_t = util::safe_ptr; -using xcb_img_t = util::c_ptr; +using xcb_img_t = util::c_ptr; using xdisplay_t = util::safe_ptr_v2; -using ximg_t = util::safe_ptr; -using xcursor_t = util::safe_ptr; +using ximg_t = util::safe_ptr; +using xcursor_t = util::safe_ptr; -using crtc_info_t = util::safe_ptr<_XRRCrtcInfo, XRRFreeCrtcInfo>; +using crtc_info_t = util::safe_ptr<_XRRCrtcInfo, XRRFreeCrtcInfo>; using output_info_t = util::safe_ptr<_XRROutputInfo, XRRFreeOutputInfo>; -using screen_res_t = util::safe_ptr<_XRRScreenResources, XRRFreeScreenResources>; +using screen_res_t = util::safe_ptr<_XRRScreenResources, XRRFreeScreenResources>; class shm_id_t { public: @@ -55,7 +54,7 @@ public: } ~shm_id_t() { - if (id != -1) { + if(id != -1) { shmctl(id, IPC_RMID, nullptr); id = -1; } @@ -65,15 +64,15 @@ public: class shm_data_t { public: - shm_data_t() : data { (void*) -1 } {} + shm_data_t() : data { (void *)-1 } {} shm_data_t(void *data) : data { data } {} shm_data_t(shm_data_t &&other) noexcept : data(other.data) { - other.data = (void*)-1; + other.data = (void *)-1; } ~shm_data_t() { - if((std::uintptr_t) data != -1) { + if((std::uintptr_t)data != -1) { shmdt(data); } } @@ -81,11 +80,11 @@ public: void *data; }; -struct x11_img_t: public img_t { +struct x11_img_t : public img_t { ximg_t img; }; -struct shm_img_t: public img_t { +struct shm_img_t : public img_t { ~shm_img_t() override { delete[] data; data = nullptr; @@ -95,7 +94,7 @@ struct shm_img_t: public img_t { void blend_cursor(Display *display, img_t &img, int offsetX, int offsetY) { xcursor_t overlay { XFixesGetCursorImage(display) }; - if (!overlay) { + if(!overlay) { BOOST_LOG(error) << "Couldn't get cursor from XFixesGetCursorImage"sv; return; } @@ -109,40 +108,39 @@ void blend_cursor(Display *display, img_t &img, int offsetX, int offsetY) { overlay->x = std::max((short)0, overlay->x); overlay->y = std::max((short)0, overlay->y); - auto pixels = (int*)img.data; + auto pixels = (int *)img.data; auto screen_height = img.height; - auto screen_width = img.width; + auto screen_width = img.width; - auto delta_height = std::min(overlay->height,std::max(0, screen_height - overlay->y)); - auto delta_width = std::min(overlay->width,std::max(0, screen_width - overlay->x)); - for (auto y = 0; y < delta_height; ++y) { + auto delta_height = std::min(overlay->height, std::max(0, screen_height - overlay->y)); + auto delta_width = std::min(overlay->width, std::max(0, screen_width - overlay->x)); + for(auto y = 0; y < delta_height; ++y) { auto overlay_begin = &overlay->pixels[y * overlay->width]; - auto overlay_end = &overlay->pixels[y * overlay->width + delta_width]; + auto overlay_end = &overlay->pixels[y * overlay->width + delta_width]; - auto pixels_begin = &pixels[(y + overlay->y)* (img.row_pitch / img.pixel_pitch) + overlay->x]; + auto pixels_begin = &pixels[(y + overlay->y) * (img.row_pitch / img.pixel_pitch) + overlay->x]; - std::for_each(overlay_begin, overlay_end,[&](long pixel) { - int *pixel_p = (int*) &pixel; + std::for_each(overlay_begin, overlay_end, [&](long pixel) { + int *pixel_p = (int *)&pixel; - auto colors_in = (uint8_t*) pixels_begin; + auto colors_in = (uint8_t *)pixels_begin; - auto alpha = (*(uint*) pixel_p) >> 24u; - if (alpha == 255) { + auto alpha = (*(uint *)pixel_p) >> 24u; + if(alpha == 255) { *pixels_begin = *pixel_p; } else { - auto colors_out = (uint8_t*) pixel_p; - colors_in[0] = colors_out[0] + (colors_in[0] * (255 - alpha) +255 /2) / 255; - colors_in[1] = colors_out[1] + (colors_in[1] * (255 - alpha) + 255 / 2) / 255; - colors_in[2] = colors_out[2] + (colors_in[2] * (255 - alpha) + 255 / 2) / 255; + auto colors_out = (uint8_t *)pixel_p; + colors_in[0] = colors_out[0] + (colors_in[0] * (255 - alpha) + 255 / 2) / 255; + colors_in[1] = colors_out[1] + (colors_in[1] * (255 - alpha) + 255 / 2) / 255; + colors_in[2] = colors_out[2] + (colors_in[2] * (255 - alpha) + 255 / 2) / 255; } ++pixels_begin; }); } } -struct x11_attr_t: public display_t -{ +struct x11_attr_t : public display_t { xdisplay_t xdisplay; Window xwindow; XWindowAttributes xattr; @@ -153,7 +151,7 @@ struct x11_attr_t: public display_t */ int lastWidth, lastHeight; - x11_attr_t() : xdisplay { XOpenDisplay(nullptr) }, xwindow { }, xattr { } { + x11_attr_t() : xdisplay { XOpenDisplay(nullptr) }, xwindow {}, xattr {} { XInitThreads(); } @@ -192,17 +190,17 @@ struct x11_attr_t: public display_t BOOST_LOG(info) << "Streaming display: "sv << out_info->name << " with res "sv << crt_info->width << 'x' << crt_info->height << " offset by "sv << crt_info->x << 'x' << crt_info->y; - width = crt_info->width; - height = crt_info->height; + width = crt_info->width; + height = crt_info->height; offset_x = crt_info->x; offset_y = crt_info->y; } else { - width = xattr.width; + width = xattr.width; height = xattr.height; } - lastWidth = xattr.width; + lastWidth = xattr.width; lastHeight = xattr.height; return 0; @@ -219,21 +217,21 @@ struct x11_attr_t: public display_t refresh(); //The whole X server changed, so we gotta reinit everything - if (xattr.width != lastWidth || xattr.height != lastHeight) { - BOOST_LOG(warning)<< "X dimensions changed in non-SHM mode, request reinit"sv; + if(xattr.width != lastWidth || xattr.height != lastHeight) { + BOOST_LOG(warning) << "X dimensions changed in non-SHM mode, request reinit"sv; return capture_e::reinit; } XImage *img { XGetImage(xdisplay.get(), xwindow, offset_x, offset_y, width, height, AllPlanes, ZPixmap) }; - auto img_out = (x11_img_t*) img_out_base; - img_out->width = img->width; - img_out->height = img->height; - img_out->data = (uint8_t*) img->data; - img_out->row_pitch = img->bytes_per_line; + auto img_out = (x11_img_t *)img_out_base; + img_out->width = img->width; + img_out->height = img->height; + img_out->data = (uint8_t *)img->data; + img_out->row_pitch = img->bytes_per_line; img_out->pixel_pitch = img->bits_per_pixel / 8; img_out->img.reset(img); - if (cursor) { + if(cursor) { blend_cursor(xdisplay.get(), *img_out_base, offset_x, offset_y); } @@ -250,7 +248,7 @@ struct x11_attr_t: public display_t } }; -struct shm_attr_t: public x11_attr_t { +struct shm_attr_t : public x11_attr_t { xdisplay_t shm_xdisplay; // Prevent race condition with x11_attr_t::xdisplay xcb_connect_t xcb; xcb_screen_t *display; @@ -265,7 +263,7 @@ struct shm_attr_t: public x11_attr_t { void delayed_refresh() { refresh(); - refresh_task_id = task_pool.pushDelayed(&shm_attr_t::delayed_refresh,2s, this).task_id; + refresh_task_id = task_pool.pushDelayed(&shm_attr_t::delayed_refresh, 2s, this).task_id; } shm_attr_t() : x11_attr_t(), shm_xdisplay { XOpenDisplay(nullptr) } { @@ -273,25 +271,26 @@ struct shm_attr_t: public x11_attr_t { } ~shm_attr_t() override { - while(!task_pool.cancel(refresh_task_id)); + while(!task_pool.cancel(refresh_task_id)) + ; } capture_e snapshot(img_t *img, std::chrono::milliseconds timeout, bool cursor) override { //The whole X server changed, so we gotta reinit everything - if(xattr.width != lastWidth || xattr.height != lastHeight) { - BOOST_LOG(warning)<< "X dimensions changed in SHM mode, request reinit"sv; + if(xattr.width != lastWidth || xattr.height != lastHeight) { + BOOST_LOG(warning) << "X dimensions changed in SHM mode, request reinit"sv; return capture_e::reinit; } else { auto img_cookie = xcb_shm_get_image_unchecked(xcb.get(), display->root, offset_x, offset_y, width, height, ~0, XCB_IMAGE_FORMAT_Z_PIXMAP, seg, 0); - xcb_img_t img_reply { xcb_shm_get_image_reply(xcb.get(), img_cookie,nullptr) }; + xcb_img_t img_reply { xcb_shm_get_image_reply(xcb.get(), img_cookie, nullptr) }; if(!img_reply) { BOOST_LOG(error) << "Could not get image reply"sv; return capture_e::reinit; } - std::copy_n((std::uint8_t*)data.data, frame_size(), img->data); + std::copy_n((std::uint8_t *)data.data, frame_size(), img->data); if(cursor) { blend_cursor(shm_xdisplay.get(), *img, offset_x, offset_y); @@ -302,12 +301,12 @@ struct shm_attr_t: public x11_attr_t { } std::shared_ptr alloc_img() override { - auto img = std::make_shared(); - img->width = width; - img->height = height; + auto img = std::make_shared(); + img->width = width; + img->height = height; img->pixel_pitch = 4; - img->row_pitch = img->pixel_pitch * width; - img->data = new std::uint8_t[height * img->row_pitch]; + img->row_pitch = img->pixel_pitch * width; + img->data = new std::uint8_t[height * img->row_pitch]; return img; } @@ -334,8 +333,8 @@ struct shm_attr_t: public x11_attr_t { } auto iter = xcb_setup_roots_iterator(xcb_get_setup(xcb.get())); - display = iter.data; - seg = xcb_generate_id(xcb.get()); + display = iter.data; + seg = xcb_generate_id(xcb.get()); shm_id.id = shmget(IPC_PRIVATE, frame_size(), IPC_CREAT | 0777); if(shm_id.id == -1) { @@ -360,19 +359,19 @@ struct shm_attr_t: public x11_attr_t { } }; -struct mic_attr_t: public mic_t { +struct mic_attr_t : public mic_t { pa_sample_spec ss; util::safe_ptr mic; explicit mic_attr_t(pa_sample_format format, std::uint32_t sample_rate, - std::uint8_t channels) : ss { format, sample_rate, channels }, mic { } { } + std::uint8_t channels) : ss { format, sample_rate, channels }, mic {} {} capture_e sample(std::vector &sample_buf) override { auto sample_size = sample_buf.size(); auto buf = sample_buf.data(); int status; - if(pa_simple_read(mic.get(), buf, sample_size *2, &status)) { + if(pa_simple_read(mic.get(), buf, sample_size * 2, &status)) { BOOST_LOG(error) << "pa_simple_read() failed: "sv << pa_strerror(status); return capture_e::error; @@ -384,7 +383,7 @@ struct mic_attr_t: public mic_t { std::shared_ptr display(platf::dev_type_e hwdevice_type) { if(hwdevice_type != platf::dev_type_e::none) { - BOOST_LOG(error)<< "Could not initialize display with the given hw device type."sv; + BOOST_LOG(error) << "Could not initialize display with the given hw device type."sv; return nullptr; } @@ -416,16 +415,16 @@ std::unique_ptr microphone(std::uint32_t sample_rate, std::uint32_t) { int status; const char *audio_sink = "@DEFAULT_MONITOR@"; - if (!config::audio.sink.empty()) { + if(!config::audio.sink.empty()) { audio_sink = config::audio.sink.c_str(); } mic->mic.reset( pa_simple_new(nullptr, "sunshine", - pa_stream_direction_t::PA_STREAM_RECORD, audio_sink, + pa_stream_direction_t::PA_STREAM_RECORD, audio_sink, "sunshine-record", &mic->ss, nullptr, nullptr, &status)); - if (!mic->mic) { + if(!mic->mic) { auto err_str = pa_strerror(status); BOOST_LOG(error) << "pa_simple_new() failed: "sv << err_str; @@ -448,14 +447,14 @@ std::string from_sockaddr(const sockaddr *const ip_addr) { char data[INET6_ADDRSTRLEN]; auto family = ip_addr->sa_family; - if (family == AF_INET6) { - inet_ntop(AF_INET6, &((sockaddr_in6*) ip_addr)->sin6_addr, data, - INET6_ADDRSTRLEN); + if(family == AF_INET6) { + inet_ntop(AF_INET6, &((sockaddr_in6 *)ip_addr)->sin6_addr, data, + INET6_ADDRSTRLEN); } - if (family == AF_INET) { - inet_ntop(AF_INET, &((sockaddr_in*) ip_addr)->sin_addr, data, - INET_ADDRSTRLEN); + if(family == AF_INET) { + inet_ntop(AF_INET, &((sockaddr_in *)ip_addr)->sin_addr, data, + INET_ADDRSTRLEN); } return std::string { data }; @@ -467,26 +466,26 @@ std::pair from_sockaddr_ex(const sockaddr *const ip_ auto family = ip_addr->sa_family; std::uint16_t port; if(family == AF_INET6) { - inet_ntop(AF_INET6, &((sockaddr_in6*)ip_addr)->sin6_addr, data, - INET6_ADDRSTRLEN); - port = ((sockaddr_in6*) ip_addr)->sin6_port; + inet_ntop(AF_INET6, &((sockaddr_in6 *)ip_addr)->sin6_addr, data, + INET6_ADDRSTRLEN); + port = ((sockaddr_in6 *)ip_addr)->sin6_port; } if(family == AF_INET) { - inet_ntop(AF_INET, &((sockaddr_in*)ip_addr)->sin_addr, data, - INET_ADDRSTRLEN); - port = ((sockaddr_in*) ip_addr)->sin_port; + inet_ntop(AF_INET, &((sockaddr_in *)ip_addr)->sin_addr, data, + INET_ADDRSTRLEN); + port = ((sockaddr_in *)ip_addr)->sin_port; } - return { port, std::string {data} }; + return { port, std::string { data } }; } std::string get_mac_address(const std::string_view &address) { auto ifaddrs = get_ifaddrs(); - for (auto pos = ifaddrs.get(); pos != nullptr; pos = pos->ifa_next) { - if (pos->ifa_addr && address == from_sockaddr(pos->ifa_addr)) { + for(auto pos = ifaddrs.get(); pos != nullptr; pos = pos->ifa_next) { + if(pos->ifa_addr && address == from_sockaddr(pos->ifa_addr)) { std::ifstream mac_file("/sys/class/net/"s + pos->ifa_name + "/address"); - if (mac_file.good()) { + if(mac_file.good()) { std::string mac_address; std::getline(mac_file, mac_address); return mac_address; @@ -504,4 +503,4 @@ void freeImage(XImage *p) { void freeX(XFixesCursorImage *p) { XFree(p); } -} +} // namespace platf diff --git a/sunshine/platform/linux/input.cpp b/sunshine/platform/linux/input.cpp index b0a5cec1..ed3b5985 100644 --- a/sunshine/platform/linux/input.cpp +++ b/sunshine/platform/linux/input.cpp @@ -1,5 +1,5 @@ -#include #include +#include #include #include @@ -10,8 +10,8 @@ #include #include -#include "sunshine/platform/common.h" #include "sunshine/main.h" +#include "sunshine/platform/common.h" #include "sunshine/utility.h" // Support older versions @@ -25,7 +25,7 @@ namespace platf { using namespace std::literals; -using evdev_t = util::safe_ptr; +using evdev_t = util::safe_ptr; using uinput_t = util::safe_ptr; using keyboard_t = util::safe_ptr_v2; @@ -66,7 +66,7 @@ public: std::filesystem::remove(gamepad_path); } - gamepads[nr] = std::make_pair(uinput_t{}, gamepad_state_t {}); + gamepads[nr] = std::make_pair(uinput_t {}, gamepad_state_t {}); } int create_mouse() { @@ -143,7 +143,7 @@ public: }; void abs_mouse(input_t &input, const touch_port_t &touch_port, float x, float y) { - auto touchscreen = ((input_raw_t*)input.get())->touch_input.get(); + auto touchscreen = ((input_raw_t *)input.get())->touch_input.get(); auto scaled_x = (int)std::lround((x + touch_port.offset_x) * ((float)target_touch_port.width / (float)touch_port.width)); auto scaled_y = (int)std::lround((y + touch_port.offset_y) * ((float)target_touch_port.height / (float)touch_port.height)); @@ -157,7 +157,7 @@ void abs_mouse(input_t &input, const touch_port_t &touch_port, float x, float y) } void move_mouse(input_t &input, int deltaX, int deltaY) { - auto mouse = ((input_raw_t*)input.get())->mouse_input.get(); + auto mouse = ((input_raw_t *)input.get())->mouse_input.get(); if(deltaX) { libevdev_uinput_write_event(mouse, EV_REL, REL_X, deltaX); @@ -176,26 +176,26 @@ void button_mouse(input_t &input, int button, bool release) { if(button == 1) { btn_type = BTN_LEFT; - scan = 90001; + scan = 90001; } else if(button == 2) { btn_type = BTN_MIDDLE; - scan = 90003; + scan = 90003; } else if(button == 3) { btn_type = BTN_RIGHT; - scan = 90002; + scan = 90002; } else if(button == 4) { btn_type = BTN_SIDE; - scan = 90004; + scan = 90004; } else { btn_type = BTN_EXTRA; - scan = 90005; + scan = 90005; } - auto mouse = ((input_raw_t*)input.get())->mouse_input.get(); + auto mouse = ((input_raw_t *)input.get())->mouse_input.get(); libevdev_uinput_write_event(mouse, EV_MSC, MSC_SCAN, scan); libevdev_uinput_write_event(mouse, EV_KEY, btn_type, release ? 0 : 1); libevdev_uinput_write_event(mouse, EV_SYN, SYN_REPORT, 0); @@ -204,7 +204,7 @@ void button_mouse(input_t &input, int button, bool release) { void scroll(input_t &input, int high_res_distance) { int distance = high_res_distance / 120; - auto mouse = ((input_raw_t*)input.get())->mouse_input.get(); + auto mouse = ((input_raw_t *)input.get())->mouse_input.get(); libevdev_uinput_write_event(mouse, EV_REL, REL_WHEEL, distance); libevdev_uinput_write_event(mouse, EV_REL, REL_WHEEL_HI_RES, high_res_distance); libevdev_uinput_write_event(mouse, EV_SYN, SYN_REPORT, 0); @@ -212,7 +212,7 @@ void scroll(input_t &input, int high_res_distance) { uint16_t keysym(uint16_t modcode) { constexpr auto VK_NUMPAD = 0x60; - constexpr auto VK_F1 = 0x70; + constexpr auto VK_F1 = 0x70; if(modcode >= VK_NUMPAD && modcode < VK_NUMPAD + 10) { return XK_KP_0 + (modcode - VK_NUMPAD); @@ -224,108 +224,108 @@ uint16_t keysym(uint16_t modcode) { switch(modcode) { - case 0x08: - return XK_BackSpace; - case 0x09: - return XK_Tab; - case 0x0D: - return XK_Return; - case 0x13: - return XK_Pause; - case 0x14: - return XK_Caps_Lock; - case 0x1B: - return XK_Escape; - case 0x21: - return XK_Page_Up; - case 0x22: - return XK_Page_Down; - case 0x23: - return XK_End; - case 0x24: - return XK_Home; - case 0x25: - return XK_Left; - case 0x26: - return XK_Up; - case 0x27: - return XK_Right; - case 0x28: - return XK_Down; - case 0x29: - return XK_Select; - case 0x2B: - return XK_Execute; - case 0x2C: - return XK_Print; - case 0x2D: - return XK_Insert; - case 0x2E: - return XK_Delete; - case 0x2F: - return XK_Help; - case 0x6A: - return XK_KP_Multiply; - case 0x6B: - return XK_KP_Add; - case 0x6C: - return XK_KP_Separator; - case 0x6D: - return XK_KP_Subtract; - case 0x6E: - return XK_KP_Decimal; - case 0x6F: - return XK_KP_Divide; - case 0x90: - return XK_Num_Lock; - case 0x91: - return XK_Scroll_Lock; - case 0xA0: - return XK_Shift_L; - case 0xA1: - return XK_Shift_R; - case 0xA2: - return XK_Control_L; - case 0xA3: - return XK_Control_R; - case 0xA4: - return XK_Alt_L; - case 0xA5: /* return XK_Alt_R; */ - return XK_Super_L; - case 0x5B: - return XK_Super_L; - case 0x5C: - return XK_Super_R; - case 0xBA: - return XK_semicolon; - case 0xBB: - return XK_equal; - case 0xBC: - return XK_comma; - case 0xBD: - return XK_minus; - case 0xBE: - return XK_period; - case 0xBF: - return XK_slash; - case 0xC0: - return XK_grave; - case 0xDB: - return XK_bracketleft; - case 0xDC: - return XK_backslash; - case 0xDD: - return XK_bracketright; - case 0xDE: - return XK_apostrophe; + case 0x08: + return XK_BackSpace; + case 0x09: + return XK_Tab; + case 0x0D: + return XK_Return; + case 0x13: + return XK_Pause; + case 0x14: + return XK_Caps_Lock; + case 0x1B: + return XK_Escape; + case 0x21: + return XK_Page_Up; + case 0x22: + return XK_Page_Down; + case 0x23: + return XK_End; + case 0x24: + return XK_Home; + case 0x25: + return XK_Left; + case 0x26: + return XK_Up; + case 0x27: + return XK_Right; + case 0x28: + return XK_Down; + case 0x29: + return XK_Select; + case 0x2B: + return XK_Execute; + case 0x2C: + return XK_Print; + case 0x2D: + return XK_Insert; + case 0x2E: + return XK_Delete; + case 0x2F: + return XK_Help; + case 0x6A: + return XK_KP_Multiply; + case 0x6B: + return XK_KP_Add; + case 0x6C: + return XK_KP_Separator; + case 0x6D: + return XK_KP_Subtract; + case 0x6E: + return XK_KP_Decimal; + case 0x6F: + return XK_KP_Divide; + case 0x90: + return XK_Num_Lock; + case 0x91: + return XK_Scroll_Lock; + case 0xA0: + return XK_Shift_L; + case 0xA1: + return XK_Shift_R; + case 0xA2: + return XK_Control_L; + case 0xA3: + return XK_Control_R; + case 0xA4: + return XK_Alt_L; + case 0xA5: /* return XK_Alt_R; */ + return XK_Super_L; + case 0x5B: + return XK_Super_L; + case 0x5C: + return XK_Super_R; + case 0xBA: + return XK_semicolon; + case 0xBB: + return XK_equal; + case 0xBC: + return XK_comma; + case 0xBD: + return XK_minus; + case 0xBE: + return XK_period; + case 0xBF: + return XK_slash; + case 0xC0: + return XK_grave; + case 0xDB: + return XK_bracketleft; + case 0xDC: + return XK_backslash; + case 0xDD: + return XK_bracketright; + case 0xDE: + return XK_apostrophe; } return modcode; } void keyboard(input_t &input, uint16_t modcode, bool release) { - auto &keyboard = ((input_raw_t*)input.get())->keyboard; - KeyCode kc = XKeysymToKeycode(keyboard.get(), keysym(modcode)); + auto &keyboard = ((input_raw_t *)input.get())->keyboard; + KeyCode kc = XKeysymToKeycode(keyboard.get(), keysym(modcode)); if(!kc) { return; @@ -338,18 +338,18 @@ void keyboard(input_t &input, uint16_t modcode, bool release) { } int alloc_gamepad(input_t &input, int nr) { - return ((input_raw_t*)input.get())->alloc_gamepad(nr); + return ((input_raw_t *)input.get())->alloc_gamepad(nr); } void free_gamepad(input_t &input, int nr) { - ((input_raw_t*)input.get())->clear_gamepad(nr); + ((input_raw_t *)input.get())->clear_gamepad(nr); } void gamepad(input_t &input, int nr, const gamepad_state_t &gamepad_state) { - TUPLE_2D_REF(uinput, gamepad_state_old, ((input_raw_t*)input.get())->gamepads[nr]); + TUPLE_2D_REF(uinput, gamepad_state_old, ((input_raw_t *)input.get())->gamepads[nr]); - auto bf = gamepad_state.buttonFlags ^ gamepad_state_old.buttonFlags; + auto bf = gamepad_state.buttonFlags ^ gamepad_state_old.buttonFlags; auto bf_new = gamepad_state.buttonFlags; if(bf) { @@ -366,17 +366,17 @@ void gamepad(input_t &input, int nr, const gamepad_state_t &gamepad_state) { libevdev_uinput_write_event(uinput.get(), EV_ABS, ABS_HAT0X, button_state); } - if(START & bf) libevdev_uinput_write_event(uinput.get(), EV_KEY, BTN_START, bf_new & START ? 1 : 0); - if(BACK & bf) libevdev_uinput_write_event(uinput.get(), EV_KEY, BTN_SELECT, bf_new & BACK ? 1 : 0); - if(LEFT_STICK & bf) libevdev_uinput_write_event(uinput.get(), EV_KEY, BTN_THUMBL, bf_new & LEFT_STICK ? 1 : 0); - if(RIGHT_STICK & bf) libevdev_uinput_write_event(uinput.get(), EV_KEY, BTN_THUMBR, bf_new & RIGHT_STICK ? 1 : 0); - if(LEFT_BUTTON & bf) libevdev_uinput_write_event(uinput.get(), EV_KEY, BTN_TL, bf_new & LEFT_BUTTON ? 1 : 0); - if(RIGHT_BUTTON & bf) libevdev_uinput_write_event(uinput.get(), EV_KEY, BTN_TR, bf_new & RIGHT_BUTTON ? 1 : 0); - if(HOME & bf) libevdev_uinput_write_event(uinput.get(), EV_KEY, BTN_MODE, bf_new & HOME ? 1 : 0); - if(A & bf) libevdev_uinput_write_event(uinput.get(), EV_KEY, BTN_SOUTH, bf_new & A ? 1 : 0); - if(B & bf) libevdev_uinput_write_event(uinput.get(), EV_KEY, BTN_EAST, bf_new & B ? 1 : 0); - if(X & bf) libevdev_uinput_write_event(uinput.get(), EV_KEY, BTN_NORTH, bf_new & X ? 1 : 0); - if(Y & bf) libevdev_uinput_write_event(uinput.get(), EV_KEY, BTN_WEST, bf_new & Y ? 1 : 0); + if(START & bf) libevdev_uinput_write_event(uinput.get(), EV_KEY, BTN_START, bf_new & START ? 1 : 0); + if(BACK & bf) libevdev_uinput_write_event(uinput.get(), EV_KEY, BTN_SELECT, bf_new & BACK ? 1 : 0); + if(LEFT_STICK & bf) libevdev_uinput_write_event(uinput.get(), EV_KEY, BTN_THUMBL, bf_new & LEFT_STICK ? 1 : 0); + if(RIGHT_STICK & bf) libevdev_uinput_write_event(uinput.get(), EV_KEY, BTN_THUMBR, bf_new & RIGHT_STICK ? 1 : 0); + if(LEFT_BUTTON & bf) libevdev_uinput_write_event(uinput.get(), EV_KEY, BTN_TL, bf_new & LEFT_BUTTON ? 1 : 0); + if(RIGHT_BUTTON & bf) libevdev_uinput_write_event(uinput.get(), EV_KEY, BTN_TR, bf_new & RIGHT_BUTTON ? 1 : 0); + if(HOME & bf) libevdev_uinput_write_event(uinput.get(), EV_KEY, BTN_MODE, bf_new & HOME ? 1 : 0); + if(A & bf) libevdev_uinput_write_event(uinput.get(), EV_KEY, BTN_SOUTH, bf_new & A ? 1 : 0); + if(B & bf) libevdev_uinput_write_event(uinput.get(), EV_KEY, BTN_EAST, bf_new & B ? 1 : 0); + if(X & bf) libevdev_uinput_write_event(uinput.get(), EV_KEY, BTN_NORTH, bf_new & X ? 1 : 0); + if(Y & bf) libevdev_uinput_write_event(uinput.get(), EV_KEY, BTN_WEST, bf_new & Y ? 1 : 0); } if(gamepad_state_old.lt != gamepad_state.lt) { @@ -408,7 +408,7 @@ void gamepad(input_t &input, int nr, const gamepad_state_t &gamepad_state) { } evdev_t mouse() { - evdev_t dev { libevdev_new() }; + evdev_t dev { libevdev_new() }; libevdev_set_uniq(dev.get(), "Sunshine Mouse"); libevdev_set_id_product(dev.get(), 0x4038); @@ -553,7 +553,7 @@ evdev_t x360() { input_t input() { input_t result { new input_raw_t() }; - auto &gp = *(input_raw_t*)result.get(); + auto &gp = *(input_raw_t *)result.get(); gp.keyboard.reset(XOpenDisplay(nullptr)); @@ -568,8 +568,8 @@ input_t input() { // Ensure starting from clean slate gp.clear(); - gp.touch_dev = touchscreen(); - gp.mouse_dev = mouse(); + gp.touch_dev = touchscreen(); + gp.mouse_dev = mouse(); gp.gamepad_dev = x360(); if(gp.create_mouse() || gp.create_touchscreen()) { @@ -581,9 +581,9 @@ input_t input() { } void freeInput(void *p) { - auto *input = (input_raw_t*)p; + auto *input = (input_raw_t *)p; delete input; } std::unique_ptr init() { return std::make_unique(); } -} +} // namespace platf diff --git a/sunshine/platform/windows/audio.cpp b/sunshine/platform/windows/audio.cpp index 4f453ac0..d59efb50 100644 --- a/sunshine/platform/windows/audio.cpp +++ b/sunshine/platform/windows/audio.cpp @@ -2,9 +2,9 @@ // Created by loki on 1/12/20. // -#include -#include #include +#include +#include #include @@ -36,7 +36,7 @@ using device_t = util::safe_ptr>; using audio_client_t = util::safe_ptr>; using audio_capture_t = util::safe_ptr>; using wave_format_t = util::safe_ptr>; -using handle_t = util::safe_ptr_v2; +using handle_t = util::safe_ptr_v2; class co_init_t : public deinit_t { public: @@ -53,32 +53,26 @@ struct format_t { std::string_view name; int channels; int channel_mask; -} formats [] { - { - "Stereo"sv, +} formats[] { + { "Stereo"sv, 2, - SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT - }, - { - "Mono"sv, + SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT }, + { "Mono"sv, 1, - SPEAKER_FRONT_CENTER - }, - { - "Surround 5.1"sv, + SPEAKER_FRONT_CENTER }, + { "Surround 5.1"sv, 6, - SPEAKER_FRONT_LEFT | - SPEAKER_FRONT_RIGHT | - SPEAKER_FRONT_CENTER | - SPEAKER_LOW_FREQUENCY | - SPEAKER_BACK_LEFT | - SPEAKER_BACK_RIGHT - } + SPEAKER_FRONT_LEFT | + SPEAKER_FRONT_RIGHT | + SPEAKER_FRONT_CENTER | + SPEAKER_LOW_FREQUENCY | + SPEAKER_BACK_LEFT | + SPEAKER_BACK_RIGHT } }; void set_wave_format(audio::wave_format_t &wave_format, const format_t &format) { - wave_format->nChannels = format.channels; - wave_format->nBlockAlign = wave_format->nChannels * wave_format->wBitsPerSample / 8; + wave_format->nChannels = format.channels; + wave_format->nBlockAlign = wave_format->nChannels * wave_format->wBitsPerSample / 8; wave_format->nAvgBytesPerSec = wave_format->nSamplesPerSec * wave_format->nBlockAlign; if(wave_format->wFormatTag == WAVE_FORMAT_EXTENSIBLE) { @@ -97,8 +91,8 @@ void surround51_to_stereo(std::vector &sample_in, const util::buff channels51 // number of channels in surround sound }; - auto sample_in_pos = std::begin(sample_in); - auto sample_end = std::begin(sample_out) + sample_in.size() / 2 * channels51; + auto sample_in_pos = std::begin(sample_in); + auto sample_end = std::begin(sample_out) + sample_in.size() / 2 * channels51; for(auto sample_out_p = std::begin(sample_out); sample_out_p != sample_end; sample_out_p += channels51) { std::uint32_t left {}, right {}; @@ -113,7 +107,8 @@ void surround51_to_stereo(std::vector &sample_in, const util::buff right += sample_out_p[front_center] * 90 / 100; right += sample_out_p[low_frequency] * 30 / 100; right += sample_out_p[back_left] * 30 / 100; - right += sample_out_p[back_right] * 70 / 100;; + right += sample_out_p[back_right] * 70 / 100; + ; *sample_in_pos++ = (std::uint16_t)left; *sample_in_pos++ = (std::uint16_t)right; @@ -121,8 +116,8 @@ void surround51_to_stereo(std::vector &sample_in, const util::buff } void mono_to_stereo(std::vector &sample_in, const util::buffer_t &sample_out) { - auto sample_in_pos = std::begin(sample_in); - auto sample_end = std::begin(sample_out) + sample_in.size() / 2; + auto sample_in_pos = std::begin(sample_in); + auto sample_end = std::begin(sample_out) + sample_in.size() / 2; for(auto sample_out_p = std::begin(sample_out); sample_out_p != sample_end; ++sample_out_p) { *sample_in_pos++ = *sample_out_p; @@ -156,23 +151,23 @@ audio_client_t make_audio_client(device_t &device, const format_t &format, int s wave_format->wBitsPerSample = 16; wave_format->nSamplesPerSec = sample_rate; switch(wave_format->wFormatTag) { - case WAVE_FORMAT_PCM: + case WAVE_FORMAT_PCM: + break; + case WAVE_FORMAT_IEEE_FLOAT: + break; + case WAVE_FORMAT_EXTENSIBLE: { + auto wave_ex = (PWAVEFORMATEXTENSIBLE)wave_format.get(); + if(IsEqualGUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, wave_ex->SubFormat)) { + wave_ex->SubFormat = KSDATAFORMAT_SUBTYPE_PCM; + wave_ex->Samples.wValidBitsPerSample = 16; break; - case WAVE_FORMAT_IEEE_FLOAT: - break; - case WAVE_FORMAT_EXTENSIBLE: { - auto wave_ex = (PWAVEFORMATEXTENSIBLE) wave_format.get(); - if (IsEqualGUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, wave_ex->SubFormat)) { - wave_ex->SubFormat = KSDATAFORMAT_SUBTYPE_PCM; - wave_ex->Samples.wValidBitsPerSample = 16; - break; - } - - BOOST_LOG(error) << "Unsupported Sub Format for WAVE_FORMAT_EXTENSIBLE: [0x"sv << util::hex(wave_ex->SubFormat).to_string_view() << ']'; } - default: - BOOST_LOG(error) << "Unsupported Wave Format: [0x"sv << util::hex(wave_format->wFormatTag).to_string_view() << ']'; - return nullptr; + + BOOST_LOG(error) << "Unsupported Sub Format for WAVE_FORMAT_EXTENSIBLE: [0x"sv << util::hex(wave_ex->SubFormat).to_string_view() << ']'; + } + default: + BOOST_LOG(error) << "Unsupported Wave Format: [0x"sv << util::hex(wave_format->wFormatTag).to_string_view() << ']'; + return nullptr; }; set_wave_format(wave_format, format); @@ -195,7 +190,7 @@ audio_client_t make_audio_client(device_t &device, const format_t &format, int s class mic_wasapi_t : public mic_t { public: capture_e sample(std::vector &sample_in) override { - auto sample_size = sample_in.size() /2 * format->channels; + auto sample_size = sample_in.size() / 2 * format->channels; while(sample_buf_pos - std::begin(sample_buf) < sample_size) { //FIXME: Use IAudioClient3 instead of IAudioClient, that would allows for adjusting the latency of the audio samples auto capture_result = _fill_buffer(); @@ -243,7 +238,7 @@ public: nullptr, CLSCTX_ALL, IID_IMMDeviceEnumerator, - (void **) &device_enum); + (void **)&device_enum); if(FAILED(status)) { BOOST_LOG(error) << "Couldn't create Device Enumerator [0x"sv << util::hex(status).to_string_view() << ']'; @@ -292,32 +287,32 @@ public: std::uint32_t frames; status = audio_client->GetBufferSize(&frames); - if (FAILED(status)) { + if(FAILED(status)) { BOOST_LOG(error) << "Couldn't acquire the number of audio frames [0x"sv << util::hex(status).to_string_view() << ']'; return -1; } // *2 --> needs to fit double - sample_buf = util::buffer_t { std::max(frames *2, frame_size * format->channels *2) }; + sample_buf = util::buffer_t { std::max(frames * 2, frame_size * format->channels * 2) }; sample_buf_pos = std::begin(sample_buf); - status = audio_client->GetService(IID_IAudioCaptureClient, (void**)&audio_capture); - if (FAILED(status)) { + status = audio_client->GetService(IID_IAudioCaptureClient, (void **)&audio_capture); + if(FAILED(status)) { BOOST_LOG(error) << "Couldn't initialize audio capture client [0x"sv << util::hex(status).to_string_view() << ']'; return -1; } status = audio_client->SetEventHandle(audio_event.get()); - if (FAILED(status)) { + if(FAILED(status)) { BOOST_LOG(error) << "Couldn't set event handle [0x"sv << util::hex(status).to_string_view() << ']'; return -1; } status = audio_client->Start(); - if (FAILED(status)) { + if(FAILED(status)) { BOOST_LOG(error) << "Couldn't start recording [0x"sv << util::hex(status).to_string_view() << ']'; return -1; @@ -331,6 +326,7 @@ public: audio_client->Stop(); } } + private: capture_e _fill_buffer() { HRESULT status; @@ -347,45 +343,45 @@ private: } block_aligned; status = WaitForSingleObjectEx(audio_event.get(), default_latency_ms, FALSE); - switch (status) { - case WAIT_OBJECT_0: - break; - case WAIT_TIMEOUT: - return capture_e::timeout; - default: - BOOST_LOG(error) << "Couldn't wait for audio event: [0x"sv << util::hex(status).to_string_view() << ']'; - return capture_e::error; + switch(status) { + case WAIT_OBJECT_0: + break; + case WAIT_TIMEOUT: + return capture_e::timeout; + default: + BOOST_LOG(error) << "Couldn't wait for audio event: [0x"sv << util::hex(status).to_string_view() << ']'; + return capture_e::error; } - std::uint32_t packet_size{}; - for ( + std::uint32_t packet_size {}; + for( status = audio_capture->GetNextPacketSize(&packet_size); SUCCEEDED(status) && packet_size > 0; - status = audio_capture->GetNextPacketSize(&packet_size) - ) { + status = audio_capture->GetNextPacketSize(&packet_size)) { DWORD buffer_flags; status = audio_capture->GetBuffer( - (BYTE **) &sample_aligned.samples, + (BYTE **)&sample_aligned.samples, &block_aligned.audio_sample_size, &buffer_flags, nullptr, nullptr); - switch (status) { - case S_OK: - break; - case AUDCLNT_E_DEVICE_INVALIDATED: - return capture_e::reinit; - default: - BOOST_LOG(error) << "Couldn't capture audio [0x"sv << util::hex(status).to_string_view() << ']'; - return capture_e::error; + switch(status) { + case S_OK: + break; + case AUDCLNT_E_DEVICE_INVALIDATED: + return capture_e::reinit; + default: + BOOST_LOG(error) << "Couldn't capture audio [0x"sv << util::hex(status).to_string_view() << ']'; + return capture_e::error; } sample_aligned.uninitialized = std::end(sample_buf) - sample_buf_pos; - auto n = std::min(sample_aligned.uninitialized, block_aligned.audio_sample_size * format->channels); + auto n = std::min(sample_aligned.uninitialized, block_aligned.audio_sample_size * format->channels); - if (buffer_flags & AUDCLNT_BUFFERFLAGS_SILENT) { + if(buffer_flags & AUDCLNT_BUFFERFLAGS_SILENT) { std::fill_n(sample_buf_pos, n, 0); - } else { + } + else { std::copy_n(sample_aligned.samples, n, sample_buf_pos); } @@ -394,16 +390,17 @@ private: audio_capture->ReleaseBuffer(block_aligned.audio_sample_size); } - if (status == AUDCLNT_E_DEVICE_INVALIDATED) { + if(status == AUDCLNT_E_DEVICE_INVALIDATED) { return capture_e::reinit; } - if (FAILED(status)) { + if(FAILED(status)) { return capture_e::error; } return capture_e::ok; } + public: handle_t audio_event; @@ -419,7 +416,7 @@ public: format_t *format; }; -} +} // namespace platf::audio namespace platf { @@ -444,4 +441,4 @@ std::unique_ptr init() { } return std::make_unique(); } -} +} // namespace platf diff --git a/sunshine/platform/windows/display.h b/sunshine/platform/windows/display.h index 8bc7ae9e..0c8f5b3d 100644 --- a/sunshine/platform/windows/display.h +++ b/sunshine/platform/windows/display.h @@ -5,14 +5,14 @@ #ifndef SUNSHINE_DISPLAY_H #define SUNSHINE_DISPLAY_H -#include #include #include #include +#include #include -#include "sunshine/utility.h" #include "sunshine/platform/common.h" +#include "sunshine/utility.h" namespace platf::dxgi { extern const char *format_str[]; @@ -43,7 +43,7 @@ using processor_t = util::safe_ptr>; using processor_in_t = util::safe_ptr>; using processor_enum_t = util::safe_ptr>; -} +} // namespace video class hwdevice_t; struct cursor_t { @@ -86,16 +86,14 @@ public: DXGI_FORMAT format; D3D_FEATURE_LEVEL feature_level; - typedef enum _D3DKMT_SCHEDULINGPRIORITYCLASS - { + typedef enum _D3DKMT_SCHEDULINGPRIORITYCLASS { D3DKMT_SCHEDULINGPRIORITYCLASS_IDLE, D3DKMT_SCHEDULINGPRIORITYCLASS_BELOW_NORMAL, D3DKMT_SCHEDULINGPRIORITYCLASS_NORMAL, D3DKMT_SCHEDULINGPRIORITYCLASS_ABOVE_NORMAL, D3DKMT_SCHEDULINGPRIORITYCLASS_HIGH, D3DKMT_SCHEDULINGPRIORITYCLASS_REALTIME - } - D3DKMT_SCHEDULINGPRIORITYCLASS; + } D3DKMT_SCHEDULINGPRIORITYCLASS; typedef NTSTATUS WINAPI (*PD3DKMTSetProcessSchedulingPriorityClass)(HANDLE, D3DKMT_SCHEDULINGPRIORITYCLASS); }; @@ -123,8 +121,8 @@ public: std::shared_ptr make_hwdevice(int width, int height, pix_fmt_e pix_fmt) override; gpu_cursor_t cursor; - std::vector hwdevices; + std::vector hwdevices; }; -} +} // namespace platf::dxgi #endif \ No newline at end of file diff --git a/sunshine/platform/windows/display_base.cpp b/sunshine/platform/windows/display_base.cpp index 46405397..58b93e62 100644 --- a/sunshine/platform/windows/display_base.cpp +++ b/sunshine/platform/windows/display_base.cpp @@ -23,18 +23,18 @@ capture_e duplication_t::next_frame(DXGI_OUTDUPL_FRAME_INFO &frame_info, std::ch auto status = dup->AcquireNextFrame(timeout.count(), &frame_info, res_p); switch(status) { - case S_OK: - has_frame = true; - return capture_e::ok; - case DXGI_ERROR_WAIT_TIMEOUT: - return capture_e::timeout; - case WAIT_ABANDONED: - case DXGI_ERROR_ACCESS_LOST: - case DXGI_ERROR_ACCESS_DENIED: - return capture_e::reinit; - default: - BOOST_LOG(error) << "Couldn't acquire next frame [0x"sv << util::hex(status).to_string_view(); - return capture_e::error; + case S_OK: + has_frame = true; + return capture_e::ok; + case DXGI_ERROR_WAIT_TIMEOUT: + return capture_e::timeout; + case WAIT_ABANDONED: + case DXGI_ERROR_ACCESS_LOST: + case DXGI_ERROR_ACCESS_DENIED: + return capture_e::reinit; + default: + BOOST_LOG(error) << "Couldn't acquire next frame [0x"sv << util::hex(status).to_string_view(); + return capture_e::error; } } @@ -52,20 +52,20 @@ capture_e duplication_t::release_frame() { } auto status = dup->ReleaseFrame(); - switch (status) { - case S_OK: - has_frame = false; - return capture_e::ok; - case DXGI_ERROR_WAIT_TIMEOUT: - return capture_e::timeout; - case WAIT_ABANDONED: - case DXGI_ERROR_ACCESS_LOST: - case DXGI_ERROR_ACCESS_DENIED: - has_frame = false; - return capture_e::reinit; - default: - BOOST_LOG(error) << "Couldn't release frame [0x"sv << util::hex(status).to_string_view(); - return capture_e::error; + switch(status) { + case S_OK: + has_frame = false; + return capture_e::ok; + case DXGI_ERROR_WAIT_TIMEOUT: + return capture_e::timeout; + case WAIT_ABANDONED: + case DXGI_ERROR_ACCESS_LOST: + case DXGI_ERROR_ACCESS_DENIED: + has_frame = false; + return capture_e::reinit; + default: + BOOST_LOG(error) << "Couldn't release frame [0x"sv << util::hex(status).to_string_view(); + return capture_e::error; } } @@ -74,7 +74,7 @@ duplication_t::~duplication_t() { } int display_base_t::init() { -/* Uncomment when use of IDXGIOutput5 is implemented + /* Uncomment when use of IDXGIOutput5 is implemented std::call_once(windows_cpp_once_flag, []() { DECLARE_HANDLE(DPI_AWARENESS_CONTEXT); const auto DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 = ((DPI_AWARENESS_CONTEXT)-4); @@ -93,7 +93,7 @@ int display_base_t::init() { HRESULT status; - status = CreateDXGIFactory1(IID_IDXGIFactory1, (void**)&factory); + status = CreateDXGIFactory1(IID_IDXGIFactory1, (void **)&factory); if(FAILED(status)) { BOOST_LOG(error) << "Failed to create DXGIFactory1 [0x"sv << util::hex(status).to_string_view() << ']'; return -1; @@ -102,7 +102,7 @@ int display_base_t::init() { std::wstring_convert, wchar_t> converter; auto adapter_name = converter.from_bytes(config::video.adapter_name); - auto output_name = converter.from_bytes(config::video.output_name); + auto output_name = converter.from_bytes(config::video.output_name); adapter_t::pointer adapter_p; for(int x = 0; factory->EnumAdapters1(x, &adapter_p) != DXGI_ERROR_NOT_FOUND; ++x) { @@ -131,8 +131,8 @@ int display_base_t::init() { offset_x = desc.DesktopCoordinates.left; offset_y = desc.DesktopCoordinates.top; - width = desc.DesktopCoordinates.right - offset_x; - height = desc.DesktopCoordinates.bottom - offset_y; + width = desc.DesktopCoordinates.right - offset_x; + height = desc.DesktopCoordinates.bottom - offset_y; } } @@ -157,7 +157,7 @@ int display_base_t::init() { D3D_FEATURE_LEVEL_9_1 }; - status = adapter->QueryInterface(IID_IDXGIAdapter, (void**)&adapter_p); + status = adapter->QueryInterface(IID_IDXGIAdapter, (void **)&adapter_p); if(FAILED(status)) { BOOST_LOG(error) << "Failed to query IDXGIAdapter interface"sv; return -1; @@ -195,7 +195,7 @@ int display_base_t::init() { << "Device Sys Mem : "sv << adapter_desc.DedicatedSystemMemory / 1048576 << " MiB"sv << std::endl << "Share Sys Mem : "sv << adapter_desc.SharedSystemMemory / 1048576 << " MiB"sv << std::endl << "Feature Level : 0x"sv << util::hex(feature_level).to_string_view() << std::endl - << "Capture size : "sv << width << 'x' << height; + << "Capture size : "sv << width << 'x' << height; // Bump up thread priority { @@ -204,13 +204,13 @@ int display_base_t::init() { HANDLE token; LUID val; - if (OpenProcessToken(GetCurrentProcess(), flags, &token) && + if(OpenProcessToken(GetCurrentProcess(), flags, &token) && !!LookupPrivilegeValue(NULL, SE_INC_BASE_PRIORITY_NAME, &val)) { - tp.PrivilegeCount = 1; - tp.Privileges[0].Luid = val; + tp.PrivilegeCount = 1; + tp.Privileges[0].Luid = val; tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; - if (!AdjustTokenPrivileges(token, false, &tp, sizeof(tp), NULL, NULL)) { + if(!AdjustTokenPrivileges(token, false, &tp, sizeof(tp), NULL, NULL)) { BOOST_LOG(warning) << "Could not set privilege to increase GPU priority"; } } @@ -218,19 +218,19 @@ int display_base_t::init() { CloseHandle(token); HMODULE gdi32 = GetModuleHandleA("GDI32"); - if (gdi32) { + if(gdi32) { PD3DKMTSetProcessSchedulingPriorityClass fn = (PD3DKMTSetProcessSchedulingPriorityClass)GetProcAddress(gdi32, "D3DKMTSetProcessSchedulingPriorityClass"); - if (fn) { + if(fn) { status = fn(GetCurrentProcess(), D3DKMT_SCHEDULINGPRIORITYCLASS_REALTIME); - if (FAILED(status)) { + if(FAILED(status)) { BOOST_LOG(warning) << "Failed to set realtime GPU priority. Please run application as administrator for optimal performance."; } } } dxgi::dxgi_t dxgi; - status = device->QueryInterface(IID_IDXGIDevice, (void**)&dxgi); + status = device->QueryInterface(IID_IDXGIDevice, (void **)&dxgi); if(FAILED(status)) { BOOST_LOG(warning) << "Failed to query DXGI interface from device [0x"sv << util::hex(status).to_string_view() << ']'; return -1; @@ -242,7 +242,7 @@ int display_base_t::init() { // Try to reduce latency { dxgi::dxgi1_t dxgi {}; - status = device->QueryInterface(IID_IDXGIDevice, (void**)&dxgi); + status = device->QueryInterface(IID_IDXGIDevice, (void **)&dxgi); if(FAILED(status)) { BOOST_LOG(error) << "Failed to query DXGI interface from device [0x"sv << util::hex(status).to_string_view() << ']'; return -1; @@ -258,7 +258,7 @@ int display_base_t::init() { //TODO: Use IDXGIOutput5 for improved performance { dxgi::output1_t output1 {}; - status = output->QueryInterface(IID_IDXGIOutput1, (void**)&output1); + status = output->QueryInterface(IID_IDXGIOutput1, (void **)&output1); if(FAILED(status)) { BOOST_LOG(error) << "Failed to query IDXGIOutput1 from the output"sv; return -1; @@ -266,7 +266,7 @@ int display_base_t::init() { // We try this twice, in case we still get an error on reinitialization for(int x = 0; x < 2; ++x) { - status = output1->DuplicateOutput((IUnknown*)device.get(), &dup.dup); + status = output1->DuplicateOutput((IUnknown *)device.get(), &dup.dup); if(SUCCEEDED(status)) { break; } @@ -414,7 +414,7 @@ const char *format_str[] = { "DXGI_FORMAT_V408" }; -} +} // namespace platf::dxgi namespace platf { std::shared_ptr display(dev_type_e hwdevice_type) { @@ -435,4 +435,4 @@ std::shared_ptr display(dev_type_e hwdevice_type) { return nullptr; } -} +} // namespace platf diff --git a/sunshine/platform/windows/display_ram.cpp b/sunshine/platform/windows/display_ram.cpp index ffcbbd25..cb3930d4 100644 --- a/sunshine/platform/windows/display_ram.cpp +++ b/sunshine/platform/windows/display_ram.cpp @@ -1,12 +1,12 @@ -#include "sunshine/main.h" #include "display.h" +#include "sunshine/main.h" namespace platf { using namespace std::literals; } namespace platf::dxgi { -struct img_t : public ::platf::img_t { +struct img_t : public ::platf::img_t { ~img_t() override { delete[] data; data = nullptr; @@ -22,29 +22,29 @@ void blend_cursor_monochrome(const cursor_t &cursor, img_t &img) { auto cursor_skip_y = -std::min(0, cursor.y); auto cursor_skip_x = -std::min(0, cursor.x); - // img cursor.{x,y} > img.{x,y}, truncate parts of the cursor.img_data + // img cursor.{x,y} > img.{x,y}, truncate parts of the cursor.img_data auto cursor_truncate_y = std::max(0, cursor.y - img.height); auto cursor_truncate_x = std::max(0, cursor.x - img.width); - auto cursor_width = width - cursor_skip_x - cursor_truncate_x; + auto cursor_width = width - cursor_skip_x - cursor_truncate_x; auto cursor_height = height - cursor_skip_y - cursor_truncate_y; if(cursor_height > height || cursor_width > width) { return; } - auto img_skip_y = std::max(0, cursor.y); - auto img_skip_x = std::max(0, cursor.x); + auto img_skip_y = std::max(0, cursor.y); + auto img_skip_x = std::max(0, cursor.x); auto cursor_img_data = cursor.img_data.data() + cursor_skip_y * pitch; int delta_height = std::min(cursor_height - cursor_truncate_y, std::max(0, img.height - img_skip_y)); - int delta_width = std::min(cursor_width - cursor_truncate_x, std::max(0, img.width - img_skip_x)); + int delta_width = std::min(cursor_width - cursor_truncate_x, std::max(0, img.width - img_skip_x)); auto pixels_per_byte = width / pitch; - auto bytes_per_row = delta_width / pixels_per_byte; + auto bytes_per_row = delta_width / pixels_per_byte; - auto img_data = (int*)img.data; + auto img_data = (int *)img.data; for(int i = 0; i < delta_height; ++i) { auto and_mask = &cursor_img_data[i * pitch]; auto xor_mask = &cursor_img_data[(i + height) * pitch]; @@ -76,8 +76,8 @@ void blend_cursor_monochrome(const cursor_t &cursor, img_t &img) { } void apply_color_alpha(int *img_pixel_p, int cursor_pixel) { - auto colors_out = (std::uint8_t*)&cursor_pixel; - auto colors_in = (std::uint8_t*)img_pixel_p; + auto colors_out = (std::uint8_t *)&cursor_pixel; + auto colors_in = (std::uint8_t *)img_pixel_p; //TODO: When use of IDXGIOutput5 is implemented, support different color formats auto alpha = colors_out[3]; @@ -85,15 +85,15 @@ void apply_color_alpha(int *img_pixel_p, int cursor_pixel) { *img_pixel_p = cursor_pixel; } else { - colors_in[0] = colors_out[0] + (colors_in[0] * (255 - alpha) + 255/2) / 255; - colors_in[1] = colors_out[1] + (colors_in[1] * (255 - alpha) + 255/2) / 255; - colors_in[2] = colors_out[2] + (colors_in[2] * (255 - alpha) + 255/2) / 255; + colors_in[0] = colors_out[0] + (colors_in[0] * (255 - alpha) + 255 / 2) / 255; + colors_in[1] = colors_out[1] + (colors_in[1] * (255 - alpha) + 255 / 2) / 255; + colors_in[2] = colors_out[2] + (colors_in[2] * (255 - alpha) + 255 / 2) / 255; } } void apply_color_masked(int *img_pixel_p, int cursor_pixel) { //TODO: When use of IDXGIOutput5 is implemented, support different color formats - auto alpha = ((std::uint8_t*)&cursor_pixel)[3]; + auto alpha = ((std::uint8_t *)&cursor_pixel)[3]; if(alpha == 0xFF) { *img_pixel_p ^= cursor_pixel; } @@ -111,30 +111,30 @@ void blend_cursor_color(const cursor_t &cursor, img_t &img, const bool masked) { auto cursor_skip_y = -std::min(0, cursor.y); auto cursor_skip_x = -std::min(0, cursor.x); - // img cursor.{x,y} > img.{x,y}, truncate parts of the cursor.img_data + // img cursor.{x,y} > img.{x,y}, truncate parts of the cursor.img_data auto cursor_truncate_y = std::max(0, cursor.y - img.height); auto cursor_truncate_x = std::max(0, cursor.x - img.width); - auto img_skip_y = std::max(0, cursor.y); - auto img_skip_x = std::max(0, cursor.x); + auto img_skip_y = std::max(0, cursor.y); + auto img_skip_x = std::max(0, cursor.x); - auto cursor_width = width - cursor_skip_x - cursor_truncate_x; + auto cursor_width = width - cursor_skip_x - cursor_truncate_x; auto cursor_height = height - cursor_skip_y - cursor_truncate_y; if(cursor_height > height || cursor_width > width) { return; } - auto cursor_img_data = (int*)&cursor.img_data[cursor_skip_y * pitch]; + auto cursor_img_data = (int *)&cursor.img_data[cursor_skip_y * pitch]; int delta_height = std::min(cursor_height - cursor_truncate_y, std::max(0, img.height - img_skip_y)); - int delta_width = std::min(cursor_width - cursor_truncate_x, std::max(0, img.width - img_skip_x)); + int delta_width = std::min(cursor_width - cursor_truncate_x, std::max(0, img.width - img_skip_x)); - auto img_data = (int*)img.data; + auto img_data = (int *)img.data; for(int i = 0; i < delta_height; ++i) { auto cursor_begin = &cursor_img_data[i * cursor.shape_info.Width + cursor_skip_x]; - auto cursor_end = &cursor_begin[delta_width]; + auto cursor_end = &cursor_begin[delta_width]; auto img_pixel_p = &img_data[(i + img_skip_y) * (img.row_pitch / img.pixel_pitch) + img_skip_x]; std::for_each(cursor_begin, cursor_end, [&](int cursor_pixel) { @@ -151,22 +151,22 @@ void blend_cursor_color(const cursor_t &cursor, img_t &img, const bool masked) { void blend_cursor(const cursor_t &cursor, img_t &img) { switch(cursor.shape_info.Type) { - case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_COLOR: - blend_cursor_color(cursor, img, false); - break; - case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MONOCHROME: - blend_cursor_monochrome(cursor, img); - break; - case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR: - blend_cursor_color(cursor, img, true); - break; - default: - BOOST_LOG(warning) << "Unsupported cursor format ["sv << cursor.shape_info.Type << ']'; + case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_COLOR: + blend_cursor_color(cursor, img, false); + break; + case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MONOCHROME: + blend_cursor_monochrome(cursor, img); + break; + case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR: + blend_cursor_color(cursor, img, true); + break; + default: + BOOST_LOG(warning) << "Unsupported cursor format ["sv << cursor.shape_info.Type << ']'; } } capture_e display_ram_t::snapshot(::platf::img_t *img_base, std::chrono::milliseconds timeout, bool cursor_visible) { - auto img = (img_t*)img_base; + auto img = (img_t *)img_base; HRESULT status; @@ -174,9 +174,9 @@ capture_e display_ram_t::snapshot(::platf::img_t *img_base, std::chrono::millise resource_t::pointer res_p {}; auto capture_status = dup.next_frame(frame_info, timeout, &res_p); - resource_t res{res_p}; + resource_t res { res_p }; - if (capture_status != capture_e::ok) { + if(capture_status != capture_e::ok) { return capture_status; } @@ -187,7 +187,7 @@ capture_e display_ram_t::snapshot(::platf::img_t *img_base, std::chrono::millise UINT dummy; status = dup.dup->GetFramePointerShape(img_data.size(), img_data.data(), &dummy, &cursor.shape_info); - if (FAILED(status)) { + if(FAILED(status)) { BOOST_LOG(error) << "Failed to get new pointer shape [0x"sv << util::hex(status).to_string_view() << ']'; return capture_e::error; @@ -195,18 +195,18 @@ capture_e display_ram_t::snapshot(::platf::img_t *img_base, std::chrono::millise } if(frame_info.LastMouseUpdateTime.QuadPart) { - cursor.x = frame_info.PointerPosition.Position.x; - cursor.y = frame_info.PointerPosition.Position.y; + cursor.x = frame_info.PointerPosition.Position.x; + cursor.y = frame_info.PointerPosition.Position.y; cursor.visible = frame_info.PointerPosition.Visible; } // If frame has been updated - if (frame_info.LastPresentTime.QuadPart != 0) { + if(frame_info.LastPresentTime.QuadPart != 0) { { texture2d_t src {}; status = res->QueryInterface(IID_ID3D11Texture2D, (void **)&src); - if (FAILED(status)) { + if(FAILED(status)) { BOOST_LOG(error) << "Couldn't query interface [0x"sv << util::hex(status).to_string_view() << ']'; return capture_e::error; } @@ -221,14 +221,14 @@ capture_e display_ram_t::snapshot(::platf::img_t *img_base, std::chrono::millise } status = device_ctx->Map(texture.get(), 0, D3D11_MAP_READ, 0, &img_info); - if (FAILED(status)) { + if(FAILED(status)) { BOOST_LOG(error) << "Failed to map texture [0x"sv << util::hex(status).to_string_view() << ']'; return capture_e::error; } } - const bool mouse_update = + const bool mouse_update = (frame_info.LastMouseUpdateTime.QuadPart || frame_info.PointerShapeBufferSize > 0) && (cursor_visible && cursor.visible); @@ -238,7 +238,7 @@ capture_e display_ram_t::snapshot(::platf::img_t *img_base, std::chrono::millise return capture_e::timeout; } - std::copy_n((std::uint8_t*)img_info.pData, height * img_info.RowPitch, (std::uint8_t*)img->data); + std::copy_n((std::uint8_t *)img_info.pData, height * img_info.RowPitch, (std::uint8_t *)img->data); if(cursor_visible && cursor.visible) { blend_cursor(cursor, *img); @@ -250,11 +250,11 @@ capture_e display_ram_t::snapshot(::platf::img_t *img_base, std::chrono::millise std::shared_ptr display_ram_t::alloc_img() { auto img = std::make_shared(); - img->pixel_pitch = 4; - img->row_pitch = img_info.RowPitch; - img->width = width; - img->height = height; - img->data = new std::uint8_t[img->row_pitch * height]; + img->pixel_pitch = 4; + img->row_pitch = img_info.RowPitch; + img->width = width; + img->height = height; + img->data = new std::uint8_t[img->row_pitch * height]; return img; } @@ -269,14 +269,14 @@ int display_ram_t::init() { } D3D11_TEXTURE2D_DESC t {}; - t.Width = width; - t.Height = height; - t.MipLevels = 1; - t.ArraySize = 1; + t.Width = width; + t.Height = height; + t.MipLevels = 1; + t.ArraySize = 1; t.SampleDesc.Count = 1; - t.Usage = D3D11_USAGE_STAGING; - t.Format = format; - t.CPUAccessFlags = D3D11_CPU_ACCESS_READ; + t.Usage = D3D11_USAGE_STAGING; + t.Format = format; + t.CPUAccessFlags = D3D11_CPU_ACCESS_READ; auto status = device->CreateTexture2D(&t, nullptr, &texture); @@ -294,4 +294,4 @@ int display_ram_t::init() { return 0; } -} +} // namespace platf::dxgi diff --git a/sunshine/platform/windows/display_vram.cpp b/sunshine/platform/windows/display_vram.cpp index 00e0c69d..9c44b6fb 100644 --- a/sunshine/platform/windows/display_vram.cpp +++ b/sunshine/platform/windows/display_vram.cpp @@ -3,8 +3,8 @@ #include #include -#include "sunshine/main.h" #include "display.h" +#include "sunshine/main.h" #define SUNSHINE_SHADERS_DIR SUNSHINE_ASSETS_DIR "/shaders" namespace platf { @@ -30,7 +30,7 @@ using depth_stencil_view_t = util::safe_ptr -buf_t make_buffer(device_t::pointer device, const T& t) { +buf_t make_buffer(device_t::pointer device, const T &t) { static_assert(sizeof(T) % 16 == 0, "Buffer needs to be aligned on a 16-byte alignment"); D3D11_BUFFER_DESC buffer_desc { @@ -91,18 +91,18 @@ buf_t make_buffer(device_t::pointer device, const T& t) { blend_t make_blend(device_t::pointer device, bool enable) { D3D11_BLEND_DESC bdesc {}; - auto &rt = bdesc.RenderTarget[0]; - rt.BlendEnable = enable; + auto &rt = bdesc.RenderTarget[0]; + rt.BlendEnable = enable; rt.RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL; if(enable) { - rt.BlendOp = D3D11_BLEND_OP_ADD; + rt.BlendOp = D3D11_BLEND_OP_ADD; rt.BlendOpAlpha = D3D11_BLEND_OP_ADD; - rt.SrcBlend = D3D11_BLEND_SRC_ALPHA; + rt.SrcBlend = D3D11_BLEND_SRC_ALPHA; rt.DestBlend = D3D11_BLEND_INV_SRC_ALPHA; - rt.SrcBlendAlpha = D3D11_BLEND_ZERO; + rt.SrcBlendAlpha = D3D11_BLEND_ZERO; rt.DestBlendAlpha = D3D11_BLEND_ZERO; } @@ -130,79 +130,79 @@ struct img_d3d_t : public platf::img_t { ~img_d3d_t() override = default; }; -util::buffer_t make_cursor_image(util::buffer_t &&img_data, DXGI_OUTDUPL_POINTER_SHAPE_INFO shape_info) { - constexpr std::uint32_t black = 0xFF000000; - constexpr std::uint32_t white = 0xFFFFFFFF; +util::buffer_t make_cursor_image(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: - std::for_each((std::uint32_t*)std::begin(img_data), (std::uint32_t*)std::end(img_data), [](auto &pixel) { - if(pixel & 0xFF000000) { - pixel = transparent; - } - }); - case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_COLOR: - return std::move(img_data); - default: - break; + case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR: + std::for_each((std::uint32_t *)std::begin(img_data), (std::uint32_t *)std::end(img_data), [](auto &pixel) { + if(pixel & 0xFF000000) { + pixel = transparent; + } + }); + case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_COLOR: + return std::move(img_data); + default: + break; } shape_info.Height /= 2; util::buffer_t cursor_img { shape_info.Width * shape_info.Height * 4 }; - auto bytes = shape_info.Pitch * shape_info.Height; - auto pixel_begin = (std::uint32_t*)std::begin(cursor_img); - auto pixel_data = pixel_begin; - auto and_mask = std::begin(img_data); - auto xor_mask = std::begin(img_data) + bytes; + auto bytes = shape_info.Pitch * shape_info.Height; + auto pixel_begin = (std::uint32_t *)std::begin(cursor_img); + auto pixel_data = pixel_begin; + auto and_mask = std::begin(img_data); + auto xor_mask = std::begin(img_data) + bytes; - for(auto x = 0; x < bytes; ++x) { + for(auto x = 0; x < bytes; ++x) { for(auto c = 7; c >= 0; --c) { - auto bit = 1 << c; + auto bit = 1 << c; auto color_type = ((*and_mask & bit) ? 1 : 0) + ((*xor_mask & bit) ? 2 : 0); switch(color_type) { - case 0: //black - *pixel_data = black; - break; - case 2: //white - *pixel_data = white; - break; - case 1: //transparent - { - *pixel_data = transparent; + case 0: //black + *pixel_data = black; + break; + case 2: //white + *pixel_data = white; + break; + case 1: //transparent + { + *pixel_data = transparent; - break; + break; + } + case 3: //inverse + { + auto top_p = pixel_data - shape_info.Width; + auto left_p = pixel_data - 1; + auto right_p = pixel_data + 1; + auto bottom_p = pixel_data + shape_info.Width; + + // Get the x coordinate of the pixel + auto column = (pixel_data - pixel_begin) % shape_info.Width != 0; + + if(top_p >= pixel_begin && *top_p == transparent) { + *top_p = black; } - case 3: //inverse - { - auto top_p = pixel_data - shape_info.Width; - auto left_p = pixel_data - 1; - auto right_p = pixel_data + 1; - auto bottom_p = pixel_data + shape_info.Width; - // Get the x coordinate of the pixel - auto column = (pixel_data - pixel_begin) % shape_info.Width != 0; - - if(top_p >= pixel_begin && *top_p == transparent) { - *top_p = black; - } - - if(column != 0 && left_p >= pixel_begin && *left_p == transparent) { - *left_p = black; - } - - if(bottom_p < (std::uint32_t*)std::end(cursor_img)) { - *bottom_p = black; - } - - if(column != shape_info.Width -1) { - *right_p = black; - } - *pixel_data = white; + if(column != 0 && left_p >= pixel_begin && *left_p == transparent) { + *left_p = black; } + + if(bottom_p < (std::uint32_t *)std::end(cursor_img)) { + *bottom_p = black; + } + + if(column != shape_info.Width - 1) { + *right_p = black; + } + *pixel_data = white; + } } ++pixel_data; @@ -225,7 +225,7 @@ blob_t compile_shader(LPCSTR file, LPCSTR entrypoint, LPCSTR shader_model) { #endif std::wstring_convert, wchar_t> converter; - auto wFile = converter.from_bytes(file); + auto wFile = converter.from_bytes(file); auto status = D3DCompileFromFile(wFile.c_str(), nullptr, nullptr, entrypoint, shader_model, flags, 0, &compiled_p, &msg_p); if(msg_p) { @@ -251,7 +251,7 @@ blob_t compile_vertex_shader(LPCSTR file) { class hwdevice_t : public platf::hwdevice_t { public: - hwdevice_t(std::vector *hwdevices_p) : hwdevices_p { hwdevices_p } {} + hwdevice_t(std::vector *hwdevices_p) : hwdevices_p { hwdevices_p } {} hwdevice_t() = delete; void set_cursor_pos(LONG rel_x, LONG rel_y, bool visible) { @@ -271,7 +271,7 @@ public: int set_cursor_texture(texture2d_t::pointer texture, LONG width, LONG height) { auto device = (device_t::pointer)data; - cursor_view.Width = width; + cursor_view.Width = width; cursor_view.Height = height; D3D11_SHADER_RESOURCE_VIEW_DESC desc { @@ -290,7 +290,7 @@ public: } int convert(platf::img_t &img_base) override { - auto &img = (img_d3d_t&)img_base; + auto &img = (img_d3d_t &)img_base; if(!img.input_res) { auto device = (device_t::pointer)data; @@ -347,17 +347,17 @@ public: } void set_colorspace(std::uint32_t colorspace, std::uint32_t color_range) override { - switch (colorspace) { - case 5: // SWS_CS_SMPTE170M - color_p = &colors[0]; - break; - case 1: // SWS_CS_ITU709 - color_p = &colors[2]; - break; - case 9: // SWS_CS_BT2020 - default: - BOOST_LOG(warning) << "Colorspace: ["sv << colorspace << "] not yet supported: switching to default"sv; - color_p = &colors[0]; + switch(colorspace) { + case 5: // SWS_CS_SMPTE170M + color_p = &colors[0]; + break; + case 1: // SWS_CS_ITU709 + color_p = &colors[2]; + break; + case 9: // SWS_CS_BT2020 + default: + BOOST_LOG(warning) << "Colorspace: ["sv << colorspace << "] not yet supported: switching to default"sv; + color_p = &colors[0]; }; if(color_range > 1) { @@ -378,8 +378,7 @@ public: int init( std::shared_ptr display, device_t::pointer device_p, device_ctx_t::pointer device_ctx_p, int in_width, int in_height, int out_width, int out_height, - pix_fmt_e pix_fmt - ) { + pix_fmt_e pix_fmt) { HRESULT status; device_p->AddRef(); @@ -387,7 +386,7 @@ public: this->device_ctx_p = device_ctx_p; - cursor_visible = false; + cursor_visible = false; cursor_view.MinDepth = 0.0f; cursor_view.MaxDepth = 1.0f; @@ -427,7 +426,7 @@ public: } blend_disable = make_blend(device_p, false); - blend_enable = make_blend(device_p, true); + blend_enable = make_blend(device_p, true); if(!blend_disable || !blend_enable) { return -1; @@ -460,14 +459,14 @@ public: &input_layout); D3D11_TEXTURE2D_DESC t {}; - t.Width = out_width; - t.Height = out_height; - t.MipLevels = 1; - t.ArraySize = 1; + t.Width = out_width; + t.Height = out_height; + t.MipLevels = 1; + t.ArraySize = 1; t.SampleDesc.Count = 1; - t.Usage = D3D11_USAGE_DEFAULT; - t.Format = pix_fmt == pix_fmt_e::nv12 ? DXGI_FORMAT_NV12 : DXGI_FORMAT_P010; - t.BindFlags = D3D11_BIND_RENDER_TARGET; + t.Usage = D3D11_USAGE_DEFAULT; + t.Format = pix_fmt == pix_fmt_e::nv12 ? DXGI_FORMAT_NV12 : DXGI_FORMAT_P010; + t.BindFlags = D3D11_BIND_RENDER_TARGET; status = device_p->CreateTexture2D(&t, nullptr, &img.texture); if(FAILED(status)) { @@ -475,11 +474,11 @@ public: return -1; } - img.display = std::move(display); - img.width = out_width; - img.height = out_height; - img.data = (std::uint8_t*)img.texture.get(); - img.row_pitch = out_width; + img.display = std::move(display); + img.width = out_width; + img.height = out_height; + img.data = (std::uint8_t *)img.texture.get(); + img.row_pitch = out_width; img.pixel_pitch = 1; D3D11_RENDER_TARGET_VIEW_DESC nv12_rt_desc { @@ -494,20 +493,20 @@ public: } nv12_rt_desc.Format = DXGI_FORMAT_R8G8_UNORM; - status = device_p->CreateRenderTargetView(img.texture.get(), &nv12_rt_desc, &nv12_UV_rt); + status = device_p->CreateRenderTargetView(img.texture.get(), &nv12_rt_desc, &nv12_UV_rt); if(FAILED(status)) { BOOST_LOG(error) << "Failed to create render target view [0x"sv << util::hex(status).to_string_view() << ']'; return -1; } D3D11_SAMPLER_DESC sampler_desc {}; - sampler_desc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR; - sampler_desc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP; - sampler_desc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP; - sampler_desc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP; + sampler_desc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR; + sampler_desc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP; + sampler_desc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP; + sampler_desc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP; sampler_desc.ComparisonFunc = D3D11_COMPARISON_NEVER; - sampler_desc.MinLOD = 0; - sampler_desc.MaxLOD = D3D11_FLOAT32_MAX; + sampler_desc.MinLOD = 0; + sampler_desc.MaxLOD = D3D11_FLOAT32_MAX; status = device_p->CreateSamplerState(&sampler_desc, &sampler_linear); if(FAILED(status)) { @@ -527,7 +526,7 @@ public: ~hwdevice_t() override { if(data) { - ((ID3D11Device*)data)->Release(); + ((ID3D11Device *)data)->Release(); } auto it = std::find(std::begin(*hwdevices_p), std::end(*hwdevices_p), this); @@ -535,6 +534,7 @@ public: hwdevices_p->erase(it); } } + private: void _init_view_port(float x, float y, float width, float height) { D3D11_VIEWPORT view { @@ -633,11 +633,11 @@ public: device_ctx_t::pointer device_ctx_p; // The destructor will remove itself from the list of hardware devices, this is done synchronously - std::vector *hwdevices_p; + std::vector *hwdevices_p; }; capture_e display_vram_t::snapshot(platf::img_t *img_base, std::chrono::milliseconds timeout, bool cursor_visible) { - auto img = (img_d3d_t*)img_base; + auto img = (img_d3d_t *)img_base; HRESULT status; @@ -645,7 +645,7 @@ capture_e display_vram_t::snapshot(platf::img_t *img_base, std::chrono::millisec resource_t::pointer res_p {}; auto capture_status = dup.next_frame(frame_info, timeout, &res_p); - resource_t res{ res_p }; + resource_t res { res_p }; if(capture_status != capture_e::ok) { return capture_status; @@ -653,7 +653,7 @@ capture_e display_vram_t::snapshot(platf::img_t *img_base, std::chrono::millisec const bool mouse_update_flag = frame_info.LastMouseUpdateTime.QuadPart != 0 || frame_info.PointerShapeBufferSize > 0; const bool frame_update_flag = frame_info.AccumulatedFrames != 0 || frame_info.LastPresentTime.QuadPart != 0; - const bool update_flag = mouse_update_flag || frame_update_flag; + const bool update_flag = mouse_update_flag || frame_update_flag; if(!update_flag) { return capture_e::timeout; @@ -666,7 +666,7 @@ capture_e display_vram_t::snapshot(platf::img_t *img_base, std::chrono::millisec UINT dummy; status = dup.dup->GetFramePointerShape(img_data.size(), std::begin(img_data), &dummy, &shape_info); - if (FAILED(status)) { + if(FAILED(status)) { BOOST_LOG(error) << "Failed to get new pointer shape [0x"sv << util::hex(status).to_string_view() << ']'; return capture_e::error; @@ -682,14 +682,14 @@ capture_e display_vram_t::snapshot(platf::img_t *img_base, std::chrono::millisec // Create texture for cursor D3D11_TEXTURE2D_DESC t {}; - t.Width = shape_info.Width; - t.Height = cursor_img.size() / data.SysMemPitch; - t.MipLevels = 1; - t.ArraySize = 1; + t.Width = shape_info.Width; + t.Height = cursor_img.size() / data.SysMemPitch; + t.MipLevels = 1; + t.ArraySize = 1; t.SampleDesc.Count = 1; - t.Usage = D3D11_USAGE_DEFAULT; - t.Format = DXGI_FORMAT_B8G8R8A8_UNORM; - t.BindFlags = D3D11_BIND_SHADER_RESOURCE; + t.Usage = D3D11_USAGE_DEFAULT; + t.Format = DXGI_FORMAT_B8G8R8A8_UNORM; + t.BindFlags = D3D11_BIND_SHADER_RESOURCE; texture2d_t texture; auto status = device->CreateTexture2D(&t, &data, &texture); @@ -734,14 +734,14 @@ std::shared_ptr display_vram_t::alloc_img() { auto img = std::make_shared(); D3D11_TEXTURE2D_DESC t {}; - t.Width = width; - t.Height = height; - t.MipLevels = 1; - t.ArraySize = 1; + t.Width = width; + t.Height = height; + t.MipLevels = 1; + t.ArraySize = 1; t.SampleDesc.Count = 1; - t.Usage = D3D11_USAGE_DEFAULT; - t.Format = format; - t.BindFlags = D3D11_BIND_SHADER_RESOURCE; + t.Usage = D3D11_USAGE_DEFAULT; + t.Format = format; + t.BindFlags = D3D11_BIND_SHADER_RESOURCE; auto status = device->CreateTexture2D(&t, nullptr, &img->texture); if(FAILED(status)) { @@ -749,7 +749,7 @@ std::shared_ptr display_vram_t::alloc_img() { return nullptr; } - img->data = (std::uint8_t*)img->texture.get(); + img->data = (std::uint8_t *)img->texture.get(); img->row_pitch = 0; img->pixel_pitch = 4; img->width = 0; @@ -760,9 +760,9 @@ std::shared_ptr display_vram_t::alloc_img() { } int display_vram_t::dummy_img(platf::img_t *img_base) { - auto img = (img_d3d_t*)img_base; + auto img = (img_d3d_t *)img_base; - img->row_pitch = width * 4; + img->row_pitch = width * 4; auto dummy_data = std::make_unique(width * height); D3D11_SUBRESOURCE_DATA data { dummy_data.get(), @@ -770,14 +770,14 @@ int display_vram_t::dummy_img(platf::img_t *img_base) { }; D3D11_TEXTURE2D_DESC t {}; - t.Width = width; - t.Height = height; - t.MipLevels = 1; - t.ArraySize = 1; + t.Width = width; + t.Height = height; + t.MipLevels = 1; + t.ArraySize = 1; t.SampleDesc.Count = 1; - t.Usage = D3D11_USAGE_DEFAULT; - t.Format = format; - t.BindFlags = D3D11_BIND_SHADER_RESOURCE; + t.Usage = D3D11_USAGE_DEFAULT; + t.Format = format; + t.BindFlags = D3D11_BIND_SHADER_RESOURCE; dxgi::texture2d_t tex; auto status = device->CreateTexture2D(&t, &data, &tex); @@ -787,7 +787,7 @@ int display_vram_t::dummy_img(platf::img_t *img_base) { } img->texture = std::move(tex); - img->data = (std::uint8_t*)img->texture.get(); + img->data = (std::uint8_t *)img->texture.get(); img->height = height; img->width = width; img->pixel_pitch = 4; @@ -864,4 +864,4 @@ int init() { return 0; } -} \ No newline at end of file +} // namespace platf::dxgi \ No newline at end of file diff --git a/sunshine/platform/windows/input.cpp b/sunshine/platform/windows/input.cpp index b372afd2..d7667694 100755 --- a/sunshine/platform/windows/input.cpp +++ b/sunshine/platform/windows/input.cpp @@ -1,12 +1,12 @@ -#include -#include #include +#include +#include -#include -#include -#include -#include #include +#include +#include +#include +#include #include @@ -100,11 +100,11 @@ std::string from_sockaddr(const sockaddr *const socket_address) { auto family = socket_address->sa_family; if(family == AF_INET6) { - inet_ntop(AF_INET6, &((sockaddr_in6*)socket_address)->sin6_addr, data, INET6_ADDRSTRLEN); + inet_ntop(AF_INET6, &((sockaddr_in6 *)socket_address)->sin6_addr, data, INET6_ADDRSTRLEN); } if(family == AF_INET) { - inet_ntop(AF_INET, &((sockaddr_in*)socket_address)->sin_addr, data, INET_ADDRSTRLEN); + inet_ntop(AF_INET, &((sockaddr_in *)socket_address)->sin_addr, data, INET_ADDRSTRLEN); } return std::string { data }; @@ -116,13 +116,13 @@ std::pair from_sockaddr_ex(const sockaddr *const ip_ auto family = ip_addr->sa_family; std::uint16_t port; if(family == AF_INET6) { - inet_ntop(AF_INET6, &((sockaddr_in6*)ip_addr)->sin6_addr, data, INET6_ADDRSTRLEN); - port = ((sockaddr_in6*)ip_addr)->sin6_port; + inet_ntop(AF_INET6, &((sockaddr_in6 *)ip_addr)->sin6_addr, data, INET6_ADDRSTRLEN); + port = ((sockaddr_in6 *)ip_addr)->sin6_port; } if(family == AF_INET) { - inet_ntop(AF_INET, &((sockaddr_in*)ip_addr)->sin_addr, data, INET_ADDRSTRLEN); - port = ((sockaddr_in*)ip_addr)->sin_port; + inet_ntop(AF_INET, &((sockaddr_in *)ip_addr)->sin_addr, data, INET_ADDRSTRLEN); + port = ((sockaddr_in *)ip_addr)->sin_port; } return { port, std::string { data } }; @@ -163,7 +163,7 @@ std::string get_mac_address(const std::string_view &address) { input_t input() { input_t result { new vigem_t {} }; - auto vigem = (vigem_t*)result.get(); + auto vigem = (vigem_t *)result.get(); if(vigem->init()) { return nullptr; } @@ -176,7 +176,7 @@ retry: auto send = SendInput(1, &i, sizeof(INPUT)); if(send != 1) { auto hDesk = pairInputDesktop(); - if (_lastKnownInputDesktop != hDesk) { + if(_lastKnownInputDesktop != hDesk) { _lastKnownInputDesktop = hDesk; goto retry; } @@ -186,7 +186,7 @@ retry: void abs_mouse(input_t &input, const touch_port_t &touch_port, float x, float y) { INPUT i {}; - i.type = INPUT_MOUSE; + i.type = INPUT_MOUSE; auto &mi = i.mi; mi.dwFlags = @@ -208,13 +208,13 @@ void abs_mouse(input_t &input, const touch_port_t &touch_port, float x, float y) void move_mouse(input_t &input, int deltaX, int deltaY) { INPUT i {}; - i.type = INPUT_MOUSE; + i.type = INPUT_MOUSE; auto &mi = i.mi; mi.dwFlags = MOUSEEVENTF_MOVE; - mi.dx = deltaX; - mi.dy = deltaY; - + mi.dx = deltaX; + mi.dy = deltaY; + send_input(i); } @@ -223,34 +223,34 @@ void button_mouse(input_t &input, int button, bool release) { INPUT i {}; - i.type = INPUT_MOUSE; + i.type = INPUT_MOUSE; auto &mi = i.mi; int mouse_button; if(button == 1) { - mi.dwFlags = release ? MOUSEEVENTF_LEFTUP : MOUSEEVENTF_LEFTDOWN; + mi.dwFlags = release ? MOUSEEVENTF_LEFTUP : MOUSEEVENTF_LEFTDOWN; mouse_button = VK_LBUTTON; } else if(button == 2) { - mi.dwFlags = release ? MOUSEEVENTF_MIDDLEUP : MOUSEEVENTF_MIDDLEDOWN; + mi.dwFlags = release ? MOUSEEVENTF_MIDDLEUP : MOUSEEVENTF_MIDDLEDOWN; mouse_button = VK_MBUTTON; } else if(button == 3) { - mi.dwFlags = release ? MOUSEEVENTF_RIGHTUP : MOUSEEVENTF_RIGHTDOWN; + mi.dwFlags = release ? MOUSEEVENTF_RIGHTUP : MOUSEEVENTF_RIGHTDOWN; mouse_button = VK_RBUTTON; } else if(button == 4) { - mi.dwFlags = release ? MOUSEEVENTF_XUP : MOUSEEVENTF_XDOWN; + mi.dwFlags = release ? MOUSEEVENTF_XUP : MOUSEEVENTF_XDOWN; mi.mouseData = XBUTTON1; mouse_button = VK_XBUTTON1; } else { - mi.dwFlags = release ? MOUSEEVENTF_XUP : MOUSEEVENTF_XDOWN; + mi.dwFlags = release ? MOUSEEVENTF_XUP : MOUSEEVENTF_XDOWN; mi.mouseData = XBUTTON2; mouse_button = VK_XBUTTON2; } - auto key_state = GetAsyncKeyState(mouse_button); + auto key_state = GetAsyncKeyState(mouse_button); bool key_state_down = (key_state & KEY_STATE_DOWN) != 0; if(key_state_down != release) { BOOST_LOG(warning) << "Button state of mouse_button ["sv << button << "] does not match the desired state"sv; @@ -264,10 +264,10 @@ void button_mouse(input_t &input, int button, bool release) { void scroll(input_t &input, int distance) { INPUT i {}; - i.type = INPUT_MOUSE; + i.type = INPUT_MOUSE; auto &mi = i.mi; - mi.dwFlags = MOUSEEVENTF_WHEEL; + mi.dwFlags = MOUSEEVENTF_WHEEL; mi.mouseData = distance; send_input(i); @@ -279,12 +279,12 @@ void keyboard(input_t &input, uint16_t modcode, bool release) { } INPUT i {}; - i.type = INPUT_KEYBOARD; + i.type = INPUT_KEYBOARD; auto &ki = i.ki; // For some reason, MapVirtualKey(VK_LWIN, MAPVK_VK_TO_VSC) doesn't seem to work :/ if(modcode != VK_LWIN && modcode != VK_RWIN && modcode != VK_PAUSE) { - ki.wScan = MapVirtualKey(modcode, MAPVK_VK_TO_VSC); + ki.wScan = MapVirtualKey(modcode, MAPVK_VK_TO_VSC); ki.dwFlags = KEYEVENTF_SCANCODE; } else { @@ -293,23 +293,23 @@ void keyboard(input_t &input, uint16_t modcode, bool release) { // https://docs.microsoft.com/en-us/windows/win32/inputdev/about-keyboard-input#keystroke-message-flags switch(modcode) { - case VK_RMENU: - case VK_RCONTROL: - case VK_INSERT: - case VK_DELETE: - case VK_HOME: - case VK_END: - case VK_PRIOR: - case VK_NEXT: - case VK_UP: - case VK_DOWN: - case VK_LEFT: - case VK_RIGHT: - case VK_DIVIDE: - ki.dwFlags |= KEYEVENTF_EXTENDEDKEY; - break; - default: - break; + case VK_RMENU: + case VK_RCONTROL: + case VK_INSERT: + case VK_DELETE: + case VK_HOME: + case VK_END: + case VK_PRIOR: + case VK_NEXT: + case VK_UP: + case VK_DOWN: + case VK_LEFT: + case VK_RIGHT: + case VK_DIVIDE: + ki.dwFlags |= KEYEVENTF_EXTENDEDKEY; + break; + default: + break; } if(release) { @@ -324,7 +324,7 @@ int alloc_gamepad(input_t &input, int nr) { return 0; } - return ((vigem_t*)input.get())->alloc_x360(nr); + return ((vigem_t *)input.get())->alloc_x360(nr); } void free_gamepad(input_t &input, int nr) { @@ -332,7 +332,7 @@ void free_gamepad(input_t &input, int nr) { return; } - ((vigem_t*)input.get())->free_target(nr); + ((vigem_t *)input.get())->free_target(nr); } void gamepad(input_t &input, int nr, const gamepad_state_t &gamepad_state) { // If there is no gamepad support @@ -340,7 +340,7 @@ void gamepad(input_t &input, int nr, const gamepad_state_t &gamepad_state) { return; } - auto vigem = (vigem_t*)input.get(); + auto vigem = (vigem_t *)input.get(); auto &xusb = *(PXUSB_REPORT)&gamepad_state; auto &x360 = vigem->x360s[nr]; @@ -354,19 +354,20 @@ void gamepad(input_t &input, int nr, const gamepad_state_t &gamepad_state) { } } -int thread_priority() { +int thread_priority() { return SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST) ? 0 : 1; } HDESK pairInputDesktop() { auto hDesk = OpenInputDesktop(DF_ALLOWOTHERACCOUNTHOOK, FALSE, GENERIC_ALL); - if (NULL == hDesk) { + if(NULL == hDesk) { auto err = GetLastError(); BOOST_LOG(error) << "Failed to OpenInputDesktop [0x"sv << util::hex(err).to_string_view() << ']'; } else { - BOOST_LOG(info) << std::endl << "Opened desktop [0x"sv << util::hex(hDesk).to_string_view() << ']'; - if (!SetThreadDesktop(hDesk) ) { + BOOST_LOG(info) << std::endl + << "Opened desktop [0x"sv << util::hex(hDesk).to_string_view() << ']'; + if(!SetThreadDesktop(hDesk)) { auto err = GetLastError(); BOOST_LOG(error) << "Failed to SetThreadDesktop [0x"sv << util::hex(err).to_string_view() << ']'; } @@ -377,8 +378,8 @@ HDESK pairInputDesktop() { } void freeInput(void *p) { - auto vigem = (vigem_t*)p; + auto vigem = (vigem_t *)p; delete vigem; } -} +} // namespace platf diff --git a/sunshine/process.cpp b/sunshine/process.cpp index 85767eac..d9c6f71e 100644 --- a/sunshine/process.cpp +++ b/sunshine/process.cpp @@ -4,14 +4,14 @@ #include "process.h" -#include #include +#include -#include #include +#include -#include "utility.h" #include "main.h" +#include "utility.h" namespace proc { using namespace std::literals; @@ -57,11 +57,11 @@ int proc_t::execute(int app_id) { // Ensure starting from a clean slate terminate(); - _app_id = app_id; + _app_id = app_id; auto &proc = _apps[app_id]; _undo_begin = std::begin(proc.prep_cmds); - _undo_it = _undo_begin; + _undo_it = _undo_begin; if(!proc.output.empty() && proc.output != "null"sv) { _pipe.reset(fopen(proc.output.c_str(), "a")); @@ -147,7 +147,7 @@ void proc_t::terminate() { std::abort(); } - for(;_undo_it != _undo_begin; --_undo_it) { + for(; _undo_it != _undo_begin; --_undo_it) { auto &cmd = (_undo_it - 1)->undo_cmd; if(cmd.empty()) { @@ -192,9 +192,11 @@ std::string_view::iterator find_match(std::string_view::iterator begin, std::str do { ++begin; switch(*begin) { - case '(': ++stack; + case '(': + ++stack; break; - case ')': --stack; + case ')': + --stack; } } while(begin != end && stack != 0); @@ -205,7 +207,7 @@ std::string_view::iterator find_match(std::string_view::iterator begin, std::str } std::string parse_env_val(bp::native_environment &env, const std::string_view &val_raw) { - auto pos = std::begin(val_raw); + auto pos = std::begin(val_raw); auto dollar = std::find(pos, std::end(val_raw), '$'); std::stringstream ss; @@ -214,23 +216,23 @@ std::string parse_env_val(bp::native_environment &env, const std::string_view &v auto next = dollar + 1; if(next != std::end(val_raw)) { switch(*next) { - case '(': { - ss.write(pos, (dollar - pos)); - auto var_begin = next + 1; - auto var_end = find_match(next, std::end(val_raw)); + case '(': { + ss.write(pos, (dollar - pos)); + auto var_begin = next + 1; + auto var_end = find_match(next, std::end(val_raw)); - ss << env[std::string { var_begin, var_end }].to_string(); + ss << env[std::string { var_begin, var_end }].to_string(); - pos = var_end + 1; - next = var_end; + pos = var_end + 1; + next = var_end; - break; - } - case '$': - ss.write(pos, (next - pos)); - pos = next + 1; - ++next; - break; + break; + } + case '$': + ss.write(pos, (next - pos)); + pos = next + 1; + ++next; + break; } dollar = std::find(next, std::end(val_raw), '$'); @@ -245,7 +247,7 @@ std::string parse_env_val(bp::native_environment &env, const std::string_view &v return ss.str(); } -std::optional parse(const std::string& file_name) { +std::optional parse(const std::string &file_name) { pt::ptree tree; try { @@ -256,19 +258,19 @@ std::optional parse(const std::string& file_name) { auto this_env = boost::this_process::environment(); - for(auto &[name,val] : env_vars) { + for(auto &[name, val] : env_vars) { this_env[name] = parse_env_val(this_env, val.get_value()); } std::vector apps; - for(auto &[_,app_node] : apps_node) { + for(auto &[_, app_node] : apps_node) { proc::ctx_t ctx; - auto prep_nodes_opt = app_node.get_child_optional("prep-cmd"s); + auto prep_nodes_opt = app_node.get_child_optional("prep-cmd"s); auto detached_nodes_opt = app_node.get_child_optional("detached"s); - auto output = app_node.get_optional("output"s); - auto name = parse_env_val(this_env, app_node.get("name"s)); - auto cmd = app_node.get_optional("cmd"s); + auto output = app_node.get_optional("output"s); + auto name = parse_env_val(this_env, app_node.get("name"s)); + auto cmd = app_node.get_optional("cmd"s); std::vector prep_cmds; if(prep_nodes_opt) { @@ -276,7 +278,7 @@ std::optional parse(const std::string& file_name) { prep_cmds.reserve(prep_nodes.size()); for(auto &[_, prep_node] : prep_nodes) { - auto do_cmd = parse_env_val(this_env, prep_node.get("do"s)); + auto do_cmd = parse_env_val(this_env, prep_node.get("do"s)); auto undo_cmd = prep_node.get_optional("undo"s); if(undo_cmd) { @@ -306,9 +308,9 @@ std::optional parse(const std::string& file_name) { ctx.cmd = parse_env_val(this_env, *cmd); } - ctx.name = std::move(name); + ctx.name = std::move(name); ctx.prep_cmds = std::move(prep_cmds); - ctx.detached = std::move(detached); + ctx.detached = std::move(detached); apps.emplace_back(std::move(ctx)); } @@ -316,7 +318,8 @@ std::optional parse(const std::string& file_name) { return proc::proc_t { std::move(this_env), std::move(apps) }; - } catch (std::exception &e) { + } + catch(std::exception &e) { BOOST_LOG(error) << e.what(); } @@ -330,4 +333,4 @@ void refresh(const std::string &file_name) { proc = std::move(*proc_opt); } } -} +} // namespace proc diff --git a/sunshine/process.h b/sunshine/process.h index e267fa44..39d7cff6 100644 --- a/sunshine/process.h +++ b/sunshine/process.h @@ -9,8 +9,8 @@ #define __kernel_entry #endif -#include #include +#include #include @@ -61,10 +61,9 @@ public: proc_t( boost::process::environment &&env, - std::vector &&apps) : - _app_id(-1), - _env(std::move(env)), - _apps(std::move(apps)) {} + std::vector &&apps) : _app_id(-1), + _env(std::move(env)), + _apps(std::move(apps)) {} int execute(int app_id); @@ -77,7 +76,7 @@ public: const std::vector &get_apps() const; std::vector &get_apps(); - + void terminate(); private: @@ -98,8 +97,8 @@ private: }; void refresh(const std::string &file_name); -std::optional parse(const std::string& file_name); +std::optional parse(const std::string &file_name); extern proc_t proc; -} +} // namespace proc #endif //SUNSHINE_PROCESS_H diff --git a/sunshine/round_robin.h b/sunshine/round_robin.h index 47e125db..e522d16c 100644 --- a/sunshine/round_robin.h +++ b/sunshine/round_robin.h @@ -10,12 +10,12 @@ public: typedef T iterator; typedef typename std::iterator::value_type class_t; - typedef class_t& reference; - typedef class_t* pointer; + typedef class_t &reference; + typedef class_t *pointer; typedef std::ptrdiff_t diff_t; - iterator operator += (diff_t step) { + iterator operator+=(diff_t step) { while(step-- > 0) { ++_this(); } @@ -23,7 +23,7 @@ public: return _this(); } - iterator operator -= (diff_t step) { + iterator operator-=(diff_t step) { while(step-- > 0) { --_this(); } @@ -31,19 +31,19 @@ public: return _this(); } - iterator operator +(diff_t step) { + iterator operator+(diff_t step) { iterator new_ = _this(); return new_ += step; } - iterator operator -(diff_t step) { + iterator operator-(diff_t step) { iterator new_ = _this(); return new_ -= step; } - diff_t operator -(iterator first) { + diff_t operator-(iterator first) { diff_t step = 0; while(first != _this()) { ++step; @@ -53,8 +53,14 @@ public: return step; } - iterator operator++() { _this().inc(); return _this(); } - iterator operator--() { _this().dec(); return _this(); } + iterator operator++() { + _this().inc(); + return _this(); + } + iterator operator--() { + _this().dec(); + return _this(); + } iterator operator++(int) { iterator new_ = _this(); @@ -78,35 +84,35 @@ public: pointer operator->() { return &*_this(); } const pointer operator->() const { return &*_this(); } - bool operator != (const iterator &other) const { + bool operator!=(const iterator &other) const { return !(_this() == other); } - bool operator < (const iterator &other) const { + bool operator<(const iterator &other) const { return !(_this() >= other); } - bool operator >= (const iterator &other) const { + bool operator>=(const iterator &other) const { return _this() == other || _this() > other; } - bool operator <= (const iterator &other) const { + bool operator<=(const iterator &other) const { return _this() == other || _this() < other; } - bool operator == (const iterator &other) const { return _this().eq(other); }; - bool operator > (const iterator &other) const { return _this().gt(other); } -private: + bool operator==(const iterator &other) const { return _this().eq(other); }; + bool operator>(const iterator &other) const { return _this().gt(other); } - iterator &_this() { return *static_cast(this); } - const iterator &_this() const { return *static_cast(this); } +private: + iterator &_this() { return *static_cast(this); } + const iterator &_this() const { return *static_cast(this); } }; template class round_robin_t : public it_wrap_t> { public: using iterator = It; - using pointer = V*; + using pointer = V *; round_robin_t(iterator begin, iterator end) : _begin(begin), _end(end), _pos(begin) {} @@ -119,10 +125,10 @@ public: } void dec() { - if(_pos == _begin) { + if(_pos == _begin) { _pos = _end; } - + --_pos; } @@ -133,6 +139,7 @@ public: pointer get() const { return &*_pos; } + private: It _begin; It _end; @@ -144,6 +151,6 @@ template round_robin_t make_round_robin(It begin, It end) { return round_robin_t(begin, end); } -} +} // namespace util #endif diff --git a/sunshine/rtsp.cpp b/sunshine/rtsp.cpp index 77d1179e..fc6b77ef 100644 --- a/sunshine/rtsp.cpp +++ b/sunshine/rtsp.cpp @@ -7,10 +7,10 @@ extern "C" { } #include "config.h" +#include "input.h" #include "main.h" #include "network.h" #include "rtsp.h" -#include "input.h" #include "stream.h" #include "sync.h" @@ -25,7 +25,7 @@ namespace stream { //FIXME: Quick and dirty workaround for bug in MinGW 9.3 causing a linker error when using std::to_string template -std::string to_string(T && t) { +std::string to_string(T &&t) { std::stringstream ss; ss << std::forward(t); return ss.str(); @@ -41,11 +41,11 @@ void free_msg(PRTSP_MESSAGE msg) { class rtsp_server_t; -using msg_t = util::safe_ptr; -using cmd_func_t = std::function; +using msg_t = util::safe_ptr; +using cmd_func_t = std::function; void print_msg(PRTSP_MESSAGE msg); -void cmd_not_found(net::host_t::pointer host, net::peer_t peer, msg_t&& req); +void cmd_not_found(net::host_t::pointer host, net::peer_t peer, msg_t &&req); class rtsp_server_t { public: @@ -82,61 +82,61 @@ public: ENetEvent event; auto res = enet_host_service(_host.get(), &event, std::chrono::floor(timeout).count()); - if (res > 0) { - switch (event.type) { - case ENET_EVENT_TYPE_RECEIVE: { - net::packet_t packet{event.packet}; - net::peer_t peer{event.peer}; + if(res > 0) { + switch(event.type) { + case ENET_EVENT_TYPE_RECEIVE: { + net::packet_t packet { event.packet }; + net::peer_t peer { event.peer }; - msg_t req { new msg_t::element_type }; + msg_t req { new msg_t::element_type }; - //TODO: compare addresses of the peers - if (_queue_packet.second == nullptr) { - parseRtspMessage(req.get(), (char *) packet->data, packet->dataLength); - for (auto option = req->options; option != nullptr; option = option->next) { - if ("Content-length"sv == option->option) { - _queue_packet = std::make_pair(peer, std::move(packet)); - return; - } + //TODO: compare addresses of the peers + if(_queue_packet.second == nullptr) { + parseRtspMessage(req.get(), (char *)packet->data, packet->dataLength); + for(auto option = req->options; option != nullptr; option = option->next) { + if("Content-length"sv == option->option) { + _queue_packet = std::make_pair(peer, std::move(packet)); + return; } } - else { - std::vector full_payload; - - auto old_msg = std::move(_queue_packet); - auto &old_packet = old_msg.second; - - std::string_view new_payload{(char *) packet->data, packet->dataLength}; - std::string_view old_payload{(char *) old_packet->data, old_packet->dataLength}; - full_payload.resize(new_payload.size() + old_payload.size()); - - std::copy(std::begin(old_payload), std::end(old_payload), std::begin(full_payload)); - std::copy(std::begin(new_payload), std::end(new_payload), std::begin(full_payload) + old_payload.size()); - - parseRtspMessage(req.get(), full_payload.data(), full_payload.size()); - } - - print_msg(req.get()); - - msg_t resp; - auto func = _map_cmd_cb.find(req->message.request.command); - if (func != std::end(_map_cmd_cb)) { - func->second(this, peer, std::move(req)); - } - else { - cmd_not_found(host(), peer, std::move(req)); - } - - return; } - case ENET_EVENT_TYPE_CONNECT: - BOOST_LOG(info) << "CLIENT CONNECTED TO RTSP"sv; - break; - case ENET_EVENT_TYPE_DISCONNECT: - BOOST_LOG(info) << "CLIENT DISCONNECTED FROM RTSP"sv; - break; - case ENET_EVENT_TYPE_NONE: - break; + else { + std::vector full_payload; + + auto old_msg = std::move(_queue_packet); + auto &old_packet = old_msg.second; + + std::string_view new_payload { (char *)packet->data, packet->dataLength }; + std::string_view old_payload { (char *)old_packet->data, old_packet->dataLength }; + full_payload.resize(new_payload.size() + old_payload.size()); + + std::copy(std::begin(old_payload), std::end(old_payload), std::begin(full_payload)); + std::copy(std::begin(new_payload), std::end(new_payload), std::begin(full_payload) + old_payload.size()); + + parseRtspMessage(req.get(), full_payload.data(), full_payload.size()); + } + + print_msg(req.get()); + + msg_t resp; + auto func = _map_cmd_cb.find(req->message.request.command); + if(func != std::end(_map_cmd_cb)) { + func->second(this, peer, std::move(req)); + } + else { + cmd_not_found(host(), peer, std::move(req)); + } + + return; + } + case ENET_EVENT_TYPE_CONNECT: + BOOST_LOG(info) << "CLIENT CONNECTED TO RTSP"sv; + break; + case ENET_EVENT_TYPE_DISCONNECT: + BOOST_LOG(info) << "CLIENT DISCONNECTED FROM RTSP"sv; + break; + case ENET_EVENT_TYPE_NONE: + break; } } } @@ -149,7 +149,7 @@ public: auto lg = _session_slots.lock(); for(auto &slot : *_session_slots) { - if (slot && (all || session::state(*slot) == session::state_e::STOPPING)) { + if(slot && (all || session::state(*slot) == session::state_e::STOPPING)) { session::stop(*slot); session::join(*slot); @@ -194,7 +194,6 @@ public: safe::event_t launch_event; private: - // named _queue_packet because I want to make it an actual queue // It's like this for convenience sake std::pair _queue_packet; @@ -225,11 +224,11 @@ void respond(net::host_t::pointer host, net::peer_t peer, msg_t &resp) { auto payload = std::make_pair(resp->payload, resp->payloadLength); auto lg = util::fail_guard([&]() { - resp->payload = payload.first; + resp->payload = payload.first; resp->payloadLength = payload.second; }); - resp->payload = nullptr; + resp->payload = nullptr; resp->payloadLength = 0; int serialized_len; @@ -264,35 +263,35 @@ void respond(net::host_t::pointer host, net::peer_t peer, msg_t &resp) { void respond(net::host_t::pointer host, net::peer_t peer, POPTION_ITEM options, int statuscode, const char *status_msg, int seqn, const std::string_view &payload) { msg_t resp { new msg_t::element_type }; - createRtspResponse(resp.get(), nullptr, 0, const_cast("RTSP/1.0"), statuscode, const_cast(status_msg), seqn, options, const_cast(payload.data()), (int)payload.size()); + 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(host, peer, resp); } -void cmd_not_found(net::host_t::pointer host, net::peer_t peer, msg_t&& req) { +void cmd_not_found(net::host_t::pointer host, net::peer_t peer, msg_t &&req) { respond(host, peer, nullptr, 404, "NOT FOUND", req->sequenceNumber, {}); } -void cmd_option(rtsp_server_t *server, net::peer_t peer, msg_t&& req) { +void cmd_option(rtsp_server_t *server, net::peer_t peer, msg_t &&req) { OPTION_ITEM option {}; // I know these string literals will not be modified - option.option = const_cast("CSeq"); + option.option = const_cast("CSeq"); - auto seqn_str = to_string(req->sequenceNumber); - option.content = const_cast(seqn_str.c_str()); + auto seqn_str = to_string(req->sequenceNumber); + option.content = const_cast(seqn_str.c_str()); respond(server->host(), peer, &option, 200, "OK", req->sequenceNumber, {}); } -void cmd_describe(rtsp_server_t *server, net::peer_t peer, msg_t&& req) { +void cmd_describe(rtsp_server_t *server, net::peer_t peer, msg_t &&req) { OPTION_ITEM option {}; // I know these string literals will not be modified - option.option = const_cast("CSeq"); + option.option = const_cast("CSeq"); - auto seqn_str = to_string(req->sequenceNumber); - option.content = const_cast(seqn_str.c_str()); + auto seqn_str = to_string(req->sequenceNumber); + option.content = const_cast(seqn_str.c_str()); std::string_view payload; if(config::video.hevc_mode == 1) { @@ -311,10 +310,10 @@ void cmd_setup(rtsp_server_t *server, net::peer_t peer, msg_t &&req) { auto &seqn = options[0]; auto &session_option = options[1]; - seqn.option = const_cast("CSeq"); + seqn.option = const_cast("CSeq"); auto seqn_str = to_string(req->sequenceNumber); - seqn.content = const_cast(seqn_str.c_str()); + seqn.content = const_cast(seqn_str.c_str()); std::string_view target { req->message.request.target }; auto begin = std::find(std::begin(target), std::end(target), '=') + 1; @@ -324,8 +323,8 @@ void cmd_setup(rtsp_server_t *server, net::peer_t peer, msg_t &&req) { if(type == "audio"sv) { seqn.next = &session_option; - session_option.option = const_cast("Session"); - session_option.content = const_cast("DEADBEEFCAFE;timeout = 90"); + session_option.option = const_cast("Session"); + session_option.content = const_cast("DEADBEEFCAFE;timeout = 90"); } else if(type != "video"sv && type != "control"sv) { cmd_not_found(server->host(), peer, std::move(req)); @@ -340,10 +339,10 @@ void cmd_announce(rtsp_server_t *server, net::peer_t peer, msg_t &&req) { OPTION_ITEM option {}; // I know these string literals will not be modified - option.option = const_cast("CSeq"); + option.option = const_cast("CSeq"); - auto seqn_str = to_string(req->sequenceNumber); - option.content = const_cast(seqn_str.c_str()); + auto seqn_str = to_string(req->sequenceNumber); + option.content = const_cast(seqn_str.c_str()); if(!server->launch_event.peek()) { // /launch has not been used @@ -362,10 +361,10 @@ void cmd_announce(rtsp_server_t *server, net::peer_t peer, msg_t &&req) { }; { - auto pos = std::begin(payload); + auto pos = std::begin(payload); auto begin = pos; - while (pos != std::end(payload)) { - if (whitespace(*pos++)) { + while(pos != std::end(payload)) { + if(whitespace(*pos++)) { lines.emplace_back(begin, pos - begin - 1); while(pos != std::end(payload) && whitespace(*pos)) { ++pos; } @@ -386,10 +385,10 @@ void cmd_announce(rtsp_server_t *server, net::peer_t peer, msg_t &&req) { auto pos = line.find(':'); auto name = line.substr(2, pos - 2); - auto val = line.substr(pos + 1); + auto val = line.substr(pos + 1); - if(val[val.size() -1] == ' ') { - val = val.substr(0, val.size() -1); + if(val[val.size() - 1] == ' ') { + val = val.substr(0, val.size() - 1); } args.emplace(name, val); } @@ -418,8 +417,8 @@ void cmd_announce(rtsp_server_t *server, net::peer_t peer, msg_t &&req) { config.monitor.encoderCscMode = util::from_view(args.at("x-nv-video[0].encoderCscMode"sv)); config.monitor.videoFormat = util::from_view(args.at("x-nv-vqos[0].bitStreamFormat"sv)); config.monitor.dynamicRange = util::from_view(args.at("x-nv-video[0].dynamicRangeMode"sv)); - - } catch(std::out_of_range &) { + } + catch(std::out_of_range &) { respond(server->host(), peer, &option, 400, "BAD REQUEST", req->sequenceNumber, {}); return; @@ -442,7 +441,7 @@ void cmd_announce(rtsp_server_t *server, net::peer_t peer, msg_t &&req) { return; } - if(session::start(*session, platf::from_sockaddr((sockaddr*)&peer->address.address))) { + if(session::start(*session, platf::from_sockaddr((sockaddr *)&peer->address.address))) { BOOST_LOG(error) << "Failed to start a streaming session"sv; server->clear(slot); @@ -457,10 +456,10 @@ void cmd_play(rtsp_server_t *server, net::peer_t peer, msg_t &&req) { OPTION_ITEM option {}; // I know these string literals will not be modified - option.option = const_cast("CSeq"); + option.option = const_cast("CSeq"); - auto seqn_str = to_string(req->sequenceNumber); - option.content = const_cast(seqn_str.c_str()); + auto seqn_str = to_string(req->sequenceNumber); + option.content = const_cast(seqn_str.c_str()); respond(server->host(), peer, &option, 200, "OK", req->sequenceNumber, {}); } @@ -518,7 +517,7 @@ void print_msg(PRTSP_MESSAGE msg) { BOOST_LOG(debug) << "status :: "sv << status; } else { - auto& req = msg->message.request; + auto &req = msg->message.request; std::string_view command { req.command }; std::string_view target { req.target }; @@ -534,6 +533,8 @@ void print_msg(PRTSP_MESSAGE msg) { BOOST_LOG(debug) << name << " :: "sv << content; } - BOOST_LOG(debug) << "---Begin MessageBuffer---"sv << std::endl << messageBuffer << std::endl << "---End MessageBuffer---"sv << std::endl; -} + BOOST_LOG(debug) << "---Begin MessageBuffer---"sv << std::endl + << messageBuffer << std::endl + << "---End MessageBuffer---"sv << std::endl; } +} // namespace stream diff --git a/sunshine/rtsp.h b/sunshine/rtsp.h index ac60e8d6..cab06fc4 100644 --- a/sunshine/rtsp.h +++ b/sunshine/rtsp.h @@ -21,6 +21,6 @@ int session_count(); void rtpThread(std::shared_ptr shutdown_event); -} +} // namespace stream #endif //SUNSHINE_RTSP_H diff --git a/sunshine/stream.cpp b/sunshine/stream.cpp index c505fc0a..8971c266 100644 --- a/sunshine/stream.cpp +++ b/sunshine/stream.cpp @@ -4,8 +4,8 @@ #include "process.h" -#include #include +#include #include #include @@ -15,14 +15,14 @@ extern "C" { #include } -#include "network.h" #include "config.h" -#include "utility.h" -#include "stream.h" -#include "thread_safe.h" -#include "sync.h" #include "input.h" #include "main.h" +#include "network.h" +#include "stream.h" +#include "sync.h" +#include "thread_safe.h" +#include "utility.h" #define IDX_START_A 0 #define IDX_REQUEST_IDR_FRAME 0 @@ -45,7 +45,7 @@ static const short packetTypes[] = { }; constexpr auto VIDEO_STREAM_PORT = 47998; -constexpr auto CONTROL_PORT = 47999; +constexpr auto CONTROL_PORT = 47999; constexpr auto AUDIO_STREAM_PORT = 48000; namespace asio = boost::asio; @@ -89,7 +89,7 @@ using rh_t = util::safe_ptr; using video_packet_t = util::c_ptr; using audio_packet_t = util::c_ptr; -using message_queue_t = std::shared_ptr>>; +using message_queue_t = std::shared_ptr>>; using message_queue_queue_t = std::shared_ptr>>; static inline void while_starting_do_nothing(std::atomic &state) { @@ -124,7 +124,7 @@ public: // Therefore, iterate is implemented further down the source file void iterate(std::chrono::milliseconds timeout); - void map(uint16_t type, std::function cb) { + void map(uint16_t type, std::function cb) { _map_type_cb.emplace(type, std::move(cb)); } @@ -140,10 +140,10 @@ public: } // Callbacks - std::unordered_map> _map_type_cb; + std::unordered_map> _map_type_cb; // Mapping ip:port to session - util::sync_t>> _map_addr_session; + util::sync_t>> _map_addr_session; ENetAddress _addr; net::host_t _host; @@ -210,7 +210,7 @@ static auto broadcast = safe::make_shared(start_broadcast, end_ safe::signal_t broadcast_shutdown_event; session_t *control_server_t::get_session(const net::peer_t peer) { - TUPLE_2D(port, addr_string, platf::from_sockaddr_ex((sockaddr*)&peer->address.address)); + TUPLE_2D(port, addr_string, platf::from_sockaddr_ex((sockaddr *)&peer->address.address)); auto lg = _map_addr_session.lock(); TUPLE_2D(begin, end, _map_addr_session->equal_range(addr_string)); @@ -231,7 +231,7 @@ session_t *control_server_t::get_session(const net::peer_t peer) { TUPLE_2D_REF(session_port, session_p, it->second); session_p->control.peer = peer; - session_port = port; + session_port = port; return session_p; } @@ -246,7 +246,7 @@ void control_server_t::iterate(std::chrono::milliseconds timeout) { if(res > 0) { auto session = get_session(event.peer); if(!session) { - BOOST_LOG(warning) << "Rejected connection from ["sv << platf::from_sockaddr((sockaddr*)&event.peer->address.address) << "]: it's not properly set up"sv; + BOOST_LOG(warning) << "Rejected connection from ["sv << platf::from_sockaddr((sockaddr *)&event.peer->address.address) << "]: it's not properly set up"sv; enet_peer_disconnect_now(event.peer, 0); return; @@ -255,37 +255,37 @@ void control_server_t::iterate(std::chrono::milliseconds timeout) { session->pingTimeout = std::chrono::steady_clock::now() + config::stream.ping_timeout; switch(event.type) { - case ENET_EVENT_TYPE_RECEIVE: - { - net::packet_t packet { event.packet }; + case ENET_EVENT_TYPE_RECEIVE: { + net::packet_t packet { event.packet }; - auto type = (std::uint16_t *)packet->data; - std::string_view payload { (char*)packet->data + sizeof(*type), packet->dataLength - sizeof(*type) }; + auto type = (std::uint16_t *)packet->data; + std::string_view payload { (char *)packet->data + sizeof(*type), packet->dataLength - sizeof(*type) }; - auto cb = _map_type_cb.find(*type); - if(cb == std::end(_map_type_cb)) { - BOOST_LOG(warning) - << "type [Unknown] { "sv << util::hex(*type).to_string_view() << " }"sv << std::endl - << "---data---"sv << std::endl << util::hex_vec(payload) << std::endl << "---end data---"sv; - } - - else { - cb->second(session, payload); - } + auto cb = _map_type_cb.find(*type); + if(cb == std::end(_map_type_cb)) { + BOOST_LOG(warning) + << "type [Unknown] { "sv << util::hex(*type).to_string_view() << " }"sv << std::endl + << "---data---"sv << std::endl + << util::hex_vec(payload) << std::endl + << "---end data---"sv; } - break; - case ENET_EVENT_TYPE_CONNECT: - BOOST_LOG(info) << "CLIENT CONNECTED"sv; - break; - case ENET_EVENT_TYPE_DISCONNECT: - BOOST_LOG(info) << "CLIENT DISCONNECTED"sv; - // No more clients to send video data to ^_^ - if(session->state == session::state_e::RUNNING) { - session::stop(*session); - } - break; - case ENET_EVENT_TYPE_NONE: - break; + + else { + cb->second(session, payload); + } + } break; + case ENET_EVENT_TYPE_CONNECT: + BOOST_LOG(info) << "CLIENT CONNECTED"sv; + break; + case ENET_EVENT_TYPE_DISCONNECT: + BOOST_LOG(info) << "CLIENT DISCONNECTED"sv; + // No more clients to send video data to ^_^ + if(session->state == session::state_e::RUNNING) { + session::stop(*session); + } + break; + case ENET_EVENT_TYPE_NONE: + break; } } } @@ -302,11 +302,11 @@ struct fec_t { util::buffer_t shards; char *data(size_t el) { - return &shards[el*blocksize]; + return &shards[el * blocksize]; } std::string_view operator[](size_t el) const { - return { &shards[el*blocksize], blocksize }; + return { &shards[el * blocksize], blocksize }; } size_t size() const { @@ -321,7 +321,7 @@ fec_t encode(const std::string_view &payload, size_t blocksize, size_t fecpercen auto data_shards = payload_size / blocksize + (pad ? 1 : 0); auto parity_shards = (data_shards * fecpercentage + 99) / 100; - auto nr_shards = data_shards + parity_shards; + auto nr_shards = data_shards + parity_shards; if(nr_shards > DATA_SHARDS_MAX) { BOOST_LOG(error) @@ -332,14 +332,14 @@ fec_t encode(const std::string_view &payload, size_t blocksize, size_t fecpercen } util::buffer_t shards { nr_shards * blocksize }; - util::buffer_t shards_p { nr_shards }; + util::buffer_t shards_p { nr_shards }; // copy payload + padding auto next = std::copy(std::begin(payload), std::end(payload), std::begin(shards)); std::fill(next, std::end(shards), 0); // padding with zero for(auto x = 0; x < nr_shards; ++x) { - shards_p[x] = (uint8_t*)&shards[x * blocksize]; + shards_p[x] = (uint8_t *)&shards[x * blocksize]; } // packets = parity_shards + data_shards @@ -355,11 +355,11 @@ fec_t encode(const std::string_view &payload, size_t blocksize, size_t fecpercen std::move(shards) }; } -} +} // namespace fec template std::vector insert(uint64_t insert_size, uint64_t slice_size, const std::string_view &data, F &&f) { - auto pad = data.size() % slice_size != 0; + auto pad = data.size() % slice_size != 0; auto elements = data.size() / slice_size + (pad ? 1 : 0); std::vector result; @@ -367,20 +367,20 @@ std::vector insert(uint64_t insert_size, uint64_t slice_size, const std auto next = std::begin(data); for(auto x = 0; x < elements - 1; ++x) { - void *p = &result[x*(insert_size + slice_size)]; + void *p = &result[x * (insert_size + slice_size)]; f(p, x, elements); - std::copy(next, next + slice_size, (char*)p + insert_size); + std::copy(next, next + slice_size, (char *)p + insert_size); next += slice_size; } - auto x = elements - 1; - void *p = &result[x*(insert_size + slice_size)]; + auto x = elements - 1; + void *p = &result[x * (insert_size + slice_size)]; f(p, x, elements); - std::copy(next, std::end(data), (char*)p + insert_size); + std::copy(next, std::end(data), (char *)p + insert_size); return result; } @@ -389,7 +389,7 @@ std::vector replace(const std::string_view &original, const std::string std::vector replaced; auto begin = std::begin(original); - auto next = std::search(begin, std::end(original), std::begin(old), std::end(old)); + auto next = std::search(begin, std::end(original), std::begin(old), std::end(old)); std::copy(begin, next, std::back_inserter(replaced)); std::copy(std::begin(_new), std::end(_new), std::back_inserter(replaced)); @@ -408,8 +408,8 @@ void controlBroadcastThread(safe::signal_t *shutdown_event, control_server_t *se }); server->map(packetTypes[IDX_LOSS_STATS], [&](session_t *session, const std::string_view &payload) { - int32_t *stats = (int32_t*)payload.data(); - auto count = stats[0]; + int32_t *stats = (int32_t *)payload.data(); + auto count = stats[0]; std::chrono::milliseconds t { stats[1] }; auto lastGoodFrame = stats[3]; @@ -424,9 +424,9 @@ void controlBroadcastThread(safe::signal_t *shutdown_event, control_server_t *se }); server->map(packetTypes[IDX_INVALIDATE_REF_FRAMES], [&](session_t *session, const std::string_view &payload) { - auto frames = (std::int64_t *)payload.data(); + auto frames = (std::int64_t *)payload.data(); auto firstFrame = frames[0]; - auto lastFrame = frames[1]; + auto lastFrame = frames[1]; BOOST_LOG(debug) << "type [IDX_INVALIDATE_REF_FRAMES]"sv << std::endl @@ -439,7 +439,7 @@ void controlBroadcastThread(safe::signal_t *shutdown_event, control_server_t *se server->map(packetTypes[IDX_INPUT_DATA], [&](session_t *session, const std::string_view &payload) { BOOST_LOG(debug) << "type [IDX_INPUT_DATA]"sv; - int32_t tagged_cipher_length = util::endian::big(*(int32_t*)payload.data()); + int32_t tagged_cipher_length = util::endian::big(*(int32_t *)payload.data()); std::string_view tagged_cipher { payload.data() + sizeof(tagged_cipher_length), (size_t)tagged_cipher_length }; crypto::cipher_t cipher { session->gcm_key }; @@ -498,7 +498,7 @@ void controlBroadcastThread(safe::signal_t *shutdown_event, control_server_t *se payload[0] = packetTypes[IDX_TERMINATION]; payload[1] = reason; - server->send(std::string_view {(char*)payload.data(), payload.size()}); + server->send(std::string_view { (char *)payload.data(), payload.size() }); auto lg = server->_map_addr_session.lock(); for(auto pos = std::begin(*server->_map_addr_session); pos != std::end(*server->_map_addr_session); ++pos) { @@ -519,7 +519,7 @@ void recvThread(broadcast_ctx_t &ctx) { auto &audio_sock = ctx.audio_sock; auto &message_queue_queue = ctx.message_queue_queue; - auto &io = ctx.io; + auto &io = ctx.io; udp::endpoint peer; @@ -532,28 +532,28 @@ void recvThread(broadcast_ctx_t &ctx) { TUPLE_3D_REF(socket_type, addr, message_queue, *message_queue_opt); switch(socket_type) { - case socket_e::video: - if(message_queue) { - peer_to_video_session.emplace(addr, message_queue); - } - else { - peer_to_video_session.erase(addr); - } - break; - case socket_e::audio: - if(message_queue) { - peer_to_audio_session.emplace(addr, message_queue); - } - else { - peer_to_audio_session.erase(addr); - } - break; + case socket_e::video: + if(message_queue) { + peer_to_video_session.emplace(addr, message_queue); + } + else { + peer_to_video_session.erase(addr); + } + break; + case socket_e::audio: + if(message_queue) { + peer_to_audio_session.emplace(addr, message_queue); + } + else { + peer_to_audio_session.erase(addr); + } + break; } } }; auto recv_func_init = [&](udp::socket &sock, int buf_elem, std::map &peer_to_session) { - recv_func[buf_elem] = [&,buf_elem](const boost::system::error_code &ec, size_t bytes) { + recv_func[buf_elem] = [&, buf_elem](const boost::system::error_code &ec, size_t bytes) { auto fg = util::fail_guard([&]() { sock.async_receive_from(asio::buffer(buf[buf_elem]), peer, 0, recv_func[buf_elem]); }); @@ -601,20 +601,20 @@ void videoBroadcastThread(safe::signal_t *shutdown_event, udp::socket &sock, vid break; } - auto session = (session_t*)packet->channel_data; - auto lowseq = session->video.lowseq; + auto session = (session_t *)packet->channel_data; + auto lowseq = session->video.lowseq; - std::string_view payload{(char *) packet->data, (size_t) packet->size}; + std::string_view payload { (char *)packet->data, (size_t)packet->size }; std::vector payload_new; auto nv_packet_header = "\0017charss"sv; std::copy(std::begin(nv_packet_header), std::end(nv_packet_header), std::back_inserter(payload_new)); std::copy(std::begin(payload), std::end(payload), std::back_inserter(payload_new)); - payload = {(char *) payload_new.data(), payload_new.size()}; + payload = { (char *)payload_new.data(), payload_new.size() }; // make sure moonlight recognizes the nalu code for IDR frames - if (packet->flags & AV_PKT_FLAG_KEY) { + if(packet->flags & AV_PKT_FLAG_KEY) { // TODO: Not all encoders encode their IDR frames with the 4 byte NALU prefix std::string_view frame_old = "\000\000\001e"sv; std::string_view frame_new = "\000\000\000\001e"sv; @@ -624,27 +624,25 @@ void videoBroadcastThread(safe::signal_t *shutdown_event, udp::socket &sock, vid } payload_new = replace(payload, frame_old, frame_new); - payload = {(char *) payload_new.data(), payload_new.size()}; + payload = { (char *)payload_new.data(), payload_new.size() }; } // insert packet headers - auto blocksize = session->config.packetsize + MAX_RTP_HEADER_SIZE; + auto blocksize = session->config.packetsize + MAX_RTP_HEADER_SIZE; auto payload_blocksize = blocksize - sizeof(video_packet_raw_t); auto fecPercentage = config::stream.fec_percentage; payload_new = insert(sizeof(video_packet_raw_t), payload_blocksize, - payload, [&](void *p, int fecIndex, int end) { + payload, [&](void *p, int fecIndex, int end) { video_packet_raw_t *video_packet = (video_packet_raw_t *)p; - video_packet->packet.flags = FLAG_CONTAINS_PIC_DATA; - video_packet->packet.frameIndex = packet->pts; + video_packet->packet.flags = FLAG_CONTAINS_PIC_DATA; + video_packet->packet.frameIndex = packet->pts; video_packet->packet.streamPacketIndex = ((uint32_t)lowseq + fecIndex) << 8; - video_packet->packet.fecInfo = ( - fecIndex << 12 | - end << 22 | - fecPercentage << 4 - ); + video_packet->packet.fecInfo = (fecIndex << 12 | + end << 22 | + fecPercentage << 4); if(fecIndex == 0) { video_packet->packet.flags |= FLAG_SOF; @@ -654,11 +652,11 @@ void videoBroadcastThread(safe::signal_t *shutdown_event, udp::socket &sock, vid video_packet->packet.flags |= FLAG_EOF; } - video_packet->rtp.header = FLAG_EXTENSION; + video_packet->rtp.header = FLAG_EXTENSION; video_packet->rtp.sequenceNumber = util::endian::big(lowseq + fecIndex); }); - payload = {(char *) payload_new.data(), payload_new.size()}; + payload = { (char *)payload_new.data(), payload_new.size() }; auto shards = fec::encode(payload, blocksize, fecPercentage); if(shards.data_shards == 0) { @@ -666,17 +664,15 @@ void videoBroadcastThread(safe::signal_t *shutdown_event, udp::socket &sock, vid continue; } - for (auto x = shards.data_shards; x < shards.size(); ++x) { + for(auto x = shards.data_shards; x < shards.size(); ++x) { auto *inspect = (video_packet_raw_t *)shards.data(x); inspect->packet.frameIndex = packet->pts; - inspect->packet.fecInfo = ( - x << 12 | - shards.data_shards << 22 | - fecPercentage << 4 - ); + inspect->packet.fecInfo = (x << 12 | + shards.data_shards << 22 | + fecPercentage << 4); - inspect->rtp.header = FLAG_EXTENSION; + inspect->rtp.header = FLAG_EXTENSION; inspect->rtp.sequenceNumber = util::endian::big(lowseq + x); } @@ -698,27 +694,27 @@ void videoBroadcastThread(safe::signal_t *shutdown_event, udp::socket &sock, vid } void audioBroadcastThread(safe::signal_t *shutdown_event, udp::socket &sock, audio::packet_queue_t packets) { - while (auto packet = packets->pop()) { + while(auto packet = packets->pop()) { if(shutdown_event->peek()) { break; } TUPLE_2D_REF(channel_data, packet_data, *packet); - auto session = (session_t*)channel_data; + auto session = (session_t *)channel_data; auto frame = session->audio.frame++; - audio_packet_t audio_packet { (audio_packet_raw_t*)malloc(sizeof(audio_packet_raw_t) + packet_data.size()) }; + audio_packet_t audio_packet { (audio_packet_raw_t *)malloc(sizeof(audio_packet_raw_t) + packet_data.size()) }; - audio_packet->rtp.header = 0; - audio_packet->rtp.packetType = 97; + audio_packet->rtp.header = 0; + audio_packet->rtp.packetType = 97; audio_packet->rtp.sequenceNumber = util::endian::big(frame); - audio_packet->rtp.timestamp = 0; - audio_packet->rtp.ssrc = 0; + audio_packet->rtp.timestamp = 0; + audio_packet->rtp.ssrc = 0; std::copy(std::begin(packet_data), std::end(packet_data), audio_packet->payload()); - sock.send_to(asio::buffer((char*)audio_packet.get(), sizeof(audio_packet_raw_t) + packet_data.size()), session->audio.peer); + sock.send_to(asio::buffer((char *)audio_packet.get(), sizeof(audio_packet_raw_t) + packet_data.size()), session->audio.peer); BOOST_LOG(verbose) << "Audio ["sv << frame << "] :: send..."sv; } @@ -761,12 +757,12 @@ int start_broadcast(broadcast_ctx_t &ctx) { return -1; } - ctx.video_packets = std::make_shared(30); - ctx.audio_packets = std::make_shared(30); + ctx.video_packets = std::make_shared(30); + ctx.audio_packets = std::make_shared(30); ctx.message_queue_queue = std::make_shared(30); - ctx.video_thread = std::thread { videoBroadcastThread, &broadcast_shutdown_event, std::ref(ctx.video_sock), ctx.video_packets }; - ctx.audio_thread = std::thread { audioBroadcastThread, &broadcast_shutdown_event, std::ref(ctx.audio_sock), ctx.audio_packets }; + ctx.video_thread = std::thread { videoBroadcastThread, &broadcast_shutdown_event, std::ref(ctx.video_sock), ctx.video_packets }; + ctx.audio_thread = std::thread { audioBroadcastThread, &broadcast_shutdown_event, std::ref(ctx.audio_sock), ctx.audio_packets }; ctx.control_thread = std::thread { controlBroadcastThread, &broadcast_shutdown_event, &ctx.control_server }; ctx.recv_thread = std::thread { recvThread, std::ref(ctx) }; @@ -842,7 +838,7 @@ void videoThread(session_t *session, std::string addr_str) { while_starting_do_nothing(session->state); auto addr = asio::ip::make_address(addr_str); - auto ref = broadcast.ref(); + auto ref = broadcast.ref(); auto port = recv_ping(ref, socket_e::video, addr, config::stream.ping_timeout); if(port < 0) { return; @@ -864,7 +860,7 @@ void audioThread(session_t *session, std::string addr_str) { auto addr = asio::ip::make_address(addr_str); - auto ref = broadcast.ref(); + auto ref = broadcast.ref(); auto port = recv_ping(ref, socket_e::audio, addr, config::stream.ping_timeout); if(port < 0) { return; @@ -884,7 +880,7 @@ state_e state(session_t &session) { void stop(session_t &session) { while_starting_do_nothing(session.state); - auto expected = state_e::RUNNING; + auto expected = state_e::RUNNING; auto already_stopping = !session.state.compare_exchange_strong(expected, state_e::STOPPING); if(already_stopping) { return; @@ -918,8 +914,8 @@ int start(session_t &session, const std::string &addr_string) { session.pingTimeout = std::chrono::steady_clock::now() + config::stream.ping_timeout; - session.audioThread = std::thread {audioThread, &session, addr_string}; - session.videoThread = std::thread {videoThread, &session, addr_string}; + session.audioThread = std::thread { audioThread, &session, addr_string }; + session.videoThread = std::thread { videoThread, &session, addr_string }; session.state.store(state_e::RUNNING, std::memory_order_relaxed); @@ -929,12 +925,12 @@ int start(session_t &session, const std::string &addr_string) { std::shared_ptr alloc(config_t &config, crypto::aes_t &gcm_key, crypto::aes_t &iv) { auto session = std::make_shared(); - session->config = config; + session->config = config; session->gcm_key = gcm_key; - session->iv = iv; + session->iv = iv; session->video.idr_events = std::make_shared(); - session->video.lowseq = 0; + session->video.lowseq = 0; session->audio.frame = 1; @@ -943,5 +939,5 @@ std::shared_ptr alloc(config_t &config, crypto::aes_t &gcm_key, crypt return session; } -} -} +} // namespace session +} // namespace stream diff --git a/sunshine/stream.h b/sunshine/stream.h index cd63fcb2..57f69403 100644 --- a/sunshine/stream.h +++ b/sunshine/stream.h @@ -7,9 +7,9 @@ #include -#include "video.h" #include "audio.h" #include "crypto.h" +#include "video.h" namespace stream { struct session_t; @@ -35,9 +35,9 @@ int start(session_t &session, const std::string &addr_string); void stop(session_t &session); void join(session_t &session); state_e state(session_t &session); -} +} // namespace session extern safe::signal_t broadcast_shutdown_event; -} +} // namespace stream #endif //SUNSHINE_STREAM_H diff --git a/sunshine/sync.h b/sunshine/sync.h index 4689e595..8d67de2a 100644 --- a/sunshine/sync.h +++ b/sunshine/sync.h @@ -5,9 +5,9 @@ #ifndef SUNSHINE_SYNC_H #define SUNSHINE_SYNC_H -#include -#include #include +#include +#include namespace util { @@ -21,8 +21,8 @@ public: return std::lock_guard { _lock }; } - template - sync_t(Args&&... args) : raw {std::forward(args)... } {} + template + sync_t(Args &&...args) : raw { std::forward(args)... } {} sync_t &operator=(sync_t &&other) noexcept { std::lock(_lock, other._lock); @@ -84,11 +84,12 @@ public: } value_t raw; + private: mutex_t _lock; }; -} +} // namespace util #endif //T_MAN_SYNC_H diff --git a/sunshine/task_pool.h b/sunshine/task_pool.h index 83fe8596..fc07e8ab 100644 --- a/sunshine/task_pool.h +++ b/sunshine/task_pool.h @@ -1,18 +1,18 @@ #ifndef KITTY_TASK_POOL_H #define KITTY_TASK_POOL_H -#include -#include -#include #include -#include +#include #include +#include #include -#include #include +#include +#include +#include -#include "utility.h" #include "move_by_copy.h" +#include "utility.h" namespace util { class _ImplBase { @@ -29,8 +29,7 @@ class _Impl : public _ImplBase { Function _func; public: - - _Impl(Function&& f) : _func(std::forward(f)) { } + _Impl(Function &&f) : _func(std::forward(f)) {} void run() override { _func(); @@ -40,7 +39,7 @@ public: class TaskPool { public: typedef std::unique_ptr<_ImplBase> __task; - typedef _ImplBase* task_id_t; + typedef _ImplBase *task_id_t; typedef std::chrono::steady_clock::time_point __time_point; @@ -53,9 +52,10 @@ public: timer_task_t(task_id_t task_id, std::future &future) : task_id { task_id }, future { std::move(future) } {} }; + protected: std::deque<__task> _tasks; - std::vector> _timer_tasks; + std::vector> _timer_tasks; std::mutex _task_mutex; public: @@ -70,8 +70,8 @@ public: } template - auto push(Function && newTask, Args &&... args) { - static_assert(std::is_invocable_v, "arguments don't match the function"); + auto push(Function &&newTask, Args &&...args) { + static_assert(std::is_invocable_v, "arguments don't match the function"); using __return = std::invoke_result_t; using task_t = std::packaged_task<__return()>; @@ -81,12 +81,12 @@ public: }; task_t task(std::move(bind)); - + auto future = task.get_future(); - + std::lock_guard lg(_task_mutex); _tasks.emplace_back(toRunnable(std::move(task))); - + return future; } @@ -107,14 +107,14 @@ public: * @return an id to potentially delay the task */ template - auto pushDelayed(Function &&newTask, std::chrono::duration duration, Args &&... args) { - static_assert(std::is_invocable_v, "arguments don't match the function"); + 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; using task_t = std::packaged_task<__return()>; __time_point time_point; - if constexpr (std::is_floating_point_v) { + if constexpr(std::is_floating_point_v) { time_point = std::chrono::steady_clock::now() + std::chrono::duration_cast(duration); } else { @@ -127,7 +127,7 @@ public: task_t task(std::move(bind)); - auto future = task.get_future(); + auto future = task.get_future(); auto runnable = toRunnable(std::move(task)); task_id_t task_id = &*runnable; @@ -160,13 +160,14 @@ public: } // smaller time goes to the back - auto prev = it -1; + auto prev = it - 1; while(it > _timer_tasks.cbegin()) { if(std::get<0>(*it) > std::get<0>(*prev)) { std::swap(*it, *prev); } - --prev; --it; + --prev; + --it; } } @@ -201,20 +202,20 @@ public: std::optional<__task> pop() { std::lock_guard lg(_task_mutex); - + if(!_tasks.empty()) { __task task = std::move(_tasks.front()); _tasks.pop_front(); return std::move(task); } - + if(!_timer_tasks.empty() && std::get<0>(_timer_tasks.back()) <= std::chrono::steady_clock::now()) { __task task = std::move(std::get<1>(_timer_tasks.back())); _timer_tasks.pop_back(); - + return std::move(task); } - + return std::nullopt; } @@ -233,12 +234,12 @@ public: return std::get<0>(_timer_tasks.back()); } -private: +private: template std::unique_ptr<_ImplBase> toRunnable(Function &&f) { - return std::make_unique<_Impl>(std::forward(f)); + return std::make_unique<_Impl>(std::forward(f)); } }; -} +} // namespace util #endif diff --git a/sunshine/thread_pool.h b/sunshine/thread_pool.h index be3f4eb9..8048e6d0 100644 --- a/sunshine/thread_pool.h +++ b/sunshine/thread_pool.h @@ -1,8 +1,8 @@ #ifndef KITTY_THREAD_POOL_H #define KITTY_THREAD_POOL_H -#include #include "task_pool.h" +#include namespace util { /* @@ -12,32 +12,33 @@ namespace util { class ThreadPool : public TaskPool { public: typedef TaskPool::__task __task; - + private: std::vector _thread; std::condition_variable _cv; std::mutex _lock; - + bool _continue; + public: ThreadPool() : _continue { false } {} explicit ThreadPool(int threads) : _thread(threads), _continue { true } { - for (auto &t : _thread) { + for(auto &t : _thread) { t = std::thread(&ThreadPool::_main, this); } } ~ThreadPool() noexcept { - if (!_continue) return; + if(!_continue) return; stop(); join(); } template - auto push(Function && newTask, Args &&... args) { + auto push(Function &&newTask, Args &&...args) { std::lock_guard lg(_lock); auto future = TaskPool::push(std::forward(newTask), std::forward(args)...); @@ -52,7 +53,7 @@ public: } template - auto pushDelayed(Function &&newTask, std::chrono::duration duration, Args &&... args) { + 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)...); @@ -79,15 +80,14 @@ public: } void join() { - for (auto & t : _thread) { + for(auto &t : _thread) { t.join(); } } public: - void _main() { - while (_continue) { + while(_continue) { if(auto task = this->pop()) { (*task)->run(); } @@ -117,5 +117,5 @@ public: } } }; -} +} // namespace util #endif diff --git a/sunshine/thread_safe.h b/sunshine/thread_safe.h index d166cc0f..21db9974 100644 --- a/sunshine/thread_safe.h +++ b/sunshine/thread_safe.h @@ -5,11 +5,11 @@ #ifndef SUNSHINE_THREAD_SAFE_H #define SUNSHINE_THREAD_SAFE_H -#include -#include -#include #include +#include #include +#include +#include #include "utility.h" @@ -19,14 +19,14 @@ class event_t { using status_t = util::optional_t; public: - template + template void raise(Args &&...args) { std::lock_guard lg { _lock }; if(!_continue) { return; } - if constexpr (std::is_same_v, status_t>) { + if constexpr(std::is_same_v, status_t>) { _status = std::make_optional(std::forward(args)...); } else { @@ -38,42 +38,42 @@ public: // pop and view shoud not be used interchangebly status_t pop() { - std::unique_lock ul{ _lock }; + std::unique_lock ul { _lock }; - if (!_continue) { + if(!_continue) { return util::false_v; } - while (!_status) { + while(!_status) { _cv.wait(ul); - if (!_continue) { + if(!_continue) { return util::false_v; } } auto val = std::move(_status); - _status = util::false_v; + _status = util::false_v; return val; } // pop and view shoud not be used interchangebly template status_t pop(std::chrono::duration delay) { - std::unique_lock ul{ _lock }; + std::unique_lock ul { _lock }; - if (!_continue) { + if(!_continue) { return util::false_v; } - while (!_status) { - if (!_continue || _cv.wait_for(ul, delay) == std::cv_status::timeout) { + while(!_status) { + if(!_continue || _cv.wait_for(ul, delay) == std::cv_status::timeout) { return util::false_v; } } auto val = std::move(_status); - _status = util::false_v; + _status = util::false_v; return val; } @@ -81,14 +81,14 @@ public: const status_t &view() { std::unique_lock ul { _lock }; - if (!_continue) { + if(!_continue) { return util::false_v; } - while (!_status) { + while(!_status) { _cv.wait(ul); - if (!_continue) { + if(!_continue) { return util::false_v; } } @@ -103,7 +103,7 @@ public: } void stop() { - std::lock_guard lg{ _lock }; + std::lock_guard lg { _lock }; _continue = false; @@ -111,7 +111,7 @@ public: } void reset() { - std::lock_guard lg{ _lock }; + std::lock_guard lg { _lock }; _continue = true; @@ -121,8 +121,8 @@ public: [[nodiscard]] bool running() const { return _continue; } -private: +private: bool _continue { true }; status_t _status { util::false_v }; @@ -137,8 +137,8 @@ class queue_t { public: queue_t(std::uint32_t max_elements) : _max_elements { max_elements } {} - template - void raise(Args &&... args) { + template + void raise(Args &&...args) { std::lock_guard ul { _lock }; if(!_continue) { @@ -164,12 +164,12 @@ public: status_t pop(std::chrono::duration delay) { std::unique_lock ul { _lock }; - if (!_continue) { + if(!_continue) { return util::false_v; } - while (_queue.empty()) { - if (!_continue || _cv.wait_for(ul, delay) == std::cv_status::timeout) { + while(_queue.empty()) { + if(!_continue || _cv.wait_for(ul, delay) == std::cv_status::timeout) { return util::false_v; } } @@ -183,14 +183,14 @@ public: status_t pop() { std::unique_lock ul { _lock }; - if (!_continue) { + if(!_continue) { return util::false_v; } - while (_queue.empty()) { + while(_queue.empty()) { _cv.wait(ul); - if (!_continue) { + if(!_continue) { return util::false_v; } } @@ -219,7 +219,6 @@ public: } private: - bool _continue { true }; std::uint32_t _max_elements; @@ -235,7 +234,7 @@ public: using element_type = T; using construct_f = std::function; - using destruct_f = std::function; + using destruct_f = std::function; struct ptr_t { shared_t *owner; @@ -252,7 +251,7 @@ public: return; } - auto tmp = ptr.owner->ref(); + auto tmp = ptr.owner->ref(); tmp.owner = nullptr; } @@ -282,7 +281,7 @@ public: } } - operator bool () const { + operator bool() const { return owner != nullptr; } @@ -298,22 +297,22 @@ public: } element_type *get() const { - return reinterpret_cast(owner->_object_buf.data()); + return reinterpret_cast(owner->_object_buf.data()); } element_type *operator->() { - return reinterpret_cast(owner->_object_buf.data()); + return reinterpret_cast(owner->_object_buf.data()); } }; template - shared_t(FC && fc, FD &&fd) : _construct { std::forward(fc) }, _destruct { std::forward(fd) } {} + shared_t(FC &&fc, FD &&fd) : _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()))) { + if(_construct(*reinterpret_cast(_object_buf.data()))) { return ptr_t { nullptr }; } } @@ -322,6 +321,7 @@ public: return ptr_t { this }; } + private: construct_f _construct; destruct_f _destruct; @@ -340,6 +340,6 @@ auto make_shared(F_Construct &&fc, F_Destruct &&fd) { } using signal_t = event_t; -} +} // namespace safe #endif //SUNSHINE_THREAD_SAFE_H diff --git a/sunshine/utility.h b/sunshine/utility.h index 63be108c..66606a38 100644 --- a/sunshine/utility.h +++ b/sunshine/utility.h @@ -1,63 +1,67 @@ #ifndef UTILITY_H #define UTILITY_H +#include +#include +#include +#include +#include +#include +#include #include #include -#include -#include -#include -#include -#include -#include -#include -#define KITTY_WHILE_LOOP(x, y, z) { x;while(y) z } -#define KITTY_DECL_CONSTR(x)\ - x(x&&) noexcept = default;\ - x&operator=(x&&) noexcept = default;\ +#define KITTY_WHILE_LOOP(x, y, z) \ + { \ + x; \ + while(y) z \ + } +#define KITTY_DECL_CONSTR(x) \ + x(x &&) noexcept = default; \ + x &operator=(x &&) noexcept = default; \ x(); -#define KITTY_DEFAULT_CONSTR(x)\ - x(x&&) noexcept = default;\ - x&operator=(x&&) noexcept = default;\ - x() = default; +#define KITTY_DEFAULT_CONSTR(x) \ + x(x &&) noexcept = default; \ + x &operator=(x &&) noexcept = default; \ + x() = default; -#define KITTY_DEFAULT_CONSTR_THROW(x)\ - x(x&&) = default;\ - x&operator=(x&&) = default;\ - x() = default; +#define KITTY_DEFAULT_CONSTR_THROW(x) \ + x(x &&) = default; \ + x &operator=(x &&) = default; \ + x() = default; -#define TUPLE_2D(a,b, expr)\ - decltype(expr) a##_##b = expr;\ - auto &a = std::get<0>(a##_##b);\ - auto &b = std::get<1>(a##_##b) +#define TUPLE_2D(a, b, expr) \ + decltype(expr) a##_##b = expr; \ + auto &a = std::get<0>(a##_##b); \ + auto &b = std::get<1>(a##_##b) -#define TUPLE_2D_REF(a,b, expr)\ - auto &a##_##b = expr;\ - auto &a = std::get<0>(a##_##b);\ - auto &b = std::get<1>(a##_##b) +#define TUPLE_2D_REF(a, b, expr) \ + auto &a##_##b = expr; \ + auto &a = std::get<0>(a##_##b); \ + auto &b = std::get<1>(a##_##b) -#define TUPLE_3D(a,b,c, expr)\ - decltype(expr) a##_##b##_##c = expr;\ - auto &a = std::get<0>(a##_##b##_##c);\ - auto &b = std::get<1>(a##_##b##_##c);\ - auto &c = std::get<2>(a##_##b##_##c) +#define TUPLE_3D(a, b, c, expr) \ + decltype(expr) a##_##b##_##c = expr; \ + auto &a = std::get<0>(a##_##b##_##c); \ + auto &b = std::get<1>(a##_##b##_##c); \ + auto &c = std::get<2>(a##_##b##_##c) -#define TUPLE_3D_REF(a,b,c, expr)\ - auto &a##_##b##_##c = expr;\ - auto &a = std::get<0>(a##_##b##_##c);\ - auto &b = std::get<1>(a##_##b##_##c);\ - auto &c = std::get<2>(a##_##b##_##c) +#define TUPLE_3D_REF(a, b, c, expr) \ + auto &a##_##b##_##c = expr; \ + auto &a = std::get<0>(a##_##b##_##c); \ + auto &b = std::get<1>(a##_##b##_##c); \ + auto &c = std::get<2>(a##_##b##_##c) namespace util { -template class X, class...Y> +template class X, class... Y> struct __instantiation_of : public std::false_type {}; template class X, class... Y> struct __instantiation_of> : public std::true_type {}; -template class X, class T, class...Y> +template class X, class T, class... Y> static constexpr auto instantiation_of_v = __instantiation_of::value; template @@ -76,14 +80,16 @@ struct __either { template using either_t = typename __either::type; -template struct overloaded : Ts... { using Ts::operator()...; }; -template overloaded(Ts...) -> overloaded; +template +struct overloaded : Ts... { using Ts::operator()...; }; +template +overloaded(Ts...) -> overloaded; template class FailGuard { public: FailGuard() = delete; - FailGuard(T && f) noexcept : _func { std::forward(f) } {} + FailGuard(T &&f) noexcept : _func { std::forward(f) } {} FailGuard(FailGuard &&other) noexcept : _func { std::move(other._func) } { this->failure = other.failure; @@ -103,12 +109,13 @@ public: void disable() { failure = false; } bool failure { true }; + private: T _func; }; template -[[nodiscard]] auto fail_guard(T && f) { +[[nodiscard]] auto fail_guard(T &&f) { return FailGuard { std::forward(f) }; } @@ -118,9 +125,9 @@ void append_struct(std::vector &buf, const T &_struct) { buf.reserve(data_len); - auto *data = (uint8_t *) & _struct; + auto *data = (uint8_t *)&_struct; - for (size_t x = 0; x < data_len; ++x) { + for(size_t x = 0; x < data_len; ++x) { buf.push_back(data[x]); } } @@ -129,24 +136,26 @@ template class Hex { public: typedef T elem_type; + private: const char _bits[16] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; char _hex[sizeof(elem_type) * 2]; + public: Hex(const elem_type &elem, bool rev) { if(!rev) { const uint8_t *data = reinterpret_cast(&elem) + sizeof(elem_type) - 1; - for (auto it = begin(); it < cend();) { + for(auto it = begin(); it < cend();) { *it++ = _bits[*data / 16]; *it++ = _bits[*data-- % 16]; } } else { const uint8_t *data = reinterpret_cast(&elem); - for (auto it = begin(); it < cend();) { + for(auto it = begin(); it < cend();) { *it++ = _bits[*data / 16]; *it++ = _bits[*data++ % 16]; } @@ -178,7 +187,7 @@ Hex hex(const T &elem, bool rev = false) { template std::string hex_vec(It begin, It end, bool rev = false) { - auto str_size = 2*std::distance(begin, end); + auto str_size = 2 * std::distance(begin, end); std::string hex; @@ -189,14 +198,14 @@ std::string hex_vec(It begin, It end, bool rev = false) { }; if(rev) { - for (auto it = std::begin(hex); it < std::end(hex);) { + for(auto it = std::begin(hex); it < std::end(hex);) { *it++ = _bits[((uint8_t)*begin) / 16]; *it++ = _bits[((uint8_t)*begin++) % 16]; } } else { --end; - for (auto it = std::begin(hex); it < std::end(hex);) { + for(auto it = std::begin(hex); it < std::end(hex);) { *it++ = _bits[((uint8_t)*end) / 16]; *it++ = _bits[((uint8_t)*end--) % 16]; } @@ -207,7 +216,7 @@ std::string hex_vec(It begin, It end, bool rev = false) { } template -std::string hex_vec(C&& c, bool rev = false) { +std::string hex_vec(C &&c, bool rev = false) { return hex_vec(std::begin(c), std::end(c), rev); } @@ -216,7 +225,7 @@ std::optional from_hex(const std::string_view &hex, bool rev = false) { std::uint8_t buf[sizeof(T)]; static char constexpr shift_bit = 'a' - 'A'; - auto is_convertable = [] (char ch) -> bool { + auto is_convertable = [](char ch) -> bool { if(isdigit(ch)) { return true; } @@ -235,9 +244,9 @@ std::optional from_hex(const std::string_view &hex, bool rev = false) { return std::nullopt; } - const char *data = hex.data() + hex.size() -1; + const char *data = hex.data() + hex.size() - 1; - auto convert = [] (char ch) -> std::uint8_t { + auto convert = [](char ch) -> std::uint8_t { if(ch >= '0' && ch <= '9') { return (std::uint8_t)ch - '0'; } @@ -266,7 +275,7 @@ inline std::string from_hex_vec(const std::string &hex, bool rev = false) { std::string buf; static char constexpr shift_bit = 'a' - 'A'; - auto is_convertable = [] (char ch) -> bool { + auto is_convertable = [](char ch) -> bool { if(isdigit(ch)) { return true; } @@ -283,9 +292,9 @@ inline std::string from_hex_vec(const std::string &hex, bool rev = false) { auto buf_size = std::count_if(std::begin(hex), std::end(hex), is_convertable) / 2; buf.resize(buf_size); - const char *data = hex.data() + hex.size() -1; + const char *data = hex.data() + hex.size() - 1; - auto convert = [] (char ch) -> std::uint8_t { + auto convert = [](char ch) -> std::uint8_t { if(ch >= '0' && ch <= '9') { return (std::uint8_t)ch - '0'; } @@ -317,18 +326,18 @@ public: std::size_t operator()(const value_type &value) const { const auto *p = reinterpret_cast(&value); - return std::hash{}(std::string_view { p, sizeof(value_type) }); + return std::hash {}(std::string_view { p, sizeof(value_type) }); } }; template -auto enm(const T& val) -> const std::underlying_type_t& { - return *reinterpret_cast*>(&val); +auto enm(const T &val) -> const std::underlying_type_t & { + return *reinterpret_cast *>(&val); } template -auto enm(T& val) -> std::underlying_type_t& { - return *reinterpret_cast*>(&val); +auto enm(T &val) -> std::underlying_type_t & { + return *reinterpret_cast *>(&val); } inline std::int64_t from_chars(const char *begin, const char *end) { @@ -377,11 +386,11 @@ public: }; // Compared to std::unique_ptr, it adds the ability to get the address of the pointer itself -template > +template> class uniq_ptr { public: using element_type = T; - using pointer = element_type*; + using pointer = element_type *; using deleter_type = D; constexpr uniq_ptr() noexcept : _p { nullptr } {} @@ -436,7 +445,7 @@ public: pointer release() { auto tmp = _p; - _p = nullptr; + _p = nullptr; return tmp; } @@ -468,69 +477,70 @@ public: return &_p; } - deleter_type& get_deleter() { + deleter_type &get_deleter() { return _deleter; } - const deleter_type& get_deleter() const { + const deleter_type &get_deleter() const { return _deleter; } explicit operator bool() const { return _p != nullptr; } + protected: pointer _p; deleter_type _deleter; }; template -bool operator==(const uniq_ptr& x, const uniq_ptr& y) { +bool operator==(const uniq_ptr &x, const uniq_ptr &y) { return x.get() == y.get(); } template -bool operator!=(const uniq_ptr& x, const uniq_ptr& y) { +bool operator!=(const uniq_ptr &x, const uniq_ptr &y) { return x.get() != y.get(); } template -bool operator==(const std::unique_ptr& x, const uniq_ptr& y) { +bool operator==(const std::unique_ptr &x, const uniq_ptr &y) { return x.get() == y.get(); } template -bool operator!=(const std::unique_ptr& x, const uniq_ptr& y) { +bool operator!=(const std::unique_ptr &x, const uniq_ptr &y) { return x.get() != y.get(); } template -bool operator==(const uniq_ptr& x, const std::unique_ptr& y) { +bool operator==(const uniq_ptr &x, const std::unique_ptr &y) { return x.get() == y.get(); } template -bool operator!=(const uniq_ptr& x, const std::unique_ptr& y) { +bool operator!=(const uniq_ptr &x, const std::unique_ptr &y) { return x.get() != y.get(); } template -bool operator==(const uniq_ptr& x, std::nullptr_t) { +bool operator==(const uniq_ptr &x, std::nullptr_t) { return !(bool)x; } template -bool operator!=(const uniq_ptr& x, std::nullptr_t) { +bool operator!=(const uniq_ptr &x, std::nullptr_t) { return (bool)x; } template -bool operator==(std::nullptr_t, const uniq_ptr& y) { +bool operator==(std::nullptr_t, const uniq_ptr &y) { return !(bool)y; } template -bool operator!=(std::nullptr_t, const uniq_ptr& y) { +bool operator!=(std::nullptr_t, const uniq_ptr &y) { return (bool)y; } @@ -538,8 +548,8 @@ template class wrap_ptr { public: using element_type = T; - using pointer = element_type*; - using reference = element_type&; + using pointer = element_type *; + using reference = element_type &; wrap_ptr() : _own_ptr { false }, _p { nullptr } {} wrap_ptr(pointer p) : _own_ptr { false }, _p { p } {} @@ -555,7 +565,7 @@ public: _p = other._p; - _own_ptr = other._own_ptr; + _own_ptr = other._own_ptr; other._own_ptr = false; return *this; @@ -565,7 +575,7 @@ public: wrap_ptr &operator=(std::unique_ptr &&uniq_ptr) { static_assert(std::is_base_of_v, "element_type must be base class of V"); _own_ptr = true; - _p = uniq_ptr.release(); + _p = uniq_ptr.release(); return *this; } @@ -575,7 +585,7 @@ public: delete _p; } - _p = p; + _p = p; _own_ptr = false; return *this; @@ -617,13 +627,11 @@ struct __false_v>> { template struct __false_v || - instantiation_of_v || - instantiation_of_v || - instantiation_of_v - ) - >> { + ( + std::is_pointer_v || + instantiation_of_v || + instantiation_of_v || + instantiation_of_v)>> { static constexpr std::nullptr_t value = nullptr; }; @@ -638,18 +646,18 @@ static constexpr auto false_v = __false_v::value; template using optional_t = either_t< (std::is_same_v || - instantiation_of_v || - instantiation_of_v || - instantiation_of_v || - std::is_pointer_v), + instantiation_of_v || + instantiation_of_v || + instantiation_of_v || + std::is_pointer_v), T, std::optional>; template class buffer_t { public: buffer_t() : _els { 0 } {}; - buffer_t(buffer_t&&) noexcept = default; - buffer_t &operator=(buffer_t&& other) noexcept = default; + buffer_t(buffer_t &&) noexcept = default; + buffer_t &operator=(buffer_t &&other) noexcept = default; explicit buffer_t(size_t elements) : _els { elements }, _buf { std::make_unique(elements) } {} explicit buffer_t(size_t elements, const T &t) : _els { elements }, _buf { std::make_unique(elements) } { @@ -703,7 +711,7 @@ T either(std::optional &&l, T &&r) { return std::forward(r); } -template +template struct Function { typedef ReturnType (*type)(Args...); }; @@ -711,18 +719,18 @@ struct Function { template::type function> struct Destroy { typedef T pointer; - + void operator()(pointer p) { function(p); } }; -template::type function> -using safe_ptr = uniq_ptr>; +template::type function> +using safe_ptr = uniq_ptr>; // You cannot specialize an alias -template::type function> -using safe_ptr_v2 = uniq_ptr>; +template::type function> +using safe_ptr_v2 = uniq_ptr>; template void c_free(T *p) { @@ -737,22 +745,22 @@ template struct endianness { enum : bool { #if defined(__BYTE_ORDER) && __BYTE_ORDER == __BIG_ENDIAN || \ - defined(__BIG_ENDIAN__) || \ - defined(__ARMEB__) || \ - defined(__THUMBEB__) || \ - defined(__AARCH64EB__) || \ - defined(_MIBSEB) || defined(__MIBSEB) || defined(__MIBSEB__) + defined(__BIG_ENDIAN__) || \ + defined(__ARMEB__) || \ + defined(__THUMBEB__) || \ + defined(__AARCH64EB__) || \ + defined(_MIBSEB) || defined(__MIBSEB) || defined(__MIBSEB__) // It's a big-endian target architecture little = false, #elif defined(__BYTE_ORDER) && __BYTE_ORDER == __LITTLE_ENDIAN || \ - defined(__LITTLE_ENDIAN__) || \ - defined(__ARMEL__) || \ - defined(__THUMBEL__) || \ - defined(__AARCH64EL__) || \ - defined(_MIPSEL) || defined(__MIPSEL) || defined(__MIPSEL__) || \ - defined(_WIN32) + defined(__LITTLE_ENDIAN__) || \ + defined(__ARMEL__) || \ + defined(__THUMBEL__) || \ + defined(__AARCH64EL__) || \ + defined(_MIPSEL) || defined(__MIPSEL) || defined(__MIPSEL__) || \ + defined(_WIN32) // It's a little-endian target architecture - little = true, + little = true, #else #error "Unknown Endianness" #endif @@ -761,15 +769,14 @@ struct endianness { }; template -struct endian_helper { }; +struct endian_helper {}; template struct endian_helper) ->> { + !(instantiation_of_v)>> { static inline T big(T x) { - if constexpr (endianness::little) { - uint8_t *data = reinterpret_cast(&x); + if constexpr(endianness::little) { + uint8_t *data = reinterpret_cast(&x); std::reverse(data, data + sizeof(x)); } @@ -778,8 +785,8 @@ struct endian_helper::big) { - uint8_t *data = reinterpret_cast(&x); + if constexpr(endianness::big) { + uint8_t *data = reinterpret_cast(&x); std::reverse(data, data + sizeof(x)); } @@ -790,32 +797,31 @@ struct endian_helper struct endian_helper ->> { -static inline T little(T x) { - if(!x) return x; + instantiation_of_v>> { + static inline T little(T x) { + if(!x) return x; - if constexpr (endianness::big) { - auto *data = reinterpret_cast(&*x); + if constexpr(endianness::big) { + auto *data = reinterpret_cast(&*x); - std::reverse(data, data + sizeof(*x)); + std::reverse(data, data + sizeof(*x)); + } + + return x; } - return x; -} + static inline T big(T x) { + if(!x) return x; -static inline T big(T x) { - if(!x) return x; + if constexpr(endianness::big) { + auto *data = reinterpret_cast(&*x); - if constexpr (endianness::big) { - auto *data = reinterpret_cast(&*x); + std::reverse(data, data + sizeof(*x)); + } - std::reverse(data, data + sizeof(*x)); + return x; } - - return x; -} }; template @@ -823,7 +829,7 @@ inline auto little(T x) { return endian_helper::little(x); } template inline auto big(T x) { return endian_helper::big(x); } -} /* endian */ +} // namespace endian -} /* util */ +} // namespace util #endif diff --git a/sunshine/uuid.h b/sunshine/uuid.h index 01fad1d6..6d8abe80 100644 --- a/sunshine/uuid.h +++ b/sunshine/uuid.h @@ -18,12 +18,12 @@ union uuid_t { std::uniform_int_distribution dist(0, std::numeric_limits::max()); uuid_t buf; - for (auto &el : buf.b8) { + for(auto &el : buf.b8) { el = dist(engine); } - buf.b8[7] &= (std::uint8_t) 0b00101111; - buf.b8[9] &= (std::uint8_t) 0b10011111; + buf.b8[7] &= (std::uint8_t)0b00101111; + buf.b8[9] &= (std::uint8_t)0b10011111; return buf; } @@ -31,7 +31,7 @@ union uuid_t { static uuid_t generate() { std::random_device r; - std::default_random_engine engine{r()}; + std::default_random_engine engine { r() }; return generate(engine); } @@ -41,7 +41,7 @@ union uuid_t { result.reserve(sizeof(uuid_t) * 2 + 4); - auto hex = util::hex(*this, true); + auto hex = util::hex(*this, true); auto hex_view = hex.to_string_view(); std::string_view slices[] = { @@ -75,5 +75,5 @@ union uuid_t { return (b64[0] > other.b64[0] || (b64[0] == other.b64[0] && b64[1] > other.b64[1])); } }; -} +} // namespace util #endif //T_MAN_UUID_H diff --git a/sunshine/video.cpp b/sunshine/video.cpp index 3127ccbc..1069d0e6 100644 --- a/sunshine/video.cpp +++ b/sunshine/video.cpp @@ -3,19 +3,19 @@ // #include -#include #include +#include extern "C" { #include } +#include "config.h" +#include "main.h" #include "platform/common.h" #include "round_robin.h" #include "sync.h" -#include "config.h" #include "video.h" -#include "main.h" #ifdef _WIN32 extern "C" { @@ -52,7 +52,7 @@ enum class profile_hevc_e : int { main_10, rext, }; -} +} // namespace nv using ctx_t = util::safe_ptr; using frame_t = util::safe_ptr; @@ -81,7 +81,7 @@ public: img.row_pitch, 0 }; - int ret = sws_scale(sws.get(), (std::uint8_t*const*)&img.data, linesizes, 0, img.height, frame->data, frame->linesize); + int ret = sws_scale(sws.get(), (std::uint8_t *const *)&img.data, linesizes, 0, img.height, frame->data, frame->linesize); if(ret <= 0) { BOOST_LOG(fatal) << "Couldn't convert image to required format and/or size"sv; @@ -94,9 +94,8 @@ public: virtual void set_colorspace(std::uint32_t colorspace, std::uint32_t color_range) { sws_setColorspaceDetails(sws.get(), sws_getCoefficients(SWS_CS_DEFAULT), 0, - sws_getCoefficients(colorspace), color_range -1, - 0, 1 << 16, 1 << 16 - ); + sws_getCoefficients(colorspace), color_range - 1, + 0, 1 << 16, 1 << 16); } int init(int in_width, int in_height, int out_width, int out_height, AVFrame *frame, AVPixelFormat format) { @@ -104,8 +103,7 @@ public: in_width, in_height, AV_PIX_FMT_BGR0, out_width, out_height, format, SWS_LANCZOS | SWS_ACCURATE_RND, - nullptr, nullptr, nullptr - )); + nullptr, nullptr, nullptr)); data = frame; return sws ? 0 : -1; @@ -119,8 +117,8 @@ public: struct encoder_t { std::string_view name; enum flag_e { - PASSED, // Is supported - REF_FRAMES_RESTRICT, // Set maximum reference frames + PASSED, // Is supported + REF_FRAMES_RESTRICT, // Set maximum reference frames REF_FRAMES_AUTOSELECT, // Allow encoder to select maximum reference frames (If !REF_FRAMES_RESTRICT --> REF_FRAMES_AUTOSELECT) DYNAMIC_RANGE, MAX_FLAGS @@ -131,9 +129,9 @@ struct encoder_t { option_t(const option_t &) = default; std::string name; - std::variant*, std::string, std::string*> value; + std::variant *, std::string, std::string *> value; - option_t(std::string &&name, decltype(value) &&value) : name { std::move(name) }, value { std::move(value) } {} + option_t(std::string &&name, decltype(value) &&value) : name { std::move(name) }, value { std::move(value) } {} }; struct { @@ -167,24 +165,22 @@ struct encoder_t { bool system_memory; bool hevc_mode; - std::function img_to_frame; + std::function img_to_frame; std::function(platf::hwdevice_t *hwdevice)> make_hwdevice_ctx; }; class session_t { public: session_t() = default; - session_t(ctx_t &&ctx, frame_t &&frame, util::wrap_ptr &&device) : - ctx { std::move(ctx) }, frame { std::move(frame) }, device { std::move(device) } {} + session_t(ctx_t &&ctx, frame_t &&frame, util::wrap_ptr &&device) : ctx { std::move(ctx) }, frame { std::move(frame) }, device { std::move(device) } {} - session_t(session_t &&other) noexcept : - ctx { std::move(other.ctx) }, frame { std::move(other.frame) }, device { std::move(other.device) } {} + session_t(session_t &&other) noexcept : ctx { std::move(other.ctx) }, frame { std::move(other.frame) }, device { std::move(other.device) } {} // Ensure objects are destroyed in the correct order session_t &operator=(session_t &&other) { device = std::move(other.device); - frame = std::move(other.frame); - ctx = std::move(other.ctx); + frame = std::move(other.frame); + ctx = std::move(other.ctx); return *this; } @@ -207,7 +203,7 @@ struct sync_session_ctx_t { struct sync_session_t { sync_session_ctx_t *ctx; - + std::chrono::steady_clock::time_point next_frame; std::chrono::nanoseconds delay; @@ -217,7 +213,7 @@ struct sync_session_t { }; using encode_session_ctx_queue_t = safe::queue_t; -using encode_e = platf::capture_e; +using encode_e = platf::capture_e; struct capture_ctx_t { img_event_t images; @@ -244,7 +240,7 @@ void end_capture_async(capture_thread_async_ctx_t &ctx); // Keep a reference counter to ensure the capture thread only runs when other threads have a reference to the capture thread auto capture_thread_async = safe::make_shared(start_capture_async, end_capture_async); -auto capture_thread_sync = safe::make_shared(start_capture_sync, end_capture_sync); +auto capture_thread_sync = safe::make_shared(start_capture_sync, end_capture_sync); #ifdef _WIN32 static encoder_t nvenc { @@ -254,26 +250,21 @@ static encoder_t nvenc { AV_PIX_FMT_D3D11, AV_PIX_FMT_NV12, AV_PIX_FMT_P010, { - { - { "forced-idr"s, 1 }, + { { "forced-idr"s, 1 }, { "zerolatency"s, 1 }, { "preset"s, &config::video.nv.preset }, - { "rc"s, &config::video.nv.rc } - }, - std::nullopt, std::nullopt, + { "rc"s, &config::video.nv.rc } }, + std::nullopt, + std::nullopt, "hevc_nvenc"s, }, - { - { - { "forced-idr"s, 1 }, + { { { "forced-idr"s, 1 }, { "zerolatency"s, 1 }, { "preset"s, &config::video.nv.preset }, { "rc"s, &config::video.nv.rc }, - { "coder"s, &config::video.nv.coder } - }, - std::nullopt, std::make_optional({"qp"s, &config::video.qp}), - "h264_nvenc"s - }, + { "coder"s, &config::video.nv.coder } }, + std::nullopt, std::make_optional({ "qp"s, &config::video.qp }), + "h264_nvenc"s }, false, true, @@ -288,26 +279,23 @@ static encoder_t amdvce { AV_PIX_FMT_D3D11, AV_PIX_FMT_NV12, AV_PIX_FMT_P010, { - { - { "header_insertion_mode"s, "idr"s }, + { { "header_insertion_mode"s, "idr"s }, { "gops_per_idr"s, 30 }, { "usage"s, "ultralowlatency"s }, { "quality"s, &config::video.amd.quality }, - { "rc"s, &config::video.amd.rc } - }, - std::nullopt, std::make_optional({"qp"s, &config::video.qp}), + { "rc"s, &config::video.amd.rc } }, + std::nullopt, + std::make_optional({ "qp"s, &config::video.qp }), "hevc_amf"s, }, - { - { + { { { "usage"s, "ultralowlatency"s }, { "quality"s, &config::video.amd.quality }, { "rc"s, &config::video.amd.rc }, - {"log_to_dbg"s,"1"s}, + { "log_to_dbg"s, "1"s }, }, - std::nullopt, std::make_optional({"qp"s, &config::video.qp}), - "h264_amf"s - }, + std::nullopt, std::make_optional({ "qp"s, &config::video.qp }), + "h264_amf"s }, false, true, @@ -322,27 +310,20 @@ static encoder_t software { AV_HWDEVICE_TYPE_NONE, AV_PIX_FMT_NONE, AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV420P10, - { - // x265's Info SEI is so long that it causes the IDR picture data to be + { // x265's Info SEI is so long that it causes the IDR picture data to be // kicked to the 2nd packet in the frame, breaking Moonlight's parsing logic. // It also looks like gop_size isn't passed on to x265, so we have to set // 'keyint=-1' in the parameters ourselves. { { "x265-params"s, "info=0:keyint=-1"s }, { "preset"s, &config::video.sw.preset }, - { "tune"s, &config::video.sw.tune } - }, + { "tune"s, &config::video.sw.tune } }, std::make_optional("crf"s, &config::video.crf), std::make_optional("qp"s, &config::video.qp), - "libx265"s - }, - { - { - { "preset"s, &config::video.sw.preset }, - { "tune"s, &config::video.sw.tune } - }, + "libx265"s }, + { { { "preset"s, &config::video.sw.preset }, + { "tune"s, &config::video.sw.tune } }, std::make_optional("crf"s, &config::video.crf), std::make_optional("qp"s, &config::video.qp), - "libx264"s - }, + "libx264"s }, true, false, @@ -366,7 +347,7 @@ void reset_display(std::shared_ptr &disp, AVHWDeviceType type) if(disp) { break; } - + std::this_thread::sleep_for(200ms); } } @@ -375,8 +356,7 @@ void captureThread( std::shared_ptr> capture_ctx_queue, util::sync_t> &display_wp, safe::signal_t &reinit_event, - const encoder_t &encoder - ) { + const encoder_t &encoder) { std::vector capture_ctxs; auto fg = util::fail_guard([&]() { @@ -410,7 +390,7 @@ void captureThread( } } - if(auto capture_ctx = capture_ctx_queue->pop()) { + if(auto capture_ctx = capture_ctx_queue->pop()) { capture_ctxs.emplace_back(std::move(*capture_ctx)); delay = capture_ctxs.back().delay; @@ -430,64 +410,64 @@ void captureThread( while(img.use_count() > 1) {} auto status = disp->snapshot(img.get(), 1000ms, display_cursor); - switch (status) { - case platf::capture_e::reinit: { - reinit_event.raise(true); + switch(status) { + case platf::capture_e::reinit: { + reinit_event.raise(true); - // Some classes of images contain references to the display --> display won't delete unless img is deleted - for(auto &img : imgs) { - img.reset(); + // Some classes of images contain references to the display --> display won't delete unless img is deleted + for(auto &img : imgs) { + img.reset(); + } + + // Some classes of display cannot have multiple instances at once + disp.reset(); + + // display_wp is modified in this thread only + while(!display_wp->expired()) { + std::this_thread::sleep_for(100ms); + } + + while(capture_ctx_queue->running()) { + reset_display(disp, encoder.dev_type); + + if(disp) { + break; } + std::this_thread::sleep_for(200ms); + } + if(!disp) { + return; + } - // Some classes of display cannot have multiple instances at once - disp.reset(); - - // display_wp is modified in this thread only - while(!display_wp->expired()) { - std::this_thread::sleep_for(100ms); - } - - while(capture_ctx_queue->running()) { - reset_display(disp, encoder.dev_type); - - if(disp) { - break; - } - std::this_thread::sleep_for(200ms); - } - if(!disp) { + display_wp = disp; + // Re-allocate images + for(auto &img : imgs) { + img = disp->alloc_img(); + if(!img) { + BOOST_LOG(error) << "Couldn't initialize an image"sv; return; } - - display_wp = disp; - // Re-allocate images - for(auto &img : imgs) { - img = disp->alloc_img(); - if(!img) { - BOOST_LOG(error) << "Couldn't initialize an image"sv; - return; - } - } - - reinit_event.reset(); - continue; } - case platf::capture_e::error: - return; - case platf::capture_e::timeout: - std::this_thread::sleep_for(1ms); - continue; - case platf::capture_e::ok: - break; - default: - BOOST_LOG(error) << "Unrecognized capture status ["sv << (int)status << ']'; - return; + + reinit_event.reset(); + continue; + } + case platf::capture_e::error: + return; + case platf::capture_e::timeout: + std::this_thread::sleep_for(1ms); + continue; + case platf::capture_e::ok: + break; + default: + BOOST_LOG(error) << "Unrecognized capture status ["sv << (int)status << ']'; + return; } KITTY_WHILE_LOOP(auto capture_ctx = std::begin(capture_ctxs), capture_ctx != std::end(capture_ctxs), { if(!capture_ctx->images->running()) { auto tmp_delay = capture_ctx->delay; - capture_ctx = capture_ctxs.erase(capture_ctx); + capture_ctx = capture_ctxs.erase(capture_ctx); if(tmp_delay == delay) { delay = std::min_element(std::begin(capture_ctxs), std::end(capture_ctxs), [](const auto &l, const auto &r) { @@ -513,21 +493,21 @@ int encode(int64_t frame_nr, ctx_t &ctx, frame_t &frame, packet_queue_t &packets /* send the frame to the encoder */ auto ret = avcodec_send_frame(ctx.get(), frame.get()); - if (ret < 0) { - char err_str[AV_ERROR_MAX_STRING_SIZE] {0}; + if(ret < 0) { + char err_str[AV_ERROR_MAX_STRING_SIZE] { 0 }; BOOST_LOG(error) << "Could not send a frame for encoding: "sv << av_make_error_string(err_str, AV_ERROR_MAX_STRING_SIZE, ret); return -1; } - while (ret >= 0) { + while(ret >= 0) { auto packet = std::make_unique(nullptr); ret = avcodec_receive_packet(ctx.get(), packet.get()); - if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { + if(ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { return 0; } - else if (ret < 0) { + else if(ret < 0) { return ret; } @@ -543,7 +523,7 @@ std::optional make_session(const encoder_t &encoder, const config_t & auto &video_format = config.videoFormat == 0 ? encoder.h264 : encoder.hevc; if(!video_format[encoder_t::PASSED]) { - BOOST_LOG(error) << encoder.name << ": "sv << video_format.name << " mode not supported"sv; + BOOST_LOG(error) << encoder.name << ": "sv << video_format.name << " mode not supported"sv; return std::nullopt; } @@ -560,10 +540,10 @@ std::optional make_session(const encoder_t &encoder, const config_t & } ctx_t ctx { avcodec_alloc_context3(codec) }; - ctx->width = config.width; - ctx->height = config.height; - ctx->time_base = AVRational{1, config.framerate}; - ctx->framerate = AVRational{config.framerate, 1}; + ctx->width = config.width; + ctx->height = config.height; + ctx->time_base = AVRational { 1, config.framerate }; + ctx->framerate = AVRational { config.framerate, 1 }; if(config.videoFormat == 0) { ctx->profile = encoder.profile.h264_high; @@ -579,7 +559,7 @@ std::optional make_session(const encoder_t &encoder, const config_t & ctx->max_b_frames = 0; // Use an infinite GOP length since I-frames are generated on demand - ctx->gop_size = std::numeric_limits::max(); + ctx->gop_size = std::numeric_limits::max(); ctx->keyint_min = ctx->gop_size; if(config.numRefFrames == 0) { @@ -596,34 +576,34 @@ std::optional make_session(const encoder_t &encoder, const config_t & ctx->color_range = (config.encoderCscMode & 0x1) ? AVCOL_RANGE_JPEG : AVCOL_RANGE_MPEG; int sws_color_space; - switch (config.encoderCscMode >> 1) { - case 0: - default: - // Rec. 601 - BOOST_LOG(info) << "Color coding [Rec. 601]"sv; - ctx->color_primaries = AVCOL_PRI_SMPTE170M; - ctx->color_trc = AVCOL_TRC_SMPTE170M; - ctx->colorspace = AVCOL_SPC_SMPTE170M; - sws_color_space = SWS_CS_SMPTE170M; - break; + switch(config.encoderCscMode >> 1) { + case 0: + default: + // Rec. 601 + BOOST_LOG(info) << "Color coding [Rec. 601]"sv; + ctx->color_primaries = AVCOL_PRI_SMPTE170M; + ctx->color_trc = AVCOL_TRC_SMPTE170M; + ctx->colorspace = AVCOL_SPC_SMPTE170M; + sws_color_space = SWS_CS_SMPTE170M; + break; - case 1: - // Rec. 709 - BOOST_LOG(info) << "Color coding [Rec. 709]"sv; - ctx->color_primaries = AVCOL_PRI_BT709; - ctx->color_trc = AVCOL_TRC_BT709; - ctx->colorspace = AVCOL_SPC_BT709; - sws_color_space = SWS_CS_ITU709; - break; + case 1: + // Rec. 709 + BOOST_LOG(info) << "Color coding [Rec. 709]"sv; + ctx->color_primaries = AVCOL_PRI_BT709; + ctx->color_trc = AVCOL_TRC_BT709; + ctx->colorspace = AVCOL_SPC_BT709; + sws_color_space = SWS_CS_ITU709; + break; - case 2: - // Rec. 2020 - BOOST_LOG(info) << "Color coding [Rec. 2020]"sv; - ctx->color_primaries = AVCOL_PRI_BT2020; - ctx->color_trc = AVCOL_TRC_BT2020_10; - ctx->colorspace = AVCOL_SPC_BT2020_NCL; - sws_color_space = SWS_CS_BT2020; - break; + case 2: + // Rec. 2020 + BOOST_LOG(info) << "Color coding [Rec. 2020]"sv; + ctx->color_primaries = AVCOL_PRI_BT2020; + ctx->color_trc = AVCOL_TRC_BT2020_10; + ctx->colorspace = AVCOL_SPC_BT2020_NCL; + sws_color_space = SWS_CS_BT2020; + break; } BOOST_LOG(info) << "Color range: ["sv << ((config.encoderCscMode & 0x1) ? "JPEG"sv : "MPEG"sv) << ']'; @@ -660,18 +640,19 @@ std::optional make_session(const encoder_t &encoder, const config_t & ctx->slices = std::max(config.slicesPerFrame, config::video.min_threads); } - ctx->thread_type = FF_THREAD_SLICE; + ctx->thread_type = FF_THREAD_SLICE; ctx->thread_count = ctx->slices; - AVDictionary *options {nullptr}; + AVDictionary *options { nullptr }; auto handle_option = [&options](const encoder_t::option_t &option) { - std::visit(util::overloaded { - [&](int v) { av_dict_set_int(&options, option.name.c_str(), v, 0); }, - [&](int *v) { av_dict_set_int(&options, option.name.c_str(), *v, 0); }, - [&](std::optional *v) { if(*v) av_dict_set_int(&options, option.name.c_str(), **v, 0); }, - [&](const std::string &v) { av_dict_set(&options, option.name.c_str(), v.c_str(), 0); }, - [&](std::string *v) { if(!v->empty()) av_dict_set(&options, option.name.c_str(), v->c_str(), 0); } - }, option.value); + std::visit( + util::overloaded { + [&](int v) { av_dict_set_int(&options, option.name.c_str(), v, 0); }, + [&](int *v) { av_dict_set_int(&options, option.name.c_str(), *v, 0); }, + [&](std::optional *v) { if(*v) av_dict_set_int(&options, option.name.c_str(), **v, 0); }, + [&](const std::string &v) { av_dict_set(&options, option.name.c_str(), v.c_str(), 0); }, + [&](std::string *v) { if(!v->empty()) av_dict_set(&options, option.name.c_str(), v->c_str(), 0); } }, + option.value); }; for(auto &option : video_format.options) { @@ -679,11 +660,11 @@ std::optional make_session(const encoder_t &encoder, const config_t & } if(config.bitrate > 500) { - auto bitrate = config.bitrate * 1000; - ctx->rc_max_rate = bitrate; + auto bitrate = config.bitrate * 1000; + ctx->rc_max_rate = bitrate; ctx->rc_buffer_size = bitrate / config.framerate; - ctx->bit_rate = bitrate; - ctx->rc_min_rate = bitrate; + ctx->bit_rate = bitrate; + ctx->rc_min_rate = bitrate; } else if(video_format.crf && config::video.crf != 0) { handle_option(*video_format.crf); @@ -698,7 +679,7 @@ std::optional make_session(const encoder_t &encoder, const config_t & avcodec_open2(ctx.get(), codec, &options); - frame_t frame {av_frame_alloc() }; + frame_t frame { av_frame_alloc() }; frame->format = ctx->pix_fmt; frame->width = ctx->width; frame->height = ctx->height; @@ -730,13 +711,12 @@ std::optional make_session(const encoder_t &encoder, const config_t & return std::make_optional(session_t { std::move(ctx), std::move(frame), - std::move(device) - }); + std::move(device) }); } void encode_run( int &frame_nr, int &key_frame_nr, // Store progress of the frame number - safe::signal_t* shutdown_event, // Signal for shutdown event of the session + safe::signal_t *shutdown_event, // Signal for shutdown event of the session packet_queue_t packets, idr_event_t idr_events, img_event_t images, @@ -763,13 +743,13 @@ void encode_run( if(idr_events->peek()) { session->frame->pict_type = AV_PICTURE_TYPE_I; session->frame->key_frame = 1; - auto event = idr_events->pop(); + auto event = idr_events->pop(); if(!event) { return; } - auto end = event->second; - frame_nr = end; + auto end = event->second; + frame_nr = end; key_frame_nr = end + config.framerate; } else if(frame_nr == key_frame_nr) { @@ -794,7 +774,7 @@ void encode_run( break; } } - + if(encode(frame_nr++, session->ctx, session->frame, packets, channel_data)) { BOOST_LOG(error) << "Could not encode video packet"sv; return; @@ -808,12 +788,12 @@ void encode_run( std::optional make_synced_session(platf::display_t *disp, const encoder_t &encoder, platf::img_t &img, sync_session_ctx_t &ctx) { sync_session_t encode_session; - encode_session.ctx = &ctx; + encode_session.ctx = &ctx; encode_session.next_frame = std::chrono::steady_clock::now(); encode_session.delay = std::chrono::nanoseconds { 1s } / ctx.config.framerate; - auto pix_fmt = ctx.config.dynamicRange == 0 ? map_pix_fmt(encoder.static_pix_fmt) : map_pix_fmt(encoder.dynamic_pix_fmt); + auto pix_fmt = ctx.config.dynamicRange == 0 ? map_pix_fmt(encoder.static_pix_fmt) : map_pix_fmt(encoder.dynamic_pix_fmt); auto hwdevice = disp->make_hwdevice(ctx.config.width, ctx.config.height, pix_fmt); if(!hwdevice) { return std::nullopt; @@ -824,9 +804,9 @@ std::optional make_synced_session(platf::display_t *disp, const return std::nullopt; } - encode_session.img_tmp = &img; + encode_session.img_tmp = &img; encode_session.hwdevice = std::move(hwdevice); - encode_session.session = std::move(*session); + encode_session.session = std::move(*session); return std::move(encode_session); } @@ -873,7 +853,7 @@ encode_e encode_run_sync(std::vector> &synce while(encode_session_ctx_queue.running()) { while(encode_session_ctx_queue.peek()) { auto encode_session_ctx = encode_session_ctx_queue.pop(); - if(!encode_session_ctx) { + if(!encode_session_ctx) { return encode_e::ok; } @@ -892,28 +872,28 @@ encode_e encode_run_sync(std::vector> &synce auto delay = std::max(0ms, std::chrono::duration_cast(next_frame - std::chrono::steady_clock::now())); auto status = disp->snapshot(img.get(), delay, display_cursor); - switch(status) { - case platf::capture_e::reinit: - case platf::capture_e::error: - return status; - case platf::capture_e::timeout: - break; - case platf::capture_e::ok: - img_tmp = img.get(); - break; + switch(status) { + case platf::capture_e::reinit: + case platf::capture_e::error: + return status; + case platf::capture_e::timeout: + break; + case platf::capture_e::ok: + img_tmp = img.get(); + break; } - + auto now = std::chrono::steady_clock::now(); - + next_frame = now + 1s; KITTY_WHILE_LOOP(auto pos = std::begin(synced_sessions), pos != std::end(synced_sessions), { auto ctx = pos->ctx; if(ctx->shutdown_event->peek()) { // Let waiting thread know it can delete shutdown_event ctx->join_event->raise(true); - + pos = synced_sessions.erase(pos); - synced_session_ctxs.erase(std::find_if(std::begin(synced_session_ctxs), std::end(synced_session_ctxs), [&ctx_p=ctx](auto &ctx) { + synced_session_ctxs.erase(std::find_if(std::begin(synced_session_ctxs), std::end(synced_session_ctxs), [&ctx_p = ctx](auto &ctx) { return ctx.get() == ctx_p; })); @@ -929,9 +909,9 @@ encode_e encode_run_sync(std::vector> &synce pos->session.frame->key_frame = 1; auto event = ctx->idr_events->pop(); - auto end = event->second; + auto end = event->second; - ctx->frame_nr = end; + ctx->frame_nr = end; ctx->key_frame_nr = end + ctx->config.framerate; } else if(ctx->frame_nr == ctx->key_frame_nr) { @@ -992,7 +972,7 @@ void captureThreadSync() { std::vector> synced_session_ctxs; auto &ctx = ref->encode_session_ctx_queue; - auto lg = util::fail_guard([&]() { + auto lg = util::fail_guard([&]() { ctx.stop(); for(auto &ctx : synced_session_ctxs) { @@ -1006,7 +986,8 @@ void captureThreadSync() { } }); - while(encode_run_sync(synced_session_ctxs, ctx) == encode_e::reinit); + while(encode_run_sync(synced_session_ctxs, ctx) == encode_e::reinit) + ; } void capture_async( @@ -1017,7 +998,7 @@ void capture_async( void *channel_data) { auto images = std::make_shared(); - auto lg = util::fail_guard([&]() { + auto lg = util::fail_guard([&]() { images->stop(); shutdown_event->raise(true); }); @@ -1029,14 +1010,13 @@ void capture_async( auto delay = std::chrono::floor(1s) / config.framerate; ref->capture_ctx_queue->raise(capture_ctx_t { - images, delay - }); + images, delay }); if(!ref->capture_ctx_queue->running()) { return; } - int frame_nr = 1; + int frame_nr = 1; int key_frame_nr = 1; while(!shutdown_event->peek() && images->running()) { @@ -1056,7 +1036,7 @@ void capture_async( display = ref->display_wp->lock(); } - auto pix_fmt = config.dynamicRange == 0 ? platf::pix_fmt_e::yuv420p : platf::pix_fmt_e::yuv420p10; + auto pix_fmt = config.dynamicRange == 0 ? platf::pix_fmt_e::yuv420p : platf::pix_fmt_e::yuv420p10; auto hwdevice = display->make_hwdevice(config.width, config.height, pix_fmt); if(!hwdevice) { return; @@ -1098,8 +1078,7 @@ void capture( safe::signal_t join_event; auto ref = capture_thread_sync.ref(); ref->encode_session_ctx_queue.raise(sync_session_ctx_t { - shutdown_event, &join_event, packets, idr_events, config, 1, 1, channel_data - }); + shutdown_event, &join_event, packets, idr_events, config, 1, 1, channel_data }); // Wait for join signal join_event.view(); @@ -1112,7 +1091,7 @@ bool validate_config(std::shared_ptr &disp, const encoder_t &e return false; } - auto pix_fmt = config.dynamicRange == 0 ? map_pix_fmt(encoder.static_pix_fmt) : map_pix_fmt(encoder.dynamic_pix_fmt); + auto pix_fmt = config.dynamicRange == 0 ? map_pix_fmt(encoder.static_pix_fmt) : map_pix_fmt(encoder.dynamic_pix_fmt); auto hwdevice = disp->make_hwdevice(config.width, config.height, pix_fmt); if(!hwdevice) { return false; @@ -1152,14 +1131,14 @@ bool validate_encoder(encoder_t &encoder) { }); auto force_hevc = config::video.hevc_mode >= 2; - auto test_hevc = force_hevc || (config::video.hevc_mode == 0 && encoder.hevc_mode); + auto test_hevc = force_hevc || (config::video.hevc_mode == 0 && encoder.hevc_mode); encoder.h264.capabilities.set(); encoder.hevc.capabilities.set(); // First, test encoder viability config_t config_max_ref_frames { 1920, 1080, 60, 1000, 1, 1, 1, 0, 0 }; - config_t config_autoselect { 1920, 1080, 60, 1000, 1, 0, 1, 0, 0 }; + config_t config_autoselect { 1920, 1080, 60, 1000, 1, 0, 1, 0, 0 }; auto max_ref_frames_h264 = validate_config(disp, encoder, config_max_ref_frames); auto autoselect_h264 = validate_config(disp, encoder, config_autoselect); @@ -1168,13 +1147,13 @@ bool validate_encoder(encoder_t &encoder) { return false; } - encoder.h264[encoder_t::REF_FRAMES_RESTRICT] = max_ref_frames_h264; + encoder.h264[encoder_t::REF_FRAMES_RESTRICT] = max_ref_frames_h264; encoder.h264[encoder_t::REF_FRAMES_AUTOSELECT] = autoselect_h264; - encoder.h264[encoder_t::PASSED] = true; + encoder.h264[encoder_t::PASSED] = true; if(test_hevc) { config_max_ref_frames.videoFormat = 1; - config_autoselect.videoFormat = 1; + config_autoselect.videoFormat = 1; auto max_ref_frames_hevc = validate_config(disp, encoder, config_max_ref_frames); auto autoselect_hevc = validate_config(disp, encoder, config_autoselect); @@ -1184,7 +1163,7 @@ bool validate_encoder(encoder_t &encoder) { return false; } - encoder.hevc[encoder_t::REF_FRAMES_RESTRICT] = max_ref_frames_hevc; + encoder.hevc[encoder_t::REF_FRAMES_RESTRICT] = max_ref_frames_hevc; encoder.hevc[encoder_t::REF_FRAMES_AUTOSELECT] = autoselect_hevc; } encoder.hevc[encoder_t::PASSED] = test_hevc; @@ -1222,10 +1201,9 @@ int init() { KITTY_WHILE_LOOP(auto pos = std::begin(encoders), pos != std::end(encoders), { if( - (!config::video.encoder.empty() && pos->name != config::video.encoder) || - !validate_encoder(*pos) || - (config::video.hevc_mode == 3 && !pos->hevc[encoder_t::DYNAMIC_RANGE]) - ) { + (!config::video.encoder.empty() && pos->name != config::video.encoder) || + !validate_encoder(*pos) || + (config::video.hevc_mode == 3 && !pos->hevc[encoder_t::DYNAMIC_RANGE])) { pos = encoders.erase(pos); continue; @@ -1235,7 +1213,7 @@ int init() { }) if(encoders.empty()) { - if(config::video.encoder.empty()) { + if(config::video.encoder.empty()) { BOOST_LOG(fatal) << "Couldn't find any encoder"sv; } else { @@ -1258,7 +1236,7 @@ int init() { BOOST_LOG(info) << "Found encoder "sv << encoder.name << ": ["sv << encoder.h264.name << ", "sv << encoder.hevc.name << ']'; } else { - BOOST_LOG(info) << "Found encoder "sv << encoder.name << ": ["sv << encoder.h264.name << ']'; + BOOST_LOG(info) << "Found encoder "sv << encoder.name << ": ["sv << encoder.h264.name << ']'; } if(config::video.hevc_mode == 0) { @@ -1274,12 +1252,12 @@ util::Either make_hwdevice_ctx(AVHWDeviceType type, void *hwdevic int err; if(hwdevice) { ctx.reset(av_hwdevice_ctx_alloc(type)); - ((AVHWDeviceContext*)ctx.get())->hwctx = hwdevice; + ((AVHWDeviceContext *)ctx.get())->hwctx = hwdevice; err = av_hwdevice_ctx_init(ctx.get()); } else { - AVBufferRef *ref {}; + AVBufferRef *ref {}; err = av_hwdevice_ctx_create(&ref, type, nullptr, nullptr, 0); ctx.reset(ref); } @@ -1292,13 +1270,13 @@ util::Either make_hwdevice_ctx(AVHWDeviceType type, void *hwdevic } int hwframe_ctx(ctx_t &ctx, buffer_t &hwdevice, AVPixelFormat format) { - buffer_t frame_ref { av_hwframe_ctx_alloc(hwdevice.get())}; + buffer_t frame_ref { av_hwframe_ctx_alloc(hwdevice.get()) }; - auto frame_ctx = (AVHWFramesContext*)frame_ref->data; - frame_ctx->format = ctx->pix_fmt; - frame_ctx->sw_format = format; - frame_ctx->height = ctx->height; - frame_ctx->width = ctx->width; + auto frame_ctx = (AVHWFramesContext *)frame_ref->data; + frame_ctx->format = ctx->pix_fmt; + frame_ctx->sw_format = format; + frame_ctx->height = ctx->height; + frame_ctx->width = ctx->width; frame_ctx->initial_pool_size = 0; if(auto err = av_hwframe_ctx_init(frame_ref.get()); err < 0) { @@ -1319,22 +1297,22 @@ void sw_img_to_frame(const platf::img_t &img, frame_t &frame) {} namespace platf::dxgi { void lock(void *hwdevice); void unlock(void *hwdevice); -} -void do_nothing(void*) {} +} // namespace platf::dxgi +void do_nothing(void *) {} namespace video { void dxgi_img_to_frame(const platf::img_t &img, frame_t &frame) { if(img.data == frame->data[0]) { return; } - + // Need to have something refcounted if(!frame->buf[0]) { frame->buf[0] = av_buffer_allocz(sizeof(AVD3D11FrameDescriptor)); } - auto desc = (AVD3D11FrameDescriptor*)frame->buf[0]->data; - desc->texture = (ID3D11Texture2D*)img.data; - desc->index = 0; + auto desc = (AVD3D11FrameDescriptor *)frame->buf[0]->data; + desc->texture = (ID3D11Texture2D *)img.data; + desc->index = 0; frame->data[0] = img.data; frame->data[1] = 0; @@ -1342,27 +1320,27 @@ void dxgi_img_to_frame(const platf::img_t &img, frame_t &frame) { frame->linesize[0] = img.row_pitch; frame->height = img.height; - frame->width = img.width; + frame->width = img.width; } util::Either dxgi_make_hwdevice_ctx(platf::hwdevice_t *hwdevice_ctx) { buffer_t ctx_buf { av_hwdevice_ctx_alloc(AV_HWDEVICE_TYPE_D3D11VA) }; - auto ctx = (AVD3D11VADeviceContext*)((AVHWDeviceContext*)ctx_buf->data)->hwctx; - - std::fill_n((std::uint8_t*)ctx, sizeof(AVD3D11VADeviceContext), 0); + auto ctx = (AVD3D11VADeviceContext *)((AVHWDeviceContext *)ctx_buf->data)->hwctx; - auto device = (ID3D11Device*)hwdevice_ctx->data; + std::fill_n((std::uint8_t *)ctx, sizeof(AVD3D11VADeviceContext), 0); + + auto device = (ID3D11Device *)hwdevice_ctx->data; device->AddRef(); ctx->device = device; - ctx->lock_ctx = (void*)1; - ctx->lock = do_nothing; - ctx->unlock = do_nothing; + ctx->lock_ctx = (void *)1; + ctx->lock = do_nothing; + ctx->unlock = do_nothing; auto err = av_hwdevice_ctx_init(ctx_buf.get()); if(err) { - char err_str[AV_ERROR_MAX_STRING_SIZE] {0}; + char err_str[AV_ERROR_MAX_STRING_SIZE] { 0 }; BOOST_LOG(error) << "Failed to create FFMpeg hardware device context: "sv << av_make_error_string(err_str, AV_ERROR_MAX_STRING_SIZE, err); return err; @@ -1402,12 +1380,12 @@ void end_capture_sync(capture_thread_sync_ctx_t &ctx) {} platf::dev_type_e map_dev_type(AVHWDeviceType type) { switch(type) { - case AV_HWDEVICE_TYPE_D3D11VA: - return platf::dev_type_e::dxgi; - case AV_PICTURE_TYPE_NONE: - return platf::dev_type_e::none; - default: - return platf::dev_type_e::unknown; + case AV_HWDEVICE_TYPE_D3D11VA: + return platf::dev_type_e::dxgi; + case AV_PICTURE_TYPE_NONE: + return platf::dev_type_e::none; + default: + return platf::dev_type_e::unknown; } return platf::dev_type_e::unknown; @@ -1415,16 +1393,16 @@ platf::dev_type_e map_dev_type(AVHWDeviceType type) { platf::pix_fmt_e map_pix_fmt(AVPixelFormat fmt) { switch(fmt) { - case AV_PIX_FMT_YUV420P10: - return platf::pix_fmt_e::yuv420p10; - case AV_PIX_FMT_YUV420P: - return platf::pix_fmt_e::yuv420p; - case AV_PIX_FMT_NV12: - return platf::pix_fmt_e::nv12; - case AV_PIX_FMT_P010: - return platf::pix_fmt_e::p010; - default: - return platf::pix_fmt_e::unknown; + case AV_PIX_FMT_YUV420P10: + return platf::pix_fmt_e::yuv420p10; + case AV_PIX_FMT_YUV420P: + return platf::pix_fmt_e::yuv420p; + case AV_PIX_FMT_NV12: + return platf::pix_fmt_e::nv12; + case AV_PIX_FMT_P010: + return platf::pix_fmt_e::p010; + default: + return platf::pix_fmt_e::unknown; } return platf::pix_fmt_e::unknown; diff --git a/sunshine/video.h b/sunshine/video.h index b628f303..6fff9875 100644 --- a/sunshine/video.h +++ b/sunshine/video.h @@ -5,9 +5,9 @@ #ifndef SUNSHINE_VIDEO_H #define SUNSHINE_VIDEO_H -#include "thread_safe.h" #include "input.h" #include "platform/common.h" +#include "thread_safe.h" extern "C" { #include @@ -18,15 +18,15 @@ namespace video { struct packet_raw_t : public AVPacket { void init_packet() { - pts = AV_NOPTS_VALUE; - dts = AV_NOPTS_VALUE; - pos = -1; - duration = 0; - flags = 0; - stream_index = 0; - buf = nullptr; - side_data = nullptr; - side_data_elems = 0; + pts = AV_NOPTS_VALUE; + dts = AV_NOPTS_VALUE; + pos = -1; + duration = 0; + flags = 0; + stream_index = 0; + buf = nullptr; + side_data = nullptr; + side_data_elems = 0; } template @@ -70,6 +70,6 @@ void capture( void *channel_data); int init(); -} +} // namespace video #endif //SUNSHINE_VIDEO_H diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 8d59af33..e3e79969 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -9,7 +9,7 @@ set_target_properties(dxgi-info PROPERTIES CXX_STANDARD 17) target_link_libraries(dxgi-info ${CMAKE_THREAD_LIBS_INIT} dxgi - ${PLATFORM_LIBRARIES}) + ${PLATFORM_LIBRARIES}) target_compile_options(dxgi-info PRIVATE ${SUNSHINE_COMPILE_OPTIONS}) add_executable(audio-info audio.cpp) @@ -17,5 +17,5 @@ set_target_properties(audio-info PROPERTIES CXX_STANDARD 17) target_link_libraries(audio-info ${CMAKE_THREAD_LIBS_INIT} ksuser - ${PLATFORM_LIBRARIES}) + ${PLATFORM_LIBRARIES}) target_compile_options(audio-info PRIVATE ${SUNSHINE_COMPILE_OPTIONS}) diff --git a/tools/audio.cpp b/tools/audio.cpp index 18556c50..b99edf5f 100644 --- a/tools/audio.cpp +++ b/tools/audio.cpp @@ -2,9 +2,9 @@ // Created by loki on 1/24/20. // -#include -#include #include +#include +#include #include @@ -16,8 +16,8 @@ #include "sunshine/utility.h" -DEFINE_PROPERTYKEY(PKEY_Device_DeviceDesc, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 2); // DEVPROP_TYPE_STRING -DEFINE_PROPERTYKEY(PKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 14); // DEVPROP_TYPE_STRING +DEFINE_PROPERTYKEY(PKEY_Device_DeviceDesc, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 2); // DEVPROP_TYPE_STRING +DEFINE_PROPERTYKEY(PKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 14); // DEVPROP_TYPE_STRING DEFINE_PROPERTYKEY(PKEY_DeviceInterface_FriendlyName, 0x026e516e, 0xb814, 0x414b, 0x83, 0xcd, 0x85, 0x6d, 0x6f, 0xef, 0x48, 0x22, 2); using namespace std::literals; @@ -27,7 +27,7 @@ const IID IID_IAudioClient = __uuidof(IAudioClient); const IID IID_IAudioCaptureClient = __uuidof(IAudioCaptureClient); constexpr auto SAMPLE_RATE = 48000; -int device_state_filter = DEVICE_STATE_ACTIVE; +int device_state_filter = DEVICE_STATE_ACTIVE; namespace audio { template @@ -73,32 +73,26 @@ struct format_t { std::string_view name; int channels; int channel_mask; -} formats [] { - { - "Mono"sv, +} formats[] { + { "Mono"sv, 1, - SPEAKER_FRONT_CENTER - }, - { - "Stereo"sv, + SPEAKER_FRONT_CENTER }, + { "Stereo"sv, 2, - SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT - }, - { - "Surround 5.1"sv, + SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT }, + { "Surround 5.1"sv, 6, - SPEAKER_FRONT_LEFT | - SPEAKER_FRONT_RIGHT | - SPEAKER_FRONT_CENTER | - SPEAKER_LOW_FREQUENCY | - SPEAKER_BACK_LEFT | - SPEAKER_BACK_RIGHT - } + SPEAKER_FRONT_LEFT | + SPEAKER_FRONT_RIGHT | + SPEAKER_FRONT_CENTER | + SPEAKER_LOW_FREQUENCY | + SPEAKER_BACK_LEFT | + SPEAKER_BACK_RIGHT } }; void set_wave_format(audio::wave_format_t &wave_format, const format_t &format) { - wave_format->nChannels = format.channels; - wave_format->nBlockAlign = wave_format->nChannels * wave_format->wBitsPerSample / 8; + wave_format->nChannels = format.channels; + wave_format->nBlockAlign = wave_format->nChannels * wave_format->wBitsPerSample / 8; wave_format->nAvgBytesPerSec = wave_format->nSamplesPerSec * wave_format->nBlockAlign; if(wave_format->wFormatTag == WAVE_FORMAT_EXTENSIBLE) { @@ -112,7 +106,7 @@ audio_client_t make_audio_client(device_t &device, const format_t &format) { IID_IAudioClient, CLSCTX_ALL, nullptr, - (void **) &audio_client); + (void **)&audio_client); if(FAILED(status)) { std::cout << "Couldn't activate Device: [0x"sv << util::hex(status).to_string_view() << ']' << std::endl; @@ -123,7 +117,7 @@ audio_client_t make_audio_client(device_t &device, const format_t &format) { wave_format_t wave_format; status = audio_client->GetMixFormat(&wave_format); - if (FAILED(status)) { + if(FAILED(status)) { std::cout << "Couldn't acquire Wave Format [0x"sv << util::hex(status).to_string_view() << ']' << std::endl; return nullptr; @@ -132,23 +126,23 @@ audio_client_t make_audio_client(device_t &device, const format_t &format) { wave_format->wBitsPerSample = 16; wave_format->nSamplesPerSec = SAMPLE_RATE; switch(wave_format->wFormatTag) { - case WAVE_FORMAT_PCM: + case WAVE_FORMAT_PCM: + break; + case WAVE_FORMAT_IEEE_FLOAT: + break; + case WAVE_FORMAT_EXTENSIBLE: { + auto wave_ex = (PWAVEFORMATEXTENSIBLE)wave_format.get(); + if(IsEqualGUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, wave_ex->SubFormat)) { + wave_ex->SubFormat = KSDATAFORMAT_SUBTYPE_PCM; + wave_ex->Samples.wValidBitsPerSample = 16; break; - case WAVE_FORMAT_IEEE_FLOAT: - break; - case WAVE_FORMAT_EXTENSIBLE: { - auto wave_ex = (PWAVEFORMATEXTENSIBLE) wave_format.get(); - if (IsEqualGUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, wave_ex->SubFormat)) { - wave_ex->SubFormat = KSDATAFORMAT_SUBTYPE_PCM; - wave_ex->Samples.wValidBitsPerSample = 16; - break; - } - - std::cout << "Unsupported Sub Format for WAVE_FORMAT_EXTENSIBLE: [0x"sv << util::hex(wave_ex->SubFormat).to_string_view() << ']' << std::endl; } - default: - std::cout << "Unsupported Wave Format: [0x"sv << util::hex(wave_format->wFormatTag).to_string_view() << ']' << std::endl; - return nullptr; + + std::cout << "Unsupported Sub Format for WAVE_FORMAT_EXTENSIBLE: [0x"sv << util::hex(wave_ex->SubFormat).to_string_view() << ']' << std::endl; + } + default: + std::cout << "Unsupported Wave Format: [0x"sv << util::hex(wave_format->wFormatTag).to_string_view() << ']' << std::endl; + return nullptr; }; set_wave_format(wave_format, format); @@ -191,18 +185,18 @@ void print_device(device_t &device) { std::wstring device_state_string = L"Unknown"s; switch(device_state) { - case DEVICE_STATE_ACTIVE: - device_state_string = L"Active"s; - break; - case DEVICE_STATE_DISABLED: - device_state_string = L"Disabled"s; - break; - case DEVICE_STATE_UNPLUGGED: - device_state_string = L"Unplugged"s; - break; - case DEVICE_STATE_NOTPRESENT: - device_state_string = L"Not present"s; - break; + case DEVICE_STATE_ACTIVE: + device_state_string = L"Active"s; + break; + case DEVICE_STATE_DISABLED: + device_state_string = L"Disabled"s; + break; + case DEVICE_STATE_UNPLUGGED: + device_state_string = L"Unplugged"s; + break; + case DEVICE_STATE_NOTPRESENT: + device_state_string = L"Not present"s; + break; } std::wcout @@ -211,7 +205,8 @@ void print_device(device_t &device) { << L"Device name : "sv << no_null((LPWSTR)device_friendly_name.prop.pszVal) << std::endl << L"Adapter name : "sv << no_null((LPWSTR)adapter_friendly_name.prop.pszVal) << std::endl << L"Device description : "sv << no_null((LPWSTR)device_desc.prop.pszVal) << std::endl - << L"Device state : "sv << device_state_string << std::endl << std::endl; + << L"Device state : "sv << device_state_string << std::endl + << std::endl; if(device_state != DEVICE_STATE_ACTIVE) { return; @@ -224,7 +219,7 @@ void print_device(device_t &device) { std::cout << format.name << ": "sv << (!audio_client ? "unsupported"sv : "supported"sv) << std::endl; } } -} +} // namespace audio void print_help() { std::cout @@ -281,9 +276,9 @@ int main(int argc, char *argv[]) { nullptr, CLSCTX_ALL, IID_IMMDeviceEnumerator, - (void **) &device_enum); + (void **)&device_enum); - if (FAILED(status)) { + if(FAILED(status)) { std::cout << "Couldn't create Device Enumerator: [0x"sv << util::hex(status).to_string_view() << ']' << std::endl; return -1; @@ -292,7 +287,7 @@ int main(int argc, char *argv[]) { audio::collection_t collection; status = device_enum->EnumAudioEndpoints(eRender, DEVICE_STATEMASK_ALL, &collection); - if (FAILED(status)) { + if(FAILED(status)) { std::cout << "Couldn't enumerate: [0x"sv << util::hex(status).to_string_view() << ']' << std::endl; return -1; diff --git a/tools/dxgi.cpp b/tools/dxgi.cpp index 3b3f9c88..9c5f7307 100644 --- a/tools/dxgi.cpp +++ b/tools/dxgi.cpp @@ -2,8 +2,8 @@ // Created by loki on 1/23/20. // -#include #include +#include #include @@ -16,17 +16,17 @@ void Release(T *dxgi) { dxgi->Release(); } -using factory1_t = util::safe_ptr>; -using adapter_t = util::safe_ptr>; -using output_t = util::safe_ptr>; +using factory1_t = util::safe_ptr>; +using adapter_t = util::safe_ptr>; +using output_t = util::safe_ptr>; -} +} // namespace dxgi int main(int argc, char *argv[]) { HRESULT status; dxgi::factory1_t::pointer factory_p {}; - status = CreateDXGIFactory1(IID_IDXGIFactory1, (void**)&factory_p); + status = CreateDXGIFactory1(IID_IDXGIFactory1, (void **)&factory_p); dxgi::factory1_t factory { factory_p }; if(FAILED(status)) { std::cout << "Failed to create DXGIFactory1 [0x"sv << util::hex(status).to_string_view() << ']' << std::endl; @@ -49,12 +49,13 @@ int main(int argc, char *argv[]) { << "Device Device ID : 0x"sv << util::hex(adapter_desc.DeviceId).to_string_view() << std::endl << "Device Video Mem : "sv << adapter_desc.DedicatedVideoMemory / 1048576 << " MiB"sv << std::endl << "Device Sys Mem : "sv << adapter_desc.DedicatedSystemMemory / 1048576 << " MiB"sv << std::endl - << "Share Sys Mem : "sv << adapter_desc.SharedSystemMemory / 1048576 << " MiB"sv << std::endl << std::endl + << "Share Sys Mem : "sv << adapter_desc.SharedSystemMemory / 1048576 << " MiB"sv << std::endl + << std::endl << " ====== OUTPUT ======"sv << std::endl; dxgi::output_t::pointer output_p {}; for(int y = 0; adapter->EnumOutputs(y, &output_p) != DXGI_ERROR_NOT_FOUND; ++y) { - dxgi::output_t output {output_p }; + dxgi::output_t output { output_p }; DXGI_OUTPUT_DESC desc; output->GetDesc(&desc); @@ -66,7 +67,8 @@ int main(int argc, char *argv[]) { << L" Output Name : "sv << desc.DeviceName << std::endl; std::cout << " AttachedToDesktop : "sv << (desc.AttachedToDesktop ? "yes"sv : "no"sv) << std::endl - << " Resolution : "sv << width << 'x' << height << std::endl << std::endl; + << " Resolution : "sv << width << 'x' << height << std::endl + << std::endl; } } From 2b04e1428cd1e6ea9913ca576f18cb313ae23450 Mon Sep 17 00:00:00 2001 From: loki Date: Tue, 18 May 2021 13:36:12 +0200 Subject: [PATCH 20/35] Select audio output on Linux --- CMakeLists.txt | 1 + assets/sunshine.conf | 4 +- sunshine/audio.cpp | 82 ++++++- sunshine/config.h | 4 +- sunshine/platform/common.h | 29 +++ sunshine/platform/linux/audio.cpp | 361 ++++++++++++++++++++++++++++ sunshine/platform/linux/display.cpp | 52 ---- sunshine/platform/linux/input.cpp | 2 - sunshine/thread_safe.h | 92 ++++++- sunshine/utility.h | 5 +- 10 files changed, 564 insertions(+), 68 deletions(-) create mode 100644 sunshine/platform/linux/audio.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 0dcbe718..ffa12301 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -97,6 +97,7 @@ else() find_package(FFmpeg REQUIRED) set(PLATFORM_TARGET_FILES sunshine/platform/linux/display.cpp + sunshine/platform/linux/audio.cpp sunshine/platform/linux/input.cpp) set(PLATFORM_LIBRARIES diff --git a/assets/sunshine.conf b/assets/sunshine.conf index 12377eda..c4ea57a4 100644 --- a/assets/sunshine.conf +++ b/assets/sunshine.conf @@ -94,8 +94,8 @@ # output_name = \\.\DISPLAY1 # !! Linux only !! -# Set the display number to stream. I have no idea how they are numbered. They start from 1, usually. -# output_name = 1 +# Set the display number to stream. I have no idea how they are numbered. They start from 0, usually. +# output_name = 0 ############################################### # FFmpeg software encoding parameters diff --git a/sunshine/audio.cpp b/sunshine/audio.cpp index c9ddab76..5960562d 100644 --- a/sunshine/audio.cpp +++ b/sunshine/audio.cpp @@ -25,7 +25,9 @@ struct opus_stream_config_t { constexpr std::uint8_t map_stereo[] { 0, 1 }; constexpr std::uint8_t map_surround51[] { 0, 4, 1, 5, 2, 3 }; constexpr std::uint8_t map_high_surround51[] { 0, 1, 2, 3, 4, 5 }; -constexpr auto SAMPLE_RATE = 48000; + +constexpr auto SAMPLE_RATE = 48000; + static opus_stream_config_t stereo = { SAMPLE_RATE, 2, @@ -79,7 +81,78 @@ void encodeThread(packet_queue_t packets, sample_queue_t samples, config_t confi } } +const platf::card_t *active_card(const std::vector &cards) { + for(auto &card : cards) { + if(card.active_profile) { + return &card; + } + } + + return nullptr; +} + void capture(safe::signal_t *shutdown_event, packet_queue_t packets, config_t config, void *channel_data) { + //FIXME: Pick correct opus_stream_config_t based on config.channels + auto stream = &stereo; + + auto control = platf::audio_control(); + if(!control) { + BOOST_LOG(error) << "Couldn't create audio control"sv; + + return; + } + + auto cards = control->card_info(); + if(cards.empty()) { + return; + } + + auto card = active_card(cards); + if(!card || (card->stereo.empty() && card->surround51.empty() && card->surround71.empty())) { + return; + } + + const platf::profile_t *profile; + switch(config.channels) { + case 2: + if(!card->stereo.empty()) { + profile = &card->stereo[0]; + } + else if(!card->surround51.empty()) { + profile = &card->surround51[0]; + } + else if(!card->surround71.empty()) { + profile = &card->surround71[0]; + } + break; + case 6: + if(!card->surround51.empty()) { + profile = &card->surround51[0]; + } + else if(!card->surround71.empty()) { + profile = &card->surround71[0]; + } + else { + profile = &card->stereo[0]; + } + break; + case 8: + if(!card->surround71.empty()) { + profile = &card->surround71[0]; + } + else if(!card->surround51.empty()) { + profile = &card->surround51[0]; + } + else { + profile = &card->stereo[0]; + } + break; + } + + if(control->set_output(*card, *profile)) { + return; + } + auto samples = std::make_shared(30); std::thread thread { encodeThread, packets, samples, config, channel_data }; @@ -90,13 +163,10 @@ void capture(safe::signal_t *shutdown_event, packet_queue_t packets, config_t co shutdown_event->view(); }); - //FIXME: Pick correct opus_stream_config_t based on config.channels - auto stream = &stereo; - auto frame_size = config.packetDuration * stream->sampleRate / 1000; int samples_per_frame = frame_size * stream->channelCount; - auto mic = platf::microphone(stream->sampleRate, frame_size); + auto mic = control->create_mic(stream->sampleRate, frame_size); if(!mic) { BOOST_LOG(error) << "Couldn't create audio input"sv; @@ -115,7 +185,7 @@ void capture(safe::signal_t *shutdown_event, packet_queue_t packets, config_t co continue; case platf::capture_e::reinit: mic.reset(); - mic = platf::microphone(stream->sampleRate, frame_size); + mic = control->create_mic(stream->sampleRate, frame_size); if(!mic) { BOOST_LOG(error) << "Couldn't re-initialize audio input"sv; diff --git a/sunshine/config.h b/sunshine/config.h index be75f3d2..3bced756 100644 --- a/sunshine/config.h +++ b/sunshine/config.h @@ -77,8 +77,8 @@ namespace flag { enum flag_e : std::size_t { PIN_STDIN = 0, // Read PIN from stdin instead of http FRESH_STATE, // Do not load or save state - FLAG_SIZE, - CONST_PIN = 4 // Use "universal" pin + CONST_PIN = 4, // Use "universal" pin + FLAG_SIZE }; } diff --git a/sunshine/platform/common.h b/sunshine/platform/common.h index c80fc750..c044aa53 100644 --- a/sunshine/platform/common.h +++ b/sunshine/platform/common.h @@ -6,6 +6,7 @@ #define SUNSHINE_COMMON_H #include "sunshine/utility.h" +#include #include #include @@ -101,6 +102,23 @@ public: virtual ~img_t() = default; }; +struct profile_t { + std::string name; + std::string description; + + bool available; +}; + +struct card_t { + std::string name; + std::string description; + + std::optional active_profile; + std::vector stereo; + std::vector surround51; + std::vector surround71; +}; + struct hwdevice_t { void *data {}; platf::img_t *img {}; @@ -148,6 +166,16 @@ public: virtual ~mic_t() = default; }; +class audio_control_t { +public: + virtual int set_output(const card_t &card, const profile_t &) = 0; + + virtual std::unique_ptr create_mic(std::uint32_t sample_rate, std::uint32_t frame_size) = 0; + + virtual std::vector card_info() = 0; + + virtual ~audio_control_t() = default; +}; void freeInput(void *); @@ -158,6 +186,7 @@ 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::unique_ptr audio_control(); std::unique_ptr microphone(std::uint32_t sample_rate, std::uint32_t frame_size); std::shared_ptr display(dev_type_e hwdevice_type); diff --git a/sunshine/platform/linux/audio.cpp b/sunshine/platform/linux/audio.cpp new file mode 100644 index 00000000..86cac399 --- /dev/null +++ b/sunshine/platform/linux/audio.cpp @@ -0,0 +1,361 @@ +// +// Created by loki on 5/16/21. +// +#include +#include + +#include +#include +#include + +#include "sunshine/platform/common.h" + +#include "sunshine/config.h" +#include "sunshine/main.h" +#include "sunshine/thread_safe.h" + +namespace platf { +using namespace std::literals; + +struct mic_attr_t : public mic_t { + pa_sample_spec ss; + util::safe_ptr mic; + + explicit mic_attr_t(pa_sample_format format, std::uint32_t sample_rate, + std::uint8_t channels) : ss { format, sample_rate, channels }, mic {} {} + + capture_e sample(std::vector &sample_buf) override { + auto sample_size = sample_buf.size(); + + auto buf = sample_buf.data(); + int status; + if(pa_simple_read(mic.get(), buf, sample_size * 2, &status)) { + BOOST_LOG(error) << "pa_simple_read() failed: "sv << pa_strerror(status); + + return capture_e::error; + } + + return capture_e::ok; + } +}; + +std::unique_ptr microphone(std::uint32_t sample_rate, std::uint32_t) { + auto mic = std::make_unique(PA_SAMPLE_S16LE, sample_rate, 2); + + int status; + + const char *audio_sink = "@DEFAULT_MONITOR@"; + if(!config::audio.sink.empty()) { + audio_sink = config::audio.sink.c_str(); + } + + mic->mic.reset( + pa_simple_new(nullptr, "sunshine", + pa_stream_direction_t::PA_STREAM_RECORD, audio_sink, + "sunshine-record", &mic->ss, nullptr, nullptr, &status)); + + if(!mic->mic) { + auto err_str = pa_strerror(status); + BOOST_LOG(error) << "pa_simple_new() failed: "sv << err_str; + + log_flush(); + std::abort(); + } + + return mic; +} + +namespace pa { +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 +using cb_t = std::function; + +template +void cb(ctx_t::pointer ctx, const T *i, int eol, void *userdata) { + auto &f = *(cb_t *)userdata; + + // For some reason, pulseaudio calls this callback after disconnecting + if(i && eol) { + return; + } + + f(ctx, i, eol); +} + +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) { + assert(userdata != nullptr); + + auto alarm = (safe::alarm_raw_t *)userdata; + alarm->ring(status ? 0 : 1); +} + +profile_t make(pa_card_profile_info2 *profile) { + return profile_t { + profile->name, + profile->description, + profile->available == 1 + }; +} + +card_t make(const pa_card_info *card) { + boost::regex stereo_expr { + ".*output(?!.*surround).*stereo.*" + }; + + boost::regex surround51_expr { + ".*output.*surround(.*(^\\d|51).*|$)" + }; + + boost::regex surround71_expr { + ".*output.*surround.*7.?1.*" + }; + + std::vector stereo; + std::vector surround51; + std::vector surround71; + + std::for_each_n(card->profiles2, card->n_profiles, [&](pa_card_profile_info2 *profile) { + if(boost::regex_match(profile->name, stereo_expr)) { + stereo.emplace_back(make(profile)); + } + if(boost::regex_match(profile->name, surround51_expr)) { + surround51.emplace_back(make(profile)); + } + if(boost::regex_match(profile->name, surround71_expr)) { + surround71.emplace_back(make(profile)); + } + }); + + std::optional active_profile; + if(card->active_profile2->name != "off"sv) { + active_profile = make(card->active_profile2); + } + + return card_t { + card->name, + card->driver, + std::move(active_profile), + std::move(stereo), + std::move(surround51), + std::move(surround71), + }; +} + +const card_t *active_card(const std::vector &cards) { + for(auto &card : cards) { + if(card.active_profile) { + return &card; + } + } + + return nullptr; +} +class server_t : public audio_control_t { + enum ctx_event_e : int { + ready, + terminated, + failed + }; + +public: + loop_t loop; + ctx_t ctx; + + std::unique_ptr> events; + std::unique_ptr> events_cb; + + std::thread worker; + int init() { + events = std::make_unique>(); + loop.reset(pa_mainloop_new()); + ctx.reset(pa_context_new(pa_mainloop_get_api(loop.get()), "sunshine")); + + events_cb = std::make_unique>([this](ctx_t::pointer ctx) { + switch(pa_context_get_state(ctx)) { + case PA_CONTEXT_READY: + events->raise(ready); + break; + case PA_CONTEXT_TERMINATED: + BOOST_LOG(debug) << "Pulseadio context terminated"sv; + events->raise(terminated); + break; + case PA_CONTEXT_FAILED: + BOOST_LOG(debug) << "Pulseadio context failed"sv; + events->raise(failed); + break; + case PA_CONTEXT_CONNECTING: + BOOST_LOG(debug) << "Connecting to pulseaudio"sv; + case PA_CONTEXT_UNCONNECTED: + case PA_CONTEXT_AUTHORIZING: + case PA_CONTEXT_SETTING_NAME: + break; + } + }); + + pa_context_set_state_callback(ctx.get(), ctx_state_cb, events_cb.get()); + + auto status = pa_context_connect(ctx.get(), nullptr, PA_CONTEXT_NOFLAGS, nullptr); + if(status) { + BOOST_LOG(error) << "Couldn't connect to pulseaudio: "sv << pa_strerror(status); + return -1; + } + + worker = std::thread { + [](loop_t::pointer loop) { + int retval; + auto status = pa_mainloop_run(loop, &retval); + + if(status < 0) { + BOOST_LOG(fatal) << "Couldn't run pulseaudio main loop"sv; + + log_flush(); + std::abort(); + } + }, + loop.get() + }; + + auto event = events->pop(); + if(event == failed) { + return -1; + } + + return 0; + } + + std::vector card_info() override { + auto alarm = safe::make_alarm(); + + std::vector cards; + cb_t f = [&cards, alarm](ctx_t::pointer ctx, const pa_card_info *card_info, int eol) { + if(!card_info) { + if(!eol) { + BOOST_LOG(error) << "Couldn't get pulseaudio card info: "sv << pa_strerror(pa_context_errno(ctx)); + } + + alarm->ring(true); + return; + } + + cards.emplace_back(make(card_info)); + }; + + op_t op { pa_context_get_card_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())); + + return {}; + } + + alarm->wait(); + + return cards; + } + + std::unique_ptr create_mic(std::uint32_t sample_rate, std::uint32_t frame_size) override { + return microphone(sample_rate, frame_size); + } + + int set_output(const card_t &card, const profile_t &profile) override { + auto alarm = safe::make_alarm(); + + op_t op { pa_context_set_card_profile_by_name( + ctx.get(), card.name.c_str(), profile.name.c_str(), success_cb, alarm.get()) + }; + + if(!op) { + BOOST_LOG(error) << "Couldn't create set profile operation: "sv << pa_strerror(pa_context_errno(ctx.get())); + return -1; + } + + alarm->wait(); + if(*alarm->status()) { + BOOST_LOG(error) << "Couldn't set profile ["sv << profile.name << "]: "sv << pa_strerror(pa_context_errno(ctx.get())); + + return -1; + } + + return 0; + } + + ~server_t() override { + if(worker.joinable()) { + pa_context_disconnect(ctx.get()); + + KITTY_WHILE_LOOP(auto event = events->pop(), event != terminated && event != failed, { + event = events->pop(); + }) + + pa_mainloop_quit(loop.get(), 0); + worker.join(); + } + } +}; +} // namespace pa + +std::unique_ptr audio_control() { + auto audio = std::make_unique(); + + if(audio->init()) { + return nullptr; + } + + return audio; +} + +std::unique_ptr init() { + pa::server_t server; + if(server.init()) { + return std::make_unique(); + } + + auto cards = server.card_info(); + + for(auto &card : cards) { + BOOST_LOG(info) << "---- CARD ----"sv; + BOOST_LOG(info) << "Name: ["sv << card.name << ']'; + BOOST_LOG(info) << "Description: ["sv << card.description << ']'; + + if(card.active_profile) { + BOOST_LOG(info) << "Active profile:"sv; + BOOST_LOG(info) << " Name: [" << card.active_profile->name << ']'; + BOOST_LOG(info) << " Description: ["sv << card.active_profile->description << ']'; + BOOST_LOG(info) << " Available: ["sv << card.active_profile->available << ']'; + BOOST_LOG(info); + } + + + BOOST_LOG(info) << " -- stereo --"sv; + for(auto &profile : card.stereo) { + BOOST_LOG(info) << " "sv << profile.name << ": "sv << profile.description << " ("sv << profile.available << ')'; + } + + BOOST_LOG(info) << " -- surround 5.1 --"sv; + for(auto &profile : card.surround51) { + BOOST_LOG(info) << " "sv << profile.name << ": "sv << profile.description << " ("sv << profile.available << ')'; + } + + BOOST_LOG(info) << " -- surround 7.1 --"sv; + for(auto &profile : card.surround71) { + BOOST_LOG(info) << " "sv << profile.name << ": "sv << profile.description << " ("sv << profile.available << ')'; + } + } + + return std::make_unique(); +} +} // namespace platf \ No newline at end of file diff --git a/sunshine/platform/linux/display.cpp b/sunshine/platform/linux/display.cpp index 0c45c749..b405ba12 100644 --- a/sunshine/platform/linux/display.cpp +++ b/sunshine/platform/linux/display.cpp @@ -4,7 +4,6 @@ #include "sunshine/platform/common.h" -#include #include #include @@ -20,9 +19,6 @@ #include #include -#include -#include - #include "sunshine/config.h" #include "sunshine/main.h" #include "sunshine/task_pool.h" @@ -359,28 +355,6 @@ struct shm_attr_t : public x11_attr_t { } }; -struct mic_attr_t : public mic_t { - pa_sample_spec ss; - util::safe_ptr mic; - - explicit mic_attr_t(pa_sample_format format, std::uint32_t sample_rate, - std::uint8_t channels) : ss { format, sample_rate, channels }, mic {} {} - - capture_e sample(std::vector &sample_buf) override { - auto sample_size = sample_buf.size(); - - auto buf = sample_buf.data(); - int status; - if(pa_simple_read(mic.get(), buf, sample_size * 2, &status)) { - BOOST_LOG(error) << "pa_simple_read() failed: "sv << pa_strerror(status); - - return capture_e::error; - } - - return capture_e::ok; - } -}; - std::shared_ptr display(platf::dev_type_e hwdevice_type) { if(hwdevice_type != platf::dev_type_e::none) { BOOST_LOG(error) << "Could not initialize display with the given hw device type."sv; @@ -409,32 +383,6 @@ std::shared_ptr display(platf::dev_type_e hwdevice_type) { return x11_disp; } -std::unique_ptr microphone(std::uint32_t sample_rate, std::uint32_t) { - auto mic = std::make_unique(PA_SAMPLE_S16LE, sample_rate, 2); - - int status; - - const char *audio_sink = "@DEFAULT_MONITOR@"; - if(!config::audio.sink.empty()) { - audio_sink = config::audio.sink.c_str(); - } - - mic->mic.reset( - pa_simple_new(nullptr, "sunshine", - pa_stream_direction_t::PA_STREAM_RECORD, audio_sink, - "sunshine-record", &mic->ss, nullptr, nullptr, &status)); - - if(!mic->mic) { - auto err_str = pa_strerror(status); - BOOST_LOG(error) << "pa_simple_new() failed: "sv << err_str; - - log_flush(); - std::abort(); - } - - return mic; -} - ifaddr_t get_ifaddrs() { ifaddrs *p { nullptr }; diff --git a/sunshine/platform/linux/input.cpp b/sunshine/platform/linux/input.cpp index ed3b5985..ad3397d5 100644 --- a/sunshine/platform/linux/input.cpp +++ b/sunshine/platform/linux/input.cpp @@ -584,6 +584,4 @@ void freeInput(void *p) { auto *input = (input_raw_t *)p; delete input; } - -std::unique_ptr init() { return std::make_unique(); } } // namespace platf diff --git a/sunshine/thread_safe.h b/sunshine/thread_safe.h index 21db9974..8918a676 100644 --- a/sunshine/thread_safe.h +++ b/sunshine/thread_safe.h @@ -16,9 +16,9 @@ namespace safe { template class event_t { +public: using status_t = util::optional_t; -public: template void raise(Args &&...args) { std::lock_guard lg { _lock }; @@ -131,10 +131,97 @@ private: }; template -class queue_t { +class alarm_raw_t { +public: using status_t = util::optional_t; + alarm_raw_t() : _status { util::false_v } {} + + void ring(const status_t &status) { + std::lock_guard lg(_lock); + + _status = status; + _cv.notify_one(); + } + + void ring(status_t &&status) { + std::lock_guard lg(_lock); + + _status = std::move(status); + _cv.notify_one(); + } + + template + auto wait_for(const std::chrono::duration &rel_time) { + std::unique_lock ul(_lock); + + return _cv.wait_for(ul, rel_time, [this]() { return (bool)status(); }); + } + + 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 (bool)status() || pred(); }); + } + + template + auto wait_until(const std::chrono::duration &rel_time) { + std::unique_lock ul(_lock); + + return _cv.wait_until(ul, rel_time, [this]() { return (bool)status(); }); + } + + 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 (bool)status() || pred(); }); + } + + auto wait() { + std::unique_lock ul(_lock); + _cv.wait(ul, [this]() { return (bool)status(); }); + } + + template + auto wait(Pred &&pred) { + std::unique_lock ul(_lock); + _cv.wait(ul, [this, &pred]() { return (bool)status() || pred(); }); + } + + const status_t &status() const { + return _status; + } + + status_t &status() { + return _status; + } + + void reset() { + _status = status_t {}; + } + +private: + std::mutex _lock; + std::condition_variable _cv; + + status_t _status; +}; + +template +using alarm_t = std::shared_ptr>; + +template +alarm_t make_alarm() { + return std::make_shared>(); +} + +template +class queue_t { public: + using status_t = util::optional_t; + queue_t(std::uint32_t max_elements) : _max_elements { max_elements } {} template @@ -202,7 +289,6 @@ public: } std::vector &unsafe() { - std::lock_guard { _lock }; return _queue; } diff --git a/sunshine/utility.h b/sunshine/utility.h index 66606a38..200f7c0f 100644 --- a/sunshine/utility.h +++ b/sunshine/utility.h @@ -53,6 +53,10 @@ auto &b = std::get<1>(a##_##b##_##c); \ auto &c = std::get<2>(a##_##b##_##c) +#define TUPLE_EL(a, b, expr) \ + decltype(expr) a##_ = expr; \ + auto &a = std::get(a##_) + namespace util { template class X, class... Y> @@ -830,6 +834,5 @@ inline auto little(T x) { return endian_helper::little(x); } template inline auto big(T x) { return endian_helper::big(x); } } // namespace endian - } // namespace util #endif From 66a78f0d406fe992684585b6649986bac9160594 Mon Sep 17 00:00:00 2001 From: loki Date: Wed, 19 May 2021 09:42:38 +0200 Subject: [PATCH 21/35] Fix windows build --- sunshine/platform/windows/input.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sunshine/platform/windows/input.cpp b/sunshine/platform/windows/input.cpp index d7667694..0d34ad1e 100755 --- a/sunshine/platform/windows/input.cpp +++ b/sunshine/platform/windows/input.cpp @@ -2,11 +2,14 @@ #include #include +// prevent clang format from "optimizing" the header include order +// clang-format off +#include #include #include -#include #include #include +// clang-format on #include From 0868d898f6331d14ab8baa53a99f3c7816bbcfc9 Mon Sep 17 00:00:00 2001 From: loki Date: Wed, 19 May 2021 18:11:06 +0200 Subject: [PATCH 22/35] Create virtual audio sinks on Linux --- assets/sunshine.conf | 4 +- sunshine/audio.cpp | 204 ++++++++++-------- sunshine/audio.h | 21 ++ sunshine/nvhttp.cpp | 4 +- sunshine/platform/common.h | 61 ++++-- sunshine/platform/linux/audio.cpp | 332 ++++++++++++++++++------------ sunshine/rtsp.cpp | 22 +- 7 files changed, 403 insertions(+), 245 deletions(-) diff --git a/assets/sunshine.conf b/assets/sunshine.conf index c4ea57a4..6dd5cecf 100644 --- a/assets/sunshine.conf +++ b/assets/sunshine.conf @@ -79,8 +79,8 @@ # # You can find the name of the audio sink using the following command: # !! Linux only !! -# pacmd list-sources | grep "name:" -# audio_sink = alsa_output.pci-0000_09_00.3.analog-stereo.monitor +# pacmd list-sinks | grep "name:" +# audio_sink = alsa_output.pci-0000_09_00.3.analog-stereo # # !! Windows only !! # tools\audio-info.exe diff --git a/sunshine/audio.cpp b/sunshine/audio.cpp index 5960562d..39158f7b 100644 --- a/sunshine/audio.cpp +++ b/sunshine/audio.cpp @@ -5,6 +5,7 @@ #include "platform/common.h" #include "audio.h" +#include "config.h" #include "main.h" #include "thread_safe.h" #include "utility.h" @@ -14,47 +15,65 @@ using namespace std::literals; using opus_t = util::safe_ptr; using sample_queue_t = std::shared_ptr>>; -struct opus_stream_config_t { - std::int32_t sampleRate; - int channelCount; - int streams; - int coupledStreams; - const std::uint8_t *mapping; +struct audio_ctx_t { + // We want to change the sink for the first stream only + std::unique_ptr sink_flag; + std::unique_ptr control; + + platf::sink_t sink; }; -constexpr std::uint8_t map_stereo[] { 0, 1 }; -constexpr std::uint8_t map_surround51[] { 0, 4, 1, 5, 2, 3 }; -constexpr std::uint8_t map_high_surround51[] { 0, 1, 2, 3, 4, 5 }; +static int start_audio_control(audio_ctx_t &ctx); +static void stop_audio_control(audio_ctx_t &); + +int map_stream(int channels, bool quality); constexpr auto SAMPLE_RATE = 48000; -static opus_stream_config_t stereo = { - SAMPLE_RATE, - 2, - 1, - 1, - map_stereo +opus_stream_config_t stream_configs[MAX_STREAM_CONFIG] { + { + SAMPLE_RATE, + 2, + 1, + 1, + platf::speaker::map_stereo, + }, + { + SAMPLE_RATE, + 6, + 4, + 2, + platf::speaker::map_surround51, + }, + { + SAMPLE_RATE, + 6, + 6, + 0, + platf::speaker::map_surround51, + }, + { + SAMPLE_RATE, + 8, + 5, + 3, + platf::speaker::map_surround71, + }, + { + SAMPLE_RATE, + 8, + 8, + 0, + platf::speaker::map_surround71, + }, }; -static opus_stream_config_t Surround51 = { - SAMPLE_RATE, - 6, - 4, - 2, - map_surround51 -}; - -static opus_stream_config_t HighSurround51 = { - SAMPLE_RATE, - 6, - 6, - 0, - map_high_surround51 -}; +auto control_shared = safe::make_shared(start_audio_control, stop_audio_control); void encodeThread(packet_queue_t packets, sample_queue_t samples, config_t config, void *channel_data) { //FIXME: Pick correct opus_stream_config_t based on config.channels - auto stream = &stereo; + auto stream = &stream_configs[map_stream(config.channels, config.high_quality)]; + opus_t opus { opus_multistream_encoder_create( stream->sampleRate, stream->channelCount, @@ -81,75 +100,43 @@ void encodeThread(packet_queue_t packets, sample_queue_t samples, config_t confi } } -const platf::card_t *active_card(const std::vector &cards) { - for(auto &card : cards) { - if(card.active_profile) { - return &card; - } - } - - return nullptr; -} - void capture(safe::signal_t *shutdown_event, packet_queue_t packets, config_t config, void *channel_data) { //FIXME: Pick correct opus_stream_config_t based on config.channels - auto stream = &stereo; + auto stream = &stream_configs[map_stream(config.channels, config.high_quality)]; - auto control = platf::audio_control(); + auto ref = control_shared.ref(); + if(!ref) { + return; + } + + auto &control = ref->control; if(!control) { BOOST_LOG(error) << "Couldn't create audio control"sv; return; } - auto cards = control->card_info(); - if(cards.empty()) { - return; + std::string *sink = &ref->sink.host; + if(ref->sink.null) { + auto &null = *ref->sink.null; + switch(stream->channelCount) { + case 2: + sink = &null.stereo; + break; + case 6: + sink = &null.surround51; + break; + case 8: + sink = &null.surround71; + break; + } } - auto card = active_card(cards); - if(!card || (card->stereo.empty() && card->surround51.empty() && card->surround71.empty())) { - return; - } + // Only the first may change the default sink + if( + !ref->sink_flag->exchange(true, std::memory_order_acquire) && + control->set_sink(*sink)) { - const platf::profile_t *profile; - switch(config.channels) { - case 2: - if(!card->stereo.empty()) { - profile = &card->stereo[0]; - } - else if(!card->surround51.empty()) { - profile = &card->surround51[0]; - } - else if(!card->surround71.empty()) { - profile = &card->surround71[0]; - } - break; - case 6: - if(!card->surround51.empty()) { - profile = &card->surround51[0]; - } - else if(!card->surround71.empty()) { - profile = &card->surround71[0]; - } - else { - profile = &card->stereo[0]; - } - break; - case 8: - if(!card->surround71.empty()) { - profile = &card->surround71[0]; - } - else if(!card->surround51.empty()) { - profile = &card->surround51[0]; - } - else { - profile = &card->stereo[0]; - } - break; - } - - if(control->set_output(*card, *profile)) { return; } @@ -166,7 +153,7 @@ void capture(safe::signal_t *shutdown_event, packet_queue_t packets, config_t co auto frame_size = config.packetDuration * stream->sampleRate / 1000; int samples_per_frame = frame_size * stream->channelCount; - auto mic = control->create_mic(stream->sampleRate, frame_size); + auto mic = control->microphone(stream->mapping, stream->channelCount, stream->sampleRate, frame_size); if(!mic) { BOOST_LOG(error) << "Couldn't create audio input"sv; @@ -185,7 +172,7 @@ void capture(safe::signal_t *shutdown_event, packet_queue_t packets, config_t co continue; case platf::capture_e::reinit: mic.reset(); - mic = control->create_mic(stream->sampleRate, frame_size); + mic = control->microphone(stream->mapping, stream->channelCount, stream->sampleRate, frame_size); if(!mic) { BOOST_LOG(error) << "Couldn't re-initialize audio input"sv; @@ -199,4 +186,41 @@ void capture(safe::signal_t *shutdown_event, packet_queue_t packets, config_t co samples->raise(std::move(sample_buffer)); } } + +int map_stream(int channels, bool quality) { + int shift = quality ? 1 : 0; + switch(channels) { + case 2: + return STEREO; + case 6: + return SURROUND51 + shift; + case 8: + return SURROUND71 + shift; + } + return STEREO; +} + +int start_audio_control(audio_ctx_t &ctx) { + ctx.sink_flag = std::make_unique(false); + + if(!(ctx.control = platf::audio_control())) { + return -1; + } + + auto sink = ctx.control->sink_info(); + if(!sink) { + return -1; + } + + ctx.sink = std::move(*sink); + return 0; +} +void stop_audio_control(audio_ctx_t &ctx) { + // restore audio-sink if applicable + const std::string &sink = config::audio.sink.empty() ? ctx.sink.host : config::audio.sink; + if(!sink.empty()) { + // Best effort, it's allowed to fail + ctx.control->set_sink(sink); + } +} } // namespace audio diff --git a/sunshine/audio.h b/sunshine/audio.h index f170fc36..01f145af 100644 --- a/sunshine/audio.h +++ b/sunshine/audio.h @@ -4,10 +4,31 @@ #include "thread_safe.h" #include "utility.h" namespace audio { +enum stream_config_e : int { + STEREO, + SURROUND51, + HIGH_SURROUND51, + SURROUND71, + HIGH_SURROUND71, + MAX_STREAM_CONFIG +}; + +struct opus_stream_config_t { + std::int32_t sampleRate; + int channelCount; + int streams; + int coupledStreams; + const std::uint8_t *mapping; +}; + +extern opus_stream_config_t stream_configs[MAX_STREAM_CONFIG]; + struct config_t { int packetDuration; int channels; int mask; + + bool high_quality; }; using packet_t = util::buffer_t; diff --git a/sunshine/nvhttp.cpp b/sunshine/nvhttp.cpp index ae413c39..6ac68d6e 100644 --- a/sunshine/nvhttp.cpp +++ b/sunshine/nvhttp.cpp @@ -117,9 +117,7 @@ void save_state() { } void load_state() { - auto file_state = fs::current_path() / config::nvhttp.file_state; - - if(!fs::exists(file_state)) { + if(!fs::exists(config::nvhttp.file_state)) { unique_id = util::uuid_t::generate().string(); return; } diff --git a/sunshine/platform/common.h b/sunshine/platform/common.h index c044aa53..978182f9 100644 --- a/sunshine/platform/common.h +++ b/sunshine/platform/common.h @@ -30,6 +30,36 @@ constexpr std::uint16_t B = 0x2000; constexpr std::uint16_t X = 0x4000; constexpr std::uint16_t Y = 0x8000; +namespace speaker { +enum speaker_e { + FRONT_LEFT, + FRONT_RIGHT, + FRONT_CENTER, + LOW_FREQUENCY, + BACK_LEFT, + BACK_RIGHT, + SIDE_LEFT, + SIDE_RIGHT, +}; + +constexpr std::uint8_t map_stereo[] { + FRONT_LEFT, FRONT_RIGHT +}; +constexpr std::uint8_t map_surround51[] { + FRONT_LEFT, BACK_LEFT, FRONT_RIGHT, BACK_RIGHT, FRONT_CENTER, LOW_FREQUENCY +}; +constexpr std::uint8_t map_surround71[] { + FRONT_LEFT, + BACK_LEFT, + FRONT_RIGHT, + BACK_RIGHT, + SIDE_LEFT, + SIDE_RIGHT, + FRONT_CENTER, + LOW_FREQUENCY, +}; +} // namespace speaker + enum class dev_type_e { none, dxgi, @@ -102,21 +132,18 @@ public: virtual ~img_t() = default; }; -struct profile_t { - std::string name; - std::string description; +struct sink_t { + // Play on host PC + std::string host; - bool available; -}; - -struct card_t { - std::string name; - std::string description; - - std::optional active_profile; - std::vector stereo; - std::vector surround51; - std::vector surround71; + // On Windows, it is not possible to create a virtual sink + // Therefore, it is optional + struct null_t { + std::string stereo; + std::string surround51; + std::string surround71; + }; + std::optional null; }; struct hwdevice_t { @@ -168,11 +195,11 @@ public: class audio_control_t { public: - virtual int set_output(const card_t &card, const profile_t &) = 0; + virtual int set_sink(const std::string &sink) = 0; - virtual std::unique_ptr create_mic(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; - virtual std::vector card_info() = 0; + virtual std::optional sink_info() = 0; virtual ~audio_control_t() = default; }; diff --git a/sunshine/platform/linux/audio.cpp b/sunshine/platform/linux/audio.cpp index 86cac399..f0044ee8 100644 --- a/sunshine/platform/linux/audio.cpp +++ b/sunshine/platform/linux/audio.cpp @@ -17,12 +17,36 @@ namespace platf { using namespace std::literals; -struct mic_attr_t : public mic_t { - pa_sample_spec ss; - util::safe_ptr mic; +constexpr pa_channel_position_t position_mapping[] { + PA_CHANNEL_POSITION_FRONT_LEFT, + PA_CHANNEL_POSITION_FRONT_RIGHT, + PA_CHANNEL_POSITION_FRONT_CENTER, + PA_CHANNEL_POSITION_LFE, + PA_CHANNEL_POSITION_REAR_LEFT, + PA_CHANNEL_POSITION_REAR_RIGHT, + PA_CHANNEL_POSITION_SIDE_LEFT, + PA_CHANNEL_POSITION_SIDE_RIGHT, +}; - explicit mic_attr_t(pa_sample_format format, std::uint32_t sample_rate, - std::uint8_t channels) : ss { format, sample_rate, channels }, mic {} {} +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=s16le channels="sv << channels << " channel_map="sv; + std::for_each_n(mapping, channels - 1, [&ss](std::uint8_t pos) { + ss << pa_channel_position_to_string(position_mapping[pos]) << ','; + }); + + ss << pa_channel_position_to_string(position_mapping[mapping[channels - 1]]); + + ss << " sink_properties=device.description="sv << name; + auto result = ss.str(); + + BOOST_LOG(debug) << "null-sink args: "sv << result; + return result; +} + +struct mic_attr_t : public mic_t { + util::safe_ptr mic; capture_e sample(std::vector &sample_buf) override { auto sample_size = sample_buf.size(); @@ -39,8 +63,16 @@ struct mic_attr_t : public mic_t { } }; -std::unique_ptr microphone(std::uint32_t sample_rate, std::uint32_t) { - auto mic = std::make_unique(PA_SAMPLE_S16LE, sample_rate, 2); +std::unique_ptr microphone(const std::uint8_t *mapping, int channels, std::uint32_t sample_rate) { + auto mic = std::make_unique(); + + pa_sample_spec ss { PA_SAMPLE_S16LE, sample_rate, (std::uint8_t)channels }; + pa_channel_map pa_map; + + pa_map.channels = channels; + std::for_each_n(pa_map.map, pa_map.channels, [mapping](auto &channel) mutable { + channel = position_mapping[*mapping++]; + }); int status; @@ -52,7 +84,7 @@ std::unique_ptr microphone(std::uint32_t sample_rate, std::uint32_t) { mic->mic.reset( pa_simple_new(nullptr, "sunshine", pa_stream_direction_t::PA_STREAM_RECORD, audio_sink, - "sunshine-record", &mic->ss, nullptr, nullptr, &status)); + "sunshine-record", &ss, &pa_map, nullptr, &status)); if(!mic->mic) { auto err_str = pa_strerror(status); @@ -66,6 +98,22 @@ std::unique_ptr microphone(std::uint32_t sample_rate, std::uint32_t) { } namespace pa { +template +struct add_const_helper; + +template +struct add_const_helper { + using type = const std::remove_pointer_t *; +}; + +template +struct add_const_helper { + using type = const T *; +}; + +template +using add_const_t = typename add_const_helper, T>::type; + template void pa_free(T *p) { pa_xfree(p); @@ -76,10 +124,10 @@ using op_t = util::safe_ptr; using string_t = util::safe_ptr>; template -using cb_t = std::function; +using cb_t = std::function i, int eol)>; template -void cb(ctx_t::pointer ctx, const T *i, int eol, void *userdata) { +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 @@ -90,6 +138,12 @@ void cb(ctx_t::pointer ctx, const T *i, int eol, void *userdata) { f(ctx, i, eol); } +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) { auto &f = *(std::function *)userdata; @@ -103,67 +157,6 @@ void success_cb(ctx_t::pointer ctx, int status, void *userdata) { alarm->ring(status ? 0 : 1); } -profile_t make(pa_card_profile_info2 *profile) { - return profile_t { - profile->name, - profile->description, - profile->available == 1 - }; -} - -card_t make(const pa_card_info *card) { - boost::regex stereo_expr { - ".*output(?!.*surround).*stereo.*" - }; - - boost::regex surround51_expr { - ".*output.*surround(.*(^\\d|51).*|$)" - }; - - boost::regex surround71_expr { - ".*output.*surround.*7.?1.*" - }; - - std::vector stereo; - std::vector surround51; - std::vector surround71; - - std::for_each_n(card->profiles2, card->n_profiles, [&](pa_card_profile_info2 *profile) { - if(boost::regex_match(profile->name, stereo_expr)) { - stereo.emplace_back(make(profile)); - } - if(boost::regex_match(profile->name, surround51_expr)) { - surround51.emplace_back(make(profile)); - } - if(boost::regex_match(profile->name, surround71_expr)) { - surround71.emplace_back(make(profile)); - } - }); - - std::optional active_profile; - if(card->active_profile2->name != "off"sv) { - active_profile = make(card->active_profile2); - } - - return card_t { - card->name, - card->driver, - std::move(active_profile), - std::move(stereo), - std::move(surround51), - std::move(surround71), - }; -} - -const card_t *active_card(const std::vector &cards) { - for(auto &card : cards) { - if(card.active_profile) { - return &card; - } - } - - return nullptr; -} class server_t : public audio_control_t { enum ctx_event_e : int { ready, @@ -175,6 +168,12 @@ public: loop_t loop; ctx_t ctx; + struct { + std::uint32_t stereo = PA_INVALID_INDEX; + std::uint32_t surround51 = PA_INVALID_INDEX; + std::uint32_t surround71 = PA_INVALID_INDEX; + } index; + std::unique_ptr> events; std::unique_ptr> events_cb; @@ -237,55 +236,169 @@ public: return 0; } - std::vector card_info() override { - auto alarm = safe::make_alarm(); + int load_null(const char *name, const std::uint8_t *channel_mapping, int channels) { + auto alarm = safe::make_alarm(); - std::vector cards; - cb_t f = [&cards, alarm](ctx_t::pointer ctx, const pa_card_info *card_info, int eol) { - if(!card_info) { + op_t op { + pa_context_load_module( + ctx.get(), + "module-null-sink", + to_string(name, channel_mapping, channels).c_str(), + cb_i, + alarm.get()), + }; + + alarm->wait(); + return *alarm->status(); + } + + int unload_null(std::uint32_t i) { + if(i == PA_INVALID_INDEX) { + return 0; + } + + auto alarm = safe::make_alarm(); + + op_t op { + pa_context_unload_module(ctx.get(), i, success_cb, alarm.get()) + }; + + alarm->wait(); + + if(*alarm->status()) { + BOOST_LOG(error) << "Couldn't unload null-sink with index ["sv << i << "]: "sv << pa_strerror(pa_context_errno(ctx.get())); + return -1; + } + + return 0; + } + + std::optional sink_info() override { + constexpr auto stereo = "sink-sunshine-stereo"; + constexpr auto surround51 = "sink-sunshine-surround51"; + constexpr auto surround71 = "sink-sunshine-surround71"; + + auto alarm = safe::make_alarm(); + + sink_t sink; + + // If hardware sink with more channels found, set that as host + int channels = 0; + // Count of all virtual sinks that are created by us + int nullcount = 0; + + cb_t f = [&](ctx_t::pointer ctx, const pa_sink_info *sink_info, int eol) { + if(!sink_info) { if(!eol) { - BOOST_LOG(error) << "Couldn't get pulseaudio card info: "sv << pa_strerror(pa_context_errno(ctx)); + BOOST_LOG(error) << "Couldn't get pulseaudio sink info: "sv << pa_strerror(pa_context_errno(ctx)); + + alarm->ring(-1); } - alarm->ring(true); + alarm->ring(0); return; } - cards.emplace_back(make(card_info)); + if(sink_info->flags & PA_SINK_HARDWARE && + sink_info->channel_map.channels > channels) { + + sink.host = sink_info->name; + channels = sink_info->channel_map.channels; + } + + // Ensure Sunshine won't create a sink that already exists. + if(!std::strcmp(sink_info->name, stereo)) { + index.stereo = sink_info->owner_module; + + ++nullcount; + } + else if(!std::strcmp(sink_info->name, surround51)) { + index.surround51 = sink_info->owner_module; + + ++nullcount; + } + else if(!std::strcmp(sink_info->name, surround71)) { + index.surround71 = sink_info->owner_module; + + ++nullcount; + } }; - op_t op { pa_context_get_card_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())); - return {}; + return std::nullopt; } alarm->wait(); - return cards; + if(*alarm->status()) { + return std::nullopt; + } + + if(!channels) { + BOOST_LOG(warning) << "Couldn't find hardware sink"sv; + } + + if(index.stereo == PA_INVALID_INDEX) { + 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 { + ++nullcount; + } + } + + if(index.surround51 == PA_INVALID_INDEX) { + 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 { + ++nullcount; + } + } + + if(index.surround71 == PA_INVALID_INDEX) { + 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 { + ++nullcount; + } + } + + if(nullcount == 3) { + sink.null = std::make_optional(sink_t::null_t { stereo, surround51, surround71 }); + } + + return std::make_optional(std::move(sink)); } - std::unique_ptr create_mic(std::uint32_t sample_rate, std::uint32_t frame_size) override { - return microphone(sample_rate, frame_size); + std::unique_ptr microphone(const std::uint8_t *mapping, int channels, std::uint32_t sample_rate, std::uint32_t frame_size) override { + return ::platf::microphone(mapping, channels, sample_rate); } - int set_output(const card_t &card, const profile_t &profile) override { + int set_sink(const std::string &sink) override { auto alarm = safe::make_alarm(); - op_t op { pa_context_set_card_profile_by_name( - ctx.get(), card.name.c_str(), profile.name.c_str(), success_cb, alarm.get()) + op_t op { + pa_context_set_default_sink( + ctx.get(), sink.c_str(), success_cb, alarm.get()), }; if(!op) { - BOOST_LOG(error) << "Couldn't create set profile operation: "sv << pa_strerror(pa_context_errno(ctx.get())); + BOOST_LOG(error) << "Couldn't create set default-sink operation: "sv << pa_strerror(pa_context_errno(ctx.get())); return -1; } alarm->wait(); if(*alarm->status()) { - BOOST_LOG(error) << "Couldn't set profile ["sv << profile.name << "]: "sv << pa_strerror(pa_context_errno(ctx.get())); + BOOST_LOG(error) << "Couldn't set default-sink ["sv << sink << "]: "sv << pa_strerror(pa_context_errno(ctx.get())); return -1; } @@ -294,6 +407,10 @@ public: } ~server_t() override { + unload_null(index.stereo); + unload_null(index.surround51); + unload_null(index.surround71); + if(worker.joinable()) { pa_context_disconnect(ctx.get()); @@ -319,43 +436,6 @@ std::unique_ptr audio_control() { } std::unique_ptr init() { - pa::server_t server; - if(server.init()) { - return std::make_unique(); - } - - auto cards = server.card_info(); - - for(auto &card : cards) { - BOOST_LOG(info) << "---- CARD ----"sv; - BOOST_LOG(info) << "Name: ["sv << card.name << ']'; - BOOST_LOG(info) << "Description: ["sv << card.description << ']'; - - if(card.active_profile) { - BOOST_LOG(info) << "Active profile:"sv; - BOOST_LOG(info) << " Name: [" << card.active_profile->name << ']'; - BOOST_LOG(info) << " Description: ["sv << card.active_profile->description << ']'; - BOOST_LOG(info) << " Available: ["sv << card.active_profile->available << ']'; - BOOST_LOG(info); - } - - - BOOST_LOG(info) << " -- stereo --"sv; - for(auto &profile : card.stereo) { - BOOST_LOG(info) << " "sv << profile.name << ": "sv << profile.description << " ("sv << profile.available << ')'; - } - - BOOST_LOG(info) << " -- surround 5.1 --"sv; - for(auto &profile : card.surround51) { - BOOST_LOG(info) << " "sv << profile.name << ": "sv << profile.description << " ("sv << profile.available << ')'; - } - - BOOST_LOG(info) << " -- surround 7.1 --"sv; - for(auto &profile : card.surround71) { - BOOST_LOG(info) << " "sv << profile.name << ": "sv << profile.description << " ("sv << profile.available << ')'; - } - } - return std::make_unique(); } } // namespace platf \ No newline at end of file diff --git a/sunshine/rtsp.cpp b/sunshine/rtsp.cpp index fc6b77ef..1dae7c94 100644 --- a/sunshine/rtsp.cpp +++ b/sunshine/rtsp.cpp @@ -293,15 +293,22 @@ void cmd_describe(rtsp_server_t *server, net::peer_t peer, msg_t &&req) { auto seqn_str = to_string(req->sequenceNumber); option.content = const_cast(seqn_str.c_str()); - std::string_view payload; - if(config::video.hevc_mode == 1) { - payload = "surround-params=NONE"sv; - } - else { - payload = "sprop-parameter-sets=AAAAAU;surround-params=NONE"sv; + std::stringstream ss; + if(config::video.hevc_mode != 1) { + ss << "sprop-parameter-sets=AAAAAU"sv << std::endl; } - respond(server->host(), peer, &option, 200, "OK", req->sequenceNumber, payload); + for(auto &stream_config : audio::stream_configs) { + ss << "a=fmtp:97 surround-params="sv << stream_config.channelCount << stream_config.streams << stream_config.coupledStreams; + + std::for_each_n(stream_config.mapping, stream_config.channelCount, [&ss](std::uint8_t digit) { + ss << (char)(digit + '0'); + }); + + ss << std::endl; + } + + respond(server->host(), peer, &option, 200, "OK", req->sequenceNumber, ss.str()); } void cmd_setup(rtsp_server_t *server, net::peer_t peer, msg_t &&req) { @@ -404,6 +411,7 @@ void cmd_announce(rtsp_server_t *server, net::peer_t peer, msg_t &&req) { try { config.audio.channels = util::from_view(args.at("x-nv-audio.surround.numChannels"sv)); config.audio.mask = util::from_view(args.at("x-nv-audio.surround.channelMask"sv)); + config.audio.high_quality = util::from_view(args.at("x-nv-audio.surround.AudioQuality"sv)); config.audio.packetDuration = util::from_view(args.at("x-nv-aqos.packetDuration"sv)); config.packetsize = util::from_view(args.at("x-nv-video[0].packetSize"sv)); From cd870bdcddf28ae5d64e22c2ac4ff76ff2951a57 Mon Sep 17 00:00:00 2001 From: loki Date: Wed, 19 May 2021 20:02:03 +0200 Subject: [PATCH 23/35] Guard against missing parameters --- sunshine/nvhttp.cpp | 51 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 44 insertions(+), 7 deletions(-) diff --git a/sunshine/nvhttp.cpp b/sunshine/nvhttp.cpp index 6ac68d6e..8f9068d7 100644 --- a/sunshine/nvhttp.cpp +++ b/sunshine/nvhttp.cpp @@ -339,12 +339,19 @@ template void pair(std::shared_ptr> &add_cert, std::shared_ptr::Response> response, std::shared_ptr::Request> request) { print_req(request); + pt::ptree tree; + auto args = request->parse_query_string(); + if(args.find("uniqueid"s) == std::end(args)) { + tree.put("root.resume", 0); + tree.put("root..status_code", 400); + + return; + } + auto uniqID { std::move(args.at("uniqueid"s)) }; auto sess_it = map_id_sess.find(uniqID); - pt::ptree tree; - args_t::const_iterator it; if(it = args.find("phrase"); it != std::end(args)) { if(it->second == "getservercert"sv) { @@ -505,9 +512,6 @@ void serverinfo(std::shared_ptr::Response> res void applist(resp_https_t response, req_https_t request) { print_req(request); - auto args = request->parse_query_string(); - auto clientID = args.at("uniqueid"s); - pt::ptree tree; auto g = util::fail_guard([&]() { @@ -517,6 +521,16 @@ void applist(resp_https_t response, req_https_t request) { response->write(data.str()); }); + auto args = request->parse_query_string(); + if(args.find("uniqueid"s) == std::end(args)) { + tree.put("root.resume", 0); + tree.put("root..status_code", 400); + + return; + } + + auto clientID = args.at("uniqueid"s); + auto client = map_id_client.find(clientID); if(client == std::end(map_id_client)) { tree.put("root..status_code", 501); @@ -558,7 +572,19 @@ void launch(resp_https_t response, req_https_t request) { return; } - auto args = request->parse_query_string(); + auto args = request->parse_query_string(); + if( + args.find("uniqueid"s) == std::end(args) || + args.find("rikey"s) == std::end(args) || + args.find("rikey"s) == std::end(args) || + args.find("appid"s) == std::end(args)) { + + tree.put("root.resume", 0); + tree.put("root..status_code", 400); + + return; + } + auto appid = util::from_view(args.at("appid")) - 1; auto current_appid = proc::proc.running(); @@ -625,7 +651,18 @@ void resume(resp_https_t response, req_https_t request) { stream::launch_session_t launch_session; - auto args = request->parse_query_string(); + auto args = request->parse_query_string(); + if( + args.find("uniqueid"s) == std::end(args) || + args.find("rikey"s) == std::end(args) || + args.find("rikeyid"s) == std::end(args)) { + + tree.put("root.resume", 0); + tree.put("root..status_code", 400); + + return; + } + auto clientID = args.at("uniqueid"s); launch_session.gcm_key = *util::from_hex(args.at("rikey"s), true); uint32_t prepend_iv = util::endian::big(util::from_view(args.at("rikeyid"s))); From 825efb512ab8d3d5a804eb913d5835445a48fe3f Mon Sep 17 00:00:00 2001 From: loki Date: Wed, 19 May 2021 21:44:42 +0200 Subject: [PATCH 24/35] Based on client request, mute the host on Linux --- sunshine/audio.cpp | 28 +++++++++++++++------- sunshine/audio.h | 8 ++++++- sunshine/nvhttp.cpp | 57 +++++++++++++++++++++------------------------ sunshine/rtsp.cpp | 7 +++++- sunshine/rtsp.h | 2 ++ sunshine/stream.h | 1 - 6 files changed, 61 insertions(+), 42 deletions(-) diff --git a/sunshine/audio.cpp b/sunshine/audio.cpp index 39158f7b..6c34a0f9 100644 --- a/sunshine/audio.cpp +++ b/sunshine/audio.cpp @@ -18,8 +18,10 @@ using sample_queue_t = std::shared_ptr>> struct audio_ctx_t { // We want to change the sink for the first stream only std::unique_ptr sink_flag; + std::unique_ptr control; + bool restore_sink; platf::sink_t sink; }; @@ -72,7 +74,7 @@ auto control_shared = safe::make_shared(start_audio_control, stop_a void encodeThread(packet_queue_t packets, sample_queue_t samples, config_t config, void *channel_data) { //FIXME: Pick correct opus_stream_config_t based on config.channels - auto stream = &stream_configs[map_stream(config.channels, config.high_quality)]; + auto stream = &stream_configs[map_stream(config.channels, config.flags[config_t::HIGH_QUALITY])]; opus_t opus { opus_multistream_encoder_create( stream->sampleRate, @@ -102,7 +104,7 @@ void encodeThread(packet_queue_t packets, sample_queue_t samples, config_t confi void capture(safe::signal_t *shutdown_event, packet_queue_t packets, config_t config, void *channel_data) { //FIXME: Pick correct opus_stream_config_t based on config.channels - auto stream = &stream_configs[map_stream(config.channels, config.high_quality)]; + auto stream = &stream_configs[map_stream(config.channels, config.flags[config_t::HIGH_QUALITY])]; auto ref = control_shared.ref(); if(!ref) { @@ -116,7 +118,8 @@ void capture(safe::signal_t *shutdown_event, packet_queue_t packets, config_t co return; } - std::string *sink = &ref->sink.host; + std::string *sink = + config::audio.sink.empty() ? &ref->sink.host : &config::audio.sink; if(ref->sink.null) { auto &null = *ref->sink.null; switch(stream->channelCount) { @@ -132,12 +135,14 @@ void capture(safe::signal_t *shutdown_event, packet_queue_t packets, config_t co } } - // Only the first may change the default sink - if( - !ref->sink_flag->exchange(true, std::memory_order_acquire) && - control->set_sink(*sink)) { + // Only the first to start a session may change the default sink + if(!ref->sink_flag->exchange(true, std::memory_order_acquire)) { + ref->restore_sink = !config.flags[config_t::HOST_AUDIO]; - return; + // If the client requests audio on the host, don't change the default sink + if(!config.flags[config_t::HOST_AUDIO] && control->set_sink(*sink)) { + return; + } } auto samples = std::make_shared(30); @@ -212,11 +217,18 @@ int start_audio_control(audio_ctx_t &ctx) { return -1; } + // The default sink has not been replaced yet. + ctx.restore_sink = false; + ctx.sink = std::move(*sink); return 0; } void stop_audio_control(audio_ctx_t &ctx) { // restore audio-sink if applicable + if(!ctx.restore_sink) { + return; + } + const std::string &sink = config::audio.sink.empty() ? ctx.sink.host : config::audio.sink; if(!sink.empty()) { // Best effort, it's allowed to fail diff --git a/sunshine/audio.h b/sunshine/audio.h index 01f145af..27018423 100644 --- a/sunshine/audio.h +++ b/sunshine/audio.h @@ -24,11 +24,17 @@ struct opus_stream_config_t { extern opus_stream_config_t stream_configs[MAX_STREAM_CONFIG]; struct config_t { + enum flags_e : int { + HIGH_QUALITY, + HOST_AUDIO, + MAX_FLAGS + }; + int packetDuration; int channels; int mask; - bool high_quality; + std::bitset flags; }; using packet_t = util::buffer_t; diff --git a/sunshine/nvhttp.cpp b/sunshine/nvhttp.cpp index 8f9068d7..ebf6468a 100644 --- a/sunshine/nvhttp.cpp +++ b/sunshine/nvhttp.cpp @@ -164,6 +164,20 @@ void update_id_client(const std::string &uniqueID, std::string &&cert, op_e op) } } +stream::launch_session_t make_launch_session(bool host_audio, const args_t &args) { + stream::launch_session_t launch_session; + + launch_session.host_audio = host_audio; + launch_session.gcm_key = *util::from_hex(args.at("rikey"s), true); + uint32_t prepend_iv = util::endian::big(util::from_view(args.at("rikeyid"s))); + auto prepend_iv_p = (uint8_t *)&prepend_iv; + + auto next = std::copy(prepend_iv_p, prepend_iv_p + sizeof(prepend_iv), std::begin(launch_session.iv)); + std::fill(next, std::end(launch_session.iv), 0); + + return launch_session; +} + void getservercert(pair_session_t &sess, pt::ptree &tree, const std::string &pin) { if(sess.async_insert_pin.salt.size() < 32) { tree.put("root.paired", 0); @@ -343,7 +357,6 @@ void pair(std::shared_ptr> &add_cert, std::shared_ auto args = request->parse_query_string(); if(args.find("uniqueid"s) == std::end(args)) { - tree.put("root.resume", 0); tree.put("root..status_code", 400); return; @@ -523,7 +536,6 @@ void applist(resp_https_t response, req_https_t request) { auto args = request->parse_query_string(); if(args.find("uniqueid"s) == std::end(args)) { - tree.put("root.resume", 0); tree.put("root..status_code", 400); return; @@ -554,7 +566,7 @@ void applist(resp_https_t response, req_https_t request) { } } -void launch(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; @@ -574,9 +586,9 @@ void launch(resp_https_t response, req_https_t request) { auto args = request->parse_query_string(); if( - args.find("uniqueid"s) == std::end(args) || - args.find("rikey"s) == std::end(args) || 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)) { tree.put("root.resume", 0); @@ -605,23 +617,14 @@ void launch(resp_https_t response, req_https_t request) { } } - stream::launch_session_t launch_session; - - auto clientID = args.at("uniqueid"s); - launch_session.gcm_key = *util::from_hex(args.at("rikey"s), true); - uint32_t prepend_iv = util::endian::big(util::from_view(args.at("rikeyid"s))); - auto prepend_iv_p = (uint8_t *)&prepend_iv; - - auto next = std::copy(prepend_iv_p, prepend_iv_p + sizeof(prepend_iv), std::begin(launch_session.iv)); - std::fill(next, std::end(launch_session.iv), 0); - - stream::launch_session_raise(launch_session); + host_audio = util::from_view(args.at("localAudioPlayMode")); + stream::launch_session_raise(make_launch_session(host_audio, args)); tree.put("root..status_code", 200); tree.put("root.gamesession", 1); } -void resume(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; @@ -649,11 +652,8 @@ void resume(resp_https_t response, req_https_t request) { return; } - stream::launch_session_t launch_session; - auto args = request->parse_query_string(); if( - args.find("uniqueid"s) == std::end(args) || args.find("rikey"s) == std::end(args) || args.find("rikeyid"s) == std::end(args)) { @@ -663,15 +663,7 @@ void resume(resp_https_t response, req_https_t request) { return; } - auto clientID = args.at("uniqueid"s); - launch_session.gcm_key = *util::from_hex(args.at("rikey"s), true); - uint32_t prepend_iv = util::endian::big(util::from_view(args.at("rikeyid"s))); - auto prepend_iv_p = (uint8_t *)&prepend_iv; - - auto next = std::copy(prepend_iv_p, prepend_iv_p + sizeof(prepend_iv), std::begin(launch_session.iv)); - std::fill(next, std::end(launch_session.iv), 0); - - stream::launch_session_raise(launch_session); + stream::launch_session_raise(make_launch_session(host_audio, args)); tree.put("root..status_code", 200); tree.put("root.resume", 1); @@ -844,6 +836,9 @@ void start(std::shared_ptr shutdown_event) { return 1; }); + // /resume doesn't get the parameter "localAudioPlayMode" + // /launch will store it in host_audio + bool host_audio {}; https_server_t https_server { ctx, boost::asio::ssl::verify_peer | boost::asio::ssl::verify_fail_if_no_peer_cert | boost::asio::ssl::verify_client_once }; http_server_t http_server; @@ -853,9 +848,9 @@ void start(std::shared_ptr shutdown_event) { 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"] = launch; + https_server.resource["^/launch$"]["GET"] = [&host_audio](auto resp, auto req) { launch(host_audio, resp, req); }; https_server.resource["^/pin/([0-9]+)$"]["GET"] = pin; - https_server.resource["^/resume$"]["GET"] = resume; + 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; diff --git a/sunshine/rtsp.cpp b/sunshine/rtsp.cpp index 1dae7c94..5b46fa76 100644 --- a/sunshine/rtsp.cpp +++ b/sunshine/rtsp.cpp @@ -69,6 +69,7 @@ public: } void session_raise(launch_session_t launch_session) { + //FIXME: If client abandons us at this stage, _slot_count won't be raised again. --_slot_count; launch_event.raise(launch_session); } @@ -408,12 +409,16 @@ void cmd_announce(rtsp_server_t *server, net::peer_t peer, msg_t &&req) { args.try_emplace("x-nv-aqos.packetDuration"sv, "5"sv); config_t config; + + config.audio.flags[audio::config_t::HOST_AUDIO] = launch_session->host_audio; try { config.audio.channels = util::from_view(args.at("x-nv-audio.surround.numChannels"sv)); config.audio.mask = util::from_view(args.at("x-nv-audio.surround.channelMask"sv)); - config.audio.high_quality = util::from_view(args.at("x-nv-audio.surround.AudioQuality"sv)); config.audio.packetDuration = util::from_view(args.at("x-nv-aqos.packetDuration"sv)); + config.audio.flags[audio::config_t::HIGH_QUALITY] = + util::from_view(args.at("x-nv-audio.surround.AudioQuality"sv)); + config.packetsize = util::from_view(args.at("x-nv-video[0].packetSize"sv)); config.monitor.height = util::from_view(args.at("x-nv-video[0].clientViewportHt"sv)); diff --git a/sunshine/rtsp.h b/sunshine/rtsp.h index cab06fc4..f5e085b3 100644 --- a/sunshine/rtsp.h +++ b/sunshine/rtsp.h @@ -14,6 +14,8 @@ namespace stream { struct launch_session_t { crypto::aes_t gcm_key; crypto::aes_t iv; + + bool host_audio; }; void launch_session_raise(launch_session_t launch_session); diff --git a/sunshine/stream.h b/sunshine/stream.h index 57f69403..9c8dc5ae 100644 --- a/sunshine/stream.h +++ b/sunshine/stream.h @@ -18,7 +18,6 @@ struct config_t { video::config_t monitor; int packetsize; - bool sops; std::optional gcmap; }; From c6a0eeef3ff48e7c143524d9e90e4ea09ff719e5 Mon Sep 17 00:00:00 2001 From: Conn O'Griofa Date: Thu, 20 May 2021 00:21:38 +0100 Subject: [PATCH 25/35] Force IDR frames for libx265 encoder Parameter is needed to avoid infinite black screen with "Waiting for IDR frame" in moonlight-qt (3.1.3) on Windows. --- sunshine/video.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/sunshine/video.cpp b/sunshine/video.cpp index 1069d0e6..fd077eea 100644 --- a/sunshine/video.cpp +++ b/sunshine/video.cpp @@ -315,6 +315,7 @@ static encoder_t software { // It also looks like gop_size isn't passed on to x265, so we have to set // 'keyint=-1' in the parameters ourselves. { + { "forced-idr"s, 1 }, { "x265-params"s, "info=0:keyint=-1"s }, { "preset"s, &config::video.sw.preset }, { "tune"s, &config::video.sw.tune } }, From b119121e108e9b3fa002f12445124fc28d816792 Mon Sep 17 00:00:00 2001 From: loki Date: Fri, 21 May 2021 13:53:12 +0200 Subject: [PATCH 26/35] Change default audio device on Windows --- .clang-format | 2 +- README.md | 1 + sunshine/platform/windows/PolicyConfig.h | 164 +++ sunshine/platform/windows/audio.cpp | 1183 ++++++++++++++-------- tools/audio.cpp | 16 +- 5 files changed, 918 insertions(+), 448 deletions(-) create mode 100644 sunshine/platform/windows/PolicyConfig.h diff --git a/.clang-format b/.clang-format index a069b562..1c31d7ba 100644 --- a/.clang-format +++ b/.clang-format @@ -2,7 +2,7 @@ BasedOnStyle: LLVM AccessModifierOffset: -2 AlignAfterOpenBracket: DontAlign -AlignConsecutiveAssignments: AcrossComments +AlignConsecutiveAssignments: true AlignOperands: Align AllowAllArgumentsOnNextLine: false AllowAllConstructorInitializersOnNextLine: false diff --git a/README.md b/README.md index 627d51c6..2d7d4d9d 100644 --- a/README.md +++ b/README.md @@ -102,6 +102,7 @@ sunshine needs access to uinput to create mouse and gamepad events: - [Simple-Web-Server](https://gitlab.com/eidheim/Simple-Web-Server) - [Moonlight](https://github.com/moonlight-stream) - [Looking-Glass](https://github.com/gnif/LookingGlass) (For showing me how to properly capture frames on Windows, saving me a lot of time :) +- [Eretik](http://eretik.omegahg.com/) (For creating PolicyConfig.h, allowing me to change the default audio device on Windows programmatically) ## Application List: - You can use Environment variables in place of values diff --git a/sunshine/platform/windows/PolicyConfig.h b/sunshine/platform/windows/PolicyConfig.h new file mode 100644 index 00000000..60f36a68 --- /dev/null +++ b/sunshine/platform/windows/PolicyConfig.h @@ -0,0 +1,164 @@ +// ---------------------------------------------------------------------------- +// PolicyConfig.h +// Undocumented COM-interface IPolicyConfig. +// Use for set default audio render endpoint +// @author EreTIk +// http://eretik.omegahg.com/ +// ---------------------------------------------------------------------------- + + +#pragma once + +#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 } } +#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 } } +#endif + +DEFINE_GUID(IID_IPolicyConfig, 0xf8679f50, 0x850a, 0x41cf, 0x9c, 0x72, 0x43, 0x0f, 0x29, 0x02, 0x90, 0xc8); +DEFINE_GUID(CLSID_CPolicyConfigClient, 0x870af99c, 0x171d, 0x4f9e, 0xaf, 0x0d, 0xe6, 0x3d, 0xf4, 0x0c, 0x2b, 0xc9); + +#endif + +interface DECLSPEC_UUID("f8679f50-850a-41cf-9c72-430f290290c8") IPolicyConfig; +class DECLSPEC_UUID("870af99c-171d-4f9e-af0d-e63df40c2bc9") CPolicyConfigClient; +// ---------------------------------------------------------------------------- +// class CPolicyConfigClient +// {870af99c-171d-4f9e-af0d-e63df40c2bc9} +// +// interface IPolicyConfig +// {f8679f50-850a-41cf-9c72-430f290290c8} +// +// Query interface: +// CComPtr PolicyConfig; +// PolicyConfig.CoCreateInstance(__uuidof(CPolicyConfigClient)); +// +// @compatible: Windows 7 and Later +// ---------------------------------------------------------------------------- +interface IPolicyConfig : public IUnknown { +public: + virtual HRESULT GetMixFormat( + PCWSTR, + WAVEFORMATEX **); + + virtual HRESULT STDMETHODCALLTYPE GetDeviceFormat( + PCWSTR, + INT, + WAVEFORMATEX **); + + virtual HRESULT STDMETHODCALLTYPE ResetDeviceFormat( + PCWSTR); + + virtual HRESULT STDMETHODCALLTYPE SetDeviceFormat( + PCWSTR, + WAVEFORMATEX *, + WAVEFORMATEX *); + + virtual HRESULT STDMETHODCALLTYPE GetProcessingPeriod( + PCWSTR, + INT, + PINT64, + PINT64); + + virtual HRESULT STDMETHODCALLTYPE SetProcessingPeriod( + PCWSTR, + PINT64); + + virtual HRESULT STDMETHODCALLTYPE GetShareMode( + PCWSTR, + struct DeviceShareMode *); + + virtual HRESULT STDMETHODCALLTYPE SetShareMode( + PCWSTR, + struct DeviceShareMode *); + + virtual HRESULT STDMETHODCALLTYPE GetPropertyValue( + PCWSTR, + const PROPERTYKEY &, + PROPVARIANT *); + + virtual HRESULT STDMETHODCALLTYPE SetPropertyValue( + PCWSTR, + const PROPERTYKEY &, + PROPVARIANT *); + + virtual HRESULT STDMETHODCALLTYPE SetDefaultEndpoint( + PCWSTR wszDeviceId, + ERole eRole); + + virtual HRESULT STDMETHODCALLTYPE SetEndpointVisibility( + PCWSTR, + INT); +}; + +interface DECLSPEC_UUID("568b9108-44bf-40b4-9006-86afe5b5a620") IPolicyConfigVista; +class DECLSPEC_UUID("294935CE-F637-4E7C-A41B-AB255460B862") CPolicyConfigVistaClient; +// ---------------------------------------------------------------------------- +// class CPolicyConfigVistaClient +// {294935CE-F637-4E7C-A41B-AB255460B862} +// +// interface IPolicyConfigVista +// {568b9108-44bf-40b4-9006-86afe5b5a620} +// +// Query interface: +// CComPtr PolicyConfig; +// PolicyConfig.CoCreateInstance(__uuidof(CPolicyConfigVistaClient)); +// +// @compatible: Windows Vista and Later +// ---------------------------------------------------------------------------- +interface IPolicyConfigVista : public IUnknown { +public: + virtual HRESULT GetMixFormat( + PCWSTR, + WAVEFORMATEX **); // not available on Windows 7, use method from IPolicyConfig + + virtual HRESULT STDMETHODCALLTYPE GetDeviceFormat( + PCWSTR, + INT, + WAVEFORMATEX **); + + virtual HRESULT STDMETHODCALLTYPE SetDeviceFormat( + PCWSTR, + WAVEFORMATEX *, + WAVEFORMATEX *); + + virtual HRESULT STDMETHODCALLTYPE GetProcessingPeriod( + PCWSTR, + INT, + PINT64, + PINT64); // not available on Windows 7, use method from IPolicyConfig + + virtual HRESULT STDMETHODCALLTYPE SetProcessingPeriod( + PCWSTR, + PINT64); // not available on Windows 7, use method from IPolicyConfig + + virtual HRESULT STDMETHODCALLTYPE GetShareMode( + PCWSTR, + struct DeviceShareMode *); // not available on Windows 7, use method from IPolicyConfig + + virtual HRESULT STDMETHODCALLTYPE SetShareMode( + PCWSTR, + struct DeviceShareMode *); // not available on Windows 7, use method from IPolicyConfig + + virtual HRESULT STDMETHODCALLTYPE GetPropertyValue( + PCWSTR, + const PROPERTYKEY &, + PROPVARIANT *); + + virtual HRESULT STDMETHODCALLTYPE SetPropertyValue( + PCWSTR, + const PROPERTYKEY &, + PROPVARIANT *); + + virtual HRESULT STDMETHODCALLTYPE SetDefaultEndpoint( + PCWSTR wszDeviceId, + ERole eRole); + + virtual HRESULT STDMETHODCALLTYPE SetEndpointVisibility( + PCWSTR, + INT); // not available on Windows 7, use method from IPolicyConfig +}; + +// ---------------------------------------------------------------------------- diff --git a/sunshine/platform/windows/audio.cpp b/sunshine/platform/windows/audio.cpp index d59efb50..b054aa27 100644 --- a/sunshine/platform/windows/audio.cpp +++ b/sunshine/platform/windows/audio.cpp @@ -1,444 +1,739 @@ -// -// Created by loki on 1/12/20. -// - -#include -#include -#include - -#include - -#include - -#include "sunshine/config.h" -#include "sunshine/main.h" -#include "sunshine/platform/common.h" - -const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator); -const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator); -const IID IID_IAudioClient = __uuidof(IAudioClient); -const IID IID_IAudioCaptureClient = __uuidof(IAudioCaptureClient); - -using namespace std::literals; -namespace platf::audio { -template -void Release(T *p) { - p->Release(); -} - -template -void co_task_free(T *p) { - CoTaskMemFree((LPVOID)p); -} - -using device_enum_t = util::safe_ptr>; -using device_t = util::safe_ptr>; -using audio_client_t = util::safe_ptr>; -using audio_capture_t = util::safe_ptr>; -using wave_format_t = util::safe_ptr>; -using handle_t = util::safe_ptr_v2; - -class co_init_t : public deinit_t { -public: - co_init_t() { - CoInitializeEx(nullptr, COINIT_MULTITHREADED | COINIT_SPEED_OVER_MEMORY); - } - - ~co_init_t() override { - CoUninitialize(); - } -}; - -struct format_t { - std::string_view name; - int channels; - int channel_mask; -} formats[] { - { "Stereo"sv, - 2, - SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT }, - { "Mono"sv, - 1, - SPEAKER_FRONT_CENTER }, - { "Surround 5.1"sv, - 6, - SPEAKER_FRONT_LEFT | - SPEAKER_FRONT_RIGHT | - SPEAKER_FRONT_CENTER | - SPEAKER_LOW_FREQUENCY | - SPEAKER_BACK_LEFT | - SPEAKER_BACK_RIGHT } -}; - -void set_wave_format(audio::wave_format_t &wave_format, const format_t &format) { - wave_format->nChannels = format.channels; - wave_format->nBlockAlign = wave_format->nChannels * wave_format->wBitsPerSample / 8; - wave_format->nAvgBytesPerSec = wave_format->nSamplesPerSec * wave_format->nBlockAlign; - - if(wave_format->wFormatTag == WAVE_FORMAT_EXTENSIBLE) { - ((PWAVEFORMATEXTENSIBLE)wave_format.get())->dwChannelMask = format.channel_mask; - } -} - -void surround51_to_stereo(std::vector &sample_in, const util::buffer_t &sample_out) { - enum surround51_e : int { - front_left, - front_right, - front_center, - low_frequency, // subwoofer - back_left, - back_right, - channels51 // number of channels in surround sound - }; - - auto sample_in_pos = std::begin(sample_in); - auto sample_end = std::begin(sample_out) + sample_in.size() / 2 * channels51; - - for(auto sample_out_p = std::begin(sample_out); sample_out_p != sample_end; sample_out_p += channels51) { - std::uint32_t left {}, right {}; - - left += sample_out_p[front_left]; - left += sample_out_p[front_center] * 90 / 100; - left += sample_out_p[low_frequency] * 30 / 100; - left += sample_out_p[back_left] * 70 / 100; - left += sample_out_p[back_right] * 30 / 100; - - right += sample_out_p[front_right]; - right += sample_out_p[front_center] * 90 / 100; - right += sample_out_p[low_frequency] * 30 / 100; - right += sample_out_p[back_left] * 30 / 100; - right += sample_out_p[back_right] * 70 / 100; - ; - - *sample_in_pos++ = (std::uint16_t)left; - *sample_in_pos++ = (std::uint16_t)right; - } -} - -void mono_to_stereo(std::vector &sample_in, const util::buffer_t &sample_out) { - auto sample_in_pos = std::begin(sample_in); - auto sample_end = std::begin(sample_out) + sample_in.size() / 2; - - for(auto sample_out_p = std::begin(sample_out); sample_out_p != sample_end; ++sample_out_p) { - *sample_in_pos++ = *sample_out_p; - *sample_in_pos++ = *sample_out_p; - } -} - -audio_client_t make_audio_client(device_t &device, const format_t &format, int sample_rate) { - audio_client_t audio_client; - auto status = device->Activate( - IID_IAudioClient, - CLSCTX_ALL, - nullptr, - (void **)&audio_client); - - if(FAILED(status)) { - BOOST_LOG(error) << "Couldn't activate Device: [0x"sv << util::hex(status).to_string_view() << ']'; - - return nullptr; - } - - wave_format_t wave_format; - status = audio_client->GetMixFormat(&wave_format); - - if(FAILED(status)) { - BOOST_LOG(error) << "Couldn't acquire Wave Format [0x"sv << util::hex(status).to_string_view() << ']'; - - return nullptr; - } - - wave_format->wBitsPerSample = 16; - wave_format->nSamplesPerSec = sample_rate; - switch(wave_format->wFormatTag) { - case WAVE_FORMAT_PCM: - break; - case WAVE_FORMAT_IEEE_FLOAT: - break; - case WAVE_FORMAT_EXTENSIBLE: { - auto wave_ex = (PWAVEFORMATEXTENSIBLE)wave_format.get(); - if(IsEqualGUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, wave_ex->SubFormat)) { - wave_ex->SubFormat = KSDATAFORMAT_SUBTYPE_PCM; - wave_ex->Samples.wValidBitsPerSample = 16; - break; - } - - BOOST_LOG(error) << "Unsupported Sub Format for WAVE_FORMAT_EXTENSIBLE: [0x"sv << util::hex(wave_ex->SubFormat).to_string_view() << ']'; - } - default: - BOOST_LOG(error) << "Unsupported Wave Format: [0x"sv << util::hex(wave_format->wFormatTag).to_string_view() << ']'; - return nullptr; - }; - - set_wave_format(wave_format, format); - - status = audio_client->Initialize( - AUDCLNT_SHAREMODE_SHARED, - AUDCLNT_STREAMFLAGS_LOOPBACK | AUDCLNT_STREAMFLAGS_EVENTCALLBACK, - 0, 0, - wave_format.get(), - nullptr); - - if(status) { - BOOST_LOG(debug) << "Couldn't initialize audio client for ["sv << format.name << "]: [0x"sv << util::hex(status).to_string_view() << ']'; - return nullptr; - } - - return audio_client; -} - -class mic_wasapi_t : public mic_t { -public: - capture_e sample(std::vector &sample_in) override { - auto sample_size = sample_in.size() / 2 * format->channels; - while(sample_buf_pos - std::begin(sample_buf) < sample_size) { - //FIXME: Use IAudioClient3 instead of IAudioClient, that would allows for adjusting the latency of the audio samples - auto capture_result = _fill_buffer(); - - if(capture_result != capture_e::ok) { - return capture_result; - } - } - - switch(format->channels) { - case 1: - mono_to_stereo(sample_in, sample_buf); - break; - case 2: - std::copy_n(std::begin(sample_buf), sample_size, std::begin(sample_in)); - break; - case 6: - surround51_to_stereo(sample_in, sample_buf); - break; - default: - BOOST_LOG(error) << '[' << format->name << "] not yet supported"sv; - return capture_e::error; - } - - // The excess samples should be in front of the queue - std::move(&sample_buf[sample_size], sample_buf_pos, std::begin(sample_buf)); - sample_buf_pos -= sample_size; - - return capture_e::ok; - } - - - int init(std::uint32_t sample_rate, std::uint32_t frame_size) { - audio_event.reset(CreateEventA(nullptr, FALSE, FALSE, nullptr)); - if(!audio_event) { - BOOST_LOG(error) << "Couldn't create Event handle"sv; - - return -1; - } - - HRESULT status; - - status = CoCreateInstance( - CLSID_MMDeviceEnumerator, - nullptr, - CLSCTX_ALL, - IID_IMMDeviceEnumerator, - (void **)&device_enum); - - if(FAILED(status)) { - BOOST_LOG(error) << "Couldn't create Device Enumerator [0x"sv << util::hex(status).to_string_view() << ']'; - - return -1; - } - - if(config::audio.sink.empty()) { - status = device_enum->GetDefaultAudioEndpoint( - eRender, - eConsole, - &device); - } - else { - std::wstring_convert, wchar_t> converter; - auto wstring_device_id = converter.from_bytes(config::audio.sink); - - status = device_enum->GetDevice(wstring_device_id.c_str(), &device); - } - - if(FAILED(status)) { - BOOST_LOG(error) << "Couldn't create audio Device [0x"sv << util::hex(status).to_string_view() << ']'; - - return -1; - } - - for(auto &format : formats) { - BOOST_LOG(debug) << "Trying audio format ["sv << format.name << ']'; - audio_client = make_audio_client(device, format, sample_rate); - - if(audio_client) { - BOOST_LOG(debug) << "Found audio format ["sv << format.name << ']'; - this->format = &format; - break; - } - } - - if(!audio_client) { - BOOST_LOG(error) << "Couldn't find supported format for audio"sv; - return -1; - } - - REFERENCE_TIME default_latency; - audio_client->GetDevicePeriod(&default_latency, nullptr); - default_latency_ms = default_latency / 1000; - - std::uint32_t frames; - status = audio_client->GetBufferSize(&frames); - if(FAILED(status)) { - BOOST_LOG(error) << "Couldn't acquire the number of audio frames [0x"sv << util::hex(status).to_string_view() << ']'; - - return -1; - } - - // *2 --> needs to fit double - sample_buf = util::buffer_t { std::max(frames * 2, frame_size * format->channels * 2) }; - sample_buf_pos = std::begin(sample_buf); - - status = audio_client->GetService(IID_IAudioCaptureClient, (void **)&audio_capture); - if(FAILED(status)) { - BOOST_LOG(error) << "Couldn't initialize audio capture client [0x"sv << util::hex(status).to_string_view() << ']'; - - return -1; - } - - status = audio_client->SetEventHandle(audio_event.get()); - if(FAILED(status)) { - BOOST_LOG(error) << "Couldn't set event handle [0x"sv << util::hex(status).to_string_view() << ']'; - - return -1; - } - - status = audio_client->Start(); - if(FAILED(status)) { - BOOST_LOG(error) << "Couldn't start recording [0x"sv << util::hex(status).to_string_view() << ']'; - - return -1; - } - - return 0; - } - - ~mic_wasapi_t() override { - if(audio_client) { - audio_client->Stop(); - } - } - -private: - capture_e _fill_buffer() { - HRESULT status; - - // Total number of samples - struct sample_aligned_t { - std::uint32_t uninitialized; - std::int16_t *samples; - } sample_aligned; - - // number of samples / number of channels - struct block_aligned_t { - std::uint32_t audio_sample_size; - } block_aligned; - - status = WaitForSingleObjectEx(audio_event.get(), default_latency_ms, FALSE); - switch(status) { - case WAIT_OBJECT_0: - break; - case WAIT_TIMEOUT: - return capture_e::timeout; - default: - BOOST_LOG(error) << "Couldn't wait for audio event: [0x"sv << util::hex(status).to_string_view() << ']'; - return capture_e::error; - } - - std::uint32_t packet_size {}; - for( - status = audio_capture->GetNextPacketSize(&packet_size); - SUCCEEDED(status) && packet_size > 0; - 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); - - switch(status) { - case S_OK: - break; - case AUDCLNT_E_DEVICE_INVALIDATED: - return capture_e::reinit; - default: - BOOST_LOG(error) << "Couldn't capture audio [0x"sv << util::hex(status).to_string_view() << ']'; - return capture_e::error; - } - - sample_aligned.uninitialized = std::end(sample_buf) - sample_buf_pos; - auto n = std::min(sample_aligned.uninitialized, block_aligned.audio_sample_size * format->channels); - - if(buffer_flags & AUDCLNT_BUFFERFLAGS_SILENT) { - std::fill_n(sample_buf_pos, n, 0); - } - else { - std::copy_n(sample_aligned.samples, n, sample_buf_pos); - } - - sample_buf_pos += n; - - audio_capture->ReleaseBuffer(block_aligned.audio_sample_size); - } - - if(status == AUDCLNT_E_DEVICE_INVALIDATED) { - return capture_e::reinit; - } - - if(FAILED(status)) { - return capture_e::error; - } - - return capture_e::ok; - } - -public: - handle_t audio_event; - - device_enum_t device_enum; - device_t device; - audio_client_t audio_client; - audio_capture_t audio_capture; - - REFERENCE_TIME default_latency_ms; - - util::buffer_t sample_buf; - std::int16_t *sample_buf_pos; - - format_t *format; -}; -} // namespace platf::audio - -namespace platf { - -// It's not big enough to justify it's own source file :/ -namespace dxgi { -int init(); -} - -std::unique_ptr microphone(std::uint32_t sample_rate, std::uint32_t frame_size) { - auto mic = std::make_unique(); - - if(mic->init(sample_rate, frame_size)) { - return nullptr; - } - - return mic; -} - -std::unique_ptr init() { - if(dxgi::init()) { - return nullptr; - } - return std::make_unique(); -} -} // namespace platf +// +// Created by loki on 1/12/20. +// + +#include +#include +#include + +#include + +#include + +#define INITGUID +#include +#undef INITGUID + +#include "sunshine/config.h" +#include "sunshine/main.h" +#include "sunshine/platform/common.h" + +// Must be the last included file +// clang-format off +#include "PolicyConfig.h" +// clang-format on + +DEFINE_PROPERTYKEY(PKEY_Device_DeviceDesc, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 2); // DEVPROP_TYPE_STRING +DEFINE_PROPERTYKEY(PKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 14); // DEVPROP_TYPE_STRING +DEFINE_PROPERTYKEY(PKEY_DeviceInterface_FriendlyName, 0x026e516e, 0xb814, 0x414b, 0x83, 0xcd, 0x85, 0x6d, 0x6f, 0xef, 0x48, 0x22, 2); + +const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator); +const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator); +const IID IID_IAudioClient = __uuidof(IAudioClient); +const IID IID_IAudioCaptureClient = __uuidof(IAudioCaptureClient); + +using namespace std::literals; +namespace platf::audio { +constexpr auto SAMPLE_RATE = 48000; + +template +void Release(T *p) { + p->Release(); +} + +template +void co_task_free(T *p) { + CoTaskMemFree((LPVOID)p); +} + +using device_enum_t = util::safe_ptr>; +using device_t = util::safe_ptr>; +using collection_t = util::safe_ptr>; +using audio_client_t = util::safe_ptr>; +using audio_capture_t = util::safe_ptr>; +using wave_format_t = util::safe_ptr>; +using wstring_t = util::safe_ptr>; +using handle_t = util::safe_ptr_v2; +using policy_t = util::safe_ptr>; +using prop_t = util::safe_ptr>; + +class co_init_t : public deinit_t { +public: + co_init_t() { + CoInitializeEx(nullptr, COINIT_MULTITHREADED | COINIT_SPEED_OVER_MEMORY); + } + + ~co_init_t() override { + CoUninitialize(); + } +}; + +class prop_var_t { +public: + prop_var_t() { + PropVariantInit(&prop); + } + + ~prop_var_t() { + PropVariantClear(&prop); + } + + PROPVARIANT prop; +}; + +static std::wstring_convert, wchar_t> converter; +struct format_t { + enum type_e : int { + none, + mono, + stereo, + surr51, + surr71, + } type; + + std::string_view name; + int channels; + int channel_mask; +} formats[] { + { + format_t::mono, + "Mono"sv, + 1, + SPEAKER_FRONT_CENTER, + }, + { + format_t::stereo, + "Stereo"sv, + 2, + SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT, + }, + { + format_t::surr51, + "Surround 5.1"sv, + 6, + SPEAKER_FRONT_LEFT | + SPEAKER_FRONT_RIGHT | + SPEAKER_FRONT_CENTER | + SPEAKER_LOW_FREQUENCY | + SPEAKER_BACK_LEFT | + SPEAKER_BACK_RIGHT, + }, + { + format_t::surr71, + "Surround 7.1"sv, + 8, + SPEAKER_FRONT_LEFT | + SPEAKER_FRONT_RIGHT | + SPEAKER_FRONT_CENTER | + SPEAKER_LOW_FREQUENCY | + SPEAKER_BACK_LEFT | + SPEAKER_BACK_RIGHT | + SPEAKER_SIDE_LEFT | + SPEAKER_SIDE_RIGHT, + }, +}; + +void set_wave_format(audio::wave_format_t &wave_format, const format_t &format) { + wave_format->nChannels = format.channels; + wave_format->nBlockAlign = wave_format->nChannels * wave_format->wBitsPerSample / 8; + wave_format->nAvgBytesPerSec = wave_format->nSamplesPerSec * wave_format->nBlockAlign; + + if(wave_format->wFormatTag == WAVE_FORMAT_EXTENSIBLE) { + ((PWAVEFORMATEXTENSIBLE)wave_format.get())->dwChannelMask = format.channel_mask; + } +} + +int init_wave_format(audio::wave_format_t &wave_format, DWORD sample_rate) { + wave_format->wBitsPerSample = 16; + wave_format->nSamplesPerSec = sample_rate; + switch(wave_format->wFormatTag) { + case WAVE_FORMAT_PCM: + break; + case WAVE_FORMAT_IEEE_FLOAT: + break; + case WAVE_FORMAT_EXTENSIBLE: { + auto wave_ex = (PWAVEFORMATEXTENSIBLE)wave_format.get(); + if(IsEqualGUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, wave_ex->SubFormat)) { + wave_ex->Samples.wValidBitsPerSample = 16; + wave_ex->SubFormat = KSDATAFORMAT_SUBTYPE_PCM; + break; + } + + BOOST_LOG(error) << "Unsupported Sub Format for WAVE_FORMAT_EXTENSIBLE: [0x"sv << util::hex(wave_ex->SubFormat).to_string_view() << ']'; + } + default: + BOOST_LOG(error) << "Unsupported Wave Format: [0x"sv << util::hex(wave_format->wFormatTag).to_string_view() << ']'; + return -1; + }; + + return 0; +} + +void surround51_to_stereo(std::vector &sample_in, const util::buffer_t &sample_out) { + enum surround51_e : int { + front_left, + front_right, + front_center, + low_frequency, // subwoofer + back_left, + back_right, + channels51 // number of channels in surround sound + }; + + auto sample_in_pos = std::begin(sample_in); + auto sample_end = std::begin(sample_out) + sample_in.size() / 2 * channels51; + + for(auto sample_out_p = std::begin(sample_out); sample_out_p != sample_end; sample_out_p += channels51) { + std::uint32_t left {}, right {}; + + left += sample_out_p[front_left]; + left += sample_out_p[front_center] * 90 / 100; + left += sample_out_p[low_frequency] * 30 / 100; + left += sample_out_p[back_left] * 70 / 100; + left += sample_out_p[back_right] * 30 / 100; + + right += sample_out_p[front_right]; + right += sample_out_p[front_center] * 90 / 100; + right += sample_out_p[low_frequency] * 30 / 100; + right += sample_out_p[back_left] * 30 / 100; + right += sample_out_p[back_right] * 70 / 100; + + *sample_in_pos++ = (std::uint16_t)left; + *sample_in_pos++ = (std::uint16_t)right; + } +} + +void mono_to_stereo(std::vector &sample_in, const util::buffer_t &sample_out) { + auto sample_in_pos = std::begin(sample_in); + auto sample_end = std::begin(sample_out) + sample_in.size() / 2; + + for(auto sample_out_p = std::begin(sample_out); sample_out_p != sample_end; ++sample_out_p) { + *sample_in_pos++ = *sample_out_p; + *sample_in_pos++ = *sample_out_p; + } +} + +audio_client_t make_audio_client(device_t &device, const format_t &format, int sample_rate) { + audio_client_t audio_client; + auto status = device->Activate( + IID_IAudioClient, + CLSCTX_ALL, + nullptr, + (void **)&audio_client); + + if(FAILED(status)) { + BOOST_LOG(error) << "Couldn't activate Device: [0x"sv << util::hex(status).to_string_view() << ']'; + + return nullptr; + } + + wave_format_t wave_format; + status = audio_client->GetMixFormat(&wave_format); + if(FAILED(status)) { + BOOST_LOG(error) << "Couldn't acquire Wave Format [0x"sv << util::hex(status).to_string_view() << ']'; + + return nullptr; + } + + if(init_wave_format(wave_format, sample_rate)) { + return nullptr; + } + set_wave_format(wave_format, format); + + status = audio_client->Initialize( + AUDCLNT_SHAREMODE_SHARED, + AUDCLNT_STREAMFLAGS_LOOPBACK | AUDCLNT_STREAMFLAGS_EVENTCALLBACK, + 0, 0, + wave_format.get(), + nullptr); + + if(status) { + BOOST_LOG(debug) << "Couldn't initialize audio client for ["sv << format.name << "]: [0x"sv << util::hex(status).to_string_view() << ']'; + return nullptr; + } + + return audio_client; +} + +const wchar_t *no_null(const wchar_t *str) { + return str ? str : L"Unknown"; +} + +format_t::type_e validate_device(device_t &device) { + for(const auto &format : formats) { + // Ensure WaveFromat is compatible + auto audio_client = make_audio_client(device, format, SAMPLE_RATE); + + BOOST_LOG(debug) << format.name << ": "sv << !audio_client ? "unsupported"sv : "supported"sv; + + if(audio_client) { + return format.type; + } + } + + return format_t::none; +} + +device_t default_device(device_enum_t &device_enum) { + device_t device; + HRESULT status; + status = device_enum->GetDefaultAudioEndpoint( + eRender, + eConsole, + &device); + + + if(FAILED(status)) { + BOOST_LOG(error) << "Couldn't create audio Device [0x"sv << util::hex(status).to_string_view() << ']'; + + return nullptr; + } + + return device; +} + +class mic_wasapi_t : public mic_t { +public: + capture_e sample(std::vector &sample_in) override { + auto sample_size = sample_in.size() / 2 * format->channels; + while(sample_buf_pos - std::begin(sample_buf) < sample_size) { + //FIXME: Use IAudioClient3 instead of IAudioClient, that would allows for adjusting the latency of the audio samples + auto capture_result = _fill_buffer(); + + if(capture_result != capture_e::ok) { + return capture_result; + } + } + + switch(format->channels) { + case 1: + mono_to_stereo(sample_in, sample_buf); + break; + case 2: + std::copy_n(std::begin(sample_buf), sample_size, std::begin(sample_in)); + break; + case 6: + surround51_to_stereo(sample_in, sample_buf); + break; + default: + BOOST_LOG(error) << '[' << format->name << "] not yet supported"sv; + return capture_e::error; + } + + // The excess samples should be in front of the queue + std::move(&sample_buf[sample_size], sample_buf_pos, std::begin(sample_buf)); + sample_buf_pos -= sample_size; + + return capture_e::ok; + } + + + int init(std::uint32_t sample_rate, std::uint32_t frame_size) { + audio_event.reset(CreateEventA(nullptr, FALSE, FALSE, nullptr)); + if(!audio_event) { + BOOST_LOG(error) << "Couldn't create Event handle"sv; + + return -1; + } + + HRESULT status; + + status = CoCreateInstance( + CLSID_MMDeviceEnumerator, + nullptr, + CLSCTX_ALL, + IID_IMMDeviceEnumerator, + (void **)&device_enum); + + if(FAILED(status)) { + BOOST_LOG(error) << "Couldn't create Device Enumerator [0x"sv << util::hex(status).to_string_view() << ']'; + + return -1; + } + + auto device = default_device(device_enum); + if(!device) { + return -1; + } + + for(auto &format : formats) { + BOOST_LOG(debug) << "Trying audio format ["sv << format.name << ']'; + audio_client = make_audio_client(device, format, sample_rate); + + if(audio_client) { + BOOST_LOG(debug) << "Found audio format ["sv << format.name << ']'; + this->format = &format; + break; + } + } + + if(!audio_client) { + BOOST_LOG(error) << "Couldn't find supported format for audio"sv; + return -1; + } + + REFERENCE_TIME default_latency; + audio_client->GetDevicePeriod(&default_latency, nullptr); + default_latency_ms = default_latency / 1000; + + std::uint32_t frames; + status = audio_client->GetBufferSize(&frames); + if(FAILED(status)) { + BOOST_LOG(error) << "Couldn't acquire the number of audio frames [0x"sv << util::hex(status).to_string_view() << ']'; + + return -1; + } + + // *2 --> needs to fit double + sample_buf = util::buffer_t { std::max(frames * 2, frame_size * format->channels * 2) }; + sample_buf_pos = std::begin(sample_buf); + + status = audio_client->GetService(IID_IAudioCaptureClient, (void **)&audio_capture); + if(FAILED(status)) { + BOOST_LOG(error) << "Couldn't initialize audio capture client [0x"sv << util::hex(status).to_string_view() << ']'; + + return -1; + } + + status = audio_client->SetEventHandle(audio_event.get()); + if(FAILED(status)) { + BOOST_LOG(error) << "Couldn't set event handle [0x"sv << util::hex(status).to_string_view() << ']'; + + return -1; + } + + status = audio_client->Start(); + if(FAILED(status)) { + BOOST_LOG(error) << "Couldn't start recording [0x"sv << util::hex(status).to_string_view() << ']'; + + return -1; + } + + return 0; + } + + ~mic_wasapi_t() override { + if(audio_client) { + audio_client->Stop(); + } + } + +private: + capture_e _fill_buffer() { + HRESULT status; + + // Total number of samples + struct sample_aligned_t { + std::uint32_t uninitialized; + std::int16_t *samples; + } sample_aligned; + + // number of samples / number of channels + struct block_aligned_t { + std::uint32_t audio_sample_size; + } block_aligned; + + status = WaitForSingleObjectEx(audio_event.get(), default_latency_ms, FALSE); + switch(status) { + case WAIT_OBJECT_0: + break; + case WAIT_TIMEOUT: + return capture_e::timeout; + default: + BOOST_LOG(error) << "Couldn't wait for audio event: [0x"sv << util::hex(status).to_string_view() << ']'; + return capture_e::error; + } + + std::uint32_t packet_size {}; + for( + status = audio_capture->GetNextPacketSize(&packet_size); + SUCCEEDED(status) && packet_size > 0; + 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); + + switch(status) { + case S_OK: + break; + case AUDCLNT_E_DEVICE_INVALIDATED: + return capture_e::reinit; + default: + BOOST_LOG(error) << "Couldn't capture audio [0x"sv << util::hex(status).to_string_view() << ']'; + return capture_e::error; + } + + sample_aligned.uninitialized = std::end(sample_buf) - sample_buf_pos; + auto n = std::min(sample_aligned.uninitialized, block_aligned.audio_sample_size * format->channels); + + if(buffer_flags & AUDCLNT_BUFFERFLAGS_SILENT) { + std::fill_n(sample_buf_pos, n, 0); + } + else { + std::copy_n(sample_aligned.samples, n, sample_buf_pos); + } + + sample_buf_pos += n; + + audio_capture->ReleaseBuffer(block_aligned.audio_sample_size); + } + + if(status == AUDCLNT_E_DEVICE_INVALIDATED) { + return capture_e::reinit; + } + + if(FAILED(status)) { + return capture_e::error; + } + + return capture_e::ok; + } + +public: + handle_t audio_event; + + device_enum_t device_enum; + device_t device; + audio_client_t audio_client; + audio_capture_t audio_capture; + + REFERENCE_TIME default_latency_ms; + + util::buffer_t sample_buf; + std::int16_t *sample_buf_pos; + + format_t *format; +}; + +class audio_control_t : public ::platf::audio_control_t { +public: + std::optional sink_info() override { + auto virtual_adapter_name = L"Steam Streaming Speakers"sv; + + sink_t sink; + + audio::device_enum_t device_enum; + auto status = CoCreateInstance( + CLSID_MMDeviceEnumerator, + nullptr, + CLSCTX_ALL, + IID_IMMDeviceEnumerator, + (void **)&device_enum); + + if(FAILED(status)) { + BOOST_LOG(error) << "Couldn't create Device Enumerator: [0x"sv << util::hex(status).to_string_view() << ']'; + + return std::nullopt; + } + + auto device = default_device(device_enum); + if(!device) { + return std::nullopt; + } + + audio::wstring_t wstring; + device->GetId(&wstring); + + sink.host = converter.to_bytes(wstring.get()); + + collection_t collection; + status = device_enum->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &collection); + if(FAILED(status)) { + BOOST_LOG(error) << "Couldn't enumerate: [0x"sv << util::hex(status).to_string_view() << ']'; + + return std::nullopt; + } + + UINT count; + collection->GetCount(&count); + + std::string virtual_device_id; + BOOST_LOG(debug) << "====== Found "sv << count << " potential audio devices ======"sv; + for(auto x = 0; x < count; ++x) { + audio::device_t device; + collection->Item(x, &device); + + auto type = validate_device(device); + if(type == format_t::none) { + continue; + } + + audio::wstring_t wstring; + device->GetId(&wstring); + + audio::prop_t prop; + device->OpenPropertyStore(STGM_READ, &prop); + + prop_var_t adapter_friendly_name; + prop_var_t device_friendly_name; + prop_var_t device_desc; + + prop->GetValue(PKEY_Device_FriendlyName, &device_friendly_name.prop); + prop->GetValue(PKEY_DeviceInterface_FriendlyName, &adapter_friendly_name.prop); + prop->GetValue(PKEY_Device_DeviceDesc, &device_desc.prop); + + auto adapter_name = no_null((LPWSTR)adapter_friendly_name.prop.pszVal); + BOOST_LOG(debug) + << L"===== Device ====="sv << std::endl + << L"Device ID : "sv << wstring.get() << std::endl + << L"Device name : "sv << no_null((LPWSTR)device_friendly_name.prop.pszVal) << std::endl + << L"Adapter name : "sv << adapter_name << std::endl + << L"Device description : "sv << no_null((LPWSTR)device_desc.prop.pszVal) << std::endl + << std::endl; + + if(virtual_device_id.empty() && adapter_name == virtual_adapter_name) { + virtual_device_id = converter.to_bytes(wstring.get()); + } + } + + if(!virtual_device_id.empty()) { + sink.null = std::make_optional(sink_t::null_t { + "virtual-"s.append(formats[format_t::stereo - 1].name) + virtual_device_id, + "virtual-"s.append(formats[format_t::surr51 - 1].name) + virtual_device_id, + "virtual-"s.append(formats[format_t::surr71 - 1].name) + virtual_device_id, + }); + } + + return sink; + } + + 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)) { + return nullptr; + } + + return mic; + } + + /** + * If the requested sink is a virtual sink, meaning no speakers attached to + * the host, then we can seamlessly set the format to stereo and surround sound. + * + * Any virtual sink detected will be prefixed by: + * 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::string_view sv { sink.c_str(), sink.size() }; + + format_t::type_e type = format_t::none; + // sink format: + // [virtual-(format name)]device_id + auto prefix = "virtual-"sv; + if(sv.find(prefix) == 0) { + sv = sv.substr(prefix.size(), sv.size() - prefix.size()); + + for(auto &format : formats) { + auto &name = format.name; + if(sv.find(name) == 0) { + type = format.type; + sv = sv.substr(name.size(), sv.size() - name.size()); + + break; + } + } + } + + auto wstring_device_id = converter.from_bytes(sv.data()); + + if(type == format_t::none) { + // wstring_device_id does not contain virtual-(format name) + // It's a simple deviceId, just pass it back + return std::make_optional(std::move(wstring_device_id)); + } + + wave_format_t wave_format; + auto status = policy->GetMixFormat(wstring_device_id.c_str(), &wave_format); + if(FAILED(status)) { + BOOST_LOG(error) << "Couldn't acquire Wave Format [0x"sv << util::hex(status).to_string_view() << ']'; + + return std::nullopt; + } + + if(init_wave_format(wave_format, SAMPLE_RATE)) { + return std::nullopt; + } + set_wave_format(wave_format, formats[(int)type - 1]); + + WAVEFORMATEX p { *wave_format.get() }; + status = policy->SetDeviceFormat(wstring_device_id.c_str(), wave_format.get(), &p); + if(FAILED(status)) { + BOOST_LOG(error) << "Couldn't set Wave Format [0x"sv << util::hex(status).to_string_view() << ']'; + + return std::nullopt; + } + + return std::make_optional(std::move(wstring_device_id)); + } + + int set_sink(const std::string &sink) override { + auto wstring_device_id = set_format(sink); + if(!wstring_device_id) { + return -1; + } + + int failure {}; + for(int x = 0; x < (int)ERole_enum_count; ++x) { + auto status = policy->SetDefaultEndpoint(wstring_device_id->c_str(), (ERole)x); + if(status) { + BOOST_LOG(warning) << "Couldn't set ["sv << sink << "] to role ["sv << x << ']'; + + ++failure; + } + } + + return failure; + } + + int init() { + auto status = CoCreateInstance( + CLSID_CPolicyConfigClient, + nullptr, + CLSCTX_ALL, + IID_IPolicyConfig, + (void **)&policy); + + if(FAILED(status)) { + BOOST_LOG(error) << "Couldn't create audio policy config: [0x"sv << util::hex(status).to_string_view() << ']'; + + return -1; + } + + return 0; + } + + ~audio_control_t() override {} + + policy_t policy; +}; +} // namespace platf::audio + +namespace platf { + +// It's not big enough to justify it's own source file :/ +namespace dxgi { +int init(); +} + +std::unique_ptr audio_control() { + auto control = std::make_unique(); + + if(control->init() || control->set_sink("virtual-Stereo{0.0.0.00000000}.{8edba70c-1125-467c-b89c-15da389bc1d4}"s)) { + return nullptr; + } + + return control; +} + +std::unique_ptr init() { + if(dxgi::init()) { + return nullptr; + } + return std::make_unique(); +} +} // namespace platf diff --git a/tools/audio.cpp b/tools/audio.cpp index b99edf5f..a130d384 100644 --- a/tools/audio.cpp +++ b/tools/audio.cpp @@ -87,7 +87,17 @@ struct format_t { SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | - SPEAKER_BACK_RIGHT } + SPEAKER_BACK_RIGHT }, + { "Surround 7.1"sv, + 8, + SPEAKER_FRONT_LEFT | + SPEAKER_FRONT_RIGHT | + SPEAKER_FRONT_CENTER | + SPEAKER_LOW_FREQUENCY | + SPEAKER_BACK_LEFT | + SPEAKER_BACK_RIGHT | + SPEAKER_SIDE_LEFT | + SPEAKER_SIDE_RIGHT } }; void set_wave_format(audio::wave_format_t &wave_format, const format_t &format) { @@ -285,7 +295,7 @@ int main(int argc, char *argv[]) { } audio::collection_t collection; - status = device_enum->EnumAudioEndpoints(eRender, DEVICE_STATEMASK_ALL, &collection); + status = device_enum->EnumAudioEndpoints(eRender, device_state_filter, &collection); if(FAILED(status)) { std::cout << "Couldn't enumerate: [0x"sv << util::hex(status).to_string_view() << ']' << std::endl; @@ -296,7 +306,7 @@ int main(int argc, char *argv[]) { UINT count; collection->GetCount(&count); - std::cout << "====== Found "sv << count << " potential audio devices ======"sv << std::endl; + std::cout << "====== Found "sv << count << " audio devices ======"sv << std::endl; for(auto x = 0; x < count; ++x) { audio::device_t device; collection->Item(x, &device); From 2fb5f8a7d01db652a78ce8a662dee8cff0700e02 Mon Sep 17 00:00:00 2001 From: loki Date: Fri, 21 May 2021 14:28:24 +0200 Subject: [PATCH 27/35] Add config option for virtual_sink on windows --- assets/sunshine.conf | 6 +++++- sunshine/config.h | 1 + sunshine/platform/common.h | 1 - sunshine/platform/windows/audio.cpp | 8 ++++---- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/assets/sunshine.conf b/assets/sunshine.conf index 6dd5cecf..78f31b2d 100644 --- a/assets/sunshine.conf +++ b/assets/sunshine.conf @@ -84,7 +84,11 @@ # # !! Windows only !! # tools\audio-info.exe -# audio_sink = {0.0.0.00000000}.{FD47D9CC-4218-4135-9CE2-0C195C87405B} +# audio_sink = {0.0.0.00000000}.{FD47D9CC-4218-4135-9CE2-0C195C87405B} +# +# The virtual sink, is the audio device that's virtual (Like Steam Streaming Speakers), it allows Sunshine +# to stream audio, while muting the speakers. +# virtual_sink = {0.0.0.00000000}.{8edba70c-1125-467c-b89c-15da389bc1d4} # !! Windows only !! # You can select the video card you want to stream: diff --git a/sunshine/config.h b/sunshine/config.h index 3bced756..1b62818e 100644 --- a/sunshine/config.h +++ b/sunshine/config.h @@ -39,6 +39,7 @@ struct video_t { struct audio_t { std::string sink; + std::string virtual_sink; }; struct stream_t { diff --git a/sunshine/platform/common.h b/sunshine/platform/common.h index 978182f9..91fe185b 100644 --- a/sunshine/platform/common.h +++ b/sunshine/platform/common.h @@ -214,7 +214,6 @@ std::string from_sockaddr(const sockaddr *const); std::pair from_sockaddr_ex(const sockaddr *const); std::unique_ptr audio_control(); -std::unique_ptr microphone(std::uint32_t sample_rate, std::uint32_t frame_size); std::shared_ptr display(dev_type_e hwdevice_type); input_t input(); diff --git a/sunshine/platform/windows/audio.cpp b/sunshine/platform/windows/audio.cpp index b054aa27..1dc82016 100644 --- a/sunshine/platform/windows/audio.cpp +++ b/sunshine/platform/windows/audio.cpp @@ -259,10 +259,10 @@ const wchar_t *no_null(const wchar_t *str) { return str ? str : L"Unknown"; } -format_t::type_e validate_device(device_t &device) { +format_t::type_e validate_device(device_t &device, int sample_rate) { for(const auto &format : formats) { // Ensure WaveFromat is compatible - auto audio_client = make_audio_client(device, format, SAMPLE_RATE); + auto audio_client = make_audio_client(device, format, sample_rate); BOOST_LOG(debug) << format.name << ": "sv << !audio_client ? "unsupported"sv : "supported"sv; @@ -550,13 +550,13 @@ public: UINT count; collection->GetCount(&count); - std::string virtual_device_id; + std::string virtual_device_id = config::audio.virtual_sink; BOOST_LOG(debug) << "====== Found "sv << count << " potential audio devices ======"sv; for(auto x = 0; x < count; ++x) { audio::device_t device; collection->Item(x, &device); - auto type = validate_device(device); + auto type = validate_device(device, SAMPLE_RATE); if(type == format_t::none) { continue; } From ba07fd510ed2466d317e42498dc603ac8b5153f5 Mon Sep 17 00:00:00 2001 From: loki Date: Sat, 22 May 2021 19:51:01 +0200 Subject: [PATCH 28/35] Upmix/Downmix audio --- sunshine/config.cpp | 1 + sunshine/platform/common.h | 14 +- sunshine/platform/windows/audio.cpp | 379 +++++++++++++++++++++++----- 3 files changed, 325 insertions(+), 69 deletions(-) diff --git a/sunshine/config.cpp b/sunshine/config.cpp index 6cc158bd..6d3b58bf 100644 --- a/sunshine/config.cpp +++ b/sunshine/config.cpp @@ -434,6 +434,7 @@ void apply_config(std::unordered_map &&vars) { string_f(vars, "external_ip", nvhttp.external_ip); string_f(vars, "audio_sink", audio.sink); + string_f(vars, "virtual_sink", audio.virtual_sink); string_restricted_f(vars, "origin_pin_allowed", nvhttp.origin_pin_allowed, { "pc"sv, "lan"sv, "wan"sv }); diff --git a/sunshine/platform/common.h b/sunshine/platform/common.h index 91fe185b..672e8f5c 100644 --- a/sunshine/platform/common.h +++ b/sunshine/platform/common.h @@ -46,17 +46,23 @@ constexpr std::uint8_t map_stereo[] { FRONT_LEFT, FRONT_RIGHT }; constexpr std::uint8_t map_surround51[] { - FRONT_LEFT, BACK_LEFT, FRONT_RIGHT, BACK_RIGHT, FRONT_CENTER, LOW_FREQUENCY + FRONT_LEFT, + FRONT_RIGHT, + FRONT_CENTER, + LOW_FREQUENCY, + BACK_LEFT, + BACK_RIGHT, }; constexpr std::uint8_t map_surround71[] { FRONT_LEFT, - BACK_LEFT, FRONT_RIGHT, + FRONT_CENTER, + LOW_FREQUENCY, + LOW_FREQUENCY, + BACK_LEFT, BACK_RIGHT, SIDE_LEFT, SIDE_RIGHT, - FRONT_CENTER, - LOW_FREQUENCY, }; } // namespace speaker diff --git a/sunshine/platform/windows/audio.cpp b/sunshine/platform/windows/audio.cpp index 1dc82016..61df413a 100644 --- a/sunshine/platform/windows/audio.cpp +++ b/sunshine/platform/windows/audio.cpp @@ -81,6 +81,257 @@ public: PROPVARIANT prop; }; +class audio_pipe_t { +public: + static constexpr auto stereo = 2; + static constexpr auto channels51 = 6; + static constexpr auto channels71 = 8; + + using samples_t = std::vector; + using buf_t = util::buffer_t; + + virtual void to_stereo(samples_t &out, const buf_t &in) = 0; + virtual void to_51(samples_t &out, const buf_t &in) = 0; + virtual void to_71(samples_t &out, const buf_t &in) = 0; +}; + +class mono_t : public audio_pipe_t { +public: + void to_stereo(samples_t &out, const buf_t &in) override { + auto sample_in_pos = std::begin(in); + auto sample_end = std::begin(out) + out.size(); + + for(auto sample_out_p = std::begin(out); sample_out_p != sample_end;) { + *sample_out_p++ = *sample_in_pos * 7 / 10; + *sample_out_p++ = *sample_in_pos++ * 7 / 10; + } + } + + void to_51(samples_t &out, const buf_t &in) override { + using namespace speaker; + + auto sample_in_pos = std::begin(in); + auto sample_end = std::begin(out) + out.size(); + + for(auto sample_out_p = std::begin(out); sample_out_p != sample_end; sample_out_p += channels51) { + int left = *sample_in_pos++; + + auto fl = (left * 7 / 10); + + sample_out_p[FRONT_LEFT] = fl; + sample_out_p[FRONT_RIGHT] = fl; + sample_out_p[FRONT_CENTER] = fl * 6; + sample_out_p[LOW_FREQUENCY] = fl / 10; + sample_out_p[BACK_LEFT] = left * 4 / 10; + sample_out_p[BACK_RIGHT] = left * 4 / 10; + } + } + + void to_71(samples_t &out, const buf_t &in) override { + using namespace speaker; + + auto sample_in_pos = std::begin(in); + auto sample_end = std::begin(out) + out.size(); + + for(auto sample_out_p = std::begin(out); sample_out_p != sample_end; sample_out_p += channels71) { + int left = *sample_in_pos++; + + auto fl = (left * 7 / 10); + + sample_out_p[FRONT_LEFT] = fl; + sample_out_p[FRONT_RIGHT] = fl; + sample_out_p[FRONT_CENTER] = fl * 6; + sample_out_p[LOW_FREQUENCY] = fl / 10; + sample_out_p[BACK_LEFT] = left * 4 / 10; + sample_out_p[BACK_RIGHT] = left * 4 / 10; + sample_out_p[SIDE_LEFT] = left * 5 / 10; + sample_out_p[SIDE_RIGHT] = left * 5 / 10; + } + } +}; + +class stereo_t : public audio_pipe_t { +public: + void to_stereo(samples_t &out, const buf_t &in) override { + std::copy_n(std::begin(in), out.size(), std::begin(out)); + } + + void to_51(samples_t &out, const buf_t &in) override { + using namespace speaker; + + auto sample_in_pos = std::begin(in); + auto sample_end = std::begin(out) + out.size(); + + for(auto sample_out_p = std::begin(out); sample_out_p != sample_end; sample_out_p += channels51) { + int left = sample_in_pos[speaker::FRONT_LEFT]; + int right = sample_in_pos[speaker::FRONT_RIGHT]; + + sample_in_pos += 2; + + auto fl = (left * 7 / 10); + auto fr = (right * 7 / 10); + + auto mix = (fl + fr) / 2; + + sample_out_p[FRONT_LEFT] = fl; + sample_out_p[FRONT_RIGHT] = fr; + sample_out_p[FRONT_CENTER] = mix; + sample_out_p[LOW_FREQUENCY] = mix / 2; + sample_out_p[BACK_LEFT] = left * 4 / 10; + sample_out_p[BACK_RIGHT] = right * 4 / 10; + } + } + + void to_71(samples_t &out, const buf_t &in) override { + using namespace speaker; + + auto sample_in_pos = std::begin(in); + auto sample_end = std::begin(out) + out.size(); + + for(auto sample_out_p = std::begin(out); sample_out_p != sample_end; sample_out_p += channels71) { + int left = sample_in_pos[speaker::FRONT_LEFT]; + int right = sample_in_pos[speaker::FRONT_RIGHT]; + + sample_in_pos += 2; + + auto fl = (left * 7 / 10); + auto fr = (right * 7 / 10); + + auto mix = (fl + fr) / 2; + + sample_out_p[FRONT_LEFT] = fl; + sample_out_p[FRONT_RIGHT] = fr; + sample_out_p[FRONT_CENTER] = mix; + sample_out_p[LOW_FREQUENCY] = mix / 2; + sample_out_p[BACK_LEFT] = left * 4 / 10; + sample_out_p[BACK_RIGHT] = right * 4 / 10; + sample_out_p[SIDE_LEFT] = left * 5 / 10; + sample_out_p[SIDE_RIGHT] = right * 5 / 10; + } + } +}; + +class surr51_t : public audio_pipe_t { +public: + void to_stereo(samples_t &out, const buf_t &in) { + using namespace speaker; + + auto sample_in_pos = std::begin(in); + auto sample_end = std::begin(out) + out.size(); + + for(auto sample_out_p = std::begin(out); sample_out_p != sample_end; sample_out_p += stereo) { + int left {}, right {}; + + left += sample_in_pos[FRONT_LEFT]; + left += sample_in_pos[FRONT_CENTER] * 9 / 10; + left += sample_in_pos[LOW_FREQUENCY] * 3 / 10; + left += sample_in_pos[BACK_LEFT] * 7 / 10; + left += sample_in_pos[BACK_RIGHT] * 3 / 10; + + right += sample_in_pos[FRONT_RIGHT]; + right += sample_in_pos[FRONT_CENTER] * 9 / 10; + right += sample_in_pos[LOW_FREQUENCY] * 3 / 10; + right += sample_in_pos[BACK_LEFT] * 3 / 10; + right += sample_in_pos[BACK_RIGHT] * 7 / 10; + + sample_out_p[0] = left; + sample_out_p[1] = right; + + sample_in_pos += channels51; + } + } + + void to_51(samples_t &out, const buf_t &in) override { + std::copy_n(std::begin(in), out.size(), std::begin(out)); + } + + void to_71(samples_t &out, const buf_t &in) override { + using namespace speaker; + + auto sample_in_pos = std::begin(in); + auto sample_end = std::begin(out) + out.size(); + + for(auto sample_out_p = std::begin(out); sample_out_p != sample_end; sample_out_p += channels71) { + int fl = sample_in_pos[FRONT_LEFT]; + int fr = sample_in_pos[FRONT_RIGHT]; + int bl = sample_in_pos[BACK_LEFT]; + int br = sample_in_pos[BACK_RIGHT]; + + auto mix_l = (fl + bl) / 2; + auto mix_r = (bl + br) / 2; + + sample_out_p[FRONT_LEFT] = fl; + sample_out_p[FRONT_RIGHT] = fr; + sample_out_p[FRONT_CENTER] = sample_in_pos[FRONT_CENTER]; + sample_out_p[LOW_FREQUENCY] = sample_in_pos[LOW_FREQUENCY]; + sample_out_p[BACK_LEFT] = bl; + sample_out_p[BACK_RIGHT] = br; + sample_out_p[SIDE_LEFT] = mix_l; + sample_out_p[SIDE_RIGHT] = mix_r; + + sample_in_pos += channels51; + } + } +}; + +class surr71_t : public audio_pipe_t { +public: + void to_stereo(samples_t &out, const buf_t &in) { + using namespace speaker; + + auto sample_in_pos = std::begin(in); + auto sample_end = std::begin(out) + out.size(); + + for(auto sample_out_p = std::begin(out); sample_out_p != sample_end; sample_out_p += stereo) { + int left {}, right {}; + + left += sample_in_pos[FRONT_LEFT]; + left += sample_in_pos[FRONT_CENTER] * 9 / 10; + left += sample_in_pos[LOW_FREQUENCY] * 3 / 10; + left += sample_in_pos[BACK_LEFT] * 7 / 10; + left += sample_in_pos[BACK_RIGHT] * 3 / 10; + left += sample_in_pos[SIDE_LEFT]; + + right += sample_in_pos[FRONT_RIGHT]; + right += sample_in_pos[FRONT_CENTER] * 9 / 10; + right += sample_in_pos[LOW_FREQUENCY] * 3 / 10; + right += sample_in_pos[BACK_LEFT] * 3 / 10; + right += sample_in_pos[BACK_RIGHT] * 7 / 10; + right += sample_in_pos[SIDE_RIGHT]; + + sample_out_p[0] = left; + sample_out_p[1] = right; + + sample_in_pos += channels71; + } + } + + void to_51(samples_t &out, const buf_t &in) override { + using namespace speaker; + + auto sample_in_pos = std::begin(in); + auto sample_end = std::begin(out) + out.size(); + + for(auto sample_out_p = std::begin(out); sample_out_p != sample_end; sample_out_p += channels51) { + auto sl = (int)sample_out_p[SIDE_LEFT] * 3 / 10; + auto sr = (int)sample_out_p[SIDE_RIGHT] * 3 / 10; + + sample_out_p[FRONT_LEFT] = sample_in_pos[FRONT_LEFT] + sl; + sample_out_p[FRONT_RIGHT] = sample_in_pos[FRONT_RIGHT] + sr; + sample_out_p[FRONT_CENTER] = sample_in_pos[FRONT_CENTER]; + sample_out_p[LOW_FREQUENCY] = sample_in_pos[LOW_FREQUENCY]; + sample_out_p[BACK_LEFT] = sample_in_pos[BACK_LEFT] + sl; + sample_out_p[BACK_RIGHT] = sample_in_pos[BACK_RIGHT] + sr; + + sample_in_pos += channels71; + } + } + + void to_71(samples_t &out, const buf_t &in) override { + std::copy_n(std::begin(in), out.size(), std::begin(out)); + } +}; + static std::wstring_convert, wchar_t> converter; struct format_t { enum type_e : int { @@ -133,6 +384,18 @@ struct format_t { }, }; +static format_t surround_51_side_speakers { + format_t::surr51, + "Surround 5.1"sv, + 6, + SPEAKER_FRONT_LEFT | + SPEAKER_FRONT_RIGHT | + SPEAKER_FRONT_CENTER | + SPEAKER_LOW_FREQUENCY | + SPEAKER_SIDE_LEFT | + SPEAKER_SIDE_RIGHT, +}; + void set_wave_format(audio::wave_format_t &wave_format, const format_t &format) { wave_format->nChannels = format.channels; wave_format->nBlockAlign = wave_format->nChannels * wave_format->wBitsPerSample / 8; @@ -169,50 +432,6 @@ int init_wave_format(audio::wave_format_t &wave_format, DWORD sample_rate) { return 0; } -void surround51_to_stereo(std::vector &sample_in, const util::buffer_t &sample_out) { - enum surround51_e : int { - front_left, - front_right, - front_center, - low_frequency, // subwoofer - back_left, - back_right, - channels51 // number of channels in surround sound - }; - - auto sample_in_pos = std::begin(sample_in); - auto sample_end = std::begin(sample_out) + sample_in.size() / 2 * channels51; - - for(auto sample_out_p = std::begin(sample_out); sample_out_p != sample_end; sample_out_p += channels51) { - std::uint32_t left {}, right {}; - - left += sample_out_p[front_left]; - left += sample_out_p[front_center] * 90 / 100; - left += sample_out_p[low_frequency] * 30 / 100; - left += sample_out_p[back_left] * 70 / 100; - left += sample_out_p[back_right] * 30 / 100; - - right += sample_out_p[front_right]; - right += sample_out_p[front_center] * 90 / 100; - right += sample_out_p[low_frequency] * 30 / 100; - right += sample_out_p[back_left] * 30 / 100; - right += sample_out_p[back_right] * 70 / 100; - - *sample_in_pos++ = (std::uint16_t)left; - *sample_in_pos++ = (std::uint16_t)right; - } -} - -void mono_to_stereo(std::vector &sample_in, const util::buffer_t &sample_out) { - auto sample_in_pos = std::begin(sample_in); - auto sample_end = std::begin(sample_out) + sample_in.size() / 2; - - for(auto sample_out_p = std::begin(sample_out); sample_out_p != sample_end; ++sample_out_p) { - *sample_in_pos++ = *sample_out_p; - *sample_in_pos++ = *sample_out_p; - } -} - audio_client_t make_audio_client(device_t &device, const format_t &format, int sample_rate) { audio_client_t audio_client; auto status = device->Activate( @@ -264,7 +483,7 @@ format_t::type_e validate_device(device_t &device, int sample_rate) { // Ensure WaveFromat is compatible auto audio_client = make_audio_client(device, format, sample_rate); - BOOST_LOG(debug) << format.name << ": "sv << !audio_client ? "unsupported"sv : "supported"sv; + BOOST_LOG(debug) << format.name << ": "sv << (!audio_client ? "unsupported"sv : "supported"sv); if(audio_client) { return format.type; @@ -294,8 +513,8 @@ device_t default_device(device_enum_t &device_enum) { class mic_wasapi_t : public mic_t { public: - capture_e sample(std::vector &sample_in) override { - auto sample_size = sample_in.size() / 2 * format->channels; + capture_e sample(std::vector &sample_out) override { + auto sample_size = sample_out.size() / channels_out * channels_in; while(sample_buf_pos - std::begin(sample_buf) < sample_size) { //FIXME: Use IAudioClient3 instead of IAudioClient, that would allows for adjusting the latency of the audio samples auto capture_result = _fill_buffer(); @@ -305,18 +524,18 @@ public: } } - switch(format->channels) { - case 1: - mono_to_stereo(sample_in, sample_buf); - break; + switch(channels_out) { case 2: - std::copy_n(std::begin(sample_buf), sample_size, std::begin(sample_in)); + pipe->to_stereo(sample_out, sample_buf); break; case 6: - surround51_to_stereo(sample_in, sample_buf); + pipe->to_51(sample_out, sample_buf); + break; + case 8: + pipe->to_71(sample_out, sample_buf); break; default: - BOOST_LOG(error) << '[' << format->name << "] not yet supported"sv; + BOOST_LOG(error) << "converting to ["sv << channels_out << "] channels is not supported"sv; return capture_e::error; } @@ -328,7 +547,7 @@ public: } - int init(std::uint32_t sample_rate, std::uint32_t frame_size) { + 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; @@ -362,7 +581,26 @@ public: if(audio_client) { BOOST_LOG(debug) << "Found audio format ["sv << format.name << ']'; - this->format = &format; + channels_in = format.channels; + this->channels_out = channels_out; + + switch(channels_in) { + case 1: + pipe = std::make_unique(); + break; + case 2: + pipe = std::make_unique(); + break; + case 6: + pipe = std::make_unique(); + break; + case 8: + pipe = std::make_unique(); + break; + default: + BOOST_LOG(error) << "converting from ["sv << channels_in << "] channels is not supported"sv; + return -1; + } break; } } @@ -385,7 +623,7 @@ public: } // *2 --> needs to fit double - sample_buf = util::buffer_t { std::max(frames * 2, frame_size * format->channels * 2) }; + sample_buf = util::buffer_t { std::max(frames, frame_size) * 2 * channels_in }; sample_buf_pos = std::begin(sample_buf); status = audio_client->GetService(IID_IAudioCaptureClient, (void **)&audio_capture); @@ -467,7 +705,7 @@ private: } sample_aligned.uninitialized = std::end(sample_buf) - sample_buf_pos; - auto n = std::min(sample_aligned.uninitialized, block_aligned.audio_sample_size * format->channels); + auto n = std::min(sample_aligned.uninitialized, block_aligned.audio_sample_size * channels_in); if(buffer_flags & AUDCLNT_BUFFERFLAGS_SILENT) { std::fill_n(sample_buf_pos, n, 0); @@ -505,7 +743,12 @@ public: util::buffer_t sample_buf; std::int16_t *sample_buf_pos; - format_t *format; + // out --> our audio output + int channels_out; + // in --> our wasapi input + int channels_in; + + std::unique_ptr pipe; }; class audio_control_t : public ::platf::audio_control_t { @@ -551,7 +794,6 @@ public: collection->GetCount(&count); std::string virtual_device_id = config::audio.virtual_sink; - BOOST_LOG(debug) << "====== Found "sv << count << " potential audio devices ======"sv; for(auto x = 0; x < count; ++x) { audio::device_t device; collection->Item(x, &device); @@ -576,7 +818,7 @@ public: prop->GetValue(PKEY_Device_DeviceDesc, &device_desc.prop); auto adapter_name = no_null((LPWSTR)adapter_friendly_name.prop.pszVal); - BOOST_LOG(debug) + BOOST_LOG(verbose) << L"===== Device ====="sv << std::endl << L"Device ID : "sv << wstring.get() << std::endl << L"Device name : "sv << no_null((LPWSTR)device_friendly_name.prop.pszVal) << std::endl @@ -603,7 +845,7 @@ public: 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)) { + if(mic->init(sample_rate, frame_size, channels)) { return nullptr; } @@ -660,8 +902,15 @@ public: } set_wave_format(wave_format, formats[(int)type - 1]); - WAVEFORMATEX p { *wave_format.get() }; - status = policy->SetDeviceFormat(wstring_device_id.c_str(), wave_format.get(), &p); + WAVEFORMATEXTENSIBLE p {}; + status = policy->SetDeviceFormat(wstring_device_id.c_str(), wave_format.get(), (WAVEFORMATEX *)&p); + + // Surround 5.1 might contain side-{left, right} instead of speaker in the back + // Try again with different speaker mask. + if(status == 0x88890008 && type == format_t::surr51) { + set_wave_format(wave_format, surround_51_side_speakers); + status = policy->SetDeviceFormat(wstring_device_id.c_str(), wave_format.get(), (WAVEFORMATEX *)&p); + } if(FAILED(status)) { BOOST_LOG(error) << "Couldn't set Wave Format [0x"sv << util::hex(status).to_string_view() << ']'; @@ -723,7 +972,7 @@ int init(); std::unique_ptr audio_control() { auto control = std::make_unique(); - if(control->init() || control->set_sink("virtual-Stereo{0.0.0.00000000}.{8edba70c-1125-467c-b89c-15da389bc1d4}"s)) { + if(control->init()) { return nullptr; } From dcb32eaaf7ddf1d800b50935393d046cd2f8f621 Mon Sep 17 00:00:00 2001 From: loki Date: Sat, 22 May 2021 23:09:53 +0200 Subject: [PATCH 29/35] Skip error correction when a video frame is to large, the alternative is skipping the frame itself --- sunshine/stream.cpp | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/sunshine/stream.cpp b/sunshine/stream.cpp index 8971c266..9500a041 100644 --- a/sunshine/stream.cpp +++ b/sunshine/stream.cpp @@ -314,7 +314,7 @@ struct fec_t { } }; -fec_t encode(const std::string_view &payload, size_t blocksize, size_t fecpercentage) { +static fec_t encode(const std::string_view &payload, size_t blocksize, size_t fecpercentage) { auto payload_size = payload.size(); auto pad = payload_size % blocksize != 0; @@ -324,11 +324,13 @@ fec_t encode(const std::string_view &payload, size_t blocksize, size_t fecpercen auto nr_shards = data_shards + parity_shards; if(nr_shards > DATA_SHARDS_MAX) { - BOOST_LOG(error) + BOOST_LOG(warning) << "Number of fragments for reed solomon exceeds DATA_SHARDS_MAX"sv << std::endl - << nr_shards << " > "sv << DATA_SHARDS_MAX; + << nr_shards << " > "sv << DATA_SHARDS_MAX + << ", skipping error correction"sv; - return { 0 }; + nr_shards = data_shards; + fecpercentage = 0; } util::buffer_t shards { nr_shards * blocksize }; @@ -342,10 +344,12 @@ fec_t encode(const std::string_view &payload, size_t blocksize, size_t fecpercen shards_p[x] = (uint8_t *)&shards[x * blocksize]; } - // packets = parity_shards + data_shards - rs_t rs { reed_solomon_new(data_shards, parity_shards) }; + if(data_shards + parity_shards <= DATA_SHARDS_MAX) { + // packets = parity_shards + data_shards + rs_t rs { reed_solomon_new(data_shards, parity_shards) }; - reed_solomon_encode(rs.get(), shards_p.begin(), nr_shards, blocksize); + reed_solomon_encode(rs.get(), shards_p.begin(), nr_shards, blocksize); + } return { data_shards, @@ -670,7 +674,7 @@ void videoBroadcastThread(safe::signal_t *shutdown_event, udp::socket &sock, vid inspect->packet.frameIndex = packet->pts; inspect->packet.fecInfo = (x << 12 | shards.data_shards << 22 | - fecPercentage << 4); + shards.percentage << 4); inspect->rtp.header = FLAG_EXTENSION; inspect->rtp.sequenceNumber = util::endian::big(lowseq + x); From 1c4435312e58fa29219fa25ae69f995ed1b7c424 Mon Sep 17 00:00:00 2001 From: loki Date: Mon, 24 May 2021 18:08:32 +0200 Subject: [PATCH 30/35] maintain aspect ratio when using software encoder --- sunshine/video.cpp | 74 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 70 insertions(+), 4 deletions(-) diff --git a/sunshine/video.cpp b/sunshine/video.cpp index fd077eea..7d41dbb5 100644 --- a/sunshine/video.cpp +++ b/sunshine/video.cpp @@ -81,9 +81,16 @@ public: img.row_pitch, 0 }; - int ret = sws_scale(sws.get(), (std::uint8_t *const *)&img.data, linesizes, 0, img.height, frame->data, frame->linesize); + std::uint8_t *const data[] { + frame->data[0] + offset, + frame->data[1] + offset / 2, + frame->data[2] + offset / 2, + 0 + }; + + int ret = sws_scale(sws.get(), (std::uint8_t *const *)&img.data, linesizes, 0, img.height, data, frame->linesize); if(ret <= 0) { - BOOST_LOG(fatal) << "Couldn't convert image to required format and/or size"sv; + BOOST_LOG(error) << "Couldn't convert image to required format and/or size"sv; return -1; } @@ -98,7 +105,63 @@ public: 0, 1 << 16, 1 << 16); } - int init(int in_width, int in_height, int out_width, int out_height, AVFrame *frame, AVPixelFormat format) { + /** + * When preserving aspect ratio, ensure that padding is black + */ + int prefill(AVFrame *frame, AVPixelFormat format) { + auto width = frame->width; + auto height = frame->height; + + sws_t sws { + sws_getContext( + width, height, AV_PIX_FMT_BGR0, + width, height, format, + SWS_LANCZOS | SWS_ACCURATE_RND, + nullptr, nullptr, nullptr) + }; + + if(!sws) { + return -1; + } + + util::buffer_t img { (std::size_t)(width * height) }; + std::fill(std::begin(img), std::end(img), 0); + + const int linesizes[2] { + width, 0 + }; + + av_frame_make_writable(frame); + + auto data = img.begin(); + int ret = sws_scale(sws.get(), (std::uint8_t *const *)&data, linesizes, 0, height, frame->data, frame->linesize); + if(ret <= 0) { + BOOST_LOG(error) << "Couldn't convert image to required format and/or size"sv; + + return -1; + } + + return 0; + } + + int init(int in_width, int in_height, AVFrame *frame, AVPixelFormat format) { + if(prefill(frame, format)) { + return -1; + } + + auto out_width = frame->width; + auto out_height = frame->height; + + // Ensure aspect ratio is maintained + auto scalar = std::fminf((float)out_width / in_width, (float)out_height / in_height); + out_width = in_width * scalar; + out_height = in_height * scalar; + + // result is always positive + auto offsetX = (frame->width - out_width) / 2; + auto offsetY = (frame->height - out_height) / 2; + offset = offsetX + offsetY * frame->width; + sws.reset(sws_getContext( in_width, in_height, AV_PIX_FMT_BGR0, out_width, out_height, format, @@ -112,6 +175,9 @@ public: ~swdevice_t() override {} sws_t sws; + + // offset of input image to output frame in pixels + int offset; }; struct encoder_t { @@ -698,7 +764,7 @@ std::optional make_session(const encoder_t &encoder, const config_t & if(!hwdevice->data) { auto device_tmp = std::make_unique(); - if(device_tmp->init(width, height, config.width, config.height, frame.get(), sw_fmt)) { + if(device_tmp->init(width, height, frame.get(), sw_fmt)) { return std::nullopt; } From 7aff15f7430310a58a9272c03f5e2fd28ec7281a Mon Sep 17 00:00:00 2001 From: loki Date: Mon, 24 May 2021 20:58:05 +0200 Subject: [PATCH 31/35] Mainain aspect ratio when using nvenc and amdvce --- sunshine/platform/windows/display_vram.cpp | 68 ++++++++++++++++++---- 1 file changed, 58 insertions(+), 10 deletions(-) diff --git a/sunshine/platform/windows/display_vram.cpp b/sunshine/platform/windows/display_vram.cpp index 9c44b6fb..b3fbde3f 100644 --- a/sunshine/platform/windows/display_vram.cpp +++ b/sunshine/platform/windows/display_vram.cpp @@ -1,3 +1,5 @@ +#include + #include #include @@ -12,8 +14,6 @@ using namespace std::literals; } namespace platf::dxgi { -constexpr float aquamarine[] { 0.498039246f, 1.000000000f, 0.831372619f, 1.000000000f }; - using input_layout_t = util::safe_ptr>; using render_target_t = util::safe_ptr>; using shader_res_t = util::safe_ptr>; @@ -329,19 +329,32 @@ public: input_res_p = scene_sr.get(); } - _init_view_port(out_width, out_height); + _init_view_port(this->img.width, this->img.height); device_ctx_p->OMSetRenderTargets(1, &nv12_Y_rt, nullptr); device_ctx_p->VSSetShader(scene_vs.get(), nullptr, 0); device_ctx_p->PSSetShader(convert_Y_ps.get(), nullptr, 0); + device_ctx_p->PSSetShaderResources(0, 1, &back_img.input_res); + device_ctx_p->Draw(3, 0); + + device_ctx_p->RSSetViewports(1, &outY_view); device_ctx_p->PSSetShaderResources(0, 1, &input_res_p); device_ctx_p->Draw(3, 0); - _init_view_port(out_width / 2, out_height / 2); + // Artifacts start appearing on the rendered image if Sunshine doesn't flush + // before rendering on the UV part of the image. + device_ctx_p->Flush(); + + _init_view_port(this->img.width / 2, this->img.height / 2); device_ctx_p->OMSetRenderTargets(1, &nv12_UV_rt, nullptr); device_ctx_p->VSSetShader(convert_UV_vs.get(), nullptr, 0); device_ctx_p->PSSetShader(convert_UV_ps.get(), nullptr, 0); + device_ctx_p->PSSetShaderResources(0, 1, &back_img.input_res); + device_ctx_p->Draw(3, 0); + + device_ctx_p->RSSetViewports(1, &outUV_view); device_ctx_p->PSSetShaderResources(0, 1, &input_res_p); device_ctx_p->Draw(3, 0); + device_ctx_p->Flush(); return 0; } @@ -377,7 +390,7 @@ public: int init( std::shared_ptr display, device_t::pointer device_p, device_ctx_t::pointer device_ctx_p, - int in_width, int in_height, int out_width, int out_height, + int out_width, int out_height, pix_fmt_e pix_fmt) { HRESULT status; @@ -392,8 +405,20 @@ public: platf::hwdevice_t::img = &img; - this->out_width = out_width; - this->out_height = out_height; + float in_width = display->width; + float in_height = display->height; + + // // Ensure aspect ratio is maintained + auto scalar = std::fminf(out_width / in_width, out_height / in_height); + auto out_width_f = in_width * scalar; + auto out_height_f = in_height * scalar; + + // result is always positive + auto offsetX = (out_width - out_width_f) / 2; + auto offsetY = (out_height - out_height_f) / 2; + + outY_view = D3D11_VIEWPORT { offsetX, offsetY, out_width_f, out_height_f, 0.0f, 1.0f }; + outUV_view = D3D11_VIEWPORT { offsetX / 2, offsetY / 2, out_width_f / 2, out_height_f / 2, 0.0f, 1.0f }; status = device_p->CreateVertexShader(scene_vs_hlsl->GetBufferPointer(), scene_vs_hlsl->GetBufferSize(), nullptr, &scene_vs); if(status) { @@ -514,6 +539,25 @@ public: return -1; } + // Color the background black, so that the padding for keeping the aspect ratio + // is black + if(img.display->dummy_img(&back_img)) { + BOOST_LOG(warning) << "Couldn't create an image to set background color to black"sv; + return -1; + } + + D3D11_SHADER_RESOURCE_VIEW_DESC desc { + DXGI_FORMAT_B8G8R8A8_UNORM, + D3D11_SRV_DIMENSION_TEXTURE2D + }; + desc.Texture2D.MipLevels = 1; + + status = device_p->CreateShaderResourceView(back_img.texture.get(), &desc, &back_img.input_res); + if(FAILED(status)) { + BOOST_LOG(error) << "Failed to create input shader resource view [0x"sv << util::hex(status).to_string_view() << ']'; + return -1; + } + device_ctx_p->OMSetBlendState(blend_disable.get(), nullptr, 0xFFFFFFFFu); device_ctx_p->PSSetSamplers(0, 1, &sampler_linear); device_ctx_p->PSSetConstantBuffers(0, 1, &color_matrix); @@ -619,17 +663,21 @@ public: img_d3d_t img; + // Clear nv12 render target to black + img_d3d_t back_img; + vs_t convert_UV_vs; ps_t convert_UV_ps; ps_t convert_Y_ps; ps_t scene_ps; vs_t scene_vs; + D3D11_VIEWPORT outY_view; + D3D11_VIEWPORT outUV_view; + D3D11_VIEWPORT cursor_view; bool cursor_visible; - float out_width, out_height; - device_ctx_t::pointer device_ctx_p; // The destructor will remove itself from the list of hardware devices, this is done synchronously @@ -768,6 +816,7 @@ int display_vram_t::dummy_img(platf::img_t *img_base) { dummy_data.get(), (UINT)img->row_pitch }; + std::fill_n(dummy_data.get(), width * height, 0); D3D11_TEXTURE2D_DESC t {}; t.Width = width; @@ -808,7 +857,6 @@ std::shared_ptr display_vram_t::make_hwdevice(int width, int shared_from_this(), device.get(), device_ctx.get(), - this->width, this->height, width, height, pix_fmt); From 9f6b4ed93bec45282f3784cb90349626fb8da98a Mon Sep 17 00:00:00 2001 From: loki Date: Tue, 25 May 2021 18:55:29 +0200 Subject: [PATCH 32/35] Work around weird mapping for audio channels --- sunshine/platform/common.h | 1 + sunshine/rtsp.cpp | 21 +++++++++++++++++++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/sunshine/platform/common.h b/sunshine/platform/common.h index 672e8f5c..cac7db34 100644 --- a/sunshine/platform/common.h +++ b/sunshine/platform/common.h @@ -40,6 +40,7 @@ enum speaker_e { BACK_RIGHT, SIDE_LEFT, SIDE_RIGHT, + MAX_SPEAKERS, }; constexpr std::uint8_t map_stereo[] { diff --git a/sunshine/rtsp.cpp b/sunshine/rtsp.cpp index 5b46fa76..2ef9e171 100644 --- a/sunshine/rtsp.cpp +++ b/sunshine/rtsp.cpp @@ -299,10 +299,27 @@ void cmd_describe(rtsp_server_t *server, net::peer_t peer, msg_t &&req) { ss << "sprop-parameter-sets=AAAAAU"sv << std::endl; } - for(auto &stream_config : audio::stream_configs) { + for(int x = 0; x < audio::MAX_STREAM_CONFIG; ++x) { + auto &stream_config = audio::stream_configs[x]; + std::uint8_t mapping[platf::speaker::MAX_SPEAKERS]; + + auto mapping_p = stream_config.mapping; + + /** + * GFE advertises incorrect mapping for normal quality configurations, + * as a result, Moonlight rotates all channels from index '3' to the right + * To work around this, rotate channels to the left from index '3' + */ + if(x == audio::SURROUND51 || x == audio::SURROUND71) { + std::copy_n(mapping_p, stream_config.channelCount, mapping); + std::rotate(mapping + 3, mapping + 4, mapping + audio::MAX_STREAM_CONFIG); + + mapping_p = mapping; + } + ss << "a=fmtp:97 surround-params="sv << stream_config.channelCount << stream_config.streams << stream_config.coupledStreams; - std::for_each_n(stream_config.mapping, stream_config.channelCount, [&ss](std::uint8_t digit) { + std::for_each_n(mapping_p, stream_config.channelCount, [&ss](std::uint8_t digit) { ss << (char)(digit + '0'); }); From 113e7a52d4b78d697b9836bcdcda43a7380110ab Mon Sep 17 00:00:00 2001 From: loki Date: Wed, 26 May 2021 17:34:25 +0200 Subject: [PATCH 33/35] advertise display modes --- .gitignore | 1 + assets/sunshine.conf | 22 +++++- sunshine/config.cpp | 184 ++++++++++++++++++++++++++++++++++++------- sunshine/config.h | 3 + sunshine/nvhttp.cpp | 29 ++++++- sunshine/process.cpp | 2 + 6 files changed, 212 insertions(+), 29 deletions(-) diff --git a/.gitignore b/.gitignore index 2584b70d..ad68b535 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,5 @@ cmake-build* *.swp *.kdev4 +.cache .idea \ No newline at end of file diff --git a/assets/sunshine.conf b/assets/sunshine.conf index 78f31b2d..a26de0c5 100644 --- a/assets/sunshine.conf +++ b/assets/sunshine.conf @@ -37,6 +37,26 @@ # The file where current state of Sunshine is stored # file_state = sunshine_state.json +# The display modes advertised by Sunshine +# +# Some versions of Moonlight, such as Moonlight-nx (Switch), +# rely on this list to ensure that the requested resolutions and fps +# are supported. +# +# fps = [10, 30, 60, 90, 120] +# resolutions = [ +# 352x240, +# 480x360, +# 858x480, +# 1280x720, +# 1920x1080, +# 2560x1080, +# 3440x1440, +# 1920x1200, +# 3860x2160, +# 3840x1600, +# ] + # How long to wait in milliseconds for data from moonlight before shutting down the stream # ping_timeout = 2000 @@ -210,4 +230,4 @@ # To set the initial state of flags -0 and -1 to on, set the following flags: # flags = 01 # -# See: sunshine --help for all options under the header: flags +# See: sunshine --help for all options under the header: flags \ No newline at end of file diff --git a/sunshine/config.cpp b/sunshine/config.cpp index 6d3b58bf..e24514b2 100644 --- a/sunshine/config.cpp +++ b/sunshine/config.cpp @@ -1,3 +1,4 @@ +#include #include #include #include @@ -183,7 +184,22 @@ nvhttp_t nvhttp { CERTIFICATE_FILE, boost::asio::ip::host_name(), // sunshine_name, - "sunshine_state.json"s // file_state + "sunshine_state.json"s, // file_state + {}, // external_ip + { + "352x240"s, + "480x360"s, + "858x480"s, + "1280x720"s, + "1920x1080"s, + "2560x1080"s, + "3440x1440"s + "1920x1200"s, + "3860x2160"s, + "3840x1600"s, + }, // supported resolutions + + { 10, 30, 60, 90, 120 }, // supported fps }; input_t input { @@ -197,28 +213,84 @@ sunshine_t sunshine { 0 // flags }; -bool whitespace(char ch) { +bool endline(char ch) { + return ch == '\r' || ch == '\n'; +} + +bool space_tab(char ch) { return ch == ' ' || ch == '\t'; } -std::string to_string(const char *begin, const char *end) { - return { begin, (std::size_t)(end - begin) }; +bool whitespace(char ch) { + return space_tab(ch) || endline(ch); } -std::optional> parse_line(std::string_view::const_iterator begin, std::string_view::const_iterator end) { - begin = std::find_if(begin, end, std::not_fn(whitespace)); - end = std::find(begin, end, '#'); - end = std::find_if(std::make_reverse_iterator(end), std::make_reverse_iterator(begin), std::not_fn(whitespace)).base(); +std::string to_string(const char *begin, const char *end) { + std::string result; - auto eq = std::find(begin, end, '='); - if(eq == end || eq == begin) { - return std::nullopt; + KITTY_WHILE_LOOP(auto pos = begin, pos != end, { + auto comment = std::find(pos, end, '#'); + auto endl = std::find_if(comment, end, endline); + + result.append(pos, comment); + + pos = endl; + }) + + return result; +} + +template +It skip_list(It skipper, It end) { + int stack = 1; + while(skipper != end && stack) { + if(*skipper == '[') { + ++stack; + } + if(*skipper == ']') { + --stack; + } + + ++skipper; } - auto end_name = std::find_if(std::make_reverse_iterator(eq), std::make_reverse_iterator(begin), std::not_fn(whitespace)).base(); - auto begin_val = std::find_if(eq + 1, end, std::not_fn(whitespace)); + return skipper; +} - return std::pair { to_string(begin, end_name), to_string(begin_val, end) }; +std::pair< + std::string_view::const_iterator, + std::optional>> +parse_option(std::string_view::const_iterator begin, std::string_view::const_iterator end) { + begin = std::find_if_not(begin, end, whitespace); + auto endl = std::find_if(begin, end, endline); + auto endc = std::find(begin, endl, '#'); + endc = std::find_if(std::make_reverse_iterator(endc), std::make_reverse_iterator(begin), std::not_fn(whitespace)).base(); + + auto eq = std::find(begin, endc, '='); + if(eq == endc || eq == begin) { + return std::make_pair(endl, std::nullopt); + } + + auto end_name = std::find_if_not(std::make_reverse_iterator(eq), std::make_reverse_iterator(begin), space_tab).base(); + auto begin_val = std::find_if_not(eq + 1, endc, space_tab); + + if(begin_val == endl) { + return std::make_pair(endl, std::nullopt); + } + + // Lists might contain newlines + if(*begin_val == '[') { + endl = skip_list(begin_val + 1, end); + if(endl == end) { + std::cout << "Warning: Config option ["sv << to_string(begin, end_name) << "] Missing ']'"sv; + + return std::make_pair(endl, std::nullopt); + } + } + + return std::make_pair( + endl, + std::make_pair(to_string(begin, end_name), to_string(begin_val, endl))); } std::unordered_map parse_config(std::string_view file_content) { @@ -228,10 +300,14 @@ std::unordered_map parse_config(std::string_view file_ auto end = std::end(file_content); while(pos < end) { - auto newline = std::find_if(pos, end, [](auto ch) { return ch == '\n' || ch == '\r'; }); - auto var = parse_line(pos, newline); + // auto newline = std::find_if(pos, end, [](auto ch) { return ch == '\n' || ch == '\r'; }); + TUPLE_2D(endl, var, parse_option(pos, end)); + + pos = endl; + if(pos != end) { + pos += (*pos == '\r') ? 2 : 1; + } - pos = (*newline == '\r') ? newline + 2 : newline + 1; if(!var) { continue; } @@ -368,16 +444,68 @@ void double_between_f(std::unordered_map &vars, const } } +void list_string_f(std::unordered_map &vars, const std::string &name, std::vector &input) { + std::string string; + string_f(vars, name, string); + + if(string.empty()) { + return; + } + + input.clear(); + + auto begin = std::cbegin(string); + if(*begin == '[') { + ++begin; + } + + begin = std::find_if_not(begin, std::cend(string), whitespace); + if(begin == std::cend(string)) { + return; + } + + auto pos = begin; + while(pos < std::cend(string)) { + if(*pos == '[') { + pos = skip_list(pos + 1, std::cend(string)) + 1; + } + else if(*pos == ']') { + break; + } + else if(*pos == ',') { + input.emplace_back(begin, pos); + pos = begin = std::find_if_not(pos + 1, std::cend(string), whitespace); + } + else { + ++pos; + } + } + + if(pos != begin) { + input.emplace_back(begin, pos); + } +} + +void list_int_f(std::unordered_map &vars, const std::string &name, std::vector &input) { + std::vector list; + list_string_f(vars, name, list); + + for(auto &el : list) { + input.emplace_back(util::from_view(el)); + } +} + void print_help(const char *name) { - std::cout << "Usage: "sv << name << " [options] [/path/to/configuration_file]"sv << std::endl - << " Any configurable option can be overwritten with: \"name=value\""sv << std::endl - << std::endl - << " --help | print help"sv << std::endl - << std::endl - << " flags"sv << std::endl - << " -0 | Read PIN from stdin"sv << std::endl - << " -1 | Do not load previously saved state and do retain any state after shutdown"sv << std::endl - << " | Effectively starting as if for the first time without overwriting any pairings with your devices"sv; + std::cout + << "Usage: "sv << name << " [options] [/path/to/configuration_file]"sv << std::endl + << " Any configurable option can be overwritten with: \"name=value\""sv << std::endl + << std::endl + << " --help | print help"sv << std::endl + << std::endl + << " flags"sv << std::endl + << " -0 | Read PIN from stdin"sv << std::endl + << " -1 | Do not load previously saved state and do retain any state after shutdown"sv << std::endl + << " | Effectively starting as if for the first time without overwriting any pairings with your devices"sv; } int apply_flags(const char *line) { @@ -432,6 +560,8 @@ void apply_config(std::unordered_map &&vars) { string_f(vars, "sunshine_name", nvhttp.sunshine_name); string_f(vars, "file_state", nvhttp.file_state); string_f(vars, "external_ip", nvhttp.external_ip); + list_string_f(vars, "resolutions"s, nvhttp.resolutions); + list_int_f(vars, "fps"s, nvhttp.fps); string_f(vars, "audio_sink", audio.sink); string_f(vars, "virtual_sink", audio.virtual_sink); @@ -536,7 +666,7 @@ int parse(int argc, char *argv[]) { config_file = line; } else { - auto var = parse_line(line, line_end); + TUPLE_EL(var, 1, parse_option(line, line_end)); if(!var) { print_help(*argv); return -1; diff --git a/sunshine/config.h b/sunshine/config.h index 1b62818e..37f59066 100644 --- a/sunshine/config.h +++ b/sunshine/config.h @@ -5,6 +5,7 @@ #include #include #include +#include namespace config { struct video_t { @@ -66,6 +67,8 @@ struct nvhttp_t { std::string file_state; std::string external_ip; + std::vector resolutions; + std::vector fps; }; struct input_t { diff --git a/sunshine/nvhttp.cpp b/sunshine/nvhttp.cpp index ebf6468a..255407cc 100644 --- a/sunshine/nvhttp.cpp +++ b/sunshine/nvhttp.cpp @@ -2,6 +2,8 @@ // Created by loki on 6/3/19. // +#define BOOST_BIND_GLOBAL_PLACEHOLDERS + #include "process.h" #include @@ -511,10 +513,35 @@ void serverinfo(std::shared_ptr::Response> res tree.put("root.ExternalIP", config::nvhttp.external_ip); } + pt::ptree display_nodes; + for(auto &resolution : config::nvhttp.resolutions) { + auto pred = [](auto ch) { return ch == ' ' || ch == '\t' || ch == 'x'; }; + + auto middle = std::find_if(std::begin(resolution), std::end(resolution), pred); + if(middle == std::end(resolution)) { + BOOST_LOG(warning) << resolution << " is not in the proper format for a resolution: WIDTHxHEIGHT"sv; + continue; + } + + auto width = util::from_chars(&*std::begin(resolution), &*middle); + auto height = util::from_chars(&*(middle + 1), &*std::end(resolution)); + for(auto fps : config::nvhttp.fps) { + pt::ptree display_node; + display_node.put("Width", width); + display_node.put("Height", height); + display_node.put("RefreshRate", fps); + + display_nodes.add_child("DisplayMode", display_node); + } + } + + if(!config::nvhttp.resolutions.empty()) { + tree.add_child("root.SupportedDisplayMode", display_nodes); + } auto current_appid = proc::proc.running(); tree.put("root.PairStatus", pair_status); tree.put("root.currentgame", current_appid >= 0 ? current_appid + 1 : 0); - tree.put("root.state", current_appid >= 0 ? "_SERVER_BUSY" : "_SERVER_FREE"); + tree.put("root.state", current_appid >= 0 ? "SUNSHINE_SERVER_BUSY" : "SUNSHINE_SERVER_FREE"); std::ostringstream data; diff --git a/sunshine/process.cpp b/sunshine/process.cpp index d9c6f71e..88532aca 100644 --- a/sunshine/process.cpp +++ b/sunshine/process.cpp @@ -2,6 +2,8 @@ // Created by loki on 12/14/19. // +#define BOOST_BIND_GLOBAL_PLACEHOLDERS + #include "process.h" #include From fd43ebc2bf52282e7bfb213d74181d6e4877c570 Mon Sep 17 00:00:00 2001 From: loki Date: Wed, 26 May 2021 21:33:28 +0200 Subject: [PATCH 34/35] add build status --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 2d7d4d9d..9185eaee 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,9 @@ # Introduction Sunshine is a Gamestream host for Moonlight +[![AppVeyor Build Status](https://ci.appveyor.com/api/projects/status/cgrtw2g3fq9b0b70/branch/master?svg=true)](https://ci.appveyor.com/project/loki-47-6F-64/sunshine/branch/master) +[![Downloads](https://img.shields.io/github/downloads/Loki-47-6F-64/sunshine/total)](https://github.com/Loki-47-6F-64/sunshine/releases) + - [Building](README.md#building) - [Credits](README.md#credits) From fe5375f17b2ae435250b52301059908453ec726d Mon Sep 17 00:00:00 2001 From: loki Date: Wed, 26 May 2021 21:40:23 +0200 Subject: [PATCH 35/35] update debian package version --- gen-deb.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gen-deb.in b/gen-deb.in index ea4e671e..22112418 100755 --- a/gen-deb.in +++ b/gen-deb.in @@ -35,7 +35,7 @@ Package: sunshine Architecture: amd64 Maintainer: @loki Priority: optional -Version: 0.3.1 +Version: 0.4.1 Depends: libssl1.1, libavdevice58, libboost-thread1.71.0, libboost-filesystem1.71.0, libboost-log1.71.0, libpulse0, libopus0, libxcb-shm0, libxcb-xfixes0 Description: Gamestream host for Moonlight EOF