From 96544b72eb75a0d67eb5f4abd35cb6ca6f1c4edb Mon Sep 17 00:00:00 2001 From: Jan Grulich Date: Wed, 23 Mar 2022 09:34:51 +0100 Subject: [PATCH] PipeWire capturer: split some PipeWire code that can be reused This is code that will be used to write tests and that makes sense to be moved out and used somewhere else. Bug: webrtc:13429 Change-Id: I2dd8f3111fdc9e6fa121c1e18644cbcf340b584e Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/256009 Reviewed-by: Mark Foltz Reviewed-by: Alexander Cooper Commit-Queue: Alexander Cooper Cr-Commit-Position: refs/heads/main@{#36307} --- modules/desktop_capture/BUILD.gn | 2 + .../linux/wayland/screencast_stream_utils.cc | 133 +++++++++++++++ .../linux/wayland/screencast_stream_utils.h | 62 +++++++ .../linux/wayland/shared_screencast_stream.cc | 157 +++--------------- .../linux/wayland/shared_screencast_stream.h | 1 + 5 files changed, 220 insertions(+), 135 deletions(-) create mode 100644 modules/desktop_capture/linux/wayland/screencast_stream_utils.cc create mode 100644 modules/desktop_capture/linux/wayland/screencast_stream_utils.h diff --git a/modules/desktop_capture/BUILD.gn b/modules/desktop_capture/BUILD.gn index 0faad43a1f..7499c16b09 100644 --- a/modules/desktop_capture/BUILD.gn +++ b/modules/desktop_capture/BUILD.gn @@ -572,6 +572,8 @@ rtc_library("desktop_capture_generic") { "linux/wayland/scoped_glib.h", "linux/wayland/screencast_portal.cc", "linux/wayland/screencast_portal.h", + "linux/wayland/screencast_stream_utils.cc", + "linux/wayland/screencast_stream_utils.h", "linux/wayland/shared_screencast_stream.cc", "linux/wayland/shared_screencast_stream.h", "linux/wayland/xdg_desktop_portal_utils.cc", diff --git a/modules/desktop_capture/linux/wayland/screencast_stream_utils.cc b/modules/desktop_capture/linux/wayland/screencast_stream_utils.cc new file mode 100644 index 0000000000..6d0df404f9 --- /dev/null +++ b/modules/desktop_capture/linux/wayland/screencast_stream_utils.cc @@ -0,0 +1,133 @@ +/* + * Copyright 2022 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/desktop_capture/linux/wayland/screencast_stream_utils.h" + +#include +#include +#include + +#include + +#include "rtc_base/string_to_number.h" + +#if !PW_CHECK_VERSION(0, 3, 29) +#define SPA_POD_PROP_FLAG_MANDATORY (1u << 3) +#endif +#if !PW_CHECK_VERSION(0, 3, 33) +#define SPA_POD_PROP_FLAG_DONT_FIXATE (1u << 4) +#endif + +namespace webrtc { + +PipeWireThreadLoopLock::PipeWireThreadLoopLock(pw_thread_loop* loop) + : loop_(loop) { + pw_thread_loop_lock(loop_); +} + +PipeWireThreadLoopLock::~PipeWireThreadLoopLock() { + pw_thread_loop_unlock(loop_); +} + +PipeWireVersion PipeWireVersion::Parse(const absl::string_view& version) { + std::vector parsed_version; + rtc::split(version, '.', &parsed_version); + + if (parsed_version.size() != 3) { + return {}; + } + + 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)); + + // Return invalid version if we failed to parse it + if (!major || !minor || !micro) { + return {}; + } + + return {major.value(), minor.value(), micro.value()}; +} + +bool PipeWireVersion::operator>=(const PipeWireVersion& other) { + if (!major && !minor && !micro) { + return false; + } + + return std::tie(major, minor, micro) >= + std::tie(other.major, other.minor, other.micro); +} + +bool PipeWireVersion::operator<=(const PipeWireVersion& other) { + if (!major && !minor && !micro) { + return false; + } + + return std::tie(major, minor, micro) <= + std::tie(other.major, other.minor, other.micro); +} + +spa_pod* BuildFormat(spa_pod_builder* builder, + uint32_t format, + const std::vector& modifiers, + const struct spa_rectangle* resolution) { + spa_pod_frame frames[2]; + spa_rectangle pw_min_screen_bounds = spa_rectangle{1, 1}; + spa_rectangle pw_max_screen_bounds = spa_rectangle{UINT32_MAX, UINT32_MAX}; + + spa_pod_builder_push_object(builder, &frames[0], SPA_TYPE_OBJECT_Format, + SPA_PARAM_EnumFormat); + spa_pod_builder_add(builder, SPA_FORMAT_mediaType, + SPA_POD_Id(SPA_MEDIA_TYPE_video), 0); + spa_pod_builder_add(builder, SPA_FORMAT_mediaSubtype, + SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw), 0); + spa_pod_builder_add(builder, SPA_FORMAT_VIDEO_format, SPA_POD_Id(format), 0); + + if (modifiers.size()) { + if (modifiers.size() == 1 && modifiers[0] == DRM_FORMAT_MOD_INVALID) { + spa_pod_builder_prop(builder, SPA_FORMAT_VIDEO_modifier, + SPA_POD_PROP_FLAG_MANDATORY); + spa_pod_builder_long(builder, modifiers[0]); + } else { + spa_pod_builder_prop( + builder, SPA_FORMAT_VIDEO_modifier, + SPA_POD_PROP_FLAG_MANDATORY | SPA_POD_PROP_FLAG_DONT_FIXATE); + spa_pod_builder_push_choice(builder, &frames[1], SPA_CHOICE_Enum, 0); + + // modifiers from the array + bool first = true; + for (int64_t val : modifiers) { + spa_pod_builder_long(builder, val); + // Add the first modifier twice as the very first value is the default + // option + if (first) { + spa_pod_builder_long(builder, val); + first = false; + } + } + spa_pod_builder_pop(builder, &frames[1]); + } + } + + if (resolution) { + spa_pod_builder_add(builder, SPA_FORMAT_VIDEO_size, + SPA_POD_Rectangle(resolution), 0); + } else { + spa_pod_builder_add(builder, SPA_FORMAT_VIDEO_size, + SPA_POD_CHOICE_RANGE_Rectangle(&pw_min_screen_bounds, + &pw_min_screen_bounds, + &pw_max_screen_bounds), + 0); + } + + return static_cast(spa_pod_builder_pop(builder, &frames[0])); +} + +} // namespace webrtc diff --git a/modules/desktop_capture/linux/wayland/screencast_stream_utils.h b/modules/desktop_capture/linux/wayland/screencast_stream_utils.h new file mode 100644 index 0000000000..70262c2e39 --- /dev/null +++ b/modules/desktop_capture/linux/wayland/screencast_stream_utils.h @@ -0,0 +1,62 @@ +/* + * Copyright 2022 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_SCREENCAST_STREAM_UTILS_H_ +#define MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_SCREENCAST_STREAM_UTILS_H_ + +#include + +#include +#include + +#include "rtc_base/string_encode.h" + +struct pw_thread_loop; +struct spa_pod; +struct spa_pod_builder; +struct spa_rectangle; + +namespace webrtc { + +// Locks pw_thread_loop in the current scope +class PipeWireThreadLoopLock { + public: + explicit PipeWireThreadLoopLock(pw_thread_loop* loop); + ~PipeWireThreadLoopLock(); + + private: + pw_thread_loop* const loop_; +}; + +struct PipeWireVersion { + static PipeWireVersion Parse(const absl::string_view& version); + + // Returns whether current version is newer or same as required version + bool operator>=(const PipeWireVersion& other); + // Returns whether current version is older or same as required version + bool operator<=(const PipeWireVersion& other); + + int major = 0; + int minor = 0; + int micro = 0; +}; + +// Returns a spa_pod used to build PipeWire stream format using given +// arguments. Modifiers are optional value and when present they will be +// used with SPA_POD_PROP_FLAG_MANDATORY and SPA_POD_PROP_FLAG_DONT_FIXATE +// flags. +spa_pod* BuildFormat(spa_pod_builder* builder, + uint32_t format, + const std::vector& modifiers, + const struct spa_rectangle* resolution); + +} // namespace webrtc + +#endif // MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_SCREENCAST_STREAM_UTILS_H_ diff --git a/modules/desktop_capture/linux/wayland/shared_screencast_stream.cc b/modules/desktop_capture/linux/wayland/shared_screencast_stream.cc index 720234fba8..535d0923ef 100644 --- a/modules/desktop_capture/linux/wayland/shared_screencast_stream.cc +++ b/modules/desktop_capture/linux/wayland/shared_screencast_stream.cc @@ -12,25 +12,18 @@ #include #include -#include -#include #include -#include #include -#include -#include -#include #include #include "absl/memory/memory.h" #include "modules/desktop_capture/linux/wayland/egl_dmabuf.h" +#include "modules/desktop_capture/linux/wayland/screencast_stream_utils.h" #include "modules/desktop_capture/screen_capture_frame_queue.h" #include "rtc_base/checks.h" #include "rtc_base/logging.h" #include "rtc_base/sanitizer.h" -#include "rtc_base/string_encode.h" -#include "rtc_base/string_to_number.h" #include "rtc_base/synchronization/mutex.h" #if defined(WEBRTC_DLOPEN_PIPEWIRE) @@ -50,110 +43,16 @@ const char kPipeWireLib[] = "libpipewire-0.3.so.0"; const char kDrmLib[] = "libdrm.so.2"; #endif -#if !PW_CHECK_VERSION(0, 3, 29) -#define SPA_POD_PROP_FLAG_MANDATORY (1u << 3) -#endif -#if !PW_CHECK_VERSION(0, 3, 33) -#define SPA_POD_PROP_FLAG_DONT_FIXATE (1u << 4) -#endif - constexpr int kCursorBpp = 4; constexpr int CursorMetaSize(int w, int h) { return (sizeof(struct spa_meta_cursor) + sizeof(struct spa_meta_bitmap) + w * h * kCursorBpp); } -struct PipeWireVersion { - int major = 0; - int minor = 0; - int micro = 0; -}; - constexpr PipeWireVersion kDmaBufMinVersion = {0, 3, 24}; constexpr PipeWireVersion kDmaBufModifierMinVersion = {0, 3, 33}; constexpr PipeWireVersion kDropSingleModifierMinVersion = {0, 3, 40}; -PipeWireVersion ParsePipeWireVersion(const char* version) { - std::vector parsed_version; - rtc::split(version, '.', &parsed_version); - - if (parsed_version.size() != 3) { - return {}; - } - - 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)); - - // Return invalid version if we failed to parse it - if (!major || !minor || !micro) { - return {0, 0, 0}; - } - - return {major.value(), micro.value(), micro.value()}; -} - -spa_pod* BuildFormat(spa_pod_builder* builder, - uint32_t format, - const std::vector& modifiers) { - bool first = true; - spa_pod_frame frames[2]; - spa_rectangle pw_min_screen_bounds = spa_rectangle{1, 1}; - spa_rectangle pw_max_screen_bounds = spa_rectangle{UINT32_MAX, UINT32_MAX}; - - spa_pod_builder_push_object(builder, &frames[0], SPA_TYPE_OBJECT_Format, - SPA_PARAM_EnumFormat); - spa_pod_builder_add(builder, SPA_FORMAT_mediaType, - SPA_POD_Id(SPA_MEDIA_TYPE_video), 0); - spa_pod_builder_add(builder, SPA_FORMAT_mediaSubtype, - SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw), 0); - spa_pod_builder_add(builder, SPA_FORMAT_VIDEO_format, SPA_POD_Id(format), 0); - - if (modifiers.size()) { - if (modifiers.size() == 1 && modifiers[0] == DRM_FORMAT_MOD_INVALID) { - spa_pod_builder_prop(builder, SPA_FORMAT_VIDEO_modifier, - SPA_POD_PROP_FLAG_MANDATORY); - spa_pod_builder_long(builder, modifiers[0]); - } else { - spa_pod_builder_prop( - builder, SPA_FORMAT_VIDEO_modifier, - SPA_POD_PROP_FLAG_MANDATORY | SPA_POD_PROP_FLAG_DONT_FIXATE); - spa_pod_builder_push_choice(builder, &frames[1], SPA_CHOICE_Enum, 0); - - // modifiers from the array - for (int64_t val : modifiers) { - spa_pod_builder_long(builder, val); - // Add the first modifier twice as the very first value is the default - // option - if (first) { - spa_pod_builder_long(builder, val); - first = false; - } - } - spa_pod_builder_pop(builder, &frames[1]); - } - } - - spa_pod_builder_add( - builder, SPA_FORMAT_VIDEO_size, - SPA_POD_CHOICE_RANGE_Rectangle( - &pw_min_screen_bounds, &pw_min_screen_bounds, &pw_max_screen_bounds), - 0); - - return static_cast(spa_pod_builder_pop(builder, &frames[0])); -} - -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_; -}; - class ScopedBuf { public: ScopedBuf() {} @@ -260,32 +159,6 @@ class SharedScreenCastStreamPrivate { static void OnRenegotiateFormat(void* data, uint64_t); }; -bool operator>=(const PipeWireVersion& current_pw_version, - const 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 PipeWireVersion& current_pw_version, - const 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 SharedScreenCastStreamPrivate::OnCoreError(void* data, uint32_t id, int seq, @@ -304,7 +177,7 @@ void SharedScreenCastStreamPrivate::OnCoreInfo(void* data, static_cast(data); RTC_DCHECK(stream); - stream->pw_server_version_ = ParsePipeWireVersion(info->version); + stream->pw_server_version_ = PipeWireVersion::Parse(info->version); } void SharedScreenCastStreamPrivate::OnCoreDone(void* data, @@ -457,9 +330,11 @@ void SharedScreenCastStreamPrivate::OnRenegotiateFormat(void* data, uint64_t) { for (uint32_t format : {SPA_VIDEO_FORMAT_BGRA, SPA_VIDEO_FORMAT_RGBA, SPA_VIDEO_FORMAT_BGRx, SPA_VIDEO_FORMAT_RGBx}) { if (!that->modifiers_.empty()) { - params.push_back(BuildFormat(&builder, format, that->modifiers_)); + params.push_back(BuildFormat(&builder, format, that->modifiers_, + /*resolution=*/nullptr)); } - params.push_back(BuildFormat(&builder, format, /*modifiers=*/{})); + params.push_back(BuildFormat(&builder, format, /*modifiers=*/{}, + /*resolution=*/nullptr)); } pw_stream_update_params(that->pw_stream_, params.data(), params.size()); @@ -530,7 +405,7 @@ bool SharedScreenCastStreamPrivate::StartScreenCastStream( return false; } - pw_client_version_ = ParsePipeWireVersion(pw_get_library_version()); + pw_client_version_ = PipeWireVersion::Parse(pw_get_library_version()); // Initialize event handlers, remote end and stream-related. pw_core_events_.version = PW_VERSION_CORE_EVENTS; @@ -546,7 +421,12 @@ bool SharedScreenCastStreamPrivate::StartScreenCastStream( { PipeWireThreadLoopLock thread_loop_lock(pw_main_loop_); - pw_core_ = pw_context_connect_fd(pw_context_, pw_fd_, nullptr, 0); + if (!pw_fd_) { + pw_core_ = pw_context_connect(pw_context_, nullptr, 0); + } else { + 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 false; @@ -590,11 +470,13 @@ bool SharedScreenCastStreamPrivate::StartScreenCastStream( modifiers_ = egl_dmabuf_->QueryDmaBufModifiers(format); if (!modifiers_.empty()) { - params.push_back(BuildFormat(&builder, format, modifiers_)); + params.push_back(BuildFormat(&builder, format, modifiers_, + /*resolution=*/nullptr)); } } - params.push_back(BuildFormat(&builder, format, /*modifiers=*/{})); + params.push_back(BuildFormat(&builder, format, /*modifiers=*/{}, + /*resolution=*/nullptr)); } if (pw_stream_connect(pw_stream_, PW_DIRECTION_INPUT, pw_stream_node_id_, @@ -735,6 +617,7 @@ void SharedScreenCastStreamPrivate::ProcessBuffer(pw_buffer* buffer) { if (!src) { return; } + struct spa_meta_region* video_metadata = static_cast(spa_buffer_find_meta_data( spa_buffer, SPA_META_VideoCrop, sizeof(*video_metadata))); @@ -845,6 +728,10 @@ SharedScreenCastStream::CreateDefault() { return rtc::scoped_refptr(new SharedScreenCastStream()); } +bool SharedScreenCastStream::StartScreenCastStream(uint32_t stream_node_id) { + return private_->StartScreenCastStream(stream_node_id, 0); +} + bool SharedScreenCastStream::StartScreenCastStream(uint32_t stream_node_id, int fd) { return private_->StartScreenCastStream(stream_node_id, fd); diff --git a/modules/desktop_capture/linux/wayland/shared_screencast_stream.h b/modules/desktop_capture/linux/wayland/shared_screencast_stream.h index 443ec745d5..1e9fbe5f70 100644 --- a/modules/desktop_capture/linux/wayland/shared_screencast_stream.h +++ b/modules/desktop_capture/linux/wayland/shared_screencast_stream.h @@ -29,6 +29,7 @@ class RTC_EXPORT SharedScreenCastStream public: static rtc::scoped_refptr CreateDefault(); + bool StartScreenCastStream(uint32_t stream_node_id); bool StartScreenCastStream(uint32_t stream_node_id, int fd); void StopScreenCastStream();