Ignore non-active fullscreen windows for shelf state.
[chromium-blink-merge.git] / content / browser / renderer_host / media / video_capture_host_unittest.cc
blob1c169a4cd5d1bd66f1c6715c1221727cb78a4efa
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/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"
33 using ::testing::_;
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;
43 namespace content {
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.
50 // #define DUMP_VIDEO
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.
57 class DumpVideo {
58 public:
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()));
73 private:
74 file_util::ScopedFILE file_;
75 int expected_size_;
78 class MockMediaStreamRequester : public MediaStreamRequester {
79 public:
80 MockMediaStreamRequester() {}
81 virtual ~MockMediaStreamRequester() {}
83 // MediaStreamRequester implementation.
84 MOCK_METHOD3(StreamGenerated,
85 void(const std::string& label,
86 const StreamDeviceInfoArray& audio_devices,
87 const StreamDeviceInfoArray& video_devices));
88 MOCK_METHOD1(StreamGenerationFailed, void(const std::string& label));
89 MOCK_METHOD3(DeviceStopped, void(int render_view_id,
90 const std::string& label,
91 const StreamDeviceInfo& device));
92 MOCK_METHOD2(DevicesEnumerated, void(const std::string& label,
93 const StreamDeviceInfoArray& devices));
94 MOCK_METHOD2(DeviceOpened, void(const std::string& label,
95 const StreamDeviceInfo& device_info));
97 private:
98 DISALLOW_COPY_AND_ASSIGN(MockMediaStreamRequester);
101 class MockVideoCaptureHost : public VideoCaptureHost {
102 public:
103 MockVideoCaptureHost(MediaStreamManager* manager)
104 : VideoCaptureHost(manager),
105 return_buffers_(false),
106 dump_video_(false) {}
108 // A list of mock methods.
109 MOCK_METHOD4(OnNewBufferCreated,
110 void(int device_id, base::SharedMemoryHandle handle,
111 int length, int buffer_id));
112 MOCK_METHOD2(OnBufferFreed,
113 void(int device_id, int buffer_id));
114 MOCK_METHOD4(OnBufferFilled,
115 void(int device_id, int buffer_id, base::Time timestamp,
116 const media::VideoCaptureFormat& format));
117 MOCK_METHOD2(OnStateChanged, void(int device_id, VideoCaptureState state));
119 // Use class DumpVideo to write I420 video to file.
120 void SetDumpVideo(bool enable) {
121 dump_video_ = enable;
124 void SetReturnReceivedDibs(bool enable) {
125 return_buffers_ = enable;
128 // Return Dibs we currently have received.
129 void ReturnReceivedDibs(int device_id) {
130 int handle = GetReceivedDib();
131 while (handle) {
132 this->OnReceiveEmptyBuffer(device_id, handle);
133 handle = GetReceivedDib();
137 int GetReceivedDib() {
138 if (filled_dib_.empty())
139 return 0;
140 std::map<int, base::SharedMemory*>::iterator it = filled_dib_.begin();
141 int h = it->first;
142 delete it->second;
143 filled_dib_.erase(it);
145 return h;
148 private:
149 virtual ~MockVideoCaptureHost() {
150 STLDeleteContainerPairSecondPointers(filled_dib_.begin(),
151 filled_dib_.end());
154 // This method is used to dispatch IPC messages to the renderer. We intercept
155 // these messages here and dispatch to our mock methods to verify the
156 // conversation between this object and the renderer.
157 virtual bool Send(IPC::Message* message) OVERRIDE {
158 CHECK(message);
160 // In this method we dispatch the messages to the according handlers as if
161 // we are the renderer.
162 bool handled = true;
163 IPC_BEGIN_MESSAGE_MAP(MockVideoCaptureHost, *message)
164 IPC_MESSAGE_HANDLER(VideoCaptureMsg_NewBuffer, OnNewBufferCreatedDispatch)
165 IPC_MESSAGE_HANDLER(VideoCaptureMsg_FreeBuffer, OnBufferFreedDispatch)
166 IPC_MESSAGE_HANDLER(VideoCaptureMsg_BufferReady, OnBufferFilledDispatch)
167 IPC_MESSAGE_HANDLER(VideoCaptureMsg_StateChanged, OnStateChangedDispatch)
168 IPC_MESSAGE_UNHANDLED(handled = false)
169 IPC_END_MESSAGE_MAP()
170 EXPECT_TRUE(handled);
172 delete message;
173 return true;
176 // These handler methods do minimal things and delegate to the mock methods.
177 void OnNewBufferCreatedDispatch(int device_id,
178 base::SharedMemoryHandle handle,
179 uint32 length,
180 int buffer_id) {
181 OnNewBufferCreated(device_id, handle, length, buffer_id);
182 base::SharedMemory* dib = new base::SharedMemory(handle, false);
183 dib->Map(length);
184 filled_dib_[buffer_id] = dib;
187 void OnBufferFreedDispatch(int device_id, int buffer_id) {
188 OnBufferFreed(device_id, buffer_id);
190 std::map<int, base::SharedMemory*>::iterator it =
191 filled_dib_.find(buffer_id);
192 ASSERT_TRUE(it != filled_dib_.end());
193 delete it->second;
194 filled_dib_.erase(it);
197 void OnBufferFilledDispatch(int device_id,
198 int buffer_id,
199 base::Time timestamp,
200 const media::VideoCaptureFormat& frame_format) {
201 base::SharedMemory* dib = filled_dib_[buffer_id];
202 ASSERT_TRUE(dib != NULL);
203 if (dump_video_) {
204 if (!format_.IsValid()) {
205 dumper_.StartDump(frame_format.frame_size.width(),
206 frame_format.frame_size.height());
207 format_ = frame_format;
209 ASSERT_EQ(format_.frame_size.width(), frame_format.frame_size.width())
210 << "Dump format does not handle variable resolution.";
211 ASSERT_EQ(format_.frame_size.height(), frame_format.frame_size.height())
212 << "Dump format does not handle variable resolution.";
213 dumper_.NewVideoFrame(dib->memory());
216 OnBufferFilled(device_id, buffer_id, timestamp, frame_format);
217 if (return_buffers_) {
218 VideoCaptureHost::OnReceiveEmptyBuffer(device_id, buffer_id);
222 void OnStateChangedDispatch(int device_id, VideoCaptureState state) {
223 OnStateChanged(device_id, state);
226 std::map<int, base::SharedMemory*> filled_dib_;
227 bool return_buffers_;
228 bool dump_video_;
229 media::VideoCaptureFormat format_;
230 DumpVideo dumper_;
233 ACTION_P2(ExitMessageLoop, message_loop, quit_closure) {
234 message_loop->PostTask(FROM_HERE, quit_closure);
237 // This is an integration test of VideoCaptureHost in conjunction with
238 // MediaStreamManager, VideoCaptureManager, VideoCaptureController, and
239 // VideoCaptureDevice.
240 class VideoCaptureHostTest : public testing::Test {
241 public:
242 VideoCaptureHostTest()
243 : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP),
244 message_loop_(base::MessageLoopProxy::current()),
245 opened_session_id_(kInvalidMediaCaptureSessionId) {}
247 virtual void SetUp() OVERRIDE {
248 SetBrowserClientForTesting(&browser_client_);
249 // Create our own MediaStreamManager.
250 audio_manager_.reset(media::AudioManager::CreateForTesting());
251 media_stream_manager_.reset(new MediaStreamManager(audio_manager_.get()));
252 #ifndef TEST_REAL_CAPTURE_DEVICE
253 media_stream_manager_->UseFakeDevice();
254 #endif
255 media_stream_manager_->UseFakeUI(scoped_ptr<FakeMediaStreamUIProxy>());
257 // Create a Host and connect it to a simulated IPC channel.
258 host_ = new MockVideoCaptureHost(media_stream_manager_.get());
259 host_->OnChannelConnected(base::GetCurrentProcId());
261 OpenSession();
264 virtual void TearDown() OVERRIDE {
265 // Verifies and removes the expectations on host_ and
266 // returns true iff successful.
267 Mock::VerifyAndClearExpectations(host_.get());
268 EXPECT_EQ(0u, host_->entries_.size());
270 CloseSession();
272 // Simulate closing the IPC channel.
273 host_->OnChannelClosing();
275 // Release the reference to the mock object. The object will be destructed
276 // on the current message loop.
277 host_ = NULL;
280 void OpenSession() {
281 const int render_process_id = 1;
282 const int render_view_id = 1;
283 const int page_request_id = 1;
284 const GURL security_origin("http://test.com");
286 ASSERT_TRUE(opened_device_label_.empty());
288 // Enumerate video devices.
289 StreamDeviceInfoArray devices;
291 base::RunLoop run_loop;
292 std::string label = media_stream_manager_->EnumerateDevices(
293 &stream_requester_,
294 render_process_id,
295 render_view_id,
296 browser_context_.GetResourceContext(),
297 page_request_id,
298 MEDIA_DEVICE_VIDEO_CAPTURE,
299 security_origin);
300 EXPECT_CALL(stream_requester_, DevicesEnumerated(label, _))
301 .Times(1).WillOnce(
302 DoAll(ExitMessageLoop(message_loop_, run_loop.QuitClosure()),
303 SaveArg<1>(&devices)));
304 run_loop.Run();
305 Mock::VerifyAndClearExpectations(&stream_requester_);
306 media_stream_manager_->CancelRequest(label);
308 ASSERT_FALSE(devices.empty());
309 ASSERT_EQ(StreamDeviceInfo::kNoId, devices[0].session_id);
311 // Open the first device.
313 base::RunLoop run_loop;
314 StreamDeviceInfo opened_device;
315 opened_device_label_ = media_stream_manager_->OpenDevice(
316 &stream_requester_,
317 render_process_id,
318 render_view_id,
319 browser_context_.GetResourceContext(),
320 page_request_id,
321 devices[0].device.id,
322 MEDIA_DEVICE_VIDEO_CAPTURE,
323 security_origin);
324 EXPECT_CALL(stream_requester_, DeviceOpened(opened_device_label_, _))
325 .Times(1).WillOnce(
326 DoAll(ExitMessageLoop(message_loop_, run_loop.QuitClosure()),
327 SaveArg<1>(&opened_device)));
328 run_loop.Run();
329 Mock::VerifyAndClearExpectations(&stream_requester_);
330 ASSERT_NE(StreamDeviceInfo::kNoId, opened_device.session_id);
331 opened_session_id_ = opened_device.session_id;
335 void CloseSession() {
336 if (opened_device_label_.empty())
337 return;
338 media_stream_manager_->CancelRequest(opened_device_label_);
339 opened_device_label_.clear();
340 opened_session_id_ = kInvalidMediaCaptureSessionId;
343 protected:
344 void StartCapture() {
345 EXPECT_CALL(*host_, OnNewBufferCreated(kDeviceId, _, _, _))
346 .Times(AnyNumber()).WillRepeatedly(Return());
348 base::RunLoop run_loop;
349 EXPECT_CALL(*host_, OnBufferFilled(kDeviceId, _, _, _))
350 .Times(AnyNumber()).WillOnce(ExitMessageLoop(
351 message_loop_, run_loop.QuitClosure()));
353 media::VideoCaptureParams params;
354 params.requested_format = media::VideoCaptureFormat(
355 gfx::Size(352, 288), 30, media::PIXEL_FORMAT_I420);
356 host_->OnStartCapture(kDeviceId, opened_session_id_, params);
357 run_loop.Run();
360 void StartStopCapture() {
361 // Quickly start and then stop capture, without giving much chance for
362 // asynchronous start operations to complete.
363 InSequence s;
364 base::RunLoop run_loop;
365 EXPECT_CALL(*host_, OnStateChanged(kDeviceId, VIDEO_CAPTURE_STATE_STOPPED));
366 media::VideoCaptureParams params;
367 params.requested_format = media::VideoCaptureFormat(
368 gfx::Size(352, 288), 30, media::PIXEL_FORMAT_I420);
369 host_->OnStartCapture(kDeviceId, opened_session_id_, params);
370 host_->OnStopCapture(kDeviceId);
371 run_loop.RunUntilIdle();
374 #ifdef DUMP_VIDEO
375 void CaptureAndDumpVideo(int width, int height, int frame_rate) {
376 InSequence s;
377 EXPECT_CALL(*host_.get(), OnNewBufferCreated(kDeviceId, _, _, _))
378 .Times(AnyNumber()).WillRepeatedly(Return());
380 base::RunLoop run_loop;
381 EXPECT_CALL(*host_, OnBufferFilled(kDeviceId, _, _, _))
382 .Times(AnyNumber())
383 .WillOnce(ExitMessageLoop(message_loop_, run_loop.QuitClosure()));
385 media::VideoCaptureParams params;
386 params.requested_format =
387 media::VideoCaptureFormat(gfx::Size(width, height), frame_rate);
388 host_->SetDumpVideo(true);
389 host_->OnStartCapture(kDeviceId, opened_session_id_, params);
390 run_loop.Run();
392 #endif
394 void StopCapture() {
395 base::RunLoop run_loop;
396 EXPECT_CALL(*host_, OnStateChanged(kDeviceId, VIDEO_CAPTURE_STATE_STOPPED))
397 .WillOnce(ExitMessageLoop(message_loop_, run_loop.QuitClosure()));
399 host_->OnStopCapture(kDeviceId);
400 host_->SetReturnReceivedDibs(true);
401 host_->ReturnReceivedDibs(kDeviceId);
403 run_loop.Run();
405 host_->SetReturnReceivedDibs(false);
406 // Expect the VideoCaptureDevice has been stopped
407 EXPECT_EQ(0u, host_->entries_.size());
410 void NotifyPacketReady() {
411 base::RunLoop run_loop;
412 EXPECT_CALL(*host_, OnBufferFilled(kDeviceId, _, _, _))
413 .Times(AnyNumber()).WillOnce(ExitMessageLoop(
414 message_loop_, run_loop.QuitClosure()))
415 .RetiresOnSaturation();
416 run_loop.Run();
419 void ReturnReceivedPackets() {
420 host_->ReturnReceivedDibs(kDeviceId);
423 void SimulateError() {
424 // Expect a change state to error state sent through IPC.
425 EXPECT_CALL(*host_, OnStateChanged(kDeviceId, VIDEO_CAPTURE_STATE_ERROR))
426 .Times(1);
427 VideoCaptureControllerID id(kDeviceId);
428 host_->OnError(id);
429 // Wait for the error callback.
430 base::RunLoop().RunUntilIdle();
433 scoped_refptr<MockVideoCaptureHost> host_;
435 private:
436 StrictMock<MockMediaStreamRequester> stream_requester_;
437 scoped_ptr<media::AudioManager> audio_manager_;
438 scoped_ptr<MediaStreamManager> media_stream_manager_;
439 content::TestBrowserThreadBundle thread_bundle_;
440 content::TestBrowserContext browser_context_;
441 content::TestContentBrowserClient browser_client_;
442 scoped_refptr<base::MessageLoopProxy> message_loop_;
443 int opened_session_id_;
444 std::string opened_device_label_;
446 DISALLOW_COPY_AND_ASSIGN(VideoCaptureHostTest);
449 TEST_F(VideoCaptureHostTest, CloseSessionWithoutStopping) {
450 StartCapture();
452 // When the session is closed via the stream without stopping capture, the
453 // ENDED event is sent.
454 EXPECT_CALL(*host_, OnStateChanged(kDeviceId, VIDEO_CAPTURE_STATE_ENDED))
455 .Times(1);
456 CloseSession();
457 base::RunLoop().RunUntilIdle();
460 TEST_F(VideoCaptureHostTest, StopWhileStartPending) {
461 StartStopCapture();
464 TEST_F(VideoCaptureHostTest, StartCapturePlayStop) {
465 StartCapture();
466 NotifyPacketReady();
467 NotifyPacketReady();
468 ReturnReceivedPackets();
469 StopCapture();
472 TEST_F(VideoCaptureHostTest, StartCaptureErrorStop) {
473 StartCapture();
474 SimulateError();
475 StopCapture();
478 TEST_F(VideoCaptureHostTest, StartCaptureError) {
479 EXPECT_CALL(*host_, OnStateChanged(kDeviceId, VIDEO_CAPTURE_STATE_STOPPED))
480 .Times(0);
481 StartCapture();
482 NotifyPacketReady();
483 SimulateError();
484 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(200));
487 #ifdef DUMP_VIDEO
488 TEST_F(VideoCaptureHostTest, CaptureAndDumpVideoVga) {
489 CaptureAndDumpVideo(640, 480, 30);
491 TEST_F(VideoCaptureHostTest, CaptureAndDump720P) {
492 CaptureAndDumpVideo(1280, 720, 30);
494 #endif
496 } // namespace content