| 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 "content/browser/renderer_host/media/screen_capturer.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 "remoting/capturer/capture_data.h" |
| 13 #include "remoting/capturer/mouse_cursor_shape.h" |
| 14 #include "third_party/skia/include/core/SkCanvas.h" |
| 15 #include "third_party/skia/include/core/SkDevice.h" |
| 16 |
| 17 namespace content { |
| 18 |
| 19 namespace { |
| 20 const int kBytesPerPixel = 4; |
| 21 } // namespace |
| 22 |
| 23 class ScreenCapturer::Core |
| 24 : public base::RefCountedThreadSafe<Core>, |
| 25 public remoting::VideoFrameCapturer::Delegate { |
| 26 public: |
| 27 Core(scoped_refptr<base::SequencedTaskRunner> task_runner); |
| 28 |
| 29 // Helper used in tests to supply a fake capturer. |
| 30 void set_test_frame_capturer( |
| 31 scoped_ptr<remoting::VideoFrameCapturer> capturer) { |
| 32 video_frame_capturer_ = capturer.Pass(); |
| 33 } |
| 34 |
| 35 void Allocate(int width, int height, |
| 36 int frame_rate, |
| 37 EventHandler* event_handler); |
| 38 void Start(); |
| 39 void Stop(); |
| 40 void DeAllocate(); |
| 41 |
| 42 // VideoFrameCapturer::Delegate interface. Called by |video_frame_capturer_| |
| 43 // on the |task_runner_|. |
| 44 virtual void OnCaptureCompleted( |
| 45 scoped_refptr<remoting::CaptureData> capture_data) OVERRIDE; |
| 46 virtual void OnCursorShapeChanged( |
| 47 scoped_ptr<remoting::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(); |
| 63 |
| 64 // Captures a single frame. |
| 65 void DoCapture(); |
| 66 |
| 67 // Task runner used for screen capturing operations. |
| 68 scoped_refptr<base::SequencedTaskRunner> task_runner_; |
| 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(). |
| 77 int frame_rate_; |
| 78 |
| 79 scoped_ptr<remoting::VideoFrameCapturer> video_frame_capturer_; |
| 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_; |
| 87 SkISize frame_size_; |
| 88 SkBitmap resized_bitmap_; |
| 89 |
| 90 // Set to true between DoStart() and DoStop(). |
| 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 ScreenCapturer::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 ScreenCapturer::Core::~Core() { |
| 115 } |
| 116 |
| 117 void ScreenCapturer::Core::Allocate(int width, int height, |
| 118 int frame_rate, |
| 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 ScreenCapturer::Core::Start() { |
| 134 task_runner_->PostTask( |
| 135 FROM_HERE, base::Bind(&Core::DoStart, this)); |
| 136 } |
| 137 |
| 138 void ScreenCapturer::Core::Stop() { |
| 139 task_runner_->PostTask( |
| 140 FROM_HERE, base::Bind(&Core::DoStop, this)); |
| 141 } |
| 142 |
| 143 void ScreenCapturer::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 ScreenCapturer::Core::OnCaptureCompleted( |
| 152 scoped_refptr<remoting::CaptureData> capture_data) { |
| 153 DCHECK(task_runner_->RunsTasksOnCurrentThread()); |
| 154 DCHECK(capture_in_progress_); |
| 155 DCHECK(!capture_data->size().isEmpty()); |
| 156 capture_in_progress_ = false; |
| 157 |
| 158 |
| 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 media::VideoCaptureCapability caps; |
| 165 caps.width = frame_size_.width(); |
| 166 caps.height = frame_size_.height(); |
| 167 caps.frame_rate = frame_rate_; |
| 168 caps.color = media::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; |
| 180 size_t buffer_size = |
| 181 frame_size_.width() * frame_size_.height() * |
| 182 remoting::CaptureData::kBytesPerPixel; |
| 183 |
| 184 if (capture_data->size() == frame_size_) { |
| 185 // If the captured frame matches the requested size, we don't need to |
| 186 // resize it. |
| 187 resized_bitmap_.reset(); |
| 188 |
| 189 base::AutoLock auto_lock(event_handler_lock_); |
| 190 if (event_handler_) { |
| 191 event_handler_->OnIncomingCapturedFrame( |
| 192 capture_data->data(), buffer_size, base::Time::Now()); |
| 193 } |
| 194 return; |
| 195 } |
| 196 |
| 197 // In case screen size has changed we need to resize the image. Resized image |
| 198 // is stored to |resized_bitmap_|. Only regions of the screen that are |
| 199 // changing are copied. |
| 200 |
| 201 SkRegion dirty_region = capture_data->dirty_region(); |
| 202 |
| 203 if (resized_bitmap_.width() != frame_size_.width() || |
| 204 resized_bitmap_.height() != frame_size_.height()) { |
| 205 resized_bitmap_.setConfig(SkBitmap::kARGB_8888_Config, |
| 206 frame_size_.width(), frame_size_.height()); |
| 207 resized_bitmap_.setIsOpaque(true); |
| 208 resized_bitmap_.allocPixels(); |
| 209 dirty_region.setRect(SkIRect::MakeSize(frame_size_)); |
| 210 } |
| 211 |
| 212 float scale_x = static_cast<float>(frame_size_.width()) / |
| 213 capture_data->size().width(); |
| 214 float scale_y = static_cast<float>(frame_size_.height()) / |
| 215 capture_data->size().height(); |
| 216 float scale; |
| 217 float x, y; |
| 218 // Center the image in case aspect ratio is different. |
| 219 if (scale_x > scale_y) { |
| 220 scale = scale_y; |
| 221 x = (scale_x - scale_y) / scale * frame_size_.width() / 2.0; |
| 222 y = 0.f; |
| 223 } else { |
| 224 scale = scale_x; |
| 225 x = 0.f; |
| 226 y = (scale_y - scale_x) / scale * frame_size_.height() / 2.0; |
| 227 } |
| 228 |
| 229 // Create skia device and canvas that draw to |resized_bitmap_|. |
| 230 SkDevice device(resized_bitmap_); |
| 231 SkCanvas canvas(&device); |
| 232 canvas.scale(scale, scale); |
| 233 |
| 234 int source_stride = capture_data->stride(); |
| 235 for (SkRegion::Iterator i(dirty_region); !i.done(); i.next()) { |
| 236 SkBitmap source_bitmap; |
| 237 source_bitmap.setConfig(SkBitmap::kARGB_8888_Config, |
| 238 i.rect().width(), i.rect().height(), |
| 239 source_stride); |
| 240 source_bitmap.setIsOpaque(true); |
| 241 source_bitmap.setPixels( |
| 242 capture_data->data() + i.rect().top() * source_stride + |
| 243 i.rect().left() * remoting::CaptureData::kBytesPerPixel); |
| 244 canvas.drawBitmap(source_bitmap, i.rect().left() + x / scale, |
| 245 i.rect().top() + y / scale, NULL); |
| 246 } |
| 247 |
| 248 base::AutoLock auto_lock(event_handler_lock_); |
| 249 if (event_handler_) { |
| 250 event_handler_->OnIncomingCapturedFrame( |
| 251 reinterpret_cast<uint8*>(resized_bitmap_.getPixels()), buffer_size, |
| 252 base::Time::Now()); |
| 253 } |
| 254 } |
| 255 |
| 256 void ScreenCapturer::Core::OnCursorShapeChanged( |
| 257 scoped_ptr<remoting::MouseCursorShape> cursor_shape) { |
| 258 DCHECK(task_runner_->RunsTasksOnCurrentThread()); |
| 259 } |
| 260 |
| 261 void ScreenCapturer::Core::DoAllocate(int frame_rate) { |
| 262 DCHECK(task_runner_->RunsTasksOnCurrentThread()); |
| 263 |
| 264 frame_rate_ = frame_rate; |
| 265 |
| 266 // Create and start frame capturer. |
| 267 if (!video_frame_capturer_) |
| 268 video_frame_capturer_ = remoting::VideoFrameCapturer::Create(); |
| 269 if (video_frame_capturer_) |
| 270 video_frame_capturer_->Start(this); |
| 271 |
| 272 // Capture first frame, so that we can call OnFrameInfo() callback. |
| 273 waiting_frame_size_ = true; |
| 274 DoCapture(); |
| 275 } |
| 276 |
| 277 void ScreenCapturer::Core::DoStart() { |
| 278 DCHECK(task_runner_->RunsTasksOnCurrentThread()); |
| 279 started_ = true; |
| 280 if (!capture_task_posted_) { |
| 281 capture_task_posted_ = true; |
| 282 task_runner_->PostDelayedTask( |
| 283 FROM_HERE, base::Bind(&Core::OnCaptureTimer, this), |
| 284 base::TimeDelta::FromSeconds(1) / frame_rate_); |
| 285 } |
| 286 } |
| 287 |
| 288 void ScreenCapturer::Core::DoStop() { |
| 289 DCHECK(task_runner_->RunsTasksOnCurrentThread()); |
| 290 started_ = false; |
| 291 if (video_frame_capturer_) |
| 292 video_frame_capturer_->Stop(); |
| 293 resized_bitmap_.reset(); |
| 294 } |
| 295 |
| 296 void ScreenCapturer::Core::DoDeAllocate() { |
| 297 DCHECK(task_runner_->RunsTasksOnCurrentThread()); |
| 298 DoStop(); |
| 299 video_frame_capturer_.reset(); |
| 300 waiting_frame_size_ = false; |
| 301 } |
| 302 |
| 303 void ScreenCapturer::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_) { |
| 314 return; |
| 315 } |
| 316 |
| 317 DoCapture(); |
| 318 } |
| 319 |
| 320 void ScreenCapturer::Core::DoCapture() { |
| 321 DCHECK(!capture_in_progress_); |
| 322 |
| 323 capture_in_progress_ = true; |
| 324 video_frame_capturer_->CaptureFrame(); |
| 325 |
| 326 // Assume that remoting::VideoFrameCapturer always calls OnCaptureCompleted() |
| 327 // callback before it returns. |
| 328 // |
| 329 // TODO(sergeyu): Fix remoting::VideoFrameCapturer to return video frame |
| 330 // synchronously instead of using Delegate interface. |
| 331 DCHECK(!capture_in_progress_); |
| 332 } |
| 333 |
| 334 ScreenCapturer::ScreenCapturer( |
| 335 scoped_refptr<base::SequencedTaskRunner> task_runner) |
| 336 : core_(new Core(task_runner)) { |
| 337 name_.device_name = "Screen"; |
| 338 } |
| 339 |
| 340 ScreenCapturer::~ScreenCapturer() { |
| 341 DeAllocate(); |
| 342 } |
| 343 |
| 344 void ScreenCapturer::set_test_frame_capturer( |
| 345 scoped_ptr<remoting::VideoFrameCapturer> capturer) { |
| 346 core_->set_test_frame_capturer(capturer.Pass()); |
| 347 } |
| 348 |
| 349 void ScreenCapturer::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 ScreenCapturer::Start() { |
| 356 core_->Start(); |
| 357 } |
| 358 |
| 359 void ScreenCapturer::Stop() { |
| 360 core_->Stop(); |
| 361 } |
| 362 |
| 363 void ScreenCapturer::DeAllocate() { |
| 364 core_->DeAllocate(); |
| 365 } |
| 366 |
| 367 const media::VideoCaptureDevice::Name& ScreenCapturer::device_name() { |
| 368 return name_; |
| 369 } |
| 370 |
| 371 } // namespace content |
| OLD | NEW |