Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 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 // Implementation notes: This needs to work on a variety of hardware | 5 // Implementation notes: This needs to work on a variety of hardware |
| 6 // configurations where the speed of the CPU and GPU greatly affect overall | 6 // configurations where the speed of the CPU and GPU greatly affect overall |
| 7 // performance. Therefore, the process of capturing has been split up into a | 7 // performance. Therefore, the process of capturing has been split up into a |
| 8 // pipeline of three stages. Each stage executes on its own thread: | 8 // pipeline of three stages. Each stage executes on its own thread: |
| 9 // | 9 // |
| 10 // 1. Capture: A bitmap is snapshotted/copied from the RenderView's backing | 10 // 1. Capture: A bitmap is snapshotted/copied from the RenderView's backing |
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 57 #include "base/time.h" | 57 #include "base/time.h" |
| 58 #include "content/browser/renderer_host/media/web_contents_capture_util.h" | 58 #include "content/browser/renderer_host/media/web_contents_capture_util.h" |
| 59 #include "content/browser/web_contents/web_contents_impl.h" | 59 #include "content/browser/web_contents/web_contents_impl.h" |
| 60 #include "content/public/browser/browser_thread.h" | 60 #include "content/public/browser/browser_thread.h" |
| 61 #include "content/public/browser/render_process_host.h" | 61 #include "content/public/browser/render_process_host.h" |
| 62 #include "content/public/browser/render_view_host.h" | 62 #include "content/public/browser/render_view_host.h" |
| 63 #include "content/public/browser/render_widget_host_view.h" | 63 #include "content/public/browser/render_widget_host_view.h" |
| 64 #include "content/public/browser/web_contents.h" | 64 #include "content/public/browser/web_contents.h" |
| 65 #include "content/public/browser/web_contents_observer.h" | 65 #include "content/public/browser/web_contents_observer.h" |
| 66 #include "media/base/bind_to_loop.h" | 66 #include "media/base/bind_to_loop.h" |
| 67 #include "media/base/video_frame.h" | |
| 67 #include "media/video/capture/video_capture_types.h" | 68 #include "media/video/capture/video_capture_types.h" |
| 68 #include "skia/ext/image_operations.h" | 69 #include "skia/ext/image_operations.h" |
| 69 #include "third_party/skia/include/core/SkBitmap.h" | 70 #include "third_party/skia/include/core/SkBitmap.h" |
| 70 #include "third_party/skia/include/core/SkColor.h" | 71 #include "third_party/skia/include/core/SkColor.h" |
| 71 #include "ui/gfx/rect.h" | 72 #include "ui/gfx/rect.h" |
| 73 #include "ui/gfx/skia_util.h" | |
| 72 | 74 |
| 73 // Used to self-trampoline invocation of methods to the approprate thread. This | 75 // Used to self-trampoline invocation of methods to the approprate thread. This |
| 74 // should be used sparingly, only when it's not clear which thread is invoking a | 76 // should be used sparingly, only when it's not clear which thread is invoking a |
| 75 // method. | 77 // method. |
| 76 #define ENSURE_INVOKED_ON_THREAD(thread, ...) { \ | 78 #define ENSURE_INVOKED_ON_THREAD(thread, ...) { \ |
| 77 DCHECK(thread.IsRunning()); \ | 79 DCHECK(thread.IsRunning()); \ |
| 78 if (MessageLoop::current() != thread.message_loop()) { \ | 80 if (MessageLoop::current() != thread.message_loop()) { \ |
| 79 thread.message_loop()->PostTask(FROM_HERE, base::Bind(__VA_ARGS__)); \ | 81 thread.message_loop()->PostTask(FROM_HERE, base::Bind(__VA_ARGS__)); \ |
| 80 return; \ | 82 return; \ |
| 81 } \ | 83 } \ |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 132 fitted_width = std::max(kMinFrameWidth, MakeEven(fitted_width)); | 134 fitted_width = std::max(kMinFrameWidth, MakeEven(fitted_width)); |
| 133 fitted_height = std::max(kMinFrameHeight, MakeEven(fitted_height)); | 135 fitted_height = std::max(kMinFrameHeight, MakeEven(fitted_height)); |
| 134 | 136 |
| 135 *fitted_size = gfx::Size(fitted_width, fitted_height); | 137 *fitted_size = gfx::Size(fitted_width, fitted_height); |
| 136 } | 138 } |
| 137 | 139 |
| 138 // Keeps track of the RenderView to be sourced, and executes copying of the | 140 // Keeps track of the RenderView to be sourced, and executes copying of the |
| 139 // backing store on the UI BrowserThread. | 141 // backing store on the UI BrowserThread. |
| 140 class BackingStoreCopier : public WebContentsObserver { | 142 class BackingStoreCopier : public WebContentsObserver { |
| 141 public: | 143 public: |
| 142 // Result status and done callback used with StartCopy(). | |
| 143 enum Result { | |
| 144 OK, | |
| 145 TRANSIENT_ERROR, | |
| 146 NO_SOURCE, | |
| 147 }; | |
| 148 typedef base::Callback<void(Result result, | |
| 149 const SkBitmap& capture, | |
| 150 const base::Time& capture_time)> DoneCB; | |
| 151 | |
| 152 BackingStoreCopier(int render_process_id, int render_view_id); | 144 BackingStoreCopier(int render_process_id, int render_view_id); |
| 153 | 145 |
| 154 // If non-NULL, use the given |override| to access the backing store. | 146 // If non-NULL, use the given |override| to access the backing store. |
| 155 // This is used for unit testing. | 147 // This is used for unit testing. |
| 156 void SetRenderWidgetHostForTesting(RenderWidgetHost* override); | 148 void SetRenderWidgetHostForTesting(RenderWidgetHost* override); |
| 157 | 149 |
| 158 // Starts the copy from the backing store. Must be run on the UI | 150 // Starts the copy from the backing store. Must be run on the UI |
| 159 // BrowserThread. |done_cb| is invoked with result status. When successful | 151 // BrowserThread. Resulting frame is conveyed back to |consumer|. |
| 160 // (OK), the bitmap of the capture is transferred to the callback along with | 152 void StartCopy(CaptureMachine* consumer, |
|
scherkus (not reviewing)
2013/02/05 22:40:44
nit: scoped-const-ref-& here + below
ncarter (slow)
2013/02/06 23:54:44
Done.
| |
| 161 // the timestamp at which the capture was completed. | 153 int frame_number, |
| 162 void StartCopy(int frame_number, int desired_width, int desired_height, | 154 int desired_width, |
| 163 const DoneCB& done_cb); | 155 int desired_height); |
| 164 | 156 |
| 157 // content::WebContentsObserver implementation. | |
| 165 virtual void DidShowFullscreenWidget(int routing_id) OVERRIDE { | 158 virtual void DidShowFullscreenWidget(int routing_id) OVERRIDE { |
| 166 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 159 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 167 fullscreen_widget_id_ = routing_id; | 160 fullscreen_widget_id_ = routing_id; |
| 168 } | 161 } |
| 169 | 162 |
| 170 virtual void DidDestroyFullscreenWidget(int routing_id) OVERRIDE { | 163 virtual void DidDestroyFullscreenWidget(int routing_id) OVERRIDE { |
| 171 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 164 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 172 DCHECK_EQ(fullscreen_widget_id_, routing_id); | 165 DCHECK_EQ(fullscreen_widget_id_, routing_id); |
| 173 fullscreen_widget_id_ = MSG_ROUTING_NONE; | 166 fullscreen_widget_id_ = MSG_ROUTING_NONE; |
| 174 } | 167 } |
| 175 | 168 |
| 176 private: | 169 private: |
| 177 void LookUpAndObserveWebContents(); | 170 void LookUpAndObserveWebContents(); |
| 178 | 171 |
| 179 void CopyFromBackingStoreComplete(int frame_number, | 172 // Response callback for RenderWidgetHost::CopyFromBackingStore. |
| 180 const DoneCB& done_cb, | 173 void DidCopyFromBackingStore(CaptureMachine* consumer, |
| 181 bool success, | 174 int frame_number, |
| 182 const SkBitmap& result); | 175 const base::Time& start_time, |
| 176 bool success, | |
| 177 const SkBitmap& frame); | |
| 178 | |
| 179 // Response callback for RenderWidgetHost::CopyFromBackingStoreToVideoFrame. | |
| 180 void DidCopyFromBackingStoreToVideoFrame( | |
| 181 CaptureMachine* consumer, | |
| 182 int frame_number, | |
| 183 const base::Time& start_time, | |
| 184 media::VideoFrame* frame); | |
| 183 | 185 |
| 184 // The "starting point" to find the capture source. | 186 // The "starting point" to find the capture source. |
| 185 const int render_process_id_; | 187 const int render_process_id_; |
| 186 const int render_view_id_; | 188 const int render_view_id_; |
| 187 | 189 |
| 188 // Routing ID of any active fullscreen render widget or MSG_ROUTING_NONE | 190 // Routing ID of any active fullscreen render widget or MSG_ROUTING_NONE |
| 189 // otherwise. | 191 // otherwise. |
| 190 int fullscreen_widget_id_; | 192 int fullscreen_widget_id_; |
| 191 | 193 |
| 192 // Last known RenderView size. | 194 // Last known RenderView size. |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 242 class SynchronizedConsumer { | 244 class SynchronizedConsumer { |
| 243 public: | 245 public: |
| 244 SynchronizedConsumer(); | 246 SynchronizedConsumer(); |
| 245 | 247 |
| 246 void SetConsumer(media::VideoCaptureDevice::EventHandler* consumer); | 248 void SetConsumer(media::VideoCaptureDevice::EventHandler* consumer); |
| 247 | 249 |
| 248 void OnFrameInfo(const media::VideoCaptureCapability& info); | 250 void OnFrameInfo(const media::VideoCaptureCapability& info); |
| 249 void OnError(); | 251 void OnError(); |
| 250 void OnIncomingCapturedFrame(const uint8* pixels, int size, | 252 void OnIncomingCapturedFrame(const uint8* pixels, int size, |
| 251 const base::Time& timestamp); | 253 const base::Time& timestamp); |
| 254 void OnIncomingCapturedVideoFrame(media::VideoFrame* video_frame, | |
| 255 const base::Time& timestamp); | |
| 252 | 256 |
| 253 private: | 257 private: |
| 254 base::Lock consumer_lock_; | 258 base::Lock consumer_lock_; |
| 255 media::VideoCaptureDevice::EventHandler* wrapped_consumer_; | 259 media::VideoCaptureDevice::EventHandler* wrapped_consumer_; |
| 256 | 260 |
| 257 DISALLOW_COPY_AND_ASSIGN(SynchronizedConsumer); | 261 DISALLOW_COPY_AND_ASSIGN(SynchronizedConsumer); |
| 258 }; | 262 }; |
| 259 | 263 |
| 260 // Delivers rendered video frames to a consumer on a separate thread. Also | 264 // Delivers rendered video frames to a consumer on a separate thread. Also |
| 261 // responsible for logging the effective frame rate. | 265 // responsible for logging the effective frame rate. |
| 262 class VideoFrameDeliverer { | 266 class VideoFrameDeliverer { |
| 263 public: | 267 public: |
| 264 explicit VideoFrameDeliverer(SynchronizedConsumer* consumer); | 268 explicit VideoFrameDeliverer(SynchronizedConsumer* consumer); |
| 265 | 269 |
| 270 // Deliver a fully rendered ARGB frame, using SkBitmap as a container. | |
| 271 // |done_cb| will be invoked after delivery is complete. | |
| 266 void Deliver(int frame_number, | 272 void Deliver(int frame_number, |
| 267 const SkBitmap& frame_buffer, const base::Time& frame_timestamp, | 273 const SkBitmap& frame_buffer, |
| 274 const base::Time& frame_timestamp, | |
| 268 const base::Closure& done_cb); | 275 const base::Closure& done_cb); |
| 276 // Deliver a fully rendered frame YV12 frame, using VideoFrame as a container. | |
| 277 // A refcount is taken on |frame| until delivery is complete. | |
| 278 void Deliver(int frame_number, | |
| 279 media::VideoFrame* frame, | |
| 280 const base::Time& frame_timestamp); | |
| 269 | 281 |
| 270 private: | 282 private: |
| 271 void DeliverOnDeliverThread(int frame_number, | 283 void DeliverOnDeliverThread(int frame_number, |
| 272 const SkBitmap& frame_buffer, | 284 const SkBitmap& frame, |
| 273 const base::Time& frame_timestamp, | 285 const base::Time& frame_timestamp, |
| 274 const base::Closure& done_cb); | 286 const base::Closure& done_cb); |
| 287 void DeliverVideoFrameOnDeliverThread(int frame_number, | |
| 288 media::VideoFrame* frame, | |
| 289 const base::Time& frame_timestamp); | |
| 290 | |
| 291 // Treat |frame_number| as having been delivered, and update the | |
| 292 // frame rate statistics accordingly. | |
| 293 void ChronicleFrameDelivery(int frame_number); | |
| 275 | 294 |
| 276 base::Thread deliver_thread_; | 295 base::Thread deliver_thread_; |
| 277 SynchronizedConsumer* const consumer_; | 296 SynchronizedConsumer* const consumer_; |
| 278 | 297 |
| 279 // The following keep track of and log the effective frame rate (from the | 298 // The following keep track of and log the effective frame rate (from the |
| 280 // deliver stage) whenever verbose logging is turned on. | 299 // deliver stage) whenever verbose logging is turned on. |
| 281 base::Time last_frame_rate_log_time_; | 300 base::Time last_frame_rate_log_time_; |
| 282 int count_frames_rendered_; | 301 int count_frames_rendered_; |
| 283 int last_frame_number_; | 302 int last_frame_number_; |
| 284 | 303 |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 316 fullscreen_widget_id_ = static_cast<WebContentsImpl*>(web_contents())-> | 335 fullscreen_widget_id_ = static_cast<WebContentsImpl*>(web_contents())-> |
| 317 GetFullscreenWidgetRoutingID(); | 336 GetFullscreenWidgetRoutingID(); |
| 318 } | 337 } |
| 319 } | 338 } |
| 320 | 339 |
| 321 void BackingStoreCopier::SetRenderWidgetHostForTesting( | 340 void BackingStoreCopier::SetRenderWidgetHostForTesting( |
| 322 RenderWidgetHost* override) { | 341 RenderWidgetHost* override) { |
| 323 rwh_for_testing_ = override; | 342 rwh_for_testing_ = override; |
| 324 } | 343 } |
| 325 | 344 |
| 326 void BackingStoreCopier::StartCopy(int frame_number, | |
| 327 int desired_width, int desired_height, | |
| 328 const DoneCB& done_cb) { | |
| 329 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 330 | |
| 331 TRACE_EVENT_ASYNC_BEGIN1("mirroring", "Capture", this, | |
| 332 "frame_number", frame_number); | |
| 333 | |
| 334 RenderWidgetHost* rwh; | |
| 335 if (rwh_for_testing_) { | |
| 336 rwh = rwh_for_testing_; | |
| 337 } else { | |
| 338 if (!web_contents()) { // No source yet. | |
| 339 LookUpAndObserveWebContents(); | |
| 340 if (!web_contents()) { // No source ever. | |
| 341 done_cb.Run(NO_SOURCE, SkBitmap(), base::Time()); | |
| 342 return; | |
| 343 } | |
| 344 } | |
| 345 | |
| 346 if (fullscreen_widget_id_ != MSG_ROUTING_NONE) { | |
| 347 RenderProcessHost* process = web_contents()->GetRenderProcessHost(); | |
| 348 rwh = process ? process->GetRenderWidgetHostByID(fullscreen_widget_id_) | |
| 349 : NULL; | |
| 350 } else { | |
| 351 rwh = web_contents()->GetRenderViewHost(); | |
| 352 } | |
| 353 | |
| 354 if (!rwh) { | |
| 355 // Transient failure state (e.g., a RenderView is being replaced). | |
| 356 done_cb.Run(TRANSIENT_ERROR, SkBitmap(), base::Time()); | |
| 357 return; | |
| 358 } | |
| 359 } | |
| 360 | |
| 361 gfx::Size fitted_size; | |
| 362 if (RenderWidgetHostView* const view = rwh->GetView()) { | |
| 363 const gfx::Size& view_size = view->GetViewBounds().size(); | |
| 364 if (!view_size.IsEmpty()) { | |
| 365 CalculateFittedSize(view_size.width(), view_size.height(), | |
| 366 desired_width, desired_height, | |
| 367 &fitted_size); | |
| 368 } | |
| 369 if (view_size != last_view_size_) { | |
| 370 last_view_size_ = view_size; | |
| 371 | |
| 372 // Measure the number of kilopixels. | |
| 373 UMA_HISTOGRAM_COUNTS_10000( | |
| 374 "TabCapture.ViewChangeKiloPixels", | |
| 375 view_size.width() * view_size.height() / 1024); | |
| 376 } | |
| 377 } | |
| 378 | |
| 379 rwh->CopyFromBackingStore( | |
| 380 gfx::Rect(), | |
| 381 fitted_size, | |
| 382 base::Bind(&BackingStoreCopier::CopyFromBackingStoreComplete, | |
| 383 base::Unretained(this), | |
| 384 frame_number, done_cb)); | |
| 385 | |
| 386 // TODO(miu): When a tab is not visible to the user, rendering stops. For | |
| 387 // mirroring, however, it's important that rendering continues to happen. | |
| 388 } | |
| 389 | |
| 390 void BackingStoreCopier::CopyFromBackingStoreComplete( | |
| 391 int frame_number, | |
| 392 const DoneCB& done_cb, | |
| 393 bool success, | |
| 394 const SkBitmap& frame) { | |
| 395 // Note: No restriction on which thread invokes this method but, currently, | |
| 396 // it's always the UI BrowserThread. | |
| 397 TRACE_EVENT_ASYNC_END1("mirroring", "Capture", this, | |
| 398 "frame_number", frame_number); | |
| 399 if (success) { | |
| 400 done_cb.Run(OK, frame, base::Time::Now()); | |
| 401 } else { | |
| 402 // Capture can fail due to transient issues, so just skip this frame. | |
| 403 DVLOG(1) << "CopyFromBackingStore was not successful; skipping frame."; | |
| 404 done_cb.Run(TRANSIENT_ERROR, SkBitmap(), base::Time()); | |
| 405 } | |
| 406 } | |
| 407 | |
| 408 VideoFrameRenderer::VideoFrameRenderer() | 345 VideoFrameRenderer::VideoFrameRenderer() |
| 409 : render_thread_("WebContentsVideo_RenderThread") { | 346 : render_thread_("WebContentsVideo_RenderThread") { |
| 410 output_[0].in_use = false; | 347 output_[0].in_use = false; |
| 411 output_[1].in_use = false; | 348 output_[1].in_use = false; |
| 412 render_thread_.Start(); | 349 render_thread_.Start(); |
| 413 } | 350 } |
| 414 | 351 |
| 415 void VideoFrameRenderer::Render(int frame_number, | 352 void VideoFrameRenderer::Render(int frame_number, |
| 416 const SkBitmap& capture, | 353 const SkBitmap& capture, |
| 417 int frame_width, int frame_height, | 354 int frame_width, int frame_height, |
| (...skipping 149 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 567 } | 504 } |
| 568 | 505 |
| 569 void SynchronizedConsumer::OnIncomingCapturedFrame( | 506 void SynchronizedConsumer::OnIncomingCapturedFrame( |
| 570 const uint8* pixels, int size, const base::Time& timestamp) { | 507 const uint8* pixels, int size, const base::Time& timestamp) { |
| 571 base::AutoLock guard(consumer_lock_); | 508 base::AutoLock guard(consumer_lock_); |
| 572 if (wrapped_consumer_) { | 509 if (wrapped_consumer_) { |
| 573 wrapped_consumer_->OnIncomingCapturedFrame(pixels, size, timestamp); | 510 wrapped_consumer_->OnIncomingCapturedFrame(pixels, size, timestamp); |
| 574 } | 511 } |
| 575 } | 512 } |
| 576 | 513 |
| 514 void SynchronizedConsumer::OnIncomingCapturedVideoFrame( | |
| 515 media::VideoFrame* video_frame, | |
| 516 const base::Time& timestamp) { | |
| 517 base::AutoLock guard(consumer_lock_); | |
| 518 if (wrapped_consumer_) { | |
| 519 wrapped_consumer_->OnIncomingCapturedVideoFrame(video_frame, timestamp); | |
| 520 } | |
| 521 } | |
| 522 | |
| 577 VideoFrameDeliverer::VideoFrameDeliverer(SynchronizedConsumer* consumer) | 523 VideoFrameDeliverer::VideoFrameDeliverer(SynchronizedConsumer* consumer) |
| 578 : deliver_thread_("WebContentsVideo_DeliverThread"), | 524 : deliver_thread_("WebContentsVideo_DeliverThread"), |
| 579 consumer_(consumer), | 525 consumer_(consumer), |
| 580 last_frame_number_(0) { | 526 last_frame_number_(0) { |
| 581 DCHECK(consumer_); | 527 DCHECK(consumer_); |
| 582 deliver_thread_.Start(); | 528 deliver_thread_.Start(); |
| 583 } | 529 } |
| 584 | 530 |
| 585 void VideoFrameDeliverer::Deliver( | 531 void VideoFrameDeliverer::Deliver( |
| 586 int frame_number, | 532 int frame_number, |
| 587 const SkBitmap& frame_buffer, const base::Time& frame_timestamp, | 533 const SkBitmap& frame_buffer, const base::Time& frame_timestamp, |
| 588 const base::Closure& done_cb) { | 534 const base::Closure& done_cb) { |
| 589 deliver_thread_.message_loop()->PostTask( | 535 deliver_thread_.message_loop()->PostTask( |
| 590 FROM_HERE, | 536 FROM_HERE, |
| 591 base::Bind(&VideoFrameDeliverer::DeliverOnDeliverThread, | 537 base::Bind(&VideoFrameDeliverer::DeliverOnDeliverThread, |
| 592 base::Unretained(this), | 538 base::Unretained(this), |
| 593 frame_number, base::ConstRef(frame_buffer), frame_timestamp, | 539 frame_number, base::ConstRef(frame_buffer), frame_timestamp, |
| 594 done_cb)); | 540 done_cb)); |
| 595 } | 541 } |
| 596 | 542 |
| 543 void VideoFrameDeliverer::Deliver( | |
| 544 int frame_number, | |
| 545 media::VideoFrame* frame, | |
| 546 const base::Time& frame_timestamp) { | |
| 547 deliver_thread_.message_loop()->PostTask( | |
| 548 FROM_HERE, | |
| 549 base::Bind(&VideoFrameDeliverer::DeliverVideoFrameOnDeliverThread, | |
| 550 base::Unretained(this), | |
| 551 frame_number, make_scoped_refptr(frame), frame_timestamp)); | |
|
scherkus (not reviewing)
2013/02/05 22:40:44
see my comments in video_capture_device.h about us
ncarter (slow)
2013/02/06 23:54:44
Done.
| |
| 552 } | |
| 553 | |
| 597 void VideoFrameDeliverer::DeliverOnDeliverThread( | 554 void VideoFrameDeliverer::DeliverOnDeliverThread( |
| 598 int frame_number, | 555 int frame_number, |
| 599 const SkBitmap& frame_buffer, const base::Time& frame_timestamp, | 556 const SkBitmap& frame_buffer, |
| 557 const base::Time& frame_timestamp, | |
| 600 const base::Closure& done_cb) { | 558 const base::Closure& done_cb) { |
| 601 DCHECK_EQ(deliver_thread_.message_loop(), MessageLoop::current()); | 559 DCHECK_EQ(deliver_thread_.message_loop(), MessageLoop::current()); |
| 602 | 560 |
| 603 TRACE_EVENT1("mirroring", "DeliverFrame", "frame_number", frame_number); | 561 TRACE_EVENT1("mirroring", "DeliverFrame", "frame_number", frame_number); |
| 604 | 562 |
| 605 // Send the frame to the consumer. | 563 // Send the frame to the consumer. |
| 606 // Note: The consumer will do an ARGB-->YUV conversion in this callback, | 564 // Note: The consumer will do an ARGB-->YUV conversion in this callback, |
| 607 // blocking the current thread for a bit. | 565 // blocking the current thread for a bit. |
| 608 SkAutoLockPixels frame_buffer_locker(frame_buffer); | 566 SkAutoLockPixels frame_buffer_locker(frame_buffer); |
| 609 consumer_->OnIncomingCapturedFrame( | 567 consumer_->OnIncomingCapturedFrame( |
| 610 static_cast<const uint8*>(frame_buffer.getPixels()), | 568 static_cast<const uint8*>(frame_buffer.getPixels()), |
| 611 frame_buffer.getSize(), | 569 frame_buffer.getSize(), |
| 612 frame_timestamp); | 570 frame_timestamp); |
| 613 | 571 |
| 572 ChronicleFrameDelivery(frame_number); | |
| 573 | |
| 574 // All done. | |
| 575 done_cb.Run(); | |
| 576 } | |
| 577 | |
| 578 void VideoFrameDeliverer::DeliverVideoFrameOnDeliverThread( | |
| 579 int frame_number, | |
| 580 media::VideoFrame* frame, | |
| 581 const base::Time& frame_timestamp) { | |
| 582 DCHECK_EQ(deliver_thread_.message_loop(), MessageLoop::current()); | |
| 583 | |
| 584 TRACE_EVENT1("mirroring", "DeliverFrame", "frame_number", frame_number); | |
| 585 | |
| 586 // Send the frame to the consumer. | |
| 587 consumer_->OnIncomingCapturedVideoFrame(frame, frame_timestamp); | |
| 588 | |
| 589 ChronicleFrameDelivery(frame_number); | |
| 590 } | |
| 591 | |
| 592 void VideoFrameDeliverer::ChronicleFrameDelivery(int frame_number) { | |
| 614 // Log frame rate, if verbose logging is turned on. | 593 // Log frame rate, if verbose logging is turned on. |
| 615 static const base::TimeDelta kFrameRateLogInterval = | 594 static const base::TimeDelta kFrameRateLogInterval = |
| 616 base::TimeDelta::FromSeconds(10); | 595 base::TimeDelta::FromSeconds(10); |
| 617 const base::Time& now = base::Time::Now(); | 596 const base::Time& now = base::Time::Now(); |
| 618 if (last_frame_rate_log_time_.is_null()) { | 597 if (last_frame_rate_log_time_.is_null()) { |
| 619 last_frame_rate_log_time_ = now; | 598 last_frame_rate_log_time_ = now; |
| 620 count_frames_rendered_ = 0; | 599 count_frames_rendered_ = 0; |
| 621 last_frame_number_ = frame_number; | 600 last_frame_number_ = frame_number; |
| 622 } else { | 601 } else { |
| 623 ++count_frames_rendered_; | 602 ++count_frames_rendered_; |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 634 UMA_HISTOGRAM_COUNTS( | 613 UMA_HISTOGRAM_COUNTS( |
| 635 "TabCapture.FrameRate", | 614 "TabCapture.FrameRate", |
| 636 static_cast<int>(measured_fps)); | 615 static_cast<int>(measured_fps)); |
| 637 VLOG(1) << "Current measured frame rate for CaptureMachine@" << this | 616 VLOG(1) << "Current measured frame rate for CaptureMachine@" << this |
| 638 << " is " << measured_fps << " FPS."; | 617 << " is " << measured_fps << " FPS."; |
| 639 last_frame_rate_log_time_ = now; | 618 last_frame_rate_log_time_ = now; |
| 640 count_frames_rendered_ = 0; | 619 count_frames_rendered_ = 0; |
| 641 last_frame_number_ = frame_number; | 620 last_frame_number_ = frame_number; |
| 642 } | 621 } |
| 643 } | 622 } |
| 644 | |
| 645 // All done. | |
| 646 done_cb.Run(); | |
| 647 } | 623 } |
| 648 | 624 |
| 649 } // namespace | 625 } // namespace |
| 650 | 626 |
| 651 // The "meat" of the video capture implementation, which is a ref-counted class. | 627 // The "meat" of the video capture implementation, which is a ref-counted class. |
| 652 // Separating this from the "shell class" WebContentsVideoCaptureDevice allows | 628 // Separating this from the "shell class" WebContentsVideoCaptureDevice allows |
| 653 // safe destruction without needing to block any threads (e.g., the IO | 629 // safe destruction without needing to block any threads (e.g., the IO |
| 654 // BrowserThread). | 630 // BrowserThread). |
| 655 // | 631 // |
| 656 // CaptureMachine manages a simple state machine and the pipeline (see notes at | 632 // CaptureMachine manages a simple state machine and the pipeline (see notes at |
| 657 // top of this file). It times the start of successive captures and | 633 // top of this file). It times the start of successive captures and |
| 658 // facilitates the processing of each through the stages of the pipeline. | 634 // facilitates the processing of each through the stages of the pipeline. |
| 659 class CaptureMachine | 635 class CaptureMachine |
| 660 : public base::RefCountedThreadSafe<CaptureMachine, CaptureMachine> { | 636 : public base::RefCountedThreadSafe<CaptureMachine, CaptureMachine> { |
| 661 public: | 637 public: |
| 638 enum SnapshotError { | |
| 639 NO_SOURCE, | |
| 640 TRANSIENT_ERROR | |
| 641 }; | |
| 642 | |
| 662 CaptureMachine(int render_process_id, int render_view_id); | 643 CaptureMachine(int render_process_id, int render_view_id); |
| 663 | 644 |
| 664 // Sets the capture source to the given |override| for unit testing. | 645 // Sets the capture source to the given |override| for unit testing. |
| 665 // Also, |destroy_cb| will be invoked after CaptureMachine is fully destroyed | 646 // Also, |destroy_cb| will be invoked after CaptureMachine is fully destroyed |
| 666 // (to synchronize tear-down). | 647 // (to synchronize tear-down). |
| 667 void InitializeForTesting(RenderWidgetHost* override, | 648 void InitializeForTesting(RenderWidgetHost* override, |
| 668 const base::Closure& destroy_cb); | 649 const base::Closure& destroy_cb); |
| 669 | 650 |
| 670 // Synchronously sets/unsets the consumer. Pass |consumer| as NULL to remove | 651 // Synchronously sets/unsets the consumer. Pass |consumer| as NULL to remove |
| 671 // the reference to the consumer; then, once this method returns, | 652 // the reference to the consumer; then, once this method returns, |
| 672 // CaptureMachine will no longer invoke callbacks on the old consumer from any | 653 // CaptureMachine will no longer invoke callbacks on the old consumer from any |
| 673 // thread. | 654 // thread. |
| 674 void SetConsumer(media::VideoCaptureDevice::EventHandler* consumer); | 655 void SetConsumer(media::VideoCaptureDevice::EventHandler* consumer); |
| 675 | 656 |
| 676 // Asynchronous requests to change CaptureMachine state. | 657 // Asynchronous requests to change CaptureMachine state. |
| 677 void Allocate(int width, int height, int frame_rate); | 658 void Allocate(int width, int height, int frame_rate); |
| 678 void Start(); | 659 void Start(); |
| 679 void Stop(); | 660 void Stop(); |
| 680 void DeAllocate(); | 661 void DeAllocate(); |
| 681 | 662 |
| 663 // Snapshot result events. | |
| 664 void OnSnapshotComplete(int frame_number, | |
| 665 const base::Time& start_time, | |
| 666 const base::TimeDelta& duration, | |
| 667 const SkBitmap& frame); | |
| 668 void OnSnapshotComplete(int frame_number, | |
| 669 const base::Time& start_time, | |
| 670 const base::TimeDelta& duration, | |
| 671 media::VideoFrame* frame); | |
| 672 void OnSnapshotFailed(SnapshotError error, | |
|
scherkus (not reviewing)
2013/02/05 22:40:44
hmm.... I believe one of my original aspirations f
ncarter (slow)
2013/02/06 23:54:44
I considered your suggested approach when writing
scherkus (not reviewing)
2013/02/07 18:25:03
SGTM
| |
| 673 int frame_number); | |
| 674 | |
| 682 private: | 675 private: |
| 683 friend class base::RefCountedThreadSafe<CaptureMachine, CaptureMachine>; | 676 friend class base::RefCountedThreadSafe<CaptureMachine, CaptureMachine>; |
| 684 | 677 |
| 685 // Flag indicating current state. | 678 // Flag indicating current state. |
| 686 enum State { | 679 enum State { |
| 687 kIdle, | 680 kIdle, |
| 688 kAllocated, | 681 kAllocated, |
| 689 kCapturing, | 682 kCapturing, |
| 690 kError, | 683 kError, |
| 691 kDestroyed | 684 kDestroyed |
| 692 }; | 685 }; |
| 693 | 686 |
| 694 virtual ~CaptureMachine(); | 687 virtual ~CaptureMachine(); |
| 695 | 688 |
| 696 void TransitionStateTo(State next_state); | 689 void TransitionStateTo(State next_state); |
| 697 | 690 |
| 698 // Stops capturing and notifies consumer_ of an error state. | 691 // Stops capturing and notifies consumer_ of an error state. |
| 699 void Error(); | 692 void Error(); |
| 700 | 693 |
| 701 // Schedules the next frame capture off of the system clock, skipping frames | 694 // Schedules the next frame capture off of the system clock, skipping frames |
| 702 // to catch-up if necessary. | 695 // to catch-up if necessary. |
| 703 void ScheduleNextFrameCapture(); | 696 void ScheduleNextFrameCapture(); |
| 704 | 697 |
| 705 // The glue between the pipeline stages. | 698 // The glue between the pipeline stages. |
| 706 void StartSnapshot(); | 699 void StartSnapshot(); |
| 707 void SnapshotComplete(int frame_number, | 700 bool FinishSnapshot(); |
| 708 const base::Time& start_time, | 701 void SnapshotCompleteBitmap(int frame_number, |
| 709 BackingStoreCopier::Result result, | 702 const base::Time& start_time, |
| 710 const SkBitmap& capture, | 703 const base::TimeDelta& duration, |
| 711 const base::Time& capture_time); | 704 const SkBitmap& frame); |
| 705 void SnapshotCompleteVideoFrame(int frame_number, | |
| 706 const base::Time& start_time, | |
| 707 const base::TimeDelta& duration, | |
| 708 media::VideoFrame* frame); | |
| 709 void SnapshotFailed(SnapshotError error, int frame_number); | |
| 710 | |
| 712 void RenderComplete(int frame_number, | 711 void RenderComplete(int frame_number, |
| 713 const base::Time& capture_time, | 712 const base::Time& capture_time, |
| 714 const SkBitmap* frame_buffer); | 713 const SkBitmap* frame_buffer); |
| 715 void DeliverComplete(const SkBitmap* frame_buffer); | 714 void DeliverComplete(const SkBitmap* frame_buffer); |
| 716 | 715 |
| 717 // Specialized RefCounted traits for CaptureMachine, so that operator delete | 716 // Specialized RefCounted traits for CaptureMachine, so that operator delete |
| 718 // is called from an "outside" thread. See comments for "traits" in | 717 // is called from an "outside" thread. See comments for "traits" in |
| 719 // base/memory/ref_counted.h. | 718 // base/memory/ref_counted.h. |
| 720 static void Destruct(const CaptureMachine* x); | 719 static void Destruct(const CaptureMachine* x); |
| 721 static void DeleteFromOutsideThread(const CaptureMachine* x); | 720 static void DeleteFromOutsideThread(const CaptureMachine* x); |
| (...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 789 if (width < kMinFrameWidth || height < kMinFrameHeight) { | 788 if (width < kMinFrameWidth || height < kMinFrameHeight) { |
| 790 DVLOG(1) << "invalid width (" << width << ") and/or height (" | 789 DVLOG(1) << "invalid width (" << width << ") and/or height (" |
| 791 << height << ")"; | 790 << height << ")"; |
| 792 Error(); | 791 Error(); |
| 793 return; | 792 return; |
| 794 } | 793 } |
| 795 | 794 |
| 796 settings_.width = width; | 795 settings_.width = width; |
| 797 settings_.height = height; | 796 settings_.height = height; |
| 798 settings_.frame_rate = frame_rate; | 797 settings_.frame_rate = frame_rate; |
| 798 // Sets the color format used by OnIncomingCapturedFrame. | |
|
scherkus (not reviewing)
2013/02/05 22:40:44
nit: () to function names
ncarter (slow)
2013/02/06 23:54:44
Done.
| |
| 799 // Does not apply to OnIncomingCapturedVideoFrame. | |
| 799 settings_.color = media::VideoCaptureCapability::kARGB; | 800 settings_.color = media::VideoCaptureCapability::kARGB; |
| 800 settings_.expected_capture_delay = 0; | 801 settings_.expected_capture_delay = 0; |
| 801 settings_.interlaced = false; | 802 settings_.interlaced = false; |
| 802 | 803 |
| 803 capture_period_ = base::TimeDelta::FromMicroseconds( | 804 capture_period_ = base::TimeDelta::FromMicroseconds( |
| 804 1000000.0 / settings_.frame_rate + 0.5); | 805 1000000.0 / settings_.frame_rate + 0.5); |
| 805 | 806 |
| 806 consumer_.OnFrameInfo(settings_); | 807 consumer_.OnFrameInfo(settings_); |
| 807 | 808 |
| 808 TransitionStateTo(kAllocated); | 809 TransitionStateTo(kAllocated); |
| (...skipping 124 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 933 void CaptureMachine::StartSnapshot() { | 934 void CaptureMachine::StartSnapshot() { |
| 934 DCHECK_EQ(manager_thread_.message_loop(), MessageLoop::current()); | 935 DCHECK_EQ(manager_thread_.message_loop(), MessageLoop::current()); |
| 935 | 936 |
| 936 if (state_ != kCapturing) { | 937 if (state_ != kCapturing) { |
| 937 return; | 938 return; |
| 938 } | 939 } |
| 939 | 940 |
| 940 if (!is_snapshotting_) { | 941 if (!is_snapshotting_) { |
| 941 is_snapshotting_ = true; | 942 is_snapshotting_ = true; |
| 942 | 943 |
| 943 const BackingStoreCopier::DoneCB& done_cb = | 944 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, |
|
scherkus (not reviewing)
2013/02/05 22:40:44
nit: formatting + indentation is incorrect (techni
ncarter (slow)
2013/02/06 23:54:44
My reading of the style guide allows what I put or
scherkus (not reviewing)
2013/02/07 18:25:03
While most media-ish code uses [B1] (having more r
| |
| 944 media::BindToLoop(manager_thread_.message_loop_proxy(), | |
| 945 base::Bind(&CaptureMachine::SnapshotComplete, this, | |
| 946 frame_number_, base::Time::Now())); | |
| 947 const base::Closure& start_cb = | |
| 948 base::Bind(&BackingStoreCopier::StartCopy, | 945 base::Bind(&BackingStoreCopier::StartCopy, |
| 949 base::Unretained(&copier_), | 946 base::Unretained(&copier_), make_scoped_refptr(this), |
| 950 frame_number_, settings_.width, settings_.height, done_cb); | 947 frame_number_, settings_.width, settings_.height) |
| 951 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, start_cb); | 948 ); |
| 952 } | 949 } |
| 953 | 950 |
| 954 ScheduleNextFrameCapture(); | 951 ScheduleNextFrameCapture(); |
| 955 } | 952 } |
| 956 | 953 |
| 957 void CaptureMachine::SnapshotComplete(int frame_number, | 954 bool CaptureMachine::FinishSnapshot() { |
| 958 const base::Time& start_time, | 955 DCHECK_EQ(manager_thread_.message_loop(), MessageLoop::current()); |
| 959 BackingStoreCopier::Result result, | 956 |
| 960 const SkBitmap& capture, | 957 DCHECK(is_snapshotting_); |
| 961 const base::Time& capture_time) { | 958 is_snapshotting_ = false; |
| 959 | |
| 960 return state_ == kCapturing; | |
| 961 } | |
| 962 | |
| 963 void CaptureMachine::OnSnapshotComplete(int frame_number, | |
| 964 const base::Time& start_time, | |
| 965 const base::TimeDelta& duration, | |
| 966 const SkBitmap& frame) { | |
| 967 manager_thread_.message_loop()->PostTask(FROM_HERE, | |
| 968 base::Bind(&CaptureMachine::SnapshotCompleteBitmap, this, | |
|
scherkus (not reviewing)
2013/02/05 22:40:44
ditto on formatting
ncarter (slow)
2013/02/06 23:54:44
Done.
| |
| 969 frame_number, start_time, duration, frame)); | |
|
scherkus (not reviewing)
2013/02/05 22:40:44
FYI the SkBitmap copy constructor says the pixels
ncarter (slow)
2013/02/06 23:54:44
The pixel-sharing behavior is intended. Think of S
| |
| 970 } | |
| 971 | |
| 972 void CaptureMachine::SnapshotCompleteBitmap(int frame_number, | |
| 973 const base::Time& start_time, | |
| 974 const base::TimeDelta& duration, | |
| 975 const SkBitmap& capture) { | |
| 976 if (!FinishSnapshot()) | |
| 977 return; | |
| 978 | |
| 979 UMA_HISTOGRAM_TIMES("TabCapture.SnapshotTime", duration); | |
| 980 if (num_renders_pending_ <= 1) { | |
| 981 ++num_renders_pending_; | |
| 982 renderer_.Render( | |
| 983 frame_number, | |
| 984 capture, | |
| 985 settings_.width, settings_.height, | |
| 986 media::BindToLoop(manager_thread_.message_loop_proxy(), | |
| 987 base::Bind(&CaptureMachine::RenderComplete, this, | |
| 988 frame_number, start_time + duration))); | |
| 989 } | |
| 990 } | |
| 991 | |
| 992 void CaptureMachine::OnSnapshotComplete(int frame_number, | |
| 993 const base::Time& start_time, | |
| 994 const base::TimeDelta& duration, | |
| 995 media::VideoFrame* frame) { | |
| 996 manager_thread_.message_loop()->PostTask(FROM_HERE, | |
|
scherkus (not reviewing)
2013/02/05 22:40:44
ditto on formatting
ncarter (slow)
2013/02/06 23:54:44
Done.
| |
| 997 base::Bind(&CaptureMachine::SnapshotCompleteVideoFrame, this, | |
| 998 frame_number, start_time, duration, | |
| 999 make_scoped_refptr(frame))); | |
| 1000 } | |
| 1001 | |
| 1002 void CaptureMachine::SnapshotCompleteVideoFrame( | |
| 1003 int frame_number, | |
| 1004 const base::Time& start_time, | |
| 1005 const base::TimeDelta& duration, | |
| 1006 media::VideoFrame* frame) { | |
| 1007 if (!FinishSnapshot()) | |
| 1008 return; | |
| 1009 | |
| 1010 UMA_HISTOGRAM_TIMES("TabCapture.SnapshotTime", duration); | |
| 1011 | |
| 1012 deliverer_.Deliver(frame_number, frame, start_time + duration); | |
| 1013 } | |
| 1014 | |
| 1015 void CaptureMachine::OnSnapshotFailed(CaptureMachine::SnapshotError error, | |
| 1016 int frame_number) { | |
| 1017 manager_thread_.message_loop()->PostTask(FROM_HERE, | |
|
scherkus (not reviewing)
2013/02/05 22:40:44
ditto on formatting
ncarter (slow)
2013/02/06 23:54:44
Done.
| |
| 1018 base::Bind(&CaptureMachine::SnapshotFailed, this, error, frame_number)); | |
| 1019 } | |
| 1020 | |
| 1021 void CaptureMachine::SnapshotFailed(CaptureMachine::SnapshotError error, | |
| 1022 int frame_number) { | |
| 962 DCHECK_EQ(manager_thread_.message_loop(), MessageLoop::current()); | 1023 DCHECK_EQ(manager_thread_.message_loop(), MessageLoop::current()); |
| 963 | 1024 |
| 964 DCHECK(is_snapshotting_); | 1025 DCHECK(is_snapshotting_); |
| 965 is_snapshotting_ = false; | 1026 is_snapshotting_ = false; |
| 966 | 1027 |
| 967 if (state_ != kCapturing) { | 1028 if (state_ != kCapturing) { |
|
scherkus (not reviewing)
2013/02/05 22:40:44
you can use FinishSnapshot() here
ncarter (slow)
2013/02/06 23:54:44
Done.
| |
| 968 return; | 1029 return; |
| 969 } | 1030 } |
| 970 | 1031 |
| 971 switch (result) { | 1032 if (error == NO_SOURCE) |
| 972 case BackingStoreCopier::OK: | 1033 Error(); |
| 973 UMA_HISTOGRAM_TIMES("TabCapture.SnapshotTime", | |
| 974 base::Time::Now() - start_time); | |
| 975 if (num_renders_pending_ <= 1) { | |
| 976 ++num_renders_pending_; | |
| 977 DCHECK(!capture_time.is_null()); | |
| 978 renderer_.Render( | |
| 979 frame_number, | |
| 980 capture, | |
| 981 settings_.width, settings_.height, | |
| 982 media::BindToLoop(manager_thread_.message_loop_proxy(), | |
| 983 base::Bind(&CaptureMachine::RenderComplete, this, | |
| 984 frame_number, capture_time))); | |
| 985 } | |
| 986 break; | |
| 987 | |
| 988 case BackingStoreCopier::TRANSIENT_ERROR: | |
| 989 // Skip this frame. | |
| 990 break; | |
| 991 | |
| 992 case BackingStoreCopier::NO_SOURCE: | |
| 993 DVLOG(1) << "no capture source"; | |
| 994 Error(); | |
| 995 break; | |
| 996 } | |
| 997 } | 1034 } |
| 998 | 1035 |
| 999 void CaptureMachine::RenderComplete(int frame_number, | 1036 void CaptureMachine::RenderComplete(int frame_number, |
| 1000 const base::Time& capture_time, | 1037 const base::Time& capture_time, |
| 1001 const SkBitmap* frame_buffer) { | 1038 const SkBitmap* frame_buffer) { |
| 1002 DCHECK_EQ(manager_thread_.message_loop(), MessageLoop::current()); | 1039 DCHECK_EQ(manager_thread_.message_loop(), MessageLoop::current()); |
| 1003 | 1040 |
| 1004 --num_renders_pending_; | 1041 --num_renders_pending_; |
| 1005 DCHECK_LE(0, num_renders_pending_); | 1042 DCHECK_LE(0, num_renders_pending_); |
| 1006 | 1043 |
| 1007 if (state_ != kCapturing || !frame_buffer) { | 1044 if (state_ != kCapturing || !frame_buffer) { |
| 1008 return; | 1045 return; |
| 1009 } | 1046 } |
| 1010 | 1047 |
| 1011 DCHECK(!capture_time.is_null()); | 1048 DCHECK(!capture_time.is_null()); |
| 1012 DCHECK(frame_buffer); | 1049 DCHECK(frame_buffer); |
| 1013 deliverer_.Deliver( | 1050 deliverer_.Deliver( |
| 1014 frame_number, *frame_buffer, capture_time, | 1051 frame_number, *frame_buffer, capture_time, |
| 1015 base::Bind(&CaptureMachine::DeliverComplete, this, frame_buffer)); | 1052 base::Bind(&CaptureMachine::DeliverComplete, this, frame_buffer)); |
| 1016 } | 1053 } |
| 1017 | 1054 |
| 1018 void CaptureMachine::DeliverComplete(const SkBitmap* frame_buffer) { | 1055 void CaptureMachine::DeliverComplete(const SkBitmap* frame_buffer) { |
| 1019 renderer_.Release(frame_buffer); | 1056 renderer_.Release(frame_buffer); |
| 1020 } | 1057 } |
| 1021 | 1058 |
| 1059 void BackingStoreCopier::StartCopy(CaptureMachine* consumer, | |
| 1060 int frame_number, | |
| 1061 int desired_width, | |
| 1062 int desired_height) { | |
| 1063 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 1064 | |
| 1065 RenderWidgetHost* rwh; | |
| 1066 if (rwh_for_testing_) { | |
| 1067 rwh = rwh_for_testing_; | |
| 1068 } else { | |
| 1069 if (!web_contents()) { // No source yet. | |
| 1070 LookUpAndObserveWebContents(); | |
| 1071 if (!web_contents()) { // No source ever. | |
| 1072 consumer->OnSnapshotFailed(CaptureMachine::NO_SOURCE, frame_number); | |
| 1073 return; | |
| 1074 } | |
| 1075 } | |
| 1076 | |
| 1077 if (fullscreen_widget_id_ != MSG_ROUTING_NONE) { | |
| 1078 RenderProcessHost* process = web_contents()->GetRenderProcessHost(); | |
| 1079 rwh = process ? process->GetRenderWidgetHostByID(fullscreen_widget_id_) | |
| 1080 : NULL; | |
| 1081 } else { | |
| 1082 rwh = web_contents()->GetRenderViewHost(); | |
| 1083 } | |
| 1084 | |
| 1085 if (!rwh) { | |
| 1086 // Transient failure state (e.g., a RenderView is being replaced). | |
| 1087 consumer->OnSnapshotFailed(CaptureMachine::TRANSIENT_ERROR, frame_number); | |
| 1088 return; | |
| 1089 } | |
| 1090 } | |
| 1091 | |
| 1092 gfx::Size fitted_size; | |
| 1093 if (RenderWidgetHostView* const view = rwh->GetView()) { | |
| 1094 const gfx::Size& view_size = view->GetViewBounds().size(); | |
| 1095 if (!view_size.IsEmpty()) { | |
| 1096 CalculateFittedSize(view_size.width(), view_size.height(), | |
| 1097 desired_width, desired_height, | |
| 1098 &fitted_size); | |
| 1099 } | |
| 1100 if (view_size != last_view_size_) { | |
| 1101 last_view_size_ = view_size; | |
| 1102 | |
| 1103 // Measure the number of kilopixels. | |
| 1104 UMA_HISTOGRAM_COUNTS_10000( | |
| 1105 "TabCapture.ViewChangeKiloPixels", | |
| 1106 view_size.width() * view_size.height() / 1024); | |
| 1107 } | |
| 1108 } | |
| 1109 | |
| 1110 TRACE_EVENT_ASYNC_BEGIN1("mirroring", "Capture", this, | |
| 1111 "frame_number", frame_number); | |
| 1112 if (rwh->CanCopyToVideoFrame()) { | |
| 1113 rwh->CopyFromBackingStoreToVideoFrame( | |
| 1114 gfx::Rect(), | |
| 1115 gfx::Size(desired_width, desired_height), | |
| 1116 base::Bind(&BackingStoreCopier::DidCopyFromBackingStoreToVideoFrame, | |
| 1117 base::Unretained(this), make_scoped_refptr(consumer), | |
| 1118 frame_number, base::Time::Now())); | |
| 1119 } else { | |
| 1120 rwh->CopyFromBackingStore( | |
| 1121 gfx::Rect(), | |
| 1122 fitted_size, | |
| 1123 base::Bind(&BackingStoreCopier::DidCopyFromBackingStore, | |
| 1124 base::Unretained(this), make_scoped_refptr(consumer), | |
| 1125 frame_number, base::Time::Now())); | |
| 1126 } | |
| 1127 // TODO(miu): When a tab is not visible to the user, rendering stops. For | |
| 1128 // mirroring, however, it's important that rendering continues to happen. | |
| 1129 } | |
| 1130 | |
| 1131 void BackingStoreCopier::DidCopyFromBackingStore( | |
| 1132 CaptureMachine* consumer, | |
| 1133 int frame_number, | |
| 1134 const base::Time& start_time, | |
| 1135 bool success, | |
| 1136 const SkBitmap& frame) { | |
| 1137 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 1138 // Note: No restriction on which thread invokes this method but, currently, | |
| 1139 // it's always the UI BrowserThread. | |
| 1140 TRACE_EVENT_ASYNC_END1("mirroring", "Capture", this, | |
| 1141 "frame_number", frame_number); | |
| 1142 | |
| 1143 if (success) { | |
| 1144 consumer->OnSnapshotComplete( | |
| 1145 frame_number, start_time, base::Time::Now() - start_time, frame); | |
| 1146 } else { | |
| 1147 // Capture can fail due to transient issues, so just skip this frame. | |
| 1148 DVLOG(1) << "CopyFromBackingStore was not successful; skipping frame."; | |
| 1149 consumer->OnSnapshotFailed(CaptureMachine::TRANSIENT_ERROR, frame_number); | |
| 1150 } | |
| 1151 } | |
| 1152 | |
| 1153 void BackingStoreCopier::DidCopyFromBackingStoreToVideoFrame( | |
| 1154 CaptureMachine* consumer, | |
| 1155 int frame_number, | |
| 1156 const base::Time& start_time, | |
| 1157 media::VideoFrame* frame) { | |
| 1158 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 1159 // Note: No restriction on which thread invokes this method but, currently, | |
| 1160 // it's always the UI BrowserThread. | |
| 1161 TRACE_EVENT_ASYNC_END1("mirroring", "Capture", this, | |
| 1162 "frame_number", frame_number); | |
| 1163 | |
| 1164 if (frame && frame->format() != media::VideoFrame::INVALID && | |
| 1165 frame->format() != media::VideoFrame::EMPTY) { | |
| 1166 consumer->OnSnapshotComplete( | |
| 1167 frame_number, start_time, base::Time::Now() - start_time, frame); | |
| 1168 } else { | |
| 1169 // Capture can fail due to transient issues, so just skip this frame. | |
| 1170 DVLOG(1) << "CopyFromBackingStoreToVideoFrame failure; skipping frame."; | |
| 1171 consumer->OnSnapshotFailed( | |
| 1172 CaptureMachine::TRANSIENT_ERROR, frame_number); | |
| 1173 } | |
| 1174 } | |
| 1175 | |
| 1022 WebContentsVideoCaptureDevice::WebContentsVideoCaptureDevice( | 1176 WebContentsVideoCaptureDevice::WebContentsVideoCaptureDevice( |
| 1023 const media::VideoCaptureDevice::Name& name, | 1177 const media::VideoCaptureDevice::Name& name, |
| 1024 int render_process_id, int render_view_id) | 1178 int render_process_id, int render_view_id) |
| 1025 : device_name_(name), | 1179 : device_name_(name), |
| 1026 capturer_(new CaptureMachine(render_process_id, render_view_id)) {} | 1180 capturer_(new CaptureMachine(render_process_id, render_view_id)) {} |
| 1027 | 1181 |
| 1028 WebContentsVideoCaptureDevice::WebContentsVideoCaptureDevice( | 1182 WebContentsVideoCaptureDevice::WebContentsVideoCaptureDevice( |
| 1029 RenderWidgetHost* test_source, const base::Closure& destroy_cb) | 1183 RenderWidgetHost* test_source, const base::Closure& destroy_cb) |
| 1030 : capturer_(new CaptureMachine(-1, -1)) { | 1184 : capturer_(new CaptureMachine(-1, -1)) { |
| 1031 device_name_.device_name = "WebContentsForTesting"; | 1185 device_name_.device_name = "WebContentsForTesting"; |
| (...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1087 capturer_->SetConsumer(NULL); | 1241 capturer_->SetConsumer(NULL); |
| 1088 capturer_->DeAllocate(); | 1242 capturer_->DeAllocate(); |
| 1089 } | 1243 } |
| 1090 | 1244 |
| 1091 const media::VideoCaptureDevice::Name& | 1245 const media::VideoCaptureDevice::Name& |
| 1092 WebContentsVideoCaptureDevice::device_name() { | 1246 WebContentsVideoCaptureDevice::device_name() { |
| 1093 return device_name_; | 1247 return device_name_; |
| 1094 } | 1248 } |
| 1095 | 1249 |
| 1096 } // namespace content | 1250 } // namespace content |
| OLD | NEW |