Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 // | 4 // |
| 5 // Implementation notes: This needs to work on a variety of hardware | 5 // Implementation notes: This needs to work on a variety of hardware |
| 6 // configurations where the speed of the CPU and GPU greatly affect overall | 6 // configurations where the speed of the CPU and GPU greatly affect overall |
| 7 // performance. Spanning several threads, the process of capturing has been | 7 // performance. Spanning several threads, the process of capturing has been |
| 8 // split up into four conceptual stages: | 8 // split up into four conceptual stages: |
| 9 // | 9 // |
| 10 // 1. Reserve Buffer: Before a frame can be captured, a slot in the client's | 10 // 1. Reserve Buffer: Before a frame can be captured, a slot in the client's |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 43 // | 43 // |
| 44 // In the above example, both capturing and rendering *each* take almost the | 44 // In the above example, both capturing and rendering *each* take almost the |
| 45 // full 33 ms available between frames, yet we see that the required throughput | 45 // full 33 ms available between frames, yet we see that the required throughput |
| 46 // is obtained. | 46 // is obtained. |
| 47 // | 47 // |
| 48 // Turning on verbose logging will cause the effective frame rate to be logged | 48 // Turning on verbose logging will cause the effective frame rate to be logged |
| 49 // at 5-second intervals. | 49 // at 5-second intervals. |
| 50 | 50 |
| 51 #include "content/browser/media/capture/web_contents_video_capture_device.h" | 51 #include "content/browser/media/capture/web_contents_video_capture_device.h" |
| 52 | 52 |
| 53 #include <algorithm> | |
|
xjz
2015/10/22 17:08:42
Still think no need to include this.
Irfan
2015/10/26 23:10:32
This was a lint error unrelated to my code change
| |
| 54 | |
| 53 #include "base/basictypes.h" | 55 #include "base/basictypes.h" |
| 54 #include "base/bind.h" | 56 #include "base/bind.h" |
| 55 #include "base/callback_helpers.h" | 57 #include "base/callback_helpers.h" |
| 56 #include "base/location.h" | 58 #include "base/location.h" |
| 57 #include "base/logging.h" | 59 #include "base/logging.h" |
| 58 #include "base/memory/scoped_ptr.h" | 60 #include "base/memory/scoped_ptr.h" |
| 59 #include "base/memory/weak_ptr.h" | 61 #include "base/memory/weak_ptr.h" |
| 60 #include "base/metrics/histogram.h" | 62 #include "base/metrics/histogram.h" |
| 61 #include "base/sequenced_task_runner.h" | 63 #include "base/sequenced_task_runner.h" |
| 62 #include "base/single_thread_task_runner.h" | 64 #include "base/single_thread_task_runner.h" |
| 63 #include "base/threading/thread.h" | 65 #include "base/threading/thread.h" |
| 64 #include "base/threading/thread_checker.h" | 66 #include "base/threading/thread_checker.h" |
| 65 #include "base/time/time.h" | 67 #include "base/time/time.h" |
| 68 #include "content/browser/media/capture/cursor_renderer.h" | |
| 66 #include "content/browser/media/capture/web_contents_capture_util.h" | 69 #include "content/browser/media/capture/web_contents_capture_util.h" |
| 67 #include "content/browser/media/capture/web_contents_tracker.h" | 70 #include "content/browser/media/capture/web_contents_tracker.h" |
| 68 #include "content/browser/renderer_host/render_widget_host_impl.h" | 71 #include "content/browser/renderer_host/render_widget_host_impl.h" |
| 69 #include "content/browser/renderer_host/render_widget_host_view_base.h" | 72 #include "content/browser/renderer_host/render_widget_host_view_base.h" |
| 70 #include "content/public/browser/browser_thread.h" | 73 #include "content/public/browser/browser_thread.h" |
| 71 #include "content/public/browser/render_process_host.h" | 74 #include "content/public/browser/render_process_host.h" |
| 72 #include "content/public/browser/render_widget_host_view.h" | 75 #include "content/public/browser/render_widget_host_view.h" |
| 73 #include "content/public/browser/render_widget_host_view_frame_subscriber.h" | 76 #include "content/public/browser/render_widget_host_view_frame_subscriber.h" |
| 74 #include "content/public/browser/web_contents.h" | 77 #include "content/public/browser/web_contents.h" |
| 75 #include "media/base/video_capture_types.h" | 78 #include "media/base/video_capture_types.h" |
| 76 #include "media/base/video_util.h" | 79 #include "media/base/video_util.h" |
| 77 #include "media/capture/content/screen_capture_device_core.h" | 80 #include "media/capture/content/screen_capture_device_core.h" |
| 78 #include "media/capture/content/thread_safe_capture_oracle.h" | 81 #include "media/capture/content/thread_safe_capture_oracle.h" |
| 79 #include "media/capture/content/video_capture_oracle.h" | 82 #include "media/capture/content/video_capture_oracle.h" |
| 80 #include "skia/ext/image_operations.h" | 83 #include "skia/ext/image_operations.h" |
| 81 #include "third_party/skia/include/core/SkBitmap.h" | 84 #include "third_party/skia/include/core/SkBitmap.h" |
| 82 #include "third_party/skia/include/core/SkColor.h" | 85 #include "third_party/skia/include/core/SkColor.h" |
| 83 #include "ui/base/layout.h" | 86 #include "ui/base/layout.h" |
| 84 #include "ui/gfx/geometry/dip_util.h" | 87 #include "ui/gfx/geometry/dip_util.h" |
| 85 #include "ui/gfx/geometry/size_conversions.h" | 88 #include "ui/gfx/geometry/size_conversions.h" |
| 86 | 89 |
| 90 #if defined(USE_AURA) | |
| 91 #include "content/browser/media/capture/cursor_renderer_aura.h" | |
| 92 #else | |
| 93 #include "content/browser/media/capture/cursor_renderer_dummy.h" | |
|
miu
2015/10/23 01:55:13
Suggest you don't provide the #else clause. See c
Irfan
2015/10/26 23:10:31
Done.
| |
| 94 #endif | |
| 95 | |
| 87 namespace content { | 96 namespace content { |
| 88 | 97 |
| 89 namespace { | 98 namespace { |
| 90 | 99 |
| 91 void DeleteOnWorkerThread(scoped_ptr<base::Thread> render_thread, | 100 void DeleteOnWorkerThread(scoped_ptr<base::Thread> render_thread, |
| 92 const base::Closure& callback) { | 101 const base::Closure& callback) { |
| 93 render_thread.reset(); | 102 render_thread.reset(); |
| 94 | 103 |
| 95 // After thread join call the callback on UI thread. | 104 // After thread join call the callback on UI thread. |
| 96 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, callback); | 105 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, callback); |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 112 | 121 |
| 113 DISALLOW_COPY_AND_ASSIGN(VideoFrameDeliveryLog); | 122 DISALLOW_COPY_AND_ASSIGN(VideoFrameDeliveryLog); |
| 114 }; | 123 }; |
| 115 | 124 |
| 116 // FrameSubscriber is a proxy to the ThreadSafeCaptureOracle that's compatible | 125 // FrameSubscriber is a proxy to the ThreadSafeCaptureOracle that's compatible |
| 117 // with RenderWidgetHostViewFrameSubscriber. We create one per event type. | 126 // with RenderWidgetHostViewFrameSubscriber. We create one per event type. |
| 118 class FrameSubscriber : public RenderWidgetHostViewFrameSubscriber { | 127 class FrameSubscriber : public RenderWidgetHostViewFrameSubscriber { |
| 119 public: | 128 public: |
| 120 FrameSubscriber(media::VideoCaptureOracle::Event event_type, | 129 FrameSubscriber(media::VideoCaptureOracle::Event event_type, |
| 121 const scoped_refptr<media::ThreadSafeCaptureOracle>& oracle, | 130 const scoped_refptr<media::ThreadSafeCaptureOracle>& oracle, |
| 122 VideoFrameDeliveryLog* delivery_log) | 131 VideoFrameDeliveryLog* delivery_log, |
| 132 base::WeakPtr<content::CursorRenderer> cursor_renderer) | |
| 123 : event_type_(event_type), | 133 : event_type_(event_type), |
| 124 oracle_proxy_(oracle), | 134 oracle_proxy_(oracle), |
| 125 delivery_log_(delivery_log) {} | 135 delivery_log_(delivery_log), |
| 136 cursor_renderer_(cursor_renderer), | |
| 137 weak_ptr_factory_(this) {} | |
| 126 | 138 |
| 127 bool ShouldCaptureFrame( | 139 bool ShouldCaptureFrame( |
| 128 const gfx::Rect& damage_rect, | 140 const gfx::Rect& damage_rect, |
| 129 base::TimeTicks present_time, | 141 base::TimeTicks present_time, |
| 130 scoped_refptr<media::VideoFrame>* storage, | 142 scoped_refptr<media::VideoFrame>* storage, |
| 131 RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback* | 143 RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback* |
| 132 deliver_frame_cb) override; | 144 deliver_frame_cb) override; |
| 133 | 145 |
| 146 static void DidCaptureFrame( | |
| 147 base::WeakPtr<FrameSubscriber> frame_subscriber_, | |
| 148 media::ThreadSafeCaptureOracle::CaptureFrameCallback capture_frame_cb, | |
|
miu
2015/10/23 01:55:13
Please pass the callback by const reference. The
Irfan
2015/10/26 23:10:32
Done.
| |
| 149 const scoped_refptr<media::VideoFrame>& frame, | |
| 150 base::TimeTicks timestamp, | |
| 151 gfx::Rect region_in_frame, | |
|
miu
2015/10/23 01:55:13
The gfx::Rect should be passed by const reference
Irfan
2015/10/26 23:10:32
Done.
| |
| 152 bool success); | |
| 153 | |
| 134 private: | 154 private: |
| 135 const media::VideoCaptureOracle::Event event_type_; | 155 const media::VideoCaptureOracle::Event event_type_; |
| 136 scoped_refptr<media::ThreadSafeCaptureOracle> oracle_proxy_; | 156 scoped_refptr<media::ThreadSafeCaptureOracle> oracle_proxy_; |
| 137 VideoFrameDeliveryLog* const delivery_log_; | 157 VideoFrameDeliveryLog* const delivery_log_; |
| 158 base::WeakPtr<content::CursorRenderer> cursor_renderer_; | |
|
miu
2015/10/23 01:55:13
Please document why we need a weak pointer to the
miu
2015/10/23 01:55:13
No need for content:: qualifier.
Irfan
2015/10/26 23:10:31
Done.
Irfan
2015/10/26 23:10:32
Done.
| |
| 159 base::WeakPtrFactory<FrameSubscriber> weak_ptr_factory_; | |
| 138 }; | 160 }; |
| 139 | 161 |
| 140 // ContentCaptureSubscription is the relationship between a RenderWidgetHost | 162 // ContentCaptureSubscription is the relationship between a RenderWidgetHost |
| 141 // whose content is updating, a subscriber that is deciding which of these | 163 // whose content is updating, a subscriber that is deciding which of these |
| 142 // updates to capture (and where to deliver them to), and a callback that | 164 // updates to capture (and where to deliver them to), and a callback that |
| 143 // knows how to do the capture and prepare the result for delivery. | 165 // knows how to do the capture and prepare the result for delivery. |
| 144 // | 166 // |
| 145 // In practice, this means (a) installing a RenderWidgetHostFrameSubscriber in | 167 // In practice, this means (a) installing a RenderWidgetHostFrameSubscriber in |
| 146 // the RenderWidgetHostView, to process compositor updates, and (b) running a | 168 // the RenderWidgetHostView, to process compositor updates, and (b) running a |
| 147 // timer to possibly initiate forced, non-event-driven captures needed by | 169 // timer to possibly initiate forced, non-event-driven captures needed by |
| (...skipping 22 matching lines...) Expand all Loading... | |
| 170 private: | 192 private: |
| 171 void OnTimer(); | 193 void OnTimer(); |
| 172 | 194 |
| 173 // Maintain a weak reference to the RenderWidgetHost (via its routing ID), | 195 // Maintain a weak reference to the RenderWidgetHost (via its routing ID), |
| 174 // since the instance could be destroyed externally during the lifetime of | 196 // since the instance could be destroyed externally during the lifetime of |
| 175 // |this|. | 197 // |this|. |
| 176 const int render_process_id_; | 198 const int render_process_id_; |
| 177 const int render_widget_id_; | 199 const int render_widget_id_; |
| 178 | 200 |
| 179 VideoFrameDeliveryLog delivery_log_; | 201 VideoFrameDeliveryLog delivery_log_; |
| 180 FrameSubscriber timer_subscriber_; | 202 scoped_ptr<FrameSubscriber> timer_subscriber_; |
| 181 CaptureCallback capture_callback_; | 203 CaptureCallback capture_callback_; |
| 182 base::Timer timer_; | 204 base::Timer timer_; |
| 183 | 205 |
| 206 // Responsible for tracking the cursor state and input events to make | |
| 207 // decisions and then render the mouse cursor on the video frame after | |
| 208 // capture is completed. | |
| 209 scoped_ptr<content::CursorRenderer> cursor_renderer_; | |
| 210 | |
| 184 DISALLOW_COPY_AND_ASSIGN(ContentCaptureSubscription); | 211 DISALLOW_COPY_AND_ASSIGN(ContentCaptureSubscription); |
| 185 }; | 212 }; |
| 186 | 213 |
| 187 // Render the SkBitmap |input| into the given VideoFrame buffer |output|, then | 214 // Render the SkBitmap |input| into the given VideoFrame buffer |output|, then |
| 188 // 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 |
| 189 // 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. |
| 190 // Scaling and letterboxing will be done as needed. | 217 // Scaling and letterboxing will be done as needed. |
| 191 // | 218 // |
| 192 // This software implementation should be used only when GPU acceleration of | 219 // This software implementation should be used only when GPU acceleration of |
| 193 // 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 |
| 194 // 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 |
| 195 // thread where such a pause would cause UI jank. | 222 // thread where such a pause would cause UI jank. |
| 196 void RenderVideoFrame(const SkBitmap& input, | 223 void RenderVideoFrame(const SkBitmap& input, |
| 197 const scoped_refptr<media::VideoFrame>& output, | 224 const scoped_refptr<media::VideoFrame>& output, |
| 198 const base::Callback<void(bool)>& done_cb); | 225 const base::Callback<void(gfx::Rect, bool)>& done_cb); |
| 199 | 226 |
| 200 // Renews capture subscriptions based on feedback from WebContentsTracker, and | 227 // Renews capture subscriptions based on feedback from WebContentsTracker, and |
| 201 // also executes copying of the backing store on the UI BrowserThread. | 228 // also executes copying of the backing store on the UI BrowserThread. |
| 202 class WebContentsCaptureMachine : public media::VideoCaptureMachine { | 229 class WebContentsCaptureMachine : public media::VideoCaptureMachine { |
| 203 public: | 230 public: |
| 204 WebContentsCaptureMachine(int render_process_id, | 231 WebContentsCaptureMachine(int render_process_id, |
| 205 int main_render_frame_id, | 232 int main_render_frame_id, |
| 206 bool enable_auto_throttling); | 233 bool enable_auto_throttling); |
| 207 ~WebContentsCaptureMachine() override; | 234 ~WebContentsCaptureMachine() override; |
| 208 | 235 |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 242 const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback& | 269 const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback& |
| 243 deliver_frame_cb, | 270 deliver_frame_cb, |
| 244 const SkBitmap& bitmap, | 271 const SkBitmap& bitmap, |
| 245 ReadbackResponse response); | 272 ReadbackResponse response); |
| 246 | 273 |
| 247 // Response callback for RWHVP::CopyFromCompositingSurfaceToVideoFrame(). | 274 // Response callback for RWHVP::CopyFromCompositingSurfaceToVideoFrame(). |
| 248 void DidCopyFromCompositingSurfaceToVideoFrame( | 275 void DidCopyFromCompositingSurfaceToVideoFrame( |
| 249 const base::TimeTicks& start_time, | 276 const base::TimeTicks& start_time, |
| 250 const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback& | 277 const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback& |
| 251 deliver_frame_cb, | 278 deliver_frame_cb, |
| 279 gfx::Rect region_in_frame, | |
| 252 bool success); | 280 bool success); |
| 253 | 281 |
| 254 // Remove the old subscription, and attempt to start a new one if |had_target| | 282 // Remove the old subscription, and attempt to start a new one if |had_target| |
| 255 // is true. | 283 // is true. |
| 256 void RenewFrameSubscription(bool had_target); | 284 void RenewFrameSubscription(bool had_target); |
| 257 | 285 |
| 258 // Called whenever the render widget is resized. | 286 // Called whenever the render widget is resized. |
| 259 void UpdateCaptureSize(); | 287 void UpdateCaptureSize(); |
| 260 | 288 |
| 261 // Parameters saved in constructor. | 289 // Parameters saved in constructor. |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 297 | 325 |
| 298 bool FrameSubscriber::ShouldCaptureFrame( | 326 bool FrameSubscriber::ShouldCaptureFrame( |
| 299 const gfx::Rect& damage_rect, | 327 const gfx::Rect& damage_rect, |
| 300 base::TimeTicks present_time, | 328 base::TimeTicks present_time, |
| 301 scoped_refptr<media::VideoFrame>* storage, | 329 scoped_refptr<media::VideoFrame>* storage, |
| 302 DeliverFrameCallback* deliver_frame_cb) { | 330 DeliverFrameCallback* deliver_frame_cb) { |
| 303 TRACE_EVENT1("gpu.capture", "FrameSubscriber::ShouldCaptureFrame", | 331 TRACE_EVENT1("gpu.capture", "FrameSubscriber::ShouldCaptureFrame", |
| 304 "instance", this); | 332 "instance", this); |
| 305 | 333 |
| 306 media::ThreadSafeCaptureOracle::CaptureFrameCallback capture_frame_cb; | 334 media::ThreadSafeCaptureOracle::CaptureFrameCallback capture_frame_cb; |
| 335 | |
| 307 bool oracle_decision = oracle_proxy_->ObserveEventAndDecideCapture( | 336 bool oracle_decision = oracle_proxy_->ObserveEventAndDecideCapture( |
| 308 event_type_, damage_rect, present_time, storage, &capture_frame_cb); | 337 event_type_, damage_rect, present_time, storage, &capture_frame_cb); |
| 309 | 338 |
| 310 if (!capture_frame_cb.is_null()) | 339 if (!capture_frame_cb.is_null()) |
| 311 *deliver_frame_cb = base::Bind(capture_frame_cb, *storage); | 340 *deliver_frame_cb = |
| 341 base::Bind(&FrameSubscriber::DidCaptureFrame, | |
| 342 weak_ptr_factory_.GetWeakPtr(), capture_frame_cb, *storage); | |
| 312 if (oracle_decision) | 343 if (oracle_decision) |
| 313 delivery_log_->ChronicleFrameDelivery(present_time); | 344 delivery_log_->ChronicleFrameDelivery(present_time); |
| 314 return oracle_decision; | 345 return oracle_decision; |
| 315 } | 346 } |
| 316 | 347 |
| 348 void FrameSubscriber::DidCaptureFrame( | |
| 349 base::WeakPtr<FrameSubscriber> frame_subscriber_, | |
| 350 media::ThreadSafeCaptureOracle::CaptureFrameCallback capture_frame_cb, | |
| 351 const scoped_refptr<media::VideoFrame>& frame, | |
| 352 base::TimeTicks timestamp, | |
| 353 gfx::Rect region_in_frame, | |
| 354 bool success) { | |
| 355 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
| 356 if (frame_subscriber_ && frame_subscriber_->cursor_renderer_ && success) { | |
| 357 if (frame_subscriber_->cursor_renderer_->Update(region_in_frame)) | |
|
miu
2015/10/23 01:55:13
Some considerations on the timing of the call to U
Irfan
2015/10/26 23:10:32
I went with a comment to see if it affects any sce
| |
| 358 frame_subscriber_->cursor_renderer_->RenderOnVideoFrame(frame); | |
| 359 } | |
| 360 capture_frame_cb.Run(frame, timestamp, success); | |
| 361 } | |
| 362 | |
| 317 ContentCaptureSubscription::ContentCaptureSubscription( | 363 ContentCaptureSubscription::ContentCaptureSubscription( |
| 318 const RenderWidgetHost& source, | 364 const RenderWidgetHost& source, |
| 319 const scoped_refptr<media::ThreadSafeCaptureOracle>& oracle_proxy, | 365 const scoped_refptr<media::ThreadSafeCaptureOracle>& oracle_proxy, |
| 320 const CaptureCallback& capture_callback) | 366 const CaptureCallback& capture_callback) |
| 321 : render_process_id_(source.GetProcess()->GetID()), | 367 : render_process_id_(source.GetProcess()->GetID()), |
| 322 render_widget_id_(source.GetRoutingID()), | 368 render_widget_id_(source.GetRoutingID()), |
| 323 delivery_log_(), | 369 delivery_log_(), |
| 324 timer_subscriber_(media::VideoCaptureOracle::kTimerPoll, oracle_proxy, | |
| 325 &delivery_log_), | |
| 326 capture_callback_(capture_callback), | 370 capture_callback_(capture_callback), |
| 327 timer_(true, true) { | 371 timer_(true, true) { |
| 328 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 372 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 329 | 373 |
| 330 RenderWidgetHostView* const view = source.GetView(); | 374 RenderWidgetHostView* const view = source.GetView(); |
| 375 if (view) | |
| 376 #if defined(USE_AURA) | |
| 377 cursor_renderer_.reset( | |
| 378 new content::CursorRendererAura(view->GetNativeView())); | |
| 379 #else | |
| 380 cursor_renderer_.reset( | |
|
miu
2015/10/23 01:55:13
Please remove the #else clause here. In Chromium
Irfan
2015/10/26 23:10:32
Done.
| |
| 381 new content::CursorRendererDummy(view->GetNativeView())); | |
| 382 #endif | |
| 383 timer_subscriber_.reset( | |
| 384 new FrameSubscriber(media::VideoCaptureOracle::kTimerPoll, oracle_proxy, | |
| 385 &delivery_log_, cursor_renderer_->GetWeakPtr())); | |
| 331 | 386 |
| 332 // Subscribe to compositor updates. These will be serviced directly by the | 387 // Subscribe to compositor updates. These will be serviced directly by the |
| 333 // oracle. | 388 // oracle. |
| 334 if (view) { | 389 if (view) { |
| 335 scoped_ptr<RenderWidgetHostViewFrameSubscriber> subscriber( | 390 scoped_ptr<RenderWidgetHostViewFrameSubscriber> subscriber( |
| 336 new FrameSubscriber(media::VideoCaptureOracle::kCompositorUpdate, | 391 new FrameSubscriber(media::VideoCaptureOracle::kCompositorUpdate, |
| 337 oracle_proxy, &delivery_log_)); | 392 oracle_proxy, &delivery_log_, |
| 393 cursor_renderer_->GetWeakPtr())); | |
| 338 view->BeginFrameSubscription(subscriber.Pass()); | 394 view->BeginFrameSubscription(subscriber.Pass()); |
| 339 } | 395 } |
| 340 | 396 |
| 341 // Subscribe to timer events. This instance will service these as well. | 397 // Subscribe to timer events. This instance will service these as well. |
| 342 timer_.Start(FROM_HERE, | 398 timer_.Start(FROM_HERE, |
| 343 std::max(oracle_proxy->min_capture_period(), | 399 std::max(oracle_proxy->min_capture_period(), |
| 344 base::TimeDelta::FromMilliseconds(media | 400 base::TimeDelta::FromMilliseconds(media |
| 345 ::VideoCaptureOracle::kMinTimerPollPeriodMillis)), | 401 ::VideoCaptureOracle::kMinTimerPollPeriodMillis)), |
| 346 base::Bind(&ContentCaptureSubscription::OnTimer, | 402 base::Bind(&ContentCaptureSubscription::OnTimer, |
| 347 base::Unretained(this))); | 403 base::Unretained(this))); |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 363 } | 419 } |
| 364 | 420 |
| 365 void ContentCaptureSubscription::OnTimer() { | 421 void ContentCaptureSubscription::OnTimer() { |
| 366 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 422 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 367 TRACE_EVENT0("gpu.capture", "ContentCaptureSubscription::OnTimer"); | 423 TRACE_EVENT0("gpu.capture", "ContentCaptureSubscription::OnTimer"); |
| 368 | 424 |
| 369 scoped_refptr<media::VideoFrame> frame; | 425 scoped_refptr<media::VideoFrame> frame; |
| 370 RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback deliver_frame_cb; | 426 RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback deliver_frame_cb; |
| 371 | 427 |
| 372 const base::TimeTicks start_time = base::TimeTicks::Now(); | 428 const base::TimeTicks start_time = base::TimeTicks::Now(); |
| 373 if (timer_subscriber_.ShouldCaptureFrame(gfx::Rect(), | 429 if (timer_subscriber_->ShouldCaptureFrame(gfx::Rect(), start_time, &frame, |
| 374 start_time, | 430 &deliver_frame_cb)) { |
| 375 &frame, | |
| 376 &deliver_frame_cb)) { | |
| 377 capture_callback_.Run(start_time, frame, deliver_frame_cb); | 431 capture_callback_.Run(start_time, frame, deliver_frame_cb); |
| 378 } | 432 } |
| 379 } | 433 } |
| 380 | 434 |
| 381 void RenderVideoFrame(const SkBitmap& input, | 435 void RenderVideoFrame(const SkBitmap& input, |
| 382 const scoped_refptr<media::VideoFrame>& output, | 436 const scoped_refptr<media::VideoFrame>& output, |
| 383 const base::Callback<void(bool)>& done_cb) { | 437 const base::Callback<void(gfx::Rect, bool)>& done_cb) { |
| 384 base::ScopedClosureRunner failure_handler(base::Bind(done_cb, false)); | 438 base::ScopedClosureRunner failure_handler( |
| 439 base::Bind(done_cb, gfx::Rect(), false)); | |
| 385 | 440 |
| 386 SkAutoLockPixels locker(input); | 441 SkAutoLockPixels locker(input); |
| 387 | 442 |
| 388 // Sanity-check the captured bitmap. | 443 // Sanity-check the captured bitmap. |
| 389 if (input.empty() || | 444 if (input.empty() || |
| 390 !input.readyToDraw() || | 445 !input.readyToDraw() || |
| 391 input.colorType() != kN32_SkColorType || | 446 input.colorType() != kN32_SkColorType || |
| 392 input.width() < 2 || input.height() < 2) { | 447 input.width() < 2 || input.height() < 2) { |
| 393 DVLOG(1) << "input unacceptable (size=" | 448 DVLOG(1) << "input unacceptable (size=" |
| 394 << input.getSize() | 449 << input.getSize() |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 405 | 460 |
| 406 // Calculate the width and height of the content region in the |output|, based | 461 // Calculate the width and height of the content region in the |output|, based |
| 407 // on the aspect ratio of |input|. | 462 // on the aspect ratio of |input|. |
| 408 const gfx::Rect region_in_frame = media::ComputeLetterboxRegion( | 463 const gfx::Rect region_in_frame = media::ComputeLetterboxRegion( |
| 409 output->visible_rect(), gfx::Size(input.width(), input.height())); | 464 output->visible_rect(), gfx::Size(input.width(), input.height())); |
| 410 | 465 |
| 411 // Scale the bitmap to the required size, if necessary. | 466 // Scale the bitmap to the required size, if necessary. |
| 412 SkBitmap scaled_bitmap; | 467 SkBitmap scaled_bitmap; |
| 413 if (input.width() != region_in_frame.width() || | 468 if (input.width() != region_in_frame.width() || |
| 414 input.height() != region_in_frame.height()) { | 469 input.height() != region_in_frame.height()) { |
| 415 | |
| 416 skia::ImageOperations::ResizeMethod method; | 470 skia::ImageOperations::ResizeMethod method; |
| 417 if (input.width() < region_in_frame.width() || | 471 if (input.width() < region_in_frame.width() || |
| 418 input.height() < region_in_frame.height()) { | 472 input.height() < region_in_frame.height()) { |
| 419 // Avoid box filtering when magnifying, because it's actually | 473 // Avoid box filtering when magnifying, because it's actually |
| 420 // nearest-neighbor. | 474 // nearest-neighbor. |
| 421 method = skia::ImageOperations::RESIZE_HAMMING1; | 475 method = skia::ImageOperations::RESIZE_HAMMING1; |
| 422 } else { | 476 } else { |
| 423 method = skia::ImageOperations::RESIZE_BOX; | 477 method = skia::ImageOperations::RESIZE_BOX; |
| 424 } | 478 } |
| 425 | 479 |
| (...skipping 20 matching lines...) Expand all Loading... | |
| 446 SkAutoLockPixels scaled_bitmap_locker(scaled_bitmap); | 500 SkAutoLockPixels scaled_bitmap_locker(scaled_bitmap); |
| 447 media::CopyRGBToVideoFrame( | 501 media::CopyRGBToVideoFrame( |
| 448 reinterpret_cast<uint8*>(scaled_bitmap.getPixels()), | 502 reinterpret_cast<uint8*>(scaled_bitmap.getPixels()), |
| 449 scaled_bitmap.rowBytes(), | 503 scaled_bitmap.rowBytes(), |
| 450 region_in_yv12_frame, | 504 region_in_yv12_frame, |
| 451 output.get()); | 505 output.get()); |
| 452 } | 506 } |
| 453 | 507 |
| 454 // The result is now ready. | 508 // The result is now ready. |
| 455 ignore_result(failure_handler.Release()); | 509 ignore_result(failure_handler.Release()); |
| 456 done_cb.Run(true); | 510 done_cb.Run(region_in_frame, true); |
| 457 } | 511 } |
| 458 | 512 |
| 459 VideoFrameDeliveryLog::VideoFrameDeliveryLog() | 513 VideoFrameDeliveryLog::VideoFrameDeliveryLog() |
| 460 : last_frame_rate_log_time_(), | 514 : last_frame_rate_log_time_(), |
| 461 count_frames_rendered_(0) { | 515 count_frames_rendered_(0) { |
| 462 } | 516 } |
| 463 | 517 |
| 464 void VideoFrameDeliveryLog::ChronicleFrameDelivery(base::TimeTicks frame_time) { | 518 void VideoFrameDeliveryLog::ChronicleFrameDelivery(base::TimeTicks frame_time) { |
| 465 // Log frame rate, if verbose logging is turned on. | 519 // Log frame rate, if verbose logging is turned on. |
| 466 static const base::TimeDelta kFrameRateLogInterval = | 520 static const base::TimeDelta kFrameRateLogInterval = |
| (...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 570 // The following cancels any outstanding callbacks and causes IsStarted() to | 624 // The following cancels any outstanding callbacks and causes IsStarted() to |
| 571 // return false from here onward. | 625 // return false from here onward. |
| 572 weak_ptr_factory_.InvalidateWeakPtrs(); | 626 weak_ptr_factory_.InvalidateWeakPtrs(); |
| 573 | 627 |
| 574 // Note: RenewFrameSubscription() must be called before stopping |tracker_| so | 628 // Note: RenewFrameSubscription() must be called before stopping |tracker_| so |
| 575 // the web_contents() can be notified that the capturing is ending. | 629 // the web_contents() can be notified that the capturing is ending. |
| 576 RenewFrameSubscription(false); | 630 RenewFrameSubscription(false); |
| 577 tracker_->Stop(); | 631 tracker_->Stop(); |
| 578 | 632 |
| 579 // The render thread cannot be stopped on the UI thread, so post a message | 633 // The render thread cannot be stopped on the UI thread, so post a message |
| 580 // to the thread pool used for blocking operations. | 634 // to the thread pool used; for blocking operations. |
|
xjz
2015/10/22 17:08:42
nit: remove ';'.
| |
| 581 if (render_thread_.get()) { | 635 if (render_thread_.get()) { |
| 582 BrowserThread::PostBlockingPoolTask( | 636 BrowserThread::PostBlockingPoolTask( |
| 583 FROM_HERE, | 637 FROM_HERE, |
| 584 base::Bind(&DeleteOnWorkerThread, base::Passed(&render_thread_), | 638 base::Bind(&DeleteOnWorkerThread, base::Passed(&render_thread_), |
| 585 callback)); | 639 callback)); |
| 586 } | 640 } |
| 587 } | 641 } |
| 588 | 642 |
| 589 void WebContentsCaptureMachine::Capture( | 643 void WebContentsCaptureMachine::Capture( |
| 590 const base::TimeTicks& start_time, | 644 const base::TimeTicks& start_time, |
| 591 const scoped_refptr<media::VideoFrame>& target, | 645 const scoped_refptr<media::VideoFrame>& target, |
| 592 const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback& | 646 const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback& |
| 593 deliver_frame_cb) { | 647 deliver_frame_cb) { |
| 594 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 648 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 595 | 649 |
| 596 RenderWidgetHost* rwh = tracker_->GetTargetRenderWidgetHost(); | 650 RenderWidgetHost* rwh = tracker_->GetTargetRenderWidgetHost(); |
| 597 RenderWidgetHostViewBase* view = | 651 RenderWidgetHostViewBase* view = |
| 598 rwh ? static_cast<RenderWidgetHostViewBase*>(rwh->GetView()) : NULL; | 652 rwh ? static_cast<RenderWidgetHostViewBase*>(rwh->GetView()) : NULL; |
| 599 if (!view) { | 653 if (!view) { |
| 600 deliver_frame_cb.Run(base::TimeTicks(), false); | 654 deliver_frame_cb.Run(base::TimeTicks(), gfx::Rect(), false); |
| 601 return; | 655 return; |
| 602 } | 656 } |
| 603 | 657 |
| 604 gfx::Size view_size = view->GetViewBounds().size(); | 658 gfx::Size view_size = view->GetViewBounds().size(); |
| 605 if (view_size != last_view_size_) { | 659 if (view_size != last_view_size_) { |
| 606 last_view_size_ = view_size; | 660 last_view_size_ = view_size; |
| 607 | 661 |
| 608 // Measure the number of kilopixels. | 662 // Measure the number of kilopixels. |
| 609 UMA_HISTOGRAM_COUNTS_10000( | 663 UMA_HISTOGRAM_COUNTS_10000( |
| 610 "TabCapture.ViewChangeKiloPixels", | 664 "TabCapture.ViewChangeKiloPixels", |
| (...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 715 if (response == READBACK_SUCCESS) { | 769 if (response == READBACK_SUCCESS) { |
| 716 UMA_HISTOGRAM_TIMES("TabCapture.CopyTimeBitmap", now - start_time); | 770 UMA_HISTOGRAM_TIMES("TabCapture.CopyTimeBitmap", now - start_time); |
| 717 TRACE_EVENT_ASYNC_STEP_INTO0("gpu.capture", "Capture", target.get(), | 771 TRACE_EVENT_ASYNC_STEP_INTO0("gpu.capture", "Capture", target.get(), |
| 718 "Render"); | 772 "Render"); |
| 719 render_thread_->task_runner()->PostTask( | 773 render_thread_->task_runner()->PostTask( |
| 720 FROM_HERE, base::Bind(&RenderVideoFrame, bitmap, target, | 774 FROM_HERE, base::Bind(&RenderVideoFrame, bitmap, target, |
| 721 base::Bind(deliver_frame_cb, start_time))); | 775 base::Bind(deliver_frame_cb, start_time))); |
| 722 } else { | 776 } else { |
| 723 // Capture can fail due to transient issues, so just skip this frame. | 777 // Capture can fail due to transient issues, so just skip this frame. |
| 724 DVLOG(1) << "CopyFromBackingStore failed; skipping frame."; | 778 DVLOG(1) << "CopyFromBackingStore failed; skipping frame."; |
| 725 deliver_frame_cb.Run(start_time, false); | 779 deliver_frame_cb.Run(start_time, gfx::Rect(), false); |
| 726 } | 780 } |
| 727 } | 781 } |
| 728 | 782 |
| 729 void WebContentsCaptureMachine::DidCopyFromCompositingSurfaceToVideoFrame( | 783 void WebContentsCaptureMachine::DidCopyFromCompositingSurfaceToVideoFrame( |
| 730 const base::TimeTicks& start_time, | 784 const base::TimeTicks& start_time, |
| 731 const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback& | 785 const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback& |
| 732 deliver_frame_cb, | 786 deliver_frame_cb, |
| 787 gfx::Rect region_in_frame, | |
| 733 bool success) { | 788 bool success) { |
| 734 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 789 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 735 base::TimeTicks now = base::TimeTicks::Now(); | 790 base::TimeTicks now = base::TimeTicks::Now(); |
| 736 | 791 |
| 737 if (success) { | 792 if (success) { |
| 738 UMA_HISTOGRAM_TIMES("TabCapture.CopyTimeVideoFrame", now - start_time); | 793 UMA_HISTOGRAM_TIMES("TabCapture.CopyTimeVideoFrame", now - start_time); |
| 739 } else { | 794 } else { |
| 740 // Capture can fail due to transient issues, so just skip this frame. | 795 // Capture can fail due to transient issues, so just skip this frame. |
| 741 DVLOG(1) << "CopyFromCompositingSurface failed; skipping frame."; | 796 DVLOG(1) << "CopyFromCompositingSurface failed; skipping frame."; |
| 742 } | 797 } |
| 743 deliver_frame_cb.Run(start_time, success); | 798 deliver_frame_cb.Run(start_time, region_in_frame, success); |
| 744 } | 799 } |
| 745 | 800 |
| 746 void WebContentsCaptureMachine::RenewFrameSubscription(bool had_target) { | 801 void WebContentsCaptureMachine::RenewFrameSubscription(bool had_target) { |
| 747 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 802 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 748 | 803 |
| 749 RenderWidgetHost* const rwh = | 804 RenderWidgetHost* const rwh = |
| 750 had_target ? tracker_->GetTargetRenderWidgetHost() : nullptr; | 805 had_target ? tracker_->GetTargetRenderWidgetHost() : nullptr; |
| 751 | 806 |
| 752 // Always destroy the old subscription before creating a new one. | 807 // Always destroy the old subscription before creating a new one. |
| 753 const bool had_subscription = !!subscription_; | 808 const bool had_subscription = !!subscription_; |
| (...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 836 scoped_ptr<Client> client) { | 891 scoped_ptr<Client> client) { |
| 837 DVLOG(1) << "Allocating " << params.requested_format.frame_size.ToString(); | 892 DVLOG(1) << "Allocating " << params.requested_format.frame_size.ToString(); |
| 838 core_->AllocateAndStart(params, client.Pass()); | 893 core_->AllocateAndStart(params, client.Pass()); |
| 839 } | 894 } |
| 840 | 895 |
| 841 void WebContentsVideoCaptureDevice::StopAndDeAllocate() { | 896 void WebContentsVideoCaptureDevice::StopAndDeAllocate() { |
| 842 core_->StopAndDeAllocate(); | 897 core_->StopAndDeAllocate(); |
| 843 } | 898 } |
| 844 | 899 |
| 845 } // namespace content | 900 } // namespace content |
| OLD | NEW |