summaryrefslogtreecommitdiffstats
path: root/media
diff options
context:
space:
mode:
authorwjia@google.com <wjia@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2010-05-28 16:36:30 +0000
committerwjia@google.com <wjia@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2010-05-28 16:36:30 +0000
commitd665492fca06d19d2cd5abc5913f7eacd3ea558b (patch)
treec2b2abfd55feed8006d0e7efe5f22ce1073218b1 /media
parent98cfec7aec302ecfd1942df3b83c8e48add82413 (diff)
downloadchromium_src-d665492fca06d19d2cd5abc5913f7eacd3ea558b.zip
chromium_src-d665492fca06d19d2cd5abc5913f7eacd3ea558b.tar.gz
chromium_src-d665492fca06d19d2cd5abc5913f7eacd3ea558b.tar.bz2
Remove omx_codec since OMX client implementation is in omx_video_decode_engine. Update omx_test to use omx_video_decode_engine.
Contributed by wjia@chromium.org BUG=none TEST=tested on tegra Review URL: http://codereview.chromium.org/2255005 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@48492 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media')
-rw-r--r--media/media.gyp6
-rw-r--r--media/omx/omx_codec.cc1212
-rw-r--r--media/omx/omx_codec.h390
-rw-r--r--media/omx/omx_codec_unittest.cc1
-rw-r--r--media/tools/omx_test/omx_test.cc157
5 files changed, 90 insertions, 1676 deletions
diff --git a/media/media.gyp b/media/media.gyp
index c705823..6821aac 100644
--- a/media/media.gyp
+++ b/media/media.gyp
@@ -334,10 +334,12 @@
'dependencies': [
'../base/base.gyp:base',
'../third_party/openmax/openmax.gyp:il',
+ # TODO(wjia): remove ffmpeg
+ '../third_party/ffmpeg/ffmpeg.gyp:ffmpeg',
],
'sources': [
- 'omx/omx_codec.cc',
- 'omx/omx_codec.h',
+ 'filters/omx_video_decode_engine.cc',
+ 'filters/omx_video_decode_engine.cc',
'omx/omx_configurator.cc',
'omx/omx_configurator.h',
],
diff --git a/media/omx/omx_codec.cc b/media/omx/omx_codec.cc
deleted file mode 100644
index e0c55ed..0000000
--- a/media/omx/omx_codec.cc
+++ /dev/null
@@ -1,1212 +0,0 @@
-// 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.
-
-#include <algorithm>
-#include <string>
-
-#include "base/callback.h"
-#include "base/logging.h"
-#include "base/message_loop.h"
-#include "base/stl_util-inl.h"
-#include "base/string_util.h"
-#include "media/omx/omx_codec.h"
-#include "media/base/buffers.h"
-
-namespace media {
-
-#if !defined(COMPILER_MSVC)
-int const OmxCodec::kEosBuffer;
-#endif
-
-template <typename T>
-static void ResetPortHeader(const OmxCodec& dec, T* param) {
- memset(param, 0, sizeof(T));
- param->nVersion.nVersion = dec.current_omx_spec_version();
- param->nSize = sizeof(T);
-}
-
-OmxCodec::OmxCodec(MessageLoop* message_loop)
- : input_buffer_count_(0),
- input_buffer_size_(0),
- input_port_(0),
- input_eos_(false),
- output_buffer_count_(0),
- output_buffer_size_(0),
- output_port_(0),
- output_eos_(false),
- state_(kEmpty),
- next_state_(kEmpty),
- component_handle_(NULL),
- message_loop_(message_loop) {
-}
-
-OmxCodec::~OmxCodec() {
- DCHECK(state_ == kError || state_ == kEmpty);
- DCHECK_EQ(0u, input_buffers_.size());
- DCHECK_EQ(0u, output_buffers_.size());
- DCHECK(available_input_buffers_.empty());
- DCHECK(pending_input_queue_.empty());
- DCHECK(processing_input_queue_.empty());
-}
-
-void OmxCodec::Setup(OmxConfigurator* configurator,
- FeedDoneCallback* feed_done_callback,
- FillDoneCallback* fill_done_callback) {
- DCHECK_EQ(kEmpty, state_);
- CHECK(configurator);
- configurator_ = configurator;
- feed_done_callback_.reset(feed_done_callback);
- fill_done_callback_.reset(fill_done_callback);
-}
-
-void OmxCodec::SetErrorCallback(Callback* callback) {
- DCHECK_EQ(kEmpty, state_);
- error_callback_.reset(callback);
-}
-
-void OmxCodec::SetFormatCallback(FormatCallback* callback) {
- DCHECK_EQ(kEmpty, state_);
- format_callback_.reset(callback);
-}
-
-void OmxCodec::Start() {
- CHECK(configurator_);
-
- message_loop_->PostTask(
- FROM_HERE,
- NewRunnableMethod(this, &OmxCodec::StartTask));
-}
-
-void OmxCodec::Stop(Callback* callback) {
- message_loop_->PostTask(
- FROM_HERE,
- NewRunnableMethod(this, &OmxCodec::StopTask, callback));
-}
-
-void OmxCodec::Feed(scoped_refptr<Buffer> buffer) {
- message_loop_->PostTask(
- FROM_HERE,
- NewRunnableMethod(this, &OmxCodec::FeedTask, buffer));
-}
-
-void OmxCodec::Flush(Callback* callback) {
- callback->Run();
- delete callback;
-}
-
-OmxCodec::State OmxCodec::GetState() const {
- return state_;
-}
-
-void OmxCodec::SetState(State state) {
- state_ = state;
-}
-
-OmxCodec::State OmxCodec::GetNextState() const {
- return next_state_;
-}
-
-void OmxCodec::SetNextState(State state) {
- next_state_ = state;
-}
-
-void OmxCodec::StartTask() {
- DCHECK_EQ(message_loop_, MessageLoop::current());
-
- StateTransitionTask(kLoaded);
-}
-
-void OmxCodec::StopTask(Callback* callback) {
- DCHECK_EQ(message_loop_, MessageLoop::current());
-
- stop_callback_.reset(callback);
-
- if (GetState() == kError) {
- DoneStop();
- return;
- }
-
- FreeInputQueue();
-
- // TODO(hclam): We should wait for all output buffers to come back from
- // output sink to proceed to stop. The proper way to do this is
- // transition to a StopWaitingForBuffers state and wait until all buffers
- // are received to proceed.
-
- if (GetState() == kExecuting)
- StateTransitionTask(kIdle);
- // TODO(hclam): The following two transitions may not be correct.
- else if (GetState() == kPortSettingDisable)
- StateTransitionTask(kIdle);
- else if (GetState() == kPortSettingEnable)
- StateTransitionTask(kIdle);
- else if (GetState() == kIdle)
- StateTransitionTask(kLoaded);
- else if (GetState() == kLoaded)
- StateTransitionTask(kEmpty);
-}
-
-void OmxCodec::FeedTask(scoped_refptr<Buffer> buffer) {
- DCHECK_EQ(message_loop_, MessageLoop::current());
-
- if (!CanAcceptInput()) {
- feed_done_callback_->Run(buffer);
- return;
- }
-
- // Queue this input buffer.
- pending_input_queue_.push(buffer);
-
- // Try to feed buffers into the decoder.
- EmptyBufferTask();
-}
-
-// This method assumes OMX_AllocateBuffer() will allocate
-// buffer internally. If this is not the case we need to
-// call OMX_UseBuffer() to allocate buffer manually and
-// assign to the headers.
-bool OmxCodec::AllocateInputBuffers() {
- DCHECK_EQ(message_loop_, MessageLoop::current());
-
- uint8* data = new uint8[input_buffer_size_];
- scoped_array<uint8> data_deleter(data);
-
- for (int i = 0; i < input_buffer_count_; ++i) {
- OMX_BUFFERHEADERTYPE* buffer;
- OMX_ERRORTYPE error =
- OMX_UseBuffer(component_handle_, &buffer, input_port_,
- NULL, input_buffer_size_, data);
- if (error != OMX_ErrorNone)
- return false;
- input_buffers_.push_back(buffer);
- available_input_buffers_.push(buffer);
- }
- return true;
-}
-
-// This method assumes OMX_AllocateBuffer() will allocate buffer
-// header internally. In additional to that memory that holds the
-// header, the same method call will allocate memory for holding
-// output data. If we use EGL images for holding output data,
-// the memory allocation will be done externally.
-bool OmxCodec::AllocateOutputBuffers() {
- DCHECK_EQ(message_loop_, MessageLoop::current());
-
- for (int i = 0; i < output_buffer_count_; ++i) {
- OMX_BUFFERHEADERTYPE* buffer;
- OMX_ERRORTYPE error =
- OMX_AllocateBuffer(component_handle_, &buffer, output_port_,
- NULL, output_buffer_size_);
- if (error != OMX_ErrorNone)
- return false;
- output_buffers_.push_back(buffer);
- }
-
- return true;
-}
-
-void OmxCodec::FreeInputBuffers() {
- DCHECK_EQ(message_loop_, MessageLoop::current());
-
- // Calls to OMX to free buffers.
- for (size_t i = 0; i < input_buffers_.size(); ++i)
- OMX_FreeBuffer(component_handle_, input_port_, input_buffers_[i]);
- input_buffers_.clear();
-
- // Empty available buffer queue.
- while (!available_input_buffers_.empty()) {
- available_input_buffers_.pop();
- }
-}
-
-void OmxCodec::FreeOutputBuffers() {
- DCHECK_EQ(message_loop_, MessageLoop::current());
-
- // Calls to OMX to free buffers.
- for (size_t i = 0; i < output_buffers_.size(); ++i)
- OMX_FreeBuffer(component_handle_, output_port_, output_buffers_[i]);
- output_buffers_.clear();
-}
-
-void OmxCodec::FreeInputQueue() {
- DCHECK_EQ(message_loop_, MessageLoop::current());
-
- while (!pending_input_queue_.empty()) {
- scoped_refptr<Buffer> buffer = pending_input_queue_.front();
- feed_done_callback_->Run(buffer);
- pending_input_queue_.pop();
- }
-
- while (!processing_input_queue_.empty()) {
- processing_input_queue_.pop();
- }
-}
-
-// Sequence of actions in this transition:
-//
-// 1. Initialize OMX (To be removed.)
-// 2. Map role name to component name.
-// 3. Get handle of the OMX component
-// 4. Get the port information.
-// 5. Set role for the component.
-// 6. Input/output ports media format configuration.
-// 7. Obtain the information about the input port.
-// 8. Obtain the information about the output port.
-void OmxCodec::Transition_EmptyToLoaded() {
- DCHECK_EQ(message_loop_, MessageLoop::current());
- DCHECK_EQ(kEmpty, GetState());
-
- static OMX_CALLBACKTYPE callback = {
- &EventHandler,
- &EmptyBufferCallback,
- &FillBufferCallback
- };
-
- // 1. Initialize the OpenMAX Core.
- // TODO(hclam): move this out.
- OMX_ERRORTYPE omxresult = OMX_Init();
- if (omxresult != OMX_ErrorNone) {
- LOG(ERROR) << "Failed to Init OpenMAX core";
- StateTransitionTask(kError);
- return;
- }
-
- // 2. Map role name to component name.
- std::string role_name = configurator_->GetRoleName();
- OMX_U32 roles = 0;
- omxresult = OMX_GetComponentsOfRole(
- const_cast<OMX_STRING>(role_name.c_str()),
- &roles, 0);
- if (omxresult != OMX_ErrorNone || roles == 0) {
- LOG(ERROR) << "Unsupported Role: " << role_name.c_str();
- StateTransitionTask(kError);
- return;
- }
- const OMX_U32 kMaxRolePerComponent = 20;
- CHECK(roles < kMaxRolePerComponent);
-
- OMX_U8** component_names = new OMX_U8*[roles];
- const int kMaxComponentNameLength = 256;
- for (size_t i = 0; i < roles; ++i)
- component_names[i] = new OMX_U8[kMaxComponentNameLength];
-
- omxresult = OMX_GetComponentsOfRole(
- const_cast<OMX_STRING>(role_name.c_str()),
- &roles, component_names);
-
- // Use first component only. Copy the name of the first component
- // so that we could free the memory.
- std::string component_name;
- if (omxresult == OMX_ErrorNone)
- component_name = reinterpret_cast<char*>(component_names[0]);
-
- for (size_t i = 0; i < roles; ++i)
- delete [] component_names[i];
- delete [] component_names;
-
- if (omxresult != OMX_ErrorNone || roles == 0) {
- LOG(ERROR) << "Unsupported Role: " << role_name.c_str();
- StateTransitionTask(kError);
- return;
- }
-
- // 3. Get the handle to the component. After OMX_GetHandle(),
- // the component is in loaded state.
- OMX_STRING component = const_cast<OMX_STRING>(component_name.c_str());
- OMX_HANDLETYPE handle = reinterpret_cast<OMX_HANDLETYPE>(component_handle_);
- omxresult = OMX_GetHandle(&handle, component, this, &callback);
- component_handle_ = reinterpret_cast<OMX_COMPONENTTYPE*>(handle);
- if (omxresult != OMX_ErrorNone) {
- LOG(ERROR) << "Failed to Load the component: " << component;
- StateTransitionTask(kError);
- return;
- }
-
- // 4. Get the port information. This will obtain information about the
- // number of ports and index of the first port.
- OMX_PORT_PARAM_TYPE port_param;
- ResetPortHeader(*this, &port_param);
- omxresult = OMX_GetParameter(component_handle_, OMX_IndexParamVideoInit,
- &port_param);
- if (omxresult != OMX_ErrorNone) {
- LOG(ERROR) << "Failed to get Port Param";
- StateTransitionTask(kError);
- return;
- }
- input_port_ = port_param.nStartPortNumber;
- output_port_ = input_port_ + 1;
-
- // 5. Set role for the component because our component could
- // have multiple roles.
- OMX_PARAM_COMPONENTROLETYPE role_type;
- ResetPortHeader(*this, &role_type);
- base::strlcpy(reinterpret_cast<char*>(role_type.cRole),
- role_name.c_str(),
- OMX_MAX_STRINGNAME_SIZE);
- role_type.cRole[OMX_MAX_STRINGNAME_SIZE - 1] = '\0';
- omxresult = OMX_SetParameter(component_handle_,
- OMX_IndexParamStandardComponentRole,
- &role_type);
- if (omxresult != OMX_ErrorNone) {
- LOG(ERROR) << "Failed to Set Role";
- StateTransitionTask(kError);
- return;
- }
-
- // 6. Input/output ports media format configuration.
- if (!ConfigureIOPorts()) {
- LOG(ERROR) << "Media format configurations failed";
- StateTransitionTask(kError);
- return;
- }
-
- // 7. Obtain the information about the input port.
- // This will have the new mini buffer count in |port_format.nBufferCountMin|.
- // Save this value to input_buf_count.
- OMX_PARAM_PORTDEFINITIONTYPE port_format;
- ResetPortHeader(*this, &port_format);
- port_format.nPortIndex = input_port_;
- omxresult = OMX_GetParameter(component_handle_,
- OMX_IndexParamPortDefinition,
- &port_format);
- if (omxresult != OMX_ErrorNone) {
- LOG(ERROR) << "GetParameter(OMX_IndexParamPortDefinition) failed";
- StateTransitionTask(kError);
- return;
- }
- if (OMX_DirInput != port_format.eDir) {
- LOG(ERROR) << "Expected input port";
- StateTransitionTask(kError);
- return;
- }
- input_buffer_count_ = port_format.nBufferCountMin;
- input_buffer_size_ = port_format.nBufferSize;
-
- // 8. Obtain the information about the output port.
- ResetPortHeader(*this, &port_format);
- port_format.nPortIndex = output_port_;
- omxresult = OMX_GetParameter(component_handle_,
- OMX_IndexParamPortDefinition,
- &port_format);
- if (omxresult != OMX_ErrorNone) {
- LOG(ERROR) << "GetParameter(OMX_IndexParamPortDefinition) failed";
- StateTransitionTask(kError);
- return;
- }
- if (OMX_DirOutput != port_format.eDir) {
- LOG(ERROR) << "Expect Output Port";
- StateTransitionTask(kError);
- return;
- }
- output_buffer_count_ = port_format.nBufferCountMin;
- output_buffer_size_ = port_format.nBufferSize;
-
- // After we have done all the configurations, we are considered loaded.
- DoneStateTransitionTask();
-}
-
-// Sequence of actions in this transition:
-//
-// 1. Send command to Idle state.
-// 2. Allocate buffers for input port.
-// 3. Allocate buffers for output port.
-void OmxCodec::Transition_LoadedToIdle() {
- DCHECK_EQ(message_loop_, MessageLoop::current());
- DCHECK_EQ(kLoaded, GetState());
-
- // 1. Sets decoder to idle state.
- OMX_ERRORTYPE omxresult = OMX_SendCommand(component_handle_,
- OMX_CommandStateSet,
- OMX_StateIdle, 0);
- if (omxresult != OMX_ErrorNone) {
- LOG(ERROR) << "SendCommand(OMX_CommandStateSet) failed";
- StateTransitionTask(kError);
- return;
- }
-
- // 2. Allocate buffer for the input port.
- if (!AllocateInputBuffers()) {
- LOG(ERROR) << "OMX_AllocateBuffer() Input buffer error";
- StateTransitionTask(kError);
- return;
- }
-
- // 3. Allocate buffer for the output port.
- if (!AllocateOutputBuffers()) {
- LOG(ERROR) << "OMX_AllocateBuffer() Output buffer error";
- StateTransitionTask(kError);
- return;
- }
-}
-
-// Sequence of actions in this transition:
-//
-// 1. Send command to Executing state.
-void OmxCodec::Transition_IdleToExecuting() {
- DCHECK_EQ(message_loop_, MessageLoop::current());
- DCHECK_EQ(kIdle, GetState());
-
- // Transist to executing state.
- OMX_ERRORTYPE omxresult = OMX_SendCommand(component_handle_,
- OMX_CommandStateSet,
- OMX_StateExecuting, 0);
- if (omxresult != OMX_ErrorNone) {
- LOG(ERROR) << "SendCommand(OMX_CommandStateSet) failed";
- StateTransitionTask(kError);
- return;
- }
-
- // Simulate a format change.
- ReportFormatChange(configurator_->input_format(),
- configurator_->output_format());
-}
-
-// Sequence of actions in this transition:
-//
-// 1. Send command to disable output port.
-// 2. Free buffers of the output port.
-void OmxCodec::Transition_ExecutingToDisable() {
- DCHECK_EQ(message_loop_, MessageLoop::current());
- DCHECK_EQ(kExecuting, GetState());
-
- // Send DISABLE command.
- OMX_ERRORTYPE omxresult = OMX_SendCommand(component_handle_,
- OMX_CommandPortDisable,
- output_port_, 0);
- if (omxresult != OMX_ErrorNone) {
- LOG(ERROR) << "SendCommand(OMX_CommandPortDisable) failed";
- StateTransitionTask(kError);
- return;
- }
-
- // Free output Buffer.
- FreeOutputBuffers();
-}
-
-// Sequence of actions in this transition:
-//
-// 1. Send command to enable output port.
-// 2. Get parameter of the output port.
-// 3. Allocate buffers for the output port.
-void OmxCodec::Transition_DisableToEnable() {
- DCHECK_EQ(message_loop_, MessageLoop::current());
- DCHECK_EQ(kPortSettingDisable, GetState());
-
- // Send Enable command.
- OMX_ERRORTYPE omxresult = OMX_SendCommand(component_handle_,
- OMX_CommandPortEnable,
- output_port_, 0);
- if (omxresult != OMX_ErrorNone) {
- LOG(ERROR) << "SendCommand(OMX_CommandPortEnable) failed";
- StateTransitionTask(kError);
- return;
- }
-
- // AllocateBuffers.
- OMX_PARAM_PORTDEFINITIONTYPE port_format;
- ResetPortHeader(*this, &port_format);
- port_format.nPortIndex = output_port_;
- omxresult = OMX_GetParameter(component_handle_, OMX_IndexParamPortDefinition,
- &port_format);
- if (omxresult != OMX_ErrorNone) {
- LOG(ERROR) << "GetParameter(OMX_IndexParamPortDefinition) failed";
- StateTransitionTask(kError);
- return;
- }
- if (OMX_DirOutput != port_format.eDir) {
- LOG(ERROR) << "Expected Output Port";
- StateTransitionTask(kError);
- return;
- }
-
- // Update the output format.
- // TODO(jiesun): check if the format really change. ( we had omit some
- // information such as frame rate / bit rate / vbv buffer info now. )
- OmxConfigurator::MediaFormat input_format, output_format;
- output_format.video_header.height = port_format.format.video.nFrameHeight;
- output_format.video_header.width = port_format.format.video.nFrameWidth;
- output_format.video_header.stride = port_format.format.video.nStride;
- input_format.video_header.height = output_format.video_header.height;
- input_format.video_header.width = output_format.video_header.width;
- input_format.video_header.stride = output_format.video_header.stride;
- ReportFormatChange(input_format, output_format);
-
- // Update the ports in buffer.
- output_buffer_count_ = port_format.nBufferCountActual;
- output_buffer_size_ = port_format.nBufferSize;
- if (!AllocateOutputBuffers()) {
- LOG(ERROR) << "OMX_AllocateBuffer() Output buffer error";
- StateTransitionTask(kError);
- return;
- }
-}
-
-// Sequence of actions in this transition:
-//
-// 1. Send command to Idle state.
-void OmxCodec::Transition_DisableToIdle() {
- DCHECK_EQ(message_loop_, MessageLoop::current());
- DCHECK_EQ(kPortSettingDisable, GetState());
-
- OMX_ERRORTYPE omxresult = OMX_SendCommand(component_handle_,
- OMX_CommandStateSet,
- OMX_StateIdle, 0);
- if (omxresult != OMX_ErrorNone) {
- LOG(ERROR) << "SendCommand(OMX_CommandStateSet) failed";
- StateTransitionTask(kError);
- return;
- }
-}
-
-// Sequence of actions in this transition:
-//
-// This transition does nothing.
-void OmxCodec::Transition_EnableToExecuting() {
- DCHECK_EQ(message_loop_, MessageLoop::current());
- DCHECK_EQ(kPortSettingEnable, GetState());
-
- // This transition is fake, nothing to do here.
- DoneStateTransitionTask();
-}
-
-void OmxCodec::Transition_EnableToIdle() {
- DCHECK_EQ(message_loop_, MessageLoop::current());
- DCHECK_EQ(kPortSettingEnable, GetState());
-
- OMX_ERRORTYPE omxresult = OMX_SendCommand(component_handle_,
- OMX_CommandStateSet,
- OMX_StateIdle, 0);
- if (omxresult != OMX_ErrorNone) {
- LOG(ERROR) << "SendCommand(OMX_CommandStateSet) failed";
- StateTransitionTask(kError);
- return;
- }
-}
-
-// Sequence of actions in this transition:
-//
-// 1. Send command to Idle state.
-void OmxCodec::Transition_ExecutingToIdle() {
- DCHECK_EQ(message_loop_, MessageLoop::current());
- DCHECK_EQ(kExecuting, GetState());
-
- OMX_ERRORTYPE omxresult = OMX_SendCommand(component_handle_,
- OMX_CommandStateSet,
- OMX_StateIdle, 0);
- if (omxresult != OMX_ErrorNone) {
- LOG(ERROR) << "SendCommand(OMX_CommandStateSet) failed";
- StateTransitionTask(kError);
- return;
- }
-}
-
-// Sequence of actions in this transition:
-//
-// 1. Send command to Loaded state
-// 2. Free input buffers
-// 2. Free output buffers
-void OmxCodec::Transition_IdleToLoaded() {
- DCHECK_EQ(message_loop_, MessageLoop::current());
- DCHECK_EQ(kIdle, GetState());
-
- OMX_ERRORTYPE omxresult = OMX_SendCommand(component_handle_,
- OMX_CommandStateSet,
- OMX_StateLoaded, 0);
- if (omxresult != OMX_ErrorNone) {
- LOG(ERROR) << "SendCommand(OMX_CommandStateSet) failed";
- StateTransitionTask(kError);
- return;
- }
-
- FreeInputBuffers();
- FreeOutputBuffers();
-}
-
-// Sequence of actions in this transition:
-//
-// 1. Free decoder handle
-// 2. Uninitialize OMX (TODO(hclam): Remove this.)
-void OmxCodec::Transition_LoadedToEmpty() {
- DCHECK_EQ(message_loop_, MessageLoop::current());
- DCHECK_EQ(kLoaded, GetState());
-
- // Free the decoder handle.
- OMX_ERRORTYPE result = OMX_FreeHandle(component_handle_);
- if (result != OMX_ErrorNone) {
- LOG(ERROR) << "Terminate: OMX_FreeHandle() error. "
- << "Error code: " << result;
- }
- component_handle_ = NULL;
-
- // Deinit OpenMAX
- // TODO(hclam): move this out.
- OMX_Deinit();
-
- DoneStateTransitionTask();
-}
-
-// Sequence of actions in this transition:
-//
-// 1. Disable input port
-// 2. Disable output port
-// 3. Free input buffer
-// 4. Free output buffer
-// 5. Free decoder handle
-// 6. Uninitialize OMX (TODO(hclam): Remove this.)
-void OmxCodec::Transition_Error() {
- DCHECK_EQ(message_loop_, MessageLoop::current());
- DCHECK_NE(kError, GetState());
-
- State old_state = GetState();
- SetState(kError);
-
- // If we are going to error state in the following states, we need to
- // send a command to disable ports for us to free buffers.
- if (old_state == kExecuting || old_state == kIdle ||
- old_state == kPortSettingEnable || old_state == kPortSettingDisable) {
- DCHECK(component_handle_);
- OMX_SendCommand(component_handle_, OMX_CommandPortDisable, input_port_, 0);
- OMX_SendCommand(component_handle_, OMX_CommandPortDisable, output_port_, 0);
- }
-
- // Free input and output buffers.
- FreeInputBuffers();
- FreeOutputBuffers();
-
- // Free input queues.
- FreeInputQueue();
-
- // Free decoder handle.
- if (component_handle_) {
- OMX_ERRORTYPE result = OMX_FreeHandle(component_handle_);
- if (result != OMX_ErrorNone)
- LOG(ERROR) << "OMX_FreeHandle() error. Error code: " << result;
- component_handle_ = NULL;
- }
-
- // Deinit OpenMAX.
- OMX_Deinit();
-
- DoneStateTransitionTask();
-}
-
-void OmxCodec::PostStateTransitionTask(State new_state) {
- message_loop_->PostTask(
- FROM_HERE,
- NewRunnableMethod(this,
- &OmxCodec::StateTransitionTask, new_state));
-}
-
-void OmxCodec::StateTransitionTask(State new_state) {
- DCHECK_EQ(message_loop_, MessageLoop::current());
-
- if (GetState() == kError)
- return;
-
- // Save the next state.
- SetNextState(new_state);
-
- // The following list defines all the possible state transitions
- // for this object:
- //
- // TRANSITIONS
- // 1. Empty -> Loaded
- // 2. Loaded -> Idle
- // 3. Idle -> Executing
- // 4. Executing -> Disable
- // 5. Executing -> Idle
- // 6. Disable -> Enable
- // 7. Disable -> Idle
- // 8. Enable -> Executing
- // 9. Enable -> Idle
- // 10. Idle -> Loaded
- // 11. Loaded -> Empty (TODO(hclam): To stopped instead.)
- // 12. *ANYTHING* -> Error
- if (GetState() == kEmpty && new_state == kLoaded)
- Transition_EmptyToLoaded();
- else if (GetState() == kLoaded && new_state == kIdle)
- Transition_LoadedToIdle();
- else if (GetState() == kIdle && new_state == kExecuting)
- Transition_IdleToExecuting();
- else if (GetState() == kExecuting && new_state == kPortSettingDisable)
- Transition_ExecutingToDisable();
- else if (GetState() == kPortSettingDisable && new_state == kPortSettingEnable)
- Transition_DisableToEnable();
- else if (GetState() == kPortSettingDisable && new_state == kIdle)
- Transition_DisableToIdle();
- else if (GetState() == kPortSettingEnable && new_state == kExecuting)
- Transition_EnableToExecuting();
- else if (GetState() == kPortSettingEnable && new_state == kIdle)
- Transition_EnableToIdle();
- else if (GetState() == kExecuting && new_state == kIdle)
- Transition_ExecutingToIdle();
- else if (GetState() == kIdle && new_state == kLoaded)
- Transition_IdleToLoaded();
- else if (GetState() == kLoaded && new_state == kEmpty)
- Transition_LoadedToEmpty();
- else if (new_state == kError)
- Transition_Error();
-}
-
-void OmxCodec::PostDoneStateTransitionTask() {
- message_loop_->PostTask(
- FROM_HERE,
- NewRunnableMethod(this, &OmxCodec::DoneStateTransitionTask));
-}
-
-void OmxCodec::DoneStateTransitionTask() {
- DCHECK_EQ(message_loop_, MessageLoop::current());
-
- if (GetState() == kError) {
- ReportError();
- return;
- }
-
- // Save the current state and completes the transition.
- State old_state = GetState();
- SetState(GetNextState());
-
- // The following list is to perform a state transition automatically
- // based on the last transition done:
- //
- // LAST TRANSITION NEXT TRANSITION
- //
- // 1. Empty -> Loaded Laoded -> Idle
- // 2. Loaded -> Idle Idle -> Executing
- // 3. Idle -> Executing
- //
- // Because of the above reoute, once we kick start the transition
- // from empty to loaded, this method will automatically route it
- // executing eventually.
- //
- // The following sequence is for transition to the stopped state.
- //
- // LAST TRANSITION NEXT TRANSITION
- //
- // 4. Executing -> Idle Idle -> Loaded
- // 5. Idle -> Loaded Loaded -> Empty
- // TODO(hclam): should go to Stopped instead of Empty.
- //
- // During dynamic port seeting, the route of state transition is:
- //
- // LAST TRANSITION NEXT TRANSITION
- //
- // 6. Executing -> Disable Disable -> Enable
- // 7. Disable -> Enable Enable -> Executing
- if (old_state == kEmpty && GetState() == kLoaded)
- StateTransitionTask(kIdle);
- else if (old_state == kLoaded && GetState() == kIdle)
- StateTransitionTask(kExecuting);
- else if (old_state == kIdle && GetState() == kExecuting) {
- // TODO(hclam): It is a little too late to issue read requests.
- // This seems to introduce some latencies.
- InitialEmptyBuffer();
- InitialFillBuffer();
- }
- else if (old_state == kExecuting && GetState() == kPortSettingDisable)
- StateTransitionTask(kPortSettingEnable);
- else if (old_state == kPortSettingDisable && GetState() == kPortSettingEnable)
- StateTransitionTask(kExecuting);
- else if (old_state == kPortSettingEnable && GetState() == kExecuting)
- InitialFillBuffer();
- else if (old_state == kPortSettingDisable && GetState() == kIdle)
- StateTransitionTask(kLoaded);
- else if (old_state == kPortSettingEnable && GetState() == kIdle)
- StateTransitionTask(kLoaded);
- else if (old_state == kExecuting && GetState() == kIdle)
- StateTransitionTask(kLoaded);
- else if (old_state == kIdle && GetState() == kLoaded)
- StateTransitionTask(kEmpty);
- else if (old_state == kLoaded && GetState() == kEmpty)
- DoneStop();
- else {
- NOTREACHED() << "Invalid state transition";
- }
-}
-
-void OmxCodec::DoneStop() {
- DCHECK_EQ(message_loop_, MessageLoop::current());
-
- if (!stop_callback_.get())
- return;
- stop_callback_->Run();
- stop_callback_.reset();
-}
-
-void OmxCodec::ReportError() {
- DCHECK_EQ(message_loop_, MessageLoop::current());
-
- if (!error_callback_.get())
- return;
- error_callback_->Run();
- error_callback_.reset();
-}
-
-void OmxCodec::ReportFormatChange(
- const OmxConfigurator::MediaFormat& input_format,
- const OmxConfigurator::MediaFormat& output_format) {
- DCHECK_EQ(message_loop_, MessageLoop::current());
-
- if (!format_callback_.get())
- return;
- format_callback_->Run(input_format, output_format);
-}
-
-bool OmxCodec::ConfigureIOPorts() {
- OMX_PARAM_PORTDEFINITIONTYPE input_port_def, output_port_def;
- OMX_ERRORTYPE omxresult = OMX_ErrorNone;
- // Get default input port definition.
- ResetPortHeader(*this, &input_port_def);
- input_port_def.nPortIndex = input_port_;
- omxresult = OMX_GetParameter(component_handle_,
- OMX_IndexParamPortDefinition,
- &input_port_def);
- if (omxresult != OMX_ErrorNone) {
- LOG(ERROR) << "GetParameter(OMX_IndexParamPortDefinition) "
- << "for input port failed";
- return false;
- }
- if (OMX_DirInput != input_port_def.eDir) {
- LOG(ERROR) << "Expected Input Port";
- return false;
- }
-
- // Get default output port definition.
- ResetPortHeader(*this, &output_port_def);
- output_port_def.nPortIndex = output_port_;
- omxresult = OMX_GetParameter(component_handle_,
- OMX_IndexParamPortDefinition,
- &output_port_def);
- if (omxresult != OMX_ErrorNone) {
- LOG(ERROR) << "GetParameter(OMX_IndexParamPortDefinition) "
- << "for output port failed";
- return false;
- }
- if (OMX_DirOutput != output_port_def.eDir) {
- LOG(ERROR) << "Expected Output Port";
- return false;
- }
-
- return configurator_->ConfigureIOPorts(component_handle_,
- &input_port_def, &output_port_def);
-}
-
-bool OmxCodec::CanEmptyBuffer() {
- // We can call empty buffer while we are in executing or enabling / disabling
- // the output port.
- return (GetState() == kExecuting || GetState() == kPortSettingDisable ||
- GetState() == kPortSettingEnable) &&
- (GetNextState() == kExecuting || GetNextState() == kPortSettingDisable ||
- GetNextState() == kPortSettingEnable);
-}
-
-bool OmxCodec::CanFillBuffer() {
- // Make sure that we are staying in the executing state and end-of-stream
- // has not been reached.
- return GetState() == kExecuting && GetState() == GetNextState();
-}
-
-bool OmxCodec::CanAcceptInput() {
- // We can't take input buffer when in error state.
- // TODO(hclam): Reject when in stopped state.
- return GetState() != kError;
-}
-
-bool OmxCodec::CanAcceptOutput() {
- // Don't output request when in error state.
- // TODO(hclam): Reject when in stopped state.
- return GetState() != kError;
-}
-
-void OmxCodec::EmptyBufferCompleteTask(OMX_BUFFERHEADERTYPE* buffer) {
- DCHECK_EQ(message_loop_, MessageLoop::current());
-
- if (!CanEmptyBuffer())
- return;
-
- scoped_refptr<Buffer> stored_buffer = processing_input_queue_.front();
- processing_input_queue_.pop();
-
- DCHECK_EQ(const_cast<OMX_U8*>(stored_buffer.get()->GetData()),
- buffer->pBuffer);
-
- feed_done_callback_->Run(stored_buffer);
-
- // Enqueue the available buffer beacuse the decoder has consumed it.
- available_input_buffers_.push(buffer);
-
- // Try to feed more data into the decoder.
- EmptyBufferTask();
-}
-
-void OmxCodec::EmptyBufferTask() {
- DCHECK_EQ(message_loop_, MessageLoop::current());
-
- if (!CanEmptyBuffer())
- return;
-
- // Loop for all available input data and input buffer for the
- // decoder. When input has reached EOS we need to stop.
- while (!pending_input_queue_.empty() &&
- !available_input_buffers_.empty() &&
- !input_eos_) {
- scoped_refptr<Buffer> buffer = pending_input_queue_.front();
- pending_input_queue_.pop();
- processing_input_queue_.push(buffer);
-
- OMX_BUFFERHEADERTYPE* omx_buffer = available_input_buffers_.front();
- available_input_buffers_.pop();
-
- input_eos_ = buffer->IsEndOfStream();
-
- // setup |omx_buffer|.
- omx_buffer->nInputPortIndex = input_port_;
- omx_buffer->nOffset = 0;
- omx_buffer->nFlags = 0;
- omx_buffer->pBuffer = const_cast<OMX_U8*>(buffer.get()->GetData());
- omx_buffer->nFilledLen = buffer.get()->GetDataSize();
- omx_buffer->nAllocLen = omx_buffer->nFilledLen;
- omx_buffer->pAppPrivate = this;
- omx_buffer->nFlags |= input_eos_ ? OMX_BUFFERFLAG_EOS : 0;
- omx_buffer->nTimeStamp = buffer->GetTimestamp().InMilliseconds();
-
- // Give this buffer to OMX.
- OMX_ERRORTYPE ret = OMX_EmptyThisBuffer(component_handle_, omx_buffer);
- if (ret != OMX_ErrorNone) {
- LOG(ERROR) << "OMX_EmptyThisBuffer() failed with result " << ret;
- StateTransitionTask(kError);
- return;
- }
- }
-}
-
-void OmxCodec::FillBufferCompleteTask(OMX_BUFFERHEADERTYPE* buffer) {
- DCHECK_EQ(message_loop_, MessageLoop::current());
-
- // If we are not in a right state to receive this buffer then return
- // immediately.
- // This condition is hit when we disable the output port and we are
- // not in executing state. In that case ignore the buffer.
- // TODO(hclam): We should count the number of buffers received with
- // this condition to make sure the disable command has completed.
- if (!CanFillBuffer())
- return;
-
- // This buffer is received with decoded frame. Enqueue it and make it
- // ready to be consumed by reads.
- int buffer_id = kEosBuffer;
- for (size_t i = 0; output_buffers_.size(); ++i) {
- if (output_buffers_[i] == buffer) {
- buffer_id = i;
- break;
- }
- }
-
- // If the buffer received from the component doesn't exist in our
- // list then we have an error.
- if (buffer_id == kEosBuffer) {
- LOG(ERROR) << "Received an unknown output buffer";
- StateTransitionTask(kError);
- return;
- }
-
- // Determine if the buffer received is a end-of-stream buffer. If
- // the condition is true then assign a EOS id to the buffer.
- if (buffer->nFlags & OMX_BUFFERFLAG_EOS || !buffer->nFilledLen) {
- buffer_id = kEosBuffer;
- output_eos_ = true;
- }
- output_buffers_ready_.push(buffer_id);
-
- // Try to fulfill one read request.
- FulfillOneRead();
-}
-
-void OmxCodec::FulfillOneRead() {
- DCHECK_EQ(message_loop_, MessageLoop::current());
-
- if (!output_buffers_ready_.empty()) {
- int buffer_id = output_buffers_ready_.front();
- output_buffers_ready_.pop();
-
- // If the buffer is real then save it to the in-use list.
- // Otherwise if it is an end-of-stream buffer then just drop it.
- if (buffer_id != kEosBuffer) {
- fill_done_callback_->Run(output_buffers_[buffer_id]);
- BufferUsedCallback(buffer_id); //hack, we will change this really soon.
- } else {
- fill_done_callback_->Run(static_cast<OMX_BUFFERHEADERTYPE*>(NULL));
- }
- }
-}
-
-void OmxCodec::BufferUsedCallback(int buffer_id) {
- // If this method is called on the message loop where OmxCodec belongs, we
- // execute the task directly to save posting another task.
- if (message_loop_ == MessageLoop::current()) {
- BufferUsedTask(buffer_id);
- return;
- }
-
- message_loop_->PostTask(
- FROM_HERE,
- NewRunnableMethod(this, &OmxCodec::BufferUsedTask, buffer_id));
-}
-
-// Handling end-of-stream:
-// Note that after we first receive the end-of-stream, we'll continue
-// to call FillThisBuffer() with the next buffer receievd from
-// OmxOutputSink. The end result is we'll call at most
-// |output_buffer_count_| of FillThisBuffer() that are expected to
-// receive end-of-stream buffers from OpenMAX.
-// It is possible to not submit FillThisBuffer() after the first
-// end-of-stream buffer is received from OpenMAX, but that will complicate
-// the logic and so we rely on OpenMAX to do the right thing.
-void OmxCodec::BufferUsedTask(int buffer_id) {
- DCHECK_EQ(message_loop_, MessageLoop::current());
-
- // Make sure an end-of-stream buffer id is not received here.
- CHECK(buffer_id != kEosBuffer);
-
- // We'll try to issue more FillThisBuffer() to the decoder.
- // If we can't do it now then just return.
- if (!CanFillBuffer())
- return;
-
- CHECK(buffer_id >= 0 &&
- buffer_id < static_cast<int>(output_buffers_.size()));
- OMX_BUFFERHEADERTYPE* omx_buffer = output_buffers_[buffer_id];
-
- omx_buffer->nOutputPortIndex = output_port_;
- omx_buffer->nFlags &= ~OMX_BUFFERFLAG_EOS;
- omx_buffer->pAppPrivate = this;
- OMX_ERRORTYPE ret = OMX_FillThisBuffer(component_handle_, omx_buffer);
- if (OMX_ErrorNone != ret) {
- LOG(ERROR) << "OMX_FillThisBuffer() failed with result " << ret;
- StateTransitionTask(kError);
- return;
- }
-}
-
-void OmxCodec::InitialEmptyBuffer() {
- DCHECK_EQ(message_loop_, MessageLoop::current());
-
- if (!CanEmptyBuffer())
- return;
-
- // Use EmptyBuffer() to use available input buffers to feed decoder.
- EmptyBufferTask();
-}
-
-void OmxCodec::InitialFillBuffer() {
- DCHECK_EQ(message_loop_, MessageLoop::current());
-
- if (!CanFillBuffer())
- return;
-
- // Ask the decoder to fill the output buffers.
- for (size_t i = 0; i < output_buffers_.size(); ++i) {
- OMX_BUFFERHEADERTYPE* omx_buffer = output_buffers_[i];
- omx_buffer->nOutputPortIndex = output_port_;
- // Need to clear the EOS flag.
- omx_buffer->nFlags &= ~OMX_BUFFERFLAG_EOS;
- omx_buffer->pAppPrivate = this;
- OMX_ERRORTYPE ret = OMX_FillThisBuffer(component_handle_, omx_buffer);
-
- if (OMX_ErrorNone != ret) {
- LOG(ERROR) << "OMX_FillThisBuffer() failed with result " << ret;
- StateTransitionTask(kError);
- return;
- }
- }
-}
-
-void OmxCodec::EventHandlerInternal(OMX_HANDLETYPE component,
- OMX_EVENTTYPE event,
- OMX_U32 data1,
- OMX_U32 data2,
- OMX_PTR event_data) {
- switch(event) {
- case OMX_EventCmdComplete: {
- // If the last command was successful, we have completed
- // a state transition. So notify that we have done it
- // accordingly.
- OMX_COMMANDTYPE cmd = static_cast<OMX_COMMANDTYPE>(data1);
- if (cmd == OMX_CommandPortEnable) {
- PostDoneStateTransitionTask();
- } else if (cmd == OMX_CommandPortDisable) {
- PostDoneStateTransitionTask();
- } else if (cmd == OMX_CommandStateSet) {
- PostDoneStateTransitionTask();
- } else {
- LOG(ERROR) << "Unknown command completed\n";
- }
- break;
- }
- case OMX_EventError:
- if (OMX_ErrorInvalidState == (OMX_ERRORTYPE)data1) {
- // TODO(hclam): what to do here?
- }
- PostStateTransitionTask(kError);
- break;
- case OMX_EventPortSettingsChanged:
- PostStateTransitionTask(kPortSettingDisable);
- break;
- default:
- LOG(ERROR) << "Warning - Unknown event received\n";
- break;
- }
-}
-
-void OmxCodec::EmptyBufferCallbackInternal(
- OMX_HANDLETYPE component,
- OMX_BUFFERHEADERTYPE* buffer) {
- message_loop_->PostTask(
- FROM_HERE,
- NewRunnableMethod(this,
- &OmxCodec::EmptyBufferCompleteTask, buffer));
-}
-
-void OmxCodec::FillBufferCallbackInternal(
- OMX_HANDLETYPE component,
- OMX_BUFFERHEADERTYPE* buffer) {
- message_loop_->PostTask(
- FROM_HERE,
- NewRunnableMethod(this,
- &OmxCodec::FillBufferCompleteTask, buffer));
-}
-
-// static
-OMX_ERRORTYPE OmxCodec::EventHandler(OMX_HANDLETYPE component,
- OMX_PTR priv_data,
- OMX_EVENTTYPE event,
- OMX_U32 data1,
- OMX_U32 data2,
- OMX_PTR event_data) {
- OmxCodec* decoder = static_cast<OmxCodec*>(priv_data);
- decoder->EventHandlerInternal(component, event, data1, data2, event_data);
- return OMX_ErrorNone;
-}
-
-// static
-OMX_ERRORTYPE OmxCodec::EmptyBufferCallback(
- OMX_HANDLETYPE component,
- OMX_PTR priv_data,
- OMX_BUFFERHEADERTYPE* buffer) {
- OmxCodec* decoder = static_cast<OmxCodec*>(priv_data);
- decoder->EmptyBufferCallbackInternal(component, buffer);
- return OMX_ErrorNone;
-}
-
-// static
-OMX_ERRORTYPE OmxCodec::FillBufferCallback(
- OMX_HANDLETYPE component,
- OMX_PTR priv_data,
- OMX_BUFFERHEADERTYPE* buffer) {
- OmxCodec* decoder = static_cast<OmxCodec*>(priv_data);
- decoder->FillBufferCallbackInternal(component, buffer);
- return OMX_ErrorNone;
-}
-
-} // namespace media
diff --git a/media/omx/omx_codec.h b/media/omx/omx_codec.h
deleted file mode 100644
index 70e02db..0000000
--- a/media/omx/omx_codec.h
+++ /dev/null
@@ -1,390 +0,0 @@
-// 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.
-
-// TODO(ajwong): Generalize this class (fix comments, API, and extract
-// implemntation) so that it can be used for encoding & decoding of both
-// Video and Audio.
-//
-// An object that works with an OpenMAX component for video decoding.
-// Operations on this object are all asynchronous and this object
-// requires a message loop that it works on.
-//
-// OWNERSHIP
-//
-// The OmxCodec works with external objects
-// OmxConfigurator
-// This object is given to OmxCodec to perform port configuration.
-// This object is provided and destroyed externally. Its references
-// are given to OmxCodec and client application is responsible
-// for cleaning them.
-//
-// INTERACTION WITH EXTERNAL OBJECTS
-//
-// ................ ............
-// | Configurator | <------------- | OmxCodec |
-// ................ ............
-// Read / Feed -----------' '
-// .-----------' v Buffer Allocation
-// .......... ...............
-// | Client | ------------------> | OutputSink |
-// .......... Buffer Ready ...............
-//
-// THREADING
-//
-// OmxCodec is given a message loop to run on. There is a strong gurantee
-// that all callbacks given to it will be executed on this message loop.
-// Communicatations with OmxConfigurator and OmxOutputSink are also done
-// on this thread.
-//
-// Public methods can be called on any thread.
-//
-// USAGES
-//
-// // Initialization.
-// MessageLoop message_loop;
-// OmxCodec* decoder = new OmxCodec(&message_loop);
-//
-// OmxConfigurator::MediaFormat input_format, output_format;
-// input_format.codec = OmxCodec::kCodecH264;
-// output_format.codec = OmxCodec::kCodecRaw;
-// scoped_ptr<OmxConfigurator> configurator(
-// new OmxDecoderConfigurator(input_format, output_format));
-//
-// decoder->Setup(configurator.get());
-// decoder->SetErrorCallback(NewCallback(this, &Client::ErrorCallback));
-// decoder->SetFormatCallback(NewCallback(this, &Client::FormatCallback));
-//
-// // Start is asynchronous. We don't need to wait for it to proceed.
-// decoder->Start();
-//
-// // We can start giving buffer to the decoder right after start. It will
-// // queue the input buffers and output requests and process them until
-// // the decoder can actually process them.
-// for (int i = 0; i < kInitialBuffers; ++i) {
-// OmxInputBuffer* buffer = PrepareInitialInputBuffer();
-// decoder->Feed(buffer, NewCallback(this, &Client::FeedCallback));
-// }
-//
-// // We can also issue read requests to the decoder.
-// decoder->Read(NewCallback(this, &Client::ReadCallback));
-//
-// // Make the following call to stop the decoder:
-// decoder->Stop(NewCallback(this, &Client::StopCallback));
-//
-// A typical FeedCallback will look like:
-// void Client::FeedCallback(OmxInputBuffer* buffer) {
-// // We have read to the end so stop feeding.
-// if (buffer->IsEndOfStream())
-// return;
-// PrepareInputBuffer(buffer);
-// decoder->Feed(buffer, NewCallback(this, &Client::FeedCallback));
-// }
-//
-// A typical ReadCallback will look like:
-// void Client::ReadCallback(int buffer_id,
-// OmxOutputSink::BufferUsedCallback* callback) {
-// // Detect end-of-stream state.
-// if (buffer_id == OmxCodec::kEosBuffer)
-// return;
-//
-// // Issue a new read immediately.
-// decoder->Read(NewCallback(this, &Client::ReadCallback));
-//
-// // Pass the buffer to OmxOutputSink.
-// output_sink->BufferReady(buffer_id, callback);
-// }
-//
-// EXTERNAL STATES
-//
-// Client of this class will only see four states from the decoder:
-// .........
-// | Error |
-// .........
-// ^
-// `-.
-// ......... ......... ........
-// | Empty | -> | Start | -> | Stop |
-// ......... ......... ........
-//
-// How to operate this object in these four states can be described by
-// usage above.
-//
-// INTERNAL STATES
-//
-// There are multiple internal states to keep track of state transitions
-// of the OpenMAX component. The state transitions and the task during
-// the transition can be summerized by the following state diagram:
-//
-// ......... -> .......... -> ........ -> .............
-// | Empty | | Loaded | | Idle | | Executing |
-// ......... <- .......... <- ........ <- .............
-// ^ `
-// ` v
-// ......... ............. ..............
-// | Error | | Port Enable | | Port Disable |
-// ......... ............. ..............
-//
-// We need to perform specific tasks in order to transition from one state
-// to another. When an error is received, this object will transition to
-// the error state.
-
-#ifndef MEDIA_OMX_OMX_CODEC_H_
-#define MEDIA_OMX_OMX_CODEC_H_
-
-#include <queue>
-#include <vector>
-
-#include "base/callback.h"
-#include "base/scoped_ptr.h"
-#include "media/omx/omx_configurator.h"
-#include "third_party/openmax/il/OMX_Component.h"
-#include "third_party/openmax/il/OMX_Core.h"
-#include "third_party/openmax/il/OMX_Video.h"
-
-class MessageLoop;
-
-namespace media {
-
-class Buffer;
-
-class OmxCodec : public base::RefCountedThreadSafe<OmxCodec> {
- public:
- // TODO(jiesun): remove callback parameters.
- typedef Callback2<
- const OmxConfigurator::MediaFormat&,
- const OmxConfigurator::MediaFormat&>::Type FormatCallback;
- typedef Callback1<scoped_refptr<Buffer> >::Type FeedDoneCallback;
- typedef Callback1<OMX_BUFFERHEADERTYPE*>::Type FillDoneCallback;
- typedef Callback0::Type Callback;
-
- // Initialize an OmxCodec object that runs on |message_loop|. It is
- // guaranteed that callbacks are executed on this message loop.
- explicit OmxCodec(MessageLoop* message_loop);
- virtual ~OmxCodec();
-
- // Setup OmxCodec using |configurator|. |configurator| and |output_sink|
- // are not owned by this class and should be cleaned up externally.
- void Setup(OmxConfigurator* configurator,
- FeedDoneCallback* feed_done_callback,
- FillDoneCallback* fill_done_callback);
-
- // Set the error callback. In case of error the callback will be called.
- void SetErrorCallback(Callback* callback);
-
- // Set the format change callback. In case of input stream changes.
- void SetFormatCallback(FormatCallback* callback);
-
- // Start the decoder, this will start the initialization asynchronously.
- // Client can start feeding to and reading from the decoder.
- void Start();
-
- // Stop the decoder. When the decoder is fully stopped, |callback|
- // is called.
- void Stop(Callback* callback);
-
- // Feed the decoder with |buffer|. When the decoder has consumed the
- // buffer |feed_done_callback_| is called with |buffer|.
- void Feed(scoped_refptr<Buffer> buffer);
-
- // Flush the decoder and reset its end-of-stream state.
- void Flush(Callback* callback);
-
- // Subclass can provide a different value.
- virtual int current_omx_spec_version() const { return 0x00000101; }
-
- static const int kEosBuffer = -1;
-
- private:
- enum State {
- kEmpty,
- kLoaded,
- kIdle,
- kExecuting,
- kPortSettingEnable,
- kPortSettingDisable,
- kError,
- };
-
- // Getter and setter for the state.
- State GetState() const;
- void SetState(State state);
- State GetNextState() const;
- void SetNextState(State state);
-
- // Methods to be executed in |message_loop_|, they correspond to the
- // public methods.
- void StartTask();
- void StopTask(Callback* callback);
- void FeedTask(scoped_refptr<Buffer> buffer);
-
- // Helper method to perform tasks when this object is stopped.
- void DoneStop();
-
- // Helper method to call |error_callback_| after transition to error
- // state is done.
- void ReportError();
-
- // Helper method to call |format_callback_| after a format change.
- // used when decoder output port had done with port reconfigure and
- // return to enabled state.
- void ReportFormatChange(
- const OmxConfigurator::MediaFormat& input_format,
- const OmxConfigurator::MediaFormat& output_format);
-
- // Helper method to configure port format at LOADED state.
- bool ConfigureIOPorts();
-
- // Methods and free input and output buffers.
- bool AllocateInputBuffers();
- bool AllocateOutputBuffers();
- void FreeInputBuffers();
- void FreeOutputBuffers();
- void FreeInputQueue();
-
- // Transition methods define the specific tasks needs to be done
- // in order transition to the next state.
- void Transition_EmptyToLoaded();
- void Transition_LoadedToIdle();
- void Transition_IdleToExecuting();
- void Transition_ExecutingToDisable();
- void Transition_DisableToEnable();
- void Transition_DisableToIdle();
- void Transition_EnableToExecuting();
- void Transition_EnableToIdle();
- void Transition_ExecutingToIdle();
- void Transition_IdleToLoaded();
- void Transition_LoadedToEmpty();
- void Transition_Error();
-
- // State transition routines. They control which task to perform based
- // on the current state and the next state.
- void PostStateTransitionTask(State state);
- void StateTransitionTask(State state);
-
- // This method does an automatic state transition after the last
- // state transition was completed. For example, after the decoder
- // has transitioned from kEmpty to kLoaded, this method will order
- // transition from kLoaded to kIdle.
- void PostDoneStateTransitionTask();
- void DoneStateTransitionTask();
-
- // Determine whether we can issue fill buffer or empty buffer
- // to the decoder based on the current state and next state.
- bool CanFillBuffer();
- bool CanEmptyBuffer();
-
- // Determine whether we can use |input_queue_| and |output_queue_|
- // based on the current state.
- bool CanAcceptInput();
- bool CanAcceptOutput();
-
- // Methods to handle incoming (encoded) buffers.
- void EmptyBufferCompleteTask(OMX_BUFFERHEADERTYPE* buffer);
- void EmptyBufferTask();
-
- // Methods to handle outgoing (decoded) buffers.
- void FillBufferCompleteTask(OMX_BUFFERHEADERTYPE* buffer);
-
- // Take on decoded buffer to fulfill one read request.
- void FulfillOneRead();
-
- // Callback method to be called from a buffer output sink.
- // BufferUsedTask() is the corresponding task that runs on
- // |message_loop_|.
- void BufferUsedCallback(int buffer_id);
- void BufferUsedTask(int buffer_id);
-
- // Methods that do initial reads to kick start the decoding process.
- void InitialFillBuffer();
- void InitialEmptyBuffer();
-
- // Member functions to handle events from the OMX component. They
- // are called on the thread that the OMX component runs on, thus
- // it is not safe to perform any operations on them. They simply
- // post a task on |message_loop_| to do the actual work.
- void EventHandlerInternal(OMX_HANDLETYPE component,
- OMX_EVENTTYPE event,
- OMX_U32 data1, OMX_U32 data2,
- OMX_PTR event_data);
-
- void EmptyBufferCallbackInternal(OMX_HANDLETYPE component,
- OMX_BUFFERHEADERTYPE* buffer);
-
- void FillBufferCallbackInternal(OMX_HANDLETYPE component,
- OMX_BUFFERHEADERTYPE* buffer);
-
- // The following three methods are static callback methods
- // for the OMX component. When these callbacks are received, the
- // call is delegated to the three internal methods above.
- static OMX_ERRORTYPE EventHandler(OMX_HANDLETYPE component,
- OMX_PTR priv_data,
- OMX_EVENTTYPE event,
- OMX_U32 data1, OMX_U32 data2,
- OMX_PTR event_data);
-
- static OMX_ERRORTYPE EmptyBufferCallback(OMX_HANDLETYPE component,
- OMX_PTR priv_data,
- OMX_BUFFERHEADERTYPE* buffer);
-
- static OMX_ERRORTYPE FillBufferCallback(OMX_HANDLETYPE component,
- OMX_PTR priv_data,
- OMX_BUFFERHEADERTYPE* buffer);
-
- std::vector<OMX_BUFFERHEADERTYPE*> input_buffers_;
- int input_buffer_count_;
- int input_buffer_size_;
- int input_port_;
- bool input_eos_;
-
- std::vector<OMX_BUFFERHEADERTYPE*> output_buffers_;
- int output_buffer_count_;
- int output_buffer_size_;
- int output_port_;
- bool output_eos_;
-
- // |state_| records the current state. During state transition
- // |next_state_| is the next state that this machine will transition
- // to. After a state transition is completed and the state becomes
- // stable then |next_state_| equals |state_|. Inequality can be
- // used to detect a state transition.
- // These two members are read and written only on |message_loop_|.
- State state_;
- State next_state_;
-
- OMX_COMPONENTTYPE* component_handle_;
- OmxConfigurator* configurator_;
- MessageLoop* message_loop_;
-
- scoped_ptr<FormatCallback> format_callback_;
- scoped_ptr<Callback> stop_callback_;
- scoped_ptr<Callback> error_callback_;
- scoped_ptr<FeedDoneCallback> feed_done_callback_;
- scoped_ptr<FillDoneCallback> fill_done_callback_;
-
- // Input queue for encoded data.
- // The input buffers will be sent to OMX component via OMX_EmptyThisBuffer()
- std::queue<scoped_refptr<Buffer> > pending_input_queue_;
-
- // Input queue for encoded data.
- // Those buffers have been sent to OMX component, but not returned
- // by EmptyBufferDone callback. Once returned from OMX component, they
- // will be returned to owner.
- std::queue<scoped_refptr<Buffer> > processing_input_queue_;
-
- // Available input OpenMAX buffers that we can use to issue
- // OMX_EmptyThisBuffer() call.
- std::queue<OMX_BUFFERHEADERTYPE*> available_input_buffers_;
-
- // A queue of buffers that carries decoded video frames. They are
- // ready to return to client.
- // TOOD(hclam): extract it to a separate class.
- std::queue<int> output_buffers_ready_;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(OmxCodec);
-};
-
-} // namespace media
-
-#endif // MEDIA_OMX_OMX_CODEC_H_
diff --git a/media/omx/omx_codec_unittest.cc b/media/omx/omx_codec_unittest.cc
index 35d6381..97aaff8 100644
--- a/media/omx/omx_codec_unittest.cc
+++ b/media/omx/omx_codec_unittest.cc
@@ -10,7 +10,6 @@
#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::_;
diff --git a/media/tools/omx_test/omx_test.cc b/media/tools/omx_test/omx_test.cc
index f12db14..560e6d6 100644
--- a/media/tools/omx_test/omx_test.cc
+++ b/media/tools/omx_test/omx_test.cc
@@ -14,37 +14,42 @@
#include "base/message_loop.h"
#include "base/scoped_ptr.h"
#include "base/time.h"
+#include "media/base/data_buffer.h"
#include "media/base/media.h"
+#include "media/base/video_frame.h"
#include "media/ffmpeg/ffmpeg_common.h"
#include "media/ffmpeg/file_protocol.h"
#include "media/filters/bitstream_converter.h"
-#include "media/omx/omx_codec.h"
-#include "media/base/data_buffer.h"
+#include "media/filters/omx_video_decode_engine.h"
#include "media/tools/omx_test/color_space_util.h"
#include "media/tools/omx_test/file_reader_util.h"
#include "media/tools/omx_test/file_sink.h"
using media::BlockFileReader;
+using media::Buffer;
+using media::DataBuffer;
using media::FFmpegFileReader;
using media::FileReader;
using media::FileSink;
using media::H264FileReader;
-using media::OmxCodec;
using media::OmxConfigurator;
using media::OmxDecoderConfigurator;
using media::OmxEncoderConfigurator;
+using media::OmxVideoDecodeEngine;
+using media::VideoFrame;
using media::YuvFileReader;
-using media::Buffer;
-using media::DataBuffer;
// This is the driver object to feed the decoder with data from a file.
// It also provides callbacks for the decoder to receive events from the
// decoder.
-class TestApp {
+// TODO(wjia): AVStream should be replaced with a new structure which is
+// neutral to any video decoder. Also change media.gyp correspondingly.
+class TestApp : public base::RefCountedThreadSafe<TestApp> {
public:
- TestApp(OmxConfigurator* configurator, FileSink* file_sink,
+ TestApp(AVStream* av_stream,
+ FileSink* file_sink,
FileReader* file_reader)
- : configurator_(configurator),
+ : av_stream_(av_stream),
file_reader_(file_reader),
file_sink_(file_sink),
stopped_(false),
@@ -65,6 +70,9 @@ class TestApp {
return true;
}
+ void InitializeDoneCallback() {
+ }
+
void StopCallback() {
// If this callback is received, mark the |stopped_| flag so that we don't
// feed more buffers into the decoder.
@@ -98,7 +106,7 @@ class TestApp {
input_format.video_header.height);
}
- void FeedCompleteCallback(scoped_refptr<Buffer> buffer) {
+ void FeedDoneCallback(scoped_refptr<Buffer> buffer) {
// We receive this callback when the decoder has consumed an input buffer.
// In this case, delete the previous buffer and enqueue a new one.
// There are some conditions we don't want to enqueue, for example when
@@ -109,9 +117,9 @@ class TestApp {
FeedInputBuffer();
}
- void ReadCompleteCallback(OMX_BUFFERHEADERTYPE* buffer) {
+ void DecodeDoneCallback(scoped_refptr<VideoFrame> frame) {
// This callback is received when the decoder has completed a decoding
- // task and given us some output data. The buffer is owned by the decoder.
+ // task and given us some output data. The frame is owned by the decoder.
if (stopped_ || error_)
return;
@@ -119,13 +127,18 @@ class TestApp {
first_sample_delivered_time_ = base::TimeTicks::HighResNow();
// If we are readding to the end, then stop.
- if (buffer == NULL) {
- codec_->Stop(NewCallback(this, &TestApp::StopCallback));
+ if (frame.get() == NULL) {
+ engine_->Stop(NewCallback(this, &TestApp::StopCallback));
return;
}
- if (file_sink_.get())
- file_sink_->BufferReady(buffer->nFilledLen, buffer->pBuffer);
+ if (file_sink_.get()) {
+ for (size_t i = 0; i < frame->planes(); i++) {
+ int plane_size = frame->width() * frame->height();
+ if (i > 0) plane_size >>= 2;
+ file_sink_->BufferReady(plane_size, frame->data(i));
+ }
+ }
// could OMX IL return patial sample for decoder?
frame_count_++;
@@ -135,23 +148,22 @@ class TestApp {
uint8* data;
int read;
file_reader_->Read(&data, &read);
- codec_->Feed(new DataBuffer(data, read));
+ engine_->EmptyThisBuffer(new DataBuffer(data, read));
}
void Run() {
StartProfiler();
- // Setup the |codec_| with the message loop of the current thread. Also
- // setup component name, codec format and callbacks.
- codec_ = new OmxCodec(&message_loop_);
- codec_->Setup(configurator_.get(),
- NewCallback(this, &TestApp::FeedCompleteCallback),
- NewCallback(this, &TestApp::ReadCompleteCallback));
- codec_->SetErrorCallback(NewCallback(this, &TestApp::ErrorCallback));
- codec_->SetFormatCallback(NewCallback(this, &TestApp::FormatCallback));
-
- // Start the |codec_|.
- codec_->Start();
+ // Setup the |engine_| with the message loop of the current thread. Also
+ // setup codec format and callbacks.
+ engine_ = new OmxVideoDecodeEngine();
+ engine_->Initialize(&message_loop_,
+ av_stream_.get(),
+ NewCallback(this, &TestApp::FeedDoneCallback),
+ NewCallback(this, &TestApp::DecodeDoneCallback),
+ NewRunnableMethod(this,
+ &TestApp::InitializeDoneCallback));
+
for (int i = 0; i < 20; ++i)
FeedInputBuffer();
@@ -184,9 +196,9 @@ class TestApp {
printf("\n");
}
- scoped_refptr<OmxCodec> codec_;
+ scoped_refptr<OmxVideoDecodeEngine> engine_;
MessageLoop message_loop_;
- scoped_ptr<OmxConfigurator> configurator_;
+ scoped_ptr<AVStream> av_stream_;
scoped_ptr<FileReader> file_reader_;
scoped_ptr<FileSink> file_sink_;
@@ -214,43 +226,48 @@ static int GetIntSwitch(const char* name) {
return 0;
}
-static bool PrepareDecodeFormats(OmxConfigurator::MediaFormat* input,
- OmxConfigurator::MediaFormat* output) {
+static bool PrepareDecodeFormats(AVStream *av_stream) {
std::string codec = GetStringSwitch("codec");
- input->codec = OmxConfigurator::kCodecNone;
+ av_stream->codec->codec_id = CODEC_ID_NONE;
if (codec == "h264") {
- input->codec = OmxConfigurator::kCodecH264;
+ av_stream->codec->codec_id = CODEC_ID_H264;
} else if (codec == "mpeg4") {
- input->codec = OmxConfigurator::kCodecMpeg4;
+ av_stream->codec->codec_id = CODEC_ID_MPEG4;
} else if (codec == "h263") {
- input->codec = OmxConfigurator::kCodecH263;
+ av_stream->codec->codec_id = CODEC_ID_H263;
} else if (codec == "vc1") {
- input->codec = OmxConfigurator::kCodecVc1;
+ av_stream->codec->codec_id = CODEC_ID_VC1;
} else {
LOG(ERROR) << "Unknown codec.";
return false;
}
- output->codec = OmxConfigurator::kCodecRaw;
return true;
}
-static bool PrepareEncodeFormats(OmxConfigurator::MediaFormat* input,
- OmxConfigurator::MediaFormat* output) {
- input->codec = OmxConfigurator::kCodecRaw;
- input->video_header.width = GetIntSwitch("width");
- input->video_header.height = GetIntSwitch("height");
- input->video_header.frame_rate = GetIntSwitch("framerate");
- // TODO(jiesun): make other format available.
- output->codec = OmxConfigurator::kCodecMpeg4;
- output->video_header.width = GetIntSwitch("width");
- output->video_header.height = GetIntSwitch("height");
- output->video_header.frame_rate = GetIntSwitch("framerate");
+static bool PrepareEncodeFormats(AVStream *av_stream) {
+ av_stream->codec->width = GetIntSwitch("width");
+ av_stream->codec->height = GetIntSwitch("height");
+ av_stream->avg_frame_rate.num = GetIntSwitch("framerate");
+ av_stream->avg_frame_rate.den = 1;
+
+ std::string codec = GetStringSwitch("codec");
+ av_stream->codec->codec_id = CODEC_ID_NONE;
+ if (codec == "h264") {
+ av_stream->codec->codec_id = CODEC_ID_H264;
+ } else if (codec == "mpeg4") {
+ av_stream->codec->codec_id = CODEC_ID_MPEG4;
+ } else if (codec == "h263") {
+ av_stream->codec->codec_id = CODEC_ID_H263;
+ } else if (codec == "vc1") {
+ av_stream->codec->codec_id = CODEC_ID_VC1;
+ } else {
+ LOG(ERROR) << "Unknown codec.";
+ return false;
+ }
// TODO(jiesun): assume constant bitrate now.
- output->video_header.bit_rate = GetIntSwitch("bitrate");
- // TODO(jiesun): one I frame per second now. make it configurable.
- output->video_header.i_dist = output->video_header.frame_rate;
- // TODO(jiesun): disable B frame now. does they support it?
- output->video_header.p_dist = 0;
+ av_stream->codec->bit_rate = GetIntSwitch("bitrate");
+
+ // TODO(wjia): add more configurations needed by encoder
return true;
}
@@ -328,21 +345,26 @@ int main(int argc, char** argv) {
return -1;
}
- // Set the media formats for I/O.
- OmxConfigurator::MediaFormat input, output;
- memset(&input, 0, sizeof(input));
- memset(&output, 0, sizeof(output));
+ // Create AVStream
+ AVStream *av_stream = new AVStream;
+ AVCodecContext *av_codec_context = new AVCodecContext;
+ memset(av_stream, 0, sizeof(AVStream));
+ memset(av_codec_context, 0, sizeof(AVCodecContext));
+ scoped_ptr<AVCodecContext> av_codec_context_deleter(av_codec_context);
+ av_stream->codec = av_codec_context;
+ av_codec_context->width = 320;
+ av_codec_context->height = 240;
if (encoder)
- PrepareEncodeFormats(&input, &output);
+ PrepareEncodeFormats(av_stream);
else
- PrepareDecodeFormats(&input, &output);
+ PrepareDecodeFormats(av_stream);
// Creates the FileReader to read input file.
FileReader* file_reader;
if (encoder) {
file_reader = new YuvFileReader(
- input_filename.c_str(), input.video_header.width,
- input.video_header.height, loop_count, enable_csc);
+ input_filename.c_str(), av_stream->codec->width,
+ av_stream->codec->height, loop_count, enable_csc);
} else if (use_ffmpeg) {
// Use ffmepg for reading.
file_reader = new FFmpegFileReader(input_filename.c_str());
@@ -354,25 +376,18 @@ int main(int argc, char** argv) {
file_reader = new BlockFileReader(input_filename.c_str(), kReadSize);
}
- // Create the configurator.
- OmxConfigurator* configurator;
- if (encoder)
- configurator = new OmxEncoderConfigurator(input, output);
- else
- configurator = new OmxDecoderConfigurator(input, output);
-
// Create a file sink.
FileSink* file_sink = new FileSink(output_filename, copy, enable_csc);
// Create a test app object and initialize it.
- TestApp test(configurator, file_sink, file_reader);
- if (!test.Initialize()) {
+ scoped_refptr<TestApp> test = new TestApp(av_stream, file_sink, file_reader);
+ if (!test->Initialize()) {
LOG(ERROR) << "can't initialize this application";
return -1;
}
// This will run the decoder until EOS is reached or an error
// is encountered.
- test.Run();
+ test->Run();
return 0;
}