summaryrefslogtreecommitdiffstats
path: root/media/omx/omx_codec_unittest.cc
diff options
context:
space:
mode:
authorhclam@chromium.org <hclam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-02-17 19:03:59 +0000
committerhclam@chromium.org <hclam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-02-17 19:03:59 +0000
commit19ffd67a4c32136d1d2abc07c97dcbb7ec5af216 (patch)
tree15ed2566ef839af4e218fc1d96880a9d1dc5ac44 /media/omx/omx_codec_unittest.cc
parent483a075709bb435ddf380aac050dc3b7a586d244 (diff)
downloadchromium_src-19ffd67a4c32136d1d2abc07c97dcbb7ec5af216.zip
chromium_src-19ffd67a4c32136d1d2abc07c97dcbb7ec5af216.tar.gz
chromium_src-19ffd67a4c32136d1d2abc07c97dcbb7ec5af216.tar.bz2
API to allow strategy class to work on the output buffer of OpenMAX
Added interface of OmxOutputSink to interact with OmxCodec to perform buffer negotiation and buffer read signaling. TEST=media_unittests --gtest_filter=Omx* BUG=32753 BUG=32754 BUG=32870 Review URL: http://codereview.chromium.org/593047 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@39247 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media/omx/omx_codec_unittest.cc')
-rw-r--r--media/omx/omx_codec_unittest.cc490
1 files changed, 490 insertions, 0 deletions
diff --git a/media/omx/omx_codec_unittest.cc b/media/omx/omx_codec_unittest.cc
new file mode 100644
index 0000000..1d3df2c
--- /dev/null
+++ b/media/omx/omx_codec_unittest.cc
@@ -0,0 +1,490 @@
+// Copyright (c) 2010 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.
+
+#define _CRT_SECURE_NO_WARNINGS
+
+#include <deque>
+
+#include "base/message_loop.h"
+#include "media/base/mock_filters.h"
+#include "media/omx/mock_omx.h"
+#include "media/omx/omx_codec.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::InSequence;
+using ::testing::IsNull;
+using ::testing::NotNull;
+using ::testing::Return;
+using ::testing::SaveArg;
+using ::testing::SetArgumentPointee;
+using ::testing::StrEq;
+using ::testing::StrictMock;
+
+namespace {
+
+const int kBufferCount = 3;
+const int kBufferSize = 4096;
+const char* kRoleName = "awsome";
+const char* kComponentName = "OMX.google.mock.awsome";
+
+} // namespace
+
+namespace media {
+
+ACTION(ReturnComponentName) {
+ strcpy(((char**)arg2)[0], kComponentName);
+}
+
+ACTION(GetHandle) {
+ *arg0 = MockOmx::get()->component();
+ MockOmx::get()->component()->pApplicationPrivate = arg2;
+ memcpy(MockOmx::get()->callbacks(), arg3, sizeof(OMX_CALLBACKTYPE));
+}
+
+ACTION(GetParameterVideoInit) {
+ ((OMX_PORT_PARAM_TYPE*)arg1)->nStartPortNumber = 0;
+}
+
+ACTION(GetParameterPortDefinition) {
+ OMX_PARAM_PORTDEFINITIONTYPE* port_format =
+ (OMX_PARAM_PORTDEFINITIONTYPE*)arg1;
+ CHECK(port_format->nPortIndex == 0 || port_format->nPortIndex == 1);
+ if (port_format->nPortIndex == 0)
+ port_format->eDir = OMX_DirInput;
+ else
+ port_format->eDir = OMX_DirOutput;
+ port_format->nBufferCountMin = kBufferCount;
+ port_format->nBufferSize = kBufferSize;
+}
+
+ACTION(AllocateBuffer) {
+ *arg0 = new OMX_BUFFERHEADERTYPE();
+ memset(*arg0, 0, sizeof(OMX_BUFFERHEADERTYPE));
+ (*arg0)->pBuffer = new uint8[kBufferSize];
+}
+
+ACTION(FreeBuffer) {
+ delete [] arg1->pBuffer;
+ delete arg1;
+}
+
+ACTION_P2(SendEvent, event, data1) {
+ // TODO(hclam): pass data2 and event data.
+ (*MockOmx::get()->callbacks()->EventHandler)(
+ MockOmx::get()->component(),
+ MockOmx::get()->component()->pApplicationPrivate,
+ event, static_cast<OMX_U32>(data1), 0, NULL);
+}
+
+ACTION(FillBuffer) {
+ arg0->nFlags = 0;
+ arg0->nFilledLen = kBufferSize;
+ (*MockOmx::get()->callbacks()->FillBufferDone)(
+ MockOmx::get()->component(),
+ MockOmx::get()->component()->pApplicationPrivate,
+ arg0);
+}
+
+ACTION(FillEosBuffer) {
+ arg0->nFlags = OMX_BUFFERFLAG_EOS;
+ arg0->nFilledLen = 0;
+ (*MockOmx::get()->callbacks()->FillBufferDone)(
+ MockOmx::get()->component(),
+ MockOmx::get()->component()->pApplicationPrivate,
+ arg0);
+}
+
+class MockOmxConfigurator : public OmxConfigurator {
+ public:
+ MockOmxConfigurator()
+ : OmxConfigurator(MediaFormat(), MediaFormat()) {}
+
+ MOCK_CONST_METHOD0(GetRoleName, std::string());
+ MOCK_CONST_METHOD3(ConfigureIOPorts,
+ bool(OMX_COMPONENTTYPE* component,
+ OMX_PARAM_PORTDEFINITIONTYPE* input_fef,
+ OMX_PARAM_PORTDEFINITIONTYPE* output_def));
+};
+
+class MockOmxOutputSink : public OmxOutputSink {
+ public:
+ MOCK_CONST_METHOD0(ProvidesEGLImages, bool());
+ MOCK_METHOD3(AllocateEGLImages,
+ bool(int width, int height,
+ std::vector<EGLImageKHR>* images));
+ MOCK_METHOD1(ReleaseEGLImages,
+ void(const std::vector<EGLImageKHR>& images));
+ MOCK_METHOD2(UseThisBuffer, void(int buffer_id,
+ OMX_BUFFERHEADERTYPE* buffer));
+ MOCK_METHOD1(StopUsingThisBuffer, void(int buffer_id));
+ MOCK_METHOD2(BufferReady, void(int buffer_id,
+ BufferUsedCallback* callback));
+};
+
+class OmxCodecTest : public testing::Test {
+ public:
+ OmxCodecTest ()
+ : omx_codec_(new OmxCodec(&message_loop_)) {
+ omx_codec_->Setup(&mock_configurator_, &mock_output_sink_);
+ }
+
+ ~OmxCodecTest() {
+ CHECK(output_units_.size() == 0);
+ }
+
+ protected:
+ void ExpectSettings() {
+ // Return the component name.
+ EXPECT_CALL(*MockOmx::get(), GetComponentsOfRole(_, _, IsNull()))
+ .WillOnce(DoAll(SetArgumentPointee<1>(1),
+ Return(OMX_ErrorNone)));
+ EXPECT_CALL(*MockOmx::get(), GetComponentsOfRole(_, _, NotNull()))
+ .WillOnce(DoAll(SetArgumentPointee<1>(1),
+ ReturnComponentName(),
+ Return(OMX_ErrorNone)));
+
+ // Handle get parameter calls.
+ EXPECT_CALL(*MockOmx::get(),
+ GetParameter(OMX_IndexParamVideoInit, NotNull()))
+ .WillRepeatedly(DoAll(GetParameterVideoInit(), Return(OMX_ErrorNone)));
+ EXPECT_CALL(*MockOmx::get(),
+ GetParameter(OMX_IndexParamPortDefinition, NotNull()))
+ .WillRepeatedly(DoAll(GetParameterPortDefinition(),
+ Return(OMX_ErrorNone)));
+
+ // Ignore all set parameter calls.
+ EXPECT_CALL(*MockOmx::get(), SetParameter(_, _))
+ .WillRepeatedly(Return(OMX_ErrorNone));
+
+ // Expect calling to configurator once.
+ EXPECT_CALL(mock_configurator_,
+ ConfigureIOPorts(MockOmx::get()->component(), _, _))
+ .WillOnce(Return(true));
+
+ EXPECT_CALL(mock_configurator_, GetRoleName())
+ .WillRepeatedly(Return(kRoleName));
+ }
+
+ void ExpectToLoaded() {
+ InSequence s;
+
+ // Expect initialization.
+ EXPECT_CALL(*MockOmx::get(), Init())
+ .WillOnce(Return(OMX_ErrorNone));
+
+ // Return the handle.
+ EXPECT_CALL(*MockOmx::get(),
+ GetHandle(NotNull(), StrEq(kComponentName),
+ NotNull(), NotNull()))
+ .WillOnce(DoAll(GetHandle(),
+ Return(OMX_ErrorNone)));
+ }
+
+ void ExpectLoadedToIdle() {
+ InSequence s;
+
+ // Expect transition to idle.
+ EXPECT_CALL(*MockOmx::get(),
+ SendCommand(OMX_CommandStateSet, OMX_StateIdle, _))
+ .WillOnce(
+ DoAll(
+ SendEvent(OMX_EventCmdComplete, OMX_CommandStateSet),
+ Return(OMX_ErrorNone)));
+
+ // Expect allocation of buffers.
+ EXPECT_CALL(*MockOmx::get(),
+ AllocateBuffer(NotNull(), 0, IsNull(), kBufferSize))
+ .Times(kBufferCount)
+ .WillRepeatedly(DoAll(AllocateBuffer(), Return(OMX_ErrorNone)));
+
+ // Don't support EGL images in this case.
+ EXPECT_CALL(mock_output_sink_, ProvidesEGLImages())
+ .WillOnce(Return(false));
+
+ // Expect allocation of output buffers and send command complete.
+ EXPECT_CALL(*MockOmx::get(),
+ AllocateBuffer(NotNull(), 1, IsNull(), kBufferSize))
+ .Times(kBufferCount)
+ .WillRepeatedly(DoAll(AllocateBuffer(), Return(OMX_ErrorNone)));
+
+ // The allocate output buffers will then be passed to output sink.
+ for (int i = 0; i < kBufferCount; ++i) {
+ EXPECT_CALL(mock_output_sink_, UseThisBuffer(i, _));
+ }
+ }
+
+ void ExpectToExecuting() {
+ InSequence s;
+
+ // Expect transition to executing.
+ EXPECT_CALL(*MockOmx::get(),
+ SendCommand(OMX_CommandStateSet, OMX_StateExecuting, _))
+ .WillOnce(DoAll(
+ SendEvent(OMX_EventCmdComplete, OMX_CommandStateSet),
+ Return(OMX_ErrorNone)));
+
+ // Expect initial FillThisBuffer() calls.
+ EXPECT_CALL(*MockOmx::get(), FillThisBuffer(NotNull()))
+ .Times(kBufferCount)
+ .WillRepeatedly(DoAll(FillBuffer(), Return(OMX_ErrorNone)));
+ }
+
+ void ExpectToIdle() {
+ // Expect going to idle
+ EXPECT_CALL(*MockOmx::get(),
+ SendCommand(OMX_CommandStateSet, OMX_StateIdle, _))
+ .WillOnce(DoAll(
+ SendEvent(OMX_EventCmdComplete, OMX_CommandStateSet),
+ Return(OMX_ErrorNone)));
+ }
+
+ void ExpectIdleToLoaded() {
+ InSequence s;
+
+ // Expect transition to loaded.
+ EXPECT_CALL(*MockOmx::get(),
+ SendCommand(OMX_CommandStateSet, OMX_StateLoaded, _))
+ .WillOnce(DoAll(
+ SendEvent(OMX_EventCmdComplete, OMX_CommandStateSet),
+ Return(OMX_ErrorNone)));
+
+ // Expect free buffer for input port.
+ EXPECT_CALL(*MockOmx::get(), FreeBuffer(0, NotNull()))
+ .Times(kBufferCount)
+ .WillRepeatedly(DoAll(FreeBuffer(), Return(OMX_ErrorNone)));
+
+ // Expect free output buffer.
+ for (int i = 0; i < kBufferCount; ++i) {
+ EXPECT_CALL(mock_output_sink_, StopUsingThisBuffer(i));
+ }
+
+ EXPECT_CALL(*MockOmx::get(), FreeBuffer(1, NotNull()))
+ .Times(kBufferCount)
+ .WillRepeatedly(DoAll(FreeBuffer(), Return(OMX_ErrorNone)));
+
+ // Report that the sink don't provide EGL images.
+ EXPECT_CALL(mock_output_sink_, ProvidesEGLImages())
+ .WillOnce(Return(false));
+ }
+
+ void ExpectToEmpty() {
+ InSequence s;
+
+ EXPECT_CALL(*MockOmx::get(), FreeHandle(MockOmx::get()->component()))
+ .WillOnce(Return(OMX_ErrorNone));
+ EXPECT_CALL(*MockOmx::get(), Deinit())
+ .WillOnce(Return(OMX_ErrorNone));
+ }
+
+ // TODO(hclam): Make a more generic about when to stop.
+ void ExpectStart() {
+ ExpectToLoaded();
+ ExpectLoadedToIdle();
+ ExpectToExecuting();
+ }
+
+ void ExpectStop() {
+ ExpectToIdle();
+ ExpectIdleToLoaded();
+ ExpectToEmpty();
+ }
+
+ void ReadCallback(int buffer_id,
+ OmxOutputSink::BufferUsedCallback* callback) {
+ output_units_.push_back(std::make_pair(buffer_id, callback));
+ }
+
+ void MakeReadRequest() {
+ omx_codec_->Read(NewCallback(this, &OmxCodecTest::ReadCallback));
+ }
+
+ void SaveFillThisBuffer(OMX_BUFFERHEADERTYPE* buffer) {
+ fill_this_buffer_received_.push_back(buffer);
+ }
+
+ void ExpectAndSaveFillThisBuffer() {
+ EXPECT_CALL(*MockOmx::get(), FillThisBuffer(NotNull()))
+ .WillOnce(DoAll(Invoke(this, &OmxCodecTest::SaveFillThisBuffer),
+ Return(OMX_ErrorNone)))
+ .RetiresOnSaturation();
+ }
+
+ typedef std::pair<int, OmxOutputSink::BufferUsedCallback*> OutputUnit;
+ std::deque<OutputUnit> output_units_;
+ std::deque<OMX_BUFFERHEADERTYPE*> fill_this_buffer_received_;
+
+ MockOmx mock_omx_;
+ MockOmxConfigurator mock_configurator_;
+ MockOmxOutputSink mock_output_sink_;
+
+ MessageLoop message_loop_;
+ scoped_refptr<OmxCodec> omx_codec_;
+
+ MockFilterCallback stop_callback_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(OmxCodecTest);
+};
+
+TEST_F(OmxCodecTest, SimpleStartAndStop) {
+ ExpectSettings();
+ ExpectStart();
+ omx_codec_->Start();
+ message_loop_.RunAllPending();
+
+ EXPECT_CALL(stop_callback_, OnFilterCallback());
+ EXPECT_CALL(stop_callback_, OnCallbackDestroyed());
+ ExpectStop();
+ omx_codec_->Stop(stop_callback_.NewCallback());
+ message_loop_.RunAllPending();
+}
+
+TEST_F(OmxCodecTest, EndOfStream) {
+ ExpectSettings();
+ ExpectStart();
+ omx_codec_->Start();
+ message_loop_.RunAllPending();
+
+ // Make read requests, OmxCodec should have gotten kBufferCount
+ // buffers already.
+ EXPECT_EQ(0u, output_units_.size());
+ for (int i = 0; i < kBufferCount; ++i) {
+ MakeReadRequest();
+ }
+ message_loop_.RunAllPending();
+ CHECK(kBufferCount == static_cast<int>(output_units_.size()));
+
+ // Make sure buffers received are in order.
+ for (int i = 0; i < kBufferCount; ++i) {
+ EXPECT_EQ(i, output_units_[i].first);
+ EXPECT_TRUE(output_units_[i].second != NULL);
+ }
+
+ // Give buffers back to OmxCodec. OmxCodec will make a new
+ // FillThisBuffer() call for each read.
+ for (int i = kBufferCount - 1; i >= 0; --i) {
+ EXPECT_CALL(*MockOmx::get(), FillThisBuffer(NotNull()))
+ .WillOnce(DoAll(FillEosBuffer(), Return(OMX_ErrorNone)))
+ .RetiresOnSaturation();
+ output_units_[i].second->Run(output_units_[i].first);
+ delete output_units_[i].second;
+ }
+ output_units_.clear();
+
+ // Make some read requests and make sure end-of-stream buffer id
+ // are received.
+ EXPECT_EQ(0u, output_units_.size());
+ for (int i = 0; i < 2 * kBufferCount; ++i) {
+ MakeReadRequest();
+ }
+ message_loop_.RunAllPending();
+ EXPECT_EQ(2 * kBufferCount, static_cast<int>(output_units_.size()));
+
+ for (size_t i = 0; i <output_units_.size(); ++i) {
+ EXPECT_EQ(OmxCodec::kEosBuffer, output_units_[i].first);
+ EXPECT_EQ(NULL, output_units_[i].second);
+ }
+ output_units_.clear();
+
+ // Stop OmxCodec.
+ EXPECT_CALL(stop_callback_, OnFilterCallback());
+ EXPECT_CALL(stop_callback_, OnCallbackDestroyed());
+ ExpectStop();
+ omx_codec_->Stop(stop_callback_.NewCallback());
+ message_loop_.RunAllPending();
+}
+
+TEST_F(OmxCodecTest, OutputFlowControl) {
+ ExpectSettings();
+ ExpectStart();
+ omx_codec_->Start();
+ message_loop_.RunAllPending();
+
+ // Since initial FillThisBuffer() calls are made and fulfilled during
+ // start. Reads issued to OmxCodec will be fulfilled now.
+ EXPECT_EQ(0u, output_units_.size());
+ for (int i = 0; i < kBufferCount; ++i) {
+ MakeReadRequest();
+ }
+ message_loop_.RunAllPending();
+ CHECK(kBufferCount == static_cast<int>(output_units_.size()));
+
+ // Make sure buffers received are in order.
+ for (int i = 0; i < kBufferCount; ++i) {
+ EXPECT_EQ(i, output_units_[i].first);
+ EXPECT_TRUE(output_units_[i].second != NULL);
+ }
+
+ // Give the buffer back in reverse order.
+ for (int i = kBufferCount - 1; i >= 0; --i) {
+ ExpectAndSaveFillThisBuffer();
+ output_units_[i].second->Run(output_units_[i].first);
+ delete output_units_[i].second;
+ }
+ output_units_.clear();
+
+ // In each loop, perform the following actions:
+ // 1. Make a read request to OmxCodec.
+ // 2. Fake a response for FillBufferDone().
+ // 3. Expect read response received.
+ // 4. Give the buffer read back to OmxCodec.
+ // 5. Expect a FillThisBuffer() is called to OpenMAX.
+ for (int i = 0; i < kBufferCount; ++i) {
+ // 1. First make a read request.
+ MakeReadRequest();
+
+ // 2. Then fake a response from OpenMAX.
+ OMX_BUFFERHEADERTYPE* buffer = fill_this_buffer_received_.front();
+ fill_this_buffer_received_.pop_front();
+ buffer->nFlags = 0;
+ buffer->nFilledLen = kBufferSize;
+ (*MockOmx::get()->callbacks()->FillBufferDone)(
+ MockOmx::get()->component(),
+ MockOmx::get()->component()->pApplicationPrivate,
+ buffer);
+
+ // Make sure actions are completed.
+ message_loop_.RunAllPending();
+
+ // 3. Expect read response received.
+ // The above action will cause a read callback be called and we should
+ // receive one buffer now. Also expect the buffer id be received in
+ // reverse order.
+ EXPECT_EQ(1u, output_units_.size());
+ EXPECT_EQ(kBufferCount - i - 1, output_units_.front().first);
+
+ // 4. Since a buffer is given back to OmxCodec. A FillThisBuffer() is called
+ // to OmxCodec.
+ EXPECT_CALL(*MockOmx::get(), FillThisBuffer(NotNull()))
+ .WillOnce(Return(OMX_ErrorNone))
+ .RetiresOnSaturation();
+
+ // 5. Give this buffer back to OmxCodec.
+ output_units_.front().second->Run(output_units_.front().first);
+ delete output_units_.front().second;
+ output_units_.pop_front();
+
+ // Make sure actions are completed.
+ message_loop_.RunAllPending();
+ }
+
+ // Now issue kBufferCount reads to OmxCodec.
+ EXPECT_EQ(0u, output_units_.size());
+
+ // Stop OmxCodec.
+ EXPECT_CALL(stop_callback_, OnFilterCallback());
+ EXPECT_CALL(stop_callback_, OnCallbackDestroyed());
+ ExpectStop();
+ omx_codec_->Stop(stop_callback_.NewCallback());
+ message_loop_.RunAllPending();
+}
+
+// TODO(hclam): Add test case for dynamic port config.
+// TODO(hclam): Create a more complicated test case so that read
+// requests and reply from FillThisBuffer() arrives out of order.
+// TODO(hclam): Add test case for Feed().
+
+} // namespace media