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 |