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();