PipeWire capturer: drop DMA-BUF modifier and renegotiate parameters on failure
In case we fail to import a DMA-BUF with given modifier, we can try to
drop the modifier we failed to use and renegotiate stream parameters
in order to use a different modifier or fallback to shared memory buffers.
Bug: chromium:1290566
Change-Id: I617513bdd67a43f62b647a172e0c166af138b3f9
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/249798
Reviewed-by: Mark Foltz <mfoltz@chromium.org>
Commit-Queue: Mark Foltz <mfoltz@chromium.org>
Cr-Commit-Position: refs/heads/main@{#35957}
diff --git a/modules/desktop_capture/linux/wayland/shared_screencast_stream.cc b/modules/desktop_capture/linux/wayland/shared_screencast_stream.cc
index 025573d..06caff6 100644
--- a/modules/desktop_capture/linux/wayland/shared_screencast_stream.cc
+++ b/modules/desktop_capture/linux/wayland/shared_screencast_stream.cc
@@ -65,6 +65,7 @@
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<std::string> parsed_version;
@@ -189,12 +190,15 @@
int64_t modifier_;
std::unique_ptr<EglDmaBuf> egl_dmabuf_;
+ // List of modifiers we query as supported by the graphics card/driver
+ std::vector<uint64_t> modifiers_;
// PipeWire types
struct pw_context* pw_context_ = nullptr;
struct pw_core* pw_core_ = nullptr;
struct pw_stream* pw_stream_ = nullptr;
struct pw_thread_loop* pw_main_loop_ = nullptr;
+ struct spa_source* renegotiate_ = nullptr;
spa_hook spa_core_listener_;
spa_hook spa_stream_listener_;
@@ -232,6 +236,11 @@
pw_stream_state state,
const char* error_message);
static void OnStreamProcess(void* data);
+ // This will be invoked in case we fail to process DMA-BUF PW buffer using
+ // negotiated stream parameters (modifier). We will drop the modifier we
+ // failed to use and try to use a different one or fallback to shared memory
+ // buffers.
+ static void OnRenegotiateFormat(void* data, uint64_t);
};
bool operator>=(const PipeWireVersion& current_pw_version,
@@ -402,6 +411,32 @@
pw_stream_queue_buffer(that->pw_stream_, buffer);
}
+void SharedScreenCastStreamPrivate::OnRenegotiateFormat(void* data, uint64_t) {
+ SharedScreenCastStreamPrivate* that =
+ static_cast<SharedScreenCastStreamPrivate*>(data);
+ RTC_DCHECK(that);
+
+ {
+ PipeWireThreadLoopLock thread_loop_lock(that->pw_main_loop_);
+
+ uint8_t buffer[2048] = {};
+
+ spa_pod_builder builder = spa_pod_builder{buffer, sizeof(buffer)};
+
+ std::vector<const spa_pod*> params;
+
+ 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, /*modifiers=*/{}));
+ }
+
+ pw_stream_update_params(that->pw_stream_, params.data(), params.size());
+ }
+}
+
SharedScreenCastStreamPrivate::SharedScreenCastStreamPrivate() {}
SharedScreenCastStreamPrivate::~SharedScreenCastStreamPrivate() {
@@ -486,6 +521,10 @@
pw_core_add_listener(pw_core_, &spa_core_listener_, &pw_core_events_, this);
+ // Add an event that can be later invoked by pw_loop_signal_event()
+ renegotiate_ = pw_loop_add_event(pw_thread_loop_get_loop(pw_main_loop_),
+ OnRenegotiateFormat, this);
+
server_version_sync_ =
pw_core_sync(pw_core_, PW_ID_CORE, server_version_sync_);
@@ -503,7 +542,6 @@
pw_stream_add_listener(pw_stream_, &spa_stream_listener_,
&pw_stream_events_, this);
uint8_t buffer[2048] = {};
- std::vector<uint64_t> modifiers;
spa_pod_builder builder = spa_pod_builder{buffer, sizeof(buffer)};
@@ -516,10 +554,10 @@
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 = egl_dmabuf_->QueryDmaBufModifiers(format);
+ modifiers_ = egl_dmabuf_->QueryDmaBufModifiers(format);
- if (!modifiers.empty()) {
- params.push_back(BuildFormat(&builder, format, modifiers));
+ if (!modifiers_.empty()) {
+ params.push_back(BuildFormat(&builder, format, modifiers_));
}
}
@@ -600,7 +638,24 @@
src_unique_ptr = egl_dmabuf_->ImageFromDmaBuf(
desktop_size_, spa_video_format_.format, plane_datas, modifier_);
- src = src_unique_ptr.get();
+ if (src_unique_ptr) {
+ src = src_unique_ptr.get();
+ } else {
+ RTC_LOG(LS_ERROR) << "Dropping DMA-BUF modifier: " << modifier_
+ << " and trying to renegotiate stream parameters";
+
+ if (pw_client_version_ >= kDropSingleModifierMinVersion) {
+ modifiers_.erase(
+ std::remove(modifiers_.begin(), modifiers_.end(), modifier_),
+ modifiers_.end());
+ } else {
+ modifiers_.clear();
+ }
+
+ pw_loop_signal_event(pw_thread_loop_get_loop(pw_main_loop_),
+ renegotiate_);
+ return;
+ }
} else if (spa_buffer->datas[0].type == SPA_DATA_MemPtr) {
src = static_cast<uint8_t*>(spa_buffer->datas[0].data);
}