diff --git a/modules/desktop_capture/linux/wayland/base_capturer_pipewire.cc b/modules/desktop_capture/linux/wayland/base_capturer_pipewire.cc index 3f93393381..aea6cdfde2 100644 --- a/modules/desktop_capture/linux/wayland/base_capturer_pipewire.cc +++ b/modules/desktop_capture/linux/wayland/base_capturer_pipewire.cc @@ -12,7 +12,6 @@ #include #include -#include #include #include #include @@ -30,7 +29,6 @@ #include "rtc_base/checks.h" #include "rtc_base/logging.h" #include "rtc_base/string_encode.h" -#include "rtc_base/string_to_number.h" #if defined(WEBRTC_DLOPEN_PIPEWIRE) #include "modules/desktop_capture/linux/wayland/pipewire_stubs.h" @@ -57,10 +55,6 @@ const char kPipeWireLib[] = "libpipewire-0.3.so.0"; const char kDrmLib[] = "libdrm.so.2"; #endif -constexpr BaseCapturerPipeWire::PipeWireVersion kDmaBufMinVersion = {0, 3, 24}; -constexpr BaseCapturerPipeWire::PipeWireVersion kDmaBufModifierMinVersion = { - 0, 3, 33}; - #if !PW_CHECK_VERSION(0, 3, 29) #define SPA_POD_PROP_FLAG_MANDATORY (1u << 3) #endif @@ -68,25 +62,31 @@ constexpr BaseCapturerPipeWire::PipeWireVersion kDmaBufModifierMinVersion = { #define SPA_POD_PROP_FLAG_DONT_FIXATE (1u << 4) #endif -BaseCapturerPipeWire::PipeWireVersion ParsePipeWireVersion( - const char* version) { +struct pw_version { + int major = 0; + int minor = 0; + int micro = 0; +}; + +bool CheckPipeWireVersion(pw_version required_version) { std::vector parsed_version; - rtc::split(version, '.', &parsed_version); + std::string version_string = pw_get_library_version(); + rtc::split(version_string, '.', &parsed_version); if (parsed_version.size() != 3) { - return {}; + return false; } - absl::optional major = rtc::StringToNumber(parsed_version.at(0)); - absl::optional minor = rtc::StringToNumber(parsed_version.at(1)); - absl::optional micro = rtc::StringToNumber(parsed_version.at(2)); + pw_version current_version = {std::stoi(parsed_version.at(0)), + std::stoi(parsed_version.at(1)), + std::stoi(parsed_version.at(2))}; - // Return invalid version if we failed to parse it - if (!major || !minor || !micro) { - return {0, 0, 0}; - } - - return {major.value(), micro.value(), micro.value()}; + return (current_version.major > required_version.major) || + (current_version.major == required_version.major && + current_version.minor > required_version.minor) || + (current_version.major == required_version.major && + current_version.minor == required_version.minor && + current_version.micro >= required_version.micro); } spa_pod* BuildFormat(spa_pod_builder* builder, @@ -106,9 +106,15 @@ spa_pod* BuildFormat(spa_pod_builder* builder, spa_pod_builder_add(builder, SPA_FORMAT_VIDEO_format, SPA_POD_Id(format), 0); if (modifiers.size()) { - spa_pod_builder_prop( - builder, SPA_FORMAT_VIDEO_modifier, - SPA_POD_PROP_FLAG_MANDATORY | SPA_POD_PROP_FLAG_DONT_FIXATE); + // SPA_POD_PROP_FLAG_DONT_FIXATE can be used with PipeWire >= 0.3.33 + if (CheckPipeWireVersion(pw_version{0, 3, 33})) { + spa_pod_builder_prop( + builder, SPA_FORMAT_VIDEO_modifier, + SPA_POD_PROP_FLAG_MANDATORY | SPA_POD_PROP_FLAG_DONT_FIXATE); + } else { + spa_pod_builder_prop(builder, SPA_FORMAT_VIDEO_modifier, + SPA_POD_PROP_FLAG_MANDATORY); + } spa_pod_builder_push_choice(builder, &frames[1], SPA_CHOICE_Enum, 0); // modifiers from the array for (int64_t val : modifiers) { @@ -159,17 +165,6 @@ class ScopedBuf { int fd_; }; -class PipeWireThreadLoopLock { - public: - explicit PipeWireThreadLoopLock(pw_thread_loop* loop) : loop_(loop) { - pw_thread_loop_lock(loop_); - } - ~PipeWireThreadLoopLock() { pw_thread_loop_unlock(loop_); } - - private: - pw_thread_loop* const loop_; -}; - template class Scoped { public: @@ -239,34 +234,6 @@ Scoped::~Scoped() { } } -bool operator>=( - const BaseCapturerPipeWire::PipeWireVersion& current_pw_version, - const BaseCapturerPipeWire::PipeWireVersion& required_pw_version) { - if (!current_pw_version.major && !current_pw_version.minor && - !current_pw_version.micro) { - return false; - } - - return std::tie(current_pw_version.major, current_pw_version.minor, - current_pw_version.micro) >= - std::tie(required_pw_version.major, required_pw_version.minor, - required_pw_version.micro); -} - -bool operator<=( - const BaseCapturerPipeWire::PipeWireVersion& current_pw_version, - const BaseCapturerPipeWire::PipeWireVersion& required_pw_version) { - if (!current_pw_version.major && !current_pw_version.minor && - !current_pw_version.micro) { - return false; - } - - return std::tie(current_pw_version.major, current_pw_version.minor, - current_pw_version.micro) <= - std::tie(required_pw_version.major, required_pw_version.minor, - required_pw_version.micro); -} - void BaseCapturerPipeWire::OnCoreError(void* data, uint32_t id, int seq, @@ -278,23 +245,6 @@ void BaseCapturerPipeWire::OnCoreError(void* data, RTC_LOG(LS_ERROR) << "PipeWire remote error: " << message; } -void BaseCapturerPipeWire::OnCoreInfo(void* data, const pw_core_info* info) { - BaseCapturerPipeWire* capturer = static_cast(data); - RTC_DCHECK(capturer); - - capturer->pw_server_version_ = ParsePipeWireVersion(info->version); -} - -void BaseCapturerPipeWire::OnCoreDone(void* data, uint32_t id, int seq) { - const BaseCapturerPipeWire* capturer = - static_cast(data); - RTC_DCHECK(capturer); - - if (id == PW_ID_CORE && capturer->server_version_sync_ == seq) { - pw_thread_loop_signal(capturer->pw_main_loop_, false); - } -} - // static void BaseCapturerPipeWire::OnStreamStateChanged(void* data, pw_stream_state old_state, @@ -336,27 +286,20 @@ void BaseCapturerPipeWire::OnStreamParamChanged(void* data, auto size = height * stride; that->desktop_size_ = DesktopSize(width, height); +#if PW_CHECK_VERSION(0, 3, 0) + that->modifier_ = that->spa_video_format_.modifier; +#endif uint8_t buffer[1024] = {}; auto builder = spa_pod_builder{buffer, sizeof(buffer)}; // Setup buffers and meta header for new format. - - // When SPA_FORMAT_VIDEO_modifier is present we can use DMA-BUFs as - // the server announces support for it. - // See https://github.com/PipeWire/pipewire/blob/master/doc/dma-buf.dox - const bool has_modifier = - spa_pod_find_prop(format, nullptr, SPA_FORMAT_VIDEO_modifier); - that->modifier_ = - has_modifier ? that->spa_video_format_.modifier : DRM_FORMAT_MOD_INVALID; - const struct spa_pod* params[3]; const int buffer_types = - has_modifier || (that->pw_server_version_ >= kDmaBufMinVersion) + spa_pod_find_prop(format, nullptr, SPA_FORMAT_VIDEO_modifier) ? (1 << SPA_DATA_DmaBuf) | (1 << SPA_DATA_MemFd) | (1 << SPA_DATA_MemPtr) : (1 << SPA_DATA_MemFd) | (1 << SPA_DATA_MemPtr); - params[0] = reinterpret_cast(spa_pod_builder_add_object( &builder, SPA_TYPE_OBJECT_ParamBuffers, SPA_PARAM_Buffers, SPA_PARAM_BUFFERS_size, SPA_POD_Int(size), SPA_PARAM_BUFFERS_stride, @@ -501,6 +444,8 @@ void BaseCapturerPipeWire::Init() { pw_main_loop_ = pw_thread_loop_new("pipewire-main-loop", nullptr); + pw_thread_loop_lock(pw_main_loop_); + pw_context_ = pw_context_new(pw_thread_loop_get_loop(pw_main_loop_), nullptr, 0); if (!pw_context_) { @@ -508,18 +453,14 @@ void BaseCapturerPipeWire::Init() { return; } - if (pw_thread_loop_start(pw_main_loop_) < 0) { - RTC_LOG(LS_ERROR) << "Failed to start main PipeWire loop"; - portal_init_failed_ = true; + pw_core_ = pw_context_connect_fd(pw_context_, pw_fd_, nullptr, 0); + if (!pw_core_) { + RTC_LOG(LS_ERROR) << "Failed to connect PipeWire context"; return; } - pw_client_version_ = ParsePipeWireVersion(pw_get_library_version()); - // Initialize event handlers, remote end and stream-related. pw_core_events_.version = PW_VERSION_CORE_EVENTS; - pw_core_events_.info = &OnCoreInfo; - pw_core_events_.done = &OnCoreDone; pw_core_events_.error = &OnCoreError; pw_stream_events_.version = PW_VERSION_STREAM_EVENTS; @@ -527,32 +468,22 @@ void BaseCapturerPipeWire::Init() { pw_stream_events_.param_changed = &OnStreamParamChanged; pw_stream_events_.process = &OnStreamProcess; - { - PipeWireThreadLoopLock thread_loop_lock(pw_main_loop_); + pw_core_add_listener(pw_core_, &spa_core_listener_, &pw_core_events_, this); - pw_core_ = pw_context_connect_fd(pw_context_, pw_fd_, nullptr, 0); - if (!pw_core_) { - RTC_LOG(LS_ERROR) << "Failed to connect PipeWire context"; - portal_init_failed_ = true; - return; - } - - pw_core_add_listener(pw_core_, &spa_core_listener_, &pw_core_events_, this); - - server_version_sync_ = - pw_core_sync(pw_core_, PW_ID_CORE, server_version_sync_); - - pw_thread_loop_wait(pw_main_loop_); - - pw_stream_ = CreateReceivingStream(); - if (!pw_stream_) { - RTC_LOG(LS_ERROR) << "Failed to create PipeWire stream"; - portal_init_failed_ = true; - return; - } - - RTC_LOG(LS_INFO) << "PipeWire remote opened."; + pw_stream_ = CreateReceivingStream(); + if (!pw_stream_) { + RTC_LOG(LS_ERROR) << "Failed to create PipeWire stream"; + return; } + + if (pw_thread_loop_start(pw_main_loop_) < 0) { + RTC_LOG(LS_ERROR) << "Failed to start main PipeWire loop"; + portal_init_failed_ = true; + } + + pw_thread_loop_unlock(pw_main_loop_); + + RTC_LOG(LS_INFO) << "PipeWire remote opened."; } pw_stream* BaseCapturerPipeWire::CreateReceivingStream() { @@ -566,14 +497,12 @@ pw_stream* BaseCapturerPipeWire::CreateReceivingStream() { spa_pod_builder builder = spa_pod_builder{buffer, sizeof(buffer)}; std::vector params; - const bool has_required_pw_client_version = - pw_client_version_ >= kDmaBufModifierMinVersion; - const bool has_required_pw_server_version = - pw_server_version_ >= kDmaBufModifierMinVersion; + const bool has_required_pw_version = + CheckPipeWireVersion(pw_version{0, 3, 29}); for (uint32_t format : {SPA_VIDEO_FORMAT_BGRA, SPA_VIDEO_FORMAT_RGBA, SPA_VIDEO_FORMAT_BGRx, SPA_VIDEO_FORMAT_RGBx}) { - // Modifiers can be used with PipeWire >= 0.3.33 - if (has_required_pw_client_version && has_required_pw_server_version) { + // Modifiers can be used with PipeWire >= 0.3.29 + if (has_required_pw_version) { modifiers = egl_dmabuf_->QueryDmaBufModifiers(format); if (!modifiers.empty()) { diff --git a/modules/desktop_capture/linux/wayland/base_capturer_pipewire.h b/modules/desktop_capture/linux/wayland/base_capturer_pipewire.h index 91c863e4b4..238439e672 100644 --- a/modules/desktop_capture/linux/wayland/base_capturer_pipewire.h +++ b/modules/desktop_capture/linux/wayland/base_capturer_pipewire.h @@ -44,12 +44,6 @@ class BaseCapturerPipeWire : public DesktopCapturer { kMetadata = 0b100 }; - struct PipeWireVersion { - int major = 0; - int minor = 0; - int micro = 0; - }; - explicit BaseCapturerPipeWire(CaptureSourceType source_type); ~BaseCapturerPipeWire() override; @@ -72,14 +66,6 @@ class BaseCapturerPipeWire : public DesktopCapturer { spa_hook spa_core_listener_; spa_hook spa_stream_listener_; - // A number used to verify all previous methods and the resulting - // events have been handled. - int server_version_sync_ = 0; - // Version of the running PipeWire server we communicate with - PipeWireVersion pw_server_version_; - // Version of the library used to run our code - PipeWireVersion pw_client_version_; - // event handlers pw_core_events pw_core_events_ = {}; pw_stream_events pw_stream_events_ = {}; @@ -132,8 +118,6 @@ class BaseCapturerPipeWire : public DesktopCapturer { int seq, int res, const char* message); - static void OnCoreDone(void* user_data, uint32_t id, int seq); - static void OnCoreInfo(void* user_data, const pw_core_info* info); static void OnStreamParamChanged(void* data, uint32_t id, const struct spa_pod* format); diff --git a/modules/desktop_capture/linux/wayland/egl_dmabuf.cc b/modules/desktop_capture/linux/wayland/egl_dmabuf.cc index 881c40bec0..e872636d04 100644 --- a/modules/desktop_capture/linux/wayland/egl_dmabuf.cc +++ b/modules/desktop_capture/linux/wayland/egl_dmabuf.cc @@ -349,11 +349,12 @@ EglDmaBuf::EglDmaBuf() { egl_.extensions.push_back(std::string(extension)); } + bool has_image_dma_buf_import_ext = false; bool has_image_dma_buf_import_modifiers_ext = false; for (const auto& extension : egl_.extensions) { if (extension == "EGL_EXT_image_dma_buf_import") { - has_image_dma_buf_import_ext_ = true; + has_image_dma_buf_import_ext = true; continue; } else if (extension == "EGL_EXT_image_dma_buf_import_modifiers") { has_image_dma_buf_import_modifiers_ext = true; @@ -361,7 +362,7 @@ EglDmaBuf::EglDmaBuf() { } } - if (has_image_dma_buf_import_ext_ && has_image_dma_buf_import_modifiers_ext) { + if (has_image_dma_buf_import_ext && has_image_dma_buf_import_modifiers_ext) { EglQueryDmaBufFormatsEXT = (eglQueryDmaBufFormatsEXT_func)EglGetProcAddress( "eglQueryDmaBufFormatsEXT"); EglQueryDmaBufModifiersEXT = @@ -500,20 +501,18 @@ std::vector EglDmaBuf::QueryDmaBufModifiers(uint32_t format) { return {}; } - // Explicit modifiers not supported, return just DRM_FORMAT_MOD_INVALID as we - // can still use modifier-less DMA-BUFs if we have required extension + // Modifiers not supported, return just DRM_FORMAT_MOD_INVALID as we can still + // use modifier-less DMA-BUFs if (EglQueryDmaBufFormatsEXT == nullptr || EglQueryDmaBufModifiersEXT == nullptr) { - if (has_image_dma_buf_import_ext_) { - return {DRM_FORMAT_MOD_INVALID}; - } else { - return {}; - } + return {DRM_FORMAT_MOD_INVALID}; } uint32_t drm_format = SpaPixelFormatToDrmFormat(format); - // Should never happen as it's us who controls the list of supported formats - RTC_DCHECK(drm_format != DRM_FORMAT_INVALID); + if (drm_format == DRM_FORMAT_INVALID) { + RTC_LOG(LS_ERROR) << "Failed to find matching DRM format."; + return {DRM_FORMAT_MOD_INVALID}; + } EGLint count = 0; EGLBoolean success = diff --git a/modules/desktop_capture/linux/wayland/egl_dmabuf.h b/modules/desktop_capture/linux/wayland/egl_dmabuf.h index 75a8d81250..bc512a3e66 100644 --- a/modules/desktop_capture/linux/wayland/egl_dmabuf.h +++ b/modules/desktop_capture/linux/wayland/egl_dmabuf.h @@ -52,7 +52,6 @@ class EglDmaBuf { private: bool egl_initialized_ = false; - bool has_image_dma_buf_import_ext_ = false; int32_t drm_fd_ = -1; // for GBM buffer mmap gbm_device* gbm_device_ = nullptr; // for passed GBM buffer retrieval diff --git a/modules/desktop_capture/linux/wayland/pipewire.sigs b/modules/desktop_capture/linux/wayland/pipewire.sigs index a488af377b..ffcd0770a4 100644 --- a/modules/desktop_capture/linux/wayland/pipewire.sigs +++ b/modules/desktop_capture/linux/wayland/pipewire.sigs @@ -39,8 +39,6 @@ void pw_thread_loop_stop(pw_thread_loop *loop); void pw_thread_loop_lock(pw_thread_loop *loop); void pw_thread_loop_unlock(pw_thread_loop *loop); pw_loop * pw_thread_loop_get_loop(pw_thread_loop *loop); -void pw_thread_loop_signal(pw_thread_loop* loop, bool wait_for_accept); -void pw_thread_loop_wait(pw_thread_loop *loop); // context.h void pw_context_destroy(pw_context *context);