frame_subscriber.cc 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  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_types.mojom.h"
  10. #include "shell/common/native_mate_converters/gfx_converter.h"
  11. #include "ui/gfx/geometry/size_conversions.h"
  12. #include "ui/gfx/image/image.h"
  13. #include "ui/gfx/skbitmap_operations.h"
  14. namespace electron {
  15. namespace api {
  16. constexpr static int kMaxFrameRate = 30;
  17. FrameSubscriber::FrameSubscriber(content::WebContents* web_contents,
  18. const FrameCaptureCallback& callback,
  19. bool only_dirty)
  20. : content::WebContentsObserver(web_contents),
  21. callback_(callback),
  22. only_dirty_(only_dirty),
  23. weak_ptr_factory_(this) {
  24. content::RenderViewHost* rvh = web_contents->GetRenderViewHost();
  25. if (rvh)
  26. AttachToHost(rvh->GetWidget());
  27. }
  28. FrameSubscriber::~FrameSubscriber() = default;
  29. void FrameSubscriber::AttachToHost(content::RenderWidgetHost* host) {
  30. host_ = host;
  31. // The view can be null if the renderer process has crashed.
  32. // (https://crbug.com/847363)
  33. if (!host_->GetView())
  34. return;
  35. // Create and configure the video capturer.
  36. gfx::Size size = GetRenderViewSize();
  37. video_capturer_ = host_->GetView()->CreateVideoCapturer();
  38. video_capturer_->SetResolutionConstraints(size, size, true);
  39. video_capturer_->SetAutoThrottlingEnabled(false);
  40. video_capturer_->SetMinSizeChangePeriod(base::TimeDelta());
  41. video_capturer_->SetFormat(media::PIXEL_FORMAT_ARGB,
  42. gfx::ColorSpace::CreateREC709());
  43. video_capturer_->SetMinCapturePeriod(base::TimeDelta::FromSeconds(1) /
  44. kMaxFrameRate);
  45. video_capturer_->Start(this);
  46. }
  47. void FrameSubscriber::DetachFromHost() {
  48. if (!host_)
  49. return;
  50. video_capturer_.reset();
  51. host_ = nullptr;
  52. }
  53. void FrameSubscriber::RenderViewCreated(content::RenderViewHost* host) {
  54. if (!host_)
  55. AttachToHost(host->GetWidget());
  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. base::ReadOnlySharedMemoryRegion data,
  71. ::media::mojom::VideoFrameInfoPtr info,
  72. const gfx::Rect& content_rect,
  73. viz::mojom::FrameSinkVideoConsumerFrameCallbacksPtr callbacks) {
  74. gfx::Size size = GetRenderViewSize();
  75. if (size != content_rect.size()) {
  76. video_capturer_->SetResolutionConstraints(size, size, true);
  77. video_capturer_->RequestRefreshFrame();
  78. return;
  79. }
  80. if (!data.IsValid()) {
  81. callbacks->Done();
  82. return;
  83. }
  84. base::ReadOnlySharedMemoryMapping mapping = data.Map();
  85. if (!mapping.IsValid()) {
  86. DLOG(ERROR) << "Shared memory mapping failed.";
  87. return;
  88. }
  89. if (mapping.size() <
  90. media::VideoFrame::AllocationSize(info->pixel_format, info->coded_size)) {
  91. DLOG(ERROR) << "Shared memory size was less than expected.";
  92. return;
  93. }
  94. // The SkBitmap's pixels will be marked as immutable, but the installPixels()
  95. // API requires a non-const pointer. So, cast away the const.
  96. void* const pixels = const_cast<void*>(mapping.memory());
  97. // Call installPixels() with a |releaseProc| that: 1) notifies the capturer
  98. // that this consumer has finished with the frame, and 2) releases the shared
  99. // memory mapping.
  100. struct FramePinner {
  101. // Keeps the shared memory that backs |frame_| mapped.
  102. base::ReadOnlySharedMemoryMapping mapping;
  103. // Prevents FrameSinkVideoCapturer from recycling the shared memory that
  104. // backs |frame_|.
  105. viz::mojom::FrameSinkVideoConsumerFrameCallbacksPtr releaser;
  106. };
  107. SkBitmap bitmap;
  108. bitmap.installPixels(
  109. SkImageInfo::MakeN32(content_rect.width(), content_rect.height(),
  110. kPremul_SkAlphaType),
  111. pixels,
  112. media::VideoFrame::RowBytes(media::VideoFrame::kARGBPlane,
  113. info->pixel_format, info->coded_size.width()),
  114. [](void* addr, void* context) {
  115. delete static_cast<FramePinner*>(context);
  116. },
  117. new FramePinner{std::move(mapping), std::move(callbacks)});
  118. bitmap.setImmutable();
  119. Done(content_rect, bitmap);
  120. }
  121. void FrameSubscriber::OnStopped() {}
  122. void FrameSubscriber::Done(const gfx::Rect& damage, const SkBitmap& frame) {
  123. if (frame.drawsNothing())
  124. return;
  125. const SkBitmap& bitmap = only_dirty_ ? SkBitmapOperations::CreateTiledBitmap(
  126. frame, damage.x(), damage.y(),
  127. damage.width(), damage.height())
  128. : frame;
  129. // Copying SkBitmap does not copy the internal pixels, we have to manually
  130. // allocate and write pixels otherwise crash may happen when the original
  131. // frame is modified.
  132. SkBitmap copy;
  133. copy.allocPixels(SkImageInfo::Make(bitmap.width(), bitmap.height(),
  134. kRGBA_8888_SkColorType,
  135. kPremul_SkAlphaType));
  136. SkPixmap pixmap;
  137. bool success = bitmap.peekPixels(&pixmap) && copy.writePixels(pixmap, 0, 0);
  138. CHECK(success);
  139. callback_.Run(gfx::Image::CreateFrom1xBitmap(copy), damage);
  140. }
  141. gfx::Size FrameSubscriber::GetRenderViewSize() const {
  142. content::RenderWidgetHostView* view = host_->GetView();
  143. gfx::Size size = view->GetViewBounds().size();
  144. return gfx::ToRoundedSize(
  145. gfx::ScaleSize(gfx::SizeF(size), view->GetDeviceScaleFactor()));
  146. }
  147. } // namespace api
  148. } // namespace electron