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 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
61 #include "base/memory/weak_ptr.h" | 61 #include "base/memory/weak_ptr.h" |
62 #include "base/metrics/histogram.h" | 62 #include "base/metrics/histogram.h" |
63 #include "base/sequenced_task_runner.h" | 63 #include "base/sequenced_task_runner.h" |
64 #include "base/single_thread_task_runner.h" | 64 #include "base/single_thread_task_runner.h" |
65 #include "base/threading/thread.h" | 65 #include "base/threading/thread.h" |
66 #include "base/threading/thread_checker.h" | 66 #include "base/threading/thread_checker.h" |
67 #include "base/time/time.h" | 67 #include "base/time/time.h" |
68 #include "content/browser/media/capture/cursor_renderer.h" | 68 #include "content/browser/media/capture/cursor_renderer.h" |
69 #include "content/browser/media/capture/web_contents_capture_util.h" | 69 #include "content/browser/media/capture/web_contents_capture_util.h" |
70 #include "content/browser/media/capture/web_contents_tracker.h" | 70 #include "content/browser/media/capture/web_contents_tracker.h" |
71 #include "content/browser/media/capture/window_activity_tracker.h" | |
71 #include "content/browser/renderer_host/render_widget_host_impl.h" | 72 #include "content/browser/renderer_host/render_widget_host_impl.h" |
72 #include "content/browser/renderer_host/render_widget_host_view_base.h" | 73 #include "content/browser/renderer_host/render_widget_host_view_base.h" |
73 #include "content/public/browser/browser_thread.h" | 74 #include "content/public/browser/browser_thread.h" |
74 #include "content/public/browser/render_process_host.h" | 75 #include "content/public/browser/render_process_host.h" |
75 #include "content/public/browser/render_widget_host_view.h" | 76 #include "content/public/browser/render_widget_host_view.h" |
76 #include "content/public/browser/render_widget_host_view_frame_subscriber.h" | 77 #include "content/public/browser/render_widget_host_view_frame_subscriber.h" |
77 #include "content/public/browser/web_contents.h" | 78 #include "content/public/browser/web_contents.h" |
78 #include "media/base/bind_to_current_loop.h" | 79 #include "media/base/bind_to_current_loop.h" |
79 #include "media/base/video_capture_types.h" | 80 #include "media/base/video_capture_types.h" |
81 #include "media/base/video_frame_metadata.h" | |
80 #include "media/base/video_util.h" | 82 #include "media/base/video_util.h" |
81 #include "media/capture/content/screen_capture_device_core.h" | 83 #include "media/capture/content/screen_capture_device_core.h" |
82 #include "media/capture/content/thread_safe_capture_oracle.h" | 84 #include "media/capture/content/thread_safe_capture_oracle.h" |
83 #include "media/capture/content/video_capture_oracle.h" | 85 #include "media/capture/content/video_capture_oracle.h" |
84 #include "skia/ext/image_operations.h" | 86 #include "skia/ext/image_operations.h" |
85 #include "third_party/skia/include/core/SkBitmap.h" | 87 #include "third_party/skia/include/core/SkBitmap.h" |
86 #include "third_party/skia/include/core/SkColor.h" | 88 #include "third_party/skia/include/core/SkColor.h" |
87 #include "ui/base/layout.h" | 89 #include "ui/base/layout.h" |
88 #include "ui/gfx/geometry/dip_util.h" | 90 #include "ui/gfx/geometry/dip_util.h" |
89 #include "ui/gfx/geometry/size_conversions.h" | 91 #include "ui/gfx/geometry/size_conversions.h" |
90 | 92 |
91 #if defined(USE_AURA) | 93 #if defined(USE_AURA) |
92 #include "content/browser/media/capture/cursor_renderer_aura.h" | 94 #include "content/browser/media/capture/cursor_renderer_aura.h" |
95 #include "content/browser/media/capture/window_activity_tracker_aura.h" | |
93 #endif | 96 #endif |
94 | 97 |
95 namespace content { | 98 namespace content { |
96 | 99 |
97 namespace { | 100 namespace { |
98 | 101 |
102 enum InteractiveModeSettings { | |
103 // Minimum amount of time for which there should be no animation detected | |
104 // to consider interactive mode being active. This is to prevent very brief | |
105 // periods of animated content not being detected (due to CPU fluctations for | |
106 // example) from causing a flip-flop on interactive mode. | |
107 kMinPeriodNoAnimationMillis = 3000 | |
108 }; | |
109 | |
99 void DeleteOnWorkerThread(scoped_ptr<base::Thread> render_thread, | 110 void DeleteOnWorkerThread(scoped_ptr<base::Thread> render_thread, |
100 const base::Closure& callback) { | 111 const base::Closure& callback) { |
101 render_thread.reset(); | 112 render_thread.reset(); |
102 | 113 |
103 // After thread join call the callback on UI thread. | 114 // After thread join call the callback on UI thread. |
104 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, callback); | 115 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, callback); |
105 } | 116 } |
106 | 117 |
107 // Responsible for logging the effective frame rate. | 118 // Responsible for logging the effective frame rate. |
108 class VideoFrameDeliveryLog { | 119 class VideoFrameDeliveryLog { |
(...skipping 12 matching lines...) Expand all Loading... | |
121 DISALLOW_COPY_AND_ASSIGN(VideoFrameDeliveryLog); | 132 DISALLOW_COPY_AND_ASSIGN(VideoFrameDeliveryLog); |
122 }; | 133 }; |
123 | 134 |
124 // FrameSubscriber is a proxy to the ThreadSafeCaptureOracle that's compatible | 135 // FrameSubscriber is a proxy to the ThreadSafeCaptureOracle that's compatible |
125 // with RenderWidgetHostViewFrameSubscriber. We create one per event type. | 136 // with RenderWidgetHostViewFrameSubscriber. We create one per event type. |
126 class FrameSubscriber : public RenderWidgetHostViewFrameSubscriber { | 137 class FrameSubscriber : public RenderWidgetHostViewFrameSubscriber { |
127 public: | 138 public: |
128 FrameSubscriber(media::VideoCaptureOracle::Event event_type, | 139 FrameSubscriber(media::VideoCaptureOracle::Event event_type, |
129 const scoped_refptr<media::ThreadSafeCaptureOracle>& oracle, | 140 const scoped_refptr<media::ThreadSafeCaptureOracle>& oracle, |
130 VideoFrameDeliveryLog* delivery_log, | 141 VideoFrameDeliveryLog* delivery_log, |
131 base::WeakPtr<content::CursorRenderer> cursor_renderer) | 142 base::WeakPtr<content::CursorRenderer> cursor_renderer, |
143 base::WeakPtr<content::WindowActivityTracker> tracker) | |
132 : event_type_(event_type), | 144 : event_type_(event_type), |
133 oracle_proxy_(oracle), | 145 oracle_proxy_(oracle), |
134 delivery_log_(delivery_log), | 146 delivery_log_(delivery_log), |
135 cursor_renderer_(cursor_renderer), | 147 cursor_renderer_(cursor_renderer), |
148 window_activity_tracker_(tracker), | |
136 weak_ptr_factory_(this) {} | 149 weak_ptr_factory_(this) {} |
137 | 150 |
138 bool ShouldCaptureFrame( | 151 bool ShouldCaptureFrame( |
139 const gfx::Rect& damage_rect, | 152 const gfx::Rect& damage_rect, |
140 base::TimeTicks present_time, | 153 base::TimeTicks present_time, |
141 scoped_refptr<media::VideoFrame>* storage, | 154 scoped_refptr<media::VideoFrame>* storage, |
142 RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback* | 155 RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback* |
143 deliver_frame_cb) override; | 156 deliver_frame_cb) override; |
144 | 157 |
145 static void DidCaptureFrame( | 158 static void DidCaptureFrame( |
146 base::WeakPtr<FrameSubscriber> frame_subscriber_, | 159 base::WeakPtr<FrameSubscriber> frame_subscriber_, |
147 const media::ThreadSafeCaptureOracle::CaptureFrameCallback& | 160 const media::ThreadSafeCaptureOracle::CaptureFrameCallback& |
148 capture_frame_cb, | 161 capture_frame_cb, |
149 const scoped_refptr<media::VideoFrame>& frame, | 162 const scoped_refptr<media::VideoFrame>& frame, |
150 base::TimeTicks timestamp, | 163 base::TimeTicks timestamp, |
151 const gfx::Rect& region_in_frame, | 164 const gfx::Rect& region_in_frame, |
152 bool success); | 165 bool success); |
153 | 166 |
167 bool IsUserInteractingWithContent(); | |
168 | |
154 private: | 169 private: |
155 const media::VideoCaptureOracle::Event event_type_; | 170 const media::VideoCaptureOracle::Event event_type_; |
156 scoped_refptr<media::ThreadSafeCaptureOracle> oracle_proxy_; | 171 scoped_refptr<media::ThreadSafeCaptureOracle> oracle_proxy_; |
157 VideoFrameDeliveryLog* const delivery_log_; | 172 VideoFrameDeliveryLog* const delivery_log_; |
158 // We need a weak pointer since FrameSubscriber is owned externally and | 173 // We need a weak pointer since FrameSubscriber is owned externally and |
159 // may outlive the cursor renderer. | 174 // may outlive the cursor renderer. |
160 base::WeakPtr<CursorRenderer> cursor_renderer_; | 175 base::WeakPtr<CursorRenderer> cursor_renderer_; |
176 // We need a weak pointer since FrameSubscriber is owned externally and | |
177 // may outlive the ui activity tracker. | |
178 base::WeakPtr<WindowActivityTracker> window_activity_tracker_; | |
161 base::WeakPtrFactory<FrameSubscriber> weak_ptr_factory_; | 179 base::WeakPtrFactory<FrameSubscriber> weak_ptr_factory_; |
162 }; | 180 }; |
163 | 181 |
164 // ContentCaptureSubscription is the relationship between a RenderWidgetHost | 182 // ContentCaptureSubscription is the relationship between a RenderWidgetHost |
165 // whose content is updating, a subscriber that is deciding which of these | 183 // whose content is updating, a subscriber that is deciding which of these |
166 // updates to capture (and where to deliver them to), and a callback that | 184 // updates to capture (and where to deliver them to), and a callback that |
167 // knows how to do the capture and prepare the result for delivery. | 185 // knows how to do the capture and prepare the result for delivery. |
168 // | 186 // |
169 // In practice, this means (a) installing a RenderWidgetHostFrameSubscriber in | 187 // In practice, this means (a) installing a RenderWidgetHostFrameSubscriber in |
170 // the RenderWidgetHostView, to process compositor updates, and (b) running a | 188 // the RenderWidgetHostView, to process compositor updates, and (b) running a |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
203 VideoFrameDeliveryLog delivery_log_; | 221 VideoFrameDeliveryLog delivery_log_; |
204 scoped_ptr<FrameSubscriber> timer_subscriber_; | 222 scoped_ptr<FrameSubscriber> timer_subscriber_; |
205 CaptureCallback capture_callback_; | 223 CaptureCallback capture_callback_; |
206 base::Timer timer_; | 224 base::Timer timer_; |
207 | 225 |
208 // Responsible for tracking the cursor state and input events to make | 226 // Responsible for tracking the cursor state and input events to make |
209 // decisions and then render the mouse cursor on the video frame after | 227 // decisions and then render the mouse cursor on the video frame after |
210 // capture is completed. | 228 // capture is completed. |
211 scoped_ptr<content::CursorRenderer> cursor_renderer_; | 229 scoped_ptr<content::CursorRenderer> cursor_renderer_; |
212 | 230 |
231 // Responsible for tracking the UI events and making a decision on whether | |
232 // user is actively interacting with content. | |
233 scoped_ptr<content::WindowActivityTracker> window_activity_tracker_; | |
234 | |
213 DISALLOW_COPY_AND_ASSIGN(ContentCaptureSubscription); | 235 DISALLOW_COPY_AND_ASSIGN(ContentCaptureSubscription); |
214 }; | 236 }; |
215 | 237 |
216 // Render the SkBitmap |input| into the given VideoFrame buffer |output|, then | 238 // Render the SkBitmap |input| into the given VideoFrame buffer |output|, then |
217 // invoke |done_cb| to indicate success or failure. |input| is expected to be | 239 // invoke |done_cb| to indicate success or failure. |input| is expected to be |
218 // ARGB. |output| must be YV12 or I420. Colorspace conversion is always done. | 240 // ARGB. |output| must be YV12 or I420. Colorspace conversion is always done. |
219 // Scaling and letterboxing will be done as needed. | 241 // Scaling and letterboxing will be done as needed. |
220 // | 242 // |
221 // This software implementation should be used only when GPU acceleration of | 243 // This software implementation should be used only when GPU acceleration of |
222 // these activities is not possible. This operation may be expensive (tens to | 244 // these activities is not possible. This operation may be expensive (tens to |
(...skipping 135 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
358 bool success) { | 380 bool success) { |
359 // We can get a callback in the shutdown sequence for the browser main loop | 381 // We can get a callback in the shutdown sequence for the browser main loop |
360 // and this can result in a DCHECK failure. Avoid this by doing DCHECK only | 382 // and this can result in a DCHECK failure. Avoid this by doing DCHECK only |
361 // on success. | 383 // on success. |
362 if (success) { | 384 if (success) { |
363 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 385 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
364 // TODO(isheriff): Unclear if taking a snapshot of cursor here affects user | 386 // TODO(isheriff): Unclear if taking a snapshot of cursor here affects user |
365 // experience in any particular scenarios. Doing it prior to capture may | 387 // experience in any particular scenarios. Doing it prior to capture may |
366 // require evaluating region_in_frame in this file. | 388 // require evaluating region_in_frame in this file. |
367 if (frame_subscriber_ && frame_subscriber_->cursor_renderer_) { | 389 if (frame_subscriber_ && frame_subscriber_->cursor_renderer_) { |
368 if (frame_subscriber_->cursor_renderer_->SnapshotCursorState( | 390 CursorRenderer* cursor_renderer = |
369 region_in_frame)) | 391 frame_subscriber_->cursor_renderer_.get(); |
370 frame_subscriber_->cursor_renderer_->RenderOnVideoFrame(frame); | 392 if (cursor_renderer->SnapshotCursorState(region_in_frame)) |
393 cursor_renderer->RenderOnVideoFrame(frame); | |
394 frame->metadata()->SetBoolean( | |
395 media::VideoFrameMetadata::INTERACTIVE_CONTENT, | |
396 frame_subscriber_->IsUserInteractingWithContent()); | |
371 } | 397 } |
372 } | 398 } |
373 capture_frame_cb.Run(frame, timestamp, success); | 399 capture_frame_cb.Run(frame, timestamp, success); |
374 } | 400 } |
375 | 401 |
402 bool FrameSubscriber::IsUserInteractingWithContent() { | |
403 bool interactive_mode = false; | |
404 bool ui_activity = false; | |
405 if (window_activity_tracker_.get()) { | |
406 ui_activity = window_activity_tracker_->IsUiInteractionActive(); | |
407 } | |
408 if (cursor_renderer_.get()) { | |
409 bool animation_active = | |
410 (base::TimeTicks::Now() - | |
411 oracle_proxy_->last_time_animation_was_detected()) < | |
412 base::TimeDelta::FromMilliseconds(kMinPeriodNoAnimationMillis); | |
413 if (ui_activity && !animation_active) { | |
414 interactive_mode = true; | |
415 } else if (animation_active) { | |
416 window_activity_tracker_->Reset(); | |
417 } | |
418 } | |
419 return interactive_mode; | |
420 } | |
421 | |
376 ContentCaptureSubscription::ContentCaptureSubscription( | 422 ContentCaptureSubscription::ContentCaptureSubscription( |
377 const RenderWidgetHost& source, | 423 const RenderWidgetHost& source, |
378 const scoped_refptr<media::ThreadSafeCaptureOracle>& oracle_proxy, | 424 const scoped_refptr<media::ThreadSafeCaptureOracle>& oracle_proxy, |
379 const CaptureCallback& capture_callback) | 425 const CaptureCallback& capture_callback) |
380 : render_process_id_(source.GetProcess()->GetID()), | 426 : render_process_id_(source.GetProcess()->GetID()), |
381 render_widget_id_(source.GetRoutingID()), | 427 render_widget_id_(source.GetRoutingID()), |
382 delivery_log_(), | 428 delivery_log_(), |
383 capture_callback_(capture_callback), | 429 capture_callback_(capture_callback), |
384 timer_(true, true) { | 430 timer_(true, true) { |
385 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 431 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
386 | 432 |
387 RenderWidgetHostView* const view = source.GetView(); | 433 RenderWidgetHostView* const view = source.GetView(); |
388 // TODO(isheriff): Cursor resources currently only available on linux. Remove | 434 // TODO(isheriff): Cursor resources currently only available on linux. Remove |
389 // this once we add the necessary resources for windows. | 435 // this once we add the necessary resources for windows. |
390 #if defined(USE_AURA) && defined(OS_LINUX) | 436 #if defined(USE_AURA) && defined(OS_LINUX) |
391 if (view) | 437 if (view) { |
392 cursor_renderer_.reset( | 438 cursor_renderer_.reset( |
393 new content::CursorRendererAura(view->GetNativeView())); | 439 new content::CursorRendererAura(view->GetNativeView())); |
440 window_activity_tracker_.reset( | |
miu
2015/12/08 00:16:16
This looks like it'll work for all Aura platforms,
Irfan
2015/12/08 01:30:18
Done.
| |
441 new content::WindowActivityTrackerAura(view->GetNativeView())); | |
442 } | |
394 #endif | 443 #endif |
395 timer_subscriber_.reset(new FrameSubscriber( | 444 timer_subscriber_.reset(new FrameSubscriber( |
396 media::VideoCaptureOracle::kTimerPoll, oracle_proxy, &delivery_log_, | 445 media::VideoCaptureOracle::kTimerPoll, oracle_proxy, &delivery_log_, |
397 cursor_renderer_ ? cursor_renderer_->GetWeakPtr() | 446 cursor_renderer_ ? cursor_renderer_->GetWeakPtr() |
398 : base::WeakPtr<CursorRenderer>())); | 447 : base::WeakPtr<CursorRenderer>(), |
448 window_activity_tracker_ ? window_activity_tracker_->GetWeakPtr() | |
449 : base::WeakPtr<WindowActivityTracker>())); | |
399 | 450 |
400 // Subscribe to compositor updates. These will be serviced directly by the | 451 // Subscribe to compositor updates. These will be serviced directly by the |
401 // oracle. | 452 // oracle. |
402 if (view) { | 453 if (view) { |
403 scoped_ptr<RenderWidgetHostViewFrameSubscriber> subscriber( | 454 scoped_ptr<RenderWidgetHostViewFrameSubscriber> subscriber( |
404 new FrameSubscriber(media::VideoCaptureOracle::kCompositorUpdate, | 455 new FrameSubscriber( |
405 oracle_proxy, &delivery_log_, | 456 media::VideoCaptureOracle::kCompositorUpdate, oracle_proxy, |
406 cursor_renderer_ | 457 &delivery_log_, cursor_renderer_ ? cursor_renderer_->GetWeakPtr() |
407 ? cursor_renderer_->GetWeakPtr() | 458 : base::WeakPtr<CursorRenderer>(), |
408 : base::WeakPtr<CursorRenderer>())); | 459 window_activity_tracker_ ? window_activity_tracker_->GetWeakPtr() |
460 : base::WeakPtr<WindowActivityTracker>())); | |
409 view->BeginFrameSubscription(subscriber.Pass()); | 461 view->BeginFrameSubscription(subscriber.Pass()); |
410 } | 462 } |
411 | 463 |
412 // Subscribe to timer events. This instance will service these as well. | 464 // Subscribe to timer events. This instance will service these as well. |
413 timer_.Start(FROM_HERE, | 465 timer_.Start(FROM_HERE, |
414 std::max(oracle_proxy->min_capture_period(), | 466 std::max(oracle_proxy->min_capture_period(), |
415 base::TimeDelta::FromMilliseconds(media | 467 base::TimeDelta::FromMilliseconds(media |
416 ::VideoCaptureOracle::kMinTimerPollPeriodMillis)), | 468 ::VideoCaptureOracle::kMinTimerPollPeriodMillis)), |
417 base::Bind(&ContentCaptureSubscription::OnTimer, | 469 base::Bind(&ContentCaptureSubscription::OnTimer, |
418 base::Unretained(this))); | 470 base::Unretained(this))); |
(...skipping 490 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
909 scoped_ptr<Client> client) { | 961 scoped_ptr<Client> client) { |
910 DVLOG(1) << "Allocating " << params.requested_format.frame_size.ToString(); | 962 DVLOG(1) << "Allocating " << params.requested_format.frame_size.ToString(); |
911 core_->AllocateAndStart(params, client.Pass()); | 963 core_->AllocateAndStart(params, client.Pass()); |
912 } | 964 } |
913 | 965 |
914 void WebContentsVideoCaptureDevice::StopAndDeAllocate() { | 966 void WebContentsVideoCaptureDevice::StopAndDeAllocate() { |
915 core_->StopAndDeAllocate(); | 967 core_->StopAndDeAllocate(); |
916 } | 968 } |
917 | 969 |
918 } // namespace content | 970 } // namespace content |
OLD | NEW |