summaryrefslogtreecommitdiffstats
path: root/content/gpu
diff options
context:
space:
mode:
authorvhiremath@nvidia.com <vhiremath@nvidia.com@0039d316-1c4b-4281-b951-d872f2087c98>2011-05-06 02:33:43 +0000
committervhiremath@nvidia.com <vhiremath@nvidia.com@0039d316-1c4b-4281-b951-d872f2087c98>2011-05-06 02:33:43 +0000
commite6cf705af80fd610d830d420ae8f74e1ed909104 (patch)
tree64ce787dd7e41f8818d2ebb5aeb3e1a43377d70b /content/gpu
parent898a42bdb170884b234d931f192a855e8d682c45 (diff)
downloadchromium_src-e6cf705af80fd610d830d420ae8f74e1ed909104.zip
chromium_src-e6cf705af80fd610d830d420ae8f74e1ed909104.tar.gz
chromium_src-e6cf705af80fd610d830d420ae8f74e1ed909104.tar.bz2
New change for OmxVideoDecodeAccelerator class.
This will use the h/w omx decoder in push model. Newly designed for using with PPAPIs. This has been tested with FakeTest "gpu_video_tests" TEST=Run gpu_video_tests on ARM device. Review URL: http://codereview.chromium.org/6894046 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@84389 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'content/gpu')
-rw-r--r--content/gpu/omx_video_decode_accelerator.cc880
-rw-r--r--content/gpu/omx_video_decode_accelerator.h163
2 files changed, 1043 insertions, 0 deletions
diff --git a/content/gpu/omx_video_decode_accelerator.cc b/content/gpu/omx_video_decode_accelerator.cc
new file mode 100644
index 0000000..2213c49
--- /dev/null
+++ b/content/gpu/omx_video_decode_accelerator.cc
@@ -0,0 +1,880 @@
+// 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.
+
+#include "content/gpu/omx_video_decode_accelerator.h"
+
+#include "content/common/gpu/gpu_channel.h"
+#include "content/common/gpu_messages.h"
+#include "content/gpu/gles2_texture_to_egl_image_translator.h"
+#include "media/base/bitstream_buffer.h"
+#include "media/base/data_buffer.h"
+#include "media/video/picture.h"
+
+static Gles2TextureToEglImageTranslator* texture2eglImage_translator(
+ new Gles2TextureToEglImageTranslator(NULL, 0));
+enum { kNumPictureBuffers = 4 };
+
+// Open the libnvomx here for now.
+void* omx_handle = dlopen("libnvomx.so", RTLD_NOW);
+
+typedef OMX_ERRORTYPE (*OMXInit)();
+typedef OMX_ERRORTYPE (*OMXGetHandle)(
+ OMX_HANDLETYPE*, OMX_STRING, OMX_PTR, OMX_CALLBACKTYPE*);
+typedef OMX_ERRORTYPE (*OMXGetComponentsOfRole)(OMX_STRING, OMX_U32*, OMX_U8**);
+typedef OMX_ERRORTYPE (*OMXFreeHandle)(OMX_HANDLETYPE);
+typedef OMX_ERRORTYPE (*OMXDeinit)();
+
+OMXInit omx_init = reinterpret_cast<OMXInit>(dlsym(omx_handle, "OMX_Init"));
+
+OMXGetHandle omx_gethandle =
+ reinterpret_cast<OMXGetHandle>(dlsym(omx_handle, "OMX_GetHandle"));
+OMXGetComponentsOfRole omx_get_components_of_role =
+ reinterpret_cast<OMXGetComponentsOfRole>(
+ dlsym(omx_handle, "OMX_GetComponentsOfRole"));
+OMXFreeHandle omx_free_handle =
+ reinterpret_cast<OMXFreeHandle>(dlsym(omx_handle, "OMX_FreeHandle"));
+OMXDeinit omx_deinit =
+ reinterpret_cast<OMXDeinit>(dlsym(omx_handle, "OMX_Deinit"));
+
+static bool AreOMXFunctionPointersInitialized() {
+ return (omx_init && omx_gethandle && omx_get_components_of_role &&
+ omx_free_handle && omx_deinit);
+}
+
+OmxVideoDecodeAccelerator::OmxVideoDecodeAccelerator(
+ media::VideoDecodeAccelerator::Client* client,
+ MessageLoop* message_loop)
+ : message_loop_(message_loop),
+ component_handle_(NULL),
+ width_(-1),
+ height_(-1),
+ input_buffer_count_(0),
+ input_buffer_size_(0),
+ input_port_(0),
+ input_buffers_at_component_(0),
+ output_buffer_count_(0),
+ output_buffer_size_(0),
+ output_port_(0),
+ output_buffers_at_component_(0),
+ uses_egl_image_(false),
+ client_(client),
+ egl_image_(NULL) {
+ if (!AreOMXFunctionPointersInitialized()) {
+ LOG(ERROR) << "Failed to load openmax library";
+ return;
+ }
+ OMX_ERRORTYPE result = omx_init();
+ if (result != OMX_ErrorNone)
+ LOG(ERROR) << "Failed to init OpenMAX core";
+}
+
+OmxVideoDecodeAccelerator::~OmxVideoDecodeAccelerator() {
+ DCHECK(free_input_buffers_.empty());
+ DCHECK_EQ(0, input_buffers_at_component_);
+ DCHECK_EQ(0, output_buffers_at_component_);
+ DCHECK(output_pictures_.empty());
+}
+
+const std::vector<uint32>& OmxVideoDecodeAccelerator::GetConfig(
+ const std::vector<uint32>& prototype_config) {
+ // TODO(vhiremath@nvidia.com) use this properly
+ NOTIMPLEMENTED();
+ return component_config_;
+}
+
+// This is to initialize the OMX data structures to default values.
+template <typename T>
+static void InitParam(const OmxVideoDecodeAccelerator& dec, T* param) {
+ memset(param, 0, sizeof(T));
+ param->nVersion.nVersion = 0x00000101;
+ param->nSize = sizeof(T);
+}
+
+bool OmxVideoDecodeAccelerator::Initialize(const std::vector<uint32>& config) {
+ // TODO(vhiremath@nvidia.com) get these acutal values from config
+ // Assume qvga for now
+ width_ = 320;
+ height_ = 240;
+
+ client_state_ = OMX_StateLoaded;
+ if (!CreateComponent()) {
+ StopOnError();
+ return false;
+ }
+ // Transition component to Idle state
+ on_state_event_func_ =
+ &OmxVideoDecodeAccelerator::OnStateChangeLoadedToIdle;
+ if (!TransitionToState(OMX_StateIdle))
+ return false;
+
+ if (!AllocateInputBuffers()) {
+ LOG(ERROR) << "OMX_AllocateBuffer() Input buffer error";
+ StopOnError();
+ return false;
+ }
+
+ // After AllocateInputBuffers ideally this should be AllocateOutputBuffers.
+ // Since in this case app provides the output buffers,
+ // we query this through ProvidePictureBuffers.
+ // This is call to ppapi to provide the output buffers initially.
+ // ProvidePictureBuffers will provide
+ // - SharedMemHandle in case of decoding to system memory.
+ // - Textures in case of decoding to egl-images.
+
+ // Output buffers will be eventually allocated in AssignPictureBuffer().
+
+ // TODO(vhiremath@nvidia.com) fill buffer_properties
+ std::vector<uint32> buffer_properties;
+ output_buffer_count_ = kNumPictureBuffers;
+ client_->ProvidePictureBuffers(output_buffer_count_, buffer_properties);
+ return true;
+}
+
+bool OmxVideoDecodeAccelerator::CreateComponent() {
+ OMX_CALLBACKTYPE omx_accelerator_callbacks = {
+ &OmxVideoDecodeAccelerator::EventHandler,
+ &OmxVideoDecodeAccelerator::EmptyBufferCallback,
+ &OmxVideoDecodeAccelerator::FillBufferCallback
+ };
+ OMX_ERRORTYPE result = OMX_ErrorNone;
+
+ // 1. Set the role and get all components of this role.
+ // TODO(vhiremath@nvidia.com) Get this role_name from the configs
+ // For now hard coding to avc.
+ const char* role_name = "video_decoder.avc";
+ OMX_U32 num_roles = 0;
+ // Get all the components with this role.
+ result = (*omx_get_components_of_role)(
+ const_cast<OMX_STRING>(role_name), &num_roles, 0);
+ if (result != OMX_ErrorNone || num_roles == 0) {
+ LOG(ERROR) << "Unsupported Role: " << role_name;
+ StopOnError();
+ return false;
+ }
+
+ // We haven't seen HW that needs more yet,
+ // but there is no reason not to raise.
+ const OMX_U32 kMaxRolePerComponent = 3;
+ CHECK_LT(num_roles, kMaxRolePerComponent);
+
+ scoped_array<scoped_array<OMX_U8> > component_names(
+ new scoped_array<OMX_U8>[num_roles]);
+ for (size_t i = 0; i < num_roles; ++i)
+ component_names[i].reset(new OMX_U8[OMX_MAX_STRINGNAME_SIZE]);
+ result = (*omx_get_components_of_role)(
+ const_cast<OMX_STRING>(role_name),
+ &num_roles, reinterpret_cast<OMX_U8**>(component_names.get()));
+
+ // Use first component only. Copy the name of the first component
+ // so that we could free the memory.
+ std::string component_name;
+ if (result == OMX_ErrorNone)
+ component_name = reinterpret_cast<char*>(component_names[0].get());
+
+ if (result != OMX_ErrorNone || num_roles == 0) {
+ LOG(ERROR) << "Unsupported Role: " << component_name.c_str();
+ StopOnError();
+ 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());
+ result = omx_gethandle(&component_handle_, component, this,
+ &omx_accelerator_callbacks);
+ if (result != OMX_ErrorNone) {
+ LOG(ERROR) << "Failed to Load the component: " << component;
+ StopOnError();
+ 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;
+ InitParam(*this, &port_param);
+ result = OMX_GetParameter(component_handle_, OMX_IndexParamVideoInit,
+ &port_param);
+ if ((result != OMX_ErrorNone) || (port_param.nPorts != 2)) {
+ LOG(ERROR) << "Failed to get Port Param";
+ StopOnError();
+ return false;
+ }
+ input_port_ = port_param.nStartPortNumber;
+ output_port_ = input_port_ + 1;
+ // 5. Set role for the component because components can have multiple roles.
+ OMX_PARAM_COMPONENTROLETYPE role_type;
+ InitParam(*this, &role_type);
+ base::strlcpy(reinterpret_cast<char*>(role_type.cRole),
+ role_name,
+ OMX_MAX_STRINGNAME_SIZE);
+
+ result = OMX_SetParameter(component_handle_,
+ OMX_IndexParamStandardComponentRole,
+ &role_type);
+ if (result != OMX_ErrorNone) {
+ LOG(ERROR) << "Failed to Set Role";
+ StopOnError();
+ return false;
+ }
+
+ // 7. Populate input-buffer-related members based on input port data.
+ OMX_PARAM_PORTDEFINITIONTYPE port_format;
+ InitParam(*this, &port_format);
+ port_format.nPortIndex = input_port_;
+ result = OMX_GetParameter(component_handle_,
+ OMX_IndexParamPortDefinition,
+ &port_format);
+ if (result != OMX_ErrorNone) {
+ LOG(ERROR) << "GetParameter(OMX_IndexParamPortDefinition) failed";
+ StopOnError();
+ return false;
+ }
+ if (OMX_DirInput != port_format.eDir) {
+ LOG(ERROR) << "Expected input port";
+ StopOnError();
+ return false;
+ }
+ input_buffer_count_ = port_format.nBufferCountActual;
+ input_buffer_size_ = port_format.nBufferSize;
+
+ // OMX_IndexParamPortDefinition on output port to be done in
+ // AllocateOutputBuffers.
+ // Since at this point we dont know if we will be using system memory
+ // or egl-image for decoding.
+ // We get this info in AssignPictureBuffers() from plugin.
+
+ return true;
+}
+
+bool OmxVideoDecodeAccelerator::Decode(
+ media::BitstreamBuffer* bitstream_buffer,
+ const media::VideoDecodeAcceleratorCallback& callback) {
+ DCHECK(!free_input_buffers_.empty());
+ DCHECK(bitstream_buffer);
+
+ if (!CanAcceptInput()) {
+ return false;
+ }
+
+ OMX_BUFFERHEADERTYPE* omx_buffer = free_input_buffers_.front();
+ free_input_buffers_.pop();
+
+ // setup |omx_buffer|.
+ omx_buffer->pBuffer = static_cast<OMX_U8*>(bitstream_buffer->bitstream());
+ omx_buffer->nFilledLen = bitstream_buffer->bitstream_size();
+ omx_buffer->nAllocLen = omx_buffer->nFilledLen;
+
+ omx_buffer->nFlags &= ~OMX_BUFFERFLAG_EOS;
+ omx_buffer->nTimeStamp = 0;
+
+ // Give this buffer to OMX.
+ OMX_ERRORTYPE result = OMX_ErrorNone;
+ result = OMX_EmptyThisBuffer(component_handle_, omx_buffer);
+ if (result != OMX_ErrorNone) {
+ LOG(ERROR) << "OMX_EmptyThisBuffer() failed with result " << result;
+ StopOnError();
+ return false;
+ }
+ input_buffers_at_component_++;
+ // OMX_EmptyThisBuffer is a non blocking call and should
+ // not make any assumptions about its completion.
+ omx_buff_cb_.insert(std::make_pair(omx_buffer, callback));
+ return true;
+}
+
+void OmxVideoDecodeAccelerator::AssignPictureBuffer(
+ std::vector<PictureBuffer*> picture_buffers) {
+ // NOTE: this is only partially-implemented as it only inspects the first
+ // picture buffer passed in each AssignPictureBuffer call, and never unsets
+ // uses_egl_image_ once set.
+ if (PictureBuffer::PICTUREBUFFER_MEMORYTYPE_GL_TEXTURE ==
+ picture_buffers[0]->GetMemoryType()) {
+ uses_egl_image_ = true;
+ }
+
+ assigned_picture_buffers_.insert(
+ assigned_picture_buffers_.begin(),
+ picture_buffers.begin(),
+ picture_buffers.end());
+
+ if (assigned_picture_buffers_.size() < kNumPictureBuffers)
+ return; // get all the buffers first.
+
+ // Obtain the information about the output port.
+ OMX_PARAM_PORTDEFINITIONTYPE port_format;
+ InitParam(*this, &port_format);
+ port_format.nPortIndex = output_port_;
+ OMX_ERRORTYPE result = OMX_GetParameter(component_handle_,
+ OMX_IndexParamPortDefinition,
+ &port_format);
+ if (result != OMX_ErrorNone) {
+ LOG(ERROR) << "GetParameter(OMX_IndexParamPortDefinition) failed";
+ StopOnError();
+ return;
+ }
+ if (OMX_DirOutput != port_format.eDir) {
+ LOG(ERROR) << "Expect Output Port";
+ StopOnError();
+ return;
+ }
+
+ if (uses_egl_image_) {
+ port_format.nBufferCountActual = kNumPictureBuffers;
+ port_format.nBufferCountMin = kNumPictureBuffers;
+ output_buffer_count_ = kNumPictureBuffers;
+
+ result = OMX_SetParameter(component_handle_,
+ OMX_IndexParamPortDefinition,
+ &port_format);
+ if (result != OMX_ErrorNone) {
+ LOG(ERROR) << "SetParameter(OMX_IndexParamPortDefinition) failed";
+ StopOnError();
+ return;
+ }
+ } else {
+ output_buffer_count_ = port_format.nBufferCountActual;
+ }
+ output_buffer_size_ = port_format.nBufferSize;
+
+ if (!AllocateOutputBuffers()) {
+ LOG(ERROR) << "OMX_AllocateBuffer() Output buffer error";
+ StopOnError();
+ }
+}
+
+void OmxVideoDecodeAccelerator::ReusePictureBuffer(int32 picture_buffer_id) {
+ // TODO(vhiremath@nvidia.com) Avoid leaking of the picture buffer.
+ if (!CanFillBuffer())
+ return;
+
+ for (int i = 0; i < output_buffer_count_; ++i) {
+ if (picture_buffer_id != assigned_picture_buffers_[i]->GetId())
+ continue;
+ output_buffers_at_component_++;
+ OMX_ERRORTYPE result =
+ OMX_FillThisBuffer(component_handle_, output_pictures_[i].second);
+ if (result != OMX_ErrorNone) {
+ LOG(ERROR) << "OMX_FillThisBuffer() failed with result " << result;
+ StopOnError();
+ }
+ // Sent one buffer to omx.
+ return;
+ }
+}
+
+void OmxVideoDecodeAccelerator::InitialFillBuffer() {
+ if (!CanFillBuffer())
+ return;
+
+ // Ask the decoder to fill the output buffers.
+ for (uint32 i = 0; i < output_pictures_.size(); ++i) {
+ OMX_BUFFERHEADERTYPE* omx_buffer = output_pictures_[i].second;
+ // clear EOS flag.
+ omx_buffer->nFlags &= ~OMX_BUFFERFLAG_EOS;
+ omx_buffer->nOutputPortIndex = output_port_;
+ output_buffers_at_component_++;
+ OMX_ERRORTYPE result = OMX_FillThisBuffer(component_handle_, omx_buffer);
+ if (result != OMX_ErrorNone) {
+ LOG(ERROR) << "OMX_FillThisBuffer() failed with result " << result;
+ StopOnError();
+ return;
+ }
+ }
+}
+
+bool OmxVideoDecodeAccelerator::Flush(
+ const media::VideoDecodeAcceleratorCallback& callback) {
+ OMX_STATETYPE il_state;
+ OMX_GetState(component_handle_, &il_state);
+ DCHECK_EQ(il_state, OMX_StateExecuting);
+ if (il_state != OMX_StateExecuting) {
+ callback.Run();
+ return false;
+ }
+ on_buffer_flag_event_func_ = &OmxVideoDecodeAccelerator::FlushBegin;
+ flush_done_callback_ = callback;
+
+ OMX_BUFFERHEADERTYPE* omx_buffer = free_input_buffers_.front();
+ omx_buffer->nFilledLen = 0;
+ omx_buffer->nAllocLen = omx_buffer->nFilledLen;
+ omx_buffer->nFlags |= OMX_BUFFERFLAG_EOS;
+ omx_buffer->nTimeStamp = 0;
+ // Give this buffer to OMX.
+ OMX_ERRORTYPE result = OMX_ErrorNone;
+ result = OMX_EmptyThisBuffer(component_handle_, omx_buffer);
+ if (result != OMX_ErrorNone) {
+ LOG(ERROR) << "OMX_EmptyThisBuffer() failed with result " << result;
+ StopOnError();
+ return false;
+ }
+ input_buffers_at_component_++;
+ return true;
+}
+
+void OmxVideoDecodeAccelerator::FlushBegin() {
+ VLOG(1) << "Starting actual flush for EOS";
+ on_state_event_func_ = &OmxVideoDecodeAccelerator::PauseFromExecuting;
+ TransitionToState(OMX_StatePause);
+}
+
+void OmxVideoDecodeAccelerator::PauseFromExecuting(OMX_STATETYPE ignored) {
+ on_state_event_func_ = NULL;
+ FlushIOPorts();
+}
+
+void OmxVideoDecodeAccelerator::FlushIOPorts() {
+ // TODO(vhiremath@nvidia.com) review again for trick modes.
+ VLOG(1) << "FlushIOPorts";
+
+ // Flush input port first.
+ on_flush_event_func_ = &OmxVideoDecodeAccelerator::PortFlushDone;
+ OMX_ERRORTYPE result;
+ result = OMX_SendCommand(component_handle_,
+ OMX_CommandFlush,
+ input_port_, 0);
+ if (result != OMX_ErrorNone) {
+ LOG(ERROR) << "OMX_SendCommand(OMX_CommandFlush) failed";
+ StopOnError();
+ return;
+ }
+}
+
+void OmxVideoDecodeAccelerator::PortFlushDone(int port) {
+ DCHECK_NE(port, static_cast<int>(OMX_ALL));
+
+ if (port == input_port_) {
+ VLOG(1) << "Input Port had been flushed";
+ DCHECK_EQ(input_buffers_at_component_, 0);
+ // Flush output port next.
+ OMX_ERRORTYPE result;
+ result = OMX_SendCommand(component_handle_,
+ OMX_CommandFlush,
+ output_port_, 0);
+ if (result != OMX_ErrorNone) {
+ LOG(ERROR) << "OMX_SendCommand(OMX_CommandFlush) failed";
+ StopOnError();
+ return;
+ }
+ return;
+ }
+
+ if (port == output_port_) {
+ VLOG(1) << "Output Port had been flushed";
+ DCHECK_EQ(output_buffers_at_component_, 0);
+ }
+
+ client_state_ = OMX_StatePause;
+ // So Finally call OnPortCommandFlush which should
+ // internally call DismissPictureBuffer();
+ OnPortCommandFlush(OMX_StateExecuting);
+}
+
+bool OmxVideoDecodeAccelerator::Abort(
+ const media::VideoDecodeAcceleratorCallback& callback) {
+ // TODO(vhiremath@nvidia.com)
+ // Need more thinking on this to handle w.r.t OMX.
+ // There is no explicit UnInitialize call for this.
+ // Also review again for trick modes.
+ callback.Run();
+ return true;
+}
+
+// Event callback during initialization to handle DoneStateSet to idle
+void OmxVideoDecodeAccelerator::OnStateChangeLoadedToIdle(OMX_STATETYPE state) {
+ DCHECK_EQ(client_state_, OMX_StateLoaded);
+ DCHECK_EQ(OMX_StateIdle, state);
+ VLOG(1) << "OMX video decode engine is in Idle";
+
+ on_state_event_func_ =
+ &OmxVideoDecodeAccelerator::OnStateChangeIdleToExecuting;
+ if (!TransitionToState(OMX_StateExecuting))
+ return;
+}
+
+// Event callback during initialization to handle DoneStateSet to executing
+void OmxVideoDecodeAccelerator::OnStateChangeIdleToExecuting(
+ OMX_STATETYPE state) {
+ DCHECK_EQ(OMX_StateExecuting, state);
+ VLOG(1) << "OMX video decode engine is in Executing";
+
+ client_state_ = OMX_StateExecuting;
+ on_state_event_func_ = NULL;
+ // This will kickoff the actual decoding
+ client_->NotifyResourcesAcquired();
+ InitialFillBuffer();
+}
+
+// Send state transition command to component.
+bool OmxVideoDecodeAccelerator::TransitionToState(OMX_STATETYPE new_state) {
+ OMX_ERRORTYPE result = OMX_SendCommand(component_handle_,
+ OMX_CommandStateSet,
+ new_state, 0);
+ if (result != OMX_ErrorNone) {
+ LOG(ERROR) << "SendCommand(OMX_CommandStateSet) failed";
+ StopOnError();
+ return false;
+ }
+ return true;
+}
+
+void OmxVideoDecodeAccelerator::OnPortCommandFlush(OMX_STATETYPE state) {
+ DCHECK_EQ(state, OMX_StateExecuting);
+
+ VLOG(1) << "Deinit from Executing";
+ on_state_event_func_ =
+ &OmxVideoDecodeAccelerator::OnStateChangeExecutingToIdle;
+ TransitionToState(OMX_StateIdle);
+ for (int i = 0; i < output_buffer_count_; ++i) {
+ OutputPicture output_picture = output_pictures_[i];
+ client_->DismissPictureBuffer(output_picture.first);
+ }
+ output_pictures_.clear();
+}
+
+void OmxVideoDecodeAccelerator::OnStateChangeExecutingToIdle(
+ OMX_STATETYPE state) {
+ DCHECK_EQ(state, OMX_StateIdle);
+
+ VLOG(1) << "Deinit from Idle";
+ on_state_event_func_ =
+ &OmxVideoDecodeAccelerator::OnStateChangeIdleToLoaded;
+ TransitionToState(OMX_StateLoaded);
+
+ if (!input_buffers_at_component_)
+ FreeInputBuffers();
+
+ if (!output_buffers_at_component_)
+ FreeOutputBuffers();
+}
+
+void OmxVideoDecodeAccelerator::OnStateChangeIdleToLoaded(OMX_STATETYPE state) {
+ DCHECK_EQ(state, OMX_StateLoaded);
+
+ VLOG(1) << "Idle to Loaded";
+
+ if (component_handle_) {
+ OMX_ERRORTYPE result = (*omx_free_handle)(component_handle_);
+ if (result != OMX_ErrorNone)
+ LOG(ERROR) << "OMX_FreeHandle() error. Error code: " << result;
+ component_handle_ = NULL;
+ }
+ client_state_ = OMX_StateLoaded;
+ (*omx_deinit)();
+ VLOG(1) << "OMX Deinit Clean exit done";
+ flush_done_callback_.Run();
+}
+
+void OmxVideoDecodeAccelerator::StopOnError() {
+ OMX_STATETYPE il_state;
+ OMX_GetState(component_handle_, &il_state);
+ client_state_ = OMX_StateInvalid;
+ switch (il_state) {
+ case OMX_StateExecuting:
+ OnPortCommandFlush(OMX_StateExecuting);
+ return;
+ case OMX_StateIdle:
+ OnStateChangeExecutingToIdle(OMX_StateIdle);
+ return;
+ case OMX_StateLoaded:
+ OnStateChangeIdleToLoaded(OMX_StateLoaded);
+ return;
+ default:
+ // LOG unexpected state or just ignore?
+ return;
+ }
+}
+
+bool OmxVideoDecodeAccelerator::AllocateInputBuffers() {
+ scoped_array<uint8> data(new uint8[input_buffer_size_]);
+
+ for (int i = 0; i < input_buffer_count_; ++i) {
+ OMX_BUFFERHEADERTYPE* buffer;
+ OMX_ERRORTYPE result =
+ OMX_UseBuffer(component_handle_, &buffer, input_port_,
+ this, input_buffer_size_, data.get());
+ if (result != OMX_ErrorNone)
+ return false;
+ buffer->nInputPortIndex = input_port_;
+ buffer->nOffset = 0;
+ buffer->nFlags = 0;
+ free_input_buffers_.push(buffer);
+ }
+ return true;
+}
+
+bool OmxVideoDecodeAccelerator::AllocateOutputBuffers() {
+ OMX_BUFFERHEADERTYPE* buffer;
+ Picture* picture;
+ OMX_ERRORTYPE result;
+ gfx::Size decoded_pixel_size(width_, height_);
+ gfx::Size visible_pixel_size(width_, height_);
+
+ if (uses_egl_image_) {
+ media::VideoDecodeAccelerator::PictureBuffer::DataPlaneHandle egl_ids;
+ std::vector<PictureBuffer::DataPlaneHandle> planes;
+ uint32 texture;
+
+ for (uint32 i = 0; i < assigned_picture_buffers_.size(); i++) {
+ picture = new media::Picture(
+ reinterpret_cast<media::PictureBuffer*>(assigned_picture_buffers_[i]),
+ decoded_pixel_size, visible_pixel_size,
+ static_cast<void*>(component_handle_));
+
+ planes = assigned_picture_buffers_[i]->GetPlaneHandles();
+ egl_ids = planes[i];
+ texture = egl_ids.texture_id;
+ egl_image_ = texture2eglImage_translator->TranslateToEglImage(texture);
+ result = OMX_UseEGLImage(
+ component_handle_,
+ &buffer,
+ output_port_,
+ reinterpret_cast<media::PictureBuffer*>(assigned_picture_buffers_[i]),
+ egl_image_);
+
+ if (result != OMX_ErrorNone) {
+ LOG(ERROR) << "OMX_UseEGLImage failed";
+ return false;
+ }
+ output_pictures_.push_back(
+ std::make_pair(
+ reinterpret_cast<media::PictureBuffer*>(
+ assigned_picture_buffers_[i]),
+ buffer));
+ buffer->pAppPrivate = picture;
+ }
+ } else {
+ for (uint32 i = 0; i < assigned_picture_buffers_.size(); i++) {
+ picture = new media::Picture(
+ reinterpret_cast<media::PictureBuffer*>(assigned_picture_buffers_[i]),
+ decoded_pixel_size, visible_pixel_size,
+ static_cast<void*>(component_handle_));
+
+ result = OMX_AllocateBuffer(component_handle_, &buffer, output_port_,
+ NULL, output_buffer_size_);
+ if (result != OMX_ErrorNone)
+ return false;
+ output_pictures_.push_back(
+ std::make_pair(
+ reinterpret_cast<media::PictureBuffer*>(
+ assigned_picture_buffers_[i]),
+ buffer));
+ buffer->pAppPrivate = picture;
+ }
+ }
+ return true;
+}
+
+void OmxVideoDecodeAccelerator::FreeInputBuffers() {
+ // Calls to OMX to free buffers.
+ OMX_ERRORTYPE result;
+ OMX_BUFFERHEADERTYPE* omx_buffer;
+ for (int i = 0; i < input_buffer_count_; ++i) {
+ omx_buffer = free_input_buffers_.front();
+ free_input_buffers_.pop();
+ result = OMX_FreeBuffer(component_handle_, input_port_, omx_buffer);
+ if (result != OMX_ErrorNone) {
+ LOG(ERROR) << "SendCommand(OMX_CommandPortDisable) failed";
+ StopOnError();
+ return;
+ }
+ }
+}
+
+void OmxVideoDecodeAccelerator::FreeOutputBuffers() {
+ // Calls to OMX to free buffers.
+ OMX_ERRORTYPE result;
+ for (size_t i = 0; i < output_pictures_.size(); ++i) {
+ OMX_BUFFERHEADERTYPE* omx_buffer = output_pictures_[i].second;
+ CHECK(omx_buffer);
+ result = OMX_FreeBuffer(component_handle_, output_port_, omx_buffer);
+ if (result != OMX_ErrorNone) {
+ LOG(ERROR) << "SendCommand(OMX_CommandPortDisable) failed";
+ StopOnError();
+ return;
+ }
+ }
+ output_pictures_.clear();
+}
+
+void OmxVideoDecodeAccelerator::OnPortSettingsChangedRun(
+ int port, OMX_INDEXTYPE index) {
+ // TODO(vhiremath@nvidia.com) visit again later
+ // Port settings changes can be called during run time
+ // changes in the resolution of video playback.
+ // In this case, the component detects PortSettingsChanged
+ // and sends the particular event to the IL-client.
+ // This needs to be handled in this method.
+ return;
+}
+
+void OmxVideoDecodeAccelerator::FillBufferDoneTask(
+ OMX_BUFFERHEADERTYPE* buffer) {
+ DCHECK_GT(output_buffers_at_component_, 0);
+ output_buffers_at_component_--;
+ client_->PictureReady(reinterpret_cast<Picture*>(buffer->pAppPrivate));
+}
+
+void OmxVideoDecodeAccelerator::EmptyBufferDoneTask(
+ OMX_BUFFERHEADERTYPE* buffer) {
+ DCHECK_GT(input_buffers_at_component_, 0);
+ free_input_buffers_.push(buffer);
+ input_buffers_at_component_--;
+ if (buffer->nFlags & OMX_BUFFERFLAG_EOS)
+ return;
+ // Retrieve the corresponding callback and run it.
+ OMXBufferCallbackMap::iterator it = omx_buff_cb_.find(buffer);
+ if (it == omx_buff_cb_.end()) {
+ LOG(ERROR) << "Unexpectedly failed to find a buffer callback.";
+ StopOnError();
+ return;
+ }
+ it->second.Run();
+ omx_buff_cb_.erase(it);
+}
+
+void OmxVideoDecodeAccelerator::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);
+ switch (cmd) {
+ case OMX_CommandPortDisable: {
+ if (on_port_disable_event_func_)
+ (this->*on_port_disable_event_func_)(static_cast<int>(data2));
+ }
+ break;
+ case OMX_CommandPortEnable: {
+ if (on_port_enable_event_func_)
+ (this->*on_port_enable_event_func_)(static_cast<int>(data2));
+ }
+ break;
+ case OMX_CommandStateSet:
+ (this->*on_state_event_func_)(static_cast<OMX_STATETYPE>(data2));
+ break;
+ case OMX_CommandFlush:
+ (this->*on_flush_event_func_)(data2);
+ break;
+ default:
+ LOG(ERROR) << "Unknown command completed\n" << data1;
+ break;
+ }
+ break;
+ }
+ case OMX_EventError:
+ if (static_cast<OMX_ERRORTYPE>(data1) == OMX_ErrorInvalidState)
+ StopOnError();
+ break;
+ case OMX_EventPortSettingsChanged:
+ // TODO(vhiremath@nvidia.com) 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;
+ case OMX_EventBufferFlag:
+ if (data1 == static_cast<OMX_U32>(output_port_)) {
+ (this->*on_buffer_flag_event_func_)();
+ }
+ break;
+ default:
+ LOG(ERROR) << "Warning - Unknown event received\n";
+ break;
+ }
+}
+
+// static
+OMX_ERRORTYPE OmxVideoDecodeAccelerator::EventHandler(OMX_HANDLETYPE component,
+ OMX_PTR priv_data,
+ OMX_EVENTTYPE event,
+ OMX_U32 data1,
+ OMX_U32 data2,
+ OMX_PTR event_data) {
+ OmxVideoDecodeAccelerator* decoder =
+ static_cast<OmxVideoDecodeAccelerator*>(priv_data);
+ DCHECK_EQ(component, decoder->component_handle_);
+
+ decoder->message_loop_->PostTask(FROM_HERE,
+ NewRunnableMethod(decoder,
+ &OmxVideoDecodeAccelerator::EventHandlerCompleteTask,
+ event, data1, data2));
+
+ return OMX_ErrorNone;
+}
+
+// static
+OMX_ERRORTYPE OmxVideoDecodeAccelerator::EmptyBufferCallback(
+ OMX_HANDLETYPE component,
+ OMX_PTR priv_data,
+ OMX_BUFFERHEADERTYPE* buffer) {
+ OmxVideoDecodeAccelerator* decoder =
+ static_cast<OmxVideoDecodeAccelerator*>(priv_data);
+ DCHECK_EQ(component, decoder->component_handle_);
+
+ decoder->message_loop_->PostTask(
+ FROM_HERE,
+ NewRunnableMethod(
+ decoder,
+ &OmxVideoDecodeAccelerator::EmptyBufferDoneTask, buffer));
+ return OMX_ErrorNone;
+}
+
+// static
+OMX_ERRORTYPE OmxVideoDecodeAccelerator::FillBufferCallback(
+ OMX_HANDLETYPE component,
+ OMX_PTR priv_data,
+ OMX_BUFFERHEADERTYPE* buffer) {
+ OmxVideoDecodeAccelerator* decoder =
+ static_cast<OmxVideoDecodeAccelerator*>(priv_data);
+ DCHECK_EQ(component, decoder->component_handle_);
+
+ decoder->message_loop_->PostTask(FROM_HERE,
+ NewRunnableMethod(
+ decoder,
+ &OmxVideoDecodeAccelerator::FillBufferDoneTask, buffer));
+ return OMX_ErrorNone;
+}
+
+bool OmxVideoDecodeAccelerator::CanAcceptInput() {
+ // We can't take input buffer when in error state.
+ return (client_state_ != OMX_StateInvalid &&
+ client_state_ != OMX_StatePause &&
+ client_state_ != OMX_StateLoaded);
+}
+
+bool OmxVideoDecodeAccelerator::CanFillBuffer() {
+ // Make sure component is in the executing state and end-of-stream
+ // has not been reached.
+ OMX_ERRORTYPE result;
+ OMX_STATETYPE il_state;
+ result = OMX_GetState(component_handle_, &il_state);
+ if (result != OMX_ErrorNone) {
+ LOG(ERROR) << "SendCommand(OMX_CommandPortDisable) failed";
+ StopOnError();
+ return false;
+ }
+ return (il_state == OMX_StateExecuting);
+}
+
+// Send command to disable/enable port.
+void OmxVideoDecodeAccelerator::ChangePort(
+ OMX_COMMANDTYPE cmd, int port_index) {
+ OMX_ERRORTYPE result = OMX_SendCommand(component_handle_,
+ cmd, port_index, 0);
+ if (result != OMX_ErrorNone) {
+ LOG(ERROR) << "SendCommand(OMX_CommandPortDisable) failed";
+ StopOnError();
+ return;
+ }
+}
+
+DISABLE_RUNNABLE_METHOD_REFCOUNT(OmxVideoDecodeAccelerator);
+
diff --git a/content/gpu/omx_video_decode_accelerator.h b/content/gpu/omx_video_decode_accelerator.h
new file mode 100644
index 0000000..522f8fa
--- /dev/null
+++ b/content/gpu/omx_video_decode_accelerator.h
@@ -0,0 +1,163 @@
+// 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.
+
+#ifndef CONTENT_GPU_OMX_VIDEO_DECODE_ACCELERATOR_H_
+#define CONTENT_GPU_OMX_VIDEO_DECODE_ACCELERATOR_H_
+
+#include <dlfcn.h>
+#include <map>
+#include <queue>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/logging.h"
+#include "base/message_loop.h"
+#include "media/video/video_decode_accelerator.h"
+#include "third_party/openmax/il/OMX_Component.h"
+#include "third_party/openmax/il/OMX_Core.h"
+#include "third_party/openmax/il/OMX_Video.h"
+
+// Class to wrap OpenMAX IL accelerator behind VideoDecodeAccelerator interface.
+class OmxVideoDecodeAccelerator : public media::VideoDecodeAccelerator {
+ public:
+ OmxVideoDecodeAccelerator(media::VideoDecodeAccelerator::Client* client,
+ MessageLoop* message_loop);
+ virtual ~OmxVideoDecodeAccelerator();
+
+ // media::VideoDecodeAccelerator implementation.
+ const std::vector<uint32>& GetConfig(
+ const std::vector<uint32>& prototype_config);
+ bool Initialize(const std::vector<uint32>& config);
+ bool Decode(media::BitstreamBuffer* bitstream_buffer,
+ const media::VideoDecodeAcceleratorCallback& callback);
+ void AssignPictureBuffer(std::vector<PictureBuffer*> picture_buffers);
+ void ReusePictureBuffer(int32 picture_buffer_id);
+ bool Flush(const media::VideoDecodeAcceleratorCallback& callback);
+ bool Abort(const media::VideoDecodeAcceleratorCallback& callback);
+
+ private:
+ MessageLoop* message_loop_;
+ OMX_HANDLETYPE component_handle_;
+
+ // Create the Component for OMX. Handles all OMX initialization.
+ bool CreateComponent();
+ // Buffer allocation/free methods for input and output buffers.
+ bool AllocateInputBuffers();
+ bool AllocateOutputBuffers();
+ void FreeInputBuffers();
+ void FreeOutputBuffers();
+
+ // Methods to handle OMX state transitions.
+ bool TransitionToState(OMX_STATETYPE new_state);
+ void OnStateChangeLoadedToIdle(OMX_STATETYPE state);
+ void OnStateChangeIdleToExecuting(OMX_STATETYPE state);
+ void OnPortCommandFlush(OMX_STATETYPE state);
+ void OnStateChangeExecutingToIdle(OMX_STATETYPE state);
+ void OnStateChangeIdleToLoaded(OMX_STATETYPE state);
+ // Stop the components when error is detected.
+ void StopOnError();
+ // Trigger the initial call to FillBuffers to start the decoding process.
+ void InitialFillBuffer();
+ // Methods for shutdown
+ void PauseFromExecuting(OMX_STATETYPE ignored);
+ void FlushIOPorts();
+ void PortFlushDone(int port);
+ void FlushBegin();
+
+ // Determine whether we actually start decoding the bitstream.
+ bool CanAcceptInput();
+ // Determine whether we can issue fill buffer or empty buffer
+ // to the decoder based on the current state and port state.
+ bool CanEmptyBuffer();
+ bool CanFillBuffer();
+ void OnPortSettingsChangedRun(int port, OMX_INDEXTYPE index);
+
+ // Decoded width/height from bitstream.
+ int width_;
+ int height_;
+ std::vector<uint32> component_config_;
+
+ // IL-client state.
+ OMX_STATETYPE client_state_;
+
+ // Following are input port related variables.
+ int input_buffer_count_;
+ int input_buffer_size_;
+ int input_port_;
+ int input_buffers_at_component_;
+
+ // Following are output port related variables.
+ int output_buffer_count_;
+ int output_buffer_size_;
+ int output_port_;
+ int output_buffers_at_component_;
+
+ bool uses_egl_image_;
+ // Free input OpenMAX buffers that can be used to take bitstream from demuxer.
+ std::queue<OMX_BUFFERHEADERTYPE*> free_input_buffers_;
+
+ // For output buffer recycling cases.
+ std::vector<media::VideoDecodeAccelerator::PictureBuffer*>
+ assigned_picture_buffers_;
+ typedef std::pair<PictureBuffer*,
+ OMX_BUFFERHEADERTYPE*> OutputPicture;
+ std::vector<OutputPicture> output_pictures_;
+
+ // To expose client callbacks from VideoDecodeAccelerator.
+ Client* client_;
+
+ media::VideoDecodeAcceleratorCallback flush_done_callback_;
+ media::VideoDecodeAcceleratorCallback abort_done_callback_;
+
+ std::vector<uint32> texture_ids_;
+ std::vector<uint32> context_ids_;
+ // Method to handle events
+ void EventHandlerCompleteTask(OMX_EVENTTYPE event,
+ OMX_U32 data1,
+ OMX_U32 data2);
+
+ // Method to receive buffers from component's input port
+ void EmptyBufferDoneTask(OMX_BUFFERHEADERTYPE* buffer);
+
+ // Method to receive buffers from component's output port
+ void FillBufferDoneTask(OMX_BUFFERHEADERTYPE* buffer);
+ typedef std::pair<OMX_BUFFERHEADERTYPE*, uint32> OMXbufferTexture;
+ // void pointer to hold EGLImage handle.
+ void* egl_image_;
+
+ typedef std::map<OMX_BUFFERHEADERTYPE*,
+ media::VideoDecodeAcceleratorCallback> OMXBufferCallbackMap;
+ OMXBufferCallbackMap omx_buff_cb_;
+
+ // Method used the change the state of the port.
+ void ChangePort(OMX_COMMANDTYPE cmd, int port_index);
+
+ // Member function pointers to respond to events
+ void (OmxVideoDecodeAccelerator::*on_port_disable_event_func_)(int port);
+ void (OmxVideoDecodeAccelerator::*on_port_enable_event_func_)(int port);
+ void (OmxVideoDecodeAccelerator::*on_state_event_func_)(OMX_STATETYPE state);
+ void (OmxVideoDecodeAccelerator::*on_flush_event_func_)(int port);
+ void (OmxVideoDecodeAccelerator::*on_buffer_flag_event_func_)();
+
+ // Callback methods for the OMX component.
+ // When these callbacks are received, the
+ // call is delegated to the three internal methods above.
+ static OMX_ERRORTYPE EventHandler(OMX_HANDLETYPE component,
+ OMX_PTR priv_data,
+ OMX_EVENTTYPE event,
+ OMX_U32 data1, OMX_U32 data2,
+ OMX_PTR event_data);
+ static OMX_ERRORTYPE EmptyBufferCallback(OMX_HANDLETYPE component,
+ OMX_PTR priv_data,
+ OMX_BUFFERHEADERTYPE* buffer);
+ static OMX_ERRORTYPE FillBufferCallback(OMX_HANDLETYPE component,
+ OMX_PTR priv_data,
+ OMX_BUFFERHEADERTYPE* buffer);
+};
+
+#endif // CONTENT_GPU_OMX_VIDEO_DECODE_ACCELERATOR_H_
+