Add long running gmail memory benchmark for background tab.
[chromium-blink-merge.git] / content / browser / renderer_host / media / video_capture_host_unittest.cc
blob4d72298d69d3255442f2f1072c307aae7c9fc73f
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/files/file_util.h"
11 #include "base/files/scoped_file.h"
12 #include "base/location.h"
13 #include "base/memory/scoped_ptr.h"
14 #include "base/run_loop.h"
15 #include "base/single_thread_task_runner.h"
16 #include "base/stl_util.h"
17 #include "base/strings/stringprintf.h"
18 #include "base/thread_task_runner_handle.h"
19 #include "content/browser/browser_thread_impl.h"
20 #include "content/browser/renderer_host/media/media_stream_manager.h"
21 #include "content/browser/renderer_host/media/media_stream_requester.h"
22 #include "content/browser/renderer_host/media/media_stream_ui_proxy.h"
23 #include "content/browser/renderer_host/media/video_capture_host.h"
24 #include "content/browser/renderer_host/media/video_capture_manager.h"
25 #include "content/common/media/video_capture_messages.h"
26 #include "content/public/common/content_switches.h"
27 #include "content/public/test/mock_resource_context.h"
28 #include "content/public/test/test_browser_context.h"
29 #include "content/public/test/test_browser_thread_bundle.h"
30 #include "content/test/test_content_browser_client.h"
31 #include "media/audio/audio_manager.h"
32 #include "media/base/media_switches.h"
33 #include "media/base/video_capture_types.h"
34 #include "media/base/video_frame.h"
35 #include "net/url_request/url_request_context.h"
36 #include "testing/gmock/include/gmock/gmock.h"
37 #include "testing/gtest/include/gtest/gtest.h"
39 #if defined(OS_CHROMEOS)
40 #include "chromeos/audio/cras_audio_handler.h"
41 #endif
43 using ::testing::_;
44 using ::testing::AtLeast;
45 using ::testing::AnyNumber;
46 using ::testing::DoAll;
47 using ::testing::InSequence;
48 using ::testing::Mock;
49 using ::testing::Return;
50 using ::testing::SaveArg;
51 using ::testing::StrictMock;
53 namespace content {
55 // Id used to identify the capture session between renderer and
56 // video_capture_host. This is an arbitrary value.
57 static const int kDeviceId = 555;
59 // Define to enable test where video is dumped to file.
60 // #define DUMP_VIDEO
62 // Define to use a real video capture device.
63 // #define TEST_REAL_CAPTURE_DEVICE
65 // Simple class used for dumping video to a file. This can be used for
66 // verifying the output.
67 class DumpVideo {
68 public:
69 DumpVideo() {}
70 const gfx::Size& coded_size() const { return coded_size_; }
71 void StartDump(const gfx::Size& coded_size) {
72 base::FilePath file_name = base::FilePath(base::StringPrintf(
73 FILE_PATH_LITERAL("dump_w%d_h%d.yuv"),
74 coded_size.width(),
75 coded_size.height()));
76 file_.reset(base::OpenFile(file_name, "wb"));
77 coded_size_ = coded_size;
79 void NewVideoFrame(const void* buffer) {
80 if (file_.get() != NULL) {
81 const int size = media::VideoFrame::AllocationSize(
82 media::PIXEL_FORMAT_I420, coded_size_);
83 ASSERT_EQ(1U, fwrite(buffer, size, 1, file_.get()));
87 private:
88 base::ScopedFILE file_;
89 gfx::Size coded_size_;
92 class MockMediaStreamRequester : public MediaStreamRequester {
93 public:
94 MockMediaStreamRequester() {}
95 virtual ~MockMediaStreamRequester() {}
97 // MediaStreamRequester implementation.
98 MOCK_METHOD5(StreamGenerated,
99 void(int render_frame_id,
100 int page_request_id,
101 const std::string& label,
102 const StreamDeviceInfoArray& audio_devices,
103 const StreamDeviceInfoArray& video_devices));
104 MOCK_METHOD3(StreamGenerationFailed,
105 void(int render_frame_id,
106 int page_request_id,
107 content::MediaStreamRequestResult result));
108 MOCK_METHOD3(DeviceStopped, void(int render_frame_id,
109 const std::string& label,
110 const StreamDeviceInfo& device));
111 MOCK_METHOD4(DevicesEnumerated, void(int render_frame_id,
112 int page_request_id,
113 const std::string& label,
114 const StreamDeviceInfoArray& devices));
115 MOCK_METHOD4(DeviceOpened, void(int render_frame_id,
116 int page_request_id,
117 const std::string& label,
118 const StreamDeviceInfo& device_info));
120 private:
121 DISALLOW_COPY_AND_ASSIGN(MockMediaStreamRequester);
124 class MockVideoCaptureHost : public VideoCaptureHost {
125 public:
126 MockVideoCaptureHost(MediaStreamManager* manager)
127 : VideoCaptureHost(manager),
128 return_buffers_(false),
129 dump_video_(false) {}
131 // A list of mock methods.
132 MOCK_METHOD4(OnNewBufferCreated,
133 void(int device_id,
134 base::SharedMemoryHandle handle,
135 int length,
136 int buffer_id));
137 MOCK_METHOD2(OnBufferFreed, void(int device_id, int buffer_id));
138 MOCK_METHOD1(OnBufferFilled, void(int device_id));
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->OnRendererFinishedWithBuffer(device_id, handle, 0, -1.0);
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 ~MockVideoCaptureHost() override {
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 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_StateChanged, OnStateChangedDispatch)
190 IPC_MESSAGE_UNHANDLED(handled = false)
191 IPC_END_MESSAGE_MAP()
192 EXPECT_TRUE(handled);
194 delete message;
195 return true;
198 // These handler methods do minimal things and delegate to the mock methods.
199 void OnNewBufferCreatedDispatch(int device_id,
200 base::SharedMemoryHandle handle,
201 uint32 length,
202 int buffer_id) {
203 OnNewBufferCreated(device_id, handle, length, buffer_id);
204 base::SharedMemory* dib = new base::SharedMemory(handle, false);
205 dib->Map(length);
206 filled_dib_[buffer_id] = dib;
209 void OnBufferFreedDispatch(int device_id, int buffer_id) {
210 OnBufferFreed(device_id, buffer_id);
212 std::map<int, base::SharedMemory*>::iterator it =
213 filled_dib_.find(buffer_id);
214 ASSERT_TRUE(it != filled_dib_.end());
215 delete it->second;
216 filled_dib_.erase(it);
219 void OnBufferFilledDispatch(
220 const VideoCaptureMsg_BufferReady_Params& params) {
221 base::SharedMemory* dib = filled_dib_[params.buffer_id];
222 ASSERT_TRUE(dib != NULL);
223 if (dump_video_) {
224 if (dumper_.coded_size().IsEmpty())
225 dumper_.StartDump(params.coded_size);
226 ASSERT_TRUE(dumper_.coded_size() == params.coded_size)
227 << "Dump format does not handle variable resolution.";
228 dumper_.NewVideoFrame(dib->memory());
231 OnBufferFilled(params.device_id);
232 if (return_buffers_) {
233 VideoCaptureHost::OnRendererFinishedWithBuffer(
234 params.device_id, params.buffer_id, 0, -1.0);
238 void OnStateChangedDispatch(int device_id, VideoCaptureState state) {
239 OnStateChanged(device_id, state);
242 std::map<int, base::SharedMemory*> filled_dib_;
243 bool return_buffers_;
244 bool dump_video_;
245 media::VideoCaptureFormat format_;
246 DumpVideo dumper_;
249 ACTION_P2(ExitMessageLoop, task_runner, quit_closure) {
250 task_runner->PostTask(FROM_HERE, quit_closure);
253 // This is an integration test of VideoCaptureHost in conjunction with
254 // MediaStreamManager, VideoCaptureManager, VideoCaptureController, and
255 // VideoCaptureDevice.
256 class VideoCaptureHostTest : public testing::Test {
257 public:
258 VideoCaptureHostTest()
259 : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP),
260 task_runner_(base::ThreadTaskRunnerHandle::Get()),
261 opened_session_id_(kInvalidMediaCaptureSessionId) {}
263 void SetUp() override {
264 SetBrowserClientForTesting(&browser_client_);
266 #if defined(OS_CHROMEOS)
267 chromeos::CrasAudioHandler::InitializeForTesting();
268 #endif
270 // Create our own MediaStreamManager.
271 audio_manager_.reset(media::AudioManager::CreateForTesting());
272 #ifndef TEST_REAL_CAPTURE_DEVICE
273 base::CommandLine::ForCurrentProcess()->AppendSwitch(
274 switches::kUseFakeDeviceForMediaStream);
275 #endif
276 media_stream_manager_.reset(new MediaStreamManager(audio_manager_.get()));
277 media_stream_manager_->UseFakeUI(scoped_ptr<FakeMediaStreamUIProxy>());
279 // Create a Host and connect it to a simulated IPC channel.
280 host_ = new MockVideoCaptureHost(media_stream_manager_.get());
281 host_->OnChannelConnected(base::GetCurrentProcId());
283 OpenSession();
286 void TearDown() override {
287 // Verifies and removes the expectations on host_ and
288 // returns true iff successful.
289 Mock::VerifyAndClearExpectations(host_.get());
290 EXPECT_EQ(0u, host_->entries_.size());
292 CloseSession();
294 // Simulate closing the IPC sender.
295 host_->OnChannelClosing();
297 // Release the reference to the mock object. The object will be destructed
298 // on the current message loop.
299 host_ = NULL;
301 #if defined(OS_CHROMEOS)
302 chromeos::CrasAudioHandler::Shutdown();
303 #endif
306 void OpenSession() {
307 const int render_process_id = 1;
308 const int render_frame_id = 1;
309 const int page_request_id = 1;
310 const GURL security_origin("http://test.com");
312 ASSERT_TRUE(opened_device_label_.empty());
314 // Enumerate video devices.
315 StreamDeviceInfoArray devices;
317 base::RunLoop run_loop;
318 std::string label = media_stream_manager_->EnumerateDevices(
319 &stream_requester_,
320 render_process_id,
321 render_frame_id,
322 browser_context_.GetResourceContext()->GetMediaDeviceIDSalt(),
323 page_request_id,
324 MEDIA_DEVICE_VIDEO_CAPTURE,
325 security_origin);
326 EXPECT_CALL(stream_requester_,
327 DevicesEnumerated(render_frame_id, page_request_id, label, _))
328 .Times(1)
329 .WillOnce(DoAll(ExitMessageLoop(task_runner_, run_loop.QuitClosure()),
330 SaveArg<3>(&devices)));
331 run_loop.Run();
332 Mock::VerifyAndClearExpectations(&stream_requester_);
333 media_stream_manager_->CancelRequest(label);
335 ASSERT_FALSE(devices.empty());
336 ASSERT_EQ(StreamDeviceInfo::kNoId, devices[0].session_id);
338 // Open the first device.
340 base::RunLoop run_loop;
341 StreamDeviceInfo opened_device;
342 media_stream_manager_->OpenDevice(
343 &stream_requester_,
344 render_process_id,
345 render_frame_id,
346 browser_context_.GetResourceContext()->GetMediaDeviceIDSalt(),
347 page_request_id,
348 devices[0].device.id,
349 MEDIA_DEVICE_VIDEO_CAPTURE,
350 security_origin);
351 EXPECT_CALL(stream_requester_,
352 DeviceOpened(render_frame_id, page_request_id, _, _))
353 .Times(1)
354 .WillOnce(DoAll(ExitMessageLoop(task_runner_, run_loop.QuitClosure()),
355 SaveArg<2>(&opened_device_label_),
356 SaveArg<3>(&opened_device)));
357 run_loop.Run();
358 Mock::VerifyAndClearExpectations(&stream_requester_);
359 ASSERT_NE(StreamDeviceInfo::kNoId, opened_device.session_id);
360 opened_session_id_ = opened_device.session_id;
364 void CloseSession() {
365 if (opened_device_label_.empty())
366 return;
367 media_stream_manager_->CancelRequest(opened_device_label_);
368 opened_device_label_.clear();
369 opened_session_id_ = kInvalidMediaCaptureSessionId;
372 protected:
373 void StartCapture() {
374 EXPECT_CALL(*host_.get(), OnNewBufferCreated(kDeviceId, _, _, _))
375 .Times(AnyNumber())
376 .WillRepeatedly(Return());
378 base::RunLoop run_loop;
379 EXPECT_CALL(*host_.get(), OnBufferFilled(kDeviceId))
380 .Times(AnyNumber())
381 .WillOnce(ExitMessageLoop(task_runner_, run_loop.QuitClosure()));
383 media::VideoCaptureParams params;
384 params.requested_format = media::VideoCaptureFormat(
385 gfx::Size(352, 288), 30, media::VIDEO_CAPTURE_PIXEL_FORMAT_I420);
386 host_->OnStartCapture(kDeviceId, opened_session_id_, params);
387 run_loop.Run();
390 void StartStopCapture() {
391 // Quickly start and then stop capture, without giving much chance for
392 // asynchronous start operations to complete.
393 InSequence s;
394 base::RunLoop run_loop;
395 EXPECT_CALL(*host_.get(),
396 OnStateChanged(kDeviceId, VIDEO_CAPTURE_STATE_STOPPED));
397 media::VideoCaptureParams params;
398 params.requested_format = media::VideoCaptureFormat(
399 gfx::Size(352, 288), 30, media::VIDEO_CAPTURE_PIXEL_FORMAT_I420);
400 host_->OnStartCapture(kDeviceId, opened_session_id_, params);
401 host_->OnStopCapture(kDeviceId);
402 run_loop.RunUntilIdle();
403 WaitForVideoDeviceThread();
406 #ifdef DUMP_VIDEO
407 void CaptureAndDumpVideo(int width, int height, int frame_rate) {
408 InSequence s;
409 EXPECT_CALL(*host_.get(), OnNewBufferCreated(kDeviceId, _, _, _))
410 .Times(AnyNumber()).WillRepeatedly(Return());
412 base::RunLoop run_loop;
413 EXPECT_CALL(*host_, OnBufferFilled(kDeviceId, _, _, _, _, _, _, _, _, _))
414 .Times(AnyNumber())
415 .WillOnce(ExitMessageLoop(message_loop_, run_loop.QuitClosure()));
417 media::VideoCaptureParams params;
418 params.requested_format =
419 media::VideoCaptureFormat(gfx::Size(width, height), frame_rate);
420 host_->SetDumpVideo(true);
421 host_->OnStartCapture(kDeviceId, opened_session_id_, params);
422 run_loop.Run();
424 #endif
426 void StopCapture() {
427 base::RunLoop run_loop;
428 EXPECT_CALL(*host_.get(),
429 OnStateChanged(kDeviceId, VIDEO_CAPTURE_STATE_STOPPED))
430 .WillOnce(ExitMessageLoop(task_runner_, run_loop.QuitClosure()));
432 host_->OnStopCapture(kDeviceId);
433 host_->SetReturnReceivedDibs(true);
434 host_->ReturnReceivedDibs(kDeviceId);
436 run_loop.Run();
438 host_->SetReturnReceivedDibs(false);
439 // Expect the VideoCaptureDevice has been stopped
440 EXPECT_EQ(0u, host_->entries_.size());
443 void NotifyPacketReady() {
444 base::RunLoop run_loop;
445 EXPECT_CALL(*host_.get(), OnBufferFilled(kDeviceId))
446 .Times(AnyNumber())
447 .WillOnce(ExitMessageLoop(task_runner_, run_loop.QuitClosure()))
448 .RetiresOnSaturation();
449 run_loop.Run();
452 void ReturnReceivedPackets() {
453 host_->ReturnReceivedDibs(kDeviceId);
456 void SimulateError() {
457 // Expect a change state to error state sent through IPC.
458 EXPECT_CALL(*host_.get(),
459 OnStateChanged(kDeviceId, VIDEO_CAPTURE_STATE_ERROR)).Times(1);
460 VideoCaptureControllerID id(kDeviceId);
461 host_->OnError(id);
462 // Wait for the error callback.
463 base::RunLoop().RunUntilIdle();
466 void WaitForVideoDeviceThread() {
467 base::RunLoop run_loop;
468 media_stream_manager_->video_capture_manager()->device_task_runner()
469 ->PostTaskAndReply(
470 FROM_HERE,
471 base::Bind(&base::DoNothing),
472 run_loop.QuitClosure());
473 run_loop.Run();
476 scoped_refptr<MockVideoCaptureHost> host_;
478 private:
479 StrictMock<MockMediaStreamRequester> stream_requester_;
480 scoped_ptr<media::AudioManager> audio_manager_;
481 scoped_ptr<MediaStreamManager> media_stream_manager_;
482 content::TestBrowserThreadBundle thread_bundle_;
483 content::TestBrowserContext browser_context_;
484 content::TestContentBrowserClient browser_client_;
485 scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
486 int opened_session_id_;
487 std::string opened_device_label_;
489 DISALLOW_COPY_AND_ASSIGN(VideoCaptureHostTest);
492 TEST_F(VideoCaptureHostTest, CloseSessionWithoutStopping) {
493 StartCapture();
495 // When the session is closed via the stream without stopping capture, the
496 // ENDED event is sent.
497 EXPECT_CALL(*host_.get(),
498 OnStateChanged(kDeviceId, VIDEO_CAPTURE_STATE_ENDED)).Times(1);
499 CloseSession();
500 base::RunLoop().RunUntilIdle();
503 TEST_F(VideoCaptureHostTest, StopWhileStartPending) {
504 StartStopCapture();
507 TEST_F(VideoCaptureHostTest, StartCapturePlayStop) {
508 StartCapture();
509 NotifyPacketReady();
510 NotifyPacketReady();
511 ReturnReceivedPackets();
512 StopCapture();
515 TEST_F(VideoCaptureHostTest, StartCaptureErrorStop) {
516 StartCapture();
517 SimulateError();
518 StopCapture();
521 TEST_F(VideoCaptureHostTest, StartCaptureError) {
522 EXPECT_CALL(*host_.get(),
523 OnStateChanged(kDeviceId, VIDEO_CAPTURE_STATE_STOPPED)).Times(0);
524 StartCapture();
525 NotifyPacketReady();
526 SimulateError();
527 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(200));
530 #ifdef DUMP_VIDEO
531 TEST_F(VideoCaptureHostTest, CaptureAndDumpVideoVga) {
532 CaptureAndDumpVideo(640, 480, 30);
534 TEST_F(VideoCaptureHostTest, CaptureAndDump720P) {
535 CaptureAndDumpVideo(1280, 720, 30);
537 #endif
539 } // namespace content