frame_subscriber.cc 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. // Copyright (c) 2015 GitHub, Inc.
  2. // Use of this source code is governed by the MIT license that can be
  3. // found in the LICENSE file.
  4. #include "shell/browser/api/frame_subscriber.h"
  5. #include <utility>
  6. #include "content/public/browser/render_view_host.h"
  7. #include "content/public/browser/render_widget_host.h"
  8. #include "content/public/browser/render_widget_host_view.h"
  9. #include "media/capture/mojom/video_capture_buffer.mojom.h"
  10. #include "media/capture/mojom/video_capture_types.mojom.h"
  11. #include "mojo/public/cpp/bindings/remote.h"
  12. #include "services/viz/privileged/mojom/compositing/frame_sink_video_capture.mojom-shared.h"
  13. #include "ui/gfx/geometry/size_conversions.h"
  14. #include "ui/gfx/image/image.h"
  15. #include "ui/gfx/skbitmap_operations.h"
  16. namespace electron {
  17. namespace api {
  18. constexpr static int kMaxFrameRate = 30;
  19. FrameSubscriber::FrameSubscriber(content::WebContents* web_contents,
  20. const FrameCaptureCallback& callback,
  21. bool only_dirty)
  22. : content::WebContentsObserver(web_contents),
  23. callback_(callback),
  24. only_dirty_(only_dirty) {
  25. content::RenderViewHost* rvh = web_contents->GetRenderViewHost();
  26. if (rvh)
  27. AttachToHost(rvh->GetWidget());
  28. }
  29. FrameSubscriber::~FrameSubscriber() = default;
  30. void FrameSubscriber::AttachToHost(content::RenderWidgetHost* host) {
  31. host_ = host;
  32. // The view can be null if the renderer process has crashed.
  33. // (https://crbug.com/847363)
  34. if (!host_->GetView())
  35. return;
  36. // Create and configure the video capturer.
  37. gfx::Size size = GetRenderViewSize();
  38. video_capturer_ = host_->GetView()->CreateVideoCapturer();
  39. video_capturer_->SetResolutionConstraints(size, size, true);
  40. video_capturer_->SetAutoThrottlingEnabled(false);
  41. video_capturer_->SetMinSizeChangePeriod(base::TimeDelta());
  42. video_capturer_->SetFormat(media::PIXEL_FORMAT_ARGB);
  43. video_capturer_->SetMinCapturePeriod(base::Seconds(1) / kMaxFrameRate);
  44. video_capturer_->Start(this, viz::mojom::BufferFormatPreference::kDefault);
  45. }
  46. void FrameSubscriber::DetachFromHost() {
  47. if (!host_)
  48. return;
  49. video_capturer_.reset();
  50. host_ = nullptr;
  51. }
  52. void FrameSubscriber::RenderFrameCreated(
  53. content::RenderFrameHost* render_frame_host) {
  54. if (!host_)
  55. AttachToHost(render_frame_host->GetRenderWidgetHost());
  56. }
  57. void FrameSubscriber::RenderViewDeleted(content::RenderViewHost* host) {
  58. if (host->GetWidget() == host_) {
  59. DetachFromHost();
  60. }
  61. }
  62. void FrameSubscriber::RenderViewHostChanged(content::RenderViewHost* old_host,
  63. content::RenderViewHost* new_host) {
  64. if ((old_host && old_host->GetWidget() == host_) || (!old_host && !host_)) {
  65. DetachFromHost();
  66. AttachToHost(new_host->GetWidget());
  67. }
  68. }
  69. void FrameSubscriber::OnFrameCaptured(
  70. ::media::mojom::VideoBufferHandlePtr data,
  71. ::media::mojom::VideoFrameInfoPtr info,
  72. const gfx::Rect& content_rect,
  73. mojo::PendingRemote<viz::mojom::FrameSinkVideoConsumerFrameCallbacks>
  74. callbacks) {
  75. auto& data_region = data->get_read_only_shmem_region();
  76. gfx::Size size = GetRenderViewSize();
  77. if (size != content_rect.size()) {
  78. video_capturer_->SetResolutionConstraints(size, size, true);
  79. video_capturer_->RequestRefreshFrame();
  80. return;
  81. }
  82. mojo::Remote<viz::mojom::FrameSinkVideoConsumerFrameCallbacks>
  83. callbacks_remote(std::move(callbacks));
  84. if (!data_region.IsValid()) {
  85. callbacks_remote->Done();
  86. return;
  87. }
  88. base::ReadOnlySharedMemoryMapping mapping = data_region.Map();
  89. if (!mapping.IsValid()) {
  90. DLOG(ERROR) << "Shared memory mapping failed.";
  91. return;
  92. }
  93. if (mapping.size() <
  94. media::VideoFrame::AllocationSize(info->pixel_format, info->coded_size)) {
  95. DLOG(ERROR) << "Shared memory size was less than expected.";
  96. return;
  97. }
  98. // The SkBitmap's pixels will be marked as immutable, but the installPixels()
  99. // API requires a non-const pointer. So, cast away the const.
  100. void* const pixels = const_cast<void*>(mapping.memory());
  101. // Call installPixels() with a |releaseProc| that: 1) notifies the capturer
  102. // that this consumer has finished with the frame, and 2) releases the shared
  103. // memory mapping.
  104. struct FramePinner {
  105. // Keeps the shared memory that backs |frame_| mapped.
  106. base::ReadOnlySharedMemoryMapping mapping;
  107. // Prevents FrameSinkVideoCapturer from recycling the shared memory that
  108. // backs |frame_|.
  109. mojo::Remote<viz::mojom::FrameSinkVideoConsumerFrameCallbacks> releaser;
  110. };
  111. SkBitmap bitmap;
  112. bitmap.installPixels(
  113. SkImageInfo::MakeN32(content_rect.width(), content_rect.height(),
  114. kPremul_SkAlphaType),
  115. pixels,
  116. media::VideoFrame::RowBytes(media::VideoFrame::kARGBPlane,
  117. info->pixel_format, info->coded_size.width()),
  118. [](void* addr, void* context) {
  119. delete static_cast<FramePinner*>(context);
  120. },
  121. new FramePinner{std::move(mapping), std::move(callbacks_remote)});
  122. bitmap.setImmutable();
  123. Done(content_rect, bitmap);
  124. }
  125. void FrameSubscriber::OnStopped() {}
  126. void FrameSubscriber::OnLog(const std::string& message) {}
  127. void FrameSubscriber::Done(const gfx::Rect& damage, const SkBitmap& frame) {
  128. if (frame.drawsNothing())
  129. return;
  130. const SkBitmap& bitmap = only_dirty_ ? SkBitmapOperations::CreateTiledBitmap(
  131. frame, damage.x(), damage.y(),
  132. damage.width(), damage.height())
  133. : frame;
  134. // Copying SkBitmap does not copy the internal pixels, we have to manually
  135. // allocate and write pixels otherwise crash may happen when the original
  136. // frame is modified.
  137. SkBitmap copy;
  138. copy.allocPixels(SkImageInfo::Make(bitmap.width(), bitmap.height(),
  139. kN32_SkColorType, kPremul_SkAlphaType));
  140. SkPixmap pixmap;
  141. bool success = bitmap.peekPixels(&pixmap) && copy.writePixels(pixmap, 0, 0);
  142. CHECK(success);
  143. callback_.Run(gfx::Image::CreateFrom1xBitmap(copy), damage);
  144. }
  145. gfx::Size FrameSubscriber::GetRenderViewSize() const {
  146. content::RenderWidgetHostView* view = host_->GetView();
  147. gfx::Size size = view->GetViewBounds().size();
  148. return gfx::ToRoundedSize(
  149. gfx::ScaleSize(gfx::SizeF(size), view->GetDeviceScaleFactor()));
  150. }
  151. } // namespace api
  152. } // namespace electron