Chromium Code Reviews| 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; | |
|
alexeypa (please no reviews)
2013/04/26 21:33:58
nit: Consider having the default implementation of
Sergey Ulanov
2013/05/07 22:25:50
Let's do it later, after this code is moved to web
alexeypa (please no reviews)
2013/05/08 22:24:59
OK.
| |
| 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 scoped_ptr<webrtc::DesktopFrame> owned_frame(frame); | |
| 165 | |
| 163 if (waiting_for_frame_size_) { | 166 if (waiting_for_frame_size_) { |
| 164 frame_size_ = capture_data->size(); | 167 frame_size_ = frame->size(); |
| 165 waiting_for_frame_size_ = false; | 168 waiting_for_frame_size_ = false; |
| 166 | 169 |
| 167 // Inform the EventHandler of the video frame dimensions and format. | 170 // Inform the EventHandler of the video frame dimensions and format. |
| 168 VideoCaptureCapability caps; | 171 VideoCaptureCapability caps; |
| 169 caps.width = frame_size_.width(); | 172 caps.width = frame_size_.width(); |
| 170 caps.height = frame_size_.height(); | 173 caps.height = frame_size_.height(); |
| 171 caps.frame_rate = frame_rate_; | 174 caps.frame_rate = frame_rate_; |
| 172 caps.color = VideoCaptureCapability::kARGB; | 175 caps.color = VideoCaptureCapability::kARGB; |
| 173 caps.expected_capture_delay = | 176 caps.expected_capture_delay = |
| 174 base::Time::kMillisecondsPerSecond / frame_rate_; | 177 base::Time::kMillisecondsPerSecond / frame_rate_; |
| 175 caps.interlaced = false; | 178 caps.interlaced = false; |
| 176 | 179 |
| 177 base::AutoLock auto_lock(event_handler_lock_); | 180 base::AutoLock auto_lock(event_handler_lock_); |
| 178 if (event_handler_) | 181 if (event_handler_) |
| 179 event_handler_->OnFrameInfo(caps); | 182 event_handler_->OnFrameInfo(caps); |
| 180 } | 183 } |
| 181 | 184 |
| 182 if (!started_) | 185 if (!started_) |
| 183 return; | 186 return; |
| 184 | 187 |
| 185 size_t buffer_size = frame_size_.width() * frame_size_.height() * | 188 size_t buffer_size = frame_size_.width() * frame_size_.height() * |
| 186 ScreenCaptureData::kBytesPerPixel; | 189 webrtc::DesktopFrame::kBytesPerPixel; |
| 187 | 190 |
| 188 if (capture_data->size() == frame_size_) { | 191 if (frame->size().equals(frame_size_)) { |
|
alexeypa (please no reviews)
2013/04/26 21:33:58
|frame| can be NULL if the capturer could not capt
Sergey Ulanov
2013/05/07 22:25:50
Done.
| |
| 189 // If the captured frame matches the requested size, we don't need to | 192 // If the captured frame matches the requested size, we don't need to |
| 190 // resize it. | 193 // resize it. |
| 191 resized_bitmap_.reset(); | 194 resized_bitmap_.reset(); |
| 192 | 195 |
| 193 base::AutoLock auto_lock(event_handler_lock_); | 196 base::AutoLock auto_lock(event_handler_lock_); |
| 194 if (event_handler_) { | 197 if (event_handler_) { |
| 195 event_handler_->OnIncomingCapturedFrame( | 198 event_handler_->OnIncomingCapturedFrame( |
| 196 capture_data->data(), buffer_size, base::Time::Now(), | 199 frame->data(), buffer_size, base::Time::Now(), 0, false, false); |
| 197 0, false, false); | |
| 198 } | 200 } |
| 199 return; | 201 return; |
| 200 } | 202 } |
| 201 | 203 |
| 202 // In case screen size has changed we need to resize the image. Resized image | 204 // 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 | 205 // is stored to |resized_bitmap_|. Only regions of the screen that are |
| 204 // changing are copied. | 206 // changing are copied. |
| 205 | 207 |
| 206 SkRegion dirty_region = capture_data->dirty_region(); | 208 webrtc::DesktopRegion dirty_region = frame->updated_region(); |
| 207 | 209 |
| 208 if (resized_bitmap_.width() != frame_size_.width() || | 210 if (resized_bitmap_.width() != frame_size_.width() || |
| 209 resized_bitmap_.height() != frame_size_.height()) { | 211 resized_bitmap_.height() != frame_size_.height()) { |
| 210 resized_bitmap_.setConfig(SkBitmap::kARGB_8888_Config, | 212 resized_bitmap_.setConfig(SkBitmap::kARGB_8888_Config, |
| 211 frame_size_.width(), frame_size_.height()); | 213 frame_size_.width(), frame_size_.height()); |
| 212 resized_bitmap_.setIsOpaque(true); | 214 resized_bitmap_.setIsOpaque(true); |
| 213 resized_bitmap_.allocPixels(); | 215 resized_bitmap_.allocPixels(); |
| 214 dirty_region.setRect(SkIRect::MakeSize(frame_size_)); | 216 dirty_region.SetRect( |
| 217 webrtc::DesktopRect::MakeWH(frame_size_.width(), frame_size_.height())); | |
|
alexeypa (please no reviews)
2013/04/26 21:33:58
nit: Consider adding MakeSize() to DesktopRect.
Sergey Ulanov
2013/05/07 22:25:50
Done.
| |
| 215 } | 218 } |
| 216 | 219 |
| 217 float scale_x = static_cast<float>(frame_size_.width()) / | 220 float scale_x = static_cast<float>(frame_size_.width()) / |
| 218 capture_data->size().width(); | 221 frame->size().width(); |
| 219 float scale_y = static_cast<float>(frame_size_.height()) / | 222 float scale_y = static_cast<float>(frame_size_.height()) / |
| 220 capture_data->size().height(); | 223 frame->size().height(); |
| 221 float scale; | 224 float scale; |
| 222 float x, y; | 225 float x, y; |
| 223 // Center the image in case aspect ratio is different. | 226 // Center the image in case aspect ratio is different. |
| 224 if (scale_x > scale_y) { | 227 if (scale_x > scale_y) { |
| 225 scale = scale_y; | 228 scale = scale_y; |
| 226 x = (scale_x - scale_y) / scale * frame_size_.width() / 2.0; | 229 x = (scale_x - scale_y) / scale * frame_size_.width() / 2.0; |
| 227 y = 0.f; | 230 y = 0.f; |
| 228 } else { | 231 } else { |
| 229 scale = scale_x; | 232 scale = scale_x; |
| 230 x = 0.f; | 233 x = 0.f; |
| 231 y = (scale_y - scale_x) / scale * frame_size_.height() / 2.0; | 234 y = (scale_y - scale_x) / scale * frame_size_.height() / 2.0; |
| 232 } | 235 } |
| 233 | 236 |
| 234 // Create skia device and canvas that draw to |resized_bitmap_|. | 237 // Create skia device and canvas that draw to |resized_bitmap_|. |
| 235 SkDevice device(resized_bitmap_); | 238 SkDevice device(resized_bitmap_); |
| 236 SkCanvas canvas(&device); | 239 SkCanvas canvas(&device); |
| 237 canvas.scale(scale, scale); | 240 canvas.scale(scale, scale); |
| 238 | 241 |
| 239 int source_stride = capture_data->stride(); | 242 int source_stride = frame->stride(); |
| 240 for (SkRegion::Iterator i(dirty_region); !i.done(); i.next()) { | 243 for (webrtc::DesktopRegion::Iterator i(dirty_region); !i.IsAtEnd(); |
| 244 i.Advance()) { | |
| 241 SkBitmap source_bitmap; | 245 SkBitmap source_bitmap; |
| 242 source_bitmap.setConfig(SkBitmap::kARGB_8888_Config, | 246 source_bitmap.setConfig(SkBitmap::kARGB_8888_Config, |
| 243 i.rect().width(), i.rect().height(), | 247 i.rect().width(), i.rect().height(), |
| 244 source_stride); | 248 source_stride); |
| 245 source_bitmap.setIsOpaque(true); | 249 source_bitmap.setIsOpaque(true); |
| 246 source_bitmap.setPixels( | 250 source_bitmap.setPixels( |
| 247 capture_data->data() + i.rect().top() * source_stride + | 251 frame->data() + i.rect().top() * source_stride + |
| 248 i.rect().left() * ScreenCaptureData::kBytesPerPixel); | 252 i.rect().left() * webrtc::DesktopFrame::kBytesPerPixel); |
| 249 canvas.drawBitmap(source_bitmap, i.rect().left() + x / scale, | 253 canvas.drawBitmap(source_bitmap, i.rect().left() + x / scale, |
| 250 i.rect().top() + y / scale, NULL); | 254 i.rect().top() + y / scale, NULL); |
| 251 } | 255 } |
| 252 | 256 |
| 253 base::AutoLock auto_lock(event_handler_lock_); | 257 base::AutoLock auto_lock(event_handler_lock_); |
| 254 if (event_handler_) { | 258 if (event_handler_) { |
| 255 event_handler_->OnIncomingCapturedFrame( | 259 event_handler_->OnIncomingCapturedFrame( |
| 256 reinterpret_cast<uint8*>(resized_bitmap_.getPixels()), buffer_size, | 260 reinterpret_cast<uint8*>(resized_bitmap_.getPixels()), buffer_size, |
| 257 base::Time::Now(), 0, false, false); | 261 base::Time::Now(), 0, false, false); |
| 258 } | 262 } |
| 259 } | 263 } |
| 260 | 264 |
| 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) { | 265 void ScreenCaptureDevice::Core::DoAllocate(int frame_rate) { |
| 269 DCHECK(task_runner_->RunsTasksOnCurrentThread()); | 266 DCHECK(task_runner_->RunsTasksOnCurrentThread()); |
| 270 | 267 |
| 271 frame_rate_ = frame_rate; | 268 frame_rate_ = frame_rate; |
| 272 | 269 |
| 273 // Create and start frame capturer. | 270 // Create and start frame capturer. |
| 274 #if defined(OS_CHROMEOS) && !defined(ARCH_CPU_ARMEL) | 271 #if defined(OS_CHROMEOS) && !defined(ARCH_CPU_ARMEL) |
| 275 // ScreenCapturerX11 polls by default, due to poor driver support for DAMAGE. | 272 // ScreenCapturerX11 polls by default, due to poor driver support for DAMAGE. |
| 276 // ChromeOS' drivers [can be patched to] support DAMAGE properly, so use it. | 273 // 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 | 274 // 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 | 328 |
| 332 if (!started_) | 329 if (!started_) |
| 333 return; | 330 return; |
| 334 | 331 |
| 335 // Schedule a task for the next frame. | 332 // Schedule a task for the next frame. |
| 336 ScheduleCaptureTimer(); | 333 ScheduleCaptureTimer(); |
| 337 DoCapture(); | 334 DoCapture(); |
| 338 } | 335 } |
| 339 | 336 |
| 340 void ScreenCaptureDevice::Core::DoCapture() { | 337 void ScreenCaptureDevice::Core::DoCapture() { |
| 338 DCHECK(task_runner_->RunsTasksOnCurrentThread()); | |
| 341 DCHECK(!capture_in_progress_); | 339 DCHECK(!capture_in_progress_); |
| 342 | 340 |
| 343 capture_in_progress_ = true; | 341 capture_in_progress_ = true; |
| 344 screen_capturer_->CaptureFrame(); | 342 screen_capturer_->Capture(webrtc::DesktopRegion()); |
| 345 | 343 |
| 346 // Assume that ScreenCapturer always calls OnCaptureCompleted() | 344 // Currently on synchronous implementations of DesktopCapturer are supported. |
|
Wez
2013/04/26 18:48:14
typo: on -> only
alexeypa (please no reviews)
2013/04/26 21:33:58
nit: on -> only.
Sergey Ulanov
2013/05/07 22:25:50
Done.
Sergey Ulanov
2013/05/07 22:25:50
Done.
| |
| 347 // callback before it returns. | |
| 348 // | |
| 349 // TODO(sergeyu): Fix ScreenCapturer to return video frame | |
| 350 // synchronously instead of using Delegate interface. | |
| 351 DCHECK(!capture_in_progress_); | 345 DCHECK(!capture_in_progress_); |
| 352 } | 346 } |
| 353 | 347 |
| 354 ScreenCaptureDevice::ScreenCaptureDevice( | 348 ScreenCaptureDevice::ScreenCaptureDevice( |
| 355 scoped_refptr<base::SequencedTaskRunner> task_runner) | 349 scoped_refptr<base::SequencedTaskRunner> task_runner) |
| 356 : core_(new Core(task_runner)) { | 350 : core_(new Core(task_runner)) { |
| 357 name_.device_name = "Screen"; | 351 name_.device_name = "Screen"; |
| 358 } | 352 } |
| 359 | 353 |
| 360 ScreenCaptureDevice::~ScreenCaptureDevice() { | 354 ScreenCaptureDevice::~ScreenCaptureDevice() { |
| (...skipping 21 matching lines...) Expand all Loading... | |
| 382 | 376 |
| 383 void ScreenCaptureDevice::DeAllocate() { | 377 void ScreenCaptureDevice::DeAllocate() { |
| 384 core_->DeAllocate(); | 378 core_->DeAllocate(); |
| 385 } | 379 } |
| 386 | 380 |
| 387 const VideoCaptureDevice::Name& ScreenCaptureDevice::device_name() { | 381 const VideoCaptureDevice::Name& ScreenCaptureDevice::device_name() { |
| 388 return name_; | 382 return name_; |
| 389 } | 383 } |
| 390 | 384 |
| 391 } // namespace media | 385 } // namespace media |
| OLD | NEW |