| 
									
										
										
										
											2015-09-18 15:57:43 +08:00
										 |  |  | // Copyright (c) 2015 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/api/frame_subscriber.h"
 | 
					
						
							| 
									
										
										
										
											2015-09-18 15:57:43 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-08 17:58:54 +01:00
										 |  |  | #include <utility>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "content/public/browser/render_view_host.h"
 | 
					
						
							|  |  |  | #include "content/public/browser/render_widget_host.h"
 | 
					
						
							|  |  |  | #include "content/public/browser/render_widget_host_view.h"
 | 
					
						
							| 
									
										
										
										
											2021-06-22 12:17:16 -07:00
										 |  |  | #include "media/capture/mojom/video_capture_buffer.mojom.h"
 | 
					
						
							| 
									
										
										
										
											2019-02-21 23:19:58 +05:30
										 |  |  | #include "media/capture/mojom/video_capture_types.mojom.h"
 | 
					
						
							| 
									
										
										
										
											2019-10-28 18:12:35 -04:00
										 |  |  | #include "mojo/public/cpp/bindings/remote.h"
 | 
					
						
							| 
									
										
										
										
											2019-04-08 11:35:33 +09:00
										 |  |  | #include "ui/gfx/geometry/size_conversions.h"
 | 
					
						
							| 
									
										
										
										
											2019-04-05 03:36:12 +09:00
										 |  |  | #include "ui/gfx/image/image.h"
 | 
					
						
							| 
									
										
										
										
											2018-05-14 19:09:05 +02:00
										 |  |  | #include "ui/gfx/skbitmap_operations.h"
 | 
					
						
							| 
									
										
										
										
											2016-09-06 17:24:37 +09:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-19 14:23:04 -07:00
										 |  |  | namespace electron { | 
					
						
							| 
									
										
										
										
											2015-09-18 15:57:43 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | namespace api { | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-08 17:58:54 +01:00
										 |  |  | constexpr static int kMaxFrameRate = 30; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-05 03:36:12 +09:00
										 |  |  | FrameSubscriber::FrameSubscriber(content::WebContents* web_contents, | 
					
						
							| 
									
										
										
										
											2018-05-14 19:09:05 +02:00
										 |  |  |                                  const FrameCaptureCallback& callback, | 
					
						
							|  |  |  |                                  bool only_dirty) | 
					
						
							| 
									
										
										
										
											2018-05-14 17:55:39 +02:00
										 |  |  |     : content::WebContentsObserver(web_contents), | 
					
						
							| 
									
										
										
										
											2018-05-14 19:09:05 +02:00
										 |  |  |       callback_(callback), | 
					
						
							| 
									
										
										
										
											2021-01-26 19:16:21 +01:00
										 |  |  |       only_dirty_(only_dirty) { | 
					
						
							| 
									
										
										
										
											2018-11-08 17:58:54 +01:00
										 |  |  |   content::RenderViewHost* rvh = web_contents->GetRenderViewHost(); | 
					
						
							|  |  |  |   if (rvh) | 
					
						
							|  |  |  |     AttachToHost(rvh->GetWidget()); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2017-04-13 19:21:30 +09:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-18 02:45:15 +02:00
										 |  |  | FrameSubscriber::~FrameSubscriber() = default; | 
					
						
							| 
									
										
										
										
											2015-09-18 15:57:43 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-08 17:58:54 +01:00
										 |  |  | void FrameSubscriber::AttachToHost(content::RenderWidgetHost* host) { | 
					
						
							|  |  |  |   host_ = host; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // The view can be null if the renderer process has crashed.
 | 
					
						
							|  |  |  |   // (https://crbug.com/847363)
 | 
					
						
							|  |  |  |   if (!host_->GetView()) | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // Create and configure the video capturer.
 | 
					
						
							| 
									
										
										
										
											2019-04-08 11:35:33 +09:00
										 |  |  |   gfx::Size size = GetRenderViewSize(); | 
					
						
							| 
									
										
										
										
											2018-11-08 17:58:54 +01:00
										 |  |  |   video_capturer_ = host_->GetView()->CreateVideoCapturer(); | 
					
						
							| 
									
										
										
										
											2019-04-08 11:35:33 +09:00
										 |  |  |   video_capturer_->SetResolutionConstraints(size, size, true); | 
					
						
							| 
									
										
										
										
											2018-11-08 17:58:54 +01:00
										 |  |  |   video_capturer_->SetAutoThrottlingEnabled(false); | 
					
						
							|  |  |  |   video_capturer_->SetMinSizeChangePeriod(base::TimeDelta()); | 
					
						
							|  |  |  |   video_capturer_->SetFormat(media::PIXEL_FORMAT_ARGB, | 
					
						
							| 
									
										
										
										
											2019-01-12 06:30:43 +05:30
										 |  |  |                              gfx::ColorSpace::CreateREC709()); | 
					
						
							| 
									
										
										
										
											2018-11-08 17:58:54 +01:00
										 |  |  |   video_capturer_->SetMinCapturePeriod(base::TimeDelta::FromSeconds(1) / | 
					
						
							|  |  |  |                                        kMaxFrameRate); | 
					
						
							|  |  |  |   video_capturer_->Start(this); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void FrameSubscriber::DetachFromHost() { | 
					
						
							|  |  |  |   if (!host_) | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  |   video_capturer_.reset(); | 
					
						
							|  |  |  |   host_ = nullptr; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-04 09:27:05 -08:00
										 |  |  | void FrameSubscriber::RenderFrameCreated( | 
					
						
							|  |  |  |     content::RenderFrameHost* render_frame_host) { | 
					
						
							| 
									
										
										
										
											2018-11-08 17:58:54 +01:00
										 |  |  |   if (!host_) | 
					
						
							| 
									
										
										
										
											2021-03-04 09:27:05 -08:00
										 |  |  |     AttachToHost(render_frame_host->GetRenderWidgetHost()); | 
					
						
							| 
									
										
										
										
											2018-11-08 17:58:54 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void FrameSubscriber::RenderViewDeleted(content::RenderViewHost* host) { | 
					
						
							|  |  |  |   if (host->GetWidget() == host_) { | 
					
						
							|  |  |  |     DetachFromHost(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void FrameSubscriber::RenderViewHostChanged(content::RenderViewHost* old_host, | 
					
						
							|  |  |  |                                             content::RenderViewHost* new_host) { | 
					
						
							|  |  |  |   if ((old_host && old_host->GetWidget() == host_) || (!old_host && !host_)) { | 
					
						
							|  |  |  |     DetachFromHost(); | 
					
						
							|  |  |  |     AttachToHost(new_host->GetWidget()); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void FrameSubscriber::OnFrameCaptured( | 
					
						
							|  |  |  |     base::ReadOnlySharedMemoryRegion data, | 
					
						
							|  |  |  |     ::media::mojom::VideoFrameInfoPtr info, | 
					
						
							|  |  |  |     const gfx::Rect& content_rect, | 
					
						
							| 
									
										
										
										
											2019-10-28 18:12:35 -04:00
										 |  |  |     mojo::PendingRemote<viz::mojom::FrameSinkVideoConsumerFrameCallbacks> | 
					
						
							|  |  |  |         callbacks) { | 
					
						
							| 
									
										
										
										
											2019-04-08 11:35:33 +09:00
										 |  |  |   gfx::Size size = GetRenderViewSize(); | 
					
						
							|  |  |  |   if (size != content_rect.size()) { | 
					
						
							|  |  |  |     video_capturer_->SetResolutionConstraints(size, size, true); | 
					
						
							| 
									
										
										
										
											2018-11-08 17:58:54 +01:00
										 |  |  |     video_capturer_->RequestRefreshFrame(); | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-28 18:12:35 -04:00
										 |  |  |   mojo::Remote<viz::mojom::FrameSinkVideoConsumerFrameCallbacks> | 
					
						
							|  |  |  |       callbacks_remote(std::move(callbacks)); | 
					
						
							| 
									
										
										
										
											2018-11-08 17:58:54 +01:00
										 |  |  |   if (!data.IsValid()) { | 
					
						
							| 
									
										
										
										
											2019-10-28 18:12:35 -04:00
										 |  |  |     callbacks_remote->Done(); | 
					
						
							| 
									
										
										
										
											2018-11-08 17:58:54 +01:00
										 |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   base::ReadOnlySharedMemoryMapping mapping = data.Map(); | 
					
						
							|  |  |  |   if (!mapping.IsValid()) { | 
					
						
							|  |  |  |     DLOG(ERROR) << "Shared memory mapping failed."; | 
					
						
							|  |  |  |     return; | 
					
						
							| 
									
										
										
										
											2018-04-18 02:45:15 +02:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-11-08 17:58:54 +01: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_|.
 | 
					
						
							| 
									
										
										
										
											2019-10-28 18:12:35 -04:00
										 |  |  |     mojo::Remote<viz::mojom::FrameSinkVideoConsumerFrameCallbacks> releaser; | 
					
						
							| 
									
										
										
										
											2018-11-08 17:58:54 +01:00
										 |  |  |   }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   SkBitmap bitmap; | 
					
						
							|  |  |  |   bitmap.installPixels( | 
					
						
							|  |  |  |       SkImageInfo::MakeN32(content_rect.width(), content_rect.height(), | 
					
						
							|  |  |  |                            kPremul_SkAlphaType), | 
					
						
							|  |  |  |       pixels, | 
					
						
							|  |  |  |       media::VideoFrame::RowBytes(media::VideoFrame::kARGBPlane, | 
					
						
							|  |  |  |                                   info->pixel_format, info->coded_size.width()), | 
					
						
							|  |  |  |       [](void* addr, void* context) { | 
					
						
							|  |  |  |         delete static_cast<FramePinner*>(context); | 
					
						
							|  |  |  |       }, | 
					
						
							| 
									
										
										
										
											2019-10-28 18:12:35 -04:00
										 |  |  |       new FramePinner{std::move(mapping), std::move(callbacks_remote)}); | 
					
						
							| 
									
										
										
										
											2018-11-08 17:58:54 +01:00
										 |  |  |   bitmap.setImmutable(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Done(content_rect, bitmap); | 
					
						
							| 
									
										
										
										
											2018-05-14 17:55:39 +02:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2017-07-28 11:57:41 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-08 17:58:54 +01:00
										 |  |  | void FrameSubscriber::OnStopped() {} | 
					
						
							| 
									
										
										
										
											2018-04-18 02:45:15 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-13 16:39:26 -07:00
										 |  |  | void FrameSubscriber::OnLog(const std::string& message) {} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-14 17:55:39 +02:00
										 |  |  | void FrameSubscriber::Done(const gfx::Rect& damage, const SkBitmap& frame) { | 
					
						
							|  |  |  |   if (frame.drawsNothing()) | 
					
						
							|  |  |  |     return; | 
					
						
							| 
									
										
										
										
											2018-04-18 02:45:15 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-19 19:49:44 +02:00
										 |  |  |   const SkBitmap& bitmap = only_dirty_ ? SkBitmapOperations::CreateTiledBitmap( | 
					
						
							|  |  |  |                                              frame, damage.x(), damage.y(), | 
					
						
							|  |  |  |                                              damage.width(), damage.height()) | 
					
						
							|  |  |  |                                        : frame; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-05 03:36:12 +09:00
										 |  |  |   // Copying SkBitmap does not copy the internal pixels, we have to manually
 | 
					
						
							|  |  |  |   // allocate and write pixels otherwise crash may happen when the original
 | 
					
						
							|  |  |  |   // frame is modified.
 | 
					
						
							|  |  |  |   SkBitmap copy; | 
					
						
							|  |  |  |   copy.allocPixels(SkImageInfo::Make(bitmap.width(), bitmap.height(), | 
					
						
							| 
									
										
										
										
											2020-12-22 14:14:44 -08:00
										 |  |  |                                      kN32_SkColorType, kPremul_SkAlphaType)); | 
					
						
							| 
									
										
										
										
											2019-04-05 03:36:12 +09:00
										 |  |  |   SkPixmap pixmap; | 
					
						
							|  |  |  |   bool success = bitmap.peekPixels(&pixmap) && copy.writePixels(pixmap, 0, 0); | 
					
						
							|  |  |  |   CHECK(success); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   callback_.Run(gfx::Image::CreateFrom1xBitmap(copy), damage); | 
					
						
							| 
									
										
										
										
											2015-09-18 15:57:43 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-08 11:35:33 +09:00
										 |  |  | gfx::Size FrameSubscriber::GetRenderViewSize() const { | 
					
						
							|  |  |  |   content::RenderWidgetHostView* view = host_->GetView(); | 
					
						
							|  |  |  |   gfx::Size size = view->GetViewBounds().size(); | 
					
						
							|  |  |  |   return gfx::ToRoundedSize( | 
					
						
							|  |  |  |       gfx::ScaleSize(gfx::SizeF(size), view->GetDeviceScaleFactor())); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-09-18 15:57:43 +08:00
										 |  |  | }  // namespace api
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-19 14:23:04 -07:00
										 |  |  | }  // namespace electron
 |