blob: 8c9bdd44adef82a4003a6b4c928eb217f9cc4878 [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
7#include "cc/paint/skia_paint_canvas.h"
8#include "components/viz/host/host_frame_sink_manager.h"
9#include "components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.h"
10#include "content/browser/compositor/surface_utils.h"
11#include "media/base/limits.h"
12#include "media/renderers/paint_canvas_video_renderer.h"
13
14namespace content {
15
16namespace {
17
18// Frame capture period is 10 frames per second by default.
19constexpr base::TimeDelta kDefaultMinCapturePeriod =
20 base::TimeDelta::FromMilliseconds(100);
21
22// Frame size can change every frame.
23constexpr base::TimeDelta kDefaultMinPeriod = base::TimeDelta();
24
25// Allow variable aspect ratio.
26const bool kDefaultUseFixedAspectRatio = false;
27
28} // namespace
29
30// static
31constexpr gfx::Size DevToolsVideoConsumer::kDefaultMinFrameSize;
32
33// static
34constexpr gfx::Size DevToolsVideoConsumer::kDefaultMaxFrameSize;
35
36DevToolsVideoConsumer::DevToolsVideoConsumer(OnFrameCapturedCallback callback)
37 : callback_(std::move(callback)),
38 min_capture_period_(kDefaultMinCapturePeriod),
39 min_frame_size_(kDefaultMinFrameSize),
40 max_frame_size_(kDefaultMaxFrameSize),
41 binding_(this) {}
42
43DevToolsVideoConsumer::~DevToolsVideoConsumer() = default;
44
45// static
46SkBitmap DevToolsVideoConsumer::GetSkBitmapFromFrame(
47 scoped_refptr<media::VideoFrame> frame) {
48 media::PaintCanvasVideoRenderer renderer;
49 SkBitmap skbitmap;
50 skbitmap.allocN32Pixels(frame->visible_rect().width(),
51 frame->visible_rect().height());
52 cc::SkiaPaintCanvas canvas(skbitmap);
53 renderer.Copy(frame, &canvas, media::Context3D());
54 return skbitmap;
55}
56
57void DevToolsVideoConsumer::StartCapture() {
58 if (capturer_)
59 return;
60 InnerStartCapture(CreateCapturer());
61}
62
63void DevToolsVideoConsumer::StopCapture() {
64 if (!capturer_)
65 return;
66 binding_.Close();
67 capturer_->Stop();
68 capturer_.reset();
69}
70
71void DevToolsVideoConsumer::SetFrameSinkId(
72 const viz::FrameSinkId& frame_sink_id) {
73 frame_sink_id_ = frame_sink_id;
74 if (capturer_)
75 capturer_->ChangeTarget(frame_sink_id_);
76}
77
78void DevToolsVideoConsumer::SetMinCapturePeriod(
79 base::TimeDelta min_capture_period) {
80 min_capture_period_ = min_capture_period;
81 if (capturer_)
82 capturer_->SetMinCapturePeriod(min_capture_period_);
83}
84
85void DevToolsVideoConsumer::SetMinAndMaxFrameSize(gfx::Size min_frame_size,
86 gfx::Size max_frame_size) {
87 DCHECK(IsValidMinAndMaxFrameSize(min_frame_size, max_frame_size));
88 min_frame_size_ = min_frame_size;
89 max_frame_size_ = max_frame_size;
90 if (capturer_) {
91 capturer_->SetResolutionConstraints(min_frame_size_, max_frame_size_,
92 kDefaultUseFixedAspectRatio);
93 }
94}
95
96viz::mojom::FrameSinkVideoCapturerPtrInfo
97DevToolsVideoConsumer::CreateCapturer() {
98 viz::HostFrameSinkManager* const manager = GetHostFrameSinkManager();
99 viz::mojom::FrameSinkVideoCapturerPtr capturer;
100 manager->CreateVideoCapturer(mojo::MakeRequest(&capturer));
101 return capturer.PassInterface();
102}
103
104void DevToolsVideoConsumer::InnerStartCapture(
105 viz::mojom::FrameSinkVideoCapturerPtrInfo capturer_info) {
106 capturer_.Bind(std::move(capturer_info));
107
108 // Give |capturer_| the capture parameters.
109 capturer_->SetMinCapturePeriod(min_capture_period_);
110 capturer_->SetMinSizeChangePeriod(kDefaultMinPeriod);
111 capturer_->SetResolutionConstraints(min_frame_size_, max_frame_size_,
112 kDefaultUseFixedAspectRatio);
113 capturer_->ChangeTarget(frame_sink_id_);
114
115 viz::mojom::FrameSinkVideoConsumerPtr consumer;
116 binding_.Bind(mojo::MakeRequest(&consumer));
117 capturer_->Start(std::move(consumer));
118}
119
120bool DevToolsVideoConsumer::IsValidMinAndMaxFrameSize(
121 gfx::Size min_frame_size,
122 gfx::Size max_frame_size) {
123 // Returns true if
124 // 0 < |min_frame_size| <= |max_frame_size| <= media::limits::kMaxDimension.
125 return 0 < min_frame_size.width() && 0 < min_frame_size.height() &&
126 min_frame_size.width() <= max_frame_size.width() &&
127 min_frame_size.height() <= max_frame_size.height() &&
128 max_frame_size.width() <= media::limits::kMaxDimension &&
129 max_frame_size.height() <= media::limits::kMaxDimension;
130}
131
132void DevToolsVideoConsumer::OnFrameCaptured(
133 mojo::ScopedSharedBufferHandle buffer,
134 uint32_t buffer_size,
135 ::media::mojom::VideoFrameInfoPtr info,
136 const gfx::Rect& update_rect,
137 const gfx::Rect& content_rect,
138 viz::mojom::FrameSinkVideoConsumerFrameCallbacksPtr callbacks) {
139 if (!buffer.is_valid())
140 return;
141
142 mojo::ScopedSharedBufferMapping mapping = buffer->Map(buffer_size);
143 if (!mapping) {
144 DLOG(ERROR) << "Shared memory mapping failed.";
145 return;
146 }
147
148 scoped_refptr<media::VideoFrame> frame;
149 // Setting |frame|'s visible rect equal to |content_rect| so that only the
150 // portion of the frame that contain content are used.
151 frame = media::VideoFrame::WrapExternalData(
152 info->pixel_format, info->coded_size, info->visible_rect,
153 info->visible_rect.size(), static_cast<uint8_t*>(mapping.get()),
154 buffer_size, info->timestamp);
155 if (!frame)
156 return;
157 frame->AddDestructionObserver(base::BindOnce(
158 [](mojo::ScopedSharedBufferMapping mapping) {}, std::move(mapping)));
Oksana Zhuravlova7bc83d1f2018-04-12 21:21:47159 frame->metadata()->MergeInternalValuesFrom(info->metadata);
Shridhar Sundarraj2af6bca2018-04-12 09:26:20160
161 callback_.Run(std::move(frame));
162}
163
164void DevToolsVideoConsumer::OnTargetLost(
165 const viz::FrameSinkId& frame_sink_id) {}
166
167void DevToolsVideoConsumer::OnStopped() {}
168
169} // namespace content