Skip to content

Commit

Permalink
Add new API to modify display devices for Windows
Browse files Browse the repository at this point in the history
  • Loading branch information
FrogTheFrog committed Jul 11, 2024
1 parent 037c61d commit 16febac
Show file tree
Hide file tree
Showing 48 changed files with 6,126 additions and 89 deletions.
1 change: 0 additions & 1 deletion .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1067,7 +1067,6 @@ jobs:
mingw-w64-ucrt-x86_64-cppwinrt \
mingw-w64-ucrt-x86_64-graphviz \
mingw-w64-ucrt-x86_64-miniupnpc \
mingw-w64-ucrt-x86_64-nlohmann-json \
mingw-w64-ucrt-x86_64-nodejs \
mingw-w64-ucrt-x86_64-nsis \
mingw-w64-ucrt-x86_64-onevpl \
Expand Down
4 changes: 4 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@
path = third-party/nanors
url = https://github.com/sleepybishop/nanors.git
branch = master
[submodule "third-party/nlohmann_json"]
path = third-party/nlohmann_json
url = https://github.com/nlohmann/json
branch = master
[submodule "third-party/nv-codec-headers"]
path = third-party/nv-codec-headers
url = https://github.com/FFmpeg/nv-codec-headers
Expand Down
10 changes: 10 additions & 0 deletions cmake/compile_definitions/common.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,15 @@ set(SUNSHINE_TARGET_FILES
"${CMAKE_SOURCE_DIR}/third-party/moonlight-common-c/src/RtspParser.c"
"${CMAKE_SOURCE_DIR}/third-party/moonlight-common-c/src/Video.h"
"${CMAKE_SOURCE_DIR}/third-party/tray/src/tray.h"
"${CMAKE_SOURCE_DIR}/src/display_device/display_device.h"
"${CMAKE_SOURCE_DIR}/src/display_device/parsed_config.cpp"
"${CMAKE_SOURCE_DIR}/src/display_device/parsed_config.h"
"${CMAKE_SOURCE_DIR}/src/display_device/session.cpp"
"${CMAKE_SOURCE_DIR}/src/display_device/session.h"
"${CMAKE_SOURCE_DIR}/src/display_device/settings.cpp"
"${CMAKE_SOURCE_DIR}/src/display_device/settings.h"
"${CMAKE_SOURCE_DIR}/src/display_device/to_string.cpp"
"${CMAKE_SOURCE_DIR}/src/display_device/to_string.h"
"${CMAKE_SOURCE_DIR}/src/upnp.cpp"
"${CMAKE_SOURCE_DIR}/src/upnp.h"
"${CMAKE_SOURCE_DIR}/src/cbs.cpp"
Expand Down Expand Up @@ -137,4 +146,5 @@ list(APPEND SUNSHINE_EXTERNAL_LIBRARIES
${FFMPEG_LIBRARIES}
${Boost_LIBRARIES}
${OPENSSL_LIBRARIES}
${JSON_LIBRARIES}
${PLATFORM_LIBRARIES})
1 change: 1 addition & 0 deletions cmake/compile_definitions/linux.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,7 @@ list(APPEND PLATFORM_TARGET_FILES
"${CMAKE_SOURCE_DIR}/src/platform/linux/misc.h"
"${CMAKE_SOURCE_DIR}/src/platform/linux/misc.cpp"
"${CMAKE_SOURCE_DIR}/src/platform/linux/audio.cpp"
"${CMAKE_SOURCE_DIR}/src/platform/linux/display_device.cpp"
"${CMAKE_SOURCE_DIR}/third-party/glad/src/egl.c"
"${CMAKE_SOURCE_DIR}/third-party/glad/src/gl.c"
"${CMAKE_SOURCE_DIR}/third-party/glad/include/EGL/eglplatform.h"
Expand Down
1 change: 1 addition & 0 deletions cmake/compile_definitions/macos.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ set(PLATFORM_TARGET_FILES
"${CMAKE_SOURCE_DIR}/src/platform/macos/av_video.h"
"${CMAKE_SOURCE_DIR}/src/platform/macos/av_video.m"
"${CMAKE_SOURCE_DIR}/src/platform/macos/display.mm"
"${CMAKE_SOURCE_DIR}/src/platform/macos/display_device.cpp"
"${CMAKE_SOURCE_DIR}/src/platform/macos/input.cpp"
"${CMAKE_SOURCE_DIR}/src/platform/macos/microphone.mm"
"${CMAKE_SOURCE_DIR}/src/platform/macos/misc.mm"
Expand Down
10 changes: 9 additions & 1 deletion cmake/compile_definitions/windows.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,15 @@ set(PLATFORM_TARGET_FILES
"${CMAKE_SOURCE_DIR}/src/platform/windows/display_ram.cpp"
"${CMAKE_SOURCE_DIR}/src/platform/windows/display_wgc.cpp"
"${CMAKE_SOURCE_DIR}/src/platform/windows/audio.cpp"
"${CMAKE_SOURCE_DIR}/src/platform/windows/display_device/device_hdr_states.cpp"
"${CMAKE_SOURCE_DIR}/src/platform/windows/display_device/device_modes.cpp"
"${CMAKE_SOURCE_DIR}/src/platform/windows/display_device/device_topology.cpp"
"${CMAKE_SOURCE_DIR}/src/platform/windows/display_device/general_functions.cpp"
"${CMAKE_SOURCE_DIR}/src/platform/windows/display_device/settings_topology.cpp"
"${CMAKE_SOURCE_DIR}/src/platform/windows/display_device/settings_topology.h"
"${CMAKE_SOURCE_DIR}/src/platform/windows/display_device/settings.cpp"
"${CMAKE_SOURCE_DIR}/src/platform/windows/display_device/windows_utils.h"
"${CMAKE_SOURCE_DIR}/src/platform/windows/display_device/windows_utils.cpp"
"${CMAKE_SOURCE_DIR}/third-party/ViGEmClient/src/ViGEmClient.cpp"
"${CMAKE_SOURCE_DIR}/third-party/ViGEmClient/include/ViGEm/Client.h"
"${CMAKE_SOURCE_DIR}/third-party/ViGEmClient/include/ViGEm/Common.h"
Expand Down Expand Up @@ -79,7 +88,6 @@ list(PREPEND PLATFORM_LIBRARIES
avrt
iphlpapi
shlwapi
PkgConfig::NLOHMANN_JSON
${CURL_STATIC_LIBRARIES})

if(SUNSHINE_ENABLE_TRAY)
Expand Down
9 changes: 9 additions & 0 deletions cmake/dependencies/common.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,15 @@ pkg_check_modules(CURL REQUIRED libcurl)
pkg_check_modules(MINIUPNP miniupnpc REQUIRED)
include_directories(SYSTEM ${MINIUPNP_INCLUDE_DIRS})

# nlohmann_json
if(SUNSHINE_SYSTEM_NLOHMANN_JSON)
pkg_check_modules(NLOHMANN_JSON nlohmann_json>=3.9.0 REQUIRED IMPORTED_TARGET)
set(JSON_LIBRARIES PkgConfig::NLOHMANN_JSON)
else()
add_subdirectory("${CMAKE_SOURCE_DIR}/third-party/nlohmann_json")
set(JSON_LIBRARIES nlohmann_json::nlohmann_json)
endif()

# ffmpeg pre-compiled binaries
if(NOT DEFINED FFMPEG_PREPARED_BINARIES)
if(WIN32)
Expand Down
3 changes: 0 additions & 3 deletions cmake/dependencies/windows.cmake
Original file line number Diff line number Diff line change
@@ -1,4 +1 @@
# windows specific dependencies

# nlohmann_json
pkg_check_modules(NLOHMANN_JSON nlohmann_json REQUIRED IMPORTED_TARGET)
1 change: 1 addition & 0 deletions cmake/prep/options.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ option(SUNSHINE_CONFIGURE_ONLY "Configure special files only, then exit." OFF)
option(SUNSHINE_ENABLE_TRAY "Enable system tray icon. This option will be ignored on macOS." ON)
option(SUNSHINE_REQUIRE_TRAY "Require system tray icon. Fail the build if tray requirements are not met." ON)

option(SUNSHINE_SYSTEM_NLOHMANN_JSON "Use system installation of nlohmann_json rather than the submodule." OFF)
option(SUNSHINE_SYSTEM_WAYLAND_PROTOCOLS "Use system installation of wayland-protocols rather than the submodule." OFF)

if(APPLE)
Expand Down
34 changes: 30 additions & 4 deletions docs/source/about/advanced_usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -586,7 +586,7 @@ keybindings
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

**Description**
Select the display number you want to stream.
Select the display you want to stream.

.. tip:: To find the name of the appropriate values follow these instructions.

Expand Down Expand Up @@ -616,9 +616,35 @@ keybindings
You need to use the id value inside the parenthesis, e.g. ``3``.

**Windows**
.. code-block:: batch
During Sunshine startup, you should see the list of detected display devices:

tools\dxgi-info.exe
.. code-block:: text
DEVICE ID: {de9bb7e2-186e-505b-9e93-f48793333810}
DISPLAY NAME: \\.\DISPLAY1
FRIENDLY NAME: ROG PG279Q
DEVICE STATE: PRIMARY
HDR STATE: UNKNOWN
-----------------------
DEVICE ID: {3bd008cd-0465-547c-8da5-c28749c041e6}
DISPLAY NAME: NOT AVAILABLE
FRIENDLY NAME: IDD HDR
DEVICE STATE: INACTIVE
HDR STATE: UNKNOWN
-----------------------
DEVICE ID: {77f67f3e-754f-5d31-af64-ee037e18100a}
DISPLAY NAME: NOT AVAILABLE
FRIENDLY NAME: SunshineHDR
DEVICE STATE: INACTIVE
HDR STATE: UNKNOWN
-----------------------
DEVICE ID: {bc172e6d-86eb-5851-aeca-56525ed716e9}
DISPLAY NAME: NOT AVAILABLE
FRIENDLY NAME: ROG PG279Q
DEVICE STATE: INACTIVE
HDR STATE: UNKNOWN
You need to use the ``DEVICE ID`` value.

**Default**
Sunshine will select the default display.
Expand All @@ -637,7 +663,7 @@ keybindings
**Windows**
.. code-block:: text
output_name = \\.\DISPLAY1
output_name = {de9bb7e2-186e-505b-9e93-f48793333810}
`resolutions <https://localhost:47990/config/#resolutions>`__
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down
1 change: 0 additions & 1 deletion docs/source/building/windows.rst
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ Install dependencies:
mingw-w64-ucrt-x86_64-curl \
mingw-w64-ucrt-x86_64-graphviz \
mingw-w64-ucrt-x86_64-miniupnpc \
mingw-w64-ucrt-x86_64-nlohmann-json \
mingw-w64-ucrt-x86_64-nodejs \
mingw-w64-ucrt-x86_64-nsis \
mingw-w64-ucrt-x86_64-onevpl \
Expand Down
20 changes: 7 additions & 13 deletions src/audio.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,6 @@ namespace audio {
using opus_t = util::safe_ptr<OpusMSEncoder, opus_multistream_encoder_destroy>;
using sample_queue_t = std::shared_ptr<safe::queue_t<std::vector<std::int16_t>>>;

struct audio_ctx_t {
// We want to change the sink for the first stream only
std::unique_ptr<std::atomic_bool> sink_flag;

std::unique_ptr<platf::audio_control_t> control;

bool restore_sink;
platf::sink_t sink;
};

static int
start_audio_control(audio_ctx_t &ctx);
static void
Expand Down Expand Up @@ -95,8 +85,6 @@ namespace audio {
},
};

auto control_shared = safe::make_shared<audio_ctx_t>(start_audio_control, stop_audio_control);

void
encodeThread(sample_queue_t samples, config_t config, void *channel_data) {
auto packets = mail::man->queue<packet_t>(mail::audio_packets);
Expand Down Expand Up @@ -149,7 +137,7 @@ namespace audio {
apply_surround_params(stream, config.customStreamParams);
}

auto ref = control_shared.ref();
auto ref = get_audio_ctx_ref();
if (!ref) {
return;
}
Expand Down Expand Up @@ -255,6 +243,12 @@ namespace audio {
}
}

audio_ctx_ref_t
get_audio_ctx_ref() {
static auto control_shared { safe::make_shared<audio_ctx_t>(start_audio_control, stop_audio_control) };
return control_shared.ref();
}

int
map_stream(int channels, bool quality) {
int shift = quality ? 1 : 0;
Expand Down
29 changes: 29 additions & 0 deletions src/audio.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@
*/
#pragma once

// local includes
#include "platform/common.h"
#include "thread_safe.h"
#include "utility.h"

namespace audio {
enum stream_config_e : int {
STEREO, ///< Stereo
Expand Down Expand Up @@ -52,8 +55,34 @@ namespace audio {
std::bitset<MAX_FLAGS> flags;
};

struct audio_ctx_t {
// We want to change the sink for the first stream only
std::unique_ptr<std::atomic_bool> sink_flag;

std::unique_ptr<platf::audio_control_t> control;

bool restore_sink;
platf::sink_t sink;
};

using buffer_t = util::buffer_t<std::uint8_t>;
using packet_t = std::pair<void *, buffer_t>;
using audio_ctx_ref_t = safe::shared_t<audio::audio_ctx_t>::ptr_t;

void
capture(safe::mail_t mail, config_t config, void *channel_data);

/**
* @brief Get the reference to the audio context.
* @returns A shared pointer reference to audio context.
* @note Aside from the configuration purposes, it can be used to extend the
* audio sink lifetime to capture sink earlier and restore it later.
*
* EXAMPLES:
* ```cpp
* audio_ctx_ref_t audio = get_audio_ctx_ref()
* ```
*/
audio_ctx_ref_t
get_audio_ctx_ref();
} // namespace audio
53 changes: 52 additions & 1 deletion src/config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "rtsp.h"
#include "utility.h"

#include "display_device/parsed_config.h"
#include "platform/common.h"

#ifdef _WIN32
Expand Down Expand Up @@ -377,7 +378,15 @@ namespace config {
{}, // capture
{}, // encoder
{}, // adapter_name

{}, // output_name
(int) display_device::parsed_config_t::device_prep_e::no_operation, // display_device_prep
(int) display_device::parsed_config_t::resolution_change_e::automatic, // resolution_change
{}, // manual_resolution
(int) display_device::parsed_config_t::refresh_rate_change_e::automatic, // refresh_rate_change
{}, // manual_refresh_rate
(int) display_device::parsed_config_t::hdr_prep_e::automatic, // hdr_prep
{} // display_mode_remapping
};

audio_t audio {
Expand Down Expand Up @@ -829,6 +838,40 @@ namespace config {
}
}

void
list_display_mode_remapping_f(std::unordered_map<std::string, std::string> &vars, const std::string &name, std::vector<video_t::display_mode_remapping_t> &input) {
std::string string;

Check warning on line 843 in src/config.cpp

View check run for this annotation

Codecov / codecov/patch

src/config.cpp#L842-L843

Added lines #L842 - L843 were not covered by tests
string_f(vars, name, string);

std::stringstream jsonStream;

// check if string is empty, i.e. when the value doesn't exist in the config file
if (string.empty()) {
return;
}

// We need to add a wrapping object to make it valid JSON, otherwise ptree cannot parse it.
jsonStream << "{\"display_mode_remapping\":" << string << "}";

boost::property_tree::ptree jsonTree;
boost::property_tree::read_json(jsonStream, jsonTree);

for (auto &[_, entry] : jsonTree.get_child("display_mode_remapping"s)) {
auto type = entry.get_optional<std::string>("type"s);
auto received_resolution = entry.get_optional<std::string>("received_resolution"s);
auto received_fps = entry.get_optional<std::string>("received_fps"s);
auto final_resolution = entry.get_optional<std::string>("final_resolution"s);
auto final_refresh_rate = entry.get_optional<std::string>("final_refresh_rate"s);

input.push_back(video_t::display_mode_remapping_t {
type.value_or(""),
received_resolution.value_or(""),
received_fps.value_or(""),
final_resolution.value_or(""),
final_refresh_rate.value_or("") });
}
}

void
list_prep_cmd_f(std::unordered_map<std::string, std::string> &vars, const std::string &name, std::vector<prep_cmd_t> &input) {
std::string string;
Expand Down Expand Up @@ -961,6 +1004,7 @@ namespace config {
}

int_f(vars, "qp", video.qp);
int_between_f(vars, "min_fps_factor", video.min_fps_factor, { 1, 3 });
int_f(vars, "min_threads", video.min_threads);
int_between_f(vars, "hevc_mode", video.hevc_mode, { 0, 3 });
int_between_f(vars, "av1_mode", video.av1_mode, { 0, 3 });
Expand Down Expand Up @@ -1030,8 +1074,15 @@ namespace config {
string_f(vars, "capture", video.capture);
string_f(vars, "encoder", video.encoder);
string_f(vars, "adapter_name", video.adapter_name);

string_f(vars, "output_name", video.output_name);
int_between_f(vars, "min_fps_factor", video.min_fps_factor, { 1, 3 });
int_f(vars, "display_device_prep", video.display_device_prep, display_device::parsed_config_t::device_prep_from_view);
int_f(vars, "resolution_change", video.resolution_change, display_device::parsed_config_t::resolution_change_from_view);
string_f(vars, "manual_resolution", video.manual_resolution);
list_display_mode_remapping_f(vars, "display_mode_remapping", video.display_mode_remapping);
int_f(vars, "refresh_rate_change", video.refresh_rate_change, display_device::parsed_config_t::refresh_rate_change_from_view);
string_f(vars, "manual_refresh_rate", video.manual_refresh_rate);
int_f(vars, "hdr_prep", video.hdr_prep, display_device::parsed_config_t::hdr_prep_from_view);

path_f(vars, "pkey", nvhttp.pkey);
path_f(vars, "cert", nvhttp.cert);
Expand Down
16 changes: 16 additions & 0 deletions src/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,23 @@ namespace config {
std::string capture;
std::string encoder;
std::string adapter_name;

struct display_mode_remapping_t {
std::string type;
std::string received_resolution;
std::string received_fps;
std::string final_resolution;
std::string final_refresh_rate;
};

std::string output_name;
int display_device_prep;
int resolution_change;
std::string manual_resolution;
int refresh_rate_change;
std::string manual_refresh_rate;
int hdr_prep;
std::vector<display_mode_remapping_t> display_mode_remapping;
};

struct audio_t {
Expand Down
Loading

0 comments on commit 16febac

Please sign in to comment.