diff options
author | akalin@chromium.org <akalin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-09-02 23:45:48 +0000 |
---|---|---|
committer | akalin@chromium.org <akalin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-09-02 23:45:48 +0000 |
commit | 175e676451c214e4082d7304c37113aedf69a1f8 (patch) | |
tree | 85ce8cc71e728b6183949cd144ccbff220e3618b /media/video/omx_video_decode_engine.cc | |
parent | ef0a1d9f2e665e5e33a2566caec6e588f2e3fc4a (diff) | |
download | chromium_src-175e676451c214e4082d7304c37113aedf69a1f8.zip chromium_src-175e676451c214e4082d7304c37113aedf69a1f8.tar.gz chromium_src-175e676451c214e4082d7304c37113aedf69a1f8.tar.bz2 |
Revert 58423 - Refactor video decode engines to move them to a new folder
Moving video decode engines and friends to media/video.
TEST=Tree is green. Video plays.
Review URL: http://codereview.chromium.org/3127027
TBR=hclam@chromium.org
Review URL: http://codereview.chromium.org/3361004
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@58428 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media/video/omx_video_decode_engine.cc')
-rw-r--r-- | media/video/omx_video_decode_engine.cc | 1339 |
1 files changed, 0 insertions, 1339 deletions
diff --git a/media/video/omx_video_decode_engine.cc b/media/video/omx_video_decode_engine.cc deleted file mode 100644 index b6d7fea..0000000 --- a/media/video/omx_video_decode_engine.cc +++ /dev/null @@ -1,1339 +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. - -// This class interacts with OmxCodec and the VideoDecoderImpl -// in the media pipeline. -// -// THREADING SEMANTICS -// -// This class is created by VideoDecoderImpl and lives on the thread -// that VideoDecoderImpl lives. This class is given the message loop -// for the above thread. The OMX callbacks are guaranteed to be -// executed on the hosting message loop. This essentially means that -// all methods in this class are executed on the same thread as -// VideoDecoderImpl. Because of that there's no need for locking anywhere. - -#include "media/video/omx_video_decode_engine.h" - -#include "base/logging.h" -#include "base/message_loop.h" -#include "base/string_util.h" -#include "media/base/buffers.h" - -namespace media { - -OmxVideoDecodeEngine::OmxVideoDecodeEngine() - : width_(16), - height_(16), - message_loop_(NULL), - input_buffer_count_(0), - input_buffer_size_(0), - input_port_(0), - input_buffers_at_component_(0), - input_pending_request_(0), - input_queue_has_eos_(false), - input_has_fed_eos_(false), - input_port_flushed_(false), - output_buffer_count_(0), - output_buffer_size_(0), - output_port_(0), - output_buffers_at_component_(0), - output_pending_request_(0), - output_eos_(false), - output_port_flushed_(false), - il_state_(kIlNone), - expected_il_state_(kIlNone), - client_state_(kClientNotInitialized), - component_handle_(NULL), - need_free_input_buffers_(false), - need_free_output_buffers_(false), - flush_pending_(false), - output_frames_allocated_(false), - need_setup_output_port_(false) { - // TODO(wjia): change uses_egl_image_ to runtime setup -#if ENABLE_EGLIMAGE == 1 - uses_egl_image_ = true; - DLOG(INFO) << "Uses egl image for output"; -#else - uses_egl_image_ = false; - DLOG(INFO) << "Uses system memory for output"; -#endif -} - -OmxVideoDecodeEngine::~OmxVideoDecodeEngine() { - DCHECK(client_state_ == kClientNotInitialized || - client_state_ == kClientStopped); - DCHECK_EQ(il_state_, kIlNone); - DCHECK_EQ(0u, input_buffers_.size()); - DCHECK(free_input_buffers_.empty()); - DCHECK(available_input_buffers_.empty()); - DCHECK_EQ(0, input_buffers_at_component_); - DCHECK_EQ(0, output_buffers_at_component_); - DCHECK(output_frames_.empty()); -} - -template <typename T> -static void ResetParamHeader(const OmxVideoDecodeEngine& dec, T* param) { - memset(param, 0, sizeof(T)); - param->nVersion.nVersion = dec.current_omx_spec_version(); - param->nSize = sizeof(T); -} - -void OmxVideoDecodeEngine::Initialize( - MessageLoop* message_loop, - VideoDecodeEngine::EventHandler* event_handler, - const VideoCodecConfig& config) { - DCHECK_EQ(message_loop, MessageLoop::current()); - - message_loop_ = message_loop; - event_handler_ = event_handler; - - width_ = config.width_; - height_ = config.height_; - - // TODO(wjia): Find the right way to determine the codec type. - OmxConfigurator::MediaFormat input_format, output_format; - memset(&input_format, 0, sizeof(input_format)); - memset(&output_format, 0, sizeof(output_format)); - input_format.codec = OmxConfigurator::kCodecH264; - output_format.codec = OmxConfigurator::kCodecRaw; - configurator_.reset( - new OmxDecoderConfigurator(input_format, output_format)); - - // TODO(jiesun): We already ensure Initialize() is called in thread context, - // We should try to merge the following function into this function. - client_state_ = kClientInitializing; - InitializeTask(); - - VideoCodecInfo info; - // TODO(jiesun): ridiculous, we never fail initialization? - info.success_ = true; - info.provides_buffers_ = !uses_egl_image_; - info.stream_info_.surface_type_ = - uses_egl_image_ ? VideoFrame::TYPE_EGL_IMAGE - : VideoFrame::TYPE_SYSTEM_MEMORY; - info.stream_info_.surface_format_ = GetSurfaceFormat(); - info.stream_info_.surface_width_ = config.width_; - info.stream_info_.surface_height_ = config.height_; - event_handler_->OnInitializeComplete(info); -} - -// This method handles only input buffer, without coupling with output -void OmxVideoDecodeEngine::EmptyThisBuffer(scoped_refptr<Buffer> buffer) { - DCHECK_EQ(message_loop_, MessageLoop::current()); - DCHECK(!free_input_buffers_.empty()); - DCHECK_GT(input_pending_request_, 0); - - --input_pending_request_; - - if (!CanAcceptInput()) { - FinishEmptyBuffer(buffer); - return; - } - - if (buffer->IsEndOfStream()) { - DLOG(INFO) << "Input queue has EOS"; - input_queue_has_eos_ = true; - } - - OMX_BUFFERHEADERTYPE* omx_buffer = free_input_buffers_.front(); - free_input_buffers_.pop(); - - // setup |omx_buffer|. - omx_buffer->pBuffer = const_cast<OMX_U8*>(buffer->GetData()); - omx_buffer->nFilledLen = buffer->GetDataSize(); - omx_buffer->nAllocLen = omx_buffer->nFilledLen; - if (input_queue_has_eos_) - omx_buffer->nFlags |= OMX_BUFFERFLAG_EOS; - else - omx_buffer->nFlags &= ~OMX_BUFFERFLAG_EOS; - omx_buffer->nTimeStamp = buffer->GetTimestamp().InMicroseconds(); - omx_buffer->pAppPrivate = buffer.get(); - buffer->AddRef(); - available_input_buffers_.push(omx_buffer); - - // Try to feed buffers into the decoder. - EmptyBufferTask(); - - if (flush_pending_ && input_pending_request_ == 0) - StartFlush(); -} - -void OmxVideoDecodeEngine::Flush() { - DCHECK_EQ(message_loop_, MessageLoop::current()); - DCHECK_EQ(il_state_, kIlExecuting); - - if (il_state_ != kIlExecuting) { - event_handler_->OnFlushComplete(); - return; - } - - client_state_ = kClientFlushing; - expected_il_state_ = kIlPause; - OnStateSetEventFunc = &OmxVideoDecodeEngine::PauseFromExecuting; - TransitionToState(OMX_StatePause); -} - -void OmxVideoDecodeEngine::PauseFromExecuting(OMX_STATETYPE state) { - DCHECK_EQ(message_loop_, MessageLoop::current()); - - OnStateSetEventFunc = NULL; - il_state_ = kIlPause; - - if (input_pending_request_ == 0) - StartFlush(); - else - flush_pending_ = true; -} - -void OmxVideoDecodeEngine::StartFlush() { - DCHECK_EQ(message_loop_, MessageLoop::current()); - DCHECK_EQ(input_pending_request_, 0); - DLOG(INFO) << "StartFlush"; - - while (!available_input_buffers_.empty()) - available_input_buffers_.pop(); - - flush_pending_ = false; - - // Flush input port first. - OnFlushEventFunc = &OmxVideoDecodeEngine::PortFlushDone; - OMX_ERRORTYPE omxresult; - omxresult = OMX_SendCommand(component_handle_, - OMX_CommandFlush, - input_port_, 0); -} - -bool OmxVideoDecodeEngine::InputPortFlushed() { - DCHECK_EQ(message_loop_, MessageLoop::current()); - DCHECK_EQ(client_state_, kClientFlushing); - // Port flushed is defined by OpenMAX component had signal flush done and - // We had all buffers returned from demuxer and OpenMAX component. - int free_input_size = static_cast<int>(free_input_buffers_.size()); - return input_port_flushed_ && free_input_size == input_buffer_count_; -} - -bool OmxVideoDecodeEngine::OutputPortFlushed() { - DCHECK_EQ(message_loop_, MessageLoop::current()); - DCHECK_EQ(client_state_, kClientFlushing); - // Port flushed is defined by OpenMAX component had signal flush done and - // We had all buffers returned from renderer and OpenMAX component. - return output_port_flushed_ && output_pending_request_ == 0; -} - -void OmxVideoDecodeEngine::ComponentFlushDone() { - DCHECK_EQ(message_loop_, MessageLoop::current()); - DLOG(INFO) << "Component had been flushed!"; - - if (input_port_flushed_ && output_port_flushed_) { - event_handler_->OnFlushComplete(); - input_port_flushed_ = false; - output_port_flushed_ = false; - } -} - -void OmxVideoDecodeEngine::PortFlushDone(int port) { - DCHECK_EQ(message_loop_, MessageLoop::current()); - DCHECK_NE(port, static_cast<int>(OMX_ALL)); - - if (port == input_port_) { - DLOG(INFO) << "Input Port had been flushed"; - DCHECK_EQ(input_buffers_at_component_, 0); - input_port_flushed_ = true; - // Flush output port next. - OMX_ERRORTYPE omxresult; - omxresult = OMX_SendCommand(component_handle_, - OMX_CommandFlush, - output_port_, 0); - return; - } - - if (port == output_port_) { - DLOG(INFO) << "Output Port had been flushed"; - DCHECK_EQ(output_buffers_at_component_, 0); - - output_port_flushed_ = true; - } - - if (kClientFlushing == client_state_ && - InputPortFlushed() && OutputPortFlushed()) - ComponentFlushDone(); -} - -void OmxVideoDecodeEngine::Seek() { - DCHECK_EQ(message_loop_, MessageLoop::current()); - - DCHECK(client_state_ == kClientFlushing || // After a flush - client_state_ == kClientInitializing); // After an initialize. - - if (client_state_ == kClientFlushing) { - InitialReadBuffer(); - OnStateSetEventFunc = &OmxVideoDecodeEngine::DoneSetStateExecuting; - TransitionToState(OMX_StateExecuting); - } - - event_handler_->OnSeekComplete(); -} - -VideoFrame::Format OmxVideoDecodeEngine::GetSurfaceFormat() const { - // TODO(jiesun): Both OmxHeaderType and EGLImage surface type could have - // different surface formats. - return uses_egl_image_ ? VideoFrame::RGBA : VideoFrame::YV12; -} - -void OmxVideoDecodeEngine::Uninitialize() { - DCHECK_EQ(message_loop_, MessageLoop::current()); - - if (client_state_ == kClientError) { - OnStopDone(); - return; - } - - // TODO(wjia): add more state checking - if (kClientRunning == client_state_ || kClientFlushing == client_state_) { - client_state_ = kClientStopping; - DeinitFromExecuting(OMX_StateExecuting); - } - - // TODO(wjia): When FillThisBuffer() is added, engine state should be - // kStopping here. engine state should be set to kStopped in OnStopDone(); - // client_state_ = kClientStopping; -} - -void OmxVideoDecodeEngine::FinishEmptyBuffer(scoped_refptr<Buffer> buffer) { - DCHECK_EQ(message_loop_, MessageLoop::current()); - - if (!input_queue_has_eos_) { - event_handler_->OnEmptyBufferCallback(buffer); - ++input_pending_request_; - } -} - -void OmxVideoDecodeEngine::FinishFillBuffer(OMX_BUFFERHEADERTYPE* buffer) { - DCHECK_EQ(message_loop_, MessageLoop::current()); - DCHECK(buffer); - - scoped_refptr<VideoFrame> frame; - frame = static_cast<VideoFrame*>(buffer->pAppPrivate); - - // We should not flush buffer to renderer during decoder flushing if decoder - // provides the buffer allocator. - if (kClientFlushing == client_state_ && !uses_egl_image_) return; - - frame->SetTimestamp(base::TimeDelta::FromMicroseconds(buffer->nTimeStamp)); - frame->SetDuration(frame->GetTimestamp() - last_pts_); - last_pts_ = frame->GetTimestamp(); - event_handler_->OnFillBufferCallback(frame); - output_pending_request_--; -} - -void OmxVideoDecodeEngine::OnStopDone() { - DCHECK_EQ(message_loop_, MessageLoop::current()); - - event_handler_->OnUninitializeComplete(); -} - -// Function sequence for initializing -void OmxVideoDecodeEngine::InitializeTask() { - DCHECK_EQ(il_state_, kIlNone); - - il_state_ = kIlNone; - expected_il_state_ = kIlLoaded; - output_port_state_ = kPortEnabled; - if (!CreateComponent()) { - StopOnError(); - return; - } - il_state_ = kIlLoaded; - - // TODO(wjia): Disabling output port is to work around racing condition - // due to bug in some vendor's driver. But it hits another bug. - // So temporarily fall back to enabling output port. Still keep the code - // disabling output port here. - // No need to respond to this PortDisable event - // OnPortDisableEventFunc = NULL; - // ChangePort(OMX_CommandPortDisable, output_port_); - // if (kClientError == client_state_) { - // StopOnError(); - // return; - // } - // output_port_state_ = kPortDisabled; - - // Transition component to Idle state - OnStateSetEventFunc = &OmxVideoDecodeEngine::DoneSetStateIdle; - if (!TransitionToState(OMX_StateIdle)) { - StopOnError(); - return; - } - expected_il_state_ = kIlIdle; - - if (!AllocateInputBuffers()) { - LOG(ERROR) << "OMX_AllocateBuffer() Input buffer error"; - client_state_ = kClientError; - StopOnError(); - return; - } - if (!AllocateOutputBuffers()) { - LOG(ERROR) << "OMX_AllocateBuffer() Output buffer error"; - client_state_ = kClientError; - return; - } -} - -// 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. -bool OmxVideoDecodeEngine::CreateComponent() { - DCHECK_EQ(message_loop_, MessageLoop::current()); - - static OMX_CALLBACKTYPE callback = { - &OmxVideoDecodeEngine::EventHandler, - &OmxVideoDecodeEngine::EmptyBufferCallback, - &OmxVideoDecodeEngine::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"; - client_state_ = kClientError; - return false; - } - - // 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(); - client_state_ = kClientError; - return false; - } - 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(); - client_state_ = kClientError; - return false; - } - - // 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()); - omxresult = OMX_GetHandle(&component_handle_, component, this, &callback); - if (omxresult != OMX_ErrorNone) { - LOG(ERROR) << "Failed to Load the component: " << component; - client_state_ = kClientError; - return false; - } - - // 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; - ResetParamHeader(*this, &port_param); - omxresult = OMX_GetParameter(component_handle_, OMX_IndexParamVideoInit, - &port_param); - if (omxresult != OMX_ErrorNone) { - LOG(ERROR) << "Failed to get Port Param"; - client_state_ = kClientError; - return false; - } - 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; - ResetParamHeader(*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"; - client_state_ = kClientError; - return false; - } - - // 6. Input/output ports media format configuration. - if (!ConfigureIOPorts()) { - LOG(ERROR) << "Media format configurations failed"; - client_state_ = kClientError; - return false; - } - - // 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; - ResetParamHeader(*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"; - client_state_ = kClientError; - return false; - } - if (OMX_DirInput != port_format.eDir) { - LOG(ERROR) << "Expected input port"; - client_state_ = kClientError; - return false; - } - input_buffer_count_ = port_format.nBufferCountMin; - input_buffer_size_ = port_format.nBufferSize; - - // 8. Obtain the information about the output port. - ResetParamHeader(*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"; - client_state_ = kClientError; - return false; - } - if (OMX_DirOutput != port_format.eDir) { - LOG(ERROR) << "Expect Output Port"; - client_state_ = kClientError; - return false; - } - - // TODO(wjia): use same buffer recycling for EGLImage and system memory. - // Override buffer count when EGLImage is used. - if (uses_egl_image_) { - // TODO(wjia): remove hard-coded value - port_format.nBufferCountActual = port_format.nBufferCountMin = - output_buffer_count_ = 4; - - omxresult = OMX_SetParameter(component_handle_, - OMX_IndexParamPortDefinition, - &port_format); - if (omxresult != OMX_ErrorNone) { - LOG(ERROR) << "SetParameter(OMX_IndexParamPortDefinition) failed"; - client_state_ = kClientError; - return false; - } - } else { - output_buffer_count_ = port_format.nBufferCountActual; - } - output_buffer_size_ = port_format.nBufferSize; - - return true; -} - -// Event callback during initialization to handle DoneStateSet to idle -void OmxVideoDecodeEngine::DoneSetStateIdle(OMX_STATETYPE state) { - DCHECK_EQ(message_loop_, MessageLoop::current()); - DCHECK_EQ(client_state_, kClientInitializing); - DCHECK_EQ(OMX_StateIdle, state); - DLOG(INFO) << "OMX video decode engine is in Idle"; - - il_state_ = kIlIdle; - - // start reading bit stream - InitialReadBuffer(); - OnStateSetEventFunc = &OmxVideoDecodeEngine::DoneSetStateExecuting; - if (!TransitionToState(OMX_StateExecuting)) { - StopOnError(); - return; - } - expected_il_state_ = kIlExecuting; -} - -// Event callback during initialization to handle DoneStateSet to executing -void OmxVideoDecodeEngine::DoneSetStateExecuting(OMX_STATETYPE state) { - DCHECK_EQ(message_loop_, MessageLoop::current()); - DCHECK(client_state_ == kClientInitializing || - client_state_ == kClientFlushing); - DCHECK_EQ(OMX_StateExecuting, state); - DLOG(INFO) << "OMX video decode engine is in Executing"; - - il_state_ = kIlExecuting; - client_state_ = kClientRunning; - OnStateSetEventFunc = NULL; - EmptyBufferTask(); - InitialFillBuffer(); - if (kClientError == client_state_) { - StopOnError(); - return; - } -} - -// Function for receiving output buffers. Hookup for buffer recycling -// and outside allocator. -void OmxVideoDecodeEngine::FillThisBuffer( - scoped_refptr<VideoFrame> video_frame) { - DCHECK(video_frame.get() && !video_frame->IsEndOfStream()); - output_pending_request_++; - - if (!CanAcceptOutput()) { - if (uses_egl_image_) { // return it to owner. - output_pending_request_--; - event_handler_->OnFillBufferCallback(video_frame); - } - return; - } - - OMX_BUFFERHEADERTYPE* omx_buffer = FindOmxBuffer(video_frame); - if (omx_buffer) { - if (kClientRunning == client_state_) { - SendOutputBufferToComponent(omx_buffer); - } else if (kClientFlushing == client_state_) { - if (uses_egl_image_) { // return it to owner. - output_pending_request_--; - event_handler_->OnFillBufferCallback(video_frame); - } - if (InputPortFlushed() && OutputPortFlushed()) - ComponentFlushDone(); - } - } else { - DCHECK(!output_frames_allocated_); - DCHECK(uses_egl_image_); - output_frames_.push_back(std::make_pair(video_frame, - static_cast<OMX_BUFFERHEADERTYPE*>(NULL))); - } - - DCHECK(static_cast<int>(output_frames_.size()) <= output_buffer_count_); - - if ((!output_frames_allocated_) && - static_cast<int>(output_frames_.size()) == output_buffer_count_) { - output_frames_allocated_ = true; - - if (need_setup_output_port_) { - SetupOutputPort(); - } - } - - if (kClientError == client_state_) { - StopOnError(); - return; - } -} - -// Reconfigure port -void OmxVideoDecodeEngine::OnPortSettingsChangedRun(int port, - OMX_INDEXTYPE index) { - DCHECK_EQ(message_loop_, MessageLoop::current()); - DCHECK_EQ(client_state_, kClientRunning); - DCHECK_EQ(port, output_port_); - - // TODO(wjia): add buffer negotiation between decoder and renderer. - if (uses_egl_image_) { - DLOG(INFO) << "Port settings are changed"; - return; - } - - // TODO(wjia): remove this checking when all vendors observe same spec. - if (index > OMX_IndexComponentStartUnused) { - if (index != OMX_IndexParamPortDefinition) - return; - } - - OMX_PARAM_PORTDEFINITIONTYPE port_format; - ResetParamHeader(*this, &port_format); - port_format.nPortIndex = output_port_; - OMX_ERRORTYPE omxresult; - omxresult = OMX_GetParameter(component_handle_, - OMX_IndexParamPortDefinition, - &port_format); - if (omxresult != OMX_ErrorNone) { - LOG(ERROR) << "GetParameter(OMX_IndexParamPortDefinition) failed"; - client_state_ = kClientError; - StopOnError(); - return; - } - if (OMX_DirOutput != port_format.eDir) { - LOG(ERROR) << "Expected Output Port"; - client_state_ = kClientError; - StopOnError(); - return; - } - - // Update the output format. - OmxConfigurator::MediaFormat 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; - output_buffer_count_ = port_format.nBufferCountActual; - output_buffer_size_ = port_format.nBufferSize; - - if (kPortEnabled == output_port_state_) { - output_port_state_ = kPortDisabling; - OnPortDisableEventFunc = &OmxVideoDecodeEngine::OnPortDisableEventRun; - ChangePort(OMX_CommandPortDisable, output_port_); - if (kClientError == client_state_) { - StopOnError(); - return; - } - FreeOutputBuffers(); - } else { - OnPortDisableEventRun(output_port_); - } -} - -// Post output port disabling -void OmxVideoDecodeEngine::OnPortDisableEventRun(int port) { - DCHECK_EQ(message_loop_, MessageLoop::current()); - DCHECK_EQ(client_state_, kClientRunning); - DCHECK_EQ(port, output_port_); - - output_port_state_ = kPortDisabled; - - // make sure all eglimages are available before enabling output port - if (output_frames_allocated_ || !uses_egl_image_) { - SetupOutputPort(); - if (kClientError == client_state_) { - StopOnError(); - return; - } - } else { - need_setup_output_port_ = true; - } -} - -// Enable output port and allocate buffers correspondingly -void OmxVideoDecodeEngine::SetupOutputPort() { - DCHECK_EQ(message_loop_, MessageLoop::current()); - - need_setup_output_port_ = false; - - // Enable output port when necessary since the port could be waiting for - // buffers, instead of port reconfiguration. - if (kPortEnabled != output_port_state_) { - output_port_state_ = kPortEnabling; - OnPortEnableEventFunc = &OmxVideoDecodeEngine::OnPortEnableEventRun; - ChangePort(OMX_CommandPortEnable, output_port_); - if (kClientError == client_state_) { - return; - } - } - - // TODO(wjia): add state checking - // Update the ports in buffer if necessary - if (!AllocateOutputBuffers()) { - LOG(ERROR) << "OMX_AllocateBuffer() Output buffer error"; - client_state_ = kClientError; - return; - } -} - -// Post output port enabling -void OmxVideoDecodeEngine::OnPortEnableEventRun(int port) { - DCHECK_EQ(message_loop_, MessageLoop::current()); - DCHECK_EQ(port, output_port_); - DCHECK_EQ(client_state_, kClientRunning); - - output_port_state_ = kPortEnabled; - last_pts_ = base::TimeDelta::FromMilliseconds(0); - OnPortEnableEventFunc = NULL; - InitialFillBuffer(); - if (kClientError == client_state_) { - StopOnError(); - return; - } -} - -void OmxVideoDecodeEngine::DeinitFromExecuting(OMX_STATETYPE state) { - DCHECK_EQ(state, OMX_StateExecuting); - - DLOG(INFO) << "Deinit from Executing"; - OnStateSetEventFunc = &OmxVideoDecodeEngine::DeinitFromIdle; - TransitionToState(OMX_StateIdle); - expected_il_state_ = kIlIdle; -} - -void OmxVideoDecodeEngine::DeinitFromIdle(OMX_STATETYPE state) { - DCHECK_EQ(message_loop_, MessageLoop::current()); - DCHECK_EQ(state, OMX_StateIdle); - - DLOG(INFO) << "Deinit from Idle"; - il_state_ = kIlIdle; - OnStateSetEventFunc = &OmxVideoDecodeEngine::DeinitFromLoaded; - TransitionToState(OMX_StateLoaded); - expected_il_state_ = kIlLoaded; - - if (!input_buffers_at_component_) - FreeInputBuffers(); - else - need_free_input_buffers_ = true; - - if (!output_buffers_at_component_) - FreeOutputBuffers(); - else - need_free_output_buffers_ = true; -} - -void OmxVideoDecodeEngine::DeinitFromLoaded(OMX_STATETYPE state) { - DCHECK_EQ(message_loop_, MessageLoop::current()); - DCHECK_EQ(state, OMX_StateLoaded); - - DLOG(INFO) << "Deinit from Loaded"; - il_state_ = kIlLoaded; - 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; - } - il_state_ = expected_il_state_ = kIlNone; - - // kClientStopped is different from kClientNotInitialized. The former can't - // accept output buffers, while the latter can. - client_state_ = kClientStopped; - - OMX_Deinit(); - - OnStopDone(); -} - -void OmxVideoDecodeEngine::StopOnError() { - DCHECK_EQ(message_loop_, MessageLoop::current()); - - client_state_ = kClientStopping; - - if (kIlExecuting == expected_il_state_) { - DeinitFromExecuting(OMX_StateExecuting); - } else if (kIlIdle == expected_il_state_) { - DeinitFromIdle(OMX_StateIdle); - } else if (kIlLoaded == expected_il_state_) { - DeinitFromLoaded(OMX_StateLoaded); - } else if (kIlPause == expected_il_state_) { - // TODO(jiesun): Make sure this works. - DeinitFromExecuting(OMX_StateExecuting); - } else { - NOTREACHED(); - } -} - -// Call OMX_UseBuffer() to avoid buffer copying when -// OMX_EmptyThisBuffer() is called -bool OmxVideoDecodeEngine::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_, - this, input_buffer_size_, data); - if (error != OMX_ErrorNone) - return false; - buffer->nInputPortIndex = input_port_; - buffer->nOffset = 0; - buffer->nFlags = 0; - input_buffers_.push_back(buffer); - free_input_buffers_.push(buffer); - } - return true; -} - -// This method handles EGLImage and internal buffer cases. Any external -// allocation case is similar to EGLImage -bool OmxVideoDecodeEngine::AllocateOutputBuffers() { - DCHECK_EQ(message_loop_, MessageLoop::current()); - - if (uses_egl_image_ && !output_frames_allocated_) { - DLOG(INFO) << "Output frames are not allocated yet"; - need_setup_output_port_ = true; - return true; - } - - for (int i = 0; i < output_buffer_count_; ++i) { - OMX_BUFFERHEADERTYPE* buffer; - scoped_refptr<VideoFrame> video_frame; - OMX_ERRORTYPE error; - if (uses_egl_image_) { - OutputFrame output_frame = output_frames_[i]; - video_frame = output_frame.first; - DCHECK(!output_frame.second); - error = OMX_UseEGLImage(component_handle_, &buffer, output_port_, - video_frame.get(), video_frame->private_buffer()); - if (error != OMX_ErrorNone) - return false; - output_frames_[i].second = buffer; - } else { - error = OMX_AllocateBuffer(component_handle_, &buffer, output_port_, - NULL, output_buffer_size_); - if (error != OMX_ErrorNone) - return false; - video_frame = CreateOmxBufferVideoFrame(buffer); - output_frames_.push_back(std::make_pair(video_frame, buffer)); - buffer->pAppPrivate = video_frame.get(); - } - } - - return true; -} - -scoped_refptr<VideoFrame> OmxVideoDecodeEngine::CreateOmxBufferVideoFrame( - OMX_BUFFERHEADERTYPE* omx_buffer) { - scoped_refptr<VideoFrame> video_frame; - uint8* data[VideoFrame::kMaxPlanes]; - int32 strides[VideoFrame::kMaxPlanes]; - - memset(data, 0, sizeof(data)); - memset(strides, 0, sizeof(strides)); - // TODO(jiesun): chroma format 4:2:0 only and 3 planes. - data[0] = omx_buffer->pBuffer; - data[1] = data[0] + width_ * height_; - data[2] = data[1] + width_ * height_ / 4; - strides[0] = width_; - strides[1] = strides[2] = width_ >> 1; - - VideoFrame::CreateFrameExternal( - VideoFrame::TYPE_OMXBUFFERHEAD, - VideoFrame::YV12, - width_, height_, 3, - data, strides, - StreamSample::kInvalidTimestamp, - StreamSample::kInvalidTimestamp, - omx_buffer, - &video_frame); - - return video_frame; -} - -void OmxVideoDecodeEngine::FreeInputBuffers() { - DCHECK_EQ(message_loop_, MessageLoop::current()); - - // Empty available buffer queue. - while (!free_input_buffers_.empty()) { - free_input_buffers_.pop(); - } - - while (!available_input_buffers_.empty()) { - OMX_BUFFERHEADERTYPE* omx_buffer = available_input_buffers_.front(); - available_input_buffers_.pop(); - Buffer* stored_buffer = static_cast<Buffer*>(omx_buffer->pAppPrivate); - FinishEmptyBuffer(stored_buffer); - stored_buffer->Release(); - } - - // 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(); - - need_free_input_buffers_ = false; -} - -void OmxVideoDecodeEngine::FreeOutputBuffers() { - DCHECK_EQ(message_loop_, MessageLoop::current()); - - // Calls to OMX to free buffers. - for (size_t i = 0; i < output_frames_.size(); ++i) { - OMX_BUFFERHEADERTYPE* omx_buffer = output_frames_[i].second; - CHECK(omx_buffer); - OMX_FreeBuffer(component_handle_, output_port_, omx_buffer); - } - output_frames_.clear(); - output_frames_allocated_ = false; - - need_free_output_buffers_ = false; -} - -bool OmxVideoDecodeEngine::ConfigureIOPorts() { - OMX_PARAM_PORTDEFINITIONTYPE input_port_def, output_port_def; - OMX_ERRORTYPE omxresult = OMX_ErrorNone; - // Get default input port definition. - ResetParamHeader(*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. - ResetParamHeader(*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( - static_cast<OMX_COMPONENTTYPE*>(component_handle_), - &input_port_def, &output_port_def); -} - -bool OmxVideoDecodeEngine::CanEmptyBuffer() { - // We can call empty buffer while we are in executing and EOS has - // not been sent - return (il_state_ == kIlExecuting && - !input_has_fed_eos_); -} - -bool OmxVideoDecodeEngine::CanFillBuffer() { - // Make sure component is in the executing state and end-of-stream - // has not been reached. - return (il_state_ == kIlExecuting && - !output_eos_ && - (output_port_state_ == kPortEnabled || - output_port_state_ == kPortEnabling)); -} - -bool OmxVideoDecodeEngine::CanAcceptInput() { - // We can't take input buffer when in error state. - return (kClientError != client_state_ && - kClientStopping != client_state_ && - kClientStopped != client_state_ && - !input_queue_has_eos_); -} - -bool OmxVideoDecodeEngine::CanAcceptOutput() { - return (kClientError != client_state_ && - kClientStopping != client_state_ && - kClientStopped != client_state_ && - output_port_state_ == kPortEnabled && - !output_eos_); -} - -// TODO(wjia): There are several things need to be done here: -// 1. Merge this method into EmptyThisBuffer(); -// 2. Get rid of the while loop, this is not needed because when we call -// OMX_EmptyThisBuffer we assume we *always* have an input buffer. -void OmxVideoDecodeEngine::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 (!available_input_buffers_.empty() && - !input_has_fed_eos_) { - OMX_BUFFERHEADERTYPE* omx_buffer = available_input_buffers_.front(); - available_input_buffers_.pop(); - - input_has_fed_eos_ = omx_buffer->nFlags & OMX_BUFFERFLAG_EOS; - if (input_has_fed_eos_) { - DLOG(INFO) << "Input has fed EOS"; - } - - // Give this buffer to OMX. - input_buffers_at_component_++; - OMX_ERRORTYPE ret = OMX_EmptyThisBuffer(component_handle_, omx_buffer); - if (ret != OMX_ErrorNone) { - LOG(ERROR) << "OMX_EmptyThisBuffer() failed with result " << ret; - client_state_ = kClientError; - return; - } - } -} - -void OmxVideoDecodeEngine::InitialReadBuffer() { - DCHECK_EQ(message_loop_, MessageLoop::current()); - - input_queue_has_eos_ = false; - input_has_fed_eos_ = false; - output_eos_ = false; - - DLOG(INFO) << "OmxVideoDecodeEngine::InitialReadBuffer"; - for (size_t i = 0; i < free_input_buffers_.size(); i++) - FinishEmptyBuffer(NULL); -} - -void OmxVideoDecodeEngine::InitialFillBuffer() { - DCHECK_EQ(message_loop_, MessageLoop::current()); - // DCHECK(output_frames_allocated_); - - if (!CanFillBuffer()) - return; - - DLOG(INFO) << "OmxVideoDecodeEngine::InitialFillBuffer"; - - // Ask the decoder to fill the output buffers. - for (uint32 i = 0; i < output_frames_.size(); ++i) { - OMX_BUFFERHEADERTYPE* omx_buffer = output_frames_[i].second; - SendOutputBufferToComponent(omx_buffer); - } -} - -// helper functions -// Send command to disable/enable port. -void OmxVideoDecodeEngine::ChangePort(OMX_COMMANDTYPE cmd, int port_index) { - DCHECK_EQ(message_loop_, MessageLoop::current()); - - OMX_ERRORTYPE omxresult = OMX_SendCommand(component_handle_, - cmd, port_index, 0); - if (omxresult != OMX_ErrorNone) { - LOG(ERROR) << "SendCommand(OMX_CommandPortDisable) failed"; - client_state_ = kClientError; - return; - } -} - -// Find if omx_buffer exists corresponding to video_frame -OMX_BUFFERHEADERTYPE* OmxVideoDecodeEngine::FindOmxBuffer( - scoped_refptr<VideoFrame> video_frame) { - for (size_t i = 0; i < output_frames_.size(); ++i) { - scoped_refptr<VideoFrame> frame = output_frames_[i].first; - if (video_frame->private_buffer() == frame->private_buffer()) - return output_frames_[i].second; - } - return NULL; -} - -OMX_STATETYPE OmxVideoDecodeEngine::GetComponentState() { - OMX_STATETYPE eState; - OMX_ERRORTYPE eError; - - eError = OMX_GetState(component_handle_, &eState); - if (OMX_ErrorNone != eError) { - LOG(ERROR) << "OMX_GetState failed"; - StopOnError(); - } - - return eState; -} - -// send one output buffer to component -void OmxVideoDecodeEngine::SendOutputBufferToComponent( - OMX_BUFFERHEADERTYPE *omx_buffer) { - DCHECK_EQ(message_loop_, MessageLoop::current()); - - if (!CanFillBuffer()) - return; - - // clear EOS flag. - omx_buffer->nFlags &= ~OMX_BUFFERFLAG_EOS; - omx_buffer->nOutputPortIndex = output_port_; - output_buffers_at_component_++; - OMX_ERRORTYPE ret = OMX_FillThisBuffer(component_handle_, omx_buffer); - - if (OMX_ErrorNone != ret) { - LOG(ERROR) << "OMX_FillThisBuffer() failed with result " << ret; - client_state_ = kClientError; - return; - } -} - -// Send state transition command to component. -bool OmxVideoDecodeEngine::TransitionToState(OMX_STATETYPE new_state) { - DCHECK_EQ(message_loop_, MessageLoop::current()); - - OMX_ERRORTYPE omxresult = OMX_SendCommand(component_handle_, - OMX_CommandStateSet, - new_state, 0); - if (omxresult != OMX_ErrorNone) { - LOG(ERROR) << "SendCommand(OMX_CommandStateSet) failed"; - client_state_ = kClientError; - return false; - } - - return true; -} - -void OmxVideoDecodeEngine::EmptyBufferDoneTask(OMX_BUFFERHEADERTYPE* buffer) { - DCHECK_EQ(message_loop_, MessageLoop::current()); - DCHECK_GT(input_buffers_at_component_, 0); - - Buffer* stored_buffer = static_cast<Buffer*>(buffer->pAppPrivate); - buffer->pAppPrivate = NULL; - if (client_state_ != kClientFlushing) - FinishEmptyBuffer(stored_buffer); - stored_buffer->Release(); - - // Enqueue the available buffer because the decoder has consumed it. - free_input_buffers_.push(buffer); - input_buffers_at_component_--; - - if (need_free_input_buffers_ && !input_buffers_at_component_) { - FreeInputBuffers(); - return; - } - - // Try to feed more data into the decoder. - EmptyBufferTask(); - - if (client_state_ == kClientFlushing && - InputPortFlushed() && OutputPortFlushed()) - ComponentFlushDone(); -} - -void OmxVideoDecodeEngine::FillBufferDoneTask(OMX_BUFFERHEADERTYPE* buffer) { - DCHECK_EQ(message_loop_, MessageLoop::current()); - DCHECK_GT(output_buffers_at_component_, 0); - - output_buffers_at_component_--; - - if (need_free_output_buffers_ && !output_buffers_at_component_) { - FreeOutputBuffers(); - return; - } - - if (!CanAcceptOutput()) { - if (uses_egl_image_) { - scoped_refptr<VideoFrame> frame; - frame = static_cast<VideoFrame*>(buffer->pAppPrivate); - event_handler_->OnFillBufferCallback(frame); - output_pending_request_--; - } - return; - } - - // This buffer is received with decoded frame. Enqueue it and make it - // ready to be consumed by reads. - - if (buffer->nFlags & OMX_BUFFERFLAG_EOS) { - output_eos_ = true; - DLOG(INFO) << "Output has EOS"; - } - - FinishFillBuffer(buffer); - - if (buffer->nFlags & OMX_BUFFERFLAG_EOS) { - // Singal end of stream. - scoped_refptr<VideoFrame> frame; - VideoFrame::CreateEmptyFrame(&frame); - event_handler_->OnFillBufferCallback(frame); - } - - if (client_state_ == kClientFlushing && - InputPortFlushed() && OutputPortFlushed()) - ComponentFlushDone(); -} - -void OmxVideoDecodeEngine::EventHandlerCompleteTask(OMX_EVENTTYPE event, - OMX_U32 data1, - OMX_U32 data2) { - 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_CommandPortDisable) { - if (OnPortDisableEventFunc) - (this->*OnPortDisableEventFunc)(static_cast<int>(data2)); - } else if (cmd == OMX_CommandPortEnable) { - if (OnPortEnableEventFunc) - (this->*OnPortEnableEventFunc)(static_cast<int>(data2)); - } else if (cmd == OMX_CommandStateSet) { - (this->*OnStateSetEventFunc)(static_cast<OMX_STATETYPE>(data2)); - } else if (cmd == OMX_CommandFlush) { - (this->*OnFlushEventFunc)(data2); - } else { - LOG(ERROR) << "Unknown command completed\n" << data1; - } - break; - } - case OMX_EventError: - if (OMX_ErrorInvalidState == (OMX_ERRORTYPE)data1) { - // TODO(hclam): what to do here? - } - StopOnError(); - break; - case OMX_EventPortSettingsChanged: - // TODO(wjia): remove this hack when all vendors observe same spec. - if (data1 < OMX_IndexComponentStartUnused) - OnPortSettingsChangedRun(static_cast<int>(data1), - static_cast<OMX_INDEXTYPE>(data2)); - else - OnPortSettingsChangedRun(static_cast<int>(data2), - static_cast<OMX_INDEXTYPE>(data1)); - break; - default: - LOG(ERROR) << "Warning - Unknown event received\n"; - break; - } -} - -// static -OMX_ERRORTYPE OmxVideoDecodeEngine::EventHandler(OMX_HANDLETYPE component, - OMX_PTR priv_data, - OMX_EVENTTYPE event, - OMX_U32 data1, - OMX_U32 data2, - OMX_PTR event_data) { - OmxVideoDecodeEngine* decoder = static_cast<OmxVideoDecodeEngine*>(priv_data); - DCHECK_EQ(component, decoder->component_handle_); - decoder->message_loop_->PostTask(FROM_HERE, - NewRunnableMethod(decoder, - &OmxVideoDecodeEngine::EventHandlerCompleteTask, - event, data1, data2)); - return OMX_ErrorNone; -} - -// static -OMX_ERRORTYPE OmxVideoDecodeEngine::EmptyBufferCallback( - OMX_HANDLETYPE component, - OMX_PTR priv_data, - OMX_BUFFERHEADERTYPE* buffer) { - OmxVideoDecodeEngine* decoder = static_cast<OmxVideoDecodeEngine*>(priv_data); - DCHECK_EQ(component, decoder->component_handle_); - decoder->message_loop_->PostTask(FROM_HERE, - NewRunnableMethod(decoder, - &OmxVideoDecodeEngine::EmptyBufferDoneTask, buffer)); - return OMX_ErrorNone; -} - -// static -OMX_ERRORTYPE OmxVideoDecodeEngine::FillBufferCallback( - OMX_HANDLETYPE component, - OMX_PTR priv_data, - OMX_BUFFERHEADERTYPE* buffer) { - OmxVideoDecodeEngine* decoder = static_cast<OmxVideoDecodeEngine*>(priv_data); - DCHECK_EQ(component, decoder->component_handle_); - decoder->message_loop_->PostTask(FROM_HERE, - NewRunnableMethod(decoder, - &OmxVideoDecodeEngine::FillBufferDoneTask, buffer)); - return OMX_ErrorNone; -} - -} // namespace media |