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. Spanning several threads, the process of capturing has been | 7 // performance. Spanning several threads, the process of capturing has been |
| 8 // split up into four conceptual stages: | 8 // split up into four conceptual stages: |
| 9 // | 9 // |
| 10 // 1. Reserve Buffer: Before a frame can be captured, a slot in the client's | 10 // 1. Reserve Buffer: Before a frame can be captured, a slot in the client's |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 62 #include "base/memory/scoped_ptr.h" | 62 #include "base/memory/scoped_ptr.h" |
| 63 #include "base/memory/weak_ptr.h" | 63 #include "base/memory/weak_ptr.h" |
| 64 #include "base/metrics/histogram.h" | 64 #include "base/metrics/histogram.h" |
| 65 #include "base/sequenced_task_runner.h" | 65 #include "base/sequenced_task_runner.h" |
| 66 #include "base/single_thread_task_runner.h" | 66 #include "base/single_thread_task_runner.h" |
| 67 #include "base/threading/thread.h" | 67 #include "base/threading/thread.h" |
| 68 #include "base/threading/thread_checker.h" | 68 #include "base/threading/thread_checker.h" |
| 69 #include "base/time/time.h" | 69 #include "base/time/time.h" |
| 70 #include "build/build_config.h" | 70 #include "build/build_config.h" |
| 71 #include "content/browser/media/capture/cursor_renderer.h" | 71 #include "content/browser/media/capture/cursor_renderer.h" |
| 72 #include "content/browser/media/capture/desktop_capture_device_uma_types.h" | |
| 72 #include "content/browser/media/capture/web_contents_tracker.h" | 73 #include "content/browser/media/capture/web_contents_tracker.h" |
| 73 #include "content/browser/media/capture/window_activity_tracker.h" | 74 #include "content/browser/media/capture/window_activity_tracker.h" |
| 74 #include "content/browser/renderer_host/render_widget_host_impl.h" | 75 #include "content/browser/renderer_host/render_widget_host_impl.h" |
| 75 #include "content/browser/renderer_host/render_widget_host_view_base.h" | 76 #include "content/browser/renderer_host/render_widget_host_view_base.h" |
| 76 #include "content/public/browser/browser_thread.h" | 77 #include "content/public/browser/browser_thread.h" |
| 77 #include "content/public/browser/render_process_host.h" | 78 #include "content/public/browser/render_process_host.h" |
| 78 #include "content/public/browser/render_widget_host_view.h" | 79 #include "content/public/browser/render_widget_host_view.h" |
| 79 #include "content/public/browser/render_widget_host_view_frame_subscriber.h" | 80 #include "content/public/browser/render_widget_host_view_frame_subscriber.h" |
| 80 #include "content/public/browser/web_contents.h" | 81 #include "content/public/browser/web_contents.h" |
| 81 #include "content/public/browser/web_contents_media_capture_id.h" | 82 #include "content/public/browser/web_contents_media_capture_id.h" |
| (...skipping 171 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 253 const SkBitmap& input, | 254 const SkBitmap& input, |
| 254 const scoped_refptr<media::VideoFrame>& output, | 255 const scoped_refptr<media::VideoFrame>& output, |
| 255 const base::Callback<void(const gfx::Rect&, bool)>& done_cb); | 256 const base::Callback<void(const gfx::Rect&, bool)>& done_cb); |
| 256 | 257 |
| 257 // Renews capture subscriptions based on feedback from WebContentsTracker, and | 258 // Renews capture subscriptions based on feedback from WebContentsTracker, and |
| 258 // also executes copying of the backing store on the UI BrowserThread. | 259 // also executes copying of the backing store on the UI BrowserThread. |
| 259 class WebContentsCaptureMachine : public media::VideoCaptureMachine { | 260 class WebContentsCaptureMachine : public media::VideoCaptureMachine { |
| 260 public: | 261 public: |
| 261 WebContentsCaptureMachine(int render_process_id, | 262 WebContentsCaptureMachine(int render_process_id, |
| 262 int main_render_frame_id, | 263 int main_render_frame_id, |
| 263 bool enable_auto_throttling); | 264 bool enable_auto_throttling, |
| 265 WebContentsVideoCaptureDevice::JavaScriptType type); | |
| 264 ~WebContentsCaptureMachine() override; | 266 ~WebContentsCaptureMachine() override; |
| 265 | 267 |
| 266 // VideoCaptureMachine overrides. | 268 // VideoCaptureMachine overrides. |
| 267 void Start(const scoped_refptr<media::ThreadSafeCaptureOracle>& oracle_proxy, | 269 void Start(const scoped_refptr<media::ThreadSafeCaptureOracle>& oracle_proxy, |
| 268 const media::VideoCaptureParams& params, | 270 const media::VideoCaptureParams& params, |
| 269 const base::Callback<void(bool)> callback) override; | 271 const base::Callback<void(bool)> callback) override; |
| 270 void Stop(const base::Closure& callback) override; | 272 void Stop(const base::Closure& callback) override; |
| 271 bool IsAutoThrottlingEnabled() const override { | 273 bool IsAutoThrottlingEnabled() const override { |
| 272 return auto_throttling_enabled_; | 274 return auto_throttling_enabled_; |
| 273 } | 275 } |
| (...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 322 | 324 |
| 323 // Tracks events and calls back to RenewFrameSubscription() to maintain | 325 // Tracks events and calls back to RenewFrameSubscription() to maintain |
| 324 // capture on the correct RenderWidgetHost. | 326 // capture on the correct RenderWidgetHost. |
| 325 const scoped_refptr<WebContentsTracker> tracker_; | 327 const scoped_refptr<WebContentsTracker> tracker_; |
| 326 | 328 |
| 327 // Set to false to prevent the capture size from automatically adjusting in | 329 // Set to false to prevent the capture size from automatically adjusting in |
| 328 // response to end-to-end utilization. This is enabled via the throttling | 330 // response to end-to-end utilization. This is enabled via the throttling |
| 329 // option in the WebContentsVideoCaptureDevice device ID. | 331 // option in the WebContentsVideoCaptureDevice device ID. |
| 330 const bool auto_throttling_enabled_; | 332 const bool auto_throttling_enabled_; |
| 331 | 333 |
| 334 bool is_first_capture_; | |
| 335 const WebContentsVideoCaptureDevice::JavaScriptType js_type_; | |
| 336 | |
| 332 // A dedicated worker thread on which SkBitmap->VideoFrame conversion will | 337 // A dedicated worker thread on which SkBitmap->VideoFrame conversion will |
| 333 // occur. Only used when this activity cannot be done on the GPU. | 338 // occur. Only used when this activity cannot be done on the GPU. |
| 334 scoped_ptr<base::Thread> render_thread_; | 339 scoped_ptr<base::Thread> render_thread_; |
| 335 | 340 |
| 336 // Makes all the decisions about which frames to copy, and how. | 341 // Makes all the decisions about which frames to copy, and how. |
| 337 scoped_refptr<media::ThreadSafeCaptureOracle> oracle_proxy_; | 342 scoped_refptr<media::ThreadSafeCaptureOracle> oracle_proxy_; |
| 338 | 343 |
| 339 // Video capture parameters that this machine is started with. | 344 // Video capture parameters that this machine is started with. |
| 340 media::VideoCaptureParams capture_params_; | 345 media::VideoCaptureParams capture_params_; |
| 341 | 346 |
| (...skipping 280 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 622 << "WebContentsVideoCaptureDevice is " << measured_fps << " FPS."; | 627 << "WebContentsVideoCaptureDevice is " << measured_fps << " FPS."; |
| 623 last_frame_rate_log_time_ = frame_time; | 628 last_frame_rate_log_time_ = frame_time; |
| 624 count_frames_rendered_ = 0; | 629 count_frames_rendered_ = 0; |
| 625 } | 630 } |
| 626 } | 631 } |
| 627 } | 632 } |
| 628 | 633 |
| 629 WebContentsCaptureMachine::WebContentsCaptureMachine( | 634 WebContentsCaptureMachine::WebContentsCaptureMachine( |
| 630 int render_process_id, | 635 int render_process_id, |
| 631 int main_render_frame_id, | 636 int main_render_frame_id, |
| 632 bool enable_auto_throttling) | 637 bool enable_auto_throttling, |
| 638 WebContentsVideoCaptureDevice::JavaScriptType type) | |
| 633 : initial_render_process_id_(render_process_id), | 639 : initial_render_process_id_(render_process_id), |
| 634 initial_main_render_frame_id_(main_render_frame_id), | 640 initial_main_render_frame_id_(main_render_frame_id), |
| 635 tracker_(new WebContentsTracker(true)), | 641 tracker_(new WebContentsTracker(true)), |
| 636 auto_throttling_enabled_(enable_auto_throttling), | 642 auto_throttling_enabled_(enable_auto_throttling), |
| 643 js_type_(type), | |
| 637 weak_ptr_factory_(this) { | 644 weak_ptr_factory_(this) { |
| 638 DVLOG(1) << "Created WebContentsCaptureMachine for " | 645 DVLOG(1) << "Created WebContentsCaptureMachine for " |
| 639 << render_process_id << ':' << main_render_frame_id | 646 << render_process_id << ':' << main_render_frame_id |
| 640 << (auto_throttling_enabled_ ? " with auto-throttling enabled" : ""); | 647 << (auto_throttling_enabled_ ? " with auto-throttling enabled" : ""); |
| 641 } | 648 } |
| 642 | 649 |
| 643 WebContentsCaptureMachine::~WebContentsCaptureMachine() {} | 650 WebContentsCaptureMachine::~WebContentsCaptureMachine() {} |
| 644 | 651 |
| 645 bool WebContentsCaptureMachine::IsStarted() const { | 652 bool WebContentsCaptureMachine::IsStarted() const { |
| 646 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 653 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 647 return weak_ptr_factory_.HasWeakPtrs(); | 654 return weak_ptr_factory_.HasWeakPtrs(); |
| 648 } | 655 } |
| 649 | 656 |
| 650 void WebContentsCaptureMachine::Start( | 657 void WebContentsCaptureMachine::Start( |
| 651 const scoped_refptr<media::ThreadSafeCaptureOracle>& oracle_proxy, | 658 const scoped_refptr<media::ThreadSafeCaptureOracle>& oracle_proxy, |
| 652 const media::VideoCaptureParams& params, | 659 const media::VideoCaptureParams& params, |
| 653 const base::Callback<void(bool)> callback) { | 660 const base::Callback<void(bool)> callback) { |
| 661 is_first_capture_ = true; | |
| 662 | |
| 654 // Starts the capture machine asynchronously. | 663 // Starts the capture machine asynchronously. |
| 655 BrowserThread::PostTaskAndReplyWithResult( | 664 BrowserThread::PostTaskAndReplyWithResult( |
| 656 BrowserThread::UI, | 665 BrowserThread::UI, |
| 657 FROM_HERE, | 666 FROM_HERE, |
| 658 base::Bind(&WebContentsCaptureMachine::InternalStart, | 667 base::Bind(&WebContentsCaptureMachine::InternalStart, |
| 659 base::Unretained(this), | 668 base::Unretained(this), |
| 660 oracle_proxy, | 669 oracle_proxy, |
| 661 params), | 670 params), |
| 662 callback); | 671 callback); |
| 663 } | 672 } |
| (...skipping 195 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 859 "Render"); | 868 "Render"); |
| 860 render_thread_->task_runner()->PostTask( | 869 render_thread_->task_runner()->PostTask( |
| 861 FROM_HERE, media::BindToCurrentLoop( | 870 FROM_HERE, media::BindToCurrentLoop( |
| 862 base::Bind(&RenderVideoFrame, bitmap, target, | 871 base::Bind(&RenderVideoFrame, bitmap, target, |
| 863 base::Bind(deliver_frame_cb, start_time)))); | 872 base::Bind(deliver_frame_cb, start_time)))); |
| 864 } else { | 873 } else { |
| 865 // Capture can fail due to transient issues, so just skip this frame. | 874 // Capture can fail due to transient issues, so just skip this frame. |
| 866 DVLOG(1) << "CopyFromBackingStore failed; skipping frame."; | 875 DVLOG(1) << "CopyFromBackingStore failed; skipping frame."; |
| 867 deliver_frame_cb.Run(start_time, gfx::Rect(), false); | 876 deliver_frame_cb.Run(start_time, gfx::Rect(), false); |
| 868 } | 877 } |
| 878 | |
| 879 if (is_first_capture_) { | |
| 880 is_first_capture_ = false; | |
| 881 if (js_type_ == | |
| 882 WebContentsVideoCaptureDevice::CHROME_CHOOSE_DESKTOP_MEDIA) { | |
| 883 if (response == READBACK_SUCCESS) | |
|
miu
2016/03/24 20:51:02
It's very possible for the first few frames to fai
GeorgeZ
2016/03/25 16:35:04
Very valuable comments. I guess this is why a owne
| |
| 884 IncrementDesktopCaptureCounter(FIRST_TAB_CAPTURE_SUCCEEDED); | |
| 885 else | |
| 886 IncrementDesktopCaptureCounter(FIRST_TAB_CAPTURE_FAILED); | |
| 887 } | |
| 888 } | |
| 869 } | 889 } |
| 870 | 890 |
| 871 void WebContentsCaptureMachine::DidCopyFromCompositingSurfaceToVideoFrame( | 891 void WebContentsCaptureMachine::DidCopyFromCompositingSurfaceToVideoFrame( |
| 872 const base::TimeTicks& start_time, | 892 const base::TimeTicks& start_time, |
| 873 const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback& | 893 const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback& |
| 874 deliver_frame_cb, | 894 deliver_frame_cb, |
| 875 const gfx::Rect& region_in_frame, | 895 const gfx::Rect& region_in_frame, |
| 876 bool success) { | 896 bool success) { |
| 877 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 897 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 878 base::TimeTicks now = base::TimeTicks::Now(); | 898 base::TimeTicks now = base::TimeTicks::Now(); |
| 879 | 899 |
| 880 if (success) { | 900 if (success) { |
| 881 UMA_HISTOGRAM_TIMES("TabCapture.CopyTimeVideoFrame", now - start_time); | 901 UMA_HISTOGRAM_TIMES("TabCapture.CopyTimeVideoFrame", now - start_time); |
| 882 } else { | 902 } else { |
| 883 // Capture can fail due to transient issues, so just skip this frame. | 903 // Capture can fail due to transient issues, so just skip this frame. |
| 884 DVLOG(1) << "CopyFromCompositingSurface failed; skipping frame."; | 904 DVLOG(1) << "CopyFromCompositingSurface failed; skipping frame."; |
| 885 } | 905 } |
| 906 | |
| 907 if (is_first_capture_) { | |
| 908 is_first_capture_ = false; | |
| 909 if (js_type_ == | |
| 910 WebContentsVideoCaptureDevice::CHROME_CHOOSE_DESKTOP_MEDIA) { | |
| 911 if (success) | |
| 912 IncrementDesktopCaptureCounter(FIRST_TAB_CAPTURE_SUCCEEDED); | |
| 913 else | |
| 914 IncrementDesktopCaptureCounter(FIRST_TAB_CAPTURE_FAILED); | |
| 915 } | |
| 916 } | |
| 917 | |
| 886 deliver_frame_cb.Run(start_time, region_in_frame, success); | 918 deliver_frame_cb.Run(start_time, region_in_frame, success); |
| 887 } | 919 } |
| 888 | 920 |
| 889 void WebContentsCaptureMachine::RenewFrameSubscription(bool had_target) { | 921 void WebContentsCaptureMachine::RenewFrameSubscription(bool had_target) { |
| 890 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 922 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 891 | 923 |
| 892 RenderWidgetHost* const rwh = | 924 RenderWidgetHost* const rwh = |
| 893 had_target ? tracker_->GetTargetRenderWidgetHost() : nullptr; | 925 had_target ? tracker_->GetTargetRenderWidgetHost() : nullptr; |
| 894 | 926 |
| 895 // Always destroy the old subscription before creating a new one. | 927 // Always destroy the old subscription before creating a new one. |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 940 << ") from view size (" << view_size.ToString() << ")."; | 972 << ") from view size (" << view_size.ToString() << ")."; |
| 941 | 973 |
| 942 oracle_proxy_->UpdateCaptureSize(physical_size); | 974 oracle_proxy_->UpdateCaptureSize(physical_size); |
| 943 } | 975 } |
| 944 | 976 |
| 945 } // namespace | 977 } // namespace |
| 946 | 978 |
| 947 WebContentsVideoCaptureDevice::WebContentsVideoCaptureDevice( | 979 WebContentsVideoCaptureDevice::WebContentsVideoCaptureDevice( |
| 948 int render_process_id, | 980 int render_process_id, |
| 949 int main_render_frame_id, | 981 int main_render_frame_id, |
| 950 bool enable_auto_throttling) | 982 bool enable_auto_throttling, |
| 983 JavaScriptType type) | |
| 951 : core_(new media::ScreenCaptureDeviceCore( | 984 : core_(new media::ScreenCaptureDeviceCore( |
| 952 scoped_ptr<media::VideoCaptureMachine>(new WebContentsCaptureMachine( | 985 scoped_ptr<media::VideoCaptureMachine>( |
| 953 render_process_id, | 986 new WebContentsCaptureMachine(render_process_id, |
| 954 main_render_frame_id, | 987 main_render_frame_id, |
| 955 enable_auto_throttling)))) {} | 988 enable_auto_throttling, |
| 989 type)))) {} | |
| 956 | 990 |
| 957 WebContentsVideoCaptureDevice::~WebContentsVideoCaptureDevice() { | 991 WebContentsVideoCaptureDevice::~WebContentsVideoCaptureDevice() { |
| 958 DVLOG(2) << "WebContentsVideoCaptureDevice@" << this << " destroying."; | 992 DVLOG(2) << "WebContentsVideoCaptureDevice@" << this << " destroying."; |
| 959 } | 993 } |
| 960 | 994 |
| 961 // static | 995 // static |
| 962 media::VideoCaptureDevice* WebContentsVideoCaptureDevice::Create( | 996 media::VideoCaptureDevice* WebContentsVideoCaptureDevice::Create( |
| 963 const std::string& device_id) { | 997 const std::string& device_id, |
| 998 JavaScriptType type) { | |
| 964 // Parse device_id into render_process_id and main_render_frame_id. | 999 // Parse device_id into render_process_id and main_render_frame_id. |
| 965 int render_process_id = -1; | 1000 int render_process_id = -1; |
| 966 int main_render_frame_id = -1; | 1001 int main_render_frame_id = -1; |
| 967 if (!WebContentsMediaCaptureId::ExtractTabCaptureTarget( | 1002 if (!WebContentsMediaCaptureId::ExtractTabCaptureTarget( |
| 968 device_id, &render_process_id, &main_render_frame_id)) { | 1003 device_id, &render_process_id, &main_render_frame_id)) { |
| 969 return NULL; | 1004 return NULL; |
| 970 } | 1005 } |
| 971 | 1006 |
| 1007 if (type == CHROME_CHOOSE_DESKTOP_MEDIA) | |
| 1008 IncrementDesktopCaptureCounter(TAB_VIDEO_CAPTURER_CREATED); | |
| 1009 | |
| 972 return new WebContentsVideoCaptureDevice( | 1010 return new WebContentsVideoCaptureDevice( |
| 973 render_process_id, main_render_frame_id, | 1011 render_process_id, main_render_frame_id, |
| 974 WebContentsMediaCaptureId::IsAutoThrottlingOptionSet(device_id)); | 1012 WebContentsMediaCaptureId::IsAutoThrottlingOptionSet(device_id), type); |
| 975 } | 1013 } |
| 976 | 1014 |
| 977 void WebContentsVideoCaptureDevice::AllocateAndStart( | 1015 void WebContentsVideoCaptureDevice::AllocateAndStart( |
| 978 const media::VideoCaptureParams& params, | 1016 const media::VideoCaptureParams& params, |
| 979 scoped_ptr<Client> client) { | 1017 scoped_ptr<Client> client) { |
| 980 DVLOG(1) << "Allocating " << params.requested_format.frame_size.ToString(); | 1018 DVLOG(1) << "Allocating " << params.requested_format.frame_size.ToString(); |
| 981 core_->AllocateAndStart(params, std::move(client)); | 1019 core_->AllocateAndStart(params, std::move(client)); |
| 982 } | 1020 } |
| 983 | 1021 |
| 984 void WebContentsVideoCaptureDevice::StopAndDeAllocate() { | 1022 void WebContentsVideoCaptureDevice::StopAndDeAllocate() { |
| 985 core_->StopAndDeAllocate(); | 1023 core_->StopAndDeAllocate(); |
| 986 } | 1024 } |
| 987 | 1025 |
| 988 } // namespace content | 1026 } // namespace content |
| OLD | NEW |