| // Copyright (c) 2011 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 OmxVideoDecoder and lives on the thread |
| // that it 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. 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" |
| #include "media/base/pipeline.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, |
| VideoDecodeContext* context, |
| 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_GL_TEXTURE |
| : 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::ConsumeVideoSample(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(); |
| } |
| |
| int OmxVideoDecodeEngine::current_omx_spec_version() const { |
| return 0x00000101; |
| } |
| |
| 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_->ProduceVideoSample(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; |
| |
| PipelineStatistics statistics; |
| statistics.video_bytes_decoded = buffer->nFilledLen; |
| |
| frame->SetTimestamp(base::TimeDelta::FromMicroseconds(buffer->nTimeStamp)); |
| frame->SetDuration(frame->GetTimestamp() - last_pts_); |
| last_pts_ = frame->GetTimestamp(); |
| event_handler_->ConsumeVideoFrame(frame, statistics); |
| 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.nBufferCountActual; |
| 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::ProduceVideoFrame( |
| scoped_refptr<VideoFrame> video_frame) { |
| DCHECK(video_frame.get() && !video_frame->IsEndOfStream()); |
| output_pending_request_++; |
| |
| PipelineStatistics statistics; |
| |
| if (!CanAcceptOutput()) { |
| if (uses_egl_image_) { // return it to owner. |
| output_pending_request_--; |
| event_handler_->ConsumeVideoFrame(video_frame, statistics); |
| } |
| return; |
| } |
| |
| OMX_BUFFERHEADERTYPE* omx_buffer = FindOmxBuffer(video_frame); |
| if (omx_buffer) { |
| statistics.video_bytes_decoded = omx_buffer->nFilledLen; |
| |
| 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_->ConsumeVideoFrame(video_frame, statistics); |
| } |
| 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_SYSTEM_MEMORY, |
| VideoFrame::YV12, |
| width_, height_, 3, |
| data, strides, |
| kNoTimestamp, |
| kNoTimestamp, |
| 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) { |
| if (video_frame == output_frames_[i].first) |
| 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; |
| } |
| |
| PipelineStatistics statistics; |
| statistics.video_bytes_decoded = buffer->nFilledLen; |
| |
| if (!CanAcceptOutput()) { |
| if (uses_egl_image_) { |
| scoped_refptr<VideoFrame> frame; |
| frame = static_cast<VideoFrame*>(buffer->pAppPrivate); |
| event_handler_->ConsumeVideoFrame(frame, statistics); |
| 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_->ConsumeVideoFrame(frame, statistics); |
| } |
| |
| 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 |
| |
| // Disable refcounting for this object because this object only lives |
| // on the video decoder thread and there's no need to refcount it. |
| DISABLE_RUNNABLE_METHOD_REFCOUNT(media::OmxVideoDecodeEngine); |