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.
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"
30 using ::testing::AtLeast
;
31 using ::testing::AnyNumber
;
32 using ::testing::DoAll
;
33 using ::testing::InSequence
;
34 using ::testing::Mock
;
35 using ::testing::Return
;
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.
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.
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());
70 file_util::ScopedFILE file_
;
74 class MockVideoCaptureHost
: public VideoCaptureHost
{
76 MockVideoCaptureHost(MediaStreamManager
* manager
)
77 : VideoCaptureHost(manager
),
78 return_buffers_(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
) {
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();
103 this->OnReceiveEmptyBuffer(device_id
, handle
);
104 handle
= GetReceivedDib();
108 int GetReceivedDib() {
109 if (filled_dib_
.empty())
111 std::map
<int, base::SharedMemory
*>::iterator it
= filled_dib_
.begin();
114 filled_dib_
.erase(it
);
120 virtual ~MockVideoCaptureHost() {
121 STLDeleteContainerPairSecondPointers(filled_dib_
.begin(),
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
{
131 // In this method we dispatch the messages to the according handlers as if
132 // we are the renderer.
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
);
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);
154 filled_dib_
[buffer_id
] = dib
;
157 void OnBufferFilledDispatch(int device_id
, int buffer_id
,
158 base::Time timestamp
) {
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
) {
178 dumper_
.StartDump(params
.width
, params
.height
);
180 OnDeviceInfo(device_id
);
183 std::map
<int, base::SharedMemory
*> filled_dib_
;
184 bool return_buffers_
;
189 ACTION_P2(ExitMessageLoop
, message_loop
, quit_closure
) {
190 message_loop
->PostTask(FROM_HERE
, quit_closure
);
193 class VideoCaptureHostTest
: public testing::Test
{
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();
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
))
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.
229 void StartCapture() {
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
;
251 params
.frame_per_second
= 30;
252 params
.session_id
= kTestFakeDeviceId
;
253 host_
->OnStartCapture(kDeviceId
, params
);
258 void CaptureAndDumpVideo(int width
, int heigt
, int frame_rate
) {
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
, _
, _
))
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
);
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
);
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();
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
);
319 // Wait for the error callback.
320 base::RunLoop().RunUntilIdle();
323 scoped_refptr
<MockVideoCaptureHost
> host_
;
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
) {
338 TEST_F(VideoCaptureHostTest
, StartCapturePlayStop
) {
342 ReturnReceivedPackets();
346 TEST_F(VideoCaptureHostTest
, StartCaptureErrorStop
) {
352 TEST_F(VideoCaptureHostTest
, StartCaptureError
) {
353 EXPECT_CALL(*host_
.get(),
354 OnStateChanged(kDeviceId
, VIDEO_CAPTURE_STATE_STOPPED
)).Times(0);
358 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(200));
362 TEST_F(VideoCaptureHostTest
, CaptureAndDumpVideoVga
) {
363 CaptureAndDumpVideo(640, 480, 30);
365 TEST_F(VideoCaptureHostTest
, CaptureAndDump720P
) {
366 CaptureAndDumpVideo(1280, 720, 30);
370 } // namespace content