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/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/media/capture/web_contents_tracker.h" |
69 #include "content/browser/renderer_host/render_widget_host_impl.h" | 69 #include "content/browser/renderer_host/render_widget_host_impl.h" |
70 #include "content/browser/renderer_host/render_widget_host_view_base.h" | 70 #include "content/browser/renderer_host/render_widget_host_view_base.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" | |
74 #include "content/public/browser/notification_source.h" | |
75 #include "content/public/browser/notification_types.h" | |
76 #include "content/public/browser/render_process_host.h" | 72 #include "content/public/browser/render_process_host.h" |
77 #include "content/public/browser/render_widget_host_view.h" | 73 #include "content/public/browser/render_widget_host_view.h" |
78 #include "content/public/browser/render_widget_host_view_frame_subscriber.h" | 74 #include "content/public/browser/render_widget_host_view_frame_subscriber.h" |
79 #include "content/public/browser/web_contents.h" | 75 #include "content/public/browser/web_contents.h" |
80 #include "media/base/video_capture_types.h" | 76 #include "media/base/video_capture_types.h" |
81 #include "media/base/video_util.h" | 77 #include "media/base/video_util.h" |
82 #include "skia/ext/image_operations.h" | 78 #include "skia/ext/image_operations.h" |
83 #include "third_party/skia/include/core/SkBitmap.h" | 79 #include "third_party/skia/include/core/SkBitmap.h" |
84 #include "third_party/skia/include/core/SkColor.h" | 80 #include "third_party/skia/include/core/SkColor.h" |
85 #include "ui/gfx/display.h" | 81 #include "ui/gfx/display.h" |
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
153 scoped_refptr<ThreadSafeCaptureOracle> oracle_proxy_; | 149 scoped_refptr<ThreadSafeCaptureOracle> oracle_proxy_; |
154 VideoFrameDeliveryLog* const delivery_log_; | 150 VideoFrameDeliveryLog* const delivery_log_; |
155 }; | 151 }; |
156 | 152 |
157 // ContentCaptureSubscription is the relationship between a RenderWidgetHost | 153 // ContentCaptureSubscription is the relationship between a RenderWidgetHost |
158 // whose content is updating, a subscriber that is deciding which of these | 154 // whose content is updating, a subscriber that is deciding which of these |
159 // updates to capture (and where to deliver them to), and a callback that | 155 // updates to capture (and where to deliver them to), and a callback that |
160 // knows how to do the capture and prepare the result for delivery. | 156 // knows how to do the capture and prepare the result for delivery. |
161 // | 157 // |
162 // In practice, this means (a) installing a RenderWidgetHostFrameSubscriber in | 158 // In practice, this means (a) installing a RenderWidgetHostFrameSubscriber in |
163 // the RenderWidgetHostView, to process updates that occur via accelerated | 159 // the RenderWidgetHostView, to process compositor updates, and (b) running a |
164 // compositing, (b) installing itself as an observer of updates to the | 160 // timer to possibly initiate forced, non-event-driven captures needed by |
165 // RenderWidgetHost's backing store, to hook updates that occur via software | 161 // downstream consumers that require frame repeats of unchanged content. |
166 // rendering, and (c) running a timer to possibly initiate non-event-driven | |
167 // captures that the subscriber might request. | |
168 // | 162 // |
169 // All of this happens on the UI thread, although the | 163 // All of this happens on the UI thread, although the |
170 // RenderWidgetHostViewFrameSubscriber we install may be dispatching updates | 164 // RenderWidgetHostViewFrameSubscriber we install may be dispatching updates |
171 // autonomously on some other thread. | 165 // autonomously on some other thread. |
172 class ContentCaptureSubscription : public content::NotificationObserver { | 166 class ContentCaptureSubscription { |
173 public: | 167 public: |
174 typedef base::Callback< | 168 typedef base::Callback< |
175 void(const base::TimeTicks&, | 169 void(const base::TimeTicks&, |
176 const scoped_refptr<media::VideoFrame>&, | 170 const scoped_refptr<media::VideoFrame>&, |
177 const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback&)> | 171 const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback&)> |
178 CaptureCallback; | 172 CaptureCallback; |
179 | 173 |
180 // Create a subscription. Whenever a manual capture is required, the | 174 // Create a subscription. Whenever a manual capture is required, the |
181 // subscription will invoke |capture_callback| on the UI thread to do the | 175 // subscription will invoke |capture_callback| on the UI thread to do the |
182 // work. | 176 // work. |
183 ContentCaptureSubscription( | 177 ContentCaptureSubscription( |
184 const RenderWidgetHost& source, | 178 const RenderWidgetHost& source, |
185 const scoped_refptr<ThreadSafeCaptureOracle>& oracle_proxy, | 179 const scoped_refptr<ThreadSafeCaptureOracle>& oracle_proxy, |
186 const CaptureCallback& capture_callback); | 180 const CaptureCallback& capture_callback); |
187 ~ContentCaptureSubscription() override; | 181 ~ContentCaptureSubscription(); |
188 | |
189 // content::NotificationObserver implementation. | |
190 void Observe(int type, | |
191 const content::NotificationSource& source, | |
192 const content::NotificationDetails& details) override; | |
193 | 182 |
194 private: | 183 private: |
195 void OnTimer(); | 184 void OnTimer(); |
196 | 185 |
197 // Maintain a weak reference to the RenderWidgetHost (via its routing ID), | 186 // Maintain a weak reference to the RenderWidgetHost (via its routing ID), |
198 // since the instance could be destroyed externally during the lifetime of | 187 // since the instance could be destroyed externally during the lifetime of |
199 // |this|. | 188 // |this|. |
200 const int render_process_id_; | 189 const int render_process_id_; |
201 const int render_widget_id_; | 190 const int render_widget_id_; |
202 | 191 |
203 VideoFrameDeliveryLog delivery_log_; | 192 VideoFrameDeliveryLog delivery_log_; |
204 FrameSubscriber paint_subscriber_; | |
205 FrameSubscriber timer_subscriber_; | 193 FrameSubscriber timer_subscriber_; |
206 content::NotificationRegistrar registrar_; | |
207 CaptureCallback capture_callback_; | 194 CaptureCallback capture_callback_; |
208 base::Timer timer_; | 195 base::Timer timer_; |
209 | 196 |
210 DISALLOW_COPY_AND_ASSIGN(ContentCaptureSubscription); | 197 DISALLOW_COPY_AND_ASSIGN(ContentCaptureSubscription); |
211 }; | 198 }; |
212 | 199 |
213 // Render the SkBitmap |input| into the given VideoFrame buffer |output|, then | 200 // Render the SkBitmap |input| into the given VideoFrame buffer |output|, then |
214 // invoke |done_cb| to indicate success or failure. |input| is expected to be | 201 // invoke |done_cb| to indicate success or failure. |input| is expected to be |
215 // ARGB. |output| must be YV12 or I420. Colorspace conversion is always done. | 202 // ARGB. |output| must be YV12 or I420. Colorspace conversion is always done. |
216 // Scaling and letterboxing will be done as needed. | 203 // Scaling and letterboxing will be done as needed. |
(...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
321 return oracle_decision; | 308 return oracle_decision; |
322 } | 309 } |
323 | 310 |
324 ContentCaptureSubscription::ContentCaptureSubscription( | 311 ContentCaptureSubscription::ContentCaptureSubscription( |
325 const RenderWidgetHost& source, | 312 const RenderWidgetHost& source, |
326 const scoped_refptr<ThreadSafeCaptureOracle>& oracle_proxy, | 313 const scoped_refptr<ThreadSafeCaptureOracle>& oracle_proxy, |
327 const CaptureCallback& capture_callback) | 314 const CaptureCallback& capture_callback) |
328 : render_process_id_(source.GetProcess()->GetID()), | 315 : render_process_id_(source.GetProcess()->GetID()), |
329 render_widget_id_(source.GetRoutingID()), | 316 render_widget_id_(source.GetRoutingID()), |
330 delivery_log_(), | 317 delivery_log_(), |
331 paint_subscriber_(VideoCaptureOracle::kSoftwarePaint, oracle_proxy, | |
332 &delivery_log_), | |
333 timer_subscriber_(VideoCaptureOracle::kTimerPoll, oracle_proxy, | 318 timer_subscriber_(VideoCaptureOracle::kTimerPoll, oracle_proxy, |
334 &delivery_log_), | 319 &delivery_log_), |
335 capture_callback_(capture_callback), | 320 capture_callback_(capture_callback), |
336 timer_(true, true) { | 321 timer_(true, true) { |
337 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 322 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
338 | 323 |
339 RenderWidgetHostView* const view = source.GetView(); | 324 RenderWidgetHostView* const view = source.GetView(); |
340 | 325 |
341 // Subscribe to accelerated presents. These will be serviced directly by the | 326 // Subscribe to compositor updates. These will be serviced directly by the |
342 // oracle. | 327 // oracle. |
343 if (view) { | 328 if (view) { |
344 scoped_ptr<RenderWidgetHostViewFrameSubscriber> subscriber( | 329 scoped_ptr<RenderWidgetHostViewFrameSubscriber> subscriber( |
345 new FrameSubscriber(VideoCaptureOracle::kCompositorUpdate, | 330 new FrameSubscriber(VideoCaptureOracle::kCompositorUpdate, |
346 oracle_proxy, &delivery_log_)); | 331 oracle_proxy, &delivery_log_)); |
347 view->BeginFrameSubscription(subscriber.Pass()); | 332 view->BeginFrameSubscription(subscriber.Pass()); |
348 } | 333 } |
349 | 334 |
350 // Subscribe to software paint events. This instance will service these by | |
351 // reflecting them back to the WebContentsCaptureMachine via | |
352 // |capture_callback|. | |
353 registrar_.Add( | |
354 this, content::NOTIFICATION_RENDER_WIDGET_HOST_DID_UPDATE_BACKING_STORE, | |
355 Source<RenderWidgetHost>(&source)); | |
356 | |
357 // Subscribe to timer events. This instance will service these as well. | 335 // Subscribe to timer events. This instance will service these as well. |
358 timer_.Start(FROM_HERE, oracle_proxy->min_capture_period(), | 336 timer_.Start(FROM_HERE, oracle_proxy->min_capture_period(), |
359 base::Bind(&ContentCaptureSubscription::OnTimer, | 337 base::Bind(&ContentCaptureSubscription::OnTimer, |
360 base::Unretained(this))); | 338 base::Unretained(this))); |
361 } | 339 } |
362 | 340 |
363 ContentCaptureSubscription::~ContentCaptureSubscription() { | 341 ContentCaptureSubscription::~ContentCaptureSubscription() { |
364 // If the BrowserThreads have been torn down, then the browser is in the final | 342 // If the BrowserThreads have been torn down, then the browser is in the final |
365 // stages of exiting and it is dangerous to take any further action. We must | 343 // stages of exiting and it is dangerous to take any further action. We must |
366 // return early. http://crbug.com/396413 | 344 // return early. http://crbug.com/396413 |
367 if (!BrowserThread::IsMessageLoopValid(BrowserThread::UI)) | 345 if (!BrowserThread::IsMessageLoopValid(BrowserThread::UI)) |
368 return; | 346 return; |
369 | 347 |
370 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 348 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
371 RenderWidgetHost* const source = | 349 RenderWidgetHost* const source = |
372 RenderWidgetHost::FromID(render_process_id_, render_widget_id_); | 350 RenderWidgetHost::FromID(render_process_id_, render_widget_id_); |
373 RenderWidgetHostView* const view = source ? source->GetView() : NULL; | 351 RenderWidgetHostView* const view = source ? source->GetView() : NULL; |
374 if (view) | 352 if (view) |
375 view->EndFrameSubscription(); | 353 view->EndFrameSubscription(); |
376 } | 354 } |
377 | 355 |
378 void ContentCaptureSubscription::Observe( | |
379 int type, | |
380 const content::NotificationSource& source, | |
381 const content::NotificationDetails& details) { | |
382 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
383 DCHECK_EQ(NOTIFICATION_RENDER_WIDGET_HOST_DID_UPDATE_BACKING_STORE, type); | |
384 | |
385 RenderWidgetHostImpl* rwh = | |
386 RenderWidgetHostImpl::From(Source<RenderWidgetHost>(source).ptr()); | |
387 | |
388 // This message occurs on window resizes and visibility changes even when | |
389 // accelerated compositing is active, so we need to filter out these cases. | |
390 if (!rwh || !rwh->GetView()) | |
391 return; | |
392 // Mac sends DID_UPDATE_BACKING_STORE messages to inform the capture system | |
393 // of new software compositor frames, so always treat these messages as | |
394 // signals of a new frame on Mac. | |
395 // http://crbug.com/333986 | |
396 #if !defined(OS_MACOSX) | |
397 if (rwh->GetView()->IsSurfaceAvailableForCopy()) | |
398 return; | |
399 #endif | |
400 | |
401 TRACE_EVENT1("mirroring", "ContentCaptureSubscription::Observe", | |
402 "instance", this); | |
403 | |
404 base::Closure copy_done_callback; | |
405 scoped_refptr<media::VideoFrame> frame; | |
406 RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback deliver_frame_cb; | |
407 const base::TimeTicks start_time = base::TimeTicks::Now(); | |
408 if (paint_subscriber_.ShouldCaptureFrame(gfx::Rect(), | |
409 start_time, | |
410 &frame, | |
411 &deliver_frame_cb)) { | |
412 // This message happens just before paint. If we post a task to do the copy, | |
413 // it should run soon after the paint. | |
414 BrowserThread::PostTask( | |
415 BrowserThread::UI, FROM_HERE, | |
416 base::Bind(capture_callback_, start_time, frame, deliver_frame_cb)); | |
417 } | |
418 } | |
419 | |
420 void ContentCaptureSubscription::OnTimer() { | 356 void ContentCaptureSubscription::OnTimer() { |
421 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 357 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
422 TRACE_EVENT0("mirroring", "ContentCaptureSubscription::OnTimer"); | 358 TRACE_EVENT0("mirroring", "ContentCaptureSubscription::OnTimer"); |
423 | 359 |
424 scoped_refptr<media::VideoFrame> frame; | 360 scoped_refptr<media::VideoFrame> frame; |
425 RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback deliver_frame_cb; | 361 RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback deliver_frame_cb; |
426 | 362 |
427 const base::TimeTicks start_time = base::TimeTicks::Now(); | 363 const base::TimeTicks start_time = base::TimeTicks::Now(); |
428 if (timer_subscriber_.ShouldCaptureFrame(gfx::Rect(), | 364 if (timer_subscriber_.ShouldCaptureFrame(gfx::Rect(), |
429 start_time, | 365 start_time, |
(...skipping 353 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
783 scoped_ptr<Client> client) { | 719 scoped_ptr<Client> client) { |
784 DVLOG(1) << "Allocating " << params.requested_format.frame_size.ToString(); | 720 DVLOG(1) << "Allocating " << params.requested_format.frame_size.ToString(); |
785 core_->AllocateAndStart(params, client.Pass()); | 721 core_->AllocateAndStart(params, client.Pass()); |
786 } | 722 } |
787 | 723 |
788 void WebContentsVideoCaptureDevice::StopAndDeAllocate() { | 724 void WebContentsVideoCaptureDevice::StopAndDeAllocate() { |
789 core_->StopAndDeAllocate(); | 725 core_->StopAndDeAllocate(); |
790 } | 726 } |
791 | 727 |
792 } // namespace content | 728 } // namespace content |
OLD | NEW |