OLD | NEW |
---|---|
(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 | |
OLD | NEW |