Index: media/omx/omx_codec.cc |
diff --git a/media/omx/omx_codec.cc b/media/omx/omx_codec.cc |
deleted file mode 100644 |
index e0c55ede06f9aed2d8706f823f619d15bd171130..0000000000000000000000000000000000000000 |
--- 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 |