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/renderer_host/media/web_contents_video_capture_device.
h" | 51 #include "content/browser/renderer_host/media/web_contents_video_capture_device.
h" |
52 | 52 |
53 #include <algorithm> | |
54 #include <list> | |
55 #include <string> | |
56 | |
57 #include "base/basictypes.h" | 53 #include "base/basictypes.h" |
58 #include "base/bind.h" | 54 #include "base/bind.h" |
59 #include "base/callback_forward.h" | |
60 #include "base/callback_helpers.h" | 55 #include "base/callback_helpers.h" |
61 #include "base/debug/trace_event.h" | |
62 #include "base/logging.h" | 56 #include "base/logging.h" |
63 #include "base/memory/scoped_ptr.h" | 57 #include "base/memory/scoped_ptr.h" |
64 #include "base/memory/weak_ptr.h" | |
65 #include "base/message_loop/message_loop_proxy.h" | 58 #include "base/message_loop/message_loop_proxy.h" |
66 #include "base/metrics/histogram.h" | 59 #include "base/metrics/histogram.h" |
67 #include "base/sequenced_task_runner.h" | 60 #include "base/sequenced_task_runner.h" |
68 #include "base/strings/stringprintf.h" | |
69 #include "base/synchronization/lock.h" | |
70 #include "base/threading/thread.h" | 61 #include "base/threading/thread.h" |
71 #include "base/threading/thread_checker.h" | 62 #include "base/threading/thread_checker.h" |
72 #include "base/time/time.h" | 63 #include "base/time/time.h" |
| 64 #include "content/browser/renderer_host/media/video_capture_device_impl.h" |
73 #include "content/browser/renderer_host/media/video_capture_oracle.h" | 65 #include "content/browser/renderer_host/media/video_capture_oracle.h" |
74 #include "content/browser/renderer_host/media/web_contents_capture_util.h" | 66 #include "content/browser/renderer_host/media/web_contents_capture_util.h" |
75 #include "content/browser/renderer_host/render_widget_host_impl.h" | 67 #include "content/browser/renderer_host/render_widget_host_impl.h" |
76 #include "content/browser/web_contents/web_contents_impl.h" | 68 #include "content/browser/web_contents/web_contents_impl.h" |
77 #include "content/port/browser/render_widget_host_view_frame_subscriber.h" | 69 #include "content/port/browser/render_widget_host_view_frame_subscriber.h" |
78 #include "content/port/browser/render_widget_host_view_port.h" | 70 #include "content/port/browser/render_widget_host_view_port.h" |
79 #include "content/public/browser/browser_thread.h" | 71 #include "content/public/browser/browser_thread.h" |
80 #include "content/public/browser/notification_source.h" | 72 #include "content/public/browser/notification_source.h" |
81 #include "content/public/browser/notification_types.h" | 73 #include "content/public/browser/notification_types.h" |
82 #include "content/public/browser/render_process_host.h" | |
83 #include "content/public/browser/render_view_host.h" | 74 #include "content/public/browser/render_view_host.h" |
84 #include "content/public/browser/render_widget_host_view.h" | 75 #include "content/public/browser/render_widget_host_view.h" |
85 #include "content/public/browser/web_contents.h" | |
86 #include "content/public/browser/web_contents_observer.h" | 76 #include "content/public/browser/web_contents_observer.h" |
87 #include "media/base/bind_to_loop.h" | |
88 #include "media/base/video_frame.h" | |
89 #include "media/base/video_util.h" | 77 #include "media/base/video_util.h" |
90 #include "media/base/yuv_convert.h" | |
91 #include "media/video/capture/video_capture_types.h" | 78 #include "media/video/capture/video_capture_types.h" |
92 #include "skia/ext/image_operations.h" | 79 #include "skia/ext/image_operations.h" |
93 #include "third_party/skia/include/core/SkBitmap.h" | 80 #include "third_party/skia/include/core/SkBitmap.h" |
94 #include "third_party/skia/include/core/SkColor.h" | 81 #include "third_party/skia/include/core/SkColor.h" |
95 #include "ui/gfx/rect.h" | |
96 #include "ui/gfx/skia_util.h" | |
97 | 82 |
98 namespace content { | 83 namespace content { |
99 | 84 |
100 namespace { | 85 namespace { |
101 | 86 |
102 const int kMinFrameWidth = 2; | |
103 const int kMinFrameHeight = 2; | |
104 | |
105 // TODO(nick): Remove this once frame subscription is supported on Aura and | |
106 // Linux. | |
107 #if (defined(OS_WIN) || defined(OS_MACOSX)) || defined(USE_AURA) | |
108 const bool kAcceleratedSubscriberIsSupported = true; | |
109 #else | |
110 const bool kAcceleratedSubscriberIsSupported = false; | |
111 #endif | |
112 | |
113 // Returns the nearest even integer closer to zero. | |
114 template<typename IntType> | |
115 IntType MakeEven(IntType x) { | |
116 return x & static_cast<IntType>(-2); | |
117 } | |
118 | |
119 // Compute a letterbox region, aligned to even coordinates. | 87 // Compute a letterbox region, aligned to even coordinates. |
120 gfx::Rect ComputeYV12LetterboxRegion(const gfx::Size& frame_size, | 88 gfx::Rect ComputeYV12LetterboxRegion(const gfx::Size& frame_size, |
121 const gfx::Size& content_size) { | 89 const gfx::Size& content_size) { |
122 | 90 |
123 gfx::Rect result = media::ComputeLetterboxRegion(gfx::Rect(frame_size), | 91 gfx::Rect result = media::ComputeLetterboxRegion(gfx::Rect(frame_size), |
124 content_size); | 92 content_size); |
125 | 93 |
126 result.set_x(MakeEven(result.x())); | 94 result.set_x(MakeEven(result.x())); |
127 result.set_y(MakeEven(result.y())); | 95 result.set_y(MakeEven(result.y())); |
128 result.set_width(std::max(kMinFrameWidth, MakeEven(result.width()))); | 96 result.set_width(std::max(kMinFrameWidth, MakeEven(result.width()))); |
129 result.set_height(std::max(kMinFrameHeight, MakeEven(result.height()))); | 97 result.set_height(std::max(kMinFrameHeight, MakeEven(result.height()))); |
130 | 98 |
131 return result; | 99 return result; |
132 } | 100 } |
133 | 101 |
134 // Thread-safe, refcounted proxy to the VideoCaptureOracle. This proxy wraps | 102 // Wrapper function to invoke ThreadSafeCaptureOracle::CaptureFrameCallback, is |
135 // the VideoCaptureOracle, which decides which frames to capture, and a | 103 // compatible with RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback. |
136 // VideoCaptureDevice::Client, which allocates and receives the captured | 104 void InvokeCaptureFrameCallback( |
137 // frames, in a lock to synchronize state between the two. | 105 const ThreadSafeCaptureOracle::CaptureFrameCallback& capture_frame_cb, |
138 class ThreadSafeCaptureOracle | 106 base::Time timestamp, |
139 : public base::RefCountedThreadSafe<ThreadSafeCaptureOracle> { | 107 bool frame_captured) { |
140 public: | 108 capture_frame_cb.Run(timestamp, frame_captured); |
141 ThreadSafeCaptureOracle(scoped_ptr<media::VideoCaptureDevice::Client> client, | 109 } |
142 scoped_ptr<VideoCaptureOracle> oracle, | |
143 const gfx::Size& capture_size, | |
144 int frame_rate); | |
145 | |
146 bool ObserveEventAndDecideCapture( | |
147 VideoCaptureOracle::Event event, | |
148 base::Time event_time, | |
149 scoped_refptr<media::VideoFrame>* storage, | |
150 RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback* callback); | |
151 | |
152 base::TimeDelta capture_period() const { | |
153 return oracle_->capture_period(); | |
154 } | |
155 | |
156 // Stop new captures from happening (but doesn't forget the client). | |
157 void Stop(); | |
158 | |
159 // Signal an error to the client. | |
160 void ReportError(); | |
161 | |
162 private: | |
163 friend class base::RefCountedThreadSafe<ThreadSafeCaptureOracle>; | |
164 virtual ~ThreadSafeCaptureOracle() {} | |
165 | |
166 // Callback invoked on completion of all captures. | |
167 void DidCaptureFrame( | |
168 scoped_refptr<media::VideoCaptureDevice::Client::Buffer> buffer, | |
169 int frame_number, | |
170 base::Time timestamp, | |
171 bool success); | |
172 // Protects everything below it. | |
173 base::Lock lock_; | |
174 | |
175 // Recipient of our capture activity. | |
176 scoped_ptr<media::VideoCaptureDevice::Client> client_; | |
177 | |
178 // Makes the decision to capture a frame. | |
179 const scoped_ptr<VideoCaptureOracle> oracle_; | |
180 | |
181 // The current capturing resolution and frame rate. | |
182 const gfx::Size capture_size_; | |
183 const int frame_rate_; | |
184 }; | |
185 | 110 |
186 // FrameSubscriber is a proxy to the ThreadSafeCaptureOracle that's compatible | 111 // FrameSubscriber is a proxy to the ThreadSafeCaptureOracle that's compatible |
187 // with RenderWidgetHostViewFrameSubscriber. We create one per event type. | 112 // with RenderWidgetHostViewFrameSubscriber. We create one per event type. |
188 class FrameSubscriber : public RenderWidgetHostViewFrameSubscriber { | 113 class FrameSubscriber : public RenderWidgetHostViewFrameSubscriber { |
189 public: | 114 public: |
190 FrameSubscriber(VideoCaptureOracle::Event event_type, | 115 FrameSubscriber(VideoCaptureOracle::Event event_type, |
191 const scoped_refptr<ThreadSafeCaptureOracle>& oracle) | 116 const scoped_refptr<ThreadSafeCaptureOracle>& oracle) |
192 : event_type_(event_type), | 117 : event_type_(event_type), |
193 oracle_proxy_(oracle) {} | 118 oracle_proxy_(oracle) {} |
194 | 119 |
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
268 const scoped_refptr<media::VideoFrame>& output, | 193 const scoped_refptr<media::VideoFrame>& output, |
269 const base::Callback<void(bool)>& done_cb); | 194 const base::Callback<void(bool)>& done_cb); |
270 | 195 |
271 // Keeps track of the RenderView to be sourced, and executes copying of the | 196 // Keeps track of the RenderView to be sourced, and executes copying of the |
272 // backing store on the UI BrowserThread. | 197 // backing store on the UI BrowserThread. |
273 // | 198 // |
274 // TODO(nick): It would be nice to merge this with WebContentsTracker, but its | 199 // TODO(nick): It would be nice to merge this with WebContentsTracker, but its |
275 // implementation is currently asynchronous -- in our case, the "rvh changed" | 200 // implementation is currently asynchronous -- in our case, the "rvh changed" |
276 // notification would get posted back to the UI thread and processed later, and | 201 // notification would get posted back to the UI thread and processed later, and |
277 // this seems disadvantageous. | 202 // this seems disadvantageous. |
278 class CaptureMachine : public WebContentsObserver, | 203 class WebContentsCaptureMachine |
279 public base::SupportsWeakPtr<CaptureMachine> { | 204 : public VideoCaptureMachine, |
| 205 public WebContentsObserver, |
| 206 public base::SupportsWeakPtr<WebContentsCaptureMachine> { |
280 public: | 207 public: |
281 virtual ~CaptureMachine(); | 208 WebContentsCaptureMachine(int render_process_id, int render_view_id); |
| 209 virtual ~WebContentsCaptureMachine(); |
282 | 210 |
283 // Creates a CaptureMachine. Must be run on the UI BrowserThread. Returns | 211 // VideoCaptureMachine overrides. |
284 // NULL if the indicated render view cannot be found. | 212 virtual bool Start( |
285 static scoped_ptr<CaptureMachine> Create( | 213 const scoped_refptr<ThreadSafeCaptureOracle>& oracle_proxy) OVERRIDE; |
286 int render_process_id, | 214 virtual void Stop() OVERRIDE; |
287 int render_view_id, | |
288 const scoped_refptr<base::SequencedTaskRunner>& render_task_runner, | |
289 const scoped_refptr<ThreadSafeCaptureOracle>& oracle_proxy); | |
290 | 215 |
291 // Starts a copy from the backing store or the composited surface. Must be run | 216 // Starts a copy from the backing store or the composited surface. Must be run |
292 // on the UI BrowserThread. |deliver_frame_cb| will be run when the operation | 217 // on the UI BrowserThread. |deliver_frame_cb| will be run when the operation |
293 // completes. The copy will occur to |target|. | 218 // completes. The copy will occur to |target|. |
294 // | 219 // |
295 // This may be used as a ContentCaptureSubscription::CaptureCallback. | 220 // This may be used as a ContentCaptureSubscription::CaptureCallback. |
296 void Capture( | 221 void Capture( |
297 const base::Time& start_time, | 222 const base::Time& start_time, |
298 const scoped_refptr<media::VideoFrame>& target, | 223 const scoped_refptr<media::VideoFrame>& target, |
299 const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback& | 224 const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback& |
(...skipping 21 matching lines...) Expand all Loading... |
321 | 246 |
322 virtual void DidNavigateMainFrame( | 247 virtual void DidNavigateMainFrame( |
323 const LoadCommittedDetails& details, | 248 const LoadCommittedDetails& details, |
324 const FrameNavigateParams& params) OVERRIDE { | 249 const FrameNavigateParams& params) OVERRIDE { |
325 RenewFrameSubscription(); | 250 RenewFrameSubscription(); |
326 } | 251 } |
327 | 252 |
328 virtual void WebContentsDestroyed(WebContents* web_contents) OVERRIDE; | 253 virtual void WebContentsDestroyed(WebContents* web_contents) OVERRIDE; |
329 | 254 |
330 private: | 255 private: |
331 CaptureMachine( | |
332 const scoped_refptr<base::SequencedTaskRunner>& render_task_runner, | |
333 const scoped_refptr<ThreadSafeCaptureOracle>& oracle_proxy); | |
334 | |
335 // Starts observing the web contents, returning false if lookup fails. | 256 // Starts observing the web contents, returning false if lookup fails. |
336 bool StartObservingWebContents(int initial_render_process_id, | 257 bool StartObservingWebContents(); |
337 int initial_render_view_id); | |
338 | 258 |
339 // Helper function to determine the view that we are currently tracking. | 259 // Helper function to determine the view that we are currently tracking. |
340 RenderWidgetHost* GetTarget(); | 260 RenderWidgetHost* GetTarget(); |
341 | 261 |
342 // Response callback for RenderWidgetHost::CopyFromBackingStore(). | 262 // Response callback for RenderWidgetHost::CopyFromBackingStore(). |
343 void DidCopyFromBackingStore( | 263 void DidCopyFromBackingStore( |
344 const base::Time& start_time, | 264 const base::Time& start_time, |
345 const scoped_refptr<media::VideoFrame>& target, | 265 const scoped_refptr<media::VideoFrame>& target, |
346 const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback& | 266 const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback& |
347 deliver_frame_cb, | 267 deliver_frame_cb, |
348 bool success, | 268 bool success, |
349 const SkBitmap& bitmap); | 269 const SkBitmap& bitmap); |
350 | 270 |
351 // Response callback for RWHVP::CopyFromCompositingSurfaceToVideoFrame(). | 271 // Response callback for RWHVP::CopyFromCompositingSurfaceToVideoFrame(). |
352 void DidCopyFromCompositingSurfaceToVideoFrame( | 272 void DidCopyFromCompositingSurfaceToVideoFrame( |
353 const base::Time& start_time, | 273 const base::Time& start_time, |
354 const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback& | 274 const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback& |
355 deliver_frame_cb, | 275 deliver_frame_cb, |
356 bool success); | 276 bool success); |
357 | 277 |
358 // Remove the old subscription, and start a new one. This should be called | 278 // Remove the old subscription, and start a new one. This should be called |
359 // after any change to the WebContents that affects the RenderWidgetHost or | 279 // after any change to the WebContents that affects the RenderWidgetHost or |
360 // attached views. | 280 // attached views. |
361 void RenewFrameSubscription(); | 281 void RenewFrameSubscription(); |
362 | 282 |
363 // The task runner of the thread on which SkBitmap->VideoFrame conversion will | 283 // Parameters saved in constructor. |
| 284 const int initial_render_process_id_; |
| 285 const int initial_render_view_id_; |
| 286 |
| 287 // A dedicated worker thread on which SkBitmap->VideoFrame conversion will |
364 // occur. Only used when this activity cannot be done on the GPU. | 288 // occur. Only used when this activity cannot be done on the GPU. |
365 const scoped_refptr<base::SequencedTaskRunner> render_task_runner_; | 289 base::Thread render_thread_; |
366 | 290 |
367 // Makes all the decisions about which frames to copy, and how. | 291 // Makes all the decisions about which frames to copy, and how. |
368 const scoped_refptr<ThreadSafeCaptureOracle> oracle_proxy_; | 292 scoped_refptr<ThreadSafeCaptureOracle> oracle_proxy_; |
369 | 293 |
370 // Routing ID of any active fullscreen render widget or MSG_ROUTING_NONE | 294 // Routing ID of any active fullscreen render widget or MSG_ROUTING_NONE |
371 // otherwise. | 295 // otherwise. |
372 int fullscreen_widget_id_; | 296 int fullscreen_widget_id_; |
373 | 297 |
374 // Last known RenderView size. | 298 // Last known RenderView size. |
375 gfx::Size last_view_size_; | 299 gfx::Size last_view_size_; |
376 | 300 |
377 // Responsible for forwarding events from the active RenderWidgetHost to the | 301 // Responsible for forwarding events from the active RenderWidgetHost to the |
378 // oracle, and initiating captures accordingly. | 302 // oracle, and initiating captures accordingly. |
379 scoped_ptr<ContentCaptureSubscription> subscription_; | 303 scoped_ptr<ContentCaptureSubscription> subscription_; |
380 | 304 |
381 DISALLOW_COPY_AND_ASSIGN(CaptureMachine); | 305 DISALLOW_COPY_AND_ASSIGN(WebContentsCaptureMachine); |
382 }; | 306 }; |
383 | 307 |
384 // Responsible for logging the effective frame rate. | 308 // Responsible for logging the effective frame rate. |
385 // TODO(nick): Make this compatible with the push model and hook it back up. | 309 // TODO(nick): Make this compatible with the push model and hook it back up. |
386 class VideoFrameDeliveryLog { | 310 class VideoFrameDeliveryLog { |
387 public: | 311 public: |
388 VideoFrameDeliveryLog(); | 312 VideoFrameDeliveryLog(); |
389 | 313 |
390 // Treat |frame_number| as having been delivered, and update the | 314 // Treat |frame_number| as having been delivered, and update the |
391 // frame rate statistics accordingly. | 315 // frame rate statistics accordingly. |
392 void ChronicleFrameDelivery(int frame_number); | 316 void ChronicleFrameDelivery(int frame_number); |
393 | 317 |
394 private: | 318 private: |
395 // The following keep track of and log the effective frame rate whenever | 319 // The following keep track of and log the effective frame rate whenever |
396 // verbose logging is turned on. | 320 // verbose logging is turned on. |
397 base::Time last_frame_rate_log_time_; | 321 base::Time last_frame_rate_log_time_; |
398 int count_frames_rendered_; | 322 int count_frames_rendered_; |
399 int last_frame_number_; | 323 int last_frame_number_; |
400 | 324 |
401 DISALLOW_COPY_AND_ASSIGN(VideoFrameDeliveryLog); | 325 DISALLOW_COPY_AND_ASSIGN(VideoFrameDeliveryLog); |
402 }; | 326 }; |
403 | 327 |
404 ThreadSafeCaptureOracle::ThreadSafeCaptureOracle( | |
405 scoped_ptr<media::VideoCaptureDevice::Client> client, | |
406 scoped_ptr<VideoCaptureOracle> oracle, | |
407 const gfx::Size& capture_size, | |
408 int frame_rate) | |
409 : client_(client.Pass()), | |
410 oracle_(oracle.Pass()), | |
411 capture_size_(capture_size), | |
412 frame_rate_(frame_rate) {} | |
413 | |
414 bool ThreadSafeCaptureOracle::ObserveEventAndDecideCapture( | |
415 VideoCaptureOracle::Event event, | |
416 base::Time event_time, | |
417 scoped_refptr<media::VideoFrame>* storage, | |
418 RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback* callback) { | |
419 base::AutoLock guard(lock_); | |
420 | |
421 if (!client_) | |
422 return false; // Capture is stopped. | |
423 | |
424 scoped_refptr<media::VideoCaptureDevice::Client::Buffer> output_buffer = | |
425 client_->ReserveOutputBuffer(media::VideoFrame::I420, capture_size_); | |
426 const bool should_capture = | |
427 oracle_->ObserveEventAndDecideCapture(event, event_time); | |
428 const bool content_is_dirty = | |
429 (event == VideoCaptureOracle::kCompositorUpdate || | |
430 event == VideoCaptureOracle::kSoftwarePaint); | |
431 const char* event_name = | |
432 (event == VideoCaptureOracle::kTimerPoll ? "poll" : | |
433 (event == VideoCaptureOracle::kCompositorUpdate ? "gpu" : | |
434 "paint")); | |
435 | |
436 // Consider the various reasons not to initiate a capture. | |
437 if (should_capture && !output_buffer) { | |
438 TRACE_EVENT_INSTANT1("mirroring", | |
439 "EncodeLimited", | |
440 TRACE_EVENT_SCOPE_THREAD, | |
441 "trigger", | |
442 event_name); | |
443 return false; | |
444 } else if (!should_capture && output_buffer) { | |
445 if (content_is_dirty) { | |
446 // This is a normal and acceptable way to drop a frame. We've hit our | |
447 // capture rate limit: for example, the content is animating at 60fps but | |
448 // we're capturing at 30fps. | |
449 TRACE_EVENT_INSTANT1("mirroring", "FpsRateLimited", | |
450 TRACE_EVENT_SCOPE_THREAD, | |
451 "trigger", event_name); | |
452 } | |
453 return false; | |
454 } else if (!should_capture && !output_buffer) { | |
455 // We decided not to capture, but we wouldn't have been able to if we wanted | |
456 // to because no output buffer was available. | |
457 TRACE_EVENT_INSTANT1("mirroring", "NearlyEncodeLimited", | |
458 TRACE_EVENT_SCOPE_THREAD, | |
459 "trigger", event_name); | |
460 return false; | |
461 } | |
462 int frame_number = oracle_->RecordCapture(); | |
463 TRACE_EVENT_ASYNC_BEGIN2("mirroring", "Capture", output_buffer.get(), | |
464 "frame_number", frame_number, | |
465 "trigger", event_name); | |
466 | |
467 *callback = base::Bind(&ThreadSafeCaptureOracle::DidCaptureFrame, | |
468 this, | |
469 output_buffer, | |
470 frame_number); | |
471 *storage = media::VideoFrame::WrapExternalPackedMemory( | |
472 media::VideoFrame::I420, | |
473 capture_size_, | |
474 gfx::Rect(capture_size_), | |
475 capture_size_, | |
476 static_cast<uint8*>(output_buffer->data()), | |
477 output_buffer->size(), | |
478 base::SharedMemory::NULLHandle(), | |
479 base::TimeDelta(), | |
480 base::Closure()); | |
481 return true; | |
482 } | |
483 | |
484 void ThreadSafeCaptureOracle::Stop() { | |
485 base::AutoLock guard(lock_); | |
486 client_.reset(); | |
487 } | |
488 | |
489 void ThreadSafeCaptureOracle::ReportError() { | |
490 base::AutoLock guard(lock_); | |
491 if (client_) | |
492 client_->OnError(); | |
493 } | |
494 | |
495 void ThreadSafeCaptureOracle::DidCaptureFrame( | |
496 scoped_refptr<media::VideoCaptureDevice::Client::Buffer> buffer, | |
497 int frame_number, | |
498 base::Time timestamp, | |
499 bool success) { | |
500 base::AutoLock guard(lock_); | |
501 TRACE_EVENT_ASYNC_END2("mirroring", "Capture", buffer.get(), | |
502 "success", success, | |
503 "timestamp", timestamp.ToInternalValue()); | |
504 | |
505 if (!client_) | |
506 return; // Capture is stopped. | |
507 | |
508 if (success) { | |
509 if (oracle_->CompleteCapture(frame_number, timestamp)) { | |
510 client_->OnIncomingCapturedBuffer(buffer, | |
511 media::VideoFrame::I420, | |
512 capture_size_, | |
513 timestamp, | |
514 frame_rate_); | |
515 } | |
516 } | |
517 } | |
518 | |
519 bool FrameSubscriber::ShouldCaptureFrame( | 328 bool FrameSubscriber::ShouldCaptureFrame( |
520 base::Time present_time, | 329 base::Time present_time, |
521 scoped_refptr<media::VideoFrame>* storage, | 330 scoped_refptr<media::VideoFrame>* storage, |
522 DeliverFrameCallback* deliver_frame_cb) { | 331 DeliverFrameCallback* deliver_frame_cb) { |
523 TRACE_EVENT1("mirroring", "FrameSubscriber::ShouldCaptureFrame", | 332 TRACE_EVENT1("mirroring", "FrameSubscriber::ShouldCaptureFrame", |
524 "instance", this); | 333 "instance", this); |
525 | 334 |
526 return oracle_proxy_->ObserveEventAndDecideCapture(event_type_, present_time, | 335 ThreadSafeCaptureOracle::CaptureFrameCallback capture_frame_cb; |
527 storage, deliver_frame_cb); | 336 bool oracle_decision = oracle_proxy_->ObserveEventAndDecideCapture( |
| 337 event_type_, present_time, storage, &capture_frame_cb); |
| 338 |
| 339 *deliver_frame_cb = base::Bind(&InvokeCaptureFrameCallback, capture_frame_cb); |
| 340 return oracle_decision; |
528 } | 341 } |
529 | 342 |
530 ContentCaptureSubscription::ContentCaptureSubscription( | 343 ContentCaptureSubscription::ContentCaptureSubscription( |
531 const RenderWidgetHost& source, | 344 const RenderWidgetHost& source, |
532 const scoped_refptr<ThreadSafeCaptureOracle>& oracle_proxy, | 345 const scoped_refptr<ThreadSafeCaptureOracle>& oracle_proxy, |
533 const CaptureCallback& capture_callback) | 346 const CaptureCallback& capture_callback) |
534 : render_process_id_(source.GetProcess()->GetID()), | 347 : render_process_id_(source.GetProcess()->GetID()), |
535 render_view_id_(source.GetRoutingID()), | 348 render_view_id_(source.GetRoutingID()), |
536 paint_subscriber_(VideoCaptureOracle::kSoftwarePaint, oracle_proxy), | 349 paint_subscriber_(VideoCaptureOracle::kSoftwarePaint, oracle_proxy), |
537 timer_subscriber_(VideoCaptureOracle::kTimerPoll, oracle_proxy), | 350 timer_subscriber_(VideoCaptureOracle::kTimerPoll, oracle_proxy), |
538 capture_callback_(capture_callback), | 351 capture_callback_(capture_callback), |
539 timer_(true, true) { | 352 timer_(true, true) { |
540 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 353 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
541 | 354 |
542 RenderWidgetHostViewPort* view = | 355 RenderWidgetHostViewPort* view = |
543 RenderWidgetHostViewPort::FromRWHV(source.GetView()); | 356 RenderWidgetHostViewPort::FromRWHV(source.GetView()); |
544 | 357 |
545 // Subscribe to accelerated presents. These will be serviced directly by the | 358 // Subscribe to accelerated presents. These will be serviced directly by the |
546 // oracle. | 359 // oracle. |
547 if (view && kAcceleratedSubscriberIsSupported) { | 360 if (view && kAcceleratedSubscriberIsSupported) { |
548 scoped_ptr<RenderWidgetHostViewFrameSubscriber> subscriber( | 361 scoped_ptr<RenderWidgetHostViewFrameSubscriber> subscriber( |
549 new FrameSubscriber(VideoCaptureOracle::kCompositorUpdate, | 362 new FrameSubscriber(VideoCaptureOracle::kCompositorUpdate, |
550 oracle_proxy)); | 363 oracle_proxy)); |
551 view->BeginFrameSubscription(subscriber.Pass()); | 364 view->BeginFrameSubscription(subscriber.Pass()); |
552 } | 365 } |
553 | 366 |
554 // Subscribe to software paint events. This instance will service these by | 367 // Subscribe to software paint events. This instance will service these by |
555 // reflecting them back to the CaptureMachine via |capture_callback|. | 368 // reflecting them back to the WebContentsCaptureMachine via |
| 369 // |capture_callback|. |
556 registrar_.Add( | 370 registrar_.Add( |
557 this, content::NOTIFICATION_RENDER_WIDGET_HOST_DID_UPDATE_BACKING_STORE, | 371 this, content::NOTIFICATION_RENDER_WIDGET_HOST_DID_UPDATE_BACKING_STORE, |
558 Source<RenderWidgetHost>(&source)); | 372 Source<RenderWidgetHost>(&source)); |
559 | 373 |
560 // Subscribe to timer events. This instance will service these as well. | 374 // Subscribe to timer events. This instance will service these as well. |
561 timer_.Start(FROM_HERE, oracle_proxy->capture_period(), | 375 timer_.Start(FROM_HERE, oracle_proxy->capture_period(), |
562 base::Bind(&ContentCaptureSubscription::OnTimer, | 376 base::Bind(&ContentCaptureSubscription::OnTimer, |
563 base::Unretained(this))); | 377 base::Unretained(this))); |
564 } | 378 } |
565 | 379 |
(...skipping 161 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
727 static_cast<int>(measured_fps)); | 541 static_cast<int>(measured_fps)); |
728 VLOG(1) << "Current measured frame rate for " | 542 VLOG(1) << "Current measured frame rate for " |
729 << "WebContentsVideoCaptureDevice is " << measured_fps << " FPS."; | 543 << "WebContentsVideoCaptureDevice is " << measured_fps << " FPS."; |
730 last_frame_rate_log_time_ = now; | 544 last_frame_rate_log_time_ = now; |
731 count_frames_rendered_ = 0; | 545 count_frames_rendered_ = 0; |
732 last_frame_number_ = frame_number; | 546 last_frame_number_ = frame_number; |
733 } | 547 } |
734 } | 548 } |
735 } | 549 } |
736 | 550 |
737 // static | 551 WebContentsCaptureMachine::WebContentsCaptureMachine(int render_process_id, |
738 scoped_ptr<CaptureMachine> CaptureMachine::Create( | 552 int render_view_id) |
739 int render_process_id, | 553 : initial_render_process_id_(render_process_id), |
740 int render_view_id, | 554 initial_render_view_id_(render_view_id), |
741 const scoped_refptr<base::SequencedTaskRunner>& render_task_runner, | 555 render_thread_("WebContentsVideo_RenderThread"), |
| 556 fullscreen_widget_id_(MSG_ROUTING_NONE) {} |
| 557 |
| 558 WebContentsCaptureMachine::~WebContentsCaptureMachine() {} |
| 559 |
| 560 bool WebContentsCaptureMachine::Start( |
742 const scoped_refptr<ThreadSafeCaptureOracle>& oracle_proxy) { | 561 const scoped_refptr<ThreadSafeCaptureOracle>& oracle_proxy) { |
743 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 562 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
744 DCHECK(render_task_runner.get()); | 563 DCHECK(!started_); |
| 564 |
745 DCHECK(oracle_proxy.get()); | 565 DCHECK(oracle_proxy.get()); |
746 scoped_ptr<CaptureMachine> machine( | 566 oracle_proxy_ = oracle_proxy; |
747 new CaptureMachine(render_task_runner, oracle_proxy)); | |
748 | 567 |
749 if (!machine->StartObservingWebContents(render_process_id, render_view_id)) | 568 if (!render_thread_.Start()) { |
750 machine.reset(); | 569 DVLOG(1) << "Failed to spawn render thread."; |
| 570 return false; |
| 571 } |
751 | 572 |
752 return machine.Pass(); | 573 if (!StartObservingWebContents()) |
| 574 return false; |
| 575 |
| 576 started_ = true; |
| 577 return true; |
753 } | 578 } |
754 | 579 |
755 CaptureMachine::CaptureMachine( | 580 void WebContentsCaptureMachine::Stop() { |
756 const scoped_refptr<base::SequencedTaskRunner>& render_task_runner, | 581 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
757 const scoped_refptr<ThreadSafeCaptureOracle>& oracle_proxy) | |
758 : render_task_runner_(render_task_runner), | |
759 oracle_proxy_(oracle_proxy), | |
760 fullscreen_widget_id_(MSG_ROUTING_NONE) {} | |
761 | |
762 CaptureMachine::~CaptureMachine() { | |
763 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) || | |
764 !BrowserThread::IsMessageLoopValid(BrowserThread::UI)); | |
765 | |
766 // Stop observing the web contents. | |
767 subscription_.reset(); | 582 subscription_.reset(); |
768 if (web_contents()) { | 583 if (web_contents()) { |
769 web_contents()->DecrementCapturerCount(); | 584 web_contents()->DecrementCapturerCount(); |
770 Observe(NULL); | 585 Observe(NULL); |
771 } | 586 } |
| 587 render_thread_.Stop(); |
| 588 started_ = false; |
772 } | 589 } |
773 | 590 |
774 void CaptureMachine::Capture( | 591 void WebContentsCaptureMachine::Capture( |
775 const base::Time& start_time, | 592 const base::Time& start_time, |
776 const scoped_refptr<media::VideoFrame>& target, | 593 const scoped_refptr<media::VideoFrame>& target, |
777 const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback& | 594 const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback& |
778 deliver_frame_cb) { | 595 deliver_frame_cb) { |
779 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 596 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
780 | 597 |
781 RenderWidgetHost* rwh = GetTarget(); | 598 RenderWidgetHost* rwh = GetTarget(); |
782 RenderWidgetHostViewPort* view = | 599 RenderWidgetHostViewPort* view = |
783 rwh ? RenderWidgetHostViewPort::FromRWHV(rwh->GetView()) : NULL; | 600 rwh ? RenderWidgetHostViewPort::FromRWHV(rwh->GetView()) : NULL; |
784 if (!view || !rwh) { | 601 if (!view || !rwh) { |
(...skipping 14 matching lines...) Expand all Loading... |
799 UMA_HISTOGRAM_COUNTS_10000( | 616 UMA_HISTOGRAM_COUNTS_10000( |
800 "TabCapture.ViewChangeKiloPixels", | 617 "TabCapture.ViewChangeKiloPixels", |
801 view_size.width() * view_size.height() / 1024); | 618 view_size.width() * view_size.height() / 1024); |
802 } | 619 } |
803 | 620 |
804 if (!view->IsSurfaceAvailableForCopy()) { | 621 if (!view->IsSurfaceAvailableForCopy()) { |
805 // Fallback to the more expensive renderer-side copy if the surface and | 622 // Fallback to the more expensive renderer-side copy if the surface and |
806 // backing store are not accessible. | 623 // backing store are not accessible. |
807 rwh->GetSnapshotFromRenderer( | 624 rwh->GetSnapshotFromRenderer( |
808 gfx::Rect(), | 625 gfx::Rect(), |
809 base::Bind(&CaptureMachine::DidCopyFromBackingStore, this->AsWeakPtr(), | 626 base::Bind(&WebContentsCaptureMachine::DidCopyFromBackingStore, |
810 start_time, target, deliver_frame_cb)); | 627 this->AsWeakPtr(), start_time, target, deliver_frame_cb)); |
811 } else if (view->CanCopyToVideoFrame()) { | 628 } else if (view->CanCopyToVideoFrame()) { |
812 view->CopyFromCompositingSurfaceToVideoFrame( | 629 view->CopyFromCompositingSurfaceToVideoFrame( |
813 gfx::Rect(view_size), | 630 gfx::Rect(view_size), |
814 target, | 631 target, |
815 base::Bind(&CaptureMachine::DidCopyFromCompositingSurfaceToVideoFrame, | 632 base::Bind(&WebContentsCaptureMachine:: |
| 633 DidCopyFromCompositingSurfaceToVideoFrame, |
816 this->AsWeakPtr(), start_time, deliver_frame_cb)); | 634 this->AsWeakPtr(), start_time, deliver_frame_cb)); |
817 } else { | 635 } else { |
818 rwh->CopyFromBackingStore( | 636 rwh->CopyFromBackingStore( |
819 gfx::Rect(), | 637 gfx::Rect(), |
820 fitted_size, // Size here is a request not always honored. | 638 fitted_size, // Size here is a request not always honored. |
821 base::Bind(&CaptureMachine::DidCopyFromBackingStore, this->AsWeakPtr(), | 639 base::Bind(&WebContentsCaptureMachine::DidCopyFromBackingStore, |
822 start_time, target, deliver_frame_cb)); | 640 this->AsWeakPtr(), start_time, target, deliver_frame_cb)); |
823 } | 641 } |
824 } | 642 } |
825 | 643 |
826 bool CaptureMachine::StartObservingWebContents(int initial_render_process_id, | 644 bool WebContentsCaptureMachine::StartObservingWebContents() { |
827 int initial_render_view_id) { | |
828 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
829 | |
830 // Look-up the RenderViewHost and, from that, the WebContents that wraps it. | 645 // Look-up the RenderViewHost and, from that, the WebContents that wraps it. |
831 // If successful, begin observing the WebContents instance. | 646 // If successful, begin observing the WebContents instance. |
832 // | 647 // |
833 // Why this can be unsuccessful: The request for mirroring originates in a | 648 // Why this can be unsuccessful: The request for mirroring originates in a |
834 // render process, and this request is based on the current RenderView | 649 // render process, and this request is based on the current RenderView |
835 // associated with a tab. However, by the time we get up-and-running here, | 650 // associated with a tab. However, by the time we get up-and-running here, |
836 // there have been multiple back-and-forth IPCs between processes, as well as | 651 // there have been multiple back-and-forth IPCs between processes, as well as |
837 // a bit of indirection across threads. It's easily possible that, in the | 652 // a bit of indirection across threads. It's easily possible that, in the |
838 // meantime, the original RenderView may have gone away. | 653 // meantime, the original RenderView may have gone away. |
839 RenderViewHost* const rvh = | 654 RenderViewHost* const rvh = |
840 RenderViewHost::FromID(initial_render_process_id, | 655 RenderViewHost::FromID(initial_render_process_id_, |
841 initial_render_view_id); | 656 initial_render_view_id_); |
842 DVLOG_IF(1, !rvh) << "RenderViewHost::FromID(" | 657 DVLOG_IF(1, !rvh) << "RenderViewHost::FromID(" |
843 << initial_render_process_id << ", " | 658 << initial_render_process_id_ << ", " |
844 << initial_render_view_id << ") returned NULL."; | 659 << initial_render_view_id_ << ") returned NULL."; |
845 Observe(rvh ? WebContents::FromRenderViewHost(rvh) : NULL); | 660 Observe(rvh ? WebContents::FromRenderViewHost(rvh) : NULL); |
846 | 661 |
847 WebContentsImpl* contents = static_cast<WebContentsImpl*>(web_contents()); | 662 WebContentsImpl* contents = static_cast<WebContentsImpl*>(web_contents()); |
848 if (contents) { | 663 if (contents) { |
849 contents->IncrementCapturerCount(); | 664 contents->IncrementCapturerCount(); |
850 fullscreen_widget_id_ = contents->GetFullscreenWidgetRoutingID(); | 665 fullscreen_widget_id_ = contents->GetFullscreenWidgetRoutingID(); |
851 RenewFrameSubscription(); | 666 RenewFrameSubscription(); |
852 return true; | 667 return true; |
853 } | 668 } |
854 | 669 |
855 DVLOG(1) << "WebContents::FromRenderViewHost(" << rvh << ") returned NULL."; | 670 DVLOG(1) << "WebContents::FromRenderViewHost(" << rvh << ") returned NULL."; |
856 return false; | 671 return false; |
857 } | 672 } |
858 | 673 |
859 void CaptureMachine::WebContentsDestroyed(WebContents* web_contents) { | 674 void WebContentsCaptureMachine::WebContentsDestroyed( |
| 675 WebContents* web_contents) { |
860 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 676 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
861 | 677 |
862 subscription_.reset(); | 678 subscription_.reset(); |
863 web_contents->DecrementCapturerCount(); | 679 web_contents->DecrementCapturerCount(); |
864 oracle_proxy_->ReportError(); | 680 oracle_proxy_->ReportError(); |
865 } | 681 } |
866 | 682 |
867 RenderWidgetHost* CaptureMachine::GetTarget() { | 683 RenderWidgetHost* WebContentsCaptureMachine::GetTarget() { |
868 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 684 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
869 if (!web_contents()) | 685 if (!web_contents()) |
870 return NULL; | 686 return NULL; |
871 | 687 |
872 RenderWidgetHost* rwh = NULL; | 688 RenderWidgetHost* rwh = NULL; |
873 if (fullscreen_widget_id_ != MSG_ROUTING_NONE) { | 689 if (fullscreen_widget_id_ != MSG_ROUTING_NONE) { |
874 RenderProcessHost* process = web_contents()->GetRenderProcessHost(); | 690 RenderProcessHost* process = web_contents()->GetRenderProcessHost(); |
875 if (process) | 691 if (process) |
876 rwh = RenderWidgetHost::FromID(process->GetID(), fullscreen_widget_id_); | 692 rwh = RenderWidgetHost::FromID(process->GetID(), fullscreen_widget_id_); |
877 } else { | 693 } else { |
878 rwh = web_contents()->GetRenderViewHost(); | 694 rwh = web_contents()->GetRenderViewHost(); |
879 } | 695 } |
880 | 696 |
881 return rwh; | 697 return rwh; |
882 } | 698 } |
883 | 699 |
884 void CaptureMachine::DidCopyFromBackingStore( | 700 void WebContentsCaptureMachine::DidCopyFromBackingStore( |
885 const base::Time& start_time, | 701 const base::Time& start_time, |
886 const scoped_refptr<media::VideoFrame>& target, | 702 const scoped_refptr<media::VideoFrame>& target, |
887 const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback& | 703 const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback& |
888 deliver_frame_cb, | 704 deliver_frame_cb, |
889 bool success, | 705 bool success, |
890 const SkBitmap& bitmap) { | 706 const SkBitmap& bitmap) { |
891 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 707 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
892 | 708 |
893 base::Time now = base::Time::Now(); | 709 base::Time now = base::Time::Now(); |
894 if (success) { | 710 if (success) { |
895 UMA_HISTOGRAM_TIMES("TabCapture.CopyTimeBitmap", now - start_time); | 711 UMA_HISTOGRAM_TIMES("TabCapture.CopyTimeBitmap", now - start_time); |
896 TRACE_EVENT_ASYNC_STEP_INTO0("mirroring", "Capture", target.get(), | 712 TRACE_EVENT_ASYNC_STEP_INTO0("mirroring", "Capture", target.get(), |
897 "Render"); | 713 "Render"); |
898 render_task_runner_->PostTask(FROM_HERE, base::Bind( | 714 render_thread_.message_loop_proxy()->PostTask(FROM_HERE, base::Bind( |
899 &RenderVideoFrame, bitmap, target, | 715 &RenderVideoFrame, bitmap, target, |
900 base::Bind(deliver_frame_cb, start_time))); | 716 base::Bind(deliver_frame_cb, start_time))); |
901 } else { | 717 } else { |
902 // Capture can fail due to transient issues, so just skip this frame. | 718 // Capture can fail due to transient issues, so just skip this frame. |
903 DVLOG(1) << "CopyFromBackingStore failed; skipping frame."; | 719 DVLOG(1) << "CopyFromBackingStore failed; skipping frame."; |
904 deliver_frame_cb.Run(start_time, false); | 720 deliver_frame_cb.Run(start_time, false); |
905 } | 721 } |
906 } | 722 } |
907 | 723 |
908 void CaptureMachine::DidCopyFromCompositingSurfaceToVideoFrame( | 724 void WebContentsCaptureMachine::DidCopyFromCompositingSurfaceToVideoFrame( |
909 const base::Time& start_time, | 725 const base::Time& start_time, |
910 const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback& | 726 const RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback& |
911 deliver_frame_cb, | 727 deliver_frame_cb, |
912 bool success) { | 728 bool success) { |
913 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 729 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
914 base::Time now = base::Time::Now(); | 730 base::Time now = base::Time::Now(); |
915 | 731 |
916 if (success) { | 732 if (success) { |
917 UMA_HISTOGRAM_TIMES("TabCapture.CopyTimeVideoFrame", now - start_time); | 733 UMA_HISTOGRAM_TIMES("TabCapture.CopyTimeVideoFrame", now - start_time); |
918 } else { | 734 } else { |
919 // Capture can fail due to transient issues, so just skip this frame. | 735 // Capture can fail due to transient issues, so just skip this frame. |
920 DVLOG(1) << "CopyFromCompositingSurface failed; skipping frame."; | 736 DVLOG(1) << "CopyFromCompositingSurface failed; skipping frame."; |
921 } | 737 } |
922 deliver_frame_cb.Run(start_time, success); | 738 deliver_frame_cb.Run(start_time, success); |
923 } | 739 } |
924 | 740 |
925 void CaptureMachine::RenewFrameSubscription() { | 741 void WebContentsCaptureMachine::RenewFrameSubscription() { |
926 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 742 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
927 | 743 |
928 // Always destroy the old subscription before creating a new one. | 744 // Always destroy the old subscription before creating a new one. |
929 subscription_.reset(); | 745 subscription_.reset(); |
930 | 746 |
931 RenderWidgetHost* rwh = GetTarget(); | 747 RenderWidgetHost* rwh = GetTarget(); |
932 if (!rwh || !rwh->GetView()) | 748 if (!rwh || !rwh->GetView()) |
933 return; | 749 return; |
934 | 750 |
935 subscription_.reset(new ContentCaptureSubscription(*rwh, oracle_proxy_, | 751 subscription_.reset(new ContentCaptureSubscription(*rwh, oracle_proxy_, |
936 base::Bind(&CaptureMachine::Capture, this->AsWeakPtr()))); | 752 base::Bind(&WebContentsCaptureMachine::Capture, this->AsWeakPtr()))); |
937 } | |
938 | |
939 void DeleteCaptureMachineOnUIThread( | |
940 scoped_ptr<CaptureMachine> capture_machine) { | |
941 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
942 capture_machine.reset(); | |
943 } | 753 } |
944 | 754 |
945 } // namespace | 755 } // namespace |
946 | 756 |
947 // The "meat" of the video capture implementation, which is a ref-counted class. | |
948 // Separating this from the "shell class" WebContentsVideoCaptureDevice allows | |
949 // safe destruction without needing to block any threads (e.g., the IO | |
950 // BrowserThread). | |
951 // | |
952 // WebContentsVideoCaptureDevice::Impl manages a simple state machine and the | |
953 // pipeline (see notes at top of this file). It times the start of successive | |
954 // captures and facilitates the processing of each through the stages of the | |
955 // pipeline. | |
956 class WebContentsVideoCaptureDevice::Impl : public base::SupportsWeakPtr<Impl> { | |
957 public: | |
958 | |
959 Impl(int render_process_id, int render_view_id); | |
960 virtual ~Impl(); | |
961 | |
962 // Asynchronous requests to change WebContentsVideoCaptureDevice::Impl state. | |
963 void AllocateAndStart(const media::VideoCaptureParams& params, | |
964 scoped_ptr<media::VideoCaptureDevice::Client> client); | |
965 void StopAndDeAllocate(); | |
966 | |
967 private: | |
968 | |
969 // Flag indicating current state. | |
970 enum State { | |
971 kIdle, | |
972 kCapturing, | |
973 kError | |
974 }; | |
975 | |
976 void TransitionStateTo(State next_state); | |
977 | |
978 // Stops capturing and notifies client_ of an error state. | |
979 void Error(); | |
980 | |
981 // Called in response to CaptureMachine::Create that runs on the UI thread. | |
982 // It will assign the capture machine to the Impl class if it still exists | |
983 // otherwise it will post a task to delete CaptureMachine on the UI thread. | |
984 static void AssignCaptureMachine( | |
985 base::WeakPtr<WebContentsVideoCaptureDevice::Impl> impl, | |
986 scoped_ptr<CaptureMachine> capture_machine); | |
987 | |
988 // Tracks that all activity occurs on the media stream manager's thread. | |
989 base::ThreadChecker thread_checker_; | |
990 | |
991 // These values identify the starting view that will be captured. After | |
992 // capture starts, the target view IDs will change as navigation occurs, and | |
993 // so these values are not relevant after the initial bootstrapping. | |
994 const int initial_render_process_id_; | |
995 const int initial_render_view_id_; | |
996 | |
997 // Current lifecycle state. | |
998 State state_; | |
999 | |
1000 // A dedicated worker thread for doing image operations. Started/joined here, | |
1001 // but used by the CaptureMachine. | |
1002 base::Thread render_thread_; | |
1003 | |
1004 // Tracks the CaptureMachine that's doing work on our behalf on the UI thread. | |
1005 // This value should never be dereferenced by this class, other than to | |
1006 // create and destroy it on the UI thread. | |
1007 scoped_ptr<CaptureMachine> capture_machine_; | |
1008 | |
1009 // Our thread-safe capture oracle which serves as the gateway to the video | |
1010 // capture pipeline. Besides the WCVCD itself, it is the only component of the | |
1011 // system with direct access to |client_|. | |
1012 scoped_refptr<ThreadSafeCaptureOracle> oracle_proxy_; | |
1013 | |
1014 DISALLOW_COPY_AND_ASSIGN(Impl); | |
1015 }; | |
1016 | |
1017 WebContentsVideoCaptureDevice::Impl::Impl(int render_process_id, | |
1018 int render_view_id) | |
1019 : initial_render_process_id_(render_process_id), | |
1020 initial_render_view_id_(render_view_id), | |
1021 state_(kIdle), | |
1022 render_thread_("WebContentsVideo_RenderThread") {} | |
1023 | |
1024 void WebContentsVideoCaptureDevice::Impl::AllocateAndStart( | |
1025 const media::VideoCaptureParams& params, | |
1026 scoped_ptr<VideoCaptureDevice::Client> client) { | |
1027 DCHECK(thread_checker_.CalledOnValidThread()); | |
1028 | |
1029 if (state_ != kIdle) { | |
1030 DVLOG(1) << "Allocate() invoked when not in state Idle."; | |
1031 return; | |
1032 } | |
1033 | |
1034 if (params.requested_format.frame_rate <= 0) { | |
1035 DVLOG(1) << "invalid frame_rate: " << params.requested_format.frame_rate; | |
1036 client->OnError(); | |
1037 return; | |
1038 } | |
1039 | |
1040 if (!render_thread_.Start()) { | |
1041 DVLOG(1) << "Failed to spawn render thread."; | |
1042 client->OnError(); | |
1043 return; | |
1044 } | |
1045 | |
1046 // Frame dimensions must each be a positive, even integer, since the client | |
1047 // wants (or will convert to) YUV420. | |
1048 gfx::Size frame_size(MakeEven(params.requested_format.frame_size.width()), | |
1049 MakeEven(params.requested_format.frame_size.height())); | |
1050 if (frame_size.width() < kMinFrameWidth || | |
1051 frame_size.height() < kMinFrameHeight) { | |
1052 DVLOG(1) << "invalid frame size: " << frame_size.ToString(); | |
1053 client->OnError(); | |
1054 return; | |
1055 } | |
1056 | |
1057 base::TimeDelta capture_period = base::TimeDelta::FromMicroseconds( | |
1058 1000000.0 / params.requested_format.frame_rate + 0.5); | |
1059 | |
1060 scoped_ptr<VideoCaptureOracle> oracle( | |
1061 new VideoCaptureOracle(capture_period, | |
1062 kAcceleratedSubscriberIsSupported)); | |
1063 oracle_proxy_ = | |
1064 new ThreadSafeCaptureOracle(client.Pass(), | |
1065 oracle.Pass(), | |
1066 frame_size, | |
1067 params.requested_format.frame_rate); | |
1068 | |
1069 // Allocates the CaptureMachine. The CaptureMachine will be tracking render | |
1070 // view swapping over its lifetime, and we don't want to lose our reference to | |
1071 // the current render view by starting over with the stale | |
1072 // |initial_render_view_id_|. | |
1073 DCHECK(!capture_machine_.get()); | |
1074 BrowserThread::PostTaskAndReplyWithResult( | |
1075 BrowserThread::UI, FROM_HERE, | |
1076 base::Bind(&CaptureMachine::Create, | |
1077 initial_render_process_id_, | |
1078 initial_render_view_id_, | |
1079 render_thread_.message_loop_proxy(), oracle_proxy_), | |
1080 base::Bind(&Impl::AssignCaptureMachine, AsWeakPtr())); | |
1081 | |
1082 TransitionStateTo(kCapturing); | |
1083 } | |
1084 | |
1085 // static | |
1086 void WebContentsVideoCaptureDevice::Impl::AssignCaptureMachine( | |
1087 base::WeakPtr<WebContentsVideoCaptureDevice::Impl> impl, | |
1088 scoped_ptr<CaptureMachine> capture_machine) { | |
1089 DCHECK(!impl.get() || impl->thread_checker_.CalledOnValidThread()); | |
1090 | |
1091 if (!impl.get()) { | |
1092 // If WCVD::Impl was destroyed before we got back on it's thread and | |
1093 // capture_machine is not NULL, then we need to return to the UI thread to | |
1094 // safely cleanup the CaptureMachine. | |
1095 if (capture_machine) { | |
1096 BrowserThread::PostTask( | |
1097 BrowserThread::UI, FROM_HERE, base::Bind( | |
1098 &DeleteCaptureMachineOnUIThread, base::Passed(&capture_machine))); | |
1099 return; | |
1100 } | |
1101 } else if (!capture_machine) { | |
1102 impl->Error(); | |
1103 } else { | |
1104 impl->capture_machine_ = capture_machine.Pass(); | |
1105 } | |
1106 } | |
1107 | |
1108 void WebContentsVideoCaptureDevice::Impl::StopAndDeAllocate() { | |
1109 DCHECK(thread_checker_.CalledOnValidThread()); | |
1110 | |
1111 if (state_ != kCapturing) { | |
1112 return; | |
1113 } | |
1114 oracle_proxy_->Stop(); | |
1115 oracle_proxy_ = NULL; | |
1116 render_thread_.Stop(); | |
1117 | |
1118 TransitionStateTo(kIdle); | |
1119 | |
1120 // There is still a capture pipeline running that is checking in with the | |
1121 // oracle, and processing captures that are already started in flight. That | |
1122 // pipeline must be shut down asynchronously, on the UI thread. | |
1123 if (capture_machine_) { | |
1124 // The task that is posted to the UI thread might not run if we are shutting | |
1125 // down, so we transfer ownership of CaptureMachine to the closure so that | |
1126 // it is still cleaned up when the closure is deleted. | |
1127 BrowserThread::PostTask( | |
1128 BrowserThread::UI, FROM_HERE, base::Bind( | |
1129 &DeleteCaptureMachineOnUIThread, base::Passed(&capture_machine_))); | |
1130 } | |
1131 } | |
1132 | |
1133 WebContentsVideoCaptureDevice::Impl::~Impl() { | |
1134 DCHECK(!capture_machine_) << "Cleanup on UI thread did not happen."; | |
1135 DVLOG(1) << "WebContentsVideoCaptureDevice::Impl@" << this << " destroying."; | |
1136 } | |
1137 | |
1138 void WebContentsVideoCaptureDevice::Impl::TransitionStateTo(State next_state) { | |
1139 DCHECK(thread_checker_.CalledOnValidThread()); | |
1140 | |
1141 #ifndef NDEBUG | |
1142 static const char* kStateNames[] = { | |
1143 "Idle", "Allocated", "Capturing", "Error" | |
1144 }; | |
1145 DVLOG(1) << "State change: " << kStateNames[state_] | |
1146 << " --> " << kStateNames[next_state]; | |
1147 #endif | |
1148 | |
1149 state_ = next_state; | |
1150 } | |
1151 | |
1152 void WebContentsVideoCaptureDevice::Impl::Error() { | |
1153 DCHECK(thread_checker_.CalledOnValidThread()); | |
1154 | |
1155 if (state_ == kIdle) | |
1156 return; | |
1157 | |
1158 if (oracle_proxy_) | |
1159 oracle_proxy_->ReportError(); | |
1160 | |
1161 StopAndDeAllocate(); | |
1162 TransitionStateTo(kError); | |
1163 } | |
1164 | |
1165 WebContentsVideoCaptureDevice::WebContentsVideoCaptureDevice( | 757 WebContentsVideoCaptureDevice::WebContentsVideoCaptureDevice( |
1166 int render_process_id, | 758 int render_process_id, int render_view_id) |
1167 int render_view_id) | 759 : impl_(new VideoCaptureDeviceImpl(scoped_ptr<VideoCaptureMachine>( |
1168 : impl_(new WebContentsVideoCaptureDevice::Impl(render_process_id, | 760 new WebContentsCaptureMachine(render_process_id, render_view_id)))) {} |
1169 render_view_id)) {} | |
1170 | 761 |
1171 WebContentsVideoCaptureDevice::~WebContentsVideoCaptureDevice() { | 762 WebContentsVideoCaptureDevice::~WebContentsVideoCaptureDevice() { |
1172 DVLOG(2) << "WebContentsVideoCaptureDevice@" << this << " destroying."; | 763 DVLOG(2) << "WebContentsVideoCaptureDevice@" << this << " destroying."; |
1173 } | 764 } |
1174 | 765 |
1175 // static | 766 // static |
1176 media::VideoCaptureDevice* WebContentsVideoCaptureDevice::Create( | 767 media::VideoCaptureDevice* WebContentsVideoCaptureDevice::Create( |
1177 const std::string& device_id) { | 768 const std::string& device_id) { |
1178 // Parse device_id into render_process_id and render_view_id. | 769 // Parse device_id into render_process_id and render_view_id. |
1179 int render_process_id = -1; | 770 int render_process_id = -1; |
(...skipping 11 matching lines...) Expand all Loading... |
1191 scoped_ptr<Client> client) { | 782 scoped_ptr<Client> client) { |
1192 DVLOG(1) << "Allocating " << params.requested_format.frame_size.ToString(); | 783 DVLOG(1) << "Allocating " << params.requested_format.frame_size.ToString(); |
1193 impl_->AllocateAndStart(params, client.Pass()); | 784 impl_->AllocateAndStart(params, client.Pass()); |
1194 } | 785 } |
1195 | 786 |
1196 void WebContentsVideoCaptureDevice::StopAndDeAllocate() { | 787 void WebContentsVideoCaptureDevice::StopAndDeAllocate() { |
1197 impl_->StopAndDeAllocate(); | 788 impl_->StopAndDeAllocate(); |
1198 } | 789 } |
1199 | 790 |
1200 } // namespace content | 791 } // namespace content |
OLD | NEW |