| 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 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 43 #include <algorithm> | 43 #include <algorithm> |
| 44 #include <string> | 44 #include <string> |
| 45 | 45 |
| 46 #include "base/basictypes.h" | 46 #include "base/basictypes.h" |
| 47 #include "base/bind.h" | 47 #include "base/bind.h" |
| 48 #include "base/bind_helpers.h" | 48 #include "base/bind_helpers.h" |
| 49 #include "base/callback_forward.h" | 49 #include "base/callback_forward.h" |
| 50 #include "base/debug/trace_event.h" | 50 #include "base/debug/trace_event.h" |
| 51 #include "base/logging.h" | 51 #include "base/logging.h" |
| 52 #include "base/memory/scoped_ptr.h" | 52 #include "base/memory/scoped_ptr.h" |
| 53 #include "base/metrics/histogram.h" |
| 53 #include "base/stringprintf.h" | 54 #include "base/stringprintf.h" |
| 54 #include "base/synchronization/lock.h" | 55 #include "base/synchronization/lock.h" |
| 55 #include "base/threading/thread.h" | 56 #include "base/threading/thread.h" |
| 56 #include "base/time.h" | 57 #include "base/time.h" |
| 57 #include "content/browser/renderer_host/media/web_contents_capture_util.h" | 58 #include "content/browser/renderer_host/media/web_contents_capture_util.h" |
| 58 #include "content/public/browser/browser_thread.h" | 59 #include "content/public/browser/browser_thread.h" |
| 59 #include "content/public/browser/render_process_host.h" | 60 #include "content/public/browser/render_process_host.h" |
| 60 #include "content/public/browser/render_view_host.h" | 61 #include "content/public/browser/render_view_host.h" |
| 61 #include "content/public/browser/render_widget_host_view.h" | 62 #include "content/public/browser/render_widget_host_view.h" |
| 62 #include "content/public/browser/web_contents.h" | 63 #include "content/public/browser/web_contents.h" |
| (...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 165 void LookUpAndObserveWebContents(); | 166 void LookUpAndObserveWebContents(); |
| 166 | 167 |
| 167 void CopyFromBackingStoreComplete(int frame_number, | 168 void CopyFromBackingStoreComplete(int frame_number, |
| 168 scoped_ptr<skia::PlatformBitmap> capture, | 169 scoped_ptr<skia::PlatformBitmap> capture, |
| 169 const DoneCB& done_cb, bool success); | 170 const DoneCB& done_cb, bool success); |
| 170 | 171 |
| 171 // The "starting point" to find the capture source. | 172 // The "starting point" to find the capture source. |
| 172 const int render_process_id_; | 173 const int render_process_id_; |
| 173 const int render_view_id_; | 174 const int render_view_id_; |
| 174 | 175 |
| 176 // Last known RenderView size. |
| 177 gfx::Size last_view_size_; |
| 178 |
| 175 // If the following is NULL (normal behavior), the implementation should | 179 // If the following is NULL (normal behavior), the implementation should |
| 176 // access RenderWidgetHost via web_contents(). | 180 // access RenderWidgetHost via web_contents(). |
| 177 RenderWidgetHost* rwh_for_testing_; | 181 RenderWidgetHost* rwh_for_testing_; |
| 178 | 182 |
| 179 DISALLOW_COPY_AND_ASSIGN(BackingStoreCopier); | 183 DISALLOW_COPY_AND_ASSIGN(BackingStoreCopier); |
| 180 }; | 184 }; |
| 181 | 185 |
| 182 // Renders captures (from the backing store) into video frame buffers on a | 186 // Renders captures (from the backing store) into video frame buffers on a |
| 183 // separate thread. Manages use of internally-owned video frame buffers. | 187 // separate thread. Manages use of internally-owned video frame buffers. |
| 184 class VideoFrameRenderer { | 188 class VideoFrameRenderer { |
| (...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 253 const base::Time& frame_timestamp, | 257 const base::Time& frame_timestamp, |
| 254 const base::Closure& done_cb); | 258 const base::Closure& done_cb); |
| 255 | 259 |
| 256 base::Thread deliver_thread_; | 260 base::Thread deliver_thread_; |
| 257 SynchronizedConsumer* const consumer_; | 261 SynchronizedConsumer* const consumer_; |
| 258 | 262 |
| 259 // The following keep track of and log the effective frame rate (from the | 263 // The following keep track of and log the effective frame rate (from the |
| 260 // deliver stage) whenever verbose logging is turned on. | 264 // deliver stage) whenever verbose logging is turned on. |
| 261 base::Time last_frame_rate_log_time_; | 265 base::Time last_frame_rate_log_time_; |
| 262 int count_frames_rendered_; | 266 int count_frames_rendered_; |
| 267 int last_frame_number_; |
| 263 | 268 |
| 264 DISALLOW_COPY_AND_ASSIGN(VideoFrameDeliverer); | 269 DISALLOW_COPY_AND_ASSIGN(VideoFrameDeliverer); |
| 265 }; | 270 }; |
| 266 | 271 |
| 267 BackingStoreCopier::BackingStoreCopier(int render_process_id, | 272 BackingStoreCopier::BackingStoreCopier(int render_process_id, |
| 268 int render_view_id) | 273 int render_view_id) |
| 269 : render_process_id_(render_process_id), render_view_id_(render_view_id), | 274 : render_process_id_(render_process_id), render_view_id_(render_view_id), |
| 270 rwh_for_testing_(NULL) {} | 275 rwh_for_testing_(NULL) {} |
| 271 | 276 |
| 272 void BackingStoreCopier::LookUpAndObserveWebContents() { | 277 void BackingStoreCopier::LookUpAndObserveWebContents() { |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 327 } | 332 } |
| 328 | 333 |
| 329 gfx::Size fitted_size; | 334 gfx::Size fitted_size; |
| 330 if (RenderWidgetHostView* const view = rwh->GetView()) { | 335 if (RenderWidgetHostView* const view = rwh->GetView()) { |
| 331 const gfx::Size& view_size = view->GetViewBounds().size(); | 336 const gfx::Size& view_size = view->GetViewBounds().size(); |
| 332 if (!view_size.IsEmpty()) { | 337 if (!view_size.IsEmpty()) { |
| 333 CalculateFittedSize(view_size.width(), view_size.height(), | 338 CalculateFittedSize(view_size.width(), view_size.height(), |
| 334 desired_width, desired_height, | 339 desired_width, desired_height, |
| 335 &fitted_size); | 340 &fitted_size); |
| 336 } | 341 } |
| 342 if (view_size != last_view_size_) { |
| 343 last_view_size_ = view_size; |
| 344 |
| 345 // Measure the number of kilopixels. |
| 346 UMA_HISTOGRAM_COUNTS_10000( |
| 347 "TabCapture.ViewChangeKiloPixels", |
| 348 view_size.width() * view_size.height() / 1024); |
| 349 } |
| 337 } | 350 } |
| 338 | 351 |
| 339 // TODO(miu): Look into tweaking the interface to CopyFromBackingStore, since | 352 // TODO(miu): Look into tweaking the interface to CopyFromBackingStore, since |
| 340 // it seems poor to have to allocate a new skia::PlatformBitmap as an output | 353 // it seems poor to have to allocate a new skia::PlatformBitmap as an output |
| 341 // buffer for each successive frame (rather than reuse buffers). Perhaps | 354 // buffer for each successive frame (rather than reuse buffers). Perhaps |
| 342 // PlatformBitmap itself should only re-Allocate when necessary? | 355 // PlatformBitmap itself should only re-Allocate when necessary? |
| 343 skia::PlatformBitmap* const bitmap = new skia::PlatformBitmap(); | 356 skia::PlatformBitmap* const bitmap = new skia::PlatformBitmap(); |
| 344 scoped_ptr<skia::PlatformBitmap> capture(bitmap); | 357 scoped_ptr<skia::PlatformBitmap> capture(bitmap); |
| 345 rwh->CopyFromBackingStore( | 358 rwh->CopyFromBackingStore( |
| 346 gfx::Rect(), | 359 gfx::Rect(), |
| (...skipping 186 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 533 | 546 |
| 534 void SynchronizedConsumer::OnIncomingCapturedFrame( | 547 void SynchronizedConsumer::OnIncomingCapturedFrame( |
| 535 const uint8* pixels, int size, const base::Time& timestamp) { | 548 const uint8* pixels, int size, const base::Time& timestamp) { |
| 536 base::AutoLock guard(consumer_lock_); | 549 base::AutoLock guard(consumer_lock_); |
| 537 if (wrapped_consumer_) { | 550 if (wrapped_consumer_) { |
| 538 wrapped_consumer_->OnIncomingCapturedFrame(pixels, size, timestamp); | 551 wrapped_consumer_->OnIncomingCapturedFrame(pixels, size, timestamp); |
| 539 } | 552 } |
| 540 } | 553 } |
| 541 | 554 |
| 542 VideoFrameDeliverer::VideoFrameDeliverer(SynchronizedConsumer* consumer) | 555 VideoFrameDeliverer::VideoFrameDeliverer(SynchronizedConsumer* consumer) |
| 543 : deliver_thread_("WebContentsVideo_DeliverThread"), consumer_(consumer) { | 556 : deliver_thread_("WebContentsVideo_DeliverThread"), |
| 557 consumer_(consumer), |
| 558 last_frame_number_(0) { |
| 544 DCHECK(consumer_); | 559 DCHECK(consumer_); |
| 545 deliver_thread_.Start(); | 560 deliver_thread_.Start(); |
| 546 } | 561 } |
| 547 | 562 |
| 548 void VideoFrameDeliverer::Deliver( | 563 void VideoFrameDeliverer::Deliver( |
| 549 int frame_number, | 564 int frame_number, |
| 550 const SkBitmap& frame_buffer, const base::Time& frame_timestamp, | 565 const SkBitmap& frame_buffer, const base::Time& frame_timestamp, |
| 551 const base::Closure& done_cb) { | 566 const base::Closure& done_cb) { |
| 552 deliver_thread_.message_loop()->PostTask( | 567 deliver_thread_.message_loop()->PostTask( |
| 553 FROM_HERE, | 568 FROM_HERE, |
| (...skipping 14 matching lines...) Expand all Loading... |
| 568 // Send the frame to the consumer. | 583 // Send the frame to the consumer. |
| 569 // Note: The consumer will do an ARGB-->YUV conversion in this callback, | 584 // Note: The consumer will do an ARGB-->YUV conversion in this callback, |
| 570 // blocking the current thread for a bit. | 585 // blocking the current thread for a bit. |
| 571 SkAutoLockPixels frame_buffer_locker(frame_buffer); | 586 SkAutoLockPixels frame_buffer_locker(frame_buffer); |
| 572 consumer_->OnIncomingCapturedFrame( | 587 consumer_->OnIncomingCapturedFrame( |
| 573 static_cast<const uint8*>(frame_buffer.getPixels()), | 588 static_cast<const uint8*>(frame_buffer.getPixels()), |
| 574 frame_buffer.getSize(), | 589 frame_buffer.getSize(), |
| 575 frame_timestamp); | 590 frame_timestamp); |
| 576 | 591 |
| 577 // Log frame rate, if verbose logging is turned on. | 592 // Log frame rate, if verbose logging is turned on. |
| 578 if (VLOG_IS_ON(1)) { | 593 static const base::TimeDelta kFrameRateLogInterval = |
| 579 static const base::TimeDelta kFrameRateLogInterval = | 594 base::TimeDelta::FromSeconds(10); |
| 580 base::TimeDelta::FromSeconds(5); | 595 const base::Time& now = base::Time::Now(); |
| 581 const base::Time& now = base::Time::Now(); | 596 if (last_frame_rate_log_time_.is_null()) { |
| 582 if (last_frame_rate_log_time_.is_null()) { | 597 last_frame_rate_log_time_ = now; |
| 598 count_frames_rendered_ = 0; |
| 599 last_frame_number_ = frame_number; |
| 600 } else { |
| 601 ++count_frames_rendered_; |
| 602 const base::TimeDelta elapsed = now - last_frame_rate_log_time_; |
| 603 if (elapsed >= kFrameRateLogInterval) { |
| 604 const double measured_fps = |
| 605 count_frames_rendered_ / elapsed.InSecondsF(); |
| 606 const int frames_elapsed = frame_number - last_frame_number_; |
| 607 const int count_frames_dropped = frames_elapsed - count_frames_rendered_; |
| 608 DCHECK_LE(0, count_frames_dropped); |
| 609 UMA_HISTOGRAM_PERCENTAGE( |
| 610 "TabCapture.FrameDropPercentage", |
| 611 (count_frames_dropped * 100 + frames_elapsed / 2) / frames_elapsed); |
| 612 UMA_HISTOGRAM_COUNTS( |
| 613 "TabCapture.FrameRate", |
| 614 static_cast<int>(measured_fps)); |
| 615 VLOG(1) << "Current measured frame rate for CaptureMachine@" << this |
| 616 << " is " << measured_fps << " FPS."; |
| 583 last_frame_rate_log_time_ = now; | 617 last_frame_rate_log_time_ = now; |
| 584 count_frames_rendered_ = 0; | 618 count_frames_rendered_ = 0; |
| 585 } else { | 619 last_frame_number_ = frame_number; |
| 586 ++count_frames_rendered_; | |
| 587 const base::TimeDelta elapsed = now - last_frame_rate_log_time_; | |
| 588 if (elapsed >= kFrameRateLogInterval) { | |
| 589 const double measured_fps = | |
| 590 count_frames_rendered_ / elapsed.InSecondsF(); | |
| 591 VLOG(1) << "Current measured frame rate for CaptureMachine@" << this | |
| 592 << " is " << measured_fps << " FPS."; | |
| 593 last_frame_rate_log_time_ = now; | |
| 594 count_frames_rendered_ = 0; | |
| 595 } | |
| 596 } | 620 } |
| 597 } | 621 } |
| 598 | 622 |
| 599 // All done. | 623 // All done. |
| 600 done_cb.Run(); | 624 done_cb.Run(); |
| 601 } | 625 } |
| 602 | 626 |
| 603 } // namespace | 627 } // namespace |
| 604 | 628 |
| 605 // The "meat" of the video capture implementation, which is a ref-counted class. | 629 // The "meat" of the video capture implementation, which is a ref-counted class. |
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 652 // Stops capturing and notifies consumer_ of an error state. | 676 // Stops capturing and notifies consumer_ of an error state. |
| 653 void Error(); | 677 void Error(); |
| 654 | 678 |
| 655 // Schedules the next frame capture off of the system clock, skipping frames | 679 // Schedules the next frame capture off of the system clock, skipping frames |
| 656 // to catch-up if necessary. | 680 // to catch-up if necessary. |
| 657 void ScheduleNextFrameCapture(); | 681 void ScheduleNextFrameCapture(); |
| 658 | 682 |
| 659 // The glue between the pipeline stages. | 683 // The glue between the pipeline stages. |
| 660 void StartSnapshot(); | 684 void StartSnapshot(); |
| 661 void SnapshotComplete(int frame_number, | 685 void SnapshotComplete(int frame_number, |
| 686 const base::Time& start_time, |
| 662 BackingStoreCopier::Result result, | 687 BackingStoreCopier::Result result, |
| 663 scoped_ptr<skia::PlatformBitmap> capture, | 688 scoped_ptr<skia::PlatformBitmap> capture, |
| 664 const base::Time& capture_time); | 689 const base::Time& capture_time); |
| 665 void RenderComplete(int frame_number, | 690 void RenderComplete(int frame_number, |
| 666 const base::Time& capture_time, | 691 const base::Time& capture_time, |
| 667 const SkBitmap* frame_buffer); | 692 const SkBitmap* frame_buffer); |
| 668 void DeliverComplete(const SkBitmap* frame_buffer); | 693 void DeliverComplete(const SkBitmap* frame_buffer); |
| 669 | 694 |
| 670 // Specialized RefCounted traits for CaptureMachine, so that operator delete | 695 // Specialized RefCounted traits for CaptureMachine, so that operator delete |
| 671 // is called from an "outside" thread. See comments for "traits" in | 696 // is called from an "outside" thread. See comments for "traits" in |
| (...skipping 217 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 889 if (state_ != kCapturing) { | 914 if (state_ != kCapturing) { |
| 890 return; | 915 return; |
| 891 } | 916 } |
| 892 | 917 |
| 893 if (!is_snapshotting_) { | 918 if (!is_snapshotting_) { |
| 894 is_snapshotting_ = true; | 919 is_snapshotting_ = true; |
| 895 | 920 |
| 896 const BackingStoreCopier::DoneCB& done_cb = | 921 const BackingStoreCopier::DoneCB& done_cb = |
| 897 media::BindToLoop(manager_thread_.message_loop_proxy(), | 922 media::BindToLoop(manager_thread_.message_loop_proxy(), |
| 898 base::Bind(&CaptureMachine::SnapshotComplete, this, | 923 base::Bind(&CaptureMachine::SnapshotComplete, this, |
| 899 frame_number_)); | 924 frame_number_, base::Time::Now())); |
| 900 const base::Closure& start_cb = | 925 const base::Closure& start_cb = |
| 901 base::Bind(&BackingStoreCopier::StartCopy, | 926 base::Bind(&BackingStoreCopier::StartCopy, |
| 902 base::Unretained(&copier_), | 927 base::Unretained(&copier_), |
| 903 frame_number_, settings_.width, settings_.height, done_cb); | 928 frame_number_, settings_.width, settings_.height, done_cb); |
| 904 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, start_cb); | 929 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, start_cb); |
| 905 } | 930 } |
| 906 | 931 |
| 907 ScheduleNextFrameCapture(); | 932 ScheduleNextFrameCapture(); |
| 908 } | 933 } |
| 909 | 934 |
| 910 void CaptureMachine::SnapshotComplete(int frame_number, | 935 void CaptureMachine::SnapshotComplete(int frame_number, |
| 936 const base::Time& start_time, |
| 911 BackingStoreCopier::Result result, | 937 BackingStoreCopier::Result result, |
| 912 scoped_ptr<skia::PlatformBitmap> capture, | 938 scoped_ptr<skia::PlatformBitmap> capture, |
| 913 const base::Time& capture_time) { | 939 const base::Time& capture_time) { |
| 914 DCHECK_EQ(manager_thread_.message_loop(), MessageLoop::current()); | 940 DCHECK_EQ(manager_thread_.message_loop(), MessageLoop::current()); |
| 915 | 941 |
| 916 DCHECK(is_snapshotting_); | 942 DCHECK(is_snapshotting_); |
| 917 is_snapshotting_ = false; | 943 is_snapshotting_ = false; |
| 918 | 944 |
| 919 if (state_ != kCapturing) { | 945 if (state_ != kCapturing) { |
| 920 return; | 946 return; |
| 921 } | 947 } |
| 922 | 948 |
| 923 switch (result) { | 949 switch (result) { |
| 924 case BackingStoreCopier::OK: | 950 case BackingStoreCopier::OK: |
| 951 UMA_HISTOGRAM_TIMES("TabCapture.SnapshotTime", |
| 952 base::Time::Now() - start_time); |
| 925 if (num_renders_pending_ <= 1) { | 953 if (num_renders_pending_ <= 1) { |
| 926 ++num_renders_pending_; | 954 ++num_renders_pending_; |
| 927 DCHECK(capture); | 955 DCHECK(capture); |
| 928 DCHECK(!capture_time.is_null()); | 956 DCHECK(!capture_time.is_null()); |
| 929 renderer_.Render( | 957 renderer_.Render( |
| 930 frame_number, | 958 frame_number, |
| 931 capture.Pass(), | 959 capture.Pass(), |
| 932 settings_.width, settings_.height, | 960 settings_.width, settings_.height, |
| 933 media::BindToLoop(manager_thread_.message_loop_proxy(), | 961 media::BindToLoop(manager_thread_.message_loop_proxy(), |
| 934 base::Bind(&CaptureMachine::RenderComplete, this, | 962 base::Bind(&CaptureMachine::RenderComplete, this, |
| (...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1038 capturer_->SetConsumer(NULL); | 1066 capturer_->SetConsumer(NULL); |
| 1039 capturer_->DeAllocate(); | 1067 capturer_->DeAllocate(); |
| 1040 } | 1068 } |
| 1041 | 1069 |
| 1042 const media::VideoCaptureDevice::Name& | 1070 const media::VideoCaptureDevice::Name& |
| 1043 WebContentsVideoCaptureDevice::device_name() { | 1071 WebContentsVideoCaptureDevice::device_name() { |
| 1044 return device_name_; | 1072 return device_name_; |
| 1045 } | 1073 } |
| 1046 | 1074 |
| 1047 } // namespace content | 1075 } // namespace content |
| OLD | NEW |