diff options
author | wjia@google.com <wjia@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-05-28 16:36:30 +0000 |
---|---|---|
committer | wjia@google.com <wjia@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-05-28 16:36:30 +0000 |
commit | d665492fca06d19d2cd5abc5913f7eacd3ea558b (patch) | |
tree | c2b2abfd55feed8006d0e7efe5f22ce1073218b1 /media | |
parent | 98cfec7aec302ecfd1942df3b83c8e48add82413 (diff) | |
download | chromium_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.gyp | 6 | ||||
-rw-r--r-- | media/omx/omx_codec.cc | 1212 | ||||
-rw-r--r-- | media/omx/omx_codec.h | 390 | ||||
-rw-r--r-- | media/omx/omx_codec_unittest.cc | 1 | ||||
-rw-r--r-- | media/tools/omx_test/omx_test.cc | 157 |
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; } |