OLD | NEW |
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "media/video/capture/screen/screen_capture_device.h" | 5 #include "media/video/capture/screen/screen_capture_device.h" |
6 | 6 |
7 #include "base/bind.h" | 7 #include "base/bind.h" |
8 #include "base/location.h" | 8 #include "base/location.h" |
9 #include "base/logging.h" | 9 #include "base/logging.h" |
10 #include "base/sequenced_task_runner.h" | 10 #include "base/sequenced_task_runner.h" |
11 #include "base/synchronization/lock.h" | 11 #include "base/synchronization/lock.h" |
12 #include "media/video/capture/screen/mouse_cursor_shape.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" | 13 #include "third_party/skia/include/core/SkCanvas.h" |
15 #include "third_party/skia/include/core/SkDevice.h" | 14 #include "third_party/skia/include/core/SkDevice.h" |
| 15 #include "third_party/webrtc/modules/desktop_capture/desktop_frame.h" |
16 | 16 |
17 namespace media { | 17 namespace media { |
18 | 18 |
19 namespace { | 19 namespace { |
20 const int kBytesPerPixel = 4; | 20 const int kBytesPerPixel = 4; |
21 } // namespace | 21 } // namespace |
22 | 22 |
23 class ScreenCaptureDevice::Core | 23 class ScreenCaptureDevice::Core |
24 : public base::RefCountedThreadSafe<Core>, | 24 : public base::RefCountedThreadSafe<Core>, |
25 public ScreenCapturer::Delegate { | 25 public webrtc::DesktopCapturer::Callback { |
26 public: | 26 public: |
27 explicit Core(scoped_refptr<base::SequencedTaskRunner> task_runner); | 27 explicit Core(scoped_refptr<base::SequencedTaskRunner> task_runner); |
28 | 28 |
29 // Helper used in tests to supply a fake capturer. | 29 // Helper used in tests to supply a fake capturer. |
30 void SetScreenCapturerForTest(scoped_ptr<ScreenCapturer> capturer) { | 30 void SetScreenCapturerForTest(scoped_ptr<ScreenCapturer> capturer) { |
31 screen_capturer_ = capturer.Pass(); | 31 screen_capturer_ = capturer.Pass(); |
32 } | 32 } |
33 | 33 |
34 // Implementation of VideoCaptureDevice methods. | 34 // Implementation of VideoCaptureDevice methods. |
35 void Allocate(int width, int height, | 35 void Allocate(int width, int height, |
36 int frame_rate, | 36 int frame_rate, |
37 EventHandler* event_handler); | 37 EventHandler* event_handler); |
38 void Start(); | 38 void Start(); |
39 void Stop(); | 39 void Stop(); |
40 void DeAllocate(); | 40 void DeAllocate(); |
41 | 41 |
42 // ScreenCapturer::Delegate interface. Called by |screen_capturer_| on the | |
43 // |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: | 42 private: |
50 friend class base::RefCountedThreadSafe<Core>; | 43 friend class base::RefCountedThreadSafe<Core>; |
51 virtual ~Core(); | 44 virtual ~Core(); |
52 | 45 |
| 46 // webrtc::DesktopCapturer::Callback interface |
| 47 virtual webrtc::SharedMemory* CreateSharedMemory(size_t size) OVERRIDE; |
| 48 virtual void OnCaptureCompleted(webrtc::DesktopFrame* frame) OVERRIDE; |
| 49 |
53 // Helper methods that run on the |task_runner_|. Posted from the | 50 // Helper methods that run on the |task_runner_|. Posted from the |
54 // corresponding public methods. | 51 // corresponding public methods. |
55 void DoAllocate(int frame_rate); | 52 void DoAllocate(int frame_rate); |
56 void DoStart(); | 53 void DoStart(); |
57 void DoStop(); | 54 void DoStop(); |
58 void DoDeAllocate(); | 55 void DoDeAllocate(); |
59 | 56 |
60 // Helper to schedule capture tasks. | 57 // Helper to schedule capture tasks. |
61 void ScheduleCaptureTimer(); | 58 void ScheduleCaptureTimer(); |
62 | 59 |
(...skipping 18 matching lines...) Expand all Loading... |
81 | 78 |
82 // The underlying ScreenCapturer instance used to capture frames. | 79 // The underlying ScreenCapturer instance used to capture frames. |
83 scoped_ptr<ScreenCapturer> screen_capturer_; | 80 scoped_ptr<ScreenCapturer> screen_capturer_; |
84 | 81 |
85 // After Allocate() is called we need to call OnFrameInfo() method of the | 82 // After Allocate() is called we need to call OnFrameInfo() method of the |
86 // |event_handler_| to specify the size of the frames this capturer will | 83 // |event_handler_| to specify the size of the frames this capturer will |
87 // produce. In order to get screen size from |screen_capturer_| we need to | 84 // produce. In order to get screen size from |screen_capturer_| we need to |
88 // capture at least one frame. Once screen size is known it's stored in | 85 // capture at least one frame. Once screen size is known it's stored in |
89 // |frame_size_|. | 86 // |frame_size_|. |
90 bool waiting_for_frame_size_; | 87 bool waiting_for_frame_size_; |
91 SkISize frame_size_; | 88 webrtc::DesktopSize frame_size_; |
92 SkBitmap resized_bitmap_; | 89 SkBitmap resized_bitmap_; |
93 | 90 |
94 // True between DoStart() and DoStop(). Can't just check |event_handler_| | 91 // True between DoStart() and DoStop(). Can't just check |event_handler_| |
95 // because |event_handler_| is used on the caller thread. | 92 // because |event_handler_| is used on the caller thread. |
96 bool started_; | 93 bool started_; |
97 | 94 |
98 // True when we have delayed OnCaptureTimer() task posted on | 95 // True when we have delayed OnCaptureTimer() task posted on |
99 // |task_runner_|. | 96 // |task_runner_|. |
100 bool capture_task_posted_; | 97 bool capture_task_posted_; |
101 | 98 |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
145 } | 142 } |
146 | 143 |
147 void ScreenCaptureDevice::Core::DeAllocate() { | 144 void ScreenCaptureDevice::Core::DeAllocate() { |
148 { | 145 { |
149 base::AutoLock auto_lock(event_handler_lock_); | 146 base::AutoLock auto_lock(event_handler_lock_); |
150 event_handler_ = NULL; | 147 event_handler_ = NULL; |
151 } | 148 } |
152 task_runner_->PostTask(FROM_HERE, base::Bind(&Core::DoDeAllocate, this)); | 149 task_runner_->PostTask(FROM_HERE, base::Bind(&Core::DoDeAllocate, this)); |
153 } | 150 } |
154 | 151 |
| 152 webrtc::SharedMemory* |
| 153 ScreenCaptureDevice::Core::CreateSharedMemory(size_t size) { |
| 154 return NULL; |
| 155 } |
| 156 |
155 void ScreenCaptureDevice::Core::OnCaptureCompleted( | 157 void ScreenCaptureDevice::Core::OnCaptureCompleted( |
156 scoped_refptr<ScreenCaptureData> capture_data) { | 158 webrtc::DesktopFrame* frame) { |
157 DCHECK(task_runner_->RunsTasksOnCurrentThread()); | 159 DCHECK(task_runner_->RunsTasksOnCurrentThread()); |
158 DCHECK(capture_in_progress_); | 160 DCHECK(capture_in_progress_); |
159 DCHECK(!capture_data->size().isEmpty()); | |
160 | 161 |
161 capture_in_progress_ = false; | 162 capture_in_progress_ = false; |
162 | 163 |
| 164 if (!frame) { |
| 165 LOG(ERROR) << "Failed to capture a frame."; |
| 166 event_handler_->OnError(); |
| 167 return; |
| 168 } |
| 169 |
| 170 scoped_ptr<webrtc::DesktopFrame> owned_frame(frame); |
| 171 |
163 if (waiting_for_frame_size_) { | 172 if (waiting_for_frame_size_) { |
164 frame_size_ = capture_data->size(); | 173 frame_size_ = frame->size(); |
165 waiting_for_frame_size_ = false; | 174 waiting_for_frame_size_ = false; |
166 | 175 |
167 // Inform the EventHandler of the video frame dimensions and format. | 176 // Inform the EventHandler of the video frame dimensions and format. |
168 VideoCaptureCapability caps; | 177 VideoCaptureCapability caps; |
169 caps.width = frame_size_.width(); | 178 caps.width = frame_size_.width(); |
170 caps.height = frame_size_.height(); | 179 caps.height = frame_size_.height(); |
171 caps.frame_rate = frame_rate_; | 180 caps.frame_rate = frame_rate_; |
172 caps.color = VideoCaptureCapability::kARGB; | 181 caps.color = VideoCaptureCapability::kARGB; |
173 caps.expected_capture_delay = | 182 caps.expected_capture_delay = |
174 base::Time::kMillisecondsPerSecond / frame_rate_; | 183 base::Time::kMillisecondsPerSecond / frame_rate_; |
175 caps.interlaced = false; | 184 caps.interlaced = false; |
176 | 185 |
177 base::AutoLock auto_lock(event_handler_lock_); | 186 base::AutoLock auto_lock(event_handler_lock_); |
178 if (event_handler_) | 187 if (event_handler_) |
179 event_handler_->OnFrameInfo(caps); | 188 event_handler_->OnFrameInfo(caps); |
180 } | 189 } |
181 | 190 |
182 if (!started_) | 191 if (!started_) |
183 return; | 192 return; |
184 | 193 |
185 size_t buffer_size = frame_size_.width() * frame_size_.height() * | 194 size_t buffer_size = frame_size_.width() * frame_size_.height() * |
186 ScreenCaptureData::kBytesPerPixel; | 195 webrtc::DesktopFrame::kBytesPerPixel; |
187 | 196 |
188 if (capture_data->size() == frame_size_) { | 197 if (frame->size().equals(frame_size_)) { |
189 // If the captured frame matches the requested size, we don't need to | 198 // If the captured frame matches the requested size, we don't need to |
190 // resize it. | 199 // resize it. |
191 resized_bitmap_.reset(); | 200 resized_bitmap_.reset(); |
192 | 201 |
193 base::AutoLock auto_lock(event_handler_lock_); | 202 base::AutoLock auto_lock(event_handler_lock_); |
194 if (event_handler_) { | 203 if (event_handler_) { |
195 event_handler_->OnIncomingCapturedFrame( | 204 event_handler_->OnIncomingCapturedFrame( |
196 capture_data->data(), buffer_size, base::Time::Now(), | 205 frame->data(), buffer_size, base::Time::Now(), 0, false, false); |
197 0, false, false); | |
198 } | 206 } |
199 return; | 207 return; |
200 } | 208 } |
201 | 209 |
202 // In case screen size has changed we need to resize the image. Resized image | 210 // In case screen size has changed we need to resize the image. Resized image |
203 // is stored to |resized_bitmap_|. Only regions of the screen that are | 211 // is stored to |resized_bitmap_|. Only regions of the screen that are |
204 // changing are copied. | 212 // changing are copied. |
205 | 213 |
206 SkRegion dirty_region = capture_data->dirty_region(); | 214 webrtc::DesktopRegion dirty_region = frame->updated_region(); |
207 | 215 |
208 if (resized_bitmap_.width() != frame_size_.width() || | 216 if (resized_bitmap_.width() != frame_size_.width() || |
209 resized_bitmap_.height() != frame_size_.height()) { | 217 resized_bitmap_.height() != frame_size_.height()) { |
210 resized_bitmap_.setConfig(SkBitmap::kARGB_8888_Config, | 218 resized_bitmap_.setConfig(SkBitmap::kARGB_8888_Config, |
211 frame_size_.width(), frame_size_.height()); | 219 frame_size_.width(), frame_size_.height()); |
212 resized_bitmap_.setIsOpaque(true); | 220 resized_bitmap_.setIsOpaque(true); |
213 resized_bitmap_.allocPixels(); | 221 resized_bitmap_.allocPixels(); |
214 dirty_region.setRect(SkIRect::MakeSize(frame_size_)); | 222 dirty_region.SetRect(webrtc::DesktopRect::MakeSize(frame_size_)); |
215 } | 223 } |
216 | 224 |
217 float scale_x = static_cast<float>(frame_size_.width()) / | 225 float scale_x = static_cast<float>(frame_size_.width()) / |
218 capture_data->size().width(); | 226 frame->size().width(); |
219 float scale_y = static_cast<float>(frame_size_.height()) / | 227 float scale_y = static_cast<float>(frame_size_.height()) / |
220 capture_data->size().height(); | 228 frame->size().height(); |
221 float scale; | 229 float scale; |
222 float x, y; | 230 float x, y; |
223 // Center the image in case aspect ratio is different. | 231 // Center the image in case aspect ratio is different. |
224 if (scale_x > scale_y) { | 232 if (scale_x > scale_y) { |
225 scale = scale_y; | 233 scale = scale_y; |
226 x = (scale_x - scale_y) / scale * frame_size_.width() / 2.0; | 234 x = (scale_x - scale_y) / scale * frame_size_.width() / 2.0; |
227 y = 0.f; | 235 y = 0.f; |
228 } else { | 236 } else { |
229 scale = scale_x; | 237 scale = scale_x; |
230 x = 0.f; | 238 x = 0.f; |
231 y = (scale_y - scale_x) / scale * frame_size_.height() / 2.0; | 239 y = (scale_y - scale_x) / scale * frame_size_.height() / 2.0; |
232 } | 240 } |
233 | 241 |
234 // Create skia device and canvas that draw to |resized_bitmap_|. | 242 // Create skia device and canvas that draw to |resized_bitmap_|. |
235 SkDevice device(resized_bitmap_); | 243 SkDevice device(resized_bitmap_); |
236 SkCanvas canvas(&device); | 244 SkCanvas canvas(&device); |
237 canvas.scale(scale, scale); | 245 canvas.scale(scale, scale); |
238 | 246 |
239 int source_stride = capture_data->stride(); | 247 int source_stride = frame->stride(); |
240 for (SkRegion::Iterator i(dirty_region); !i.done(); i.next()) { | 248 for (webrtc::DesktopRegion::Iterator i(dirty_region); !i.IsAtEnd(); |
| 249 i.Advance()) { |
241 SkBitmap source_bitmap; | 250 SkBitmap source_bitmap; |
242 source_bitmap.setConfig(SkBitmap::kARGB_8888_Config, | 251 source_bitmap.setConfig(SkBitmap::kARGB_8888_Config, |
243 i.rect().width(), i.rect().height(), | 252 i.rect().width(), i.rect().height(), |
244 source_stride); | 253 source_stride); |
245 source_bitmap.setIsOpaque(true); | 254 source_bitmap.setIsOpaque(true); |
246 source_bitmap.setPixels( | 255 source_bitmap.setPixels( |
247 capture_data->data() + i.rect().top() * source_stride + | 256 frame->data() + i.rect().top() * source_stride + |
248 i.rect().left() * ScreenCaptureData::kBytesPerPixel); | 257 i.rect().left() * webrtc::DesktopFrame::kBytesPerPixel); |
249 canvas.drawBitmap(source_bitmap, i.rect().left() + x / scale, | 258 canvas.drawBitmap(source_bitmap, i.rect().left() + x / scale, |
250 i.rect().top() + y / scale, NULL); | 259 i.rect().top() + y / scale, NULL); |
251 } | 260 } |
252 | 261 |
253 base::AutoLock auto_lock(event_handler_lock_); | 262 base::AutoLock auto_lock(event_handler_lock_); |
254 if (event_handler_) { | 263 if (event_handler_) { |
255 event_handler_->OnIncomingCapturedFrame( | 264 event_handler_->OnIncomingCapturedFrame( |
256 reinterpret_cast<uint8*>(resized_bitmap_.getPixels()), buffer_size, | 265 reinterpret_cast<uint8*>(resized_bitmap_.getPixels()), buffer_size, |
257 base::Time::Now(), 0, false, false); | 266 base::Time::Now(), 0, false, false); |
258 } | 267 } |
259 } | 268 } |
260 | 269 |
261 void ScreenCaptureDevice::Core::OnCursorShapeChanged( | |
262 scoped_ptr<MouseCursorShape> cursor_shape) { | |
263 // TODO(sergeyu): Store mouse cursor shape and then render it to each captured | |
264 // frame. crbug.com/173265 . | |
265 DCHECK(task_runner_->RunsTasksOnCurrentThread()); | |
266 } | |
267 | |
268 void ScreenCaptureDevice::Core::DoAllocate(int frame_rate) { | 270 void ScreenCaptureDevice::Core::DoAllocate(int frame_rate) { |
269 DCHECK(task_runner_->RunsTasksOnCurrentThread()); | 271 DCHECK(task_runner_->RunsTasksOnCurrentThread()); |
270 | 272 |
271 frame_rate_ = frame_rate; | 273 frame_rate_ = frame_rate; |
272 | 274 |
273 // Create and start frame capturer. | 275 // Create and start frame capturer. |
274 #if defined(OS_CHROMEOS) && !defined(ARCH_CPU_ARMEL) | 276 #if defined(OS_CHROMEOS) && !defined(ARCH_CPU_ARMEL) |
275 // ScreenCapturerX11 polls by default, due to poor driver support for DAMAGE. | 277 // ScreenCapturerX11 polls by default, due to poor driver support for DAMAGE. |
276 // ChromeOS' drivers [can be patched to] support DAMAGE properly, so use it. | 278 // ChromeOS' drivers [can be patched to] support DAMAGE properly, so use it. |
277 // However ARM driver seems to not support this properly, so disable it for | 279 // However ARM driver seems to not support this properly, so disable it for |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
331 | 333 |
332 if (!started_) | 334 if (!started_) |
333 return; | 335 return; |
334 | 336 |
335 // Schedule a task for the next frame. | 337 // Schedule a task for the next frame. |
336 ScheduleCaptureTimer(); | 338 ScheduleCaptureTimer(); |
337 DoCapture(); | 339 DoCapture(); |
338 } | 340 } |
339 | 341 |
340 void ScreenCaptureDevice::Core::DoCapture() { | 342 void ScreenCaptureDevice::Core::DoCapture() { |
| 343 DCHECK(task_runner_->RunsTasksOnCurrentThread()); |
341 DCHECK(!capture_in_progress_); | 344 DCHECK(!capture_in_progress_); |
342 | 345 |
343 capture_in_progress_ = true; | 346 capture_in_progress_ = true; |
344 screen_capturer_->CaptureFrame(); | 347 screen_capturer_->Capture(webrtc::DesktopRegion()); |
345 | 348 |
346 // Assume that ScreenCapturer always calls OnCaptureCompleted() | 349 // Currently only synchronous implementations of DesktopCapturer are |
347 // callback before it returns. | 350 // supported. |
348 // | |
349 // TODO(sergeyu): Fix ScreenCapturer to return video frame | |
350 // synchronously instead of using Delegate interface. | |
351 DCHECK(!capture_in_progress_); | 351 DCHECK(!capture_in_progress_); |
352 } | 352 } |
353 | 353 |
354 ScreenCaptureDevice::ScreenCaptureDevice( | 354 ScreenCaptureDevice::ScreenCaptureDevice( |
355 scoped_refptr<base::SequencedTaskRunner> task_runner) | 355 scoped_refptr<base::SequencedTaskRunner> task_runner) |
356 : core_(new Core(task_runner)) { | 356 : core_(new Core(task_runner)) { |
357 name_.device_name = "Screen"; | 357 name_.device_name = "Screen"; |
358 } | 358 } |
359 | 359 |
360 ScreenCaptureDevice::~ScreenCaptureDevice() { | 360 ScreenCaptureDevice::~ScreenCaptureDevice() { |
(...skipping 21 matching lines...) Expand all Loading... |
382 | 382 |
383 void ScreenCaptureDevice::DeAllocate() { | 383 void ScreenCaptureDevice::DeAllocate() { |
384 core_->DeAllocate(); | 384 core_->DeAllocate(); |
385 } | 385 } |
386 | 386 |
387 const VideoCaptureDevice::Name& ScreenCaptureDevice::device_name() { | 387 const VideoCaptureDevice::Name& ScreenCaptureDevice::device_name() { |
388 return name_; | 388 return name_; |
389 } | 389 } |
390 | 390 |
391 } // namespace media | 391 } // namespace media |
OLD | NEW |