summaryrefslogtreecommitdiffstats
path: root/content/browser/renderer_host/media/video_capture_host_unittest.cc
diff options
context:
space:
mode:
authorwjia@chromium.org <wjia@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-06-14 22:07:34 +0000
committerwjia@chromium.org <wjia@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-06-14 22:07:34 +0000
commitba164c912c0c30dc432d9385b92266a5f732f538 (patch)
tree32196e1754531f18568e86a5c4ac5b625caf7037 /content/browser/renderer_host/media/video_capture_host_unittest.cc
parent3fbf1cb96804988bbc37ffcd329b1878e14f4971 (diff)
downloadchromium_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.cc396
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