Revert 268405 "Make sure that ScratchBuffer::Allocate() always r..."
[chromium-blink-merge.git] / content / browser / renderer_host / media / video_capture_host_unittest.cc
blob171d300cf924c3b4d72f967cc4305bb05830cacb
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/command_line.h"
10 #include "base/file_util.h"
11 #include "base/files/scoped_file.h"
12 #include "base/memory/scoped_ptr.h"
13 #include "base/message_loop/message_loop.h"
14 #include "base/run_loop.h"
15 #include "base/stl_util.h"
16 #include "base/strings/stringprintf.h"
17 #include "content/browser/browser_thread_impl.h"
18 #include "content/browser/renderer_host/media/media_stream_manager.h"
19 #include "content/browser/renderer_host/media/media_stream_requester.h"
20 #include "content/browser/renderer_host/media/media_stream_ui_proxy.h"
21 #include "content/browser/renderer_host/media/video_capture_host.h"
22 #include "content/browser/renderer_host/media/video_capture_manager.h"
23 #include "content/common/media/video_capture_messages.h"
24 #include "content/public/common/content_switches.h"
25 #include "content/public/test/mock_resource_context.h"
26 #include "content/public/test/test_browser_context.h"
27 #include "content/public/test/test_browser_thread_bundle.h"
28 #include "content/test/test_content_browser_client.h"
29 #include "media/audio/audio_manager.h"
30 #include "media/base/video_frame.h"
31 #include "media/video/capture/video_capture_types.h"
32 #include "net/url_request/url_request_context.h"
33 #include "testing/gmock/include/gmock/gmock.h"
34 #include "testing/gtest/include/gtest/gtest.h"
36 using ::testing::_;
37 using ::testing::AtLeast;
38 using ::testing::AnyNumber;
39 using ::testing::DoAll;
40 using ::testing::InSequence;
41 using ::testing::Mock;
42 using ::testing::Return;
43 using ::testing::SaveArg;
44 using ::testing::StrictMock;
46 namespace content {
48 // Id used to identify the capture session between renderer and
49 // video_capture_host. This is an arbitrary value.
50 static const int kDeviceId = 555;
52 // Define to enable test where video is dumped to file.
53 // #define DUMP_VIDEO
55 // Define to use a real video capture device.
56 // #define TEST_REAL_CAPTURE_DEVICE
58 // Simple class used for dumping video to a file. This can be used for
59 // verifying the output.
60 class DumpVideo {
61 public:
62 DumpVideo() : expected_size_(0) {}
63 void StartDump(int width, int height) {
64 base::FilePath file_name = base::FilePath(base::StringPrintf(
65 FILE_PATH_LITERAL("dump_w%d_h%d.yuv"), width, height));
66 file_.reset(base::OpenFile(file_name, "wb"));
67 expected_size_ = media::VideoFrame::AllocationSize(
68 media::VideoFrame::I420, gfx::Size(width, height));
70 void NewVideoFrame(const void* buffer) {
71 if (file_.get() != NULL) {
72 ASSERT_EQ(1U, fwrite(buffer, expected_size_, 1, file_.get()));
76 private:
77 base::ScopedFILE file_;
78 int expected_size_;
81 class MockMediaStreamRequester : public MediaStreamRequester {
82 public:
83 MockMediaStreamRequester() {}
84 virtual ~MockMediaStreamRequester() {}
86 // MediaStreamRequester implementation.
87 MOCK_METHOD5(StreamGenerated,
88 void(int render_view_id,
89 int page_request_id,
90 const std::string& label,
91 const StreamDeviceInfoArray& audio_devices,
92 const StreamDeviceInfoArray& video_devices));
93 MOCK_METHOD3(StreamGenerationFailed,
94 void(int render_view_id,
95 int page_request_id,
96 content::MediaStreamRequestResult result));
97 MOCK_METHOD3(DeviceStopped, void(int render_view_id,
98 const std::string& label,
99 const StreamDeviceInfo& device));
100 MOCK_METHOD4(DevicesEnumerated, void(int render_view_id,
101 int page_request_id,
102 const std::string& label,
103 const StreamDeviceInfoArray& devices));
104 MOCK_METHOD4(DeviceOpened, void(int render_view_id,
105 int page_request_id,
106 const std::string& label,
107 const StreamDeviceInfo& device_info));
109 private:
110 DISALLOW_COPY_AND_ASSIGN(MockMediaStreamRequester);
113 class MockVideoCaptureHost : public VideoCaptureHost {
114 public:
115 MockVideoCaptureHost(MediaStreamManager* manager)
116 : VideoCaptureHost(manager),
117 return_buffers_(false),
118 dump_video_(false) {}
120 // A list of mock methods.
121 MOCK_METHOD4(OnNewBufferCreated,
122 void(int device_id,
123 base::SharedMemoryHandle handle,
124 int length,
125 int buffer_id));
126 MOCK_METHOD2(OnBufferFreed,
127 void(int device_id, int buffer_id));
128 MOCK_METHOD4(OnBufferFilled,
129 void(int device_id,
130 int buffer_id,
131 const media::VideoCaptureFormat& format,
132 base::TimeTicks timestamp));
133 MOCK_METHOD5(OnMailboxBufferFilled,
134 void(int device_id,
135 int buffer_id,
136 const gpu::MailboxHolder& mailbox_holder,
137 const media::VideoCaptureFormat& format,
138 base::TimeTicks timestamp));
139 MOCK_METHOD2(OnStateChanged, void(int device_id, VideoCaptureState state));
141 // Use class DumpVideo to write I420 video to file.
142 void SetDumpVideo(bool enable) {
143 dump_video_ = enable;
146 void SetReturnReceivedDibs(bool enable) {
147 return_buffers_ = enable;
150 // Return Dibs we currently have received.
151 void ReturnReceivedDibs(int device_id) {
152 int handle = GetReceivedDib();
153 while (handle) {
154 this->OnReceiveEmptyBuffer(device_id, handle, std::vector<uint32>());
155 handle = GetReceivedDib();
159 int GetReceivedDib() {
160 if (filled_dib_.empty())
161 return 0;
162 std::map<int, base::SharedMemory*>::iterator it = filled_dib_.begin();
163 int h = it->first;
164 delete it->second;
165 filled_dib_.erase(it);
167 return h;
170 private:
171 virtual ~MockVideoCaptureHost() {
172 STLDeleteContainerPairSecondPointers(filled_dib_.begin(),
173 filled_dib_.end());
176 // This method is used to dispatch IPC messages to the renderer. We intercept
177 // these messages here and dispatch to our mock methods to verify the
178 // conversation between this object and the renderer.
179 virtual bool Send(IPC::Message* message) OVERRIDE {
180 CHECK(message);
182 // In this method we dispatch the messages to the according handlers as if
183 // we are the renderer.
184 bool handled = true;
185 IPC_BEGIN_MESSAGE_MAP(MockVideoCaptureHost, *message)
186 IPC_MESSAGE_HANDLER(VideoCaptureMsg_NewBuffer, OnNewBufferCreatedDispatch)
187 IPC_MESSAGE_HANDLER(VideoCaptureMsg_FreeBuffer, OnBufferFreedDispatch)
188 IPC_MESSAGE_HANDLER(VideoCaptureMsg_BufferReady, OnBufferFilledDispatch)
189 IPC_MESSAGE_HANDLER(VideoCaptureMsg_MailboxBufferReady,
190 OnMailboxBufferFilledDispatch)
191 IPC_MESSAGE_HANDLER(VideoCaptureMsg_StateChanged, OnStateChangedDispatch)
192 IPC_MESSAGE_UNHANDLED(handled = false)
193 IPC_END_MESSAGE_MAP()
194 EXPECT_TRUE(handled);
196 delete message;
197 return true;
200 // These handler methods do minimal things and delegate to the mock methods.
201 void OnNewBufferCreatedDispatch(int device_id,
202 base::SharedMemoryHandle handle,
203 uint32 length,
204 int buffer_id) {
205 OnNewBufferCreated(device_id, handle, length, buffer_id);
206 base::SharedMemory* dib = new base::SharedMemory(handle, false);
207 dib->Map(length);
208 filled_dib_[buffer_id] = dib;
211 void OnBufferFreedDispatch(int device_id, int buffer_id) {
212 OnBufferFreed(device_id, buffer_id);
214 std::map<int, base::SharedMemory*>::iterator it =
215 filled_dib_.find(buffer_id);
216 ASSERT_TRUE(it != filled_dib_.end());
217 delete it->second;
218 filled_dib_.erase(it);
221 void OnBufferFilledDispatch(int device_id,
222 int buffer_id,
223 const media::VideoCaptureFormat& frame_format,
224 base::TimeTicks timestamp) {
225 base::SharedMemory* dib = filled_dib_[buffer_id];
226 ASSERT_TRUE(dib != NULL);
227 if (dump_video_) {
228 if (!format_.IsValid()) {
229 dumper_.StartDump(frame_format.frame_size.width(),
230 frame_format.frame_size.height());
231 format_ = frame_format;
233 ASSERT_EQ(format_.frame_size.width(), frame_format.frame_size.width())
234 << "Dump format does not handle variable resolution.";
235 ASSERT_EQ(format_.frame_size.height(), frame_format.frame_size.height())
236 << "Dump format does not handle variable resolution.";
237 dumper_.NewVideoFrame(dib->memory());
240 OnBufferFilled(device_id, buffer_id, frame_format, timestamp);
241 if (return_buffers_) {
242 VideoCaptureHost::OnReceiveEmptyBuffer(
243 device_id, buffer_id, std::vector<uint32>());
247 void OnMailboxBufferFilledDispatch(int device_id,
248 int buffer_id,
249 const gpu::MailboxHolder& mailbox_holder,
250 const media::VideoCaptureFormat& format,
251 base::TimeTicks timestamp) {
252 OnMailboxBufferFilled(
253 device_id, buffer_id, mailbox_holder, format, timestamp);
254 if (return_buffers_) {
255 VideoCaptureHost::OnReceiveEmptyBuffer(
256 device_id, buffer_id, std::vector<uint32>());
260 void OnStateChangedDispatch(int device_id, VideoCaptureState state) {
261 OnStateChanged(device_id, state);
264 std::map<int, base::SharedMemory*> filled_dib_;
265 bool return_buffers_;
266 bool dump_video_;
267 media::VideoCaptureFormat format_;
268 DumpVideo dumper_;
271 ACTION_P2(ExitMessageLoop, message_loop, quit_closure) {
272 message_loop->PostTask(FROM_HERE, quit_closure);
275 // This is an integration test of VideoCaptureHost in conjunction with
276 // MediaStreamManager, VideoCaptureManager, VideoCaptureController, and
277 // VideoCaptureDevice.
278 class VideoCaptureHostTest : public testing::Test {
279 public:
280 VideoCaptureHostTest()
281 : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP),
282 message_loop_(base::MessageLoopProxy::current()),
283 opened_session_id_(kInvalidMediaCaptureSessionId) {}
285 virtual void SetUp() OVERRIDE {
286 SetBrowserClientForTesting(&browser_client_);
287 // Create our own MediaStreamManager.
288 audio_manager_.reset(media::AudioManager::CreateForTesting());
289 #ifndef TEST_REAL_CAPTURE_DEVICE
290 base::CommandLine::ForCurrentProcess()->AppendSwitch(
291 switches::kUseFakeDeviceForMediaStream);
292 #endif
293 media_stream_manager_.reset(new MediaStreamManager(audio_manager_.get()));
294 media_stream_manager_->UseFakeUI(scoped_ptr<FakeMediaStreamUIProxy>());
296 // Create a Host and connect it to a simulated IPC channel.
297 host_ = new MockVideoCaptureHost(media_stream_manager_.get());
298 host_->OnChannelConnected(base::GetCurrentProcId());
300 OpenSession();
303 virtual void TearDown() OVERRIDE {
304 // Verifies and removes the expectations on host_ and
305 // returns true iff successful.
306 Mock::VerifyAndClearExpectations(host_.get());
307 EXPECT_EQ(0u, host_->entries_.size());
309 CloseSession();
311 // Simulate closing the IPC channel.
312 host_->OnChannelClosing();
314 // Release the reference to the mock object. The object will be destructed
315 // on the current message loop.
316 host_ = NULL;
319 void OpenSession() {
320 const int render_process_id = 1;
321 const int render_view_id = 1;
322 const int page_request_id = 1;
323 const GURL security_origin("http://test.com");
325 ASSERT_TRUE(opened_device_label_.empty());
327 // Enumerate video devices.
328 StreamDeviceInfoArray devices;
330 base::RunLoop run_loop;
331 std::string label = media_stream_manager_->EnumerateDevices(
332 &stream_requester_,
333 render_process_id,
334 render_view_id,
335 browser_context_.GetResourceContext()->GetMediaDeviceIDSalt(),
336 page_request_id,
337 MEDIA_DEVICE_VIDEO_CAPTURE,
338 security_origin);
339 EXPECT_CALL(stream_requester_, DevicesEnumerated(render_view_id,
340 page_request_id,
341 label,
343 .Times(1).WillOnce(
344 DoAll(ExitMessageLoop(message_loop_, run_loop.QuitClosure()),
345 SaveArg<3>(&devices)));
346 run_loop.Run();
347 Mock::VerifyAndClearExpectations(&stream_requester_);
348 media_stream_manager_->CancelRequest(label);
350 ASSERT_FALSE(devices.empty());
351 ASSERT_EQ(StreamDeviceInfo::kNoId, devices[0].session_id);
353 // Open the first device.
355 base::RunLoop run_loop;
356 StreamDeviceInfo opened_device;
357 media_stream_manager_->OpenDevice(
358 &stream_requester_,
359 render_process_id,
360 render_view_id,
361 browser_context_.GetResourceContext()->GetMediaDeviceIDSalt(),
362 page_request_id,
363 devices[0].device.id,
364 MEDIA_DEVICE_VIDEO_CAPTURE,
365 security_origin);
366 EXPECT_CALL(stream_requester_, DeviceOpened(render_view_id,
367 page_request_id,
370 .Times(1).WillOnce(
371 DoAll(ExitMessageLoop(message_loop_, run_loop.QuitClosure()),
372 SaveArg<2>(&opened_device_label_),
373 SaveArg<3>(&opened_device)));
374 run_loop.Run();
375 Mock::VerifyAndClearExpectations(&stream_requester_);
376 ASSERT_NE(StreamDeviceInfo::kNoId, opened_device.session_id);
377 opened_session_id_ = opened_device.session_id;
381 void CloseSession() {
382 if (opened_device_label_.empty())
383 return;
384 media_stream_manager_->CancelRequest(opened_device_label_);
385 opened_device_label_.clear();
386 opened_session_id_ = kInvalidMediaCaptureSessionId;
389 protected:
390 void StartCapture() {
391 EXPECT_CALL(*host_, OnNewBufferCreated(kDeviceId, _, _, _))
392 .Times(AnyNumber()).WillRepeatedly(Return());
394 base::RunLoop run_loop;
395 EXPECT_CALL(*host_, OnBufferFilled(kDeviceId, _, _, _))
396 .Times(AnyNumber()).WillOnce(ExitMessageLoop(
397 message_loop_, run_loop.QuitClosure()));
399 media::VideoCaptureParams params;
400 params.requested_format = media::VideoCaptureFormat(
401 gfx::Size(352, 288), 30, media::PIXEL_FORMAT_I420);
402 host_->OnStartCapture(kDeviceId, opened_session_id_, params);
403 run_loop.Run();
406 void StartStopCapture() {
407 // Quickly start and then stop capture, without giving much chance for
408 // asynchronous start operations to complete.
409 InSequence s;
410 base::RunLoop run_loop;
411 EXPECT_CALL(*host_, OnStateChanged(kDeviceId, VIDEO_CAPTURE_STATE_STOPPED));
412 media::VideoCaptureParams params;
413 params.requested_format = media::VideoCaptureFormat(
414 gfx::Size(352, 288), 30, media::PIXEL_FORMAT_I420);
415 host_->OnStartCapture(kDeviceId, opened_session_id_, params);
416 host_->OnStopCapture(kDeviceId);
417 run_loop.RunUntilIdle();
420 #ifdef DUMP_VIDEO
421 void CaptureAndDumpVideo(int width, int height, int frame_rate) {
422 InSequence s;
423 EXPECT_CALL(*host_.get(), OnNewBufferCreated(kDeviceId, _, _, _))
424 .Times(AnyNumber()).WillRepeatedly(Return());
426 base::RunLoop run_loop;
427 EXPECT_CALL(*host_, OnBufferFilled(kDeviceId, _, _, _))
428 .Times(AnyNumber())
429 .WillOnce(ExitMessageLoop(message_loop_, run_loop.QuitClosure()));
431 media::VideoCaptureParams params;
432 params.requested_format =
433 media::VideoCaptureFormat(gfx::Size(width, height), frame_rate);
434 host_->SetDumpVideo(true);
435 host_->OnStartCapture(kDeviceId, opened_session_id_, params);
436 run_loop.Run();
438 #endif
440 void StopCapture() {
441 base::RunLoop run_loop;
442 EXPECT_CALL(*host_, OnStateChanged(kDeviceId, VIDEO_CAPTURE_STATE_STOPPED))
443 .WillOnce(ExitMessageLoop(message_loop_, run_loop.QuitClosure()));
445 host_->OnStopCapture(kDeviceId);
446 host_->SetReturnReceivedDibs(true);
447 host_->ReturnReceivedDibs(kDeviceId);
449 run_loop.Run();
451 host_->SetReturnReceivedDibs(false);
452 // Expect the VideoCaptureDevice has been stopped
453 EXPECT_EQ(0u, host_->entries_.size());
456 void NotifyPacketReady() {
457 base::RunLoop run_loop;
458 EXPECT_CALL(*host_, OnBufferFilled(kDeviceId, _, _, _))
459 .Times(AnyNumber()).WillOnce(ExitMessageLoop(
460 message_loop_, run_loop.QuitClosure()))
461 .RetiresOnSaturation();
462 run_loop.Run();
465 void ReturnReceivedPackets() {
466 host_->ReturnReceivedDibs(kDeviceId);
469 void SimulateError() {
470 // Expect a change state to error state sent through IPC.
471 EXPECT_CALL(*host_, OnStateChanged(kDeviceId, VIDEO_CAPTURE_STATE_ERROR))
472 .Times(1);
473 VideoCaptureControllerID id(kDeviceId);
474 host_->OnError(id);
475 // Wait for the error callback.
476 base::RunLoop().RunUntilIdle();
479 scoped_refptr<MockVideoCaptureHost> host_;
481 private:
482 StrictMock<MockMediaStreamRequester> stream_requester_;
483 scoped_ptr<media::AudioManager> audio_manager_;
484 scoped_ptr<MediaStreamManager> media_stream_manager_;
485 content::TestBrowserThreadBundle thread_bundle_;
486 content::TestBrowserContext browser_context_;
487 content::TestContentBrowserClient browser_client_;
488 scoped_refptr<base::MessageLoopProxy> message_loop_;
489 int opened_session_id_;
490 std::string opened_device_label_;
492 DISALLOW_COPY_AND_ASSIGN(VideoCaptureHostTest);
495 TEST_F(VideoCaptureHostTest, CloseSessionWithoutStopping) {
496 StartCapture();
498 // When the session is closed via the stream without stopping capture, the
499 // ENDED event is sent.
500 EXPECT_CALL(*host_, OnStateChanged(kDeviceId, VIDEO_CAPTURE_STATE_ENDED))
501 .Times(1);
502 CloseSession();
503 base::RunLoop().RunUntilIdle();
506 TEST_F(VideoCaptureHostTest, StopWhileStartPending) {
507 StartStopCapture();
510 TEST_F(VideoCaptureHostTest, StartCapturePlayStop) {
511 StartCapture();
512 NotifyPacketReady();
513 NotifyPacketReady();
514 ReturnReceivedPackets();
515 StopCapture();
518 TEST_F(VideoCaptureHostTest, StartCaptureErrorStop) {
519 StartCapture();
520 SimulateError();
521 StopCapture();
524 TEST_F(VideoCaptureHostTest, StartCaptureError) {
525 EXPECT_CALL(*host_, OnStateChanged(kDeviceId, VIDEO_CAPTURE_STATE_STOPPED))
526 .Times(0);
527 StartCapture();
528 NotifyPacketReady();
529 SimulateError();
530 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(200));
533 #ifdef DUMP_VIDEO
534 TEST_F(VideoCaptureHostTest, CaptureAndDumpVideoVga) {
535 CaptureAndDumpVideo(640, 480, 30);
537 TEST_F(VideoCaptureHostTest, CaptureAndDump720P) {
538 CaptureAndDumpVideo(1280, 720, 30);
540 #endif
542 } // namespace content