tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

screen_capturer_fuchsia.cc (15116B)


      1 /*
      2 *  Copyright 2022 The WebRTC project authors. All Rights Reserved.
      3 *
      4 *  Use of this source code is governed by a BSD-style license
      5 *  that can be found in the LICENSE file in the root of the source
      6 *  tree. An additional intellectual property rights grant can be found
      7 *  in the file PATENTS.  All contributing project authors may
      8 *  be found in the AUTHORS file in the root of the source tree.
      9 */
     10 
     11 #include "modules/desktop_capture/screen_capturer_fuchsia.h"
     12 
     13 #include <fuchsia/sysmem2/cpp/fidl.h>
     14 #include <fuchsia/ui/composition/cpp/fidl.h>
     15 #include <fuchsia/ui/display/singleton/cpp/fidl.h>
     16 #include <lib/sys/cpp/component_context.h>
     17 
     18 #include <algorithm>
     19 #include <cstdint>
     20 #include <memory>
     21 #include <string>
     22 #include <utility>
     23 
     24 #include "modules/desktop_capture/blank_detector_desktop_capturer_wrapper.h"
     25 #include "modules/desktop_capture/desktop_capture_options.h"
     26 #include "modules/desktop_capture/desktop_capture_types.h"
     27 #include "modules/desktop_capture/desktop_capturer.h"
     28 #include "modules/desktop_capture/desktop_frame.h"
     29 #include "modules/desktop_capture/desktop_geometry.h"
     30 #include "modules/desktop_capture/fallback_desktop_capturer_wrapper.h"
     31 #include "rtc_base/checks.h"
     32 #include "rtc_base/logging.h"
     33 #include "rtc_base/numerics/divide_round.h"
     34 #include "rtc_base/time_utils.h"
     35 
     36 namespace webrtc {
     37 
     38 namespace {
     39 
     40 static constexpr uint32_t kMinBufferCount = 2;
     41 static constexpr uint32_t kFuchsiaBytesPerPixel = 4;
     42 static constexpr DesktopCapturer::SourceId kFuchsiaScreenId = 1;
     43 // 500 milliseconds
     44 static constexpr zx::duration kEventDelay = zx::msec(500);
     45 static constexpr fuchsia::images2::ColorSpace kSRGBColorSpace =
     46    fuchsia::images2::ColorSpace::SRGB;
     47 static constexpr fuchsia::images2::PixelFormat kBGRA32PixelFormatType =
     48    fuchsia::images2::PixelFormat::B8G8R8A8;
     49 
     50 // Round |value| up to the closest multiple of |multiple|
     51 size_t RoundUpToMultiple(size_t value, size_t multiple) {
     52  return DivideRoundUp(value, multiple) * multiple;
     53 }
     54 
     55 }  // namespace
     56 
     57 std::unique_ptr<DesktopCapturer> DesktopCapturer::CreateRawScreenCapturer(
     58    const DesktopCaptureOptions& options) {
     59  RTC_LOG(LS_INFO)
     60      << "video capture: DesktopCapturer::CreateRawScreenCapturer creates "
     61         "DesktopCapturer of type ScreenCapturerFuchsia";
     62  std::unique_ptr<ScreenCapturerFuchsia> capturer(new ScreenCapturerFuchsia());
     63  return capturer;
     64 }
     65 
     66 ScreenCapturerFuchsia::ScreenCapturerFuchsia()
     67    : component_context_(sys::ComponentContext::Create()) {}
     68 
     69 ScreenCapturerFuchsia::~ScreenCapturerFuchsia() {
     70  // unmap virtual memory mapped pointers
     71  uint32_t virt_mem_bytes =
     72      buffer_collection_info_.settings().buffer_settings().size_bytes();
     73  for (uint32_t buffer_index = 0;
     74       buffer_index < buffer_collection_info_.buffers().size();
     75       buffer_index++) {
     76    uintptr_t address =
     77        reinterpret_cast<uintptr_t>(virtual_memory_mapped_addrs_[buffer_index]);
     78    zx_status_t status = zx::vmar::root_self()->unmap(address, virt_mem_bytes);
     79    RTC_DCHECK(status == ZX_OK);
     80  }
     81 }
     82 
     83 void ScreenCapturerFuchsia::Start(Callback* callback) {
     84  RTC_DCHECK(!callback_);
     85  RTC_DCHECK(callback);
     86  callback_ = callback;
     87 
     88  fatal_error_ = false;
     89 
     90  SetupBuffers();
     91 }
     92 
     93 void ScreenCapturerFuchsia::CaptureFrame() {
     94  if (fatal_error_) {
     95    callback_->OnCaptureResult(Result::ERROR_PERMANENT, nullptr);
     96    return;
     97  }
     98 
     99  int64_t capture_start_time_nanos = TimeNanos();
    100 
    101  zx::event event;
    102  zx::event dup;
    103  zx_status_t status = zx::event::create(0, &event);
    104  if (status != ZX_OK) {
    105    RTC_LOG(LS_ERROR) << "Failed to create event: " << status;
    106    callback_->OnCaptureResult(Result::ERROR_TEMPORARY, nullptr);
    107    return;
    108  }
    109  event.duplicate(ZX_RIGHT_SAME_RIGHTS, &dup);
    110 
    111  fuchsia::ui::composition::GetNextFrameArgs next_frame_args;
    112  next_frame_args.set_event(std::move(dup));
    113 
    114  fuchsia::ui::composition::ScreenCapture_GetNextFrame_Result result;
    115  screen_capture_->GetNextFrame(std::move(next_frame_args), &result);
    116  if (result.is_err()) {
    117    RTC_LOG(LS_ERROR) << "fuchsia.ui.composition.GetNextFrame() failed: "
    118                      << result.err() << "\n";
    119    callback_->OnCaptureResult(Result::ERROR_TEMPORARY, nullptr);
    120    return;
    121  }
    122 
    123  status = event.wait_one(ZX_EVENT_SIGNALED, zx::deadline_after(kEventDelay),
    124                          nullptr);
    125  if (status != ZX_OK) {
    126    RTC_LOG(LS_ERROR) << "Timed out waiting for ScreenCapture to render frame: "
    127                      << status;
    128    callback_->OnCaptureResult(Result::ERROR_TEMPORARY, nullptr);
    129    return;
    130  }
    131  uint32_t buffer_index = result.response().buffer_id();
    132 
    133  // TODO(bugs.webrtc.org/14097): Use SharedMemoryDesktopFrame and
    134  // ScreenCaptureFrameQueue
    135  std::unique_ptr<BasicDesktopFrame> frame(
    136      new BasicDesktopFrame(DesktopSize(width_, height_)));
    137 
    138  uint32_t pixels_per_row = GetPixelsPerRow(
    139      buffer_collection_info_.settings().image_format_constraints());
    140  uint32_t stride = kFuchsiaBytesPerPixel * pixels_per_row;
    141  frame->CopyPixelsFrom(virtual_memory_mapped_addrs_[buffer_index], stride,
    142                        DesktopRect::MakeWH(width_, height_));
    143  // Mark the whole screen as having been updated.
    144  frame->mutable_updated_region()->SetRect(
    145      DesktopRect::MakeWH(width_, height_));
    146 
    147  fuchsia::ui::composition::ScreenCapture_ReleaseFrame_Result release_result;
    148  screen_capture_->ReleaseFrame(buffer_index, &release_result);
    149  if (release_result.is_err()) {
    150    RTC_LOG(LS_ERROR) << "fuchsia.ui.composition.ReleaseFrame() failed: "
    151                      << release_result.err();
    152  }
    153 
    154  int capture_time_ms =
    155      (TimeNanos() - capture_start_time_nanos) / kNumNanosecsPerMillisec;
    156  frame->set_capture_time_ms(capture_time_ms);
    157  callback_->OnCaptureResult(Result::SUCCESS, std::move(frame));
    158 }
    159 
    160 bool ScreenCapturerFuchsia::GetSourceList(SourceList* screens) {
    161  RTC_DCHECK(screens->size() == 0);
    162  // Fuchsia only supports single monitor display at this point
    163  screens->push_back({kFuchsiaScreenId, std::string("Fuchsia monitor")});
    164  return true;
    165 }
    166 
    167 bool ScreenCapturerFuchsia::SelectSource(SourceId id) {
    168  if (id == kFuchsiaScreenId || id == kFullDesktopScreenId) {
    169    return true;
    170  }
    171  return false;
    172 }
    173 
    174 fuchsia::sysmem2::BufferCollectionConstraints
    175 ScreenCapturerFuchsia::GetBufferConstraints() {
    176  fuchsia::sysmem2::BufferCollectionConstraints constraints;
    177  constraints.mutable_usage()->set_cpu(fuchsia::sysmem2::CPU_USAGE_READ |
    178                                       fuchsia::sysmem2::CPU_USAGE_WRITE);
    179  constraints.set_min_buffer_count(kMinBufferCount);
    180 
    181  auto& memory_constraints = *constraints.mutable_buffer_memory_constraints();
    182  memory_constraints.set_ram_domain_supported(true);
    183  memory_constraints.set_cpu_domain_supported(true);
    184 
    185  fuchsia::sysmem2::ImageFormatConstraints& image_constraints =
    186      constraints.mutable_image_format_constraints()->emplace_back();
    187  image_constraints.mutable_color_spaces()->emplace_back(kSRGBColorSpace);
    188  image_constraints.set_pixel_format(kBGRA32PixelFormatType);
    189  image_constraints.set_pixel_format_modifier(
    190      fuchsia::images2::PixelFormatModifier::LINEAR);
    191 
    192  image_constraints.set_required_min_size(
    193      fuchsia::math::SizeU{width_, height_});
    194  image_constraints.set_required_max_size(
    195      fuchsia::math::SizeU{width_, height_});
    196 
    197  image_constraints.set_bytes_per_row_divisor(kFuchsiaBytesPerPixel);
    198 
    199  return constraints;
    200 }
    201 
    202 void ScreenCapturerFuchsia::SetupBuffers() {
    203  fuchsia::ui::display::singleton::InfoSyncPtr display_info;
    204  zx_status_t status =
    205      component_context_->svc()->Connect(display_info.NewRequest());
    206  if (status != ZX_OK) {
    207    fatal_error_ = true;
    208    RTC_LOG(LS_ERROR)
    209        << "Failed to connect to fuchsia.ui.display.singleton.Info: " << status;
    210    return;
    211  }
    212 
    213  fuchsia::ui::display::singleton::Metrics metrics;
    214  status = display_info->GetMetrics(&metrics);
    215  if (status != ZX_OK) {
    216    fatal_error_ = true;
    217    RTC_LOG(LS_ERROR) << "Failed to connect to get display dimensions: "
    218                      << status;
    219    return;
    220  }
    221  width_ = metrics.extent_in_px().width;
    222  height_ = metrics.extent_in_px().height;
    223 
    224  status = component_context_->svc()->Connect(sysmem_allocator_.NewRequest());
    225  if (status != ZX_OK) {
    226    fatal_error_ = true;
    227    RTC_LOG(LS_ERROR) << "Failed to connect to fuchsia.sysmem2.Allocator: "
    228                      << status;
    229    return;
    230  }
    231 
    232  fuchsia::sysmem2::BufferCollectionTokenSyncPtr sysmem_token;
    233  status = sysmem_allocator_->AllocateSharedCollection(
    234      std::move(fuchsia::sysmem2::AllocatorAllocateSharedCollectionRequest{}
    235                    .set_token_request(sysmem_token.NewRequest())));
    236  if (status != ZX_OK) {
    237    fatal_error_ = true;
    238    RTC_LOG(LS_ERROR)
    239        << "fuchsia.sysmem2.Allocator.AllocateSharedCollection() failed: "
    240        << status;
    241    return;
    242  }
    243 
    244  fuchsia::sysmem2::BufferCollectionTokenSyncPtr flatland_token;
    245  status = sysmem_token->Duplicate(
    246      std::move(fuchsia::sysmem2::BufferCollectionTokenDuplicateRequest{}
    247                    .set_rights_attenuation_mask(ZX_RIGHT_SAME_RIGHTS)
    248                    .set_token_request(flatland_token.NewRequest())));
    249  if (status != ZX_OK) {
    250    fatal_error_ = true;
    251    RTC_LOG(LS_ERROR)
    252        << "fuchsia.sysmem2.BufferCollectionToken.Duplicate() failed: "
    253        << status;
    254    return;
    255  }
    256 
    257  fuchsia::sysmem2::Node_Sync_Result sync_result;
    258  status = sysmem_token->Sync(&sync_result);
    259  if (status != ZX_OK) {
    260    fatal_error_ = true;
    261    RTC_LOG(LS_ERROR) << "fuchsia.sysmem2.BufferCollectionToken.Sync() failed: "
    262                      << status;
    263    return;
    264  }
    265 
    266  status = sysmem_allocator_->BindSharedCollection(
    267      std::move(fuchsia::sysmem2::AllocatorBindSharedCollectionRequest{}
    268                    .set_token(std::move(sysmem_token))
    269                    .set_buffer_collection_request(collection_.NewRequest())));
    270  if (status != ZX_OK) {
    271    fatal_error_ = true;
    272    RTC_LOG(LS_ERROR)
    273        << "fuchsia.sysmem2.Allocator.BindSharedCollection() failed: "
    274        << status;
    275    return;
    276  }
    277 
    278  status = collection_->SetConstraints(std::move(
    279      fuchsia::sysmem2::BufferCollectionSetConstraintsRequest{}.set_constraints(
    280          GetBufferConstraints())));
    281  if (status != ZX_OK) {
    282    fatal_error_ = true;
    283    RTC_LOG(LS_ERROR)
    284        << "fuchsia.sysmem2.BufferCollection.SetConstraints() failed: "
    285        << status;
    286    return;
    287  }
    288 
    289  fuchsia::ui::composition::BufferCollectionImportToken import_token;
    290  fuchsia::ui::composition::BufferCollectionExportToken export_token;
    291  status = zx::eventpair::create(0, &export_token.value, &import_token.value);
    292  if (status != ZX_OK) {
    293    fatal_error_ = true;
    294    RTC_LOG(LS_ERROR)
    295        << "Failed to create BufferCollection import and export tokens: "
    296        << status;
    297    return;
    298  }
    299 
    300  status = component_context_->svc()->Connect(flatland_allocator_.NewRequest());
    301  if (status != ZX_OK) {
    302    fatal_error_ = true;
    303    RTC_LOG(LS_ERROR) << "Failed to connect to Flatland Allocator: " << status;
    304    return;
    305  }
    306 
    307  fuchsia::ui::composition::RegisterBufferCollectionArgs buffer_collection_args;
    308  buffer_collection_args.set_export_token(std::move(export_token));
    309  buffer_collection_args.set_buffer_collection_token2(
    310      std::move(flatland_token));
    311  buffer_collection_args.set_usage(
    312      fuchsia::ui::composition::RegisterBufferCollectionUsage::SCREENSHOT);
    313 
    314  fuchsia::ui::composition::Allocator_RegisterBufferCollection_Result
    315      buffer_collection_result;
    316  flatland_allocator_->RegisterBufferCollection(
    317      std::move(buffer_collection_args), &buffer_collection_result);
    318  if (buffer_collection_result.is_err()) {
    319    fatal_error_ = true;
    320    RTC_LOG(LS_ERROR) << "fuchsia.ui.composition.Allocator."
    321                         "RegisterBufferCollection() failed.";
    322    return;
    323  }
    324 
    325  fuchsia::sysmem2::BufferCollection_WaitForAllBuffersAllocated_Result
    326      wait_result;
    327  status = collection_->WaitForAllBuffersAllocated(&wait_result);
    328  if (status != ZX_OK) {
    329    fatal_error_ = true;
    330    RTC_LOG(LS_ERROR) << "Failed to wait for buffer collection info: "
    331                      << status;
    332    return;
    333  }
    334  if (!wait_result.is_response()) {
    335    if (wait_result.is_framework_err()) {
    336      RTC_LOG(LS_ERROR)
    337          << "Failed to allocate buffer collection (framework_err): "
    338          << fidl::ToUnderlying(wait_result.framework_err());
    339    } else {
    340      RTC_LOG(LS_ERROR) << "Failed to allocate buffer collection (err): "
    341                        << static_cast<uint32_t>(wait_result.err());
    342    }
    343    fatal_error_ = true;
    344    return;
    345  }
    346  buffer_collection_info_ =
    347      std::move(*wait_result.response().mutable_buffer_collection_info());
    348 
    349  status = collection_->Release();
    350  if (status != ZX_OK) {
    351    fatal_error_ = true;
    352    RTC_LOG(LS_ERROR) << "Failed to close buffer collection token: " << status;
    353    return;
    354  }
    355 
    356  status = component_context_->svc()->Connect(screen_capture_.NewRequest());
    357  if (status != ZX_OK) {
    358    fatal_error_ = true;
    359    RTC_LOG(LS_ERROR) << "Failed to connect to Screen Capture: " << status;
    360    return;
    361  }
    362 
    363  // Configure buffers in ScreenCapture client.
    364  fuchsia::ui::composition::ScreenCaptureConfig configure_args;
    365  configure_args.set_import_token(std::move(import_token));
    366  configure_args.set_buffer_count(buffer_collection_info_.buffers().size());
    367  configure_args.set_size({width_, height_});
    368 
    369  fuchsia::ui::composition::ScreenCapture_Configure_Result configure_result;
    370  screen_capture_->Configure(std::move(configure_args), &configure_result);
    371  if (configure_result.is_err()) {
    372    fatal_error_ = true;
    373    RTC_LOG(LS_ERROR)
    374        << "fuchsia.ui.composition.ScreenCapture.Configure() failed: "
    375        << configure_result.err();
    376    return;
    377  }
    378 
    379  // We have a collection of virtual memory objects which the ScreenCapture
    380  // client will write the frame data to when requested. We map each of these
    381  // onto a pointer stored in virtual_memory_mapped_addrs_ which we can use to
    382  // access this data.
    383  uint32_t virt_mem_bytes =
    384      buffer_collection_info_.settings().buffer_settings().size_bytes();
    385  RTC_DCHECK(virt_mem_bytes > 0);
    386  for (uint32_t buffer_index = 0;
    387       buffer_index < buffer_collection_info_.buffers().size();
    388       buffer_index++) {
    389    const zx::vmo& virt_mem =
    390        buffer_collection_info_.buffers()[buffer_index].vmo();
    391    virtual_memory_mapped_addrs_[buffer_index] = nullptr;
    392    status = zx::vmar::root_self()->map(
    393        ZX_VM_PERM_READ, /*vmar_offset*/ 0, virt_mem,
    394        /*vmo_offset*/ 0, virt_mem_bytes,
    395        reinterpret_cast<uintptr_t*>(
    396            &virtual_memory_mapped_addrs_[buffer_index]));
    397    if (status != ZX_OK) {
    398      fatal_error_ = true;
    399      RTC_LOG(LS_ERROR) << "Failed to map virtual memory: " << status;
    400      return;
    401    }
    402  }
    403 }
    404 
    405 uint32_t ScreenCapturerFuchsia::GetPixelsPerRow(
    406    const fuchsia::sysmem2::ImageFormatConstraints& constraints) {
    407  uint32_t stride = RoundUpToMultiple(
    408      std::max(constraints.min_bytes_per_row(), width_ * kFuchsiaBytesPerPixel),
    409      constraints.bytes_per_row_divisor());
    410  uint32_t pixels_per_row = stride / kFuchsiaBytesPerPixel;
    411 
    412  return pixels_per_row;
    413 }
    414 
    415 }  // namespace webrtc