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 |