Backed out changeset e7acb4e12051 (bug 1893666) for causing xpcshell failures on...
[gecko.git] / third_party / libwebrtc / modules / desktop_capture / screen_capturer_fuchsia.cc
blobf2198bf5759f56e5df908cc6474364174dc6d675
1 /*
2 * Copyright 2022 The WebRTC project authors. All Rights Reserved.
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 */
11 #include "modules/desktop_capture/screen_capturer_fuchsia.h"
13 #include <fuchsia/sysmem/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>
18 #include <algorithm>
19 #include <cstdint>
20 #include <memory>
21 #include <string>
22 #include <utility>
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"
36 namespace webrtc {
38 namespace {
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::sysmem::ColorSpaceType kSRGBColorSpace =
46 fuchsia::sysmem::ColorSpaceType::SRGB;
47 static constexpr fuchsia::sysmem::PixelFormatType kBGRA32PixelFormatType =
48 fuchsia::sysmem::PixelFormatType::BGRA32;
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;
55 } // namespace
57 std::unique_ptr<DesktopCapturer> DesktopCapturer::CreateRawScreenCapturer(
58 const DesktopCaptureOptions& options) {
59 std::unique_ptr<ScreenCapturerFuchsia> capturer(new ScreenCapturerFuchsia());
60 return capturer;
63 ScreenCapturerFuchsia::ScreenCapturerFuchsia()
64 : component_context_(sys::ComponentContext::Create()) {}
66 ScreenCapturerFuchsia::~ScreenCapturerFuchsia() {
67 // unmap virtual memory mapped pointers
68 uint32_t virt_mem_bytes =
69 buffer_collection_info_.settings.buffer_settings.size_bytes;
70 for (uint32_t buffer_index = 0;
71 buffer_index < buffer_collection_info_.buffer_count; buffer_index++) {
72 uintptr_t address =
73 reinterpret_cast<uintptr_t>(virtual_memory_mapped_addrs_[buffer_index]);
74 zx_status_t status = zx::vmar::root_self()->unmap(address, virt_mem_bytes);
75 RTC_DCHECK(status == ZX_OK);
79 void ScreenCapturerFuchsia::Start(Callback* callback) {
80 RTC_DCHECK(!callback_);
81 RTC_DCHECK(callback);
82 callback_ = callback;
84 fatal_error_ = false;
86 SetupBuffers();
89 void ScreenCapturerFuchsia::CaptureFrame() {
90 if (fatal_error_) {
91 callback_->OnCaptureResult(Result::ERROR_PERMANENT, nullptr);
92 return;
95 int64_t capture_start_time_nanos = rtc::TimeNanos();
97 zx::event event;
98 zx::event dup;
99 zx_status_t status = zx::event::create(0, &event);
100 if (status != ZX_OK) {
101 RTC_LOG(LS_ERROR) << "Failed to create event: " << status;
102 callback_->OnCaptureResult(Result::ERROR_TEMPORARY, nullptr);
103 return;
105 event.duplicate(ZX_RIGHT_SAME_RIGHTS, &dup);
107 fuchsia::ui::composition::GetNextFrameArgs next_frame_args;
108 next_frame_args.set_event(std::move(dup));
110 fuchsia::ui::composition::ScreenCapture_GetNextFrame_Result result;
111 screen_capture_->GetNextFrame(std::move(next_frame_args), &result);
112 if (result.is_err()) {
113 RTC_LOG(LS_ERROR) << "fuchsia.ui.composition.GetNextFrame() failed: "
114 << result.err() << "\n";
115 callback_->OnCaptureResult(Result::ERROR_TEMPORARY, nullptr);
116 return;
119 status = event.wait_one(ZX_EVENT_SIGNALED, zx::deadline_after(kEventDelay),
120 nullptr);
121 if (status != ZX_OK) {
122 RTC_LOG(LS_ERROR) << "Timed out waiting for ScreenCapture to render frame: "
123 << status;
124 callback_->OnCaptureResult(Result::ERROR_TEMPORARY, nullptr);
125 return;
127 uint32_t buffer_index = result.response().buffer_id();
129 // TODO(bugs.webrtc.org/14097): Use SharedMemoryDesktopFrame and
130 // ScreenCaptureFrameQueue
131 std::unique_ptr<BasicDesktopFrame> frame(
132 new BasicDesktopFrame(DesktopSize(width_, height_)));
134 uint32_t pixels_per_row = GetPixelsPerRow(
135 buffer_collection_info_.settings.image_format_constraints);
136 uint32_t stride = kFuchsiaBytesPerPixel * pixels_per_row;
137 frame->CopyPixelsFrom(virtual_memory_mapped_addrs_[buffer_index], stride,
138 DesktopRect::MakeWH(width_, height_));
139 // Mark the whole screen as having been updated.
140 frame->mutable_updated_region()->SetRect(
141 DesktopRect::MakeWH(width_, height_));
143 fuchsia::ui::composition::ScreenCapture_ReleaseFrame_Result release_result;
144 screen_capture_->ReleaseFrame(buffer_index, &release_result);
145 if (release_result.is_err()) {
146 RTC_LOG(LS_ERROR) << "fuchsia.ui.composition.ReleaseFrame() failed: "
147 << release_result.err();
150 int capture_time_ms = (rtc::TimeNanos() - capture_start_time_nanos) /
151 rtc::kNumNanosecsPerMillisec;
152 frame->set_capture_time_ms(capture_time_ms);
153 callback_->OnCaptureResult(Result::SUCCESS, std::move(frame));
156 bool ScreenCapturerFuchsia::GetSourceList(SourceList* screens) {
157 RTC_DCHECK(screens->size() == 0);
158 // Fuchsia only supports single monitor display at this point
159 screens->push_back({kFuchsiaScreenId, std::string("Fuchsia monitor")});
160 return true;
163 bool ScreenCapturerFuchsia::SelectSource(SourceId id) {
164 if (id == kFuchsiaScreenId || id == kFullDesktopScreenId) {
165 return true;
167 return false;
170 fuchsia::sysmem::BufferCollectionConstraints
171 ScreenCapturerFuchsia::GetBufferConstraints() {
172 fuchsia::sysmem::BufferCollectionConstraints constraints;
173 constraints.usage.cpu =
174 fuchsia::sysmem::cpuUsageRead | fuchsia::sysmem::cpuUsageWrite;
175 constraints.min_buffer_count = kMinBufferCount;
177 constraints.has_buffer_memory_constraints = true;
178 constraints.buffer_memory_constraints.ram_domain_supported = true;
179 constraints.buffer_memory_constraints.cpu_domain_supported = true;
181 constraints.image_format_constraints_count = 1;
182 fuchsia::sysmem::ImageFormatConstraints& image_constraints =
183 constraints.image_format_constraints[0];
184 image_constraints.color_spaces_count = 1;
185 image_constraints.color_space[0] =
186 fuchsia::sysmem::ColorSpace{.type = kSRGBColorSpace};
187 image_constraints.pixel_format.type = kBGRA32PixelFormatType;
188 image_constraints.pixel_format.has_format_modifier = true;
189 image_constraints.pixel_format.format_modifier.value =
190 fuchsia::sysmem::FORMAT_MODIFIER_LINEAR;
192 image_constraints.required_min_coded_width = width_;
193 image_constraints.required_min_coded_height = height_;
194 image_constraints.required_max_coded_width = width_;
195 image_constraints.required_max_coded_height = height_;
197 image_constraints.bytes_per_row_divisor = kFuchsiaBytesPerPixel;
199 return constraints;
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;
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;
221 width_ = metrics.extent_in_px().width;
222 height_ = metrics.extent_in_px().height;
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 Sysmem Allocator: " << status;
228 return;
231 fuchsia::sysmem::BufferCollectionTokenSyncPtr sysmem_token;
232 status =
233 sysmem_allocator_->AllocateSharedCollection(sysmem_token.NewRequest());
234 if (status != ZX_OK) {
235 fatal_error_ = true;
236 RTC_LOG(LS_ERROR)
237 << "fuchsia.sysmem.Allocator.AllocateSharedCollection() failed: "
238 << status;
239 return;
242 fuchsia::sysmem::BufferCollectionTokenSyncPtr flatland_token;
243 status = sysmem_token->Duplicate(ZX_RIGHT_SAME_RIGHTS,
244 flatland_token.NewRequest());
245 if (status != ZX_OK) {
246 fatal_error_ = true;
247 RTC_LOG(LS_ERROR)
248 << "fuchsia.sysmem.BufferCollectionToken.Duplicate() failed: "
249 << status;
250 return;
253 status = sysmem_token->Sync();
254 if (status != ZX_OK) {
255 fatal_error_ = true;
256 RTC_LOG(LS_ERROR) << "fuchsia.sysmem.BufferCollectionToken.Sync() failed: "
257 << status;
258 return;
261 status = sysmem_allocator_->BindSharedCollection(std::move(sysmem_token),
262 collection_.NewRequest());
263 if (status != ZX_OK) {
264 fatal_error_ = true;
265 RTC_LOG(LS_ERROR)
266 << "fuchsia.sysmem.Allocator.BindSharedCollection() failed: " << status;
267 return;
270 status = collection_->SetConstraints(/*has_constraints=*/true,
271 GetBufferConstraints());
272 if (status != ZX_OK) {
273 fatal_error_ = true;
274 RTC_LOG(LS_ERROR)
275 << "fuchsia.sysmem.BufferCollection.SetConstraints() failed: "
276 << status;
277 return;
280 fuchsia::ui::composition::BufferCollectionImportToken import_token;
281 fuchsia::ui::composition::BufferCollectionExportToken export_token;
282 status = zx::eventpair::create(0, &export_token.value, &import_token.value);
283 if (status != ZX_OK) {
284 fatal_error_ = true;
285 RTC_LOG(LS_ERROR)
286 << "Failed to create BufferCollection import and export tokens: "
287 << status;
288 return;
291 status = component_context_->svc()->Connect(flatland_allocator_.NewRequest());
292 if (status != ZX_OK) {
293 fatal_error_ = true;
294 RTC_LOG(LS_ERROR) << "Failed to connect to Flatland Allocator: " << status;
295 return;
298 fuchsia::ui::composition::RegisterBufferCollectionArgs buffer_collection_args;
299 buffer_collection_args.set_export_token(std::move(export_token));
300 buffer_collection_args.set_buffer_collection_token(std::move(flatland_token));
301 buffer_collection_args.set_usage(
302 fuchsia::ui::composition::RegisterBufferCollectionUsage::SCREENSHOT);
304 fuchsia::ui::composition::Allocator_RegisterBufferCollection_Result
305 buffer_collection_result;
306 flatland_allocator_->RegisterBufferCollection(
307 std::move(buffer_collection_args), &buffer_collection_result);
308 if (buffer_collection_result.is_err()) {
309 fatal_error_ = true;
310 RTC_LOG(LS_ERROR) << "fuchsia.ui.composition.Allocator."
311 "RegisterBufferCollection() failed.";
312 return;
315 zx_status_t allocation_status;
316 status = collection_->WaitForBuffersAllocated(&allocation_status,
317 &buffer_collection_info_);
318 if (status != ZX_OK) {
319 fatal_error_ = true;
320 RTC_LOG(LS_ERROR) << "Failed to wait for buffer collection info: "
321 << status;
322 return;
324 if (allocation_status != ZX_OK) {
325 fatal_error_ = true;
326 RTC_LOG(LS_ERROR) << "Failed to allocate buffer collection: " << status;
327 return;
329 status = collection_->Close();
330 if (status != ZX_OK) {
331 fatal_error_ = true;
332 RTC_LOG(LS_ERROR) << "Failed to close buffer collection token: " << status;
333 return;
336 status = component_context_->svc()->Connect(screen_capture_.NewRequest());
337 if (status != ZX_OK) {
338 fatal_error_ = true;
339 RTC_LOG(LS_ERROR) << "Failed to connect to Screen Capture: " << status;
340 return;
343 // Configure buffers in ScreenCapture client.
344 fuchsia::ui::composition::ScreenCaptureConfig configure_args;
345 configure_args.set_import_token(std::move(import_token));
346 configure_args.set_buffer_count(buffer_collection_info_.buffer_count);
347 configure_args.set_size({width_, height_});
349 fuchsia::ui::composition::ScreenCapture_Configure_Result configure_result;
350 screen_capture_->Configure(std::move(configure_args), &configure_result);
351 if (configure_result.is_err()) {
352 fatal_error_ = true;
353 RTC_LOG(LS_ERROR)
354 << "fuchsia.ui.composition.ScreenCapture.Configure() failed: "
355 << configure_result.err();
356 return;
359 // We have a collection of virtual memory objects which the ScreenCapture
360 // client will write the frame data to when requested. We map each of these
361 // onto a pointer stored in virtual_memory_mapped_addrs_ which we can use to
362 // access this data.
363 uint32_t virt_mem_bytes =
364 buffer_collection_info_.settings.buffer_settings.size_bytes;
365 RTC_DCHECK(virt_mem_bytes > 0);
366 for (uint32_t buffer_index = 0;
367 buffer_index < buffer_collection_info_.buffer_count; buffer_index++) {
368 const zx::vmo& virt_mem = buffer_collection_info_.buffers[buffer_index].vmo;
369 virtual_memory_mapped_addrs_[buffer_index] = nullptr;
370 auto status = zx::vmar::root_self()->map(
371 ZX_VM_PERM_READ, /*vmar_offset*/ 0, virt_mem,
372 /*vmo_offset*/ 0, virt_mem_bytes,
373 reinterpret_cast<uintptr_t*>(
374 &virtual_memory_mapped_addrs_[buffer_index]));
375 if (status != ZX_OK) {
376 fatal_error_ = true;
377 RTC_LOG(LS_ERROR) << "Failed to map virtual memory: " << status;
378 return;
383 uint32_t ScreenCapturerFuchsia::GetPixelsPerRow(
384 const fuchsia::sysmem::ImageFormatConstraints& constraints) {
385 uint32_t stride = RoundUpToMultiple(
386 std::max(constraints.min_bytes_per_row, width_ * kFuchsiaBytesPerPixel),
387 constraints.bytes_per_row_divisor);
388 uint32_t pixels_per_row = stride / kFuchsiaBytesPerPixel;
390 return pixels_per_row;
393 } // namespace webrtc