Migrate virtual display config to new Display Device API

This commit is contained in:
Yukino Song
2025-01-15 22:50:08 +08:00
parent 4a2d8b474b
commit 4b78e3b47d
9 changed files with 62 additions and 165 deletions

View File

@@ -417,8 +417,6 @@ namespace config {
video_t video {
false, // headless_mode
false, // follow_client_hdr
true, // set_vdisplay_primary
28, // qp
0, // hevc_mode
@@ -1091,8 +1089,6 @@ namespace config {
}
bool_f(vars, "headless_mode", video.headless_mode);
bool_f(vars, "follow_client_hdr", video.follow_client_hdr);
bool_f(vars, "set_vdisplay_primary", video.set_vdisplay_primary);
int_f(vars, "qp", video.qp);
int_between_f(vars, "hevc_mode", video.hevc_mode, { 0, 3 });
int_between_f(vars, "av1_mode", video.av1_mode, { 0, 3 });

View File

@@ -16,8 +16,6 @@
namespace config {
struct video_t {
bool headless_mode;
bool follow_client_hdr;
bool set_vdisplay_primary;
// ffmpeg params
int qp; // higher == more compression and less quality

View File

@@ -738,6 +738,24 @@ namespace display_device {
return DD_DATA.sm_instance->execute([&output_name](auto &settings_iface) { return settings_iface.getDisplayName(output_name); });
}
std::string
map_display_name(const std::string &display_name) {
std::lock_guard lock { DD_DATA.mutex };
if (!DD_DATA.sm_instance) {
return {};
}
const auto available_devices { DD_DATA.sm_instance->execute([](auto &settings_iface) { return settings_iface.enumAvailableDevices(); }) };
for (auto &i : available_devices) {
if (i.m_display_name == display_name) {
return i.m_device_id;
}
}
return {};
}
void
configure_display(const config::video_t &video_config, const rtsp_stream::launch_session_t &session) {
const auto result { parse_configuration(video_config, session) };

View File

@@ -48,6 +48,9 @@ namespace display_device {
[[nodiscard]] std::string
map_output_name(const std::string &output_name);
[[nodiscard]] std::string
map_display_name(const std::string &display_name);
/**
* @brief Configure the display device based on the user configuration and the session information.
* @note This is a convenience method for calling similar method of a different signature.
@@ -119,7 +122,7 @@ namespace display_device {
* const auto result = reset_persistence();
* @examples_end
*/
[[nodiscard]] bool
bool
reset_persistence();
/**

View File

@@ -163,9 +163,6 @@ namespace proc {
// Ensure starting from a clean slate
terminate();
// Save the original output name in case we modify it temporary later
std::string output_name_orig = config::video.output_name;
_app = app;
_app_id = app_id;
_launch_session = launch_session;
@@ -191,17 +188,19 @@ namespace proc {
render_height &= ~1;
}
launch_session->width = render_width;
launch_session->height = render_height;
#ifdef _WIN32
bool create_virtual_display = config::video.headless_mode || launch_session->virtual_display || _app.virtual_display;
this->initial_display = config::video.output_name;
// Executed when returning from function
auto fg = util::fail_guard([&]() {
// Restore to user defined output name
config::video.output_name = output_name_orig;
config::video.output_name = this->initial_display;
terminate();
if (!create_virtual_display) {
display_device::revert_configuration();
}
display_device::revert_configuration();
});
if (create_virtual_display) {
@@ -211,8 +210,6 @@ namespace proc {
}
if (vDisplayDriverStatus == VDISPLAY::DRIVER_STATUS::OK) {
std::wstring prevPrimaryDisplayName = VDISPLAY::getPrimaryDisplay();
// Try set the render adapter matching the capture adapter if user has specified one
if (!config::video.adapter_name.empty()) {
VDISPLAY::setRenderAdapterByName(platf::from_utf8(config::video.adapter_name));
@@ -236,69 +233,26 @@ namespace proc {
BOOST_LOG(info) << "Virtual Display created at " << vdisplayName;
std::wstring currentPrimaryDisplayName = VDISPLAY::getPrimaryDisplay();
// Don't change display settings when no params are given
if (launch_session->width && launch_session->height && launch_session->fps) {
// Apply display settings
VDISPLAY::changeDisplaySettings(vdisplayName.c_str(), render_width, render_height, launch_session->fps);
}
bool shouldActuallySetPrimary = false;
// Determine if we need to set the virtual display as primary
// Client request overrides local config
bool shouldSetVDisplayPrimary = launch_session->virtual_display;
if (!shouldSetVDisplayPrimary) {
// App config overrides global config
if (_app.virtual_display) {
shouldSetVDisplayPrimary = _app.virtual_display_primary;
} else {
shouldSetVDisplayPrimary = config::video.set_vdisplay_primary;
}
}
if (shouldSetVDisplayPrimary) {
shouldActuallySetPrimary = (currentPrimaryDisplayName != vdisplayName);
} else {
shouldActuallySetPrimary = (currentPrimaryDisplayName != prevPrimaryDisplayName);
}
// Set primary display if needed
if (shouldActuallySetPrimary) {
auto disp = shouldSetVDisplayPrimary ? vdisplayName : prevPrimaryDisplayName;
BOOST_LOG(info) << "Setting display " << disp << " primary";
if (!VDISPLAY::setPrimaryDisplay(disp.c_str())) {
BOOST_LOG(info) << "Setting display " << disp << " primary failed! Are you using Windows 11 24H2?";
}
}
// Set virtual_display to true when everything went fine
this->virtual_display = true;
this->display_name = platf::to_utf8(vdisplayName);
if (config::video.headless_mode) {
// When using headless mode, we don't care which display user configured to use.
// So we always set output_name to the newly created virtual display as a workaround for
// empty name when probing graphics cards.
config::video.output_name = this->display_name;
}
// When using virtual display, we don't care which display user configured to use.
// So we always set output_name to the newly created virtual display as a workaround for
// empty name when probing graphics cards.
config::video.output_name = display_device::map_display_name(this->display_name);
}
} else {
display_device::configure_display(config::video, *launch_session);
}
#else
// Executed when returning from function
auto fg = util::fail_guard([&]() {
// Restore to user defined output name
config::video.output_name = output_name_orig;
terminate();
display_device::revert_configuration();
});
#endif
display_device::configure_display(config::video, *launch_session);
#endif
// Probe encoders again before streaming to ensure our chosen
// encoder matches the active GPU (which could have changed
@@ -420,60 +374,8 @@ namespace proc {
_app_launch_time = std::chrono::steady_clock::now();
#ifdef _WIN32
auto resetHDRThread = std::thread([this, enable_hdr = launch_session->enable_hdr]{
// Windows doesn't seem to be able to set HDR correctly when a display is just connected / changed resolution,
// so we have tooggle HDR for the virtual display manually after a delay.
auto retryInterval = 200ms;
while (is_changing_settings_going_to_fail()) {
if (retryInterval > 2s) {
BOOST_LOG(warning) << "Restoring HDR settings failed due to retry timeout!";
return;
}
std::this_thread::sleep_for(retryInterval);
retryInterval *= 2;
}
// We should have got the actual streaming display by now
std::string currentDisplay = this->display_name;
if (currentDisplay.empty()) {
BOOST_LOG(warning) << "Not getting current display in time! HDR will not be toggled.";
} else {
auto currentDisplayW = platf::from_utf8(currentDisplay).c_str();
this->initial_display = currentDisplay;
this->initial_hdr = VDISPLAY::getDisplayHDRByName(currentDisplayW);
if (config::video.follow_client_hdr) {
if (!VDISPLAY::setDisplayHDRByName(currentDisplayW, false)) {
return;
}
if (enable_hdr) {
if (VDISPLAY::setDisplayHDRByName(currentDisplayW, true)) {
BOOST_LOG(info) << "HDR enabled for display " << currentDisplay;
} else {
BOOST_LOG(info) << "HDR enable failed for display " << currentDisplay;
}
}
} else if (this->initial_hdr) {
if (VDISPLAY::setDisplayHDRByName(currentDisplayW, false) && VDISPLAY::setDisplayHDRByName(currentDisplayW, true)) {
BOOST_LOG(info) << "HDR toggled successfully for display " << currentDisplay;
} else {
BOOST_LOG(info) << "HDR toggle failed for display " << currentDisplay;
}
}
}
});
resetHDRThread.detach();
#endif
fg.disable();
// Restore to user defined output name
config::video.output_name = output_name_orig;
#if defined SUNSHINE_TRAY && SUNSHINE_TRAY >= 1
system_tray::update_tray_playing(_app.name);
#endif
@@ -561,36 +463,39 @@ namespace proc {
_pipe.reset();
#ifdef _WIN32
if (config::video.follow_client_hdr && !this->initial_display.empty()) {
if (VDISPLAY::setDisplayHDRByName(platf::from_utf8(this->initial_display).c_str(), this->initial_hdr)) {
BOOST_LOG(info) << "HDR restored successfully for display " << this->initial_display;
} else {
BOOST_LOG(info) << "HDR restore failed for display " << this->initial_display;
};
}
bool has_run = _app_id > 0;
if (vDisplayDriverStatus == VDISPLAY::DRIVER_STATUS::OK && _launch_session && this->virtual_display) {
#ifdef _WIN32
bool used_virtual_display = vDisplayDriverStatus == VDISPLAY::DRIVER_STATUS::OK && _launch_session && this->virtual_display;
if (used_virtual_display) {
if (VDISPLAY::removeVirtualDisplay(_launch_session->display_guid)) {
BOOST_LOG(info) << "Virtual Display removed successfully";
} else {
BOOST_LOG(info) << "Virtual Display remove failed";
}
}
#endif
bool has_run = _app_id > 0;
// Only show the Stopped notification if we actually have an app to stop
// Since terminate() is always run when a new app has started
if (proc::proc.get_last_run_app_name().length() > 0 && has_run) {
if (used_virtual_display) {
display_device::reset_persistence();
} else {
display_device::revert_configuration();
}
#else
if (proc::proc.get_last_run_app_name().length() > 0 && has_run) {
display_device::revert_configuration();
#endif
#if defined SUNSHINE_TRAY && SUNSHINE_TRAY >= 1
system_tray::update_tray_stopped(proc::proc.get_last_run_app_name());
#endif
display_device::revert_configuration();
}
// Restore output name to its original value
config::video.output_name = initial_display;
_app_id = -1;
display_name.clear();
initial_hdr = false;
@@ -937,7 +842,6 @@ namespace proc {
auto wait_all = app_node.get_optional<bool>("wait-all"s);
auto exit_timeout = app_node.get_optional<int>("exit-timeout"s);
auto virtual_display = app_node.get_optional<bool>("virtual-display"s);
auto virtual_display_primary = app_node.get_optional<bool>("virtual-display-primary"s);
auto resolution_scale_factor = app_node.get_optional<int>("scale-factor"s);
auto use_app_identity = app_node.get_optional<bool>("use-app-identity"s);
@@ -1012,7 +916,6 @@ namespace proc {
ctx.wait_all = wait_all.value_or(true);
ctx.exit_timeout = std::chrono::seconds { exit_timeout.value_or(5) };
ctx.virtual_display = virtual_display.value_or(false);
ctx.virtual_display_primary = virtual_display_primary.value_or(true);
ctx.scale_factor = resolution_scale_factor.value_or(100);
ctx.use_app_identity = use_app_identity.value_or(false);
@@ -1041,7 +944,6 @@ namespace proc {
ctx.name = "Virtual Display";
ctx.image_path = parse_env_val(this_env, "virtual_desktop.png");
ctx.virtual_display = true;
ctx.virtual_display_primary = true;
ctx.scale_factor = 100;
ctx.use_app_identity = false;