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 #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 |