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/message_loop.h"
12 #include "base/run_loop.h"
13 #include "base/stl_util.h"
14 #include "base/strings/stringprintf.h"
15 #include "content/browser/browser_thread_impl.h"
16 #include "content/browser/renderer_host/media/media_stream_manager.h"
17 #include "content/browser/renderer_host/media/media_stream_requester.h"
18 #include "content/browser/renderer_host/media/media_stream_ui_proxy.h"
19 #include "content/browser/renderer_host/media/video_capture_host.h"
20 #include "content/browser/renderer_host/media/video_capture_manager.h"
21 #include "content/common/media/video_capture_messages.h"
22 #include "content/public/test/mock_resource_context.h"
23 #include "content/public/test/test_browser_context.h"
24 #include "content/public/test/test_browser_thread_bundle.h"
25 #include "content/test/test_content_browser_client.h"
26 #include "media/audio/audio_manager.h"
27 #include "media/base/video_frame.h"
28 #include "media/video/capture/video_capture_types.h"
29 #include "net/url_request/url_request_context.h"
30 #include "testing/gmock/include/gmock/gmock.h"
31 #include "testing/gtest/include/gtest/gtest.h"
34 using ::testing::AtLeast;
35 using ::testing::AnyNumber;
36 using ::testing::DoAll;
37 using ::testing::InSequence;
38 using ::testing::Mock;
39 using ::testing::Return;
40 using ::testing::SaveArg;
41 using ::testing::StrictMock;
45 // Id used to identify the capture session between renderer and
46 // video_capture_host. This is an arbitrary value.
47 static const int kDeviceId = 555;
49 // Define to enable test where video is dumped to file.
52 // Define to use a real video capture device.
53 // #define TEST_REAL_CAPTURE_DEVICE
55 // Simple class used for dumping video to a file. This can be used for
56 // verifying the output.
59 DumpVideo() : expected_size_(0) {}
60 void StartDump(int width, int height) {
61 base::FilePath file_name = base::FilePath(base::StringPrintf(
62 FILE_PATH_LITERAL("dump_w%d_h%d.yuv"), width, height));
63 file_.reset(base::OpenFile(file_name, "wb"));
64 expected_size_ = media::VideoFrame::AllocationSize(
65 media::VideoFrame::I420, gfx::Size(width, height));
67 void NewVideoFrame(const void* buffer) {
68 if (file_.get() != NULL) {
69 ASSERT_EQ(1U, fwrite(buffer, expected_size_, 1, file_.get()));
74 file_util::ScopedFILE file_;
78 class MockMediaStreamRequester : public MediaStreamRequester {
80 MockMediaStreamRequester() {}
81 virtual ~MockMediaStreamRequester() {}
83 // MediaStreamRequester implementation.
84 MOCK_METHOD5(StreamGenerated,
85 void(int render_view_id,
87 const std::string& label,
88 const StreamDeviceInfoArray& audio_devices,
89 const StreamDeviceInfoArray& video_devices));
90 MOCK_METHOD2(StreamGenerationFailed, void(int render_view_id,
91 int page_request_id));
92 MOCK_METHOD3(DeviceStopped, void(int render_view_id,
93 const std::string& label,
94 const StreamDeviceInfo& device));
95 MOCK_METHOD4(DevicesEnumerated, void(int render_view_id,
97 const std::string& label,
98 const StreamDeviceInfoArray& devices));
99 MOCK_METHOD4(DeviceOpened, void(int render_view_id,
101 const std::string& label,
102 const StreamDeviceInfo& device_info));
105 DISALLOW_COPY_AND_ASSIGN(MockMediaStreamRequester);
108 class MockVideoCaptureHost : public VideoCaptureHost {
110 MockVideoCaptureHost(MediaStreamManager* manager)
111 : VideoCaptureHost(manager),
112 return_buffers_(false),
113 dump_video_(false) {}
115 // A list of mock methods.
116 MOCK_METHOD4(OnNewBufferCreated,
117 void(int device_id, base::SharedMemoryHandle handle,
118 int length, int buffer_id));
119 MOCK_METHOD2(OnBufferFreed,
120 void(int device_id, int buffer_id));
121 MOCK_METHOD4(OnBufferFilled,
124 base::TimeTicks timestamp,
125 const media::VideoCaptureFormat& format));
126 MOCK_METHOD2(OnStateChanged, void(int device_id, VideoCaptureState state));
128 // Use class DumpVideo to write I420 video to file.
129 void SetDumpVideo(bool enable) {
130 dump_video_ = enable;
133 void SetReturnReceivedDibs(bool enable) {
134 return_buffers_ = enable;
137 // Return Dibs we currently have received.
138 void ReturnReceivedDibs(int device_id) {
139 int handle = GetReceivedDib();
141 this->OnReceiveEmptyBuffer(device_id, handle);
142 handle = GetReceivedDib();
146 int GetReceivedDib() {
147 if (filled_dib_.empty())
149 std::map<int, base::SharedMemory*>::iterator it = filled_dib_.begin();
152 filled_dib_.erase(it);
158 virtual ~MockVideoCaptureHost() {
159 STLDeleteContainerPairSecondPointers(filled_dib_.begin(),
163 // This method is used to dispatch IPC messages to the renderer. We intercept
164 // these messages here and dispatch to our mock methods to verify the
165 // conversation between this object and the renderer.
166 virtual bool Send(IPC::Message* message) OVERRIDE {
169 // In this method we dispatch the messages to the according handlers as if
170 // we are the renderer.
172 IPC_BEGIN_MESSAGE_MAP(MockVideoCaptureHost, *message)
173 IPC_MESSAGE_HANDLER(VideoCaptureMsg_NewBuffer, OnNewBufferCreatedDispatch)
174 IPC_MESSAGE_HANDLER(VideoCaptureMsg_FreeBuffer, OnBufferFreedDispatch)
175 IPC_MESSAGE_HANDLER(VideoCaptureMsg_BufferReady, OnBufferFilledDispatch)
176 IPC_MESSAGE_HANDLER(VideoCaptureMsg_StateChanged, OnStateChangedDispatch)
177 IPC_MESSAGE_UNHANDLED(handled = false)
178 IPC_END_MESSAGE_MAP()
179 EXPECT_TRUE(handled);
185 // These handler methods do minimal things and delegate to the mock methods.
186 void OnNewBufferCreatedDispatch(int device_id,
187 base::SharedMemoryHandle handle,
190 OnNewBufferCreated(device_id, handle, length, buffer_id);
191 base::SharedMemory* dib = new base::SharedMemory(handle, false);
193 filled_dib_[buffer_id] = dib;
196 void OnBufferFreedDispatch(int device_id, int buffer_id) {
197 OnBufferFreed(device_id, buffer_id);
199 std::map<int, base::SharedMemory*>::iterator it =
200 filled_dib_.find(buffer_id);
201 ASSERT_TRUE(it != filled_dib_.end());
203 filled_dib_.erase(it);
206 void OnBufferFilledDispatch(int device_id,
208 base::TimeTicks timestamp,
209 const media::VideoCaptureFormat& frame_format) {
210 base::SharedMemory* dib = filled_dib_[buffer_id];
211 ASSERT_TRUE(dib != NULL);
213 if (!format_.IsValid()) {
214 dumper_.StartDump(frame_format.frame_size.width(),
215 frame_format.frame_size.height());
216 format_ = frame_format;
218 ASSERT_EQ(format_.frame_size.width(), frame_format.frame_size.width())
219 << "Dump format does not handle variable resolution.";
220 ASSERT_EQ(format_.frame_size.height(), frame_format.frame_size.height())
221 << "Dump format does not handle variable resolution.";
222 dumper_.NewVideoFrame(dib->memory());
225 OnBufferFilled(device_id, buffer_id, timestamp, frame_format);
226 if (return_buffers_) {
227 VideoCaptureHost::OnReceiveEmptyBuffer(device_id, buffer_id);
231 void OnStateChangedDispatch(int device_id, VideoCaptureState state) {
232 OnStateChanged(device_id, state);
235 std::map<int, base::SharedMemory*> filled_dib_;
236 bool return_buffers_;
238 media::VideoCaptureFormat format_;
242 ACTION_P2(ExitMessageLoop, message_loop, quit_closure) {
243 message_loop->PostTask(FROM_HERE, quit_closure);
246 // This is an integration test of VideoCaptureHost in conjunction with
247 // MediaStreamManager, VideoCaptureManager, VideoCaptureController, and
248 // VideoCaptureDevice.
249 class VideoCaptureHostTest : public testing::Test {
251 VideoCaptureHostTest()
252 : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP),
253 message_loop_(base::MessageLoopProxy::current()),
254 opened_session_id_(kInvalidMediaCaptureSessionId) {}
256 virtual void SetUp() OVERRIDE {
257 SetBrowserClientForTesting(&browser_client_);
258 // Create our own MediaStreamManager.
259 audio_manager_.reset(media::AudioManager::CreateForTesting());
260 media_stream_manager_.reset(new MediaStreamManager(audio_manager_.get()));
261 #ifndef TEST_REAL_CAPTURE_DEVICE
262 media_stream_manager_->UseFakeDevice();
264 media_stream_manager_->UseFakeUI(scoped_ptr<FakeMediaStreamUIProxy>());
266 // Create a Host and connect it to a simulated IPC channel.
267 host_ = new MockVideoCaptureHost(media_stream_manager_.get());
268 host_->OnChannelConnected(base::GetCurrentProcId());
273 virtual void TearDown() OVERRIDE {
274 // Verifies and removes the expectations on host_ and
275 // returns true iff successful.
276 Mock::VerifyAndClearExpectations(host_.get());
277 EXPECT_EQ(0u, host_->entries_.size());
281 // Simulate closing the IPC channel.
282 host_->OnChannelClosing();
284 // Release the reference to the mock object. The object will be destructed
285 // on the current message loop.
290 const int render_process_id = 1;
291 const int render_view_id = 1;
292 const int page_request_id = 1;
293 const GURL security_origin("http://test.com");
295 ASSERT_TRUE(opened_device_label_.empty());
297 // Enumerate video devices.
298 StreamDeviceInfoArray devices;
300 base::RunLoop run_loop;
301 std::string label = media_stream_manager_->EnumerateDevices(
305 browser_context_.GetResourceContext()->GetMediaDeviceIDSalt(),
307 MEDIA_DEVICE_VIDEO_CAPTURE,
309 EXPECT_CALL(stream_requester_, DevicesEnumerated(render_view_id,
314 DoAll(ExitMessageLoop(message_loop_, run_loop.QuitClosure()),
315 SaveArg<3>(&devices)));
317 Mock::VerifyAndClearExpectations(&stream_requester_);
318 media_stream_manager_->CancelRequest(label);
320 ASSERT_FALSE(devices.empty());
321 ASSERT_EQ(StreamDeviceInfo::kNoId, devices[0].session_id);
323 // Open the first device.
325 base::RunLoop run_loop;
326 StreamDeviceInfo opened_device;
327 media_stream_manager_->OpenDevice(
331 browser_context_.GetResourceContext()->GetMediaDeviceIDSalt(),
333 devices[0].device.id,
334 MEDIA_DEVICE_VIDEO_CAPTURE,
336 EXPECT_CALL(stream_requester_, DeviceOpened(render_view_id,
341 DoAll(ExitMessageLoop(message_loop_, run_loop.QuitClosure()),
342 SaveArg<2>(&opened_device_label_),
343 SaveArg<3>(&opened_device)));
345 Mock::VerifyAndClearExpectations(&stream_requester_);
346 ASSERT_NE(StreamDeviceInfo::kNoId, opened_device.session_id);
347 opened_session_id_ = opened_device.session_id;
351 void CloseSession() {
352 if (opened_device_label_.empty())
354 media_stream_manager_->CancelRequest(opened_device_label_);
355 opened_device_label_.clear();
356 opened_session_id_ = kInvalidMediaCaptureSessionId;
360 void StartCapture() {
361 EXPECT_CALL(*host_, OnNewBufferCreated(kDeviceId, _, _, _))
362 .Times(AnyNumber()).WillRepeatedly(Return());
364 base::RunLoop run_loop;
365 EXPECT_CALL(*host_, OnBufferFilled(kDeviceId, _, _, _))
366 .Times(AnyNumber()).WillOnce(ExitMessageLoop(
367 message_loop_, run_loop.QuitClosure()));
369 media::VideoCaptureParams params;
370 params.requested_format = media::VideoCaptureFormat(
371 gfx::Size(352, 288), 30, media::PIXEL_FORMAT_I420);
372 host_->OnStartCapture(kDeviceId, opened_session_id_, params);
376 void StartStopCapture() {
377 // Quickly start and then stop capture, without giving much chance for
378 // asynchronous start operations to complete.
380 base::RunLoop run_loop;
381 EXPECT_CALL(*host_, OnStateChanged(kDeviceId, VIDEO_CAPTURE_STATE_STOPPED));
382 media::VideoCaptureParams params;
383 params.requested_format = media::VideoCaptureFormat(
384 gfx::Size(352, 288), 30, media::PIXEL_FORMAT_I420);
385 host_->OnStartCapture(kDeviceId, opened_session_id_, params);
386 host_->OnStopCapture(kDeviceId);
387 run_loop.RunUntilIdle();
391 void CaptureAndDumpVideo(int width, int height, int frame_rate) {
393 EXPECT_CALL(*host_.get(), OnNewBufferCreated(kDeviceId, _, _, _))
394 .Times(AnyNumber()).WillRepeatedly(Return());
396 base::RunLoop run_loop;
397 EXPECT_CALL(*host_, OnBufferFilled(kDeviceId, _, _, _))
399 .WillOnce(ExitMessageLoop(message_loop_, run_loop.QuitClosure()));
401 media::VideoCaptureParams params;
402 params.requested_format =
403 media::VideoCaptureFormat(gfx::Size(width, height), frame_rate);
404 host_->SetDumpVideo(true);
405 host_->OnStartCapture(kDeviceId, opened_session_id_, params);
411 base::RunLoop run_loop;
412 EXPECT_CALL(*host_, OnStateChanged(kDeviceId, VIDEO_CAPTURE_STATE_STOPPED))
413 .WillOnce(ExitMessageLoop(message_loop_, run_loop.QuitClosure()));
415 host_->OnStopCapture(kDeviceId);
416 host_->SetReturnReceivedDibs(true);
417 host_->ReturnReceivedDibs(kDeviceId);
421 host_->SetReturnReceivedDibs(false);
422 // Expect the VideoCaptureDevice has been stopped
423 EXPECT_EQ(0u, host_->entries_.size());
426 void NotifyPacketReady() {
427 base::RunLoop run_loop;
428 EXPECT_CALL(*host_, OnBufferFilled(kDeviceId, _, _, _))
429 .Times(AnyNumber()).WillOnce(ExitMessageLoop(
430 message_loop_, run_loop.QuitClosure()))
431 .RetiresOnSaturation();
435 void ReturnReceivedPackets() {
436 host_->ReturnReceivedDibs(kDeviceId);
439 void SimulateError() {
440 // Expect a change state to error state sent through IPC.
441 EXPECT_CALL(*host_, OnStateChanged(kDeviceId, VIDEO_CAPTURE_STATE_ERROR))
443 VideoCaptureControllerID id(kDeviceId);
445 // Wait for the error callback.
446 base::RunLoop().RunUntilIdle();
449 scoped_refptr<MockVideoCaptureHost> host_;
452 StrictMock<MockMediaStreamRequester> stream_requester_;
453 scoped_ptr<media::AudioManager> audio_manager_;
454 scoped_ptr<MediaStreamManager> media_stream_manager_;
455 content::TestBrowserThreadBundle thread_bundle_;
456 content::TestBrowserContext browser_context_;
457 content::TestContentBrowserClient browser_client_;
458 scoped_refptr<base::MessageLoopProxy> message_loop_;
459 int opened_session_id_;
460 std::string opened_device_label_;
462 DISALLOW_COPY_AND_ASSIGN(VideoCaptureHostTest);
465 TEST_F(VideoCaptureHostTest, CloseSessionWithoutStopping) {
468 // When the session is closed via the stream without stopping capture, the
469 // ENDED event is sent.
470 EXPECT_CALL(*host_, OnStateChanged(kDeviceId, VIDEO_CAPTURE_STATE_ENDED))
473 base::RunLoop().RunUntilIdle();
476 TEST_F(VideoCaptureHostTest, StopWhileStartPending) {
480 TEST_F(VideoCaptureHostTest, StartCapturePlayStop) {
484 ReturnReceivedPackets();
488 TEST_F(VideoCaptureHostTest, StartCaptureErrorStop) {
494 TEST_F(VideoCaptureHostTest, StartCaptureError) {
495 EXPECT_CALL(*host_, OnStateChanged(kDeviceId, VIDEO_CAPTURE_STATE_STOPPED))
500 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(200));
504 TEST_F(VideoCaptureHostTest, CaptureAndDumpVideoVga) {
505 CaptureAndDumpVideo(640, 480, 30);
507 TEST_F(VideoCaptureHostTest, CaptureAndDump720P) {
508 CaptureAndDumpVideo(1280, 720, 30);
512 } // namespace content