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 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 int measured_fps = | |
|
miu
2013/01/04 18:05:27
nit: It was useful to see the fractional part in t
| |
| 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("TabCapture.FrameRate", measured_fps); | |
| 613 VLOG(1) << "Current measured frame rate for CaptureMachine@" << this | |
| 614 << " is " << measured_fps << " FPS."; | |
| 583 last_frame_rate_log_time_ = now; | 615 last_frame_rate_log_time_ = now; |
| 584 count_frames_rendered_ = 0; | 616 count_frames_rendered_ = 0; |
| 585 } else { | 617 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 } | 618 } |
| 597 } | 619 } |
| 598 | 620 |
| 599 // All done. | 621 // All done. |
| 600 done_cb.Run(); | 622 done_cb.Run(); |
| 601 } | 623 } |
| 602 | 624 |
| 603 } // namespace | 625 } // namespace |
| 604 | 626 |
| 605 // 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. |
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 652 // Stops capturing and notifies consumer_ of an error state. | 674 // Stops capturing and notifies consumer_ of an error state. |
| 653 void Error(); | 675 void Error(); |
| 654 | 676 |
| 655 // Schedules the next frame capture off of the system clock, skipping frames | 677 // Schedules the next frame capture off of the system clock, skipping frames |
| 656 // to catch-up if necessary. | 678 // to catch-up if necessary. |
| 657 void ScheduleNextFrameCapture(); | 679 void ScheduleNextFrameCapture(); |
| 658 | 680 |
| 659 // The glue between the pipeline stages. | 681 // The glue between the pipeline stages. |
| 660 void StartSnapshot(); | 682 void StartSnapshot(); |
| 661 void SnapshotComplete(int frame_number, | 683 void SnapshotComplete(int frame_number, |
| 684 const base::Time& start_time, | |
| 662 BackingStoreCopier::Result result, | 685 BackingStoreCopier::Result result, |
| 663 scoped_ptr<skia::PlatformBitmap> capture, | 686 scoped_ptr<skia::PlatformBitmap> capture, |
| 664 const base::Time& capture_time); | 687 const base::Time& capture_time); |
| 665 void RenderComplete(int frame_number, | 688 void RenderComplete(int frame_number, |
| 666 const base::Time& capture_time, | 689 const base::Time& capture_time, |
| 667 const SkBitmap* frame_buffer); | 690 const SkBitmap* frame_buffer); |
| 668 void DeliverComplete(const SkBitmap* frame_buffer); | 691 void DeliverComplete(const SkBitmap* frame_buffer); |
| 669 | 692 |
| 670 // Specialized RefCounted traits for CaptureMachine, so that operator delete | 693 // Specialized RefCounted traits for CaptureMachine, so that operator delete |
| 671 // is called from an "outside" thread. See comments for "traits" in | 694 // 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) { | 912 if (state_ != kCapturing) { |
| 890 return; | 913 return; |
| 891 } | 914 } |
| 892 | 915 |
| 893 if (!is_snapshotting_) { | 916 if (!is_snapshotting_) { |
| 894 is_snapshotting_ = true; | 917 is_snapshotting_ = true; |
| 895 | 918 |
| 896 const BackingStoreCopier::DoneCB& done_cb = | 919 const BackingStoreCopier::DoneCB& done_cb = |
| 897 media::BindToLoop(manager_thread_.message_loop_proxy(), | 920 media::BindToLoop(manager_thread_.message_loop_proxy(), |
| 898 base::Bind(&CaptureMachine::SnapshotComplete, this, | 921 base::Bind(&CaptureMachine::SnapshotComplete, this, |
| 899 frame_number_)); | 922 frame_number_, base::Time::Now())); |
| 900 const base::Closure& start_cb = | 923 const base::Closure& start_cb = |
| 901 base::Bind(&BackingStoreCopier::StartCopy, | 924 base::Bind(&BackingStoreCopier::StartCopy, |
| 902 base::Unretained(&copier_), | 925 base::Unretained(&copier_), |
| 903 frame_number_, settings_.width, settings_.height, done_cb); | 926 frame_number_, settings_.width, settings_.height, done_cb); |
| 904 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, start_cb); | 927 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, start_cb); |
| 905 } | 928 } |
| 906 | 929 |
| 907 ScheduleNextFrameCapture(); | 930 ScheduleNextFrameCapture(); |
| 908 } | 931 } |
| 909 | 932 |
| 910 void CaptureMachine::SnapshotComplete(int frame_number, | 933 void CaptureMachine::SnapshotComplete(int frame_number, |
| 934 const base::Time& start_time, | |
| 911 BackingStoreCopier::Result result, | 935 BackingStoreCopier::Result result, |
| 912 scoped_ptr<skia::PlatformBitmap> capture, | 936 scoped_ptr<skia::PlatformBitmap> capture, |
| 913 const base::Time& capture_time) { | 937 const base::Time& capture_time) { |
| 914 DCHECK_EQ(manager_thread_.message_loop(), MessageLoop::current()); | 938 DCHECK_EQ(manager_thread_.message_loop(), MessageLoop::current()); |
| 915 | 939 |
| 916 DCHECK(is_snapshotting_); | 940 DCHECK(is_snapshotting_); |
| 917 is_snapshotting_ = false; | 941 is_snapshotting_ = false; |
| 918 | 942 |
| 919 if (state_ != kCapturing) { | 943 if (state_ != kCapturing) { |
| 920 return; | 944 return; |
| 921 } | 945 } |
| 922 | 946 |
| 923 switch (result) { | 947 switch (result) { |
| 924 case BackingStoreCopier::OK: | 948 case BackingStoreCopier::OK: |
| 949 UMA_HISTOGRAM_TIMES("TabCapture.SnapshotTime", | |
| 950 base::Time::Now() - start_time); | |
| 925 if (num_renders_pending_ <= 1) { | 951 if (num_renders_pending_ <= 1) { |
| 926 ++num_renders_pending_; | 952 ++num_renders_pending_; |
| 927 DCHECK(capture); | 953 DCHECK(capture); |
| 928 DCHECK(!capture_time.is_null()); | 954 DCHECK(!capture_time.is_null()); |
| 929 renderer_.Render( | 955 renderer_.Render( |
| 930 frame_number, | 956 frame_number, |
| 931 capture.Pass(), | 957 capture.Pass(), |
| 932 settings_.width, settings_.height, | 958 settings_.width, settings_.height, |
| 933 media::BindToLoop(manager_thread_.message_loop_proxy(), | 959 media::BindToLoop(manager_thread_.message_loop_proxy(), |
| 934 base::Bind(&CaptureMachine::RenderComplete, this, | 960 base::Bind(&CaptureMachine::RenderComplete, this, |
| (...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1038 capturer_->SetConsumer(NULL); | 1064 capturer_->SetConsumer(NULL); |
| 1039 capturer_->DeAllocate(); | 1065 capturer_->DeAllocate(); |
| 1040 } | 1066 } |
| 1041 | 1067 |
| 1042 const media::VideoCaptureDevice::Name& | 1068 const media::VideoCaptureDevice::Name& |
| 1043 WebContentsVideoCaptureDevice::device_name() { | 1069 WebContentsVideoCaptureDevice::device_name() { |
| 1044 return device_name_; | 1070 return device_name_; |
| 1045 } | 1071 } |
| 1046 | 1072 |
| 1047 } // namespace content | 1073 } // namespace content |
| OLD | NEW |