Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(483)

Side by Side Diff: content/browser/renderer_host/media/screen_capturer.cc

Issue 11680002: Implement screen capturer for MediaStream API. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 7 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2012 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/renderer_host/media/screen_capturer.h"
6
7 #include "base/bind.h"
8 #include "base/location.h"
9 #include "base/logging.h"
10 #include "base/sequenced_task_runner.h"
11 #include "base/synchronization/lock.h"
12 #include "remoting/capturer/capture_data.h"
13 #include "remoting/capturer/mouse_cursor_shape.h"
14 #include "third_party/skia/include/core/SkCanvas.h"
15 #include "third_party/skia/include/core/SkDevice.h"
16
17 namespace content {
18
19 namespace {
20 const int kBytesPerPixel = 4;
21 } // namespace
22
23 class ScreenCapturer::Core
24 : public base::RefCountedThreadSafe<Core>,
25 public remoting::VideoFrameCapturer::Delegate {
26 public:
27 Core(scoped_refptr<base::SequencedTaskRunner> task_runner);
28
29 // Helper used in tests to supply a fake capturer.
30 void set_test_frame_capturer(
31 scoped_ptr<remoting::VideoFrameCapturer> capturer) {
32 video_frame_capturer_ = capturer.Pass();
33 }
34
35 void Allocate(int width, int height,
36 int frame_rate,
37 EventHandler* event_handler);
38 void Start();
39 void Stop();
40 void DeAllocate();
41
42 // VideoFrameCapturer::Delegate interface. Called by |video_frame_capturer_|
43 // on the |task_runner_|.
44 virtual void OnCaptureCompleted(
45 scoped_refptr<remoting::CaptureData> capture_data) OVERRIDE;
46 virtual void OnCursorShapeChanged(
47 scoped_ptr<remoting::MouseCursorShape> cursor_shape) OVERRIDE;
48
49 private:
50 friend class base::RefCountedThreadSafe<Core>;
51 virtual ~Core();
52
53 // Helper methods that run on the |task_runner_|. Posted from the
54 // corresponding public methods.
55 void DoAllocate(int frame_rate);
56 void DoStart();
57 void DoStop();
58 void DoDeAllocate();
59
60 // Method that is scheduled on |task_runner_| to be called on regular interval
61 // to capture the screen.
62 void OnCaptureTimer();
63
64 // Captures a single frame.
65 void DoCapture();
66
67 // Task runner used for screen capturing operations.
68 scoped_refptr<base::SequencedTaskRunner> task_runner_;
69
70 // |event_handler_lock_| must be locked whenever |event_handler_| is used.
71 // It's necessary because DeAllocate() needs to reset it on the calling thread
72 // to ensure that the event handler is not called once DeAllocate() returns.
73 base::Lock event_handler_lock_;
74 EventHandler* event_handler_;
75
76 // Frame rate specified in Allocate().
77 int frame_rate_;
78
79 scoped_ptr<remoting::VideoFrameCapturer> video_frame_capturer_;
80
81 // After Allocate() is called we need to call OnFrameInfo() method of the
82 // |event_handler_| to specify the size of the frames this capturer will
83 // produce. In order to get screen size from |video_frame_capturer_| we need
84 // to capture at least one frame. Once screen size is known it's stored in
85 // |frame_size_|.
86 bool waiting_frame_size_;
87 SkISize frame_size_;
88 SkBitmap resized_bitmap_;
89
90 // Set to true between DoStart() and DoStop().
91 bool started_;
92
93 // Set to true when we have delayed OnCaptureTimer() task posted on
94 // |task_runner_|.
95 bool capture_task_posted_;
Wez 2013/01/15 06:13:27 nit: capture_pending_
Sergey Ulanov 2013/01/15 19:29:18 IMO capture_task_poster_ better reflects what this
96
97 // Set to true when waiting for |video_frame_capturer_| to capture current
98 // frame.
99 bool capture_pending_;
Wez 2013/01/15 06:13:27 nit: capture_in_progress_
Sergey Ulanov 2013/01/15 19:29:18 Done.
100
101 DISALLOW_COPY_AND_ASSIGN(Core);
102 };
103
104 ScreenCapturer::Core::Core(
105 scoped_refptr<base::SequencedTaskRunner> task_runner)
106 : task_runner_(task_runner),
107 event_handler_(NULL),
108 waiting_frame_size_(false),
109 started_(false),
110 capture_task_posted_(false),
111 capture_pending_(false) {
112 }
113
114 ScreenCapturer::Core::~Core() {
115 }
116
117 void ScreenCapturer::Core::Allocate(int width, int height,
118 int frame_rate,
119 EventHandler* event_handler) {
120 DCHECK_GT(width, 0);
121 DCHECK_GT(height, 0);
122 DCHECK_GT(frame_rate, 0);
123
124 {
125 base::AutoLock auto_lock(event_handler_lock_);
126 event_handler_ = event_handler;
127 }
128
129 task_runner_->PostTask(
130 FROM_HERE, base::Bind(&Core::DoAllocate, this, frame_rate));
131 }
132
133 void ScreenCapturer::Core::Start() {
134 task_runner_->PostTask(
135 FROM_HERE, base::Bind(&Core::DoStart, this));
136 }
137
138 void ScreenCapturer::Core::Stop() {
139 task_runner_->PostTask(
140 FROM_HERE, base::Bind(&Core::DoStop, this));
141 }
142
143 void ScreenCapturer::Core::DeAllocate() {
144 {
145 base::AutoLock auto_lock(event_handler_lock_);
Wez 2013/01/15 06:13:27 This will block if the capture thread is mid-captu
Sergey Ulanov 2013/01/15 19:29:18 The lock is not acquired during screen capture - i
Wez 2013/01/16 21:26:45 Agreed, but I think we should try to resolve this
146 event_handler_ = NULL;
147 }
148 task_runner_->PostTask(FROM_HERE, base::Bind(&Core::DoDeAllocate, this));
149 }
150
151 void ScreenCapturer::Core::OnCaptureCompleted(
152 scoped_refptr<remoting::CaptureData> capture_data) {
153 DCHECK(task_runner_->RunsTasksOnCurrentThread());
154 DCHECK(capture_pending_);
155 DCHECK(!capture_data->size().isEmpty());
156 capture_pending_ = false;
157
158
159 if (waiting_frame_size_) {
160 frame_size_ = capture_data->size();
161 waiting_frame_size_ = false;
162
163 // Inform the EventHandler of the video frame dimensions and format.
164 media::VideoCaptureCapability caps;
165 caps.width = frame_size_.width();
166 caps.height = frame_size_.height();
167 caps.frame_rate = frame_rate_;
168 caps.color = media::VideoCaptureCapability::kARGB;
169 caps.expected_capture_delay =
170 base::Time::kMillisecondsPerSecond / frame_rate_;
171 caps.interlaced = false;
172
173 base::AutoLock auto_lock(event_handler_lock_);
174 if (event_handler_)
175 event_handler_->OnFrameInfo(caps);
176 }
177
178 if (!started_)
179 return;
180 size_t buffer_size =
181 frame_size_.width() * frame_size_.height() *
182 remoting::CaptureData::kBytesPerPixel;
183
184 if (capture_data->size() == frame_size_) {
185 // If the captured frame matches the requested size, we don't need to
186 // resize it.
187 resized_bitmap_.reset();
188
189 base::AutoLock auto_lock(event_handler_lock_);
190 if (event_handler_) {
191 event_handler_->OnIncomingCapturedFrame(
192 capture_data->data(), buffer_size, base::Time::Now());
193 }
194 return;
195 }
196
197 // In case screen size has changed we need to resize the image. Resized image
198 // is stored to |resized_bitmap_|. Only regions of the screen that are
199 // changing are copied.
200
201 SkRegion dirty_region = capture_data->dirty_region();
202
203 if (resized_bitmap_.width() != frame_size_.width() ||
204 resized_bitmap_.height() != frame_size_.height()) {
205 resized_bitmap_.setConfig(SkBitmap::kARGB_8888_Config,
206 frame_size_.width(), frame_size_.height());
207 resized_bitmap_.setIsOpaque(true);
208 resized_bitmap_.allocPixels();
209 dirty_region.setRect(SkIRect::MakeSize(frame_size_));
210 }
211
212 float scale_x = static_cast<float>(frame_size_.width()) /
213 capture_data->size().width();
214 float scale_y = static_cast<float>(frame_size_.height()) /
215 capture_data->size().height();
216 float scale;
217 float x, y;
218 // Center the image in case aspect ratio is different.
219 if (scale_x > scale_y) {
220 scale = scale_y;
221 x = (scale_x - scale_y) / scale * frame_size_.width() / 2.0;
222 y = 0.f;
223 } else {
224 scale = scale_x;
225 x = 0.f;
226 y = (scale_y - scale_x) / scale * frame_size_.height() / 2.0;
227 }
228
229 // Create skia device and canvas that draw to |resized_bitmap_|.
230 SkDevice device(resized_bitmap_);
231 SkCanvas canvas(&device);
232 canvas.scale(scale, scale);
233
234 int source_stride = capture_data->stride();
235 for (SkRegion::Iterator i(dirty_region); !i.done(); i.next()) {
236 SkBitmap source_bitmap;
237 source_bitmap.setConfig(SkBitmap::kARGB_8888_Config,
238 i.rect().width(), i.rect().height(),
239 source_stride);
240 source_bitmap.setIsOpaque(true);
241 source_bitmap.setPixels(
242 capture_data->data() + i.rect().top() * source_stride +
243 i.rect().left() * remoting::CaptureData::kBytesPerPixel);
244 canvas.drawBitmap(source_bitmap, i.rect().left() + x / scale,
245 i.rect().top() + y / scale, NULL);
246 }
247
248 base::AutoLock auto_lock(event_handler_lock_);
249 if (event_handler_) {
250 event_handler_->OnIncomingCapturedFrame(
251 reinterpret_cast<uint8*>(resized_bitmap_.getPixels()), buffer_size,
252 base::Time::Now());
253 }
254 }
255
256 void ScreenCapturer::Core::OnCursorShapeChanged(
257 scoped_ptr<remoting::MouseCursorShape> cursor_shape) {
258 DCHECK(task_runner_->RunsTasksOnCurrentThread());
259 }
260
261 void ScreenCapturer::Core::DoAllocate(int frame_rate) {
262 DCHECK(task_runner_->RunsTasksOnCurrentThread());
263
264 frame_rate_ = frame_rate;
265
266 // Create and start frame capturer.
267 if (!video_frame_capturer_)
268 video_frame_capturer_ = remoting::VideoFrameCapturer::Create();
269 if (video_frame_capturer_)
270 video_frame_capturer_->Start(this);
271
272 // Capture first frame, so that we can call OnFrameInfo() callback.
273 waiting_frame_size_ = true;
274 DoCapture();
275 }
276
277 void ScreenCapturer::Core::DoStart() {
278 DCHECK(task_runner_->RunsTasksOnCurrentThread());
279 started_ = true;
280 if (!capture_task_posted_) {
281 capture_task_posted_ = true;
282 task_runner_->PostDelayedTask(
283 FROM_HERE, base::Bind(&Core::OnCaptureTimer, this),
284 base::TimeDelta::FromSeconds(1) / frame_rate_);
285 }
286 }
287
288 void ScreenCapturer::Core::DoStop() {
289 DCHECK(task_runner_->RunsTasksOnCurrentThread());
290 started_ = false;
291 if (video_frame_capturer_)
292 video_frame_capturer_->Stop();
293 resized_bitmap_.reset();
294 }
295
296 void ScreenCapturer::Core::DoDeAllocate() {
297 DCHECK(task_runner_->RunsTasksOnCurrentThread());
298 DoStop();
299 video_frame_capturer_.reset();
300 waiting_frame_size_ = false;
301 }
302
303 void ScreenCapturer::Core::OnCaptureTimer() {
304 DCHECK(capture_task_posted_);
305 capture_task_posted_ = false;
306
307 if (started_) {
308 // Schedule a task for the next frame.
309 capture_task_posted_ = true;
310 task_runner_->PostDelayedTask(
311 FROM_HERE, base::Bind(&Core::OnCaptureTimer, this),
312 base::TimeDelta::FromSeconds(1) / frame_rate_);
313 } else if (!waiting_frame_size_) {
314 return;
315 }
316
317 DoCapture();
318 }
319
320 void ScreenCapturer::Core::DoCapture() {
321 DCHECK(!capture_pending_);
322
323 capture_pending_ = true;
324 video_frame_capturer_->CaptureFrame();
325
326 // Currently remoting::VideoFrameCapturer always calls OnCaptureCompleted()
327 // and we rely on it.
Wez 2013/01/15 06:13:27 This comment is a little weird; the current interf
Sergey Ulanov 2013/01/15 19:29:18 We do rely on the fact that OnCaptureCompleted() i
Wez 2013/01/16 21:26:45 So what the comment should say is that we assume t
Sergey Ulanov 2013/01/17 02:21:17 Done.
328 //
329 // TODO(sergeyu): Fix remoting::VideoFrameCapturer to return video frame
330 // synchronously instead of using Delegate interface.
331 DCHECK(!capture_pending_);
332 }
333
334 ScreenCapturer::ScreenCapturer(
335 scoped_refptr<base::SequencedTaskRunner> task_runner)
336 : core_(new Core(task_runner)) {
337 name_.device_name = "Screen";
338 }
339
340 ScreenCapturer::~ScreenCapturer() {
341 core_->DeAllocate();
Wez 2013/01/15 06:13:27 nit: Call this->DeAllocate() here in case the impl
Sergey Ulanov 2013/01/15 19:29:18 Done.
342 }
343
344 void ScreenCapturer::set_test_frame_capturer(
345 scoped_ptr<remoting::VideoFrameCapturer> capturer) {
346 core_->set_test_frame_capturer(capturer.Pass());
347 }
348
349 void ScreenCapturer::Allocate(int width, int height,
350 int frame_rate,
351 EventHandler* event_handler) {
352 core_->Allocate(width, height, frame_rate, event_handler);
Wez 2013/01/15 06:13:27 Can you PostTask() from here directly to ScreenCap
Sergey Ulanov 2013/01/15 19:29:18 DeAllocate() needs to reset event_handler_ on the
353 }
354
355 void ScreenCapturer::Start() {
356 core_->Start();
357 }
358
359 void ScreenCapturer::Stop() {
360 core_->Stop();
361 }
362
363 void ScreenCapturer::DeAllocate() {
364 core_->DeAllocate();
365 }
366
367 const media::VideoCaptureDevice::Name& ScreenCapturer::device_name() {
368 return name_;
369 }
370
371 } // namespace content
OLDNEW
« no previous file with comments | « content/browser/renderer_host/media/screen_capturer.h ('k') | content/browser/renderer_host/media/screen_capturer_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698