From 83dd07469e40b38fddc6bf2248eadf630367d38b Mon Sep 17 00:00:00 2001 From: Zlatko Zahariev Date: Fri, 15 Jan 2021 02:07:49 -0500 Subject: [PATCH 01/15] Initial elevated windows work --- sunshine/config.cpp | 3 ++ sunshine/config.h | 3 +- sunshine/nvhttp.cpp | 6 +++- sunshine/platform/windows/desktop.h | 18 ++++++++++++ sunshine/platform/windows/display_base.cpp | 9 ++++-- sunshine/platform/windows/input.cpp | 34 ++++++++++++++++++++-- 6 files changed, 65 insertions(+), 8 deletions(-) create mode 100644 sunshine/platform/windows/desktop.h diff --git a/sunshine/config.cpp b/sunshine/config.cpp index 83c7db9b..39bc9b93 100644 --- a/sunshine/config.cpp +++ b/sunshine/config.cpp @@ -332,6 +332,9 @@ int apply_flags(const char *line) { 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; diff --git a/sunshine/config.h b/sunshine/config.h index 9c14b1fe..a194d956 100644 --- a/sunshine/config.h +++ b/sunshine/config.h @@ -71,7 +71,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 + FLAG_SIZE, + CONST_PIN= 4 // Use "universal" pin }; } diff --git a/sunshine/nvhttp.cpp b/sunshine/nvhttp.cpp index 6ce58a7e..da402706 100644 --- a/sunshine/nvhttp.cpp +++ b/sunshine/nvhttp.cpp @@ -360,7 +360,11 @@ void pair(std::shared_ptr> &add_cert, std::shared_ ptr->second.async_insert_pin.salt = std::move(args.at("salt"s)); - if(config::sunshine.flags[config::flag::PIN_STDIN]) { + if(config::sunshine.flags[config::flag::CONST_PIN]) { + std::string pin("6174"); + getservercert(ptr->second, tree, pin); + } + else if(config::sunshine.flags[config::flag::PIN_STDIN]) { std::string pin; std::cout << "Please insert pin: "sv; diff --git a/sunshine/platform/windows/desktop.h b/sunshine/platform/windows/desktop.h new file mode 100644 index 00000000..b0b2e69b --- /dev/null +++ b/sunshine/platform/windows/desktop.h @@ -0,0 +1,18 @@ +namespace platf { + using namespace std::literals; + inline auto pairInputDesktop(){ + auto hDesk = OpenInputDesktop(DF_ALLOWOTHERACCOUNTHOOK, FALSE, GENERIC_ALL); + 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) ) { + auto err = GetLastError(); + BOOST_LOG(error) << "Failed to SetThreadDesktop [0x"sv << util::hex(err).to_string_view() << ']'; + } + CloseDesktop(hDesk); + } + return hDesk; + }; +}; \ No newline at end of file diff --git a/sunshine/platform/windows/display_base.cpp b/sunshine/platform/windows/display_base.cpp index 4b42880c..86b7127b 100644 --- a/sunshine/platform/windows/display_base.cpp +++ b/sunshine/platform/windows/display_base.cpp @@ -10,6 +10,8 @@ #include "display.h" +#include "desktop.h" + namespace platf { using namespace std::literals; } @@ -90,6 +92,8 @@ int display_base_t::init() { FreeLibrary(user32); }); */ + pairInputDesktop(); + dxgi::factory1_t::pointer factory_p {}; dxgi::adapter_t::pointer adapter_p {}; dxgi::output_t::pointer output_p {}; @@ -150,8 +154,8 @@ int display_base_t::init() { } D3D_FEATURE_LEVEL featureLevels[] { - D3D_FEATURE_LEVEL_12_1, - D3D_FEATURE_LEVEL_12_0, + //D3D_FEATURE_LEVEL_12_1, + //D3D_FEATURE_LEVEL_12_0, D3D_FEATURE_LEVEL_11_1, D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_1, @@ -164,7 +168,6 @@ int display_base_t::init() { status = adapter->QueryInterface(IID_IDXGIAdapter, (void**)&adapter_p); if(FAILED(status)) { BOOST_LOG(error) << "Failed to query IDXGIAdapter interface"sv; - return -1; } diff --git a/sunshine/platform/windows/input.cpp b/sunshine/platform/windows/input.cpp index 60120738..9b43494f 100755 --- a/sunshine/platform/windows/input.cpp +++ b/sunshine/platform/windows/input.cpp @@ -12,11 +12,15 @@ #include "sunshine/main.h" #include "sunshine/platform/common.h" +#include "desktop.h" + namespace platf { using namespace std::literals; using adapteraddrs_t = util::c_ptr; +volatile HDESK _lastKnownInputDesktop = NULL; + class vigem_t { public: using client_t = util::safe_ptr<_VIGEM_CLIENT_T, vigem_free>; @@ -171,9 +175,15 @@ void move_mouse(input_t &input, int deltaX, int deltaY) { mi.dwFlags = MOUSEEVENTF_MOVE; 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; } } @@ -218,8 +228,14 @@ 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; } } @@ -233,9 +249,15 @@ 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) { - BOOST_LOG(warning) << "Couldn't send moue movement input"sv; + auto hDesk = pairInputDesktop(); + if (_lastKnownInputDesktop != hDesk) { + _lastKnownInputDesktop = hDesk; + goto retry; + } + BOOST_LOG(warning) << "Couldn't send mouse scroll input"sv; } } @@ -282,9 +304,15 @@ 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) { - BOOST_LOG(warning) << "Couldn't send moue movement input"sv; + auto hDesk = pairInputDesktop(); + if (_lastKnownInputDesktop != hDesk) { + _lastKnownInputDesktop = hDesk; + goto retry; + } + BOOST_LOG(warning) << "Couldn't send keyboard input"sv; } } From 7abcfc0390bb778d7830be3d3ef5585628e0ba66 Mon Sep 17 00:00:00 2001 From: kiralycraft Date: Sun, 28 Feb 2021 15:52:47 +0200 Subject: [PATCH 02/15] Added ability to stream specific monitor on Linux --- CMakeLists.txt | 1 + assets/sunshine.conf | 11 +- sunshine/config.h | 2 + sunshine/platform/linux/display.cpp | 751 +++++++++++++++++----------- 4 files changed, 463 insertions(+), 302 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 01e933ff..177ce096 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -94,6 +94,7 @@ else() xcb xcb-shm xcb-xfixes + Xrandr ${X11_LIBRARIES} evdev pulse diff --git a/assets/sunshine.conf b/assets/sunshine.conf index 3fabb634..b0216ada 100644 --- a/assets/sunshine.conf +++ b/assets/sunshine.conf @@ -93,6 +93,9 @@ # adapter_name = Radeon RX 580 Series # output_name = \\.\DISPLAY1 +# !! Linux only !! +# Set the display number to stream. I have no idea how they are numbered. They start from 0. +linux_monitor_id = 3 ############################################### # FFmpeg software encoding parameters @@ -102,7 +105,7 @@ # Constant Rate Factor. Between 1 and 52. It allows QP to go up during motion and down with still image, resulting in constant perceived quality # Higher value means more compression, but less quality # If crf == 0, then use QP directly instead -# crf = 0 +crf = 18 # Quantitization Parameter # Higher value means more compression, but less quality @@ -113,7 +116,7 @@ # Increasing the value slightly reduces encoding efficiency, but the tradeoff is usually # worth it to gain the use of more CPU cores for encoding. The ideal value is the lowest # value that can reliably encode at your desired streaming settings on your hardware. -# min_threads = 1 +min_threads = 1 # Allows the client to request HEVC Main or HEVC Main10 video streams. # HEVC is more CPU-intensive to encode, so enabling this may reduce performance when using software encoding. @@ -121,14 +124,14 @@ # If set to 1, Sunshine will not advertise support for HEVC # If set to 2, Sunshine will advertise support for HEVC Main profile # If set to 3, Sunshine will advertise support for HEVC Main and Main10 (HDR) profiles -# hevc_mode = 0 +hevc_mode = 0 # Force a specific encoder, otherwise Sunshine will use the first encoder that is available # supported encoders: # nvenc # software # -# encoder = nvenc +# encoder = vaapi ##################################### Software ##################################### # See x264 --fullhelp for the different presets # sw_preset = superfast diff --git a/sunshine/config.h b/sunshine/config.h index 9c14b1fe..0102fd84 100644 --- a/sunshine/config.h +++ b/sunshine/config.h @@ -29,6 +29,8 @@ struct video_t { std::string encoder; std::string adapter_name; std::string output_name; + + int linux_monitor_id; //Only used on linux }; struct audio_t { diff --git a/sunshine/platform/linux/display.cpp b/sunshine/platform/linux/display.cpp index 1452920e..b3869d92 100644 --- a/sunshine/platform/linux/display.cpp +++ b/sunshine/platform/linux/display.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -26,11 +27,12 @@ #include "sunshine/config.h" #include "sunshine/main.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 xcb_connect_t = util::safe_ptr; @@ -41,407 +43,560 @@ using xdisplay_t = util::safe_ptr_v2; using ximg_t = util::safe_ptr; using xcursor_t = util::safe_ptr; -class shm_id_t { +class shm_id_t +{ public: - shm_id_t() : id { -1 } {} - shm_id_t(int id) : id {id } {} - shm_id_t(shm_id_t &&other) noexcept : id(other.id) { - other.id = -1; - } + shm_id_t() : id { -1 } + { + } + shm_id_t(int id) : id { id } + { + } + shm_id_t(shm_id_t &&other) noexcept : id(other.id) + { + other.id = -1; + } - ~shm_id_t() { - if(id != -1) { - shmctl(id, IPC_RMID, nullptr); - id = -1; - } - } - int id; + ~shm_id_t() + { + if (id != -1) + { + shmctl(id, IPC_RMID, nullptr); + id = -1; + } + } + int id; }; -class shm_data_t { +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) { - shmdt(data); - data = (void*)-1; - } - } + ~shm_data_t() + { + if ((std::uintptr_t) data != -1) + { + shmdt(data); + data = (void*) -1; + } + } - void *data; + void *data; }; -struct x11_img_t : public img_t { - ximg_t img; +struct x11_img_t: public img_t +{ + ximg_t img; }; -struct shm_img_t : public img_t { - ~shm_img_t() override { - delete[] data; - data = nullptr; - } +struct shm_img_t: public img_t +{ + ~shm_img_t() override + { + delete[] data; + data = nullptr; + } }; -void blend_cursor(Display *display, img_t &img) { - xcursor_t overlay { XFixesGetCursorImage(display) }; +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; - return; - } + if (!overlay) + { + BOOST_LOG(error) + << "Couldn't get cursor from XFixesGetCursorImage"sv; + return; + } - overlay->x -= overlay->xhot; - overlay->y -= overlay->yhot; + overlay->x -= overlay->xhot; + overlay->y -= overlay->yhot; - overlay->x = std::max((short)0, overlay->x); - overlay->y = std::max((short)0, overlay->y); + overlay->x -= offsetX; + overlay->y -= offsetY; - auto pixels = (int*)img.data; + overlay->x = std::max((short) 0, overlay->x); + overlay->y = std::max((short) 0, overlay->y); - auto screen_height = img.height; - auto screen_width = img.width; + auto pixels = (int*) img.data; - 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 screen_height = img.height; + auto screen_width = img.width; - auto overlay_begin = &overlay->pixels[y * overlay->width]; - auto overlay_end = &overlay->pixels[y * overlay->width + delta_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 overlay_begin = &overlay->pixels[y * overlay->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]; - std::for_each(overlay_begin, overlay_end, [&](long pixel) { - int *pixel_p = (int*)&pixel; + auto pixels_begin = &pixels[(y + overlay->y)* (img.row_pitch / img.pixel_pitch) + overlay->x]; - auto colors_in = (uint8_t*)pixels_begin; + std::for_each(overlay_begin, overlay_end,[&](long pixel) + { + int *pixel_p = (int*) &pixel; - 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; - } - ++pixels_begin; - }); - } + auto colors_in = (uint8_t*) pixels_begin; + + 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; + } + ++pixels_begin; + }); + } } -struct x11_attr_t : public display_t { - x11_attr_t() : xdisplay {XOpenDisplay(nullptr) }, xwindow { }, xattr {} { - if(!xdisplay) { - BOOST_LOG(fatal) << "Could not open x11 display"sv; - log_flush(); - std::abort(); - } +struct x11_attr_t: public display_t +{ + xdisplay_t xdisplay; + Window xwindow; + XWindowAttributes xattr; - xwindow = DefaultRootWindow(xdisplay.get()); + Display* displayDisplay; - refresh(); + /* + * Remember last X (NOT the streamed monitor!) size. This way we can trigger reinitialization if the dimensions changed while streaming + */ + int lastWidth,lastHeight; - width = xattr.width; - height = xattr.height; - } + /* + * Offsets for when streaming a specific monitor. By default, they are 0. + */ + int displayOffsetX,displayOffsetY; - void refresh() { - XGetWindowAttributes(xdisplay.get(), xwindow, &xattr); - } + x11_attr_t() : xdisplay { displayDisplay = XOpenDisplay(nullptr) }, xwindow { }, xattr { } + { + XInitThreads(); + if (!xdisplay) + { + BOOST_LOG(fatal) + << "Could not open x11 display"sv; + log_flush(); + std::abort(); + } - capture_e snapshot(img_t *img_out_base, std::chrono::milliseconds timeout, bool cursor) override { - refresh(); + xwindow = DefaultRootWindow(xdisplay.get()); - if(width != xattr.width || height != xattr.height) { - return capture_e::reinit; - } + refresh(); - XImage *img { XGetImage( - xdisplay.get(), - xwindow, - 0, 0, - xattr.width, xattr.height, - AllPlanes, ZPixmap) - }; + int streamedMonitor = config::video.linux_monitor_id; - 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 (streamedMonitor != -1) //If the value has been set at all + { + BOOST_LOG(warning) << "Configuring selected monitor 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 + int output = screenr->noutput; - if(cursor) { - blend_cursor(xdisplay.get(), *img_out_base); - } + if (streamedMonitor >= output) + { + BOOST_LOG(error)<< "Could not stream selected display number because there aren't so many."sv; + } + 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; - return capture_e::ok; - } + width = crt_info -> width; + height = crt_info -> height; + displayOffsetX = crt_info -> x; + displayOffsetY = crt_info -> y; - std::shared_ptr alloc_img() override { - return std::make_shared(); - } + XRRFreeCrtcInfo(crt_info); + } + XRRFreeOutputInfo(out_info); + } + XRRFreeScreenResources(screenr); + } + else + { + width = xattr.width; + height = xattr.height; + } - int dummy_img(img_t *img) override { - snapshot(img, 0s, true); - return 0; - } + lastWidth = xattr.width; + lastHeight = xattr.height; + } - xdisplay_t xdisplay; - Window xwindow; - XWindowAttributes xattr; + /** + * Called when the display attributes should change. + */ + void refresh() + { + XGetWindowAttributes(xdisplay.get(), xwindow, &xattr); //Update xattr's + } + + capture_e snapshot(img_t *img_out_base, std::chrono::milliseconds timeout, bool cursor) override + { + refresh(); + capture_e toReturn; + if (xattr.width != lastWidth || xattr.height != lastHeight) //The whole X server changed, so we gotta reinit everything + { + BOOST_LOG(warning)<< "X dimensions changed in non-SHM mode, request reinit"sv; + toReturn = capture_e::reinit; + } + else + { + 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; + 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) + { + blend_cursor(xdisplay.get(), *img_out_base,displayOffsetX,displayOffsetY); + } + toReturn = capture_e::ok; + } + return toReturn; + } + + std::shared_ptr alloc_img() override + { + return std::make_shared(); + } + + int dummy_img(img_t *img) override + { + snapshot(img, 0s, true); + return 0; + } }; -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; - std::uint32_t seg; +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; + std::uint32_t seg; - shm_id_t shm_id; + shm_id_t shm_id; - shm_data_t data; + shm_data_t data; - util::TaskPool::task_id_t refresh_task_id; - void delayed_refresh() { - refresh(); + util::TaskPool::task_id_t refresh_task_id; + 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) } { - 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) } + { + 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)); - } + ~shm_attr_t() override + { + while (!task_pool.cancel(refresh_task_id)); + } - capture_e snapshot(img_t *img, std::chrono::milliseconds timeout, bool cursor) override { - if(width != xattr.width || height != xattr.height) { - return capture_e::reinit; - } + capture_e snapshot(img_t *img, std::chrono::milliseconds timeout, bool cursor) override + { + capture_e toReturn; + if (xattr.width != lastWidth || xattr.height != lastHeight) //The whole X server changed, so we gotta reinit everything + { + BOOST_LOG(warning)<< "X dimensions changed in SHM mode, request reinit"sv; + toReturn = 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, - 0, 0, - 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; + return capture_e::reinit; + } - 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,displayOffsetX,displayOffsetY); + } - if(cursor) { - blend_cursor(shm_xdisplay.get(), *img); - } + toReturn = capture_e::ok; + } + return toReturn; + } - return capture_e::ok; - } + std::shared_ptr alloc_img() override + { + 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]; - std::shared_ptr alloc_img() override { - 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]; + return img; + } - return img; - } + int dummy_img(platf::img_t *img) override + { + return 0; + } - int dummy_img(platf::img_t *img) override { - return 0; - } + int init() + { + shm_xdisplay.reset(XOpenDisplay(nullptr)); + xcb.reset(xcb_connect(nullptr, nullptr)); + if (xcb_connection_has_error(xcb.get())) + { + return -1; + } - int init() { - shm_xdisplay.reset(XOpenDisplay(nullptr)); - xcb.reset(xcb_connect(nullptr, nullptr)); - 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; + } - return -1; - } + auto iter = xcb_setup_roots_iterator(xcb_get_setup(xcb.get())); + display = iter.data; + seg = xcb_generate_id(xcb.get()); - auto iter = xcb_setup_roots_iterator(xcb_get_setup(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) + { + BOOST_LOG(error) + << "shmget failed"sv; + return -1; + } - shm_id.id = shmget(IPC_PRIVATE, frame_size(), IPC_CREAT | 0777); - 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); - 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; + } - 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; - width = display->width_in_pixels; - height = display->height_in_pixels; + return 0; + } - return 0; - } - - std::uint32_t frame_size() { - return width * height * 4; - } + std::uint32_t frame_size() + { + return width * height * 4; + } }; -struct mic_attr_t : public mic_t { - pa_sample_spec ss; - util::safe_ptr mic; +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(); + 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); + 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::error; + } - return capture_e::ok; - } + return capture_e::ok; + } }; -std::shared_ptr shm_display() { - auto shm = std::make_shared(); +std::shared_ptr shm_display() +{ + auto shm = std::make_shared(); - if(shm->init()) { - return nullptr; - } + if (shm->init()) + { + return nullptr; + } - return shm; + return shm; } -std::shared_ptr display(platf::dev_type_e hwdevice_type) { - if(hwdevice_type != platf::dev_type_e::none) { - return nullptr; - } +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; + return nullptr; + } - auto shm_disp = shm_display(); + auto shm_disp = shm_display(); //Attempt to use shared memory X11 to avoid copying the frame - if(!shm_disp) { - return std::make_shared(); - } + if (!shm_disp) + { + return std::make_shared(); //Fallback to standard way if else + } - return shm_disp; + return shm_disp; } -std::unique_ptr microphone(std::uint32_t sample_rate) { - auto mic = std::make_unique(PA_SAMPLE_S16LE, sample_rate, 2); +std::unique_ptr microphone(std::uint32_t sample_rate) +{ + auto mic = std::make_unique(PA_SAMPLE_S16LE, sample_rate, 2); - int status; + int status; - const char *audio_sink = "@DEFAULT_MONITOR@"; - if(!config::audio.sink.empty()) { - audio_sink = config::audio.sink.c_str(); - } + 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) - ); + 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; + if (!mic->mic) + { + auto err_str = pa_strerror(status); + BOOST_LOG(error) + << "pa_simple_new() failed: "sv << err_str; - log_flush(); - std::abort(); - } + log_flush(); + std::abort(); + } - return mic; + return mic; } -ifaddr_t get_ifaddrs() { - ifaddrs *p { nullptr }; +ifaddr_t get_ifaddrs() +{ + ifaddrs *p { nullptr }; - getifaddrs(&p); + getifaddrs(&p); - return ifaddr_t { p }; + return ifaddr_t { p }; } -std::string from_sockaddr(const sockaddr *const ip_addr) { - char data[INET6_ADDRSTRLEN]; +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); - } + 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_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 }; + return std::string { data }; } -std::pair from_sockaddr_ex(const sockaddr *const ip_addr) { - char data[INET6_ADDRSTRLEN]; +std::pair from_sockaddr_ex( + const sockaddr *const ip_addr) +{ + char data[INET6_ADDRSTRLEN]; - 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; - } + 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; + } - if(family == AF_INET) { - inet_ntop(AF_INET, &((sockaddr_in*)ip_addr)->sin_addr, data, INET_ADDRSTRLEN); - port = ((sockaddr_in*)ip_addr)->sin_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; + } - 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)) { - std::ifstream mac_file("/sys/class/net/"s + pos->ifa_name + "/address"); - if(mac_file.good()) { - std::string mac_address; - std::getline(mac_file, mac_address); - return mac_address; - } - } - } - BOOST_LOG(warning) << "Unable to find MAC address for "sv << address; - return "00:00:00:00:00:00"s; +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)) + { + std::ifstream mac_file( + "/sys/class/net/"s + pos->ifa_name + "/address"); + if (mac_file.good()) + { + std::string mac_address; + std::getline(mac_file, mac_address); + return mac_address; + } + } + } + BOOST_LOG(warning) + << "Unable to find MAC address for "sv << address; + return "00:00:00:00:00:00"s; } -void freeImage(XImage *p) { - XDestroyImage(p); +void freeImage(XImage *p) +{ + XDestroyImage(p); } -void freeX(XFixesCursorImage *p) { - XFree(p); +void freeX(XFixesCursorImage *p) +{ + XFree(p); } } From 87be37293e87cc3c58a3f3652d2579c7326115d8 Mon Sep 17 00:00:00 2001 From: kiralycraft Date: Sun, 28 Feb 2021 15:56:23 +0200 Subject: [PATCH 03/15] Accidentally modified stock config file --- assets/sunshine.conf | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/assets/sunshine.conf b/assets/sunshine.conf index b0216ada..46e4516b 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 0. -linux_monitor_id = 3 +# Set the display number to stream. I have no idea how they are numbered. They start from 1, usually. +# linux_monitor_id = 2 ############################################### # FFmpeg software encoding parameters @@ -105,7 +105,7 @@ linux_monitor_id = 3 # Constant Rate Factor. Between 1 and 52. It allows QP to go up during motion and down with still image, resulting in constant perceived quality # Higher value means more compression, but less quality # If crf == 0, then use QP directly instead -crf = 18 +# crf = 0 # Quantitization Parameter # Higher value means more compression, but less quality @@ -116,7 +116,7 @@ crf = 18 # Increasing the value slightly reduces encoding efficiency, but the tradeoff is usually # worth it to gain the use of more CPU cores for encoding. The ideal value is the lowest # value that can reliably encode at your desired streaming settings on your hardware. -min_threads = 1 +# min_threads = 1 # Allows the client to request HEVC Main or HEVC Main10 video streams. # HEVC is more CPU-intensive to encode, so enabling this may reduce performance when using software encoding. @@ -124,14 +124,14 @@ min_threads = 1 # If set to 1, Sunshine will not advertise support for HEVC # If set to 2, Sunshine will advertise support for HEVC Main profile # If set to 3, Sunshine will advertise support for HEVC Main and Main10 (HDR) profiles -hevc_mode = 0 +# hevc_mode = 2 # Force a specific encoder, otherwise Sunshine will use the first encoder that is available # supported encoders: # nvenc # software # -# encoder = vaapi +# encoder = nvenc ##################################### Software ##################################### # See x264 --fullhelp for the different presets # sw_preset = superfast From b336bf2fcbd94d5b490e46ab385882ecdc7af6ab Mon Sep 17 00:00:00 2001 From: kiralycraft Date: Sun, 28 Feb 2021 16:10:29 +0200 Subject: [PATCH 04/15] Attempt to update GCC to version 8 for appveyor --- appveyor.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/appveyor.yml b/appveyor.yml index 2bbc9b2c..1467c82f 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -6,6 +6,7 @@ environment: matrix: - BUILD_TYPE: Debug - BUILD_TYPE: Release + - GCC_VERSION: 8 install: - sh: sudo apt update From fe3784454ac3808dd7f8e5d04d6fc0259d9b479c Mon Sep 17 00:00:00 2001 From: kiralycraft Date: Sun, 28 Feb 2021 17:56:38 +0200 Subject: [PATCH 05/15] Fixed unspecified monitor streaming the whole X --- sunshine/config.cpp | 5 ++++- sunshine/platform/linux/display.cpp | 3 ++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/sunshine/config.cpp b/sunshine/config.cpp index 83c7db9b..cccce8be 100644 --- a/sunshine/config.cpp +++ b/sunshine/config.cpp @@ -105,7 +105,8 @@ video_t video { {}, // encoder {}, // adapter_name - {} // output_name + {}, // output_name + -1 }; audio_t audio {}; @@ -371,6 +372,8 @@ void apply_config(std::unordered_map &&vars) { string_f(vars, "audio_sink", audio.sink); + int_between_f(vars, "linux_monitor_id", video.linux_monitor_id, { 0, 32 }); + string_restricted_f(vars, "origin_pin_allowed", nvhttp.origin_pin_allowed, { "pc"sv, "lan"sv, "wan"sv }); diff --git a/sunshine/platform/linux/display.cpp b/sunshine/platform/linux/display.cpp index b3869d92..5729afbf 100644 --- a/sunshine/platform/linux/display.cpp +++ b/sunshine/platform/linux/display.cpp @@ -202,7 +202,8 @@ struct x11_attr_t: public display_t if (streamedMonitor != -1) //If the value has been set at all { - BOOST_LOG(warning) << "Configuring selected monitor to stream. If it fails here, you may need Xrandr >= 1.5"sv; + + 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 int output = screenr->noutput; From 6112c81db7ba3102aa04fe38bec382ee5c495d93 Mon Sep 17 00:00:00 2001 From: loki Date: Sat, 24 Apr 2021 12:07:26 +0200 Subject: [PATCH 06/15] =?UTF-8?q?Fix=20=C2=96undefined=20reference=20to=20?= =?UTF-8?q?=5F=5Fmemcpy=5Fchk=20when=20building=20on=20msys64?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 01e933ff..554344b3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -69,6 +69,7 @@ if(WIN32) ViGEmClient/include/ViGEm/Util.h ViGEmClient/include/ViGEm/km/BusShared.h) list(PREPEND PLATFORM_LIBRARIES + ssp winmm ksuser wsock32 From 0cfb440cf683f9479bb279ab1e91dbea5e02dfdc Mon Sep 17 00:00:00 2001 From: loki Date: Sat, 24 Apr 2021 14:23:12 +0200 Subject: [PATCH 07/15] Added config examples to the config file --- assets/sunshine.conf | 27 ++++++++++++++++++++++++++- sunshine/video.cpp | 4 +++- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/assets/sunshine.conf b/assets/sunshine.conf index f887e45a..08b1b209 100644 --- a/assets/sunshine.conf +++ b/assets/sunshine.conf @@ -126,7 +126,7 @@ # Force a specific encoder, otherwise Sunshine will use the first encoder that is available # supported encoders: # nvenc -# amdvce +# amdvce # NOTE: alpha stage. The cursor is not yet displayed # software # # encoder = nvenc @@ -171,6 +171,31 @@ ########################## # nv_coder = auto +##################################### AMD ##################################### +###### presets ########### +# default +# speed +# balanced +########################## +# amd_preset = balanced +# +####### rate control ##### +# auto -- let ffmpeg decide rate control +# constqp -- constant QP mode +# vbr -- variable bitrate +# cbr -- constant bitrate +# cbr_hq -- cbr high quality +# cbr_ld_hq -- cbr low delay high quality +# vbr_hq -- vbr high quality +########################## +# amd_rc = auto + +###### h264 entropy ###### +# auto -- let ffmpeg nvenc decide the entropy encoding +# cabac +# cavlc +########################## +# amd_coder = auto ############################################## # Some configurable parameters, are merely toggles for specific features diff --git a/sunshine/video.cpp b/sunshine/video.cpp index 90e1ded5..d5ba02ed 100644 --- a/sunshine/video.cpp +++ b/sunshine/video.cpp @@ -373,9 +373,11 @@ static encoder_t software { static std::vector encoders { #ifdef _WIN32 nvenc, +#endif + software, +#ifdef _WIN32 amdvce, #endif - software }; void reset_display(std::shared_ptr &disp, AVHWDeviceType type) { From 438ae6a7615d1b9731d04dfef80f0b3ff4907fd7 Mon Sep 17 00:00:00 2001 From: loki Date: Sat, 24 Apr 2021 15:53:48 +0200 Subject: [PATCH 08/15] Don't stop streaming when UAC is running --- sunshine/platform/windows/display_base.cpp | 2 -- sunshine/video.cpp | 20 ++++++++++++++++++-- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/sunshine/platform/windows/display_base.cpp b/sunshine/platform/windows/display_base.cpp index c510b044..dbddf29a 100644 --- a/sunshine/platform/windows/display_base.cpp +++ b/sunshine/platform/windows/display_base.cpp @@ -150,8 +150,6 @@ int display_base_t::init() { } D3D_FEATURE_LEVEL featureLevels[] { - D3D_FEATURE_LEVEL_12_1, - D3D_FEATURE_LEVEL_12_0, D3D_FEATURE_LEVEL_11_1, D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_1, diff --git a/sunshine/video.cpp b/sunshine/video.cpp index d5ba02ed..b2e23855 100644 --- a/sunshine/video.cpp +++ b/sunshine/video.cpp @@ -469,7 +469,14 @@ void captureThread( std::this_thread::sleep_for(100ms); } - reset_display(disp, encoder.dev_type); + while(capture_ctx_queue->running()) { + reset_display(disp, encoder.dev_type); + + if(disp) { + break; + } + std::this_thread::sleep_for(200ms); + } if(!disp) { return; } @@ -846,7 +853,16 @@ encode_e encode_run_sync(std::vector> &synce const auto &encoder = encoders.front(); std::shared_ptr disp; - reset_display(disp, encoder.dev_type); + + while(encode_session_ctx_queue.running()) { + reset_display(disp, encoder.dev_type); + if(disp) { + break; + } + + std::this_thread::sleep_for(200ms); + } + if(!disp) { return encode_e::error; } From 0049b36471270bfa2187c081a4602263656c0b71 Mon Sep 17 00:00:00 2001 From: loki Date: Sat, 24 Apr 2021 23:41:56 +0200 Subject: [PATCH 09/15] Use existing config option for selecting monitor --- assets/sunshine.conf | 2 +- pre-compiled | 2 +- sunshine/config.cpp | 3 - sunshine/config.h | 2 - sunshine/platform/linux/display.cpp | 813 +++++++++++++--------------- 5 files changed, 375 insertions(+), 447 deletions(-) diff --git a/assets/sunshine.conf b/assets/sunshine.conf index 46e4516b..7a318ed4 100644 --- a/assets/sunshine.conf +++ b/assets/sunshine.conf @@ -95,7 +95,7 @@ # !! Linux only !! # Set the display number to stream. I have no idea how they are numbered. They start from 1, usually. -# linux_monitor_id = 2 +# output_name = 1 ############################################### # FFmpeg software encoding parameters 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/config.cpp b/sunshine/config.cpp index cccce8be..26878011 100644 --- a/sunshine/config.cpp +++ b/sunshine/config.cpp @@ -106,7 +106,6 @@ video_t video { {}, // encoder {}, // adapter_name {}, // output_name - -1 }; audio_t audio {}; @@ -372,8 +371,6 @@ void apply_config(std::unordered_map &&vars) { string_f(vars, "audio_sink", audio.sink); - int_between_f(vars, "linux_monitor_id", video.linux_monitor_id, { 0, 32 }); - string_restricted_f(vars, "origin_pin_allowed", nvhttp.origin_pin_allowed, { "pc"sv, "lan"sv, "wan"sv }); diff --git a/sunshine/config.h b/sunshine/config.h index 0102fd84..9c14b1fe 100644 --- a/sunshine/config.h +++ b/sunshine/config.h @@ -29,8 +29,6 @@ struct video_t { std::string encoder; std::string adapter_name; std::string output_name; - - int linux_monitor_id; //Only used on linux }; struct audio_t { diff --git a/sunshine/platform/linux/display.cpp b/sunshine/platform/linux/display.cpp index 5729afbf..cde96cf7 100644 --- a/sunshine/platform/linux/display.cpp +++ b/sunshine/platform/linux/display.cpp @@ -43,561 +43,494 @@ using xdisplay_t = util::safe_ptr_v2; using ximg_t = util::safe_ptr; using xcursor_t = util::safe_ptr; -class shm_id_t -{ +class shm_id_t { public: - shm_id_t() : id { -1 } - { - } - shm_id_t(int id) : id { id } - { - } - shm_id_t(shm_id_t &&other) noexcept : id(other.id) - { - other.id = -1; - } + shm_id_t() : id { -1 } {} + shm_id_t(int id) : id { id } {} + shm_id_t(shm_id_t &&other) noexcept : id(other.id) { + other.id = -1; + } - ~shm_id_t() - { - if (id != -1) - { - shmctl(id, IPC_RMID, nullptr); - id = -1; - } - } - int id; + ~shm_id_t() { + if (id != -1) { + shmctl(id, IPC_RMID, nullptr); + id = -1; + } + } + int id; }; -class shm_data_t -{ +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) - { - shmdt(data); - data = (void*) -1; - } - } + ~shm_data_t() + { + if ((std::uintptr_t) data != -1) + { + shmdt(data); + data = (void*) -1; + } + } - void *data; + void *data; }; -struct x11_img_t: public img_t -{ - ximg_t img; +struct x11_img_t: public img_t { + ximg_t img; }; -struct shm_img_t: public img_t -{ - ~shm_img_t() override - { - delete[] data; - data = nullptr; - } +struct shm_img_t: public img_t { + ~shm_img_t() override + { + delete[] data; + data = nullptr; + } }; -void blend_cursor(Display *display, img_t &img, int offsetX,int offsetY) -{ - xcursor_t overlay { XFixesGetCursorImage(display) }; +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; - return; - } + if (!overlay) { + BOOST_LOG(error) + << "Couldn't get cursor from XFixesGetCursorImage"sv; + return; + } - overlay->x -= overlay->xhot; - overlay->y -= overlay->yhot; + overlay->x -= overlay->xhot; + overlay->y -= overlay->yhot; - overlay->x -= offsetX; - overlay->y -= 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; + auto screen_height = img.height; + 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 overlay_begin = &overlay->pixels[y * overlay->width]; - auto overlay_end = &overlay->pixels[y * overlay->width + delta_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 overlay_begin = &overlay->pixels[y * overlay->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) - { - *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; - } - ++pixels_begin; - }); - } + 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; + } + ++pixels_begin; + }); + } } struct x11_attr_t: public display_t { - xdisplay_t xdisplay; - Window xwindow; - XWindowAttributes xattr; + xdisplay_t xdisplay; + Window xwindow; + XWindowAttributes xattr; - Display* displayDisplay; + Display* displayDisplay; - /* - * Remember last X (NOT the streamed monitor!) size. This way we can trigger reinitialization if the dimensions changed while streaming - */ - int lastWidth,lastHeight; + /* + * Remember last X (NOT the streamed monitor!) size. This way we can trigger reinitialization if the dimensions changed while streaming + */ + int lastWidth,lastHeight; - /* - * Offsets for when streaming a specific monitor. By default, they are 0. - */ - int displayOffsetX,displayOffsetY; + /* + * Offsets for when streaming a specific monitor. By default, they are 0. + */ + int displayOffsetX,displayOffsetY; - x11_attr_t() : xdisplay { displayDisplay = XOpenDisplay(nullptr) }, xwindow { }, xattr { } - { - XInitThreads(); - if (!xdisplay) - { - BOOST_LOG(fatal) - << "Could not open x11 display"sv; - log_flush(); - std::abort(); - } + x11_attr_t() : xdisplay { displayDisplay = XOpenDisplay(nullptr) }, xwindow { }, xattr { } + { + XInitThreads(); + if (!xdisplay) { + BOOST_LOG(fatal) << "Could not open x11 display"sv; + log_flush(); + std::abort(); + } - xwindow = DefaultRootWindow(xdisplay.get()); + xwindow = DefaultRootWindow(xdisplay.get()); - refresh(); + refresh(); - int streamedMonitor = config::video.linux_monitor_id; + int streamedMonitor = -1; + if(!config::video.output_name.empty()) { + streamedMonitor = (int)util::from_view(config::video.output_name); + } - if (streamedMonitor != -1) //If the value has been set at all - { + //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 - int output = screenr->noutput; + 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 + int output = screenr->noutput; - if (streamedMonitor >= output) - { - BOOST_LOG(error)<< "Could not stream selected display number because there aren't so many."sv; - } - 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; + if (streamedMonitor >= output) { + BOOST_LOG(error)<< "Could not stream selected display number because there aren't so many."sv; + } + 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; + width = crt_info -> width; + height = crt_info -> height; + displayOffsetX = crt_info -> x; + displayOffsetY = crt_info -> y; - XRRFreeCrtcInfo(crt_info); - } - XRRFreeOutputInfo(out_info); - } - XRRFreeScreenResources(screenr); - } - else - { - width = xattr.width; - height = xattr.height; - } + XRRFreeCrtcInfo(crt_info); + } + XRRFreeOutputInfo(out_info); + } + XRRFreeScreenResources(screenr); + } + else { + width = xattr.width; + height = xattr.height; + } - lastWidth = xattr.width; - lastHeight = xattr.height; - } + lastWidth = xattr.width; + lastHeight = xattr.height; + } - /** - * Called when the display attributes should change. - */ - void refresh() - { - XGetWindowAttributes(xdisplay.get(), xwindow, &xattr); //Update xattr's - } + /** + * Called when the display attributes should change. + */ + void refresh() + { + XGetWindowAttributes(xdisplay.get(), xwindow, &xattr); //Update xattr's + } - capture_e snapshot(img_t *img_out_base, std::chrono::milliseconds timeout, bool cursor) override - { - refresh(); - capture_e toReturn; - if (xattr.width != lastWidth || xattr.height != lastHeight) //The whole X server changed, so we gotta reinit everything - { - BOOST_LOG(warning)<< "X dimensions changed in non-SHM mode, request reinit"sv; - toReturn = capture_e::reinit; - } - else - { - XImage *img { XGetImage(xdisplay.get(), xwindow, displayOffsetX, displayOffsetY, width,height, AllPlanes, ZPixmap) }; + capture_e snapshot(img_t *img_out_base, std::chrono::milliseconds timeout, bool cursor) override { + refresh(); - 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); + //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; + return capture_e::reinit; + } + XImage *img { XGetImage(xdisplay.get(), xwindow, displayOffsetX, displayOffsetY, width,height, AllPlanes, ZPixmap) }; - if (cursor) - { - blend_cursor(xdisplay.get(), *img_out_base,displayOffsetX,displayOffsetY); - } - toReturn = capture_e::ok; - } - return toReturn; - } + 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); - std::shared_ptr alloc_img() override - { - return std::make_shared(); - } + if (cursor) { + blend_cursor(xdisplay.get(), *img_out_base,displayOffsetX,displayOffsetY); + } - int dummy_img(img_t *img) override - { - snapshot(img, 0s, true); - return 0; - } + return capture_e::ok; + } + + std::shared_ptr alloc_img() override { + return std::make_shared(); + } + + int dummy_img(img_t *img) override { + snapshot(img, 0s, true); + return 0; + } }; -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; - std::uint32_t seg; +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; + std::uint32_t seg; - shm_id_t shm_id; + shm_id_t shm_id; - shm_data_t data; + shm_data_t data; - util::TaskPool::task_id_t refresh_task_id; - void delayed_refresh() - { - refresh(); + util::TaskPool::task_id_t refresh_task_id; - refresh_task_id = task_pool.pushDelayed(&shm_attr_t::delayed_refresh,2s, this).task_id; - } + void delayed_refresh() { + refresh(); - 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)); - } + 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; + } - capture_e snapshot(img_t *img, std::chrono::milliseconds timeout, bool cursor) override - { - capture_e toReturn; - if (xattr.width != lastWidth || xattr.height != lastHeight) //The whole X server changed, so we gotta reinit everything - { - BOOST_LOG(warning)<< "X dimensions changed in SHM mode, request reinit"sv; - toReturn = 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); + ~shm_attr_t() override { + while (!task_pool.cancel(refresh_task_id)); + } - 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; - } + 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; + 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); - std::copy_n((std::uint8_t*) data.data, frame_size(), img->data); + 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; + } - if (cursor) - { - blend_cursor(shm_xdisplay.get(), *img,displayOffsetX,displayOffsetY); - } + std::copy_n((std::uint8_t*) data.data, frame_size(), img->data); - toReturn = capture_e::ok; - } - return toReturn; - } + if (cursor) { + blend_cursor(shm_xdisplay.get(), *img,displayOffsetX,displayOffsetY); + } - std::shared_ptr alloc_img() override - { - 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]; + return capture_e::ok; + } + } - return img; - } + std::shared_ptr alloc_img() override { + 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]; - int dummy_img(platf::img_t *img) override - { - return 0; - } + return img; + } - int init() - { - shm_xdisplay.reset(XOpenDisplay(nullptr)); - xcb.reset(xcb_connect(nullptr, nullptr)); - if (xcb_connection_has_error(xcb.get())) - { - return -1; - } + int dummy_img(platf::img_t *img) override { + return 0; + } - if (!xcb_get_extension_data(xcb.get(), &xcb_shm_id)->present) - { - BOOST_LOG(error) - << "Missing SHM extension"sv; + int init() { + shm_xdisplay.reset(XOpenDisplay(nullptr)); + xcb.reset(xcb_connect(nullptr, nullptr)); + if (xcb_connection_has_error(xcb.get())) + { + return -1; + } - return -1; - } + if (!xcb_get_extension_data(xcb.get(), &xcb_shm_id)->present) + { + BOOST_LOG(error) + << "Missing SHM extension"sv; - auto iter = xcb_setup_roots_iterator(xcb_get_setup(xcb.get())); - display = iter.data; - seg = xcb_generate_id(xcb.get()); + return -1; + } - shm_id.id = shmget(IPC_PRIVATE, frame_size(), IPC_CREAT | 0777); - if (shm_id.id == -1) - { - BOOST_LOG(error) - << "shmget failed"sv; - return -1; - } + auto iter = xcb_setup_roots_iterator(xcb_get_setup(xcb.get())); + display = iter.data; + seg = xcb_generate_id(xcb.get()); - xcb_shm_attach(xcb.get(), seg, shm_id.id, false); - data.data = shmat(shm_id.id, nullptr, 0); + shm_id.id = shmget(IPC_PRIVATE, frame_size(), IPC_CREAT | 0777); + if (shm_id.id == -1) + { + BOOST_LOG(error) + << "shmget failed"sv; + return -1; + } - if ((uintptr_t) data.data == -1) - { - BOOST_LOG(error) - << "shmat failed"sv; + xcb_shm_attach(xcb.get(), seg, shm_id.id, false); + data.data = shmat(shm_id.id, nullptr, 0); - return -1; - } + if ((uintptr_t) data.data == -1) + { + BOOST_LOG(error) + << "shmat failed"sv; - /* - * 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 -1; + } - return 0; - } + /* + * 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; - std::uint32_t frame_size() - { - return width * height * 4; - } + return 0; + } + + std::uint32_t frame_size() { + return width * height * 4; + } }; -struct mic_attr_t: public mic_t -{ - pa_sample_spec ss; - util::safe_ptr mic; +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(); + explicit mic_attr_t(pa_sample_format format, std::uint32_t sample_rate, + std::uint8_t channels) : ss { format, sample_rate, channels }, mic { } { } - 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); + capture_e sample(std::vector &sample_buf) override { + auto sample_size = sample_buf.size(); - return capture_e::error; - } + 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::ok; - } + return capture_e::error; + } + + return capture_e::ok; + } }; -std::shared_ptr shm_display() -{ - auto shm = std::make_shared(); +std::shared_ptr shm_display() { + auto shm = std::make_shared(); - if (shm->init()) - { - return nullptr; - } + if (shm->init()) { + return nullptr; + } - return shm; + return shm; } -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; - return nullptr; - } +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; + return nullptr; + } - auto shm_disp = shm_display(); //Attempt to use shared memory X11 to avoid copying the frame + auto shm_disp = shm_display(); //Attempt to use shared memory X11 to avoid copying the frame - if (!shm_disp) - { - return std::make_shared(); //Fallback to standard way if else - } + if (!shm_disp) { + return std::make_shared(); //Fallback to standard way if else + } - return shm_disp; + return shm_disp; } -std::unique_ptr microphone(std::uint32_t sample_rate) -{ - auto mic = std::make_unique(PA_SAMPLE_S16LE, sample_rate, 2); +std::unique_ptr microphone(std::uint32_t sample_rate) { + auto mic = std::make_unique(PA_SAMPLE_S16LE, sample_rate, 2); - int status; + int status; - const char *audio_sink = "@DEFAULT_MONITOR@"; - if (!config::audio.sink.empty()) - { - audio_sink = config::audio.sink.c_str(); - } + 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)); + 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; + if (!mic->mic) { + auto err_str = pa_strerror(status); + BOOST_LOG(error) << "pa_simple_new() failed: "sv << err_str; - log_flush(); - std::abort(); - } + log_flush(); + std::abort(); + } - return mic; + return mic; } -ifaddr_t get_ifaddrs() -{ - ifaddrs *p { nullptr }; +ifaddr_t get_ifaddrs() { + ifaddrs *p { nullptr }; - getifaddrs(&p); + getifaddrs(&p); - return ifaddr_t { p }; + return ifaddr_t { p }; } -std::string from_sockaddr(const sockaddr *const ip_addr) -{ - char data[INET6_ADDRSTRLEN]; +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); - } + 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_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 }; + return std::string { data }; } -std::pair from_sockaddr_ex( - const sockaddr *const ip_addr) -{ - char data[INET6_ADDRSTRLEN]; +std::pair from_sockaddr_ex(const sockaddr *const ip_addr) { + char data[INET6_ADDRSTRLEN]; - 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; - } + 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; + } - if (family == AF_INET) - { - inet_ntop(AF_INET, &((sockaddr_in*) ip_addr)->sin_addr, data, - INET_ADDRSTRLEN); - port = ((sockaddr_in*) ip_addr)->sin_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; + } - 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)) - { - std::ifstream mac_file( - "/sys/class/net/"s + pos->ifa_name + "/address"); - if (mac_file.good()) - { - std::string mac_address; - std::getline(mac_file, mac_address); - return mac_address; - } - } - } - BOOST_LOG(warning) - << "Unable to find MAC address for "sv << address; - return "00:00:00:00:00:00"s; +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)) { + std::ifstream mac_file("/sys/class/net/"s + pos->ifa_name + "/address"); + if (mac_file.good()) { + std::string mac_address; + std::getline(mac_file, mac_address); + return mac_address; + } + } + } + BOOST_LOG(warning) + << "Unable to find MAC address for "sv << address; + return "00:00:00:00:00:00"s; } -void freeImage(XImage *p) -{ - XDestroyImage(p); +void freeImage(XImage *p) { + XDestroyImage(p); } -void freeX(XFixesCursorImage *p) -{ - XFree(p); +void freeX(XFixesCursorImage *p) { + XFree(p); } } From e81db118d5a1fa097e1c672ecd7b3fa8b21975a5 Mon Sep 17 00:00:00 2001 From: Loki Date: Mon, 26 Apr 2021 14:46:57 +0200 Subject: [PATCH 10/15] Fix windows build --- .gitmodules | 3 -- CMakeLists.txt | 110 ++++++++++++++++++++++++++++++------------------- pre-compiled | 1 - 3 files changed, 68 insertions(+), 46 deletions(-) delete mode 160000 pre-compiled diff --git a/.gitmodules b/.gitmodules index db296a31..93af2e68 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,6 +7,3 @@ [submodule "ViGEmClient"] path = ViGEmClient url = https://github.com/ViGEm/ViGEmClient -[submodule "pre-compiled"] - path = pre-compiled - url = https://github.com/TheElixZammuto/sunshine-prebuilt.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 63f705ec..18cd222f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8) +cmake_minimum_required(VERSION 3.0) project(Sunshine) @@ -7,56 +7,65 @@ set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}) # On MSYS2, building a stand-alone binary that links with ffmpeg is not possible, # Therefore, ffmpeg, libx264 and libx265 must be build from source if(WIN32) - option(SUNSHINE_STANDALONE "Compile stand-alone binary of Sunshine" OFF) - if(SUNSHINE_STANDALONE) - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static") + file( + DOWNLOAD "https://github.com/TheElixZammuto/sunshine-prebuilt/releases/download/1.0.0/pre-compiled.zip" "${CMAKE_CURRENT_BINARY_DIR}/pre-compiled.zip" + TIMEOUT 60 + EXPECTED_HASH SHA256=5d59986bd7f619eaaf82b2dd56b5127b747c9cbe8db61e3b898ff6b485298ed6) + + file(ARCHIVE_EXTRACT + INPUT "${CMAKE_CURRENT_BINARY_DIR}/pre-compiled.zip" + DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/pre-compiled) + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static") - if(NOT DEFINED SUNSHINE_PREPARED_BINARIES) - set(SUNSHINE_PREPARED_BINARIES "${CMAKE_CURRENT_SOURCE_DIR}/pre-compiled/windows") - endif() - list(PREPEND PLATFORM_LIBRARIES - C:/msys64/mingw64/lib/gcc/x86_64-w64-mingw32/${CMAKE_CXX_COMPILER_VERSION}/libstdc++.a - C:/msys64/mingw64/x86_64-w64-mingw32/lib/libwinpthread.a - ) - - set(FFMPEG_INCLUDE_DIRS - ${SUNSHINE_PREPARED_BINARIES}/include) - set(FFMPEG_LIBRARIES - ${SUNSHINE_PREPARED_BINARIES}/lib/libavcodec.a - ${SUNSHINE_PREPARED_BINARIES}/lib/libavdevice.a - ${SUNSHINE_PREPARED_BINARIES}/lib/libavfilter.a - ${SUNSHINE_PREPARED_BINARIES}/lib/libavformat.a - ${SUNSHINE_PREPARED_BINARIES}/lib/libavutil.a - ${SUNSHINE_PREPARED_BINARIES}/lib/libpostproc.a - ${SUNSHINE_PREPARED_BINARIES}/lib/libswresample.a - ${SUNSHINE_PREPARED_BINARIES}/lib/libswscale.a - ${SUNSHINE_PREPARED_BINARIES}/lib/libx264.a - ${SUNSHINE_PREPARED_BINARIES}/lib/libx265.a - ${SUNSHINE_PREPARED_BINARIES}/lib/libhdr10plus.a - z lzma bcrypt C:/msys64/mingw64/lib/libiconv.a) + if(NOT DEFINED SUNSHINE_PREPARED_BINARIES) + set(SUNSHINE_PREPARED_BINARIES "${CMAKE_CURRENT_BINARY_DIR}/pre-compiled/windows") endif() -else() - set(SUNSHINE_STANDALONE OFF) -endif() + set(FFMPEG_INCLUDE_DIRS + ${SUNSHINE_PREPARED_BINARIES}/include) + set(FFMPEG_LIBRARIES + ${SUNSHINE_PREPARED_BINARIES}/lib/libavcodec.a + ${SUNSHINE_PREPARED_BINARIES}/lib/libavdevice.a + ${SUNSHINE_PREPARED_BINARIES}/lib/libavfilter.a + ${SUNSHINE_PREPARED_BINARIES}/lib/libavformat.a + ${SUNSHINE_PREPARED_BINARIES}/lib/libavutil.a + ${SUNSHINE_PREPARED_BINARIES}/lib/libpostproc.a + ${SUNSHINE_PREPARED_BINARIES}/lib/libswresample.a + ${SUNSHINE_PREPARED_BINARIES}/lib/libswscale.a + ${SUNSHINE_PREPARED_BINARIES}/lib/libx264.a + ${SUNSHINE_PREPARED_BINARIES}/lib/libx265.a + ${SUNSHINE_PREPARED_BINARIES}/lib/libhdr10plus.a + z lzma bcrypt libiconv.a) +endif() add_subdirectory(Simple-Web-Server) add_subdirectory(moonlight-common-c/enet) find_package(Threads REQUIRED) find_package(OpenSSL REQUIRED) -if(NOT SUNSHINE_STANDALONE) - find_package(FFmpeg REQUIRED) -endif() - list(APPEND SUNSHINE_COMPILE_OPTIONS -fPIC -Wall -Wno-missing-braces -Wno-maybe-uninitialized -Wno-sign-compare) if(WIN32) + file( + DOWNLOAD "https://github.com/TheElixZammuto/sunshine-prebuilt/releases/download/1.0.0/pre-compiled.zip" "${CMAKE_CURRENT_BINARY_DIR}/pre-compiled.zip" + TIMEOUT 60 + EXPECTED_HASH SHA256=5d59986bd7f619eaaf82b2dd56b5127b747c9cbe8db61e3b898ff6b485298ed6) + + file(ARCHIVE_EXTRACT + INPUT "${CMAKE_CURRENT_BINARY_DIR}/pre-compiled.zip" + DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/pre-compiled) + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static") + + if(NOT DEFINED SUNSHINE_PREPARED_BINARIES) + set(SUNSHINE_PREPARED_BINARIES "${CMAKE_CURRENT_BINARY_DIR}/pre-compiled/windows") + endif() + add_subdirectory(tools) #This is temporary, only tools for Windows are needed, for now list(APPEND SUNSHINE_DEFINITIONS APPS_JSON="apps_windows.json") - include_directories( - ViGEmClient/include) + + include_directories(ViGEmClient/include) + set(PLATFORM_TARGET_FILES sunshine/platform/windows/input.cpp sunshine/platform/windows/display.h @@ -69,8 +78,27 @@ if(WIN32) ViGEmClient/include/ViGEm/Common.h ViGEmClient/include/ViGEm/Util.h ViGEmClient/include/ViGEm/km/BusShared.h) + + set(FFMPEG_INCLUDE_DIRS + ${SUNSHINE_PREPARED_BINARIES}/include) + set(FFMPEG_LIBRARIES + ${SUNSHINE_PREPARED_BINARIES}/lib/libavcodec.a + ${SUNSHINE_PREPARED_BINARIES}/lib/libavdevice.a + ${SUNSHINE_PREPARED_BINARIES}/lib/libavfilter.a + ${SUNSHINE_PREPARED_BINARIES}/lib/libavformat.a + ${SUNSHINE_PREPARED_BINARIES}/lib/libavutil.a + ${SUNSHINE_PREPARED_BINARIES}/lib/libpostproc.a + ${SUNSHINE_PREPARED_BINARIES}/lib/libswresample.a + ${SUNSHINE_PREPARED_BINARIES}/lib/libswscale.a + ${SUNSHINE_PREPARED_BINARIES}/lib/libx264.a + ${SUNSHINE_PREPARED_BINARIES}/lib/libx265.a + ${SUNSHINE_PREPARED_BINARIES}/lib/libhdr10plus.a + z lzma bcrypt libiconv.a) + list(PREPEND PLATFORM_LIBRARIES - ssp + libstdc++.a + libwinpthread.a + libssp.a winmm ksuser wsock32 @@ -181,11 +209,9 @@ if(NOT SUNSHINE_ASSETS_DIR) set(SUNSHINE_ASSETS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/assets") endif() -if(SUNSHINE_STANDALONE) - set(OPENSSL_LIBRARIES - C:/msys64/mingw64/lib/libssl.a - C:/msys64/mingw64/lib/libcrypto.a) -endif() +set(OPENSSL_LIBRARIES + libssl.a + libcrypto.a) list(APPEND SUNSHINE_EXTERNAL_LIBRARIES ${CMAKE_THREAD_LIBS_INIT} diff --git a/pre-compiled b/pre-compiled deleted file mode 160000 index bbf56474..00000000 --- a/pre-compiled +++ /dev/null @@ -1 +0,0 @@ -Subproject commit bbf5647402859945dee541a300b22702a2ee9271 From 208de3dae9ef8ca040d7a8363a92064df9db1592 Mon Sep 17 00:00:00 2001 From: loki Date: Mon, 26 Apr 2021 20:36:54 +0200 Subject: [PATCH 11/15] Fix Linux build --- CMakeLists.txt | 9 +++++---- README.md | 15 ++------------- appveyor.yml | 7 +++---- 3 files changed, 10 insertions(+), 21 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 18cd222f..1347b7d7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -79,6 +79,10 @@ if(WIN32) ViGEmClient/include/ViGEm/Util.h ViGEmClient/include/ViGEm/km/BusShared.h) + set(OPENSSL_LIBRARIES + libssl.a + libcrypto.a) + set(FFMPEG_INCLUDE_DIRS ${SUNSHINE_PREPARED_BINARIES}/include) set(FFMPEG_LIBRARIES @@ -114,6 +118,7 @@ else() list(APPEND SUNSHINE_DEFINITIONS APPS_JSON="apps_linux.json") find_package(X11 REQUIRED) + find_package(FFmpeg REQUIRED) set(PLATFORM_TARGET_FILES sunshine/platform/linux/display.cpp sunshine/platform/linux/input.cpp) @@ -209,10 +214,6 @@ if(NOT SUNSHINE_ASSETS_DIR) set(SUNSHINE_ASSETS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/assets") endif() -set(OPENSSL_LIBRARIES - libssl.a - libcrypto.a) - list(APPEND SUNSHINE_EXTERNAL_LIBRARIES ${CMAKE_THREAD_LIBS_INIT} stdc++fs diff --git a/README.md b/README.md index fc414370..3a1bac44 100644 --- a/README.md +++ b/README.md @@ -50,10 +50,10 @@ sunshine needs access to uinput to create mouse and gamepad events: ### Requirements: - MSYS2 : mingw-w64-x86_64-openssl mingw-w64-x86_64-cmake mingw-w64-x86_64-toolchain mingw-w64-x86_64-ffmpeg mingw-w64-x86_64-boost + 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 ### Compilation: -- `git clone https://github.com/loki-47-6F-64/sunshine.git --recurse-submodules` +- `git clone https://github.com/loki-47-6F-64/sunshine.git --recursive` - `cd sunshine && mkdir build && cd build` - `cmake -G"Unix Makefiles" ..` - `make` @@ -61,17 +61,6 @@ sunshine needs access to uinput to create mouse and gamepad events: ### Setup: - **OPTIONAL** Gamepad support: Download and run 'ViGEmBus_Setup_1.16.116.exe' from [https://github.com/ViGEm/ViGEmBus/releases] -### Static build -#### Requirements: - MSYS2 : mingw-w64-x86_64-openssl mingw-w64-x86_64-cmake mingw-w64-x86_64-toolchain mingw-w64-x86_64-ffmpeg mingw-w64-x86_64-boost git-lfs - -#### Compilation: -- `git lfs install` -- `git clone https://github.com/loki-47-6F-64/sunshine.git --recurse-submodules` -- `cd sunshine && mkdir build && cd build` -- `cmake -DSUNSHINE_STANDALONE=ON -G"Unix Makefiles" ..` -- `make` - # Common diff --git a/appveyor.yml b/appveyor.yml index 1467c82f..4a11c494 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,16 +1,15 @@ image: - - Ubuntu + - Ubuntu2004 - Visual Studio 2019 environment: matrix: - BUILD_TYPE: Debug - BUILD_TYPE: Release - - GCC_VERSION: 8 install: - sh: sudo apt update - - 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 libxfixes-dev libevdev-dev libxcb1-dev libxcb-shm0-dev libxcb-xfixes0-dev + - 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 - 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: @@ -22,7 +21,7 @@ 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 .. - - cmd: cmake -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DSUNSHINE_STANDALONE=ON -DSUNSHINE_ASSETS_DIR=assets -G "MinGW Makefiles" .. + - cmd: cmake -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DSUNSHINE_ASSETS_DIR=assets -G "MinGW Makefiles" .. - sh: make -j$(nproc) - cmd: mingw32-make -j2 - cmd: set PATH=%OLDPATH% From a7030641f3e83c5ffd5bc4338ec16688c97d9ff5 Mon Sep 17 00:00:00 2001 From: loki Date: Mon, 26 Apr 2021 20:50:37 +0200 Subject: [PATCH 12/15] Second attempt to fix linux build --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 4a11c494..8c5359c9 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,5 +1,5 @@ image: - - Ubuntu2004 + - Ubuntu - Visual Studio 2019 environment: From bb88d6f8282d472981c57977ce01164a6fff28a8 Mon Sep 17 00:00:00 2001 From: loki Date: Mon, 26 Apr 2021 21:08:20 +0200 Subject: [PATCH 13/15] Third attempt to fix linux build for appveyor --- appveyor.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 8c5359c9..e0115dab 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,5 +1,5 @@ image: - - Ubuntu + - Ubuntu2004 - Visual Studio 2019 environment: @@ -8,7 +8,7 @@ environment: - BUILD_TYPE: Release install: - - sh: sudo apt update + - 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 - 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" From 1be5b4787f3aa22bd32171b8a8cae6e27237a4c8 Mon Sep 17 00:00:00 2001 From: loki Date: Mon, 26 Apr 2021 19:27:55 +0200 Subject: [PATCH 14/15] Moved pairInputDesktop() from desktop.h to input.cpp --- sunshine/platform/windows/desktop.h | 18 ------------------ sunshine/platform/windows/display_base.cpp | 3 --- sunshine/platform/windows/input.cpp | 21 +++++++++++++++++++-- 3 files changed, 19 insertions(+), 23 deletions(-) delete mode 100644 sunshine/platform/windows/desktop.h diff --git a/sunshine/platform/windows/desktop.h b/sunshine/platform/windows/desktop.h deleted file mode 100644 index b0b2e69b..00000000 --- a/sunshine/platform/windows/desktop.h +++ /dev/null @@ -1,18 +0,0 @@ -namespace platf { - using namespace std::literals; - inline auto pairInputDesktop(){ - auto hDesk = OpenInputDesktop(DF_ALLOWOTHERACCOUNTHOOK, FALSE, GENERIC_ALL); - 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) ) { - auto err = GetLastError(); - BOOST_LOG(error) << "Failed to SetThreadDesktop [0x"sv << util::hex(err).to_string_view() << ']'; - } - CloseDesktop(hDesk); - } - return hDesk; - }; -}; \ No newline at end of file diff --git a/sunshine/platform/windows/display_base.cpp b/sunshine/platform/windows/display_base.cpp index cf5c5e4f..29b53523 100644 --- a/sunshine/platform/windows/display_base.cpp +++ b/sunshine/platform/windows/display_base.cpp @@ -10,8 +10,6 @@ #include "display.h" -#include "desktop.h" - namespace platf { using namespace std::literals; } @@ -92,7 +90,6 @@ int display_base_t::init() { FreeLibrary(user32); }); */ - pairInputDesktop(); dxgi::factory1_t::pointer factory_p {}; dxgi::adapter_t::pointer adapter_p {}; diff --git a/sunshine/platform/windows/input.cpp b/sunshine/platform/windows/input.cpp index 9b43494f..e69fea96 100755 --- a/sunshine/platform/windows/input.cpp +++ b/sunshine/platform/windows/input.cpp @@ -12,14 +12,13 @@ #include "sunshine/main.h" #include "sunshine/platform/common.h" -#include "desktop.h" - namespace platf { using namespace std::literals; using adapteraddrs_t = util::c_ptr; volatile HDESK _lastKnownInputDesktop = NULL; +HDESK pairInputDesktop(); class vigem_t { public: @@ -355,6 +354,24 @@ int thread_priority() { return SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST) ? 0 : 1; } +HDESK pairInputDesktop() { + auto hDesk = OpenInputDesktop(DF_ALLOWOTHERACCOUNTHOOK, FALSE, GENERIC_ALL); + 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) ) { + auto err = GetLastError(); + BOOST_LOG(error) << "Failed to SetThreadDesktop [0x"sv << util::hex(err).to_string_view() << ']'; + } + CloseDesktop(hDesk); + } + + return hDesk; +}; + void freeInput(void *p) { auto vigem = (vigem_t*)p; From fe8c2ceab9bba05da62202502563293c9e195fab Mon Sep 17 00:00:00 2001 From: loki Date: Wed, 28 Apr 2021 13:44:42 +0200 Subject: [PATCH 15/15] Warn when VideoProcessorSetStreamAlpha isn't supported --- sunshine/platform/windows/display_vram.cpp | 19 +++++++++++++------ sunshine/platform/windows/input.cpp | 4 ++-- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/sunshine/platform/windows/display_vram.cpp b/sunshine/platform/windows/display_vram.cpp index 55fce9a4..0d87b40c 100644 --- a/sunshine/platform/windows/display_vram.cpp +++ b/sunshine/platform/windows/display_vram.cpp @@ -99,6 +99,7 @@ util::buffer_t make_cursor_image(util::buffer_t &&im class hwdevice_t : public platf::hwdevice_t { public: + hwdevice_t(std::vector *hwdevices_p) : hwdevices_p { hwdevices_p } {} hwdevice_t() = delete; @@ -168,8 +169,8 @@ public: auto &processor_in = it->second; D3D11_VIDEO_PROCESSOR_STREAM stream[] { - { TRUE, 0, 0, 0, 0, nullptr, processor_in.get(), nullptr }, - { TRUE, 0, 0, 0, 0, nullptr, cursor_in.get(), nullptr } + { TRUE, 0, 0, 0, 0, nullptr, processor_in.get() }, + { TRUE, 0, 0, 0, 0, nullptr, cursor_in.get() } }; auto status = ctx->VideoProcessorBlt(processor.get(), processor_out.get(), 0, cursor_visible ? 2 : 1, stream); @@ -233,6 +234,12 @@ public: } processor_e.reset(vp_e_p); + D3D11_VIDEO_PROCESSOR_CAPS proc_caps; + processor_e->GetVideoProcessorCaps(&proc_caps); + if(!(proc_caps.FeatureCaps & D3D11_VIDEO_PROCESSOR_FEATURE_CAPS_ALPHA_STREAM)) { + BOOST_LOG(warning) << "VideoProcessorSetStreamAlpha() not supported, hardware accelerated mouse cannot be added to the video stream"sv; + } + video::processor_t::pointer processor_p; status = device->CreateVideoProcessor(processor_e.get(), 0, &processor_p); if(FAILED(status)) { @@ -241,6 +248,9 @@ public: } processor.reset(processor_p); + // Tell video processor alpha values need to be enabled + ctx->VideoProcessorSetStreamAlpha(processor.get(), 1, TRUE, 1.0f); + D3D11_TEXTURE2D_DESC t {}; t.Width = out_width; t.Height = out_height; @@ -275,9 +285,6 @@ public: } processor_out.reset(processor_out_p); - // Tell video processor alpha values need to be enabled - ctx->VideoProcessorSetStreamAlpha(processor.get(), 1, TRUE, 1.0f); - device_p->AddRef(); data = device_p; return 0; @@ -373,7 +380,7 @@ capture_e display_vram_t::snapshot(platf::img_t *img_base, std::chrono::millisec dxgi::texture2d_t::pointer tex_p {}; auto status = device->CreateTexture2D(&t, &data, &tex_p); if(FAILED(status)) { - BOOST_LOG(error) << "Failed to create dummy texture [0x"sv << util::hex(status).to_string_view() << ']'; + BOOST_LOG(error) << "Failed to create mouse texture [0x"sv << util::hex(status).to_string_view() << ']'; return capture_e::error; } texture2d_t texture { tex_p }; diff --git a/sunshine/platform/windows/input.cpp b/sunshine/platform/windows/input.cpp index e69fea96..284f3c83 100755 --- a/sunshine/platform/windows/input.cpp +++ b/sunshine/platform/windows/input.cpp @@ -188,7 +188,7 @@ retry: } void button_mouse(input_t &input, int button, bool release) { - constexpr SHORT KEY_STATE_DOWN = 0x8000; + constexpr auto KEY_STATE_DOWN = (SHORT)0x8000; INPUT i {}; @@ -370,7 +370,7 @@ HDESK pairInputDesktop() { } return hDesk; -}; +} void freeInput(void *p) { auto vigem = (vigem_t*)p;