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