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 #include "content/browser/renderer_host/media/web_contents_video_capture_device. h" | 5 #include "content/browser/renderer_host/media/web_contents_video_capture_device. h" |
6 | 6 |
7 #include "base/bind_helpers.h" | 7 #include "base/bind_helpers.h" |
8 #include "base/synchronization/condition_variable.h" | 8 #include "base/debug/debugger.h" |
9 #include "base/synchronization/waitable_event.h" | |
10 #include "base/time.h" | 9 #include "base/time.h" |
10 #include "base/timer.h" | |
11 #include "content/browser/browser_thread_impl.h" | 11 #include "content/browser/browser_thread_impl.h" |
12 #include "content/browser/renderer_host/render_widget_host_delegate.h" | 12 #include "content/browser/renderer_host/media/web_contents_capture_util.h" |
13 #include "content/browser/renderer_host/render_view_host_factory.h" | |
13 #include "content/browser/renderer_host/render_widget_host_impl.h" | 14 #include "content/browser/renderer_host/render_widget_host_impl.h" |
14 #include "content/public/test/mock_render_process_host.h" | 15 #include "content/browser/renderer_host/test_render_view_host.h" |
16 #include "content/browser/web_contents/test_web_contents.h" | |
15 #include "content/public/test/test_browser_context.h" | 17 #include "content/public/test/test_browser_context.h" |
18 #include "content/public/test/test_browser_thread.h" | |
19 #include "content/public/test/test_utils.h" | |
16 #include "media/base/video_util.h" | 20 #include "media/base/video_util.h" |
17 #include "media/video/capture/video_capture_types.h" | 21 #include "media/video/capture/video_capture_types.h" |
18 #include "skia/ext/platform_canvas.h" | 22 #include "skia/ext/platform_canvas.h" |
19 #include "testing/gtest/include/gtest/gtest.h" | 23 #include "testing/gtest/include/gtest/gtest.h" |
20 #include "third_party/skia/include/core/SkColor.h" | 24 #include "third_party/skia/include/core/SkColor.h" |
21 | 25 |
22 namespace content { | 26 namespace content { |
23 namespace { | 27 namespace { |
24 const int kTestWidth = 1280; | 28 const int kTestWidth = 1280; |
25 const int kTestHeight = 720; | 29 const int kTestHeight = 720; |
26 const int kBytesPerPixel = 4; | 30 const int kBytesPerPixel = 4; |
27 const int kTestFramesPerSecond = 8; | 31 const int kTestFramesPerSecond = 20; |
28 const base::TimeDelta kWaitTimeout = | 32 const base::TimeDelta kWaitTimeout = base::TimeDelta::FromMilliseconds(2000); |
29 base::TimeDelta::FromMilliseconds(2000); | |
30 const SkColor kNothingYet = 0xdeadbeef; | 33 const SkColor kNothingYet = 0xdeadbeef; |
31 const SkColor kNotInterested = ~kNothingYet; | 34 const SkColor kNotInterested = ~kNothingYet; |
35 | |
36 void DeadlineExceeded(base::Closure quit_closure) { | |
37 if (!base::debug::BeingDebugged()) { | |
38 FAIL() << "Deadline exceeded while waiting, quitting"; | |
39 quit_closure.Run(); | |
40 } else { | |
41 LOG(WARNING) << "Deadline exceeded; test would fail if debugger weren't " | |
42 << "attached."; | |
43 } | |
32 } | 44 } |
33 | 45 |
46 void RunCurrentLoopWithDeadline() { | |
47 base::Timer deadline(false, false); | |
48 deadline.Start(FROM_HERE, kWaitTimeout, base::Bind( | |
49 &DeadlineExceeded, MessageLoop::current()->QuitClosure())); | |
50 MessageLoop::current()->Run(); | |
51 deadline.Stop(); | |
52 } | |
53 | |
54 // Thread-safe class that controls the source pattern to be captured by the | |
55 // system under test. The lifetime of this class is greater than the lifetime | |
56 // of all objects that reference it, so it does not need to be reference | |
57 // counted. | |
58 class CaptureTestSourceController { | |
59 public: | |
60 CaptureTestSourceController() | |
61 : color_(SK_ColorMAGENTA), | |
62 copy_result_size_(kTestWidth, kTestHeight), | |
63 can_copy_to_video_frame_(false) {} | |
64 | |
65 void SetSolidColor(SkColor color) { | |
66 base::AutoLock guard(lock_); | |
67 color_ = color; | |
68 } | |
69 | |
70 SkColor GetSolidColor() { | |
71 base::AutoLock guard(lock_); | |
72 return color_; | |
73 } | |
74 | |
75 void SetCopyResultSize(int width, int height) { | |
76 base::AutoLock guard(lock_); | |
77 copy_result_size_ = gfx::Size(width, height); | |
78 } | |
79 | |
80 gfx::Size GetCopyResultSize() { | |
81 base::AutoLock guard(lock_); | |
82 return copy_result_size_; | |
83 } | |
84 | |
85 void SignalBackingStoreCopy() { | |
86 // TODO(nick): This actually should always be happening on the UI thread. | |
87 base::AutoLock guard(lock_); | |
88 if (!copy_done_.is_null()) { | |
89 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, copy_done_); | |
90 copy_done_.Reset(); | |
91 } | |
92 } | |
93 | |
94 void SetCanCopyToVideoFrame(bool value) { | |
95 base::AutoLock guard(lock_); | |
96 can_copy_to_video_frame_ = value; | |
97 } | |
98 | |
99 bool CanCopyToVideoFrame() { | |
100 base::AutoLock guard(lock_); | |
101 return can_copy_to_video_frame_; | |
102 } | |
103 | |
104 void WaitForNextBackingStoreCopy() { | |
105 { | |
106 base::AutoLock guard(lock_); | |
107 copy_done_ = MessageLoop::current()->QuitClosure(); | |
108 } | |
109 RunCurrentLoopWithDeadline(); | |
110 } | |
111 | |
112 private: | |
113 base::Lock lock_; // Guards changes to |color_| and |copy_result_size_| | |
miu
2013/02/20 06:45:10
Comment should just say "guards changes to all mem
ncarter (slow)
2013/02/22 02:16:11
Done.
| |
114 SkColor color_; | |
115 gfx::Size copy_result_size_; | |
116 bool can_copy_to_video_frame_; | |
117 base::Closure copy_done_; | |
118 }; | |
miu
2013/02/20 06:45:10
nit: Add DISALLOW_COPY_AND_ASSIGN.
ncarter (slow)
2013/02/22 02:16:11
Done.
| |
119 | |
34 // A stub implementation which returns solid-color bitmaps in calls to | 120 // A stub implementation which returns solid-color bitmaps in calls to |
miu
2013/02/20 06:45:10
Comment needs adjusting. FWICT, this class simula
ncarter (slow)
2013/02/22 02:16:11
Done.
| |
35 // CopyFromBackingStore(). The unit tests can change the color for successive | 121 // CopyFromBackingStore(). The unit tests can change the color for successive |
36 // captures. | 122 // captures. |
37 class StubRenderWidgetHost : public RenderWidgetHostImpl { | 123 class CaptureTestView : public TestRenderWidgetHostView { |
38 public: | 124 public: |
39 StubRenderWidgetHost(RenderProcessHost* process, int routing_id) | 125 explicit CaptureTestView(RenderWidgetHostImpl* rwh, |
40 : RenderWidgetHostImpl(&delegate_, process, routing_id), | 126 CaptureTestSourceController* controller) |
41 color_(kNothingYet), | 127 : TestRenderWidgetHostView(rwh), |
42 copy_result_size_(kTestWidth, kTestHeight), | 128 controller_(controller) {} |
43 copy_event_(false, false) {} | 129 virtual ~CaptureTestView() {} |
44 | 130 |
45 void SetSolidColor(SkColor color) { | 131 // TestRenderWidgetHostView overrides. |
46 base::AutoLock guard(lock_); | 132 virtual gfx::Rect GetViewBounds() const OVERRIDE { |
47 color_ = color; | 133 return gfx::Rect(100, 100, 100 + kTestWidth, 100 + kTestHeight); |
48 } | 134 } |
49 | 135 |
50 void SetCopyResultSize(int width, int height) { | 136 // |
miu
2013/02/20 06:45:10
Unfinished comment. Looks like this returns true
ncarter (slow)
2013/02/22 02:16:11
Done.
| |
51 base::AutoLock guard(lock_); | 137 virtual bool CanCopyToVideoFrame() const OVERRIDE { |
52 copy_result_size_ = gfx::Size(width, height); | 138 return controller_->CanCopyToVideoFrame(); |
53 } | 139 } |
54 | 140 |
55 bool WaitForNextBackingStoreCopy() { | 141 virtual void CopyFromCompositingSurfaceToVideoFrame( |
56 if (!copy_event_.TimedWait(kWaitTimeout)) { | 142 const gfx::Rect& src_subrect, |
57 ADD_FAILURE() << "WaitForNextBackingStoreCopy: wait deadline exceeded"; | 143 const scoped_refptr<media::VideoFrame>& target, |
58 return false; | 144 const base::Callback<void(bool)>& callback) OVERRIDE { |
59 } | 145 SkColor c = controller_->GetSolidColor(); |
60 return true; | 146 media::FillYUV(target, SkColorGetR(c), SkColorGetG(c), SkColorGetB(c)); |
61 } | 147 callback.Run(true); |
62 | 148 controller_->SignalBackingStoreCopy(); |
63 // RenderWidgetHostImpl overrides. | 149 } |
150 | |
151 private: | |
152 CaptureTestSourceController* controller_; | |
miu
2013/02/20 06:45:10
nit: Type should be "CaptureTestSourceController*
ncarter (slow)
2013/02/22 02:16:11
Done.
| |
153 DISALLOW_IMPLICIT_CONSTRUCTORS(CaptureTestView); | |
miu
2013/02/20 06:45:10
nit: Add newline above this line.
ncarter (slow)
2013/02/22 02:16:11
Done.
| |
154 }; | |
155 | |
156 #if defined(COMPILER_MSVC) | |
157 // See comment for same warning on RenderViewHostImpl. | |
miu
2013/02/20 06:45:10
nit: Just so people don't have to go looking it up
ncarter (slow)
2013/02/22 02:16:11
Done.
| |
158 #pragma warning(push) | |
159 #pragma warning(disable: 4250) | |
160 #endif | |
161 | |
162 // A stub implementation which returns solid-color bitmaps in calls to | |
163 // CopyFromBackingStore(). The behavior is controlled by a | |
164 // CaptureTestSourceController. | |
165 class CaptureTestRenderViewHost : public TestRenderViewHost { | |
166 public: | |
167 CaptureTestRenderViewHost(SiteInstance* instance, | |
168 RenderViewHostDelegate* delegate, | |
169 RenderWidgetHostDelegate* widget_delegate, | |
170 int routing_id, | |
171 bool swapped_out, | |
172 CaptureTestSourceController* controller) | |
173 : TestRenderViewHost(instance, delegate, widget_delegate, routing_id, | |
174 swapped_out), | |
175 controller_(controller) { | |
176 // Override the default view installed by TestRenderViewHost; we need | |
177 // our special subclass which has mocked-out tab capture support. | |
178 SetView(new CaptureTestView(this, controller)); | |
179 } | |
180 | |
181 // TestRenderViewHost overrides. | |
64 virtual void CopyFromBackingStore( | 182 virtual void CopyFromBackingStore( |
65 const gfx::Rect& src_rect, | 183 const gfx::Rect& src_rect, |
66 const gfx::Size& accelerated_dst_size, | 184 const gfx::Size& accelerated_dst_size, |
67 const base::Callback<void(bool, const SkBitmap&)>& callback) OVERRIDE { | 185 const base::Callback<void(bool, const SkBitmap&)>& callback) OVERRIDE { |
186 gfx::Size size = controller_->GetCopyResultSize(); | |
187 SkColor color = controller_->GetSolidColor(); | |
188 | |
68 // Although it's not necessary, use a PlatformBitmap here (instead of a | 189 // Although it's not necessary, use a PlatformBitmap here (instead of a |
69 // regular SkBitmap) to exercise possible threading issues. | 190 // regular SkBitmap) to exercise possible threading issues. |
70 scoped_ptr<skia::PlatformBitmap> platform_bitmap(new skia::PlatformBitmap); | 191 skia::PlatformBitmap output; |
71 EXPECT_TRUE(platform_bitmap->Allocate( | 192 EXPECT_TRUE(output.Allocate(size.width(), size.height(), false)); |
72 copy_result_size_.width(), copy_result_size_.height(), false)); | 193 { |
73 { | 194 SkAutoLockPixels locker(output.GetBitmap()); |
74 SkAutoLockPixels locker(platform_bitmap->GetBitmap()); | 195 output.GetBitmap().eraseColor(color); |
75 base::AutoLock guard(lock_); | 196 } |
76 platform_bitmap->GetBitmap().eraseColor(color_); | 197 callback.Run(true, output.GetBitmap()); |
77 } | 198 controller_->SignalBackingStoreCopy(); |
78 | 199 } |
79 callback.Run(true, platform_bitmap->GetBitmap()); | 200 |
80 copy_event_.Signal(); | 201 private: |
81 } | 202 CaptureTestSourceController* controller_; |
82 | 203 |
83 private: | 204 DISALLOW_IMPLICIT_CONSTRUCTORS(CaptureTestRenderViewHost); |
84 class StubRenderWidgetHostDelegate : public RenderWidgetHostDelegate { | 205 }; |
85 public: | 206 |
86 StubRenderWidgetHostDelegate() {} | 207 #if defined(COMPILER_MSVC) |
87 virtual ~StubRenderWidgetHostDelegate() {} | 208 // Re-enable warning 4250 |
88 | 209 #pragma warning(pop) |
89 private: | 210 #endif |
90 DISALLOW_COPY_AND_ASSIGN(StubRenderWidgetHostDelegate); | 211 |
91 }; | 212 class CaptureTestRenderViewHostFactory : public RenderViewHostFactory { |
92 | 213 public: |
93 StubRenderWidgetHostDelegate delegate_; | 214 explicit CaptureTestRenderViewHostFactory( |
94 base::Lock lock_; // Guards changes to color_. | 215 CaptureTestSourceController* controller) : controller_(controller) { |
95 SkColor color_; | 216 RegisterFactory(this); |
96 gfx::Size copy_result_size_; | 217 } |
97 base::WaitableEvent copy_event_; | 218 |
98 | 219 virtual ~CaptureTestRenderViewHostFactory() { |
99 DISALLOW_IMPLICIT_CONSTRUCTORS(StubRenderWidgetHost); | 220 UnregisterFactory(); |
221 } | |
222 | |
223 // RenderViewHostFactory implementation. | |
224 virtual RenderViewHost* CreateRenderViewHost( | |
225 SiteInstance* instance, | |
226 RenderViewHostDelegate* delegate, | |
227 RenderWidgetHostDelegate* widget_delegate, | |
228 int routing_id, | |
229 bool swapped_out, | |
230 SessionStorageNamespace* session_storage_namespace) { | |
231 return new CaptureTestRenderViewHost(instance, delegate, widget_delegate, | |
232 routing_id, swapped_out, controller_); | |
233 } | |
234 private: | |
235 CaptureTestSourceController* controller_; | |
236 | |
237 DISALLOW_IMPLICIT_CONSTRUCTORS(CaptureTestRenderViewHostFactory); | |
100 }; | 238 }; |
101 | 239 |
102 // A stub consumer of captured video frames, which checks the output of | 240 // A stub consumer of captured video frames, which checks the output of |
103 // WebContentsVideoCaptureDevice. | 241 // WebContentsVideoCaptureDevice. |
104 class StubConsumer : public media::VideoCaptureDevice::EventHandler { | 242 class StubConsumer : public media::VideoCaptureDevice::EventHandler { |
105 public: | 243 public: |
106 StubConsumer() : output_changed_(&lock_), | 244 StubConsumer() : error_encountered_(false), wait_color_(0xcafe1950) {} |
107 picture_color_(kNothingYet), | |
108 error_encountered_(false) {} | |
109 virtual ~StubConsumer() {} | 245 virtual ~StubConsumer() {} |
110 | 246 |
111 // Returns false if an error was encountered. | 247 void QuitIfConditionMet(SkColor color) { |
112 bool WaitForNextColorOrError(SkColor expected_color) { | 248 base::AutoLock guard(lock_); |
113 base::TimeTicks deadline = base::TimeTicks::Now() + kWaitTimeout; | 249 |
114 base::AutoLock guard(lock_); | 250 if (wait_color_ == color || error_encountered_) |
115 while (picture_color_ != expected_color && !error_encountered_) { | 251 MessageLoop::current()->Quit(); |
116 output_changed_.TimedWait(kWaitTimeout); | 252 } |
117 if (base::TimeTicks::Now() >= deadline) { | 253 |
118 ADD_FAILURE() << "WaitForNextColorOrError: wait deadline exceeded"; | 254 void WaitForNextColor(SkColor expected_color) { |
119 return false; | 255 { |
120 } | 256 base::AutoLock guard(lock_); |
121 } | 257 wait_color_ = expected_color; |
122 if (!error_encountered_) { | 258 error_encountered_ = false; |
123 EXPECT_EQ(expected_color, picture_color_); | 259 } |
124 return true; | 260 RunCurrentLoopWithDeadline(); |
125 } else { | 261 { |
126 return false; | 262 base::AutoLock guard(lock_); |
127 } | 263 ASSERT_FALSE(error_encountered_); |
128 } | 264 } |
129 | 265 } |
266 | |
267 void WaitForError() { | |
268 { | |
269 base::AutoLock guard(lock_); | |
270 wait_color_ = kNotInterested; | |
271 error_encountered_ = false; | |
272 } | |
273 RunCurrentLoopWithDeadline(); | |
274 { | |
275 base::AutoLock guard(lock_); | |
276 ASSERT_TRUE(error_encountered_); | |
277 } | |
278 } | |
279 | |
130 virtual void OnIncomingCapturedFrame(const uint8* data, int length, | 280 virtual void OnIncomingCapturedFrame(const uint8* data, int length, |
131 base::Time timestamp) OVERRIDE { | 281 base::Time timestamp) OVERRIDE { |
132 DCHECK(data); | 282 DCHECK(data); |
133 static const int kNumPixels = kTestWidth * kTestHeight; | 283 static const int kNumPixels = kTestWidth * kTestHeight; |
134 EXPECT_EQ(kNumPixels * kBytesPerPixel, length); | 284 EXPECT_EQ(kNumPixels * kBytesPerPixel, length); |
135 const uint32* p = reinterpret_cast<const uint32*>(data); | 285 const uint32* p = reinterpret_cast<const uint32*>(data); |
136 const uint32* const p_end = p + kNumPixels; | 286 const uint32* const p_end = p + kNumPixels; |
137 const SkColor color = *p; | 287 const SkColor color = *p; |
138 bool all_pixels_are_the_same_color = true; | 288 bool all_pixels_are_the_same_color = true; |
139 for (++p; p < p_end; ++p) { | 289 for (++p; p < p_end; ++p) { |
140 if (*p != color) { | 290 if (*p != color) { |
141 all_pixels_are_the_same_color = false; | 291 all_pixels_are_the_same_color = false; |
142 break; | 292 break; |
143 } | 293 } |
144 } | 294 } |
145 EXPECT_TRUE(all_pixels_are_the_same_color); | 295 EXPECT_TRUE(all_pixels_are_the_same_color); |
146 | 296 PostColorOrError(color); |
147 { | |
148 base::AutoLock guard(lock_); | |
149 if (color != picture_color_) { | |
150 picture_color_ = color; | |
151 output_changed_.Signal(); | |
152 } | |
153 } | |
154 } | 297 } |
155 | 298 |
156 virtual void OnIncomingCapturedVideoFrame(media::VideoFrame* frame, | 299 virtual void OnIncomingCapturedVideoFrame(media::VideoFrame* frame, |
157 base::Time timestamp) OVERRIDE { | 300 base::Time timestamp) OVERRIDE { |
158 EXPECT_EQ(gfx::Size(kTestWidth, kTestHeight), frame->coded_size()); | 301 EXPECT_EQ(gfx::Size(kTestWidth, kTestHeight), frame->coded_size()); |
159 EXPECT_EQ(media::VideoFrame::YV12, frame->format()); | 302 EXPECT_EQ(media::VideoFrame::YV12, frame->format()); |
160 bool all_pixels_are_the_same_color = true; | |
161 uint8 yuv[3] = {0}; | 303 uint8 yuv[3] = {0}; |
162 for (int plane = 0; plane < 3; ++plane) { | 304 for (int plane = 0; plane < 3; ++plane) { |
163 yuv[plane] = frame->data(plane)[0]; | 305 yuv[plane] = frame->data(plane)[0]; |
164 for (int y = 0; y < frame->rows(plane); ++y) { | |
165 for (int x = 0; x < frame->row_bytes(plane); ++x) { | |
166 if (yuv[plane] != frame->data(plane)[x + y * frame->stride(plane)]) { | |
167 all_pixels_are_the_same_color = false; | |
168 break; | |
169 } | |
170 } | |
171 } | |
172 } | 306 } |
173 EXPECT_TRUE(all_pixels_are_the_same_color); | 307 // TODO(nick): We just look at the first pixel presently, because if |
174 const SkColor color = SkColorSetRGB(yuv[0], yuv[1], yuv[2]); | 308 // the analysis is too slow, the backlog of frames will grow without bound |
309 // and trouble erupts. http://crbug.com/174519 | |
310 PostColorOrError(SkColorSetRGB(yuv[0], yuv[1], yuv[2])); | |
311 } | |
175 | 312 |
176 { | 313 void PostColorOrError(SkColor new_color) { |
177 base::AutoLock guard(lock_); | 314 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind( |
178 if (color != picture_color_) { | 315 &StubConsumer::QuitIfConditionMet, base::Unretained(this), new_color)); |
179 picture_color_ = color; | |
180 output_changed_.Signal(); | |
181 } | |
182 } | |
183 } | 316 } |
184 | 317 |
185 virtual void OnError() OVERRIDE { | 318 virtual void OnError() OVERRIDE { |
186 base::AutoLock guard(lock_); | 319 { |
187 error_encountered_ = true; | 320 base::AutoLock guard(lock_); |
188 output_changed_.Signal(); | 321 error_encountered_ = true; |
322 } | |
323 PostColorOrError(kNothingYet); | |
189 } | 324 } |
190 | 325 |
191 virtual void OnFrameInfo(const media::VideoCaptureCapability& info) OVERRIDE { | 326 virtual void OnFrameInfo(const media::VideoCaptureCapability& info) OVERRIDE { |
192 EXPECT_EQ(kTestWidth, info.width); | 327 EXPECT_EQ(kTestWidth, info.width); |
193 EXPECT_EQ(kTestHeight, info.height); | 328 EXPECT_EQ(kTestHeight, info.height); |
194 EXPECT_EQ(kTestFramesPerSecond, info.frame_rate); | 329 EXPECT_EQ(kTestFramesPerSecond, info.frame_rate); |
195 EXPECT_EQ(media::VideoCaptureCapability::kARGB, info.color); | 330 EXPECT_EQ(media::VideoCaptureCapability::kARGB, info.color); |
196 } | 331 } |
197 | 332 |
198 private: | 333 private: |
199 base::Lock lock_; | 334 base::Lock lock_; |
200 base::ConditionVariable output_changed_; | |
201 SkColor picture_color_; | |
202 bool error_encountered_; | 335 bool error_encountered_; |
336 SkColor wait_color_; | |
203 | 337 |
204 DISALLOW_COPY_AND_ASSIGN(StubConsumer); | 338 DISALLOW_COPY_AND_ASSIGN(StubConsumer); |
205 }; | 339 }; |
206 | 340 |
341 } // namespace | |
342 | |
207 // Test harness that sets up a minimal environment with necessary stubs. | 343 // Test harness that sets up a minimal environment with necessary stubs. |
208 class WebContentsVideoCaptureDeviceTest : public testing::Test { | 344 class WebContentsVideoCaptureDeviceTest : public testing::Test { |
209 public: | 345 public: |
210 WebContentsVideoCaptureDeviceTest() {} | 346 WebContentsVideoCaptureDeviceTest() {} |
211 | 347 |
212 protected: | 348 protected: |
213 virtual void SetUp() { | 349 virtual void SetUp() { |
214 // This is a MessageLoop for the current thread. The MockRenderProcessHost | 350 // The main thread will serve as the UI thread as well as the test thread. |
215 // will schedule its destruction in this MessageLoop during TearDown(). | 351 // We'll manually pump the run loop at appropriate times in the test. |
216 message_loop_.reset(new MessageLoop(MessageLoop::TYPE_IO)); | 352 ui_thread_.reset(new TestBrowserThread(BrowserThread::UI, &message_loop_)); |
217 | 353 |
218 // The CopyFromBackingStore and WebContents tracking occur on the UI thread. | 354 // Create our (self-registering) RVH factory, so that when we create a |
219 ui_thread_.reset(new BrowserThreadImpl(BrowserThread::UI)); | 355 // WebContents, it in turn creates CaptureTestRenderViewHosts. |
220 ui_thread_->Start(); | 356 render_view_host_factory_.reset( |
357 new CaptureTestRenderViewHostFactory(&controller_)); | |
221 | 358 |
222 // And the rest... | |
223 browser_context_.reset(new TestBrowserContext()); | 359 browser_context_.reset(new TestBrowserContext()); |
224 source_.reset(new StubRenderWidgetHost( | 360 |
225 new MockRenderProcessHost(browser_context_.get()), MSG_ROUTING_NONE)); | 361 scoped_refptr<SiteInstance> site_instance = |
226 destroyed_.reset(new base::WaitableEvent(true, false)); | 362 SiteInstance::Create(browser_context_.get()); |
227 device_.reset(WebContentsVideoCaptureDevice::CreateForTesting( | 363 web_contents_.reset( |
228 source_.get(), | 364 TestWebContents::Create(browser_context_.get(), site_instance)); |
229 base::Bind(&base::WaitableEvent::Signal, | 365 |
230 base::Unretained(destroyed_.get())))); | 366 // This is actually a CaptureTestRenderViewHost. |
231 consumer_.reset(new StubConsumer); | 367 RenderWidgetHostImpl* rwh = |
368 RenderWidgetHostImpl::From(web_contents_->GetRenderViewHost()); | |
369 | |
370 std::string device_id = | |
371 WebContentsCaptureUtil::AppendWebContentsDeviceScheme( | |
372 base::StringPrintf("%d:%d", rwh->GetProcess()->GetID(), | |
373 rwh->GetRoutingID())); | |
374 device_.reset(WebContentsVideoCaptureDevice::Create(device_id)); | |
232 } | 375 } |
233 | 376 |
234 virtual void TearDown() { | 377 virtual void TearDown() { |
235 // Tear down in opposite order of set-up. | 378 // Tear down in opposite order of set-up. |
236 device_->DeAllocate(); // Guarantees no more use of consumer_. | 379 device_->DeAllocate(); |
237 consumer_.reset(); | |
238 device_.reset(); // Release reference to internal CaptureMachine. | 380 device_.reset(); // Release reference to internal CaptureMachine. |
239 message_loop_->RunUntilIdle(); // Just in case. | 381 content::RunAllPendingInMessageLoop(); |
240 destroyed_->Wait(); // Wait until CaptureMachine is fully destroyed. | 382 |
241 destroyed_.reset(); | 383 web_contents_.reset(); |
242 source_.reset(); | |
243 browser_context_.reset(); | 384 browser_context_.reset(); |
244 ui_thread_->Stop(); | 385 |
245 ui_thread_.reset(); | 386 content::RunAllPendingInMessageLoop(); |
246 message_loop_->RunUntilIdle(); // Deletes MockRenderProcessHost. | 387 message_loop_.RunUntilIdle(); |
miu
2013/02/20 06:45:10
Can you confirm whether this is still needed? Sin
ncarter (slow)
2013/02/22 02:16:11
Reworked this.
| |
247 message_loop_.reset(); | 388 |
389 render_view_host_factory_.reset(); | |
248 } | 390 } |
249 | 391 |
250 // Accessors. | 392 // Accessors. |
251 StubRenderWidgetHost* source() const { return source_.get(); } | 393 CaptureTestSourceController* source() { return &controller_; } |
252 media::VideoCaptureDevice* device() const { return device_.get(); } | 394 media::VideoCaptureDevice* device() { return device_.get(); } |
253 StubConsumer* consumer() const { return consumer_.get(); } | 395 StubConsumer* consumer() { return &consumer_; } |
254 | 396 |
255 private: | 397 private: |
256 scoped_ptr<MessageLoop> message_loop_; | 398 // The consumer is the ultimate recipient of captured pixel data. |
257 scoped_ptr<BrowserThreadImpl> ui_thread_; | 399 StubConsumer consumer_; |
400 | |
401 // The controller controls which pixel patterns to produce. | |
402 CaptureTestSourceController controller_; | |
403 | |
404 // We run the UI message loop on the main thread. The capture device | |
405 // will also spin up its own threads. | |
406 MessageLoopForUI message_loop_; | |
407 scoped_ptr<TestBrowserThread> ui_thread_; | |
408 | |
409 // Creates capture-capable RenderViewHosts whose pixel content production is | |
410 // under the control of |controller_|. | |
411 scoped_ptr<CaptureTestRenderViewHostFactory> render_view_host_factory_; | |
412 | |
413 // A mocked-out browser and tab. | |
258 scoped_ptr<TestBrowserContext> browser_context_; | 414 scoped_ptr<TestBrowserContext> browser_context_; |
259 scoped_ptr<StubRenderWidgetHost> source_; | 415 scoped_ptr<WebContents> web_contents_; |
260 scoped_ptr<base::WaitableEvent> destroyed_; | 416 |
417 // Finally, the WebContentsVideoCaptureDevice under test. | |
261 scoped_ptr<media::VideoCaptureDevice> device_; | 418 scoped_ptr<media::VideoCaptureDevice> device_; |
262 scoped_ptr<StubConsumer> consumer_; | |
263 | 419 |
264 DISALLOW_COPY_AND_ASSIGN(WebContentsVideoCaptureDeviceTest); | 420 DISALLOW_COPY_AND_ASSIGN(WebContentsVideoCaptureDeviceTest); |
265 }; | 421 }; |
266 | 422 |
267 // The "happy case" test. No scaling is needed, so we should be able to change | 423 // The "happy case" test. No scaling is needed, so we should be able to change |
268 // the picture emitted from the source and expect to see each delivered to the | 424 // the picture emitted from the source and expect to see each delivered to the |
269 // consumer. The test will alternate between the SkBitmap and the VideoFrame | 425 // consumer. The test will alternate between the SkBitmap and the VideoFrame |
270 // paths, just as RenderWidgetHost might if the content falls in and out of | 426 // paths, just as RenderWidgetHost might if the content falls in and out of |
271 // accelerated compositing. | 427 // accelerated compositing. |
272 TEST_F(WebContentsVideoCaptureDeviceTest, GoesThroughAllTheMotions) { | 428 TEST_F(WebContentsVideoCaptureDeviceTest, GoesThroughAllTheMotions) { |
273 device()->Allocate(kTestWidth, kTestHeight, kTestFramesPerSecond, | 429 device()->Allocate(kTestWidth, kTestHeight, kTestFramesPerSecond, consumer()); |
274 consumer()); | |
275 | 430 |
276 device()->Start(); | 431 device()->Start(); |
277 | 432 |
278 bool use_video_frames = false; | 433 bool use_video_frames = false; |
279 for (int i = 0; i < 4; i++, use_video_frames = !use_video_frames) { | 434 for (int i = 0; i < 4; i++, use_video_frames = !use_video_frames) { |
280 SCOPED_TRACE( | 435 SCOPED_TRACE(StringPrintf("Using %s path, iteration #%d", |
281 testing::Message() << "Using " | 436 use_video_frames ? "VideoFrame" : "SkBitmap", i)); |
282 << (use_video_frames ? "VideoFrame" : "SkBitmap") | 437 source()->SetCanCopyToVideoFrame(use_video_frames); |
283 << " path, iteration #" << i); | |
284 // TODO(nick): Implement this. | |
285 // source()->SetUseVideoFrames(use_video_frames); | |
286 source()->SetSolidColor(SK_ColorRED); | 438 source()->SetSolidColor(SK_ColorRED); |
287 source()->WaitForNextBackingStoreCopy(); | 439 ASSERT_NO_FATAL_FAILURE(source()->WaitForNextBackingStoreCopy()); |
288 ASSERT_TRUE(consumer()->WaitForNextColorOrError(SK_ColorRED)); | 440 ASSERT_NO_FATAL_FAILURE(consumer()->WaitForNextColor(SK_ColorRED)); |
289 source()->SetSolidColor(SK_ColorGREEN); | 441 source()->SetSolidColor(SK_ColorGREEN); |
290 ASSERT_TRUE(consumer()->WaitForNextColorOrError(SK_ColorGREEN)); | 442 ASSERT_NO_FATAL_FAILURE(consumer()->WaitForNextColor(SK_ColorGREEN)); |
291 source()->SetSolidColor(SK_ColorBLUE); | 443 source()->SetSolidColor(SK_ColorBLUE); |
292 ASSERT_TRUE(consumer()->WaitForNextColorOrError(SK_ColorBLUE)); | 444 ASSERT_NO_FATAL_FAILURE(consumer()->WaitForNextColor(SK_ColorBLUE)); |
293 source()->SetSolidColor(SK_ColorBLACK); | 445 source()->SetSolidColor(SK_ColorBLACK); |
294 ASSERT_TRUE(consumer()->WaitForNextColorOrError(SK_ColorBLACK)); | 446 ASSERT_NO_FATAL_FAILURE(consumer()->WaitForNextColor(SK_ColorBLACK)); |
295 } | 447 } |
296 | 448 |
297 device()->DeAllocate(); | 449 device()->DeAllocate(); |
298 } | 450 } |
299 | 451 |
300 TEST_F(WebContentsVideoCaptureDeviceTest, RejectsInvalidAllocateParams) { | 452 TEST_F(WebContentsVideoCaptureDeviceTest, RejectsInvalidAllocateParams) { |
301 device()->Allocate(1280, 720, -2, consumer()); | 453 device()->Allocate(1280, 720, -2, consumer()); |
302 EXPECT_FALSE(consumer()->WaitForNextColorOrError(kNotInterested)); | 454 ASSERT_NO_FATAL_FAILURE(consumer()->WaitForError()); |
303 } | 455 } |
304 | 456 |
305 TEST_F(WebContentsVideoCaptureDeviceTest, BadFramesGoodFrames) { | 457 TEST_F(WebContentsVideoCaptureDeviceTest, BadFramesGoodFrames) { |
306 device()->Allocate(kTestWidth, kTestHeight, kTestFramesPerSecond, | 458 device()->Allocate(kTestWidth, kTestHeight, kTestFramesPerSecond, consumer()); |
307 consumer()); | |
308 | |
309 | 459 |
310 // 1x1 is too small to process; we intend for this to result in an error. | 460 // 1x1 is too small to process; we intend for this to result in an error. |
311 source()->SetCopyResultSize(1, 1); | 461 source()->SetCopyResultSize(1, 1); |
312 source()->SetSolidColor(SK_ColorRED); | 462 source()->SetSolidColor(SK_ColorRED); |
313 device()->Start(); | 463 device()->Start(); |
314 | 464 |
315 // These frames ought to be dropped during the Render stage. Let | 465 // These frames ought to be dropped during the Render stage. Let |
316 // several captures to happen. | 466 // several captures to happen. |
317 ASSERT_TRUE(source()->WaitForNextBackingStoreCopy()); | 467 ASSERT_NO_FATAL_FAILURE(source()->WaitForNextBackingStoreCopy()); |
318 ASSERT_TRUE(source()->WaitForNextBackingStoreCopy()); | 468 ASSERT_NO_FATAL_FAILURE(source()->WaitForNextBackingStoreCopy()); |
319 ASSERT_TRUE(source()->WaitForNextBackingStoreCopy()); | 469 ASSERT_NO_FATAL_FAILURE(source()->WaitForNextBackingStoreCopy()); |
320 ASSERT_TRUE(source()->WaitForNextBackingStoreCopy()); | 470 ASSERT_NO_FATAL_FAILURE(source()->WaitForNextBackingStoreCopy()); |
321 ASSERT_TRUE(source()->WaitForNextBackingStoreCopy()); | 471 ASSERT_NO_FATAL_FAILURE(source()->WaitForNextBackingStoreCopy()); |
322 | 472 |
323 // Now push some good frames through; they should be processed normally. | 473 // Now push some good frames through; they should be processed normally. |
324 source()->SetCopyResultSize(kTestWidth, kTestHeight); | 474 source()->SetCopyResultSize(kTestWidth, kTestHeight); |
325 source()->SetSolidColor(SK_ColorGREEN); | 475 source()->SetSolidColor(SK_ColorGREEN); |
326 EXPECT_TRUE(consumer()->WaitForNextColorOrError(SK_ColorGREEN)); | 476 ASSERT_NO_FATAL_FAILURE(consumer()->WaitForNextColor(SK_ColorGREEN)); |
327 source()->SetSolidColor(SK_ColorRED); | 477 source()->SetSolidColor(SK_ColorRED); |
328 EXPECT_TRUE(consumer()->WaitForNextColorOrError(SK_ColorRED)); | 478 ASSERT_NO_FATAL_FAILURE(consumer()->WaitForNextColor(SK_ColorRED)); |
479 | |
480 device()->Stop(); | |
329 device()->DeAllocate(); | 481 device()->DeAllocate(); |
330 } | 482 } |
331 | 483 |
332 } // namespace content | 484 } // namespace content |
OLD | NEW |