Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(109)

Side by Side Diff: content/browser/renderer_host/video_capture_host_unittest.cc

Issue 7002027: VideoCaptureHost (Closed) Base URL: http://src.chromium.org/svn/trunk/src/
Patch Set: '' Created 9 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <list>
6 #include <string>
7
8 #include "base/file_util.h"
9 #include "base/memory/scoped_ptr.h"
10 #include "base/message_loop.h"
11 #include "base/process_util.h"
12 #include "base/stringprintf.h"
13 #include "content/browser/browser_thread.h"
14 #include "content/browser/media_stream/video_capture_manager.h"
15 #include "content/browser/renderer_host/video_capture_host.h"
16 #include "content/common/video_capture_messages.h"
17 #include "media/video/capture/video_capture_types.h"
18
19 #include "testing/gmock/include/gmock/gmock.h"
20 #include "testing/gtest/include/gtest/gtest.h"
21
22 using ::testing::_;
23 using ::testing::AtLeast;
24 using ::testing::AnyNumber;
25 using ::testing::DoAll;
26 using ::testing::InSequence;
27 using ::testing::Mock;
28 using ::testing::Return;
29
30 // IPC::Msg.routing_id.
31 static const int32 kRouteId = 200;
32 // Id used to identify the capture session between renderer and
33 // video_capture_host.
34 static const int kDeviceId = 1;
35 // Id of a video capture device
36 static const media::VideoCaptureSessionId kTestFakeDeviceId =
37 media_stream::VideoCaptureManager::kStartOpenSessionId;
38
39 // Define to enable test where video is dumped to file.
40 // #define DUMP_VIDEO
41
42 // Define to use a real video capture device.
43 // #define TEST_REAL_CAPTURE_DEVICE
44
45 // Simple class used for dumping video to a file. This can be used for
46 // verifying the output.
47 class DumpVideo {
48 public:
49 DumpVideo() : expected_size_(0) {}
50 void StartDump(int widht, int height) {
scherkus (not reviewing) 2011/05/23 05:05:09 s/widht/width
Per K 2011/05/23 12:05:47 Done.
51 // Create an FilePath that works on all platforms. There should
52 // be a better way.
53 FilePath file_name =
54 FilePath::FromWStringHack(StringPrintf(L"dump_w%d_h%d.yuv", widht,
55 height));
56 file_.reset(file_util::OpenFile(file_name, "wb"));
57 expected_size_ = widht * height * 3 / 2;
58 }
59 void NewVideoFrame(const void* buffer) {
60 if (file_.get() != NULL) {
61 fwrite(buffer, expected_size_, 1, file_.get());
62 }
63 }
64
65 private:
66 file_util::ScopedFILE file_;
67 int expected_size_;
68 };
69
70 class MockVideoCaptureHost : public VideoCaptureHost {
71 public:
72 MockVideoCaptureHost() : return_buffers_(false), dump_video_(false) {}
73 virtual ~MockVideoCaptureHost() {}
74
75 // A list of mock methods.
76 MOCK_METHOD3(OnBufferFilled,
77 void(int routing_id, int device_id, TransportDIB::Handle));
78 MOCK_METHOD3(OnStateChanged,
79 void(int routing_id, int device_id,
80 media::VideoCapture::State state));
81 MOCK_METHOD2(OnDeviceInfo, void(int routing_id, int device_id));
82
83 // Use class DumpVideo to write I420 video to file.
84 void SetDumpVideo(bool enable) {
85 dump_video_ = enable;
86 }
87 void SetReturnReceviedDibs(bool enable) {
88 return_buffers_ = enable;
89 }
90
91 // Return Dibs we currently have received.
92 void ReturnReceivedDibs(int device_id) {
93 TransportDIB::Handle handle = GetReceivedDib();
94 while (handle != 0) {
95 IPC::Message msg;
96 msg.set_routing_id(kRouteId);
97 this->OnReceiveEmptyBuffer(msg, device_id, handle);
98 handle = GetReceivedDib();
99 }
100 }
101 TransportDIB::Handle GetReceivedDib() {
102 if (filled_dib_.empty())
103 return 0;
104 TransportDIB::Handle h = filled_dib_.front();
105 filled_dib_.pop_front();
106
107 return h;
108 }
109
110 private:
111 // This method is used to dispatch IPC messages to the renderer. We intercept
112 // these messages here and dispatch to our mock methods to verify the
113 // conversation between this object and the renderer.
114 virtual bool Send(IPC::Message* message) {
115 CHECK(message);
116
117 // In this method we dispatch the messages to the according handlers as if
118 // we are the renderer.
119 bool handled = true;
120 IPC_BEGIN_MESSAGE_MAP(MockVideoCaptureHost, *message)
121 IPC_MESSAGE_HANDLER(VideoCaptureMsg_BufferReady, OnBufferFilled)
122 IPC_MESSAGE_HANDLER(VideoCaptureMsg_StateChanged, OnStateChanged)
123 IPC_MESSAGE_HANDLER(VideoCaptureMsg_DeviceInfo, OnDeviceInfo)
124 IPC_MESSAGE_UNHANDLED(handled = false)
125 IPC_END_MESSAGE_MAP()
126 EXPECT_TRUE(handled);
127
128 delete message;
129 return true;
130 }
131
132 // These handler methods do minimal things and delegate to the mock methods.
133 void OnBufferFilled(const IPC::Message& msg, int device_id,
134 TransportDIB::Handle dib_handle) {
135 if (dump_video_) {
136 TransportDIB* dib = TransportDIB::Map(dib_handle);
137 ASSERT_TRUE(dib != NULL);
138 dumper_.NewVideoFrame(dib->memory());
139 }
140
141 OnBufferFilled(msg.routing_id(), device_id, dib_handle);
142 if (return_buffers_) {
143 VideoCaptureHost::OnReceiveEmptyBuffer(msg, device_id, dib_handle);
144 } else {
145 filled_dib_.push_back(dib_handle);
146 }
147 }
148
149 void OnStateChanged(const IPC::Message& msg, int device_id,
150 media::VideoCapture::State state) {
151 OnStateChanged(msg.routing_id(), device_id, state);
152 }
153
154 void OnDeviceInfo(const IPC::Message& msg, int device_id,
155 media::VideoCaptureParams params) {
156 if (dump_video_) {
157 dumper_.StartDump(params.width, params.height);
scherkus (not reviewing) 2011/05/23 05:05:09 de-indent by 2 spaces
Per K 2011/05/23 12:05:47 Done.
158 }
159 OnDeviceInfo(msg.routing_id(), device_id);
160 }
161
162 std::list<TransportDIB::Handle> filled_dib_;
163 bool return_buffers_;
164 bool dump_video_;
165 DumpVideo dumper_;
166 };
167
168 ACTION_P(ExitMessageLoop, message_loop) {
169 message_loop->PostTask(FROM_HERE, new MessageLoop::QuitTask());
170 }
171
172 class VideoCaptureHostTest : public testing::Test {
173 public:
174 VideoCaptureHostTest() {}
175
176 protected:
177 virtual void SetUp() {
178 // Setup the VideoCaptureManager to use fake video capture device.
179 #ifndef TEST_REAL_CAPTURE_DEVICE
scherkus (not reviewing) 2011/05/23 05:05:09 nit: don't indent preprocessor statements
Per K 2011/05/23 12:05:47 Done.
180 media_stream::VideoCaptureManager* manager =
181 media_stream::VideoCaptureManager::Get();
182 manager->UseFakeDevice();
183 #endif
184 // Create a message loop so VideoCaptureHostTest can use it.
185 message_loop_.reset(new MessageLoop(MessageLoop::TYPE_IO));
186 io_thread_.reset(new BrowserThread(BrowserThread::IO, message_loop_.get()));
187 host_ = new MockVideoCaptureHost();
188
189 // Simulate IPC channel connected.
190 host_->OnChannelConnected(base::GetCurrentProcId());
191 }
192
193 virtual void TearDown() {
194 // Verifies and removes the expectations on host_ and
195 // returns true iff successful.
196 Mock::VerifyAndClearExpectations(host_);
197
198 EXPECT_CALL(*host_, OnStateChanged(kRouteId, kDeviceId,
199 media::VideoCapture::kStopped))
200 .Times(AnyNumber());
scherkus (not reviewing) 2011/05/23 05:05:09 indentation
Per K 2011/05/23 12:05:47 Done.
201
202 // Simulate closing the IPC channel.
203 host_->OnChannelClosing();
204
205 // Release the reference to the mock object. The object will be destructed
206 // on message_loop_.
207 host_ = NULL;
208
209 // We need to continue running message_loop_ to complete all destructions.
210 SyncWithVideoCaptureManagerThread();
211
212 io_thread_.reset();
213 }
214
215 // Called on the VideoCaptureManager thread.
216 static void PostQuitMessageLoop(MessageLoop* message_loop) {
217 message_loop->PostTask(FROM_HERE, new MessageLoop::QuitTask());
218 }
219
220 // Called on the main thread.
221 static void PostQuitOnVideoCaptureManagerThread(MessageLoop* message_loop) {
222 media_stream::VideoCaptureManager::Get()->GetMessageLoop()->PostTask(
223 FROM_HERE, NewRunnableFunction(&PostQuitMessageLoop, message_loop));
224 }
225
226 // SyncWithVideoCaptureManagerThread() waits until all pending tasks on the
227 // video_capture_manager thread are executed while also processing pending
228 // task in message_loop_ on the current thread. It is used to synchronize
229 // with the video capture manager thread when we are stopping a video
230 // capture device.
231 void SyncWithVideoCaptureManagerThread() {
232 message_loop_->PostTask(
233 FROM_HERE, NewRunnableFunction(&PostQuitOnVideoCaptureManagerThread,
scherkus (not reviewing) 2011/05/23 05:05:09 I think you can replace this by calling message_lo
Per K 2011/05/23 12:05:47 Please see below.
234 message_loop_.get()));
235 message_loop_->Run();
236 }
237
238 void StartCapture() {
239 InSequence s;
240 // 1. First - get info about the new resolution
241 EXPECT_CALL(*host_, OnDeviceInfo(kRouteId, kDeviceId));
242
243 // 2. Change state to started
244 EXPECT_CALL(*host_, OnStateChanged(kRouteId, kDeviceId,
245 media::VideoCapture::kStarted));
246
247 // 3. First filled buffer will arrive.
248 EXPECT_CALL(*host_, OnBufferFilled(kRouteId, kDeviceId, _))
249 .Times(AnyNumber())
scherkus (not reviewing) 2011/05/23 05:05:09 indentation (should be 4 spaces)
Per K 2011/05/23 12:05:47 Done.
250 .WillOnce(ExitMessageLoop(message_loop_.get()));
scherkus (not reviewing) 2011/05/23 05:05:09 you usually don't need to do this kind of stuff an
Per K 2011/05/23 12:05:47 Video Capture uses 3 different threads : 1. Browse
scherkus (not reviewing) 2011/05/26 05:27:35 Hrmm... if you design your classes to accept messa
251
252 IPC::Message msg;
253 msg.set_routing_id(kRouteId);
254
255 media::VideoCaptureParams params;
256 params.width = 352;
257 params.height = 288;
258 params.frame_per_second = 30;
259 params.session_id = kTestFakeDeviceId;
260 host_->OnStartCapture(msg, kDeviceId, params);
261 message_loop_->Run();
262 }
263
264 void CaptureAndDumpVideo(int width, int heigt, int frame_rate) {
265 InSequence s;
266 // 1. First - get info about the new resolution
267 EXPECT_CALL(*host_, OnDeviceInfo(kRouteId, kDeviceId));
268
269 // 2. Change state to started
270 EXPECT_CALL(*host_, OnStateChanged(kRouteId, kDeviceId,
271 media::VideoCapture::kStarted));
272
273 // 3. First filled buffer will arrive.
274 EXPECT_CALL(*host_, OnBufferFilled(kRouteId, kDeviceId, _))
275 .Times(AnyNumber())
scherkus (not reviewing) 2011/05/23 05:05:09 indentation (should be 4 spaces)
Per K 2011/05/23 12:05:47 Done.
276 .WillOnce(ExitMessageLoop(message_loop_.get()));
scherkus (not reviewing) 2011/05/23 05:05:09 ditto w/ RunAllPending()
Per K 2011/05/23 12:05:47 please see comment above.
277
278 IPC::Message msg;
279 msg.set_routing_id(kRouteId);
280
281 media::VideoCaptureParams params;
282 params.width = width;
283 params.height = heigt;
284 params.frame_per_second = frame_rate;
285 params.session_id = kTestFakeDeviceId;
286 host_->SetDumpVideo(true);
287 host_->OnStartCapture(msg, kDeviceId, params);
288 message_loop_->Run();
289 }
290
291 void StopCapture() {
292 EXPECT_CALL(*host_, OnStateChanged(kRouteId, kDeviceId,
293 media::VideoCapture::kStopped))
294 .Times(AtLeast(1));
scherkus (not reviewing) 2011/05/23 05:05:09 indentation (should be 4 spaces)
Per K 2011/05/23 12:05:47 Done.
295
296 IPC::Message msg;
297 msg.set_routing_id(kRouteId);
298 host_->OnStopCapture(msg, kDeviceId);
299 host_->SetReturnReceviedDibs(true);
300 host_->ReturnReceivedDibs(kDeviceId);
301
302 SyncWithVideoCaptureManagerThread();
303 host_->SetReturnReceviedDibs(false);
304 // Expect the VideoCaptureDevice has been stopped
305 EXPECT_EQ(0u, host_->entries_.size());
306 }
307
308 void NotifyPacketReady() {
309 EXPECT_CALL(*host_, OnBufferFilled(kRouteId, kDeviceId, _))
310 .Times(AnyNumber())
scherkus (not reviewing) 2011/05/23 05:05:09 indentation (should be 4 spaces)
Per K 2011/05/23 12:05:47 Done.
311 .WillOnce(ExitMessageLoop(message_loop_.get()))
scherkus (not reviewing) 2011/05/23 05:05:09 ditto w/ RunAllPending()
Per K 2011/05/23 12:05:47 please see comment above.
312 .RetiresOnSaturation();
313 message_loop_->Run();
314 }
315
316 void ReturnReceivedPackets() {
317 host_->ReturnReceivedDibs(kDeviceId);
318 }
319
320 void SimulateError() {
321 // Expect a change state to error state sent through IPC.
322 EXPECT_CALL(*host_, OnStateChanged(kRouteId, kDeviceId,
323 media::VideoCapture::kError))
324 .Times(1);
325 VideoCaptureController::ControllerId id(kRouteId, kDeviceId);
326 host_->OnError(id);
327 SyncWithVideoCaptureManagerThread();
328 }
329
330 scoped_refptr<MockVideoCaptureHost> host_;
331 private:
332 scoped_ptr<MessageLoop> message_loop_;
333 scoped_ptr<BrowserThread> io_thread_;
334
335 DISALLOW_COPY_AND_ASSIGN(VideoCaptureHostTest);
336 };
337
338 TEST_F(VideoCaptureHostTest, StartCapture) {
339 StartCapture();
340 }
341
342 TEST_F(VideoCaptureHostTest, StartCapturePlayStop) {
343 StartCapture();
344 NotifyPacketReady();
345 NotifyPacketReady();
346 ReturnReceivedPackets();
347 StopCapture();
348 }
349
350 TEST_F(VideoCaptureHostTest, StartCaptureErrorStop) {
351 StartCapture();
352 SimulateError();
353 StopCapture();
354 }
355
356 TEST_F(VideoCaptureHostTest, StartCaptureError) {
357 EXPECT_CALL(*host_, OnStateChanged(kRouteId, kDeviceId,
358 media::VideoCapture::kStopped))
359 .Times(0);
360 StartCapture();
361 NotifyPacketReady();
362 SimulateError();
363 base::PlatformThread::Sleep(200);
364 }
365
366 #ifdef DUMP_VIDEO
367 TEST_F(VideoCaptureHostTest, CaptureAndDumpVideoVga) {
368 CaptureAndDumpVideo(640, 480, 30);
369 }
370 TEST_F(VideoCaptureHostTest, CaptureAndDump720P) {
371 CaptureAndDumpVideo(1280, 720, 30);
372 }
373 #endif
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698