summaryrefslogtreecommitdiffstats
path: root/media/omx/omx_codec_unittest.cc
diff options
context:
space:
mode:
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