feat(linux)!: Support streaming through XDG portals and Pipewire (#4417)

Co-authored-by: Carlos Garnacho <carlosg@gnome.org>
Co-authored-by: Carson Katri <Carson.katri@gmail.com>
Co-authored-by: Bond <bond-d@ukr.net>
Co-authored-by: d.bondarev <d.bondarev@crm-onebox.com>
Co-authored-by: Conn O'Griofa <connogriofa@gmail.com>
This commit is contained in:
David Lane 2026-02-03 08:19:02 -05:00 committed by GitHub
commit 874880e5ea
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
23 changed files with 1338 additions and 68 deletions

View file

@ -1660,9 +1660,9 @@ namespace platf {
if (!fb->handles[0]) {
BOOST_LOG(error) << "Couldn't get handle for DRM Framebuffer ["sv << plane->fb_id << "]: Probably not permitted"sv;
BOOST_LOG((window_system != window_system_e::X11 || config::video.capture == "kms") ? fatal : error)
<< "You must run [sudo setcap cap_sys_admin+p $(readlink -f $(which sunshine))] for KMS display capture to work!\n"sv
<< "If you installed from AppImage or Flatpak, please refer to the official documentation:\n"sv
BOOST_LOG((config::video.capture == "kms") ? fatal : error)
<< "If you installed from AppImage or Flatpak, KMS capture is not supported.\n"sv
<< "Please refer to the official documentation:\n"sv
<< "https://docs.lizardbyte.dev/projects/sunshine/latest/md_docs_2getting__started.html#linux"sv;
break;
}

View file

@ -888,6 +888,9 @@ namespace platf {
#endif
#ifdef SUNSHINE_BUILD_X11
X11, ///< X11
#endif
#ifdef SUNSHINE_BUILD_PORTAL
PORTAL, ///< XDG PORTAL
#endif
MAX_FLAGS ///< The maximum number of flags
};
@ -931,6 +934,15 @@ namespace platf {
}
#endif
#ifdef SUNSHINE_BUILD_PORTAL
std::vector<std::string> portal_display_names();
std::shared_ptr<display_t> portal_display(mem_type_e hwdevice_type, const std::string &display_name, const video::config_t &config);
bool verify_portal() {
return !portal_display_names().empty();
}
#endif
std::vector<std::string> display_names(mem_type_e hwdevice_type) {
#ifdef SUNSHINE_BUILD_CUDA
// display using NvFBC only supports mem_type_e::cuda
@ -952,6 +964,11 @@ namespace platf {
if (sources[source::X11]) {
return x11_display_names();
}
#endif
#ifdef SUNSHINE_BUILD_PORTAL
if (sources[source::PORTAL]) {
return portal_display_names();
}
#endif
return {};
}
@ -990,6 +1007,12 @@ namespace platf {
return x11_display(hwdevice_type, display_name, config);
}
#endif
#ifdef SUNSHINE_BUILD_PORTAL
if (sources[source::PORTAL]) {
BOOST_LOG(info) << "Screencasting with XDG portal"sv;
return portal_display(hwdevice_type, display_name, config);
}
#endif
return nullptr;
}
@ -1019,33 +1042,30 @@ namespace platf {
#endif
#ifdef SUNSHINE_BUILD_CUDA
if ((config::video.capture.empty() && sources.none()) || config::video.capture == "nvfbc") {
if (verify_nvfbc()) {
sources[source::NVFBC] = true;
}
if (((config::video.capture.empty() && sources.none()) || config::video.capture == "nvfbc") && verify_nvfbc()) {
sources[source::NVFBC] = true;
}
#endif
#ifdef SUNSHINE_BUILD_WAYLAND
if ((config::video.capture.empty() && sources.none()) || config::video.capture == "wlr") {
if (verify_wl()) {
sources[source::WAYLAND] = true;
}
if (((config::video.capture.empty() && sources.none()) || config::video.capture == "wlr") && verify_wl()) {
sources[source::WAYLAND] = true;
}
#endif
#ifdef SUNSHINE_BUILD_DRM
if ((config::video.capture.empty() && sources.none()) || config::video.capture == "kms") {
if (verify_kms()) {
sources[source::KMS] = true;
}
if (((config::video.capture.empty() && sources.none()) || config::video.capture == "kms") && verify_kms()) {
sources[source::KMS] = true;
}
#endif
#ifdef SUNSHINE_BUILD_X11
// We enumerate this capture backend regardless of other suitable sources,
// since it may be needed as a NvFBC fallback for software encoding on X11.
if (config::video.capture.empty() || config::video.capture == "x11") {
if (verify_x11()) {
sources[source::X11] = true;
}
if ((config::video.capture.empty() || config::video.capture == "x11") && verify_x11()) {
sources[source::X11] = true;
}
#endif
#ifdef SUNSHINE_BUILD_PORTAL
if ((config::video.capture.empty() || config::video.capture == "portal") && verify_portal()) {
sources[source::PORTAL] = true;
}
#endif

File diff suppressed because it is too large Load diff