PipeWire capturer: copy content from PW buffer directly to DesktopFrame

This avoids an additional step where we originally copied content from
PipeWire buffer to a temporary location and from there to DesktopFrame.
This results into less copy operations and hopefully to faster
screensharing.

I didn't do some exact measures, but simply running htop while sharing a
4k screen I can see following results (usage per top 5 processes):
1) Without this change - 66%, 64%, 26% 23%, 10%
2) With this change - 41%, 39%, 19%, 17%, 12%,

Bug: webrtc:13239
Change-Id: I6a661ecc96bfeef370c1a5a3b9dc5e3c0fc665c8
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/231684
Reviewed-by: Tommi <tommi@webrtc.org>
Reviewed-by: Erik Språng <sprang@webrtc.org>
Commit-Queue: Tommi <tommi@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#35156}
diff --git a/modules/desktop_capture/linux/base_capturer_pipewire.cc b/modules/desktop_capture/linux/base_capturer_pipewire.cc
index 45229b2..54ff575 100644
--- a/modules/desktop_capture/linux/base_capturer_pipewire.cc
+++ b/modules/desktop_capture/linux/base_capturer_pipewire.cc
@@ -537,8 +537,6 @@
     return;
   }
 
-  std::function<void()> cleanup;
-  const int32_t src_stride = spa_buffer->datas[0].chunk->stride;
   if (spa_buffer->datas[0].type == SPA_DATA_MemFd) {
     map.initialize(
         static_cast<uint8_t*>(
@@ -608,7 +606,6 @@
     video_metadata_use = true;
   }
 
-  DesktopSize video_size_prev = video_size_;
   if (video_metadata_use) {
     video_size_ =
         DesktopSize(video_metadata_size->width, video_metadata_size->height);
@@ -616,54 +613,41 @@
     video_size_ = desktop_size_;
   }
 
+  uint32_t y_offset = video_metadata_use && (video_metadata->region.position.y +
+                                                 video_size_.height() <=
+                                             desktop_size_.height())
+                          ? video_metadata->region.position.y
+                          : 0;
+  uint32_t x_offset = video_metadata_use && (video_metadata->region.position.x +
+                                                 video_size_.width() <=
+                                             desktop_size_.width())
+                          ? video_metadata->region.position.x
+                          : 0;
+
   webrtc::MutexLock lock(&current_frame_lock_);
-  if (!current_frame_ || !video_size_.equals(video_size_prev)) {
-    current_frame_ = std::make_unique<uint8_t[]>(
-        video_size_.width() * video_size_.height() * kBytesPerPixel);
-  }
 
-  const int32_t dst_stride = video_size_.width() * kBytesPerPixel;
+  uint8_t* updated_src = src + (spa_buffer->datas[0].chunk->stride * y_offset) +
+                         (kBytesPerPixel * x_offset);
+  current_frame_ = std::make_unique<BasicDesktopFrame>(
+      DesktopSize(video_size_.width(), video_size_.height()));
+  current_frame_->CopyPixelsFrom(
+      updated_src,
+      (spa_buffer->datas[0].chunk->stride - (kBytesPerPixel * x_offset)),
+      DesktopRect::MakeWH(video_size_.width(), video_size_.height()));
 
-  if (src_stride != (desktop_size_.width() * kBytesPerPixel)) {
-    RTC_LOG(LS_ERROR) << "Got buffer with stride different from screen stride: "
-                      << src_stride
-                      << " != " << (desktop_size_.width() * kBytesPerPixel);
-    portal_init_failed_ = true;
-
-    return;
-  }
-
-  // Adjust source content based on metadata video position
-  if (video_metadata_use &&
-      (video_metadata->region.position.y + video_size_.height() <=
-       desktop_size_.height())) {
-    src += src_stride * video_metadata->region.position.y;
-  }
-  const int x_offset =
-      video_metadata_use &&
-              (video_metadata->region.position.x + video_size_.width() <=
-               desktop_size_.width())
-          ? video_metadata->region.position.x * kBytesPerPixel
-          : 0;
-
-  uint8_t* dst = current_frame_.get();
-  for (int i = 0; i < video_size_.height(); ++i) {
-    // Adjust source content based on crop video position if needed
-    src += x_offset;
-    std::memcpy(dst, src, dst_stride);
-    // If both sides decided to go with the RGBx format we need to convert it to
-    // BGRx to match color format expected by WebRTC.
-    if (spa_video_format_.format == SPA_VIDEO_FORMAT_RGBx ||
-        spa_video_format_.format == SPA_VIDEO_FORMAT_RGBA) {
-      ConvertRGBxToBGRx(dst, dst_stride);
+  if (spa_video_format_.format == SPA_VIDEO_FORMAT_RGBx ||
+      spa_video_format_.format == SPA_VIDEO_FORMAT_RGBA) {
+    uint8_t* tmp_src = current_frame_->data();
+    for (int i = 0; i < video_size_.height(); ++i) {
+      // If both sides decided to go with the RGBx format we need to convert it
+      // to BGRx to match color format expected by WebRTC.
+      ConvertRGBxToBGRx(tmp_src, current_frame_->stride());
+      tmp_src += current_frame_->stride();
     }
-    src += src_stride - x_offset;
-    dst += dst_stride;
   }
 }
 
 void BaseCapturerPipeWire::ConvertRGBxToBGRx(uint8_t* frame, uint32_t size) {
-  // Change color format for KDE KWin which uses RGBx and not BGRx
   for (uint32_t i = 0; i < size; i += 4) {
     uint8_t tempR = frame[i];
     uint8_t tempB = frame[i + 2];
@@ -1103,18 +1087,7 @@
   }
 
   webrtc::MutexLock lock(&current_frame_lock_);
-  if (!current_frame_) {
-    callback_->OnCaptureResult(Result::ERROR_TEMPORARY, nullptr);
-    return;
-  }
-
-  DesktopSize frame_size = video_size_;
-
-  std::unique_ptr<DesktopFrame> result(new BasicDesktopFrame(frame_size));
-  result->CopyPixelsFrom(
-      current_frame_.get(), (frame_size.width() * kBytesPerPixel),
-      DesktopRect::MakeWH(frame_size.width(), frame_size.height()));
-  if (!result) {
+  if (!current_frame_ || !current_frame_->data()) {
     callback_->OnCaptureResult(Result::ERROR_TEMPORARY, nullptr);
     return;
   }
@@ -1122,7 +1095,7 @@
   // TODO(julien.isorce): http://crbug.com/945468. Set the icc profile on the
   // frame, see ScreenCapturerX11::CaptureFrame.
 
-  callback_->OnCaptureResult(Result::SUCCESS, std::move(result));
+  callback_->OnCaptureResult(Result::SUCCESS, std::move(current_frame_));
 }
 
 bool BaseCapturerPipeWire::GetSourceList(SourceList* sources) {
diff --git a/modules/desktop_capture/linux/base_capturer_pipewire.h b/modules/desktop_capture/linux/base_capturer_pipewire.h
index 787ee5e..a7f954d 100644
--- a/modules/desktop_capture/linux/base_capturer_pipewire.h
+++ b/modules/desktop_capture/linux/base_capturer_pipewire.h
@@ -95,7 +95,7 @@
   DesktopCaptureOptions options_ = {};
 
   webrtc::Mutex current_frame_lock_;
-  std::unique_ptr<uint8_t[]> current_frame_;
+  std::unique_ptr<BasicDesktopFrame> current_frame_;
   Callback* callback_ = nullptr;
 
   bool portal_init_failed_ = false;
diff --git a/modules/desktop_capture/linux/egl_dmabuf.cc b/modules/desktop_capture/linux/egl_dmabuf.cc
index ae34b69..4c4e182 100644
--- a/modules/desktop_capture/linux/egl_dmabuf.cc
+++ b/modules/desktop_capture/linux/egl_dmabuf.cc
@@ -20,6 +20,7 @@
 #include <xf86drm.h>
 
 #include "absl/memory/memory.h"
+#include "absl/types/optional.h"
 #include "rtc_base/checks.h"
 #include "rtc_base/logging.h"
 #include "rtc_base/sanitizer.h"