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

Side by Side Diff: media/video/capture/screen/screen_capture_device.cc

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

Powered by Google App Engine
This is Rietveld 408576698