Reland "Add Fuchsia desktop capturer."

This is a reland of commit 39b6cb651ed158630cde4d11f5e981cad599f215

Original change's description:
> Add Fuchsia desktop capturer.
>
> This enables screen sharing on Fuchsia.
>
> Bug: chromium:1322341
> Change-Id: I2f52f6bfe7406b5fe36ae904a0cdf30e8168cac5
> Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/262340
> Reviewed-by: Emircan Uysaler <emircan@google.com>
> Commit-Queue: Sarah Pham <smpham@google.com>
> Reviewed-by: Alexander Cooper <alcooper@chromium.org>
> Cr-Commit-Position: refs/heads/main@{#37029}

Bug: chromium:1322341
Change-Id: Iac7c764da03d91b3c79ac0bbd9eb4c717e8c11df
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/264824
Reviewed-by: Christoffer Jansson <jansson@webrtc.org>
Owners-Override: Mirko Bonadei <mbonadei@webrtc.org>
Commit-Queue: Mirko Bonadei <mbonadei@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#37095}
diff --git a/modules/desktop_capture/BUILD.gn b/modules/desktop_capture/BUILD.gn
index 3aa1b98..04d8cd5 100644
--- a/modules/desktop_capture/BUILD.gn
+++ b/modules/desktop_capture/BUILD.gn
@@ -478,7 +478,8 @@
     ]
   }
 
-  if (!is_win && !is_mac && !rtc_use_x11_extensions && !rtc_use_pipewire) {
+  if (!is_win && !is_mac && !rtc_use_x11_extensions && !rtc_use_pipewire &&
+      !is_fuchsia) {
     sources += [
       "mouse_cursor_monitor_null.cc",
       "screen_capturer_null.cc",
@@ -509,6 +510,23 @@
     "../../system_wrappers:metrics",
   ]
 
+  if (is_fuchsia) {
+    sources += [
+      "mouse_cursor_monitor_null.cc",
+      "screen_capturer_fuchsia.cc",
+      "screen_capturer_fuchsia.h",
+      "window_capturer_null.cc",
+    ]
+    deps += [
+      "../../rtc_base:rtc_numerics",
+      "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.sysmem",
+      "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.ui.composition",
+      "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.ui.scenic",
+      "//third_party/fuchsia-sdk/sdk/pkg/scenic_cpp",
+      "//third_party/fuchsia-sdk/sdk/pkg/sys_cpp",
+    ]
+  }
+
   if (is_win) {
     sources += [
       "cropping_window_capturer_win.cc",
diff --git a/modules/desktop_capture/screen_capturer_fuchsia.cc b/modules/desktop_capture/screen_capturer_fuchsia.cc
new file mode 100644
index 0000000..6ccde6d
--- /dev/null
+++ b/modules/desktop_capture/screen_capturer_fuchsia.cc
@@ -0,0 +1,398 @@
+/*
+ *  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/screen_capturer_fuchsia.h"
+
+#include <fuchsia/sysmem/cpp/fidl.h>
+#include <fuchsia/ui/composition/cpp/fidl.h>
+#include <fuchsia/ui/scenic/cpp/fidl.h>
+#include <lib/sys/cpp/component_context.h>
+
+#include <algorithm>
+#include <cstdint>
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "modules/desktop_capture/blank_detector_desktop_capturer_wrapper.h"
+#include "modules/desktop_capture/desktop_capture_options.h"
+#include "modules/desktop_capture/desktop_capture_types.h"
+#include "modules/desktop_capture/desktop_capturer.h"
+#include "modules/desktop_capture/desktop_frame.h"
+#include "modules/desktop_capture/desktop_geometry.h"
+#include "modules/desktop_capture/fallback_desktop_capturer_wrapper.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/numerics/divide_round.h"
+#include "rtc_base/time_utils.h"
+
+namespace webrtc {
+
+namespace {
+
+static constexpr uint32_t kMinBufferCount = 2;
+static constexpr uint32_t kFuchsiaBytesPerPixel = 4;
+static constexpr DesktopCapturer::SourceId kFuchsiaScreenId = 1;
+// 500 milliseconds
+static constexpr zx::duration kEventDelay = zx::msec(500);
+static constexpr fuchsia::sysmem::ColorSpaceType kSRGBColorSpace =
+    fuchsia::sysmem::ColorSpaceType::SRGB;
+static constexpr fuchsia::sysmem::PixelFormatType kBGRA32PixelFormatType =
+    fuchsia::sysmem::PixelFormatType::BGRA32;
+
+// Round |value| up to the closest multiple of |multiple|
+size_t RoundUpToMultiple(size_t value, size_t multiple) {
+  return DivideRoundUp(value, multiple) * multiple;
+}
+
+}  // namespace
+
+std::unique_ptr<DesktopCapturer> DesktopCapturer::CreateRawScreenCapturer(
+    const DesktopCaptureOptions& options) {
+  std::unique_ptr<ScreenCapturerFuchsia> capturer(new ScreenCapturerFuchsia());
+  return capturer;
+}
+
+ScreenCapturerFuchsia::ScreenCapturerFuchsia()
+    : component_context_(
+          sys::ComponentContext::CreateAndServeOutgoingDirectory()) {}
+
+ScreenCapturerFuchsia::~ScreenCapturerFuchsia() {
+  // unmap virtual memory mapped pointers
+  uint32_t virt_mem_bytes =
+      buffer_collection_info_.settings.buffer_settings.size_bytes;
+  for (uint32_t buffer_index = 0;
+       buffer_index < buffer_collection_info_.buffer_count; buffer_index++) {
+    uintptr_t address =
+        reinterpret_cast<uintptr_t>(virtual_memory_mapped_addrs_[buffer_index]);
+    zx_status_t status = zx::vmar::root_self()->unmap(address, virt_mem_bytes);
+    RTC_DCHECK(status == ZX_OK);
+  }
+}
+
+void ScreenCapturerFuchsia::Start(Callback* callback) {
+  RTC_DCHECK(!callback_);
+  RTC_DCHECK(callback);
+  callback_ = callback;
+
+  fatal_error_ = false;
+
+  SetupBuffers();
+}
+
+void ScreenCapturerFuchsia::CaptureFrame() {
+  if (fatal_error_) {
+    callback_->OnCaptureResult(Result::ERROR_PERMANENT, nullptr);
+    return;
+  }
+
+  int64_t capture_start_time_nanos = rtc::TimeNanos();
+
+  zx::event event;
+  zx::event dup;
+  zx_status_t status = zx::event::create(0, &event);
+  if (status != ZX_OK) {
+    RTC_LOG(LS_ERROR) << "Failed to create event: " << status;
+    callback_->OnCaptureResult(Result::ERROR_TEMPORARY, nullptr);
+    return;
+  }
+  event.duplicate(ZX_RIGHT_SAME_RIGHTS, &dup);
+
+  fuchsia::ui::composition::GetNextFrameArgs next_frame_args;
+  next_frame_args.set_event(std::move(dup));
+
+  fuchsia::ui::composition::ScreenCapture_GetNextFrame_Result result;
+  screen_capture_->GetNextFrame(std::move(next_frame_args), &result);
+  if (result.is_err()) {
+    RTC_LOG(LS_ERROR) << "fuchsia.ui.composition.GetNextFrame() failed: "
+                      << result.err() << "\n";
+    callback_->OnCaptureResult(Result::ERROR_TEMPORARY, nullptr);
+    return;
+  }
+
+  status = event.wait_one(ZX_EVENT_SIGNALED, zx::deadline_after(kEventDelay),
+                          nullptr);
+  if (status != ZX_OK) {
+    RTC_LOG(LS_ERROR) << "Timed out waiting for ScreenCapture to render frame: "
+                      << status;
+    callback_->OnCaptureResult(Result::ERROR_TEMPORARY, nullptr);
+    return;
+  }
+  uint32_t buffer_index = result.response().buffer_id();
+
+  // TODO(bugs.webrtc.org/14097): Use SharedMemoryDesktopFrame and
+  // ScreenCaptureFrameQueue
+  std::unique_ptr<BasicDesktopFrame> frame(
+      new BasicDesktopFrame(DesktopSize(width_, height_)));
+
+  uint32_t pixels_per_row = GetPixelsPerRow(
+      buffer_collection_info_.settings.image_format_constraints);
+  uint32_t stride = kFuchsiaBytesPerPixel * pixels_per_row;
+  frame->CopyPixelsFrom(virtual_memory_mapped_addrs_[buffer_index], stride,
+                        DesktopRect::MakeWH(width_, height_));
+
+  fuchsia::ui::composition::ScreenCapture_ReleaseFrame_Result release_result;
+  screen_capture_->ReleaseFrame(buffer_index, &release_result);
+  if (release_result.is_err()) {
+    RTC_LOG(LS_ERROR) << "fuchsia.ui.composition.ReleaseFrame() failed: "
+                      << release_result.err();
+  }
+
+  int capture_time_ms = (rtc::TimeNanos() - capture_start_time_nanos) /
+                        rtc::kNumNanosecsPerMillisec;
+  frame->set_capture_time_ms(capture_time_ms);
+  callback_->OnCaptureResult(Result::SUCCESS, std::move(frame));
+}
+
+bool ScreenCapturerFuchsia::GetSourceList(SourceList* screens) {
+  RTC_DCHECK(screens->size() == 0);
+  // Fuchsia only supports single monitor display at this point
+  screens->push_back({kFuchsiaScreenId, std::string("Fuchsia monitor")});
+  return true;
+}
+
+bool ScreenCapturerFuchsia::SelectSource(SourceId id) {
+  if (id == kFuchsiaScreenId || id == kFullDesktopScreenId) {
+    return true;
+  }
+  return false;
+}
+
+fuchsia::sysmem::BufferCollectionConstraints
+ScreenCapturerFuchsia::GetBufferConstraints() {
+  fuchsia::sysmem::BufferCollectionConstraints constraints;
+  constraints.usage.cpu =
+      fuchsia::sysmem::cpuUsageRead | fuchsia::sysmem::cpuUsageWrite;
+  constraints.min_buffer_count = kMinBufferCount;
+
+  constraints.has_buffer_memory_constraints = true;
+  constraints.buffer_memory_constraints.ram_domain_supported = true;
+  constraints.buffer_memory_constraints.cpu_domain_supported = true;
+
+  constraints.image_format_constraints_count = 1;
+  fuchsia::sysmem::ImageFormatConstraints& image_constraints =
+      constraints.image_format_constraints[0];
+  image_constraints.color_spaces_count = 1;
+  image_constraints.color_space[0] =
+      fuchsia::sysmem::ColorSpace{.type = kSRGBColorSpace};
+  image_constraints.pixel_format.type = kBGRA32PixelFormatType;
+  image_constraints.pixel_format.has_format_modifier = true;
+  image_constraints.pixel_format.format_modifier.value =
+      fuchsia::sysmem::FORMAT_MODIFIER_LINEAR;
+
+  image_constraints.required_min_coded_width = width_;
+  image_constraints.required_min_coded_height = height_;
+  image_constraints.required_max_coded_width = width_;
+  image_constraints.required_max_coded_height = height_;
+
+  image_constraints.bytes_per_row_divisor = kFuchsiaBytesPerPixel;
+
+  return constraints;
+}
+
+void ScreenCapturerFuchsia::SetupBuffers() {
+  fuchsia::ui::scenic::ScenicSyncPtr scenic;
+  zx_status_t status = component_context_->svc()->Connect(scenic.NewRequest());
+  if (status != ZX_OK) {
+    fatal_error_ = true;
+    RTC_LOG(LS_ERROR) << "Failed to connect to Scenic: " << status;
+    return;
+  }
+
+  bool scenic_uses_flatland = false;
+  // TODO(fxbug.dev/100303): Remove when Flatland is the only API.
+  scenic->UsesFlatland(&scenic_uses_flatland);
+  if (!scenic_uses_flatland) {
+    fatal_error_ = true;
+    RTC_LOG(LS_ERROR) << "Screen capture not supported without Flatland.";
+    return;
+  }
+
+  fuchsia::ui::gfx::DisplayInfo display_info;
+  status = scenic->GetDisplayInfo(&display_info);
+  if (status != ZX_OK) {
+    fatal_error_ = true;
+    RTC_LOG(LS_ERROR) << "Failed to connect to get display dimensions: "
+                      << status;
+    return;
+  }
+  width_ = display_info.width_in_px;
+  height_ = display_info.height_in_px;
+
+  status = component_context_->svc()->Connect(sysmem_allocator_.NewRequest());
+  if (status != ZX_OK) {
+    fatal_error_ = true;
+    RTC_LOG(LS_ERROR) << "Failed to connect to Sysmem Allocator: " << status;
+    return;
+  }
+
+  fuchsia::sysmem::BufferCollectionTokenSyncPtr sysmem_token;
+  status =
+      sysmem_allocator_->AllocateSharedCollection(sysmem_token.NewRequest());
+  if (status != ZX_OK) {
+    fatal_error_ = true;
+    RTC_LOG(LS_ERROR)
+        << "fuchsia.sysmem.Allocator.AllocateSharedCollection() failed: "
+        << status;
+    return;
+  }
+
+  fuchsia::sysmem::BufferCollectionTokenSyncPtr flatland_token;
+  status = sysmem_token->Duplicate(ZX_RIGHT_SAME_RIGHTS,
+                                   flatland_token.NewRequest());
+  if (status != ZX_OK) {
+    fatal_error_ = true;
+    RTC_LOG(LS_ERROR)
+        << "fuchsia.sysmem.BufferCollectionToken.Duplicate() failed: "
+        << status;
+    return;
+  }
+
+  status = sysmem_token->Sync();
+  if (status != ZX_OK) {
+    fatal_error_ = true;
+    RTC_LOG(LS_ERROR) << "fuchsia.sysmem.BufferCollectionToken.Sync() failed: "
+                      << status;
+    return;
+  }
+
+  status = sysmem_allocator_->BindSharedCollection(std::move(sysmem_token),
+                                                   collection_.NewRequest());
+  if (status != ZX_OK) {
+    fatal_error_ = true;
+    RTC_LOG(LS_ERROR)
+        << "fuchsia.sysmem.Allocator.BindSharedCollection() failed: " << status;
+    return;
+  }
+
+  status = collection_->SetConstraints(/*has_constraints=*/true,
+                                       GetBufferConstraints());
+  if (status != ZX_OK) {
+    fatal_error_ = true;
+    RTC_LOG(LS_ERROR)
+        << "fuchsia.sysmem.BufferCollection.SetConstraints() failed: "
+        << status;
+    return;
+  }
+
+  fuchsia::ui::composition::BufferCollectionImportToken import_token;
+  fuchsia::ui::composition::BufferCollectionExportToken export_token;
+  status = zx::eventpair::create(0, &export_token.value, &import_token.value);
+  if (status != ZX_OK) {
+    fatal_error_ = true;
+    RTC_LOG(LS_ERROR)
+        << "Failed to create BufferCollection import and export tokens: "
+        << status;
+    return;
+  }
+
+  status = component_context_->svc()->Connect(flatland_allocator_.NewRequest());
+  if (status != ZX_OK) {
+    fatal_error_ = true;
+    RTC_LOG(LS_ERROR) << "Failed to connect to Flatland Allocator: " << status;
+    return;
+  }
+
+  fuchsia::ui::composition::RegisterBufferCollectionArgs buffer_collection_args;
+  buffer_collection_args.set_export_token(std::move(export_token));
+  buffer_collection_args.set_buffer_collection_token(std::move(flatland_token));
+  buffer_collection_args.set_usage(
+      fuchsia::ui::composition::RegisterBufferCollectionUsage::SCREENSHOT);
+
+  fuchsia::ui::composition::Allocator_RegisterBufferCollection_Result
+      buffer_collection_result;
+  flatland_allocator_->RegisterBufferCollection(
+      std::move(buffer_collection_args), &buffer_collection_result);
+  if (buffer_collection_result.is_err()) {
+    fatal_error_ = true;
+    RTC_LOG(LS_ERROR) << "fuchsia.ui.composition.Allocator."
+                         "RegisterBufferCollection() failed.";
+    return;
+  }
+
+  zx_status_t allocation_status;
+  status = collection_->WaitForBuffersAllocated(&allocation_status,
+                                                &buffer_collection_info_);
+  if (status != ZX_OK) {
+    fatal_error_ = true;
+    RTC_LOG(LS_ERROR) << "Failed to wait for buffer collection info: "
+                      << status;
+    return;
+  }
+  if (allocation_status != ZX_OK) {
+    fatal_error_ = true;
+    RTC_LOG(LS_ERROR) << "Failed to allocate buffer collection: " << status;
+    return;
+  }
+  status = collection_->Close();
+  if (status != ZX_OK) {
+    fatal_error_ = true;
+    RTC_LOG(LS_ERROR) << "Failed to close buffer collection token: " << status;
+    return;
+  }
+
+  status = component_context_->svc()->Connect(screen_capture_.NewRequest());
+  if (status != ZX_OK) {
+    fatal_error_ = true;
+    RTC_LOG(LS_ERROR) << "Failed to connect to Screen Capture: " << status;
+    return;
+  }
+
+  // Configure buffers in ScreenCapture client.
+  fuchsia::ui::composition::ScreenCaptureConfig configure_args;
+  configure_args.set_import_token(std::move(import_token));
+  configure_args.set_buffer_count(buffer_collection_info_.buffer_count);
+  configure_args.set_size({width_, height_});
+
+  fuchsia::ui::composition::ScreenCapture_Configure_Result configure_result;
+  screen_capture_->Configure(std::move(configure_args), &configure_result);
+  if (configure_result.is_err()) {
+    fatal_error_ = true;
+    RTC_LOG(LS_ERROR)
+        << "fuchsia.ui.composition.ScreenCapture.Configure() failed: "
+        << configure_result.err();
+    return;
+  }
+
+  // We have a collection of virtual memory objects which the ScreenCapture
+  // client will write the frame data to when requested. We map each of these
+  // onto a pointer stored in virtual_memory_mapped_addrs_ which we can use to
+  // access this data.
+  uint32_t virt_mem_bytes =
+      buffer_collection_info_.settings.buffer_settings.size_bytes;
+  RTC_DCHECK(virt_mem_bytes > 0);
+  for (uint32_t buffer_index = 0;
+       buffer_index < buffer_collection_info_.buffer_count; buffer_index++) {
+    const zx::vmo& virt_mem = buffer_collection_info_.buffers[buffer_index].vmo;
+    virtual_memory_mapped_addrs_[buffer_index] = nullptr;
+    auto status = zx::vmar::root_self()->map(
+        ZX_VM_PERM_READ, /*vmar_offset*/ 0, virt_mem,
+        /*vmo_offset*/ 0, virt_mem_bytes,
+        reinterpret_cast<uintptr_t*>(
+            &virtual_memory_mapped_addrs_[buffer_index]));
+    if (status != ZX_OK) {
+      fatal_error_ = true;
+      RTC_LOG(LS_ERROR) << "Failed to map virtual memory: " << status;
+      return;
+    }
+  }
+}
+
+uint32_t ScreenCapturerFuchsia::GetPixelsPerRow(
+    const fuchsia::sysmem::ImageFormatConstraints& constraints) {
+  uint32_t stride = RoundUpToMultiple(
+      std::max(constraints.min_bytes_per_row, width_ * kFuchsiaBytesPerPixel),
+      constraints.bytes_per_row_divisor);
+  uint32_t pixels_per_row = stride / kFuchsiaBytesPerPixel;
+
+  return pixels_per_row;
+}
+
+}  // namespace webrtc
diff --git a/modules/desktop_capture/screen_capturer_fuchsia.h b/modules/desktop_capture/screen_capturer_fuchsia.h
new file mode 100644
index 0000000..6e0f87c
--- /dev/null
+++ b/modules/desktop_capture/screen_capturer_fuchsia.h
@@ -0,0 +1,63 @@
+/*
+ *  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_SCREEN_CAPTURER_FUCHSIA_H_
+#define MODULES_DESKTOP_CAPTURE_SCREEN_CAPTURER_FUCHSIA_H_
+
+#include <fuchsia/sysmem/cpp/fidl.h>
+#include <fuchsia/ui/composition/cpp/fidl.h>
+#include <lib/sys/cpp/component_context.h>
+
+#include <memory>
+#include <unordered_map>
+
+#include "modules/desktop_capture/desktop_capture_options.h"
+#include "modules/desktop_capture/desktop_capturer.h"
+#include "modules/desktop_capture/desktop_frame.h"
+
+namespace webrtc {
+
+class ScreenCapturerFuchsia final : public DesktopCapturer {
+ public:
+  ScreenCapturerFuchsia();
+  ~ScreenCapturerFuchsia() override;
+
+  // DesktopCapturer interface.
+  void Start(Callback* callback) override;
+  void CaptureFrame() override;
+  bool GetSourceList(SourceList* screens) override;
+  bool SelectSource(SourceId id) override;
+
+ private:
+  fuchsia::sysmem::BufferCollectionConstraints GetBufferConstraints();
+  void SetupBuffers();
+  uint32_t GetPixelsPerRow(
+      const fuchsia::sysmem::ImageFormatConstraints& constraints);
+
+  Callback* callback_ = nullptr;
+
+  std::unique_ptr<sys::ComponentContext> component_context_;
+  fuchsia::sysmem::AllocatorSyncPtr sysmem_allocator_;
+  fuchsia::ui::composition::AllocatorSyncPtr flatland_allocator_;
+  fuchsia::ui::composition::ScreenCaptureSyncPtr screen_capture_;
+  fuchsia::sysmem::BufferCollectionSyncPtr collection_;
+  fuchsia::sysmem::BufferCollectionInfo_2 buffer_collection_info_;
+  std::unordered_map<uint32_t, uint8_t*> virtual_memory_mapped_addrs_;
+
+  bool fatal_error_;
+
+  // Dimensions of the screen we are capturing
+  uint32_t width_;
+  uint32_t height_;
+};
+
+}  // namespace webrtc
+
+#endif  // MODULES_DESKTOP_CAPTURE_SCREEN_CAPTURER_FUCHSIA_H_