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 |