blob: 2bd1bbd848cc9bcbb136497462edba99f1718d42 [file] [log] [blame]
Shridhar Sundarraj2af6bca2018-04-12 09:26:201// Copyright 2018 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "content/browser/devtools/devtools_video_consumer.h"
6
Yuri Wiitalafc5fe702018-06-20 06:14:007#include <utility>
8
Sebastien Marchandf8cbfab2019-01-25 16:02:309#include "base/bind.h"
Yuri Wiitala4a74fb02018-08-29 06:09:3510#include "base/memory/shared_memory_mapping.h"
Shridhar Sundarraj2af6bca2018-04-12 09:26:2011#include "cc/paint/skia_paint_canvas.h"
12#include "components/viz/host/host_frame_sink_manager.h"
13#include "components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.h"
14#include "content/browser/compositor/surface_utils.h"
15#include "media/base/limits.h"
Takuto Ikutaa6ea2fa2019-01-31 05:42:3616#include "media/capture/mojom/video_capture_types.mojom.h"
Shridhar Sundarraj2af6bca2018-04-12 09:26:2017#include "media/renderers/paint_canvas_video_renderer.h"
18
19namespace content {
20
21namespace {
22
23// Frame capture period is 10 frames per second by default.
24constexpr base::TimeDelta kDefaultMinCapturePeriod =
25 base::TimeDelta::FromMilliseconds(100);
26
27// Frame size can change every frame.
28constexpr base::TimeDelta kDefaultMinPeriod = base::TimeDelta();
29
30// Allow variable aspect ratio.
31const bool kDefaultUseFixedAspectRatio = false;
32
Saman Samia4c3ee72018-05-25 19:38:0733// Creates a ClientFrameSinkVideoCapturer via HostFrameSinkManager.
34std::unique_ptr<viz::ClientFrameSinkVideoCapturer> CreateCapturer() {
35 return GetHostFrameSinkManager()->CreateVideoCapturer();
36}
37
Shridhar Sundarraj2af6bca2018-04-12 09:26:2038} // namespace
39
40// static
41constexpr gfx::Size DevToolsVideoConsumer::kDefaultMinFrameSize;
42
43// static
44constexpr gfx::Size DevToolsVideoConsumer::kDefaultMaxFrameSize;
45
46DevToolsVideoConsumer::DevToolsVideoConsumer(OnFrameCapturedCallback callback)
47 : callback_(std::move(callback)),
48 min_capture_period_(kDefaultMinCapturePeriod),
49 min_frame_size_(kDefaultMinFrameSize),
Saman Samia4c3ee72018-05-25 19:38:0750 max_frame_size_(kDefaultMaxFrameSize) {}
Shridhar Sundarraj2af6bca2018-04-12 09:26:2051
52DevToolsVideoConsumer::~DevToolsVideoConsumer() = default;
53
54// static
55SkBitmap DevToolsVideoConsumer::GetSkBitmapFromFrame(
56 scoped_refptr<media::VideoFrame> frame) {
57 media::PaintCanvasVideoRenderer renderer;
58 SkBitmap skbitmap;
59 skbitmap.allocN32Pixels(frame->visible_rect().width(),
60 frame->visible_rect().height());
61 cc::SkiaPaintCanvas canvas(skbitmap);
Antoine Labourfee4ae52019-04-24 23:53:5762 renderer.Copy(frame, &canvas, nullptr);
Shridhar Sundarraj2af6bca2018-04-12 09:26:2063 return skbitmap;
64}
65
66void DevToolsVideoConsumer::StartCapture() {
67 if (capturer_)
68 return;
69 InnerStartCapture(CreateCapturer());
70}
71
72void DevToolsVideoConsumer::StopCapture() {
73 if (!capturer_)
74 return;
Shridhar Sundarraj2af6bca2018-04-12 09:26:2075 capturer_.reset();
76}
77
78void DevToolsVideoConsumer::SetFrameSinkId(
79 const viz::FrameSinkId& frame_sink_id) {
80 frame_sink_id_ = frame_sink_id;
Yuri Wiitalafc5fe702018-06-20 06:14:0081 if (capturer_) {
82 if (frame_sink_id_.is_valid())
83 capturer_->ChangeTarget(frame_sink_id_);
84 else
85 capturer_->ChangeTarget(base::nullopt);
86 }
Shridhar Sundarraj2af6bca2018-04-12 09:26:2087}
88
89void DevToolsVideoConsumer::SetMinCapturePeriod(
90 base::TimeDelta min_capture_period) {
91 min_capture_period_ = min_capture_period;
92 if (capturer_)
93 capturer_->SetMinCapturePeriod(min_capture_period_);
94}
95
96void DevToolsVideoConsumer::SetMinAndMaxFrameSize(gfx::Size min_frame_size,
97 gfx::Size max_frame_size) {
98 DCHECK(IsValidMinAndMaxFrameSize(min_frame_size, max_frame_size));
99 min_frame_size_ = min_frame_size;
100 max_frame_size_ = max_frame_size;
101 if (capturer_) {
102 capturer_->SetResolutionConstraints(min_frame_size_, max_frame_size_,
103 kDefaultUseFixedAspectRatio);
104 }
105}
106
Shridhar Sundarraj2af6bca2018-04-12 09:26:20107void DevToolsVideoConsumer::InnerStartCapture(
Saman Samia4c3ee72018-05-25 19:38:07108 std::unique_ptr<viz::ClientFrameSinkVideoCapturer> capturer) {
109 capturer_ = std::move(capturer);
Shridhar Sundarraj2af6bca2018-04-12 09:26:20110
111 // Give |capturer_| the capture parameters.
112 capturer_->SetMinCapturePeriod(min_capture_period_);
113 capturer_->SetMinSizeChangePeriod(kDefaultMinPeriod);
114 capturer_->SetResolutionConstraints(min_frame_size_, max_frame_size_,
115 kDefaultUseFixedAspectRatio);
Yuri Wiitalafc5fe702018-06-20 06:14:00116 if (frame_sink_id_.is_valid())
117 capturer_->ChangeTarget(frame_sink_id_);
Shridhar Sundarraj2af6bca2018-04-12 09:26:20118
Saman Samia4c3ee72018-05-25 19:38:07119 capturer_->Start(this);
Shridhar Sundarraj2af6bca2018-04-12 09:26:20120}
121
122bool DevToolsVideoConsumer::IsValidMinAndMaxFrameSize(
123 gfx::Size min_frame_size,
124 gfx::Size max_frame_size) {
125 // Returns true if
126 // 0 < |min_frame_size| <= |max_frame_size| <= media::limits::kMaxDimension.
127 return 0 < min_frame_size.width() && 0 < min_frame_size.height() &&
128 min_frame_size.width() <= max_frame_size.width() &&
129 min_frame_size.height() <= max_frame_size.height() &&
130 max_frame_size.width() <= media::limits::kMaxDimension &&
131 max_frame_size.height() <= media::limits::kMaxDimension;
132}
133
134void DevToolsVideoConsumer::OnFrameCaptured(
Yuri Wiitala4a74fb02018-08-29 06:09:35135 base::ReadOnlySharedMemoryRegion data,
Shridhar Sundarraj2af6bca2018-04-12 09:26:20136 ::media::mojom::VideoFrameInfoPtr info,
Shridhar Sundarraj2af6bca2018-04-12 09:26:20137 const gfx::Rect& content_rect,
138 viz::mojom::FrameSinkVideoConsumerFrameCallbacksPtr callbacks) {
Yuri Wiitala4a74fb02018-08-29 06:09:35139 if (!data.IsValid())
Shridhar Sundarraj2af6bca2018-04-12 09:26:20140 return;
141
Yuri Wiitala4a74fb02018-08-29 06:09:35142 base::ReadOnlySharedMemoryMapping mapping = data.Map();
143 if (!mapping.IsValid()) {
Shridhar Sundarraj2af6bca2018-04-12 09:26:20144 DLOG(ERROR) << "Shared memory mapping failed.";
145 return;
146 }
Yuri Wiitala4a74fb02018-08-29 06:09:35147 if (mapping.size() <
148 media::VideoFrame::AllocationSize(info->pixel_format, info->coded_size)) {
149 DLOG(ERROR) << "Shared memory size was less than expected.";
Shridhar Sundarraj2af6bca2018-04-12 09:26:20150 return;
Yuri Wiitala4a74fb02018-08-29 06:09:35151 }
152
153 // Create a media::VideoFrame that wraps the read-only shared memory data.
154 // Unfortunately, a deep web of not-const-correct code exists in
155 // media::VideoFrame and media::PaintCanvasVideoRenderer (see
156 // GetSkBitmapFromFrame() above). So, the pointer's const attribute must be
157 // casted away. This is safe since the operating system will page fault if
158 // there is any attempt downstream to mutate the data.
159 //
160 // Setting |frame|'s visible rect equal to |content_rect| so that only the
161 // portion of the frame that contains content is used.
162 scoped_refptr<media::VideoFrame> frame = media::VideoFrame::WrapExternalData(
163 info->pixel_format, info->coded_size, content_rect, content_rect.size(),
164 const_cast<uint8_t*>(static_cast<const uint8_t*>(mapping.memory())),
165 mapping.size(), info->timestamp);
166 if (!frame) {
167 DLOG(ERROR) << "Unable to create VideoFrame wrapper around the shmem.";
168 return;
169 }
Shridhar Sundarraj2af6bca2018-04-12 09:26:20170 frame->AddDestructionObserver(base::BindOnce(
Yuri Wiitala4a74fb02018-08-29 06:09:35171 [](base::ReadOnlySharedMemoryMapping mapping,
172 viz::mojom::FrameSinkVideoConsumerFrameCallbacksPtr callbacks) {},
173 std::move(mapping), std::move(callbacks)));
Oksana Zhuravlova7bc83d1f2018-04-12 21:21:47174 frame->metadata()->MergeInternalValuesFrom(info->metadata);
Christian Fremereyf205b1d2018-10-04 20:14:06175 if (info->color_space.has_value())
176 frame->set_color_space(info->color_space.value());
Shridhar Sundarraj2af6bca2018-04-12 09:26:20177
178 callback_.Run(std::move(frame));
179}
180
Shridhar Sundarraj2af6bca2018-04-12 09:26:20181void DevToolsVideoConsumer::OnStopped() {}
182
183} // namespace content