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.
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>
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"
40 static constexpr uint32_t kMinBufferCount
= 2;
41 static constexpr uint32_t kFuchsiaBytesPerPixel
= 4;
42 static constexpr DesktopCapturer::SourceId kFuchsiaScreenId
= 1;
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
;
57 std::unique_ptr
<DesktopCapturer
> DesktopCapturer::CreateRawScreenCapturer(
58 const DesktopCaptureOptions
& options
) {
59 std::unique_ptr
<ScreenCapturerFuchsia
> capturer(new ScreenCapturerFuchsia());
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
++) {
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_
);
89 void ScreenCapturerFuchsia::CaptureFrame() {
91 callback_
->OnCaptureResult(Result::ERROR_PERMANENT
, nullptr);
95 int64_t capture_start_time_nanos
= rtc::TimeNanos();
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);
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);
119 status
= event
.wait_one(ZX_EVENT_SIGNALED
, zx::deadline_after(kEventDelay
),
121 if (status
!= ZX_OK
) {
122 RTC_LOG(LS_ERROR
) << "Timed out waiting for ScreenCapture to render frame: "
124 callback_
->OnCaptureResult(Result::ERROR_TEMPORARY
, nullptr);
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")});
163 bool ScreenCapturerFuchsia::SelectSource(SourceId id
) {
164 if (id
== kFuchsiaScreenId
|| id
== kFullDesktopScreenId
) {
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
;
202 void ScreenCapturerFuchsia::SetupBuffers() {
203 fuchsia::ui::display::singleton::InfoSyncPtr display_info
;
205 component_context_
->svc()->Connect(display_info
.NewRequest());
206 if (status
!= ZX_OK
) {
209 << "Failed to connect to fuchsia.ui.display.singleton.Info: " << status
;
213 fuchsia::ui::display::singleton::Metrics metrics
;
214 status
= display_info
->GetMetrics(&metrics
);
215 if (status
!= ZX_OK
) {
217 RTC_LOG(LS_ERROR
) << "Failed to connect to get display dimensions: "
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
) {
227 RTC_LOG(LS_ERROR
) << "Failed to connect to Sysmem Allocator: " << status
;
231 fuchsia::sysmem::BufferCollectionTokenSyncPtr sysmem_token
;
233 sysmem_allocator_
->AllocateSharedCollection(sysmem_token
.NewRequest());
234 if (status
!= ZX_OK
) {
237 << "fuchsia.sysmem.Allocator.AllocateSharedCollection() failed: "
242 fuchsia::sysmem::BufferCollectionTokenSyncPtr flatland_token
;
243 status
= sysmem_token
->Duplicate(ZX_RIGHT_SAME_RIGHTS
,
244 flatland_token
.NewRequest());
245 if (status
!= ZX_OK
) {
248 << "fuchsia.sysmem.BufferCollectionToken.Duplicate() failed: "
253 status
= sysmem_token
->Sync();
254 if (status
!= ZX_OK
) {
256 RTC_LOG(LS_ERROR
) << "fuchsia.sysmem.BufferCollectionToken.Sync() failed: "
261 status
= sysmem_allocator_
->BindSharedCollection(std::move(sysmem_token
),
262 collection_
.NewRequest());
263 if (status
!= ZX_OK
) {
266 << "fuchsia.sysmem.Allocator.BindSharedCollection() failed: " << status
;
270 status
= collection_
->SetConstraints(/*has_constraints=*/true,
271 GetBufferConstraints());
272 if (status
!= ZX_OK
) {
275 << "fuchsia.sysmem.BufferCollection.SetConstraints() failed: "
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
) {
286 << "Failed to create BufferCollection import and export tokens: "
291 status
= component_context_
->svc()->Connect(flatland_allocator_
.NewRequest());
292 if (status
!= ZX_OK
) {
294 RTC_LOG(LS_ERROR
) << "Failed to connect to Flatland Allocator: " << status
;
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()) {
310 RTC_LOG(LS_ERROR
) << "fuchsia.ui.composition.Allocator."
311 "RegisterBufferCollection() failed.";
315 zx_status_t allocation_status
;
316 status
= collection_
->WaitForBuffersAllocated(&allocation_status
,
317 &buffer_collection_info_
);
318 if (status
!= ZX_OK
) {
320 RTC_LOG(LS_ERROR
) << "Failed to wait for buffer collection info: "
324 if (allocation_status
!= ZX_OK
) {
326 RTC_LOG(LS_ERROR
) << "Failed to allocate buffer collection: " << status
;
329 status
= collection_
->Close();
330 if (status
!= ZX_OK
) {
332 RTC_LOG(LS_ERROR
) << "Failed to close buffer collection token: " << status
;
336 status
= component_context_
->svc()->Connect(screen_capture_
.NewRequest());
337 if (status
!= ZX_OK
) {
339 RTC_LOG(LS_ERROR
) << "Failed to connect to Screen Capture: " << status
;
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()) {
354 << "fuchsia.ui.composition.ScreenCapture.Configure() failed: "
355 << configure_result
.err();
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
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
) {
377 RTC_LOG(LS_ERROR
) << "Failed to map virtual memory: " << status
;
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