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

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.
Wez 2013/01/31 00:15:29 Drop (c), change to 2013.
Sergey Ulanov 2013/01/31 02:05:43 same as in the previous file.
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
Wez 2013/01/31 00:15:29 nit: No need for // namespace comment if it's a on
Sergey Ulanov 2013/01/31 02:05:43 It's better to have it for consistency.
22
23 class ScreenCaptureDevice::Core
24 : public base::RefCountedThreadSafe<Core>,
25 public ScreenCapturer::Delegate {
26 public:
27 Core(scoped_refptr<base::SequencedTaskRunner> task_runner);
Wez 2013/01/31 00:15:29 explicit
Sergey Ulanov 2013/01/31 02:05:43 Done.
28
29 // Helper used in tests to supply a fake capturer.
30 void set_test_frame_capturer(
Wez 2013/01/31 00:15:29 See comment on public interface re naming.
Sergey Ulanov 2013/01/31 02:05:43 Done.
31 scoped_ptr<ScreenCapturer> capturer) {
32 video_frame_capturer_ = capturer.Pass();
33 }
34
35 void Allocate(int width, int height,
Wez 2013/01/31 00:15:29 nit: Add comment, e.g. "Implementation of VideoCap
Sergey Ulanov 2013/01/31 02:05:43 This is not implementation of the interface. Core
Wez 2013/02/01 00:44:34 No, but these are the methods that actually implem
36 int frame_rate,
37 EventHandler* event_handler);
38 void Start();
39 void Stop();
40 void DeAllocate();
41
42 // ScreenCapturer::Delegate interface. Called by |video_frame_capturer_| on
43 // the |task_runner_|.
44 virtual void OnCaptureCompleted(
45 scoped_refptr<ScreenCaptureData> capture_data) OVERRIDE;
46 virtual void OnCursorShapeChanged(
47 scoped_ptr<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();
Wez 2013/01/31 00:15:29 nit: CaptureFrame() would be a better name for thi
Sergey Ulanov 2013/01/31 02:05:43 IMHO OnCaptureTimer() is better because it makes i
63
64 // Captures a single frame.
65 void DoCapture();
Wez 2013/01/31 00:15:29 nit: I'd prefer that DoFoo() only be used where th
Sergey Ulanov 2013/01/31 02:05:43 Because OnCaptureTimer() also schedules next captu
66
67 // Task runner used for screen capturing operations.
68 scoped_refptr<base::SequencedTaskRunner> task_runner_;
Wez 2013/01/31 00:15:29 nit: capture_task_runner_ would be clearer, then.
Sergey Ulanov 2013/01/31 02:05:43 I don't think it's necessary because there is only
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().
Wez 2013/01/31 00:15:29 nit: in -> to
Sergey Ulanov 2013/01/31 02:05:43 Done.
77 int frame_rate_;
78
79 scoped_ptr<ScreenCapturer> video_frame_capturer_;
Wez 2013/01/31 00:15:29 nit: Please add a comment, e.g. "The underlying Sc
Sergey Ulanov 2013/01/31 02:05:43 Done.
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_;
Wez 2013/01/31 00:15:29 nit: waiting_for_frame_size_ or !have_frame_size_
Sergey Ulanov 2013/01/31 02:05:43 Done.
87 SkISize frame_size_;
88 SkBitmap resized_bitmap_;
89
90 // Set to true between DoStart() and DoStop().
Wez 2013/01/31 00:15:29 nit: I don't think you need "Set to" for this or t
Wez 2013/01/31 00:15:29 Please make clear that we can't just test |event_h
Sergey Ulanov 2013/01/31 02:05:43 Done.
Sergey Ulanov 2013/01/31 02:05:43 Done.
91 bool started_;
92
93 // Set to true when we have delayed OnCaptureTimer() task posted on
94 // |task_runner_|.
95 bool capture_task_posted_;
96
97 // Set to true when waiting for |video_frame_capturer_| to capture current
98 // frame.
99 bool capture_in_progress_;
100
101 DISALLOW_COPY_AND_ASSIGN(Core);
102 };
103
104 ScreenCaptureDevice::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_in_progress_(false) {
112 }
113
114 ScreenCaptureDevice::Core::~Core() {
115 }
116
117 void ScreenCaptureDevice::Core::Allocate(int width, int height,
118 int frame_rate,
Wez 2013/01/31 00:15:29 Indentation.
Sergey Ulanov 2013/01/31 02:05:43 Done.
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 ScreenCaptureDevice::Core::Start() {
134 task_runner_->PostTask(
135 FROM_HERE, base::Bind(&Core::DoStart, this));
136 }
137
138 void ScreenCaptureDevice::Core::Stop() {
139 task_runner_->PostTask(
140 FROM_HERE, base::Bind(&Core::DoStop, this));
141 }
142
143 void ScreenCaptureDevice::Core::DeAllocate() {
144 {
145 base::AutoLock auto_lock(event_handler_lock_);
146 event_handler_ = NULL;
147 }
148 task_runner_->PostTask(FROM_HERE, base::Bind(&Core::DoDeAllocate, this));
149 }
150
151 void ScreenCaptureDevice::Core::OnCaptureCompleted(
152 scoped_refptr<ScreenCaptureData> capture_data) {
153 DCHECK(task_runner_->RunsTasksOnCurrentThread());
154 DCHECK(capture_in_progress_);
155 DCHECK(!capture_data->size().isEmpty());
156 capture_in_progress_ = false;
Wez 2013/01/31 00:15:29 nit: Leave a blank line between the DCHECKs and th
Sergey Ulanov 2013/01/31 02:05:43 Done.
157
158
Wez 2013/01/31 00:15:29 nit: Spurious blank line.
Sergey Ulanov 2013/01/31 02:05:43 Done.
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 VideoCaptureCapability caps;
165 caps.width = frame_size_.width();
166 caps.height = frame_size_.height();
167 caps.frame_rate = frame_rate_;
168 caps.color = 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;
Wez 2013/01/31 00:15:29 Move this before |waiting_frame_size_| check?
Sergey Ulanov 2013/01/31 02:05:43 It's in the right place. We want to call OnFrameIn
180 size_t buffer_size = frame_size_.width() * frame_size_.height() *
181 ScreenCaptureData::kBytesPerPixel;
182
183 if (capture_data->size() == frame_size_) {
184 // If the captured frame matches the requested size, we don't need to
185 // resize it.
186 resized_bitmap_.reset();
187
188 base::AutoLock auto_lock(event_handler_lock_);
189 if (event_handler_) {
190 event_handler_->OnIncomingCapturedFrame(
191 capture_data->data(), buffer_size, base::Time::Now());
192 }
193 return;
194 }
195
196 // In case screen size has changed we need to resize the image. Resized image
Wez 2013/01/31 00:15:29 Scaling to cope with changes in resolution after s
Sergey Ulanov 2013/01/31 02:05:43 Tab capture scales the screen too: https://code.go
197 // is stored to |resized_bitmap_|. Only regions of the screen that are
198 // changing are copied.
199
200 SkRegion dirty_region = capture_data->dirty_region();
201
202 if (resized_bitmap_.width() != frame_size_.width() ||
203 resized_bitmap_.height() != frame_size_.height()) {
204 resized_bitmap_.setConfig(SkBitmap::kARGB_8888_Config,
205 frame_size_.width(), frame_size_.height());
206 resized_bitmap_.setIsOpaque(true);
207 resized_bitmap_.allocPixels();
208 dirty_region.setRect(SkIRect::MakeSize(frame_size_));
209 }
210
211 float scale_x = static_cast<float>(frame_size_.width()) /
212 capture_data->size().width();
213 float scale_y = static_cast<float>(frame_size_.height()) /
214 capture_data->size().height();
215 float scale;
216 float x, y;
217 // Center the image in case aspect ratio is different.
218 if (scale_x > scale_y) {
219 scale = scale_y;
220 x = (scale_x - scale_y) / scale * frame_size_.width() / 2.0;
221 y = 0.f;
222 } else {
223 scale = scale_x;
224 x = 0.f;
225 y = (scale_y - scale_x) / scale * frame_size_.height() / 2.0;
226 }
227
228 // Create skia device and canvas that draw to |resized_bitmap_|.
229 SkDevice device(resized_bitmap_);
230 SkCanvas canvas(&device);
231 canvas.scale(scale, scale);
232
233 int source_stride = capture_data->stride();
234 for (SkRegion::Iterator i(dirty_region); !i.done(); i.next()) {
235 SkBitmap source_bitmap;
236 source_bitmap.setConfig(SkBitmap::kARGB_8888_Config,
237 i.rect().width(), i.rect().height(),
238 source_stride);
239 source_bitmap.setIsOpaque(true);
240 source_bitmap.setPixels(
241 capture_data->data() + i.rect().top() * source_stride +
242 i.rect().left() * ScreenCaptureData::kBytesPerPixel);
243 canvas.drawBitmap(source_bitmap, i.rect().left() + x / scale,
244 i.rect().top() + y / scale, NULL);
245 }
246
247 base::AutoLock auto_lock(event_handler_lock_);
248 if (event_handler_) {
249 event_handler_->OnIncomingCapturedFrame(
250 reinterpret_cast<uint8*>(resized_bitmap_.getPixels()), buffer_size,
251 base::Time::Now());
252 }
253 }
254
255 void ScreenCaptureDevice::Core::OnCursorShapeChanged(
256 scoped_ptr<MouseCursorShape> cursor_shape) {
257 DCHECK(task_runner_->RunsTasksOnCurrentThread());
Wez 2013/01/31 00:15:29 Please add a TODO (and file a bug) for storing the
Sergey Ulanov 2013/01/31 02:05:43 Filed crbug.com/173265
258 }
259
260 void ScreenCaptureDevice::Core::DoAllocate(int frame_rate) {
261 DCHECK(task_runner_->RunsTasksOnCurrentThread());
262
263 frame_rate_ = frame_rate;
264
265 // Create and start frame capturer.
266 if (!video_frame_capturer_)
267 video_frame_capturer_ = ScreenCapturer::Create();
268 if (video_frame_capturer_)
269 video_frame_capturer_->Start(this);
Wez 2013/01/31 00:15:29 Why are we not Start()ing and Stop()ing in DoStart
Sergey Ulanov 2013/01/31 02:05:43 because we need to capture first frame to get scre
Wez 2013/02/01 00:44:34 So there is. Can we post a delayed task to Stop()
Wez 2013/02/01 00:44:34 Good point! Can we post a task here to Stop the u
Sergey Ulanov 2013/02/01 02:02:26 We could, but it won't make any difference. Stop()
Wez 2013/02/02 00:19:56 SGTM.
270
271 // Capture first frame, so that we can call OnFrameInfo() callback.
272 waiting_frame_size_ = true;
273 DoCapture();
274 }
275
276 void ScreenCaptureDevice::Core::DoStart() {
277 DCHECK(task_runner_->RunsTasksOnCurrentThread());
278 started_ = true;
279 if (!capture_task_posted_) {
280 capture_task_posted_ = true;
281 task_runner_->PostDelayedTask(
282 FROM_HERE, base::Bind(&Core::OnCaptureTimer, this),
283 base::TimeDelta::FromSeconds(1) / frame_rate_);
Wez 2013/01/31 00:15:29 Why post a task here rather than triggering a capt
Sergey Ulanov 2013/01/31 02:05:43 Done.
284 }
285 }
286
287 void ScreenCaptureDevice::Core::DoStop() {
288 DCHECK(task_runner_->RunsTasksOnCurrentThread());
289 started_ = false;
290 resized_bitmap_.reset();
291 }
292
293 void ScreenCaptureDevice::Core::DoDeAllocate() {
294 DCHECK(task_runner_->RunsTasksOnCurrentThread());
295 DoStop();
296 if (video_frame_capturer_) {
297 video_frame_capturer_->Stop();
298 video_frame_capturer_.reset();
299 }
300 waiting_frame_size_ = false;
301 }
302
303 void ScreenCaptureDevice::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_) {
Wez 2013/01/31 00:15:29 If you always have the frame size populated by a s
Sergey Ulanov 2013/01/31 02:05:43 Done.
314 return;
315 }
316
317 DoCapture();
318 }
319
320 void ScreenCaptureDevice::Core::DoCapture() {
321 DCHECK(!capture_in_progress_);
Wez 2013/01/31 00:15:29 DCHECK(started_ || waiting_frame_size_)?
Sergey Ulanov 2013/01/31 02:05:43 I don't think it's necessary to check it here.
322
323 capture_in_progress_ = true;
324 video_frame_capturer_->CaptureFrame();
325
326 // Assume that ScreenCapturer always calls OnCaptureCompleted()
327 // callback before it returns.
328 //
329 // TODO(sergeyu): Fix ScreenCapturer to return video frame
330 // synchronously instead of using Delegate interface.
331 DCHECK(!capture_in_progress_);
332 }
333
334 ScreenCaptureDevice::ScreenCaptureDevice(
335 scoped_refptr<base::SequencedTaskRunner> task_runner)
336 : core_(new Core(task_runner)) {
337 name_.device_name = "Screen";
338 }
339
340 ScreenCaptureDevice::~ScreenCaptureDevice() {
341 DeAllocate();
342 }
343
344 void ScreenCaptureDevice::set_test_screen_capturer(
345 scoped_ptr<ScreenCapturer> capturer) {
346 core_->set_test_frame_capturer(capturer.Pass());
347 }
348
349 void ScreenCaptureDevice::Allocate(int width, int height,
350 int frame_rate,
351 EventHandler* event_handler) {
352 core_->Allocate(width, height, frame_rate, event_handler);
353 }
354
355 void ScreenCaptureDevice::Start() {
356 core_->Start();
357 }
358
359 void ScreenCaptureDevice::Stop() {
360 core_->Stop();
361 }
362
363 void ScreenCaptureDevice::DeAllocate() {
364 core_->DeAllocate();
365 }
366
367 const VideoCaptureDevice::Name& ScreenCaptureDevice::device_name() {
368 return name_;
369 }
370
371 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698