Save errno for logging before potentially overwriting it.
[chromium-blink-merge.git] / content / browser / renderer_host / media / video_capture_host_unittest.cc
blob1d8c79a77370b9e0fc325f819de30a75680107d1
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
3 // found in the LICENSE file.
5 #include <map>
6 #include <string>
8 #include "base/bind.h"
9 #include "base/file_util.h"
10 #include "base/memory/scoped_ptr.h"
11 #include "base/message_loop.h"
12 #include "base/process_util.h"
13 #include "base/run_loop.h"
14 #include "base/stl_util.h"
15 #include "base/strings/stringprintf.h"
16 #include "content/browser/browser_thread_impl.h"
17 #include "content/browser/renderer_host/media/media_stream_manager.h"
18 #include "content/browser/renderer_host/media/video_capture_host.h"
19 #include "content/browser/renderer_host/media/video_capture_manager.h"
20 #include "content/common/media/video_capture_messages.h"
21 #include "content/public/test/mock_resource_context.h"
22 #include "content/public/test/test_browser_thread_bundle.h"
23 #include "media/audio/audio_manager.h"
24 #include "media/video/capture/video_capture_types.h"
25 #include "net/url_request/url_request_context.h"
26 #include "testing/gmock/include/gmock/gmock.h"
27 #include "testing/gtest/include/gtest/gtest.h"
29 using ::testing::_;
30 using ::testing::AtLeast;
31 using ::testing::AnyNumber;
32 using ::testing::DoAll;
33 using ::testing::InSequence;
34 using ::testing::Mock;
35 using ::testing::Return;
37 namespace content {
39 // Id used to identify the capture session between renderer and
40 // video_capture_host.
41 static const int kDeviceId = 1;
42 // Id of a video capture device
43 static const media::VideoCaptureSessionId kTestFakeDeviceId =
44 VideoCaptureManager::kStartOpenSessionId;
46 // Define to enable test where video is dumped to file.
47 // #define DUMP_VIDEO
49 // Define to use a real video capture device.
50 // #define TEST_REAL_CAPTURE_DEVICE
52 // Simple class used for dumping video to a file. This can be used for
53 // verifying the output.
54 class DumpVideo {
55 public:
56 DumpVideo() : expected_size_(0) {}
57 void StartDump(int width, int height) {
58 base::FilePath file_name = base::FilePath(base::StringPrintf(
59 FILE_PATH_LITERAL("dump_w%d_h%d.yuv"), width, height));
60 file_.reset(file_util::OpenFile(file_name, "wb"));
61 expected_size_ = width * height * 3 / 2;
63 void NewVideoFrame(const void* buffer) {
64 if (file_.get() != NULL) {
65 fwrite(buffer, expected_size_, 1, file_.get());
69 private:
70 file_util::ScopedFILE file_;
71 int expected_size_;
74 class MockVideoCaptureHost : public VideoCaptureHost {
75 public:
76 MockVideoCaptureHost(MediaStreamManager* manager)
77 : VideoCaptureHost(manager),
78 return_buffers_(false),
79 dump_video_(false) {}
81 // A list of mock methods.
82 MOCK_METHOD4(OnNewBufferCreated,
83 void(int device_id, base::SharedMemoryHandle handle,
84 int length, int buffer_id));
85 MOCK_METHOD3(OnBufferFilled,
86 void(int device_id, int buffer_id, base::Time timestamp));
87 MOCK_METHOD2(OnStateChanged, void(int device_id, VideoCaptureState state));
88 MOCK_METHOD1(OnDeviceInfo, void(int device_id));
90 // Use class DumpVideo to write I420 video to file.
91 void SetDumpVideo(bool enable) {
92 dump_video_ = enable;
95 void SetReturnReceviedDibs(bool enable) {
96 return_buffers_ = enable;
99 // Return Dibs we currently have received.
100 void ReturnReceivedDibs(int device_id) {
101 int handle = GetReceivedDib();
102 while (handle) {
103 this->OnReceiveEmptyBuffer(device_id, handle);
104 handle = GetReceivedDib();
108 int GetReceivedDib() {
109 if (filled_dib_.empty())
110 return 0;
111 std::map<int, base::SharedMemory*>::iterator it = filled_dib_.begin();
112 int h = it->first;
113 delete it->second;
114 filled_dib_.erase(it);
116 return h;
119 private:
120 virtual ~MockVideoCaptureHost() {
121 STLDeleteContainerPairSecondPointers(filled_dib_.begin(),
122 filled_dib_.end());
125 // This method is used to dispatch IPC messages to the renderer. We intercept
126 // these messages here and dispatch to our mock methods to verify the
127 // conversation between this object and the renderer.
128 virtual bool Send(IPC::Message* message) OVERRIDE {
129 CHECK(message);
131 // In this method we dispatch the messages to the according handlers as if
132 // we are the renderer.
133 bool handled = true;
134 IPC_BEGIN_MESSAGE_MAP(MockVideoCaptureHost, *message)
135 IPC_MESSAGE_HANDLER(VideoCaptureMsg_NewBuffer, OnNewBufferCreatedDispatch)
136 IPC_MESSAGE_HANDLER(VideoCaptureMsg_BufferReady, OnBufferFilledDispatch)
137 IPC_MESSAGE_HANDLER(VideoCaptureMsg_StateChanged, OnStateChangedDispatch)
138 IPC_MESSAGE_HANDLER(VideoCaptureMsg_DeviceInfo, OnDeviceInfoDispatch)
139 IPC_MESSAGE_UNHANDLED(handled = false)
140 IPC_END_MESSAGE_MAP()
141 EXPECT_TRUE(handled);
143 delete message;
144 return true;
147 // These handler methods do minimal things and delegate to the mock methods.
148 void OnNewBufferCreatedDispatch(int device_id,
149 base::SharedMemoryHandle handle,
150 int length, int buffer_id) {
151 OnNewBufferCreated(device_id, handle, length, buffer_id);
152 base::SharedMemory* dib = new base::SharedMemory(handle, false);
153 dib->Map(length);
154 filled_dib_[buffer_id] = dib;
157 void OnBufferFilledDispatch(int device_id, int buffer_id,
158 base::Time timestamp) {
159 if (dump_video_) {
160 base::SharedMemory* dib = filled_dib_[buffer_id];
161 ASSERT_TRUE(dib != NULL);
162 dumper_.NewVideoFrame(dib->memory());
165 OnBufferFilled(device_id, buffer_id, timestamp);
166 if (return_buffers_) {
167 VideoCaptureHost::OnReceiveEmptyBuffer(device_id, buffer_id);
171 void OnStateChangedDispatch(int device_id, VideoCaptureState state) {
172 OnStateChanged(device_id, state);
175 void OnDeviceInfoDispatch(int device_id,
176 media::VideoCaptureParams params) {
177 if (dump_video_) {
178 dumper_.StartDump(params.width, params.height);
180 OnDeviceInfo(device_id);
183 std::map<int, base::SharedMemory*> filled_dib_;
184 bool return_buffers_;
185 bool dump_video_;
186 DumpVideo dumper_;
189 ACTION_P2(ExitMessageLoop, message_loop, quit_closure) {
190 message_loop->PostTask(FROM_HERE, quit_closure);
193 class VideoCaptureHostTest : public testing::Test {
194 public:
195 VideoCaptureHostTest()
196 : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP),
197 message_loop_(base::MessageLoopProxy::current()) {
198 // Create our own MediaStreamManager.
199 audio_manager_.reset(media::AudioManager::Create());
200 media_stream_manager_.reset(new MediaStreamManager(audio_manager_.get()));
201 #ifndef TEST_REAL_CAPTURE_DEVICE
202 media_stream_manager_->UseFakeDevice();
203 #endif
205 host_ = new MockVideoCaptureHost(media_stream_manager_.get());
207 // Simulate IPC channel connected.
208 host_->OnChannelConnected(base::GetCurrentProcId());
211 virtual ~VideoCaptureHostTest() {
212 // Verifies and removes the expectations on host_ and
213 // returns true iff successful.
214 Mock::VerifyAndClearExpectations(host_.get());
216 EXPECT_CALL(*host_.get(),
217 OnStateChanged(kDeviceId, VIDEO_CAPTURE_STATE_STOPPED))
218 .Times(AnyNumber());
220 // Simulate closing the IPC channel.
221 host_->OnChannelClosing();
223 // Release the reference to the mock object. The object will be destructed
224 // on the current message loop.
225 host_ = NULL;
228 protected:
229 void StartCapture() {
230 InSequence s;
231 // 1. First - get info about the new resolution
232 EXPECT_CALL(*host_.get(), OnDeviceInfo(kDeviceId));
234 // 2. Change state to started
235 EXPECT_CALL(*host_.get(),
236 OnStateChanged(kDeviceId, VIDEO_CAPTURE_STATE_STARTED));
238 // 3. Newly created buffers will arrive.
239 EXPECT_CALL(*host_.get(), OnNewBufferCreated(kDeviceId, _, _, _))
240 .Times(AnyNumber()).WillRepeatedly(Return());
242 // 4. First filled buffer will arrive.
243 base::RunLoop run_loop;
244 EXPECT_CALL(*host_.get(), OnBufferFilled(kDeviceId, _, _))
245 .Times(AnyNumber()).WillOnce(ExitMessageLoop(
246 message_loop_, run_loop.QuitClosure()));
248 media::VideoCaptureParams params;
249 params.width = 352;
250 params.height = 288;
251 params.frame_per_second = 30;
252 params.session_id = kTestFakeDeviceId;
253 host_->OnStartCapture(kDeviceId, params);
254 run_loop.Run();
257 #ifdef DUMP_VIDEO
258 void CaptureAndDumpVideo(int width, int heigt, int frame_rate) {
259 InSequence s;
260 // 1. First - get info about the new resolution
261 EXPECT_CALL(*host_, OnDeviceInfo(kDeviceId));
263 // 2. Change state to started
264 EXPECT_CALL(*host_, OnStateChanged(kDeviceId, VIDEO_CAPTURE_STATE_STARTED));
266 // 3. First filled buffer will arrive.
267 base::RunLoop run_loop;
268 EXPECT_CALL(*host_, OnBufferFilled(kDeviceId, _, _))
269 .Times(AnyNumber())
270 .WillOnce(ExitMessageLoop(message_loop_, run_loop.QuitClosure()));
272 media::VideoCaptureParams params;
273 params.width = width;
274 params.height = heigt;
275 params.frame_per_second = frame_rate;
276 params.session_id = kTestFakeDeviceId;
277 host_->SetDumpVideo(true);
278 host_->OnStartCapture(kDeviceId, params);
279 run_loop.Run();
281 #endif
283 void StopCapture() {
284 base::RunLoop run_loop;
285 EXPECT_CALL(*host_.get(),
286 OnStateChanged(kDeviceId, VIDEO_CAPTURE_STATE_STOPPED))
287 .WillOnce(ExitMessageLoop(message_loop_, run_loop.QuitClosure()));
289 host_->OnStopCapture(kDeviceId);
290 host_->SetReturnReceviedDibs(true);
291 host_->ReturnReceivedDibs(kDeviceId);
293 run_loop.Run();
295 host_->SetReturnReceviedDibs(false);
296 // Expect the VideoCaptureDevice has been stopped
297 EXPECT_EQ(0u, host_->entries_.size());
300 void NotifyPacketReady() {
301 base::RunLoop run_loop;
302 EXPECT_CALL(*host_.get(), OnBufferFilled(kDeviceId, _, _))
303 .Times(AnyNumber()).WillOnce(ExitMessageLoop(
304 message_loop_, run_loop.QuitClosure()))
305 .RetiresOnSaturation();
306 run_loop.Run();
309 void ReturnReceivedPackets() {
310 host_->ReturnReceivedDibs(kDeviceId);
313 void SimulateError() {
314 // Expect a change state to error state sent through IPC.
315 EXPECT_CALL(*host_.get(),
316 OnStateChanged(kDeviceId, VIDEO_CAPTURE_STATE_ERROR)).Times(1);
317 VideoCaptureControllerID id(kDeviceId);
318 host_->OnError(id);
319 // Wait for the error callback.
320 base::RunLoop().RunUntilIdle();
323 scoped_refptr<MockVideoCaptureHost> host_;
325 private:
326 scoped_ptr<media::AudioManager> audio_manager_;
327 scoped_ptr<MediaStreamManager> media_stream_manager_;
328 content::TestBrowserThreadBundle thread_bundle_;
329 scoped_refptr<base::MessageLoopProxy> message_loop_;
331 DISALLOW_COPY_AND_ASSIGN(VideoCaptureHostTest);
334 TEST_F(VideoCaptureHostTest, StartCapture) {
335 StartCapture();
338 TEST_F(VideoCaptureHostTest, StartCapturePlayStop) {
339 StartCapture();
340 NotifyPacketReady();
341 NotifyPacketReady();
342 ReturnReceivedPackets();
343 StopCapture();
346 TEST_F(VideoCaptureHostTest, StartCaptureErrorStop) {
347 StartCapture();
348 SimulateError();
349 StopCapture();
352 TEST_F(VideoCaptureHostTest, StartCaptureError) {
353 EXPECT_CALL(*host_.get(),
354 OnStateChanged(kDeviceId, VIDEO_CAPTURE_STATE_STOPPED)).Times(0);
355 StartCapture();
356 NotifyPacketReady();
357 SimulateError();
358 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(200));
361 #ifdef DUMP_VIDEO
362 TEST_F(VideoCaptureHostTest, CaptureAndDumpVideoVga) {
363 CaptureAndDumpVideo(640, 480, 30);
365 TEST_F(VideoCaptureHostTest, CaptureAndDump720P) {
366 CaptureAndDumpVideo(1280, 720, 30);
368 #endif
370 } // namespace content