diff options
author | wjia@chromium.org <wjia@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-06-14 22:07:34 +0000 |
---|---|---|
committer | wjia@chromium.org <wjia@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-06-14 22:07:34 +0000 |
commit | ba164c912c0c30dc432d9385b92266a5f732f538 (patch) | |
tree | 32196e1754531f18568e86a5c4ac5b625caf7037 /content/browser/renderer_host/media/video_capture_host_unittest.cc | |
parent | 3fbf1cb96804988bbc37ffcd329b1878e14f4971 (diff) | |
download | chromium_src-ba164c912c0c30dc432d9385b92266a5f732f538.zip chromium_src-ba164c912c0c30dc432d9385b92266a5f732f538.tar.gz chromium_src-ba164c912c0c30dc432d9385b92266a5f732f538.tar.bz2 |
move all media and media_stream related files into content/browser/renderer_host/media and content/renderer/media
BUG=none
TEST=try bots
Review URL: http://codereview.chromium.org/7135008
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@89078 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'content/browser/renderer_host/media/video_capture_host_unittest.cc')
-rw-r--r-- | content/browser/renderer_host/media/video_capture_host_unittest.cc | 396 |
1 files changed, 396 insertions, 0 deletions
diff --git a/content/browser/renderer_host/media/video_capture_host_unittest.cc b/content/browser/renderer_host/media/video_capture_host_unittest.cc new file mode 100644 index 0000000..a7a56ab --- /dev/null +++ b/content/browser/renderer_host/media/video_capture_host_unittest.cc @@ -0,0 +1,396 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <map> +#include <string> + +#include "base/file_util.h" +#include "base/memory/scoped_ptr.h" +#include "base/message_loop.h" +#include "base/process_util.h" +#include "base/stl_util-inl.h" +#include "base/stringprintf.h" +#include "content/browser/browser_thread.h" +#include "content/browser/renderer_host/media/video_capture_host.h" +#include "content/browser/renderer_host/media/video_capture_manager.h" +#include "content/common/video_capture_messages.h" +#include "media/video/capture/video_capture_types.h" + +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using ::testing::_; +using ::testing::AtLeast; +using ::testing::AnyNumber; +using ::testing::DoAll; +using ::testing::InSequence; +using ::testing::Mock; +using ::testing::Return; + +// IPC::Msg.routing_id. +static const int32 kRouteId = 200; +// Id used to identify the capture session between renderer and +// video_capture_host. +static const int kDeviceId = 1; +// Id of a video capture device +static const media::VideoCaptureSessionId kTestFakeDeviceId = + media_stream::VideoCaptureManager::kStartOpenSessionId; + +// Define to enable test where video is dumped to file. +// #define DUMP_VIDEO + +// Define to use a real video capture device. +// #define TEST_REAL_CAPTURE_DEVICE + +// Simple class used for dumping video to a file. This can be used for +// verifying the output. +class DumpVideo { + public: + DumpVideo() : expected_size_(0) {} + void StartDump(int width, int height) { + // Create an FilePath that works on all platforms. There should + // be a better way. + FilePath file_name = + FilePath::FromWStringHack(StringPrintf(L"dump_w%d_h%d.yuv", width, + height)); + file_.reset(file_util::OpenFile(file_name, "wb")); + expected_size_ = width * height * 3 / 2; + } + void NewVideoFrame(const void* buffer) { + if (file_.get() != NULL) { + fwrite(buffer, expected_size_, 1, file_.get()); + } + } + + private: + file_util::ScopedFILE file_; + int expected_size_; +}; + +class MockVideoCaptureHost : public VideoCaptureHost { + public: + MockVideoCaptureHost() : return_buffers_(false), dump_video_(false) {} + virtual ~MockVideoCaptureHost() { + STLDeleteContainerPairSecondPointers(filled_dib_.begin(), + filled_dib_.end()); + } + + // A list of mock methods. + MOCK_METHOD5(OnNewBufferCreated, + void(int routing_id, int device_id, + base::SharedMemoryHandle handle, + int length, int buffer_id)); + MOCK_METHOD3(OnBufferFilled, + void(int routing_id, int device_id, int buffer_id)); + MOCK_METHOD3(OnStateChanged, + void(int routing_id, int device_id, + media::VideoCapture::State state)); + MOCK_METHOD2(OnDeviceInfo, void(int routing_id, int device_id)); + + // Use class DumpVideo to write I420 video to file. + void SetDumpVideo(bool enable) { + dump_video_ = enable; + } + void SetReturnReceviedDibs(bool enable) { + return_buffers_ = enable; + } + + // Return Dibs we currently have received. + void ReturnReceivedDibs(int device_id) { + int handle = GetReceivedDib(); + while (handle) { + IPC::Message msg; + msg.set_routing_id(kRouteId); + this->OnReceiveEmptyBuffer(msg, device_id, handle); + handle = GetReceivedDib(); + } + } + int GetReceivedDib() { + if (filled_dib_.empty()) + return 0; + std::map<int, base::SharedMemory*>::iterator it = filled_dib_.begin(); + int h = it->first; + delete it->second; + filled_dib_.erase(it); + + return h; + } + + private: + // This method is used to dispatch IPC messages to the renderer. We intercept + // these messages here and dispatch to our mock methods to verify the + // conversation between this object and the renderer. + virtual bool Send(IPC::Message* message) { + CHECK(message); + + // In this method we dispatch the messages to the according handlers as if + // we are the renderer. + bool handled = true; + IPC_BEGIN_MESSAGE_MAP(MockVideoCaptureHost, *message) + IPC_MESSAGE_HANDLER(VideoCaptureMsg_NewBuffer, OnNewBufferCreated) + IPC_MESSAGE_HANDLER(VideoCaptureMsg_BufferReady, OnBufferFilled) + IPC_MESSAGE_HANDLER(VideoCaptureMsg_StateChanged, OnStateChanged) + IPC_MESSAGE_HANDLER(VideoCaptureMsg_DeviceInfo, OnDeviceInfo) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + EXPECT_TRUE(handled); + + delete message; + return true; + } + + // These handler methods do minimal things and delegate to the mock methods. + void OnNewBufferCreated(const IPC::Message& msg, int device_id, + base::SharedMemoryHandle handle, + int length, int buffer_id) { + OnNewBufferCreated(msg.routing_id(), device_id, handle, length, buffer_id); + base::SharedMemory* dib = new base::SharedMemory(handle, false); + dib->Map(length); + filled_dib_[buffer_id] = dib; + } + + void OnBufferFilled(const IPC::Message& msg, int device_id, + int buffer_id) { + if (dump_video_) { + base::SharedMemory* dib = filled_dib_[buffer_id]; + ASSERT_TRUE(dib != NULL); + dumper_.NewVideoFrame(dib->memory()); + } + + OnBufferFilled(msg.routing_id(), device_id, buffer_id); + if (return_buffers_) { + VideoCaptureHost::OnReceiveEmptyBuffer(msg, device_id, buffer_id); + } + } + + void OnStateChanged(const IPC::Message& msg, int device_id, + media::VideoCapture::State state) { + OnStateChanged(msg.routing_id(), device_id, state); + } + + void OnDeviceInfo(const IPC::Message& msg, int device_id, + media::VideoCaptureParams params) { + if (dump_video_) { + dumper_.StartDump(params.width, params.height); + } + OnDeviceInfo(msg.routing_id(), device_id); + } + + std::map<int, base::SharedMemory*> filled_dib_; + bool return_buffers_; + bool dump_video_; + DumpVideo dumper_; +}; + +ACTION_P(ExitMessageLoop, message_loop) { + message_loop->PostTask(FROM_HERE, new MessageLoop::QuitTask()); +} + +class VideoCaptureHostTest : public testing::Test { + public: + VideoCaptureHostTest() {} + + protected: + virtual void SetUp() { + // Setup the VideoCaptureManager to use fake video capture device. +#ifndef TEST_REAL_CAPTURE_DEVICE + media_stream::VideoCaptureManager* manager = + media_stream::VideoCaptureManager::Get(); + manager->UseFakeDevice(); +#endif + // Create a message loop so VideoCaptureHostTest can use it. + message_loop_.reset(new MessageLoop(MessageLoop::TYPE_IO)); + io_thread_.reset(new BrowserThread(BrowserThread::IO, message_loop_.get())); + host_ = new MockVideoCaptureHost(); + + // Simulate IPC channel connected. + host_->OnChannelConnected(base::GetCurrentProcId()); + } + + virtual void TearDown() { + // Verifies and removes the expectations on host_ and + // returns true iff successful. + Mock::VerifyAndClearExpectations(host_); + + EXPECT_CALL(*host_, OnStateChanged(kRouteId, kDeviceId, + media::VideoCapture::kStopped)) + .Times(AnyNumber()); + + // Simulate closing the IPC channel. + host_->OnChannelClosing(); + + // Release the reference to the mock object. The object will be destructed + // on message_loop_. + host_ = NULL; + + // We need to continue running message_loop_ to complete all destructions. + SyncWithVideoCaptureManagerThread(); + + io_thread_.reset(); + } + + // Called on the VideoCaptureManager thread. + static void PostQuitMessageLoop(MessageLoop* message_loop) { + message_loop->PostTask(FROM_HERE, new MessageLoop::QuitTask()); + } + + // Called on the main thread. + static void PostQuitOnVideoCaptureManagerThread(MessageLoop* message_loop) { + media_stream::VideoCaptureManager::Get()->GetMessageLoop()->PostTask( + FROM_HERE, NewRunnableFunction(&PostQuitMessageLoop, message_loop)); + } + + // SyncWithVideoCaptureManagerThread() waits until all pending tasks on the + // video_capture_manager thread are executed while also processing pending + // task in message_loop_ on the current thread. It is used to synchronize + // with the video capture manager thread when we are stopping a video + // capture device. + void SyncWithVideoCaptureManagerThread() { + message_loop_->PostTask( + FROM_HERE, NewRunnableFunction(&PostQuitOnVideoCaptureManagerThread, + message_loop_.get())); + message_loop_->Run(); + } + + void StartCapture() { + InSequence s; + // 1. Newly created buffers will arrive. + EXPECT_CALL(*host_, OnNewBufferCreated(kRouteId, kDeviceId, _, _, _)) + .Times(AnyNumber()) + .WillRepeatedly(Return()); + + // 2. First - get info about the new resolution + EXPECT_CALL(*host_, OnDeviceInfo(kRouteId, kDeviceId)); + + // 3. Change state to started + EXPECT_CALL(*host_, OnStateChanged(kRouteId, kDeviceId, + media::VideoCapture::kStarted)); + + // 4. First filled buffer will arrive. + EXPECT_CALL(*host_, OnBufferFilled(kRouteId, kDeviceId, _)) + .Times(AnyNumber()) + .WillOnce(ExitMessageLoop(message_loop_.get())); + + IPC::Message msg; + msg.set_routing_id(kRouteId); + + media::VideoCaptureParams params; + params.width = 352; + params.height = 288; + params.frame_per_second = 30; + params.session_id = kTestFakeDeviceId; + host_->OnStartCapture(msg, kDeviceId, params); + message_loop_->Run(); + } + + void CaptureAndDumpVideo(int width, int heigt, int frame_rate) { + InSequence s; + // 1. First - get info about the new resolution + EXPECT_CALL(*host_, OnDeviceInfo(kRouteId, kDeviceId)); + + // 2. Change state to started + EXPECT_CALL(*host_, OnStateChanged(kRouteId, kDeviceId, + media::VideoCapture::kStarted)); + + // 3. First filled buffer will arrive. + EXPECT_CALL(*host_, OnBufferFilled(kRouteId, kDeviceId, _)) + .Times(AnyNumber()) + .WillOnce(ExitMessageLoop(message_loop_.get())); + + IPC::Message msg; + msg.set_routing_id(kRouteId); + + media::VideoCaptureParams params; + params.width = width; + params.height = heigt; + params.frame_per_second = frame_rate; + params.session_id = kTestFakeDeviceId; + host_->SetDumpVideo(true); + host_->OnStartCapture(msg, kDeviceId, params); + message_loop_->Run(); + } + + void StopCapture() { + EXPECT_CALL(*host_, OnStateChanged(kRouteId, kDeviceId, + media::VideoCapture::kStopped)) + .Times(AtLeast(1)); + + IPC::Message msg; + msg.set_routing_id(kRouteId); + host_->OnStopCapture(msg, kDeviceId); + host_->SetReturnReceviedDibs(true); + host_->ReturnReceivedDibs(kDeviceId); + + SyncWithVideoCaptureManagerThread(); + host_->SetReturnReceviedDibs(false); + // Expect the VideoCaptureDevice has been stopped + EXPECT_EQ(0u, host_->entries_.size()); + } + + void NotifyPacketReady() { + EXPECT_CALL(*host_, OnBufferFilled(kRouteId, kDeviceId, _)) + .Times(AnyNumber()) + .WillOnce(ExitMessageLoop(message_loop_.get())) + .RetiresOnSaturation(); + message_loop_->Run(); + } + + void ReturnReceivedPackets() { + host_->ReturnReceivedDibs(kDeviceId); + } + + void SimulateError() { + // Expect a change state to error state sent through IPC. + EXPECT_CALL(*host_, OnStateChanged(kRouteId, kDeviceId, + media::VideoCapture::kError)) + .Times(1); + VideoCaptureControllerID id(kRouteId, kDeviceId); + host_->OnError(id); + SyncWithVideoCaptureManagerThread(); + } + + scoped_refptr<MockVideoCaptureHost> host_; + private: + scoped_ptr<MessageLoop> message_loop_; + scoped_ptr<BrowserThread> io_thread_; + + DISALLOW_COPY_AND_ASSIGN(VideoCaptureHostTest); +}; + +TEST_F(VideoCaptureHostTest, StartCapture) { + StartCapture(); +} + +TEST_F(VideoCaptureHostTest, StartCapturePlayStop) { + StartCapture(); + NotifyPacketReady(); + NotifyPacketReady(); + ReturnReceivedPackets(); + StopCapture(); +} + +TEST_F(VideoCaptureHostTest, StartCaptureErrorStop) { + StartCapture(); + SimulateError(); + StopCapture(); +} + +TEST_F(VideoCaptureHostTest, StartCaptureError) { + EXPECT_CALL(*host_, OnStateChanged(kRouteId, kDeviceId, + media::VideoCapture::kStopped)) + .Times(0); + StartCapture(); + NotifyPacketReady(); + SimulateError(); + base::PlatformThread::Sleep(200); +} + +#ifdef DUMP_VIDEO +TEST_F(VideoCaptureHostTest, CaptureAndDumpVideoVga) { + CaptureAndDumpVideo(640, 480, 30); +} +TEST_F(VideoCaptureHostTest, CaptureAndDump720P) { + CaptureAndDumpVideo(1280, 720, 30); +} +#endif |