| 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 |
| 11 // shared-memory IPC buffer is reserved. There are only a few of these; | 11 // shared-memory IPC buffer is reserved. There are only a few of these; |
| 12 // when they run out, it indicates that the downstream client -- likely a | 12 // when they run out, it indicates that the downstream client -- likely a |
| 13 // video encoder -- is the performance bottleneck, and that the rate of | 13 // video encoder -- is the performance bottleneck, and that the rate of |
| 14 // frame capture should be throttled back. | 14 // frame capture should be throttled back. |
| 15 // | 15 // |
| 16 // 2. Capture: A bitmap is snapshotted/copied from the RenderView's backing | 16 // 2. Capture: A bitmap is snapshotted/copied from the RenderWidget's backing |
| 17 // store. This is initiated on the UI BrowserThread, and often occurs | 17 // store. This is initiated on the UI BrowserThread, and often occurs |
| 18 // asynchronously. Where supported, the GPU scales and color converts | 18 // asynchronously. Where supported, the GPU scales and color converts |
| 19 // frames to our desired size, and the readback happens directly into the | 19 // frames to our desired size, and the readback happens directly into the |
| 20 // shared-memory buffer. But this is not always possible, particularly when | 20 // shared-memory buffer. But this is not always possible, particularly when |
| 21 // accelerated compositing is disabled. | 21 // accelerated compositing is disabled. |
| 22 // | 22 // |
| 23 // 3. Render (if needed): If the web contents cannot be captured directly into | 23 // 3. Render (if needed): If the web contents cannot be captured directly into |
| 24 // our target size and color format, scaling and colorspace conversion must | 24 // our target size and color format, scaling and colorspace conversion must |
| 25 // be done on the CPU. A dedicated thread is used for this operation, to | 25 // be done on the CPU. A dedicated thread is used for this operation, to |
| 26 // avoid blocking the UI thread. The Render stage always reads from a | 26 // avoid blocking the UI thread. The Render stage always reads from a |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 58 #include "base/memory/weak_ptr.h" | 58 #include "base/memory/weak_ptr.h" |
| 59 #include "base/message_loop/message_loop_proxy.h" | 59 #include "base/message_loop/message_loop_proxy.h" |
| 60 #include "base/metrics/histogram.h" | 60 #include "base/metrics/histogram.h" |
| 61 #include "base/sequenced_task_runner.h" | 61 #include "base/sequenced_task_runner.h" |
| 62 #include "base/threading/thread.h" | 62 #include "base/threading/thread.h" |
| 63 #include "base/threading/thread_checker.h" | 63 #include "base/threading/thread_checker.h" |
| 64 #include "base/time/time.h" | 64 #include "base/time/time.h" |
| 65 #include "content/browser/media/capture/content_video_capture_device_core.h" | 65 #include "content/browser/media/capture/content_video_capture_device_core.h" |
| 66 #include "content/browser/media/capture/video_capture_oracle.h" | 66 #include "content/browser/media/capture/video_capture_oracle.h" |
| 67 #include "content/browser/media/capture/web_contents_capture_util.h" | 67 #include "content/browser/media/capture/web_contents_capture_util.h" |
| 68 #include "content/browser/media/capture/web_contents_tracker.h" |
| 68 #include "content/browser/renderer_host/render_widget_host_impl.h" | 69 #include "content/browser/renderer_host/render_widget_host_impl.h" |
| 69 #include "content/browser/renderer_host/render_widget_host_view_base.h" | 70 #include "content/browser/renderer_host/render_widget_host_view_base.h" |
| 70 #include "content/browser/web_contents/web_contents_impl.h" | |
| 71 #include "content/public/browser/browser_thread.h" | 71 #include "content/public/browser/browser_thread.h" |
| 72 #include "content/public/browser/notification_observer.h" |
| 73 #include "content/public/browser/notification_registrar.h" |
| 72 #include "content/public/browser/notification_source.h" | 74 #include "content/public/browser/notification_source.h" |
| 73 #include "content/public/browser/notification_types.h" | 75 #include "content/public/browser/notification_types.h" |
| 74 #include "content/public/browser/render_view_host.h" | 76 #include "content/public/browser/render_process_host.h" |
| 75 #include "content/public/browser/render_widget_host_view.h" | 77 #include "content/public/browser/render_widget_host_view.h" |
| 76 #include "content/public/browser/render_widget_host_view_frame_subscriber.h" | 78 #include "content/public/browser/render_widget_host_view_frame_subscriber.h" |
| 77 #include "content/public/browser/web_contents_observer.h" | 79 #include "content/public/browser/web_contents.h" |
| 78 #include "media/base/video_util.h" | 80 #include "media/base/video_util.h" |
| 79 #include "media/video/capture/video_capture_types.h" | 81 #include "media/video/capture/video_capture_types.h" |
| 80 #include "skia/ext/image_operations.h" | 82 #include "skia/ext/image_operations.h" |
| 81 #include "third_party/skia/include/core/SkBitmap.h" | 83 #include "third_party/skia/include/core/SkBitmap.h" |
| 82 #include "third_party/skia/include/core/SkColor.h" | 84 #include "third_party/skia/include/core/SkColor.h" |
| 83 #include "ui/gfx/display.h" | 85 #include "ui/gfx/display.h" |
| 84 #include "ui/gfx/geometry/size.h" | 86 #include "ui/gfx/geometry/size.h" |
| 85 #include "ui/gfx/geometry/size_conversions.h" | 87 #include "ui/gfx/geometry/size_conversions.h" |
| 86 #include "ui/gfx/screen.h" | 88 #include "ui/gfx/screen.h" |
| 87 | 89 |
| (...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 186 virtual ~ContentCaptureSubscription(); | 188 virtual ~ContentCaptureSubscription(); |
| 187 | 189 |
| 188 // content::NotificationObserver implementation. | 190 // content::NotificationObserver implementation. |
| 189 virtual void Observe(int type, | 191 virtual void Observe(int type, |
| 190 const content::NotificationSource& source, | 192 const content::NotificationSource& source, |
| 191 const content::NotificationDetails& details) OVERRIDE; | 193 const content::NotificationDetails& details) OVERRIDE; |
| 192 | 194 |
| 193 private: | 195 private: |
| 194 void OnTimer(); | 196 void OnTimer(); |
| 195 | 197 |
| 198 // Maintain a weak reference to the RenderWidgetHost (via its routing ID), |
| 199 // since the instance could be destroyed externally during the lifetime of |
| 200 // |this|. |
| 196 const int render_process_id_; | 201 const int render_process_id_; |
| 197 const int render_view_id_; | 202 const int render_widget_id_; |
| 198 | 203 |
| 199 VideoFrameDeliveryLog delivery_log_; | 204 VideoFrameDeliveryLog delivery_log_; |
| 200 FrameSubscriber paint_subscriber_; | 205 FrameSubscriber paint_subscriber_; |
| 201 FrameSubscriber timer_subscriber_; | 206 FrameSubscriber timer_subscriber_; |
| 202 content::NotificationRegistrar registrar_; | 207 content::NotificationRegistrar registrar_; |
| 203 CaptureCallback capture_callback_; | 208 CaptureCallback capture_callback_; |
| 204 base::Timer timer_; | 209 base::Timer timer_; |
| 205 | 210 |
| 206 DISALLOW_COPY_AND_ASSIGN(ContentCaptureSubscription); | 211 DISALLOW_COPY_AND_ASSIGN(ContentCaptureSubscription); |
| 207 }; | 212 }; |
| 208 | 213 |
| 209 // Render the SkBitmap |input| into the given VideoFrame buffer |output|, then | 214 // Render the SkBitmap |input| into the given VideoFrame buffer |output|, then |
| 210 // invoke |done_cb| to indicate success or failure. |input| is expected to be | 215 // invoke |done_cb| to indicate success or failure. |input| is expected to be |
| 211 // ARGB. |output| must be YV12 or I420. Colorspace conversion is always done. | 216 // ARGB. |output| must be YV12 or I420. Colorspace conversion is always done. |
| 212 // Scaling and letterboxing will be done as needed. | 217 // Scaling and letterboxing will be done as needed. |
| 213 // | 218 // |
| 214 // This software implementation should be used only when GPU acceleration of | 219 // This software implementation should be used only when GPU acceleration of |
| 215 // these activities is not possible. This operation may be expensive (tens to | 220 // these activities is not possible. This operation may be expensive (tens to |
| 216 // hundreds of milliseconds), so the caller should ensure that it runs on a | 221 // hundreds of milliseconds), so the caller should ensure that it runs on a |
| 217 // thread where such a pause would cause UI jank. | 222 // thread where such a pause would cause UI jank. |
| 218 void RenderVideoFrame(const SkBitmap& input, | 223 void RenderVideoFrame(const SkBitmap& input, |
| 219 const scoped_refptr<media::VideoFrame>& output, | 224 const scoped_refptr<media::VideoFrame>& output, |
| 220 const base::Callback<void(bool)>& done_cb); | 225 const base::Callback<void(bool)>& done_cb); |
| 221 | 226 |
| 222 // Keeps track of the RenderView to be sourced, and executes copying of the | 227 // Renews capture subscriptions based on feedback from WebContentsTracker, and |
| 223 // backing store on the UI BrowserThread. | 228 // also executes copying of the backing store on the UI BrowserThread. |
| 224 // | 229 class WebContentsCaptureMachine : public VideoCaptureMachine { |
| 225 // TODO(nick): It would be nice to merge this with WebContentsTracker, but its | |
| 226 // implementation is currently asynchronous -- in our case, the "rvh changed" | |
| 227 // notification would get posted back to the UI thread and processed later, and | |
| 228 // this seems disadvantageous. | |
| 229 class WebContentsCaptureMachine | |
| 230 : public VideoCaptureMachine, | |
| 231 public WebContentsObserver { | |
| 232 public: | 230 public: |
| 233 WebContentsCaptureMachine(int render_process_id, int main_render_frame_id); | 231 WebContentsCaptureMachine(int render_process_id, int main_render_frame_id); |
| 234 virtual ~WebContentsCaptureMachine(); | 232 virtual ~WebContentsCaptureMachine(); |
| 235 | 233 |
| 236 // VideoCaptureMachine overrides. | 234 // VideoCaptureMachine overrides. |
| 237 virtual bool Start(const scoped_refptr<ThreadSafeCaptureOracle>& oracle_proxy, | 235 virtual bool Start(const scoped_refptr<ThreadSafeCaptureOracle>& oracle_proxy, |
| 238 const media::VideoCaptureParams& params) OVERRIDE; | 236 const media::VideoCaptureParams& params) OVERRIDE; |
| 239 virtual void Stop(const base::Closure& callback) OVERRIDE; | 237 virtual void Stop(const base::Closure& callback) OVERRIDE; |
| 240 | 238 |
| 241 // Starts a copy from the backing store or the composited surface. Must be run | 239 // Starts a copy from the backing store or the composited surface. Must be run |
| 242 // on the UI BrowserThread. |deliver_frame_cb| will be run when the operation | 240 // on the UI BrowserThread. |deliver_frame_cb| will be run when the operation |
| 243 // completes. The copy will occur to |target|. | 241 // completes. The copy will occur to |target|. |
| 244 // | 242 // |
| 245 // This may be used as a ContentCaptureSubscription::CaptureCallback. | 243 // This may be used as a ContentCaptureSubscription::CaptureCallback. |
| 246 void Capture(const base::TimeTicks& start_time, | 244 void Capture(const base::TimeTicks& start_time, |
| 247 const scoped_refptr<media::VideoFrame>& target, | 245 const scoped_refptr<media::VideoFrame>& target, |
| 248 const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback& | 246 const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback& |
| 249 deliver_frame_cb); | 247 deliver_frame_cb); |
| 250 | 248 |
| 251 // content::WebContentsObserver implementation. | 249 private: |
| 252 virtual void DidShowFullscreenWidget(int routing_id) OVERRIDE { | 250 bool IsStarted() const; |
| 253 fullscreen_widget_id_ = routing_id; | |
| 254 RenewFrameSubscription(); | |
| 255 } | |
| 256 | 251 |
| 257 virtual void DidDestroyFullscreenWidget(int routing_id) OVERRIDE { | |
| 258 DCHECK_EQ(fullscreen_widget_id_, routing_id); | |
| 259 fullscreen_widget_id_ = MSG_ROUTING_NONE; | |
| 260 RenewFrameSubscription(); | |
| 261 } | |
| 262 | |
| 263 virtual void RenderViewReady() OVERRIDE { | |
| 264 RenewFrameSubscription(); | |
| 265 } | |
| 266 | |
| 267 virtual void AboutToNavigateRenderView(RenderViewHost* rvh) OVERRIDE { | |
| 268 RenewFrameSubscription(); | |
| 269 } | |
| 270 | |
| 271 virtual void DidNavigateMainFrame( | |
| 272 const LoadCommittedDetails& details, | |
| 273 const FrameNavigateParams& params) OVERRIDE { | |
| 274 RenewFrameSubscription(); | |
| 275 } | |
| 276 | |
| 277 virtual void WebContentsDestroyed() OVERRIDE; | |
| 278 | |
| 279 private: | |
| 280 // Computes the preferred size of the target RenderWidget for optimal capture. | 252 // Computes the preferred size of the target RenderWidget for optimal capture. |
| 281 gfx::Size ComputeOptimalTargetSize() const; | 253 gfx::Size ComputeOptimalTargetSize() const; |
| 282 | 254 |
| 283 // Starts observing the web contents, returning false if lookup fails. | |
| 284 bool StartObservingWebContents(); | |
| 285 | |
| 286 // Helper function to determine the view that we are currently tracking. | |
| 287 RenderWidgetHost* GetTarget() const; | |
| 288 | |
| 289 // Response callback for RenderWidgetHost::CopyFromBackingStore(). | 255 // Response callback for RenderWidgetHost::CopyFromBackingStore(). |
| 290 void DidCopyFromBackingStore( | 256 void DidCopyFromBackingStore( |
| 291 const base::TimeTicks& start_time, | 257 const base::TimeTicks& start_time, |
| 292 const scoped_refptr<media::VideoFrame>& target, | 258 const scoped_refptr<media::VideoFrame>& target, |
| 293 const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback& | 259 const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback& |
| 294 deliver_frame_cb, | 260 deliver_frame_cb, |
| 295 bool success, | 261 bool success, |
| 296 const SkBitmap& bitmap); | 262 const SkBitmap& bitmap); |
| 297 | 263 |
| 298 // Response callback for RWHVP::CopyFromCompositingSurfaceToVideoFrame(). | 264 // Response callback for RWHVP::CopyFromCompositingSurfaceToVideoFrame(). |
| 299 void DidCopyFromCompositingSurfaceToVideoFrame( | 265 void DidCopyFromCompositingSurfaceToVideoFrame( |
| 300 const base::TimeTicks& start_time, | 266 const base::TimeTicks& start_time, |
| 301 const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback& | 267 const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback& |
| 302 deliver_frame_cb, | 268 deliver_frame_cb, |
| 303 bool success); | 269 bool success); |
| 304 | 270 |
| 305 // Remove the old subscription, and start a new one. This should be called | 271 // Remove the old subscription, and start a new one if |rwh| is not NULL. |
| 306 // after any change to the WebContents that affects the RenderWidgetHost or | 272 void RenewFrameSubscription(RenderWidgetHost* rwh); |
| 307 // attached views. | |
| 308 void RenewFrameSubscription(); | |
| 309 | 273 |
| 310 // Parameters saved in constructor. | 274 // Parameters saved in constructor. |
| 311 const int initial_render_process_id_; | 275 const int initial_render_process_id_; |
| 312 const int initial_main_render_frame_id_; | 276 const int initial_main_render_frame_id_; |
| 313 | 277 |
| 278 // Tracks events and calls back to RenewFrameSubscription() to maintain |
| 279 // capture on the correct RenderWidgetHost. |
| 280 const scoped_refptr<WebContentsTracker> tracker_; |
| 281 |
| 314 // A dedicated worker thread on which SkBitmap->VideoFrame conversion will | 282 // A dedicated worker thread on which SkBitmap->VideoFrame conversion will |
| 315 // occur. Only used when this activity cannot be done on the GPU. | 283 // occur. Only used when this activity cannot be done on the GPU. |
| 316 scoped_ptr<base::Thread> render_thread_; | 284 scoped_ptr<base::Thread> render_thread_; |
| 317 | 285 |
| 318 // Makes all the decisions about which frames to copy, and how. | 286 // Makes all the decisions about which frames to copy, and how. |
| 319 scoped_refptr<ThreadSafeCaptureOracle> oracle_proxy_; | 287 scoped_refptr<ThreadSafeCaptureOracle> oracle_proxy_; |
| 320 | 288 |
| 321 // Video capture parameters that this machine is started with. | 289 // Video capture parameters that this machine is started with. |
| 322 media::VideoCaptureParams capture_params_; | 290 media::VideoCaptureParams capture_params_; |
| 323 | 291 |
| 324 // Routing ID of any active fullscreen render widget or MSG_ROUTING_NONE | |
| 325 // otherwise. | |
| 326 int fullscreen_widget_id_; | |
| 327 | |
| 328 // Last known RenderView size. | 292 // Last known RenderView size. |
| 329 gfx::Size last_view_size_; | 293 gfx::Size last_view_size_; |
| 330 | 294 |
| 331 // Responsible for forwarding events from the active RenderWidgetHost to the | 295 // Responsible for forwarding events from the active RenderWidgetHost to the |
| 332 // oracle, and initiating captures accordingly. | 296 // oracle, and initiating captures accordingly. |
| 333 scoped_ptr<ContentCaptureSubscription> subscription_; | 297 scoped_ptr<ContentCaptureSubscription> subscription_; |
| 334 | 298 |
| 335 // Weak pointer factory used to invalidate callbacks. | 299 // Weak pointer factory used to invalidate callbacks. |
| 336 // NOTE: Weak pointers must be invalidated before all other member variables. | 300 // NOTE: Weak pointers must be invalidated before all other member variables. |
| 337 base::WeakPtrFactory<WebContentsCaptureMachine> weak_ptr_factory_; | 301 base::WeakPtrFactory<WebContentsCaptureMachine> weak_ptr_factory_; |
| (...skipping 18 matching lines...) Expand all Loading... |
| 356 if (oracle_decision) | 320 if (oracle_decision) |
| 357 delivery_log_->ChronicleFrameDelivery(present_time); | 321 delivery_log_->ChronicleFrameDelivery(present_time); |
| 358 return oracle_decision; | 322 return oracle_decision; |
| 359 } | 323 } |
| 360 | 324 |
| 361 ContentCaptureSubscription::ContentCaptureSubscription( | 325 ContentCaptureSubscription::ContentCaptureSubscription( |
| 362 const RenderWidgetHost& source, | 326 const RenderWidgetHost& source, |
| 363 const scoped_refptr<ThreadSafeCaptureOracle>& oracle_proxy, | 327 const scoped_refptr<ThreadSafeCaptureOracle>& oracle_proxy, |
| 364 const CaptureCallback& capture_callback) | 328 const CaptureCallback& capture_callback) |
| 365 : render_process_id_(source.GetProcess()->GetID()), | 329 : render_process_id_(source.GetProcess()->GetID()), |
| 366 render_view_id_(source.GetRoutingID()), | 330 render_widget_id_(source.GetRoutingID()), |
| 367 delivery_log_(), | 331 delivery_log_(), |
| 368 paint_subscriber_(VideoCaptureOracle::kSoftwarePaint, oracle_proxy, | 332 paint_subscriber_(VideoCaptureOracle::kSoftwarePaint, oracle_proxy, |
| 369 &delivery_log_), | 333 &delivery_log_), |
| 370 timer_subscriber_(VideoCaptureOracle::kTimerPoll, oracle_proxy, | 334 timer_subscriber_(VideoCaptureOracle::kTimerPoll, oracle_proxy, |
| 371 &delivery_log_), | 335 &delivery_log_), |
| 372 capture_callback_(capture_callback), | 336 capture_callback_(capture_callback), |
| 373 timer_(true, true) { | 337 timer_(true, true) { |
| 374 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 338 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 375 | 339 |
| 376 RenderWidgetHostViewBase* view = static_cast<RenderWidgetHostViewBase*>( | 340 RenderWidgetHostView* const view = source.GetView(); |
| 377 source.GetView()); | |
| 378 | 341 |
| 379 // Subscribe to accelerated presents. These will be serviced directly by the | 342 // Subscribe to accelerated presents. These will be serviced directly by the |
| 380 // oracle. | 343 // oracle. |
| 381 if (view && kAcceleratedSubscriberIsSupported) { | 344 if (view && kAcceleratedSubscriberIsSupported) { |
| 382 scoped_ptr<RenderWidgetHostViewFrameSubscriber> subscriber( | 345 scoped_ptr<RenderWidgetHostViewFrameSubscriber> subscriber( |
| 383 new FrameSubscriber(VideoCaptureOracle::kCompositorUpdate, | 346 new FrameSubscriber(VideoCaptureOracle::kCompositorUpdate, |
| 384 oracle_proxy, &delivery_log_)); | 347 oracle_proxy, &delivery_log_)); |
| 385 view->BeginFrameSubscription(subscriber.Pass()); | 348 view->BeginFrameSubscription(subscriber.Pass()); |
| 386 } | 349 } |
| 387 | 350 |
| (...skipping 12 matching lines...) Expand all Loading... |
| 400 | 363 |
| 401 ContentCaptureSubscription::~ContentCaptureSubscription() { | 364 ContentCaptureSubscription::~ContentCaptureSubscription() { |
| 402 // If the BrowserThreads have been torn down, then the browser is in the final | 365 // If the BrowserThreads have been torn down, then the browser is in the final |
| 403 // stages of exiting and it is dangerous to take any further action. We must | 366 // stages of exiting and it is dangerous to take any further action. We must |
| 404 // return early. http://crbug.com/396413 | 367 // return early. http://crbug.com/396413 |
| 405 if (!BrowserThread::IsMessageLoopValid(BrowserThread::UI)) | 368 if (!BrowserThread::IsMessageLoopValid(BrowserThread::UI)) |
| 406 return; | 369 return; |
| 407 | 370 |
| 408 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 371 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 409 if (kAcceleratedSubscriberIsSupported) { | 372 if (kAcceleratedSubscriberIsSupported) { |
| 410 RenderViewHost* source = RenderViewHost::FromID(render_process_id_, | 373 RenderWidgetHost* const source = |
| 411 render_view_id_); | 374 RenderWidgetHost::FromID(render_process_id_, render_widget_id_); |
| 412 if (source) { | 375 RenderWidgetHostView* const view = source ? source->GetView() : NULL; |
| 413 RenderWidgetHostViewBase* view = static_cast<RenderWidgetHostViewBase*>( | 376 if (view) |
| 414 source->GetView()); | 377 view->EndFrameSubscription(); |
| 415 if (view) | |
| 416 view->EndFrameSubscription(); | |
| 417 } | |
| 418 } | 378 } |
| 419 } | 379 } |
| 420 | 380 |
| 421 void ContentCaptureSubscription::Observe( | 381 void ContentCaptureSubscription::Observe( |
| 422 int type, | 382 int type, |
| 423 const content::NotificationSource& source, | 383 const content::NotificationSource& source, |
| 424 const content::NotificationDetails& details) { | 384 const content::NotificationDetails& details) { |
| 425 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 385 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 426 DCHECK_EQ(NOTIFICATION_RENDER_WIDGET_HOST_DID_UPDATE_BACKING_STORE, type); | 386 DCHECK_EQ(NOTIFICATION_RENDER_WIDGET_HOST_DID_UPDATE_BACKING_STORE, type); |
| 427 | 387 |
| (...skipping 143 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 571 last_frame_rate_log_time_ = frame_time; | 531 last_frame_rate_log_time_ = frame_time; |
| 572 count_frames_rendered_ = 0; | 532 count_frames_rendered_ = 0; |
| 573 } | 533 } |
| 574 } | 534 } |
| 575 } | 535 } |
| 576 | 536 |
| 577 WebContentsCaptureMachine::WebContentsCaptureMachine(int render_process_id, | 537 WebContentsCaptureMachine::WebContentsCaptureMachine(int render_process_id, |
| 578 int main_render_frame_id) | 538 int main_render_frame_id) |
| 579 : initial_render_process_id_(render_process_id), | 539 : initial_render_process_id_(render_process_id), |
| 580 initial_main_render_frame_id_(main_render_frame_id), | 540 initial_main_render_frame_id_(main_render_frame_id), |
| 581 fullscreen_widget_id_(MSG_ROUTING_NONE), | 541 tracker_(new WebContentsTracker(true)), |
| 582 weak_ptr_factory_(this) {} | 542 weak_ptr_factory_(this) {} |
| 583 | 543 |
| 584 WebContentsCaptureMachine::~WebContentsCaptureMachine() {} | 544 WebContentsCaptureMachine::~WebContentsCaptureMachine() {} |
| 585 | 545 |
| 546 bool WebContentsCaptureMachine::IsStarted() const { |
| 547 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 548 return weak_ptr_factory_.HasWeakPtrs(); |
| 549 } |
| 550 |
| 586 bool WebContentsCaptureMachine::Start( | 551 bool WebContentsCaptureMachine::Start( |
| 587 const scoped_refptr<ThreadSafeCaptureOracle>& oracle_proxy, | 552 const scoped_refptr<ThreadSafeCaptureOracle>& oracle_proxy, |
| 588 const media::VideoCaptureParams& params) { | 553 const media::VideoCaptureParams& params) { |
| 589 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 554 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 590 DCHECK(!weak_ptr_factory_.HasWeakPtrs()); // Should not be started. | 555 DCHECK(!IsStarted()); |
| 591 | 556 |
| 592 DCHECK(oracle_proxy.get()); | 557 DCHECK(oracle_proxy.get()); |
| 593 oracle_proxy_ = oracle_proxy; | 558 oracle_proxy_ = oracle_proxy; |
| 594 capture_params_ = params; | 559 capture_params_ = params; |
| 595 | 560 |
| 596 render_thread_.reset(new base::Thread("WebContentsVideo_RenderThread")); | 561 render_thread_.reset(new base::Thread("WebContentsVideo_RenderThread")); |
| 597 if (!render_thread_->Start()) { | 562 if (!render_thread_->Start()) { |
| 598 DVLOG(1) << "Failed to spawn render thread."; | 563 DVLOG(1) << "Failed to spawn render thread."; |
| 599 render_thread_.reset(); | 564 render_thread_.reset(); |
| 600 return false; | 565 return false; |
| 601 } | 566 } |
| 602 | 567 |
| 603 if (!StartObservingWebContents()) { | 568 // Note: Creation of the first WeakPtr in the following statement will cause |
| 604 DVLOG(1) << "Failed to observe web contents."; | 569 // IsStarted() to return true from now on. |
| 605 render_thread_.reset(); | 570 tracker_->Start(initial_render_process_id_, initial_main_render_frame_id_, |
| 606 return false; | 571 base::Bind(&WebContentsCaptureMachine::RenewFrameSubscription, |
| 607 } | 572 weak_ptr_factory_.GetWeakPtr())); |
| 608 | 573 |
| 609 return true; | 574 return true; |
| 610 } | 575 } |
| 611 | 576 |
| 612 void WebContentsCaptureMachine::Stop(const base::Closure& callback) { | 577 void WebContentsCaptureMachine::Stop(const base::Closure& callback) { |
| 613 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 578 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 614 subscription_.reset(); | 579 |
| 615 if (web_contents()) { | 580 if (!IsStarted()) { |
| 616 web_contents()->DecrementCapturerCount(); | 581 callback.Run(); |
| 617 Observe(NULL); | 582 return; |
| 618 } | 583 } |
| 619 | 584 |
| 620 // Any callback that intend to use render_thread_ will not work after it is | 585 // The following cancels any outstanding callbacks and causes IsStarted() to |
| 621 // passed. | 586 // return false from here onward. |
| 622 weak_ptr_factory_.InvalidateWeakPtrs(); | 587 weak_ptr_factory_.InvalidateWeakPtrs(); |
| 623 | 588 |
| 589 // Note: RenewFrameSubscription() must be called before stopping |tracker_| so |
| 590 // the web_contents() can be notified that the capturing is ending. |
| 591 RenewFrameSubscription(NULL); |
| 592 tracker_->Stop(); |
| 593 |
| 624 // The render thread cannot be stopped on the UI thread, so post a message | 594 // The render thread cannot be stopped on the UI thread, so post a message |
| 625 // to the thread pool used for blocking operations. | 595 // to the thread pool used for blocking operations. |
| 626 if (render_thread_.get()) { | 596 if (render_thread_.get()) { |
| 627 BrowserThread::PostBlockingPoolTask( | 597 BrowserThread::PostBlockingPoolTask( |
| 628 FROM_HERE, | 598 FROM_HERE, |
| 629 base::Bind(&DeleteOnWorkerThread, base::Passed(&render_thread_), | 599 base::Bind(&DeleteOnWorkerThread, base::Passed(&render_thread_), |
| 630 callback)); | 600 callback)); |
| 631 } | 601 } |
| 632 } | 602 } |
| 633 | 603 |
| 634 void WebContentsCaptureMachine::Capture( | 604 void WebContentsCaptureMachine::Capture( |
| 635 const base::TimeTicks& start_time, | 605 const base::TimeTicks& start_time, |
| 636 const scoped_refptr<media::VideoFrame>& target, | 606 const scoped_refptr<media::VideoFrame>& target, |
| 637 const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback& | 607 const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback& |
| 638 deliver_frame_cb) { | 608 deliver_frame_cb) { |
| 639 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 609 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 640 | 610 |
| 641 RenderWidgetHost* rwh = GetTarget(); | 611 RenderWidgetHost* rwh = tracker_->GetTargetRenderWidgetHost(); |
| 642 RenderWidgetHostViewBase* view = | 612 RenderWidgetHostViewBase* view = |
| 643 rwh ? static_cast<RenderWidgetHostViewBase*>(rwh->GetView()) : NULL; | 613 rwh ? static_cast<RenderWidgetHostViewBase*>(rwh->GetView()) : NULL; |
| 644 if (!view || !rwh) { | 614 if (!view) { |
| 645 deliver_frame_cb.Run(base::TimeTicks(), false); | 615 deliver_frame_cb.Run(base::TimeTicks(), false); |
| 646 return; | 616 return; |
| 647 } | 617 } |
| 648 | 618 |
| 649 gfx::Size video_size = target->coded_size(); | 619 gfx::Size video_size = target->coded_size(); |
| 650 gfx::Size view_size = view->GetViewBounds().size(); | 620 gfx::Size view_size = view->GetViewBounds().size(); |
| 651 gfx::Size fitted_size; | 621 gfx::Size fitted_size; |
| 652 if (!view_size.IsEmpty()) { | 622 if (!view_size.IsEmpty()) { |
| 653 fitted_size = ComputeYV12LetterboxRegion(video_size, view_size).size(); | 623 fitted_size = ComputeYV12LetterboxRegion(video_size, view_size).size(); |
| 654 } | 624 } |
| (...skipping 30 matching lines...) Expand all Loading... |
| 685 gfx::Size WebContentsCaptureMachine::ComputeOptimalTargetSize() const { | 655 gfx::Size WebContentsCaptureMachine::ComputeOptimalTargetSize() const { |
| 686 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 656 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 687 | 657 |
| 688 gfx::Size optimal_size = oracle_proxy_->GetCaptureSize(); | 658 gfx::Size optimal_size = oracle_proxy_->GetCaptureSize(); |
| 689 | 659 |
| 690 // If the ratio between physical and logical pixels is greater than 1:1, | 660 // If the ratio between physical and logical pixels is greater than 1:1, |
| 691 // shrink |optimal_size| by that amount. Then, when external code resizes the | 661 // shrink |optimal_size| by that amount. Then, when external code resizes the |
| 692 // render widget to the "preferred size," the widget will be physically | 662 // render widget to the "preferred size," the widget will be physically |
| 693 // rendered at the exact capture size, thereby eliminating unnecessary scaling | 663 // rendered at the exact capture size, thereby eliminating unnecessary scaling |
| 694 // operations in the graphics pipeline. | 664 // operations in the graphics pipeline. |
| 695 RenderWidgetHost* const rwh = GetTarget(); | 665 RenderWidgetHost* const rwh = tracker_->GetTargetRenderWidgetHost(); |
| 696 RenderWidgetHostView* const rwhv = rwh ? rwh->GetView() : NULL; | 666 RenderWidgetHostView* const rwhv = rwh ? rwh->GetView() : NULL; |
| 697 if (rwhv) { | 667 if (rwhv) { |
| 698 const gfx::NativeView view = rwhv->GetNativeView(); | 668 const gfx::NativeView view = rwhv->GetNativeView(); |
| 699 gfx::Screen* const screen = gfx::Screen::GetScreenFor(view); | 669 gfx::Screen* const screen = gfx::Screen::GetScreenFor(view); |
| 700 if (screen->IsDIPEnabled()) { | 670 if (screen->IsDIPEnabled()) { |
| 701 const gfx::Display display = screen->GetDisplayNearestWindow(view); | 671 const gfx::Display display = screen->GetDisplayNearestWindow(view); |
| 702 const float scale = display.device_scale_factor(); | 672 const float scale = display.device_scale_factor(); |
| 703 if (scale > 1.0f) { | 673 if (scale > 1.0f) { |
| 704 const gfx::Size shrunk_size( | 674 const gfx::Size shrunk_size( |
| 705 gfx::ToFlooredSize(gfx::ScaleSize(optimal_size, 1.0f / scale))); | 675 gfx::ToFlooredSize(gfx::ScaleSize(optimal_size, 1.0f / scale))); |
| 706 if (shrunk_size.width() > 0 && shrunk_size.height() > 0) | 676 if (shrunk_size.width() > 0 && shrunk_size.height() > 0) |
| 707 optimal_size = shrunk_size; | 677 optimal_size = shrunk_size; |
| 708 } | 678 } |
| 709 } | 679 } |
| 710 } | 680 } |
| 711 | 681 |
| 712 VLOG(1) << "Computed optimal target size: " << optimal_size.ToString(); | 682 VLOG(1) << "Computed optimal target size: " << optimal_size.ToString(); |
| 713 return optimal_size; | 683 return optimal_size; |
| 714 } | 684 } |
| 715 | 685 |
| 716 bool WebContentsCaptureMachine::StartObservingWebContents() { | |
| 717 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
| 718 | |
| 719 // Look-up the RenderFrameHost and, from that, the WebContents that wraps it. | |
| 720 // If successful, begin observing the WebContents instance. | |
| 721 // | |
| 722 // Why this can be unsuccessful: The request for mirroring originates in a | |
| 723 // render process, and this request is based on the current main RenderFrame | |
| 724 // associated with a tab. However, by the time we get up-and-running here, | |
| 725 // there have been multiple back-and-forth IPCs between processes, as well as | |
| 726 // a bit of indirection across threads. It's easily possible that, in the | |
| 727 // meantime, the original RenderFrame may have gone away. | |
| 728 Observe(WebContents::FromRenderFrameHost(RenderFrameHost::FromID( | |
| 729 initial_render_process_id_, initial_main_render_frame_id_))); | |
| 730 DVLOG_IF(1, !web_contents()) | |
| 731 << "Could not find WebContents associated with main RenderFrameHost " | |
| 732 << "referenced by render_process_id=" << initial_render_process_id_ | |
| 733 << ", routing_id=" << initial_main_render_frame_id_; | |
| 734 | |
| 735 WebContentsImpl* contents = static_cast<WebContentsImpl*>(web_contents()); | |
| 736 if (contents) { | |
| 737 contents->IncrementCapturerCount(ComputeOptimalTargetSize()); | |
| 738 fullscreen_widget_id_ = contents->GetFullscreenWidgetRoutingID(); | |
| 739 RenewFrameSubscription(); | |
| 740 return true; | |
| 741 } | |
| 742 return false; | |
| 743 } | |
| 744 | |
| 745 void WebContentsCaptureMachine::WebContentsDestroyed() { | |
| 746 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
| 747 | |
| 748 subscription_.reset(); | |
| 749 web_contents()->DecrementCapturerCount(); | |
| 750 oracle_proxy_->ReportError("WebContentsDestroyed()"); | |
| 751 } | |
| 752 | |
| 753 RenderWidgetHost* WebContentsCaptureMachine::GetTarget() const { | |
| 754 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
| 755 if (!web_contents()) | |
| 756 return NULL; | |
| 757 | |
| 758 RenderWidgetHost* rwh = NULL; | |
| 759 if (fullscreen_widget_id_ != MSG_ROUTING_NONE) { | |
| 760 RenderProcessHost* process = web_contents()->GetRenderProcessHost(); | |
| 761 if (process) | |
| 762 rwh = RenderWidgetHost::FromID(process->GetID(), fullscreen_widget_id_); | |
| 763 } else { | |
| 764 rwh = web_contents()->GetRenderViewHost(); | |
| 765 } | |
| 766 | |
| 767 return rwh; | |
| 768 } | |
| 769 | |
| 770 void WebContentsCaptureMachine::DidCopyFromBackingStore( | 686 void WebContentsCaptureMachine::DidCopyFromBackingStore( |
| 771 const base::TimeTicks& start_time, | 687 const base::TimeTicks& start_time, |
| 772 const scoped_refptr<media::VideoFrame>& target, | 688 const scoped_refptr<media::VideoFrame>& target, |
| 773 const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback& | 689 const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback& |
| 774 deliver_frame_cb, | 690 deliver_frame_cb, |
| 775 bool success, | 691 bool success, |
| 776 const SkBitmap& bitmap) { | 692 const SkBitmap& bitmap) { |
| 777 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 693 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 778 | 694 |
| 779 base::TimeTicks now = base::TimeTicks::Now(); | 695 base::TimeTicks now = base::TimeTicks::Now(); |
| (...skipping 22 matching lines...) Expand all Loading... |
| 802 | 718 |
| 803 if (success) { | 719 if (success) { |
| 804 UMA_HISTOGRAM_TIMES("TabCapture.CopyTimeVideoFrame", now - start_time); | 720 UMA_HISTOGRAM_TIMES("TabCapture.CopyTimeVideoFrame", now - start_time); |
| 805 } else { | 721 } else { |
| 806 // Capture can fail due to transient issues, so just skip this frame. | 722 // Capture can fail due to transient issues, so just skip this frame. |
| 807 DVLOG(1) << "CopyFromCompositingSurface failed; skipping frame."; | 723 DVLOG(1) << "CopyFromCompositingSurface failed; skipping frame."; |
| 808 } | 724 } |
| 809 deliver_frame_cb.Run(start_time, success); | 725 deliver_frame_cb.Run(start_time, success); |
| 810 } | 726 } |
| 811 | 727 |
| 812 void WebContentsCaptureMachine::RenewFrameSubscription() { | 728 void WebContentsCaptureMachine::RenewFrameSubscription(RenderWidgetHost* rwh) { |
| 813 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 729 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 814 | 730 |
| 815 // Always destroy the old subscription before creating a new one. | 731 // Always destroy the old subscription before creating a new one. |
| 732 const bool had_subscription = !!subscription_; |
| 816 subscription_.reset(); | 733 subscription_.reset(); |
| 817 | 734 |
| 818 RenderWidgetHost* rwh = GetTarget(); | 735 DVLOG(1) << "Renewing frame subscription to RWH@" << rwh |
| 819 if (!rwh || !rwh->GetView()) | 736 << ", had_subscription=" << had_subscription; |
| 737 |
| 738 if (!rwh) { |
| 739 if (had_subscription && tracker_->web_contents()) |
| 740 tracker_->web_contents()->DecrementCapturerCount(); |
| 741 if (IsStarted()) { |
| 742 // Tracking of WebContents and/or its main frame has failed before Stop() |
| 743 // was called, so report this as an error: |
| 744 oracle_proxy_->ReportError("WebContents and/or main frame are gone."); |
| 745 } |
| 820 return; | 746 return; |
| 747 } |
| 748 |
| 749 if (!had_subscription && tracker_->web_contents()) { |
| 750 tracker_->web_contents()->IncrementCapturerCount( |
| 751 ComputeOptimalTargetSize()); |
| 752 } |
| 821 | 753 |
| 822 subscription_.reset(new ContentCaptureSubscription(*rwh, oracle_proxy_, | 754 subscription_.reset(new ContentCaptureSubscription(*rwh, oracle_proxy_, |
| 823 base::Bind(&WebContentsCaptureMachine::Capture, | 755 base::Bind(&WebContentsCaptureMachine::Capture, |
| 824 weak_ptr_factory_.GetWeakPtr()))); | 756 weak_ptr_factory_.GetWeakPtr()))); |
| 825 } | 757 } |
| 826 | 758 |
| 827 } // namespace | 759 } // namespace |
| 828 | 760 |
| 829 WebContentsVideoCaptureDevice::WebContentsVideoCaptureDevice( | 761 WebContentsVideoCaptureDevice::WebContentsVideoCaptureDevice( |
| 830 int render_process_id, int main_render_frame_id) | 762 int render_process_id, int main_render_frame_id) |
| (...skipping 25 matching lines...) Expand all Loading... |
| 856 scoped_ptr<Client> client) { | 788 scoped_ptr<Client> client) { |
| 857 DVLOG(1) << "Allocating " << params.requested_format.frame_size.ToString(); | 789 DVLOG(1) << "Allocating " << params.requested_format.frame_size.ToString(); |
| 858 core_->AllocateAndStart(params, client.Pass()); | 790 core_->AllocateAndStart(params, client.Pass()); |
| 859 } | 791 } |
| 860 | 792 |
| 861 void WebContentsVideoCaptureDevice::StopAndDeAllocate() { | 793 void WebContentsVideoCaptureDevice::StopAndDeAllocate() { |
| 862 core_->StopAndDeAllocate(); | 794 core_->StopAndDeAllocate(); |
| 863 } | 795 } |
| 864 | 796 |
| 865 } // namespace content | 797 } // namespace content |
| OLD | NEW |