| 
									
										
										
										
											2019-04-17 23:10:04 +02:00
										 |  |  | // Copyright (c) 2019 GitHub, Inc.
 | 
					
						
							|  |  |  | // Use of this source code is governed by the MIT license that can be
 | 
					
						
							|  |  |  | // found in the LICENSE file.
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-19 13:46:59 -07:00
										 |  |  | #include "shell/browser/osr/osr_video_consumer.h"
 | 
					
						
							| 
									
										
										
										
											2019-04-17 23:10:04 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | #include <utility>
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-05 15:53:39 +02:00
										 |  |  | #include "media/base/limits.h"
 | 
					
						
							| 
									
										
										
										
											2019-04-17 23:10:04 +02:00
										 |  |  | #include "media/base/video_frame_metadata.h"
 | 
					
						
							| 
									
										
										
										
											2021-06-22 12:17:16 -07:00
										 |  |  | #include "media/capture/mojom/video_capture_buffer.mojom.h"
 | 
					
						
							| 
									
										
										
										
											2019-04-17 23:10:04 +02:00
										 |  |  | #include "media/capture/mojom/video_capture_types.mojom.h"
 | 
					
						
							| 
									
										
										
										
											2022-01-10 17:31:39 -05:00
										 |  |  | #include "services/viz/privileged/mojom/compositing/frame_sink_video_capture.mojom-shared.h"
 | 
					
						
							| 
									
										
										
										
											2019-06-19 13:46:59 -07:00
										 |  |  | #include "shell/browser/osr/osr_render_widget_host_view.h"
 | 
					
						
							| 
									
										
										
										
											2024-07-25 04:25:45 -05:00
										 |  |  | #include "third_party/skia/include/core/SkImageInfo.h"
 | 
					
						
							|  |  |  | #include "third_party/skia/include/core/SkRegion.h"
 | 
					
						
							| 
									
										
										
										
											2019-04-17 23:10:04 +02:00
										 |  |  | #include "ui/gfx/skbitmap_operations.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-19 14:23:04 -07:00
										 |  |  | namespace electron { | 
					
						
							| 
									
										
										
										
											2019-04-17 23:10:04 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | OffScreenVideoConsumer::OffScreenVideoConsumer( | 
					
						
							|  |  |  |     OffScreenRenderWidgetHostView* view, | 
					
						
							|  |  |  |     OnPaintCallback callback) | 
					
						
							|  |  |  |     : callback_(callback), | 
					
						
							|  |  |  |       view_(view), | 
					
						
							| 
									
										
										
										
											2021-01-26 19:16:21 +01:00
										 |  |  |       video_capturer_(view->CreateVideoCapturer()) { | 
					
						
							| 
									
										
										
										
											2019-04-17 23:10:04 +02:00
										 |  |  |   video_capturer_->SetAutoThrottlingEnabled(false); | 
					
						
							|  |  |  |   video_capturer_->SetMinSizeChangePeriod(base::TimeDelta()); | 
					
						
							| 
									
										
										
										
											2022-01-10 17:31:39 -05:00
										 |  |  |   video_capturer_->SetFormat(media::PIXEL_FORMAT_ARGB); | 
					
						
							| 
									
										
										
										
											2022-05-05 15:53:39 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-22 16:18:21 +08:00
										 |  |  |   // https://crrev.org/c/6438681
 | 
					
						
							|  |  |  |   // Disable capturer's animation lock-in feature for offscreen capture to
 | 
					
						
							|  |  |  |   // avoid output stutter.
 | 
					
						
							|  |  |  |   video_capturer_->SetAnimationFpsLockIn(false, 1); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-23 08:23:13 +08:00
										 |  |  |   // Previous design of OSR try to set the resolution constraint to match the
 | 
					
						
							|  |  |  |   // view's size. It is actually not necessary and creates faulty textures
 | 
					
						
							|  |  |  |   // when the window/view's size changes frequently. The constraint may not
 | 
					
						
							|  |  |  |   // take effect before the internal frame size changes, and makes the capturer
 | 
					
						
							|  |  |  |   // try to resize the new frame size to the old constraint size, which makes
 | 
					
						
							|  |  |  |   // the output image blurry. (For example, small window suddenly expands to
 | 
					
						
							|  |  |  |   // maximum size, will actually produce a small output image with maximized
 | 
					
						
							|  |  |  |   // window resized to fit the small image, however, the expected output is
 | 
					
						
							|  |  |  |   // a maximized image without resizing).
 | 
					
						
							|  |  |  |   // So, we just set the constraint to no limit (1x1 to max). When the window
 | 
					
						
							|  |  |  |   // size changed, a new frame with new size will be automatically generated.
 | 
					
						
							|  |  |  |   // There's no need to manually set the constraint and request a new frame.
 | 
					
						
							|  |  |  |   video_capturer_->SetResolutionConstraints( | 
					
						
							|  |  |  |       gfx::Size(1, 1), | 
					
						
							|  |  |  |       gfx::Size(media::limits::kMaxDimension, media::limits::kMaxDimension), | 
					
						
							|  |  |  |       false); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-29 20:43:28 -06:00
										 |  |  |   SetFrameRate(view_->frame_rate()); | 
					
						
							| 
									
										
										
										
											2019-04-17 23:10:04 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | OffScreenVideoConsumer::~OffScreenVideoConsumer() = default; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void OffScreenVideoConsumer::SetActive(bool active) { | 
					
						
							|  |  |  |   if (active) { | 
					
						
							| 
									
										
										
										
											2024-08-23 08:23:13 +08:00
										 |  |  |     video_capturer_->Start( | 
					
						
							|  |  |  |         this, view_->offscreen_use_shared_texture() | 
					
						
							|  |  |  |                   ? viz::mojom::BufferFormatPreference::kPreferGpuMemoryBuffer | 
					
						
							|  |  |  |                   : viz::mojom::BufferFormatPreference::kDefault); | 
					
						
							| 
									
										
										
										
											2019-04-17 23:10:04 +02:00
										 |  |  |   } else { | 
					
						
							|  |  |  |     video_capturer_->Stop(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void OffScreenVideoConsumer::SetFrameRate(int frame_rate) { | 
					
						
							| 
									
										
										
										
											2021-11-24 09:45:59 +01:00
										 |  |  |   video_capturer_->SetMinCapturePeriod(base::Seconds(1) / frame_rate); | 
					
						
							| 
									
										
										
										
											2019-04-17 23:10:04 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void OffScreenVideoConsumer::OnFrameCaptured( | 
					
						
							| 
									
										
										
										
											2022-01-10 17:31:39 -05:00
										 |  |  |     ::media::mojom::VideoBufferHandlePtr data, | 
					
						
							| 
									
										
										
										
											2019-04-17 23:10:04 +02:00
										 |  |  |     ::media::mojom::VideoFrameInfoPtr info, | 
					
						
							|  |  |  |     const gfx::Rect& content_rect, | 
					
						
							| 
									
										
										
										
											2020-02-27 11:00:07 -08:00
										 |  |  |     mojo::PendingRemote<viz::mojom::FrameSinkVideoConsumerFrameCallbacks> | 
					
						
							|  |  |  |         callbacks) { | 
					
						
							| 
									
										
										
										
											2024-08-23 08:23:13 +08:00
										 |  |  |   // Since we don't call ProvideFeedback, just need Done to release the frame,
 | 
					
						
							|  |  |  |   // there's no need to call the callbacks, see in_flight_frame_delivery.cc
 | 
					
						
							|  |  |  |   // The destructor will call Done for us once the pipe closed.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // Offscreen using GPU shared texture
 | 
					
						
							|  |  |  |   if (view_->offscreen_use_shared_texture()) { | 
					
						
							|  |  |  |     CHECK(data->is_gpu_memory_buffer_handle()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     auto& orig_handle = data->get_gpu_memory_buffer_handle(); | 
					
						
							|  |  |  |     CHECK(!orig_handle.is_null()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Clone the handle to support keep the handle alive after the callback
 | 
					
						
							|  |  |  |     auto gmb_handle = orig_handle.Clone(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     OffscreenSharedTextureValue texture; | 
					
						
							|  |  |  |     texture.pixel_format = info->pixel_format; | 
					
						
							|  |  |  |     texture.coded_size = info->coded_size; | 
					
						
							|  |  |  |     texture.visible_rect = info->visible_rect; | 
					
						
							|  |  |  |     texture.content_rect = content_rect; | 
					
						
							|  |  |  |     texture.timestamp = info->timestamp.InMicroseconds(); | 
					
						
							|  |  |  |     texture.frame_count = info->metadata.capture_counter.value_or(0); | 
					
						
							|  |  |  |     texture.capture_update_rect = info->metadata.capture_update_rect; | 
					
						
							|  |  |  |     texture.source_size = info->metadata.source_size; | 
					
						
							|  |  |  |     texture.region_capture_rect = info->metadata.region_capture_rect; | 
					
						
							|  |  |  |     texture.widget_type = view_->GetWidgetType(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #if BUILDFLAG(IS_WIN)
 | 
					
						
							|  |  |  |     texture.shared_texture_handle = | 
					
						
							| 
									
										
										
										
											2025-02-11 17:26:39 -06:00
										 |  |  |         reinterpret_cast<uintptr_t>(gmb_handle.dxgi_handle().buffer_handle()); | 
					
						
							| 
									
										
										
										
											2024-08-23 08:23:13 +08:00
										 |  |  | #elif BUILDFLAG(IS_APPLE)
 | 
					
						
							|  |  |  |     texture.shared_texture_handle = | 
					
						
							|  |  |  |         reinterpret_cast<uintptr_t>(gmb_handle.io_surface.get()); | 
					
						
							|  |  |  | #elif BUILDFLAG(IS_LINUX)
 | 
					
						
							| 
									
										
										
										
											2025-05-01 23:53:40 -05:00
										 |  |  |     const auto& native_pixmap = gmb_handle.native_pixmap_handle(); | 
					
						
							| 
									
										
										
										
											2024-08-23 08:23:13 +08:00
										 |  |  |     texture.modifier = native_pixmap.modifier; | 
					
						
							|  |  |  |     for (const auto& plane : native_pixmap.planes) { | 
					
						
							|  |  |  |       texture.planes.emplace_back(plane.stride, plane.offset, plane.size, | 
					
						
							|  |  |  |                                   plane.fd.get()); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // The release holder will be released from JS side when `release` called
 | 
					
						
							|  |  |  |     texture.releaser_holder = new OffscreenReleaserHolder(std::move(gmb_handle), | 
					
						
							|  |  |  |                                                           std::move(callbacks)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     callback_.Run(content_rect, {}, std::move(texture)); | 
					
						
							| 
									
										
										
										
											2019-04-17 23:10:04 +02:00
										 |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-23 08:23:13 +08:00
										 |  |  |   // Regular shared texture capture using shared memory
 | 
					
						
							|  |  |  |   const auto& data_region = data->get_read_only_shmem_region(); | 
					
						
							| 
									
										
										
										
											2020-02-27 11:00:07 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-10 17:31:39 -05:00
										 |  |  |   if (!data_region.IsValid()) { | 
					
						
							| 
									
										
										
										
											2019-04-17 23:10:04 +02:00
										 |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2024-08-23 08:23:13 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-10 17:31:39 -05:00
										 |  |  |   base::ReadOnlySharedMemoryMapping mapping = data_region.Map(); | 
					
						
							| 
									
										
										
										
											2019-04-17 23:10:04 +02:00
										 |  |  |   if (!mapping.IsValid()) { | 
					
						
							|  |  |  |     DLOG(ERROR) << "Shared memory mapping failed."; | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2024-08-23 08:23:13 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 23:10:04 +02:00
										 |  |  |   if (mapping.size() < | 
					
						
							|  |  |  |       media::VideoFrame::AllocationSize(info->pixel_format, info->coded_size)) { | 
					
						
							|  |  |  |     DLOG(ERROR) << "Shared memory size was less than expected."; | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // The SkBitmap's pixels will be marked as immutable, but the installPixels()
 | 
					
						
							|  |  |  |   // API requires a non-const pointer. So, cast away the const.
 | 
					
						
							|  |  |  |   void* const pixels = const_cast<void*>(mapping.memory()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // Call installPixels() with a |releaseProc| that: 1) notifies the capturer
 | 
					
						
							|  |  |  |   // that this consumer has finished with the frame, and 2) releases the shared
 | 
					
						
							|  |  |  |   // memory mapping.
 | 
					
						
							|  |  |  |   struct FramePinner { | 
					
						
							|  |  |  |     // Keeps the shared memory that backs |frame_| mapped.
 | 
					
						
							|  |  |  |     base::ReadOnlySharedMemoryMapping mapping; | 
					
						
							|  |  |  |     // Prevents FrameSinkVideoCapturer from recycling the shared memory that
 | 
					
						
							|  |  |  |     // backs |frame_|.
 | 
					
						
							| 
									
										
										
										
											2020-02-27 11:00:07 -08:00
										 |  |  |     mojo::PendingRemote<viz::mojom::FrameSinkVideoConsumerFrameCallbacks> | 
					
						
							|  |  |  |         releaser; | 
					
						
							| 
									
										
										
										
											2019-04-17 23:10:04 +02:00
										 |  |  |   }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   SkBitmap bitmap; | 
					
						
							|  |  |  |   bitmap.installPixels( | 
					
						
							|  |  |  |       SkImageInfo::MakeN32(content_rect.width(), content_rect.height(), | 
					
						
							|  |  |  |                            kPremul_SkAlphaType), | 
					
						
							|  |  |  |       pixels, | 
					
						
							| 
									
										
										
										
											2024-06-07 17:18:35 -04:00
										 |  |  |       media::VideoFrame::RowBytes(media::VideoFrame::Plane::kARGB, | 
					
						
							| 
									
										
										
										
											2019-04-17 23:10:04 +02:00
										 |  |  |                                   info->pixel_format, info->coded_size.width()), | 
					
						
							|  |  |  |       [](void* addr, void* context) { | 
					
						
							|  |  |  |         delete static_cast<FramePinner*>(context); | 
					
						
							|  |  |  |       }, | 
					
						
							| 
									
										
										
										
											2024-08-23 08:23:13 +08:00
										 |  |  |       new FramePinner{std::move(mapping), std::move(callbacks)}); | 
					
						
							| 
									
										
										
										
											2019-04-17 23:10:04 +02:00
										 |  |  |   bitmap.setImmutable(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-23 08:23:13 +08:00
										 |  |  |   // Since update_rect is already offset-ed with same origin of content_rect,
 | 
					
						
							|  |  |  |   // there's nothing more to do with the imported bitmap.
 | 
					
						
							| 
									
										
										
										
											2024-01-10 23:23:35 +01:00
										 |  |  |   std::optional<gfx::Rect> update_rect = info->metadata.capture_update_rect; | 
					
						
							| 
									
										
										
										
											2020-06-22 10:35:10 -07:00
										 |  |  |   if (!update_rect.has_value() || update_rect->IsEmpty()) { | 
					
						
							|  |  |  |     update_rect = content_rect; | 
					
						
							| 
									
										
										
										
											2019-04-17 23:10:04 +02:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-23 08:23:13 +08:00
										 |  |  |   callback_.Run(*update_rect, bitmap, {}); | 
					
						
							| 
									
										
										
										
											2019-04-17 23:10:04 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-19 14:23:04 -07:00
										 |  |  | }  // namespace electron
 |